@mmerterden/multi-agent-pipeline 12.4.0 → 12.6.0

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 (70) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +1 -1
  3. package/docs/features.md +20 -0
  4. package/index.js +7 -1
  5. package/install/_dev-only-files.mjs +1 -0
  6. package/package.json +4 -3
  7. package/pipeline/agents/security-auditor.md +1 -1
  8. package/pipeline/commands/archive-guard.md +5 -5
  9. package/pipeline/commands/multi-agent/SKILL.md +2 -0
  10. package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
  11. package/pipeline/commands/multi-agent/help/SKILL.md +45 -5
  12. package/pipeline/commands/multi-agent/refactor/SKILL.md +92 -12
  13. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/sync/SKILL.md +119 -12
  15. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  16. package/pipeline/commands/sim-test.md +5 -5
  17. package/pipeline/lib/credential-store.sh +32 -0
  18. package/pipeline/lib/extract-conventions.sh +1 -0
  19. package/pipeline/lib/repo-cache.sh +1 -0
  20. package/pipeline/lib/shadow-git.sh +8 -0
  21. package/pipeline/multi-agent-refs/cross-cli-contract.md +3 -3
  22. package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -1
  23. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
  24. package/pipeline/multi-agent-refs/phases/phase-4-review.md +3 -2
  25. package/pipeline/multi-agent-refs/phases/phase-5-test.md +14 -4
  26. package/pipeline/multi-agent-refs/phases.md +2 -0
  27. package/pipeline/multi-agent-refs/prompt-assembly.md +31 -0
  28. package/pipeline/preferences-template.json +18 -1
  29. package/pipeline/schemas/agent-state.schema.json +90 -0
  30. package/pipeline/schemas/design-check-config.schema.json +162 -0
  31. package/pipeline/schemas/learnings-ledger.schema.json +4 -0
  32. package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
  33. package/pipeline/schemas/prefs.schema.json +161 -5
  34. package/pipeline/schemas/token-budget.json +2 -2
  35. package/pipeline/schemas/triage-corpus.schema.json +5 -1
  36. package/pipeline/scripts/README.md +7 -5
  37. package/pipeline/scripts/classify-plan-safety.mjs +8 -3
  38. package/pipeline/scripts/cost-budget-check.mjs +9 -5
  39. package/pipeline/scripts/eval-mine-corpus.mjs +19 -5
  40. package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
  41. package/pipeline/scripts/gc-worktrees.sh +23 -1
  42. package/pipeline/scripts/learning-curve.mjs +167 -0
  43. package/pipeline/scripts/learnings-ledger.mjs +27 -2
  44. package/pipeline/scripts/lint-mcp-refs.mjs +207 -0
  45. package/pipeline/scripts/memory-load.sh +5 -1
  46. package/pipeline/scripts/render-work-summary.sh +4 -1
  47. package/pipeline/scripts/repo-map.mjs +1 -1
  48. package/pipeline/scripts/smoke-command-inventory.sh +81 -0
  49. package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
  50. package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
  51. package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
  52. package/pipeline/scripts/smoke-generate-issue.sh +6 -5
  53. package/pipeline/scripts/smoke-learning-curve.sh +61 -0
  54. package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
  55. package/pipeline/scripts/smoke-review-readiness.sh +3 -2
  56. package/pipeline/scripts/smoke-schema-validation.sh +19 -5
  57. package/pipeline/scripts/smoke-shadow-git.sh +4 -2
  58. package/pipeline/scripts/triage-memory.mjs +66 -10
  59. package/pipeline/scripts/uninstall.mjs +1 -1
  60. package/pipeline/skills/.skill-manifest.json +24 -8
  61. package/pipeline/skills/.skills-index.json +39 -3
  62. package/pipeline/skills/shared/README.md +10 -6
  63. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
  64. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
  65. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +22 -0
  66. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +67 -11
  67. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
  68. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
  69. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
  70. package/pipeline/skills/skills-index.md +8 -4
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: "One-shot sync of the entire multi-agent ecosystem: Claude Code, Copilot CLI, pipeline repo, and website."
3
- description-tr: "Tüm multi-agent ekosisteminin tek atımlık senkronu: Claude Code, Copilot CLI, pipeline repo ve website."
4
- argument-hint: "[release] [--platform=<macos|linux|windows>]"
2
+ description: "One-shot sync of the entire multi-agent ecosystem: Claude Code, Copilot CLI, pipeline repo, website, and the dev-toolkit MCP server."
3
+ description-tr: "Tüm multi-agent ekosisteminin tek atımlık senkronu: Claude Code, Copilot CLI, pipeline repo, website ve dev-toolkit MCP sunucusu."
4
+ argument-hint: "[release] [dev-toolkit] [--platform=<macos|linux|windows>]"
5
5
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, WebFetch
6
6
  ---
7
7
 
@@ -15,8 +15,9 @@ When invoked, it synchronizes all targets in order. It detects what changed, upd
15
15
 
16
16
  | Arg | Meaning |
17
17
  |---|---|
18
- | (none) | Full ecosystem sync: Claude Code, Copilot CLI, pipeline repo, website. |
19
- | `release` | Full sync + version bump + tag + npm publish + website deploy |
18
+ | (none) | Full ecosystem sync: Claude Code, Copilot CLI, pipeline repo, website, dev-toolkit MCP server. |
19
+ | `release` | Full sync + version bump + tag + npm publish + website deploy (includes the dev-toolkit ship path) |
20
+ | `dev-toolkit` | Run Step 3d only: gate, commit and publish the companion MCP server |
20
21
  | `--platform=<macos\|linux\|windows>` | Override automatic platform detection. For CI / cross-platform smoke. |
21
22
  | `"change description"` | Apply the description to every target at once |
22
23
 
