aboutsummaryrefslogtreecommitdiff
path: root/claude-templates/bin
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-13 16:40:07 -0500
committerCraig Jennings <c@cjennings.net>2026-07-13 16:40:07 -0500
commit04c3b29a968d62eac1a4123cf03606fc667c2d14 (patch)
treedf10eb05ce73b74184bef5d8f64bb144a2a5d6c3 /claude-templates/bin
parent6cd3aa32b9f3b033c3890f08ab430e757696e5e3 (diff)
downloadrulesets-04c3b29a968d62eac1a4123cf03606fc667c2d14.tar.gz
rulesets-04c3b29a968d62eac1a4123cf03606fc667c2d14.zip
feat(launcher): add ai --runtime for choosing the agent CLI
The ai launcher can now start codex sessions: --runtime claude|codex (or AI_RUNTIME), a runtime-to-CLI map with local reserved behind a clear error until the local-model evaluation picks a CLI, and a runtime-aware dependency check. A new --print-launch mode prints the pane launch command without touching tmux or fzf, and is the seam the six new bats tests drive. Both current CLIs take the opening line as a positional prompt, so only the command name varies.
Diffstat (limited to 'claude-templates/bin')
-rwxr-xr-xclaude-templates/bin/ai98
1 files changed, 84 insertions, 14 deletions
diff --git a/claude-templates/bin/ai b/claude-templates/bin/ai
index 994dc1f..c09faab 100755
--- a/claude-templates/bin/ai
+++ b/claude-templates/bin/ai
@@ -1,5 +1,5 @@
#!/bin/bash
-# ai — Claude Code session launcher (unified aix + hey)
+# ai — agent session launcher (unified aix + hey)
#
# Usage:
# ai Select one or more projects via fzf and open each in
@@ -11,6 +11,10 @@
# in the 'ai' session (new window or switch to existing).
# Use '.' for current directory. Git prep per dir.
#
+# ai --runtime <rt> Launch with a different agent CLI: claude (default) or
+# codex. 'local' is reserved for a local-LLM CLI, pending
+# the model evaluation. Also settable via AI_RUNTIME.
+#
# ai --attach Attach to the existing 'ai' session without changes.
#
# ai -h | --help Show this help.
@@ -25,7 +29,25 @@
# would kill the script.
SESSION="ai"
-CLAUDE_CMD="claude"
+RUNTIME="${AI_RUNTIME:-claude}"
+
+# Map the runtime name to the agent CLI a pane launches. Both current CLIs
+# take the opening instructions as a positional prompt, so only the command
+# name varies; a runtime whose CLI needs different plumbing gets its own case.
+resolve_agent_cmd() {
+ case "$RUNTIME" in
+ claude) AGENT_CMD="claude" ;;
+ codex) AGENT_CMD="codex" ;;
+ local)
+ echo "ai: the 'local' runtime is reserved but not wired yet (pending the local-model evaluation) — valid runtimes: claude, codex" >&2
+ exit 2
+ ;;
+ *)
+ echo "ai: unknown runtime '$RUNTIME' — valid runtimes: claude, codex" >&2
+ exit 2
+ ;;
+ esac
+}
# Run in the pane's shell just before Claude launches. `stty susp undef` clears
# the tty's SIGTSTP (C-z) character for this pane only, so an accidental C-z is
@@ -50,12 +72,14 @@ usage() {
exit 0
}
-for cmd in fzf tmux claude; do
- if ! command -v "$cmd" &>/dev/null; then
- echo "ai: $cmd is not installed" >&2
- exit 1
- fi
-done
+check_deps() {
+ for cmd in fzf tmux "$AGENT_CMD"; do
+ if ! command -v "$cmd" &>/dev/null; then
+ echo "ai: $cmd is not installed" >&2
+ exit 1
+ fi
+ done
+}
# ---------- shared helpers ----------
@@ -73,7 +97,7 @@ create_window() {
wid=$(tmux new-window -a -t "$SESSION:{end}" -n "$name" -c "$dir" -P -F '#{window_id}')
sleep 0.1
instructions=$(build_instructions "$name")
- tmux send-keys -t "$wid" "${LAUNCH_PREFIX}$CLAUDE_CMD \"$instructions\"" Enter
+ tmux send-keys -t "$wid" "${LAUNCH_PREFIX}$AGENT_CMD \"$instructions\"" Enter
echo "$wid"
}
@@ -319,7 +343,7 @@ single_mode() {
local instructions
wid=$(tmux new-session -d -s "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}')
instructions=$(build_instructions "$name")
- tmux send-keys -t "$wid" "${LAUNCH_PREFIX}$CLAUDE_CMD \"$instructions\"" Enter
+ tmux send-keys -t "$wid" "${LAUNCH_PREFIX}$AGENT_CMD \"$instructions\"" Enter
fi
sort_windows
@@ -379,7 +403,7 @@ multi_mode() {
local instructions
first_wid=$(tmux new-session -d -s "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}')
instructions=$(build_instructions "$name")
- tmux send-keys -t "$first_wid" "${LAUNCH_PREFIX}$CLAUDE_CMD \"$instructions\"" Enter
+ tmux send-keys -t "$first_wid" "${LAUNCH_PREFIX}$AGENT_CMD \"$instructions\"" Enter
for entry in "${selected[@]:1}"; do
dir="${entry/#\~/$HOME}"
name="$(basename "$dir")"
@@ -403,12 +427,58 @@ multi_mode() {
attach_session
}
+# Print the launch command a real run would send to the pane, then exit.
+# Exists for the launcher's bats tests: exercises runtime resolution and the
+# opening line with no tmux or fzf involved.
+print_launch_mode() {
+ local arg="$1" dir name
+ dir="$(cd "$arg" 2>/dev/null && pwd)" || { echo "ai: cannot access '$arg'" >&2; exit 1; }
+ if [ ! -f "$dir/.ai/protocols.org" ]; then
+ echo "ai: $dir has no .ai/protocols.org — not an agent-template project" >&2
+ exit 1
+ fi
+ name="$(basename "$dir")"
+ printf '%s "%s"\n' "$AGENT_CMD" "$(build_instructions "$name")"
+ exit 0
+}
+
# ---------- dispatch ----------
+print_launch=""
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ usage
+ ;;
+ --runtime)
+ [ -z "${2:-}" ] && { echo "ai: --runtime needs a value — valid runtimes: claude, codex" >&2; exit 2; }
+ RUNTIME="$2"
+ shift 2
+ ;;
+ --runtime=*)
+ RUNTIME="${1#--runtime=}"
+ shift
+ ;;
+ --print-launch)
+ print_launch=1
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+resolve_agent_cmd
+
+if [ -n "$print_launch" ]; then
+ [ $# -eq 0 ] && { echo "ai: --print-launch needs a project directory" >&2; exit 2; }
+ print_launch_mode "$1"
+fi
+
+check_deps
+
case "${1:-}" in
- -h|--help)
- usage
- ;;
--attach)
attach_mode
;;