@mmerterden/multi-agent-pipeline 20.2.1 → 20.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (622) hide show
  1. package/CHANGELOG.md +919 -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 +617 -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/usage-endpoint.mjs +33 -0
  148. package/pipeline/lib/user-facing.mjs +82 -0
  149. package/pipeline/lib/user-facing.sh +58 -0
  150. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  151. package/pipeline/multi-agent-refs/analysis/evidence.md +4 -0
  152. package/pipeline/multi-agent-refs/analysis/locked.md +2 -2
  153. package/pipeline/multi-agent-refs/analysis/render.md +4 -3
  154. package/pipeline/multi-agent-refs/analysis/resolve.md +3 -1
  155. package/pipeline/multi-agent-refs/analysis/synthesis.md +4 -5
  156. package/pipeline/multi-agent-refs/analysis-template.md +10 -17
  157. package/pipeline/multi-agent-refs/channels/jira.md +1 -1
  158. package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
  159. package/pipeline/multi-agent-refs/conventions-defaults.md +32 -32
  160. package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -17
  161. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +37 -9
  162. package/pipeline/multi-agent-refs/features/autopilot-operations.md +277 -0
  163. package/pipeline/multi-agent-refs/features/constitution.md +196 -0
  164. package/pipeline/multi-agent-refs/features/doctor.md +6 -3
  165. package/pipeline/multi-agent-refs/features/external-context-injection.md +6 -1
  166. package/pipeline/multi-agent-refs/features/jira-context.md +1 -1
  167. package/pipeline/multi-agent-refs/features/maturity-followup.md +43 -23
  168. package/pipeline/multi-agent-refs/features/model-fallback.md +9 -0
  169. package/pipeline/multi-agent-refs/features/phone-api.md +306 -0
  170. package/pipeline/multi-agent-refs/features/plan-critic.md +159 -0
  171. package/pipeline/multi-agent-refs/features/research.md +150 -0
  172. package/pipeline/multi-agent-refs/features/review-decision.md +185 -0
  173. package/pipeline/multi-agent-refs/features/scaffold.md +160 -0
  174. package/pipeline/multi-agent-refs/features/security-audit.md +6 -2
  175. package/pipeline/multi-agent-refs/features/skill-conformance.md +4 -1
  176. package/pipeline/multi-agent-refs/features/stack-adapters.md +116 -0
  177. package/pipeline/multi-agent-refs/features/unattended-gates.md +316 -0
  178. package/pipeline/multi-agent-refs/features/unattended-security.md +731 -0
  179. package/pipeline/multi-agent-refs/features/url-enrichment.md +2 -2
  180. package/pipeline/multi-agent-refs/features/usage-reporting.md +127 -35
  181. package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
  182. package/pipeline/multi-agent-refs/features/visual-evidence.md +7 -7
  183. package/pipeline/multi-agent-refs/keychain.md +6 -11
  184. package/pipeline/multi-agent-refs/payload-contracts.md +2 -2
  185. package/pipeline/multi-agent-refs/phases/log-format.md +2 -3
  186. package/pipeline/multi-agent-refs/phases/modes.md +10 -12
  187. package/pipeline/multi-agent-refs/phases/operations.md +11 -5
  188. package/pipeline/multi-agent-refs/phases/phase-0-init.md +73 -81
  189. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +22 -22
  190. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +42 -66
  191. package/pipeline/multi-agent-refs/phases/phase-3-review.md +45 -79
  192. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +28 -38
  193. package/pipeline/multi-agent-refs/phases/phase-5-report.md +27 -25
  194. package/pipeline/multi-agent-refs/phases.md +1 -1
  195. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  196. package/pipeline/multi-agent-refs/progress-contract.md +13 -16
  197. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  198. package/pipeline/multi-agent-refs/research/engine.md +91 -0
  199. package/pipeline/multi-agent-refs/rules.md +6 -4
  200. package/pipeline/multi-agent-refs/setup/repo-discovery.md +2 -2
  201. package/pipeline/multi-agent-refs/tracker-contract.md +2 -2
  202. package/pipeline/multi-agent-refs/unattended-contract.md +68 -16
  203. package/pipeline/rules/figma-pipeline.md +12 -12
  204. package/pipeline/schemas/agent-state.schema.json +480 -18
  205. package/pipeline/schemas/analysis-spec.schema.json +4 -4
  206. package/pipeline/schemas/answer-request.schema.json +24 -0
  207. package/pipeline/schemas/answer-result.schema.json +28 -0
  208. package/pipeline/schemas/autopilot-config.schema.json +111 -13
  209. package/pipeline/schemas/command-parameters.schema.json +99 -0
  210. package/pipeline/schemas/constitution.schema.json +56 -0
  211. package/pipeline/schemas/contract-error.schema.json +52 -0
  212. package/pipeline/schemas/design-check-config.schema.json +5 -1
  213. package/pipeline/schemas/issues.schema.json +61 -0
  214. package/pipeline/schemas/launch-plan.schema.json +46 -0
  215. package/pipeline/schemas/launch-request.schema.json +45 -0
  216. package/pipeline/schemas/launch.json +61 -0
  217. package/pipeline/schemas/launch.schema.json +84 -0
  218. package/pipeline/schemas/phases.json +2 -2
  219. package/pipeline/schemas/phases.schema.json +68 -0
  220. package/pipeline/schemas/phone-devices.schema.json +61 -0
  221. package/pipeline/schemas/phone-signed-request.schema.json +67 -0
  222. package/pipeline/schemas/plan-critique.schema.json +99 -0
  223. package/pipeline/schemas/plan-todos.schema.json +7 -7
  224. package/pipeline/schemas/planning-output.schema.json +5 -0
  225. package/pipeline/schemas/pr-request.schema.json +46 -0
  226. package/pipeline/schemas/prefs.schema.json +79 -8
  227. package/pipeline/schemas/research-output.schema.json +118 -0
  228. package/pipeline/schemas/review-file-exclusions.schema.json +25 -0
  229. package/pipeline/schemas/reviewer-output.schema.json +40 -4
  230. package/pipeline/schemas/run-questions.json +392 -0
  231. package/pipeline/schemas/run-questions.schema.json +118 -0
  232. package/pipeline/schemas/runs-index.schema.json +189 -0
  233. package/pipeline/schemas/scaffold-manifest.schema.json +43 -0
  234. package/pipeline/schemas/secret-patterns.schema.json +28 -0
  235. package/pipeline/schemas/stack-adapters.json +527 -0
  236. package/pipeline/schemas/stack-adapters.schema.json +184 -0
  237. package/pipeline/schemas/token-budget.json +1 -1
  238. package/pipeline/schemas/token-budget.schema.json +26 -0
  239. package/pipeline/schemas/triage-output.schema.json +64 -3
  240. package/pipeline/schemas/unattended-policy.json +139 -0
  241. package/pipeline/schemas/unattended-policy.schema.json +73 -0
  242. package/pipeline/schemas/unattended-profile.json +248 -0
  243. package/pipeline/schemas/unattended-profile.schema.json +198 -0
  244. package/pipeline/schemas/worktrees.schema.json +51 -0
  245. package/pipeline/scripts/README.md +1 -0
  246. package/pipeline/scripts/_autopilot-config.mjs +130 -0
  247. package/pipeline/scripts/_autopilot-ops.mjs +567 -0
  248. package/pipeline/scripts/_autopilot-outcomes.mjs +174 -0
  249. package/pipeline/scripts/_command-contract.mjs +384 -0
  250. package/pipeline/scripts/_cost.mjs +40 -0
  251. package/pipeline/scripts/_notices.mjs +160 -0
  252. package/pipeline/scripts/_phone-auth.mjs +485 -0
  253. package/pipeline/scripts/_pre-existing.mjs +294 -0
  254. package/pipeline/scripts/_redact.mjs +77 -0
  255. package/pipeline/scripts/_run-paths.mjs +4 -2
  256. package/pipeline/scripts/_stack-adapter.mjs +678 -0
  257. package/pipeline/scripts/_stack-routing.mjs +1 -1
  258. package/pipeline/scripts/agent-guard.py +348 -37
  259. package/pipeline/scripts/agent-guard.sh +41 -13
  260. package/pipeline/scripts/analysis-story-tree.mjs +79 -3
  261. package/pipeline/scripts/answer-question.mjs +181 -0
  262. package/pipeline/scripts/audit-log-rotate.sh +1 -4
  263. package/pipeline/scripts/audit-log.sh +4 -4
  264. package/pipeline/scripts/autopilot-arming.mjs +389 -21
  265. package/pipeline/scripts/autopilot-awake.mjs +255 -0
  266. package/pipeline/scripts/autopilot-intake.mjs +137 -36
  267. package/pipeline/scripts/autopilot-menubar.swift +156 -44
  268. package/pipeline/scripts/autopilot-publish.mjs +1625 -0
  269. package/pipeline/scripts/autopilot-runner.mjs +1678 -222
  270. package/pipeline/scripts/autopilot-status.sh +198 -33
  271. package/pipeline/scripts/build-lock.sh +120 -0
  272. package/pipeline/scripts/build-references.mjs +4 -1
  273. package/pipeline/scripts/build-stack-plugins.mjs +59 -22
  274. package/pipeline/scripts/capture-flush.sh +1 -1
  275. package/pipeline/scripts/capture-resume.sh +13 -9
  276. package/pipeline/scripts/check-derived-drift.mjs +52 -11
  277. package/pipeline/scripts/commands.mjs +88 -0
  278. package/pipeline/scripts/constitution.mjs +362 -0
  279. package/pipeline/scripts/contract-server.mjs +776 -0
  280. package/pipeline/scripts/cost-analyze.mjs +89 -39
  281. package/pipeline/scripts/diff-explain.mjs +12 -1
  282. package/pipeline/scripts/doctor.mjs +77 -28
  283. package/pipeline/scripts/evidence-gate.mjs +192 -12
  284. package/pipeline/scripts/feedback-send.mjs +4 -2
  285. package/pipeline/scripts/gate-ledger.mjs +449 -0
  286. package/pipeline/scripts/gc-abandoned.sh +132 -13
  287. package/pipeline/scripts/gen-facts.mjs +31 -15
  288. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  289. package/pipeline/scripts/github-ssh-setup.sh +140 -29
  290. package/pipeline/scripts/graph-mermaid.mjs +4 -1
  291. package/pipeline/scripts/issues.mjs +236 -0
  292. package/pipeline/scripts/jira-attach.sh +6 -2
  293. package/pipeline/scripts/jira-search.sh +4 -3
  294. package/pipeline/scripts/keychain-save.sh +125 -24
  295. package/pipeline/scripts/keychain.py +63 -93
  296. package/pipeline/scripts/launch-request.mjs +747 -0
  297. package/pipeline/scripts/localize-commands.mjs +4 -10
  298. package/pipeline/scripts/log-metric.sh +6 -5
  299. package/pipeline/scripts/maturity-followup.mjs +13 -4
  300. package/pipeline/scripts/memory-save.sh +25 -0
  301. package/pipeline/scripts/migrate-prefs.mjs +4 -3
  302. package/pipeline/scripts/open-questions-gate.mjs +276 -0
  303. package/pipeline/scripts/phase-tracker.sh +41 -27
  304. package/pipeline/scripts/phase0-exit-gate.mjs +22 -4
  305. package/pipeline/scripts/phone-devices.mjs +224 -0
  306. package/pipeline/scripts/plan-coverage-gate.mjs +200 -66
  307. package/pipeline/scripts/plan-critique-gate.mjs +591 -0
  308. package/pipeline/scripts/pr-request.mjs +188 -0
  309. package/pipeline/scripts/pre-commit-check.sh +115 -4
  310. package/pipeline/scripts/probe-evidence-capability.sh +44 -5
  311. package/pipeline/scripts/record-phase.mjs +71 -0
  312. package/pipeline/scripts/render-agent-log-cost.sh +17 -2
  313. package/pipeline/scripts/render-cost-summary.sh +1 -1
  314. package/pipeline/scripts/render-work-summary.sh +1 -1
  315. package/pipeline/scripts/require-supported-version.sh +4 -1
  316. package/pipeline/scripts/research-gate.mjs +704 -0
  317. package/pipeline/scripts/review-decision-gate.mjs +403 -0
  318. package/pipeline/scripts/routine-registry.mjs +5 -2
  319. package/pipeline/scripts/runs-index.mjs +135 -27
  320. package/pipeline/scripts/scaffold-gate.mjs +393 -0
  321. package/pipeline/scripts/skill-conformance.mjs +25 -8
  322. package/pipeline/scripts/skill-siblings.mjs +2 -1
  323. package/pipeline/scripts/smoke-cross-cli-behavior.sh +32 -6
  324. package/pipeline/scripts/smoke-schema-validation.sh +6 -2
  325. package/pipeline/scripts/spec-consistency-gate.mjs +469 -0
  326. package/pipeline/scripts/symbol-existence-gate.mjs +450 -0
  327. package/pipeline/scripts/test-gap-scan.mjs +40 -2
  328. package/pipeline/scripts/test-integrity-gate.mjs +20 -4
  329. package/pipeline/scripts/test-strength.mjs +484 -0
  330. package/pipeline/scripts/test-summary.mjs +651 -0
  331. package/pipeline/scripts/triage-memory.mjs +49 -9
  332. package/pipeline/scripts/unattended_policy.py +2786 -0
  333. package/pipeline/scripts/uninstall.mjs +10 -10
  334. package/pipeline/scripts/update-issue-progress.sh +1 -1
  335. package/pipeline/scripts/usage-identity.mjs +288 -0
  336. package/pipeline/scripts/usage-register.mjs +187 -67
  337. package/pipeline/scripts/usage-report.mjs +232 -67
  338. package/pipeline/scripts/validate-complaint-doc.mjs +28 -10
  339. package/pipeline/scripts/validate-planning.mjs +6 -0
  340. package/pipeline/scripts/verify-citations.mjs +151 -38
  341. package/pipeline/scripts/verify.mjs +58 -18
  342. package/pipeline/scripts/worktree-prepare.sh +126 -0
  343. package/pipeline/scripts/worktrees.mjs +124 -0
  344. package/pipeline/scripts/write-state.mjs +48 -17
  345. package/pipeline/skills/.skill-manifest.json +222 -226
  346. package/pipeline/skills/.skills-index.json +77 -88
  347. package/pipeline/skills/shared/README.md +44 -45
  348. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +3 -2
  349. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +3 -2
  350. package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -9
  351. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +6 -5
  352. package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +11 -3
  353. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +2 -1
  354. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -3
  355. package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +32 -6
  356. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +66 -8
  357. package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +27 -9
  358. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -1
  359. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +7 -4
  360. package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +3 -2
  361. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +2 -1
  362. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +7 -5
  363. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +2 -1
  364. package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +3 -2
  365. package/pipeline/skills/shared/core/multi-agent-feedback/SKILL.md +2 -1
  366. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +2 -1
  367. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +2 -1
  368. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +2 -1
  369. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -1
  370. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -1
  371. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +3 -2
  372. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +2 -1
  373. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +7 -4
  374. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -1
  375. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +2 -1
  376. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +2 -1
  377. package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -1
  378. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +2 -1
  379. package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +2 -1
  380. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +2 -1
  381. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +2 -1
  382. package/pipeline/skills/shared/core/multi-agent-research/SKILL.md +35 -0
  383. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +7 -3
  384. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +7 -5
  385. package/pipeline/skills/shared/core/multi-agent-review-analysis/SKILL.md +2 -1
  386. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +3 -2
  387. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +2 -1
  388. package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +2 -1
  389. package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +2 -1
  390. package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +2 -1
  391. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +2 -1
  392. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +3 -2
  393. package/pipeline/skills/shared/core/multi-agent-scaffold/SKILL.md +30 -0
  394. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -1
  395. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +4 -3
  396. package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +2 -1
  397. package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +60 -0
  398. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +8 -8
  399. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +2 -1
  400. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +7 -8
  401. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -1
  402. package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +2 -1
  403. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +10 -10
  404. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -1
  405. package/pipeline/skills/shared/core/multi-agent-test-accessibility/SKILL.md +2 -1
  406. package/pipeline/skills/shared/core/multi-agent-test-dark-mode/SKILL.md +2 -1
  407. package/pipeline/skills/shared/core/multi-agent-test-dynamic-type/SKILL.md +2 -1
  408. package/pipeline/skills/shared/core/multi-agent-test-screenshots/SKILL.md +2 -1
  409. package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -1
  410. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +3 -2
  411. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -1
  412. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +46 -0
  413. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +9 -3
  414. package/pipeline/skills/shared/external/NOTICE-paul-hudson-skills.md +54 -0
  415. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +9 -11
  416. package/pipeline/skills/shared/external/NOTICE-vibeship-spawner-skills.md +203 -0
  417. package/pipeline/skills/shared/external/NOTICE-xcode-build-skills.md +10 -3
  418. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +131 -25
  419. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -3
  420. package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -1
  421. package/pipeline/skills/shared/external/android-architecture/SKILL.md +85 -76
  422. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +51 -3
  423. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +9 -0
  424. package/pipeline/skills/shared/external/android-datastore/SKILL.md +4 -3
  425. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +4 -3
  426. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +106 -181
  427. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +147 -0
  428. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +45 -4
  429. package/pipeline/skills/shared/external/android-performance/SKILL.md +27 -2
  430. package/pipeline/skills/shared/external/android-performance/references/patterns.md +37 -37
  431. package/pipeline/skills/shared/external/api-patterns/SKILL.md +111 -67
  432. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +128 -0
  433. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +136 -186
  434. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +140 -0
  435. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +207 -0
  436. package/pipeline/skills/shared/external/api-security-best-practices/references/operations.md +66 -0
  437. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +175 -0
  438. package/pipeline/skills/shared/external/app-clips/SKILL.md +2 -0
  439. package/pipeline/skills/shared/external/app-intents/SKILL.md +2 -0
  440. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +4 -3
  441. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +2 -0
  442. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -0
  443. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +2 -0
  444. package/pipeline/skills/shared/external/architecture/SKILL.md +106 -39
  445. package/pipeline/skills/shared/external/architecture/references/adr-and-review.md +76 -0
  446. package/pipeline/skills/shared/external/authentication/SKILL.md +2 -0
  447. package/pipeline/skills/shared/external/avkit/SKILL.md +2 -0
  448. package/pipeline/skills/shared/external/background-processing/SKILL.md +2 -1
  449. package/pipeline/skills/shared/external/backlog/SKILL.md +3 -2
  450. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +2 -0
  451. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +1 -1
  452. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +3 -2
  453. package/pipeline/skills/shared/external/clean-code/SKILL.md +192 -90
  454. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +2 -1
  455. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +1 -1
  456. package/pipeline/skills/shared/external/compose-components/SKILL.md +4 -4
  457. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +22 -22
  458. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +10 -10
  459. package/pipeline/skills/shared/external/compose-testing/SKILL.md +13 -6
  460. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +59 -59
  461. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +2 -0
  462. package/pipeline/skills/shared/external/context-compression/SKILL.md +118 -250
  463. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +2 -0
  464. package/pipeline/skills/shared/external/core-data/SKILL.md +2 -0
  465. package/pipeline/skills/shared/external/core-motion/SKILL.md +2 -0
  466. package/pipeline/skills/shared/external/core-nfc/SKILL.md +2 -0
  467. package/pipeline/skills/shared/external/coreml/SKILL.md +2 -1
  468. package/pipeline/skills/shared/external/council/SKILL.md +2 -1
  469. package/pipeline/skills/shared/external/cryptokit/SKILL.md +2 -0
  470. package/pipeline/skills/shared/external/css-modern/SKILL.md +3 -2
  471. package/pipeline/skills/shared/external/database-patterns/SKILL.md +3 -2
  472. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +2 -0
  473. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +130 -20
  474. package/pipeline/skills/shared/external/debugging-strategies/references/hard-cases.md +54 -0
  475. package/pipeline/skills/shared/external/device-integrity/SKILL.md +2 -0
  476. package/pipeline/skills/shared/external/docker-expert/SKILL.md +137 -381
  477. package/pipeline/skills/shared/external/docker-expert/references/patterns.md +98 -0
  478. package/pipeline/skills/shared/external/energykit/SKILL.md +2 -1
  479. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +3 -1
  480. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +2 -2
  481. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +116 -185
  482. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +235 -0
  483. package/pipeline/skills/shared/external/fastapi-pro/references/testing-and-deployment.md +69 -0
  484. package/pipeline/skills/shared/external/firebase/SKILL.md +4 -3
  485. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +151 -293
  486. package/pipeline/skills/shared/external/github-actions-templates/references/patterns.md +96 -0
  487. package/pipeline/skills/shared/external/healthkit/SKILL.md +2 -0
  488. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +69 -72
  489. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +111 -0
  490. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +77 -86
  491. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +98 -0
  492. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +150 -78
  493. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +76 -97
  494. package/pipeline/skills/shared/external/hig-components-system/references/surfaces.md +98 -0
  495. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +48 -76
  496. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +132 -0
  497. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +147 -106
  498. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +61 -77
  499. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +92 -0
  500. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +147 -77
  501. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +61 -121
  502. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +108 -0
  503. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +2 -0
  504. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +1 -1
  505. package/pipeline/skills/shared/external/html-semantic/SKILL.md +3 -2
  506. package/pipeline/skills/shared/external/humanizer/SKILL.md +2 -1
  507. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +2 -0
  508. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +2 -1
  509. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +14 -2
  510. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
  511. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +4 -3
  512. package/pipeline/skills/shared/external/ios-localization/SKILL.md +8 -6
  513. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +2 -2
  514. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +11 -11
  515. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +2 -1
  516. package/pipeline/skills/shared/external/ios-networking/SKILL.md +2 -0
  517. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +2 -0
  518. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +102 -193
  519. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +103 -0
  520. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -2
  521. package/pipeline/skills/shared/external/live-activities/evals/evals.json +2 -2
  522. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +7 -9
  523. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +3 -3
  524. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +2 -2
  525. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +4 -3
  526. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +5 -3
  527. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +2 -0
  528. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +1 -1
  529. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +2 -0
  530. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +1 -1
  531. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +149 -46
  532. package/pipeline/skills/shared/external/monorepo-architect/references/patterns.md +70 -0
  533. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +2 -0
  534. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +1 -1
  535. package/pipeline/skills/shared/external/natural-language/SKILL.md +2 -0
  536. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +3 -2
  537. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +91 -21
  538. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +247 -0
  539. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/testing-and-frameworks.md +57 -0
  540. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +155 -232
  541. package/pipeline/skills/shared/external/observability-engineer/references/patterns.md +75 -0
  542. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +2 -0
  543. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +2 -2
  544. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +18 -18
  545. package/pipeline/skills/shared/external/pdfkit/SKILL.md +2 -1
  546. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -0
  547. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
  548. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -0
  549. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +2 -1
  550. package/pipeline/skills/shared/external/push-notifications/SKILL.md +2 -0
  551. package/pipeline/skills/shared/external/python-patterns/SKILL.md +3 -2
  552. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +3 -2
  553. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +2 -2
  554. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
  555. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +3 -2
  556. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +7 -7
  557. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +69 -69
  558. package/pipeline/skills/shared/external/room-database/references/patterns.md +126 -126
  559. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -1
  560. package/pipeline/skills/shared/external/security-review/SKILL.md +4 -3
  561. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +2 -2
  562. package/pipeline/skills/shared/external/signal-community/SKILL.md +1 -1
  563. package/pipeline/skills/shared/external/skill-creator/SKILL.md +2 -1
  564. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +2 -1
  565. package/pipeline/skills/shared/external/spm-build-analysis/SKILL.md +2 -0
  566. package/pipeline/skills/shared/external/storekit/SKILL.md +2 -0
  567. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +2 -0
  568. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +2 -1
  569. package/pipeline/skills/shared/external/swift-charts/SKILL.md +2 -1
  570. package/pipeline/skills/shared/external/swift-codable/SKILL.md +2 -0
  571. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -1
  572. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +5 -4
  573. package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +2 -1
  574. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +2 -0
  575. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -1
  576. package/pipeline/skills/shared/external/swift-security/SKILL.md +2 -1
  577. package/pipeline/skills/shared/external/swift-testing/SKILL.md +2 -0
  578. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +2 -1
  579. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +10 -0
  580. package/pipeline/skills/shared/external/swiftdata/SKILL.md +2 -0
  581. package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +2 -1
  582. package/pipeline/skills/shared/external/swiftlint/SKILL.md +2 -0
  583. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +2 -0
  584. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +3 -1
  585. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +2 -0
  586. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +2 -0
  587. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +2 -0
  588. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +2 -0
  589. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +3 -1
  590. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +3 -1
  591. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +5 -4
  592. package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +2 -1
  593. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +5 -4
  594. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +2 -0
  595. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +4 -3
  596. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +2 -0
  597. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +3 -2
  598. package/pipeline/skills/shared/external/testing-backend/SKILL.md +3 -2
  599. package/pipeline/skills/shared/external/tipkit/SKILL.md +2 -0
  600. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +3 -2
  601. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -0
  602. package/pipeline/skills/shared/external/vue-composition/SKILL.md +3 -2
  603. package/pipeline/skills/shared/external/weatherkit/SKILL.md +2 -0
  604. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +3 -2
  605. package/pipeline/skills/shared/external/web-performance/SKILL.md +3 -2
  606. package/pipeline/skills/shared/external/web-testing/SKILL.md +3 -2
  607. package/pipeline/skills/shared/external/widgetkit/SKILL.md +2 -0
  608. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +1 -1
  609. package/pipeline/skills/shared/external/xcode-build-benchmark/SKILL.md +2 -0
  610. package/pipeline/skills/shared/external/xcode-build-fixer/SKILL.md +2 -0
  611. package/pipeline/skills/shared/external/xcode-build-orchestrator/SKILL.md +2 -0
  612. package/pipeline/skills/shared/external/xcode-compilation-analyzer/SKILL.md +2 -0
  613. package/pipeline/skills/shared/external/xcode-project-analyzer/SKILL.md +2 -0
  614. package/pipeline/skills/skills-index.md +40 -41
  615. package/docs/token-budget-history.md +0 -24
  616. package/pipeline/skills/shared/external/agentflow/SKILL.md +0 -199
  617. package/pipeline/skills/shared/external/android-ui-verification/SKILL.md +0 -66
  618. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +0 -299
  619. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +0 -255
  620. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +0 -167
  621. package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +0 -116
  622. package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -216