@@ -28,6 +29,7 @@ When invoked, it synchronizes all targets in order. It detects what changed, upd
28
29
  | 2 | Copilot CLI | `~/.copilot/copilot-instructions.md` + `~/.copilot/skills/` | <- from Claude |
29
30
  | 3 | multi-agent-pipeline repo | `~/multi-agent-pipeline/pipeline/` | <- from Claude (genericized) |
30
31
  | 4 | Website | `{owner}/{website-host}` | <- version + features |
32
+ | 5 | dev-toolkit MCP server | resolved from `prefs.global.devToolkit` or the `mcpServers` registration | own repo: gate, commit, publish |
31
33
 
32
34
  ## Platform Support (macOS / Linux / Windows)
33
35
 
@@ -56,13 +58,14 @@ Run every step automatically:
56
58
  ```
57
59
  Step 1: PLATFORM Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
58
60
  Step 1.5: DETECT Compare timestamps, find stale targets
59
- Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 41 sub-command skills)
61
+ Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 42 sub-command skills)
60
62
  Step 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
61
63
  Step 3c: PLUGINS pipeline shared/external -> multi-agent-plugins marketplace (rebuild knowledge/,
62
64
  bump changed plugins' patch version, commit + push the plugins repo)
65
+ Step 3d: DEV-TOOLKIT companion MCP server -> detect movement, run the ship gates, commit + publish
63
66
  Step 4: WEBSITE Version + phase/model counts -> {website-host} (i18n + projects.ts)
64
67
  Step 5: Commit Commit + push all changed repos
65
- Step 6: Report Summary: synced targets, platform, changed files, deploy status
68
+ Step 6: Report Summary: synced targets, platform, changed files, deploy status, dev-toolkit version
66
69
  ```
67
70
 
68
71
  If nothing is stale → report "All targets up to date" and stop.
@@ -73,6 +76,7 @@ If nothing is stale → report "All targets up to date" and stop.
73
76
  |-------|----------|
74
77
  | (no args) | Full ecosystem sync (default) |
75
78
  | `release` | Full sync + version bump + tag + npm publish + website deploy |
79
+ | `dev-toolkit` | Run Step 3d only: gate, commit and publish the companion MCP server |
76
80
  | `"change description"` | Apply the description to EVERY target at once |
77
81
 
78
82
  ---
@@ -207,6 +211,107 @@ Never author skills directly in the plugins repo's `knowledge/` — they will be
207
211
 
208
212
  ---
209
213
 
