@mmerterden/multi-agent-pipeline 20.34.0 → 20.36.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 (335) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/README.md +7 -5
  3. package/README.tr.md +6 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +4 -4
  7. package/install/catalog-history.json +1 -1
  8. package/install/claude.mjs +16 -1
  9. package/install/templates/claude-hooks.json +23 -1
  10. package/manifest.json +338 -305
  11. package/package.json +5 -1
  12. package/pipeline/agents/bulk-reader.md +3 -0
  13. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  15. package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
  16. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  17. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  18. package/pipeline/contract/CHANGELOG.md +9 -0
  19. package/pipeline/contract/README.md +1 -0
  20. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  21. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  22. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  23. package/pipeline/contract/manifest.json +14 -1
  24. package/pipeline/contract/types/index.d.ts +24 -2
  25. package/pipeline/lib/json-file-lock.mjs +61 -15
  26. package/pipeline/lib/office-remote.mjs +228 -0
  27. package/pipeline/lib/qr-code.mjs +386 -0
  28. package/pipeline/lib/repo-hygiene.sh +1 -0
  29. package/pipeline/lib/run-completion.mjs +17 -0
  30. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  31. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  32. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  33. package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
  34. package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
  35. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  36. package/pipeline/multi-agent-refs/help/en.md +1 -0
  37. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  38. package/pipeline/multi-agent-refs/keychain.md +7 -0
  39. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  40. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
  41. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
  42. package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
  43. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
  44. package/pipeline/multi-agent-refs/rules.md +3 -2
  45. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  46. package/pipeline/office/NOTICE.md +4 -1
  47. package/pipeline/office/assets/char_12.png +0 -0
  48. package/pipeline/office/assets/char_13.png +0 -0
  49. package/pipeline/office/assets/char_14.png +0 -0
  50. package/pipeline/office/assets/char_15.png +0 -0
  51. package/pipeline/office/assets/char_16.png +0 -0
  52. package/pipeline/office/assets/char_17.png +0 -0
  53. package/pipeline/office/assets/char_18.png +0 -0
  54. package/pipeline/office/assets/char_19.png +0 -0
  55. package/pipeline/office/assets/char_20.png +0 -0
  56. package/pipeline/office/assets/char_21.png +0 -0
  57. package/pipeline/office/assets/char_22.png +0 -0
  58. package/pipeline/office/assets/char_23.png +0 -0
  59. package/pipeline/office/assets/char_24.png +0 -0
  60. package/pipeline/office/assets/char_25.png +0 -0
  61. package/pipeline/office/assets/char_26.png +0 -0
  62. package/pipeline/office/assets/char_27.png +0 -0
  63. package/pipeline/office/assets/char_28.png +0 -0
  64. package/pipeline/office/assets/char_29.png +0 -0
  65. package/pipeline/office/assets/char_30.png +0 -0
  66. package/pipeline/office/index.html +293 -49
  67. package/pipeline/schemas/agent-state.schema.json +18 -0
  68. package/pipeline/schemas/planning-output.schema.json +18 -0
  69. package/pipeline/schemas/pr-reach.schema.json +55 -0
  70. package/pipeline/schemas/prefs.schema.json +13 -7
  71. package/pipeline/schemas/triage-output.schema.json +12 -1
  72. package/pipeline/schemas/unattended-policy.json +2 -1
  73. package/pipeline/scripts/_guard_rules.py +449 -1
  74. package/pipeline/scripts/_test-gaming.mjs +36 -0
  75. package/pipeline/scripts/agent-guard.py +79 -19
  76. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  77. package/pipeline/scripts/answer-check.mjs +113 -0
  78. package/pipeline/scripts/answer-check.sh +20 -0
  79. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  80. package/pipeline/scripts/contract-server.mjs +35 -1
  81. package/pipeline/scripts/gate-ledger.mjs +144 -21
  82. package/pipeline/scripts/office-build.mjs +276 -5
  83. package/pipeline/scripts/office-control.mjs +70 -10
  84. package/pipeline/scripts/office-pair.mjs +438 -0
  85. package/pipeline/scripts/office-remote-client.mjs +463 -0
  86. package/pipeline/scripts/phase-tracker.sh +8 -0
  87. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  88. package/pipeline/scripts/phone-devices.mjs +9 -1
  89. package/pipeline/scripts/pre-commit-check.sh +28 -2
  90. package/pipeline/scripts/pre-push-check.sh +6 -12
  91. package/pipeline/scripts/rework-base.mjs +87 -0
  92. package/pipeline/scripts/run-commit-check.mjs +104 -0
  93. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  94. package/pipeline/scripts/run-scope-guard.sh +13 -1
  95. package/pipeline/scripts/scope-check-gate.mjs +70 -6
  96. package/pipeline/scripts/task-mirror.mjs +106 -15
  97. package/pipeline/scripts/task-mirror.sh +3 -2
  98. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  99. package/pipeline/scripts/update-check.sh +7 -1
  100. package/pipeline/scripts/update-issue-progress.sh +73 -8
  101. package/pipeline/scripts/validate-planning.mjs +105 -1
  102. package/pipeline/scripts/validate-triage.mjs +42 -2
  103. package/pipeline/scripts/work-items.mjs +119 -1
  104. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  105. package/pipeline/scripts/worktree-prepare.sh +40 -4
  106. package/pipeline/scripts/write-state.mjs +67 -24
  107. package/pipeline/skills/.skill-manifest.json +12 -8
  108. package/pipeline/skills/.skills-index.json +15 -4
  109. package/pipeline/skills/shared/README.md +4 -3
  110. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
  111. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +6 -6
  112. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  113. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  114. package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
  115. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  116. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  117. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  118. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  119. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  120. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  121. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  122. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  123. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  124. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  125. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  126. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  127. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  128. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  129. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  130. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  131. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  132. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  133. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  134. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  135. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  136. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  137. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  138. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  139. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  140. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  141. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  142. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  143. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  144. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  145. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  146. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  147. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  148. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  149. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  150. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  151. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  152. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  153. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  154. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  155. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  156. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  157. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  158. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  159. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  160. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  161. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  162. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  163. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  164. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  165. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  166. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  167. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  168. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  169. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  170. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  171. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  172. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  173. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  174. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  175. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  176. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  177. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  178. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  179. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  180. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  181. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  182. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  183. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  184. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  185. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  186. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  187. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  188. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  189. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  190. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  191. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  192. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  193. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  194. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  195. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  196. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  197. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  198. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  199. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  200. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  201. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  202. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  203. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  204. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  205. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  206. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  207. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  208. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  209. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  210. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  211. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  212. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  213. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  214. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  215. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  216. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  217. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  218. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  219. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  220. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  221. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  222. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  223. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  224. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  225. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  226. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  227. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  228. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  229. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  230. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  231. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  232. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  233. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  234. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  235. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  236. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  237. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  238. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  239. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  240. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  241. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  242. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  243. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  244. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  245. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  246. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  247. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  248. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  249. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  250. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  251. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  252. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  254. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  255. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  256. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  257. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  258. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  259. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  261. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  262. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  263. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  264. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  265. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  268. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  269. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  270. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  271. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  272. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  273. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  274. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  275. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  276. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  277. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  278. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  279. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  280. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  281. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  282. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  283. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  284. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  285. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  286. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  287. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  288. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  289. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  290. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  291. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  292. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  293. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  294. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  295. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  296. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  297. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  298. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  299. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  300. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  301. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  302. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  303. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  304. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  305. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  306. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  307. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  308. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  309. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  310. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  311. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  312. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  313. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  314. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  315. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  316. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  317. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  318. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  319. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  320. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  321. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  322. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  323. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  324. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  325. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  326. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  327. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  328. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  329. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  330. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  331. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  332. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  333. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  334. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
  335. package/pipeline/skills/skills-index.md +2 -1
