From dc9293c7be3eda25bb064a5e7d3719d2a2bd3fc8 Mon Sep 17 00:00:00 2001 From: Steve Beaulac Date: Fri, 26 Jun 2026 13:42:56 -0400 Subject: [PATCH] feat(implement-issue): make child agent dispatch agent-agnostic - Add formal arg syntax: --agent, --base, --force - Add process-tree detection scripts (Linux / macOS) to auto-detect parent agent - Add dispatch table supporting pi, opencode, goose, codex, claude + unknown fallback - Write child prompt to temp file instead of inline escaping - Name tmux windows for discoverability - Add new pre-flight checks: tmux session, agent detection, binary on PATH --- common/engineering/implement-issue/SKILL.md | 89 +++++++++++++++++-- .../implement-issue/detect-agent-linux.sh | 44 +++++++++ .../implement-issue/detect-agent-macos.sh | 48 ++++++++++ 3 files changed, 174 insertions(+), 7 deletions(-) create mode 100755 common/engineering/implement-issue/detect-agent-linux.sh create mode 100755 common/engineering/implement-issue/detect-agent-macos.sh diff --git a/common/engineering/implement-issue/SKILL.md b/common/engineering/implement-issue/SKILL.md index 3482585..2bef924 100644 --- a/common/engineering/implement-issue/SKILL.md +++ b/common/engineering/implement-issue/SKILL.md @@ -1,5 +1,6 @@ --- name: implement-issue +description: Dispatch a child agent in an isolated git worktree to implement a `ready-for-agent` issue end-to-end. disable-model-invocation: true --- @@ -9,6 +10,17 @@ Dispatch a child agent in an isolated git worktree to implement a `ready-for-age The issue tracker conventions live in [`docs/agents/issue-tracker.md`](../../../docs/agents/issue-tracker.md) and the triage label vocabulary in [`docs/agents/triage-labels.md`](../../../docs/agents/triage-labels.md). Both should have been provided to you already. +## Invocation + +``` +/skill:implement-issue [--agent ] [--base ] [--force] +``` + +- `` — required, the issue number +- `--agent ` — optional, one of `pi`, `opencode`, `goose`, `codex`, `claude`. If omitted, auto-detected from the process tree. +- `--base ` — optional, target base branch (default: repo default branch). +- `--force` — optional, allow overwriting an existing worktree. + ## Process ### 1. Pre-flight checks @@ -79,6 +91,37 @@ ls -d ../-issue- If it exists, report and stop. Let the user override with `--force`. +#### 1h. Running inside a tmux session + +Confirm `$TMUX` is set. If not, report "This skill requires a tmux session. Start tmux and rerun." and stop. + +#### 1i. Detect the agent binary + +If `--agent ` was provided in the invocation args, use that name directly. Otherwise, run the platform-appropriate detection script: + +```bash +bash ./detect-agent-linux.sh +# or on macOS: +bash ./detect-agent-macos.sh +``` + +The script outputs the agent name on success (exit 0), or nothing on failure (exit 1). + +If detection fails (no `--agent` flag and script returns nothing), report: + +> Could not detect which agent is running. Rerun with `--agent `. +> Supported agents: pi, opencode, goose, codex, claude. + +Stop. + +If detection succeeds, confirm the binary is on PATH: + +```bash +command -v +``` + +If the binary is not found, report "Agent binary '' not found on PATH." and stop. + ### 2. Setup #### 2a. Fetch latest base @@ -99,7 +142,7 @@ tea issues edit --add-labels "in-progress" --remove-labels "ready-for-agent" git worktree add -b ../-issue- ``` -### 3. Compose the child prompt +### 3. Compose and write the child prompt Assemble a single prompt that the child agent will receive. Include: @@ -120,15 +163,46 @@ Assemble a single prompt that the child agent will receive. Include: 10. Print `DONE — issue #` - **Failure instruction**: "If any step fails, report where you stopped and what remains for manual recovery. Print the exact commands needed." -### 4. Launch the child - -Open a new tmux window in the worktree directory and run `pi` with the composed prompt: +Write the full prompt to a temp file: ```bash -tmux new-window -c "pi --prompt ''" +cat > /tmp/issue--prompt.md <<'PROMPT_EOF' + +PROMPT_EOF ``` -The pane stays open after the child completes so the user can review the output. +### 4. Launch the child + +Look up the full shell command from the dispatch table below, using the agent name detected in step 1i. + +| Agent | Shell command | +|-------|--------------| +| `pi` | `pi -p @/tmp/issue--prompt.md` | +| `opencode` | `opencode run -f /tmp/issue--prompt.md` | +| `goose` | `goose run -i /tmp/issue--prompt.md` | +| `codex` | `cat /tmp/issue--prompt.md \| codex exec` | +| `claude` | `cat /tmp/issue--prompt.md \| claude -p` | +| *unknown* | `cat /tmp/issue--prompt.md \| ` | + +Substitute the actual temp file path and issue number, then launch in a new tmux window: + +```bash +tmux new-window -n "issue--" -c "" +``` + +For example, with pi: + +```bash +tmux new-window -n "issue-42-myrepo" -c ../myrepo-issue-42 "pi -p @/tmp/issue-42-prompt.md" +``` + +The window stays open after the child completes so the user can review the output. + +After tmux launches, clean up the temp file: + +```bash +rm /tmp/issue--prompt.md +``` ### 5. Print summary @@ -137,7 +211,8 @@ After launching, print: - Worktree path - Branch name - Base branch -- Tmux window (so the user can find it) +- Agent used +- Tmux window name (so the user can find it) - Cleanup command: `git worktree remove && git worktree prune` The parent's turn ends here. The user can dispatch another issue immediately. diff --git a/common/engineering/implement-issue/detect-agent-linux.sh b/common/engineering/implement-issue/detect-agent-linux.sh new file mode 100755 index 0000000..6e68cd2 --- /dev/null +++ b/common/engineering/implement-issue/detect-agent-linux.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Walk the process tree upward from $PPID to find a known agent binary. +# Outputs the agent name on stdout if found, exits 0. +# Outputs nothing and exits 1 if no known agent is found. + +set -euo pipefail + +# Dispatch table: binary names we recognize. +KNOWN_AGENTS="pi opencode goose codex claude" + +pid=$PPID + +while [ "$pid" -gt 1 ]; do + # Read command name and parent PID from /proc. + comm=$(cat /proc/$pid/comm 2>/dev/null) || { pid=1; continue; } + ppid=$(awk '{print $4}' /proc/$pid/stat 2>/dev/null) || ppid=1 + + # Direct match against known binaries. + for agent in $KNOWN_AGENTS; do + if [ "$comm" = "$agent" ]; then + echo "$agent" + exit 0 + fi + done + + # If the process is node or bun, inspect cmdline for agent script paths. + case "$comm" in + node|bun) + cmdline=$(tr '\0' ' ' < /proc/$pid/cmdline 2>/dev/null) || cmdline="" + for agent in $KNOWN_AGENTS; do + case "$cmdline" in + *"$agent"*) + echo "$agent" + exit 0 + ;; + esac + done + ;; + esac + + pid=$ppid +done + +exit 1 diff --git a/common/engineering/implement-issue/detect-agent-macos.sh b/common/engineering/implement-issue/detect-agent-macos.sh new file mode 100755 index 0000000..0d369fa --- /dev/null +++ b/common/engineering/implement-issue/detect-agent-macos.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Walk the process tree upward from $PPID to find a known agent binary. +# Outputs the agent name on stdout if found, exits 0. +# Outputs nothing and exits 1 if no known agent is found. +# +# macOS version — uses ps(1) since /proc is not available. +# Requires bash 3.2+ (macOS ships bash 3.2). + +set -euo pipefail + +KNOWN_AGENTS="pi opencode goose codex claude" + +pid=$PPID + +while [ "$pid" -gt 1 ]; do + # Get parent PID and command name. + ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') || ppid=1 + comm=$(ps -o comm= -p "$pid" 2>/dev/null) || { pid=$ppid; continue; } + # Strip leading path, keep only the basename. + comm=$(basename "$comm" 2>/dev/null || echo "$comm") + + # Direct match against known binaries. + for agent in $KNOWN_AGENTS; do + if [ "$comm" = "$agent" ]; then + echo "$agent" + exit 0 + fi + done + + # If the process is node or bun, inspect full command line for agent names. + case "$comm" in + node|bun) + args=$(ps -o args= -p "$pid" 2>/dev/null) || args="" + for agent in $KNOWN_AGENTS; do + case "$args" in + *"$agent"*) + echo "$agent" + exit 0 + ;; + esac + done + ;; + esac + + pid=$ppid +done + +exit 1