@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
@@ -64,10 +64,7 @@ export const PIPELINE_AGENT_FILES = [
64
64
  * Non-multi-agent core skills the installer ships from
65
65
  * pipeline/skills/shared/core/ (the multi-agent* dirs are matched by prefix).
66
66
  */
67
- export const PIPELINE_CORE_SKILL_DIRS = [
68
- "apple-archive-compliance",
69
- "google-play-compliance",
70
- ];
67
+ export const PIPELINE_CORE_SKILL_DIRS = ["apple-archive-compliance", "google-play-compliance"];
71
68
 
72
69
  const flags = process.argv.slice(2).filter((a) => a !== "uninstall");
73
70
 
@@ -79,14 +76,15 @@ const TOOL_FLAGS = [
79
76
  "--antigravity",
80
77
  "--codex",
81
78
  ];
82
- const explicitlyTargeted =
83
- flags.some((f) => TOOL_FLAGS.includes(f)) || flags.includes("--all");
79
+ const explicitlyTargeted = flags.some((f) => TOOL_FLAGS.includes(f)) || flags.includes("--all");
84
80
 
85
81
  const forClaude = flags.includes("--claude") || flags.includes("--all") || !explicitlyTargeted;
86
82
  const forCopilot = flags.includes("--copilot") || flags.includes("--all") || !explicitlyTargeted;
87
83
  const forCursor = flags.includes("--cursor") || flags.includes("--all") || !explicitlyTargeted;
88
- const forCopilotChat = flags.includes("--copilot-chat") || flags.includes("--all") || !explicitlyTargeted;
89
- const forAntigravity = flags.includes("--antigravity") || flags.includes("--all") || !explicitlyTargeted;
84
+ const forCopilotChat =
85
+ flags.includes("--copilot-chat") || flags.includes("--all") || !explicitlyTargeted;
86
+ const forAntigravity =
87
+ flags.includes("--antigravity") || flags.includes("--all") || !explicitlyTargeted;
90
88
  const forCodex = flags.includes("--codex") || flags.includes("--all") || !explicitlyTargeted;
91
89
 
92
90
  const dryRun = flags.includes("--dry-run");
@@ -194,8 +192,7 @@ function legacyTrailingContent(section) {
194
192
  function stripManagedBlock(filePath) {
195
193
  if (!existsSync(filePath)) return false;
196
194
  const content = readFileSync(filePath, "utf-8");
197
- const re =
198
- /\s*<!-- multi-agent-pipeline:begin -->[\s\S]*?<!-- multi-agent-pipeline:end -->\s*/m;
195
+ const re = /\s*<!-- multi-agent-pipeline:begin -->[\s\S]*?<!-- multi-agent-pipeline:end -->\s*/m;
199
196
  if (!re.test(content)) {
200
197
  // Copilot's install (install/copilot.mjs) does not wrap the block in
201
198
  // begin/end markers. v11.4.1+ writes an explicit end marker after the
@@ -276,8 +273,10 @@ function cleanClaudeSettings(settingsPath) {
276
273
  // entry as ours either way.
277
274
  settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
278
275
  (h) =>
279
- !((h.matcher === "Bash" || h.matcher === "Bash(git commit:*)") &&
280
- h.hooks?.some((sub) => sub.command?.includes("pre-commit-check.sh"))),
276
+ !(
277
+ (h.matcher === "Bash" || h.matcher === "Bash(git commit:*)") &&
278
+ h.hooks?.some((sub) => sub.command?.includes("pre-commit-check.sh"))
279
+ ),
281
280
  );
282
281
  if (settings.hooks.PreToolUse.length < before) touched = true;
283
282
  if (settings.hooks.PreToolUse.length === 0) delete settings.hooks.PreToolUse;
@@ -305,7 +304,7 @@ async function confirm(question) {
305
304
  });
306
305
  }
307
306
 
308
- async function main() {
307
+ export async function main() {
309
308
  console.log("");
310
309
  console.log(" multi-agent-pipeline uninstaller");
311
310
  console.log(" ================================");
@@ -313,12 +312,28 @@ async function main() {
313
312
  if (dryRun) console.log(" [DRY-RUN] Nothing will be modified.");
314
313
  console.log("");
315
314
  console.log(" Targets:");
316
- if (forClaude) console.log(" - Claude Code (~/.claude/{commands/multi-agent, scripts, multi-agent-refs, schemas, lib} + pipeline skills + pipeline agent files)");
317
- if (forCopilot) console.log(" - Copilot CLI (~/.copilot/{scripts, schemas, lib} + pipeline skills + pipeline agent files + copilot-instructions.md section)");
318
- if (forCursor) console.log(` - Cursor (${adapterTarget}/.cursor/rules/multi-agent-* + .cursorrules)`);
319
- if (forCopilotChat) console.log(` - GitHub Copilot Chat (${adapterTarget}/.github/copilot-instructions.md block + .github/instructions/multi-agent-*)`);
320
- if (forAntigravity) console.log(` - Antigravity (${adapterTarget}/.agent/rules + .agent/workflows + AGENTS.md block + .agent/mcp_config.json)`);
321
- if (forCodex) console.log(" - OpenAI Codex CLI (~/.codex/prompts/multi-agent.md + AGENTS.md block + config.toml mcp block)");
315
+ if (forClaude)
316
+ console.log(
317
+ " - Claude Code (~/.claude/{commands/multi-agent, scripts, multi-agent-refs, schemas, lib} + pipeline skills + pipeline agent files)",
318
+ );
319
+ if (forCopilot)
320
+ console.log(
321
+ " - Copilot CLI (~/.copilot/{scripts, schemas, lib} + pipeline skills + pipeline agent files + copilot-instructions.md section)",
322
+ );
323
+ if (forCursor)
324
+ console.log(` - Cursor (${adapterTarget}/.cursor/rules/multi-agent-* + .cursorrules)`);
325
+ if (forCopilotChat)
326
+ console.log(
327
+ ` - GitHub Copilot Chat (${adapterTarget}/.github/copilot-instructions.md block + .github/instructions/multi-agent-*)`,
328
+ );
329
+ if (forAntigravity)
330
+ console.log(
331
+ ` - Antigravity (${adapterTarget}/.agent/rules + .agent/workflows + AGENTS.md block + .agent/mcp_config.json)`,
332
+ );
333
+ if (forCodex)
334
+ console.log(
335
+ " - OpenAI Codex CLI (~/.codex/prompts/multi-agent.md + AGENTS.md block + config.toml mcp block)",
336
+ );
322
337
  console.log("");
323
338
  if (allData) {
324
339
  console.log(" FULL CLEANUP (--all-data) also removes:");
@@ -327,7 +342,9 @@ async function main() {
327
342
  console.log("");
328
343
  }
329
344
  console.log(" PRESERVED (never touched):");
330
- console.log(" - Personal access tokens in OS credential store (Keychain / Credential Manager / libsecret)");
345
+ console.log(
346
+ " - Personal access tokens in OS credential store (Keychain / Credential Manager / libsecret)",
347
+ );
331
348
  console.log(" - ~/.claude/CLAUDE.md (your customizations)");
332
349
  console.log(" - ~/.claude/rules/ (user-owned; installed write-if-missing, never overwritten)");
333
350
  if (!allData) console.log(" - ~/.claude/multi-agent-preferences.json (your settings)");
@@ -357,8 +374,18 @@ async function main() {
357
374
  // (e.g. the git-attribution rule) -- asymmetric data loss. Leave rules
358
375
  // in place on uninstall, exactly like CLAUDE.md and preferences.
359
376
  const skills = join(CLAUDE, "skills");
360
- let n = rmMatchingDirs(skills, (name) => name === "multi-agent" || name.startsWith("multi-agent-"));
361
- n += rmMatchingDirs(skills, (name) => name === "figma-ios" || name === "figma-android" || name === "figma-common" || name === "figma-to-component");
377
+ let n = rmMatchingDirs(
378
+ skills,
379
+ (name) => name === "multi-agent" || name.startsWith("multi-agent-"),
380
+ );
381
+ n += rmMatchingDirs(
382
+ skills,
383
+ (name) =>
384
+ name === "figma-ios" ||
385
+ name === "figma-android" ||
386
+ name === "figma-common" ||
387
+ name === "figma-to-component",
388
+ );
362
389
  n += rmMatchingDirs(skills, (name) => PIPELINE_CORE_SKILL_DIRS.includes(name));
363
390
  if (n > 0) console.log(` removed ${n} skill dir(s) under ${skills}`);
364
391
  rmIfExists(join(skills, ".skills-index.json"));
@@ -380,8 +407,18 @@ async function main() {
380
407
  rmIfExists(join(COP, "lib"));
381
408
  rmIfExists(join(COP, ".pipeline-version"));
382
409
  const skills = join(COP, "skills");
383
- let n = rmMatchingDirs(skills, (name) => name === "multi-agent" || name.startsWith("multi-agent-"));
384
- n += rmMatchingDirs(skills, (name) => name === "figma-ios" || name === "figma-android" || name === "figma-common" || name === "figma-to-component");
410
+ let n = rmMatchingDirs(
411
+ skills,
412
+ (name) => name === "multi-agent" || name.startsWith("multi-agent-"),
413
+ );
414
+ n += rmMatchingDirs(
415
+ skills,
416
+ (name) =>
417
+ name === "figma-ios" ||
418
+ name === "figma-android" ||
419
+ name === "figma-common" ||
420
+ name === "figma-to-component",
421
+ );
385
422
  n += rmMatchingDirs(skills, (name) => PIPELINE_CORE_SKILL_DIRS.includes(name));
386
423
  if (n > 0) console.log(` removed ${n} skill dir(s) under ${skills}`);
387
424
  rmIfExists(join(skills, ".skills-index.json"));
@@ -434,7 +471,9 @@ async function main() {
434
471
  if (n > 0) console.log(` removed ${n} .agent file(s)`);
435
472
  stripManagedBlock(join(adapterTarget, "AGENTS.md"));
436
473
  if (existsSync(join(adapterTarget, ".agent", "mcp_config.json")))
437
- console.log(" note: .agent/mcp_config.json left untouched (may hold user servers) - remove pipeline entries manually if present");
474
+ console.log(
475
+ " note: .agent/mcp_config.json left untouched (may hold user servers) - remove pipeline entries manually if present",
476
+ );
438
477
  }
439
478
 
440
479
  if (forCodex && HOME) {
@@ -444,23 +483,29 @@ async function main() {
444
483
  rmIfExists(join(CODEX, "prompts", "multi-agent.md"));
445
484
  stripManagedBlock(join(CODEX, "AGENTS.md"));
446
485
  if (existsSync(join(CODEX, "config.toml")))
447
- console.log(" note: ~/.codex/config.toml left untouched (may hold user MCP servers) - remove pipeline entries manually if present");
486
+ console.log(
487
+ " note: ~/.codex/config.toml left untouched (may hold user MCP servers) - remove pipeline entries manually if present",
488
+ );
448
489
  }
449
490
 
450
491
  console.log("");
451
492
  console.log(" ✓ Pipeline removed.");
452
493
  if (!allData) {
453
494
  console.log("");
454
- console.log(" Kept your pipeline settings + logs. To remove those too, re-run with --all-data.");
495
+ console.log(
496
+ " Kept your pipeline settings + logs. To remove those too, re-run with --all-data.",
497
+ );
455
498
  } else {
456
499
  console.log("");
457
- console.log(" Full cleanup done. Still on disk (user-owned, remove by hand if you want a bare system):");
500
+ console.log(
501
+ " Full cleanup done. Still on disk (user-owned, remove by hand if you want a bare system):",
502
+ );
458
503
  console.log(" - ~/.claude/CLAUDE.md");
459
504
  console.log(" - ~/.claude/rules/");
460
505
  }
461
506
  console.log("");
462
507
  console.log(" Personal access tokens were NOT touched. Inspect with:");
463
- console.log(" macOS: security find-generic-password -s \"<service>\" -w");
508
+ console.log(' macOS: security find-generic-password -s "<service>" -w');
464
509
  console.log(" Windows: cmdkey /list:<target>");
465
510
  console.log(" Linux: secret-tool search service <service>");
466
511
  console.log("");
@@ -470,8 +515,7 @@ async function main() {
470
515
 
471
516
  // Run only when executed directly (node uninstall.mjs). Guard lets tests
472
517
  // import the exported constants without triggering the uninstall.
473
- const isMainModule =
474
- process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
518
+ const isMainModule = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
475
519
  if (isMainModule) {
476
520
  main().catch((e) => {
477
521
  console.error("uninstall failed:", e.message);
@@ -131,7 +131,9 @@ function main() {
131
131
  headingBlobLower.includes("ui test") ||
132
132
  headingBlobLower.includes("ui-test");
133
133
  if (!hasUi) {
134
- errors.push("front-matter ui_tests: true but no Section 15.6 UI test scenarios found (Locked 31)");
134
+ errors.push(
135
+ "front-matter ui_tests: true but no Section 15.6 UI test scenarios found (Locked 31)",
136
+ );
135
137
  }
136
138
  }
137
139
  if (a11yDepth === "full") {
@@ -140,7 +142,9 @@ function main() {
140
142
  headingBlobLower.includes("walkthrough") ||
141
143
  headingBlobLower.includes("gezinme");
142
144
  if (!hasWalkthrough) {
143
- errors.push("front-matter a11y_depth: full but no Section 16.2 screen-reader walkthrough found");
145
+ errors.push(
146
+ "front-matter a11y_depth: full but no Section 16.2 screen-reader walkthrough found",
147
+ );
144
148
  }
145
149
  }
146
150
 
@@ -156,7 +160,9 @@ function main() {
156
160
 
157
161
  // 4. Placeholder whole-section bodies (Locked 2)
158
162
  if (/^\s*(TBD|Not applicable|N\/A)\s*$/im.test(text)) {
159
- warns.push('a line is a bare "TBD"/"Not applicable"/"N/A" body; omit the section instead (Locked 2)');
163
+ warns.push(
164
+ 'a line is a bare "TBD"/"Not applicable"/"N/A" body; omit the section instead (Locked 2)',
165
+ );
160
166
  }
161
167
 
162
168
  // 5. Traceability (Full mode only, Locked 31)
@@ -173,7 +179,9 @@ function main() {
173
179
  }
174
180
  for (const [id, n] of counts) {
175
181
  if (n < 2) {
176
- warns.push(`business rule ${id} is defined but never referenced downstream (test/acceptance/analytics) - Locked 31 traceability`);
182
+ warns.push(
183
+ `business rule ${id} is defined but never referenced downstream (test/acceptance/analytics) - Locked 31 traceability`,
184
+ );
177
185
  }
178
186
  }
179
187
  }
@@ -182,7 +190,9 @@ function main() {
182
190
  for (const w of warns) console.error(`WARN: ${w}`);
183
191
  for (const e of errors) console.error(`ERROR: ${e}`);
184
192
  if (errors.length > 0) {
185
- console.error(`validate-analysis-doc: FAIL (${errors.length} error(s), ${warns.length} warning(s))`);
193
+ console.error(
194
+ `validate-analysis-doc: FAIL (${errors.length} error(s), ${warns.length} warning(s))`,
195
+ );
186
196
  process.exit(1);
187
197
  }
188
198
  if (strict && warns.length > 0) {
@@ -49,22 +49,27 @@ function validate(obj) {
49
49
  if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
50
50
  return ["root must be an object"];
51
51
  }
52
- if (obj.schemaVersion !== "1.1.0") errors.push(`schemaVersion must be "1.1.0", got ${JSON.stringify(obj.schemaVersion)}`);
52
+ if (obj.schemaVersion !== "1.1.0")
53
+ errors.push(`schemaVersion must be "1.1.0", got ${JSON.stringify(obj.schemaVersion)}`);
53
54
 
54
55
  if (typeof obj.task !== "object" || obj.task === null) {
55
56
  errors.push("task must be an object");
56
57
  } else {
57
- if (typeof obj.task.base !== "string" || !obj.task.base.length) errors.push("task.base must be a non-empty string");
58
- if (typeof obj.task.head !== "string" || !obj.task.head.length) errors.push("task.head must be a non-empty string");
58
+ if (typeof obj.task.base !== "string" || !obj.task.base.length)
59
+ errors.push("task.base must be a non-empty string");
60
+ if (typeof obj.task.head !== "string" || !obj.task.head.length)
61
+ errors.push("task.head must be a non-empty string");
59
62
  }
60
63
 
61
64
  if (typeof obj.totals !== "object" || obj.totals === null) {
62
65
  errors.push("totals must be an object");
63
66
  } else {
64
67
  for (const k of ["files", "loc_added", "loc_removed"]) {
65
- if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0) errors.push(`totals.${k} must be a non-negative integer`);
68
+ if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
69
+ errors.push(`totals.${k} must be a non-negative integer`);
66
70
  }
67
- if (typeof obj.totals.max_score !== "number" || obj.totals.max_score < 0) errors.push("totals.max_score must be a non-negative number");
71
+ if (typeof obj.totals.max_score !== "number" || obj.totals.max_score < 0)
72
+ errors.push("totals.max_score must be a non-negative number");
68
73
  }
69
74
 
70
75
  if (!Array.isArray(obj.files)) {
@@ -73,25 +78,33 @@ function validate(obj) {
73
78
  obj.files.forEach((row, i) => {
74
79
  const p = `files[${i}]`;
75
80
  if (typeof row !== "object" || row === null) {
76
- errors.push(`${p} must be an object`); return;
81
+ errors.push(`${p} must be an object`);
82
+ return;
77
83
  }
78
- if (typeof row.path !== "string" || !row.path.length) errors.push(`${p}.path must be a non-empty string`);
79
- if (typeof row.score !== "number" || row.score < 0) errors.push(`${p}.score must be a non-negative number`);
84
+ if (typeof row.path !== "string" || !row.path.length)
85
+ errors.push(`${p}.path must be a non-empty string`);
86
+ if (typeof row.score !== "number" || row.score < 0)
87
+ errors.push(`${p}.score must be a non-negative number`);
80
88
  for (const k of ["loc_added", "loc_removed"]) {
81
- if (!Number.isInteger(row[k]) || row[k] < 0) errors.push(`${p}.${k} must be a non-negative integer`);
89
+ if (!Number.isInteger(row[k]) || row[k] < 0)
90
+ errors.push(`${p}.${k} must be a non-negative integer`);
82
91
  }
83
92
  if (!Array.isArray(row.signals)) errors.push(`${p}.signals must be an array`);
84
- else row.signals.forEach((s, j) => {
85
- const sp = `${p}.signals[${j}]`;
86
- if (!ALLOWED_SIGNALS.has(s.name)) errors.push(`${sp}.name "${s.name}" not in allowed set`);
87
- if (typeof s.weight !== "number") errors.push(`${sp}.weight must be a number`);
88
- if (typeof s.value !== "number") errors.push(`${sp}.value must be a number`);
89
- });
93
+ else
94
+ row.signals.forEach((s, j) => {
95
+ const sp = `${p}.signals[${j}]`;
96
+ if (!ALLOWED_SIGNALS.has(s.name))
97
+ errors.push(`${sp}.name "${s.name}" not in allowed set`);
98
+ if (typeof s.weight !== "number") errors.push(`${sp}.weight must be a number`);
99
+ if (typeof s.value !== "number") errors.push(`${sp}.value must be a number`);
100
+ });
90
101
  });
91
102
  // Sort invariant
92
103
  for (let i = 1; i < obj.files.length; i++) {
93
104
  if (obj.files[i].score > obj.files[i - 1].score) {
94
- errors.push(`files[] must be sorted by score descending (row ${i} score ${obj.files[i].score} > row ${i - 1} score ${obj.files[i - 1].score})`);
105
+ errors.push(
106
+ `files[] must be sorted by score descending (row ${i} score ${obj.files[i].score} > row ${i - 1} score ${obj.files[i - 1].score})`,
107
+ );
95
108
  break;
96
109
  }
97
110
  }
@@ -103,8 +116,9 @@ function validate(obj) {
103
116
  (async () => {
104
117
  const raw = await readInput();
105
118
  let obj;
106
- try { obj = JSON.parse(raw); }
107
- catch (e) {
119
+ try {
120
+ obj = JSON.parse(raw);
121
+ } catch (e) {
108
122
  console.log(JSON.stringify({ ok: false, errors: [`JSON parse error: ${e.message}`] }));
109
123
  process.exit(1);
110
124
  }
@@ -34,15 +34,18 @@ function readInput() {
34
34
 
35
35
  function validate(obj) {
36
36
  const errors = [];
37
- if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return ["root must be an object"];
37
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj))
38
+ return ["root must be an object"];
38
39
  if (obj.schemaVersion !== "1.0.0") errors.push(`schemaVersion must be "1.0.0"`);
39
- if (!ALLOWED_STACK.has(obj.stack)) errors.push(`stack must be one of ${Array.from(ALLOWED_STACK).join(",")}`);
40
+ if (!ALLOWED_STACK.has(obj.stack))
41
+ errors.push(`stack must be one of ${Array.from(ALLOWED_STACK).join(",")}`);
40
42
 
41
43
  if (typeof obj.totals !== "object" || obj.totals === null) {
42
44
  errors.push("totals must be an object");
43
45
  } else {
44
46
  for (const k of ["sourcesScanned", "testsScanned", "gapCount"]) {
45
- if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0) errors.push(`totals.${k} must be a non-negative integer`);
47
+ if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
48
+ errors.push(`totals.${k} must be a non-negative integer`);
46
49
  }
47
50
  if (typeof obj.totals.gapBySeverity !== "object" || obj.totals.gapBySeverity === null) {
48
51
  errors.push("totals.gapBySeverity must be an object");
@@ -60,12 +63,17 @@ function validate(obj) {
60
63
  } else {
61
64
  obj.gaps.forEach((g, i) => {
62
65
  const p = `gaps[${i}]`;
63
- if (typeof g !== "object" || g === null) { errors.push(`${p} must be object`); return; }
64
- if (typeof g.sourcePath !== "string" || !g.sourcePath.length) errors.push(`${p}.sourcePath required`);
66
+ if (typeof g !== "object" || g === null) {
67
+ errors.push(`${p} must be object`);
68
+ return;
69
+ }
70
+ if (typeof g.sourcePath !== "string" || !g.sourcePath.length)
71
+ errors.push(`${p}.sourcePath required`);
65
72
  if (typeof g.symbol !== "string" || !g.symbol.length) errors.push(`${p}.symbol required`);
66
73
  if (typeof g.kind !== "string" || !g.kind.length) errors.push(`${p}.kind required`);
67
74
  if (!ALLOWED_SEVERITY.has(g.severity)) errors.push(`${p}.severity invalid: ${g.severity}`);
68
- if (!Array.isArray(g.expectedTestPaths) || g.expectedTestPaths.length === 0) errors.push(`${p}.expectedTestPaths must be a non-empty array`);
75
+ if (!Array.isArray(g.expectedTestPaths) || g.expectedTestPaths.length === 0)
76
+ errors.push(`${p}.expectedTestPaths must be a non-empty array`);
69
77
  if (typeof g.hint !== "string") errors.push(`${p}.hint must be a string`);
70
78
  });
71
79
  }
@@ -76,7 +84,9 @@ function validate(obj) {
76
84
  (async () => {
77
85
  const raw = await readInput();
78
86
  let obj;
79
- try { obj = JSON.parse(raw); } catch (e) {
87
+ try {
88
+ obj = JSON.parse(raw);
89
+ } catch (e) {
80
90
  console.log(JSON.stringify({ ok: false, errors: [`JSON parse error: ${e.message}`] }));
81
91
  process.exit(1);
82
92
  }
@@ -25,7 +25,12 @@ import { readFileSync } from "node:fs";
25
25
 
26
26
  const ALLOWED_REVIEWERS = new Set(["fable", "opus", "sonnet", "gpt"]);
27
27
  const ALLOWED_SEVERITIES = new Set(["blocking", "important", "suggestion"]);
28
- const ALLOWED_CONSENSUS_VERDICTS = new Set(["unanimous-pass", "unanimous-block", "split", "unverified"]);
28
+ const ALLOWED_CONSENSUS_VERDICTS = new Set([
29
+ "unanimous-pass",
30
+ "unanimous-block",
31
+ "split",
32
+ "unverified",
33
+ ]);
29
34
  const ALLOWED_VERIFICATION_RESULTS = new Set(["confirmed", "not-reproduced", "inconclusive"]);
30
35
  const OVER_REJECT_THRESHOLD = 0.8;
31
36
  const OVER_REJECT_MIN_FINDINGS = 5;
@@ -79,7 +84,9 @@ function validateVerification(v, label, errors) {
79
84
  return;
80
85
  }
81
86
  if (!ALLOWED_VERIFICATION_RESULTS.has(v.result)) {
82
- errors.push(`${label}: bad result "${v.result}" (allowed: confirmed|not-reproduced|inconclusive)`);
87
+ errors.push(
88
+ `${label}: bad result "${v.result}" (allowed: confirmed|not-reproduced|inconclusive)`,
89
+ );
83
90
  return;
84
91
  }
85
92
  if (v.result === "confirmed" || v.result === "not-reproduced") {
@@ -115,10 +122,14 @@ function validateConsensus(c, errors) {
115
122
  return;
116
123
  }
117
124
  if (!Number.isInteger(c.reviewerCount) || c.reviewerCount < 1) {
118
- errors.push(`consensus: reviewerCount must be an integer >= 1 (got ${JSON.stringify(c.reviewerCount)})`);
125
+ errors.push(
126
+ `consensus: reviewerCount must be an integer >= 1 (got ${JSON.stringify(c.reviewerCount)})`,
127
+ );
119
128
  }
120
129
  if (!ALLOWED_CONSENSUS_VERDICTS.has(c.verdict)) {
121
- errors.push(`consensus: bad verdict "${c.verdict}" (allowed: unanimous-pass|unanimous-block|split|unverified)`);
130
+ errors.push(
131
+ `consensus: bad verdict "${c.verdict}" (allowed: unanimous-pass|unanimous-block|split|unverified)`,
132
+ );
122
133
  }
123
134
  if (c.disagreements !== undefined) {
124
135
  if (!Array.isArray(c.disagreements)) {
@@ -211,7 +222,8 @@ function validate(raw) {
211
222
  errors.push('consensus.verdict "unanimous-pass" contradicts an accepted blocking finding');
212
223
  }
213
224
  if (
214
- typeof c.reviewerCount === "number" && c.reviewerCount < 2 &&
225
+ typeof c.reviewerCount === "number" &&
226
+ c.reviewerCount < 2 &&
215
227
  (c.verdict === "unanimous-pass" || c.verdict === "unanimous-block")
216
228
  ) {
217
229
  errors.push(
@@ -5,7 +5,7 @@
5
5
  * Why this exists:
6
6
  * Multiple worktrees can run the pipeline concurrently against the same
7
7
  * task id. Two CLI instances writing agent-state.json in the usual
8
- * fs.writeFileSync('path', JSON.stringify(...)) pattern is racy -
8
+ * fs.writeFileSync('path', JSON.stringify(...)) pattern is racy -
9
9
  * second write wipes the first. This helper uses tmpfile + rename, which
10
10
  * is atomic on POSIX, combined with an advisory .lock file for the tiny
11
11
  * window between "read current state" and "write new state".
@@ -35,8 +35,7 @@ import {
35
35
  renameSync,
36
36
  existsSync,
37
37
  unlinkSync,
38
- openSync,
39
- closeSync,
38
+ linkSync,
40
39
  statSync,
41
40
  } from "fs";
42
41
  import { dirname, basename } from "path";
@@ -70,17 +69,36 @@ function parseArgs() {
70
69
  * @param {number} timeoutMs
71
70
  * @returns {Promise<void>}
72
71
  */
73
- async function acquireLock(path, timeoutMs = Number(process.env.WRITE_STATE_LOCK_TIMEOUT_MS) || 15_000) {
72
+ async function acquireLock(
73
+ path,
74
+ timeoutMs = Number(process.env.WRITE_STATE_LOCK_TIMEOUT_MS) || 15_000,
75
+ ) {
74
76
  const lockPath = `${path}.lock`;
75
77
  const staleMs = Number(process.env.WRITE_STATE_LOCK_STALE_MS) || 30_000;
76
78
  const deadline = Date.now() + timeoutMs;
79
+ // The lock file must never be observable without its owning PID inside it.
80
+ // It used to be created with openSync(wx) and filled on the next line, so
81
+ // between those two calls it existed and was empty. A writer arriving in
82
+ // that window read an unparseable PID, concluded the lock was stale, and
83
+ // deleted a live one - after which two writers held it and one write was
84
+ // lost. Under CPU load that reproduced 3 times in 15 runs.
85
+ //
86
+ // Writing the PID into a private temp file and hard-linking it into place
87
+ // closes the window: link() fails with EEXIST if the lock is held, and the
88
+ // instant the lock exists it already has its contents.
89
+ const stagePath = `${lockPath}.${process.pid}.stage`;
77
90
  while (Date.now() < deadline) {
78
91
  try {
79
- const fd = openSync(lockPath, "wx"); // O_EXCL - fails if exists
80
- writeFileSync(fd, String(process.pid));
81
- closeSync(fd);
92
+ writeFileSync(stagePath, String(process.pid));
93
+ linkSync(stagePath, lockPath); // fails EEXIST if another writer holds it
94
+ unlinkSync(stagePath);
82
95
  return;
83
96
  } catch (err) {
97
+ try {
98
+ unlinkSync(stagePath);
99
+ } catch {
100
+ /* already gone */
101
+ }
84
102
  if (err.code !== "EEXIST") throw err;
85
103
  if (isLockStale(lockPath, staleMs)) {
86
104
  // Holder crashed or the lock outlived any plausible writer - reclaim it.
@@ -105,7 +123,7 @@ async function acquireLock(path, timeoutMs = Number(process.env.WRITE_STATE_LOCK
105
123
 
106
124
  /**
107
125
  * A lock is stale when its owning PID is no longer alive, or when the lock
108
- * file is older than `staleMs` (covers PID reuse and unreadable PIDs).
126
+ * file is older than `staleMs` (which also covers PID reuse).
109
127
  * @param {string} lockPath
110
128
  * @param {number} staleMs
111
129
  * @returns {boolean}
@@ -121,7 +139,12 @@ function isLockStale(lockPath, staleMs) {
121
139
  return false;
122
140
  }
123
141
  if (Date.now() - mtimeMs > staleMs) return true;
124
- if (!Number.isInteger(pid) || pid <= 0) return true;
142
+ // An unreadable PID is NOT proof of staleness. Treating it as such is what
143
+ // let a writer delete a live lock and lose another writer's update. With the
144
+ // link-based acquire above a lock is never observable without its PID, so
145
+ // this can only be genuine corruption - which the staleMs check reclaims
146
+ // anyway, without racing a writer that is merely mid-flight.
147
+ if (!Number.isInteger(pid) || pid <= 0) return false;
125
148
  try {
126
149
  process.kill(pid, 0); // probe liveness without signalling
127
150
  return false; // holder alive
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-07-20T07:34:26Z",
4
- "skillCount": 189,
3
+ "generatedAt": "2026-07-25T10:41:38Z",
4
+ "skillCount": 193,
5
5
  "entries": [
6
6
  {
7
7
  "path": "shared/core/apple-archive-compliance/SKILL.md",
8
- "sha256": "38cb4c257f7b2915d1784ec6f355b9a7f14bb32427e09c01872d3c8d6a039a71"
8
+ "sha256": "d3826905606b6ec7d86d3434b03bfdbbd52a424f7108e58ca12a8cb490f1e4ad"
9
9
  },
10
10
  {
11
11
  "path": "shared/core/google-play-compliance/SKILL.md",
@@ -35,6 +35,10 @@
35
35
  "path": "shared/core/multi-agent-create-jira/SKILL.md",
36
36
  "sha256": "40caf0489ad8322cd00d792a244e772d4ddb6ccfd7c3807729d6b17590e7f81c"
37
37
  },
38
+ {
39
+ "path": "shared/core/multi-agent-design-check/SKILL.md",
40
+ "sha256": "a004cf24661789d2a3595c5246dfebe00b76aaee6ece057b458748f502a8cd5c"
41
+ },
38
42
  {
39
43
  "path": "shared/core/multi-agent-dev-autopilot/SKILL.md",
40
44
  "sha256": "82b1cf4a4bbd84fcfef54fcdbcdefa27f2747df6d4fac5c86b42e861013c6d58"
@@ -59,13 +63,17 @@
59
63
  "path": "shared/core/multi-agent-finish/SKILL.md",
60
64
  "sha256": "3e8e430f7aee626be96129a9d0ae6c9ac5ef0dbf80cbe580dde2d7fb5fccfed1"
61
65
  },
66
+ {
67
+ "path": "shared/core/multi-agent-forget/SKILL.md",
68
+ "sha256": "68f6a92a00844ed34c336548888d3e623534c12b5e85afe5762e7a9c8d41aae4"
69
+ },
62
70
  {
63
71
  "path": "shared/core/multi-agent-garbage-collect/SKILL.md",
64
72
  "sha256": "24736f163e645e2bee7d552e555ae6e32cd3fc3d15c844813a75b908a58d52fd"
65
73
  },
66
74
  {
67
75
  "path": "shared/core/multi-agent-help/SKILL.md",
68
- "sha256": "00772bab27906cdbdcae72d115c4e1f12ddd914fa7aa274923618a3a906e6caa"
76
+ "sha256": "330780854ab3b48f788e34f918ecad585bd46ffbb44f14fd38de7b48a4b568aa"
69
77
  },
70
78
  {
71
79
  "path": "shared/core/multi-agent-issue/SKILL.md",
@@ -109,7 +117,7 @@
109
117
  },
110
118
  {
111
119
  "path": "shared/core/multi-agent-refactor/SKILL.md",
112
- "sha256": "f8a9495f6aebacba634672d6c9416046521cbdfa769746c9ff3a4a993482a85a"
120
+ "sha256": "60766ee3a6b974b3872417db5c40d1fc03bfddd99daf4aca11d7a59b57a41b57"
113
121
  },
114
122
  {
115
123
  "path": "shared/core/multi-agent-resume/SKILL.md",
@@ -127,6 +135,14 @@
127
135
  "path": "shared/core/multi-agent-review/SKILL.md",
128
136
  "sha256": "b1fd57d2b80ee0fe2490439d9f2688fcf415857c2aab60549c5456e27693b9a9"
129
137
  },
138
+ {
139
+ "path": "shared/core/multi-agent-routines/SKILL.md",
140
+ "sha256": "91d3bdb15da7addadd1afd0b170057daf3d3fc335da3f6e1feb4feff1af0881e"
141
+ },
142
+ {
143
+ "path": "shared/core/multi-agent-save/SKILL.md",
144
+ "sha256": "f9c6e2ba1dd1b224b80cabe35f4327b5bcc0b1e05868b378547bec616193ef1b"
145
+ },
130
146
  {
131
147
  "path": "shared/core/multi-agent-scan/SKILL.md",
132
148
  "sha256": "266e4e7c9b702bc3205b7d926455c5863e144cddb1e4b883aa81e570a362fd3f"
@@ -149,11 +165,11 @@
149
165
  },
150
166
  {
151
167
  "path": "shared/core/multi-agent-sync/SKILL.md",
152
- "sha256": "de38486056c99a599d93ae5166a2071875331a7d77f1e34f66d8b72657a73c2f"
168
+ "sha256": "3a9f7d8caa47d41d1a4759ce6673bfb94a1ef9f62f7113a4cd3b1e9451464601"
153
169
  },
154
170
  {
155
171
  "path": "shared/core/multi-agent-test/SKILL.md",
156
- "sha256": "f90c34f71ea6cd5cccbfa128597eaaa221a0ca5f62924eb9cfb6b1ed2d07de04"
172
+ "sha256": "2b662ecd9497149c4c2e87cc79e906cc1cab7ea4a7d0efe300b0b018559960c4"
157
173
  },
158
174
  {
159
175
  "path": "shared/core/multi-agent-uninstall/SKILL.md",
@@ -425,7 +441,7 @@
425
441
  },
426
442
  {
427
443
  "path": "shared/external/humanizer/SKILL.md",
428
- "sha256": "7f9d49b90eba79bf7acd8a5d8d5ae091cbc970188b8ecac310cce04b82add42e"
444
+ "sha256": "2fc8fe79a5832b9e1316f515c73f0545023153e85aba4645ba42060a0afdd049"
429
445
  },
430
446
  {
431
447
  "path": "shared/external/ios-accessibility/SKILL.md",