@mmerterden/multi-agent-pipeline 20.33.0 → 20.35.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 (390) hide show
  1. package/CHANGELOG.md +98 -1
  2. package/README.md +3 -1
  3. package/README.tr.md +2 -1
  4. package/docs/adr/0018-cross-repo-links-not-a-global-graph.md +43 -0
  5. package/docs/adr/README.md +2 -1
  6. package/docs/facts.json +5 -4
  7. package/docs/features.md +1 -1
  8. package/install/_dev-only-files.mjs +1 -0
  9. package/install/catalog-history.json +1 -1
  10. package/install/claude.mjs +24 -2
  11. package/install/templates/claude-hooks.json +34 -1
  12. package/manifest.json +393 -339
  13. package/package.json +3 -1
  14. package/pipeline/agents/bulk-reader.md +3 -0
  15. package/pipeline/agents/code-reviewer.md +9 -0
  16. package/pipeline/agents/explorer.md +4 -0
  17. package/pipeline/agents/plan-critic.md +4 -2
  18. package/pipeline/commands/multi-agent/SKILL.md +17 -136
  19. package/pipeline/commands/multi-agent/channels/SKILL.md +16 -298
  20. package/pipeline/commands/multi-agent/graph/SKILL.md +5 -2
  21. package/pipeline/commands/multi-agent/language/SKILL.md +3 -3
  22. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  23. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  24. package/pipeline/commands/multi-agent/resume/SKILL.md +8 -22
  25. package/pipeline/commands/multi-agent/review/SKILL.md +7 -131
  26. package/pipeline/commands/multi-agent/setup/SKILL.md +19 -224
  27. package/pipeline/contract/CHANGELOG.md +9 -0
  28. package/pipeline/contract/README.md +6 -2
  29. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  30. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  31. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  32. package/pipeline/contract/manifest.json +14 -1
  33. package/pipeline/contract/types/index.d.ts +24 -2
  34. package/pipeline/lib/json-file-lock.mjs +61 -15
  35. package/pipeline/lib/office-remote.mjs +228 -0
  36. package/pipeline/lib/qr-code.mjs +360 -0
  37. package/pipeline/lib/repo-hygiene.sh +1 -0
  38. package/pipeline/lib/run-completion.mjs +17 -0
  39. package/pipeline/multi-agent-refs/channels/generator.md +146 -0
  40. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  41. package/pipeline/multi-agent-refs/channels/menu.md +55 -0
  42. package/pipeline/multi-agent-refs/channels/multi-repo.md +6 -0
  43. package/pipeline/multi-agent-refs/channels/preview.md +25 -0
  44. package/pipeline/multi-agent-refs/channels/reference.md +98 -0
  45. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +1 -1
  46. package/pipeline/multi-agent-refs/features/code-graph.md +27 -0
  47. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  48. package/pipeline/multi-agent-refs/features/office-threat-model.md +2 -2
  49. package/pipeline/multi-agent-refs/features/plan-critic.md +5 -2
  50. package/pipeline/multi-agent-refs/features/review-delta.md +4 -1
  51. package/pipeline/multi-agent-refs/features/scope-check.md +25 -1
  52. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  53. package/pipeline/multi-agent-refs/keychain.md +7 -0
  54. package/pipeline/multi-agent-refs/knowledge.md +9 -0
  55. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +163 -0
  56. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  57. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +8 -10
  58. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +18 -20
  59. package/pipeline/multi-agent-refs/phases/phase-3-review.md +20 -14
  60. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +8 -7
  61. package/pipeline/multi-agent-refs/phases/phase-5-report.md +6 -3
  62. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  63. package/pipeline/multi-agent-refs/resume/details.md +35 -0
  64. package/pipeline/multi-agent-refs/review/inputs.md +87 -0
  65. package/pipeline/multi-agent-refs/review/module-guides.md +25 -0
  66. package/pipeline/multi-agent-refs/review/valven.md +42 -0
  67. package/pipeline/multi-agent-refs/rules.md +3 -2
  68. package/pipeline/multi-agent-refs/setup/accounts.md +27 -0
  69. package/pipeline/multi-agent-refs/setup/app-store-connect.md +5 -0
  70. package/pipeline/multi-agent-refs/setup/firebase.md +5 -0
  71. package/pipeline/multi-agent-refs/setup/google-play.md +4 -0
  72. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +21 -0
  73. package/pipeline/multi-agent-refs/setup/language.md +46 -0
  74. package/pipeline/multi-agent-refs/setup/multi-repo-group.md +51 -0
  75. package/pipeline/multi-agent-refs/setup/summaries.md +63 -0
  76. package/pipeline/multi-agent-refs/setup/token-save-flow.md +51 -0
  77. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  78. package/pipeline/office/NOTICE.md +4 -1
  79. package/pipeline/office/assets/char_12.png +0 -0
  80. package/pipeline/office/assets/char_13.png +0 -0
  81. package/pipeline/office/assets/char_14.png +0 -0
  82. package/pipeline/office/assets/char_15.png +0 -0
  83. package/pipeline/office/assets/char_16.png +0 -0
  84. package/pipeline/office/assets/char_17.png +0 -0
  85. package/pipeline/office/assets/char_18.png +0 -0
  86. package/pipeline/office/assets/char_19.png +0 -0
  87. package/pipeline/office/assets/char_20.png +0 -0
  88. package/pipeline/office/assets/char_21.png +0 -0
  89. package/pipeline/office/assets/char_22.png +0 -0
  90. package/pipeline/office/assets/char_23.png +0 -0
  91. package/pipeline/office/assets/char_24.png +0 -0
  92. package/pipeline/office/assets/char_25.png +0 -0
  93. package/pipeline/office/assets/char_26.png +0 -0
  94. package/pipeline/office/assets/char_27.png +0 -0
  95. package/pipeline/office/assets/char_28.png +0 -0
  96. package/pipeline/office/assets/char_29.png +0 -0
  97. package/pipeline/office/assets/char_30.png +0 -0
  98. package/pipeline/office/index.html +304 -55
  99. package/pipeline/office/toolkit.json +1 -1
  100. package/pipeline/rules/response-shape.md +6 -7
  101. package/pipeline/schemas/agent-state.schema.json +36 -0
  102. package/pipeline/schemas/phases.json +4 -4
  103. package/pipeline/schemas/plan-critique.schema.json +9 -2
  104. package/pipeline/schemas/planning-output.schema.json +43 -2
  105. package/pipeline/schemas/pr-reach.schema.json +55 -0
  106. package/pipeline/schemas/prefs.schema.json +37 -8
  107. package/pipeline/schemas/reviewer-output.schema.json +20 -2
  108. package/pipeline/schemas/token-budget.json +4 -4
  109. package/pipeline/schemas/triage-output.schema.json +32 -3
  110. package/pipeline/schemas/unattended-policy.json +2 -1
  111. package/pipeline/scripts/_graph-analysis.mjs +270 -0
  112. package/pipeline/scripts/_guard_rules.py +449 -1
  113. package/pipeline/scripts/_memory-policy.mjs +165 -0
  114. package/pipeline/scripts/_secret-scan.mjs +105 -0
  115. package/pipeline/scripts/_test-gaming.mjs +36 -0
  116. package/pipeline/scripts/_unified-diff.mjs +2 -2
  117. package/pipeline/scripts/agent-guard.py +79 -19
  118. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  119. package/pipeline/scripts/answer-check.mjs +113 -0
  120. package/pipeline/scripts/answer-check.sh +20 -0
  121. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  122. package/pipeline/scripts/capture-flush.sh +12 -2
  123. package/pipeline/scripts/check-read-size.sh +9 -0
  124. package/pipeline/scripts/code-graph-rules/android.json +8 -3
  125. package/pipeline/scripts/contract-server.mjs +71 -16
  126. package/pipeline/scripts/debt-ledger.mjs +323 -0
  127. package/pipeline/scripts/file-context.mjs +261 -0
  128. package/pipeline/scripts/file-context.sh +20 -0
  129. package/pipeline/scripts/gate-ledger.mjs +144 -21
  130. package/pipeline/scripts/graph-links.mjs +230 -0
  131. package/pipeline/scripts/graph-query.mjs +133 -1
  132. package/pipeline/scripts/graph-report.mjs +65 -0
  133. package/pipeline/scripts/learn-from-transcripts.mjs +12 -48
  134. package/pipeline/scripts/learnings-ledger.mjs +143 -24
  135. package/pipeline/scripts/office-build.mjs +276 -5
  136. package/pipeline/scripts/office-control.mjs +70 -10
  137. package/pipeline/scripts/office-pair.mjs +407 -0
  138. package/pipeline/scripts/office-remote-client.mjs +463 -0
  139. package/pipeline/scripts/phase-tracker.sh +8 -0
  140. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  141. package/pipeline/scripts/phone-devices.mjs +9 -1
  142. package/pipeline/scripts/plan-critique-gate.mjs +11 -2
  143. package/pipeline/scripts/pre-commit-check.sh +28 -2
  144. package/pipeline/scripts/pre-push-check.sh +6 -12
  145. package/pipeline/scripts/rework-base.mjs +87 -0
  146. package/pipeline/scripts/rework-rulings.mjs +106 -0
  147. package/pipeline/scripts/run-commit-check.mjs +104 -0
  148. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  149. package/pipeline/scripts/run-scope-guard.sh +13 -1
  150. package/pipeline/scripts/scope-check-gate.mjs +213 -2
  151. package/pipeline/scripts/task-mirror.mjs +106 -15
  152. package/pipeline/scripts/task-mirror.sh +3 -2
  153. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  154. package/pipeline/scripts/triage-memory.mjs +64 -37
  155. package/pipeline/scripts/update-check.sh +7 -1
  156. package/pipeline/scripts/update-issue-progress.sh +73 -8
  157. package/pipeline/scripts/validate-planning.mjs +172 -4
  158. package/pipeline/scripts/validate-reviewer.mjs +20 -0
  159. package/pipeline/scripts/validate-triage.mjs +149 -4
  160. package/pipeline/scripts/work-items.mjs +119 -1
  161. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  162. package/pipeline/scripts/worktree-prepare.sh +40 -4
  163. package/pipeline/scripts/write-state.mjs +67 -24
  164. package/pipeline/skills/.skill-manifest.json +9 -9
  165. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +5 -5
  166. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  167. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  168. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +5 -2
  169. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +4 -4
  170. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  171. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -0
  172. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +4 -4
  173. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  174. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  175. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  176. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  177. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  178. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  179. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  180. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  181. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  182. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  183. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  184. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  185. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  186. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  187. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  188. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  189. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  190. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  191. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  192. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  193. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  194. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  195. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  196. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  197. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  198. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  199. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  200. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  201. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  202. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  203. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  204. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  205. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  206. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  207. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  208. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  209. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  210. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  211. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  212. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  213. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  214. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  215. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  216. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  217. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  218. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  219. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  220. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  221. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  222. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  223. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  224. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  225. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  226. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  227. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  228. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  229. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  230. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  231. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  232. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  233. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  234. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  235. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  236. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  237. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  238. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  239. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  240. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  241. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  242. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  243. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  244. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  245. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  246. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  247. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  248. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  249. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  250. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  251. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  252. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  253. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  254. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  255. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  256. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  257. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  258. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  259. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  260. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  261. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  262. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  263. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  264. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  265. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  266. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  267. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  268. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  269. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  270. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  271. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  272. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  273. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  274. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  275. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  276. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  277. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  278. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  279. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  280. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  281. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  282. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  283. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  284. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  285. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  286. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  287. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  288. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  289. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  290. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  291. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  292. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  293. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  294. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  295. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  296. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  297. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  298. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  299. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  300. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  301. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  302. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  303. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  304. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  305. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  306. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  307. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  308. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  309. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  310. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  311. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  312. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  313. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  314. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  315. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  316. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  317. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  318. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  319. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  320. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  321. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  322. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  323. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  324. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  325. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  326. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  327. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  328. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  329. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  330. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  331. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  332. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  333. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  334. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  335. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  336. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  337. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  338. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  339. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  340. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  341. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  342. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  343. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  344. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  345. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  346. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  347. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  348. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  349. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  350. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  351. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  352. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  353. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  354. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  355. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  356. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  357. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  358. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  359. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  360. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  361. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  362. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  363. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  364. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  365. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  366. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  367. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  368. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  369. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  370. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  371. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  372. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  373. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  374. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  375. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  376. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  377. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  378. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  379. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  380. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  381. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  382. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  383. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  384. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  385. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  386. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  387. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  388. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  389. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  390. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
