@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.
- package/README.md +1 -1
- package/package.json +4 -1
- package/pipeline/commands/multi-agent/refs/features/dev-critic.md +44 -0
- package/pipeline/commands/multi-agent/refs/features/external-context-injection.md +63 -0
- package/pipeline/commands/multi-agent/refs/features/plan-todos.md +20 -0
- package/pipeline/commands/multi-agent/refs/features/prior-fix-detection.md +49 -0
- package/pipeline/commands/multi-agent/refs/features/repo-map.md +30 -0
- package/pipeline/commands/multi-agent/refs/features/shadow-git.md +24 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-0-init.md +3 -3
- package/pipeline/commands/multi-agent/refs/phases/phase-1-analysis.md +4 -121
- package/pipeline/commands/multi-agent/refs/phases/phase-3-dev.md +4 -75
- package/pipeline/commands/multi-agent/refs/phases/phase-6-commit.md +1 -1
- package/pipeline/commands/multi-agent/setup.md +14 -6
- package/pipeline/commands/multi-agent/sync.md +25 -24
- package/pipeline/scripts/fixtures/install-layout.tsv +11 -11
- package/pipeline/scripts/smoke-issue-comment-template.sh +1 -1
- package/pipeline/scripts/smoke-sync-adapters.sh +113 -0
- package/pipeline/scripts/smoke-sync-delegation.sh +1 -1
- package/pipeline/scripts/smoke-url-enrichment.sh +1 -1
- package/pipeline/scripts/sync-adapters.mjs +156 -0
- package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +1 -1
- package/pipeline/skills/figma-common/figma-issue/SKILL.md +5 -5
- package/pipeline/skills/figma-common/figma-setup/SKILL.md +17 -17
- package/pipeline/skills/figma-common/figma-validate/SKILL.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +5 -5
- package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +1 -1
- 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 |
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)**:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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>" "$(
|
|
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 (
|
|
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
|