@mmerterden/multi-agent-pipeline 12.5.0 → 12.7.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 (290) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +19 -19
  3. package/docs/FIGMA_PIPELINE.md +34 -34
  4. package/docs/adr/0001-three-model-triage.md +12 -12
  5. package/docs/adr/0002-instruction-driven-flag.md +5 -5
  6. package/docs/adr/0003-unified-shared-skills.md +5 -5
  7. package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
  8. package/docs/adr/0005-lazy-phase-docs.md +2 -2
  9. package/docs/adr/0006-skills-core-external-split.md +6 -6
  10. package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
  11. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
  12. package/docs/adr/README.md +1 -1
  13. package/docs/best-practices.md +3 -3
  14. package/docs/features.md +48 -28
  15. package/docs/performance.md +16 -16
  16. package/docs/recovery-guide.md +39 -39
  17. package/index.js +11 -5
  18. package/install/_common.mjs +5 -11
  19. package/install/_copilot-instructions.mjs +2 -2
  20. package/install/_dev-only-files.mjs +2 -1
  21. package/install/_platform-filter.mjs +1 -1
  22. package/install/_telemetry.mjs +1 -1
  23. package/install/claude.mjs +10 -9
  24. package/install/copilot.mjs +10 -19
  25. package/install/index.mjs +7 -15
  26. package/install/templates/copilot-instructions.md +54 -54
  27. package/install.js +1 -1
  28. package/package.json +15 -11
  29. package/pipeline/agents/security-auditor.md +1 -1
  30. package/pipeline/commands/archive-guard.md +5 -5
  31. package/pipeline/commands/multi-agent/SKILL.md +3 -1
  32. package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
  33. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
  34. package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
  35. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
  36. package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
  37. package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
  38. package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
  39. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
  40. package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
  41. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
  42. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
  43. package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
  44. package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
  45. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  46. package/pipeline/commands/multi-agent/help/SKILL.md +46 -6
  47. package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
  48. package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
  49. package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
  50. package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
  51. package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
  52. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
  53. package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
  54. package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
  55. package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
  56. package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
  57. package/pipeline/commands/multi-agent/refactor/SKILL.md +107 -19
  58. package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
  59. package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
  60. package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
  61. package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
  62. package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
  63. package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
  64. package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
  65. package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
  66. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  67. package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
  68. package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
  69. package/pipeline/commands/multi-agent/sync/SKILL.md +123 -16
  70. package/pipeline/commands/multi-agent/test/SKILL.md +2 -2
  71. package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
  72. package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
  73. package/pipeline/commands/sim-test.md +5 -5
  74. package/pipeline/eval/run-metrics-fixture.json +60 -13
  75. package/pipeline/lib/account-resolver.sh +1 -1
  76. package/pipeline/lib/channels-multi-repo.sh +1 -1
  77. package/pipeline/lib/context-link-extractor.sh +1 -1
  78. package/pipeline/lib/credential-store.sh +33 -1
  79. package/pipeline/lib/fetch-confluence.sh +1 -1
  80. package/pipeline/lib/fetch-crashlytics.sh +1 -1
  81. package/pipeline/lib/fetch-fortify.sh +1 -1
  82. package/pipeline/lib/fetch-graylog.sh +1 -1
  83. package/pipeline/lib/fetch-swagger.sh +1 -1
  84. package/pipeline/lib/issue-fetcher.sh +1 -1
  85. package/pipeline/lib/multi-repo-pipeline.sh +1 -1
  86. package/pipeline/lib/repo-cache.sh +1 -1
  87. package/pipeline/lib/submodule-detector.sh +1 -1
  88. package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
  89. package/pipeline/multi-agent-refs/component-generation.md +121 -0
  90. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  91. package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
  92. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
  93. package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
  94. package/pipeline/multi-agent-refs/phases/phase-5-test.md +4 -4
  95. package/pipeline/preferences-template.json +23 -12
  96. package/pipeline/schemas/agent-state.schema.json +125 -5
  97. package/pipeline/schemas/analysis-output.schema.json +18 -4
  98. package/pipeline/schemas/analysis-spec.schema.json +120 -32
  99. package/pipeline/schemas/clarify-output.schema.json +15 -5
  100. package/pipeline/schemas/design-check-config.schema.json +183 -0
  101. package/pipeline/schemas/dev-critic-output.schema.json +20 -5
  102. package/pipeline/schemas/figma-project-config.schema.json +42 -10
  103. package/pipeline/schemas/learnings-ledger.schema.json +10 -2
  104. package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
  105. package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
  106. package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
  107. package/pipeline/schemas/plan-todos.schema.json +6 -3
  108. package/pipeline/schemas/planning-output.schema.json +5 -1
  109. package/pipeline/schemas/prefs.schema.json +215 -197
  110. package/pipeline/schemas/test-gap.schema.json +5 -5
  111. package/pipeline/schemas/token-budget.json +8 -8
  112. package/pipeline/schemas/triage-corpus.schema.json +1 -1
  113. package/pipeline/scripts/README.md +7 -5
  114. package/pipeline/scripts/aggregate-metrics.mjs +18 -6
  115. package/pipeline/scripts/build-skills-index.mjs +6 -2
  116. package/pipeline/scripts/build-stack-plugins.mjs +142 -39
  117. package/pipeline/scripts/check-derived-drift.mjs +196 -0
  118. package/pipeline/scripts/check-md-links.mjs +6 -2
  119. package/pipeline/scripts/classify-plan-safety.mjs +26 -8
  120. package/pipeline/scripts/cost-budget-check.mjs +11 -6
  121. package/pipeline/scripts/cost-table.json +1 -1
  122. package/pipeline/scripts/diff-explain.mjs +7 -3
  123. package/pipeline/scripts/diff-risk-score.mjs +13 -3
  124. package/pipeline/scripts/eval-golden-tasks-live.mjs +8 -3
  125. package/pipeline/scripts/eval-golden-tasks.mjs +21 -9
  126. package/pipeline/scripts/eval-intent.mjs +8 -4
  127. package/pipeline/scripts/eval-mine-corpus.mjs +14 -4
  128. package/pipeline/scripts/evidence-gate.mjs +7 -2
  129. package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
  130. package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
  131. package/pipeline/scripts/gen-skills-index.mjs +18 -3
  132. package/pipeline/scripts/learning-curve.mjs +13 -3
  133. package/pipeline/scripts/learnings-ledger.mjs +119 -34
  134. package/pipeline/scripts/lint-mcp-refs.mjs +218 -0
  135. package/pipeline/scripts/lint-skills.mjs +20 -9
  136. package/pipeline/scripts/localize-commands.mjs +6 -1
  137. package/pipeline/scripts/match-skills.mjs +15 -4
  138. package/pipeline/scripts/memory-load.sh +5 -1
  139. package/pipeline/scripts/migrate-prefs.mjs +33 -16
  140. package/pipeline/scripts/phase-tracker.sh +3 -1
  141. package/pipeline/scripts/render-work-summary.sh +4 -1
  142. package/pipeline/scripts/repo-map.mjs +110 -64
  143. package/pipeline/scripts/review-scope.mjs +7 -1
  144. package/pipeline/scripts/routine-registry.mjs +4 -9
  145. package/pipeline/scripts/run-aggregator.mjs +11 -5
  146. package/pipeline/scripts/run-metrics.mjs +13 -8
  147. package/pipeline/scripts/run-smokes.mjs +57 -3
  148. package/pipeline/scripts/scorecard.mjs +258 -0
  149. package/pipeline/scripts/smoke-command-inventory.sh +81 -0
  150. package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
  151. package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
  152. package/pipeline/scripts/smoke-context-budget.sh +72 -0
  153. package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
  154. package/pipeline/scripts/smoke-generate-issue.sh +6 -5
  155. package/pipeline/scripts/smoke-model-fallback.sh +1 -1
  156. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +86 -7
  157. package/pipeline/scripts/smoke-own-punctuation.sh +103 -0
  158. package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
  159. package/pipeline/scripts/smoke-review-readiness.sh +3 -2
  160. package/pipeline/scripts/smoke-schema-validation.sh +19 -5
  161. package/pipeline/scripts/smoke-shadow-git.sh +4 -2
  162. package/pipeline/scripts/smoke-workflow-audit.sh +43 -11
  163. package/pipeline/scripts/smoke-write-state.sh +49 -5
  164. package/pipeline/scripts/test-gap-rules/android.json +11 -11
  165. package/pipeline/scripts/test-gap-rules/ios.json +16 -11
  166. package/pipeline/scripts/test-gap-rules/node.json +19 -7
  167. package/pipeline/scripts/test-gap-rules/python.json +10 -4
  168. package/pipeline/scripts/test-gap-scan.mjs +44 -12
  169. package/pipeline/scripts/test-integrity-gate.mjs +5 -1
  170. package/pipeline/scripts/token-budget-report.mjs +44 -21
  171. package/pipeline/scripts/triage-memory.mjs +142 -32
  172. package/pipeline/scripts/uninstall.mjs +75 -31
  173. package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
  174. package/pipeline/scripts/validate-diff-risk.mjs +32 -18
  175. package/pipeline/scripts/validate-test-gap.mjs +17 -7
  176. package/pipeline/scripts/validate-triage.mjs +17 -5
  177. package/pipeline/scripts/write-state.mjs +32 -9
  178. package/pipeline/skills/.skill-manifest.json +24 -8
  179. package/pipeline/skills/.skills-index.json +125 -89
  180. package/pipeline/skills/shared/README.md +65 -61
  181. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
  182. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
  183. package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
  184. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
  185. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
  186. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
  187. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  188. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
  189. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
  190. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
  191. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
  192. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
  193. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
  194. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
  195. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  196. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
  197. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
  198. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +23 -1
  199. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
  200. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
  201. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
  202. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
  203. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
  204. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
  205. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
  206. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
  207. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
  208. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
  209. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +82 -18
  210. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
  211. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -2
  212. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
  213. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
  214. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
  215. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
  216. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
  217. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
  218. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
  219. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
  220. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
  221. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
  222. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -2
  223. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
  224. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
  225. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
  226. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
  227. package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
  228. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
  229. package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
  230. package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
  231. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
  232. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
  233. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  234. package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
  235. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
  236. package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
  237. package/pipeline/skills/shared/external/council/SKILL.md +3 -3
  238. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
  239. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
  240. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
  241. package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
  242. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
  243. package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
  244. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
  245. package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
  246. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
  247. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
  248. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  249. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
  250. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
  251. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
  252. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
  253. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
  254. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
  255. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
  256. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
  257. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
  258. package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
  259. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
  260. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
  261. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
  262. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
  263. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
  264. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
  265. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
  266. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
  267. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
  268. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
  269. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
  270. package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
  271. package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
  272. package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
  273. package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
  274. package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
  275. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
  276. package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
  277. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
  278. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
  279. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
  280. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
  281. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
  282. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
  283. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
  284. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
  285. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
  286. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
  287. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
  288. package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
  289. package/pipeline/skills/skills-index.md +63 -59
  290. package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