@@ -32,6 +32,8 @@ import re
32
32
  import shlex
33
33
  import time
34
34
 
35
+ RUN_STATE = "agent-state.json"
36
+
35
37
  MAX_FILE_BYTES = 256 * 1024
36
38
  CONFIRM_TTL_SECONDS = 600
37
39
 
@@ -47,6 +49,16 @@ AI_GENERATED = re.compile(
47
49
  )
48
50
  JIRA_TRANSITION = re.compile(r"/rest/api/(?:\d+|latest)/issue/[^/\s'\"]+/transitions\b", re.IGNORECASE)
49
51
  POST_METHOD = re.compile(r"(?:^|\s)(?:-X|--request)\s*=?\s*['\"]?post\b", re.IGNORECASE)
52
+ WRITE_METHOD = re.compile(r"(?:^|\s)(?:-X|--request)\s*=?\s*['\"]?(?:post|put|patch)\b", re.IGNORECASE)
53
+ PUT_METHOD = re.compile(r"(?:^|\s)(?:-X|--request)\s*=?\s*['\"]?put\b", re.IGNORECASE)
54
+ SHARED_WRITE = re.compile(
55
+ r"/rest/api/(?:\d+|latest)/issue/[^/\s'\"]+/comment\b|/rest/api/content\b|hooks\.slack\.com/services/",
56
+ re.IGNORECASE,
57
+ )
58
+ PR_UPDATE = re.compile(
59
+ r"/rest/api/(?:[\d.]+|latest)/projects/[^/\s'\"]+/repos/[^/\s'\"]+/pull-requests/\d+(?=[\s'\"?]|$)"
60
+ )
61
+ DATA_FLAGS = {"-d", "--data", "--data-binary", "--data-raw", "--data-ascii"}
50
62
  BODY_FLAG = re.compile(r"(?:^|\s)(?:-d|--data[\w-]*|--json|-F|--form)(?:\s|=|$)")
