From e6e322e5424d1deefb305c0c733d55021eece468 Mon Sep 17 00:00:00 2001 From: Steve Beaulac Date: Fri, 26 Jun 2026 11:39:21 -0400 Subject: [PATCH 1/2] feat: add implement-issue skill for dispatching child agents via worktree isolation (#7) --- README.md | 1 + common/engineering/README.md | 1 + common/engineering/implement-issue/SKILL.md | 143 ++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 common/engineering/implement-issue/SKILL.md diff --git a/README.md b/README.md index 34ba919..88c582c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A collection of agent skills (slash commands and behaviors) loaded into Steve Be - [grill-me](common/productivity/grill-me/SKILL.md) — A relentless interview to sharpen a plan or design. - [grill-with-docs](common/engineering/grill-with-docs/SKILL.md) — A relentless interview to sharpen a plan or design, which also creates docs (ADRs and glossary) as we go. - [handoff](common/productivity/handoff/SKILL.md) — Compact the current conversation into a handoff document for another agent to pick up. +- [implement-issue](common/engineering/implement-issue/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a ready-for-agent issue end-to-end. - [improve-codebase-architecture](common/engineering/improve-codebase-architecture/SKILL.md) — Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. - [knowledge-gardener](common/pkm/knowledge-gardener/SKILL.md) — Run vault-aware semantic search, synthesis, note creation, linking, and Zettelkasten workflows for this Obsidian vault. - [pkm-curation](common/pkm/pkm-curation/SKILL.md) — Curate an Obsidian-style personal knowledge vault by classifying notes, normalizing frontmatter, improving structure, extracting atomic notes, and adding meaningful wikilinks. diff --git a/common/engineering/README.md b/common/engineering/README.md index 07d2256..cbb9cb2 100644 --- a/common/engineering/README.md +++ b/common/engineering/README.md @@ -2,6 +2,7 @@ ## User-invoked +- [implement-issue](implement-issue/SKILL.md) — Dispatch a child agent in an isolated git worktree to implement a ready-for-agent issue end-to-end. - [grill-with-docs](grill-with-docs/SKILL.md) — A relentless interview to sharpen a plan or design, which also creates docs (ADRs and glossary) as we go. - [improve-codebase-architecture](improve-codebase-architecture/SKILL.md) — Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. - [project-context-pack](project-context-pack/SKILL.md) — Build and refresh a bounded repo context memory file so agents use disciplined search instead of repeated browsing. diff --git a/common/engineering/implement-issue/SKILL.md b/common/engineering/implement-issue/SKILL.md new file mode 100644 index 0000000..3482585 --- /dev/null +++ b/common/engineering/implement-issue/SKILL.md @@ -0,0 +1,143 @@ +--- +name: implement-issue +disable-model-invocation: true +--- + +# Implement Issue + +Dispatch a child agent in an isolated git worktree to implement a `ready-for-agent` issue end-to-end — explore, implement with TDD, run the full quality gate, push a branch, create a PR, comment on the issue, and update labels. + +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. + +## Process + +### 1. Pre-flight checks + +Stop on any failure and report clearly what needs fixing. Do not proceed. + +#### 1a. `tea` CLI + +Confirm `tea` is on PATH. If missing, tell the user to install it and stop. + +#### 1b. Issue exists and is open + +```bash +tea issue -o json +``` + +If the issue is not found (404) or state is not `open`, report and stop. + +#### 1c. Issue is labeled `ready-for-agent` + +Check that the issue's labels include `ready-for-agent`. If not, report the current state and point the user to `/triage`. Stop. + +#### 1d. No open blockers + +Read the issue body for "Blocked by #M" references. For each referenced issue, check whether it is open: + +```bash +tea issue -o json +``` + +If any blocker is open, report the blocking issues and stop. + +#### 1e. Remote exists and base branch is reachable + +Default base is the repo's default branch — infer from `git ls-remote --heads origin` (look for `main` or `master`). Allow an explicit `--base ` override. + +```bash +git ls-remote --heads origin +``` + +If the remote is unreachable or the base branch doesn't exist, report and stop. SSH auth issues (e.g. `Permission denied (publickey)`) are a failure — tell the user which key is needed. + +> **SSH note:** `tea` uses HTTPS and may work when git-over-SSH does not. If the first `git ls-remote` fails with an SSH error, try `export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)` and retry before failing. + +#### 1f. Branch name is free + +Derive the branch name from the issue title: + +- Format: `issue--` +- Slug: lowercase the title, strip to `[a-z0-9-]`, truncate to ~4–5 short words (max ~50 chars), collapse double hyphens +- If the title yields no usable slug, fall back to `issue-` + +Check that the branch doesn't already exist on the remote: + +```bash +git ls-remote --heads origin +``` + +If it exists, report and stop. (No auto-suffix, no force-push.) + +#### 1g. Worktree path is free + +The worktree path is a sibling to the main repo: `../-issue-`. Check that it doesn't already exist: + +```bash +ls -d ../-issue- +``` + +If it exists, report and stop. Let the user override with `--force`. + +### 2. Setup + +#### 2a. Fetch latest base + +```bash +git fetch origin +``` + +#### 2b. Label the issue `in-progress` + +```bash +tea issues edit --add-labels "in-progress" --remove-labels "ready-for-agent" +``` + +#### 2c. Create the worktree + +```bash +git worktree add -b ../-issue- +``` + +### 3. Compose the child prompt + +Assemble a single prompt that the child agent will receive. Include: + +- **Issue body** — the full markdown body from `tea issue -o json` +- **Comments** — all comments, if any +- **Standing instruction**: "Implement using TDD: write a failing test first, make it pass, refactor. Explore the codebase before writing code. Respect ADRs and the project domain glossary. Set up the project (install dependencies, build) before starting. The issue body may contain Implementation Decisions and Testing Decisions sections — treat these as constraints." +- **Context**: "Base branch: ``. Target your PR at ``. Branch name: ``." +- **Checklist** — a numbered checklist the child should work through: + 1. Explore the codebase and read the issue body + comments thoroughly + 2. Install dependencies and build the project + 3. Implement the change using TDD (red-green-refactor) + 4. Run the full project quality gate (infer from package.json, Makefile, Cargo.toml, etc.), fix until green + 5. Commit with conventional commits (`feat:` for enhancement, `fix:` for bug) referencing `(#)` + 6. Push the branch: `git push origin ` + 7. Create a PR with: link to the issue, one-sentence summary, short key-changes list + 8. Comment on the issue with the PR link: `tea comment "PR opened: "` + 9. Change the issue label to `in-review`: `tea issues edit --add-labels "in-review" --remove-labels "in-progress"` + 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: + +```bash +tmux new-window -c "pi --prompt ''" +``` + +The pane stays open after the child completes so the user can review the output. + +### 5. Print summary + +After launching, print: +- Issue number and title +- Worktree path +- Branch name +- Base branch +- Tmux window (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. From dc9293c7be3eda25bb064a5e7d3719d2a2bd3fc8 Mon Sep 17 00:00:00 2001 From: Steve Beaulac Date: Fri, 26 Jun 2026 13:42:56 -0400 Subject: [PATCH 2/2] 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