@@ -2,19 +2,13 @@
2
2
  # pre-push-check.sh - primary gate before every push.
3
3
  #
4
4
  # Purpose:
5
- # The repo HAS workflows (test.yml, ci-lite.yml), but they have not executed
6
- # since 2026-07-02: every attempt after that day - the last on 2026-07-25 -
7
- # finished in about four seconds with zero steps recorded, which is a
8
- # runner/quota block rather than a test failure. Both are now dormant by
9
- # declaration too (ADR-0011: the push triggers are commented out). So this
10
- # hook is not a second opinion - it is the only thing standing between a
11
- # broken commit and npm.
5
+ # The local gate before every push. The repository's CI workflows are dormant
6
+ # (ADR-0011), so this is what stands between a commit and npm.
12
7
  #
13
- # It therefore runs the CANONICAL chain, `npm test`, rather than a hand-picked
14
- # subset. A subset drifts from the chain as steps are added, and a bare
15
- # `for f in smoke-*.sh` loop cannot tell a passing suite from one that exited 0
16
- # having asserted nothing; run-smokes.mjs can. A safety net with holes in it is worse than a known gap:
17
- # it is trusted.
8
+ # It runs the canonical chain, `npm test`, rather than a hand-picked subset:
9
+ # a subset drifts from the chain as steps are added, and a bare
10
+ # `for f in smoke-*.sh` loop cannot tell a passing suite from one that exited
11
+ # 0 having asserted nothing; run-smokes.mjs can.
18
12
  #
19
13
  # Two modes, because a gate that runs INSIDE the push is a gate that breaks