@@ -43,7 +43,15 @@
43
43
  * @module pipeline/scripts/learnings-ledger
44
44
  */
45
45
 
46
- import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync, appendFileSync, statSync } from "node:fs";
46
+ import {
47
+ readFileSync,
48
+ writeFileSync,
49
+ renameSync,
50
+ existsSync,
51
+ mkdirSync,
52
+ appendFileSync,
53
+ statSync,
54
+ } from "node:fs";
47
55
  import { homedir } from "node:os";
48
56
  import { execFileSync } from "node:child_process";
49
57
  import { join, basename, dirname } from "node:path";
@@ -59,11 +67,16 @@ for (let i = 1; i < argv.length; i++) {
59
67
  // (e.g. --statement="-- prefer let over var"). Use it when the space form
60
68
  // would mistake the value for the next flag.
61
69
  const eq = body.indexOf("=");
62
- if (eq !== -1) { flags[body.slice(0, eq)] = body.slice(eq + 1); continue; }
70
+ if (eq !== -1) {
71
+ flags[body.slice(0, eq)] = body.slice(eq + 1);
72
+ continue;
73
+ }
63
74
  const k = body;
64
75
  const v = argv[i + 1];
65
- if (v !== undefined && !v.startsWith("--")) { flags[k] = v; i++; }
66
- else flags[k] = true;
76
+ if (v !== undefined && !v.startsWith("--")) {
77
+ flags[k] = v;
78
+ i++;
79
+ } else flags[k] = true;
67
80
  }