@@ -40,27 +40,32 @@ npx @mmerterden/multi-agent-pipeline install --unattended --dry-run # see it f
40
40
  npx @mmerterden/multi-agent-pipeline install --unattended # write it
41
41
  ```
42
42
 
43
- It prints the whole profile with a reason per line before writing anything, and
44
- it is additive: an entry you added by hand survives, a narrower `Bash(...)` rule
45
- is kept alongside, unrelated settings are untouched, and a second run changes
46
- nothing. A `settings.json` that does not parse is refused rather than
47
- overwritten.
48
-
49
- By hand, the same thing is:
50
-
51
- ```jsonc
52
- // ~/.claude/settings.json
53
- {
54
- "permissions": {
55
- "allow": ["Bash", "Edit", "Write", "Agent"]
56
- }
57
- }
58
- ```
59
-
60
- Grant the narrowest set the work needs. A server that only reviews does not
61
- need `Write` - though note that the shipped profile is deliberately broad,
62
- because a run builds and tests whatever the target repo uses and a narrow list
63
- is the one the first unfamiliar repo stops at.
43
+ It prints the whole profile with a reason per line before writing anything,
44
+ and writes it to `~/.claude/multi-agent-unattended.settings.json`, which the
45
+ autopilot runner passes to each run with `claude --settings`.
46
+ `~/.claude/settings.json`, the posture of every attended session, is not changed.
47
+ The profile (`pipeline/schemas/unattended-profile.json`) is
48
+ `permissions.defaultMode: "dontAsk"`, a narrow allow list (`Bash(git *)`,
49
+ `Bash(xcodebuild *)`, `Bash(xcrun simctl *)`, `Edit`, `Write` and the rest,
50
+ each with its reason), deny
51
+ rules for merging, pushing and reading `~/.ssh`, `~/.aws`, `~/.gnupg` and
52
+ `~/Library/Keychains`, and the toolkit's strict URL policy. `--unattended-sandbox`
53
+ adds an OS sandbox block (`sandbox.enabled`, `failIfUnavailable`,
54
+ `allowUnsandboxedCommands: false`, a network allowlist, `denyRead` for `~/.ssh`
55
+ and `~/.aws`).
56
+
57
+ It is additive over the profile file: an entry already there survives, a
58
+ narrower `Bash(...)` rule is kept alongside without standing in for the
59
+ profile's own entry, and a second run changes nothing. Permission lists merge
60
+ across settings files, so a whole-`Bash` allow in `settings.json` still reaches
61
+ an unattended run; the installer reports it and doctor warns, and neither
62
+ removes it. If an earlier install wrote the profile into `settings.json`, the
63
+ installer prints the move and removes exactly those entries from it. A profile
64
+ file that does not parse is refused rather than overwritten.
65
+
66
+ The permission list is the second line. The guard hook under
67
+ `MULTI_AGENT_UNATTENDED=1` and a separate macOS user are the first and the
68
+ boundary: `pipeline/multi-agent-refs/features/unattended-security.md`.
64
69
 
65
70
  `doctor --profile=server` reports this as `unattended-permissions`, against the
66
71
  same rule the writer applies.
@@ -142,8 +147,10 @@ empty and the ledger is a list of identical failures with no indication which
142
147
  came first. After `MA_AP_BREAKER_LIMIT` consecutive attempts that produced
143
148
  nothing (3 by default, 0 disables), the runner stops taking new work, writes the
144
149
  reason into `queue.json` so `autopilot-status` shows it, and says what to check.
145
- One successful attempt clears it. A run waiting for a person (`needs-input`) and
146
- an item arming refused (`blocked-*`) are deliberately not failures.
150
+ One successful attempt clears it. A run waiting for a person (`needs-input`,
151
+ `awaiting-answer`), an item arming refused (`blocked-*`), a run the runner lost
152
+ across a restart (`died`) and a rate limit (which has its own backoff) are
153
+ deliberately not failures.
147
154
 
148
155
  **Telemetry.** `ticks.jsonl` gets one JSON line per tick. The human log answers
149
156
  "what happened just now"; a server is only ever asked "how has this been
@@ -183,6 +190,6 @@ single-owner machine, not on a shared one.
183
190
  ## What this page does NOT cover
184
191
 
185
192
  - Turning any of it on. Every step here is the operator's to take.
186
- - Linux, Windows, systemd, Docker. Out of scope and staying there.
193
+ - Any host other than macOS, and containers. Out of scope and staying there (ADR-0012).
187
194
  - Multi-user servers. Everything above assumes one account owns the install,
188
195
  the keychain and the queue.
package/index.js CHANGED
@@ -92,7 +92,7 @@ if (command === "--version" || command === "-v" || command === "version") {
92
92
  npx @mmerterden/multi-agent-pipeline install --index-only Ship only the skills index (dynamic skill loading)
93
93
  npx @mmerterden/multi-agent-pipeline install --prune-external Also remove pre-v15 catalog copies that differ from the shipped catalog
94
94
 
95
- Uninstall (token-preserving - Keychain/Credential Manager untouched):
95
+ Uninstall (token-preserving - Keychain untouched):
96
96
  npx @mmerterden/multi-agent-pipeline uninstall Interactive: remove from all installed targets
97
97
  npx @mmerterden/multi-agent-pipeline uninstall --yes Skip prompt
98
98
  npx @mmerterden/multi-agent-pipeline uninstall --dry-run Report what would be removed
@@ -184,9 +184,7 @@ export function copyDir(src, dest, opts = {}) {
184
184
  // user-owned trees (rules/) so an update adds new baseline files but preserves
185
185
  // the user's locally-evolved copies. cpSync with force:false + errorOnExist:false
186
186
  // silently skips existing files.
187
- // Normalize to forward slashes so exclude patterns (written posix-style)
188
- // also match on win32, where cpSync hands back backslash paths.
189
- const relOf = (source) => source.slice(src.length).split("\\").join("/").replace(/^\//, "");
187
+ const relOf = (source) => source.slice(src.length).replace(/^\//, "");
190
188
  // Junk is filtered on EVERY path, including the two that take no exclude
191
189
  // list: the point is that the caller never has to remember it.
192
190
  const filter = (source) => {
@@ -571,8 +569,8 @@ export function atomicWrite(path, content) {
571
569
  }
572
570
 
573
571
  /**
574
- * Count files in a directory tree (pure fs recursion - works on win32 where
575
- * `find` is unavailable, and avoids a subprocess per call). A plain-file path
572
+ * Count files in a directory tree (pure fs recursion - avoids a subprocess
573
+ * per call). A plain-file path
576
574
  * counts as 1, matching the old `find <path> -type f` behavior relied on by
577
575
  * the DEV_ONLY_SCRIPTS exclusion accounting.
578
576
  * @param {string} dir
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { readdirSync, writeFileSync } from "fs";
13
13
  import { join } from "path";
14
- import { isDryRun } from "./_common.mjs";
14
+ import { copyFile, isDryRun } from "./_common.mjs";
15
15
  import { routeSkill } from "../pipeline/scripts/_stack-routing.mjs";
16
16
 
17
17
  /** Uninstall reads this to know exactly which external skill dirs are ours to remove. */
@@ -71,3 +71,25 @@ export function writeExternalSkillsManifest(dest, names) {
71
71
  /* best-effort - a missing manifest just means uninstall falls back */
72
72
  }
73
73
  }
74
+
75
+ /**
76
+ * Copy every NOTICE-*.md from the external catalog into `dest`, beside the
77
+ * skill dirs, unchanged. Each one attributes a set of vendored skills to its
78
+ * upstream, so a host that receives the catalog locally carries the notices
79
+ * with it. All of them are copied whatever the stack filter kept: a notice
80
+ * costs nothing on disk and never enters a skills block. Uninstall removes
81
+ * them together with the manifest-listed dirs.
82
+ *
83
+ * @param {string} externalSrc - absolute path to `pipeline/skills/shared/external/`
84
+ * @param {string} dest - the installed skills directory
85
+ * @returns {number} files copied
86
+ */
87
+ export function copyExternalNotices(externalSrc, dest) {
88
+ let n = 0;
89
+ for (const e of readdirSync(externalSrc, { withFileTypes: true })) {
90
+ if (!e.isFile() || !/^NOTICE-.*\.md$/.test(e.name)) continue;
91
+ copyFile(join(externalSrc, e.name), join(dest, e.name));
92
+ n++;
93
+ }
94
+ return n;
95
+ }
@@ -1,113 +1,359 @@
1
1
  /**
2
- * _unattended-profile.mjs - the permission posture a machine with nobody at
3
- * the keyboard needs, written only when asked for.
2
+ * _unattended-profile.mjs - the posture a machine with nobody at the keyboard
3
+ * needs, written only when asked for.
4
4
  *
5
5
  * autopilot spawns its child with `--permission-prompts none`. That stops Claude
6
6
  * Code from ASKING; it grants nothing. The tools the child then calls still have
7
7
  * to be allowed somewhere, and on a fresh machine they are not - so the run
8
8
  * stops at the first tool call, silently, with no prompt anywhere for a person
9
- * to answer. It looks exactly like a queue that has nothing to do.
9
+ * to answer.
10
10
  *
11
- * This is opt-in and always will be. A default install writes no permissions at
12
- * all, because widening a permission set is not a thing an installer should do
13
- * to somebody who only wanted the pipeline files copied. `--unattended` prints
14
- * the full list and the reason for each line BEFORE writing anything, because
15
- * "the installer changed my permissions" should never be something a person
16
- * discovers afterwards.
11
+ * The profile is `permissions.defaultMode: "dontAsk"` (a call that would prompt
12
+ * is denied, not left waiting; any other mode already in the file is replaced),
13
+ * a narrow allow list, deny rules for what no unattended run does, the
14
+ * toolkit's strict URL policy and index deny list, and an OS sandbox block. The
15
+ * entries and their reasons live in pipeline/schemas/unattended-profile.json;
16
+ * lib/unattended-profile-check.mjs judges a file against it for this
17
+ * installer, for doctor and for the autopilot runner, which will not launch a
18
+ * child unless the file still holds the posture.
17
19
  *
18
- * The entries are broad on purpose, and that breadth is the cost of unattended
19
- * operation rather than an oversight. A pipeline run builds, tests, edits, and
20
- * shells out to whatever the repo's own toolchain is; an allowlist narrow
21
- * enough to be interesting would be one the first unfamiliar repo breaks. The
22
- * honest framing is: this is a machine you have decided to let run on its own.
20
+ * The sandbox is the boundary: Seatbelt on macOS, enforced for every Bash
21
+ * command and every process it starts, with writes limited to the working
22
+ * directory, the session temp directory and the unattended run directory, and
23
+ * egress limited to the listed hosts. The file tools and MCP tools are outside
24
+ * it; the Edit and Read deny rules cover the file tools, and agent-guard.sh
25
+ * under MULTI_AGENT_UNATTENDED=1 is a second layer over all of them
26
+ * (multi-agent-refs/features/unattended-security.md).
27
+ *
28
+ * It is written to its own file, ~/.claude/multi-agent-unattended.settings.json
29
+ * (pipeline/lib/unattended-settings-location.mjs), which the autopilot runner
30
+ * hands to each child with `claude --settings`. ~/.claude/settings.json is the
31
+ * attended posture and is not touched, with one exception: when it carries the
32
+ * profile the v20.2.1 install wrote there (install/unattended-profile-legacy.json),
33
+ * exactly those entries are removed from it.
34
+ *
35
+ * Opt-in, printed in full with a reason per line BEFORE anything is written.
36
+ * Allow and deny rules are additive; a sandbox block already in the file is
37
+ * corrected to the shipped one, keeping its extra deny entries and dropping
38
+ * whatever widens it.
23
39
  *
24
40
  * @module install/_unattended-profile
25
41
  */
26
42
 
27
43
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
28
- import { join } from "node:path";
44
+ import { dirname, join } from "node:path";
45
+ import { fileURLToPath } from "node:url";
46
+
47
+ import { unattendedSettingsPath } from "../pipeline/lib/unattended-settings-location.mjs";
48
+ import { prefsHosts, profileGaps } from "../pipeline/lib/unattended-profile-check.mjs";
49
+
50
+ const PROFILE_PATH = join(
51
+ dirname(dirname(fileURLToPath(import.meta.url))),
52
+ "pipeline",
53
+ "schemas",
54
+ "unattended-profile.json",
55
+ );
56
+
57
+ /** The profile data, as shipped. */
58
+ export function loadUnattendedProfile(path = PROFILE_PATH) {
59
+ return JSON.parse(readFileSync(path, "utf-8"));
60
+ }
61
+
62
+ const PROFILE = loadUnattendedProfile();
63
+ const LEGACY = JSON.parse(
64
+ readFileSync(
65
+ join(dirname(fileURLToPath(import.meta.url)), "unattended-profile-legacy.json"),
66
+ "utf-8",
67
+ ),
68
+ );
69
+
70
+ /** Allow rules and their reasons: [rule, why][]. */
71
+ export const UNATTENDED_ALLOW = PROFILE.allow;
72
+ /** Deny rules and their reasons: [rule, why][]. */
73
+ export const UNATTENDED_DENY = PROFILE.deny;
74
+ /** Env entries: [key, value, why][]. */
75
+ export const UNATTENDED_ENV = PROFILE.env;
76
+
77
+ const hasRule = (list, rule) => list.some((a) => String(a) === rule);
78
+
79
+ function readSettingsFile(path) {
80
+ if (!existsSync(path)) return {};
81
+ try {
82
+ return JSON.parse(readFileSync(path, "utf-8")) || {};
83
+ } catch (err) {
84
+ // A settings file that does not parse is not ours to rewrite: overwriting
85
+ // it would lose whatever is in there.
86
+ throw new Error(`${path} is not valid JSON, so it will not be modified: ${err.message}`, {
87
+ cause: err,
88
+ });
89
+ }
90
+ }
91
+
92
+ /**
93
+ * The profile entries an earlier install wrote into settings.json, when it
94
+ * carries that install's signature: the profile's defaultMode and its whole
95
+ * deny set. Returns the labels to remove, [] when the signature is absent.
96
+ */
97
+ function legacyEntries(settings) {
98
+ const perms = settings.permissions || {};
99
+ const allow = Array.isArray(perms.allow) ? perms.allow.map(String) : [];
100
+ const deny = Array.isArray(perms.deny) ? perms.deny.map(String) : [];
101
+ const signed =
102
+ perms.defaultMode === LEGACY.defaultMode && LEGACY.deny.every((rule) => deny.includes(rule));
103
+ if (!signed) return [];
104
+ const env = settings.env || {};
105
+ return [
106
+ `defaultMode:${LEGACY.defaultMode}`,
107
+ ...LEGACY.allow.filter((r) => allow.includes(r)),
108
+ ...LEGACY.deny.map((r) => `deny:${r}`),
109
+ ...LEGACY.env.filter(([k, v]) => env[k] === v).map(([k, v]) => `env:${k}=${v}`),
110
+ ];
111
+ }
112
+
113
+ const union = (a, b) => [...new Set([...(Array.isArray(a) ? a.map(String) : []), ...b])];
114
+
115
+ /**
116
+ * The sandbox block to write: the shipped one, with the extra deny entries
117
+ * and other keys of a block already in the file kept, and everything that
118
+ * would widen it (an excluded command, an extra write path or domain, a
119
+ * re-opened read, a lifted protection) dropped.
120
+ *
121
+ * @param {object|undefined} existing
122
+ * @param {string[]} extraHosts
123
+ */
124
+ function sandboxBlock(existing, extraHosts) {
125
+ const { $comment: _omit, ...want } = PROFILE.sandbox;
126
+ const cur = existing && typeof existing === "object" ? existing : {};
127
+ const {
128
+ excludedCommands: _ex,
129
+ allowAppleEvents: _ae,
130
+ enableWeakerNestedSandbox: _wn,
131
+ ...keep
132
+ } = cur;
133
+ const curFs = cur.filesystem && typeof cur.filesystem === "object" ? cur.filesystem : {};
134
+ const { disabled: _d, allowRead: _ar, ...keepFs } = curFs;
135
+ const curNet = cur.network && typeof cur.network === "object" ? cur.network : {};
136
+ const { allowAllUnixSockets: _us, ...keepNet } = curNet;
137
+ return {
138
+ ...keep,
139
+ ...Object.fromEntries(Object.entries(want).filter(([, v]) => typeof v !== "object")),
140
+ filesystem: {
141
+ ...keepFs,
142
+ allowWrite: [...want.filesystem.allowWrite],
143
+ denyWrite: union(curFs.denyWrite, want.filesystem.denyWrite),
144
+ denyRead: union(curFs.denyRead, want.filesystem.denyRead),
145
+ },
146
+ network: {
147
+ ...keepNet,
148
+ ...Object.fromEntries(Object.entries(want.network).filter(([, v]) => !Array.isArray(v))),
149
+ allowMachLookup: [...want.network.allowMachLookup],
150
+ allowedDomains: [...new Set([...want.network.allowedDomains, ...extraHosts])],
151
+ },
152
+ };
153
+ }
29
154
 
30
155
  /**
31
- * Tool name plus why an unattended run cannot proceed without it. The reason is
32
- * printed, not just stored: a list of fifteen tool names with no reasons is a
33
- * list nobody audits.
156
+ * What applying the profile would do. describe and apply both read this, so
157
+ * the printed plan and the write cannot disagree.
158
+ *
159
+ * @param {object} current the profile file's parsed content, or {}
160
+ * @param {{home?: string, settings?: object, extraHosts?: string[]}} [opts]
161
+ * settings: ~/.claude/settings.json, parsed, for the whole-Bash warning and
162
+ * the migration; extraHosts: the prefs hosts, read from home when omitted
34
163
  */
35
- export const UNATTENDED_ALLOW = [
36
- ["Bash", "every build, test, git and gh call the pipeline makes"],
37
- ["Edit", "Phase 3 cannot change a file without it"],
38
- ["Write", "new files, and every artefact a phase leaves behind"],
39
- ["Read", "reading the repo it was pointed at"],
40
- ["Glob", "finding files by pattern instead of shelling out to find"],
41
- ["Grep", "the same, for content"],
42
- ["Agent", "Phase 1 and Phase 4 dispatch subagents"],
43
- ["Skill", "the phase specs are skills"],
44
- ["WebFetch", "issue and ticket bodies referenced by URL"],
45
- ["TaskCreate", "the phase tracker's task list"],
46
- ["TaskUpdate", "the same"],
47
- ["TaskList", "the same"],
48
- ["TaskGet", "the same"],
49
- ];
164
+ export function planUnattendedProfile(current = {}, { home = "", settings, extraHosts } = {}) {
165
+ const perms = current.permissions || {};
166
+ const allow = Array.isArray(perms.allow) ? perms.allow.map(String) : [];
167
+ const deny = Array.isArray(perms.deny) ? perms.deny.map(String) : [];
168
+ const env = current.env || {};
169
+ const attended =
170
+ settings ?? (home ? readSettingsFile(join(home, ".claude", "settings.json")) : {});
171
+ const attendedAllow = Array.isArray(attended.permissions?.allow)
172
+ ? attended.permissions.allow.map(String)
173
+ : [];
174
+ const hosts = extraHosts ?? (home ? prefsHosts(home) : []);
175
+ const rows = [];
176
+
177
+ const mode = perms.defaultMode;
178
+ const wantMode = PROFILE.defaultMode.value;
179
+ const modeLabel = `defaultMode:${wantMode}`;
180
+ if (mode === wantMode)
181
+ rows.push({ label: modeLabel, why: PROFILE.defaultMode.why, mark: "already" });
182
+ else if (mode === undefined || mode === "default" || mode === "manual")
183
+ rows.push({ label: modeLabel, why: PROFILE.defaultMode.why, mark: "add" });
184
+ else
185
+ rows.push({
186
+ label: modeLabel,
187
+ why: `${PROFILE.defaultMode.why}; replaces ${mode}, which would let an unattended run act without the allow list`,
188
+ mark: "replace",
189
+ });
190
+
191
+ for (const { rule, why } of PROFILE.wideAllows) {
192
+ if (hasRule(attendedAllow, rule)) rows.push({ label: `warn:${rule}`, why, mark: "warn" });
193
+ }
194
+ for (const [rule, why] of PROFILE.allow) {
195
+ rows.push({ label: rule, why, mark: hasRule(allow, rule) ? "already" : "add" });
196
+ }
197
+ for (const [rule, why] of PROFILE.deny) {
198
+ rows.push({ label: `deny:${rule}`, why, mark: hasRule(deny, rule) ? "already" : "add" });
199
+ }
200
+ for (const [key, value, why] of PROFILE.env) {
201
+ let mark = "add";
202
+ if (env[key] === value) mark = "already";
203
+ else if (env[key] !== undefined) mark = "kept";
204
+ rows.push({ label: `env:${key}=${value}`, why, mark });
205
+ }
206
+ const present = current.sandbox && typeof current.sandbox === "object";
207
+ const target = sandboxBlock(current.sandbox, hosts);
208
+ let sandboxMark = "add";
209
+ if (present)
210
+ sandboxMark =
211
+ JSON.stringify(current.sandbox) === JSON.stringify(target) ? "already" : "correct";
212
+ rows.push({
213
+ label: "sandbox",
214
+ why: "OS sandbox for Bash and everything it starts: required, no unsandboxed retry, no excluded commands, writes limited to the worktree and the unattended run directory, protected host files denied, credential stores unreadable, network limited to the listed hosts",
215
+ mark: sandboxMark,
216
+ });
217
+ for (const label of legacyEntries(attended)) {
218
+ rows.push({
219
+ label: `migrate:${label}`,
220
+ why: "written into ~/.claude/settings.json by an earlier install; it moves to the profile file",
221
+ mark: "migrate",
222
+ });
223
+ }
224
+ return { rows, extraHosts: hosts };
225
+ }
226
+
227
+ const MARK_TEXT = {
228
+ add: "will be added",
229
+ already: "already there",
230
+ kept: "kept as is",
231
+ replace: "will be replaced",
232
+ correct: "will be corrected",
233
+ warn: "left in settings.json",
234
+ migrate: "removed from settings.json",
235
+ };
50
236
 
51
237
  /**
52
238
  * Render the profile for a human to read before it is applied.
53
239
  *
54
- * @param {string[]} already entries the settings file already carries
240
+ * @param {object} current the profile file's parsed content, or {}
241
+ * @param {{home?: string, settings?: object}} [opts]
55
242
  * @returns {string}
56
243
  */
57
- export function describeUnattendedProfile(already = []) {
244
+ export function describeUnattendedProfile(current = {}, opts = {}) {
245
+ const { rows } = planUnattendedProfile(current || {}, opts);
246
+ const target = opts.home ? unattendedSettingsPath(opts.home) : unattendedSettingsPath();
58
247
  const lines = [
59
- " Unattended permission profile - these go into ~/.claude/settings.json",
60
- " under permissions.allow. Nothing is removed; existing entries are kept.",
248
+ ` Unattended permission profile - these go into ${target},`,
249
+ " which the autopilot runner passes to each run with `claude --settings`.",
250
+ " ~/.claude/settings.json (the attended posture) is not changed.",
251
+ " An entry is 'already there' only when that exact rule is present.",
61
252
  "",
62
253
  ];
63
- for (const [tool, why] of UNATTENDED_ALLOW) {
64
- const mark = already.includes(tool) ? "already there" : "will be added";
65
- lines.push(` ${tool.padEnd(12)} ${why} (${mark})`);
254
+ for (const r of rows) {
255
+ lines.push(` ${r.label.padEnd(44)} ${r.why} (${MARK_TEXT[r.mark]})`);
66
256
  }
67
257
  lines.push("");
68
- lines.push(" This is broad, and that breadth is what unattended operation costs: a run");
69
- lines.push(" builds and tests whatever the target repo uses, so a narrow list is one the");
70
- lines.push(" first unfamiliar repo stops at, with nobody there to answer.");
258
+ lines.push(
259
+ " The OS sandbox is the boundary for Bash; the deny rules cover the file tools it does",
260
+ );
261
+ lines.push(
262
+ " not reach, and agent-guard is a second layer under MULTI_AGENT_UNATTENDED=1. The runner",
263
+ );
264
+ lines.push(
265
+ " refuses to launch unless this file still holds the posture. See features/unattended-security.md.",
266
+ );
71
267
  return lines.join("\n");
72
268
  }
73
269
 
270
+ function removeLegacy(settings, labels) {
271
+ const perms = { ...(settings.permissions || {}) };
272
+ const env = { ...(settings.env || {}) };
273
+ for (const label of labels) {
274
+ if (label.startsWith("defaultMode:")) delete perms.defaultMode;
275
+ else if (label.startsWith("deny:")) {
276
+ const rule = label.slice("deny:".length);
277
+ perms.deny = (perms.deny || []).filter((r) => String(r) !== rule);
278
+ } else if (label.startsWith("env:")) delete env[label.slice("env:".length).split("=")[0]];
279
+ else perms.allow = (perms.allow || []).filter((r) => String(r) !== label);
280
+ }
281
+ for (const k of ["allow", "deny"])
282
+ if (Array.isArray(perms[k]) && !perms[k].length) delete perms[k];
283
+ const out = { ...settings, permissions: perms, env };
284
+ if (!Object.keys(perms).length) delete out.permissions;
285
+ if (!Object.keys(env).length) delete out.env;
286
+ return out;
287
+ }
288
+
74
289
  /**
75
- * Merge the profile into settings.json. Additive only, and idempotent: running
76
- * it twice changes nothing the second time, and an entry a person added by hand
77
- * survives.
290
+ * The profile file's next content: `current` with every "add", "replace" and
291
+ * "correct" row of the plan applied. Pure; applyUnattendedProfile writes it.
292
+ *
293
+ * @param {object} current the profile file's parsed content, or {}
294
+ * @param {{home?: string, settings?: object, extraHosts?: string[]}} [opts]
295
+ * @returns {object}
296
+ */
297
+ export function buildUnattendedProfile(current = {}, opts = {}) {
298
+ const { rows, extraHosts } = planUnattendedProfile(current, { settings: {}, ...opts });
299
+ const perms = { ...(current.permissions || {}) };
300
+ const allow = Array.isArray(perms.allow) ? [...perms.allow] : [];
301
+ const deny = Array.isArray(perms.deny) ? [...perms.deny] : [];
302
+ const next = { ...current };
303
+ for (const { label, mark } of rows) {
304
+ if (!["add", "replace", "correct"].includes(mark)) continue;
305
+ if (label.startsWith("defaultMode:")) perms.defaultMode = label.slice("defaultMode:".length);
306
+ else if (label.startsWith("deny:")) deny.push(label.slice("deny:".length));
307
+ else if (label.startsWith("env:")) {
308
+ const [key, ...rest] = label.slice("env:".length).split("=");
309
+ next.env = { ...(next.env || {}), [key]: rest.join("=") };
310
+ } else if (label === "sandbox") next.sandbox = sandboxBlock(current.sandbox, extraHosts);
311
+ else allow.push(label);
312
+ }
313
+ perms.allow = allow;
314
+ if (deny.length) perms.deny = deny;
315
+ next.permissions = perms;
316
+ return next;
317
+ }
318
+
319
+ /**
320
+ * Write the profile into its own settings file. Idempotent: running it twice
321
+ * changes nothing the second time.
78
322
  *
79
323
  * @param {{home:string, dryRun?:boolean}} opts
80
- * @returns {{path:string, added:string[], already:string[], written:boolean}}
324
+ * @returns {{path:string, added:string[], warned:string[], migrated:string[], removed:string[], already:string[], kept:string[], written:boolean}}
81
325
  */
82
326
  export function applyUnattendedProfile({ home, dryRun = false }) {
83
- const path = join(home, ".claude", "settings.json");
84
- let settings = {};
85
- if (existsSync(path)) {
86
- try {
87
- settings = JSON.parse(readFileSync(path, "utf-8"));
88
- } catch (err) {
89
- // A settings file that does not parse is the user's, not ours to
90
- // rewrite: overwriting it would lose whatever is in there.
91
- throw new Error(`${path} is not valid JSON, so it will not be modified: ${err.message}`, {
92
- cause: err,
93
- });
94
- }
327
+ const path = unattendedSettingsPath(home);
328
+ const settingsPath = join(home, ".claude", "settings.json");
329
+ const current = readSettingsFile(path);
330
+ const settings = readSettingsFile(settingsPath);
331
+ const { rows, extraHosts } = planUnattendedProfile(current, { home, settings });
332
+ const pick = (...marks) => rows.filter((r) => marks.includes(r.mark)).map((r) => r.label);
333
+ const added = pick("add", "replace", "correct");
334
+ const migrated = pick("migrate").map((l) => l.slice("migrate:".length));
335
+ const result = {
336
+ path,
337
+ added,
338
+ warned: pick("warn").map((l) => l.slice("warn:".length)),
339
+ migrated,
340
+ removed: [],
341
+ already: pick("already"),
342
+ kept: pick("kept"),
343
+ };
344
+ if (dryRun || (!added.length && !migrated.length)) return { ...result, written: false };
345
+
346
+ if (added.length) {
347
+ const next = buildUnattendedProfile(current, { home, settings, extraHosts });
348
+ const gaps = profileGaps(next, PROFILE, { extraHosts });
349
+ if (gaps.sandbox.length || gaps.permissions.length)
350
+ throw new Error(
351
+ `the profile would still fall short: ${[...gaps.sandbox, ...gaps.permissions].join("; ")}`,
352
+ );
353
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
95
354
  }
96
- const permissions = settings.permissions || {};
97
- const allow = Array.isArray(permissions.allow) ? [...permissions.allow] : [];
98
- const already = [];
99
- const added = [];
100
- for (const [tool] of UNATTENDED_ALLOW) {
101
- if (allow.some((a) => String(a) === tool || String(a).startsWith(`${tool}(`)))
102
- already.push(tool);
103
- else {
104
- allow.push(tool);
105
- added.push(tool);
106
- }
355
+ if (migrated.length) {
356
+ writeFileSync(settingsPath, `${JSON.stringify(removeLegacy(settings, migrated), null, 2)}\n`);
107
357
  }
108
- if (dryRun || !added.length) return { path, added, already, written: false };
109
-
110
- settings.permissions = { ...permissions, allow };
111
- writeFileSync(path, `${JSON.stringify(settings, null, 2)}\n`);
112
- return { path, added, already, written: true };
358
+ return { ...result, written: true };
113
359
  }