20
14
  # pushes. Git opens the connection to the remote before this hook fires, so a
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file rework-base.mjs - the tree a rework round started from.
4
+ *
5
+ * scope-check-gate.mjs --rejected compares the rework against the tree the
6
+ * review read, because rejected findings carry that tree's line numbers. Each
7
+ * review round rewrites triage-output.json, so the base is kept per round:
8
+ * `.pipeline/rework-base-<iteration>`. `record` writes HEAD for the round when
9
+ * the round has none yet (a resumed re-entry keeps the base it started from)
10
+ * and prints it; `get` only prints it.
11
+ *
12
+ * Usage:
13
+ * rework-base.mjs record --worktree <dir> --iteration <N>
14
+ * rework-base.mjs get --worktree <dir> --iteration <N>
15
+ *
16
+ * Exit: 0 printed, 1 no base recorded for the round (get) or HEAD unreadable
17
+ * (record), 64 usage.
18
+ */
19
+
20
+ import { execFileSync } from "node:child_process";
21
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
22
+ import { join } from "node:path";
23
+
24
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
25
+
26
+ const SHA = /^[0-9a-f]{40,64}$/;
27
+
28
+ export function basePath(worktree, iteration) {
29
+ return join(worktree, ".pipeline", `rework-base-${iteration}`);
30
+ }
31
+
32
+ function read(path) {
33
+ if (!existsSync(path)) return null;
34
+ const sha = readFileSync(path, "utf8").trim();
35
+ return SHA.test(sha) ? sha : null;
36
+ }
37
+
38
+ export function record(worktree, iteration) {
39
+ const path = basePath(worktree, iteration);
40
+ const kept = read(path);
41
+ if (kept) return kept;
42
+ const head = execFileSync("git", ["-C", worktree, "rev-parse", "HEAD"], {
43
+ encoding: "utf8",
44
+ stdio: ["ignore", "pipe", "ignore"],
45
+ timeout: 30_000,
46
+ }).trim();
47
+ if (!SHA.test(head)) throw new Error("HEAD is not a commit");
48
+ mkdirSync(join(worktree, ".pipeline"), { recursive: true });
49
+ writeFileSync(path, head + "\n");
50
+ return head;
51
+ }
52
+
53
+ export function get(worktree, iteration) {
54
+ return read(basePath(worktree, iteration));
55
+ }
56
+
57
+ function main() {
58
+ const [cmd, ...rest] = process.argv.slice(2);
59
+ const opt = (f) => {
60
+ const i = rest.indexOf(f);
61
+ return i >= 0 ? rest[i + 1] : undefined;
62
+ };
63
+ const worktree = opt("--worktree");
64
+ const iteration = Number(opt("--iteration"));
65
+ if (
66
+ !["record", "get"].includes(cmd) ||
67
+ rest.length !== 4 ||
68
+ !worktree ||
69
+ !Number.isInteger(iteration) ||
70
+ iteration < 1
71
+ ) {
72
+ console.error("usage: rework-base.mjs record|get --worktree <dir> --iteration <N>");
73
+ return 64;
74
+ }
75
+ let sha;
76
+ try {
77
+ sha = cmd === "record" ? record(worktree, iteration) : get(worktree, iteration);
78
+ } catch (err) {
79
+ console.error(`rework-base: ${err.message}`);
80
+ return 1;
81
+ }
82
+ if (!sha) return 1;
83
+ process.stdout.write(sha + "\n");
84
+ return 0;
85
+ }
86
+
87
+ if (invokedDirectly(import.meta.url)) process.exit(main());
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file run-commit-check.mjs - a commit inside a pipeline run is authored by
4
+ * the identity Phase 0 chose for it.
5
+ *
6
+ * Called by pre-commit-check.sh with the hook payload on stdin. For a `git
7
+ * commit` made in the session a live run is bound to, the author git will
8
+ * record (`--author`, `-c user.email=`, else `git var GIT_AUTHOR_IDENT` in the
9
+ * repository) must carry the run's identity email (`projects[].identity` for
10
+ * the repository's worktree, else `identity`). Outside a bound run, for a run
11
+ * that is not live, or with no identity recorded, it says nothing.
12
+ *
13
+ * Usage: run-commit-check.mjs --repo <dir> < hook-payload.json
14
+ * Exit: 0 fine or not judged; 2 the author is not the run's identity.
15
+ *
16
+ * @module pipeline/scripts/run-commit-check
17
+ */
18
+
19
+ import { spawnSync } from "node:child_process";
20
+ import { readFileSync, realpathSync } from "node:fs";
21
+ import { resolve } from "node:path";
22
+
23
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
24
+ import { findActiveRun } from "./_active-run.mjs";
25
+
26
+ const SESSION_BOUND = new Set(["session", "host-session"]);
27
+ const LIVE = new Set(["in_progress", "awaiting_input"]);
28
+ const COMMIT = /\bgit\b(?:\s+-[cC]\s+\S+|\s+--?[\w-]+(?:=\S+)?)*\s+commit\b/;
29
+
30
+ const real = (p) => {
31
+ try {
32
+ return realpathSync(resolve(p));
33
+ } catch {
34
+ return resolve(p);
35
+ }
36
+ };
37
+
38
+ /** The author email this command makes git record, or null to ask git. */
39
+ export function commandAuthor(command) {
40
+ const author = /--author[=\s]+(['"]?)[^<'"]*<([^>]+)>\1/.exec(command);
41
+ if (author) return author[2].trim();
42
+ const cfg = /-c\s+user\.email=(['"]?)([^\s'"]+)\1/.exec(command);
43
+ return cfg ? cfg[2].trim() : null;
44
+ }
45
+
46
+ /** The identity email the run expects for a commit in `repo`, or null. */
47
+ export function expectedEmail(state, repo) {
48
+ const here = real(repo);
49
+ for (const p of Array.isArray(state.projects) ? state.projects : []) {
50
+ if (p?.worktreePath && real(p.worktreePath) === here && p.identity?.email)
51
+ return p.identity.email;
52
+ }
53
+ return typeof state.identity?.email === "string" && state.identity.email
54
+ ? state.identity.email
55
+ : null;
56
+ }
57
+
58
+ function gitAuthorEmail(repo) {
59
+ const r = spawnSync("git", ["-C", repo, "var", "GIT_AUTHOR_IDENT"], {
60
+ encoding: "utf8",
61
+ timeout: 5000,
62
+ });
63
+ return /<([^>]*)>/.exec(r.stdout || "")?.[1]?.trim() || null;
64
+ }
65
+
66
+ /** The refusal line, or null. */
67
+ export function verdict(payload, repo, env = process.env) {
68
+ const command = payload?.tool_input?.command;
69
+ if (typeof command !== "string" || !COMMIT.test(command)) return null;
70
+ const run = findActiveRun({
71
+ sessionId: typeof payload.session_id === "string" ? payload.session_id : null,
72
+ cwd: typeof payload.cwd === "string" ? payload.cwd : repo,
73
+ env,
74
+ });
75
+ if (!run || !SESSION_BOUND.has(run.matchedBy) || !LIVE.has(run.state?.status)) return null;
76
+ const want = expectedEmail(run.state, repo);
77
+ if (!want) return null;
78
+ const got = commandAuthor(command) || gitAuthorEmail(repo);
79
+ if (!got || got.toLowerCase() === want.toLowerCase()) return null;
80
+ return `BLOCKED: this commit would be authored by ${got}, and run ${run.state.taskId || run.statePath} commits as ${want} (the identity chosen in Phase 0). Set the worktree's user.email to it, or drop the override.`;
81
+ }
82
+
83
+ function main(argv) {
84
+ const i = argv.indexOf("--repo");
85
+ const repo = i >= 0 && argv[i + 1] ? argv[i + 1] : process.cwd();
86
+ let payload;
87
+ try {
88
+ payload = JSON.parse(readFileSync(0, "utf8"));
89
+ } catch {
90
+ return 0;
91
+ }
92
+ const line = verdict(payload, repo);
93
+ if (!line) return 0;
94
+ process.stderr.write(`${line}\n`);
95
+ return 2;
96
+ }
97
+
98
+ if (invokedDirectly(import.meta.url)) {
99
+ try {
100
+ process.exitCode = main(process.argv.slice(2));
101
+ } catch {
102
+ process.exitCode = 0;
103
+ }
104
+ }
@@ -51,6 +51,12 @@ const FIGMA_TOOL = /^mcp__.*figma/i;
51
51
  const SESSION_BOUND = new Set(["session", "host-session"]);
52
52
  const LIVE = new Set(["in_progress", "awaiting_input"]);
53
53
  const FILE_TOOLS = new Set(["Edit", "Write", "NotebookEdit", "MultiEdit"]);
54
+ const STATE_FILES = new Set(["agent-state.json", "tracker-state.json"]);
55
+ const STATE_REDIRECT =
56
+ /(?:>>?|\btee\s+(?:-a\s+)?)\s*['"]?[^\s'"|;&]*(?:agent|tracker)-state\.json\b/;
57
+ const FIGMA_URL = /\bhttps?:\/\/(?:[\w-]+\.)*figma\.com(?=[/:?#\s'"]|$)/i;
58
+ const ADD_ALL = /\bgit\s+(?:-C\s+\S+\s+)?add\s+(?:[^;&|\n]*\s)?(?:-A|--all|\.)(?=$|[\s;&|])/;
59
+ const HTTP_CLIENT = /(?:^|[\s;&|(])(?:curl|wget|http|https|xh)\s/;
54
60
 
55
61
  const deny = (reason) => ({
56
62
  hookSpecificOutput: {
@@ -76,7 +82,39 @@ async function recordMcpCall(statePath, tool, phase) {
76
82
  });
77
83
  }
78
84
 
79
- async function figmaDecision(payload, run) {
85
+ /** A Figma design read over HTTP: a WebFetch of figma.com, or curl and the like at it. */
86
+ function figmaOverHttp(payload) {
87
+ const ti = payload.tool_input || {};
88
+ if (payload.tool_name === "WebFetch") return typeof ti.url === "string" && FIGMA_URL.test(ti.url);
89
+ if (payload.tool_name !== "Bash" || typeof ti.command !== "string") return false;
90
+ return HTTP_CLIENT.test(ti.command) && FIGMA_URL.test(ti.command);
91
+ }
92
+
93
+ /** An autopilot run answers its own questions: it derives, or it records a halt. */
94
+ function askDecision(run) {
95
+ const s = run.state;
96
+ if (!LIVE.has(s.status) || modeOf(s) !== "autopilot") return null;
97
+ return deny(
98
+ `run ${s.taskId || run.statePath} is on autopilot, which asks nobody. Derive the answer from the analysis, the repo and the preferences, or record a halt (status "paused" with a haltReason) for a decision only the user can make.`,
99
+ );
100
+ }
101
+
102
+ /** A write to run state that bypasses write-state.mjs, or null. */
103
+ function stateWriteDecision(payload) {
104
+ const ti = payload.tool_input || {};
105
+ const raw = FILE_TOOLS.has(payload.tool_name) ? ti.file_path || ti.notebook_path : null;
106
+ const byFile = typeof raw === "string" && STATE_FILES.has(basename(raw));
107
+ const byShell =
108
+ payload.tool_name === "Bash" &&
109
+ typeof ti.command === "string" &&
110
+ STATE_REDIRECT.test(ti.command);
111
+ if (!byFile && !byShell) return null;
112
+ return deny(
113
+ 'run state is written through write-state.mjs only, which locks the file, merges the patch and refuses unearned phase moves; pipe the patch into node "$HOME/.claude/scripts/write-state.mjs" <path> instead.',
114
+ );
115
+ }
116
+
117
+ async function figmaDecision(payload, run, { record = true } = {}) {
80
118
  const tool = payload.tool_name;
81
119
  const state = run.state;
82
120
  // A finished, failed or abandoned run governs nothing and records nothing.
@@ -88,7 +126,7 @@ async function figmaDecision(payload, run) {
88
126
  `${tool} is a Figma call at Phase ${at} of run ${state.taskId || run.statePath}. The design is read during analysis only (through Phase ${figmaThroughPhase()}); later phases work from the analysis document and the repo's Code Connect mappings. If a variant or value is missing, halt and re-run /multi-agent:analysis.`,
89
127
  );
90
128
  }
91
- await recordMcpCall(run.statePath, tool, Math.min(5, Math.max(0, at)));
129
+ if (record) await recordMcpCall(run.statePath, tool, Math.min(5, Math.max(0, at)));
92
130
  return null;
93
131
  }
94
132
 
@@ -152,8 +190,16 @@ function boundaryDecision(payload, run, env) {
152
190
  export async function decide(payload, env = process.env) {
153
191
  if (!payload || typeof payload !== "object") return null;
154
192
  const tool = typeof payload.tool_name === "string" ? payload.tool_name : "";
193
+ const stateWrite = stateWriteDecision(payload);
194
+ if (stateWrite) return stateWrite;
155
195
  const figma = FIGMA_TOOL.test(tool);
156
- if (!figma && !FILE_TOOLS.has(tool)) return null;
196
+ const figmaHttp = !figma && figmaOverHttp(payload);
197
+ const asking = tool === "AskUserQuestion";
198
+ const addAll =
199
+ tool === "Bash" &&
200
+ typeof payload.tool_input?.command === "string" &&
201
+ ADD_ALL.test(payload.tool_input.command);
202
+ if (!figma && !figmaHttp && !asking && !addAll && !FILE_TOOLS.has(tool)) return null;
157
203
  const run = findActiveRun({
158
204
  sessionId: typeof payload.session_id === "string" ? payload.session_id : null,
159
205
  cwd: typeof payload.cwd === "string" ? payload.cwd : null,
@@ -162,6 +208,15 @@ export async function decide(payload, env = process.env) {
162
208
  // Only a run bound to this session: an old local-mode run matched by its
163
209
  // worktree (the project root) must not govern every later session there.
164
210
  if (!run || !SESSION_BOUND.has(run.matchedBy)) return null;
211
+ if (asking) return askDecision(run);
212
+ if (addAll) {
213
+ return LIVE.has(run.state.status)
214
+ ? deny(
215
+ `run ${run.state.taskId || run.statePath} stages the task's files by name: git add -A, --all and . also take agent artefacts and anything sensitive in the tree.`,
216
+ )
217
+ : null;
218
+ }
219
+ if (figmaHttp) return figmaDecision(payload, run, { record: false });
165
220
  return figma ? figmaDecision(payload, run) : boundaryDecision(payload, run, env);
166
221
  }
167
222
 
@@ -19,5 +19,17 @@ CORE="$HERE/run-scope-guard.mjs"
19
19
  [ -f "$CORE" ] || exit 0
20
20
  command -v node >/dev/null 2>&1 || exit 0
21
21
 
22
- node "$CORE" 2>/dev/null || true
22
+ # Most Bash and WebFetch calls concern neither Figma nor run state; those end
23
+ # here without starting node.
24
+ INPUT="$(cat)"
25
+ case "$INPUT" in
26
+ *'"tool_name":"Bash"'* | *'"tool_name": "Bash"'* | *'"tool_name":"WebFetch"'* | *'"tool_name": "WebFetch"'*)
27
+ case "$INPUT" in
28
+ *[Ff]igma* | *agent-state.json* | *tracker-state.json* | *" add "*) ;;
29
+ *) exit 0 ;;
30
+ esac
31
+ ;;
32
+ esac
33
+
34
+ printf '%s' "$INPUT" | node "$CORE" 2>/dev/null || true
23
35
  exit 0
@@ -11,6 +11,7 @@
11
11
  * git diff --name-only <base>...HEAD | node scope-check-gate.mjs --check .pipeline/scope-check.json --diff-files -
12
12
  * node scope-check-gate.mjs --check scope-check.json --diff-files files.txt [--advisory]
13
13
  * [--patch <unified diff> | --repo <dir> --patch-range <a>...<b>]
14
+ * [--rejected <triage-output.json> --repo <dir> --rework-base <rev>]
14
15
  *
15
16
  * Output (stdout, JSON): {ok, justified, unjustified[], unlisted[], notDone, formatOnly[]}
16
17
  * unjustified = in the diff, missing from files[] (or with an empty reason)
@@ -20,6 +21,11 @@
20
21
  * touch. Reported, never fatal. --patch-range reads that diff
21
22
  * with git itself, its output format pinned, so a caller needs
22
23
  * no file or process substitution for it.
24
+ * rejectedTouched = with --rejected and --rework-base, findings triage
25
+ * rejected whose lines a rework hunk (rework base against the
26
+ * working tree) changed: Phase 2 acts on accepted findings
27
+ * only, so a rework that edits a rejected finding's lines is
28
+ * "fixing" a finding triage ruled out. Reported, never fatal.
23
29
  *
24
30
  * Exit codes: 0 every diff file justified (or --advisory), 1 otherwise or on
25
31
  * unreadable input, 64 usage.
@@ -32,7 +38,10 @@ import { normalizeFile } from "./_fingerprint.mjs";
32
38
  import { parseUnifiedDiff } from "./_unified-diff.mjs";
33
39
 
34
40
  const USAGE =
35
- "usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory] [--patch <diff> | --repo <dir> --patch-range <a>...<b>]";
41
+ "usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory] [--patch <diff> | --repo <dir> --patch-range <a>...<b>] [--rejected <triage.json> --repo <dir> --rework-base <rev>]";
42
+
43
+ const REV = /^[A-Za-z0-9_./~^@{}-]+$/;
44
+ const NEAR = 2;
36
45
 
37
46
  function parseArgs(argv) {
38
47
  const opts = {};
@@ -44,16 +53,19 @@ function parseArgs(argv) {
44
53
  else if (a === "--patch") opts.patch = argv[++i];
45
54
  else if (a === "--patch-range") opts.patchRange = argv[++i];
46
55
  else if (a === "--repo") opts.repo = argv[++i];
56
+ else if (a === "--rejected") opts.rejected = argv[++i];
57
+ else if (a === "--rework-base") opts.reworkBase = argv[++i];
47
58
  else return null;
48
59
  }
49
- if (opts.patchRange !== undefined && !/^[A-Za-z0-9_./~^@{}-]+$/.test(opts.patchRange))
50
- return null;
51
- if (opts.patchRange?.startsWith("-")) return null;
60
+ for (const rev of [opts.patchRange, opts.reworkBase]) {
61
+ if (rev !== undefined && (!REV.test(rev) || rev.startsWith("-"))) return null;
62
+ }
63
+ if (Boolean(opts.rejected) !== Boolean(opts.reworkBase)) return null;
52
64
  return opts.check && opts.diffFiles ? opts : null;
53
65
  }
54
66
 
55
- /** The diff of a revision range, in the one shape formatOnlyHunks reads. */
56
- function rangeDiff(repo, range) {
67
+ /** The diff of a revision range (or a revision against the working tree), in the one shape the hunk readers take. */
68
+ function rangeDiff(repo, range, unified = []) {
57
69
  return execFileSync(
58
70
  "git",
59
71
  [
@@ -67,6 +79,7 @@ function rangeDiff(repo, range) {
67
79
  "--no-textconv",
68
80
  "--src-prefix=a/",
69
81
  "--dst-prefix=b/",
82
+ ...unified,
70
83
  range,
71
84
  "--",
72
85
  ],
@@ -180,6 +193,45 @@ export function formatOnlyHunks(patch) {
180
193
  return found;
181
194
  }
182
195
 
196
+ /**
197
+ * Rejected findings whose line a hunk of the rework diff changed. The diff
198
+ * runs from the reviewed tree, so a finding's line is read on the hunk's old
199
+ * side: a hunk that removes or rewrites lines within NEAR of it, or inserts
200
+ * next to it, touches it.
201
+ */
202
+ export function rejectedTouched(triage, patch) {
203
+ const rejected = (Array.isArray(triage?.rejected) ? triage.rejected : [])
204
+ .map((r) => r?.finding)
205
+ .filter((f) => f && typeof f.file === "string" && Number.isInteger(f.line) && f.line > 0);
206
+ if (!rejected.length) return [];
207
+ const ranges = new Map();
208
+ for (const [file, diff] of parseUnifiedDiff(patch)) {
209
+ const list = [];
210
+ for (const h of diff.hunks) {
211
+ const m = /^@@ -(\d+)(?:,(\d+))?/.exec(h.header);
212
+ if (!m) continue;
213
+ const start = Number(m[1]);
214
+ const count = m[2] === undefined ? 1 : Number(m[2]);
215
+ list.push({ from: start - NEAR, to: start + Math.max(count, 1) - 1 + NEAR, hunk: h.header });
216
+ }
217
+ ranges.set(normalizeFile(file), list);
218
+ }
219
+ const out = [];
220
+ for (const f of rejected) {
221
+ const hit = (ranges.get(normalizeFile(f.file)) || []).find(
222
+ (r) => f.line >= r.from && f.line <= r.to,
223
+ );
224
+ if (hit)
225
+ out.push({
226
+ file: normalizeFile(f.file),
227
+ line: f.line,
228
+ issue: f.issue ?? null,
229
+ hunk: hit.hunk,
230
+ });
231
+ }
232
+ return out;
233
+ }
234
+
183
235
  export function evaluate(check, diffFiles) {
184
236
  const files = Array.isArray(check?.files) ? check.files : [];
185
237
  const reasons = new Map();
@@ -228,6 +280,18 @@ function main() {
228
280
  result.patchError = err.message;
229
281
  }
230
282
  }
283
+ if (opts.rejected) {
284
+ try {
285
+ const triage = JSON.parse(readText(opts.rejected));
286
+ result.rejectedTouched = rejectedTouched(
287
+ triage,
288
+ rangeDiff(opts.repo, opts.reworkBase, ["-U0"]),
289
+ );
290
+ } catch (err) {
291
+ result.rejectedTouched = [];
292
+ result.rejectedError = err.message;
293
+ }
294
+ }
231
295
  process.stdout.write(JSON.stringify(result) + "\n");
232
296
  return result.ok ? 0 : fail;
233
297
  }
@@ -21,12 +21,21 @@
21
21
  * is left alone.
22
22
  * The task goes under the phase in progress when it was created.
23
23
  *
24
- * Exits 0 on every path and prints nothing: a mirror, not a gate. A failure is
25
- * written to the hook failure log.
24
+ * Phase tiles: a TaskCreate of "Phase N ..." records its id as the phase's
25
+ * `tasklist_id` (what :resume rebuilds the widget from), replacing the id a
26
+ * previous session left. The widget renders tiles in creation order, so a
27
+ * tile created after a higher-numbered one in the same session shows out of
28
+ * order (tracker-contract.md, "TaskCreate ordering"); the hook says so to the
29
+ * model as additionalContext. Which tiles this session created is kept in
30
+ * `tile-session.json` beside the tracker and starts over with a new session,
31
+ * so tiles a resume recreates are not compared with the old session's.
32
+ *
33
+ * Exits 0 on every path, blocks nothing, and prints only that ordering note. A
34
+ * failure is written to the hook failure log.
26
35
  */
27
36
 
28
37
  import { spawnSync } from "node:child_process";
29
- import { readFileSync } from "node:fs";
38
+ import { readFileSync, renameSync, writeFileSync } from "node:fs";
30
39
  import { dirname, join } from "node:path";
31
40
  import { fileURLToPath } from "node:url";
32
41
 
@@ -37,6 +46,7 @@ const HOOK = "task-mirror";
37
46
  const SESSION_BOUND = new Set(["session", "host-session"]);
38
47
  const STATUSES = new Set(["pending", "in_progress", "completed"]);
39
48
  const TILE = /^Phase \d+\b/;
49
+ const TILE_NO = /^Phase (\d+)\b/;
40
50
  const MAX_INPUT = 256 * 1024;
41
51
  const HERE = dirname(fileURLToPath(import.meta.url));
42
52
 
@@ -102,6 +112,43 @@ export function mirrorArgs(payload, tracker) {
102
112
  return null;
103
113
  }
104
114
 
115
+ /**
116
+ * For a phase tile's TaskCreate: the phase it is, the id it got, and the
117
+ * higher-numbered phases whose tiles already exist. Null for anything else.
118
+ */
119
+ export function tileOf(payload, tracker) {
120
+ if (payload?.tool_name !== "TaskCreate") return null;
121
+ const m = TILE_NO.exec(String(payload.tool_input?.subject ?? "").trim());
122
+ if (!m) return null;
123
+ const n = Number(m[1]);
124
+ const phases = Array.isArray(tracker?.phases) ? tracker.phases : [];
125
+ const phase = phases.find((p) => Number(p.id) === n) ?? null;
126
+ return {
127
+ phase,
128
+ id: createdId(payload.tool_response),
129
+ recorded: phase ? String(phase.meta?.tasklist_id ?? "") : "",
130
+ };
131
+ }
132
+
133
+ /**
134
+ * This session's tile record after creating phase `n`, and the phases whose
135
+ * tiles this session created before it with a higher number.
136
+ */
137
+ export function sessionTiles(previous, sessionId, n) {
138
+ if (!sessionId) return { record: previous, after: [] };
139
+ const same =
140
+ previous &&
141
+ previous.session === sessionId &&
142
+ previous.tiles &&
143
+ typeof previous.tiles === "object";
144
+ const tiles = same ? { ...previous.tiles } : {};
145
+ const after = Object.keys(tiles)
146
+ .filter((p) => Number(p) > n)
147
+ .sort((a, b) => Number(a) - Number(b));
148
+ tiles[String(n)] = true;
149
+ return { record: { session: sessionId, tiles }, after };
150
+ }
151
+
105
152
  export function mirror(payload, env = process.env) {
106
153
  if (!payload || !["TaskCreate", "TaskUpdate"].includes(payload.tool_name)) return null;
107
154
  const run = findActiveRun({
@@ -119,26 +166,70 @@ export function mirror(payload, env = process.env) {
119
166
  } catch {
120
167
  return null;
121
168
  }
169
+ const tracked = (args) => {
170
+ const r = spawnSync("bash", [join(HERE, "phase-tracker.sh"), ...args], {
171
+ env: {
172
+ ...env,
173
+ MULTI_AGENT_TASK_ID: run.state.taskId,
174
+ TRACKER_FILE: trackerPath,
175
+ TRACKER_QUIET: "1",
176
+ },
177
+ encoding: "utf8",
178
+ timeout: 8000,
179
+ });
180
+ if (r.status !== 0) logHookFailure(HOOK, `phase-tracker ${args[0]} exited ${r.status}`, env);
181
+ };
182
+ const tile = tileOf(payload, tracker);
183
+ if (tile) {
184
+ if (!tile.phase) return null;
185
+ if (tile.id && tile.recorded !== tile.id)
186
+ tracked(["meta", String(tile.phase.id), "tasklist_id", tile.id]);
187
+ const sidecar = join(runDirOf(run.statePath), "tile-session.json");
188
+ let previous;
189
+ try {
190
+ previous = JSON.parse(readFileSync(sidecar, "utf8"));
191
+ } catch {
192
+ previous = null;
193
+ }
194
+ const { record, after } = sessionTiles(
195
+ previous,
196
+ String(payload.session_id ?? ""),
197
+ Number(tile.phase.id),
198
+ );
199
+ if (record)
200
+ try {
201
+ const tmp = `${sidecar}.${process.pid}.tmp`;
202
+ writeFileSync(tmp, JSON.stringify(record));
203
+ renameSync(tmp, sidecar);
204
+ } catch (e) {
205
+ logHookFailure(HOOK, `tile record: ${String(e?.message || e).slice(0, 120)}`, env);
206
+ }
207
+ return after.length
208
+ ? {
209
+ note:
210
+ `Phase ${tile.phase.id}'s tile was created after the tiles of phase ${after.join(", ")}, ` +
211
+ "so the task widget shows it out of order. Tiles are created in phase-number order before any " +
212
+ 'TaskUpdate (tracker-contract.md, "TaskCreate ordering").',
213
+ }
214
+ : null;
215
+ }
122
216
  const args = mirrorArgs(payload, tracker);
123
217
  if (!args) return null;
124
- const r = spawnSync("bash", [join(HERE, "phase-tracker.sh"), ...args], {
125
- env: {
126
- ...env,
127
- MULTI_AGENT_TASK_ID: run.state.taskId,
128
- TRACKER_FILE: trackerPath,
129
- TRACKER_QUIET: "1",
130
- },
131
- encoding: "utf8",
132
- timeout: 8000,
133
- });
134
- if (r.status !== 0) logHookFailure(HOOK, `phase-tracker task exited ${r.status}`, env);
218
+ tracked(args);
135
219
  return args;
136
220
  }
137
221
 
138
222
  if (invokedDirectly(import.meta.url)) {
139
223
  try {
140
224
  const raw = readFileSync(0, "utf8").slice(0, MAX_INPUT);
141
- mirror(JSON.parse(raw));
225
+ const out = mirror(JSON.parse(raw));
226
+ if (out?.note) {
227
+ process.stdout.write(
228
+ JSON.stringify({
229
+ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: out.note },
230
+ }) + "\n",
231
+ );
232
+ }
142
233
  } catch (e) {
143
234
  try {
144
235
  logHookFailure(HOOK, String(e?.message || e).slice(0, 200));
@@ -6,8 +6,9 @@
6
6
  # logic is in task-mirror.mjs; this wrapper exists because every hook the
7
7
  # pipeline registers is a `bash "$HOME/.<host>/scripts/<name>"` command.
8
8
  #
9
- # Exits 0 on every path and prints nothing: a missing node or any internal
10
- # error means no mirror, never a blocked tool call.
9
+ # Exits 0 on every path; its only output is a tile-ordering note for the
10
+ # model. A missing node or any internal error means no mirror, never a blocked
11
+ # tool call.
11
12
 
12
13
  set -u
13
14