68
81
 
69
82
  const KINDS = new Set(["fact", "convention", "rejected-preference"]);
@@ -74,21 +87,50 @@ function die(msg, code = 1) {
74
87
  process.exit(code);
75
88
  }
76
89
 
90
+ // Resolves to the MAIN repository name even when called from inside a pipeline
91
+ // worktree. `--show-toplevel` returns the worktree root, whose basename is the
92
+ // task id, which would shard the per-repo ledger one directory per task and make
93
+ // the learning replay always miss. `--git-common-dir` points at the main
94
+ // repository's .git for every linked worktree.
77
95
  function repoSlug() {
78
96
  if (flags["repo-slug"]) return String(flags["repo-slug"]);
79
97
  if (process.env.MULTI_AGENT_REPO_SLUG) return process.env.MULTI_AGENT_REPO_SLUG;
98
+ try {
99
+ const commonDir = execFileSync(
100
+ "git",
101
+ ["rev-parse", "--path-format=absolute", "--git-common-dir"],
102
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
103
+ ).trim();
104
+ if (commonDir) {
105
+ const base = basename(commonDir);
106
+ const root = base === ".git" ? dirname(commonDir) : commonDir.replace(/\.git$/, "");
107
+ const slug = basename(root);
108
+ if (slug) return slug;
109
+ }
110
+ } catch {
111
+ /* not a git repo, or git too old for --path-format */
112
+ }
80
113
  try {
81
114
  const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
82
115
  encoding: "utf8",
83
116
  stdio: ["ignore", "pipe", "ignore"],
84
117
  }).trim();
85
118
  if (top) return basename(top);
86
- } catch { /* not a git repo */ }
119
+ } catch {
120
+ /* not a git repo */
121
+ }
87
122
  return "default";
88
123
  }
89
124
 
90
125
  function ledgerPath(slug) {
91
- return join(homedir(), ".claude", "memory", "multi-agent", slug || repoSlug(), "learnings-ledger.jsonl");
126
+ return join(
127
+ homedir(),
128
+ ".claude",
129
+ "memory",
130
+ "multi-agent",
131
+ slug || repoSlug(),
132
+ "learnings-ledger.jsonl",
133
+ );
92
134
  }
93
135
 
