@mmerterden/multi-agent-pipeline 8.6.0 → 8.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -1
  3. package/pipeline/commands/multi-agent/refs/features/dev-critic.md +44 -0
  4. package/pipeline/commands/multi-agent/refs/features/external-context-injection.md +63 -0
  5. package/pipeline/commands/multi-agent/refs/features/plan-todos.md +20 -0
  6. package/pipeline/commands/multi-agent/refs/features/prior-fix-detection.md +49 -0
  7. package/pipeline/commands/multi-agent/refs/features/repo-map.md +30 -0
  8. package/pipeline/commands/multi-agent/refs/features/shadow-git.md +24 -0
  9. package/pipeline/commands/multi-agent/refs/phases/phase-0-init.md +3 -3
  10. package/pipeline/commands/multi-agent/refs/phases/phase-1-analysis.md +4 -121
  11. package/pipeline/commands/multi-agent/refs/phases/phase-3-dev.md +4 -75
  12. package/pipeline/commands/multi-agent/refs/phases/phase-6-commit.md +1 -1
  13. package/pipeline/commands/multi-agent/setup.md +14 -6
  14. package/pipeline/commands/multi-agent/sync.md +25 -24
  15. package/pipeline/scripts/fixtures/install-layout.tsv +11 -11
  16. package/pipeline/scripts/smoke-issue-comment-template.sh +1 -1
  17. package/pipeline/scripts/smoke-sync-adapters.sh +113 -0
  18. package/pipeline/scripts/smoke-sync-delegation.sh +1 -1
  19. package/pipeline/scripts/smoke-url-enrichment.sh +1 -1
  20. package/pipeline/scripts/sync-adapters.mjs +156 -0
  21. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +1 -1
  22. package/pipeline/skills/figma-common/figma-issue/SKILL.md +5 -5
  23. package/pipeline/skills/figma-common/figma-setup/SKILL.md +17 -17
  24. package/pipeline/skills/figma-common/figma-validate/SKILL.md +1 -1
  25. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +1 -1
  26. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +5 -5
  27. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +1 -1
  28. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +1 -1
  29. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +1 -1
  30. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +1 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  | Figma skills (iOS + Android + Common) | 37 |
19
19
  | External skill catalog (`shared/external/`) | 127 |
20
20
  | Total `SKILL.md` files across all groups | 196 |
21
- | Smoke suites | 87 |
21
+ | Smoke suites | 88 |
22
22
  | Golden-task fixtures (`pipeline/eval/golden-tasks/`) | 2 |
23
23
  | Eval-triage fixtures | 11 |
24
24
  | JSON schemas | 13 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "8.6.0",
3
+ "version": "8.6.1",
4
4
  "description": "8-phase AI development pipeline. Full orchestration on Claude Code + Copilot CLI; knowledge layer (rules + skills) ports to Cursor, Windsurf, and Cline. Analysis, planning, TDD, CLI-aware parallel review with Opus triage, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -46,6 +46,9 @@
46
46
  ],
47
47
  "author": "Mert Erden",
48
48
  "license": "MIT",