214
+ ## Dev-Toolkit Sync (Step 3d)
215
+
216
+ The companion MCP server (`dev-toolkit-mcp`) is its own repo with its own registry. Pipeline skills call its tools and several declare a minimum version, so when it moves it has to ship - otherwise the pipeline documents a tool no consumer has.
217
+
218
+ **Resolution**: identical to `/multi-agent:refactor` Step 0c - `prefs.global.devToolkit` first, then auto-detect from the `mcpServers` registration (`~/.claude.json`, `~/.claude/settings.json`), then skip. `enabled: false` skips.
219
+
220
+ **Detect movement**:
221
+
222
+ ```bash
223
+ DT="<resolved localPath>"; cd "$DT"
224
+ git fetch origin --quiet 2>/dev/null || true
225
+ DIRTY=$(git status --porcelain)
226
+ AHEAD=$(git log --oneline @{u}..HEAD 2>/dev/null | wc -l | tr -d ' ')
227
+ VER=$(node -p "require('./package.json').version")
228
+ TAGGED=$(git tag --list "v$VER")
229
+ ```
230
+
231
+ Moved = `DIRTY` non-empty, or `AHEAD` > 0, or no tag for `VER`. Nothing moved -> report `dev-toolkit: up to date (v$VER)` and continue with the sync.
232
+
233
+ **Ship gates** - all must pass before commit or publish. A gate failure aborts THIS step only, reported with file + line; the rest of the sync continues.
234
+
235
+ **Prefer the repo's own gate script.** When the toolkit ships one (`npm run gates`, or `scripts/gates.sh`), run THAT and skip the inline list below - the repo owns the definition, this skill only owns the requirement. The inline gates are the fallback for a repo that has none, and the checklist the gate script itself must cover.
236
+
237
+ ```bash
238
+ cd "$DT"
239
+ if node -e "process.exit(require('./package.json').scripts?.gates ? 0 : 1)" 2>/dev/null; then
240
+ npm run gates || echo "ABORT: dev-toolkit gates failed"
241
+ elif [ -f scripts/gates.sh ]; then
242
+ bash scripts/gates.sh || echo "ABORT: dev-toolkit gates failed"
243
+ else
244
+ : # run the inline gates below
245
+ fi
246
+ ```
247
+
248
+ 1. **Syntax**: `node --check index.js` plus every `tools/**/*.js`.
249
+ 2. **Boot + inventory**: handshake the server over stdio and count the registered tools.
250
+
251
+ ```bash
252
+ TOOLS=$(printf '%s\n' \
253
+ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"sync","version":"1"}}}' \
254
+ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
255
+ '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
256
+ | node index.js 2>/dev/null \
257
+ | node -e 'let b="";process.stdin.on("data",d=>b+=d).on("end",()=>{const m=b.split("\n").filter(Boolean).map(l=>{try{return JSON.parse(l)}catch{return null}}).find(x=>x&&x.id===2&&x.result&&x.result.tools);console.log(m?m.result.tools.length:0)})')
258
+ [ "${TOOLS:-0}" -gt 0 ] || echo "ABORT: the toolkit served 0 tools"
259
+ ```
260
+
261
+ 3. **Advertised-count match**: `$TOOLS` must equal every advertised count (`package.json` `description`, the `README.md` header and its per-family rows). Fix the docs, never the gate - a stale count has shipped before.
262
+ 4. **Packaging**: every directory the server loads at runtime must be inside `files[]`. A missing entry publishes a broken package (this has already caused one patch release).
263
+
264
+ ```bash
265
+ PACK=$(npm pack --dry-run 2>&1)
266
+ for d in tools/*/; do printf '%s' "$PACK" | grep -q "$d" || echo "ABORT: $d missing from files[]"; done
267
+ ```
268
+
269
+ 5. **stdout hygiene**: `grep -rn "console\.log(" index.js tools/` must be empty - stdout carries the JSON-RPC frames, diagnostics belong on `console.error`.
270
+ 6. **Personal-data scan**: no absolute `/Users/<name>` paths, no tokens, no corporate hostnames in the shipped files.
271
+ 7. **Version contract**: every pipeline-side minimum (the `cross-cli-contract.md` tool table, `design-check`'s MCP requirement, `apple-archive-compliance`) must be satisfied by the version about to ship. A newly added tool that a pipeline skill now needs -> bump that declared minimum in the pipeline during this same sync.
272
+
273
+ **Version bump**: patch for fixes and docs, minor for a new tool, major for a removed or renamed tool (pipeline minimums depend on the surface).
274
+
275
+ **Ship**:
276
+
277
+ ```bash
278
+ gh auth switch --user {owner}
279
+ git add -A
280
+ git commit -m "{type}(v$NEW): {what changed}" # that repo's own commit convention
281
+ git tag "v$NEW" && git push origin main --tags
282
+ ```
283
+
284
+ Publish to the registry declared in `publishConfig` through a throwaway userconfig. Never edit `~/.npmrc`, and never a bare `npm publish` - it lands on whichever registry the ambient config happens to name.
285
+
286
+ **The token depends on the registry**: `npm.pkg.github.com` authenticates with a GitHub PAT carrying `write:packages` (logical key `github`), `registry.npmjs.org` with an npm token (logical key `npm`). Picking the wrong one fails with a 401 that reads like a missing package.
287
+
288
+ ```bash
289
+ NPMRC=$(mktemp); trap 'rm -f "$NPMRC"' EXIT
290
+ REG=$(node -p "require('./package.json').publishConfig?.registry || 'https://registry.npmjs.org'")
291
+ HOST=${REG#https://}; HOST=${HOST%/}
292
+ case "$HOST" in npm.pkg.github.com*) KEY=github ;; *) KEY=npm ;; esac
293
+ TOKEN=$(bash "$HOME/.claude/lib/credential-store.sh" get "$KEY")
294
+ [ -n "$TOKEN" ] || echo "ABORT: no '$KEY' token - onboard it via /multi-agent:setup before publishing"
295
+ printf '%s\n' "registry=$REG" "//$HOST/:_authToken=$TOKEN" > "$NPMRC"
296
+ npm publish --userconfig "$NPMRC"
297
+ ```
298
+
299
+ Verify the publish landed: on GitHub Packages `npm view <package> version --userconfig "$NPMRC"`; on npmjs read the registry directly (`curl -s https://registry.npmjs.org/<package>/latest`) rather than `npm view`, which can answer from a stale cache.
300
+
301
+ **Approval**: outside autopilot and outside `release`, ask before shipping.
302
+
303
+ > "dev-toolkit moved (N changed files, M unpushed commits, v$VER). What should I do?"
304
+
305
+ - Commit + push (no publish)
306
+ - Commit + push + publish v$NEW
307
+ - Skip this step
308
+
309
+ With `release`, or in autopilot, run the full ship path without asking. If the working copy holds band-E work from `/multi-agent:refactor` that was never approved, do not ship it silently - report it and ask.
310
+
311
+ **Consumer note**: a path-based stdio registration (`node <localPath>/index.js`) picks the change up on the next MCP restart with no reinstall; a packaged registration (`npx <package>`) needs the published version. State which one the resolved config uses in the Step 6 report.
312
+
313
+ ---
314
+
210
315
  ## Website Sync (Step 4)
211
316
 
212
317
  Propagate pipeline version, phase count, model count, and feature descriptions to the website.
@@ -242,9 +347,10 @@ When invoked with the `release` argument:
242
347
  4. VERSION package.json version bump (patch/minor/major)
243
348
  5. Commit + TAG git commit + git tag v{VERSION}
244
349
  6. PUSH git push --tags -> release.yml auto-publish
245
- 7. WEBSITE Version + features -> {website-host}
246
- 8. COPILOT Copilot CLI instructions + skills sync
247
- 9. Report Summary: version, touched repos, deploy status
350
+ 7. DEV-TOOLKIT Ship the companion MCP server if it moved (Step 3d gates, then publish)
351
+ 8. WEBSITE Version + features -> {website-host}
352
+ 9. COPILOT Copilot CLI instructions + skills sync
353
+ 10. Report Summary: version, touched repos, deploy status
248
354
  ```
249
355
 
250
356
  ---
@@ -257,10 +363,10 @@ This runs on the Claude <-> Copilot axis — the two CLIs the pipeline supports
257
363
  |-------------|-------------|
258
364
  | `~/.claude/commands/multi-agent/{cmd}/SKILL.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |
259
365
 
260
- **41 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
366
+ **42 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
261
367
 
262
368
  ```
263
- analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, dev,
369
+ analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
264
370
  dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, forget, garbage-collect,
265
371
  help, issue, jira, kill, language, local,
266
372
  local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
@@ -281,6 +387,7 @@ routines, save, scan, search, setup, stack, status, sync, test, uninstall, updat
281
387
  | `gh` CLI (personal) | `{owner}` gh auth (Keychain) |
282
388
  | `gh` CLI (work) | `{user}_<work-label>` gh auth (Keychain) - see `prefs.global.accounts[]` |
283
389
  | npm publish | `NODE_AUTH_TOKEN` - `GITHUB_TOKEN` in CI, Keychain PAT locally |
390
+ | dev-toolkit publish (Step 3d) | `npm` logical Keychain key -> throwaway `--userconfig`, registry from that repo's `publishConfig` |
284
391
 
285
392
  ```bash
286
393
  gh auth switch --user {owner} # for personal repos
@@ -35,6 +35,6 @@ Pass `$ARGUMENTS` through verbatim - sim-test.md decides which test matrix to
35
35
 
36
36
  - **iOS**: Xcode + a booted Simulator (`xcrun simctl list | grep Booted`)
37
37
  - **Android**: Android SDK + a running emulator or USB device (`adb devices`)
38
- - **MCP**: the `dev-toolkit` MCP server must be installed (tool names start with `mcp__dev_toolkit__`)
38
+ - **MCP**: the `dev-toolkit` MCP server must be installed (tool names start with `mcp__dev-toolkit__`)
39
39
 
40
40
  For manual-test mode (checkout the task branch + Xcode-open hint), see `/multi-agent:manual-test`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Mobile UI Bug Hunter - iOS Simulator (simctl) + Android Emulator (adb) - auto-detects platform"
3
- allowed-tools: Agent, Bash, Read, Write, Edit, TaskCreate, TaskUpdate, TaskList, TaskGet, mcp__dev_toolkit__ios_list_devices, mcp__dev_toolkit__ios_boot_device, mcp__dev_toolkit__ios_screenshot, mcp__dev_toolkit__ios_tap, mcp__dev_toolkit__ios_swipe, mcp__dev_toolkit__ios_type_text, mcp__dev_toolkit__ios_launch_app, mcp__dev_toolkit__ios_terminate_app, mcp__dev_toolkit__ios_list_apps, mcp__dev_toolkit__ios_go_home, mcp__dev_toolkit__ios_set_appearance, mcp__dev_toolkit__ios_set_content_size, mcp__dev_toolkit__ios_set_locale, mcp__dev_toolkit__ios_open_url, mcp__dev_toolkit__ios_status_bar, mcp__dev_toolkit__ios_push_notification, mcp__dev_toolkit__ios_grant_permission, mcp__dev_toolkit__ios_revoke_permission, mcp__dev_toolkit__ios_reset_permissions, mcp__dev_toolkit__ios_set_location, mcp__dev_toolkit__ios_clear_location, mcp__dev_toolkit__ios_set_increase_contrast, mcp__dev_toolkit__ios_record_video, mcp__dev_toolkit__ios_add_media, mcp__dev_toolkit__ios_keychain_reset, mcp__dev_toolkit__ios_get_app_container, mcp__dev_toolkit__ios_erase_device, mcp__dev_toolkit__ios_get_ui_tree, mcp__dev_toolkit__android_list_devices, mcp__dev_toolkit__android_screenshot, mcp__dev_toolkit__android_tap, mcp__dev_toolkit__android_swipe, mcp__dev_toolkit__android_type_text, mcp__dev_toolkit__android_key_event, mcp__dev_toolkit__android_launch_app, mcp__dev_toolkit__android_stop_app, mcp__dev_toolkit__android_list_packages, mcp__dev_toolkit__android_go_home, mcp__dev_toolkit__android_go_back, mcp__dev_toolkit__android_get_ui_tree, mcp__dev_toolkit__android_set_dark_mode, mcp__dev_toolkit__android_set_font_scale, mcp__dev_toolkit__android_set_locale, mcp__dev_toolkit__android_set_location, mcp__dev_toolkit__android_grant_permission, mcp__dev_toolkit__android_revoke_permission, mcp__dev_toolkit__android_record_screen, mcp__dev_toolkit__android_install_apk, mcp__dev_toolkit__android_uninstall_app, mcp__dev_toolkit__android_logcat, mcp__dev_toolkit__android_get_screen_size, mcp__dev_toolkit__android_open_url, mcp__dev_toolkit__android_clear_app_data
3
+ allowed-tools: Agent, Bash, Read, Write, Edit, TaskCreate, TaskUpdate, TaskList, TaskGet, mcp__dev-toolkit__ios_list_devices, mcp__dev-toolkit__ios_boot_device, mcp__dev-toolkit__ios_screenshot, mcp__dev-toolkit__ios_tap, mcp__dev-toolkit__ios_swipe, mcp__dev-toolkit__ios_type_text, mcp__dev-toolkit__ios_launch_app, mcp__dev-toolkit__ios_terminate_app, mcp__dev-toolkit__ios_list_apps, mcp__dev-toolkit__ios_go_home, mcp__dev-toolkit__ios_set_appearance, mcp__dev-toolkit__ios_set_content_size, mcp__dev-toolkit__ios_set_locale, mcp__dev-toolkit__ios_open_url, mcp__dev-toolkit__ios_status_bar, mcp__dev-toolkit__ios_push_notification, mcp__dev-toolkit__ios_grant_permission, mcp__dev-toolkit__ios_revoke_permission, mcp__dev-toolkit__ios_reset_permissions, mcp__dev-toolkit__ios_set_location, mcp__dev-toolkit__ios_clear_location, mcp__dev-toolkit__ios_set_increase_contrast, mcp__dev-toolkit__ios_record_video, mcp__dev-toolkit__ios_add_media, mcp__dev-toolkit__ios_keychain_reset, mcp__dev-toolkit__ios_get_app_container, mcp__dev-toolkit__ios_erase_device, mcp__dev-toolkit__ios_get_ui_tree, mcp__dev-toolkit__android_list_devices, mcp__dev-toolkit__android_screenshot, mcp__dev-toolkit__android_tap, mcp__dev-toolkit__android_swipe, mcp__dev-toolkit__android_type_text, mcp__dev-toolkit__android_key_event, mcp__dev-toolkit__android_launch_app, mcp__dev-toolkit__android_stop_app, mcp__dev-toolkit__android_list_packages, mcp__dev-toolkit__android_go_home, mcp__dev-toolkit__android_go_back, mcp__dev-toolkit__android_get_ui_tree, mcp__dev-toolkit__android_set_dark_mode, mcp__dev-toolkit__android_set_font_scale, mcp__dev-toolkit__android_set_locale, mcp__dev-toolkit__android_set_location, mcp__dev-toolkit__android_grant_permission, mcp__dev-toolkit__android_revoke_permission, mcp__dev-toolkit__android_record_screen, mcp__dev-toolkit__android_install_apk, mcp__dev-toolkit__android_uninstall_app, mcp__dev-toolkit__android_logcat, mcp__dev-toolkit__android_get_screen_size, mcp__dev-toolkit__android_open_url, mcp__dev-toolkit__android_clear_app_data
4
4
  ---
5
5
 
6
6
  # Mobile UI Bug Hunter
@@ -22,8 +22,8 @@ Auto-detects platform: iOS (simctl) or Android (adb). No external apps needed.
22
22
  5. If argument contains "android" or "ios" -> use that
23
23
  ```
24
24
 
25
- For iOS: use `mcp__dev_toolkit__ios_*` tools
26
- For Android: use `mcp__dev_toolkit__android_*` tools
25
+ For iOS: use `mcp__dev-toolkit__ios_*` tools
26
+ For Android: use `mcp__dev-toolkit__android_*` tools
27
27
 
28
28
  **Android extras**: `get_ui_tree` returns XML with bounds/text/resource-id (uiautomator dump), `logcat` for crash detection, `go_back` button.
29
29
 
@@ -134,11 +134,11 @@ Step A - run the standard visual + accessibility sweep (light + dark + large t
134
134
  Step B - dispatch compliance skill:
135
135
  iOS:
136
136
  Load pipeline/skills/shared/core/apple-archive-compliance/SKILL.md
137
- Prereq: @mmerterden/dev-toolkit-mcp ≥ v2.4 (provides ios_app_store_audit MCP tool)
137
+ Prereq: @mmerterden/dev-toolkit-mcp ≥ v2.9.0 (provides ios_app_store_audit MCP tool)
138
138
  - installable via npm; standalone ArchiveGuard binary deprecated in v8.4.0
139
139
  Artifact: pick newest .xcarchive under ~/Library/Developer/Xcode/Archives/**
140
140
  OR accept explicit path argument after "store-ready"
141
- Invoke: mcp__dev_toolkit__ios_app_store_audit({ archive_path: <path>, rules: "all" })
141
+ Invoke: mcp__dev-toolkit__ios_app_store_audit({ archive_path: <path>, rules: "all" })
142
142
  OR (Node fallback): node -e "import('@mmerterden/dev-toolkit-mcp/tools/ios-app-store-audit/index.js').then(m => m.runAudit({ archivePath: '<path>', rules: 'all' }).then(r => console.log(JSON.stringify(r))))"
143
143
  Humanize via --lang en (promptLanguage is locked to "en"; pass --lang=tr explicitly to opt into Turkish)
144
144
  Android:
@@ -84,10 +84,39 @@ require_cmd() {
84
84
  fi
85
85
  }
86
86
 
87
+ # --- Logical key -> backend key --------------------------------------------
88
+ # Callers pass a LOGICAL key ("github", "jira", "figma_pat"). The actual
89
+ # Keychain / libsecret / Credential Manager entry is named by
90
+ # prefs.global.keychainMapping, so a lookup that uses the logical key verbatim
91
+ # finds nothing whenever the two differ - silently, because a missing entry and
92
+ # a missing mapping look identical from here. Resolve the mapping first and fall
93
+ # back to the logical key when no mapping exists (backward compatible with
94
+ # entries whose name already equals the logical key).
95
+ PREFS_FILE="${MULTI_AGENT_PREFS:-$HOME/.claude/multi-agent-preferences.json}"
96
+
97
+ resolve_key() {
98
+ local logical="$1" mapped=""
99
+ if [ -f "$PREFS_FILE" ] && command -v python3 >/dev/null 2>&1; then
100
+ mapped=$(python3 -c '
101
+ import json, sys
102
+ try:
103
+ with open(sys.argv[1], encoding="utf-8") as fh:
104
+ prefs = json.load(fh)
105
+ except Exception:
106
+ sys.exit(0)
107
+ name = prefs.get("global", {}).get("keychainMapping", {}).get(sys.argv[2])
108
+ if isinstance(name, str) and name.strip():
109
+ sys.stdout.write(name.strip())
110
+ ' "$PREFS_FILE" "$logical" 2>/dev/null || true)
111
+ fi
112
+ printf '%s' "${mapped:-$logical}"
113
+ }
114
+
87
115
  # --- Subcommands ------------------------------------------------------------
88
116
  do_get() {
89
117
  local key="${1:-}"
90
118
  [ -z "$key" ] && { echo "usage: $0 get <key>" >&2; exit 3; }
119
+ key=$(resolve_key "$key")
91
120
  if delegate_to_python; then
92
121
  local val rc
93
122
  val=$(python3 "$KEYCHAIN_PY" get "$key" 2>/dev/null) || rc=$?
@@ -128,6 +157,8 @@ if (\$c) { \$c.GetNetworkCredential().Password }
128
157
  do_set() {
129
158
  local key="${1:-}" val="${2:-}"
130
159
  [ -z "$key" ] && { echo "usage: $0 set <key> <value|->" >&2; exit 3; }
160
+ # Resolve through the same mapping as do_get so set/get stay symmetric.
161
+ key=$(resolve_key "$key")
131
162
  # "-" means: read the secret from stdin (keeps it off argv).
132
163
  if [ "$val" = "-" ]; then
133
164
  val=$(cat)
@@ -173,6 +204,7 @@ New-StoredCredential -Target '$key_esc' -UserName '${USER:-${USERNAME:-claude}}'
173
204
  do_delete() {
174
205
  local key="${1:-}"
175
206
  [ -z "$key" ] && { echo "usage: $0 delete <key>" >&2; exit 3; }
207
+ key=$(resolve_key "$key")
176
208
  if delegate_to_python; then
177
209
  python3 "$KEYCHAIN_PY" delete "$key" >/dev/null 2>&1 || true
178
210
  return 0
@@ -63,6 +63,7 @@ budget_exhausted() {
63
63
 
64
64
  # ---- skip dirs ----------------------------------------------------------------
65
65
  SKIP_PRUNE_ARGS=(
66
+ -path '*/.worktrees' -prune -o
66
67
  -path '*/.build' -prune -o
67
68
  -path '*/DerivedData' -prune -o
68
69
  -path '*/Pods' -prune -o
@@ -212,6 +212,7 @@ print(json.dumps(out))
212
212
  find "$root" -maxdepth "$MAXDEPTH" -type d -name ".git" \
213
213
  \( -path "*/node_modules/*" -prune -o -path "*/.build/*" -prune -o \
214
214
  -path "*/DerivedData/*" -prune -o -path "*/Pods/*" -prune -o \
215
+ -path "*/.worktrees/*" -prune -o \
215
216
  -path "*/.next/*" -prune -o -print \) 2>/dev/null \
216
217
  | while read -r gitdir; do
217
218
  [ -n "$gitdir" ] || continue
@@ -110,6 +110,13 @@ do_init() {
110
110
  if [ -f "$project_root/.gitignore" ]; then
111
111
  cat "$project_root/.gitignore" >> "$sd/.git/info/exclude"
112
112
  fi
113
+ # `.worktrees/` is forced-excluded too: worktrees live INSIDE the repo at
114
+ # <repo>/.worktrees/<id>/ and each carries a `.git` gitlink, so a blanket
115
+ # `add -A` would record every worktree as a "Subproject commit" and churn a
116
+ # fresh gitlink into every per-tool-call snapshot. The real clone keeps this
117
+ # out of its own index via .git/info/exclude; the shadow repo has its own
118
+ # exclude file and must repeat the rule. This mirrors the traversal-prune
119
+ # contract in phase-0-init.md (never descend into .worktrees on a tree walk).
113
120
  cat >> "$sd/.git/info/exclude" <<EOF
114
121
  node_modules/
115
122
  Pods/
@@ -119,6 +126,7 @@ DerivedData/
119
126
  .gradle/
120
127
  __pycache__/
121
128
  .venv/
129
+ .worktrees/
122
130
  *.log
123
131
  EOF
124
132
  # Initial author setup - keep separate from user's global git identity to
@@ -6,10 +6,10 @@
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Command Inventory (41 commands)
9
+ ## 1. Command Inventory (42 commands)
10
10
 
11
11
  ```
12
- analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, dev,
12
+ analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
13
13
  dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, forget, garbage-collect,
14
14
  help, issue, jira, kill, language, local,
15
15
  local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
@@ -43,7 +43,7 @@ Two parallel shared/core skills under `pipeline/skills/shared/core/` wrap extern
43
43
 
44
44
  | Skill | Path | Tool wrapper | Rules |
45
45
  |---|---|---|---|
46
- | `apple-archive-compliance` | `pipeline/skills/shared/core/apple-archive-compliance/` | `ios_app_store_audit` MCP tool (in `@mmerterden/dev-toolkit-mcp` ≥ v2.4) | 17 (Apple ITMS + App Store Review Guidelines) |
46
+ | `apple-archive-compliance` | `pipeline/skills/shared/core/apple-archive-compliance/` | `ios_app_store_audit` MCP tool (in `@mmerterden/dev-toolkit-mcp` ≥ v2.9.0) | 18 (Apple ITMS + App Store Review Guidelines) |
47
47
  | `google-play-compliance` | `pipeline/skills/shared/core/google-play-compliance/` | bundletool + aapt2 + apksigner | 21 (4 categories: Technical / Security / Privacy / Hygiene) |
48
48
 
49
49
  Both skills are wired to 4 consumers: `/multi-agent:test "store-ready"` (primary), Phase 4 Security Auditor (`pipeline/agents/security-auditor.md`), `/multi-agent:review` + SKILL.md counterpart, `/multi-agent:channels` PR-body auto-augmentation. Contract enforced by `smoke-compliance-skills.sh` (46 assertions).
@@ -450,7 +450,7 @@ fi
450
450
 
451
451
  If the path is still registered and healthy after the heal, enter + pull instead of re-adding (existing behavior). Only when `worktree add` still fails after the heal do the rollback / collision flow in the multi-repo block below.
452
452
 
453
- **Worktree residue guard (required once per repo, idempotent):** every worktree dir contains a `.git` file, so a blanket `git add -A` / `git add .` in the parent tree records `.worktrees/{id}` as a gitlink ("Subproject commit ...") that pollutes the branch and later needs a manual removal commit. Keep `.worktrees/` out of the index via the clone-local exclude file (never committed, so no PR noise in shared repos):
453
+ **Worktree residue guard (required once per repo, idempotent):** every worktree dir holds a `.git` file, so a blanket `git add -A` in the parent tree records `.worktrees/{id}` as a gitlink that pollutes the branch. Keep `.worktrees/` out of the index via the clone-local exclude file (never committed, so no PR noise):
454
454
 
455
455
  ```bash
456
456
  ex="$(git -C "$PROJECT_ROOT" rev-parse --path-format=absolute --git-common-dir)/info/exclude"
@@ -458,6 +458,14 @@ mkdir -p "$(dirname "$ex")"
458
458
  grep -qxF '.worktrees/' "$ex" 2>/dev/null || printf '.worktrees/\n' >> "$ex"
459
459
  ```
460
460
 
461
+ **Traversal-prune contract:** the exclude guard covers only the git INDEX, not
462
+ filesystem scans. Since each worktree is a full checkout, an unpruned tree walk
463
+ double-processes every file and can re-stage gitlinks. So `.worktrees` joins the
464
+ skip set (`node_modules`, `Pods`, `.build`, `DerivedData`, `.next`): every
465
+ `find`, walker, or `git add -A` MUST prune it. Already applied in the Step 2
466
+ scan, `shadow-git.sh` excludes, and the shared walkers (`extract-conventions.sh`,
467
+ `repo-cache.sh`, `repo-map.mjs`); add it to any new tree walk too.
468
+
461
469
  **v2.1.0+ Multi-Repo Worktree Setup**:
462
470
 
463
471
  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):