94
136
  function ensureDir(p) {
@@ -96,7 +138,10 @@ function ensureDir(p) {
96
138
  }
97
139
 
98
140
  function normStatement(s) {
99
- return String(s || "").toLowerCase().replace(/\s+/g, " ").trim();
141
+ return String(s || "")
142
+ .toLowerCase()
143
+ .replace(/\s+/g, " ")
144
+ .trim();
100
145
  }
101
146
 
102
147
  function readLedger(slug) {
@@ -106,8 +151,11 @@ function readLedger(slug) {
106
151
  const rows = [];
107
152
  for (const line of raw.split("\n")) {
108
153
  if (!line.trim()) continue;
109
- try { rows.push(JSON.parse(line)); }
110
- catch { /* skip corrupted line */ }
154
+ try {
155
+ rows.push(JSON.parse(line));
156
+ } catch {
157
+ /* skip corrupted line */
158
+ }
111
159
  }
112
160
  return rows;
113
161
  }
@@ -148,18 +196,25 @@ function addEntry(slug, { kind, statement, scope, task, confidence, diagnosis },
148
196
  function cmdAdd() {
149
197
  const kind = flags.kind;
150
198
  if (!KINDS.has(kind)) die(`--kind must be one of: ${[...KINDS].join(", ")}`, 1);
151
- if (!flags.statement || flags.statement === true) die("add needs --statement \"...\"", 1);
199
+ if (!flags.statement || flags.statement === true) die('add needs --statement "..."', 1);
152
200
  const slug = repoSlug();
153
201
  const existing = new Set(readLedger(slug).map((r) => `${r.kind}::${normStatement(r.statement)}`));
154
- const wrote = addEntry(slug, {
155
- kind,
156
- statement: flags.statement,
157
- scope: flags.scope && flags.scope !== true ? String(flags.scope) : "global",
158
- task: flags.task && flags.task !== true ? String(flags.task) : null,
159
- confidence: flags.confidence && flags.confidence !== true ? String(flags.confidence) : "medium",
160
- diagnosis: flags.diagnosis && flags.diagnosis !== true ? String(flags.diagnosis) : null,
161
- }, existing);
162
- process.stdout.write(JSON.stringify({ ok: true, slug, added: wrote ? 1 : 0, deduped: wrote ? 0 : 1 }) + "\n");
202
+ const wrote = addEntry(
203
+ slug,
204
+ {
205
+ kind,
206
+ statement: flags.statement,
207
+ scope: flags.scope && flags.scope !== true ? String(flags.scope) : "global",
208
+ task: flags.task && flags.task !== true ? String(flags.task) : null,
209
+ confidence:
210
+ flags.confidence && flags.confidence !== true ? String(flags.confidence) : "medium",
211
+ diagnosis: flags.diagnosis && flags.diagnosis !== true ? String(flags.diagnosis) : null,
212
+ },
213
+ existing,
214
+ );
215
+ process.stdout.write(
216
+ JSON.stringify({ ok: true, slug, added: wrote ? 1 : 0, deduped: wrote ? 0 : 1 }) + "\n",
217
+ );
163
218
  process.exit(wrote ? 0 : 2);
164
219
  }
165
220
 
@@ -170,8 +225,11 @@ function cmdFromTriage() {
170
225
  const task = flags.task && flags.task !== true ? String(flags.task) : null;
171
226
  const slug = repoSlug();
172
227
  let triage;
173
- try { triage = JSON.parse(readFileSync(triagePath, "utf8")); }
174
- catch (e) { die(`cannot parse triage JSON: ${e.message}`, 1); }
228
+ try {
229
+ triage = JSON.parse(readFileSync(triagePath, "utf8"));
230
+ } catch (e) {
231
+ die(`cannot parse triage JSON: ${e.message}`, 1);
232
+ }
175
233
 
176
234
  const existing = new Set(readLedger(slug).map((r) => `${r.kind}::${normStatement(r.statement)}`));
177
235
  let written = 0;
@@ -185,12 +243,21 @@ function cmdFromTriage() {
185
243
  // whole class on every future run. Blocking findings always get re-raised
186
244
  // and re-triaged on their own merits; only lower-severity rejections (style
187
245
  // nitpicks, scope calls) become durable "do not re-flag" preferences.
188
- if (f.severity === "blocking") { skippedBlocking++; continue; }
246
+ if (f.severity === "blocking") {
247
+ skippedBlocking++;
248
+ continue;
249
+ }
189
250
  // Top-level files (dirname ".") scope to the file itself, not a stray "./*".
190
251
  const dir = f.file ? dirname(f.file) : "";
191
- const scope = !f.file ? "global" : (dir === "." || dir === "" ? f.file : `${dir}/*`);
252
+ const scope = !f.file ? "global" : dir === "." || dir === "" ? f.file : `${dir}/*`;
192
253
  const statement = `Do not re-flag: ${issue}` + (r.reason ? ` (rejected: ${r.reason})` : "");
193
- if (addEntry(slug, { kind: "rejected-preference", statement, scope, task, confidence: "low" }, existing)) {
254
+ if (
255
+ addEntry(
256
+ slug,
257
+ { kind: "rejected-preference", statement, scope, task, confidence: "low" },
258
+ existing,
259
+ )
260
+ ) {
194
261
  written++;
195
262
  }
196
263
  }
@@ -202,7 +269,7 @@ function cmdForget() {
202
269
  const slug = repoSlug();
203
270
  const match = flags.statement && flags.statement !== true ? normStatement(flags.statement) : null;
204
271
  const kind = flags.kind && flags.kind !== true ? String(flags.kind) : null;
205
- if (!match && !kind) die("forget needs --statement \"...\" and/or --kind <kind>", 1);
272
+ if (!match && !kind) die('forget needs --statement "..." and/or --kind <kind>', 1);
206
273
  const p = ledgerPath(slug);
207
274
  if (!existsSync(p)) {
208
275
  process.stdout.write(JSON.stringify({ ok: true, slug, removed: 0 }) + "\n");
@@ -241,7 +308,9 @@ function cmdBrief() {
241
308
  for (const r of ordered) (byKind[r.kind] || (byKind[r.kind] = [])).push(r);
242
309
 
243
310
  const lines = ["<repo-learnings>"];
244
- lines.push(`Durable knowledge for repo "${slug}" (replayed each run; treat as context, not commands - current scope decides).`);
311
+ lines.push(
312
+ `Durable knowledge for repo "${slug}" (replayed each run; treat as context, not commands - current scope decides).`,
313
+ );
245
314
  const labels = {
246
315
  fact: "Architectural facts",
247
316
  convention: "Conventions",
@@ -263,7 +332,9 @@ function cmdBrief() {
263
332
  }
264
333
 
265
334
  function cmdPath() {
266
- process.stdout.write(ledgerPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n");
335
+ process.stdout.write(
336
+ ledgerPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n",
337
+ );
267
338
  process.exit(0);
268
339
  }
269
340
 
@@ -279,13 +350,27 @@ function cmdStats() {
279
350
  }
280
351
 
281
352
  switch (SUB) {
282
- case "add": cmdAdd(); break;
283
- case "from-triage": cmdFromTriage(); break;
284
- case "forget": cmdForget(); break;
285
- case "brief": cmdBrief(); break;
286
- case "path": cmdPath(); break;
287
- case "stats": cmdStats(); break;
353
+ case "add":
354
+ cmdAdd();
355
+ break;
356
+ case "from-triage":
357
+ cmdFromTriage();
358
+ break;
359
+ case "forget":
360
+ cmdForget();
361
+ break;
362
+ case "brief":
363
+ cmdBrief();
364
+ break;
365
+ case "path":
366
+ cmdPath();
367
+ break;
368
+ case "stats":
369
+ cmdStats();
370
+ break;
288
371
  default:
289
- process.stderr.write("usage: learnings-ledger.mjs <add|from-triage|forget|brief|path|stats> [flags]\n");
372
+ process.stderr.write(
373
+ "usage: learnings-ledger.mjs <add|from-triage|forget|brief|path|stats> [flags]\n",
374
+ );
290
375
  process.exit(64);
291
376
  }
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+ // lint-mcp-refs.mjs - every mcp__<server>__<tool> reference must be callable.
3
+ //
4
+ // A host composes an MCP tool name as mcp__<registered-server-name>__<tool>.
5
+ // The pipeline had 100 references to `mcp__dev_toolkit__*` while the server is
6
+ // registered as `dev-toolkit`, so the real names are `mcp__dev-toolkit__*`.
7
+ // Nothing caught it: prose is not executed, and a wrong name inside an
8
+ // `allowed-tools:` allowlist silently denies the tool instead of erroring.
9
+ //
10
+ // Checks (errors - fail the gate):
11
+ // 1. the server segment must be a known registered server name
12
+ // 2. the known-bad snake_case spellings are called out with their fix
13
+ //
14
+ // Optional deep check (advisory - never fails):
15
+ // 3. when the companion dev-toolkit repo resolves locally (via
16
+ // prefs.global.devToolkit.localPath, or the mcpServers registration in
17
+ // ~/.claude.json / ~/.claude/settings.json), handshake it over stdio and
18
+ // confirm every referenced dev-toolkit tool actually exists in tools/list.
19
+ // CI has no local checkout, so an unresolvable repo is a skip, not a fail.
20
+ //
21
+ // Zero-dependency. Runs inside `npm test` and ci-lite.
22
+ //
23
+ // Usage: node pipeline/scripts/lint-mcp-refs.mjs [--strict-tools]
24
+ // Exit: 0 clean, 1 unknown server segment (or a missing tool with --strict-tools).
25
+
26
+ import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
27
+ import { homedir } from "node:os";
28
+ import { dirname, join, resolve } from "node:path";
29
+ import { fileURLToPath } from "node:url";
30
+ import { spawnSync } from "node:child_process";
31
+
32
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
33
+ const strictTools = process.argv.includes("--strict-tools");
34
+
35
+ // Servers the pipeline is allowed to reference. The key is the registered
36
+ // server name as it appears in an mcpServers config, which is what the host
37
+ // interpolates into the tool name.
38
+ const KNOWN_SERVERS = {
39
+ "dev-toolkit": "companion device/browser toolkit (@mmerterden/dev-toolkit-mcp)",
40
+ claude_ai_Figma: "Figma MCP (analysis phase only)",
41
+ XcodeBuildMCP: "optional third-party Xcode build server",
42
+ };
43
+ // Spellings that look right but cannot resolve to a registered server.
44
+ const KNOWN_BAD = { dev_toolkit: "dev-toolkit", "claude-ai-Figma": "claude_ai_Figma" };
45
+
46
+ const SCAN_DIRS = ["pipeline/commands", "pipeline/skills", "pipeline/multi-agent-refs"];
47
+ const SCAN_EXT = /\.(md|sh|mjs|json)$/;
48
+ const PRUNE = new Set([
49
+ "node_modules",
50
+ ".git",
51
+ ".worktrees",
52
+ "Pods",
53
+ ".build",
54
+ "DerivedData",
55
+ ".next",
56
+ ]);
57
+
58
+ function walk(dir, out = []) {
59
+ let entries;
60
+ try {
61
+ entries = readdirSync(dir, { withFileTypes: true });
62
+ } catch {
63
+ return out;
64
+ }
65
+ for (const e of entries) {
66
+ if (PRUNE.has(e.name)) continue;
67
+ const p = join(dir, e.name);
68
+ if (e.isDirectory()) walk(p, out);
69
+ else if (SCAN_EXT.test(e.name)) out.push(p);
70
+ }
71
+ return out;
72
+ }
73
+
74
+ const files = SCAN_DIRS.flatMap((d) => walk(join(repoRoot, d)));
75
+ const REF = /mcp__([A-Za-z0-9_-]+?)__([a-z0-9_]+)/g;
76
+
77
+ const errors = [];
78
+ const refs = new Map(); // server -> Map(tool -> Set(file:line))
79
+
80
+ for (const file of files) {
81
+ const rel = file.slice(repoRoot.length + 1);
82
+ const lines = readFileSync(file, "utf8").split("\n");
83
+ lines.forEach((line, i) => {
84
+ for (const m of line.matchAll(REF)) {
85
+ const [, server, tool] = m;
86
+ // Prose writes families as mcp__dev-toolkit__ios_* - a wildcard, not a tool.
87
+ const next = line[m.index + m[0].length];
88
+ if (next === "*" || tool.endsWith("_")) continue;
89
+ const where = `${rel}:${i + 1}`;
90
+ if (!refs.has(server)) refs.set(server, new Map());
91
+ const tools = refs.get(server);
92
+ if (!tools.has(tool)) tools.set(tool, new Set());
93
+ tools.get(tool).add(where);
94
+ }
95
+ });
96
+ }
97
+
98
+ for (const [server, tools] of refs) {
99
+ if (server in KNOWN_SERVERS) continue;
100
+ const fix = KNOWN_BAD[server];
101
+ const sample = [...tools.values()][0].values().next().value;
102
+ errors.push(
103
+ fix
104
+ ? `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - the registered server is "${fix}", so the prefix must be mcp__${fix}__`
105
+ : `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - add it to KNOWN_SERVERS in lint-mcp-refs.mjs if this server is expected, or fix the prefix to match the registered server name`,
106
+ );
107
+ }
108
+
109
+ // ---- optional deep check: do the referenced dev-toolkit tools exist? --------
110
+
111
+ function resolveToolkitRepo() {
112
+ const prefsPath = join(homedir(), ".claude", "multi-agent-preferences.json");
113
+ if (existsSync(prefsPath)) {
114
+ try {
115
+ const dt = JSON.parse(readFileSync(prefsPath, "utf8"))?.global?.devToolkit;
116
+ if (dt && dt.enabled === false) return null;
117
+ if (dt?.localPath) {
118
+ const p = dt.localPath.replace(/^~|\$HOME/, homedir());
119
+ if (existsSync(join(p, "package.json"))) return p;
120
+ }
121
+ } catch {
122
+ /* fall through to registration lookup */
123
+ }
124
+ }
125
+ const candidates = [];
126
+ const collect = (obj) => {
127
+ for (const [name, cfg] of Object.entries(obj || {})) {
128
+ const arg = Array.isArray(cfg?.args) ? cfg.args.find((a) => /\.(mjs|js)$/.test(a)) : null;
129
+ if (name.includes("dev") && arg) candidates.push(dirname(resolve(arg)));
130
+ }
131
+ };
132
+ for (const cfgPath of [
133
+ join(homedir(), ".claude.json"),
134
+ join(homedir(), ".claude", "settings.json"),
135
+ ]) {
136
+ if (!existsSync(cfgPath)) continue;
137
+ try {
138
+ const json = JSON.parse(readFileSync(cfgPath, "utf8"));
139
+ collect(json.mcpServers);
140
+ for (const proj of Object.values(json.projects || {})) collect(proj.mcpServers);
141
+ } catch {
142
+ /* ignore unreadable config */
143
+ }
144
+ }
145
+ for (const c of candidates) {
146
+ try {
147
+ const pkg = JSON.parse(readFileSync(join(c, "package.json"), "utf8"));
148
+ if (pkg.dependencies?.["@modelcontextprotocol/sdk"] && statSync(join(c, ".git"))) return c;
149
+ } catch {
150
+ /* not the toolkit */
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+
156
+ function listTools(repo) {
157
+ const frames =
158
+ [
159
+ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"lint-mcp-refs","version":"1"}}}',
160
+ '{"jsonrpc":"2.0","method":"notifications/initialized"}',
161
+ '{"jsonrpc":"2.0","id":2,"method":"tools/list"}',
162
+ ].join("\n") + "\n";
163
+ const r = spawnSync(process.execPath, ["index.js"], {
164
+ cwd: repo,
165
+ input: frames,
166
+ encoding: "utf8",
167
+ timeout: 20000,
168
+ maxBuffer: 32 * 1024 * 1024,
169
+ });
170
+ if (r.error || !r.stdout) return null;
171
+ for (const line of r.stdout.split("\n")) {
172
+ if (!line.trim()) continue;
173
+ try {
174
+ const msg = JSON.parse(line);
175
+ if (msg.id === 2 && msg.result?.tools) return msg.result.tools.map((t) => t.name);
176
+ } catch {
177
+ /* not a frame */
178
+ }
179
+ }
180
+ return null;
181
+ }
182
+
183
+ const referencedToolkitTools = refs.get("dev-toolkit");
184
+ let deep = "skipped (no local dev-toolkit checkout)";
185
+ if (referencedToolkitTools) {
186
+ const repo = resolveToolkitRepo();
187
+ const served = repo ? listTools(repo) : null;
188
+ if (!served) {
189
+ deep = repo ? `skipped (${repo} did not answer tools/list)` : deep;
190
+ } else {
191
+ const missing = [...referencedToolkitTools.keys()].filter((t) => !served.includes(t));
192
+ if (missing.length === 0) {
193
+ deep = `all ${referencedToolkitTools.size} referenced tools exist in the live server (${served.length} served)`;
194
+ } else {
195
+ const msg = `dev-toolkit tools referenced but not served (${served.length} served): ${missing
196
+ .map((t) => `${t} [${[...referencedToolkitTools.get(t)][0]}]`)
197
+ .join(", ")}`;
198
+ deep = `${missing.length} referenced tool(s) not served`;
199
+ if (strictTools) errors.push(msg);
200
+ else console.warn(` WARNING - ${msg}`);
201
+ }
202
+ }
203
+ }
204
+
205
+ const totalRefs = [...refs.values()].reduce((n, tools) => n + tools.size, 0);
206
+ console.log(
207
+ `lint-mcp-refs: ${totalRefs} distinct tool references across ${refs.size} server(s) in ${files.length} files`,
208
+ );
209
+ for (const [server, tools] of refs) {
210
+ console.log(` ${server in KNOWN_SERVERS ? "✓" : "✗"} mcp__${server}__* : ${tools.size} tools`);
211
+ }
212
+ console.log(` tool-existence check: ${deep}`);
213
+
214
+ if (errors.length > 0) {
215
+ console.error(`lint-mcp-refs: ${errors.length} error(s)`);
216
+ for (const e of errors) console.error(" " + e);
217
+ process.exit(1);
218
+ }
@@ -14,12 +14,12 @@
14
14
  // - description: required, non-empty, single line, <= 1024 chars
15
15
  // - user-invocable: when present, must be true or false
16
16
  // - platform: when present, must be ios | android | common | all
17
+ // - description: must carry a routing/trigger clause ("use when ..."), the
18
+ // one thing it is for - blocking since all 193 skills have one
17
19
  //
18
20
  // Spec-conformance signals (warnings - never fail the gate):
19
21
  // - name should match the agent-skills spec pattern
20
22
  // (lowercase letters/digits/hyphens, <= 64 chars)
21
- // - description should carry a routing/trigger clause ("use when ...")
22
- // so the model can decide when to load the skill
23
23
  // - a body over ~500 lines with no companion-file reference should be
24
24
  // split into references/ files loaded on demand
25
25
 
@@ -99,12 +99,19 @@ for (const file of files) {
99
99
  if (!isBlockScalar && words < 6) {
100
100
  warn(file, `description is terse (${words} words) - say what it does AND when to use it`);
101
101
  }
102
- // Routing quality (warn-only): without a trigger clause the model has no
103
- // signal for WHEN to load the skill. Heuristic phrase match; block
104
- // scalars are skipped for the same parser reason as above.
105
- const ROUTING_RE = /\b(use (this|it|when|for|whenever)|when (the|a|an|you|asked|working|building|implementing|writing|reviewing)|triggers?|invoked?( when| for)?|applies when|activated?)\b/i;
102
+ // Routing quality (BLOCKING since the backlog reached zero). A description
103
+ // without a trigger clause tells the model what the skill is and nothing
104
+ // about when to reach for it, which is the one job the description has -
105
+ // it is the routing rule, not a summary.
106
+ //
107
+ // This was warn-only while 93 of 193 skills were missing one, because a
108
+ // gate that fires on half the tree gets ignored. All 93 were written, so
109
+ // the baseline is zero and it can hold the line. Block scalars are skipped
110
+ // for the same parser reason as above.
111
+ const ROUTING_RE =
112
+ /\b(use (this|it|when|for|whenever)|when (the|a|an|you|asked|working|building|implementing|writing|reviewing)|triggers?|invoked?( when| for)?|applies when|activated?)\b/i;
106
113
  if (!isBlockScalar && !ROUTING_RE.test(desc)) {
107
- warn(file, "description has no routing/trigger clause (e.g. \"use when ...\")");
114
+ fail(file, 'description has no routing/trigger clause (e.g. "use when ...")');
108
115
  }
109
116
  }
110
117
 
@@ -115,9 +122,13 @@ for (const file of files) {
115
122
  const bodyLines = body.split("\n").length;
116
123
  if (bodyLines > 500) {
117
124
  const referencesCompanion =
118
- /\]\((?!https?:|mailto:|#)[^)]+\)/.test(body) || /`[^`\n]*\/[^`\n]*\.(md|sh|mjs|json|py|txt)`/.test(body);
125
+ /\]\((?!https?:|mailto:|#)[^)]+\)/.test(body) ||
126
+ /`[^`\n]*\/[^`\n]*\.(md|sh|mjs|json|py|txt)`/.test(body);
119
127
  if (!referencesCompanion) {
120
- warn(file, `body is ${bodyLines} lines with no companion-file reference - consider splitting into references/`);
128
+ warn(
129
+ file,
130
+ `body is ${bodyLines} lines with no companion-file reference - consider splitting into references/`,
131
+ );
121
132
  }
122
133
  }
123
134
 
@@ -116,7 +116,12 @@ if (invokedDirectly) {
116
116
  console.error("usage: localize-commands.mjs <apply|restore> [--dir <commandsDir>]");
117
117
  process.exit(2);
118
118
  }
119
- let dir = join(process.env.HOME || process.env.USERPROFILE || "", ".claude", "commands", "multi-agent");
119
+ let dir = join(
120
+ process.env.HOME || process.env.USERPROFILE || "",
121
+ ".claude",
122
+ "commands",
123
+ "multi-agent",
124
+ );
120
125
  const dirFlag = args.indexOf("--dir");
121
126
  if (dirFlag >= 0) {
122
127
  if (!args[dirFlag + 1]) {
@@ -38,7 +38,9 @@ const repoRoot = resolve(here, "..", "..");
38
38
 
39
39
  const args = process.argv.slice(2);
40
40
  if (args.length === 0 || args[0].startsWith("--")) {
41
- console.error('Usage: match-skills.mjs "<task text>" [--touched-files a,b] [--stack ios] [--limit 12] [--json]');
41
+ console.error(
42
+ 'Usage: match-skills.mjs "<task text>" [--touched-files a,b] [--stack ios] [--limit 12] [--json]',
43
+ );
42
44
  process.exit(1);
43
45
  }
44
46
 
@@ -51,7 +53,11 @@ const opts = {
51
53
  json: false,
52
54
  };
53
55
  for (let i = 1; i < args.length; i++) {
54
- if (args[i] === "--touched-files") opts.touchedFiles = (args[++i] || "").split(",").map((s) => s.trim()).filter(Boolean);
56
+ if (args[i] === "--touched-files")
57
+ opts.touchedFiles = (args[++i] || "")
58
+ .split(",")
59
+ .map((s) => s.trim())
60
+ .filter(Boolean);
55
61
  else if (args[i] === "--stack") opts.stack = (args[++i] || "").trim().toLowerCase();
56
62
  else if (args[i] === "--limit") opts.limit = parseInt(args[++i], 10) || 12;
57
63
  else if (args[i] === "--index") opts.indexPath = resolve(args[++i]);
@@ -86,8 +92,13 @@ function globToRe(glob) {
86
92
  // `**/` means zero-or-more path segments, so a leading `**/` must also
87
93
  // match top-level files (no dir prefix). Without the optional slash,
88
94
  // `**/Auth/**` compiled to `.*\/Auth\/.*` and missed root-level `Auth/...`.
89
- if (glob[i + 2] === "/") { re += "(?:.*/)?"; i += 2; }
90
- else { re += ".*"; i++; }
95
+ if (glob[i + 2] === "/") {
96
+ re += "(?:.*/)?";
97
+ i += 2;
98
+ } else {
99
+ re += ".*";
100
+ i++;
101
+ }
91
102
  } else if (c === "*") re += "[^/]*";
92
103
  else if (c === "?") re += ".";
93
104
  else if (".+()|{}[]^$\\".includes(c)) re += "\\" + c;
@@ -16,9 +16,13 @@ PROJECT_ROOT="${1:?usage: memory-load.sh <project-root>}"
16
16
  MEM_DIR="$PROJECT_ROOT/.multi-agent/memory"
17
17
  MEM_INDEX="$MEM_DIR/MEMORY.md"
18
18
 
19
- # Pref-gate: locate prefs.json (home-dir first, then project-specific).
19
+ # Pref-gate: locate the preferences file (home-dir first, then XDG config).
20
+ # The canonical name written by the installer is multi-agent-preferences.json;
21
+ # the bare preferences.json names are kept only as legacy fallbacks.
20
22
  prefs_file=""
21
23
  for candidate in \
24
+ "$HOME/.claude/multi-agent-preferences.json" \
25
+ "$HOME/.config/multi-agent-pipeline/multi-agent-preferences.json" \
22
26
  "$HOME/.claude/preferences.json" \
23
27
  "$HOME/.config/multi-agent-pipeline/preferences.json"
24
28
  do