@mmerterden/multi-agent-pipeline 20.2.1 → 20.3.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 (621) hide show
  1. package/CHANGELOG.md +912 -1
  2. package/README.md +104 -81
  3. package/README.tr.md +103 -62
  4. package/docs/FIGMA_PIPELINE.md +35 -35
  5. package/docs/adr/0006-skills-core-external-split.md +1 -1
  6. package/docs/adr/0007-multi-tool-adapter-framework.md +6 -0
  7. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +7 -0
  8. package/docs/architecture.md +50 -14
  9. package/docs/best-practices.md +1 -1
  10. package/docs/ecosystem.md +56 -32
  11. package/docs/facts.json +10 -10
  12. package/docs/features.md +97 -5
  13. package/docs/recovery-guide.md +7 -14
  14. package/docs/server-readiness.md +31 -24
  15. package/index.js +1 -1
  16. package/install/_common.mjs +3 -5
  17. package/install/_platform-filter.mjs +23 -1
  18. package/install/_unattended-profile.mjs +321 -75
  19. package/install/claude.mjs +51 -10
  20. package/install/codex.mjs +2 -0
  21. package/install/copilot.mjs +2 -0
  22. package/install/index.mjs +30 -17
  23. package/install/templates/claude-hooks.json +16 -5
  24. package/install/templates/copilot-instructions.md +1 -1
  25. package/install/templates/multi-agent-autopilot-awake.plist.template +48 -0
  26. package/install/templates/multi-agent-autopilot.plist.template +12 -5
  27. package/install/unattended-profile-legacy.json +80 -0
  28. package/manifest.json +616 -483
  29. package/package.json +8 -3
  30. package/pipeline/agents/code-reviewer.md +10 -0
  31. package/pipeline/agents/plan-critic.md +98 -0
  32. package/pipeline/agents/security-auditor.md +10 -0
  33. package/pipeline/agents/task-clarifier.md +10 -0
  34. package/pipeline/commands/multi-agent/SKILL.md +2 -2
  35. package/pipeline/commands/multi-agent/analysis/SKILL.md +6 -2
  36. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +12 -1
  37. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +4 -0
  38. package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
  39. package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +36 -6
  40. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +77 -12
  41. package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +29 -8
  42. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +4 -0
  43. package/pipeline/commands/multi-agent/channels/SKILL.md +9 -9
  44. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +5 -1
  45. package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -0
  46. package/pipeline/commands/multi-agent/design-check/SKILL.md +16 -11
  47. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +4 -0
  48. package/pipeline/commands/multi-agent/doctor/SKILL.md +4 -0
  49. package/pipeline/commands/multi-agent/feedback/SKILL.md +5 -1
  50. package/pipeline/commands/multi-agent/forget/SKILL.md +4 -0
  51. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +4 -0
  52. package/pipeline/commands/multi-agent/graph/SKILL.md +4 -0
  53. package/pipeline/commands/multi-agent/help/SKILL.md +37 -39
  54. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +4 -0
  55. package/pipeline/commands/multi-agent/issue/SKILL.md +7 -1
  56. package/pipeline/commands/multi-agent/jira/SKILL.md +7 -1
  57. package/pipeline/commands/multi-agent/kill/SKILL.md +9 -3
  58. package/pipeline/commands/multi-agent/language/SKILL.md +4 -0
  59. package/pipeline/commands/multi-agent/log/SKILL.md +4 -0
  60. package/pipeline/commands/multi-agent/manual-test/SKILL.md +5 -0
  61. package/pipeline/commands/multi-agent/model/SKILL.md +4 -0
  62. package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -0
  63. package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +4 -0
  64. package/pipeline/commands/multi-agent/purge/SKILL.md +4 -0
  65. package/pipeline/commands/multi-agent/refactor/SKILL.md +5 -0
  66. package/pipeline/commands/multi-agent/research/SKILL.md +49 -0
  67. package/pipeline/commands/multi-agent/resume/SKILL.md +29 -6
  68. package/pipeline/commands/multi-agent/review/SKILL.md +28 -9
  69. package/pipeline/commands/multi-agent/review-analysis/SKILL.md +5 -1
  70. package/pipeline/commands/multi-agent/review-issue/SKILL.md +4 -0
  71. package/pipeline/commands/multi-agent/review-jira/SKILL.md +4 -0
  72. package/pipeline/commands/multi-agent/route-off/SKILL.md +4 -0
  73. package/pipeline/commands/multi-agent/route-on/SKILL.md +4 -0
  74. package/pipeline/commands/multi-agent/route-status/SKILL.md +4 -0
  75. package/pipeline/commands/multi-agent/routines/SKILL.md +4 -0
  76. package/pipeline/commands/multi-agent/save/SKILL.md +6 -2
  77. package/pipeline/commands/multi-agent/scaffold/SKILL.md +47 -0
  78. package/pipeline/commands/multi-agent/scan/SKILL.md +4 -0
  79. package/pipeline/commands/multi-agent/search/SKILL.md +12 -8
  80. package/pipeline/commands/multi-agent/security-review/SKILL.md +4 -0
  81. package/pipeline/commands/multi-agent/serve/SKILL.md +62 -0
  82. package/pipeline/commands/multi-agent/setup/SKILL.md +27 -36
  83. package/pipeline/commands/multi-agent/stack/SKILL.md +4 -0
  84. package/pipeline/commands/multi-agent/status/SKILL.md +10 -9
  85. package/pipeline/commands/multi-agent/steer/SKILL.md +5 -1
  86. package/pipeline/commands/multi-agent/store-ready/SKILL.md +4 -0
  87. package/pipeline/commands/multi-agent/sync/SKILL.md +13 -13
  88. package/pipeline/commands/multi-agent/test/SKILL.md +4 -0
  89. package/pipeline/commands/multi-agent/test-accessibility/SKILL.md +4 -0
  90. package/pipeline/commands/multi-agent/test-dark-mode/SKILL.md +4 -0
  91. package/pipeline/commands/multi-agent/test-dynamic-type/SKILL.md +4 -0
  92. package/pipeline/commands/multi-agent/test-screenshots/SKILL.md +5 -1
  93. package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +4 -0
  94. package/pipeline/commands/multi-agent/uninstall/SKILL.md +5 -1
  95. package/pipeline/commands/multi-agent/update/SKILL.md +4 -0
  96. package/pipeline/contract/CHANGELOG.md +74 -0
  97. package/pipeline/contract/README.md +126 -0
  98. package/pipeline/contract/build.mjs +427 -0
  99. package/pipeline/contract/fixtures/answer-result.json +11 -0
  100. package/pipeline/contract/fixtures/error-invalid-request.json +8 -0
  101. package/pipeline/contract/fixtures/error-unauthorized.json +5 -0
  102. package/pipeline/contract/fixtures/error-unsigned.json +5 -0
  103. package/pipeline/contract/fixtures/issues-empty.json +18 -0
  104. package/pipeline/contract/fixtures/launch-plan.json +31 -0
  105. package/pipeline/contract/fixtures/runs-awaiting-question.json +70 -0
  106. package/pipeline/contract/fixtures/runs-empty.json +6 -0
  107. package/pipeline/contract/fixtures/runs-failed.json +84 -0
  108. package/pipeline/contract/fixtures/runs-old-schema.json +70 -0
  109. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +101 -0
  110. package/pipeline/contract/fixtures/runs-pr-opened.json +106 -0
  111. package/pipeline/contract/fixtures/runs-running.json +84 -0
  112. package/pipeline/contract/fixtures/worktrees-empty.json +4 -0
  113. package/pipeline/contract/frozen/toolbox.json +107 -0
  114. package/pipeline/contract/manifest.json +263 -0
  115. package/pipeline/contract/types/index.d.ts +343 -0
  116. package/pipeline/lib/_jira-auth.sh +6 -2
  117. package/pipeline/lib/account-resolver.sh +1 -1
  118. package/pipeline/lib/autopilot-state.sh +19 -0
  119. package/pipeline/lib/context-link-extractor.sh +12 -5
  120. package/pipeline/lib/credential-inventory.sh +12 -5
  121. package/pipeline/lib/credential-store.sh +116 -185
  122. package/pipeline/lib/fetch-confluence.sh +44 -3
  123. package/pipeline/lib/fetch-document.sh +3 -4
  124. package/pipeline/lib/fetch-fortify.sh +1 -1
  125. package/pipeline/lib/figma-mcp-refresh.sh +2 -2
  126. package/pipeline/lib/figma-token.sh +5 -1
  127. package/pipeline/lib/issue-fetcher.sh +233 -16
  128. package/pipeline/lib/json-file-lock.mjs +172 -0
  129. package/pipeline/lib/model-dispatch.sh +21 -12
  130. package/pipeline/lib/model-rung.sh +6 -1
  131. package/pipeline/lib/multi-repo-pipeline.sh +1 -1
  132. package/pipeline/lib/outbound-gate.mjs +46 -16
  133. package/pipeline/lib/parse-complaints.sh +14 -7
  134. package/pipeline/lib/plan-todos.sh +3 -3
  135. package/pipeline/lib/post-pr-review.sh +9 -9
  136. package/pipeline/lib/pr-request-location.mjs +85 -0
  137. package/pipeline/lib/regular-file.mjs +153 -0
  138. package/pipeline/lib/repo-hygiene.sh +17 -0
  139. package/pipeline/lib/route-state.sh +5 -1
  140. package/pipeline/lib/run-paths.sh +3 -2
  141. package/pipeline/lib/stack-detect.sh +19 -1
  142. package/pipeline/lib/unattended-profile-check.mjs +178 -0
  143. package/pipeline/lib/unattended-settings-location.mjs +28 -0
  144. package/pipeline/lib/unattended.mjs +76 -0
  145. package/pipeline/lib/unattended.sh +32 -0
  146. package/pipeline/lib/untrusted.mjs +76 -0
  147. package/pipeline/lib/user-facing.mjs +82 -0
  148. package/pipeline/lib/user-facing.sh +58 -0
  149. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  150. package/pipeline/multi-agent-refs/analysis/evidence.md +4 -0
  151. package/pipeline/multi-agent-refs/analysis/locked.md +2 -2
  152. package/pipeline/multi-agent-refs/analysis/render.md +4 -3
  153. package/pipeline/multi-agent-refs/analysis/resolve.md +3 -1
  154. package/pipeline/multi-agent-refs/analysis/synthesis.md +4 -5
  155. package/pipeline/multi-agent-refs/analysis-template.md +10 -17
  156. package/pipeline/multi-agent-refs/channels/jira.md +1 -1
  157. package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
  158. package/pipeline/multi-agent-refs/conventions-defaults.md +32 -32
  159. package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -17
  160. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +37 -9
  161. package/pipeline/multi-agent-refs/features/autopilot-operations.md +277 -0
  162. package/pipeline/multi-agent-refs/features/constitution.md +196 -0
  163. package/pipeline/multi-agent-refs/features/doctor.md +6 -3
  164. package/pipeline/multi-agent-refs/features/external-context-injection.md +6 -1
  165. package/pipeline/multi-agent-refs/features/jira-context.md +1 -1
  166. package/pipeline/multi-agent-refs/features/maturity-followup.md +43 -23
  167. package/pipeline/multi-agent-refs/features/model-fallback.md +9 -0
  168. package/pipeline/multi-agent-refs/features/phone-api.md +306 -0
  169. package/pipeline/multi-agent-refs/features/plan-critic.md +159 -0
  170. package/pipeline/multi-agent-refs/features/research.md +150 -0
  171. package/pipeline/multi-agent-refs/features/review-decision.md +185 -0
  172. package/pipeline/multi-agent-refs/features/scaffold.md +160 -0
  173. package/pipeline/multi-agent-refs/features/security-audit.md +6 -2
  174. package/pipeline/multi-agent-refs/features/skill-conformance.md +4 -1
  175. package/pipeline/multi-agent-refs/features/stack-adapters.md +116 -0
  176. package/pipeline/multi-agent-refs/features/unattended-gates.md +316 -0
  177. package/pipeline/multi-agent-refs/features/unattended-security.md +731 -0
  178. package/pipeline/multi-agent-refs/features/url-enrichment.md +2 -2
  179. package/pipeline/multi-agent-refs/features/usage-reporting.md +127 -35
  180. package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
  181. package/pipeline/multi-agent-refs/features/visual-evidence.md +7 -7
  182. package/pipeline/multi-agent-refs/keychain.md +6 -11
  183. package/pipeline/multi-agent-refs/payload-contracts.md +2 -2
  184. package/pipeline/multi-agent-refs/phases/log-format.md +2 -3
  185. package/pipeline/multi-agent-refs/phases/modes.md +10 -12
  186. package/pipeline/multi-agent-refs/phases/operations.md +11 -5
  187. package/pipeline/multi-agent-refs/phases/phase-0-init.md +73 -81
  188. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +22 -22
  189. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +42 -66
  190. package/pipeline/multi-agent-refs/phases/phase-3-review.md +45 -79
  191. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +28 -38
  192. package/pipeline/multi-agent-refs/phases/phase-5-report.md +27 -25
  193. package/pipeline/multi-agent-refs/phases.md +1 -1
  194. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  195. package/pipeline/multi-agent-refs/progress-contract.md +13 -16
  196. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  197. package/pipeline/multi-agent-refs/research/engine.md +91 -0
  198. package/pipeline/multi-agent-refs/rules.md +6 -4
  199. package/pipeline/multi-agent-refs/setup/repo-discovery.md +2 -2
  200. package/pipeline/multi-agent-refs/tracker-contract.md +2 -2
  201. package/pipeline/multi-agent-refs/unattended-contract.md +68 -16
  202. package/pipeline/rules/figma-pipeline.md +12 -12
  203. package/pipeline/schemas/agent-state.schema.json +480 -18
  204. package/pipeline/schemas/analysis-spec.schema.json +4 -4
  205. package/pipeline/schemas/answer-request.schema.json +24 -0
  206. package/pipeline/schemas/answer-result.schema.json +28 -0
  207. package/pipeline/schemas/autopilot-config.schema.json +111 -13
  208. package/pipeline/schemas/command-parameters.schema.json +99 -0
  209. package/pipeline/schemas/constitution.schema.json +56 -0
  210. package/pipeline/schemas/contract-error.schema.json +52 -0
  211. package/pipeline/schemas/design-check-config.schema.json +5 -1
  212. package/pipeline/schemas/issues.schema.json +61 -0
  213. package/pipeline/schemas/launch-plan.schema.json +46 -0
  214. package/pipeline/schemas/launch-request.schema.json +45 -0
  215. package/pipeline/schemas/launch.json +61 -0
  216. package/pipeline/schemas/launch.schema.json +84 -0
  217. package/pipeline/schemas/phases.json +2 -2
  218. package/pipeline/schemas/phases.schema.json +68 -0
  219. package/pipeline/schemas/phone-devices.schema.json +61 -0
  220. package/pipeline/schemas/phone-signed-request.schema.json +67 -0
  221. package/pipeline/schemas/plan-critique.schema.json +99 -0
  222. package/pipeline/schemas/plan-todos.schema.json +7 -7
  223. package/pipeline/schemas/planning-output.schema.json +5 -0
  224. package/pipeline/schemas/pr-request.schema.json +46 -0
  225. package/pipeline/schemas/prefs.schema.json +79 -8
  226. package/pipeline/schemas/research-output.schema.json +118 -0
  227. package/pipeline/schemas/review-file-exclusions.schema.json +25 -0
  228. package/pipeline/schemas/reviewer-output.schema.json +40 -4
  229. package/pipeline/schemas/run-questions.json +392 -0
  230. package/pipeline/schemas/run-questions.schema.json +118 -0
  231. package/pipeline/schemas/runs-index.schema.json +189 -0
  232. package/pipeline/schemas/scaffold-manifest.schema.json +43 -0
  233. package/pipeline/schemas/secret-patterns.schema.json +28 -0
  234. package/pipeline/schemas/stack-adapters.json +527 -0
  235. package/pipeline/schemas/stack-adapters.schema.json +184 -0
  236. package/pipeline/schemas/token-budget.json +1 -1
  237. package/pipeline/schemas/token-budget.schema.json +26 -0
  238. package/pipeline/schemas/triage-output.schema.json +64 -3
  239. package/pipeline/schemas/unattended-policy.json +139 -0
  240. package/pipeline/schemas/unattended-policy.schema.json +73 -0
  241. package/pipeline/schemas/unattended-profile.json +248 -0
  242. package/pipeline/schemas/unattended-profile.schema.json +198 -0
  243. package/pipeline/schemas/worktrees.schema.json +51 -0
  244. package/pipeline/scripts/README.md +1 -0
  245. package/pipeline/scripts/_autopilot-config.mjs +130 -0
  246. package/pipeline/scripts/_autopilot-ops.mjs +567 -0
  247. package/pipeline/scripts/_autopilot-outcomes.mjs +174 -0
  248. package/pipeline/scripts/_command-contract.mjs +384 -0
  249. package/pipeline/scripts/_cost.mjs +40 -0
  250. package/pipeline/scripts/_notices.mjs +160 -0
  251. package/pipeline/scripts/_phone-auth.mjs +485 -0
  252. package/pipeline/scripts/_pre-existing.mjs +294 -0
  253. package/pipeline/scripts/_redact.mjs +77 -0
  254. package/pipeline/scripts/_run-paths.mjs +4 -2
  255. package/pipeline/scripts/_stack-adapter.mjs +678 -0
  256. package/pipeline/scripts/_stack-routing.mjs +1 -1
  257. package/pipeline/scripts/agent-guard.py +348 -37
  258. package/pipeline/scripts/agent-guard.sh +41 -13
  259. package/pipeline/scripts/analysis-story-tree.mjs +79 -3
  260. package/pipeline/scripts/answer-question.mjs +181 -0
  261. package/pipeline/scripts/audit-log-rotate.sh +1 -4
  262. package/pipeline/scripts/audit-log.sh +4 -4
  263. package/pipeline/scripts/autopilot-arming.mjs +389 -21
  264. package/pipeline/scripts/autopilot-awake.mjs +255 -0
  265. package/pipeline/scripts/autopilot-intake.mjs +137 -36
  266. package/pipeline/scripts/autopilot-menubar.swift +156 -44
  267. package/pipeline/scripts/autopilot-publish.mjs +1625 -0
  268. package/pipeline/scripts/autopilot-runner.mjs +1678 -222
  269. package/pipeline/scripts/autopilot-status.sh +198 -33
  270. package/pipeline/scripts/build-lock.sh +120 -0
  271. package/pipeline/scripts/build-references.mjs +4 -1
  272. package/pipeline/scripts/build-stack-plugins.mjs +59 -22
  273. package/pipeline/scripts/capture-flush.sh +1 -1
  274. package/pipeline/scripts/capture-resume.sh +13 -9
  275. package/pipeline/scripts/check-derived-drift.mjs +52 -11
  276. package/pipeline/scripts/commands.mjs +88 -0
  277. package/pipeline/scripts/constitution.mjs +362 -0
  278. package/pipeline/scripts/contract-server.mjs +776 -0
  279. package/pipeline/scripts/cost-analyze.mjs +89 -39
  280. package/pipeline/scripts/diff-explain.mjs +12 -1
  281. package/pipeline/scripts/doctor.mjs +77 -28
  282. package/pipeline/scripts/evidence-gate.mjs +192 -12
  283. package/pipeline/scripts/feedback-send.mjs +4 -2
  284. package/pipeline/scripts/gate-ledger.mjs +449 -0
  285. package/pipeline/scripts/gc-abandoned.sh +132 -13
  286. package/pipeline/scripts/gen-facts.mjs +31 -15
  287. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  288. package/pipeline/scripts/github-ssh-setup.sh +140 -29
  289. package/pipeline/scripts/graph-mermaid.mjs +4 -1
  290. package/pipeline/scripts/issues.mjs +236 -0
  291. package/pipeline/scripts/jira-attach.sh +6 -2
  292. package/pipeline/scripts/jira-search.sh +4 -3
  293. package/pipeline/scripts/keychain-save.sh +125 -24
  294. package/pipeline/scripts/keychain.py +63 -93
  295. package/pipeline/scripts/launch-request.mjs +747 -0
  296. package/pipeline/scripts/localize-commands.mjs +4 -10
  297. package/pipeline/scripts/log-metric.sh +6 -5
  298. package/pipeline/scripts/maturity-followup.mjs +13 -4
  299. package/pipeline/scripts/memory-save.sh +25 -0
  300. package/pipeline/scripts/migrate-prefs.mjs +4 -3
  301. package/pipeline/scripts/open-questions-gate.mjs +276 -0
  302. package/pipeline/scripts/phase-tracker.sh +41 -27
  303. package/pipeline/scripts/phase0-exit-gate.mjs +22 -4
  304. package/pipeline/scripts/phone-devices.mjs +224 -0
  305. package/pipeline/scripts/plan-coverage-gate.mjs +200 -66
  306. package/pipeline/scripts/plan-critique-gate.mjs +591 -0
  307. package/pipeline/scripts/pr-request.mjs +188 -0
  308. package/pipeline/scripts/pre-commit-check.sh +115 -4
  309. package/pipeline/scripts/probe-evidence-capability.sh +44 -5
  310. package/pipeline/scripts/record-phase.mjs +71 -0
  311. package/pipeline/scripts/render-agent-log-cost.sh +17 -2
  312. package/pipeline/scripts/render-cost-summary.sh +1 -1
  313. package/pipeline/scripts/render-work-summary.sh +1 -1
  314. package/pipeline/scripts/require-supported-version.sh +4 -1
  315. package/pipeline/scripts/research-gate.mjs +704 -0
  316. package/pipeline/scripts/review-decision-gate.mjs +403 -0
  317. package/pipeline/scripts/routine-registry.mjs +5 -2
  318. package/pipeline/scripts/runs-index.mjs +135 -27
  319. package/pipeline/scripts/scaffold-gate.mjs +393 -0
  320. package/pipeline/scripts/skill-conformance.mjs +25 -8
  321. package/pipeline/scripts/skill-siblings.mjs +2 -1
  322. package/pipeline/scripts/smoke-cross-cli-behavior.sh +32 -6
  323. package/pipeline/scripts/smoke-schema-validation.sh +6 -2
  324. package/pipeline/scripts/spec-consistency-gate.mjs +469 -0
  325. package/pipeline/scripts/symbol-existence-gate.mjs +450 -0
  326. package/pipeline/scripts/test-gap-scan.mjs +40 -2
  327. package/pipeline/scripts/test-integrity-gate.mjs +20 -4
  328. package/pipeline/scripts/test-strength.mjs +484 -0
  329. package/pipeline/scripts/test-summary.mjs +651 -0
  330. package/pipeline/scripts/triage-memory.mjs +49 -9
  331. package/pipeline/scripts/unattended_policy.py +2786 -0
  332. package/pipeline/scripts/uninstall.mjs +10 -10
  333. package/pipeline/scripts/update-issue-progress.sh +1 -1
  334. package/pipeline/scripts/usage-identity.mjs +288 -0
  335. package/pipeline/scripts/usage-register.mjs +185 -65
  336. package/pipeline/scripts/usage-report.mjs +230 -66
  337. package/pipeline/scripts/validate-complaint-doc.mjs +28 -10
  338. package/pipeline/scripts/validate-planning.mjs +6 -0
  339. package/pipeline/scripts/verify-citations.mjs +151 -38
  340. package/pipeline/scripts/verify.mjs +58 -18
  341. package/pipeline/scripts/worktree-prepare.sh +126 -0
  342. package/pipeline/scripts/worktrees.mjs +124 -0
  343. package/pipeline/scripts/write-state.mjs +48 -17
  344. package/pipeline/skills/.skill-manifest.json +222 -226
  345. package/pipeline/skills/.skills-index.json +77 -88
  346. package/pipeline/skills/shared/README.md +44 -45
  347. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +3 -2
  348. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +3 -2
  349. package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -9
  350. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +6 -5
  351. package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +11 -3
  352. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +2 -1
  353. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -3
  354. package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +32 -6
  355. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +66 -8
  356. package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +27 -9
  357. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -1
  358. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +7 -4
  359. package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +3 -2
  360. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +2 -1
  361. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +7 -5
  362. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +2 -1
  363. package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +3 -2
  364. package/pipeline/skills/shared/core/multi-agent-feedback/SKILL.md +2 -1
  365. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +2 -1
  366. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +2 -1
  367. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +2 -1
  368. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -1
  369. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -1
  370. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +3 -2
  371. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +2 -1
  372. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +7 -4
  373. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -1
  374. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +2 -1
  375. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +2 -1
  376. package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -1
  377. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +2 -1
  378. package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +2 -1
  379. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +2 -1
  380. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +2 -1
  381. package/pipeline/skills/shared/core/multi-agent-research/SKILL.md +35 -0
  382. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +7 -3
  383. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +7 -5
  384. package/pipeline/skills/shared/core/multi-agent-review-analysis/SKILL.md +2 -1
  385. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +3 -2
  386. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +2 -1
  387. package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +2 -1
  388. package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +2 -1
  389. package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +2 -1
  390. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +2 -1
  391. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +3 -2
  392. package/pipeline/skills/shared/core/multi-agent-scaffold/SKILL.md +30 -0
  393. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -1
  394. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +4 -3
  395. package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +2 -1
  396. package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +60 -0
  397. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +8 -8
  398. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +2 -1
  399. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +7 -8
  400. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -1
  401. package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +2 -1
  402. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +10 -10
  403. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -1
  404. package/pipeline/skills/shared/core/multi-agent-test-accessibility/SKILL.md +2 -1
  405. package/pipeline/skills/shared/core/multi-agent-test-dark-mode/SKILL.md +2 -1
  406. package/pipeline/skills/shared/core/multi-agent-test-dynamic-type/SKILL.md +2 -1
  407. package/pipeline/skills/shared/core/multi-agent-test-screenshots/SKILL.md +2 -1
  408. package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -1
  409. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +3 -2
  410. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -1
  411. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +46 -0
  412. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +9 -3
  413. package/pipeline/skills/shared/external/NOTICE-paul-hudson-skills.md +54 -0
  414. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +9 -11
  415. package/pipeline/skills/shared/external/NOTICE-vibeship-spawner-skills.md +203 -0
  416. package/pipeline/skills/shared/external/NOTICE-xcode-build-skills.md +10 -3
  417. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +131 -25
  418. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -3
  419. package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -1
  420. package/pipeline/skills/shared/external/android-architecture/SKILL.md +85 -76
  421. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +51 -3
  422. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +9 -0
  423. package/pipeline/skills/shared/external/android-datastore/SKILL.md +4 -3
  424. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +4 -3
  425. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +106 -181
  426. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +147 -0
  427. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +45 -4
  428. package/pipeline/skills/shared/external/android-performance/SKILL.md +27 -2
  429. package/pipeline/skills/shared/external/android-performance/references/patterns.md +37 -37
  430. package/pipeline/skills/shared/external/api-patterns/SKILL.md +111 -67
  431. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +128 -0
  432. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +136 -186
  433. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +140 -0
  434. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +207 -0
  435. package/pipeline/skills/shared/external/api-security-best-practices/references/operations.md +66 -0
  436. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +175 -0
  437. package/pipeline/skills/shared/external/app-clips/SKILL.md +2 -0
  438. package/pipeline/skills/shared/external/app-intents/SKILL.md +2 -0
  439. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +4 -3
  440. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +2 -0
  441. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -0
  442. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +2 -0
  443. package/pipeline/skills/shared/external/architecture/SKILL.md +106 -39
  444. package/pipeline/skills/shared/external/architecture/references/adr-and-review.md +76 -0
  445. package/pipeline/skills/shared/external/authentication/SKILL.md +2 -0
  446. package/pipeline/skills/shared/external/avkit/SKILL.md +2 -0
  447. package/pipeline/skills/shared/external/background-processing/SKILL.md +2 -1
  448. package/pipeline/skills/shared/external/backlog/SKILL.md +3 -2
  449. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +2 -0
  450. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +1 -1
  451. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +3 -2
  452. package/pipeline/skills/shared/external/clean-code/SKILL.md +192 -90
  453. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +2 -1
  454. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +1 -1
  455. package/pipeline/skills/shared/external/compose-components/SKILL.md +4 -4
  456. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +22 -22
  457. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +10 -10
  458. package/pipeline/skills/shared/external/compose-testing/SKILL.md +13 -6
  459. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +59 -59
  460. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +2 -0
  461. package/pipeline/skills/shared/external/context-compression/SKILL.md +118 -250
  462. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +2 -0
  463. package/pipeline/skills/shared/external/core-data/SKILL.md +2 -0
  464. package/pipeline/skills/shared/external/core-motion/SKILL.md +2 -0
  465. package/pipeline/skills/shared/external/core-nfc/SKILL.md +2 -0
  466. package/pipeline/skills/shared/external/coreml/SKILL.md +2 -1
  467. package/pipeline/skills/shared/external/council/SKILL.md +2 -1
  468. package/pipeline/skills/shared/external/cryptokit/SKILL.md +2 -0
  469. package/pipeline/skills/shared/external/css-modern/SKILL.md +3 -2
  470. package/pipeline/skills/shared/external/database-patterns/SKILL.md +3 -2
  471. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +2 -0
  472. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +130 -20
  473. package/pipeline/skills/shared/external/debugging-strategies/references/hard-cases.md +54 -0
  474. package/pipeline/skills/shared/external/device-integrity/SKILL.md +2 -0
  475. package/pipeline/skills/shared/external/docker-expert/SKILL.md +137 -381
  476. package/pipeline/skills/shared/external/docker-expert/references/patterns.md +98 -0
  477. package/pipeline/skills/shared/external/energykit/SKILL.md +2 -1
  478. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +3 -1
  479. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +2 -2
  480. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +116 -185
  481. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +235 -0
  482. package/pipeline/skills/shared/external/fastapi-pro/references/testing-and-deployment.md +69 -0
  483. package/pipeline/skills/shared/external/firebase/SKILL.md +4 -3
  484. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +151 -293
  485. package/pipeline/skills/shared/external/github-actions-templates/references/patterns.md +96 -0
  486. package/pipeline/skills/shared/external/healthkit/SKILL.md +2 -0
  487. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +69 -72
  488. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +111 -0
  489. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +77 -86
  490. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +98 -0
  491. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +150 -78
  492. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +76 -97
  493. package/pipeline/skills/shared/external/hig-components-system/references/surfaces.md +98 -0
  494. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +48 -76
  495. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +132 -0
  496. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +147 -106
  497. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +61 -77
  498. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +92 -0
  499. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +147 -77
  500. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +61 -121
  501. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +108 -0
  502. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +2 -0
  503. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +1 -1
  504. package/pipeline/skills/shared/external/html-semantic/SKILL.md +3 -2
  505. package/pipeline/skills/shared/external/humanizer/SKILL.md +2 -1
  506. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +2 -0
  507. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +2 -1
  508. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +14 -2
  509. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
  510. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +4 -3
  511. package/pipeline/skills/shared/external/ios-localization/SKILL.md +8 -6
  512. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +2 -2
  513. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +11 -11
  514. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +2 -1
  515. package/pipeline/skills/shared/external/ios-networking/SKILL.md +2 -0
  516. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +2 -0
  517. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +102 -193
  518. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +103 -0
  519. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -2
  520. package/pipeline/skills/shared/external/live-activities/evals/evals.json +2 -2
  521. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +7 -9
  522. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +3 -3
  523. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +2 -2
  524. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +4 -3
  525. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +5 -3
  526. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +2 -0
  527. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +1 -1
  528. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +2 -0
  529. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +1 -1
  530. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +149 -46
  531. package/pipeline/skills/shared/external/monorepo-architect/references/patterns.md +70 -0
  532. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +2 -0
  533. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +1 -1
  534. package/pipeline/skills/shared/external/natural-language/SKILL.md +2 -0
  535. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +3 -2
  536. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +91 -21
  537. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +247 -0
  538. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/testing-and-frameworks.md +57 -0
  539. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +155 -232
  540. package/pipeline/skills/shared/external/observability-engineer/references/patterns.md +75 -0
  541. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +2 -0
  542. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +2 -2
  543. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +18 -18
  544. package/pipeline/skills/shared/external/pdfkit/SKILL.md +2 -1
  545. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -0
  546. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
  547. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -0
  548. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +2 -1
  549. package/pipeline/skills/shared/external/push-notifications/SKILL.md +2 -0
  550. package/pipeline/skills/shared/external/python-patterns/SKILL.md +3 -2
  551. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +3 -2
  552. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +2 -2
  553. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
  554. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +3 -2
  555. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +7 -7
  556. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +69 -69
  557. package/pipeline/skills/shared/external/room-database/references/patterns.md +126 -126
  558. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -1
  559. package/pipeline/skills/shared/external/security-review/SKILL.md +4 -3
  560. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +2 -2
  561. package/pipeline/skills/shared/external/signal-community/SKILL.md +1 -1
  562. package/pipeline/skills/shared/external/skill-creator/SKILL.md +2 -1
  563. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +2 -1
  564. package/pipeline/skills/shared/external/spm-build-analysis/SKILL.md +2 -0
  565. package/pipeline/skills/shared/external/storekit/SKILL.md +2 -0
  566. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +2 -0
  567. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +2 -1
  568. package/pipeline/skills/shared/external/swift-charts/SKILL.md +2 -1
  569. package/pipeline/skills/shared/external/swift-codable/SKILL.md +2 -0
  570. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -1
  571. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +5 -4
  572. package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +2 -1
  573. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +2 -0
  574. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -1
  575. package/pipeline/skills/shared/external/swift-security/SKILL.md +2 -1
  576. package/pipeline/skills/shared/external/swift-testing/SKILL.md +2 -0
  577. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +2 -1
  578. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +10 -0
  579. package/pipeline/skills/shared/external/swiftdata/SKILL.md +2 -0
  580. package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +2 -1
  581. package/pipeline/skills/shared/external/swiftlint/SKILL.md +2 -0
  582. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +2 -0
  583. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +3 -1
  584. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +2 -0
  585. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +2 -0
  586. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +2 -0
  587. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +2 -0
  588. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +3 -1
  589. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +3 -1
  590. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +5 -4
  591. package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +2 -1
  592. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +5 -4
  593. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +2 -0
  594. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +4 -3
  595. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +2 -0
  596. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +3 -2
  597. package/pipeline/skills/shared/external/testing-backend/SKILL.md +3 -2
  598. package/pipeline/skills/shared/external/tipkit/SKILL.md +2 -0
  599. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +3 -2
  600. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -0
  601. package/pipeline/skills/shared/external/vue-composition/SKILL.md +3 -2
  602. package/pipeline/skills/shared/external/weatherkit/SKILL.md +2 -0
  603. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +3 -2
  604. package/pipeline/skills/shared/external/web-performance/SKILL.md +3 -2
  605. package/pipeline/skills/shared/external/web-testing/SKILL.md +3 -2
  606. package/pipeline/skills/shared/external/widgetkit/SKILL.md +2 -0
  607. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +1 -1
  608. package/pipeline/skills/shared/external/xcode-build-benchmark/SKILL.md +2 -0
  609. package/pipeline/skills/shared/external/xcode-build-fixer/SKILL.md +2 -0
  610. package/pipeline/skills/shared/external/xcode-build-orchestrator/SKILL.md +2 -0
  611. package/pipeline/skills/shared/external/xcode-compilation-analyzer/SKILL.md +2 -0
  612. package/pipeline/skills/shared/external/xcode-project-analyzer/SKILL.md +2 -0
  613. package/pipeline/skills/skills-index.md +40 -41
  614. package/docs/token-budget-history.md +0 -24
  615. package/pipeline/skills/shared/external/agentflow/SKILL.md +0 -199
  616. package/pipeline/skills/shared/external/android-ui-verification/SKILL.md +0 -66
  617. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +0 -299
  618. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +0 -255
  619. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +0 -167
  620. package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +0 -116
  621. package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -216
