buildanything 1.8.0 → 2.0.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 (458) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +9 -3
  3. package/CHANGELOG.md +57 -0
  4. package/README.md +2 -2
  5. package/agents/a11y-architect.md +166 -0
  6. package/agents/business-model.md +80 -29
  7. package/agents/code-architect.md +75 -0
  8. package/agents/code-reviewer.md +255 -0
  9. package/agents/code-simplifier.md +64 -0
  10. package/agents/design-brand-guardian.md +293 -53
  11. package/agents/design-critic.md +139 -0
  12. package/agents/design-inclusive-visuals-specialist.md +6 -19
  13. package/agents/design-ui-designer.md +335 -56
  14. package/agents/design-ux-architect.md +403 -55
  15. package/agents/design-ux-researcher.md +264 -49
  16. package/agents/engineering-ai-engineer.md +26 -36
  17. package/agents/engineering-backend-architect.md +185 -36
  18. package/agents/engineering-data-engineer.md +225 -43
  19. package/agents/engineering-devops-automator.md +227 -74
  20. package/agents/engineering-frontend-developer.md +210 -34
  21. package/agents/engineering-mobile-app-builder.md +6 -1
  22. package/agents/engineering-rapid-prototyper.md +30 -9
  23. package/agents/engineering-security-engineer.md +263 -61
  24. package/agents/engineering-senior-developer.md +128 -19
  25. package/agents/engineering-sre.md +84 -0
  26. package/agents/engineering-technical-writer.md +285 -41
  27. package/agents/feature-intel.md +110 -0
  28. package/agents/ios-app-review-guardian.md +19 -2
  29. package/agents/ios-foundation-models-specialist.md +20 -2
  30. package/agents/ios-storekit-specialist.md +9 -2
  31. package/agents/ios-swift-architect.md +28 -1
  32. package/agents/ios-swift-search.md +8 -1
  33. package/agents/ios-swift-ui-design.md +33 -1
  34. package/agents/marketing-app-store-optimizer.md +246 -64
  35. package/agents/planner.md +216 -0
  36. package/agents/pr-test-analyzer.md +63 -0
  37. package/agents/product-feedback-synthesizer.md +8 -2
  38. package/agents/refactor-cleaner.md +102 -0
  39. package/agents/security-reviewer.md +128 -0
  40. package/agents/silent-failure-hunter.md +54 -0
  41. package/agents/swift-build-resolver.md +119 -0
  42. package/agents/swift-reviewer.md +112 -0
  43. package/agents/tech-feasibility.md +21 -1
  44. package/agents/testing-api-tester.md +236 -59
  45. package/agents/testing-evidence-collector.md +26 -1
  46. package/agents/testing-performance-benchmarker.md +21 -1
  47. package/agents/testing-reality-checker.md +6 -1
  48. package/agents/visual-research.md +116 -0
  49. package/bin/adapters/cycle-counter-tool.ts +155 -0
  50. package/bin/adapters/scribe-tool.ts +71 -0
  51. package/bin/adapters/state-save-tool.ts +130 -0
  52. package/bin/adapters/write-lease-tool.ts +127 -0
  53. package/bin/buildanything-runtime.js +15 -0
  54. package/bin/buildanything-runtime.ts +328 -0
  55. package/bin/setup.js +83 -8
  56. package/commands/add-feature.md +2 -0
  57. package/commands/build.md +782 -266
  58. package/commands/fix.md +1 -1
  59. package/commands/self-check.md +121 -0
  60. package/commands/setup.md +50 -9
  61. package/commands/ux-review.md +2 -2
  62. package/commands/verify.md +6 -9
  63. package/docs/migration/agents.yaml +729 -0
  64. package/docs/migration/phase-graph.yaml +1088 -0
  65. package/docs/migration/sdk-host-compat.md +18 -0
  66. package/hooks/compile-writer-owner-cache.ts +171 -0
  67. package/hooks/hooks.json +36 -0
  68. package/hooks/pre-tool-use +19 -0
  69. package/hooks/pre-tool-use.ts +776 -0
  70. package/hooks/record-mode-transitions.ts +178 -0
  71. package/hooks/session-start +71 -1
  72. package/hooks/subagent-start +17 -0
  73. package/hooks/subagent-start.ts +471 -0
  74. package/hooks/subagent-stop +17 -0
  75. package/hooks/subagent-stop.ts +153 -0
  76. package/package.json +24 -4
  77. package/protocols/architecture-schema.md +171 -0
  78. package/protocols/decision-log.md +131 -0
  79. package/protocols/ios-context.md +10 -11
  80. package/protocols/ios-phase-branches.md +208 -33
  81. package/protocols/launch-readiness.md +258 -0
  82. package/protocols/metric-loop.md +62 -2
  83. package/protocols/smoke-test.md +9 -1
  84. package/protocols/state-schema.json +388 -0
  85. package/protocols/state-schema.md +172 -0
  86. package/protocols/verify.md +62 -2
  87. package/protocols/visual-dna.md +185 -0
  88. package/protocols/web-phase-branches.md +222 -72
  89. package/skills/ios/_VENDORED.md +2 -0
  90. package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
  91. package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
  92. package/skills/ios/hig-components-content/SKILL.md +86 -0
  93. package/skills/ios/hig-components-content/references/activity-views.md +79 -0
  94. package/skills/ios/hig-components-content/references/charts.md +180 -0
  95. package/skills/ios/hig-components-content/references/collections.md +48 -0
  96. package/skills/ios/hig-components-content/references/color-wells.md +42 -0
  97. package/skills/ios/hig-components-content/references/image-views.md +82 -0
  98. package/skills/ios/hig-components-content/references/image-wells.md +34 -0
  99. package/skills/ios/hig-components-content/references/lockups.md +78 -0
  100. package/skills/ios/hig-components-content/references/web-views.md +36 -0
  101. package/skills/ios/hig-components-controls/SKILL.md +88 -0
  102. package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
  103. package/skills/ios/hig-components-controls/references/controls.md +112 -0
  104. package/skills/ios/hig-components-controls/references/gauges.md +74 -0
  105. package/skills/ios/hig-components-controls/references/labels.md +92 -0
  106. package/skills/ios/hig-components-controls/references/pickers.md +128 -0
  107. package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
  108. package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
  109. package/skills/ios/hig-components-controls/references/sliders.md +92 -0
  110. package/skills/ios/hig-components-controls/references/steppers.md +40 -0
  111. package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
  112. package/skills/ios/hig-components-controls/references/text-views.md +56 -0
  113. package/skills/ios/hig-components-controls/references/toggles.md +127 -0
  114. package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
  115. package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
  116. package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
  117. package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
  118. package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
  119. package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
  120. package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
  121. package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
  122. package/skills/ios/hig-components-layout/SKILL.md +99 -0
  123. package/skills/ios/hig-components-layout/references/boxes.md +48 -0
  124. package/skills/ios/hig-components-layout/references/column-views.md +44 -0
  125. package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
  126. package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
  127. package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
  128. package/skills/ios/hig-components-layout/references/panels.md +75 -0
  129. package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
  130. package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
  131. package/skills/ios/hig-components-layout/references/split-views.md +110 -0
  132. package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
  133. package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
  134. package/skills/ios/hig-components-layout/references/windows.md +188 -0
  135. package/skills/ios/hig-components-menus/SKILL.md +81 -0
  136. package/skills/ios/hig-components-menus/references/action-button.md +61 -0
  137. package/skills/ios/hig-components-menus/references/buttons.md +261 -0
  138. package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
  139. package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
  140. package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
  141. package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
  142. package/skills/ios/hig-components-menus/references/menus.md +171 -0
  143. package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
  144. package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
  145. package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
  146. package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
  147. package/skills/ios/hig-components-search/SKILL.md +68 -0
  148. package/skills/ios/hig-components-search/references/page-controls.md +120 -0
  149. package/skills/ios/hig-components-search/references/path-controls.md +40 -0
  150. package/skills/ios/hig-components-search/references/search-fields.md +189 -0
  151. package/skills/ios/hig-components-status/SKILL.md +80 -0
  152. package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
  153. package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
  154. package/skills/ios/hig-components-status/references/status-bars.md +38 -0
  155. package/skills/ios/hig-components-system/SKILL.md +88 -0
  156. package/skills/ios/hig-components-system/references/app-clips.md +387 -0
  157. package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
  158. package/skills/ios/hig-components-system/references/complications.md +425 -0
  159. package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
  160. package/skills/ios/hig-components-system/references/live-activities.md +442 -0
  161. package/skills/ios/hig-components-system/references/notifications.md +153 -0
  162. package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
  163. package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
  164. package/skills/ios/hig-components-system/references/widgets.md +517 -0
  165. package/skills/ios/hig-foundations/SKILL.md +98 -0
  166. package/skills/ios/hig-foundations/references/accessibility.md +291 -0
  167. package/skills/ios/hig-foundations/references/app-icons.md +210 -0
  168. package/skills/ios/hig-foundations/references/branding.md +44 -0
  169. package/skills/ios/hig-foundations/references/color.md +274 -0
  170. package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
  171. package/skills/ios/hig-foundations/references/icons.md +263 -0
  172. package/skills/ios/hig-foundations/references/images.md +176 -0
  173. package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
  174. package/skills/ios/hig-foundations/references/inclusion.md +189 -0
  175. package/skills/ios/hig-foundations/references/layout.md +425 -0
  176. package/skills/ios/hig-foundations/references/materials.md +238 -0
  177. package/skills/ios/hig-foundations/references/motion.md +103 -0
  178. package/skills/ios/hig-foundations/references/privacy.md +231 -0
  179. package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
  180. package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
  181. package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
  182. package/skills/ios/hig-foundations/references/typography.md +1146 -0
  183. package/skills/ios/hig-foundations/references/writing.md +91 -0
  184. package/skills/ios/hig-inputs/SKILL.md +94 -0
  185. package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
  186. package/skills/ios/hig-inputs/references/camera-control.md +107 -0
  187. package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
  188. package/skills/ios/hig-inputs/references/eyes.md +120 -0
  189. package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
  190. package/skills/ios/hig-inputs/references/game-controls.md +156 -0
  191. package/skills/ios/hig-inputs/references/gestures.md +208 -0
  192. package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
  193. package/skills/ios/hig-inputs/references/keyboards.md +234 -0
  194. package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
  195. package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
  196. package/skills/ios/hig-inputs/references/remotes.md +67 -0
  197. package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
  198. package/skills/ios/hig-patterns/SKILL.md +104 -0
  199. package/skills/ios/hig-patterns/references/charting-data.md +81 -0
  200. package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
  201. package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
  202. package/skills/ios/hig-patterns/references/entering-data.md +69 -0
  203. package/skills/ios/hig-patterns/references/feedback.md +67 -0
  204. package/skills/ios/hig-patterns/references/file-management.md +135 -0
  205. package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
  206. package/skills/ios/hig-patterns/references/launching.md +81 -0
  207. package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
  208. package/skills/ios/hig-patterns/references/loading.md +59 -0
  209. package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
  210. package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
  211. package/skills/ios/hig-patterns/references/modality.md +82 -0
  212. package/skills/ios/hig-patterns/references/multitasking.md +131 -0
  213. package/skills/ios/hig-patterns/references/offering-help.md +117 -0
  214. package/skills/ios/hig-patterns/references/onboarding.md +69 -0
  215. package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
  216. package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
  217. package/skills/ios/hig-patterns/references/playing-video.md +180 -0
  218. package/skills/ios/hig-patterns/references/printing.md +50 -0
  219. package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
  220. package/skills/ios/hig-patterns/references/searching.md +70 -0
  221. package/skills/ios/hig-patterns/references/settings.md +84 -0
  222. package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
  223. package/skills/ios/hig-patterns/references/workouts.md +76 -0
  224. package/skills/ios/hig-platforms/SKILL.md +84 -0
  225. package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
  226. package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
  227. package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
  228. package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
  229. package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
  230. package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
  231. package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
  232. package/skills/ios/hig-project-context/SKILL.md +133 -0
  233. package/skills/ios/hig-technologies/SKILL.md +107 -0
  234. package/skills/ios/hig-technologies/references/airplay.md +125 -0
  235. package/skills/ios/hig-technologies/references/always-on.md +62 -0
  236. package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
  237. package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
  238. package/skills/ios/hig-technologies/references/carekit.md +224 -0
  239. package/skills/ios/hig-technologies/references/carplay.md +119 -0
  240. package/skills/ios/hig-technologies/references/game-center.md +343 -0
  241. package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
  242. package/skills/ios/hig-technologies/references/healthkit.md +120 -0
  243. package/skills/ios/hig-technologies/references/homekit.md +343 -0
  244. package/skills/ios/hig-technologies/references/icloud.md +52 -0
  245. package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
  246. package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
  247. package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
  248. package/skills/ios/hig-technologies/references/live-photos.md +54 -0
  249. package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
  250. package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
  251. package/skills/ios/hig-technologies/references/maps.md +221 -0
  252. package/skills/ios/hig-technologies/references/nfc.md +51 -0
  253. package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
  254. package/skills/ios/hig-technologies/references/researchkit.md +134 -0
  255. package/skills/ios/hig-technologies/references/shareplay.md +142 -0
  256. package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
  257. package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
  258. package/skills/ios/hig-technologies/references/siri.md +523 -0
  259. package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
  260. package/skills/ios/hig-technologies/references/voiceover.md +90 -0
  261. package/skills/ios/hig-technologies/references/wallet.md +420 -0
  262. package/skills/ios/ios-bootstrap/SKILL.md +16 -7
  263. package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
  264. package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
  265. package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
  266. package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
  267. package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
  268. package/skills/web/accessibility/SKILL.md +146 -0
  269. package/skills/web/aceternity-ui/SKILL.md +719 -0
  270. package/skills/web/aceternity-ui/metadata.json +10 -0
  271. package/skills/web/api-design/SKILL.md +523 -0
  272. package/skills/web/chart-accessibility/SKILL.md +332 -0
  273. package/skills/web/composition-patterns/AGENTS.md +946 -0
  274. package/skills/web/composition-patterns/README.md +60 -0
  275. package/skills/web/composition-patterns/SKILL.md +89 -0
  276. package/skills/web/composition-patterns/metadata.json +11 -0
  277. package/skills/web/composition-patterns/rules/_sections.md +29 -0
  278. package/skills/web/composition-patterns/rules/_template.md +24 -0
  279. package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  280. package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
  281. package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  282. package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  283. package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
  284. package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
  285. package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
  286. package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
  287. package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
  288. package/skills/web/database-migrations/SKILL.md +429 -0
  289. package/skills/web/deployment-patterns/SKILL.md +427 -0
  290. package/skills/web/docker-patterns/SKILL.md +364 -0
  291. package/skills/web/e2e-testing/SKILL.md +326 -0
  292. package/skills/web/lighthouse-ci/SKILL.md +361 -0
  293. package/skills/web/mcp-server-patterns/SKILL.md +69 -0
  294. package/skills/web/next-best-practices/SKILL.md +153 -0
  295. package/skills/web/next-best-practices/async-patterns.md +87 -0
  296. package/skills/web/next-best-practices/bundling.md +180 -0
  297. package/skills/web/next-best-practices/data-patterns.md +297 -0
  298. package/skills/web/next-best-practices/debug-tricks.md +105 -0
  299. package/skills/web/next-best-practices/directives.md +73 -0
  300. package/skills/web/next-best-practices/error-handling.md +227 -0
  301. package/skills/web/next-best-practices/file-conventions.md +140 -0
  302. package/skills/web/next-best-practices/font.md +245 -0
  303. package/skills/web/next-best-practices/functions.md +108 -0
  304. package/skills/web/next-best-practices/hydration-error.md +91 -0
  305. package/skills/web/next-best-practices/image.md +173 -0
  306. package/skills/web/next-best-practices/metadata.md +301 -0
  307. package/skills/web/next-best-practices/parallel-routes.md +287 -0
  308. package/skills/web/next-best-practices/route-handlers.md +146 -0
  309. package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
  310. package/skills/web/next-best-practices/runtime-selection.md +39 -0
  311. package/skills/web/next-best-practices/scripts.md +141 -0
  312. package/skills/web/next-best-practices/self-hosting.md +371 -0
  313. package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
  314. package/skills/web/next-cache-components/SKILL.md +411 -0
  315. package/skills/web/postgres-best-practices/SKILL.md +14 -0
  316. package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
  317. package/skills/web/react-best-practices/AGENTS.md +3810 -0
  318. package/skills/web/react-best-practices/README.md +123 -0
  319. package/skills/web/react-best-practices/SKILL.md +149 -0
  320. package/skills/web/react-best-practices/metadata.json +15 -0
  321. package/skills/web/react-best-practices/rules/_sections.md +46 -0
  322. package/skills/web/react-best-practices/rules/_template.md +28 -0
  323. package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
  324. package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  325. package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
  326. package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
  327. package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
  328. package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  329. package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
  330. package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
  331. package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
  332. package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  333. package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
  334. package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
  335. package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
  336. package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  337. package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  338. package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
  339. package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
  340. package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
  341. package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  342. package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
  343. package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
  344. package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
  345. package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
  346. package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
  347. package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
  348. package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
  349. package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
  350. package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
  351. package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
  352. package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
  353. package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
  354. package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
  355. package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
  356. package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  357. package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
  358. package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  359. package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
  360. package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
  361. package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  362. package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  363. package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  364. package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
  365. package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
  366. package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
  367. package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  368. package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
  369. package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
  370. package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  371. package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
  372. package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  373. package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  374. package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  375. package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
  376. package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  377. package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
  378. package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  379. package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  380. package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
  381. package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  382. package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  383. package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
  384. package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
  385. package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
  386. package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
  387. package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
  388. package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
  389. package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
  390. package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
  391. package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  392. package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
  393. package/skills/web/seo/SKILL.md +154 -0
  394. package/skills/web/web-design-guidelines/SKILL.md +39 -0
  395. package/skills/web/zap-scan-config/SKILL.md +444 -0
  396. package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
  397. package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
  398. package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
  399. package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
  400. package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
  401. package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
  402. package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
  403. package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
  404. package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
  405. package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
  406. package/src/lrr/aggregator.ts +80 -0
  407. package/src/orchestrator/hooks/context-header.ts +95 -0
  408. package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
  409. package/src/orchestrator/hooks/token-accounting.ts +101 -0
  410. package/src/orchestrator/mcp/cycle-counter.ts +129 -0
  411. package/src/orchestrator/mcp/scribe.ts +283 -0
  412. package/src/orchestrator/mcp/state-save.ts +149 -0
  413. package/src/orchestrator/mcp/write-lease.ts +167 -0
  414. package/src/orchestrator/phase4-shared-context.ts +41 -0
  415. package/src/orchestrator/schemas/backward-edge.ts +46 -0
  416. package/agents/agentic-identity-trust.md +0 -121
  417. package/agents/data-consolidation-agent.md +0 -39
  418. package/agents/design-image-prompt-engineer.md +0 -105
  419. package/agents/design-visual-storyteller.md +0 -147
  420. package/agents/design-whimsy-injector.md +0 -89
  421. package/agents/engineering-autonomous-optimization-architect.md +0 -105
  422. package/agents/market-intel.md +0 -35
  423. package/agents/marketing-instagram-curator.md +0 -111
  424. package/agents/marketing-reddit-community-builder.md +0 -121
  425. package/agents/marketing-social-media-strategist.md +0 -74
  426. package/agents/marketing-tiktok-strategist.md +0 -123
  427. package/agents/marketing-twitter-engager.md +0 -124
  428. package/agents/marketing-wechat-official-account.md +0 -143
  429. package/agents/marketing-xiaohongshu-specialist.md +0 -136
  430. package/agents/marketing-zhihu-strategist.md +0 -160
  431. package/agents/product-behavioral-nudge-engine.md +0 -78
  432. package/agents/project-management-experiment-tracker.md +0 -102
  433. package/agents/report-distribution-agent.md +0 -43
  434. package/agents/risk-analysis.md +0 -45
  435. package/agents/sales-data-extraction-agent.md +0 -46
  436. package/agents/specialized-cultural-intelligence-strategist.md +0 -65
  437. package/agents/specialized-developer-advocate.md +0 -146
  438. package/agents/support-analytics-reporter.md +0 -133
  439. package/agents/support-executive-summary-generator.md +0 -64
  440. package/agents/support-finance-tracker.md +0 -145
  441. package/agents/support-legal-compliance-checker.md +0 -129
  442. package/agents/support-support-responder.md +0 -91
  443. package/agents/testing-accessibility-auditor.md +0 -110
  444. package/agents/testing-test-results-analyzer.md +0 -97
  445. package/agents/testing-tool-evaluator.md +0 -76
  446. package/agents/testing-workflow-optimizer.md +0 -99
  447. package/agents/user-research.md +0 -40
  448. package/protocols/brainstorm.md +0 -99
  449. package/protocols/design.md +0 -269
  450. package/protocols/planning.md +0 -87
  451. package/skills/ios/ios-hig/SKILL.md +0 -41
  452. package/skills/ios/ios-hig/references/accessibility.md +0 -81
  453. package/skills/ios/ios-hig/references/content.md +0 -142
  454. package/skills/ios/ios-hig/references/feedback.md +0 -123
  455. package/skills/ios/ios-hig/references/interaction.md +0 -199
  456. package/skills/ios/ios-hig/references/performance-platform.md +0 -129
  457. package/skills/ios/ios-hig/references/privacy-permissions.md +0 -181
  458. package/skills/ios/ios-hig/references/visual-design.md +0 -84
