From 9f93f54b1f71a853464bc8cd626405ad86dc938d Mon Sep 17 00:00:00 2001 From: Trevor Hinesley Date: Mon, 2 Feb 2026 14:26:30 -0600 Subject: [PATCH 01/39] Improvement: protect plan files from review deletion (#142) * fix: protect plan and solution files from review/resolve deletion The review/resolve pipeline could flag docs/plans/*.md and docs/solutions/*.md files for deletion, contradicting /workflows:work which treats them as living documents. Adds protection at three layers: - review.md: Protected Artifacts section + synthesis filter - code-simplicity-reviewer.md: YAGNI exception for pipeline artifacts - resolve_todo_parallel.md: Safety check to skip/wont_fix such todos Fixes #140 * fix: protect plan and solution files from review/resolve deletion The review/resolve pipeline could flag docs/plans/*.md and docs/solutions/*.md files for deletion, contradicting /workflows:work which treats them as living documents. Adds protection at four layers: - review.md: Protected Artifacts section and synthesis filter - code-simplicity-reviewer.md: YAGNI exception for pipeline artifacts - resolve_todo_parallel.md: Skip and wont_fix todos targeting these paths - git-history-analyzer.md: Note not to characterize them as unnecessary Fixes #140 --------- Co-authored-by: Axl Ottle --- .../agents/research/git-history-analyzer.md | 2 ++ .../agents/review/code-simplicity-reviewer.md | 1 + .../commands/resolve_todo_parallel.md | 2 ++ .../commands/workflows/review.md | 12 ++++++++++++ 4 files changed, 17 insertions(+) diff --git a/plugins/compound-engineering/agents/research/git-history-analyzer.md b/plugins/compound-engineering/agents/research/git-history-analyzer.md index 7a90c4d..45da283 100644 --- a/plugins/compound-engineering/agents/research/git-history-analyzer.md +++ b/plugins/compound-engineering/agents/research/git-history-analyzer.md @@ -40,3 +40,5 @@ When analyzing, consider: - The evolution of coding patterns and practices over time Your insights should help developers understand not just what the code does, but why it evolved to its current state, informing better decisions for future changes. + +Note that files in `docs/plans/` and `docs/solutions/` are compound-engineering pipeline artifacts created by `/workflows:plan`. They are intentional, permanent living documents — do not recommend their removal or characterize them as unnecessary. diff --git a/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md b/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md index 9e20770..63bbbb0 100644 --- a/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +++ b/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md @@ -33,6 +33,7 @@ When reviewing code, you will: - Eliminate extensibility points without clear use cases - Question generic solutions for specific problems - Remove "just in case" code + - Never flag `docs/plans/*.md` or `docs/solutions/*.md` for removal — these are compound-engineering pipeline artifacts created by `/workflows:plan` and used as living documents by `/workflows:work` 6. **Optimize for Readability**: - Prefer self-documenting code over comments diff --git a/plugins/compound-engineering/commands/resolve_todo_parallel.md b/plugins/compound-engineering/commands/resolve_todo_parallel.md index a98b5f9..afd653d 100644 --- a/plugins/compound-engineering/commands/resolve_todo_parallel.md +++ b/plugins/compound-engineering/commands/resolve_todo_parallel.md @@ -12,6 +12,8 @@ Resolve all TODO comments using parallel processing. Get all unresolved TODOs from the /todos/\*.md directory +If any todo recommends deleting, removing, or gitignoring files in `docs/plans/` or `docs/solutions/`, skip it and mark it as `wont_fix`. These are compound-engineering pipeline artifacts that are intentional and permanent. + ### 2. Plan Create a TodoWrite list of all unresolved items grouped by type.Make sure to look at dependencies that might occur and prioritize the ones needed by others. For example, if you need to change a name, you must wait to do the others. Output a mermaid flow diagram showing how we can do this. Can we do everything in parallel? Do we need to do one first that leads to others in parallel? I'll put the to-dos in the mermaid diagram flow‑wise so the agent knows how to proceed in order. diff --git a/plugins/compound-engineering/commands/workflows/review.md b/plugins/compound-engineering/commands/workflows/review.md index 282c9f0..e659303 100644 --- a/plugins/compound-engineering/commands/workflows/review.md +++ b/plugins/compound-engineering/commands/workflows/review.md @@ -48,6 +48,17 @@ Ensure that the code is ready for analysis (either in worktree or on current bra +#### Protected Artifacts + + +The following paths are compound-engineering pipeline artifacts and must never be flagged for deletion, removal, or gitignore by any review agent: + +- `docs/plans/*.md` — Plan files created by `/workflows:plan`. These are living documents that track implementation progress (checkboxes are checked off by `/workflows:work`). +- `docs/solutions/*.md` — Solution documents created during the pipeline. + +If a review agent flags any file in these directories for cleanup or removal, discard that finding during synthesis. Do not create a todo for it. + + #### Parallel Agents to review the PR: @@ -207,6 +218,7 @@ Remove duplicates, prioritize by severity and impact. - [ ] Collect findings from all parallel agents +- [ ] Discard any findings that recommend deleting or gitignoring files in `docs/plans/` or `docs/solutions/` (see Protected Artifacts above) - [ ] Categorize by type: security, performance, architecture, quality, etc. - [ ] Assign severity levels: 🔴 CRITICAL (P1), 🟡 IMPORTANT (P2), 🔵 NICE-TO-HAVE (P3) - [ ] Remove duplicate or overlapping findings From 2429f59529054d8e15ec70e244d8e20e548afc51 Mon Sep 17 00:00:00 2001 From: Kieran Klaassen Date: Wed, 4 Feb 2026 21:17:06 -0800 Subject: [PATCH 02/39] [2.29.0] Add schema-drift-detector agent - New agent: schema-drift-detector for catching unrelated schema.rb changes in PRs - Compares schema.rb diff against migrations in the PR - Detects columns, indexes, tables from other branches - Provides clear fix instructions - Essential pre-merge check for database changes Also updates component counts in README (were outdated) Co-Authored-By: Claude Opus 4.5 --- .claude-plugin/marketplace.json | 4 +- .../.claude-plugin/plugin.json | 4 +- plugins/compound-engineering/CHANGELOG.md | 13 ++ plugins/compound-engineering/README.md | 14 +- .../agents/review/schema-drift-detector.md | 139 ++++++++++++++++++ 5 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 plugins/compound-engineering/agents/review/schema-drift-detector.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4e22718..59720b3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,8 +11,8 @@ "plugins": [ { "name": "compound-engineering", - "description": "AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes 28 specialized agents, 24 commands, and 15 skills.", - "version": "2.28.0", + "description": "AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes 29 specialized agents, 24 commands, and 16 skills.", + "version": "2.29.0", "author": { "name": "Kieran Klaassen", "url": "https://github.com/kieranklaassen", diff --git a/plugins/compound-engineering/.claude-plugin/plugin.json b/plugins/compound-engineering/.claude-plugin/plugin.json index 97ea742..3b52617 100644 --- a/plugins/compound-engineering/.claude-plugin/plugin.json +++ b/plugins/compound-engineering/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "compound-engineering", - "version": "2.28.0", - "description": "AI-powered development tools. 28 agents, 24 commands, 15 skills, 1 MCP server for code review, research, design, and workflow automation.", + "version": "2.29.0", + "description": "AI-powered development tools. 29 agents, 24 commands, 16 skills, 1 MCP server for code review, research, design, and workflow automation.", "author": { "name": "Kieran Klaassen", "email": "kieran@every.to", diff --git a/plugins/compound-engineering/CHANGELOG.md b/plugins/compound-engineering/CHANGELOG.md index dd1c7f9..b4bebc1 100644 --- a/plugins/compound-engineering/CHANGELOG.md +++ b/plugins/compound-engineering/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to the compound-engineering plugin will be documented in thi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.29.0] - 2026-02-04 + +### Added + +- **`schema-drift-detector` agent** - Detects unrelated schema.rb changes in PRs + - Compares schema.rb diff against migrations in the PR + - Catches columns, indexes, and tables from other branches + - Prevents accidental inclusion of local database state + - Provides clear fix instructions (checkout + migrate) + - Essential pre-merge check for any PR with database changes + +--- + ## [2.28.0] - 2026-01-21 ### Added diff --git a/plugins/compound-engineering/README.md b/plugins/compound-engineering/README.md index b1a710d..77f60b8 100644 --- a/plugins/compound-engineering/README.md +++ b/plugins/compound-engineering/README.md @@ -6,16 +6,16 @@ AI-powered development tools that get smarter with every use. Make each unit of | Component | Count | |-----------|-------| -| Agents | 27 | -| Commands | 20 | -| Skills | 14 | +| Agents | 29 | +| Commands | 24 | +| Skills | 16 | | MCP Servers | 1 | ## Agents Agents are organized into categories for easier discovery. -### Review (14) +### Review (15) | Agent | Description | |-------|-------------| @@ -26,21 +26,23 @@ Agents are organized into categories for easier discovery. | `data-migration-expert` | Validate ID mappings match production, check for swapped values | | `deployment-verification-agent` | Create Go/No-Go deployment checklists for risky data changes | | `dhh-rails-reviewer` | Rails review from DHH's perspective | +| `julik-frontend-races-reviewer` | Review JavaScript/Stimulus code for race conditions | | `kieran-rails-reviewer` | Rails code review with strict conventions | | `kieran-python-reviewer` | Python code review with strict conventions | | `kieran-typescript-reviewer` | TypeScript code review with strict conventions | | `pattern-recognition-specialist` | Analyze code for patterns and anti-patterns | | `performance-oracle` | Performance analysis and optimization | +| `schema-drift-detector` | Detect unrelated schema.rb changes in PRs | | `security-sentinel` | Security audits and vulnerability assessments | -| `julik-frontend-races-reviewer` | Review JavaScript/Stimulus code for race conditions | -### Research (4) +### Research (5) | Agent | Description | |-------|-------------| | `best-practices-researcher` | Gather external best practices and examples | | `framework-docs-researcher` | Research framework documentation and best practices | | `git-history-analyzer` | Analyze git history and code evolution | +| `learnings-researcher` | Search institutional learnings for relevant past solutions | | `repo-research-analyst` | Research repository structure and conventions | ### Design (3) diff --git a/plugins/compound-engineering/agents/review/schema-drift-detector.md b/plugins/compound-engineering/agents/review/schema-drift-detector.md new file mode 100644 index 0000000..a778a78 --- /dev/null +++ b/plugins/compound-engineering/agents/review/schema-drift-detector.md @@ -0,0 +1,139 @@ +--- +name: schema-drift-detector +description: "Use this agent when reviewing PRs that include db/schema.rb changes to detect unrelated schema modifications. This agent compares schema.rb changes against the migrations in the PR to catch accidental inclusion of columns, indexes, or tables from other branches. Essential before merging any PR with database changes. Context: The user has a PR with a migration and wants to verify schema.rb is clean. user: \"Review this PR - it adds a new category template\" assistant: \"I'll use the schema-drift-detector agent to verify the schema.rb only contains changes from your migration\" Since the PR includes schema.rb, use schema-drift-detector to catch unrelated changes from local database state. Context: The PR has schema changes that look suspicious. user: \"The schema.rb diff looks larger than expected\" assistant: \"Let me use the schema-drift-detector to identify which schema changes are unrelated to your PR's migrations\" Schema drift is common when developers run migrations from main while on a feature branch." +model: inherit +--- + +You are a Schema Drift Detector. Your mission is to prevent accidental inclusion of unrelated schema.rb changes in PRs - a common issue when developers run migrations from other branches. + +## The Problem + +When developers work on feature branches, they often: +1. Pull main and run `db:migrate` to stay current +2. Switch back to their feature branch +3. Run their new migration +4. Commit the schema.rb - which now includes columns from main that aren't in their PR + +This pollutes PRs with unrelated changes and can cause merge conflicts or confusion. + +## Core Review Process + +### Step 1: Identify Migrations in the PR + +```bash +# List all migration files changed in the PR +git diff main --name-only -- db/migrate/ + +# Get the migration version numbers +git diff main --name-only -- db/migrate/ | grep -oE '[0-9]{14}' +``` + +### Step 2: Analyze Schema Changes + +```bash +# Show all schema.rb changes +git diff main -- db/schema.rb +``` + +### Step 3: Cross-Reference + +For each change in schema.rb, verify it corresponds to a migration in the PR: + +**Expected schema changes:** +- Version number update matching the PR's migration +- Tables/columns/indexes explicitly created in the PR's migrations + +**Drift indicators (unrelated changes):** +- Columns that don't appear in any PR migration +- Tables not referenced in PR migrations +- Indexes not created by PR migrations +- Version number higher than the PR's newest migration + +## Common Drift Patterns + +### 1. Extra Columns +```diff +# DRIFT: These columns aren't in any PR migration ++ t.text "openai_api_key" ++ t.text "anthropic_api_key" ++ t.datetime "api_key_validated_at" +``` + +### 2. Extra Indexes +```diff +# DRIFT: Index not created by PR migrations ++ t.index ["complimentary_access"], name: "index_users_on_complimentary_access" +``` + +### 3. Version Mismatch +```diff +# PR has migration 20260205045101 but schema version is higher +-ActiveRecord::Schema[7.2].define(version: 2026_01_29_133857) do ++ActiveRecord::Schema[7.2].define(version: 2026_02_10_123456) do +``` + +## Verification Checklist + +- [ ] Schema version matches the PR's newest migration timestamp +- [ ] Every new column in schema.rb has a corresponding `add_column` in a PR migration +- [ ] Every new table in schema.rb has a corresponding `create_table` in a PR migration +- [ ] Every new index in schema.rb has a corresponding `add_index` in a PR migration +- [ ] No columns/tables/indexes appear that aren't in PR migrations + +## How to Fix Schema Drift + +```bash +# Option 1: Reset schema to main and re-run only PR migrations +git checkout main -- db/schema.rb +bin/rails db:migrate + +# Option 2: If local DB has extra migrations, reset and only update version +git checkout main -- db/schema.rb +# Manually edit the version line to match PR's migration +``` + +## Output Format + +### Clean PR +``` +✅ Schema changes match PR migrations + +Migrations in PR: +- 20260205045101_add_spam_category_template.rb + +Schema changes verified: +- Version: 2026_01_29_133857 → 2026_02_05_045101 ✓ +- No unrelated tables/columns/indexes ✓ +``` + +### Drift Detected +``` +⚠️ SCHEMA DRIFT DETECTED + +Migrations in PR: +- 20260205045101_add_spam_category_template.rb + +Unrelated schema changes found: + +1. **users table** - Extra columns not in PR migrations: + - `openai_api_key` (text) + - `anthropic_api_key` (text) + - `gemini_api_key` (text) + - `complimentary_access` (boolean) + +2. **Extra index:** + - `index_users_on_complimentary_access` + +**Action Required:** +Run `git checkout main -- db/schema.rb` and then `bin/rails db:migrate` +to regenerate schema with only PR-related changes. +``` + +## Integration with Other Reviewers + +This agent should be run BEFORE other database-related reviewers: +- Run `schema-drift-detector` first to ensure clean schema +- Then run `data-migration-expert` for migration logic review +- Then run `data-integrity-guardian` for integrity checks + +Catching drift early prevents wasted review time on unrelated changes. From e4ff6a874c1330e3e6b672db14e6d0b8847910de Mon Sep 17 00:00:00 2001 From: Kieran Klaassen Date: Thu, 5 Feb 2026 12:25:50 -0600 Subject: [PATCH 03/39] [2.30.0] Add orchestrating-swarms skill, /slfg command, and swarm mode (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New skill: orchestrating-swarms — comprehensive guide to multi-agent swarm orchestration with TeammateTool operations, spawn backends, task dependencies, and orchestration patterns - New command: /slfg — swarm-enabled variant of /lfg that parallelizes review + browser tests after work phase, then resolves and records video - Updated /workflows:work with optional Swarm Mode section - Added missing skills (brainstorming, orchestrating-swarms) to README - Added missing commands (/lfg, /slfg) to README Co-authored-by: Claude Opus 4.6 --- .claude-plugin/marketplace.json | 4 +- .../.claude-plugin/plugin.json | 4 +- plugins/compound-engineering/CHANGELOG.md | 19 + plugins/compound-engineering/README.md | 11 +- plugins/compound-engineering/commands/slfg.md | 31 + .../commands/workflows/work.md | 70 + .../skills/orchestrating-swarms/SKILL.md | 1717 +++++++++++++++++ 7 files changed, 1851 insertions(+), 5 deletions(-) create mode 100644 plugins/compound-engineering/commands/slfg.md create mode 100644 plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 59720b3..ce32527 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,8 +11,8 @@ "plugins": [ { "name": "compound-engineering", - "description": "AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes 29 specialized agents, 24 commands, and 16 skills.", - "version": "2.29.0", + "description": "AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes 29 specialized agents, 25 commands, and 16 skills.", + "version": "2.30.0", "author": { "name": "Kieran Klaassen", "url": "https://github.com/kieranklaassen", diff --git a/plugins/compound-engineering/.claude-plugin/plugin.json b/plugins/compound-engineering/.claude-plugin/plugin.json index 3b52617..842ed85 100644 --- a/plugins/compound-engineering/.claude-plugin/plugin.json +++ b/plugins/compound-engineering/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "compound-engineering", - "version": "2.29.0", - "description": "AI-powered development tools. 29 agents, 24 commands, 16 skills, 1 MCP server for code review, research, design, and workflow automation.", + "version": "2.30.0", + "description": "AI-powered development tools. 29 agents, 25 commands, 16 skills, 1 MCP server for code review, research, design, and workflow automation.", "author": { "name": "Kieran Klaassen", "email": "kieran@every.to", diff --git a/plugins/compound-engineering/CHANGELOG.md b/plugins/compound-engineering/CHANGELOG.md index b4bebc1..e55f5c7 100644 --- a/plugins/compound-engineering/CHANGELOG.md +++ b/plugins/compound-engineering/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to the compound-engineering plugin will be documented in thi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.30.0] - 2026-02-05 + +### Added + +- **`orchestrating-swarms` skill** - Comprehensive guide to multi-agent orchestration + - Covers primitives: Agent, Team, Teammate, Leader, Task, Inbox, Message, Backend + - Documents two spawning methods: subagents vs teammates + - Explains all 13 TeammateTool operations + - Includes orchestration patterns: Parallel Specialists, Pipeline, Self-Organizing Swarm + - Details spawn backends: in-process, tmux, iterm2 + - Provides complete workflow examples +- **`/slfg` command** - Swarm-enabled variant of `/lfg` that uses swarm mode for parallel execution + +### Changed + +- **`/workflows:work` command** - Added optional Swarm Mode section for parallel execution with coordinated agents + +--- + ## [2.29.0] - 2026-02-04 ### Added diff --git a/plugins/compound-engineering/README.md b/plugins/compound-engineering/README.md index 77f60b8..94398ce 100644 --- a/plugins/compound-engineering/README.md +++ b/plugins/compound-engineering/README.md @@ -7,7 +7,7 @@ AI-powered development tools that get smarter with every use. Make each unit of | Component | Count | |-----------|-------| | Agents | 29 | -| Commands | 24 | +| Commands | 25 | | Skills | 16 | | MCP Servers | 1 | @@ -87,6 +87,8 @@ Core workflow commands use `workflows:` prefix to avoid collisions with built-in | Command | Description | |---------|-------------| +| `/lfg` | Full autonomous engineering workflow | +| `/slfg` | Full autonomous workflow with swarm mode for parallel execution | | `/deepen-plan` | Enhance plans with parallel research agents for each section | | `/changelog` | Create engaging changelogs for recent merges | | `/create-agent-skill` | Create or edit Claude Code skills | @@ -127,10 +129,17 @@ Core workflow commands use `workflows:` prefix to avoid collisions with built-in | Skill | Description | |-------|-------------| +| `brainstorming` | Guided ideation for exploring requirements and approaches | | `every-style-editor` | Review copy for Every's style guide compliance | | `file-todos` | File-based todo tracking system | | `git-worktree` | Manage Git worktrees for parallel development | +### Multi-Agent Orchestration + +| Skill | Description | +|-------|-------------| +| `orchestrating-swarms` | Comprehensive guide to multi-agent swarm orchestration | + ### File Transfer | Skill | Description | diff --git a/plugins/compound-engineering/commands/slfg.md b/plugins/compound-engineering/commands/slfg.md new file mode 100644 index 0000000..3993855 --- /dev/null +++ b/plugins/compound-engineering/commands/slfg.md @@ -0,0 +1,31 @@ +--- +name: slfg +description: Full autonomous engineering workflow using swarm mode for parallel execution +argument-hint: "[feature description]" +--- + +Swarm-enabled LFG. Run these steps in order, parallelizing where indicated. + +## Sequential Phase + +1. `/ralph-wiggum:ralph-loop "finish all slash commands" --completion-promise "DONE"` +2. `/workflows:plan $ARGUMENTS` +3. `/compound-engineering:deepen-plan` +4. `/workflows:work` — **Use swarm mode**: Make a Task list and launch an army of agent swarm subagents to build the plan + +## Parallel Phase + +After work completes, launch steps 5 and 6 as **parallel swarm agents** (both only need code to be written): + +5. `/workflows:review` — spawn as background Task agent +6. `/compound-engineering:test-browser` — spawn as background Task agent + +Wait for both to complete before continuing. + +## Finalize Phase + +7. `/compound-engineering:resolve_todo_parallel` — resolve any findings from the review +8. `/compound-engineering:feature-video` — record the final walkthrough and add to PR +9. Output `DONE` when video is in PR + +Start with step 1 now. diff --git a/plugins/compound-engineering/commands/workflows/work.md b/plugins/compound-engineering/commands/workflows/work.md index 36f95ae..230300c 100644 --- a/plugins/compound-engineering/commands/workflows/work.md +++ b/plugins/compound-engineering/commands/workflows/work.md @@ -292,6 +292,76 @@ This command takes a work document (plan, specification, or todo file) and execu --- +## Swarm Mode (Optional) + +For complex plans with multiple independent workstreams, enable swarm mode for parallel execution with coordinated agents. + +### When to Use Swarm Mode + +| Use Swarm Mode when... | Use Standard Mode when... | +|------------------------|---------------------------| +| Plan has 5+ independent tasks | Plan is linear/sequential | +| Multiple specialists needed (review + test + implement) | Single-focus work | +| Want maximum parallelism | Simpler mental model preferred | +| Large feature with clear phases | Small feature or bug fix | + +### Enabling Swarm Mode + +To trigger swarm execution, say: + +> "Make a Task list and launch an army of agent swarm subagents to build the plan" + +Or explicitly request: "Use swarm mode for this work" + +### Swarm Workflow + +When swarm mode is enabled, the workflow changes: + +1. **Create Team** + ``` + Teammate({ operation: "spawnTeam", team_name: "work-{timestamp}" }) + ``` + +2. **Create Task List with Dependencies** + - Parse plan into TaskCreate items + - Set up blockedBy relationships for sequential dependencies + - Independent tasks have no blockers (can run in parallel) + +3. **Spawn Specialized Teammates** + ``` + Task({ + team_name: "work-{timestamp}", + name: "implementer", + subagent_type: "general-purpose", + prompt: "Claim implementation tasks, execute, mark complete", + run_in_background: true + }) + + Task({ + team_name: "work-{timestamp}", + name: "tester", + subagent_type: "general-purpose", + prompt: "Claim testing tasks, run tests, mark complete", + run_in_background: true + }) + ``` + +4. **Coordinate and Monitor** + - Team lead monitors task completion + - Spawn additional workers as phases unblock + - Handle plan approval if required + +5. **Cleanup** + ``` + Teammate({ operation: "requestShutdown", target_agent_id: "implementer" }) + Teammate({ operation: "requestShutdown", target_agent_id: "tester" }) + Teammate({ operation: "cleanup" }) + ``` + +See the `orchestrating-swarms` skill for detailed swarm patterns and best practices. + +--- + ## Key Principles ### Start Fast, Execute Faster diff --git a/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md b/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md new file mode 100644 index 0000000..0748e28 --- /dev/null +++ b/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md @@ -0,0 +1,1717 @@ +--- +name: orchestrating-swarms +description: This skill should be used when orchestrating multi-agent swarms using Claude Code's TeammateTool and Task system. It applies when coordinating multiple agents, running parallel code reviews, creating pipeline workflows with dependencies, building self-organizing task queues, or any task benefiting from divide-and-conquer patterns. +--- + +# Claude Code Swarm Orchestration + +Master multi-agent orchestration using Claude Code's TeammateTool and Task system. + +--- + +## Primitives + +| Primitive | What It Is | File Location | +|-----------|-----------|---------------| +| **Agent** | A Claude instance that can use tools. You are an agent. Subagents are agents you spawn. | N/A (process) | +| **Team** | A named group of agents working together. One leader, multiple teammates. | `~/.claude/teams/{name}/config.json` | +| **Teammate** | An agent that joined a team. Has a name, color, inbox. Spawned via Task with `team_name` + `name`. | Listed in team config | +| **Leader** | The agent that created the team. Receives teammate messages, approves plans/shutdowns. | First member in config | +| **Task** | A work item with subject, description, status, owner, and dependencies. | `~/.claude/tasks/{team}/N.json` | +| **Inbox** | JSON file where an agent receives messages from teammates. | `~/.claude/teams/{name}/inboxes/{agent}.json` | +| **Message** | A JSON object sent between agents. Can be text or structured (shutdown_request, idle_notification, etc). | Stored in inbox files | +| **Backend** | How teammates run. Auto-detected: `in-process` (same Node.js, invisible), `tmux` (separate panes, visible), `iterm2` (split panes in iTerm2). See [Spawn Backends](#spawn-backends). | Auto-detected based on environment | + +### How They Connect + +```mermaid +flowchart TB + subgraph TEAM[TEAM] + Leader[Leader - you] + T1[Teammate 1] + T2[Teammate 2] + + Leader <-->|messages via inbox| T1 + Leader <-->|messages via inbox| T2 + T1 <-.->|can message| T2 + end + + subgraph TASKS[TASK LIST] + Task1["#1 completed: Research
owner: teammate1"] + Task2["#2 in_progress: Implement
owner: teammate2"] + Task3["#3 pending: Test
blocked by #2"] + end + + T1 --> Task1 + T2 --> Task2 + Task2 -.->|unblocks| Task3 +``` + +### Lifecycle + +```mermaid +flowchart LR + A[1. Create Team] --> B[2. Create Tasks] + B --> C[3. Spawn Teammates] + C --> D[4. Work] + D --> E[5. Coordinate] + E --> F[6. Shutdown] + F --> G[7. Cleanup] +``` + +### Message Flow + +```mermaid +sequenceDiagram + participant L as Leader + participant T1 as Teammate 1 + participant T2 as Teammate 2 + participant Tasks as Task List + + L->>Tasks: TaskCreate (3 tasks) + L->>T1: spawn with prompt + L->>T2: spawn with prompt + + T1->>Tasks: claim task #1 + T2->>Tasks: claim task #2 + + T1->>Tasks: complete #1 + T1->>L: send findings (inbox) + + Note over Tasks: #3 auto-unblocks + + T2->>Tasks: complete #2 + T2->>L: send findings (inbox) + + L->>T1: requestShutdown + T1->>L: approveShutdown + L->>T2: requestShutdown + T2->>L: approveShutdown + + L->>L: cleanup +``` + +--- + +## Table of Contents + +1. [Core Architecture](#core-architecture) +2. [Two Ways to Spawn Agents](#two-ways-to-spawn-agents) +3. [Built-in Agent Types](#built-in-agent-types) +4. [Plugin Agent Types](#plugin-agent-types) +5. [TeammateTool Operations](#teammatetool-operations) +6. [Task System Integration](#task-system-integration) +7. [Message Formats](#message-formats) +8. [Orchestration Patterns](#orchestration-patterns) +9. [Environment Variables](#environment-variables) +10. [Spawn Backends](#spawn-backends) +11. [Error Handling](#error-handling) +12. [Complete Workflows](#complete-workflows) + +--- + +## Core Architecture + +### How Swarms Work + +A swarm consists of: +- **Leader** (you) - Creates team, spawns workers, coordinates work +- **Teammates** (spawned agents) - Execute tasks, report back +- **Task List** - Shared work queue with dependencies +- **Inboxes** - JSON files for inter-agent messaging + +### File Structure + +``` +~/.claude/teams/{team-name}/ +├── config.json # Team metadata and member list +└── inboxes/ + ├── team-lead.json # Leader's inbox + ├── worker-1.json # Worker 1's inbox + └── worker-2.json # Worker 2's inbox + +~/.claude/tasks/{team-name}/ +├── 1.json # Task #1 +├── 2.json # Task #2 +└── 3.json # Task #3 +``` + +### Team Config Structure + +```json +{ + "name": "my-project", + "description": "Working on feature X", + "leadAgentId": "team-lead@my-project", + "createdAt": 1706000000000, + "members": [ + { + "agentId": "team-lead@my-project", + "name": "team-lead", + "agentType": "team-lead", + "color": "#4A90D9", + "joinedAt": 1706000000000, + "backendType": "in-process" + }, + { + "agentId": "worker-1@my-project", + "name": "worker-1", + "agentType": "Explore", + "model": "haiku", + "prompt": "Analyze the codebase structure...", + "color": "#D94A4A", + "planModeRequired": false, + "joinedAt": 1706000001000, + "tmuxPaneId": "in-process", + "cwd": "/Users/me/project", + "backendType": "in-process" + } + ] +} +``` + +--- + +## Two Ways to Spawn Agents + +### Method 1: Task Tool (Subagents) + +Use Task for **short-lived, focused work** that returns a result: + +```javascript +Task({ + subagent_type: "Explore", + description: "Find auth files", + prompt: "Find all authentication-related files in this codebase", + model: "haiku" // Optional: haiku, sonnet, opus +}) +``` + +**Characteristics:** +- Runs synchronously (blocks until complete) or async with `run_in_background: true` +- Returns result directly to you +- No team membership required +- Best for: searches, analysis, focused research + +### Method 2: Task Tool + team_name + name (Teammates) + +Use Task with `team_name` and `name` to **spawn persistent teammates**: + +```javascript +// First create a team +Teammate({ operation: "spawnTeam", team_name: "my-project" }) + +// Then spawn a teammate into that team +Task({ + team_name: "my-project", // Required: which team to join + name: "security-reviewer", // Required: teammate's name + subagent_type: "security-sentinel", + prompt: "Review all authentication code for vulnerabilities. Send findings to team-lead via Teammate write.", + run_in_background: true // Teammates usually run in background +}) +``` + +**Characteristics:** +- Joins team, appears in `config.json` +- Communicates via inbox messages +- Can claim tasks from shared task list +- Persists until shutdown +- Best for: parallel work, ongoing collaboration, pipeline stages + +### Key Difference + +| Aspect | Task (subagent) | Task + team_name + name (teammate) | +|--------|-----------------|-----------------------------------| +| Lifespan | Until task complete | Until shutdown requested | +| Communication | Return value | Inbox messages | +| Task access | None | Shared task list | +| Team membership | No | Yes | +| Coordination | One-off | Ongoing | + +--- + +## Built-in Agent Types + +These are always available without plugins: + +### Bash +```javascript +Task({ + subagent_type: "Bash", + description: "Run git commands", + prompt: "Check git status and show recent commits" +}) +``` +- **Tools:** Bash only +- **Model:** Inherits from parent +- **Best for:** Git operations, command execution, system tasks + +### Explore +```javascript +Task({ + subagent_type: "Explore", + description: "Find API endpoints", + prompt: "Find all API endpoints in this codebase. Be very thorough.", + model: "haiku" // Fast and cheap +}) +``` +- **Tools:** All read-only tools (no Edit, Write, NotebookEdit, Task) +- **Model:** Haiku (optimized for speed) +- **Best for:** Codebase exploration, file searches, code understanding +- **Thoroughness levels:** "quick", "medium", "very thorough" + +### Plan +```javascript +Task({ + subagent_type: "Plan", + description: "Design auth system", + prompt: "Create an implementation plan for adding OAuth2 authentication" +}) +``` +- **Tools:** All read-only tools +- **Model:** Inherits from parent +- **Best for:** Architecture planning, implementation strategies + +### general-purpose +```javascript +Task({ + subagent_type: "general-purpose", + description: "Research and implement", + prompt: "Research React Query best practices and implement caching for the user API" +}) +``` +- **Tools:** All tools (*) +- **Model:** Inherits from parent +- **Best for:** Multi-step tasks, research + action combinations + +### claude-code-guide +```javascript +Task({ + subagent_type: "claude-code-guide", + description: "Help with Claude Code", + prompt: "How do I configure MCP servers?" +}) +``` +- **Tools:** Read-only + WebFetch + WebSearch +- **Best for:** Questions about Claude Code, Agent SDK, Anthropic API + +### statusline-setup +```javascript +Task({ + subagent_type: "statusline-setup", + description: "Configure status line", + prompt: "Set up a status line showing git branch and node version" +}) +``` +- **Tools:** Read, Edit only +- **Model:** Sonnet +- **Best for:** Configuring Claude Code status line + +--- + +## Plugin Agent Types + +From the `compound-engineering` plugin (examples): + +### Review Agents +```javascript +// Security review +Task({ + subagent_type: "compound-engineering:review:security-sentinel", + description: "Security audit", + prompt: "Audit this PR for security vulnerabilities" +}) + +// Performance review +Task({ + subagent_type: "compound-engineering:review:performance-oracle", + description: "Performance check", + prompt: "Analyze this code for performance bottlenecks" +}) + +// Rails code review +Task({ + subagent_type: "compound-engineering:review:kieran-rails-reviewer", + description: "Rails review", + prompt: "Review this Rails code for best practices" +}) + +// Architecture review +Task({ + subagent_type: "compound-engineering:review:architecture-strategist", + description: "Architecture review", + prompt: "Review the system architecture of the authentication module" +}) + +// Code simplicity +Task({ + subagent_type: "compound-engineering:review:code-simplicity-reviewer", + description: "Simplicity check", + prompt: "Check if this implementation can be simplified" +}) +``` + +**All review agents from compound-engineering:** +- `agent-native-reviewer` - Ensures features work for agents too +- `architecture-strategist` - Architectural compliance +- `code-simplicity-reviewer` - YAGNI and minimalism +- `data-integrity-guardian` - Database and data safety +- `data-migration-expert` - Migration validation +- `deployment-verification-agent` - Pre-deploy checklists +- `dhh-rails-reviewer` - DHH/37signals Rails style +- `julik-frontend-races-reviewer` - JavaScript race conditions +- `kieran-python-reviewer` - Python best practices +- `kieran-rails-reviewer` - Rails best practices +- `kieran-typescript-reviewer` - TypeScript best practices +- `pattern-recognition-specialist` - Design patterns and anti-patterns +- `performance-oracle` - Performance analysis +- `security-sentinel` - Security vulnerabilities + +### Research Agents +```javascript +// Best practices research +Task({ + subagent_type: "compound-engineering:research:best-practices-researcher", + description: "Research auth best practices", + prompt: "Research current best practices for JWT authentication in Rails 2024-2026" +}) + +// Framework documentation +Task({ + subagent_type: "compound-engineering:research:framework-docs-researcher", + description: "Research Active Storage", + prompt: "Gather comprehensive documentation about Active Storage file uploads" +}) + +// Git history analysis +Task({ + subagent_type: "compound-engineering:research:git-history-analyzer", + description: "Analyze auth history", + prompt: "Analyze the git history of the authentication module to understand its evolution" +}) +``` + +**All research agents:** +- `best-practices-researcher` - External best practices +- `framework-docs-researcher` - Framework documentation +- `git-history-analyzer` - Code archaeology +- `learnings-researcher` - Search docs/solutions/ +- `repo-research-analyst` - Repository patterns + +### Design Agents +```javascript +Task({ + subagent_type: "compound-engineering:design:figma-design-sync", + description: "Sync with Figma", + prompt: "Compare implementation with Figma design at [URL]" +}) +``` + +### Workflow Agents +```javascript +Task({ + subagent_type: "compound-engineering:workflow:bug-reproduction-validator", + description: "Validate bug", + prompt: "Reproduce and validate this reported bug: [description]" +}) +``` + +--- + +## TeammateTool Operations + +### 1. spawnTeam - Create a Team + +```javascript +Teammate({ + operation: "spawnTeam", + team_name: "feature-auth", + description: "Implementing OAuth2 authentication" +}) +``` + +**Creates:** +- `~/.claude/teams/feature-auth/config.json` +- `~/.claude/tasks/feature-auth/` directory +- You become the team leader + +### 2. discoverTeams - List Available Teams + +```javascript +Teammate({ operation: "discoverTeams" }) +``` + +**Returns:** List of teams you can join (not already a member of) + +### 3. requestJoin - Request to Join Team + +```javascript +Teammate({ + operation: "requestJoin", + team_name: "feature-auth", + proposed_name: "helper", + capabilities: "I can help with code review and testing" +}) +``` + +### 4. approveJoin - Accept Join Request (Leader Only) + +When you receive a `join_request` message: +```json +{"type": "join_request", "proposedName": "helper", "requestId": "join-123", ...} +``` + +Approve it: +```javascript +Teammate({ + operation: "approveJoin", + target_agent_id: "helper", + request_id: "join-123" +}) +``` + +### 5. rejectJoin - Decline Join Request (Leader Only) + +```javascript +Teammate({ + operation: "rejectJoin", + target_agent_id: "helper", + request_id: "join-123", + reason: "Team is at capacity" +}) +``` + +### 6. write - Message One Teammate + +```javascript +Teammate({ + operation: "write", + target_agent_id: "security-reviewer", + value: "Please prioritize the authentication module. The deadline is tomorrow." +}) +``` + +**Important for teammates:** Your text output is NOT visible to the team. You MUST use `write` to communicate. + +### 7. broadcast - Message ALL Teammates + +```javascript +Teammate({ + operation: "broadcast", + name: "team-lead", // Your name + value: "Status check: Please report your progress" +}) +``` + +**WARNING:** Broadcasting is expensive - sends N separate messages for N teammates. Prefer `write` to specific teammates. + +**When to broadcast:** +- Critical issues requiring immediate attention +- Major announcements affecting everyone + +**When NOT to broadcast:** +- Responding to one teammate +- Normal back-and-forth +- Information relevant to only some teammates + +### 8. requestShutdown - Ask Teammate to Exit (Leader Only) + +```javascript +Teammate({ + operation: "requestShutdown", + target_agent_id: "security-reviewer", + reason: "All tasks complete, wrapping up" +}) +``` + +### 9. approveShutdown - Accept Shutdown (Teammate Only) + +When you receive a `shutdown_request` message: +```json +{"type": "shutdown_request", "requestId": "shutdown-123", "from": "team-lead", "reason": "Done"} +``` + +**MUST** call: +```javascript +Teammate({ + operation: "approveShutdown", + request_id: "shutdown-123" +}) +``` + +This sends confirmation and terminates your process. + +### 10. rejectShutdown - Decline Shutdown (Teammate Only) + +```javascript +Teammate({ + operation: "rejectShutdown", + request_id: "shutdown-123", + reason: "Still working on task #3, need 5 more minutes" +}) +``` + +### 11. approvePlan - Approve Teammate's Plan (Leader Only) + +When teammate with `plan_mode_required` sends a plan: +```json +{"type": "plan_approval_request", "from": "architect", "requestId": "plan-456", ...} +``` + +Approve: +```javascript +Teammate({ + operation: "approvePlan", + target_agent_id: "architect", + request_id: "plan-456" +}) +``` + +### 12. rejectPlan - Reject Plan with Feedback (Leader Only) + +```javascript +Teammate({ + operation: "rejectPlan", + target_agent_id: "architect", + request_id: "plan-456", + feedback: "Please add error handling for the API calls and consider rate limiting" +}) +``` + +### 13. cleanup - Remove Team Resources + +```javascript +Teammate({ operation: "cleanup" }) +``` + +**Removes:** +- `~/.claude/teams/{team-name}/` directory +- `~/.claude/tasks/{team-name}/` directory + +**IMPORTANT:** Will fail if teammates are still active. Use `requestShutdown` first. + +--- + +## Task System Integration + +### TaskCreate - Create Work Items + +```javascript +TaskCreate({ + subject: "Review authentication module", + description: "Review all files in app/services/auth/ for security vulnerabilities", + activeForm: "Reviewing auth module..." // Shown in spinner when in_progress +}) +``` + +### TaskList - See All Tasks + +```javascript +TaskList() +``` + +Returns: +``` +#1 [completed] Analyze codebase structure +#2 [in_progress] Review authentication module (owner: security-reviewer) +#3 [pending] Generate summary report [blocked by #2] +``` + +### TaskGet - Get Task Details + +```javascript +TaskGet({ taskId: "2" }) +``` + +Returns full task with description, status, blockedBy, etc. + +### TaskUpdate - Update Task Status + +```javascript +// Claim a task +TaskUpdate({ taskId: "2", owner: "security-reviewer" }) + +// Start working +TaskUpdate({ taskId: "2", status: "in_progress" }) + +// Mark complete +TaskUpdate({ taskId: "2", status: "completed" }) + +// Set up dependencies +TaskUpdate({ taskId: "3", addBlockedBy: ["1", "2"] }) +``` + +### Task Dependencies + +When a blocking task is completed, blocked tasks are automatically unblocked: + +```javascript +// Create pipeline +TaskCreate({ subject: "Step 1: Research" }) // #1 +TaskCreate({ subject: "Step 2: Implement" }) // #2 +TaskCreate({ subject: "Step 3: Test" }) // #3 +TaskCreate({ subject: "Step 4: Deploy" }) // #4 + +// Set up dependencies +TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) // #2 waits for #1 +TaskUpdate({ taskId: "3", addBlockedBy: ["2"] }) // #3 waits for #2 +TaskUpdate({ taskId: "4", addBlockedBy: ["3"] }) // #4 waits for #3 + +// When #1 completes, #2 auto-unblocks +// When #2 completes, #3 auto-unblocks +// etc. +``` + +### Task File Structure + +`~/.claude/tasks/{team-name}/1.json`: +```json +{ + "id": "1", + "subject": "Review authentication module", + "description": "Review all files in app/services/auth/...", + "status": "in_progress", + "owner": "security-reviewer", + "activeForm": "Reviewing auth module...", + "blockedBy": [], + "blocks": ["3"], + "createdAt": 1706000000000, + "updatedAt": 1706000001000 +} +``` + +--- + +## Message Formats + +### Regular Message + +```json +{ + "from": "team-lead", + "text": "Please prioritize the auth module", + "timestamp": "2026-01-25T23:38:32.588Z", + "read": false +} +``` + +### Structured Messages (JSON in text field) + +#### Shutdown Request +```json +{ + "type": "shutdown_request", + "requestId": "shutdown-abc123@worker-1", + "from": "team-lead", + "reason": "All tasks complete", + "timestamp": "2026-01-25T23:38:32.588Z" +} +``` + +#### Shutdown Approved +```json +{ + "type": "shutdown_approved", + "requestId": "shutdown-abc123@worker-1", + "from": "worker-1", + "paneId": "%5", + "backendType": "in-process", + "timestamp": "2026-01-25T23:39:00.000Z" +} +``` + +#### Idle Notification (auto-sent when teammate stops) +```json +{ + "type": "idle_notification", + "from": "worker-1", + "timestamp": "2026-01-25T23:40:00.000Z", + "completedTaskId": "2", + "completedStatus": "completed" +} +``` + +#### Task Completed +```json +{ + "type": "task_completed", + "from": "worker-1", + "taskId": "2", + "taskSubject": "Review authentication module", + "timestamp": "2026-01-25T23:40:00.000Z" +} +``` + +#### Plan Approval Request +```json +{ + "type": "plan_approval_request", + "from": "architect", + "requestId": "plan-xyz789", + "planContent": "# Implementation Plan\n\n1. ...", + "timestamp": "2026-01-25T23:41:00.000Z" +} +``` + +#### Join Request +```json +{ + "type": "join_request", + "proposedName": "helper", + "requestId": "join-abc123", + "capabilities": "Code review and testing", + "timestamp": "2026-01-25T23:42:00.000Z" +} +``` + +#### Permission Request (for sandbox/tool permissions) +```json +{ + "type": "permission_request", + "requestId": "perm-123", + "workerId": "worker-1@my-project", + "workerName": "worker-1", + "workerColor": "#4A90D9", + "toolName": "Bash", + "toolUseId": "toolu_abc123", + "description": "Run npm install", + "input": {"command": "npm install"}, + "permissionSuggestions": ["Bash(npm *)"], + "createdAt": 1706000000000 +} +``` + +--- + +## Orchestration Patterns + +### Pattern 1: Parallel Specialists (Leader Pattern) + +Multiple specialists review code simultaneously: + +```javascript +// 1. Create team +Teammate({ operation: "spawnTeam", team_name: "code-review" }) + +// 2. Spawn specialists in parallel (single message, multiple Task calls) +Task({ + team_name: "code-review", + name: "security", + subagent_type: "compound-engineering:review:security-sentinel", + prompt: "Review the PR for security vulnerabilities. Focus on: SQL injection, XSS, auth bypass. Send findings to team-lead.", + run_in_background: true +}) + +Task({ + team_name: "code-review", + name: "performance", + subagent_type: "compound-engineering:review:performance-oracle", + prompt: "Review the PR for performance issues. Focus on: N+1 queries, memory leaks, slow algorithms. Send findings to team-lead.", + run_in_background: true +}) + +Task({ + team_name: "code-review", + name: "simplicity", + subagent_type: "compound-engineering:review:code-simplicity-reviewer", + prompt: "Review the PR for unnecessary complexity. Focus on: over-engineering, premature abstraction, YAGNI violations. Send findings to team-lead.", + run_in_background: true +}) + +// 3. Wait for results (check inbox) +// cat ~/.claude/teams/code-review/inboxes/team-lead.json + +// 4. Synthesize findings and cleanup +Teammate({ operation: "requestShutdown", target_agent_id: "security" }) +Teammate({ operation: "requestShutdown", target_agent_id: "performance" }) +Teammate({ operation: "requestShutdown", target_agent_id: "simplicity" }) +// Wait for approvals... +Teammate({ operation: "cleanup" }) +``` + +### Pattern 2: Pipeline (Sequential Dependencies) + +Each stage depends on the previous: + +```javascript +// 1. Create team and task pipeline +Teammate({ operation: "spawnTeam", team_name: "feature-pipeline" }) + +TaskCreate({ subject: "Research", description: "Research best practices for the feature", activeForm: "Researching..." }) +TaskCreate({ subject: "Plan", description: "Create implementation plan based on research", activeForm: "Planning..." }) +TaskCreate({ subject: "Implement", description: "Implement the feature according to plan", activeForm: "Implementing..." }) +TaskCreate({ subject: "Test", description: "Write and run tests for the implementation", activeForm: "Testing..." }) +TaskCreate({ subject: "Review", description: "Final code review before merge", activeForm: "Reviewing..." }) + +// Set up sequential dependencies +TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) +TaskUpdate({ taskId: "3", addBlockedBy: ["2"] }) +TaskUpdate({ taskId: "4", addBlockedBy: ["3"] }) +TaskUpdate({ taskId: "5", addBlockedBy: ["4"] }) + +// 2. Spawn workers that claim and complete tasks +Task({ + team_name: "feature-pipeline", + name: "researcher", + subagent_type: "compound-engineering:research:best-practices-researcher", + prompt: "Claim task #1, research best practices, complete it, send findings to team-lead. Then check for more work.", + run_in_background: true +}) + +Task({ + team_name: "feature-pipeline", + name: "implementer", + subagent_type: "general-purpose", + prompt: "Poll TaskList every 30 seconds. When task #3 unblocks, claim it and implement. Then complete and notify team-lead.", + run_in_background: true +}) + +// Tasks auto-unblock as dependencies complete +``` + +### Pattern 3: Swarm (Self-Organizing) + +Workers grab available tasks from a pool: + +```javascript +// 1. Create team and task pool +Teammate({ operation: "spawnTeam", team_name: "file-review-swarm" }) + +// Create many independent tasks (no dependencies) +for (const file of ["auth.rb", "user.rb", "api_controller.rb", "payment.rb"]) { + TaskCreate({ + subject: `Review ${file}`, + description: `Review ${file} for security and code quality issues`, + activeForm: `Reviewing ${file}...` + }) +} + +// 2. Spawn worker swarm +Task({ + team_name: "file-review-swarm", + name: "worker-1", + subagent_type: "general-purpose", + prompt: ` + You are a swarm worker. Your job: + 1. Call TaskList to see available tasks + 2. Find a task with status 'pending' and no owner + 3. Claim it with TaskUpdate (set owner to your name) + 4. Do the work + 5. Mark it completed with TaskUpdate + 6. Send findings to team-lead via Teammate write + 7. Repeat until no tasks remain + `, + run_in_background: true +}) + +Task({ + team_name: "file-review-swarm", + name: "worker-2", + subagent_type: "general-purpose", + prompt: `[Same prompt as worker-1]`, + run_in_background: true +}) + +Task({ + team_name: "file-review-swarm", + name: "worker-3", + subagent_type: "general-purpose", + prompt: `[Same prompt as worker-1]`, + run_in_background: true +}) + +// Workers race to claim tasks, naturally load-balance +``` + +### Pattern 4: Research + Implementation + +Research first, then implement: + +```javascript +// 1. Research phase (synchronous, returns results) +const research = await Task({ + subagent_type: "compound-engineering:research:best-practices-researcher", + description: "Research caching patterns", + prompt: "Research best practices for implementing caching in Rails APIs. Include: cache invalidation strategies, Redis vs Memcached, cache key design." +}) + +// 2. Use research to guide implementation +Task({ + subagent_type: "general-purpose", + description: "Implement caching", + prompt: ` + Implement API caching based on this research: + + ${research.content} + + Focus on the user_controller.rb endpoints. + ` +}) +``` + +### Pattern 5: Plan Approval Workflow + +Require plan approval before implementation: + +```javascript +// 1. Create team +Teammate({ operation: "spawnTeam", team_name: "careful-work" }) + +// 2. Spawn architect with plan_mode_required +Task({ + team_name: "careful-work", + name: "architect", + subagent_type: "Plan", + prompt: "Design an implementation plan for adding OAuth2 authentication", + mode: "plan", // Requires plan approval + run_in_background: true +}) + +// 3. Wait for plan approval request +// You'll receive: {"type": "plan_approval_request", "from": "architect", "requestId": "plan-xxx", ...} + +// 4. Review and approve/reject +Teammate({ + operation: "approvePlan", + target_agent_id: "architect", + request_id: "plan-xxx" +}) +// OR +Teammate({ + operation: "rejectPlan", + target_agent_id: "architect", + request_id: "plan-xxx", + feedback: "Please add rate limiting considerations" +}) +``` + +### Pattern 6: Coordinated Multi-File Refactoring + +```javascript +// 1. Create team for coordinated refactoring +Teammate({ operation: "spawnTeam", team_name: "refactor-auth" }) + +// 2. Create tasks with clear file boundaries +TaskCreate({ + subject: "Refactor User model", + description: "Extract authentication methods to AuthenticatableUser concern", + activeForm: "Refactoring User model..." +}) + +TaskCreate({ + subject: "Refactor Session controller", + description: "Update to use new AuthenticatableUser concern", + activeForm: "Refactoring Sessions..." +}) + +TaskCreate({ + subject: "Update specs", + description: "Update all authentication specs for new structure", + activeForm: "Updating specs..." +}) + +// Dependencies: specs depend on both refactors completing +TaskUpdate({ taskId: "3", addBlockedBy: ["1", "2"] }) + +// 3. Spawn workers for each task +Task({ + team_name: "refactor-auth", + name: "model-worker", + subagent_type: "general-purpose", + prompt: "Claim task #1, refactor the User model, complete when done", + run_in_background: true +}) + +Task({ + team_name: "refactor-auth", + name: "controller-worker", + subagent_type: "general-purpose", + prompt: "Claim task #2, refactor the Session controller, complete when done", + run_in_background: true +}) + +Task({ + team_name: "refactor-auth", + name: "spec-worker", + subagent_type: "general-purpose", + prompt: "Wait for task #3 to unblock (when #1 and #2 complete), then update specs", + run_in_background: true +}) +``` + +--- + +## Environment Variables + +Spawned teammates automatically receive these: + +```bash +CLAUDE_CODE_TEAM_NAME="my-project" +CLAUDE_CODE_AGENT_ID="worker-1@my-project" +CLAUDE_CODE_AGENT_NAME="worker-1" +CLAUDE_CODE_AGENT_TYPE="Explore" +CLAUDE_CODE_AGENT_COLOR="#4A90D9" +CLAUDE_CODE_PLAN_MODE_REQUIRED="false" +CLAUDE_CODE_PARENT_SESSION_ID="session-xyz" +``` + +**Using in prompts:** +```javascript +Task({ + team_name: "my-project", + name: "worker", + subagent_type: "general-purpose", + prompt: "Your name is $CLAUDE_CODE_AGENT_NAME. Use it when sending messages to team-lead." +}) +``` + +--- + +## Spawn Backends + +A **backend** determines how teammate Claude instances actually run. Claude Code supports three backends, and **auto-detects** the best one based on your environment. + +### Backend Comparison + +| Backend | How It Works | Visibility | Persistence | Speed | +|---------|-------------|------------|-------------|-------| +| **in-process** | Same Node.js process as leader | Hidden (background) | Dies with leader | Fastest | +| **tmux** | Separate terminal in tmux session | Visible in tmux | Survives leader exit | Medium | +| **iterm2** | Split panes in iTerm2 window | Visible side-by-side | Dies with window | Medium | + +### Auto-Detection Logic + +Claude Code automatically selects a backend using this decision tree: + +```mermaid +flowchart TD + A[Start] --> B{Running inside tmux?} + B -->|Yes| C[Use tmux backend] + B -->|No| D{Running in iTerm2?} + D -->|No| E{tmux available?} + E -->|Yes| F[Use tmux - external session] + E -->|No| G[Use in-process] + D -->|Yes| H{it2 CLI installed?} + H -->|Yes| I[Use iterm2 backend] + H -->|No| J{tmux available?} + J -->|Yes| K[Use tmux - prompt to install it2] + J -->|No| L[Error: Install tmux or it2] +``` + +**Detection checks:** +1. `$TMUX` environment variable → inside tmux +2. `$TERM_PROGRAM === "iTerm.app"` or `$ITERM_SESSION_ID` → in iTerm2 +3. `which tmux` → tmux available +4. `which it2` → it2 CLI installed + +### in-process (Default for non-tmux) + +Teammates run as async tasks within the same Node.js process. + +**How it works:** +- No new process spawned +- Teammates share the same Node.js event loop +- Communication via in-memory queues (fast) +- You don't see teammate output directly + +**When it's used:** +- Not running inside tmux session +- Non-interactive mode (CI, scripts) +- Explicitly set via `CLAUDE_CODE_SPAWN_BACKEND=in-process` + +**Characteristics:** +``` +┌─────────────────────────────────────────┐ +│ Node.js Process │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ Leader │ │Worker 1 │ │Worker 2 │ │ +│ │ (main) │ │ (async) │ │ (async) │ │ +│ └─────────┘ └─────────┘ └─────────┘ │ +└─────────────────────────────────────────┘ +``` + +**Pros:** +- Fastest startup (no process spawn) +- Lowest overhead +- Works everywhere + +**Cons:** +- Can't see teammate output in real-time +- All die if leader dies +- Harder to debug + +```javascript +// in-process is automatic when not in tmux +Task({ + team_name: "my-project", + name: "worker", + subagent_type: "general-purpose", + prompt: "...", + run_in_background: true +}) + +// Force in-process explicitly +// export CLAUDE_CODE_SPAWN_BACKEND=in-process +``` + +### tmux + +Teammates run as separate Claude instances in tmux panes/windows. + +**How it works:** +- Each teammate gets its own tmux pane +- Separate process per teammate +- You can switch panes to see teammate output +- Communication via inbox files + +**When it's used:** +- Running inside a tmux session (`$TMUX` is set) +- tmux available and not in iTerm2 +- Explicitly set via `CLAUDE_CODE_SPAWN_BACKEND=tmux` + +**Layout modes:** + +1. **Inside tmux (native):** Splits your current window +``` +┌─────────────────┬─────────────────┐ +│ │ Worker 1 │ +│ Leader ├─────────────────┤ +│ (your pane) │ Worker 2 │ +│ ├─────────────────┤ +│ │ Worker 3 │ +└─────────────────┴─────────────────┘ +``` + +2. **Outside tmux (external session):** Creates a new tmux session called `claude-swarm` +```bash +# Your terminal stays as-is +# Workers run in separate tmux session + +# View workers: +tmux attach -t claude-swarm +``` + +**Pros:** +- See teammate output in real-time +- Teammates survive leader exit +- Can attach/detach sessions +- Works in CI/headless environments + +**Cons:** +- Slower startup (process spawn) +- Requires tmux installed +- More resource usage + +```bash +# Start tmux session first +tmux new-session -s claude + +# Or force tmux backend +export CLAUDE_CODE_SPAWN_BACKEND=tmux +``` + +**Useful tmux commands:** +```bash +# List all panes in current window +tmux list-panes + +# Switch to pane by number +tmux select-pane -t 1 + +# Kill a specific pane +tmux kill-pane -t %5 + +# View swarm session (if external) +tmux attach -t claude-swarm + +# Rebalance pane layout +tmux select-layout tiled +``` + +### iterm2 (macOS only) + +Teammates run as split panes within your iTerm2 window. + +**How it works:** +- Uses iTerm2's Python API via `it2` CLI +- Splits your current window into panes +- Each teammate visible side-by-side +- Communication via inbox files + +**When it's used:** +- Running in iTerm2 (`$TERM_PROGRAM === "iTerm.app"`) +- `it2` CLI is installed and working +- Python API enabled in iTerm2 preferences + +**Layout:** +``` +┌─────────────────┬─────────────────┐ +│ │ Worker 1 │ +│ Leader ├─────────────────┤ +│ (your pane) │ Worker 2 │ +│ ├─────────────────┤ +│ │ Worker 3 │ +└─────────────────┴─────────────────┘ +``` + +**Pros:** +- Visual debugging - see all teammates +- Native macOS experience +- No tmux needed +- Automatic pane management + +**Cons:** +- macOS + iTerm2 only +- Requires setup (it2 CLI + Python API) +- Panes die with window + +**Setup:** +```bash +# 1. Install it2 CLI +uv tool install it2 +# OR +pipx install it2 +# OR +pip install --user it2 + +# 2. Enable Python API in iTerm2 +# iTerm2 → Settings → General → Magic → Enable Python API + +# 3. Restart iTerm2 + +# 4. Verify +it2 --version +it2 session list +``` + +**If setup fails:** +Claude Code will prompt you to set up it2 when you first spawn a teammate. You can choose to: +1. Install it2 now (guided setup) +2. Use tmux instead +3. Cancel + +### Forcing a Backend + +```bash +# Force in-process (fastest, no visibility) +export CLAUDE_CODE_SPAWN_BACKEND=in-process + +# Force tmux (visible panes, persistent) +export CLAUDE_CODE_SPAWN_BACKEND=tmux + +# Auto-detect (default) +unset CLAUDE_CODE_SPAWN_BACKEND +``` + +### Backend in Team Config + +The backend type is recorded per-teammate in `config.json`: + +```json +{ + "members": [ + { + "name": "worker-1", + "backendType": "in-process", + "tmuxPaneId": "in-process" + }, + { + "name": "worker-2", + "backendType": "tmux", + "tmuxPaneId": "%5" + } + ] +} +``` + +### Troubleshooting Backends + +| Issue | Cause | Solution | +|-------|-------|----------| +| "No pane backend available" | Neither tmux nor iTerm2 available | Install tmux: `brew install tmux` | +| "it2 CLI not installed" | In iTerm2 but missing it2 | Run `uv tool install it2` | +| "Python API not enabled" | it2 can't communicate with iTerm2 | Enable in iTerm2 Settings → General → Magic | +| Workers not visible | Using in-process backend | Start inside tmux or iTerm2 | +| Workers dying unexpectedly | Outside tmux, leader exited | Use tmux for persistence | + +### Checking Current Backend + +```bash +# See what backend was detected +cat ~/.claude/teams/{team}/config.json | jq '.members[].backendType' + +# Check if inside tmux +echo $TMUX + +# Check if in iTerm2 +echo $TERM_PROGRAM + +# Check tmux availability +which tmux + +# Check it2 availability +which it2 +``` + +--- + +## Error Handling + +### Common Errors + +| Error | Cause | Solution | +|-------|-------|----------| +| "Cannot cleanup with active members" | Teammates still running | `requestShutdown` all teammates first, wait for approval | +| "Already leading a team" | Team already exists | `cleanup` first, or use different team name | +| "Agent not found" | Wrong teammate name | Check `config.json` for actual names | +| "Team does not exist" | No team created | Call `spawnTeam` first | +| "team_name is required" | Missing team context | Provide `team_name` parameter | +| "Agent type not found" | Invalid subagent_type | Check available agents with proper prefix | + +### Graceful Shutdown Sequence + +**Always follow this sequence:** + +```javascript +// 1. Request shutdown for all teammates +Teammate({ operation: "requestShutdown", target_agent_id: "worker-1" }) +Teammate({ operation: "requestShutdown", target_agent_id: "worker-2" }) + +// 2. Wait for shutdown approvals +// Check for {"type": "shutdown_approved", ...} messages + +// 3. Verify no active members +// Read ~/.claude/teams/{team}/config.json + +// 4. Only then cleanup +Teammate({ operation: "cleanup" }) +``` + +### Handling Crashed Teammates + +Teammates have a 5-minute heartbeat timeout. If a teammate crashes: + +1. They'll be automatically marked as inactive after timeout +2. Their tasks remain in the task list +3. Another teammate can claim their tasks +4. Cleanup will work after timeout expires + +### Debugging + +```bash +# Check team config +cat ~/.claude/teams/{team}/config.json | jq '.members[] | {name, agentType, backendType}' + +# Check teammate inboxes +cat ~/.claude/teams/{team}/inboxes/{agent}.json | jq '.' + +# List all teams +ls ~/.claude/teams/ + +# Check task states +cat ~/.claude/tasks/{team}/*.json | jq '{id, subject, status, owner, blockedBy}' + +# Watch for new messages +tail -f ~/.claude/teams/{team}/inboxes/team-lead.json +``` + +--- + +## Complete Workflows + +### Workflow 1: Full Code Review with Parallel Specialists + +```javascript +// === STEP 1: Setup === +Teammate({ operation: "spawnTeam", team_name: "pr-review-123", description: "Reviewing PR #123" }) + +// === STEP 2: Spawn reviewers in parallel === +// (Send all these in a single message for parallel execution) +Task({ + team_name: "pr-review-123", + name: "security", + subagent_type: "compound-engineering:review:security-sentinel", + prompt: `Review PR #123 for security vulnerabilities. + + Focus on: + - SQL injection + - XSS vulnerabilities + - Authentication/authorization bypass + - Sensitive data exposure + + When done, send your findings to team-lead using: + Teammate({ operation: "write", target_agent_id: "team-lead", value: "Your findings here" })`, + run_in_background: true +}) + +Task({ + team_name: "pr-review-123", + name: "perf", + subagent_type: "compound-engineering:review:performance-oracle", + prompt: `Review PR #123 for performance issues. + + Focus on: + - N+1 queries + - Missing indexes + - Memory leaks + - Inefficient algorithms + + Send findings to team-lead when done.`, + run_in_background: true +}) + +Task({ + team_name: "pr-review-123", + name: "arch", + subagent_type: "compound-engineering:review:architecture-strategist", + prompt: `Review PR #123 for architectural concerns. + + Focus on: + - Design pattern adherence + - SOLID principles + - Separation of concerns + - Testability + + Send findings to team-lead when done.`, + run_in_background: true +}) + +// === STEP 3: Monitor and collect results === +// Poll inbox or wait for idle notifications +// cat ~/.claude/teams/pr-review-123/inboxes/team-lead.json + +// === STEP 4: Synthesize findings === +// Combine all reviewer findings into a cohesive report + +// === STEP 5: Cleanup === +Teammate({ operation: "requestShutdown", target_agent_id: "security" }) +Teammate({ operation: "requestShutdown", target_agent_id: "perf" }) +Teammate({ operation: "requestShutdown", target_agent_id: "arch" }) +// Wait for approvals... +Teammate({ operation: "cleanup" }) +``` + +### Workflow 2: Research → Plan → Implement → Test Pipeline + +```javascript +// === SETUP === +Teammate({ operation: "spawnTeam", team_name: "feature-oauth" }) + +// === CREATE PIPELINE === +TaskCreate({ subject: "Research OAuth providers", description: "Research OAuth2 best practices and compare providers (Google, GitHub, Auth0)", activeForm: "Researching OAuth..." }) +TaskCreate({ subject: "Create implementation plan", description: "Design OAuth implementation based on research findings", activeForm: "Planning..." }) +TaskCreate({ subject: "Implement OAuth", description: "Implement OAuth2 authentication according to plan", activeForm: "Implementing OAuth..." }) +TaskCreate({ subject: "Write tests", description: "Write comprehensive tests for OAuth implementation", activeForm: "Writing tests..." }) +TaskCreate({ subject: "Final review", description: "Review complete implementation for security and quality", activeForm: "Final review..." }) + +// Set dependencies +TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) +TaskUpdate({ taskId: "3", addBlockedBy: ["2"] }) +TaskUpdate({ taskId: "4", addBlockedBy: ["3"] }) +TaskUpdate({ taskId: "5", addBlockedBy: ["4"] }) + +// === SPAWN SPECIALIZED WORKERS === +Task({ + team_name: "feature-oauth", + name: "researcher", + subagent_type: "compound-engineering:research:best-practices-researcher", + prompt: "Claim task #1. Research OAuth2 best practices, compare providers, document findings. Mark task complete and send summary to team-lead.", + run_in_background: true +}) + +Task({ + team_name: "feature-oauth", + name: "planner", + subagent_type: "Plan", + prompt: "Wait for task #2 to unblock. Read research from task #1. Create detailed implementation plan. Mark complete and send plan to team-lead.", + run_in_background: true +}) + +Task({ + team_name: "feature-oauth", + name: "implementer", + subagent_type: "general-purpose", + prompt: "Wait for task #3 to unblock. Read plan from task #2. Implement OAuth2 authentication. Mark complete when done.", + run_in_background: true +}) + +Task({ + team_name: "feature-oauth", + name: "tester", + subagent_type: "general-purpose", + prompt: "Wait for task #4 to unblock. Write comprehensive tests for the OAuth implementation. Run tests. Mark complete with results.", + run_in_background: true +}) + +Task({ + team_name: "feature-oauth", + name: "reviewer", + subagent_type: "compound-engineering:review:security-sentinel", + prompt: "Wait for task #5 to unblock. Review the complete OAuth implementation for security. Send final assessment to team-lead.", + run_in_background: true +}) + +// Pipeline auto-progresses as each stage completes +``` + +### Workflow 3: Self-Organizing Code Review Swarm + +```javascript +// === SETUP === +Teammate({ operation: "spawnTeam", team_name: "codebase-review" }) + +// === CREATE TASK POOL (all independent, no dependencies) === +const filesToReview = [ + "app/models/user.rb", + "app/models/payment.rb", + "app/controllers/api/v1/users_controller.rb", + "app/controllers/api/v1/payments_controller.rb", + "app/services/payment_processor.rb", + "app/services/notification_service.rb", + "lib/encryption_helper.rb" +] + +for (const file of filesToReview) { + TaskCreate({ + subject: `Review ${file}`, + description: `Review ${file} for security vulnerabilities, code quality, and performance issues`, + activeForm: `Reviewing ${file}...` + }) +} + +// === SPAWN WORKER SWARM === +const swarmPrompt = ` +You are a swarm worker. Your job is to continuously process available tasks. + +LOOP: +1. Call TaskList() to see available tasks +2. Find a task that is: + - status: 'pending' + - no owner + - not blocked +3. If found: + - Claim it: TaskUpdate({ taskId: "X", owner: "YOUR_NAME" }) + - Start it: TaskUpdate({ taskId: "X", status: "in_progress" }) + - Do the review work + - Complete it: TaskUpdate({ taskId: "X", status: "completed" }) + - Send findings to team-lead via Teammate write + - Go back to step 1 +4. If no tasks available: + - Send idle notification to team-lead + - Wait 30 seconds + - Try again (up to 3 times) + - If still no tasks, exit + +Replace YOUR_NAME with your actual agent name from $CLAUDE_CODE_AGENT_NAME. +` + +// Spawn 3 workers +Task({ team_name: "codebase-review", name: "worker-1", subagent_type: "general-purpose", prompt: swarmPrompt, run_in_background: true }) +Task({ team_name: "codebase-review", name: "worker-2", subagent_type: "general-purpose", prompt: swarmPrompt, run_in_background: true }) +Task({ team_name: "codebase-review", name: "worker-3", subagent_type: "general-purpose", prompt: swarmPrompt, run_in_background: true }) + +// Workers self-organize: race to claim tasks, naturally load-balance +// Monitor progress with TaskList() or by reading inbox +``` + +--- + +## Best Practices + +### 1. Always Cleanup +Don't leave orphaned teams. Always call `cleanup` when done. + +### 2. Use Meaningful Names +```javascript +// Good +name: "security-reviewer" +name: "oauth-implementer" +name: "test-writer" + +// Bad +name: "worker-1" +name: "agent-2" +``` + +### 3. Write Clear Prompts +Tell workers exactly what to do: +```javascript +// Good +prompt: ` + 1. Review app/models/user.rb for N+1 queries + 2. Check all ActiveRecord associations have proper includes + 3. Document any issues found + 4. Send findings to team-lead via Teammate write +` + +// Bad +prompt: "Review the code" +``` + +### 4. Use Task Dependencies +Let the system manage unblocking: +```javascript +// Good: Auto-unblocking +TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) + +// Bad: Manual polling +"Wait until task #1 is done, check every 30 seconds..." +``` + +### 5. Check Inboxes for Results +Workers send results to your inbox. Check it: +```bash +cat ~/.claude/teams/{team}/inboxes/team-lead.json | jq '.' +``` + +### 6. Handle Worker Failures +- Workers have 5-minute heartbeat timeout +- Tasks of crashed workers can be reclaimed +- Build retry logic into worker prompts + +### 7. Prefer write Over broadcast +`broadcast` sends N messages for N teammates. Use `write` for targeted communication. + +### 8. Match Agent Type to Task +- **Explore** for searching/reading +- **Plan** for architecture design +- **general-purpose** for implementation +- **Specialized reviewers** for specific review types + +--- + +## Quick Reference + +### Spawn Subagent (No Team) +```javascript +Task({ subagent_type: "Explore", description: "Find files", prompt: "..." }) +``` + +### Spawn Teammate (With Team) +```javascript +Teammate({ operation: "spawnTeam", team_name: "my-team" }) +Task({ team_name: "my-team", name: "worker", subagent_type: "general-purpose", prompt: "...", run_in_background: true }) +``` + +### Message Teammate +```javascript +Teammate({ operation: "write", target_agent_id: "worker-1", value: "..." }) +``` + +### Create Task Pipeline +```javascript +TaskCreate({ subject: "Step 1", description: "..." }) +TaskCreate({ subject: "Step 2", description: "..." }) +TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) +``` + +### Shutdown Team +```javascript +Teammate({ operation: "requestShutdown", target_agent_id: "worker-1" }) +// Wait for approval... +Teammate({ operation: "cleanup" }) +``` + +--- + +*Based on Claude Code v2.1.19 - Tested and verified 2026-01-25* From 0c404f9544a0e6553a81c113938a9e7cc6f9a776 Mon Sep 17 00:00:00 2001 From: David Alley Date: Sun, 8 Feb 2026 16:41:44 -0600 Subject: [PATCH 04/39] fix(git-worktree): detect worktrees where .git is a file, not a directory (#159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In git worktrees, .git is a regular file containing a gitdir: pointer back to the main repository — not a directory. The -d check caused list and cleanup to silently skip all worktrees, reporting "No worktrees found". Changed to -e (exists) which handles both cases. Fixes #158 Co-authored-by: Claude Opus 4.6 --- .../skills/git-worktree/scripts/worktree-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh b/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh index 713c7fa..9a75334 100755 --- a/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +++ b/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh @@ -134,7 +134,7 @@ list_worktrees() { local count=0 for worktree_path in "$WORKTREE_DIR"/*; do - if [[ -d "$worktree_path" && -d "$worktree_path/.git" ]]; then + if [[ -d "$worktree_path" && -e "$worktree_path/.git" ]]; then count=$((count + 1)) local worktree_name=$(basename "$worktree_path") local branch=$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") @@ -231,7 +231,7 @@ cleanup_worktrees() { local to_remove=() for worktree_path in "$WORKTREE_DIR"/*; do - if [[ -d "$worktree_path" && -d "$worktree_path/.git" ]]; then + if [[ -d "$worktree_path" && -e "$worktree_path/.git" ]]; then local worktree_name=$(basename "$worktree_path") # Skip if current worktree From c40eb2eaa23723166486de080b9bd73bc529ad6c Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Sun, 8 Feb 2026 14:43:47 -0800 Subject: [PATCH 05/39] Remove the confirmation of worktree creation (#144) --- .../skills/git-worktree/scripts/worktree-manager.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh b/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh index 9a75334..181d6d1 100755 --- a/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +++ b/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh @@ -91,14 +91,6 @@ create_worktree() { echo -e "${BLUE}Creating worktree: $branch_name${NC}" echo " From: $from_branch" echo " Path: $worktree_path" - echo "" - echo "Proceed? (y/n)" - read -r response - - if [[ "$response" != "y" ]]; then - echo -e "${YELLOW}Cancelled${NC}" - return - fi # Update main branch echo -e "${BLUE}Updating $from_branch...${NC}" From 895d340dd4cc5fc9fdac61a849d3c2615a0c8237 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 9 Feb 2026 00:53:47 +0200 Subject: [PATCH 06/39] Note new repository URL (#108) --- plans/grow-your-own-garden-plugin-architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plans/grow-your-own-garden-plugin-architecture.md b/plans/grow-your-own-garden-plugin-architecture.md index ebb0b02..b64c054 100644 --- a/plans/grow-your-own-garden-plugin-architecture.md +++ b/plans/grow-your-own-garden-plugin-architecture.md @@ -1,6 +1,6 @@ # Grow Your Own Garden: Adaptive Agent Ecosystem -> **Issue:** https://github.com/kieranklaassen/compound-engineering-plugin/issues/20 +> **Issue:** https://github.com/EveryInc/compound-engineering-plugin/issues/20 ## The Idea From c69c47fe9bdf1886d6fbeb0c020c0f9db46687d8 Mon Sep 17 00:00:00 2001 From: Zac Williams <15909+jzw@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:58:51 -0600 Subject: [PATCH 07/39] fix: backup existing config files before overwriting (#119) Before writing config.toml (Codex) or opencode.json (OpenCode), the CLI attempts to create a timestamped backup of any existing config file. This prevents accidental data loss when users have customized configs. Backup is best-effort - if it fails (e.g., unusual permissions), the install continues without blocking. Backup files are named: config.toml.bak.2026-01-23T21-16-40-065Z --- src/targets/codex.ts | 9 +++++++-- src/targets/opencode.ts | 7 ++++++- src/utils/files.ts | 13 +++++++++++++ tests/codex-writer.test.ts | 32 ++++++++++++++++++++++++++++++++ tests/opencode-writer.test.ts | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/targets/codex.ts b/src/targets/codex.ts index f38a4dd..9e8ba8b 100644 --- a/src/targets/codex.ts +++ b/src/targets/codex.ts @@ -1,5 +1,5 @@ import path from "path" -import { copyDir, ensureDir, writeText } from "../utils/files" +import { backupFile, copyDir, ensureDir, writeText } from "../utils/files" import type { CodexBundle } from "../types/codex" import type { ClaudeMcpServer } from "../types/claude" @@ -30,7 +30,12 @@ export async function writeCodexBundle(outputRoot: string, bundle: CodexBundle): const config = renderCodexConfig(bundle.mcpServers) if (config) { - await writeText(path.join(codexRoot, "config.toml"), config) + const configPath = path.join(codexRoot, "config.toml") + const backupPath = await backupFile(configPath) + if (backupPath) { + console.log(`Backed up existing config to ${backupPath}`) + } + await writeText(configPath, config) } } diff --git a/src/targets/opencode.ts b/src/targets/opencode.ts index 09f372a..24e8faf 100644 --- a/src/targets/opencode.ts +++ b/src/targets/opencode.ts @@ -1,10 +1,15 @@ import path from "path" -import { copyDir, ensureDir, writeJson, writeText } from "../utils/files" +import { backupFile, copyDir, ensureDir, writeJson, writeText } from "../utils/files" import type { OpenCodeBundle } from "../types/opencode" export async function writeOpenCodeBundle(outputRoot: string, bundle: OpenCodeBundle): Promise { const paths = resolveOpenCodePaths(outputRoot) await ensureDir(paths.root) + + const backupPath = await backupFile(paths.configPath) + if (backupPath) { + console.log(`Backed up existing config to ${backupPath}`) + } await writeJson(paths.configPath, bundle.config) const agentsDir = paths.agentsDir diff --git a/src/utils/files.ts b/src/utils/files.ts index 5fd1453..9994d0c 100644 --- a/src/utils/files.ts +++ b/src/utils/files.ts @@ -1,6 +1,19 @@ import { promises as fs } from "fs" import path from "path" +export async function backupFile(filePath: string): Promise { + if (!(await pathExists(filePath))) return null + + try { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-") + const backupPath = `${filePath}.bak.${timestamp}` + await fs.copyFile(filePath, backupPath) + return backupPath + } catch { + return null + } +} + export async function pathExists(filePath: string): Promise { try { await fs.access(filePath) diff --git a/tests/codex-writer.test.ts b/tests/codex-writer.test.ts index ad2f03a..3aeb42e 100644 --- a/tests/codex-writer.test.ts +++ b/tests/codex-writer.test.ts @@ -73,4 +73,36 @@ describe("writeCodexBundle", () => { expect(await exists(path.join(codexRoot, "prompts", "command-one.md"))).toBe(true) expect(await exists(path.join(codexRoot, "skills", "skill-one", "SKILL.md"))).toBe(true) }) + + test("backs up existing config.toml before overwriting", async () => { + const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-backup-")) + const codexRoot = path.join(tempRoot, ".codex") + const configPath = path.join(codexRoot, "config.toml") + + // Create existing config + await fs.mkdir(codexRoot, { recursive: true }) + const originalContent = "# My original config\n[custom]\nkey = \"value\"\n" + await fs.writeFile(configPath, originalContent) + + const bundle: CodexBundle = { + prompts: [], + skillDirs: [], + generatedSkills: [], + mcpServers: { test: { command: "echo" } }, + } + + await writeCodexBundle(codexRoot, bundle) + + // New config should be written + const newConfig = await fs.readFile(configPath, "utf8") + expect(newConfig).toContain("[mcp_servers.test]") + + // Backup should exist with original content + const files = await fs.readdir(codexRoot) + const backupFileName = files.find((f) => f.startsWith("config.toml.bak.")) + expect(backupFileName).toBeDefined() + + const backupContent = await fs.readFile(path.join(codexRoot, backupFileName!), "utf8") + expect(backupContent).toBe(originalContent) + }) }) diff --git a/tests/opencode-writer.test.ts b/tests/opencode-writer.test.ts index c481520..0bafcc0 100644 --- a/tests/opencode-writer.test.ts +++ b/tests/opencode-writer.test.ts @@ -84,4 +84,36 @@ describe("writeOpenCodeBundle", () => { expect(await exists(path.join(outputRoot, "skills", "skill-one", "SKILL.md"))).toBe(true) expect(await exists(path.join(outputRoot, ".opencode"))).toBe(false) }) + + test("backs up existing opencode.json before overwriting", async () => { + const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-backup-")) + const outputRoot = path.join(tempRoot, ".opencode") + const configPath = path.join(outputRoot, "opencode.json") + + // Create existing config + await fs.mkdir(outputRoot, { recursive: true }) + const originalConfig = { $schema: "https://opencode.ai/config.json", custom: "value" } + await fs.writeFile(configPath, JSON.stringify(originalConfig, null, 2)) + + const bundle: OpenCodeBundle = { + config: { $schema: "https://opencode.ai/config.json", new: "config" }, + agents: [], + plugins: [], + skillDirs: [], + } + + await writeOpenCodeBundle(outputRoot, bundle) + + // New config should be written + const newConfig = JSON.parse(await fs.readFile(configPath, "utf8")) + expect(newConfig.new).toBe("config") + + // Backup should exist with original content + const files = await fs.readdir(outputRoot) + const backupFileName = files.find((f) => f.startsWith("opencode.json.bak.")) + expect(backupFileName).toBeDefined() + + const backupContent = JSON.parse(await fs.readFile(path.join(outputRoot, backupFileName!), "utf8")) + expect(backupContent.custom).toBe("value") + }) }) From f7cab16b06d510ed4ed151793660eda5ce49905a Mon Sep 17 00:00:00 2001 From: Roberto Mello Date: Sun, 8 Feb 2026 15:59:57 -0700 Subject: [PATCH 08/39] Fix crash when hook entries have no matcher (#160) Claude Code allows hook entries without a `matcher` field (e.g., SessionStart and SubagentStop hooks don't need one). The OpenCode converter assumed `matcher.matcher` was always present, causing "undefined is not an object (evaluating 'matcher.matcher.split')" when converting plugins with matcher-less hooks. Make `matcher` optional in the type and guard all accesses. --- src/converters/claude-to-opencode.ts | 12 +++++++----- src/types/claude.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/converters/claude-to-opencode.ts b/src/converters/claude-to-opencode.ts index ad8cc00..5e3cf71 100644 --- a/src/converters/claude-to-opencode.ts +++ b/src/converters/claude-to-opencode.ts @@ -209,9 +209,11 @@ function renderHookStatements( ): string[] { if (!matcher.hooks || matcher.hooks.length === 0) return [] const tools = matcher.matcher - .split("|") - .map((tool) => tool.trim().toLowerCase()) - .filter(Boolean) + ? matcher.matcher + .split("|") + .map((tool) => tool.trim().toLowerCase()) + .filter(Boolean) + : [] const useMatcher = useToolMatcher && tools.length > 0 && !tools.includes("*") const condition = useMatcher @@ -232,10 +234,10 @@ function renderHookStatements( continue } if (hook.type === "prompt") { - statements.push(`// Prompt hook for ${matcher.matcher}: ${hook.prompt.replace(/\n/g, " ")}`) + statements.push(`// Prompt hook for ${matcher.matcher ?? "*"}: ${hook.prompt.replace(/\n/g, " ")}`) continue } - statements.push(`// Agent hook for ${matcher.matcher}: ${hook.agent}`) + statements.push(`// Agent hook for ${matcher.matcher ?? "*"}: ${hook.agent}`) } return statements diff --git a/src/types/claude.ts b/src/types/claude.ts index 4b1c050..a878274 100644 --- a/src/types/claude.ts +++ b/src/types/claude.ts @@ -79,7 +79,7 @@ export type ClaudeHookAgent = { export type ClaudeHookEntry = ClaudeHookCommand | ClaudeHookPrompt | ClaudeHookAgent export type ClaudeHookMatcher = { - matcher: string + matcher?: string hooks: ClaudeHookEntry[] } From 1bdd1030f532abdbb4581380796a5a6c46881906 Mon Sep 17 00:00:00 2001 From: Terry Li <12233004+terry-li-hm@users.noreply.github.com> Date: Mon, 9 Feb 2026 07:00:48 +0800 Subject: [PATCH 09/39] feat: Add sync command for Claude Code personal config (#123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add sync command for Claude Code personal config Add `compound-plugin sync` command to sync ~/.claude/ personal config (skills and MCP servers) to OpenCode or Codex. Features: - Parses ~/.claude/skills/ for personal skills (supports symlinks) - Parses ~/.claude/settings.json for MCP servers - Syncs skills as symlinks (single source of truth) - Converts MCP to JSON (OpenCode) or TOML (Codex) - Dedicated sync functions bypass existing converter architecture Usage: compound-plugin sync --target opencode compound-plugin sync --target codex 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * fix: address security and quality review issues Security fixes: - Add path traversal validation with isValidSkillName() - Warn when MCP servers contain potential secrets (API keys, tokens) - Set restrictive file permissions (600) on config files - Safe forceSymlink refuses to delete real directories - Proper TOML escaping for quotes/backslashes/control chars Code quality fixes: - Extract shared symlink utils to src/utils/symlink.ts - Replace process.exit(1) with thrown error - Distinguish ENOENT from other errors in catch blocks - Remove unused `root` field from ClaudeHomeConfig - Make Codex sync idempotent (remove+rewrite managed section) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * fix: revert version bump (leave to maintainers) * feat: bump root version to 0.2.0 for sync command --------- Co-authored-by: Claude --- README.md | 20 ++++++++- package.json | 2 +- src/commands/sync.ts | 84 ++++++++++++++++++++++++++++++++++ src/index.ts | 2 + src/parsers/claude-home.ts | 65 +++++++++++++++++++++++++++ src/sync/codex.ts | 92 ++++++++++++++++++++++++++++++++++++++ src/sync/opencode.ts | 75 +++++++++++++++++++++++++++++++ src/utils/symlink.ts | 43 ++++++++++++++++++ 8 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 src/commands/sync.ts create mode 100644 src/parsers/claude-home.ts create mode 100644 src/sync/codex.ts create mode 100644 src/sync/opencode.ts create mode 100644 src/utils/symlink.ts diff --git a/README.md b/README.md index fe0df98..f7b64fd 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,28 @@ Local dev: bun run src/index.ts install ./plugins/compound-engineering --to opencode ``` -OpenCode output is written to `~/.opencode` by default, with `opencode.json` at the root and `agents/`, `skills/`, and `plugins/` alongside it. +OpenCode output is written to `~/.config/opencode` by default, with `opencode.json` at the root and `agents/`, `skills/`, and `plugins/` alongside it. Both provider targets are experimental and may change as the formats evolve. Codex output is written to `~/.codex/prompts` and `~/.codex/skills`, with each Claude command converted into both a prompt and a skill (the prompt instructs Codex to load the corresponding skill). Generated Codex skill descriptions are truncated to 1024 characters (Codex limit). +## Sync Personal Config + +Sync your personal Claude Code config (`~/.claude/`) to OpenCode or Codex: + +```bash +# Sync skills and MCP servers to OpenCode +bunx @every-env/compound-plugin sync --target opencode + +# Sync to Codex +bunx @every-env/compound-plugin sync --target codex +``` + +This syncs: +- Personal skills from `~/.claude/skills/` (as symlinks) +- MCP servers from `~/.claude/settings.json` + +Skills are symlinked (not copied) so changes in Claude Code are reflected immediately. + ## Workflow ``` diff --git a/package.json b/package.json index 591df8d..118f1c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@every-env/compound-plugin", - "version": "0.1.1", + "version": "0.2.0", "type": "module", "private": false, "bin": { diff --git a/src/commands/sync.ts b/src/commands/sync.ts new file mode 100644 index 0000000..5678b2e --- /dev/null +++ b/src/commands/sync.ts @@ -0,0 +1,84 @@ +import { defineCommand } from "citty" +import os from "os" +import path from "path" +import { loadClaudeHome } from "../parsers/claude-home" +import { syncToOpenCode } from "../sync/opencode" +import { syncToCodex } from "../sync/codex" + +function isValidTarget(value: string): value is "opencode" | "codex" { + return value === "opencode" || value === "codex" +} + +/** Check if any MCP servers have env vars that might contain secrets */ +function hasPotentialSecrets(mcpServers: Record): boolean { + const sensitivePatterns = /key|token|secret|password|credential|api_key/i + for (const server of Object.values(mcpServers)) { + const env = (server as { env?: Record }).env + if (env) { + for (const key of Object.keys(env)) { + if (sensitivePatterns.test(key)) return true + } + } + } + return false +} + +export default defineCommand({ + meta: { + name: "sync", + description: "Sync Claude Code config (~/.claude/) to OpenCode or Codex", + }, + args: { + target: { + type: "string", + required: true, + description: "Target: opencode | codex", + }, + claudeHome: { + type: "string", + alias: "claude-home", + description: "Path to Claude home (default: ~/.claude)", + }, + }, + async run({ args }) { + if (!isValidTarget(args.target)) { + throw new Error(`Unknown target: ${args.target}. Use 'opencode' or 'codex'.`) + } + + const claudeHome = expandHome(args.claudeHome ?? path.join(os.homedir(), ".claude")) + const config = await loadClaudeHome(claudeHome) + + // Warn about potential secrets in MCP env vars + if (hasPotentialSecrets(config.mcpServers)) { + console.warn( + "⚠️ Warning: MCP servers contain env vars that may include secrets (API keys, tokens).\n" + + " These will be copied to the target config. Review before sharing the config file.", + ) + } + + console.log( + `Syncing ${config.skills.length} skills, ${Object.keys(config.mcpServers).length} MCP servers...`, + ) + + const outputRoot = + args.target === "opencode" + ? path.join(os.homedir(), ".config", "opencode") + : path.join(os.homedir(), ".codex") + + if (args.target === "opencode") { + await syncToOpenCode(config, outputRoot) + } else { + await syncToCodex(config, outputRoot) + } + + console.log(`✓ Synced to ${args.target}: ${outputRoot}`) + }, +}) + +function expandHome(value: string): string { + if (value === "~") return os.homedir() + if (value.startsWith(`~${path.sep}`)) { + return path.join(os.homedir(), value.slice(2)) + } + return value +} diff --git a/src/index.ts b/src/index.ts index 49c5774..bfd0b72 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import { defineCommand, runMain } from "citty" import convert from "./commands/convert" import install from "./commands/install" import listCommand from "./commands/list" +import sync from "./commands/sync" const main = defineCommand({ meta: { @@ -14,6 +15,7 @@ const main = defineCommand({ convert: () => convert, install: () => install, list: () => listCommand, + sync: () => sync, }, }) diff --git a/src/parsers/claude-home.ts b/src/parsers/claude-home.ts new file mode 100644 index 0000000..c8f1818 --- /dev/null +++ b/src/parsers/claude-home.ts @@ -0,0 +1,65 @@ +import path from "path" +import os from "os" +import fs from "fs/promises" +import type { ClaudeSkill, ClaudeMcpServer } from "../types/claude" + +export interface ClaudeHomeConfig { + skills: ClaudeSkill[] + mcpServers: Record +} + +export async function loadClaudeHome(claudeHome?: string): Promise { + const home = claudeHome ?? path.join(os.homedir(), ".claude") + + const [skills, mcpServers] = await Promise.all([ + loadPersonalSkills(path.join(home, "skills")), + loadSettingsMcp(path.join(home, "settings.json")), + ]) + + return { skills, mcpServers } +} + +async function loadPersonalSkills(skillsDir: string): Promise { + try { + const entries = await fs.readdir(skillsDir, { withFileTypes: true }) + const skills: ClaudeSkill[] = [] + + for (const entry of entries) { + // Check if directory or symlink (symlinks are common for skills) + if (!entry.isDirectory() && !entry.isSymbolicLink()) continue + + const entryPath = path.join(skillsDir, entry.name) + const skillPath = path.join(entryPath, "SKILL.md") + + try { + await fs.access(skillPath) + // Resolve symlink to get the actual source directory + const sourceDir = entry.isSymbolicLink() + ? await fs.realpath(entryPath) + : entryPath + skills.push({ + name: entry.name, + sourceDir, + skillPath, + }) + } catch { + // No SKILL.md, skip + } + } + return skills + } catch { + return [] // Directory doesn't exist + } +} + +async function loadSettingsMcp( + settingsPath: string, +): Promise> { + try { + const content = await fs.readFile(settingsPath, "utf-8") + const settings = JSON.parse(content) as { mcpServers?: Record } + return settings.mcpServers ?? {} + } catch { + return {} // File doesn't exist or invalid JSON + } +} diff --git a/src/sync/codex.ts b/src/sync/codex.ts new file mode 100644 index 0000000..c0414bd --- /dev/null +++ b/src/sync/codex.ts @@ -0,0 +1,92 @@ +import fs from "fs/promises" +import path from "path" +import type { ClaudeHomeConfig } from "../parsers/claude-home" +import type { ClaudeMcpServer } from "../types/claude" +import { forceSymlink, isValidSkillName } from "../utils/symlink" + +export async function syncToCodex( + config: ClaudeHomeConfig, + outputRoot: string, +): Promise { + // Ensure output directories exist + const skillsDir = path.join(outputRoot, "skills") + await fs.mkdir(skillsDir, { recursive: true }) + + // Symlink skills (with validation) + for (const skill of config.skills) { + if (!isValidSkillName(skill.name)) { + console.warn(`Skipping skill with invalid name: ${skill.name}`) + continue + } + const target = path.join(skillsDir, skill.name) + await forceSymlink(skill.sourceDir, target) + } + + // Write MCP servers to config.toml (TOML format) + if (Object.keys(config.mcpServers).length > 0) { + const configPath = path.join(outputRoot, "config.toml") + const mcpToml = convertMcpForCodex(config.mcpServers) + + // Read existing config and merge idempotently + let existingContent = "" + try { + existingContent = await fs.readFile(configPath, "utf-8") + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== "ENOENT") { + throw err + } + } + + // Remove any existing Claude Code MCP section to make idempotent + const marker = "# MCP servers synced from Claude Code" + const markerIndex = existingContent.indexOf(marker) + if (markerIndex !== -1) { + existingContent = existingContent.slice(0, markerIndex).trimEnd() + } + + const newContent = existingContent + ? existingContent + "\n\n" + marker + "\n" + mcpToml + : "# Codex config - synced from Claude Code\n\n" + mcpToml + + await fs.writeFile(configPath, newContent, { mode: 0o600 }) + } +} + +/** Escape a string for TOML double-quoted strings */ +function escapeTomlString(str: string): string { + return str + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/\t/g, "\\t") +} + +function convertMcpForCodex(servers: Record): string { + const sections: string[] = [] + + for (const [name, server] of Object.entries(servers)) { + if (!server.command) continue + + const lines: string[] = [] + lines.push(`[mcp_servers.${name}]`) + lines.push(`command = "${escapeTomlString(server.command)}"`) + + if (server.args && server.args.length > 0) { + const argsStr = server.args.map((arg) => `"${escapeTomlString(arg)}"`).join(", ") + lines.push(`args = [${argsStr}]`) + } + + if (server.env && Object.keys(server.env).length > 0) { + lines.push("") + lines.push(`[mcp_servers.${name}.env]`) + for (const [key, value] of Object.entries(server.env)) { + lines.push(`${key} = "${escapeTomlString(value)}"`) + } + } + + sections.push(lines.join("\n")) + } + + return sections.join("\n\n") + "\n" +} diff --git a/src/sync/opencode.ts b/src/sync/opencode.ts new file mode 100644 index 0000000..e61e638 --- /dev/null +++ b/src/sync/opencode.ts @@ -0,0 +1,75 @@ +import fs from "fs/promises" +import path from "path" +import type { ClaudeHomeConfig } from "../parsers/claude-home" +import type { ClaudeMcpServer } from "../types/claude" +import type { OpenCodeMcpServer } from "../types/opencode" +import { forceSymlink, isValidSkillName } from "../utils/symlink" + +export async function syncToOpenCode( + config: ClaudeHomeConfig, + outputRoot: string, +): Promise { + // Ensure output directories exist + const skillsDir = path.join(outputRoot, "skills") + await fs.mkdir(skillsDir, { recursive: true }) + + // Symlink skills (with validation) + for (const skill of config.skills) { + if (!isValidSkillName(skill.name)) { + console.warn(`Skipping skill with invalid name: ${skill.name}`) + continue + } + const target = path.join(skillsDir, skill.name) + await forceSymlink(skill.sourceDir, target) + } + + // Merge MCP servers into opencode.json + if (Object.keys(config.mcpServers).length > 0) { + const configPath = path.join(outputRoot, "opencode.json") + const existing = await readJsonSafe(configPath) + const mcpConfig = convertMcpForOpenCode(config.mcpServers) + existing.mcp = { ...(existing.mcp ?? {}), ...mcpConfig } + await fs.writeFile(configPath, JSON.stringify(existing, null, 2), { mode: 0o600 }) + } +} + +async function readJsonSafe(filePath: string): Promise> { + try { + const content = await fs.readFile(filePath, "utf-8") + return JSON.parse(content) as Record + } catch (err) { + if ((err as NodeJS.ErrnoException).code === "ENOENT") { + return {} + } + throw err + } +} + +function convertMcpForOpenCode( + servers: Record, +): Record { + const result: Record = {} + + for (const [name, server] of Object.entries(servers)) { + if (server.command) { + result[name] = { + type: "local", + command: [server.command, ...(server.args ?? [])], + environment: server.env, + enabled: true, + } + continue + } + + if (server.url) { + result[name] = { + type: "remote", + url: server.url, + headers: server.headers, + enabled: true, + } + } + } + + return result +} diff --git a/src/utils/symlink.ts b/src/utils/symlink.ts new file mode 100644 index 0000000..8855adb --- /dev/null +++ b/src/utils/symlink.ts @@ -0,0 +1,43 @@ +import fs from "fs/promises" + +/** + * Create a symlink, safely replacing any existing symlink at target. + * Only removes existing symlinks - refuses to delete real directories. + */ +export async function forceSymlink(source: string, target: string): Promise { + try { + const stat = await fs.lstat(target) + if (stat.isSymbolicLink()) { + // Safe to remove existing symlink + await fs.unlink(target) + } else if (stat.isDirectory()) { + // Refuse to delete real directories + throw new Error( + `Cannot create symlink at ${target}: a real directory exists there. ` + + `Remove it manually if you want to replace it with a symlink.` + ) + } else { + // Regular file - remove it + await fs.unlink(target) + } + } catch (err) { + // ENOENT means target doesn't exist, which is fine + if ((err as NodeJS.ErrnoException).code !== "ENOENT") { + throw err + } + } + await fs.symlink(source, target) +} + +/** + * Validate a skill name to prevent path traversal attacks. + * Returns true if safe, false if potentially malicious. + */ +export function isValidSkillName(name: string): boolean { + if (!name || name.length === 0) return false + if (name.includes("/") || name.includes("\\")) return false + if (name.includes("..")) return false + if (name.includes("\0")) return false + if (name === "." || name === "..") return false + return true +} From a5bba3dc54610afa4e0c963a9fe88510fb2441cb Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Sun, 8 Feb 2026 15:04:45 -0800 Subject: [PATCH 10/39] feat(skills): add document-review skill for brainstorm/plan refinement (#112) Add optional review and refine step to workflows:brainstorm and workflows:plan that checks documents for clarity, completeness, user intent, and YAGNI. - New document-review skill with review questions and evaluation criteria - Brainstorm Phase 4 offers "Review and refine" option - Plan post-generation offers "Review and refine" after technical review - Includes YAGNI-based simplification guidance - Max 2 iteration rounds before suggesting to proceed Co-authored-by: Kieran Klaassen --- plugins/compound-engineering/README.md | 5 +- .../commands/deepen-plan.md | 4 +- .../commands/plan_review.md | 7 -- .../commands/technical_review.md | 7 ++ .../commands/workflows/brainstorm.md | 13 ++- .../commands/workflows/plan.md | 18 ++-- .../skills/document-review/SKILL.md | 87 +++++++++++++++++++ 7 files changed, 119 insertions(+), 22 deletions(-) delete mode 100644 plugins/compound-engineering/commands/plan_review.md create mode 100644 plugins/compound-engineering/commands/technical_review.md create mode 100644 plugins/compound-engineering/skills/document-review/SKILL.md diff --git a/plugins/compound-engineering/README.md b/plugins/compound-engineering/README.md index 94398ce..0aa5543 100644 --- a/plugins/compound-engineering/README.md +++ b/plugins/compound-engineering/README.md @@ -94,7 +94,7 @@ Core workflow commands use `workflows:` prefix to avoid collisions with built-in | `/create-agent-skill` | Create or edit Claude Code skills | | `/generate_command` | Generate new slash commands | | `/heal-skill` | Fix skill documentation issues | -| `/plan_review` | Multi-agent plan review in parallel | +| `/technical_review` | Multi-agent technical/architecture review in parallel | | `/report-bug` | Report a bug in the plugin | | `/reproduce-bug` | Reproduce bugs using logs and console | | `/resolve_parallel` | Resolve TODO comments in parallel | @@ -129,7 +129,8 @@ Core workflow commands use `workflows:` prefix to avoid collisions with built-in | Skill | Description | |-------|-------------| -| `brainstorming` | Guided ideation for exploring requirements and approaches | +| `brainstorming` | Explore requirements and approaches through collaborative dialogue | +| `document-review` | Improve documents through structured self-review | | `every-style-editor` | Review copy for Every's style guide compliance | | `file-todos` | File-based todo tracking system | | `git-worktree` | Manage Git worktrees for parallel development | diff --git a/plugins/compound-engineering/commands/deepen-plan.md b/plugins/compound-engineering/commands/deepen-plan.md index 2a2ed4f..a705476 100644 --- a/plugins/compound-engineering/commands/deepen-plan.md +++ b/plugins/compound-engineering/commands/deepen-plan.md @@ -480,14 +480,14 @@ After writing the enhanced plan, use the **AskUserQuestion tool** to present the **Options:** 1. **View diff** - Show what was added/changed -2. **Run `/plan_review`** - Get feedback from reviewers on enhanced plan +2. **Run `/technical_review`** - Get feedback from reviewers on enhanced plan 3. **Start `/workflows:work`** - Begin implementing this enhanced plan 4. **Deepen further** - Run another round of research on specific sections 5. **Revert** - Restore original plan (if backup exists) Based on selection: - **View diff** → Run `git diff [plan_path]` or show before/after -- **`/plan_review`** → Call the /plan_review command with the plan file path +- **`/technical_review`** → Call the /technical_review command with the plan file path - **`/workflows:work`** → Call the /workflows:work command with the plan file path - **Deepen further** → Ask which sections need more research, then re-run those agents - **Revert** → Restore from git or backup diff --git a/plugins/compound-engineering/commands/plan_review.md b/plugins/compound-engineering/commands/plan_review.md deleted file mode 100644 index 70ef5dd..0000000 --- a/plugins/compound-engineering/commands/plan_review.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: plan_review -description: Have multiple specialized agents review a plan in parallel -argument-hint: "[plan file path or plan content]" ---- - -Have @agent-dhh-rails-reviewer @agent-kieran-rails-reviewer @agent-code-simplicity-reviewer review this plan in parallel. diff --git a/plugins/compound-engineering/commands/technical_review.md b/plugins/compound-engineering/commands/technical_review.md new file mode 100644 index 0000000..8199542 --- /dev/null +++ b/plugins/compound-engineering/commands/technical_review.md @@ -0,0 +1,7 @@ +--- +name: technical_review +description: Have multiple specialized agents review the technical approach and architecture of a plan in parallel +argument-hint: "[plan file path or plan content]" +--- + +Have @agent-dhh-rails-reviewer @agent-kieran-rails-reviewer @agent-code-simplicity-reviewer review the technical approach in this plan in parallel. diff --git a/plugins/compound-engineering/commands/workflows/brainstorm.md b/plugins/compound-engineering/commands/workflows/brainstorm.md index 3cb204d..b9432db 100644 --- a/plugins/compound-engineering/commands/workflows/brainstorm.md +++ b/plugins/compound-engineering/commands/workflows/brainstorm.md @@ -85,10 +85,19 @@ Use **AskUserQuestion tool** to present next steps: **Question:** "Brainstorm captured. What would you like to do next?" **Options:** -1. **Proceed to planning** - Run `/workflows:plan` (will auto-detect this brainstorm) -2. **Refine design further** - Continue exploring +1. **Review and refine** - Improve the document through structured self-review +2. **Proceed to planning** - Run `/workflows:plan` (will auto-detect this brainstorm) 3. **Done for now** - Return later +**If user selects "Review and refine":** + +Load the `document-review` skill and apply it to the brainstorm document. + +When document-review returns "Review complete", present next steps: + +1. **Move to planning** - Continue to `/workflows:plan` with this document +2. **Done for now** - Brainstorming complete. To start planning later: `/workflows:plan [document-path]` + ## Output Summary When complete, display: diff --git a/plugins/compound-engineering/commands/workflows/plan.md b/plugins/compound-engineering/commands/workflows/plan.md index 92e5aa3..a199cca 100644 --- a/plugins/compound-engineering/commands/workflows/plan.md +++ b/plugins/compound-engineering/commands/workflows/plan.md @@ -498,25 +498,25 @@ After writing the plan file, use the **AskUserQuestion tool** to present these o **Options:** 1. **Open plan in editor** - Open the plan file for review 2. **Run `/deepen-plan`** - Enhance each section with parallel research agents (best practices, performance, UI) -3. **Run `/plan_review`** - Get feedback from reviewers (DHH, Kieran, Simplicity) -4. **Start `/workflows:work`** - Begin implementing this plan locally -5. **Start `/workflows:work` on remote** - Begin implementing in Claude Code on the web (use `&` to run in background) -6. **Create Issue** - Create issue in project tracker (GitHub/Linear) -7. **Simplify** - Reduce detail level +3. **Run `/technical_review`** - Technical feedback from code-focused reviewers (DHH, Kieran, Simplicity) +4. **Review and refine** - Improve the document through structured self-review +5. **Start `/workflows:work`** - Begin implementing this plan locally +6. **Start `/workflows:work` on remote** - Begin implementing in Claude Code on the web (use `&` to run in background) +7. **Create Issue** - Create issue in project tracker (GitHub/Linear) Based on selection: - **Open plan in editor** → Run `open docs/plans/.md` to open the file in the user's default editor - **`/deepen-plan`** → Call the /deepen-plan command with the plan file path to enhance with research -- **`/plan_review`** → Call the /plan_review command with the plan file path +- **`/technical_review`** → Call the /technical_review command with the plan file path +- **Review and refine** → Load `document-review` skill. - **`/workflows:work`** → Call the /workflows:work command with the plan file path - **`/workflows:work` on remote** → Run `/workflows:work docs/plans/.md &` to start work in background for Claude Code web - **Create Issue** → See "Issue Creation" section below -- **Simplify** → Ask "What should I simplify?" then regenerate simpler version - **Other** (automatically provided) → Accept free text for rework or specific changes **Note:** If running `/workflows:plan` with ultrathink enabled, automatically run `/deepen-plan` after plan creation for maximum depth and grounding. -Loop back to options after Simplify or Other changes until user selects `/workflows:work` or `/plan_review`. +Loop back to options after Simplify or Other changes until user selects `/workflows:work` or `/technical_review`. ## Issue Creation @@ -546,6 +546,6 @@ When user selects "Create Issue", detect their project tracker from CLAUDE.md: 5. **After creation:** - Display the issue URL - - Ask if they want to proceed to `/workflows:work` or `/plan_review` + - Ask if they want to proceed to `/workflows:work` or `/technical_review` NEVER CODE! Just research and write the plan. diff --git a/plugins/compound-engineering/skills/document-review/SKILL.md b/plugins/compound-engineering/skills/document-review/SKILL.md new file mode 100644 index 0000000..e9cb3b2 --- /dev/null +++ b/plugins/compound-engineering/skills/document-review/SKILL.md @@ -0,0 +1,87 @@ +--- +name: document-review +description: This skill should be used to refine brainstorm or plan documents before proceeding to the next workflow step. It applies when a brainstorm or plan document exists and the user wants to improve it. +--- + +# Document Review + +Improve brainstorm or plan documents through structured review. + +## Step 1: Get the Document + +**If a document path is provided:** Read it, then proceed to Step 2. + +**If no document is specified:** Ask which document to review, or look for the most recent brainstorm/plan in `docs/brainstorms/` or `docs/plans/`. + +## Step 2: Assess + +Read through the document and ask: + +- What is unclear? +- What is unnecessary? +- What decision is being avoided? +- What assumptions are unstated? +- Where could scope accidentally expand? + +These questions surface issues. Don't fix yet—just note what you find. + +## Step 3: Evaluate + +Score the document against these criteria: + +| Criterion | What to Check | +|-----------|---------------| +| **Clarity** | Problem statement is clear, no vague language ("probably," "consider," "try to") | +| **Completeness** | Required sections present, constraints stated, open questions flagged | +| **Specificity** | Concrete enough for next step (brainstorm → can plan, plan → can implement) | +| **YAGNI** | No hypothetical features, simplest approach chosen | + +If invoked within a workflow (after `/workflows:brainstorm` or `/workflows:plan`), also check: +- **User intent fidelity** — Document reflects what was discussed, assumptions validated + +## Step 4: Identify the Critical Improvement + +Among everything found in Steps 2-3, does one issue stand out? If something would significantly improve the document's quality, this is the "must address" item. Highlight it prominently. + +## Step 5: Make Changes + +Present your findings, then: + +1. **Auto-fix** minor issues (vague language, formatting) without asking +2. **Ask approval** before substantive changes (restructuring, removing sections, changing meaning) +3. **Update** the document inline—no separate files, no metadata sections + +### Simplification Guidance + +Simplification is purposeful removal of unnecessary complexity, not shortening for its own sake. + +**Simplify when:** +- Content serves hypothetical future needs, not current ones +- Sections repeat information already covered elsewhere +- Detail exceeds what's needed to take the next step +- Abstractions or structure add overhead without clarity + +**Don't simplify:** +- Constraints or edge cases that affect implementation +- Rationale that explains why alternatives were rejected +- Open questions that need resolution + +## Step 6: Offer Next Action + +After changes are complete, ask: + +1. **Refine again** - Another review pass +2. **Review complete** - Document is ready + +### Iteration Guidance + +After 2 refinement passes, recommend completion—diminishing returns are likely. But if the user wants to continue, allow it. + +Return control to the caller (workflow or user) after selection. + +## What NOT to Do + +- Do not rewrite the entire document +- Do not add new sections or requirements the user didn't discuss +- Do not over-engineer or add complexity +- Do not create separate review files or add metadata sections From 04ee7e450653a3f6f8721a128d3c372867adfcc8 Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Sun, 8 Feb 2026 15:08:59 -0800 Subject: [PATCH 11/39] fix(compound): prevent subagents from writing intermediary files (#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /workflows:compound command was inconsistently creating intermediary files during research phase instead of having subagents return text data to the orchestrator. Changes: - Add block explicitly forbidding subagent file writes - Restructure into clear two-phase orchestration (research → assembly) - Remove Documentation Writer as parallel subagent (was incorrectly parallel) - Add Phase 3 for optional enhancement agents with proper sequencing - Add Common Mistakes table for quick reference - Update success output to reflect new structure Co-authored-by: Kieran Klaassen --- .../commands/workflows/compound.md | 91 +++++++++++++------ 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/plugins/compound-engineering/commands/workflows/compound.md b/plugins/compound-engineering/commands/workflows/compound.md index 4042685..7f647d2 100644 --- a/plugins/compound-engineering/commands/workflows/compound.md +++ b/plugins/compound-engineering/commands/workflows/compound.md @@ -21,53 +21,83 @@ Captures problem solutions while context is fresh, creating structured documenta /workflows:compound [brief context] # Provide additional context hint ``` -## Execution Strategy: Parallel Subagents +## Execution Strategy: Two-Phase Orchestration -This command launches multiple specialized subagents IN PARALLEL to maximize efficiency: + +**Only ONE file gets written - the final documentation.** -### 1. **Context Analyzer** (Parallel) +Phase 1 subagents return TEXT DATA to the orchestrator. They must NOT use Write, Edit, or create any files. Only the orchestrator (Phase 2) writes the final documentation file. + + +### Phase 1: Parallel Research + + + +Launch these subagents IN PARALLEL. Each returns text data to the orchestrator. + +#### 1. **Context Analyzer** - Extracts conversation history - Identifies problem type, component, symptoms - - Validates against solution schema + - Validates against schema - Returns: YAML frontmatter skeleton -### 2. **Solution Extractor** (Parallel) +#### 2. **Solution Extractor** - Analyzes all investigation steps - Identifies root cause - Extracts working solution with code examples - Returns: Solution content block -### 3. **Related Docs Finder** (Parallel) +#### 3. **Related Docs Finder** - Searches `docs/solutions/` for related documentation - Identifies cross-references and links - Finds related GitHub issues - Returns: Links and relationships -### 4. **Prevention Strategist** (Parallel) +#### 4. **Prevention Strategist** - Develops prevention strategies - Creates best practices guidance - Generates test cases if applicable - Returns: Prevention/testing content -### 5. **Category Classifier** (Parallel) +#### 5. **Category Classifier** - Determines optimal `docs/solutions/` category - Validates category against schema - Suggests filename based on slug - Returns: Final path and filename -### 6. **Documentation Writer** (Parallel) - - Assembles complete markdown file - - Validates YAML frontmatter - - Formats content for readability - - Creates the file in correct location + -### 7. **Optional: Specialized Agent Invocation** (Post-Documentation) - Based on problem type detected, automatically invoke applicable agents: - - **performance_issue** → `performance-oracle` - - **security_issue** → `security-sentinel` - - **database_issue** → `data-integrity-guardian` - - **test_failure** → `cora-test-reviewer` - - Any code-heavy issue → `kieran-rails-reviewer` + `code-simplicity-reviewer` +### Phase 2: Assembly & Write + + + +**WAIT for all Phase 1 subagents to complete before proceeding.** + +The orchestrating agent (main conversation) performs these steps: + +1. Collect all text results from Phase 1 subagents +2. Assemble complete markdown file from the collected pieces +3. Validate YAML frontmatter against schema +4. Create directory if needed: `mkdir -p docs/solutions/[category]/` +5. Write the SINGLE final file: `docs/solutions/[category]/[filename].md` + + + +### Phase 3: Optional Enhancement + +**WAIT for Phase 2 to complete before proceeding.** + + + +Based on problem type, optionally invoke specialized agents to review the documentation: + +- **performance_issue** → `performance-oracle` +- **security_issue** → `security-sentinel` +- **database_issue** → `data-integrity-guardian` +- **test_failure** → `cora-test-reviewer` +- Any code-heavy issue → `kieran-rails-reviewer` + `code-simplicity-reviewer` + + ## What It Captures @@ -110,18 +140,25 @@ This command launches multiple specialized subagents IN PARALLEL to maximize eff - integration-issues/ - logic-errors/ +## Common Mistakes to Avoid + +| ❌ Wrong | ✅ Correct | +|----------|-----------| +| Subagents write files like `context-analysis.md`, `solution-draft.md` | Subagents return text data; orchestrator writes one final file | +| Research and assembly run in parallel | Research completes → then assembly runs | +| Multiple files created during workflow | Single file: `docs/solutions/[category]/[filename].md` | + ## Success Output ``` -✓ Parallel documentation generation complete +✓ Documentation complete -Primary Subagent Results: +Subagent Results: ✓ Context Analyzer: Identified performance_issue in brief_system - ✓ Solution Extractor: Extracted 3 code fixes - ✓ Related Docs Finder: Found 2 related issues - ✓ Prevention Strategist: Generated test cases - ✓ Category Classifier: docs/solutions/performance-issues/ - ✓ Documentation Writer: Created complete markdown + ✓ Solution Extractor: 3 code fixes + ✓ Related Docs Finder: 2 related issues + ✓ Prevention Strategist: Prevention strategies, test suggestions + ✓ Category Classifier: `performance-issues` Specialized Agent Reviews (Auto-Triggered): ✓ performance-oracle: Validated query optimization approach From 4f4873f8c066044117df70cfba11dc967309b53e Mon Sep 17 00:00:00 2001 From: Kieran Klaassen Date: Sun, 8 Feb 2026 14:39:02 -0800 Subject: [PATCH 12/39] Update create-agent-skills to match 2026 official docs, add /triage-prs command - Rewrite SKILL.md to document that commands and skills are now merged - Add new frontmatter fields: disable-model-invocation, user-invocable, context, agent - Add invocation control table and dynamic context injection docs - Fix skill-structure.md: was incorrectly recommending XML tags over markdown headings - Update official-spec.md with complete 2026 specification - Add local /triage-prs command for PR triage workflow - Add PR triage plan document Co-Authored-By: Claude Opus 4.6 --- .claude/commands/triage-prs.md | 193 ++++++++ ...026-02-08-feat-pr-triage-and-merge-plan.md | 128 +++++ .../skills/create-agent-skills/SKILL.md | 378 +++++++-------- .../references/official-spec.md | 205 ++++---- .../references/skill-structure.md | 436 +++++------------- 5 files changed, 683 insertions(+), 657 deletions(-) create mode 100644 .claude/commands/triage-prs.md create mode 100644 docs/plans/2026-02-08-feat-pr-triage-and-merge-plan.md diff --git a/.claude/commands/triage-prs.md b/.claude/commands/triage-prs.md new file mode 100644 index 0000000..45e9717 --- /dev/null +++ b/.claude/commands/triage-prs.md @@ -0,0 +1,193 @@ +--- +name: triage-prs +description: Triage all open PRs with parallel agents, label, group, and review one-by-one +argument-hint: "[optional: repo owner/name or GitHub PRs URL]" +disable-model-invocation: true +allowed-tools: Bash(gh *), Bash(git log *) +--- + +# Triage Open Pull Requests + +Review, label, and act on all open PRs for a repository using parallel review agents. Produces a grouped triage report, applies labels, cross-references with issues, and walks through each PR for merge/comment decisions. + +## Step 0: Detect Repository + +Detect repo context: +- Current repo: !`gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || echo "no repo detected"` +- Current branch: !`git branch --show-current 2>/dev/null` + +If `$ARGUMENTS` contains a GitHub URL or `owner/repo`, use that instead. Confirm the repo with the user if ambiguous. + +## Step 1: Gather Context (Parallel) + +Run these in parallel: + +1. **List all open PRs:** + ```bash + gh pr list --repo OWNER/REPO --state open --limit 50 + ``` + +2. **List all open issues:** + ```bash + gh issue list --repo OWNER/REPO --state open --limit 50 + ``` + +3. **List existing labels:** + ```bash + gh label list --repo OWNER/REPO --limit 50 + ``` + +4. **Check recent merges** (to detect duplicate/superseded PRs): + ```bash + git log --oneline -20 main + ``` + +## Step 2: Batch PRs by Theme + +Group PRs into review batches of 4-6 based on apparent type: + +- **Bug fixes** - titles with `fix`, `bug`, error descriptions +- **Features** - titles with `feat`, `add`, new functionality +- **Documentation** - titles with `docs`, `readme`, terminology +- **Configuration/Setup** - titles with `config`, `setup`, `install` +- **Stale/Old** - PRs older than 30 days + +## Step 3: Parallel Review (Team of Agents) + +Spawn one review agent per batch using the Task tool. Each agent should: + +For each PR in their batch: +1. Run `gh pr view --repo OWNER/REPO --json title,body,files,additions,deletions,author,createdAt` +2. Run `gh pr diff --repo OWNER/REPO ` (pipe to `head -200` for large diffs) +3. Determine: + - **Description:** 1-2 sentence summary of the change + - **Label:** Which existing repo label fits best + - **Action:** merge / request changes / close / needs discussion + - **Related PRs:** Any PRs in this or other batches that touch the same files or feature + - **Quality notes:** Code quality, test coverage, staleness concerns + +Instruct each agent to: +- Flag PRs that touch the same files (potential merge conflicts) +- Flag PRs that duplicate recently merged work +- Flag PRs that are part of a group solving the same problem differently +- Report findings as a markdown table +- Send findings back via message when done + +## Step 4: Cross-Reference Issues + +After all agents report, match issues to PRs: + +- Check if any PR title/body mentions `Fixes #X` or `Closes #X` +- Check if any issue title matches a PR's topic +- Look for duplicate issues (same bug reported twice) + +Build a mapping table: +``` +| Issue | PR | Relationship | +|-------|-----|--------------| +| #158 | #159 | PR fixes issue | +``` + +## Step 5: Identify Themes + +Group all issues into themes (3-6 themes): +- Count issues per theme +- Note which themes have PRs addressing them and which don't +- Flag themes with competing/overlapping PRs + +## Step 6: Compile Triage Report + +Present a single report with: + +1. **Summary stats:** X open PRs, Y open issues, Z themes +2. **PR groups** with recommended actions: + - Group name and related PRs + - Per-PR: #, title, author, description, label, action +3. **Issue-to-PR mapping** +4. **Themes across issues** +5. **Suggested cleanup:** spam issues, duplicates, stale items + +## Step 7: Apply Labels + +After presenting the report, ask user: + +> "Apply these labels to all PRs on GitHub?" + +If yes, run `gh pr edit --repo OWNER/REPO --add-label "