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,283 @@
1
+ /**
2
+ * scribe_decision MCP handler.
3
+ *
4
+ * The ONLY writer to docs/plans/decisions.jsonl.
5
+ * Subagents return deviation_row objects → orchestrator routes them here.
6
+ * Append-only — NEVER rewrites or truncates the file.
7
+ *
8
+ * Spec: protocols/decision-log.md
9
+ * Schema: docs/migration/decisions.schema.json
10
+ * Migration ref: MIGRATION-PLAN-FINAL.md §4 Stage 1 (tasks 1.2.1–1.2.3)
11
+ */
12
+
13
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
14
+ import { dirname } from 'node:path';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Types
18
+ // ---------------------------------------------------------------------------
19
+
20
+ export interface RejectedAlternative {
21
+ approach: string;
22
+ reason: string;
23
+ revisit_criterion: string;
24
+ }
25
+
26
+ export interface DecisionRow {
27
+ decision_id: string;
28
+ phase: string;
29
+ timestamp: string;
30
+ decision: string;
31
+ chosen_approach: string;
32
+ rejected_alternatives: RejectedAlternative[];
33
+ decided_by: string;
34
+ ref: string;
35
+ status: 'open' | 'triggered' | 'resolved';
36
+ }
37
+
38
+ export interface ScribeInput {
39
+ phase: string;
40
+ category: string;
41
+ summary: string;
42
+ decided_by: string;
43
+ impact_level: 'low' | 'medium' | 'high' | 'critical';
44
+ chosen_approach: string;
45
+ rejected_alternatives?: RejectedAlternative[];
46
+ ref?: string;
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Constants — sourced from protocols/decision-log.md §Hard Field Constraints
51
+ // ---------------------------------------------------------------------------
52
+
53
+ const VALID_IMPACTS = ['low', 'medium', 'high', 'critical'] as const;
54
+ const VALID_STATUSES = ['open', 'triggered', 'resolved'] as const;
55
+ const MAX_ALTERNATIVES = 3;
56
+ const MAX_ROWS_PER_PHASE = 5;
57
+ const MAX_DECISION_LEN = 240;
58
+ const MAX_CHOSEN_APPROACH_LEN = 480;
59
+ const MAX_APPROACH_LEN = 240;
60
+ const MAX_REASON_LEN = 400;
61
+ const MAX_REVISIT_LEN = 240;
62
+
63
+ /** Ref field pattern from decisions.schema.json */
64
+ const REF_PATTERN = /^[a-zA-Z0-9_\-./]+\.(md|json|jsonl|yaml|yml)(#[a-zA-Z0-9_\-/.]+)?$/;
65
+
66
+ /** Phase string pattern from decisions.schema.json */
67
+ const PHASE_PATTERN = /^[0-9]+(\.[0-9]+)?$/;
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Per-phase sequence counters (task 1.2.3 — ID allocation)
71
+ // ---------------------------------------------------------------------------
72
+
73
+ const counters: Record<string, number> = {};
74
+
75
+ /**
76
+ * Load existing counters from a decisions.jsonl file.
77
+ * Scans all rows to find the max seq per phase so new IDs don't collide.
78
+ */
79
+ export function loadCounters(filePath: string): void {
80
+ if (!existsSync(filePath)) return;
81
+ const content = readFileSync(filePath, 'utf-8').trim();
82
+ if (!content) return;
83
+ const lines = content.split('\n').filter(Boolean);
84
+ for (const line of lines) {
85
+ try {
86
+ const row = JSON.parse(line) as DecisionRow;
87
+ const match = row.decision_id.match(/^D-(.+)-(\d{2,})$/);
88
+ if (match) {
89
+ const phase = match[1];
90
+ const seq = parseInt(match[2], 10);
91
+ counters[phase] = Math.max(counters[phase] ?? 0, seq);
92
+ }
93
+ } catch {
94
+ /* skip malformed lines */
95
+ }
96
+ }
97
+ }
98
+
99
+ /** Allocate the next decision ID for a phase. Format: D-{phase}-{seq} */
100
+ function allocateId(phase: string): string {
101
+ counters[phase] = (counters[phase] ?? 0) + 1;
102
+ return `D-${phase}-${String(counters[phase]).padStart(2, '0')}`;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Exclusive-write lock (task 1.2.3)
107
+ // ---------------------------------------------------------------------------
108
+
109
+ let writeLocked = false;
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Schema validation (task 1.2.2)
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * Count rows for a given phase in the target file.
117
+ * Used to enforce the max-5-rows-per-phase constraint.
118
+ */
119
+ function countPhaseRows(filePath: string, phase: string): number {
120
+ if (!existsSync(filePath)) return 0;
121
+ const content = readFileSync(filePath, 'utf-8').trim();
122
+ if (!content) return 0;
123
+ let count = 0;
124
+ for (const line of content.split('\n')) {
125
+ try {
126
+ const row = JSON.parse(line) as DecisionRow;
127
+ if (row.phase === phase) count++;
128
+ } catch {
129
+ /* skip */
130
+ }
131
+ }
132
+ return count;
133
+ }
134
+
135
+ /**
136
+ * Validate a single rejected alternative against schema constraints.
137
+ */
138
+ function validateAlternative(alt: RejectedAlternative, index: number): void {
139
+ if (!alt.approach || alt.approach.length === 0) {
140
+ throw new Error(`rejected_alternatives[${index}].approach is required`);
141
+ }
142
+ if (alt.approach.length > MAX_APPROACH_LEN) {
143
+ throw new Error(`rejected_alternatives[${index}].approach exceeds ${MAX_APPROACH_LEN} chars`);
144
+ }
145
+ if (!alt.reason || alt.reason.length === 0) {
146
+ throw new Error(`rejected_alternatives[${index}].reason is required`);
147
+ }
148
+ if (alt.reason.length > MAX_REASON_LEN) {
149
+ throw new Error(`rejected_alternatives[${index}].reason exceeds ${MAX_REASON_LEN} chars (max 2 sentences)`);
150
+ }
151
+ if (!alt.revisit_criterion || alt.revisit_criterion.length === 0) {
152
+ throw new Error(`rejected_alternatives[${index}].revisit_criterion is required`);
153
+ }
154
+ if (alt.revisit_criterion.length > MAX_REVISIT_LEN) {
155
+ throw new Error(`rejected_alternatives[${index}].revisit_criterion exceeds ${MAX_REVISIT_LEN} chars (max 1 sentence)`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Validate scribe input against the decision log schema.
161
+ * Throws on any validation failure.
162
+ */
163
+ export function validate(input: ScribeInput): void {
164
+ if (!input.phase || !PHASE_PATTERN.test(input.phase)) {
165
+ throw new Error(`Invalid phase: "${input.phase ?? ''}". Must match pattern ${PHASE_PATTERN.source}`);
166
+ }
167
+ if (!input.summary || input.summary.length === 0) {
168
+ throw new Error('summary is required');
169
+ }
170
+ if (input.summary.length > MAX_DECISION_LEN) {
171
+ throw new Error(`summary exceeds ${MAX_DECISION_LEN} chars`);
172
+ }
173
+ if (!input.chosen_approach || input.chosen_approach.length === 0) {
174
+ throw new Error('chosen_approach is required');
175
+ }
176
+ if (input.chosen_approach.length > MAX_CHOSEN_APPROACH_LEN) {
177
+ throw new Error(`chosen_approach exceeds ${MAX_CHOSEN_APPROACH_LEN} chars`);
178
+ }
179
+ if (!input.decided_by || input.decided_by.length === 0) {
180
+ throw new Error('decided_by is required');
181
+ }
182
+ if (!input.category) {
183
+ throw new Error('category is required');
184
+ }
185
+ if (!VALID_IMPACTS.includes(input.impact_level as typeof VALID_IMPACTS[number])) {
186
+ throw new Error(`Invalid impact_level: ${input.impact_level}. Must be one of: ${VALID_IMPACTS.join(', ')}`);
187
+ }
188
+ if (input.ref !== undefined && input.ref !== '' && !REF_PATTERN.test(input.ref)) {
189
+ throw new Error(`Invalid ref: "${input.ref}". Must match pattern ${REF_PATTERN.source}`);
190
+ }
191
+
192
+ const alts = input.rejected_alternatives ?? [];
193
+ if (alts.length > MAX_ALTERNATIVES) {
194
+ throw new Error(`Max ${MAX_ALTERNATIVES} rejected alternatives per decision row`);
195
+ }
196
+ for (let i = 0; i < alts.length; i++) {
197
+ validateAlternative(alts[i], i);
198
+ }
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // MCP handler (task 1.2.1)
203
+ // ---------------------------------------------------------------------------
204
+
205
+ /**
206
+ * scribe_decision — in-process MCP tool handler.
207
+ *
208
+ * Validates input, allocates a sequential ID, acquires the exclusive-write
209
+ * lock, and appends a single JSON line to the decisions file.
210
+ * Returns the written DecisionRow.
211
+ *
212
+ * @param input - ScribeInput from the calling subagent
213
+ * @param filePath - Absolute path to decisions.jsonl (default: docs/plans/decisions.jsonl)
214
+ */
215
+ export function scribeDecision(input: ScribeInput, filePath: string): DecisionRow {
216
+ if (writeLocked) {
217
+ throw new Error('Exclusive write lock held — concurrent scribe call rejected');
218
+ }
219
+
220
+ writeLocked = true;
221
+ try {
222
+ validate(input);
223
+
224
+ // Enforce max 5 rows per phase
225
+ const existing = countPhaseRows(filePath, input.phase);
226
+ if (existing >= MAX_ROWS_PER_PHASE) {
227
+ throw new Error(
228
+ `Phase "${input.phase}" already has ${existing} decision rows (max ${MAX_ROWS_PER_PHASE}). ` +
229
+ 'Split into sub-phases or consolidate before adding more.',
230
+ );
231
+ }
232
+
233
+ const row: DecisionRow = {
234
+ decision_id: allocateId(input.phase),
235
+ phase: input.phase,
236
+ timestamp: new Date().toISOString(),
237
+ decision: input.summary,
238
+ chosen_approach: input.chosen_approach,
239
+ rejected_alternatives: input.rejected_alternatives ?? [],
240
+ decided_by: input.decided_by,
241
+ ref: input.ref ?? '',
242
+ status: 'open',
243
+ };
244
+
245
+ // Ensure parent directory exists
246
+ const dir = dirname(filePath);
247
+ if (!existsSync(dir)) {
248
+ mkdirSync(dir, { recursive: true });
249
+ }
250
+
251
+ // Append-only write — NEVER rewrite or truncate
252
+ writeFileSync(filePath, JSON.stringify(row) + '\n', { flag: 'a' });
253
+
254
+ return row;
255
+ } finally {
256
+ writeLocked = false;
257
+ }
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Test helpers
262
+ // ---------------------------------------------------------------------------
263
+
264
+ /**
265
+ * Reset internal state (counters + lock). For testing only.
266
+ */
267
+ export function reset(): void {
268
+ for (const key of Object.keys(counters)) delete counters[key];
269
+ writeLocked = false;
270
+ }
271
+
272
+ /** Expose constants for test assertions */
273
+ export const LIMITS = {
274
+ MAX_ALTERNATIVES,
275
+ MAX_ROWS_PER_PHASE,
276
+ MAX_DECISION_LEN,
277
+ MAX_CHOSEN_APPROACH_LEN,
278
+ MAX_APPROACH_LEN,
279
+ MAX_REASON_LEN,
280
+ MAX_REVISIT_LEN,
281
+ VALID_IMPACTS,
282
+ VALID_STATUSES,
283
+ } as const;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * state_save MCP handler.
3
+ *
4
+ * Atomic state persistence for .build-state.json (and any state file).
5
+ * All mutations to .build-state.json MUST route through this MCP —
6
+ * raw Write|Edit is denied by the writer-owner hook (Stage 3.3.1).
7
+ *
8
+ * Protocol: write-to-.tmp + fsync + rename (POSIX atomic on same filesystem).
9
+ * Every write produces a SHA-256 integrity checksum for verification.
10
+ *
11
+ * Spec: MIGRATION-PLAN-FINAL.md §4 Stage 3 (tasks 3.2.1–3.2.3)
12
+ */
13
+
14
+ import {
15
+ writeFileSync,
16
+ readFileSync,
17
+ renameSync,
18
+ unlinkSync,
19
+ existsSync,
20
+ mkdirSync,
21
+ openSync,
22
+ fsyncSync,
23
+ closeSync,
24
+ } from 'node:fs';
25
+ import { createHash } from 'node:crypto';
26
+ import { dirname } from 'node:path';
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Types
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export interface StateSaveResult {
33
+ success: boolean;
34
+ path: string;
35
+ sha256: string;
36
+ bytesWritten: number;
37
+ }
38
+
39
+ export interface StateReadResult {
40
+ state: Record<string, unknown>;
41
+ sha256: string;
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Exclusive-write lock — prevents concurrent state_save calls
46
+ // ---------------------------------------------------------------------------
47
+
48
+ let writeLocked = false;
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Core: atomic state save (tasks 3.2.1 + 3.2.2 + 3.2.3)
52
+ // ---------------------------------------------------------------------------
53
+
54
+ /**
55
+ * Atomically save state to a JSON file.
56
+ *
57
+ * Protocol (write-to-.tmp + fsync + os.replace):
58
+ * 1. Serialize state to deterministic JSON
59
+ * 2. Compute SHA-256 checksum over serialized content
60
+ * 3. Write to {path}.tmp
61
+ * 4. fsync the .tmp fd (flush to disk — survives power loss)
62
+ * 5. rename .tmp → target (POSIX atomic on same filesystem)
63
+ * 6. On failure: delete .tmp, release lock, re-throw
64
+ *
65
+ * Callers: orchestrator phase transitions, cycle_counter_check MCP,
66
+ * SubagentStart hook, write-lease persistence, token accounting.
67
+ */
68
+ export function stateSave(path: string, state: Record<string, unknown>): StateSaveResult {
69
+ if (writeLocked) {
70
+ throw new Error('Exclusive write lock held — concurrent state_save call rejected');
71
+ }
72
+
73
+ writeLocked = true;
74
+ const tmp = `${path}.tmp`;
75
+
76
+ try {
77
+ // Ensure parent directory exists
78
+ const dir = dirname(path);
79
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
80
+
81
+ // 1. Serialize — deterministic pretty-print + trailing newline
82
+ const content = JSON.stringify(state, null, 2) + '\n';
83
+
84
+ // 2. SHA-256 integrity checksum (task 3.2.3)
85
+ const sha256 = createHash('sha256').update(content).digest('hex');
86
+
87
+ // 3. Write to .tmp
88
+ writeFileSync(tmp, content, 'utf-8');
89
+
90
+ // 4. fsync — flush kernel buffers to disk (task 3.2.2)
91
+ // Open with 'r+' (read-write) to guarantee data buffer flush on all platforms.
92
+ // Opening with 'r' (read-only) may only flush metadata on some systems.
93
+ const fd = openSync(tmp, 'r+');
94
+ try {
95
+ fsyncSync(fd);
96
+ } finally {
97
+ closeSync(fd);
98
+ }
99
+
100
+ // 5. Atomic rename — os.replace() equivalent (task 3.2.2)
101
+ renameSync(tmp, path);
102
+
103
+ return { success: true, path, sha256, bytesWritten: Buffer.byteLength(content) };
104
+ } catch (err) {
105
+ // 6. Clean up .tmp on failure — never leave partial state
106
+ try { if (existsSync(tmp)) unlinkSync(tmp); } catch { /* best effort */ }
107
+ throw err;
108
+ } finally {
109
+ writeLocked = false;
110
+ }
111
+ }
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // State read + integrity verification
115
+ // ---------------------------------------------------------------------------
116
+
117
+ /**
118
+ * Read state from a JSON file and compute its SHA-256 checksum.
119
+ * Used by SubagentStart hook, cycle_counter_check, write-lease init.
120
+ */
121
+ export function stateRead(path: string): StateReadResult {
122
+ const content = readFileSync(path, 'utf-8');
123
+ const sha256 = createHash('sha256').update(content).digest('hex');
124
+ return { state: JSON.parse(content) as Record<string, unknown>, sha256 };
125
+ }
126
+
127
+ /**
128
+ * Verify integrity of a state file against an expected SHA-256 checksum.
129
+ * Returns true if the file content matches the expected hash.
130
+ *
131
+ * Use case: crash recovery — confirm .build-state.json wasn't corrupted
132
+ * by a partial write (the atomic protocol prevents this, but defense-in-depth).
133
+ */
134
+ export function verifyIntegrity(path: string, expectedSha256: string): boolean {
135
+ const content = readFileSync(path, 'utf-8');
136
+ const actual = createHash('sha256').update(content).digest('hex');
137
+ return actual === expectedSha256;
138
+ }
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Test helpers
142
+ // ---------------------------------------------------------------------------
143
+
144
+ /**
145
+ * Reset internal state (write lock). For testing only.
146
+ */
147
+ export function reset(): void {
148
+ writeLocked = false;
149
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Write-lease MCP handler (A5).
3
+ * Prevents intra-phase file collisions by requiring implementer dispatches
4
+ * to acquire exclusive leases on file paths before Write|Edit operations.
5
+ *
6
+ * Leases are persisted atomically to .build-state.json.active_write_leases[]
7
+ * so the PreToolUse hook (which re-reads from disk) sees them.
8
+ */
9
+
10
+ import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
11
+
12
+ export interface Lease {
13
+ holder: string; // task_id of the lease holder
14
+ paths: string[]; // file paths under lease
15
+ acquired_at: string; // ISO timestamp
16
+ }
17
+
18
+ export interface LeaseConflict {
19
+ holder: string; // task_id of the existing lease holder
20
+ paths: string[]; // overlapping file paths
21
+ }
22
+
23
+ export interface AcquireResult {
24
+ granted: boolean;
25
+ lease?: Lease;
26
+ conflict?: LeaseConflict;
27
+ }
28
+
29
+ /** In-memory lease store — kept in sync with disk via persistLeases(). */
30
+ const leases: Lease[] = [];
31
+
32
+ /** Path to .build-state.json — set via init() or defaults to docs/plans/.build-state.json */
33
+ let statePath = 'docs/plans/.build-state.json';
34
+
35
+ /**
36
+ * Initialize the lease manager with the state file path.
37
+ * Loads existing leases from disk into memory.
38
+ */
39
+ export function init(buildStatePath: string): void {
40
+ statePath = buildStatePath;
41
+ leases.length = 0;
42
+ if (!existsSync(statePath)) return;
43
+ try {
44
+ const state = JSON.parse(readFileSync(statePath, 'utf-8'));
45
+ const diskLeases = state?.active_write_leases;
46
+ if (Array.isArray(diskLeases)) {
47
+ for (const l of diskLeases) {
48
+ if (l?.holder && Array.isArray(l?.paths)) {
49
+ leases.push({ holder: l.holder, paths: l.paths, acquired_at: l.acquired_at ?? '' });
50
+ }
51
+ }
52
+ }
53
+ } catch { /* fresh state or parse error — start with empty leases */ }
54
+ }
55
+
56
+ /**
57
+ * Persist current leases to .build-state.json atomically.
58
+ * Uses write-to-.tmp + rename (same protocol as state_save MCP).
59
+ *
60
+ * CRITICAL: If persist fails, the in-memory lease exists but the PreToolUse
61
+ * hook (a separate process) reads from disk and won't see it — creating a
62
+ * silent fail-closed where legitimate writes are denied. On persist failure,
63
+ * we roll back the in-memory state to match disk and re-throw so the caller
64
+ * knows the acquire didn't stick.
65
+ */
66
+ function persistLeases(): void {
67
+ if (!existsSync(statePath)) return;
68
+ try {
69
+ const state = JSON.parse(readFileSync(statePath, 'utf-8'));
70
+ state.active_write_leases = leases.map(l => ({ ...l }));
71
+ const tmp = `${statePath}.tmp`;
72
+ writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', 'utf-8');
73
+ renameSync(tmp, statePath);
74
+ } catch (err) {
75
+ // Persist failed — roll back in-memory state to match what's on disk,
76
+ // then re-throw. This prevents the MCP thinking a lease exists while
77
+ // the hook (reading disk) sees nothing and denies writes.
78
+ try {
79
+ const diskState = JSON.parse(readFileSync(statePath, 'utf-8'));
80
+ const diskLeases = diskState?.active_write_leases;
81
+ leases.length = 0;
82
+ if (Array.isArray(diskLeases)) {
83
+ for (const l of diskLeases) {
84
+ if (l?.holder && Array.isArray(l?.paths)) {
85
+ leases.push({ holder: l.holder, paths: l.paths, acquired_at: l.acquired_at ?? '' });
86
+ }
87
+ }
88
+ }
89
+ } catch { /* disk unreadable — empty leases is the safest state */ leases.length = 0; }
90
+ throw new Error(`write-lease: persist failed, in-memory rolled back: ${err instanceof Error ? err.message : err}`);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Acquire a write lease for the given file paths.
96
+ * Persists to disk atomically so the PreToolUse hook sees the lease.
97
+ */
98
+ export function acquireWriteLease(taskId: string, filePaths: string[]): AcquireResult {
99
+ if (!taskId) throw new Error('task_id is required');
100
+ if (!filePaths.length) throw new Error('file_paths must be non-empty');
101
+
102
+ for (const existing of leases) {
103
+ const overlap = filePaths.filter(p => existing.paths.includes(p));
104
+ if (overlap.length > 0) {
105
+ return { granted: false, conflict: { holder: existing.holder, paths: overlap } };
106
+ }
107
+ }
108
+
109
+ const lease: Lease = { holder: taskId, paths: [...filePaths], acquired_at: new Date().toISOString() };
110
+ leases.push(lease);
111
+ persistLeases();
112
+ return { granted: true, lease };
113
+ }
114
+
115
+ /**
116
+ * Release ALL leases held by a task (handles multiple leases per task).
117
+ * Persists to disk. Called by SubagentStop hook on dispatch return.
118
+ */
119
+ export function releaseLease(taskId: string): boolean {
120
+ let released = false;
121
+ for (let i = leases.length - 1; i >= 0; i--) {
122
+ if (leases[i].holder === taskId) {
123
+ leases.splice(i, 1);
124
+ released = true;
125
+ }
126
+ }
127
+ if (released) persistLeases();
128
+ return released;
129
+ }
130
+
131
+ /**
132
+ * Release specific paths for a task. Persists to disk.
133
+ */
134
+ export function releasePathsForTask(taskId: string, paths: string[]): void {
135
+ const lease = leases.find(l => l.holder === taskId);
136
+ if (!lease) return;
137
+ lease.paths = lease.paths.filter(p => !paths.includes(p));
138
+ if (lease.paths.length === 0) {
139
+ releaseLease(taskId);
140
+ } else {
141
+ persistLeases();
142
+ }
143
+ }
144
+
145
+ /** Get all active leases. */
146
+ export function getActiveLeases(): readonly Lease[] {
147
+ return leases;
148
+ }
149
+
150
+ /**
151
+ * Check if a specific file path is leased and by whom.
152
+ * Used by the writer-owner PreToolUse hook extension.
153
+ */
154
+ export function checkPathLease(filePath: string, callerTaskId: string): { allowed: boolean; conflict?: LeaseConflict } {
155
+ for (const lease of leases) {
156
+ if (lease.paths.includes(filePath)) {
157
+ if (lease.holder === callerTaskId) return { allowed: true };
158
+ return { allowed: false, conflict: { holder: lease.holder, paths: [filePath] } };
159
+ }
160
+ }
161
+ return { allowed: false, conflict: undefined };
162
+ }
163
+
164
+ /** Reset all leases (for testing). */
165
+ export function reset(): void {
166
+ leases.length = 0;
167
+ }
@@ -0,0 +1,41 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ export interface SprintContextInput {
4
+ buildState: Record<string, unknown>;
5
+ refs: Record<string, unknown>;
6
+ architecture: string;
7
+ qualityTargets: Record<string, unknown>;
8
+ iosFeatures?: string[];
9
+ }
10
+
11
+ export interface SprintContextBlock {
12
+ content: string;
13
+ hash: string;
14
+ }
15
+
16
+ /**
17
+ * Render the sprint-scoped shared-context block for Phase 4 dispatches.
18
+ * Injected once per sprint via SubagentStart hook; hash-cached for reuse.
19
+ */
20
+ export function renderSprintContext(input: SprintContextInput): SprintContextBlock {
21
+ const sections = [
22
+ `## Architecture Snapshot\n${input.architecture}`,
23
+ `## Quality Targets\n${JSON.stringify(input.qualityTargets, null, 2)}`,
24
+ `## Refs Index\n${JSON.stringify(input.refs, null, 2)}`,
25
+ ];
26
+ if (input.iosFeatures?.length) {
27
+ sections.push(`## iOS Features\n${input.iosFeatures.join(', ')}`);
28
+ }
29
+ const content = sections.join('\n\n');
30
+ const hash = createHash('sha256').update(content).digest('hex').slice(0, 16);
31
+ return { content, hash };
32
+ }
33
+
34
+ /**
35
+ * Check if the sprint context needs re-rendering (hash invalidation).
36
+ * Returns true if refs have changed since last render.
37
+ */
38
+ export function shouldInvalidate(currentHash: string, newInput: SprintContextInput): boolean {
39
+ const newBlock = renderSprintContext(newInput);
40
+ return newBlock.hash !== currentHash;
41
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Schema for the in_flight_backward_edge field in .build-state.json.
3
+ * Added at Stage 4 (A3 crash-seam fix).
4
+ *
5
+ * When a backward-routing edge is dispatched, this field is written atomically
6
+ * alongside the counter increment. The target phase clears it on re-entry.
7
+ * If --resume finds this field with age > 60s, the counter is decremented
8
+ * (the edge never started).
9
+ */
10
+
11
+ export interface InFlightBackwardEdge {
12
+ /** The decision ID that triggered the backward route */
13
+ decision_id: string;
14
+ /** The phase being routed back to */
15
+ target_phase: string;
16
+ /** The counter value at the time of dispatch */
17
+ counter_value: number;
18
+ /** ISO timestamp when the edge was dispatched */
19
+ started_at: string;
20
+ }
21
+
22
+ /** Staleness threshold in milliseconds — edges older than this are considered crashed */
23
+ export const STALE_EDGE_THRESHOLD_MS = 60_000;
24
+
25
+ /**
26
+ * Check if an in-flight edge is stale (crash recovery).
27
+ * If stale, the counter should be decremented because the edge never completed.
28
+ */
29
+ export function isStaleEdge(edge: InFlightBackwardEdge): boolean {
30
+ const age = Date.now() - new Date(edge.started_at).getTime();
31
+ return age > STALE_EDGE_THRESHOLD_MS;
32
+ }
33
+
34
+ /**
35
+ * Schema for the per-target-phase backward routing counter (A6).
36
+ * Tracks how many times each target phase has been routed to,
37
+ * regardless of which decision triggered it.
38
+ */
39
+ export interface BackwardRoutingCounters {
40
+ /** Per-decision counter: decision_id -> count */
41
+ backward_routing_count: Record<string, number>;
42
+ /** Per-target-phase counter: phase -> count */
43
+ backward_routing_count_by_target_phase: Record<string, number>;
44
+ /** In-flight edge, if any */
45
+ in_flight_backward_edge?: InFlightBackwardEdge;
46
+ }