@@ -52,6 +52,8 @@ import { join } from "path";
52
52
  import { homedir } from "os";
53
53
  import { pathToFileURL } from "url";
54
54
 
55
+ import { gatesActive } from "../lib/unattended.mjs";
56
+
55
57
  const FIGMA_URL = /figma\.com\/(design|make|file)\//i;
56
58
 
57
59
  /** Fields that can carry the user's original request text. */
@@ -106,9 +108,11 @@ export function inputTextOf(state) {
106
108
  *
107
109
  * @param {object|null} state - parsed agent-state.json, or null when absent
108
110
  * @param {string} extraInput - input text supplied on the command line
111
+ * @param {{env?: Record<string, string|undefined>}} [opts] - the environment; with
112
+ * state.autopilot it decides autopilot mode (lib/unattended.mjs, gatesActive)
109
113
  * @returns {{ok: boolean, failures: string[], taskType: string|undefined, figmaSeen: boolean}}
110
114
  */
111
- export function evaluate(state, extraInput = "") {
115
+ export function evaluate(state, extraInput = "", { env = process.env } = {}) {
112
116
  const failures = [];
113
117
 
114
118
  if (state === null) {
@@ -188,7 +192,9 @@ export function evaluate(state, extraInput = "") {
188
192
  `branch the user chose from one the run picked and announced.`,
189
193
  );
190
194
  }
191
- const isAutopilot = state.autopilot === true;
195
+ // Autopilot mode is the launcher's MULTI_AGENT_UNATTENDED or the run's own
196
+ // `autopilot: true` (a terminal /multi-agent:autopilot run), lib/unattended.mjs.
197
+ const isAutopilot = gatesActive(state, env);
192
198
  const AUTOPILOT_ONLY_SOURCES = ["remembered", "default", "derived"];
193
199
  if (!isAutopilot && AUTOPILOT_ONLY_SOURCES.includes(branchSource)) {
194
200
  failures.push(
@@ -255,8 +261,9 @@ export function evaluate(state, extraInput = "") {
255
261
  // whether anyone decided: `false` is both "the user chose a worktree" and
256
262
  // "nothing asked and the default stood". Same shape as baseBranchSource, and
257
263
  // the same reason - an autopilot run resolves it rather than asking, so
258
- // `autopilot` is a legal source there and nowhere else.
259
- const WORKSPACE_SOURCES = ["asked", "command", "autopilot"];
264
+ // `autopilot` is a legal source there and nowhere else. `request` is a
265
+ // launch request that answered the picker before the run started.
266
+ const WORKSPACE_SOURCES = ["asked", "command", "autopilot", "request"];
260
267
  const workspaceSource =
261
268
  typeof state.workspaceSource === "string" ? state.workspaceSource.trim() : "";
262
269
  if (!WORKSPACE_SOURCES.includes(workspaceSource)) {
@@ -274,6 +281,17 @@ export function evaluate(state, extraInput = "") {
274
281
  );
275
282
  }
276
283
 
284
+ // A request is validated before Phase 0 applies it, and local under autopilot
285
+ // is one of the answers it rejects. The gate holds the outcome to the same
286
+ // rule, so a state that reached this point some other way still stops here.
287
+ if (isAutopilot && state.localMode === true) {
288
+ failures.push(
289
+ `localMode=true on an autopilot run (workspaceSource="${workspaceSource || "<unset>"}"). ` +
290
+ `An unattended run always gets a worktree; a launch request asking for local under ` +
291
+ `autopilot is rejected by launch-request.mjs, never applied.`,
292
+ );
293
+ }
294
+
277
295
  // Step 2b's dev-context picker writes siblings[], empty included. The empty array is
278
296
  // the record that it ran; absent, a multi-repo task silently became a single-repo one
279
297
  // and Phase 4's parity cross-check lost its fourth counterpart source.
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file phone-devices.mjs - enrol, list and revoke the phones allowed to
4
+ * reach the phone routes of contract-server.mjs, and switch phone launch on or
5
+ * off.
6
+ *
7
+ * Enrolment is local on purpose: there is no HTTP route that adds a device,
8
+ * so reaching the server is never enough to be let into it. A device is its
9
+ * Ed25519 public key plus the scopes it may use. The private key stays on the
10
+ * phone; this file never sees it and refuses a PEM block that holds one.
11
+ *
12
+ * read GET /v1/phone/runs, GET /v1/phone/runs/{id}
13
+ * answer POST /v1/phone/runs/{id}/answer
14
+ * launch POST /v1/phone/launch, and only while `launch on` is set
15
+ *
16
+ * The registry is <autopilot root>/phone/devices.json (0600, directory 0700;
17
+ * phone-devices.schema.json). An unattended run may not change it: every
18
+ * subcommand but `list` refuses under MULTI_AGENT_UNATTENDED=1, and the guard
19
+ * blocks the same subcommands by name (unattended-policy.json).
20
+ *
21
+ * Usage:
22
+ * node phone-devices.mjs add --name <name> --public-key <base64url | PEM> --scopes read[,answer][,launch]
23
+ * node phone-devices.mjs add --name <name> --public-key-file <path.pem> --scopes ...
24
+ * node phone-devices.mjs list [--json]
25
+ * node phone-devices.mjs revoke <device-id>
26
+ * node phone-devices.mjs launch on|off
27
+ *
28
+ * Output: `add` prints {id, name, fingerprint, scopes}; `list --json` prints
29
+ * {launchEnabled, devices[]} without keys.
30
+ * Exit: 0 done, 1 refused (unknown device, already revoked), 2 usage,
31
+ * 3 unattended.
32
+ *
33
+ * @module pipeline/scripts/phone-devices
34
+ */
35
+
36
+ import { readFileSync } from "node:fs";
37
+ import { runMain } from "../lib/fatal.mjs";
38
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
39
+ import { isUnattended } from "../lib/unattended.mjs";
40
+ import {
41
+ DEVICE_ID,
42
+ SCOPES,
43
+ fingerprint,
44
+ newDeviceId,
45
+ parsePublicKey,
46
+ readRegistry,
47
+ registryPath,
48
+ writeRegistry,
49
+ } from "./_phone-auth.mjs";
50
+
51
+ const NAME = /^[\p{L}\p{N} ._'-]{1,64}$/u;
52
+ const USAGE =
53
+ "usage: phone-devices.mjs add --name <name> --public-key <key> | --public-key-file <pem> --scopes <read,answer,launch>\n" +
54
+ " phone-devices.mjs list [--json] | revoke <device-id> | launch on|off\n";
55
+
56
+ class Usage extends Error {}
57
+
58
+ function options(argv) {
59
+ const out = {};
60
+ for (let i = 0; i < argv.length; i++) {
61
+ const a = argv[i];
62
+ const value = () => {
63
+ const v = argv[++i];
64
+ if (v === undefined) throw new Usage(`${a} needs a value`);
65
+ return v;
66
+ };
67
+ if (a === "--name") out.name = value();
68
+ else if (a === "--public-key") out.key = value();
69
+ else if (a === "--public-key-file") out.keyFile = value();
70
+ else if (a === "--scopes") out.scopes = value();
71
+ else if (a === "--json") out.json = true;
72
+ else out.rest = [...(out.rest ?? []), a];
73
+ }
74
+ return out;
75
+ }
76
+
77
+ function registryForWrite() {
78
+ const reg = readRegistry();
79
+ if (reg.insecure) {
80
+ throw new Error(
81
+ `${registryPath()} is ${reg.insecure === "registry-insecure" ? "writable by other users" : "unreadable"}; fix or remove it first`,
82
+ );
83
+ }
84
+ return reg;
85
+ }
86
+
87
+ function add(o) {
88
+ if (!o.name || !NAME.test(o.name))
89
+ throw new Usage("--name is 1 to 64 letters, digits, spaces or ._'-");
90
+ if (Boolean(o.key) === Boolean(o.keyFile))
91
+ throw new Usage("give one of --public-key or --public-key-file");
92
+ const scopes = String(o.scopes ?? "")
93
+ .split(",")
94
+ .map((s) => s.trim())
95
+ .filter(Boolean);
96
+ if (!scopes.length) throw new Usage(`--scopes is one or more of ${SCOPES.join(", ")}`);
97
+ const unknown = scopes.filter((s) => !SCOPES.includes(s));
98
+ if (unknown.length)
99
+ throw new Usage(`unknown scope ${unknown.join(", ")}; known: ${SCOPES.join(", ")}`);
100
+ let publicKey;
101
+ try {
102
+ publicKey = parsePublicKey(o.key ?? readFileSync(o.keyFile, "utf8"));
103
+ } catch (err) {
104
+ throw new Usage(`--public-key: ${err.message}`);
105
+ }
106
+ const reg = registryForWrite();
107
+ if (reg.devices.some((d) => d.publicKey === publicKey && !d.revokedAt)) {
108
+ throw new Usage("that key is already enrolled");
109
+ }
110
+ const device = {
111
+ id: newDeviceId(),
112
+ name: o.name,
113
+ publicKey,
114
+ scopes: SCOPES.filter((s) => scopes.includes(s)),
115
+ addedAt: new Date().toISOString(),
116
+ revokedAt: null,
117
+ };
118
+ writeRegistry({ ...reg, devices: [...reg.devices, device] });
119
+ return {
120
+ id: device.id,
121
+ name: device.name,
122
+ fingerprint: fingerprint(publicKey),
123
+ scopes: device.scopes,
124
+ };
125
+ }
126
+
127
+ function list() {
128
+ const reg = readRegistry();
129
+ return {
130
+ launchEnabled: reg.launchEnabled,
131
+ ...(reg.insecure ? { problem: reg.insecure } : {}),
132
+ devices: reg.devices.map((d) => ({
133
+ id: d.id,
134
+ name: d.name,
135
+ fingerprint: fingerprint(d.publicKey),
136
+ scopes: d.scopes,
137
+ addedAt: d.addedAt,
138
+ revokedAt: d.revokedAt ?? null,
139
+ })),
140
+ };
141
+ }
142
+
143
+ function main(argv) {
144
+ const [cmd, ...rest] = argv;
145
+ let o;
146
+ try {
147
+ o = options(rest);
148
+ } catch (err) {
149
+ process.stderr.write(`phone-devices: ${err.message}\n${USAGE}`);
150
+ return 2;
151
+ }
152
+ if (!["add", "list", "revoke", "launch"].includes(cmd)) {
153
+ process.stderr.write(USAGE);
154
+ return 2;
155
+ }
156
+ if (cmd !== "list" && isUnattended()) {
157
+ process.stderr.write(
158
+ "phone-devices: an unattended run may not change which devices reach this machine\n",
159
+ );
160
+ return 3;
161
+ }
162
+ try {
163
+ if (cmd === "add") {
164
+ if (o.rest?.length) throw new Usage(`unknown argument ${o.rest[0]}`);
165
+ process.stdout.write(JSON.stringify(add(o)) + "\n");
166
+ return 0;
167
+ }
168
+ if (cmd === "list") {
169
+ const out = list();
170
+ if (o.json) {
171
+ process.stdout.write(JSON.stringify(out) + "\n");
172
+ return 0;
173
+ }
174
+ const lines = [`launch: ${out.launchEnabled ? "on" : "off"}`];
175
+ if (out.problem) lines.push(`problem: ${out.problem}`);
176
+ for (const d of out.devices) {
177
+ lines.push(
178
+ `${d.id} ${d.fingerprint} ${d.scopes.join(",").padEnd(18)} ${d.revokedAt ? `revoked ${d.revokedAt}` : "active"} ${d.name}`,
179
+ );
180
+ }
181
+ process.stdout.write(lines.join("\n") + "\n");
182
+ return 0;
183
+ }
184
+ if (cmd === "revoke") {
185
+ const id = o.rest?.[0];
186
+ if (!id || o.rest.length !== 1 || !DEVICE_ID.test(id))
187
+ throw new Usage("revoke takes one device id");
188
+ const reg = registryForWrite();
189
+ const d = reg.devices.find((x) => x.id === id);
190
+ if (!d) {
191
+ process.stderr.write(`phone-devices: no device ${id}\n`);
192
+ return 1;
193
+ }
194
+ if (d.revokedAt) {
195
+ process.stderr.write(`phone-devices: ${id} was revoked at ${d.revokedAt}\n`);
196
+ return 1;
197
+ }
198
+ d.revokedAt = new Date().toISOString();
199
+ writeRegistry(reg);
200
+ process.stdout.write(JSON.stringify({ id, revokedAt: d.revokedAt }) + "\n");
201
+ return 0;
202
+ }
203
+ const v = o.rest?.[0];
204
+ if (o.rest?.length !== 1 || (v !== "on" && v !== "off"))
205
+ throw new Usage("launch takes on or off");
206
+ const reg = registryForWrite();
207
+ writeRegistry({ ...reg, launchEnabled: v === "on" });
208
+ process.stdout.write(JSON.stringify({ launchEnabled: v === "on" }) + "\n");
209
+ return 0;
210
+ } catch (err) {
211
+ if (err instanceof Usage) {
212
+ process.stderr.write(`phone-devices: ${err.message}\n${USAGE}`);
213
+ return 2;
214
+ }
215
+ process.stderr.write(`phone-devices: ${err.message}\n`);
216
+ return 1;
217
+ }
218
+ }
219
+
220
+ if (invokedDirectly(import.meta.url)) {
221
+ runMain("phone-devices", () => {
222
+ process.exitCode = main(process.argv.slice(2));
223
+ });
224
+ }
@@ -23,15 +23,40 @@
23
23
  // `in_progress` is the gap this exists to catch: not "we decided not to do it"
24
24
  // but "nobody noticed it was still open".
25
25
  //
26
+ // With no plan todos (prefs.global.planTodos off) there is no step list to
27
+ // audit. An attended run reports that as not-applicable.
28
+ //
29
+ // While the quality gates are active (lib/unattended.mjs, gatesActive) Phase 1
30
+ // always writes plan todos, and a run with no todos falls back to the analysis
31
+ // Section 14 `Add new` rows, the one promise every analysed plan carries. With
32
+ // neither, a run that had a Plan phase (`state.analysis.docStatus` recorded, or
33
+ // `state.phases["1"]` entered) is a gap: it planned work and left nothing to
34
+ // check it against. Only a run with no Plan phase, the pipeline tail, is
35
+ // not-applicable. Those verdicts are appended to state.gates[] as
36
+ // `plan-coverage`.
37
+ //
26
38
  // Usage:
27
39
  // plan-coverage-gate.mjs --state <agent-state.json> [--analysis <doc.md>] [--root <dir>] [--json]
28
40
  // plan-coverage-gate.mjs --todos <plan-todos.json> [--analysis <doc.md>] [--root <dir>] [--json]
29
41
  //
30
- // Exit codes: 0 clean, 1 coverage gap, 2 usage / parse error.
42
+ // --analysis-from-state adds one --analysis per entry of the state's
43
+ // analysis.docPath[] (one per platform), and --json-out <file> writes the --json
44
+ // result to that file instead of stdout. Together they make the Phase 4 step
45
+ // one command, with no shell array built from jq output.
46
+ //
47
+ // Exit codes, gates active: 0 clean, 1 coverage gap (a zero-step plan and a
48
+ // planned run with no coverage source included), 2 not-applicable (no Plan
49
+ // phase), 3 usage or unreadable input.
50
+ // Attended: 0 clean, 1 gap, 2 everything else - not-applicable (verdict
51
+ // `not-applicable` in --json), a zero-step plan (verdict `unusable`), usage or
52
+ // unreadable input (no verdict). Phase 4 reads only exit 2 with verdict
53
+ // `not-applicable` as not-applicable.
31
54
 
32
- import { readFileSync, existsSync, writeFileSync } from "node:fs";
33
- import { join, isAbsolute } from "node:path";
34
- import { pathToFileURL } from "node:url";
55
+ import { readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
56
+ import { dirname, join, isAbsolute } from "node:path";
57
+
58
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
59
+ import { gateContext, recordQuietly } from "./gate-ledger.mjs";
35
60
 
36
61
  export function auditTodos(todos) {
37
62
  const gaps = [];
@@ -90,9 +115,48 @@ function readJson(path) {
90
115
  return JSON.parse(readFileSync(path, "utf8"));
91
116
  }
92
117
 
93
- const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
94
- if (isMain) {
95
- const args = process.argv.slice(2);
118
+ /**
119
+ * Promised files across every analysis document.
120
+ * @returns {{rows: string[], missing: string[], verdict: string}}
121
+ */
122
+ function auditFiles(analysisPaths, root) {
123
+ const allRows = [];
124
+ const docsWithoutSection = [];
125
+ for (const docPath of analysisPaths) {
126
+ const rows = filesToAdd(readFileSync(docPath, "utf8"));
127
+ if (rows === null) docsWithoutSection.push(docPath);
128
+ else allRows.push(...rows);
129
+ }
130
+ const rows = [...new Set(allRows)];
131
+ const missing = rows.filter((p) => !existsSync(isAbsolute(p) ? p : join(root, p)));
132
+ const docNote = docsWithoutSection.length
133
+ ? `; ${docsWithoutSection.length} doc(s) carried no Section 14`
134
+ : "";
135
+ const verdict = rows.length
136
+ ? `${rows.length - missing.length}/${rows.length} promised files present across ${analysisPaths.length} doc(s)${docNote}`
137
+ : `no Section 14 rows in ${analysisPaths.length} doc(s)${docNote}`;
138
+ return { rows, missing, verdict };
139
+ }
140
+
141
+ /**
142
+ * Did this run have a Plan phase? Phase 1 Step 4 always records
143
+ * `analysis.docStatus`; the phase tracker records `phases["1"]`.
144
+ *
145
+ * @param {object|null} state
146
+ * @returns {boolean}
147
+ */
148
+ export function planPhaseRan(state) {
149
+ if (!state || typeof state !== "object" || state.mode === "analysis") return false;
150
+ const p1 = state.phases?.["1"];
151
+ if (p1 && typeof p1 === "object" && p1.status && !["pending", "skipped"].includes(p1.status)) {
152
+ return true;
153
+ }
154
+ return typeof state.analysis?.docStatus === "string";
155
+ }
156
+
157
+ const LEDGER = { clean: "pass", gap: "fail", unusable: "fail", "not-applicable": "not-applicable" };
158
+
159
+ async function main(args) {
96
160
  const flag = (name) => {
97
161
  const i = args.indexOf(name);
98
162
  return i === -1 ? undefined : args[i + 1];
@@ -117,86 +181,150 @@ if (isMain) {
117
181
  "Fails when a plan step never reached a terminal status, when a skip or",
118
182
  "failure carries no reason, or when an analysis Section 14 `Add new` file",
119
183
  "is missing from the tree. Run before Phase 4 commit.",
184
+ "Exit: 0 clean, 1 gap, 2 not-applicable, 3 usage or unreadable input.",
120
185
  "",
121
186
  ].join("\n"),
122
187
  );
123
- process.exit(args.includes("--help") ? 0 : 2);
188
+ return args.includes("--help") ? 0 : 2;
124
189
  }
125
190
 
126
191
  const statePath = flag("--state");
127
192
  const todosPath = flag("--todos");
128
193
  const analysisPaths = flagAll("--analysis");
194
+ if (statePath && args.includes("--analysis-from-state")) {
195
+ try {
196
+ const docs = readJson(statePath)?.analysis?.docPath;
197
+ if (Array.isArray(docs)) {
198
+ analysisPaths.push(...docs.filter((d) => typeof d === "string" && d !== ""));
199
+ }
200
+ } catch {
201
+ /* an unreadable state is reported below, where the plan is read */
202
+ }
203
+ }
129
204
  const root = flag("--root") ?? process.cwd();
130
- const asJson = args.includes("--json");
205
+ const jsonOut = flag("--json-out");
206
+ const asJson = args.includes("--json") || jsonOut !== undefined;
207
+ const writeJson = (obj) => {
208
+ const text = `${JSON.stringify(obj, null, 2)}\n`;
209
+ if (jsonOut === undefined) {
210
+ writeFileSync(1, text);
211
+ return;
212
+ }
213
+ mkdirSync(dirname(jsonOut), { recursive: true });
214
+ writeFileSync(jsonOut, text);
215
+ };
216
+ // The quality gates run for an unattended run and for a run in autopilot
217
+ // mode (lib/unattended.mjs, gatesActive); neither has a person to ask.
218
+ const gatesOn = gateContext({ statePath: statePath ?? null, repo: root }).active;
219
+ // Attended runs keep the base exit contract: every non-verdict is exit 2.
220
+ const UNREADABLE = gatesOn ? 3 : 2;
221
+ const ZERO_STEPS = gatesOn ? 1 : 2;
222
+
223
+ const finish = async (code, result, detail = {}) => {
224
+ if (gatesOn) {
225
+ await recordQuietly(
226
+ "plan-coverage",
227
+ LEDGER[result.verdict] ?? "fail",
228
+ { verdict: result.verdict, ...detail },
229
+ { repo: root, statePath: statePath ?? null },
230
+ );
231
+ }
232
+ return code;
233
+ };
131
234
 
132
235
  if (!statePath && !todosPath) {
133
236
  process.stderr.write("plan-coverage-gate: pass --state or --todos\n");
134
- process.exit(2);
237
+ return UNREADABLE;
135
238
  }
136
239
 
137
240
  let todos;
241
+ let state = null;
138
242
  try {
139
243
  if (todosPath) {
140
244
  const parsed = readJson(todosPath);
141
245
  todos = parsed?.todos ?? parsed;
142
246
  } else {
143
- const state = readJson(statePath);
247
+ state = readJson(statePath);
144
248
  todos = state?.plan?.todos ?? state?.plan?.todoList?.todos ?? null;
145
249
  }
146
250
  } catch (e) {
147
251
  process.stderr.write(`plan-coverage-gate: cannot read the plan: ${e.message}\n`);
148
- process.exit(2);
252
+ return finish(UNREADABLE, { verdict: "unusable" }, { reason: `unreadable plan: ${e.message}` });
149
253
  }
150
254
 
151
- // No plan at all is not a pass, and neither is an empty one: a Phase 2 that
152
- // produced no steps, or a state whose todos got cleared, would otherwise read
153
- // as "0/0 accounted for" and let the commit through. A mode with no Phase 2
154
- // (analysis) is expected to skip this gate explicitly rather than let it
155
- // report clean.
156
- if (!Array.isArray(todos) || todos.length === 0) {
157
- const msg = Array.isArray(todos)
158
- ? "the plan has zero steps - that is an unusable plan, not a clean one; skip this gate explicitly for modes with no Phase 2"
159
- : "no plan todos found - if this mode has no Phase 2, skip this gate explicitly";
160
- // fd 1 synchronously, then exit. This sits in `if (isMain)` at module scope,
161
- // not in a function, so there is nothing to return from - and a
162
- // process.stdout.write followed by process.exit() loses the verdict when
163
- // stdout is a pipe.
164
- if (asJson)
165
- writeFileSync(1, `${JSON.stringify({ verdict: "unusable", reason: msg }, null, 2)}\n`);
166
- else process.stderr.write(`plan-coverage-gate: ${msg}\n`);
167
- process.exit(2);
255
+ let files = null;
256
+ if (analysisPaths.length) {
257
+ try {
258
+ files = auditFiles(analysisPaths, root);
259
+ } catch (e) {
260
+ process.stderr.write(`plan-coverage-gate: cannot read the analysis doc: ${e.message}\n`);
261
+ return finish(
262
+ UNREADABLE,
263
+ { verdict: "unusable" },
264
+ { reason: `unreadable analysis: ${e.message}` },
265
+ );
266
+ }
168
267
  }
169
268
 
170
- const { gaps, accounted } = auditTodos(todos);
171
-
172
- let missingFiles = [];
173
- let filesVerdict;
174
- if (analysisPaths.length) {
175
- const allRows = [];
176
- const docsWithoutSection = [];
177
- for (const docPath of analysisPaths) {
178
- let rows = null;
179
- try {
180
- rows = filesToAdd(readFileSync(docPath, "utf8"));
181
- } catch (e) {
182
- process.stderr.write(`plan-coverage-gate: cannot read the analysis doc: ${e.message}\n`);
183
- process.exit(2);
184
- }
185
- if (rows === null) docsWithoutSection.push(docPath);
186
- else allRows.push(...rows);
269
+ // No step list. Attended: nothing to audit. Unattended: the promised files
270
+ // are the coverage source when there are any.
271
+ if (!Array.isArray(todos)) {
272
+ if (gatesOn && files && files.rows.length) {
273
+ const failed = files.missing.length > 0;
274
+ const result = {
275
+ verdict: failed ? "gap" : "clean",
276
+ source: "analysis-section-14",
277
+ todos: { total: 0, accounted: 0, gaps: [] },
278
+ filesToAdd: { verdict: files.verdict, missing: files.missing },
279
+ };
280
+ if (asJson) writeJson(result);
281
+ else if (failed) {
282
+ writeFileSync(
283
+ 2,
284
+ `${[
285
+ `plan coverage (from analysis Section 14, no plan todos): ${files.missing.length} missing file(s)`,
286
+ ...files.missing.map((f) => ` missing file promised by analysis Section 14: ${f}`),
287
+ ].join("\n")}\n`,
288
+ );
289
+ } else writeFileSync(1, `plan coverage (from analysis Section 14): ${files.verdict}\n`);
290
+ return finish(failed ? 1 : 0, result, {
291
+ source: result.source,
292
+ missing: files.missing.length,
293
+ });
187
294
  }
188
- const unique = [...new Set(allRows)];
189
- missingFiles = unique.filter((p) => !existsSync(isAbsolute(p) ? p : join(root, p)));
190
- const docNote = docsWithoutSection.length
191
- ? `; ${docsWithoutSection.length} doc(s) carried no Section 14`
192
- : "";
193
- filesVerdict = unique.length
194
- ? `${unique.length - missingFiles.length}/${unique.length} promised files present across ${analysisPaths.length} doc(s)${docNote}`
195
- : `no Section 14 rows in ${analysisPaths.length} doc(s)${docNote}`;
196
- } else {
197
- filesVerdict = "skipped (no --analysis given)";
295
+ if (gatesOn && planPhaseRan(state)) {
296
+ const reason =
297
+ "the run had a Plan phase and left no plan todos and no analysis Section 14 `Add new` row to check it against";
298
+ const result = { verdict: "gap", reason, todos: { total: 0, accounted: 0, gaps: [] } };
299
+ if (asJson) writeJson(result);
300
+ else process.stderr.write(`plan-coverage-gate: ${reason}\n`);
301
+ return finish(1, result, { reason });
302
+ }
303
+ const reason = gatesOn
304
+ ? "no Plan phase in this run, so no plan todos and no analysis Section 14 row to cover"
305
+ : "no plan todos found - this mode has no step list to audit";
306
+ const result = { verdict: "not-applicable", reason };
307
+ // fd 1 synchronously: process.exit() follows, and a pipe would keep only
308
+ // what fit in its buffer.
309
+ if (asJson) writeJson(result);
310
+ else process.stderr.write(`plan-coverage-gate: not-applicable: ${reason}\n`);
311
+ return finish(2, result, { reason });
198
312
  }
199
313
 
314
+ // An empty step list is not a clean one: a Phase 2 that produced no steps, or
315
+ // a state whose todos got cleared, would otherwise read as "0/0 accounted for"
316
+ // and let the commit through.
317
+ if (todos.length === 0) {
318
+ const reason = "the plan has zero steps - that is an unusable plan, not a clean one";
319
+ if (asJson) writeJson({ verdict: "unusable", reason });
320
+ else process.stderr.write(`plan-coverage-gate: ${reason}\n`);
321
+ return finish(ZERO_STEPS, { verdict: "unusable" }, { reason });
322
+ }
323
+
324
+ const { gaps, accounted } = auditTodos(todos);
325
+ const missingFiles = files ? files.missing : [];
326
+ const filesVerdict = files ? files.verdict : "skipped (no --analysis given)";
327
+
200
328
  const failed = gaps.length > 0 || missingFiles.length > 0;
201
329
  const result = {
202
330
  verdict: failed ? "gap" : "clean",
@@ -204,21 +332,27 @@ if (isMain) {
204
332
  filesToAdd: { verdict: filesVerdict, missing: missingFiles },
205
333
  };
206
334
 
335
+ // Same rule as above: every verdict goes to its fd synchronously.
207
336
  if (asJson) {
208
- process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
337
+ writeJson(result);
209
338
  } else if (failed) {
210
- process.stderr.write(
211
- `plan coverage: ${gaps.length} unaccounted step(s), ${missingFiles.length} missing file(s)\n`,
212
- );
213
- for (const g of gaps) process.stderr.write(` ${g.id} [${g.status}] ${g.task} - ${g.why}\n`);
214
- for (const f of missingFiles)
215
- process.stderr.write(` missing file promised by analysis Section 14: ${f}\n`);
216
- process.stderr.write(` files-to-add: ${filesVerdict}\n`);
339
+ const lines = [
340
+ `plan coverage: ${gaps.length} unaccounted step(s), ${missingFiles.length} missing file(s)`,
341
+ ...gaps.map((g) => ` ${g.id} [${g.status}] ${g.task} - ${g.why}`),
342
+ ...missingFiles.map((f) => ` missing file promised by analysis Section 14: ${f}`),
343
+ ` files-to-add: ${filesVerdict}`,
344
+ ];
345
+ writeFileSync(2, `${lines.join("\n")}\n`);
217
346
  } else {
218
347
  // spec-kit's rule: a clean pass says one line and writes nothing.
219
- process.stdout.write(
348
+ writeFileSync(
349
+ 1,
220
350
  `plan coverage: ${accounted.length}/${todos.length} steps accounted for; files-to-add: ${filesVerdict}\n`,
221
351
  );
222
352
  }
223
- process.exit(failed ? 1 : 0);
353
+ return finish(failed ? 1 : 0, result, { gaps: gaps.length, missing: missingFiles.length });
354
+ }
355
+
356
+ if (invokedDirectly(import.meta.url)) {
357
+ main(process.argv.slice(2)).then((code) => process.exit(code));
224
358
  }