diff --git a/common/misc/tmux-launch-agent/SKILL.md b/common/misc/tmux-launch-agent/SKILL.md
index 124bf38..db1b51c 100644
--- a/common/misc/tmux-launch-agent/SKILL.md
+++ b/common/misc/tmux-launch-agent/SKILL.md
@@ -6,52 +6,26 @@ disable-model-invocation: true
## Agent CLI Seed Data
-The agent config (binary, `args` convention per agent) and field meanings are in [`agents-seed.md`](agents-seed.md). Step 2 reads it to find the calling agent's entry.
+Agent config (binary, `args`, `modelflag`) is in [`agents-seed.md`](agents-seed.md). Step 2 reads it by `name`.
+## Dispatch
-## Process
+1. **Detect agent** — Run `./detect-agent` (sibling). On success, the agent name is known. On failure (exit 1), stop.
-1. **Detect the calling agent** — Run `./detect-agent` (sibling to this skill). If it exits 1 (agent unknown), report the failure and stop — the agent name is required.
+2. **Look up config** — Find the agent's entry in [`agents-seed.md`](agents-seed.md) by `name`. Extract `binary`, `args`, and `modelflag`.
- Completion criterion: The agent name is known and non-empty.
+3. **Parse flags** — Scan user arguments for flags (before prompt text). For each flag, extract its value and remove both the flag and value from the argument list:
+ - `--name
` / `-n ` — tmux window title
+ - `-c ` — working directory (default: project directory)
+ - `--model ` / `-m ` — model for child session
-2. **Look up the agent config** — Find the agent's entry in [`agents-seed.md`](agents-seed.md) by `name`. Extract its `binary` and `args` fields.
+ Remaining text is the prompt. If `--name` is absent, tmux auto-names the window.
- Completion criterion: The agent's entry is found and its `binary` and `args` are known.
-
-3. **Parse flags** — Scan the user's arguments for optional flags (which must come before the prompt text):
- - `--name ` or `-n ` — the tmux window title. Extract the title and remove the flag and its value from the arguments list.
- - `-c ` — the working directory for the new window. Extract the path and remove the flag and its value from the arguments list.
-
- The remaining text after stripping both flags is the prompt for the child agent.
-
- If `--name`/`-n` is absent, tmux auto-names the window.
- If `-c` is absent, the new window inherits the current pane's working directory.
-
- Completion criterion: The arguments are split into an optional window name, an optional directory path, and the remaining prompt text.
-
-4. **Build the inner command** — Combine the agent config with the remaining user-supplied prompt arguments. The `args` template determines how the prompt is delivered:
-
- - **Prompt-file agents** (`args` contains `{prompt}`) — Write the prompt text to a temporary file under `/tmp/` and substitute the file path for `{prompt}` in the args template. For example, an agent with `args: "@{prompt}"` becomes ` @/tmp/tmux-launch-XXXX.md`.
- - **Stdin-pipe agents** (`args` is empty) — Pipe the prompt text via `echo` into the binary.
- - **No prompt** — If the user passed no arguments (after removing flags), launch the binary bare (interactive start) with no prompt file or pipe.
-
- Completion criterion: The inner command is correctly built per the target agent's `args` convention (prompt-file, stdin-pipe, or bare).
-
-5. **Wrap with the environment runner** — Detect whether `mise` is available via `command -v mise`:
-
- - **mise available** — Wrap the inner command as `mise x --allow-env='*' -- `.
- - **mise absent** — Wrap the inner command as `$SHELL -c ''`.
-
- Completion criterion: A valid shell command string is ready.
-
-6. **Fork into a new tmux window** — Build and run:
- ```
- tmux new-window ""
- ```
- - `` — the wrapped command from step 5.
- - `` — `-n ""` if a window name was parsed in step 3, omitted otherwise.
- - `` — `-c ` if a directory was parsed in step 3, omitted otherwise.
-
- Completion criterion: `tmux new-window` exits 0 and a new tmux window appears with the agent CLI session active.
+4. **Build command** — Assemble the inner command:
+ - Start with ``.
+ - If model specified: append ` `.
+ - If prompt exists and `args` contains `{prompt}`: write to `/tmp/`, substitute path for `{prompt}`.
+ - If prompt exists and `args` is empty: pipe via `echo`.
+ - If no prompt: launch bare.
+5. **Open window** — Call `./tmux-open ` where `` is the assembled command from step 4 as separate arguments. On success, a new tmux window appears with the agent session active.
diff --git a/common/misc/tmux-launch-agent/agents-seed.md b/common/misc/tmux-launch-agent/agents-seed.md
index e9f10bc..41a41b6 100644
--- a/common/misc/tmux-launch-agent/agents-seed.md
+++ b/common/misc/tmux-launch-agent/agents-seed.md
@@ -7,28 +7,33 @@ agents:
- name: pi
binary: pi
args: "@{prompt}"
+ modelflag: "--model"
description: "My primary agent harness. Accepts prompt file via {prompt}."
- name: opencode
binary: opencode
args: ""
+ modelflag: "-m"
description: "OpenCode agent. Pipes stdin via cat."
note: "Uses stdin piping: args must be empty, prompt via pipe."
- name: goose
binary: goose
args: ""
+ modelflag: "--model"
description: "Goose agent. Accepts prompt file via -i flag."
note: "Uses stdin piping: args must be empty, prompt via pipe."
- name: codex
binary: codex
args: "@{prompt}"
+ modelflag: "-m"
description: "OpenAI Codex. Accepts prompt file via {prompt}."
- name: claude
binary: claude
args: ""
+ modelflag: "--model"
description: "Anthropic Claude CLI. Pipes stdin via cat."
note: "Uses stdin piping: args must be empty, prompt via pipe."
```
@@ -36,9 +41,10 @@ agents:
## Field meanings
| Field | Description |
-|-------|-------------|
+| ------- | ------------- |
| `name` | Display name used in menus and `--agent` flag |
| `binary` | Command name expected on PATH |
| `args` | Static arguments appended after the binary. May include `{prompt}` which is substituted at invocation time with the absolute path to the prompt file. Empty string means stdin piping (prompt is piped via `cat`). |
| `description` | Short human-readable description for the setup menu |
+| `modelflag` | CLI flag used to select a model (e.g. `--model`, `-m`). Injected into the inner command when `--model ` is passed by the user. |
| `note` | Optional additional context |
diff --git a/common/misc/tmux-launch-agent/tmux-open b/common/misc/tmux-launch-agent/tmux-open
new file mode 100755
index 0000000..6b50930
--- /dev/null
+++ b/common/misc/tmux-launch-agent/tmux-open
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# tmux-open — open a new tmux window (or session) and run a command
+# Usage: tmux-open [-k|--keep] [-h|--help] [args...]
+
+usage() {
+ cat <<'EOF'
+Usage: tmux-open [-k|--keep] [-h|--help] [args...]
+
+Open a new tmux window in the current session and run a command.
+If not inside tmux, create a new session named after the window and attach.
+
+Positional arguments:
+ window-name Name for the new tmux window (and session, if creating one)
+ start-dir Working directory for the new window
+ cmd [args...] Command to run (variadic)
+
+Options:
+ -k, --keep After the command exits, keep the pane visible (remain-on-exit)
+ so you can inspect output. Default: pane closes when command exits.
+ -h, --help Show this help message
+
+If `mise` is on PATH, the command runs under `mise x`.
+
+Errors:
+ - tmux not installed
+ - fewer than 3 positional args
+ - start-dir does not exist
+ - a window with the same name already exists in the current session
+EOF
+}
+
+# --- Flag parsing ---
+keep_shell=0
+while (( $# > 0 )); do
+ case "$1" in
+ -k|--keep) keep_shell=1; shift ;;
+ -h|--help) usage; exit 0 ;;
+ --) shift; break ;;
+ -*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;;
+ *) break ;;
+ esac
+done
+
+# --- Positional arg validation ---
+if (( $# < 3 )); then
+ echo "error: expected at least 3 positional arguments (window-name, start-dir, cmd)" >&2
+ usage >&2
+ exit 1
+fi
+
+window_name="$1"; shift
+start_dir="$1"; shift
+cmd_args=("$@")
+
+# --- Pre-flight checks ---
+if ! command -v tmux >/dev/null 2>&1; then
+ echo "error: tmux is not installed or not on PATH" >&2
+ exit 1
+fi
+
+if [[ ! -d "$start_dir" ]]; then
+ echo "error: directory does not exist: $start_dir" >&2
+ exit 1
+fi
+
+if (( ${#cmd_args[@]} == 0 )); then
+ echo "error: no command specified" >&2
+ exit 1
+fi
+
+# Duplicate window name check (only relevant inside tmux)
+if [[ -n "${TMUX:-}" ]]; then
+ if tmux list-windows -F '#{window_name}' | grep -Fxq "$window_name"; then
+ echo "error: a window named '$window_name' already exists in the current session" >&2
+ exit 1
+ fi
+fi
+
+# --- Detect mise ---
+has_mise=0
+if command -v mise >/dev/null 2>&1; then
+ has_mise=1
+fi
+
+# --- Build the command for the new window ---
+# We need to construct a single shell command string for tmux.
+# The command runs; pane closes on exit (or stays visible with --keep via remain-on-exit).
+
+# Build the inner command based on mode and mise availability
+# For argv mode, quote each argument
+quoted_args=()
+for arg in "${cmd_args[@]}"; do
+ quoted_args+=("$(printf '%q' "$arg")")
+done
+if (( has_mise )); then
+ inner_cmd="mise x -- ${quoted_args[*]}"
+else
+ inner_cmd="${quoted_args[*]}"
+fi
+
+# --- Create window or session ---
+if [[ -n "${TMUX:-}" ]]; then
+ tmux new-window -n "$window_name" -c "$start_dir" "$inner_cmd"
+ if (( keep_shell )); then
+ tmux set-option remain-on-exit on
+ fi
+else
+ tmux new-session -d -n "$window_name" -s "$window_name" -c "$start_dir" "$inner_cmd"
+ if (( keep_shell )); then
+ tmux set-option -t "$window_name" remain-on-exit on
+ fi
+ tmux attach-session -t "$window_name"
+fi