51
63
  HEREDOC_OPEN = re.compile(r"<<(-?)\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\2")
52
64
  ASSIGN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
@@ -446,6 +458,7 @@ def _gh_ask(argv, cwd):
446
458
  if group == "pr" and sub in ("create", "edit"):
447
459
  bodies = _flag_values(cleaned, {"--body"}, "-b")
448
460
  bodies += [read_bounded(p, cwd) for p in _flag_values(cleaned, {"--body-file"}, "-F")]
461
+ bodies += _flag_values(cleaned, {"--title"}, "-t")
449
462
  if any(CLOSING.search(b or "") for b in bodies):
450
463
  return (
451
464
  f"gh pr {sub} with a closing keyword (Closes/Fixes/Resolves #N) closes the issue on merge; "
@@ -468,10 +481,445 @@ def _jira_ask(argv, seg):
468
481
  return None
469
482
 
470
483
 
484
+ def _shared_ask(argv, seg):
485
+ """A comment, page or message posted to a system other people read."""
486
+ if not argv or os.path.basename(argv[0]) not in HTTP_CLIENTS:
487
+ return None
488
+ if not SHARED_WRITE.search(seg):
489
+ return None
490
+ if WRITE_METHOD.search(seg) or BODY_FLAG.search(seg):
491
+ return "this posts to a shared system (Jira comment, Confluence page or Slack); the user confirms each post"
492
+ return None
493
+
494
+
495
+ def _payload(argv, cwd):
496
+ """The request body a curl-like command sends, read from @file when given."""
497
+ for i, a in enumerate(argv):
498
+ val = None
499
+ if a in DATA_FLAGS and i + 1 < len(argv):
500
+ val = argv[i + 1]
501
+ else:
502
+ for f in DATA_FLAGS:
503
+ if a.startswith(f + "="):
504
+ val = a.split("=", 1)[1]
505
+ if val is not None:
506
+ return read_bounded(val[1:], cwd) if val.startswith("@") else val
507
+ return None
508
+
509
+
510
+ def pr_update_reason(toks, seg, cwd=None):
511
+ """A Bitbucket pull request PUT replaces the whole record: a body without
512
+ reviewers, fromRef or toRef wipes them."""
513
+ _, argv = command_word(toks)
514
+ if not argv or os.path.basename(argv[0]) not in HTTP_CLIENTS:
515
+ return None
516
+ if not PR_UPDATE.search(seg) or not PUT_METHOD.search(seg):
517
+ return None
518
+ body = _payload(argv, cwd)
519
+ try:
520
+ doc = json.loads(body) if body else None
521
+ except ValueError:
522
+ doc = None
523
+ if not isinstance(doc, dict):
524
+ return "this pull request update sends no readable JSON body; send the full record with reviewers, fromRef and toRef"
525
+ missing = [k for k in ("fromRef", "toRef") if not doc.get(k)]
526
+ if not doc.get("reviewers"):
527
+ missing.insert(0, "reviewers")
528
+ if missing:
529
+ return (
530
+ "a pull request PUT replaces the record, and this body has no "
531
+ + ", ".join(missing)
532
+ + "; read the current pull request and send its reviewers, fromRef, toRef and draft back"
533
+ )
534
+ return None
535
+
536
+
537
+ AGENT_NAME = re.compile(r"^[A-Za-z0-9._:-]{1,80}$")
538
+ GH_WRITE_SUBS = {
539
+ ("pr", "create"), ("pr", "edit"), ("pr", "comment"), ("pr", "review"), ("pr", "merge"),
540
+ ("pr", "close"), ("pr", "ready"), ("issue", "create"), ("issue", "edit"),
541
+ ("issue", "comment"), ("issue", "close"), ("issue", "reopen"), ("release", "create"),
542
+ }
543
+
544
+
545
+ def read_only_agent(agent_type, env=None):
546
+ """True when the subagent's definition denies it the Write tool: it reviews,
547
+ reads or reports, and anything it does outside its answer is a side effect."""
548
+ if not isinstance(agent_type, str) or not AGENT_NAME.match(agent_type):
549
+ return False
550
+ env = os.environ if env is None else env
551
+ home = env.get("HOME") or os.path.expanduser("~")
552
+ name = agent_type.split(":")[-1]
553
+ for root in (os.path.join(home, ".claude", "agents"), os.path.join(os.getcwd(), ".claude", "agents")):
554
+ text = read_bounded(os.path.join(root, name + ".md"), None)
555
+ if not text.startswith("---"):
556
+ continue
557
+ head = text.split("---", 2)[1] if text.count("---") >= 2 else ""
558
+ m = re.search(r"^disallowedTools:\s*(.+)$", head, re.MULTILINE)
559
+ return bool(m and re.search(r"\bWrite\b", m.group(1)))
560
+ return False
561
+
562
+
563
+ def subagent_side_effect(cmd):
564
+ """What in this command reaches past a read-only subagent's answer, or None."""
565
+ stripped, _ = strip_heredoc_bodies(cmd)
566
+ if re.search(r"write-state\.mjs", stripped):
567
+ return "it writes run state (write-state.mjs)"
568
+ for seg in split_simple(stripped):
569
+ toks = tokens(seg)
570
+ split = _git_split(toks)
571
+ if split and split[2] in WRITE_SUBS:
572
+ return f"it runs git {split[2]}"
573
+ _, argv = command_word(toks)
574
+ if not argv:
575
+ continue
576
+ base = os.path.basename(argv[0])
577
+ if base in HTTP_CLIENTS and (WRITE_METHOD.search(seg) or BODY_FLAG.search(seg)):
578
+ return f"it sends a write request with {base}"
579
+ if base == "gh":
580
+ plains = [a for a in argv[1:] if not a.startswith("-")]
581
+ if len(plains) >= 2 and (GH_ALIASES.get(plains[0], plains[0]), plains[1]) in GH_WRITE_SUBS:
582
+ return f"it runs gh {plains[0]} {plains[1]}"
583
+ if plains[:1] == ["api"] and (
584
+ re.search(r"(?:^|\s)(?:-X|--method)\s*=?\s*(?:post|put|patch|delete)\b", seg, re.I)
585
+ or re.search(r"(?:^|\s)(?:-f|-F|--field|--raw-field|--input)(?:\s|=)", seg)
586
+ ):
587
+ return "it writes through gh api"
588
+ return None
589
+
590
+
591
+ CLEANUP_SCRIPTS = {"purge.sh": None, "prune-logs.sh": "--yes", "run-kill.mjs": "apply"}
592
+
593
+
594
+ def _cleanup_ask(argv):
595
+ """purge, prune-logs --yes and run-kill apply delete run history for good."""
596
+ if not argv:
597
+ return None
598
+ rest = argv
599
+ if os.path.basename(argv[0]) in ("bash", "sh", "zsh", "node"):
600
+ rest = argv[1:]
601
+ if not rest:
602
+ return None
603
+ name = os.path.basename(rest[0])
604
+ if name not in CLEANUP_SCRIPTS:
605
+ return None
606
+ needs = CLEANUP_SCRIPTS[name]
607
+ if needs and needs not in rest[1:]:
608
+ return None
609
+ return f"{name} deletes run logs or worktrees for good; the user confirms each clean-up"
610
+
611
+
471
612
  def ask_reason(toks, seg, cwd=None):
472
613
  """Why a person should confirm this segment, or None."""
473
614
  _, argv = command_word(toks)
474
- return _gh_ask(argv, cwd) or _jira_ask(argv, seg)
615
+ return _gh_ask(argv, cwd) or _jira_ask(argv, seg) or _shared_ask(argv, seg) or _cleanup_ask(argv)
616
+
617
+
618
+ GH_TEXT_SUBS = {
619
+ ("pr", "create"), ("pr", "edit"), ("pr", "comment"), ("pr", "review"),
620
+ ("issue", "create"), ("issue", "edit"), ("issue", "comment"),
621
+ }
622
+
623
+
624
+ def gh_text(toks, cwd=None):
625
+ """The title and body text a gh pull request or issue write publishes."""
626
+ _, argv = command_word(toks)
627
+ if not argv or os.path.basename(argv[0]) != "gh":
628
+ return ""
629
+ args = [a for a in argv[1:]]
630
+ plains = [a for a in args if not a.startswith("-")]
631
+ if len(plains) < 2:
632
+ return ""
633
+ if (GH_ALIASES.get(plains[0], plains[0]), plains[1]) not in GH_TEXT_SUBS:
634
+ return ""
635
+ parts = _flag_values(args, {"--body", "--title"}, "-b") + _flag_values(args, {"--title"}, "-t")
636
+ parts += [read_bounded(p, cwd) for p in _flag_values(args, {"--body-file"}, "-F")]
637
+ return "\n".join(p for p in parts if p)
638
+
639
+
640
+ def vercel_token_reason(toks):
641
+ """A --token on the vercel command line lands in the transcript."""
642
+ _, argv = command_word(toks)
643
+ if not argv:
644
+ return None
645
+ words = [os.path.basename(argv[0])] + argv[1:3]
646
+ if "vercel" not in words:
647
+ return None
648
+ if any(a in ("--token", "-t") or a.startswith("--token=") for a in argv[1:]):
649
+ return (
650
+ "vercel with --token puts the token on the command line; run it through the "
651
+ "vercel wrapper, which reads the token from the credential store"
652
+ )
653
+ return None
654
+
655
+
656
+ SECRET_READ = re.compile(
657
+ r"credential-store\.sh['\"]?\s+get\b|\bsecurity\s+find-(?:generic|internet)-password\b[^;&|\n]*\s-w\b"
658
+ )
659
+
660
+
661
+ def secret_echo_reason(cmd):
662
+ """A stored secret read with nothing capturing or consuming its output prints
663
+ it into the transcript."""
664
+ stripped, _ = strip_heredoc_bodies(cmd)
665
+ for m in SECRET_READ.finditer(stripped):
666
+ before = stripped[: m.start()]
667
+ if before.count("$(") > before.count(")") or before.count("`") % 2:
668
+ continue
669
+ tail = re.split(r";|&&|\|\||\n", stripped[m.end():], maxsplit=1)[0]
670
+ if re.search(r"(?<!\|)\|(?!\|)|>", tail):
671
+ continue
672
+ return (
673
+ "this prints a stored secret into the transcript; capture it in a variable "
674
+ "(VAR=\"$(...)\"), pipe it into the consumer, or send it to /dev/null"
675
+ )
676
+ return None
677
+
678
+
679
+ # --- the run this session drives ----------------------------------------------
680
+
681
+
682
+ def _logs_root(env=None):
683
+ env = os.environ if env is None else env
684
+ return env.get("LOGS_ROOT") or os.path.join(os.path.expanduser("~"), ".claude", "logs", "multi-agent")
685
+
686
+
687
+ LIVE_STATUSES = ("in_progress", "awaiting_input")
688
+ _DRIVING = {}
689
+
690
+
691
+ def driving_run(session_id, env=None):
692
+ """The live run whose hostSessionId is this session, or None: status
693
+ in_progress or awaiting_input (the statuses _active-run.mjs treats as
694
+ live), the most recently written one when several match. Read once per
695
+ hook process.
696
+
697
+ Runs live at <root>/<project>/<task>/agent-state.json or <root>/<task>/; both
698
+ depths are read. A missing session id matches nothing."""
699
+ if not isinstance(session_id, str) or not session_id:
700
+ return None
701
+ root = _logs_root(env)
702
+ key = (root, session_id)
703
+ if key not in _DRIVING:
704
+ _DRIVING[key] = _scan_driving(root, session_id)
705
+ return _DRIVING[key]
706
+
707
+
708
+ def _scan_driving(root, session_id):
709
+ candidates = []
710
+ try:
711
+ for a in os.listdir(root):
712
+ pa = os.path.join(root, a)
713
+ if not os.path.isdir(pa):
714
+ continue
715
+ candidates.append(os.path.join(pa, RUN_STATE))
716
+ try:
717
+ for b in os.listdir(pa):
718
+ candidates.append(os.path.join(pa, b, RUN_STATE))
719
+ except OSError:
720
+ continue
721
+ except OSError:
722
+ return None
723
+ best, best_t = None, -1.0
724
+ for path in candidates:
725
+ try:
726
+ st = os.stat(path)
727
+ if st.st_size > 4 * 1024 * 1024:
728
+ continue
729
+ with open(path, encoding="utf-8") as fh:
730
+ doc = json.load(fh)
731
+ except (OSError, ValueError):
732
+ continue
733
+ if not isinstance(doc, dict) or doc.get("hostSessionId") != session_id:
734
+ continue
735
+ if doc.get("status") in LIVE_STATUSES and st.st_mtime > best_t:
736
+ best, best_t = doc, st.st_mtime
737
+ return best
738
+
739
+
740
+ def run_commit_reason(doc):
741
+ """Why a commit in the attended run this session drives needs the user's
742
+ yes (rules.md, "No auto-commit"), or None for no run or an unattended one."""
743
+ if not isinstance(doc, dict):
744
+ return None
745
+ launch = doc.get("launchRequest") if isinstance(doc.get("launchRequest"), dict) else {}
746
+ if doc.get("autopilot") is True or launch.get("mode") in ("autopilot", "background"):
747
+ return None
748
+ return (
749
+ "this commits inside the pipeline run this session is driving (" + str(doc.get("taskId") or "?")
750
+ + "); a commit waits for the user's explicit yes"
751
+ )
752
+
753
+
754
+ # --- output format inside a run --------------------------------------------------
755
+
756
+ # A repo profile may put the ticket first in any of its styles: [KEY-1], (KEY-1),
757
+ # KEY-1:, #1 (repo-profile-derive.mjs PREFIX_RE). The type and scope follow the
758
+ # same reader's CONV_RE: a lowercase word, any parenthesised scope.
759
+ TICKET_PREFIX = r"(\s*[\[(]?\s*(?:[A-Z][A-Z0-9]+-\d+|#\d+)\s*[\])]?:?\s+)?"
760
+ COMMIT_SUBJECT = re.compile(r"^" + TICKET_PREFIX + r"[a-z]+(\([^)]+\))?!?:\s+\S")
761
+ SUBJECT_MAX = 72
762
+ SKIPPED_SUBJECTS = re.compile(r"^(Merge |Revert \"|fixup! |squash! |amend! )|^\s*wip\b", re.I)
763
+ ENTITY = re.compile(r"&(amp|lt|gt|quot|#39|#x27);")
764
+ FENCE = re.compile(r"```.*?(```|$)", re.S)
765
+ INLINE = re.compile(r"`[^`\n]*`")
766
+ JIRA_CODE = re.compile(r"\{(code|noformat)[^}]*\}.*?(\{\1\}|$)", re.S)
767
+ # A literal backslash-n meant as a line break sits between two pieces of
768
+ # text: glued to the word before it and followed by more text
769
+ # ("Summary\nThe change", "Changes:\n- a"), or after a space before a
770
+ # capital ("Summary \nNext"). One ending a token (printf hello\n), quoted or
771
+ # bracketed is a mention of the escape; \r\n and drive paths (C:\Users\name)
772
+ # are removed before the search.
773
+ LOST_BREAK = re.compile(r"(?<=[^\s\\'\"`(\[])\\n(?=[^\s'\"`)\]])|(?<=\s)\\n(?=[A-Z])")
774
+ CRLF = re.compile(r"\\r\\n")
775
+ DRIVE_PATH = re.compile(r"\b[A-Za-z]:\\\S*")
776
+
777
+
778
+ def _subject(message):
779
+ """The first line of a commit message that carries text. A heredoc message
780
+ (`-m "$(cat <<EOF ...)"`) reaches the hook with the opening quote on its
781
+ own line, ahead of the body."""
782
+ for line in (message or "").split("\n"):
783
+ if re.search(r"[A-Za-z0-9]", line):
784
+ return line.strip()
785
+ return ""
786
+
787
+
788
+ def profile_conventional(cwd):
789
+ """False when the repo's stored profile says its subjects are not
790
+ conventional, else True (no profile, unreadable, or conventional)."""
791
+ script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "repo-profile.mjs")
792
+ if not os.path.isfile(script):
793
+ return True
794
+ try:
795
+ import subprocess
796
+
797
+ r = subprocess.run(
798
+ ["node", script, "resolve", "commit.format", "--repo", cwd or os.getcwd()],
799
+ capture_output=True, text=True, timeout=3,
800
+ )
801
+ doc = json.loads(r.stdout) if r.returncode == 0 and r.stdout.strip() else {}
802
+ except (OSError, ValueError, Exception):
803
+ return True
804
+ value = doc.get("value") if isinstance(doc, dict) and doc.get("use") else None
805
+ return not (isinstance(value, dict) and value.get("conventional") is False)
806
+
807
+
808
+ def commit_subject_reason(message, cwd=None):
809
+ """Why a commit subject breaks the commit convention (rules.md), or None.
810
+
811
+ Not judged: a subject the hook cannot read for certain (a shell expansion
812
+ in it), git's merge, revert, fixup and squash subjects, checkpoint commits
813
+ (wip ...), and the type check in a repo whose profile is not conventional."""
814
+ subject = _subject(message)
815
+ if not subject or "$" in subject or "`" in subject or SKIPPED_SUBJECTS.search(subject):
816
+ return None
817
+ if len(subject) > SUBJECT_MAX:
818
+ return "the commit subject is " + str(len(subject)) + " characters; the convention caps it at " + str(SUBJECT_MAX)
819
+ if not COMMIT_SUBJECT.match(subject) and profile_conventional(cwd):
820
+ return (
821
+ "the commit subject does not start with a conventional type, as in type(scope): description "
822
+ "(after a repo profile's ticket prefix if any)"
823
+ )
824
+ return None
825
+
826
+
827
+ def body_text_reason(text, entities=False):
828
+ """Why text published as a PR, issue or Jira body will render wrong, or
829
+ None: a literal backslash-n where a line break was meant, outside Markdown
830
+ and Jira code. With entities, an HTML entity too: a Jira payload renders
831
+ it verbatim, while Markdown on GitHub decodes it (and needs &lt; for a
832
+ literal angle bracket)."""
833
+ prose = DRIVE_PATH.sub(" ", CRLF.sub(" ", INLINE.sub("", FENCE.sub("", JIRA_CODE.sub("", text or "")))))
834
+ if LOST_BREAK.search(prose):
835
+ return "the body carries a literal \\n; write real line breaks (a heredoc or a body file)"
836
+ m = ENTITY.search(prose) if entities else None
837
+ if m:
838
+ return "the body carries the HTML entity " + m.group(0) + "; write the character itself"
839
+ return None
840
+
841
+
842
+ def _json_strings(value, out):
843
+ """The prose strings of a JSON body, leaving out ADF code blocks and text
844
+ marked as code."""
845
+ if isinstance(value, str):
846
+ out.append(value)
847
+ elif isinstance(value, dict):
848
+ if value.get("type") == "codeBlock":
849
+ return out
850
+ marks = value.get("marks")
851
+ if isinstance(marks, list) and any(isinstance(m, dict) and m.get("type") == "code" for m in marks):
852
+ return out
853
+ for v in value.values():
854
+ _json_strings(v, out)
855
+ elif isinstance(value, list):
856
+ for v in value:
857
+ _json_strings(v, out)
858
+ return out
859
+
860
+
861
+ def payload_files(argv):
862
+ """Files a request sends as its body: curl -d/--data/--data-binary/--json
863
+ @file and --data-urlencode [name]@file, and gh api --input file and
864
+ -F/--field name=@file. "@-" (stdin) is the caller's to resolve."""
865
+ if argv and os.path.basename(argv[0]) == "gh" and len(argv) > 1 and argv[1] == "api":
866
+ out, i = [], 2
867
+ while i < len(argv):
868
+ a = argv[i]
869
+ if a == "--input" and i + 1 < len(argv):
870
+ out.append(argv[i + 1])
871
+ i += 1
872
+ elif a in ("-F", "--field") and i + 1 < len(argv):
873
+ v = argv[i + 1]
874
+ if "=@" in v and not v.endswith("=@-"):
875
+ out.append(v.split("=@", 1)[1])
876
+ i += 1
877
+ i += 1
878
+ return out
879
+ if not argv or os.path.basename(argv[0]) not in HTTP_CLIENTS:
880
+ return []
881
+ out, i = [], 1
882
+ while i < len(argv):
883
+ a = argv[i]
884
+ val = None
885
+ if a in ("-d", "--data", "--data-binary", "--data-ascii", "--json") and i + 1 < len(argv):
886
+ val = argv[i + 1]
887
+ i += 1
888
+ elif a.startswith(("--data=", "--data-binary=", "--data-ascii=", "--json=")):
889
+ val = a.split("=", 1)[1]
890
+ elif a == "--data-urlencode" and i + 1 < len(argv):
891
+ m = re.match(r"^[A-Za-z0-9_.-]*@(.+)$", argv[i + 1])
892
+ i += 1
893
+ val = "@" + m.group(1) if m else None
894
+ if val and val.startswith("@") and len(val) > 1 and val != "@-":
895
+ out.append(val[1:])
896
+ i += 1
897
+ return out
898
+
899
+
900
+ def stdin_payload(argv):
901
+ """True when the request reads its body from stdin (-d @-, --data-binary @-, --input -)."""
902
+ return "@-" in argv or (len(argv) > 2 and argv[1] == "api" and "--input" in argv and "-" in argv)
903
+
904
+
905
+ # Jira issue and comment endpoints only: Bitbucket Server (/rest/api/1.0/)
906
+ # takes Markdown and Confluence storage format needs entities.
907
+ JIRA_API = re.compile(r"/rest/api/(?:2|3|latest)/(?:issue|comment)\b", re.I)
908
+
909
+
910
+ def payload_reason(path, cwd, entities=True, raw=None):
911
+ """body_text_reason over a request body file (or raw text). A JSON body is
912
+ judged on its decoded string values, where an escaped newline is a real
913
+ line break. Entities are judged only for a Jira target, which renders
914
+ them verbatim; GitHub and Bitbucket Markdown decode them."""
915
+ raw = read_bounded(path, cwd) if raw is None else raw
916
+ if not raw:
917
+ return None
918
+ try:
919
+ text = "\n".join(_json_strings(json.loads(raw), []))
920
+ except ValueError:
921
+ text = raw
922
+ return body_text_reason(text, entities=entities)
475
923
 