@@ -0,0 +1,776 @@
1
+ #!/usr/bin/env tsx
2
+ /*
3
+ * buildanything: PreToolUse writer-owner hook handler (tasks 2.1.1–2.1.3, 2.2.2, 2.4.1, 2.4.2, 3.3.1, 5.2.1).
4
+ *
5
+ * Reads a Claude Code tool-call JSON from stdin, consults the writer-owner
6
+ * table in docs/migration/phase-graph.yaml, and denies Write|Edit|MultiEdit
7
+ * when the current phase (from docs/plans/.build-state.json) does not match
8
+ * the owning phase for file_path.
9
+ *
10
+ * Task 2.1.3 adds default-deny for paths NOT in the writer-owner table when
11
+ * they fall under PROTECTED_PREFIXES (docs/plans/, docs/migration/, commands/,
12
+ * agents/, protocols/, hooks/, src/orchestrator/, .claude-plugin/, CLAUDE.md).
13
+ * Phase-scratch globs (phase_internal_scratch.path_glob from phase-graph.yaml,
14
+ * surfaced as cache.scratch_globs) are exempt from default-deny. Source code
15
+ * under the user's project (outside the protected prefix set) is allowed.
16
+ *
17
+ * Task 2.4.1 layers a task-level write-lease check on top of the phase-level
18
+ * decision. After the phase-level check returns ALLOW (either via a matching
19
+ * writer-owner entry or by falling outside PROTECTED_PREFIXES), the handler
20
+ * consults docs/plans/.build-state.json.active_write_leases[] and denies when
21
+ * the calling task_id lacks a lease covering file_path.
22
+ *
23
+ * Task 2.4.2 tightens task_id derivation to STRICT mode: task_id comes ONLY
24
+ * from stdin `parent_tool_use_id`, which per SDK subagent propagation
25
+ * identifies the parent Agent dispatch. The transitional 2.4.1 fallbacks
26
+ * (`tool_use_id`, env `BUILDANYTHING_TASK_ID`) are removed because
27
+ * `tool_use_id` is per-tool-invocation (too granular — the writer-owner
28
+ * table encodes per-AGENT permissions) and env vars are forgeable / leaky.
29
+ * When `parent_tool_use_id` is absent (main orchestrator context), task_id
30
+ * is null and the lease check short-circuits to allow (same as 2.4.1's
31
+ * "no task_id known" branch).
32
+ *
33
+ * Task 3.3.1 adds a HARD-DENY branch for raw Write|Edit|MultiEdit targeting
34
+ * docs/plans/.build-state.json. All mutations to that file must route through
35
+ * the `state_save` MCP tool (src/orchestrator/mcp/state-save.ts) which owns
36
+ * atomic write semantics + SHA-256 integrity. The lease mechanism from 2.4.1
37
+ * does NOT override this deny — the rule is "only state_save writes the state
38
+ * file, period". MCP tool calls are not Write/Edit and therefore never enter
39
+ * this hook. Emergency rollback via BUILDANYTHING_ALLOW_RAW_STATE_WRITES=on
40
+ * downgrades to warn (off by default).
41
+ *
42
+ * Task 5.2.1 extends writer-owner enforcement to agent-role owners. When the
43
+ * matched artifact's writers list contains tokens that are neither phases
44
+ * (`phase-N`) nor the known non-agent pseudos (orchestrator,
45
+ * orchestrator-scribe, every-phase, auto-rendered-view, all-subagents-auto),
46
+ * those tokens are treated as `subagent_type` names. The hook reads the
47
+ * subagent_type that subagent-start staged to
48
+ * `.buildanything/subagent-start-cache/<parent_tool_use_id>.json` and denies
49
+ * when the staged subagent_type is not in the writers list. This restricts
50
+ * `lrr/*.json` to the 5 LRR chapter subagents (the "chapter-judge" role) —
51
+ * aggregator + orchestrator are NOT writers of chapter files.
52
+ *
53
+ * Exit codes per Claude Code PreToolUse protocol:
54
+ * 0 — allow
55
+ * 2 — deny (stderr shown to Claude)
56
+ *
57
+ * Rollback:
58
+ * BUILDANYTHING_ENFORCE_WRITER_OWNER=false → ALL denies (writer-owner AND
59
+ * lease) downgrade to stdout warnings and exit 0.
60
+ * BUILDANYTHING_ENFORCE_WRITE_LEASE=false → only the lease check downgrades
61
+ * to stdout warning; writer-owner denies still block.
62
+ * BUILDANYTHING_SCRIBE_SINGLE_WRITER=off → only the decisions.jsonl
63
+ * writer-owner deny downgrades to stdout warning (task 2.2.2). Every
64
+ * other artifact still blocks on mismatch. Use this flag to regress to
65
+ * dual-write mode if the scribe MCP pipeline misbehaves in production
66
+ * without disabling the rest of the writer-owner table.
67
+ * BUILDANYTHING_ALLOW_RAW_STATE_WRITES=on → 3.3.1 hard-deny on raw
68
+ * Write|Edit targeting docs/plans/.build-state.json downgrades to warn.
69
+ * Default OFF. Emergency debugging only — normal operation MUST route
70
+ * state mutations through the state_save MCP.
71
+ * BUILDANYTHING_STRICT_TASK_ID=off → restore 2.4.1 env fallback: when
72
+ * stdin lacks `parent_tool_use_id`, consult env BUILDANYTHING_TASK_ID
73
+ * before giving up. Default is on (strict, SDK-only). The `tool_use_id`
74
+ * fallback is NOT restored by this flag — it was always wrong.
75
+ *
76
+ * Task 2.1.2: boot-compiled cache at .buildanything/writer-owner.json is the
77
+ * fast path. Cache misses (missing / corrupt / stale-mtime) fall back to live
78
+ * YAML parse with a single stderr warning.
79
+ */
80
+
81
+ import { readFileSync, realpathSync, statSync } from "node:fs";
82
+ import { isAbsolute, relative, resolve } from "node:path";
83
+ import process from "node:process";
84
+ import { parse as parseYaml } from "yaml";
85
+
86
+ const WATCHED_TOOLS = new Set(["Write", "Edit", "MultiEdit"]);
87
+ const ENFORCE_ENV = "BUILDANYTHING_ENFORCE_WRITER_OWNER";
88
+ const ENFORCE_LEASE_ENV = "BUILDANYTHING_ENFORCE_WRITE_LEASE";
89
+ const SCRIBE_SINGLE_WRITER_ENV = "BUILDANYTHING_SCRIBE_SINGLE_WRITER";
90
+ const DECISIONS_JSONL_PATH = "docs/plans/decisions.jsonl";
91
+ // Task 3.3.1: raw Write|Edit on .build-state.json is unconditionally denied;
92
+ // this flag downgrades the deny to warn. Default OFF — emergency use only.
93
+ const ALLOW_RAW_STATE_WRITES_ENV = "BUILDANYTHING_ALLOW_RAW_STATE_WRITES";
94
+ const BUILD_STATE_PATH = "docs/plans/.build-state.json";
95
+ const TASK_ID_ENV = "BUILDANYTHING_TASK_ID";
96
+ // Task 2.4.2: default ON (strict, parent_tool_use_id only). Set to "off" to
97
+ // restore 2.4.1's env-var fallback. The per-invocation `tool_use_id` fallback
98
+ // is unconditionally removed — no flag re-enables it.
99
+ const STRICT_TASK_ID_ENV = "BUILDANYTHING_STRICT_TASK_ID";
100
+
101
+ // Claude Code PreToolUse stdin shape (only the fields we consume).
102
+ interface ToolInput {
103
+ file_path?: string;
104
+ }
105
+ interface ToolCall {
106
+ tool_name?: string;
107
+ tool_input?: ToolInput;
108
+ // Per SDK subagent propagation, `parent_tool_use_id` identifies the parent
109
+ // Agent dispatch that owns this tool call. 2.4.2 relies on this field
110
+ // exclusively (see `deriveTaskId`). Absent in main-orchestrator context.
111
+ parent_tool_use_id?: string;
112
+ }
113
+
114
+ // Persisted lease shape per MIGRATION-PLAN-FINAL §4 A5:
115
+ // `.build-state.json.active_write_leases[] = {task_id, file_paths[], ...}`.
116
+ // We also accept the in-memory shape from write-lease.ts (`{holder, paths[]}`)
117
+ // for robustness during cutover.
118
+ interface PersistedLease {
119
+ task_id?: string;
120
+ file_paths?: string[];
121
+ holder?: string;
122
+ paths?: string[];
123
+ }
124
+
125
+ interface NormalizedLease {
126
+ task_id: string;
127
+ file_paths: string[];
128
+ }
129
+
130
+ interface ArtifactEntry {
131
+ path: string;
132
+ writer?: string;
133
+ writers?: string[];
134
+ // Populated when the entry comes from the boot-compiled cache.
135
+ regex?: string;
136
+ is_glob?: boolean;
137
+ }
138
+
139
+ interface PhaseScratch {
140
+ path_glob?: string;
141
+ }
142
+
143
+ interface PhaseGraph {
144
+ artifacts?: ArtifactEntry[];
145
+ phase_internal_scratch?: PhaseScratch;
146
+ }
147
+
148
+ interface CachedArtifact {
149
+ path: string;
150
+ writers: string[];
151
+ is_glob: boolean;
152
+ regex?: string;
153
+ }
154
+
155
+ interface CachedScratchGlob {
156
+ glob: string;
157
+ regex: string;
158
+ }
159
+
160
+ interface WriterOwnerCache {
161
+ version: number;
162
+ source_sha: string;
163
+ source_mtime: number;
164
+ compiled_at: string;
165
+ artifacts: CachedArtifact[];
166
+ // Task 2.1.3 additive field. Older caches may omit it.
167
+ scratch_globs?: CachedScratchGlob[];
168
+ }
169
+
170
+ // Paths under these prefixes are subject to default-deny when absent from the
171
+ // writer-owner table and not a scratch glob. Everything outside this set is
172
+ // assumed to be user-project source code and allowed through.
173
+ const PROTECTED_PREFIXES = [
174
+ "docs/plans/",
175
+ "docs/migration/",
176
+ ".claude-plugin/",
177
+ "commands/",
178
+ "agents/",
179
+ "protocols/",
180
+ "hooks/",
181
+ "src/orchestrator/",
182
+ ];
183
+ const PROTECTED_FILES = new Set(["CLAUDE.md"]);
184
+
185
+ const CACHE_VERSION = 1;
186
+ const CACHE_REL_PATH = ".buildanything/writer-owner.json";
187
+ const SUBAGENT_START_CACHE_DIR_REL = ".buildanything/subagent-start-cache";
188
+
189
+ // Tokens in `writers:` / `writer:` that are NOT agent-role names. Anything
190
+ // outside this set AND not matching /^phase--?\d+$/ is treated as a
191
+ // subagent_type (task 5.2.1).
192
+ const NON_AGENT_OWNER_PSEUDOS = new Set([
193
+ "orchestrator",
194
+ "orchestrator-scribe",
195
+ "every-phase",
196
+ "auto-rendered-view",
197
+ "all-subagents-auto",
198
+ ]);
199
+
200
+ interface BuildState {
201
+ current_phase?: string | number;
202
+ phase?: string | number;
203
+ active_write_leases?: PersistedLease[];
204
+ }
205
+
206
+ function readStdin(): string {
207
+ try {
208
+ return readFileSync(0, "utf8");
209
+ } catch {
210
+ return "";
211
+ }
212
+ }
213
+
214
+ function pluginRoot(): string {
215
+ // Prefer env injected by Claude Code; fall back to walking up from __dirname.
216
+ const fromEnv = process.env.CLAUDE_PLUGIN_ROOT;
217
+ if (fromEnv) return fromEnv;
218
+ return resolve(__dirname, "..");
219
+ }
220
+
221
+ function projectRoot(): string {
222
+ // User's project cwd — where docs/plans/.build-state.json lives. The plugin
223
+ // repo itself also contains a phase-graph.yaml, but not a build-state.
224
+ return process.cwd();
225
+ }
226
+
227
+ function globToRegex(pattern: string): RegExp {
228
+ // Simple glob: escape regex metachars except `*`, translate `*` → `[^/]*`,
229
+ // `**` → `.*`, `{a,b}` → `(?:a|b)`. Anchored on both sides.
230
+ let out = "";
231
+ for (let i = 0; i < pattern.length; i += 1) {
232
+ const ch = pattern[i];
233
+ if (ch === "*") {
234
+ if (pattern[i + 1] === "*") {
235
+ out += ".*";
236
+ i += 1;
237
+ } else {
238
+ out += "[^/]*";
239
+ }
240
+ } else if (ch === "{") {
241
+ const close = pattern.indexOf("}", i);
242
+ if (close === -1) {
243
+ out += "\\{";
244
+ continue;
245
+ }
246
+ const parts = pattern.slice(i + 1, close).split(",").map((p) =>
247
+ p.replace(/[.+?^${}()|[\]\\]/g, (m) => `\\${m}`),
248
+ );
249
+ out += `(?:${parts.join("|")})`;
250
+ i = close;
251
+ } else if (/[.+?^${}()|[\]\\]/.test(ch)) {
252
+ out += `\\${ch}`;
253
+ } else {
254
+ out += ch;
255
+ }
256
+ }
257
+ return new RegExp(`^${out}$`);
258
+ }
259
+
260
+ function findArtifact(filePath: string, artifacts: ArtifactEntry[]): ArtifactEntry | null {
261
+ // Exact match first, then glob match. Exact wins if both are present.
262
+ const exact = artifacts.find((a) => a.path === filePath);
263
+ if (exact) return exact;
264
+ for (const a of artifacts) {
265
+ const isGlob = a.is_glob ?? (a.path.includes("*") || a.path.includes("["));
266
+ if (!isGlob) continue;
267
+ let re: RegExp;
268
+ if (a.regex) {
269
+ re = new RegExp(a.regex);
270
+ } else {
271
+ // Treat `[task-id]` placeholder in path-with-id entries as `*` for matching.
272
+ const normalized = a.path.replace(/\[[^\]]+\]/g, "*");
273
+ re = globToRegex(normalized);
274
+ }
275
+ if (re.test(filePath)) return a;
276
+ }
277
+ return null;
278
+ }
279
+
280
+ function normalizePhase(raw: unknown): string | null {
281
+ if (raw === undefined || raw === null) return null;
282
+ const s = String(raw).trim();
283
+ if (!s) return null;
284
+ // Accept "phase-2", "2", "P2". Normalize to "phase-N". Must match the
285
+ // whole string; otherwise agent names containing digits (e.g. "a11y-architect")
286
+ // or compound pseudos like "phase-6-aggregator" get mis-classified as phases
287
+ // (task 5.2.1: the latter was a latent bug surfaced by agent-role writers
288
+ // landing in the phase-graph).
289
+ const m = s.match(/^(?:phase-?|P)?(-?\d+)$/i);
290
+ if (!m) return null;
291
+ return `phase-${m[1]}`;
292
+ }
293
+
294
+ function ownerPhases(entry: ArtifactEntry): string[] {
295
+ const raw: string[] = [];
296
+ if (entry.writer) raw.push(entry.writer);
297
+ if (entry.writers) raw.push(...entry.writers);
298
+ return raw.map((w) => String(w).trim()).filter(Boolean);
299
+ }
300
+
301
+ interface LoadedState {
302
+ phase: string | null;
303
+ leases: NormalizedLease[];
304
+ }
305
+
306
+ function normalizeLease(raw: PersistedLease): NormalizedLease | null {
307
+ const taskId = raw.task_id ?? raw.holder;
308
+ const paths = raw.file_paths ?? raw.paths;
309
+ if (typeof taskId !== "string" || !taskId.trim()) return null;
310
+ if (!Array.isArray(paths)) return null;
311
+ const cleaned = paths
312
+ .filter((p): p is string => typeof p === "string" && p.length > 0);
313
+ if (cleaned.length === 0) return null;
314
+ return { task_id: taskId, file_paths: cleaned };
315
+ }
316
+
317
+ function loadBuildState(projectDir: string): LoadedState {
318
+ const path = resolve(projectDir, "docs/plans/.build-state.json");
319
+ let text: string;
320
+ try {
321
+ text = readFileSync(path, "utf8");
322
+ } catch {
323
+ return { phase: null, leases: [] };
324
+ }
325
+ let state: BuildState;
326
+ try {
327
+ state = JSON.parse(text) as BuildState;
328
+ } catch {
329
+ return { phase: null, leases: [] };
330
+ }
331
+ const rawLeases = Array.isArray(state.active_write_leases)
332
+ ? state.active_write_leases
333
+ : [];
334
+ const leases: NormalizedLease[] = [];
335
+ for (const r of rawLeases) {
336
+ const n = normalizeLease(r);
337
+ if (n) leases.push(n);
338
+ }
339
+ return {
340
+ phase: normalizePhase(state.current_phase ?? state.phase),
341
+ leases,
342
+ };
343
+ }
344
+
345
+ function phaseGraphPath(pluginDir: string): string {
346
+ return resolve(pluginDir, "docs/migration/phase-graph.yaml");
347
+ }
348
+
349
+ function cachePath(projectDir: string): string {
350
+ return resolve(projectDir, CACHE_REL_PATH);
351
+ }
352
+
353
+ interface LoadedTable {
354
+ artifacts: ArtifactEntry[];
355
+ scratchRegexes: RegExp[];
356
+ }
357
+
358
+ function cachedToEntry(c: CachedArtifact): ArtifactEntry {
359
+ return { path: c.path, writers: c.writers, is_glob: c.is_glob, regex: c.regex };
360
+ }
361
+
362
+ function loadFromCache(pluginDir: string, projectDir: string): LoadedTable | null {
363
+ // Cache hit requires: file exists, parses, version matches, and the source
364
+ // YAML's current mtime matches cache.source_mtime. Any mismatch is a miss.
365
+ const cPath = cachePath(projectDir);
366
+ let text: string;
367
+ try {
368
+ text = readFileSync(cPath, "utf8");
369
+ } catch {
370
+ return null;
371
+ }
372
+ let cache: WriterOwnerCache;
373
+ try {
374
+ cache = JSON.parse(text) as WriterOwnerCache;
375
+ } catch {
376
+ process.stderr.write(
377
+ "buildanything: writer-owner cache malformed; parsing YAML live\n",
378
+ );
379
+ return null;
380
+ }
381
+ if (cache.version !== CACHE_VERSION || !Array.isArray(cache.artifacts)) {
382
+ process.stderr.write(
383
+ "buildanything: writer-owner cache schema mismatch; parsing YAML live\n",
384
+ );
385
+ return null;
386
+ }
387
+ try {
388
+ const srcMtime = Math.floor(statSync(phaseGraphPath(pluginDir)).mtimeMs);
389
+ if (srcMtime !== cache.source_mtime) {
390
+ process.stderr.write(
391
+ "buildanything: writer-owner cache stale; parsing YAML live\n",
392
+ );
393
+ return null;
394
+ }
395
+ } catch {
396
+ // Source YAML missing — fall through to live parse (which will also fail
397
+ // gracefully and treat the table as empty).
398
+ return null;
399
+ }
400
+ const scratchRegexes: RegExp[] = (cache.scratch_globs ?? []).map((g) => {
401
+ try {
402
+ return new RegExp(g.regex);
403
+ } catch {
404
+ return globToRegex(g.glob);
405
+ }
406
+ });
407
+ return {
408
+ artifacts: cache.artifacts.map(cachedToEntry),
409
+ scratchRegexes,
410
+ };
411
+ }
412
+
413
+ function loadFromYaml(pluginDir: string): LoadedTable {
414
+ const text = readFileSync(phaseGraphPath(pluginDir), "utf8");
415
+ const doc = parseYaml(text) as PhaseGraph;
416
+ const artifacts = Array.isArray(doc?.artifacts) ? doc.artifacts : [];
417
+ const scratchRegexes: RegExp[] = [];
418
+ const scratchGlob = doc?.phase_internal_scratch?.path_glob;
419
+ if (typeof scratchGlob === "string" && scratchGlob.trim()) {
420
+ scratchRegexes.push(globToRegex(scratchGlob));
421
+ }
422
+ return { artifacts, scratchRegexes };
423
+ }
424
+
425
+ function loadArtifacts(pluginDir: string, projectDir: string): LoadedTable {
426
+ const cached = loadFromCache(pluginDir, projectDir);
427
+ if (cached) return cached;
428
+ // Live parse fallback. Do NOT rewrite the cache — that is session-start's job.
429
+ return loadFromYaml(pluginDir);
430
+ }
431
+
432
+ function isProtectedPath(relPath: string): boolean {
433
+ if (PROTECTED_FILES.has(relPath)) return true;
434
+ return PROTECTED_PREFIXES.some((p) => relPath.startsWith(p));
435
+ }
436
+
437
+ function matchesScratch(relPath: string, scratchRegexes: RegExp[]): boolean {
438
+ return scratchRegexes.some((re) => re.test(relPath));
439
+ }
440
+
441
+ function enforceMode(): "deny" | "warn" {
442
+ return process.env[ENFORCE_ENV] === "false" ? "warn" : "deny";
443
+ }
444
+
445
+ // Task 2.2.2: scoped rollback for decisions.jsonl writer-owner enforcement.
446
+ // Returns true when the candidate path set includes docs/plans/decisions.jsonl
447
+ // AND the operator has set the scoped flag off. Lets users regress to Stage 1
448
+ // dual-write without disabling the rest of the writer-owner table.
449
+ function decisionsJsonlDowngrade(candidates: Iterable<string>): boolean {
450
+ if (process.env[SCRIBE_SINGLE_WRITER_ENV] !== "off") return false;
451
+ for (const c of candidates) if (c === DECISIONS_JSONL_PATH) return true;
452
+ return false;
453
+ }
454
+
455
+ function leaseEnforceMode(): "deny" | "warn" {
456
+ // Either rollback flag downgrades the lease decision. The writer-owner
457
+ // flag is the umbrella "turn off all enforcement" switch; the lease flag
458
+ // is the targeted "keep writer-owner, soften only the lease layer" switch.
459
+ if (process.env[ENFORCE_ENV] === "false") return "warn";
460
+ if (process.env[ENFORCE_LEASE_ENV] === "false") return "warn";
461
+ return "deny";
462
+ }
463
+
464
+ interface StagedSubagent {
465
+ subagent_type?: string | null;
466
+ }
467
+
468
+ function readStagedSubagentType(projectDir: string, taskId: string): string | null {
469
+ // The SubagentStart hook (hooks/subagent-start.ts) writes a staging file
470
+ // keyed by parent_tool_use_id. We read only the subagent_type field. Any
471
+ // filesystem / parse error yields null (caller falls through — agent-role
472
+ // enforcement is best-effort; missing staging data never blocks a write).
473
+ const path = resolve(projectDir, SUBAGENT_START_CACHE_DIR_REL, `${taskId}.json`);
474
+ let text: string;
475
+ try {
476
+ text = readFileSync(path, "utf8");
477
+ } catch {
478
+ return null;
479
+ }
480
+ try {
481
+ const parsed = JSON.parse(text) as StagedSubagent;
482
+ const t = parsed?.subagent_type;
483
+ return typeof t === "string" && t.length > 0 ? t : null;
484
+ } catch {
485
+ return null;
486
+ }
487
+ }
488
+
489
+ function agentRoleOwners(owners: string[]): string[] {
490
+ // Owners that are neither phases nor known non-agent pseudos. The caller
491
+ // has already normalized phase-like strings to `phase-N`.
492
+ return owners.filter(
493
+ (o) => !/^phase--?\d+$/.test(o) && !NON_AGENT_OWNER_PSEUDOS.has(o),
494
+ );
495
+ }
496
+
497
+ function deriveTaskId(call: ToolCall): string | null {
498
+ // Task 2.4.2: STRICT by default. The writer-owner/lease table encodes
499
+ // per-AGENT permissions, and `parent_tool_use_id` is the SDK-propagated
500
+ // identifier of the parent Agent dispatch. `tool_use_id` (per-invocation)
501
+ // is too granular and env vars are forgeable; both were transitional
502
+ // 2.4.1 fallbacks. Absence of `parent_tool_use_id` is the legitimate
503
+ // main-orchestrator case and returns null (callers short-circuit).
504
+ const fromCall = call.parent_tool_use_id;
505
+ if (typeof fromCall === "string" && fromCall.trim()) return fromCall.trim();
506
+
507
+ // Rollback path only: restores the 2.4.1 env-var fallback. Unset or any
508
+ // value other than "off" keeps strict mode. The `tool_use_id` fallback
509
+ // is NOT restored by this flag.
510
+ if (process.env[STRICT_TASK_ID_ENV] === "off") {
511
+ const fromEnv = process.env[TASK_ID_ENV];
512
+ if (typeof fromEnv === "string" && fromEnv.trim()) return fromEnv.trim();
513
+ }
514
+ return null;
515
+ }
516
+
517
+ function leaseCoversPath(lease: NormalizedLease, candidates: Iterable<string>): boolean {
518
+ const candidateList = [...candidates];
519
+ for (const entry of lease.file_paths) {
520
+ const isGlob = entry.includes("*") || /\[[^\]]+\]/.test(entry);
521
+ if (!isGlob) {
522
+ if (candidateList.includes(entry)) return true;
523
+ continue;
524
+ }
525
+ const normalized = entry.replace(/\[[^\]]+\]/g, "*");
526
+ const re = globToRegex(normalized);
527
+ if (candidateList.some((c) => re.test(c))) return true;
528
+ }
529
+ return false;
530
+ }
531
+
532
+ interface LeaseDecision {
533
+ kind: "allow" | "deny" | "warn";
534
+ message?: string;
535
+ }
536
+
537
+ function evaluateLease(
538
+ taskId: string | null,
539
+ leases: NormalizedLease[],
540
+ toolName: string,
541
+ displayPath: string,
542
+ candidates: Iterable<string>,
543
+ ): LeaseDecision {
544
+ // No task_id known → main-orchestrator context (no parent_tool_use_id on
545
+ // stdin) or 2.4.1-rollback env-unset. Lease layer intentionally no-ops;
546
+ // the writer-owner layer still ran above. This preserves Phase 0/1/2
547
+ // behavior and the main orchestrator case.
548
+ if (!taskId) return { kind: "allow" };
549
+
550
+ if (leases.length === 0) {
551
+ // TODO(phase-4-cutover): once Phase 4 orchestrator reliably acquires
552
+ // leases before dispatching implementers, flip this to a hard deny.
553
+ // For now, warn + allow so the system doesn't break during cutover
554
+ // from Stage 1 to Stage 2.
555
+ return {
556
+ kind: "warn",
557
+ message: `buildanything: write-lease WARNING on ${toolName} ${displayPath} — task_id '${taskId}' has no active leases in .build-state.json; lease acquisition will be required once Phase 4 implementer dispatches are fully cut over`,
558
+ };
559
+ }
560
+
561
+ const mine = leases.find((l) => l.task_id === taskId);
562
+ if (!mine) {
563
+ return {
564
+ kind: "deny",
565
+ message: `buildanything: write lease required — current task_id '${taskId}' has no active lease covering ${displayPath}`,
566
+ };
567
+ }
568
+
569
+ if (!leaseCoversPath(mine, candidates)) {
570
+ return {
571
+ kind: "deny",
572
+ message: `buildanything: write lease for task '${taskId}' does not include path ${displayPath}`,
573
+ };
574
+ }
575
+
576
+ return { kind: "allow" };
577
+ }
578
+
579
+ function applyLeaseDecision(decision: LeaseDecision): number {
580
+ if (decision.kind === "allow") return 0;
581
+ if (decision.kind === "warn") {
582
+ if (decision.message) process.stdout.write(`${decision.message}\n`);
583
+ return 0;
584
+ }
585
+ // deny
586
+ if (leaseEnforceMode() === "warn") {
587
+ if (decision.message) process.stdout.write(`WARNING: ${decision.message}\n`);
588
+ return 0;
589
+ }
590
+ if (decision.message) process.stderr.write(`${decision.message}\n`);
591
+ return 2;
592
+ }
593
+
594
+ function main(): number {
595
+ const raw = readStdin();
596
+ if (!raw.trim()) return 0;
597
+
598
+ let call: ToolCall;
599
+ try {
600
+ call = JSON.parse(raw) as ToolCall;
601
+ } catch {
602
+ // Malformed stdin is not this hook's problem — fail open.
603
+ return 0;
604
+ }
605
+
606
+ const toolName = call.tool_name ?? "";
607
+ if (!WATCHED_TOOLS.has(toolName)) return 0;
608
+
609
+ const filePath = call.tool_input?.file_path;
610
+ if (!filePath) return 0;
611
+
612
+ const plugin = pluginRoot();
613
+ const project = projectRoot();
614
+
615
+ let table: LoadedTable;
616
+ try {
617
+ table = loadArtifacts(plugin, project);
618
+ } catch {
619
+ // Cache missing AND phase-graph.yaml unreadable — fail open rather than
620
+ // brick every write. A compiler/YAML failure is a buildanything bug, not
621
+ // a reason to block the user's work.
622
+ return 0;
623
+ }
624
+ const { artifacts, scratchRegexes } = table;
625
+
626
+ // Match file_path against the path being written. The writer-owner table
627
+ // lists paths relative to project root; stdin paths may be absolute. Build
628
+ // a candidate set: raw, relative-to-cwd, relative-to-realpath(cwd). Also
629
+ // probe the filePath's realpath when possible so /tmp vs /private/tmp on
630
+ // macOS resolves consistently.
631
+ const relCandidates = new Set<string>([filePath]);
632
+ if (isAbsolute(filePath)) {
633
+ const projectCandidates = new Set<string>([project]);
634
+ try { projectCandidates.add(realpathSync(project)); } catch { /* ignore */ }
635
+ const absCandidates = new Set<string>([filePath]);
636
+ try {
637
+ // filePath may not exist yet (pre-write); realpath the parent dir.
638
+ const parent = resolve(filePath, "..");
639
+ absCandidates.add(resolve(realpathSync(parent), filePath.slice(parent.length + 1)));
640
+ } catch { /* ignore */ }
641
+ for (const p of projectCandidates) {
642
+ for (const f of absCandidates) {
643
+ relCandidates.add(relative(p, f));
644
+ }
645
+ }
646
+ }
647
+
648
+ // Task 3.3.1: raw Write|Edit|MultiEdit on docs/plans/.build-state.json is
649
+ // unconditionally denied — all state mutations must route through the
650
+ // `state_save` MCP tool. This runs BEFORE the writer-owner table lookup
651
+ // and lease check because the rule is absolute: no lease and no phase
652
+ // ownership overrides it. The state_save MCP call itself is not a Write
653
+ // tool call, so PreToolUse never fires on it — no false positive on the
654
+ // legitimate writer. We match on `relCandidates` (covers raw + absolute-
655
+ // resolved-to-project forms) plus a fresh cwd-relative normalization of
656
+ // filePath to catch the `./docs/plans/.build-state.json` form that
657
+ // `relCandidates` does not otherwise normalize for relative inputs.
658
+ const buildStateMatched =
659
+ relCandidates.has(BUILD_STATE_PATH) ||
660
+ relative(project, resolve(project, filePath)) === BUILD_STATE_PATH;
661
+ if (buildStateMatched) {
662
+ const msg = `buildanything: raw ${toolName} on ${BUILD_STATE_PATH} denied. Use the state_save MCP tool instead. Set ${ALLOW_RAW_STATE_WRITES_ENV}=on to downgrade to warn.`;
663
+ if (process.env[ALLOW_RAW_STATE_WRITES_ENV] === "on") {
664
+ process.stdout.write(`WARNING: ${msg}\n`);
665
+ return 0;
666
+ }
667
+ process.stderr.write(`${msg}\n`);
668
+ return 2;
669
+ }
670
+
671
+ let hit: ArtifactEntry | null = null;
672
+ let hitPath = filePath;
673
+ for (const candidate of relCandidates) {
674
+ hit = findArtifact(candidate, artifacts);
675
+ if (hit) {
676
+ hitPath = candidate;
677
+ break;
678
+ }
679
+ }
680
+
681
+ const { phase: currentPhase, leases } = loadBuildState(project);
682
+ const taskId = deriveTaskId(call);
683
+
684
+ if (!hit) {
685
+ // Task 2.1.3: default-deny for unknown paths under PROTECTED_PREFIXES.
686
+ // Exemptions: scratch globs (phase_internal_scratch) and any path outside
687
+ // the protected prefix set (user-project source code).
688
+ const scratchMatched = [...relCandidates].some((c) =>
689
+ matchesScratch(c, scratchRegexes),
690
+ );
691
+ if (scratchMatched) {
692
+ return applyLeaseDecision(
693
+ evaluateLease(taskId, leases, toolName, filePath, relCandidates),
694
+ );
695
+ }
696
+
697
+ const underProtected = [...relCandidates].some((c) => isProtectedPath(c));
698
+ if (!underProtected) {
699
+ return applyLeaseDecision(
700
+ evaluateLease(taskId, leases, toolName, filePath, relCandidates),
701
+ );
702
+ }
703
+
704
+ const denyPath = [...relCandidates].find((c) => isProtectedPath(c)) ?? filePath;
705
+ const msg = `buildanything: writer-owner hook denied ${toolName} on ${denyPath} — path not in writer-owner table. Please add an entry to docs/migration/phase-graph.yaml or route the write through the scribe_decision MCP.`;
706
+
707
+ if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
708
+ process.stdout.write(`WARNING: ${msg}\n`);
709
+ return 0;
710
+ }
711
+ process.stderr.write(`${msg}\n`);
712
+ return 2;
713
+ }
714
+
715
+ // Boot-time race: no state file yet. Fail open on phase-mismatch check so
716
+ // Phase 1's first write isn't blocked before .build-state.json exists.
717
+ if (!currentPhase) return 0;
718
+
719
+ const owners = ownerPhases(hit).map((w) => normalizePhase(w) ?? w);
720
+ if (owners.length === 0) {
721
+ return applyLeaseDecision(
722
+ evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
723
+ );
724
+ }
725
+
726
+ // Task 5.2.1: agent-role enforcement. When the artifact's writers list
727
+ // contains subagent_type names (e.g. chapter-judge subagents for
728
+ // lrr/*.json), deny when the calling subagent's staged subagent_type is
729
+ // not in that list. Best-effort: if no task_id or no staged cache file
730
+ // exists, fall through (matches lease semantics — main orchestrator has
731
+ // no parent_tool_use_id; writer-owner still enforces phases).
732
+ const agentOwners = agentRoleOwners(owners);
733
+ if (agentOwners.length > 0 && taskId) {
734
+ const stagedType = readStagedSubagentType(project, taskId);
735
+ if (stagedType && !agentOwners.includes(stagedType)) {
736
+ const ownerStr = agentOwners.join(" | ");
737
+ const msg = `buildanything: writer-owner hook denied ${toolName} on ${hitPath} — subagent '${stagedType}' is not an owner; writer(s): ${ownerStr}`;
738
+ if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
739
+ process.stdout.write(`WARNING: ${msg}\n`);
740
+ return 0;
741
+ }
742
+ process.stderr.write(`${msg}\n`);
743
+ return 2;
744
+ }
745
+ // stagedType match OR staging file missing → fall through to phase / lease.
746
+ }
747
+
748
+ // Non-phase owners (e.g. "orchestrator", "orchestrator-scribe",
749
+ // "every-phase", "auto-rendered-view") are out of scope for this phase-vs-
750
+ // phase check. Leave them to task 2.2.x / 2.4.x.
751
+ const phaseOwners = owners.filter((o) => /^phase--?\d+$/.test(o));
752
+ if (phaseOwners.length === 0) {
753
+ return applyLeaseDecision(
754
+ evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
755
+ );
756
+ }
757
+
758
+ if (phaseOwners.includes(currentPhase)) {
759
+ return applyLeaseDecision(
760
+ evaluateLease(taskId, leases, toolName, hitPath, relCandidates),
761
+ );
762
+ }
763
+
764
+ const ownerStr = phaseOwners.join(" | ");
765
+ const msg = `buildanything: writer-owner hook denied ${toolName} on ${hitPath} — current phase ${currentPhase}, path owned by ${ownerStr}`;
766
+
767
+ if (enforceMode() === "warn" || decisionsJsonlDowngrade(relCandidates)) {
768
+ process.stdout.write(`WARNING: ${msg}\n`);
769
+ return 0;
770
+ }
771
+
772
+ process.stderr.write(`${msg}\n`);
773
+ return 2;
774
+ }
775
+
776
+ process.exit(main());