@@ -164,7 +164,7 @@ For each task (respecting dependency order):
164
164
  **Preferred - MCP tool (token-efficient, dev-toolkit-mcp ≥ 2.3.0):**
165
165
  ```
166
166
  acquire_build_lock "$TASK_ID"
167
- mcp__dev_toolkit__ios_xcodebuild({
167
+ mcp__dev-toolkit__ios_xcodebuild({
168
168
  project: "{projectPath}", // or workspace
169
169
  scheme: "{scheme}",
170
170
  configuration: "Release",
@@ -173,7 +173,7 @@ For each task (respecting dependency order):
173
173
  })
174
174
  release_build_lock
175
175
  ```
176
- Returns one line: `Build: SUCCESS|FAILURE (E errors, W warnings) [xcresult-<id>]`. On failure, drill in only when needed via `mcp__dev_toolkit__ios_xcresult({id, mode: "errors"})`. Saves thousands of tokens per build cycle vs. raw xcodebuild output.
176
+ Returns one line: `Build: SUCCESS|FAILURE (E errors, W warnings) [xcresult-<id>]`. On failure, drill in only when needed via `mcp__dev-toolkit__ios_xcresult({id, mode: "errors"})`. Saves thousands of tokens per build cycle vs. raw xcodebuild output.
177
177
 