49
+ "publishConfig": {
50
+ "registry": "https://npm.pkg.github.com"
51
+ },
49
52
  "repository": {
50
53
  "type": "git",
51
54
  "url": "https://github.com/mmerterden/multi-agent-pipeline.git"
@@ -0,0 +1,44 @@
1
+ # Feature: Dev Critic (Phase 3 Step 3.5 Evaluator-Optimizer)
2
+
3
+ **Pattern**: Anthropic's evaluator-optimizer from "Building Effective Agents" (Dec 2024). Generator (Phase 3 Dev) and critic (`agents/dev-critic.md`) loop on deterministic criteria already written in `rules/*.md` before any Phase 4 reviewer call.
4
+
5
+ **Gated by `prefs.global.devCritic.enabled`** (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4:
6
+
7
+ 1. Dispatch `dev-critic` sub-agent (Sonnet by default, tools: `Read, Grep, Glob, Bash`).
8
+ 2. Critic runs 4 deterministic gates (build / lint / test / secrets) — failure of any → `pass: false` with `blocking` finding.
9
+ 3. If gates green, critic walks the platform checklist (iOS 13-item / Android Kotlin / Backend generic — selected by Phase 1 `detectedStack`).
10
+ 4. Returns schema-validated JSON (`pipeline/schemas/dev-critic-output.schema.json`): `{pass, iteration, gates, findings[], escalate}`.
11
+
12
+ ## Loop cap (STRICT) — max 2 iterations
13
+
14
+ | Round | Behavior |
15
+ |---|---|
16
+ | 1 | Full critic pass — all gates + full checklist |
17
+ | 2 | Re-check only round-1 failures. No new findings allowed (scope creep prevention) |
18
+ | 3+ | NOT ALLOWED. Critic returns `escalate: true`; orchestrator pauses (interactive) or proceeds to Phase 4 with logged failure (autopilot) |
19
+
20
+ ## Action by severity
21
+
22
+ - `blocking` → generator MUST fix; another Dev iteration before re-critic
23
+ - `important` → generator SHOULD fix; if skipped, pass through to Phase 4 (legitimate reviewer ground)
24
+ - `suggestion` → generator's judgement; never blocks round 2
25
+
26
+ ## Telemetry
27
+
28
+ Each critic call emits `dev_critic.call` with `iteration`, `pass`, `gates_failed`, `blocking`, `important`, `duration_ms`, `tokens_in/out`. Phase 7 cost rollup lists these as `phase 3.5` line items so the net saving (Phase 4 reviewer/triage calls avoided) is measurable.
29
+
30
+ ## Off by default reason
31
+
32
+ Introduces ~1× Sonnet call per Dev iteration. On simple bug fixes the cost outweighs the benefit (Phase 4 would have caught the same thing for similar cost). Recommended on:
33
+
34
+ - Feature work (≥200 LOC diff)
35
+ - Security-touching paths (auth, keychain, network)
36
+ - Multi-file refactors where rule violations compound
37
+
38
+ ## Why this fits orchestrator-workers + evaluator-optimizer hybrid
39
+
40
+ Phase 4 is parallelization-with-voting — good for *adversarial* perspectives (security, architecture). Phase 3.5 is evaluator-optimizer — good for *deterministic* criteria (build, tests, checklists). Sending failing builds into Phase 4 wastes 2–3 reviewer calls + Opus triage; Phase 3.5 absorbs that cost at one Sonnet call.
41
+
42
+ ## Reference
43
+
44
+ See `pipeline/agents/dev-critic.md` for the full agent specification (gates, checklist enumeration, output schema, severity semantics).
@@ -0,0 +1,63 @@
1
+ # Feature: External Context Injection (Phase 1 Step 1.5)
2
+
3
+ Phase 0 Step 1b catalogued every typed external link from the task description into `state.contextLinks[]`. Phase 1 dispatches each entry to its matching fetcher and prepends the result to the analysis prompt under a **Referenced External Sources** section, so the agent doesn't re-discover what the ticket already pointed at.
4
+
5
+ ```bash
6
+ jq -c '.contextLinks // []' "$STATE_FILE" \
7
+ | python3 -c '
8
+ import json, sys, os
9
+ links = json.loads(sys.stdin.read() or "[]")
10
+ by_type = {}
11
+ for l in links:
12
+ by_type.setdefault(l["type"], []).append(l)
13
+ print(json.dumps(by_type))
14
+ ' > /tmp/context-by-type-${TASK_ID}.json
15
+ ```
16
+
17
+ ## Dispatch table
18
+
19
+ One fetcher per type. Each fetcher emits a normalized JSON view that the analysis prompt consumes as ground truth for the referenced source.
20
+
21
+ | Type | Fetcher | Output stored in |
22
+ |---|---|---|
23
+ | `crashlytics` | `~/.claude/lib/fetch-crashlytics.sh <url>` (already invoked in Phase 0 Step 1b.1 for the legacy `state.crashContext` field; Phase 1 reads that field directly) | `state.crashContext` |
24
+ | `fortify` | `~/.claude/lib/fetch-fortify.sh <url>` (already invoked in Phase 0 Step 1b.2 for the legacy `state.fortifyFinding` field; Phase 1 reads that field, Phase 4 reads the full payload for the security gate) | `state.fortifyFinding` |
25
+ | `swagger` | `~/.claude/lib/fetch-swagger.sh <url>` → endpoints[], request/response examples | `state.fetchedContext.swagger[]` |
26
+ | `confluence` | `~/.claude/lib/fetch-confluence.sh <url>` → page body, code blocks, extracted API contracts | `state.fetchedContext.confluence[]` |
27
+ | `figma` | `~/.claude/lib/fetch-figma.sh <url>` (via existing MCP / REST fallback path) when the task is a component; otherwise advisory only | `state.fetchedContext.figma[]` |
28
+ | `generic-doc` | no automatic fetcher — surfaced as advisory; the agent uses WebFetch on demand when the URL turns out to be load-bearing | n/a |
29
+
30
+ ## Exit code handling
31
+
32
+ - `0` (success) → output appended to `state.fetchedContext.<type>[]` and injected into the prompt.
33
+ - `2` (blocked / missing token) → run the inline Token Save Flow per `setup.md`; on skip, mark the entry as `{status: "skipped", reason: "missing-token"}` and continue.
34
+ - `3` (network/auth) → mark as `{status: "failed", reason: "<exit-msg>"}`, continue.
35
+ - `4`/`5`/`6` (config errors) → mark as `{status: "failed", reason: "<exit-msg>"}`, continue and log a setup hint.
36
+
37
+ Failures are never fatal at Phase 1 — the agent still runs analysis, just without the referenced source.
38
+
39
+ ## Prompt injection shape
40
+
41
+ Added immediately after the knowledge-injection block, before codebase exploration:
42
+
43
+ ```
44
+ ## Referenced External Sources
45
+
46
+ (when fetchers populated `state.fetchedContext.<type>`)
47
+ ### <type> — <url>
48
+ <fetched content, scoped to relevance>
49
+
50
+ (when fetcher pending)
51
+ ### Pending references
52
+ - <type>: <url> — <one-line metadata>
53
+ ```
54
+
55
+ The agent treats fetched content as **ground truth** for the referenced source (no re-discovery, no contradiction). Pending references are advisories — the agent decides whether to fetch via WebFetch on demand when the task hinges on that source.
56
+
57
+ ## Log line shape
58
+
59
+ ```
60
+ → context injection: total=<N>, fetched=<n>, pending=<n>, by-type={swagger:F/P, confluence:F/P, ...}
61
+ ```
62
+
63
+ `F` = fetched, `P` = pending. Empty contextLinks → `total=0`, skip the section entirely.
@@ -0,0 +1,20 @@
1
+ # Feature: Plan Todos Iteration (Phase 3)
2
+
3
+ **Gated by `prefs.global.planTodos.enabled`** (default: `false`). When enabled and Phase 2 Step 4.5 emitted a `plan.todos[]` (conforming to `pipeline/schemas/plan-todos.schema.json`), Phase 3 iterates with the helper instead of walking `tasks[]` directly:
4
+
5
+ ```bash
6
+ while next=$(bash "$HOME/.claude/lib/plan-todos.sh" next "$TASK_ID"); [ -n "$next" ]; do
7
+ id=$(printf '%s' "$next" | cut -f1)
8
+ task=$(printf '%s' "$next" | cut -f2)
9
+ bash "$HOME/.claude/lib/plan-todos.sh" start "$TASK_ID" "$id"
10
+ # ... TDD cycle for "$task" ...
11
+ if build_passed; then
12
+ bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "${COMMIT_HASH} · tests:${TEST_COUNT}"
13
+ else
14
+ bash "$HOME/.claude/lib/plan-todos.sh" fail "$TASK_ID" "$id" "build failed after 3 retries"
15
+ break
16
+ fi
17
+ done
18
+ ```
19
+
20
+ When disabled, the loop still walks `tasks[]` from `planning-output` — `plan-todos.sh` adds visibility (status, notes, deps-respecting `next`) but the underlying TDD contract is unchanged.
@@ -0,0 +1,49 @@
1
+ # Feature: Prior Fix Detection (Phase 1 Step 0)
2
+
3
+ Before any analysis, check if this issue was already fixed by someone else.
4
+
5
+ ## Check order
6
+
7
+ 1. **Git commit search** — look for commits referencing this issue ID across all branches:
8
+
9
+ ```bash
10
+ git -C $PROJECT_ROOT log --all --oneline --grep="{jiraId}" --since="8 weeks ago"
11
+ ```
12
+
13
+ 2. **Affected file history** — after identifying key files (from issue description keywords), check recent changes:
14
+
15
+ ```bash
16
+ git -C $PROJECT_ROOT log --oneline --since="4 weeks ago" -- {affected_file_paths}
17
+ ```
18
+
19
+ 3. **Jira comments/status** — fetch latest comments to see if someone reported a fix:
20
+
21
+ ```bash
22
+ curl -s -H "Authorization: Bearer $JIRA_TOKEN" \
23
+ "$JIRA_BASE/issue/{jiraId}?fields=status,comment&expand=changelog" \
24
+ | jq '.fields.status.name, .fields.comment.comments[-3:][]?.body'
25
+ ```
26
+
27
+ ## If prior fix commit(s) found
28
+
29
+ ```
30
+ This issue appears to already have been addressed:
31
+ {hash} — {message} (by {author}, {date})
32
+
33
+ Options:
34
+ 1. Verify fix (check if cherry-pick/merge to base branch is needed)
35
+ 2. Stop pipeline (already resolved)
36
+ 3. Continue anyway (different fix needed)
37
+ ```
38
+
39
+ - Option 1 → Check if the commit exists on `baseBranch`. If not, suggest cherry-pick. Pipeline pauses.
40
+ - Option 2 → Cleanup and exit:
41
+ 1. Checkout back to base branch: `git -C $PROJECT_ROOT checkout {baseBranch}`
42
+ 2. Delete the created branch: `git -C $PROJECT_ROOT branch -D {branch}`
43
+ 3. If worktree was used: `git -C $PROJECT_ROOT worktree remove {worktreePath} --force`
44
+ 4. Set `agent-state.json` status to `"stopped_already_fixed"`. Log and exit.
45
+ - Option 3 → Continue to Step 1 as normal.
46
+
47
+ ## If no prior fix found
48
+
49
+ Continue to Step 1 (Knowledge Injection).
@@ -0,0 +1,30 @@
1
+ # Feature: Repo Map Injection (Phase 1 Step 2.5)
2
+
3
+ **Gated by `prefs.global.repoMap.enabled`** (default: `false`). Pattern source: <https://aider.chat/docs/repomap.html>.
4
+
5
+ Before launching Explore agents, run the deterministic repo map renderer and inject the result into each Explore prompt as `${REPO_MAP}`. This gives every explorer a token-budgeted overview of the most-referenced files in the repo — ranked via cross-file declaration references with TF-IDF-style credit splitting so universally-shared helper names (`pass`, `fail`, `init`) don't dominate.
6
+
7
+ ```bash
8
+ REPO_MAP=""
9
+ if [ "$(jq -r '.global.repoMap.enabled // false' "$PREFS")" = "true" ]; then
10
+ BUDGET=$(jq -r '.global.repoMap.tokenBudget // 1500' "$PREFS")
11
+ TOP=$(jq -r '.global.repoMap.topFiles // 25' "$PREFS")
12
+ INCLUDE=$(jq -r '.global.repoMap.include // empty' "$PREFS")
13
+ EXCLUDE=$(jq -r '.global.repoMap.exclude // empty' "$PREFS")
14
+ args=(--root "$WORKTREE" --budget "$BUDGET" --top "$TOP" --format md)
15
+ [ -n "$INCLUDE" ] && args+=(--include "$INCLUDE")
16
+ [ -n "$EXCLUDE" ] && args+=(--exclude "$EXCLUDE")
17
+ REPO_MAP=$(node pipeline/scripts/repo-map.mjs "${args[@]}" 2>/dev/null || echo "")
18
+ fi
19
+ ```
20
+
21
+ ## Properties
22
+
23
+ - **Deterministic** — same worktree state → same map. No embeddings, no network. Sub-second on monorepos.
24
+ - **Advisory** — Explore agents may ignore the map if they have stronger signals (Phase 0 knowledge cache, explicit user context). Never gates the pipeline.
25
+ - **Truncation-safe** — output is capped at `tokenBudget`; the script falls back to "no files extracted within budget" rather than over-spending.
26
+ - **Cost ledger** — emit `phase-1.repo_map_emitted bytes=$(wc -c <<<"$REPO_MAP") budget=$BUDGET` so Phase 7 cost summary can show the size injected.
27
+
28
+ ## When to enable
29
+
30
+ Large repos (>200 source files) where Explore otherwise spends multiple rounds finding the right starting files. Skip on small repos — the overhead exceeds the gain.
@@ -0,0 +1,24 @@
1
+ # Feature: Shadow-Git Checkpoints (Phase 3)
2
+
3
+ **Gated by `prefs.global.shadowGit.enabled`** (default: `false`). The orchestrator snapshots the worktree via `pipeline/lib/shadow-git.sh` so sub-phase rollback is possible without polluting the project's real `.git` history. Pattern source: Cline checkpoints (<https://docs.cline.bot/features/checkpoints>).
4
+
5
+ ```bash
6
+ # Phase 0 (one-time per task): initialize shadow repo + baseline snapshot.
7
+ bash "$HOME/.claude/lib/shadow-git.sh" init "$TASK_ID" "$WORKTREE"
8
+
9
+ # Per-step (mode: per-todo-step — default): snapshot AFTER each plan-todos
10
+ # step completes successfully, so restore lands on a known-good state.
11
+ bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "$notes"
12
+ bash "$HOME/.claude/lib/shadow-git.sh" snapshot "$TASK_ID" "$WORKTREE" "step $id: $task"
13
+
14
+ # Per-mutation (mode: per-tool-call): the orchestrator hooks into every Edit/
15
+ # Write/MultiEdit/Bash-mutation tool call. Higher fidelity, ~50ms × tool-call
16
+ # overhead — recommended only for security-critical paths or experimental
17
+ # refactors where the user values fine-grained rollback over speed.
18
+
19
+ # Rollback on demand (interactive helper):
20
+ bash "$HOME/.claude/lib/shadow-git.sh" list "$TASK_ID"
21
+ bash "$HOME/.claude/lib/shadow-git.sh" restore "$TASK_ID" "$WORKTREE" <sha> --files
22
+ ```
23
+
24
+ Shadow repo location: `~/.claude/state/shadow-git/<task-id>/.git/`. Storage cap: `prefs.shadowGit.pruneAfterDays` (default 14). `/multi-agent:purge` honors this prune; manual cleanup via `shadow-git.sh prune <task-id>`.
@@ -65,7 +65,7 @@ Used for: input parsing, branch naming, commit messages.
65
65
 
66
66
  **Save rule**: After EVERY selection, append chosen value to relevant array (dedup, most recent first, max 10). Save prefs after Phase 0 and Phase 7.
67
67
 
68
- **Recents update map** (which selection writes to which prefs path):
68
+ **v2.1.0+ Recents update map** (which selection writes to which prefs path):
69
69
 
70
70
  | Selection event | Prefs path | Shape | Cap |
71
71
  |---|---|---|---|
@@ -208,7 +208,7 @@ Scan `$HOME` (maxdepth 2) for project markers (`.xcodeproj`, `Package.swift`, `b
208
208
 
209
209
  **Otherwise**: Present discovered projects as numbered list (standard UX pattern). Stack tag (`[iOS]`, `[Android]`, etc.) auto-detected from project files. Set `PROJECT_ROOT` to selected directory — all subsequent commands use `git -C $PROJECT_ROOT`. Save to `prefs.global.recentProjects` (dedup, max 10). If bare `#N` was given, now fetch GitHub issue from project's remote.
210
210
 
211
- **Multi-Repo Mode** (gated by `prefs.global.settings.multiRepoEnabled`):
211
+ **v2.1.0+ Multi-Repo Mode** (gated by `prefs.global.settings.multiRepoEnabled`):
212
212
 
213
213
  - The picker accepts space-separated numbers (`1 3 4`) for multi-select.
214
214
  - `prefs.global.recentGroups[]` (set in setup.md Step 6) surfaces at the top: pressing the group's number selects all its repos in one keystroke.
@@ -354,7 +354,7 @@ git -C $PROJECT_ROOT config user.email "{identity.email}"
354
354
 
355
355
  **If normal mode** (worktree — default): 2. Worktree path: Jira → `.worktrees/{jiraId}/`, GitHub → `.worktrees/GH{issueNo}/`, free-text → `.worktrees/task-{shortId}/` 3. `git -C $PROJECT_ROOT worktree add {path} -b {branch} origin/{baseBranch}` (if exists: enter, pull) 4. Set identity: `git -C {worktree-path} config user.name/email` 5. Create log dir + `agent-log.md` + `agent-state.json`:
356
356
 
357
- **Multi-Repo Worktree Setup**:
357
+ **v2.1.0+ Multi-Repo Worktree Setup**:
358
358
 
359
359
  When `state.projects[].length > 1`, repeat steps 2-4 **serially per repo** (worktrees are cheap; serial keeps git index sane and surfaces collisions one at a time):
360
360
 
@@ -7,50 +7,7 @@ Progress emission per `refs/progress-contract.md` — lines for each Explore dis
7
7
 
8
8
  #### Step 0 — Prior Fix Detection
9
9
 
10
- Before any analysis, check if this issue was already fixed by someone else.
11
-
12
- **Check order:**
13
-
14
- 1. **Git commit search** — look for commits referencing this issue ID across all branches:
15
-
16
- ```bash
17
- git -C $PROJECT_ROOT log --all --oneline --grep="{jiraId}" --since="8 weeks ago"
18
- ```
19
-
20
- 2. **Affected file history** — after identifying key files (from issue description keywords), check recent changes:
21
-
22
- ```bash
23
- git -C $PROJECT_ROOT log --oneline --since="4 weeks ago" -- {affected_file_paths}
24
- ```
25
-
26
- 3. **Jira comments/status** — fetch latest comments to see if someone reported a fix:
27
- ```bash
28
- curl -s -H "Authorization: Bearer $JIRA_TOKEN" \
29
- "$JIRA_BASE/issue/{jiraId}?fields=status,comment&expand=changelog" \
30
- | jq '.fields.status.name, .fields.comment.comments[-3:][]?.body'
31
- ```
32
-
33
- **If prior fix commit(s) found:**
34
-
35
- ```
36
- This issue appears to already have been addressed:
37
- {hash} — {message} (by {author}, {date})
38
-
39
- Options:
40
- 1. Verify fix (check if cherry-pick/merge to base branch is needed)
41
- 2. Stop pipeline (already resolved)
42
- 3. Continue anyway (different fix needed)
43
- ```
44
-
45
- - Option 1 → Check if the commit exists on `baseBranch`. If not, suggest cherry-pick. Pipeline pauses.
46
- - Option 2 → Cleanup and exit:
47
- 1. Checkout back to base branch: `git -C $PROJECT_ROOT checkout {baseBranch}`
48
- 2. Delete the created branch: `git -C $PROJECT_ROOT branch -D {branch}`
49
- 3. If worktree was used: `git -C $PROJECT_ROOT worktree remove {worktreePath} --force`
50
- 4. Set `agent-state.json` status to `"stopped_already_fixed"`. Log and exit.
51
- - Option 3 → Continue to Step 1 as normal.
52
-
53
- **If no prior fix found** → continue to Step 1.
10
+ Before any analysis, check if this issue was already fixed by someone else. Three signals (git commit grep on issue ID over 8 weeks, recent file-path history over 4 weeks, Jira/issue comments). On hit → prompt: Verify (cherry-pick path) / Stop (cleanup + `stopped_already_fixed` state) / Continue. Miss → Step 1. Full check commands, prompt block, and cleanup commands: `refs/features/prior-fix-detection.md`.
54
11
 
55
12
  #### Step 1 — Knowledge Injection (cached context)
56
13
 
@@ -90,63 +47,14 @@ Exit 0 with empty output = pref off or no memory on disk — skip. Otherwise the
90
47
 
91
48
  #### Step 1.5 — External Context Injection (`state.contextLinks[]`)
92
49
 
93
- Phase 0 Step 1b catalogued every typed external link from the task description into `state.contextLinks[]`. Phase 1 now dispatches each entry to its matching fetcher and prepends the result to the analysis prompt under a **Referenced External Sources** section, so the agent doesn't re-discover what the ticket already pointed at.
94
-
95
- ```bash
96
- jq -c '.contextLinks // []' "$STATE_FILE" \
97
- | python3 -c '
98
- import json, sys, os
99
- links = json.loads(sys.stdin.read() or "[]")
100
- by_type = {}
101
- for l in links:
102
- by_type.setdefault(l["type"], []).append(l)
103
- print(json.dumps(by_type))
104
- ' > /tmp/context-by-type-${TASK_ID}.json
105
- ```
106
-
107
- Dispatch table — one fetcher per type. Each fetcher emits a normalized JSON view that the analysis prompt consumes as ground truth for the referenced source.
108
-
109
- | Type | Fetcher | Output stored in |
110
- |---|---|---|
111
- | `crashlytics` | `~/.claude/lib/fetch-crashlytics.sh <url>` (already invoked in Phase 0 Step 1b.1 for the legacy `state.crashContext` field; Phase 1 reads that field directly) | `state.crashContext` |
112
- | `fortify` | `~/.claude/lib/fetch-fortify.sh <url>` (already invoked in Phase 0 Step 1b.2 for the legacy `state.fortifyFinding` field; Phase 1 reads that field, Phase 4 reads the full payload for the security gate) | `state.fortifyFinding` |
113
- | `swagger` | `~/.claude/lib/fetch-swagger.sh <url>` → endpoints[], request/response examples | `state.fetchedContext.swagger[]` |
114
- | `confluence` | `~/.claude/lib/fetch-confluence.sh <url>` → page body, code blocks, extracted API contracts | `state.fetchedContext.confluence[]` |
115
- | `figma` | `~/.claude/lib/fetch-figma.sh <url>` (via existing MCP / REST fallback path) when the task is a component; otherwise advisory only | `state.fetchedContext.figma[]` |
116
- | `generic-doc` | no automatic fetcher — surfaced as advisory; the agent uses WebFetch on demand when the URL turns out to be load-bearing | n/a |
50
+ Phase 0 Step 1b catalogued every typed external link from the task description into `state.contextLinks[]`. Phase 1 dispatches each entry to its matching fetcher (crashlytics, fortify, swagger, confluence, figma, generic-doc) and prepends results under a **Referenced External Sources** section in the analysis prompt — so the agent doesn't re-discover what the ticket already pointed at. Failures never fatal; pending refs are advisories. Full dispatch table, exit-code handling, prompt injection shape, log line shape: `refs/features/external-context-injection.md`.
117
51
 
118
- Each fetcher exit code is treated as follows:
119
- - `0` (success) → output appended to `state.fetchedContext.<type>[]` and injected into the prompt.
120
- - `2` (blocked / missing token) → run the inline Token Save Flow per `setup.md`; on skip, mark the entry as `{status: "skipped", reason: "missing-token"}` and continue.
121
- - `3` (network/auth) → mark as `{status: "failed", reason: "<exit-msg>"}`, continue.
122
- - `4`/`5`/`6` (config errors) → mark as `{status: "failed", reason: "<exit-msg>"}`, continue and log a setup hint.
123
-
124
- Failures are never fatal at Phase 1 — the agent still runs analysis, just without the referenced source.
125
-
126
- **Prompt injection shape** (added immediately after the knowledge-injection block, before codebase exploration):
127
-
128
- ```
129
- ## Referenced External Sources
130
-
131
- (when fetchers populated `state.fetchedContext.<type>`)
132
- ### <type> — <url>
133
- <fetched content, scoped to relevance>
134
-
135
- (when fetcher pending)
136
- ### Pending references
137
- - <type>: <url> — <one-line metadata>
138
- ```
139
-
140
- The agent treats fetched content as **ground truth** for the referenced source (no re-discovery, no contradiction). Pending references are advisories — the agent decides whether to fetch via WebFetch on demand when the task hinges on that source.
141
-
142
- **Log line shape**:
52
+ **Log line shape** (progress contract):
143
53
 
144
54
  ```
145
55
  → context injection: total=<N>, fetched=<n>, pending=<n>, by-type={swagger:F/P, confluence:F/P, ...}
146
56
  ```
147
57
 
148
- `F` = fetched, `P` = pending. Empty contextLinks → `total=0`, skip the section entirely.
149
-
150
58
  #### Step 2 — Stack Detection
151
59
 
152
60
  Detect the project's tech stack to load appropriate skills and tooling throughout the pipeline. Auto-detect by scanning `$PROJECT_ROOT` (maxdepth 2) for project markers:
@@ -171,32 +79,7 @@ This informs:
171
79
 
172
80
  #### Step 2.5 — Repo Map Injection (advisory, opt-in)
173
81
 
174
- **Gated by `prefs.global.repoMap.enabled`** (default: `false`). Pattern source: <https://aider.chat/docs/repomap.html>.
175
-
176
- Before launching Explore agents, run the deterministic repo map renderer and inject the result into each Explore prompt as `${REPO_MAP}`. This gives every explorer a token-budgeted overview of the most-referenced files in the repo — ranked via cross-file declaration references with TF-IDF-style credit splitting so universally-shared helper names (`pass`, `fail`, `init`) don't dominate.
177
-
178
- ```bash
179
- REPO_MAP=""
180
- if [ "$(jq -r '.global.repoMap.enabled // false' "$PREFS")" = "true" ]; then
181
- BUDGET=$(jq -r '.global.repoMap.tokenBudget // 1500' "$PREFS")
182
- TOP=$(jq -r '.global.repoMap.topFiles // 25' "$PREFS")
183
- INCLUDE=$(jq -r '.global.repoMap.include // empty' "$PREFS")
184
- EXCLUDE=$(jq -r '.global.repoMap.exclude // empty' "$PREFS")
185
- args=(--root "$WORKTREE" --budget "$BUDGET" --top "$TOP" --format md)
186
- [ -n "$INCLUDE" ] && args+=(--include "$INCLUDE")
187
- [ -n "$EXCLUDE" ] && args+=(--exclude "$EXCLUDE")
188
- REPO_MAP=$(node pipeline/scripts/repo-map.mjs "${args[@]}" 2>/dev/null || echo "")
189
- fi
190
- ```
191
-
192
- **Properties:**
193
-
194
- - **Deterministic** — same worktree state → same map. No embeddings, no network. Sub-second on monorepos.
195
- - **Advisory** — Explore agents may ignore the map if they have stronger signals (Phase 0 knowledge cache, explicit user context). Never gates the pipeline.
196
- - **Truncation-safe** — output is capped at `tokenBudget`; the script falls back to "no files extracted within budget" rather than over-spending.
197
- - **Cost ledger** — emit `phase-1.repo_map_emitted bytes=$(wc -c <<<"$REPO_MAP") budget=$BUDGET` so Phase 7 cost summary can show the size injected.
198
-
199
- **When to enable:** large repos (>200 source files) where Explore otherwise spends multiple rounds finding the right starting files. Skip on small repos — the overhead exceeds the gain.
82
+ Gated by `prefs.global.repoMap.enabled` (default: `false`). When enabled, runs `pipeline/scripts/repo-map.mjs` and injects the budgeted result into each Explore prompt as `${REPO_MAP}`. Aider-style: deterministic, no embeddings, sub-second, advisory only. Full wiring (helper invocation, properties, when-to-enable): `refs/features/repo-map.md`.
200
83
 
201
84
  #### Step 3 — Codebase Exploration
202
85
 
@@ -10,47 +10,9 @@
10
10
 
11
11
  Phase 3 consumes the Phase 2 output object conforming to `pipeline/schemas/planning-output.schema.json` — the task graph (`tasks[]` with `id`, `subject`, `targetFiles`, `complexity`, `blockedBy`) plus the architecture review notes. Tasks execute in dependency order; the schema's `blockedBy` field drives the ready-task picker. In `--dev` mode (no Phase 2), Sonnet/Opus generates the equivalent task list inline before entering the loop below.
12
12
 
13
- **Plan Todo iteration (opt-in)**: when `prefs.global.planTodos.enabled` is true and Phase 2 Step 4.5 emitted a `plan.todos[]` (conforming to `pipeline/schemas/plan-todos.schema.json`), Phase 3 iterates with the helper instead of walking `tasks[]` directly:
13
+ **Plan Todo iteration (opt-in)**: gated by `prefs.global.planTodos.enabled` (default: `false`). When enabled and Phase 2 Step 4.5 emitted a `plan.todos[]`, Phase 3 iterates via `pipeline/lib/plan-todos.sh next/start/complete/fail` instead of walking `tasks[]` directly. When disabled, the loop walks `tasks[]` from `planning-output` — TDD contract is unchanged. Full helper loop + state semantics: `refs/features/plan-todos.md`.
14
14
 
15
- ```bash
16
- while next=$(bash "$HOME/.claude/lib/plan-todos.sh" next "$TASK_ID"); [ -n "$next" ]; do
17
- id=$(printf '%s' "$next" | cut -f1)
18
- task=$(printf '%s' "$next" | cut -f2)
19
- bash "$HOME/.claude/lib/plan-todos.sh" start "$TASK_ID" "$id"
20
- # ... TDD cycle for "$task" ...
21
- if build_passed; then
22
- bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "${COMMIT_HASH} · tests:${TEST_COUNT}"
23
- else
24
- bash "$HOME/.claude/lib/plan-todos.sh" fail "$TASK_ID" "$id" "build failed after 3 retries"
25
- break
26
- fi
27
- done
28
- ```
29
-
30
- When disabled, the loop still walks `tasks[]` from `planning-output` — `plan-todos.sh` adds visibility (status, notes, deps-respecting `next`) but the underlying TDD contract is unchanged.
31
-
32
- **Shadow-Git checkpoints (opt-in)**: when `prefs.global.shadowGit.enabled` is true, the orchestrator snapshots the worktree via `pipeline/lib/shadow-git.sh` so sub-phase rollback is possible without polluting the project's real `.git` history. Pattern source: Cline checkpoints (<https://docs.cline.bot/features/checkpoints>).
33
-
34
- ```bash
35
- # Phase 0 (one-time per task): initialize shadow repo + baseline snapshot.
36
- bash "$HOME/.claude/lib/shadow-git.sh" init "$TASK_ID" "$WORKTREE"
37
-
38
- # Per-step (mode: per-todo-step — default): snapshot AFTER each plan-todos
39
- # step completes successfully, so restore lands on a known-good state.
40
- bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "$notes"
41
- bash "$HOME/.claude/lib/shadow-git.sh" snapshot "$TASK_ID" "$WORKTREE" "step $id: $task"
42
-
43
- # Per-mutation (mode: per-tool-call): the orchestrator hooks into every Edit/
44
- # Write/MultiEdit/Bash-mutation tool call. Higher fidelity, ~50ms × tool-call
45
- # overhead — recommended only for security-critical paths or experimental
46
- # refactors where the user values fine-grained rollback over speed.
47
-
48
- # Rollback on demand (interactive helper):
49
- bash "$HOME/.claude/lib/shadow-git.sh" list "$TASK_ID"
50
- bash "$HOME/.claude/lib/shadow-git.sh" restore "$TASK_ID" "$WORKTREE" <sha> --files
51
- ```
52
-
53
- Shadow repo location: `~/.claude/state/shadow-git/<task-id>/.git/`. Storage cap: `prefs.shadowGit.pruneAfterDays` (default 14). `/multi-agent:purge` honors this prune; manual cleanup via `shadow-git.sh prune <task-id>`.
15
+ **Shadow-Git checkpoints (opt-in)**: gated by `prefs.global.shadowGit.enabled` (default: `false`). When enabled, the orchestrator snapshots the worktree via `pipeline/lib/shadow-git.sh` so sub-phase rollback is possible without touching the project's real `.git` history. Cline-style. Lifecycle: `shadow-git.sh init` (Phase 0 baseline), `shadow-git.sh snapshot` (per step after `plan-todos complete`), `shadow-git.sh restore <sha> --files` (rollback). Modes: `per-todo-step` (default) or `per-tool-call`. Full wiring + storage cap: `refs/features/shadow-git.md`.
54
16
 
55
17
  #### Component tasks — delegated dispatch (taskType === "component")
56
18
 
@@ -239,40 +201,7 @@ release_build_lock() { rm -rf "$BUILD_LOCK"; }
239
201
 
240
202
  #### Step 3.5 — Dev Critic (Evaluator-Optimizer, opt-in)
241
203
 
242
- **Pattern**: Anthropic's evaluator-optimizer from "Building Effective Agents" (Dec 2024). Generator (Phase 3 Dev) and critic (`agents/dev-critic.md`) loop on deterministic criteria already written in `rules/*.md` before any Phase 4 reviewer call.
243
-
244
- **Gated by `prefs.global.devCritic.enabled`** (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4:
245
-
246
- 1. Dispatch `dev-critic` sub-agent (Sonnet by default, tools: `Read, Grep, Glob, Bash`).
247
- 2. Critic runs 4 mandatory deterministic gates (build / lint / test / secrets) — failure of any → `pass: false` with `blocking` finding.
248
- 3. If gates green, critic walks the platform checklist (iOS 13-item / Android Kotlin / Backend generic — selected by Phase 1 `detectedStack`).
249
- 4. Returns schema-validated JSON (`pipeline/schemas/dev-critic-output.schema.json`): `{pass, iteration, gates, findings[], escalate}`.
250
-
251
- **Loop cap (STRICT)**: max **2** iterations.
252
-
253
- | Round | Behavior |
254
- |---|---|
255
- | 1 | Full critic pass — all gates + full checklist |
256
- | 2 | Re-check only round-1 failures. No new findings allowed (scope creep prevention) |
257
- | 3+ | NOT ALLOWED. Critic returns `escalate: true`; orchestrator pauses (interactive) or proceeds to Phase 4 with logged failure (autopilot) |
258
-
259
- **Action by severity:**
260
-
261
- - `blocking` → generator MUST fix; another Dev iteration before re-critic
262
- - `important` → generator SHOULD fix; if skipped, pass through to Phase 4 (legitimate reviewer ground)
263
- - `suggestion` → generator's judgement; never blocks round 2
264
-
265
- **Telemetry**: each critic call emits `dev_critic.call` with `iteration`, `pass`, `gates_failed`, `blocking`, `important`, `duration_ms`, `tokens_in/out`. Phase 7 cost rollup lists these as `phase 3.5` line items so the net saving (Phase 4 reviewer/triage calls avoided) is measurable.
266
-
267
- **Off by default reason**: introduces ~1× Sonnet call per Dev iteration. On simple bug fixes the cost outweighs the benefit (Phase 4 would have caught the same thing for similar cost). Recommended on:
268
-
269
- - Feature work (≥200 LOC diff)
270
- - Security-touching paths (auth, keychain, network)
271
- - Multi-file refactors where rule violations compound
272
-
273
- **Reference**: see `pipeline/agents/dev-critic.md` for the full agent specification (mandatory gates, checklist enumeration, output schema, severity semantics).
274
-
275
- **Why this fits orchestrator-workers + evaluator-optimizer hybrid**: Phase 4 is parallelization-with-voting — good for *adversarial* perspectives (security, architecture). Phase 3.5 is evaluator-optimizer — good for *deterministic* criteria (build, tests, checklists). Sending failing builds into Phase 4 wastes 2–3 reviewer calls + Opus triage; Phase 3.5 absorbs that cost at one Sonnet call.
204
+ Gated by `prefs.global.devCritic.enabled` (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4: dispatch `dev-critic` sub-agent (Sonnet), run 4 deterministic gates (build / lint / test / secrets), then platform checklist. STRICT loop cap **max 2 iterations** (round 2 re-checks round-1 failures only); round 3+ returns `escalate: true`. Severity routing: `blocking` generator must fix; `important` → SHOULD fix or pass through to Phase 4; `suggestion` → generator's judgement. Full agent contract, schema, telemetry, when-to-enable: `refs/features/dev-critic.md` + `pipeline/agents/dev-critic.md`.
276
205
 
277
206
  ---
278
207
 
@@ -302,7 +231,7 @@ When `agent-state.json.onlyDevelop === true`, Phase 3 runs self-contained with *
302
231
 
303
232
  ---
304
233
 
305
- #### Multi-Repo Mode
234
+ #### v2.1.0+ Multi-Repo Mode
306
235
 
307
236
  Active when `state.projects[].length > 1` (set by Phase 0 multi-select). Single-repo flow above is preserved verbatim — this section adds the deltas.
308
237
 
@@ -216,7 +216,7 @@ Before creating PR, ask: "DRAFT or READY? [1/2], default 1" — DRAFT blocks mer
216
216
 
217
217
  ---
218
218
 
219
- #### Multi-Repo Mode
219
+ #### v2.1.0+ Multi-Repo Mode
220
220
 
221
221
  Active when `state.projects[].length > 1`. The single-repo flow above is preserved verbatim — multi-repo is an extension, not a replacement.
222
222
 
@@ -259,19 +259,23 @@ pbcopy < /dev/null
259
259
 
260
260
  (The shell driver auto-delegates to `~/.claude/scripts/keychain.py` on macOS / Linux, which writes both `-l` (label) and `-s` (service) attributes so callers using either convention find the same entry.)
261
261
 
262
- For JSON file (Firebase):
262
+ For JSON file (Firebase) — same clipboard flow, JSON content never lands in argv or shell history:
263
263
  ```
264
- Download the JSON file, then press Enter.
265
- (File will be base64-encoded and saved to Keychain automatically)
264
+ Open the Service Account JSON, copy its FULL contents to your clipboard,
265
+ then press Enter.
266
+ (Contents will be base64-encoded and saved to Keychain automatically)
266
267
 
267
- Path to JSON file [~/Downloads/firebase-sa.json]: ___
268
+ Press Enter when ready...
268
269
  ```
269
270
 
270
271
  Pipeline runs silently:
271
272
  ```bash
272
- ~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(base64 < <PATH>)"
273
+ pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(cat)"
274
+ pbcopy < /dev/null # clear clipboard
273
275
  ```
274
276
 
277
+ Linux substitutes: `xclip -selection clipboard -o` or `wl-paste` for `pbpaste`; matching clear command for `pbcopy < /dev/null`. The shell driver detects platform and routes accordingly (see `~/.claude/lib/credential-store.sh`).
278
+
275
279
  For GitHub (special case):
276
280
  ```
277
281
  ! gh auth login
@@ -699,7 +703,11 @@ pbcopy < /dev/null # clear clipboard
699
703
  # Plain text (direct value)
700
704
  ~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "<VALUE>"
701
705
 
702
- # JSON file (base64 encode)
706
+ # JSON file from clipboard (preferred — no file path leaks into history)
707
+ pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(cat)"
708
+ pbcopy < /dev/null
709
+
710
+ # JSON file from disk (fallback when clipboard backend unavailable)
703
711
  ~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(base64 < /path/to/file.json)"
704
712
 
705
713
  # Read