476
924
 
477
925
  # --- confirmations ---------------------------------------------------------------
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @file _memory-policy.mjs - what the per-repo memory stores may keep, and
3
+ * how much weight an entry has earned.
4
+ *
5
+ * ledgerRowId(row) the stable id of a learnings-ledger row, the one
6
+ * feedback is recorded against
7
+ * checkedSlug(slug) the slug as one folder name; a separator, NUL, `.`
8
+ * or `..`, anything that could leave
9
+ * ~/.claude/memory/multi-agent/, throws
10
+ * memoryExcluded(slug) prefs.global.memory.excludeProjects lists repo slugs
11
+ * whose memory is never written; every store asks
12
+ * before it writes
13
+ * standing(feedback) what the outcomes recorded against one ledger entry
14
+ * say about it: suppressed (marked corrected, or
15
+ * judged a dead end more often than useful, twice or
16
+ * more), corroborated (useful at least twice) and a
17
+ * strength that decays with age (ACT-R base level,
18
+ * ln of the sum of age^-0.5 over the useful dates)
19
+ * nearDuplicate(a, b) two statements that say the same thing in other
20
+ * words: TF-IDF cosine at or above the threshold, the
21
+ * same numbers, the same negations and mostly the
22
+ * same word order ("A over B" is not "B over A")
23
+ *
24
+ * Zero dependencies.
25
+ *
26
+ * @module pipeline/scripts/_memory-policy
27
+ */
28
+
29
+ import { readFileSync } from "node:fs";
30
+ import { homedir } from "node:os";
31
+ import { join } from "node:path";
32
+
33
+ import { shortId, tokens } from "./_retrieval.mjs";
34
+
35
+ const CORROBORATION = 2;
36
+ const NEAR_DUP = 0.9;
37
+ const ORDER_OVERLAP = 0.5;
38
+ const NEGATIONS = /\b(not|never|no|none|without|except|only|avoid|don't|do not)\b/gi;
39
+
40
+ function prefs(env) {
41
+ const p =
42
+ env.MULTI_AGENT_PREFS || join(env.HOME || homedir(), ".claude", "multi-agent-preferences.json");
43
+ try {
44
+ return JSON.parse(readFileSync(p, "utf8"));
45
+ } catch {
46
+ return {};
47
+ }
48
+ }
49
+
50
+ /** Stable id of a ledger row: its kind and its whitespace-folded statement. */
51
+ export function ledgerRowId(row) {
52
+ const statement = String(row?.statement || "")
53
+ .toLowerCase()
54
+ .replace(/\s+/g, " ")
55
+ .trim();
56
+ return shortId("L", row?.kind, statement);
57
+ }
58
+
59
+ /**
60
+ * The slug, when it names one folder; throws on anything that could leave the
61
+ * memory folder (a separator, NUL, `.` or `..`). Spaces and non-ASCII letters
62
+ * are fine: a repository folder may carry them.
63
+ */
64
+ export function checkedSlug(slug) {
65
+ const s = String(slug ?? "");
66
+ if (!s || s.length > 255 || /[/\\\0]/.test(s) || s === "." || s === "..") {
67
+ throw new Error(`not a repository slug: ${JSON.stringify(s.slice(0, 80))}`);
68
+ }
69
+ return s.normalize("NFC");
70
+ }
71
+
72
+ /** True when the repo's memory must not be written. */
73
+ export function memoryExcluded(slug, env = process.env) {
74
+ const list = prefs(env)?.global?.memory?.excludeProjects;
75
+ const want = String(slug ?? "").normalize("NFC");
76
+ return Array.isArray(list) && list.some((p) => String(p).normalize("NFC") === want);
77
+ }
78
+
79
+ /**
80
+ * @param {Array<{ts: string, outcome: string}>} feedback rows for one entry
81
+ * @returns {{useful: number, deadEnd: number, corrected: number, suppressed: boolean, corroborated: boolean, strength: number}}
82
+ */
83
+ export function standing(feedback, now = Date.now()) {
84
+ const useful = feedback.filter((f) => f.outcome === "useful");
85
+ const deadEnd = feedback.filter((f) => f.outcome === "dead_end").length;
86
+ const corrected = feedback.filter((f) => f.outcome === "corrected").length;
87
+ const sum = useful.reduce((s, f) => {
88
+ const t = Date.parse(f.ts);
89
+ const days = Number.isNaN(t) ? 365 : Math.max(0, (now - t) / 86400000);
90
+ return s + Math.pow(days + 1, -0.5);
91
+ }, 0);
92
+ return {
93
+ useful: useful.length,
94
+ deadEnd,
95
+ corrected,
96
+ suppressed: corrected > 0 || (deadEnd >= CORROBORATION && deadEnd > useful.length),
97
+ corroborated: useful.length >= CORROBORATION,
98
+ strength: sum > 0 ? Math.log(sum) + 10 : 0,
99
+ };
100
+ }
101
+
102
+ const numbersIn = (s) => (String(s).match(/\d+(?:\.\d+)?/g) || []).sort().join(",");
103
+ const negationsIn = (s) =>
104
+ (String(s).toLowerCase().match(NEGATIONS) || [])
105
+ .map((w) => w.replace(/\s+/g, " "))
106
+ .sort()
107
+ .join(",");
108
+
109
+ /** Inverse document frequencies over a corpus of token lists. */
110
+ function idf(corpus) {
111
+ const df = new Map();
112
+ for (const doc of corpus) for (const t of new Set(doc)) df.set(t, (df.get(t) || 0) + 1);
113
+ const n = corpus.length;
114
+ return (t) => Math.log(1 + n / (df.get(t) || 1));
115
+ }
116
+
117
+ function tfidf(doc, weight) {
118
+ const tf = new Map();
119
+ for (const t of doc) tf.set(t, (tf.get(t) || 0) + 1);
120
+ const v = new Map();
121
+ for (const [t, c] of tf) v.set(t, c * weight(t));
122
+ return v;
123
+ }
124
+
125
+ function cosine(va, vb) {
126
+ let dot = 0;
127
+ for (const [t, w] of va) dot += w * (vb.get(t) || 0);
128
+ const norm = (v) => Math.sqrt([...v.values()].reduce((s, w) => s + w * w, 0));
129
+ const d = norm(va) * norm(vb);
130
+ return d ? dot / d : 0;
131
+ }
132
+
133
+ /** Jaccard overlap of adjacent token pairs: low when the same words come in another order. */
134
+ function orderOverlap(a, b) {
135
+ const pairs = (doc) => new Set(doc.slice(1).map((t, i) => `${doc[i]} ${t}`));
136
+ const pa = pairs(a);
137
+ const pb = pairs(b);
138
+ if (!pa.size || !pb.size) return 0;
139
+ let shared = 0;
140
+ for (const p of pa) if (pb.has(p)) shared++;
141
+ return shared / (pa.size + pb.size - shared);
142
+ }
143
+
144
+ /**
145
+ * The existing statement `candidate` repeats in other words, or null.
146
+ *
147
+ * @param {string} candidate
148
+ * @param {string[]} existing statements of the same kind
149
+ */
150
+ export function nearDuplicate(candidate, existing) {
151
+ const c = tokens(candidate);
152
+ if (c.length < 3) return null;
153
+ const docs = existing.map((s) => tokens(s));
154
+ const weight = idf([...docs, c]);
155
+ const vc = tfidf(c, weight);
156
+ const nums = numbersIn(candidate);
157
+ const negs = negationsIn(candidate);
158
+ for (let i = 0; i < existing.length; i++) {
159
+ if (numbersIn(existing[i]) !== nums) continue;
160
+ if (negationsIn(existing[i]) !== negs) continue;
161
+ if (orderOverlap(c, docs[i]) < ORDER_OVERLAP) continue;
162
+ if (cosine(vc, tfidf(docs[i], weight)) >= NEAR_DUP) return existing[i];
163
+ }
164
+ return null;
165
+ }