178
178
  **Fallback - raw xcodebuild** (when MCP server is unavailable):
179
179
  ```bash
@@ -414,11 +414,12 @@ At the end of every fix/rework round (each Phase 3 re-entry that resolved accept
414
414
 
415
415
  ```bash
416
416
  node pipeline/scripts/learnings-ledger.mjs add --kind fact \
417
- --statement "<one-line root cause: what class of mistake produced the finding and why (<=140 chars)>" \
417
+ --statement "<one-line rule/what-to-do so this class of finding does not recur (<=140 chars)>" \
418
+ --diagnosis "<the causal WHY the failure happened, one line>" \
418
419
  --scope "<file-or-area glob from the finding>" --task "$TASK_ID"
419
420
  ```
420
421
 
421
- Statement shape: root cause, not symptom - "force-unwrapped optional in async callback path", not "fixed crash in LoginView". Dedup built in (exit 2 = already stored, skip). Lessons re-enter Phase 1 + triage via `learnings-ledger.mjs brief`.
422
+ Statement shape: the durable rule/root cause, not the symptom - "force-unwrapped optional in async callback path", not "fixed crash in LoginView". Always pass `--diagnosis` with the causal why (Reflexion: the verbal reason prevents recurrence; a bare outcome does not). Dedup built in (exit 2 = already stored). Lessons re-enter Phase 1 + triage via `learnings-ledger.mjs brief` (renders diagnosis as "(why: ...)").
422
423
 
423
424
  Progress line: ` → writing lesson to learnings ledger ({N} entries)`
424
425
 
@@ -103,6 +103,16 @@ Tier 1 / Tier 2 records print `screenshotUrl` from the captured evidence (Tier 2
103
103
  The waiting state persists in `tracker-state.json` across the handoff; `/multi-agent:finish` and `/multi-agent:manual-test` CONTINUE this state file and never re-init it (`$HOME/.claude/multi-agent-refs/tracker-contract.md` "Continuation runs").
104
104
  6. If fix needed:
105
105
  - Branch already has WIP commit (from step 2) - changes are safe
106
+ - **Heal stale admin state first** (same contract as Phase 0 - step 3's
107
+ `worktree remove` or an interrupted run can leave a stale entry, so a bare
108
+ re-add fails with `already exists`/`already registered`):
109
+ ```bash
110
+ git -C "$PROJECT_ROOT" worktree prune 2>/dev/null || true
111
+ if git -C "$PROJECT_ROOT" worktree list --porcelain | grep -qF "{worktree-path}"; then
112
+ git -C "$PROJECT_ROOT" worktree unlock "{worktree-path}" 2>/dev/null || true
113
+ fi
114
+ ```
115
+ Phase 0's `.worktrees/` residue guard is already in `.git/info/exclude` - no re-add.
106
116
  - Recreate worktree from branch: `git -C $PROJECT_ROOT worktree add {worktree-path} {branch}`
107
117
  - Re-set git identity: `git -C {worktree-path} config user.name/email` (from state)
108
118
  - Go back to Phase 3
@@ -116,12 +126,12 @@ Before or during user testing, run device-level audits via Bash if user requests
116
126
 
117
127
  | Check | When | Command |
118
128
  | ------------------- | ----------------- | ----------------------------------------- |
119
- | Accessibility audit | UI changes | `mcp__dev_toolkit__ios_accessibility_audit` (or `swift ui-tree-dumper.swift`) |
120
- | Biometric test | Auth flow changes | `mcp__dev_toolkit__ios_biometric` (or `xcrun simctl keychain biometric-match`) |
129
+ | Accessibility audit | UI changes | `mcp__dev-toolkit__ios_accessibility_audit` (or `swift ui-tree-dumper.swift`) |
130
+ | Biometric test | Auth flow changes | `mcp__dev-toolkit__ios_biometric` (or `xcrun simctl keychain biometric-match`) |
121
131
  | Launch time | Android project | `adb shell am start -W` |
122
132
  | Visual test | Any UI changes | `/multi-agent test` (sim-test) |
123
- | Snapshot regression | Component / pixel-stable UI changes | `mcp__dev_toolkit__ios_visual_diff` (dev-toolkit-mcp ≥ 2.3.0) |
124
- | App Store screenshots | `taskType === screenshot` | `mcp__dev_toolkit__ios_status_bar({preset: "clean"})` before each capture |
133
+ | Snapshot regression | Component / pixel-stable UI changes | `mcp__dev-toolkit__ios_visual_diff` (dev-toolkit-mcp ≥ 2.3.0) |
134
+ | App Store screenshots | `taskType === screenshot` | `mcp__dev-toolkit__ios_status_bar({preset: "clean"})` before each capture |
125
135
 
126
136
  Results included in Phase 7 report. MCP tools preferred when available - concise structured output, lower token cost.
127
137
 
@@ -149,6 +149,8 @@ Each phase doc is lazy-loaded - only the current phase's spec is in context. B
149
149
 
150
150
  Token estimate: `ceil(chars / 4)`. Budget covers phase-\*.md files only. Guides, rules, and agents are loaded separately on demand.
151
151
 
152
+ **Prompt caching (token learning-curve):** assemble each phase prompt as a stable, cacheable prefix (phase instructions + repo learnings brief + conventions + repo evidence) followed by the volatile task suffix, so repeated runs on a known repo pay cache-read price on the prefix. Full contract: `$HOME/.claude/multi-agent-refs/prompt-assembly.md`. The effect (rising cache ratio, falling tokens/task) is what `learning-curve.mjs` trends.
153
+
152
154
  ## SubPhase Convention
153
155
 
154
156
  When a specialized skill takes over a main pipeline phase, progress is reported as **SubPhases** nested under the parent. Top-level stays 8 phases (0-7); specialized flows get sub-phase detail.
@@ -0,0 +1,31 @@
1
+ # Prompt assembly: stable-prefix caching (token learning-curve)
2
+
3
+ Anthropic prompt caching is **prefix-based**: the longest unchanged leading span of a request is served from cache at a large discount on repeated calls. As the pipeline accumulates durable knowledge about a repo (conventions, learnings brief, repo evidence), that knowledge should become a near-free cached prefix instead of a growing per-run token cost. This is the mechanism that makes "knowing the repo better" literally cheaper each run.
4
+
5
+ ## The rule
6
+
7
+ Assemble every phase prompt as two spans, in this order:
8
+
9
+ 1. **Stable prefix (cacheable, repo-stable, changes rarely):**
10
+ - system / role instructions for the phase
11
+ - the repo learnings brief (`learnings-ledger.mjs brief`)
12
+ - extracted conventions (Phase 1c) and the design/analysis doc when applicable
13
+ - repo structural evidence (the reuse-first buckets, Code Connect index)
14
+ 2. **Volatile suffix (task-specific, changes every run):**
15
+ - the specific task / issue / diff
16
+ - the current file(s) under edit
17
+ - per-run state and the immediate instruction
18
+
19
+ Put the stable material FIRST and the volatile material LAST. Reordering volatile content into the middle of the prefix invalidates the cache for everything after it, so keep the boundary clean.
20
+
21
+ ## Why it matters
22
+
23
+ - Repeated runs on the same repo pay roughly cache-read price (~10% of input) on the stable prefix instead of full price - the dominant per-run token cost once a repo is "known."
24
+ - It turns accumulated learnings from a cost into an asset: more durable knowledge => bigger cached prefix => lower marginal cost, not higher.
25
+ - The effect is visible in `metrics.jsonl` `tokens_cached` / `cache_ratio` and is exactly what `learning-curve.mjs` trends over time (rising cache ratio + falling tokens/task = the learning curve working).
26
+
27
+ ## Anti-patterns
28
+
29
+ - Interleaving the task text with conventions/learnings (breaks the prefix).
30
+ - Letting the stable prefix bloat past what the phase needs (context rot; cache a lean prefix, not everything - see the corpus merge/drop + progressive-disclosure discipline).
31
+ - Regenerating repo evidence from scratch each run instead of reusing cached/learned artifacts (defeats both the cache and the learning curve).
@@ -73,7 +73,12 @@
73
73
  "onExceed": "warn",
74
74
  "pricingModel": "opus"
75
75
  },
76
- "derivedSkillSources": []
76
+ "derivedSkillSources": [],
77
+ "devToolkit": {
78
+ "enabled": true,
79
+ "mcpServerName": "dev-toolkit",
80
+ "registry": "github-packages"
81
+ }
77
82
  },
78
83
  "projects": {},
79
84
  "_figmaConfigTemplate": {
@@ -170,5 +175,17 @@
170
175
  "derivedFromVersion": "0.0.0",
171
176
  "upstreamRepoUrl": "https://github.com/<org>/<repo>"
172
177
  }
178
+ },
179
+ "_devToolkitTemplate": {
180
+ "_comment": "Optional. global.devToolkit points at the companion MCP server that serves the pipeline's device and browser tools. /multi-agent:refactor Step 0c researches current MCP practice and audits that repo against it (plan band E); /multi-agent:sync Step 3d ships it when it moved (gates, commit, publish). Leave localPath out and both commands auto-detect the repo from your mcpServers registration; set enabled:false to skip both.",
181
+ "_example": {
182
+ "enabled": true,
183
+ "label": "dev-toolkit MCP server",
184
+ "localPath": "$HOME/<repo-dir>",
185
+ "mcpServerName": "dev-toolkit",
186
+ "packageName": "@<scope>/<package>",
187
+ "registry": "github-packages",
188
+ "repoUrl": "https://github.com/<owner>/<repo>"
189
+ }
173
190
  }
174
191
  }