@bonesofspring/ai-rules 0.2.23 → 0.2.24

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 (285) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +5 -1
  3. package/bin/cli.js +32 -9
  4. package/bin/opencode-install.js +531 -0
  5. package/package.json +10 -3
  6. package/presets/_shared/core/agent-team/agent-team-orchestrator.md +2 -2
  7. package/presets/_shared/core/meta/preset-layering.md +3 -3
  8. package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +1 -1
  9. package/presets/_shared/core/meta/preset-pr-checklist.md +4 -3
  10. package/presets/_shared/core/meta/preset-token-budget.md +3 -0
  11. package/presets/_shared/core/meta/preset-twin-sync.md +16 -0
  12. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-layering.md +3 -3
  13. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  14. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  15. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-token-budget.md +3 -0
  16. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +16 -0
  17. package/presets/claude/go/rules/tooling-and-review/preset-layering.md +3 -3
  18. package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  19. package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  20. package/presets/claude/go/rules/tooling-and-review/preset-token-budget.md +3 -0
  21. package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +16 -0
  22. package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +3 -3
  23. package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  24. package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  25. package/presets/claude/ios-swift/rules/tooling-and-review/preset-token-budget.md +3 -0
  26. package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +16 -0
  27. package/presets/claude/java/rules/tooling-and-review/preset-layering.md +3 -3
  28. package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  29. package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  30. package/presets/claude/java/rules/tooling-and-review/preset-token-budget.md +3 -0
  31. package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +16 -0
  32. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +3 -3
  33. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  34. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  35. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-token-budget.md +3 -0
  36. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +16 -0
  37. package/presets/claude/next/REPO_AGENTS.md +10 -5
  38. package/presets/claude/next/agents/build-verifier.md +3 -1
  39. package/presets/claude/next/rules/tooling-and-review/ai-local-overlay.md +1 -1
  40. package/presets/claude/next/rules/tooling-and-review/preset-layering.md +3 -3
  41. package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  42. package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  43. package/presets/claude/next/rules/tooling-and-review/preset-token-budget.md +3 -0
  44. package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +16 -0
  45. package/presets/claude/nuxt/rules/tooling-and-review/preset-layering.md +3 -3
  46. package/presets/claude/nuxt/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  47. package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  48. package/presets/claude/nuxt/rules/tooling-and-review/preset-token-budget.md +3 -0
  49. package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +16 -0
  50. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +3 -3
  51. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  52. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  53. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +3 -0
  54. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +16 -0
  55. package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +3 -3
  56. package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  57. package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  58. package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +3 -0
  59. package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +16 -0
  60. package/presets/claude/svelte/rules/tooling-and-review/preset-layering.md +3 -3
  61. package/presets/claude/svelte/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  62. package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  63. package/presets/claude/svelte/rules/tooling-and-review/preset-token-budget.md +3 -0
  64. package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +16 -0
  65. package/presets/cursor/android-kotlin/rules/preset-layering.mdc +3 -3
  66. package/presets/cursor/android-kotlin/rules/preset-no-cross-stack-leakage.mdc +1 -1
  67. package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +4 -3
  68. package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
  69. package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +16 -0
  70. package/presets/cursor/go/rules/preset-layering.mdc +3 -3
  71. package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +1 -1
  72. package/presets/cursor/go/rules/preset-pr-checklist.mdc +4 -3
  73. package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
  74. package/presets/cursor/go/rules/preset-twin-sync.mdc +16 -0
  75. package/presets/cursor/ios-swift/rules/preset-layering.mdc +3 -3
  76. package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +1 -1
  77. package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +4 -3
  78. package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
  79. package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +16 -0
  80. package/presets/cursor/java/rules/preset-layering.mdc +3 -3
  81. package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +1 -1
  82. package/presets/cursor/java/rules/preset-pr-checklist.mdc +4 -3
  83. package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
  84. package/presets/cursor/java/rules/preset-twin-sync.mdc +16 -0
  85. package/presets/cursor/mcp-ts/rules/preset-layering.mdc +3 -3
  86. package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +1 -1
  87. package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +4 -3
  88. package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
  89. package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +16 -0
  90. package/presets/cursor/next/REPO_AGENTS.md +10 -5
  91. package/presets/cursor/next/agents/build-verifier.md +3 -1
  92. package/presets/cursor/next/rules/ai-local-overlay.mdc +1 -1
  93. package/presets/cursor/next/rules/preset-layering.mdc +3 -3
  94. package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +1 -1
  95. package/presets/cursor/next/rules/preset-pr-checklist.mdc +4 -3
  96. package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
  97. package/presets/cursor/next/rules/preset-twin-sync.mdc +16 -0
  98. package/presets/cursor/nuxt/rules/preset-layering.mdc +3 -3
  99. package/presets/cursor/nuxt/rules/preset-no-cross-stack-leakage.mdc +1 -1
  100. package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +4 -3
  101. package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
  102. package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +16 -0
  103. package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +3 -3
  104. package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +1 -1
  105. package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +4 -3
  106. package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
  107. package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +16 -0
  108. package/presets/cursor/php-laravel/rules/preset-layering.mdc +3 -3
  109. package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +1 -1
  110. package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +4 -3
  111. package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
  112. package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +16 -0
  113. package/presets/cursor/svelte/rules/preset-layering.mdc +3 -3
  114. package/presets/cursor/svelte/rules/preset-no-cross-stack-leakage.mdc +1 -1
  115. package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +4 -3
  116. package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
  117. package/presets/cursor/svelte/rules/preset-twin-sync.mdc +16 -0
  118. package/presets/mavis/next/REPO_AGENTS.md +9 -7
  119. package/presets/mavis/next/adapter-map.json +1 -1
  120. package/presets/mavis/next/agents/build-verifier.md +3 -1
  121. package/presets/mavis/next/rules/agent-team-orchestrator.md +2 -2
  122. package/presets/mavis/next/rules/ai-local-overlay.md +1 -1
  123. package/presets/mavis/next/rules/preset-layering.md +3 -3
  124. package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +1 -1
  125. package/presets/mavis/next/rules/preset-pr-checklist.md +4 -3
  126. package/presets/mavis/next/rules/preset-token-budget.md +3 -0
  127. package/presets/mavis/next/rules/preset-twin-sync.md +16 -0
  128. package/presets/opencode/README.md +61 -0
  129. package/presets/opencode/next/MCP.md +36 -0
  130. package/presets/opencode/next/OPENCODE.md +65 -0
  131. package/presets/opencode/next/README.md +16 -0
  132. package/presets/opencode/next/REPO_AGENTS.md +97 -0
  133. package/presets/opencode/next/adapter-map.json +959 -0
  134. package/presets/opencode/next/agents/README.md +201 -0
  135. package/presets/opencode/next/agents/accessibility-reviewer.md +73 -0
  136. package/presets/opencode/next/agents/api-contract-reviewer.md +69 -0
  137. package/presets/opencode/next/agents/build-verifier.md +124 -0
  138. package/presets/opencode/next/agents/ci-investigator.md +71 -0
  139. package/presets/opencode/next/agents/code-reviewer.md +73 -0
  140. package/presets/opencode/next/agents/codebase-analyzer.md +33 -0
  141. package/presets/opencode/next/agents/debugger.md +88 -0
  142. package/presets/opencode/next/agents/feature-developer.md +109 -0
  143. package/presets/opencode/next/agents/migration-specialist.md +79 -0
  144. package/presets/opencode/next/agents/mockup-reviewer.md +65 -0
  145. package/presets/opencode/next/agents/openspec-author.md +169 -0
  146. package/presets/opencode/next/agents/performance-auditor.md +80 -0
  147. package/presets/opencode/next/agents/playwright-test-generator.md +36 -0
  148. package/presets/opencode/next/agents/playwright-test-healer.md +36 -0
  149. package/presets/opencode/next/agents/playwright-test-planner.md +33 -0
  150. package/presets/opencode/next/agents/qa-tester.md +60 -0
  151. package/presets/opencode/next/agents/security-reviewer.md +66 -0
  152. package/presets/opencode/next/agents/solution-architect.md +80 -0
  153. package/presets/opencode/next/agents/task-analyst.md +178 -0
  154. package/presets/opencode/next/agents/task-router.md +190 -0
  155. package/presets/opencode/next/agents/tech-writer.md +69 -0
  156. package/presets/opencode/next/agents/unit-test-generator.md +39 -0
  157. package/presets/opencode/next/agents/unit-test-healer.md +40 -0
  158. package/presets/opencode/next/agents/unit-test-planner.md +66 -0
  159. package/presets/opencode/next/commands/README.md +49 -0
  160. package/presets/opencode/next/commands/spec-start.md +32 -0
  161. package/presets/opencode/next/commands/task-continue.md +58 -0
  162. package/presets/opencode/next/commands/task.md +58 -0
  163. package/presets/opencode/next/commands/technical-retro.md +88 -0
  164. package/presets/opencode/next/hooks/README.md +22 -0
  165. package/presets/opencode/next/hooks/chain-team-phases.sh +845 -0
  166. package/presets/opencode/next/opencode.json +33 -0
  167. package/presets/opencode/next/rules/README.md +20 -0
  168. package/presets/opencode/next/rules/agent-team-intake.md +11 -0
  169. package/presets/opencode/next/rules/agent-team-orchestrator.md +214 -0
  170. package/presets/opencode/next/rules/ai-local-overlay.md +37 -0
  171. package/presets/opencode/next/rules/anti-sycophancy-discipline.md +23 -0
  172. package/presets/opencode/next/rules/api-services.md +72 -0
  173. package/presets/opencode/next/rules/architecture-boundaries-ui.md +9 -0
  174. package/presets/opencode/next/rules/architecture-boundaries.md +86 -0
  175. package/presets/opencode/next/rules/arrow-functions.md +25 -0
  176. package/presets/opencode/next/rules/code-quality-and-refactoring.md +40 -0
  177. package/presets/opencode/next/rules/code-review-mr.md +40 -0
  178. package/presets/opencode/next/rules/css-property-order-stylelint.md +5 -0
  179. package/presets/opencode/next/rules/design-guidance.md +99 -0
  180. package/presets/opencode/next/rules/feature-delivery-workflow.md +49 -0
  181. package/presets/opencode/next/rules/http-client.md +55 -0
  182. package/presets/opencode/next/rules/layer-barrel-exports.md +68 -0
  183. package/presets/opencode/next/rules/mcp-usage.md +86 -0
  184. package/presets/opencode/next/rules/mockup-review.md +107 -0
  185. package/presets/opencode/next/rules/navigation-router-stack.md +56 -0
  186. package/presets/opencode/next/rules/navigation-router-ui.md +10 -0
  187. package/presets/opencode/next/rules/next-app-core.md +29 -0
  188. package/presets/opencode/next/rules/next-app-router.md +44 -0
  189. package/presets/opencode/next/rules/no-props-spread.md +56 -0
  190. package/presets/opencode/next/rules/no-type-assertion-as-import-export.md +51 -0
  191. package/presets/opencode/next/rules/package-manager.md +11 -0
  192. package/presets/opencode/next/rules/playwright-agents.md +67 -0
  193. package/presets/opencode/next/rules/post-change-lint.md +35 -0
  194. package/presets/opencode/next/rules/preset-layering.md +27 -0
  195. package/presets/opencode/next/rules/preset-no-cross-stack-leakage.md +90 -0
  196. package/presets/opencode/next/rules/preset-pr-checklist.md +36 -0
  197. package/presets/opencode/next/rules/preset-token-budget.md +39 -0
  198. package/presets/opencode/next/rules/preset-twin-sync.md +81 -0
  199. package/presets/opencode/next/rules/product-specs-authoring.md +112 -0
  200. package/presets/opencode/next/rules/product-specs.md +56 -0
  201. package/presets/opencode/next/rules/public-imports.md +50 -0
  202. package/presets/opencode/next/rules/react-a11y-coding.md +31 -0
  203. package/presets/opencode/next/rules/react-next-anti-hallucination.md +60 -0
  204. package/presets/opencode/next/rules/react-ui.md +105 -0
  205. package/presets/opencode/next/rules/reference-features.md +45 -0
  206. package/presets/opencode/next/rules/security-next.md +45 -0
  207. package/presets/opencode/next/rules/store-rtk.md +78 -0
  208. package/presets/opencode/next/rules/technical-retro.md +11 -0
  209. package/presets/opencode/next/rules/tests-e2e-structure.md +72 -0
  210. package/presets/opencode/next/rules/tests-unit.md +72 -0
  211. package/presets/opencode/next/rules/types-jsdoc.md +43 -0
  212. package/presets/opencode/next/rules/ux-design.md +99 -0
  213. package/presets/opencode/next/skills/README.md +21 -0
  214. package/presets/opencode/next/skills/ci-investigation/SKILL.md +44 -0
  215. package/presets/opencode/next/skills/code-review/SKILL.md +44 -0
  216. package/presets/opencode/next/skills/debug-investigation/SKILL.md +59 -0
  217. package/presets/opencode/next/skills/feature-delivery/SKILL.md +39 -0
  218. package/presets/opencode/next/skills/mockup-review/SKILL.md +29 -0
  219. package/presets/opencode/next/skills/playwright-e2e/SKILL.md +33 -0
  220. package/presets/opencode/next/skills/react-performance/SKILL.md +44 -0
  221. package/presets/opencode/next/skills/spec-authoring/SKILL.md +26 -0
  222. package/presets/opencode/next/skills/spec-discovery/SKILL.md +58 -0
  223. package/presets/opencode/next/skills/technical-retro/SKILL.md +54 -0
  224. package/presets/opencode/next/skills/unit-testing/SKILL.md +34 -0
  225. package/presets/opencode/next/skills/ux-design/SKILL.md +36 -0
  226. package/presets/opencode/next/skills/write-adr/SKILL.md +41 -0
  227. package/presets/opencode/next/team/README.md +148 -0
  228. package/presets/opencode/next/team/conventions.md +21 -0
  229. package/presets/opencode/next/team/fixtures/artifact-manifest.example.json +79 -0
  230. package/presets/opencode/next/team/fixtures/bugfix-standard.json +25 -0
  231. package/presets/opencode/next/team/fixtures/feature-full.json +29 -0
  232. package/presets/opencode/next/team/fixtures/feature-light.json +27 -0
  233. package/presets/opencode/next/team/fixtures/feature-with-figma-url.json +17 -0
  234. package/presets/opencode/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  235. package/presets/opencode/next/team/fixtures/memlog.example.md +9 -0
  236. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  237. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  238. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  239. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  240. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  241. package/presets/opencode/next/team/fixtures/metrics.example.json +54 -0
  242. package/presets/opencode/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  243. package/presets/opencode/next/team/fixtures/mockup-review-standalone.json +14 -0
  244. package/presets/opencode/next/team/fixtures/ux-design-standalone.json +15 -0
  245. package/presets/opencode/next/team/tasks/.gitkeep +0 -0
  246. package/presets/opencode/platform-contract.json +131 -0
  247. package/scripts/capture-cli-golden.mjs +5 -1
  248. package/scripts/check-chain-hook-coverage.sh +1 -0
  249. package/scripts/check-opencode-adapter-map.mjs +294 -0
  250. package/scripts/check-opencode-local-overlay.mjs +56 -0
  251. package/scripts/check-opencode-platform-contract.mjs +388 -0
  252. package/scripts/check-opencode-token-budget.mjs +48 -0
  253. package/scripts/check-preset-leakage.sh +30 -1
  254. package/scripts/check-preset-structure.sh +45 -0
  255. package/scripts/check-task-router-intents.sh +4 -4
  256. package/scripts/fixtures/cli-golden/claude/next.json +8 -8
  257. package/scripts/fixtures/cli-golden/cursor/next.json +8 -8
  258. package/scripts/fixtures/cli-golden/mavis/next.json +9 -9
  259. package/scripts/fixtures/cli-golden/opencode/next.json +125 -0
  260. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  261. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  262. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  263. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/OPENCODE.md +1 -0
  264. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/adapter-map.json +26 -0
  265. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/rules/sample.md +2 -0
  266. package/scripts/fixtures/opencode-adapter-map/valid/cursor/AGENTS.md +1 -0
  267. package/scripts/fixtures/opencode-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  268. package/scripts/fixtures/opencode-adapter-map/valid/opencode/OPENCODE.md +1 -0
  269. package/scripts/fixtures/opencode-adapter-map/valid/opencode/adapter-map.json +26 -0
  270. package/scripts/fixtures/opencode-adapter-map/valid/opencode/rules/sample.md +2 -0
  271. package/scripts/fixtures/opencode-platform-contract/invalid-home-destination.json +131 -0
  272. package/scripts/fixtures/opencode-platform-contract/invalid-missing-capability.json +126 -0
  273. package/scripts/fixtures/opencode-platform-contract/invalid-released-unknown-stack.json +133 -0
  274. package/scripts/fixtures/opencode-platform-contract/invalid-unknown-native-path.json +131 -0
  275. package/scripts/fixtures/opencode-platform-contract/invalid-version-pin.json +132 -0
  276. package/scripts/fixtures/opencode-platform-contract/valid.json +131 -0
  277. package/scripts/generate-mavis-next.mjs +9 -7
  278. package/scripts/generate-opencode-next.mjs +831 -0
  279. package/scripts/lib/discover-hooks.mjs +23 -1
  280. package/scripts/sync-dogfood-mavis.sh +90 -0
  281. package/scripts/sync-dogfood-opencode.sh +85 -0
  282. package/scripts/test-agent-task-metrics-hooks.mjs +4 -1
  283. package/scripts/test-chain-team-phases-coverage.mjs +6 -1
  284. package/scripts/test-cli-init-clean.mjs +111 -0
  285. package/scripts/test-opencode-context-budget.mjs +53 -0
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Validate presets/opencode/platform-contract.json.
4
+ *
5
+ * Usage:
6
+ * node scripts/check-opencode-platform-contract.mjs
7
+ * node scripts/check-opencode-platform-contract.mjs --contract <path>
8
+ * node scripts/check-opencode-platform-contract.mjs --self-test
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
15
+ const packageRoot = path.join(scriptDirectory, '..');
16
+ const defaultContractPath = path.join(
17
+ packageRoot,
18
+ 'presets',
19
+ 'opencode',
20
+ 'platform-contract.json',
21
+ );
22
+ const fixtureDirectory = path.join(
23
+ scriptDirectory,
24
+ 'fixtures',
25
+ 'opencode-platform-contract',
26
+ );
27
+
28
+ const ALLOWED_STATUS = new Set([
29
+ 'confirmed',
30
+ 'partial',
31
+ 'unknown',
32
+ 'no-equivalent',
33
+ ]);
34
+ const KNOWN_STACKS = new Set([
35
+ 'next',
36
+ 'nuxt',
37
+ 'svelte',
38
+ 'ios-swift',
39
+ 'android-kotlin',
40
+ 'go',
41
+ 'java',
42
+ 'mcp-ts',
43
+ 'php-hexagonal',
44
+ 'php-laravel',
45
+ ]);
46
+ const REQUIRED_CAPABILITIES = [
47
+ 'cliVersion',
48
+ 'rootAgents',
49
+ 'explicitAgentFile',
50
+ 'structuredOutput',
51
+ 'referencedRule',
52
+ 'referencedSkill',
53
+ 'freshInit',
54
+ 'nativeProjectRules',
55
+ 'nativeWorkspaceSkills',
56
+ 'nativeWorkspaceAgents',
57
+ 'projectMcp',
58
+ 'projectHooks',
59
+ 'slashCommands',
60
+ 'pluginSkillMcp',
61
+ 'nativeTeam',
62
+ ];
63
+ const REQUIRED_ENTRY_PAIRS = [
64
+ { source: 'REPO_AGENTS.md', destination: 'AGENTS.md' },
65
+ { source: 'OPENCODE.md', destination: '.opencode/AGENTS.md' },
66
+ ];
67
+ const REQUIRED_DIRECTORY_SOURCES = [
68
+ 'rules',
69
+ 'agents',
70
+ 'skills',
71
+ 'commands',
72
+ 'hooks',
73
+ 'team',
74
+ ];
75
+ const FORBIDDEN_PATH_RE =
76
+ /(?:^|[\\/])\.config[\\/]opencode(?:[\\/]|$)|~\/\.config\/opencode|\$HOME\/\.config\/opencode|~\/\.opencode/i;
77
+ const VERSION_PIN_KEYS = new Set([
78
+ 'requiredCliVersion',
79
+ 'requiredDesktopVersion',
80
+ 'minCliVersion',
81
+ 'pinnedVersion',
82
+ 'requiredVersion',
83
+ ]);
84
+ const EVIDENCE_MARKER = 'OPENCODE-COMPATIBILITY.md';
85
+
86
+ export function validateContract(contract, options = {}) {
87
+ const failures = [];
88
+ const fail = (message) => {
89
+ failures.push(message);
90
+ };
91
+
92
+ if (contract === null || typeof contract !== 'object' || Array.isArray(contract)) {
93
+ return ['contract must be a JSON object'];
94
+ }
95
+
96
+ if (contract.schemaVersion !== 1) {
97
+ fail('schemaVersion must be 1');
98
+ }
99
+ if (contract.id !== 'opencode') {
100
+ fail('id must be "opencode"');
101
+ }
102
+ if (contract.runtimeExecutable !== 'opencode') {
103
+ fail('runtimeExecutable must be "opencode"');
104
+ }
105
+ if (contract.sourceRoot !== 'presets/opencode') {
106
+ fail('sourceRoot must be "presets/opencode"');
107
+ }
108
+ if (contract.managedRoot !== '.opencode') {
109
+ fail('managedRoot must be ".opencode"');
110
+ }
111
+ if (contract.cleanStrategy !== 'manifest-owned') {
112
+ fail('cleanStrategy must be "manifest-owned"');
113
+ }
114
+
115
+ for (const key of Object.keys(contract)) {
116
+ if (VERSION_PIN_KEYS.has(key)) {
117
+ fail(`forbidden version-pin key "${key}"`);
118
+ }
119
+ }
120
+
121
+ if (!Array.isArray(contract.releasedStacks)) {
122
+ fail('releasedStacks must be an array');
123
+ } else {
124
+ for (const stack of contract.releasedStacks) {
125
+ if (typeof stack !== 'string' || !KNOWN_STACKS.has(stack)) {
126
+ fail(`releasedStacks contains unknown stack "${stack}"`);
127
+ continue;
128
+ }
129
+ if (options.requireReleasedStackDir) {
130
+ const stackDir = path.join(packageRoot, 'presets', 'opencode', stack);
131
+ if (!fs.existsSync(stackDir)) {
132
+ fail(`released stack "${stack}" has no source directory ${stackDir}`);
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ if (!Array.isArray(contract.entryFiles)) {
139
+ fail('entryFiles must be an array');
140
+ } else {
141
+ const pairs = new Set(
142
+ contract.entryFiles.map((entry) => `${entry?.source}→${entry?.destination}`),
143
+ );
144
+ for (const required of REQUIRED_ENTRY_PAIRS) {
145
+ if (!pairs.has(`${required.source}→${required.destination}`)) {
146
+ fail(
147
+ `entryFiles must include ${required.source} → ${required.destination}`,
148
+ );
149
+ }
150
+ }
151
+ for (const [index, entry] of contract.entryFiles.entries()) {
152
+ if (!isSourceDestination(entry)) {
153
+ fail(`entryFiles[${index}] must have string source and destination`);
154
+ continue;
155
+ }
156
+ failIfForbiddenPath(fail, `entryFiles[${index}].destination`, entry.destination);
157
+ if (
158
+ entry.destination !== 'AGENTS.md' &&
159
+ !entry.destination.startsWith('.opencode/')
160
+ ) {
161
+ fail(
162
+ `entryFiles[${index}].destination must be AGENTS.md or under .opencode/`,
163
+ );
164
+ }
165
+ }
166
+ }
167
+
168
+ if (!Array.isArray(contract.directoryMappings)) {
169
+ fail('directoryMappings must be an array');
170
+ } else {
171
+ const sources = new Set(
172
+ contract.directoryMappings
173
+ .filter((entry) => entry && typeof entry.source === 'string')
174
+ .map((entry) => entry.source),
175
+ );
176
+ for (const source of REQUIRED_DIRECTORY_SOURCES) {
177
+ if (!sources.has(source)) {
178
+ fail(`directoryMappings must include source "${source}"`);
179
+ }
180
+ }
181
+ for (const [index, entry] of contract.directoryMappings.entries()) {
182
+ if (!isSourceDestination(entry)) {
183
+ fail(
184
+ `directoryMappings[${index}] must have string source and destination`,
185
+ );
186
+ continue;
187
+ }
188
+ failIfForbiddenPath(
189
+ fail,
190
+ `directoryMappings[${index}].destination`,
191
+ entry.destination,
192
+ );
193
+ if (!entry.destination.startsWith('.opencode/')) {
194
+ fail(
195
+ `directoryMappings[${index}].destination must start with .opencode/`,
196
+ );
197
+ }
198
+ }
199
+ }
200
+
201
+ const mcp = contract.mcp;
202
+ if (mcp === null || typeof mcp !== 'object' || Array.isArray(mcp)) {
203
+ fail('mcp must be an object');
204
+ } else {
205
+ if (mcp.source !== 'opencode.json') {
206
+ fail('mcp.source must be "opencode.json"');
207
+ }
208
+ if (mcp.fallbackDestination !== 'opencode.json') {
209
+ fail('mcp.fallbackDestination must be "opencode.json"');
210
+ }
211
+ if (mcp.mode !== 'merge-opencode-json') {
212
+ fail('mcp.mode must be "merge-opencode-json"');
213
+ }
214
+ failIfForbiddenPath(fail, 'mcp.fallbackDestination', mcp.fallbackDestination);
215
+ }
216
+
217
+ const docsSeed = contract.docsSeed;
218
+ if (docsSeed === null || typeof docsSeed !== 'object' || Array.isArray(docsSeed)) {
219
+ fail('docsSeed must be an object');
220
+ } else {
221
+ if (docsSeed.source !== 'presets/_shared/assets/docs-specs') {
222
+ fail('docsSeed.source must be "presets/_shared/assets/docs-specs"');
223
+ }
224
+ if (docsSeed.behavior !== 'absent-only') {
225
+ fail('docsSeed.behavior must be "absent-only"');
226
+ }
227
+ }
228
+
229
+ const capabilities = contract.nativeCapabilities;
230
+ if (
231
+ capabilities === null ||
232
+ typeof capabilities !== 'object' ||
233
+ Array.isArray(capabilities)
234
+ ) {
235
+ fail('nativeCapabilities must be an object');
236
+ } else {
237
+ for (const key of REQUIRED_CAPABILITIES) {
238
+ if (!Object.hasOwn(capabilities, key)) {
239
+ fail(`nativeCapabilities missing required key "${key}"`);
240
+ }
241
+ }
242
+ for (const [key, capability] of Object.entries(capabilities)) {
243
+ if (capability === null || typeof capability !== 'object' || Array.isArray(capability)) {
244
+ fail(`nativeCapabilities.${key} must be an object`);
245
+ continue;
246
+ }
247
+ if (!ALLOWED_STATUS.has(capability.status)) {
248
+ fail(
249
+ `nativeCapabilities.${key}.status must be confirmed|partial|unknown|no-equivalent`,
250
+ );
251
+ }
252
+ if (
253
+ typeof capability.evidence !== 'string' ||
254
+ !capability.evidence.includes(EVIDENCE_MARKER)
255
+ ) {
256
+ fail(
257
+ `nativeCapabilities.${key}.evidence must reference ${EVIDENCE_MARKER}`,
258
+ );
259
+ }
260
+ const nativePath = capability.nativePath ?? null;
261
+ if (nativePath !== null && typeof nativePath !== 'string') {
262
+ fail(`nativeCapabilities.${key}.nativePath must be a string or null`);
263
+ continue;
264
+ }
265
+ if (
266
+ (capability.status === 'unknown' || capability.status === 'no-equivalent') &&
267
+ nativePath
268
+ ) {
269
+ fail(
270
+ `nativeCapabilities.${key} is ${capability.status} and cannot declare nativePath`,
271
+ );
272
+ }
273
+ if (nativePath) {
274
+ failIfForbiddenPath(fail, `nativeCapabilities.${key}.nativePath`, nativePath);
275
+ }
276
+ }
277
+ }
278
+
279
+ return failures;
280
+ }
281
+
282
+ function isSourceDestination(entry) {
283
+ return (
284
+ entry &&
285
+ typeof entry === 'object' &&
286
+ typeof entry.source === 'string' &&
287
+ entry.source.length > 0 &&
288
+ typeof entry.destination === 'string' &&
289
+ entry.destination.length > 0
290
+ );
291
+ }
292
+
293
+ function failIfForbiddenPath(fail, label, value) {
294
+ if (typeof value === 'string' && FORBIDDEN_PATH_RE.test(value)) {
295
+ fail(`${label} claims an OpenCode home path: ${value}`);
296
+ }
297
+ }
298
+
299
+ function readJson(filePath) {
300
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
301
+ }
302
+
303
+ function printFailures(label, failures) {
304
+ for (const failure of failures) {
305
+ console.error(`FAIL ${label}: ${failure}`);
306
+ }
307
+ }
308
+
309
+ function runSelfTest() {
310
+ const cases = [
311
+ { name: 'valid.json', expectFail: false },
312
+ { name: 'invalid-unknown-native-path.json', expectFail: true },
313
+ { name: 'invalid-home-destination.json', expectFail: true },
314
+ { name: 'invalid-missing-capability.json', expectFail: true },
315
+ { name: 'invalid-released-unknown-stack.json', expectFail: true },
316
+ { name: 'invalid-version-pin.json', expectFail: true },
317
+ ];
318
+ let failed = 0;
319
+ for (const testCase of cases) {
320
+ const filePath = path.join(fixtureDirectory, testCase.name);
321
+ const failures = validateContract(readJson(filePath));
322
+ const didFail = failures.length > 0;
323
+ if (didFail !== testCase.expectFail) {
324
+ failed += 1;
325
+ console.error(
326
+ `FAIL self-test ${testCase.name}: expected ${
327
+ testCase.expectFail ? 'invalid' : 'valid'
328
+ }, got ${didFail ? 'invalid' : 'valid'}`,
329
+ );
330
+ printFailures(testCase.name, failures);
331
+ } else {
332
+ console.log(
333
+ `ok self-test ${testCase.name} (${didFail ? 'invalid' : 'valid'})`,
334
+ );
335
+ }
336
+ }
337
+ return failed;
338
+ }
339
+
340
+ function parseArgs(argv) {
341
+ const options = { contract: defaultContractPath, selfTest: false };
342
+ for (let index = 0; index < argv.length; index += 1) {
343
+ const arg = argv[index];
344
+ if (arg === '--self-test') {
345
+ options.selfTest = true;
346
+ } else if (arg === '--contract') {
347
+ options.contract = argv[index + 1];
348
+ index += 1;
349
+ } else {
350
+ throw new Error(`unknown argument: ${arg}`);
351
+ }
352
+ }
353
+ return options;
354
+ }
355
+
356
+ function main() {
357
+ const options = parseArgs(process.argv.slice(2));
358
+ let exitCode = 0;
359
+
360
+ if (options.selfTest) {
361
+ exitCode = runSelfTest() === 0 ? 0 : 1;
362
+ }
363
+
364
+ if (!options.selfTest || exitCode === 0) {
365
+ const liveFailures = validateContract(readJson(options.contract), {
366
+ requireReleasedStackDir: options.contract === defaultContractPath,
367
+ });
368
+ if (liveFailures.length > 0) {
369
+ printFailures(path.relative(packageRoot, options.contract), liveFailures);
370
+ exitCode = 1;
371
+ } else if (!options.selfTest) {
372
+ console.log(`ok ${path.relative(packageRoot, options.contract)}`);
373
+ } else {
374
+ console.log('ok live platform-contract.json');
375
+ }
376
+ }
377
+
378
+ process.exit(exitCode);
379
+ }
380
+
381
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
382
+ try {
383
+ main();
384
+ } catch (error) {
385
+ console.error(`FAIL: ${error.message}`);
386
+ process.exit(1);
387
+ }
388
+ }
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Isolated OpenCode token-budget analog. Does not change Cursor/Claude 3/4 caps.
4
+ * Root-entry rules in adapter-map.json must stay ≤ 4.
5
+ */
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { loadReleasedOpencodeStacks } from './lib/discover-hooks.mjs';
10
+
11
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
12
+ const presetsRoot = path.join(scriptDirectory, '..', 'presets');
13
+ const ROOT_ENTRY_CAP = 4;
14
+
15
+ const stacks = loadReleasedOpencodeStacks(presetsRoot);
16
+ if (stacks.length === 0) {
17
+ console.log('ok opencode token-budget: no released stacks');
18
+ process.exit(0);
19
+ }
20
+
21
+ let failed = 0;
22
+ for (const stack of stacks) {
23
+ const mapPath = path.join(presetsRoot, 'opencode', stack, 'adapter-map.json');
24
+ if (!fs.existsSync(mapPath)) {
25
+ console.error(`FAIL opencode/${stack}: missing adapter-map.json`);
26
+ failed += 1;
27
+ continue;
28
+ }
29
+ const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
30
+ const rootRules = (map.entries || []).filter(
31
+ (entry) =>
32
+ entry.category === 'rules' &&
33
+ entry.loadMode === 'root-entry' &&
34
+ entry.target,
35
+ );
36
+ if (rootRules.length > ROOT_ENTRY_CAP) {
37
+ console.error(
38
+ `FAIL opencode/${stack}: ${rootRules.length} root-entry rules (cap ${ROOT_ENTRY_CAP})`,
39
+ );
40
+ failed += 1;
41
+ } else {
42
+ console.log(
43
+ `ok opencode/${stack}: ${rootRules.length} root-entry rules (cap ${ROOT_ENTRY_CAP})`,
44
+ );
45
+ }
46
+ }
47
+
48
+ process.exit(failed === 0 ? 0 : 1);
@@ -18,7 +18,7 @@ import sys
18
18
  from pathlib import Path
19
19
 
20
20
  presets = Path(os.environ["AI_RULES_PRESETS_ROOT"])
21
- PLATFORMS = ("cursor", "claude", "mavis")
21
+ PLATFORMS = ("cursor", "claude", "mavis", "opencode")
22
22
  STACKS = ("next", "nuxt", "svelte", "ios-swift", "android-kotlin", "go", "java", "mcp-ts", "php-hexagonal", "php-laravel")
23
23
 
24
24
 
@@ -33,6 +33,17 @@ def released_mavis_stacks() -> list[str]:
33
33
  return []
34
34
 
35
35
 
36
+ def released_opencode_stacks() -> list[str]:
37
+ contract = presets / "opencode" / "platform-contract.json"
38
+ if not contract.is_file():
39
+ return []
40
+ try:
41
+ data = json.loads(contract.read_text(encoding="utf-8"))
42
+ return list(data.get("releasedStacks") or [])
43
+ except json.JSONDecodeError:
44
+ return []
45
+
46
+
36
47
  NEGATION_RE = re.compile(
37
48
  r"(?i)\b("
38
49
  r"never|do\s+not|don't|must\s+not|forbidden|ban(?:ned)?|avoid|"
@@ -340,6 +351,8 @@ for stack in STACKS:
340
351
  platforms_for_stack = ["cursor", "claude"]
341
352
  if stack in released_mavis_stacks():
342
353
  platforms_for_stack.append("mavis")
354
+ if stack in released_opencode_stacks():
355
+ platforms_for_stack.append("opencode")
343
356
 
344
357
  for platform in platforms_for_stack:
345
358
  for script in iter_hook_scripts(stack, platform):
@@ -465,6 +478,22 @@ for stack in released_mavis_stacks():
465
478
  elif other.is_file():
466
479
  print(f"OK: [mavis/{stack}] chain ≠ [{platform}/{stack}] chain")
467
480
 
481
+ for stack in released_opencode_stacks():
482
+ opencode_chain = presets / "opencode" / stack / "hooks" / "chain-team-phases.sh"
483
+ if not opencode_chain.is_file():
484
+ print(f"FAIL: [opencode/{stack}] missing chain-team-phases.sh")
485
+ failed += 1
486
+ continue
487
+ for platform in ("cursor", "claude", "mavis"):
488
+ other = presets / platform / stack / "hooks" / "chain-team-phases.sh"
489
+ if other.is_file() and opencode_chain.read_bytes() == other.read_bytes():
490
+ print(
491
+ f"FAIL: [opencode/{stack}] chain is byte-identical to [{platform}/{stack}]"
492
+ )
493
+ failed += 1
494
+ elif other.is_file():
495
+ print(f"OK: [opencode/{stack}] chain ≠ [{platform}/{stack}] chain")
496
+
468
497
  print()
469
498
  print(f"WARN count: {warned} (build-verifier packaging refs / chain skips)")
470
499
  print(
@@ -29,6 +29,51 @@ run_gate() {
29
29
  echo
30
30
  }
31
31
 
32
+ echo ">>> opencode-platform-contract"
33
+ if node "$SCRIPT_DIR/check-opencode-platform-contract.mjs" --self-test; then
34
+ echo "<<< opencode-platform-contract: OK"
35
+ else
36
+ echo "<<< opencode-platform-contract: FAIL"
37
+ status=1
38
+ fi
39
+ echo
40
+
41
+ echo ">>> opencode-adapter-map"
42
+ if node "$SCRIPT_DIR/check-opencode-adapter-map.mjs" --self-test; then
43
+ echo "<<< opencode-adapter-map: OK"
44
+ else
45
+ echo "<<< opencode-adapter-map: FAIL"
46
+ status=1
47
+ fi
48
+ echo
49
+
50
+ echo ">>> opencode-token-budget"
51
+ if node "$SCRIPT_DIR/check-opencode-token-budget.mjs"; then
52
+ echo "<<< opencode-token-budget: OK"
53
+ else
54
+ echo "<<< opencode-token-budget: FAIL"
55
+ status=1
56
+ fi
57
+ echo
58
+
59
+ echo ">>> opencode-local-overlay"
60
+ if node "$SCRIPT_DIR/check-opencode-local-overlay.mjs"; then
61
+ echo "<<< opencode-local-overlay: OK"
62
+ else
63
+ echo "<<< opencode-local-overlay: FAIL"
64
+ status=1
65
+ fi
66
+ echo
67
+
68
+ echo ">>> opencode-context-budget"
69
+ if node "$SCRIPT_DIR/test-opencode-context-budget.mjs"; then
70
+ echo "<<< opencode-context-budget: OK"
71
+ else
72
+ echo "<<< opencode-context-budget: FAIL"
73
+ status=1
74
+ fi
75
+ echo
76
+
32
77
  echo ">>> mavis-platform-contract"
33
78
  if node "$SCRIPT_DIR/check-mavis-platform-contract.mjs" --self-test; then
34
79
  echo "<<< mavis-platform-contract: OK"
@@ -37,12 +37,12 @@ import sys
37
37
  from pathlib import Path
38
38
 
39
39
  presets = Path(os.environ["AI_RULES_PRESETS_ROOT"])
40
- PLATFORMS = ("cursor", "claude", "mavis")
40
+ PLATFORMS = ("cursor", "claude", "mavis", "opencode")
41
41
  STACKS = ("next", "nuxt", "svelte", "ios-swift", "android-kotlin", "go", "java", "mcp-ts", "php-hexagonal", "php-laravel")
42
42
 
43
43
 
44
- def released_mavis_stacks() -> list[str]:
45
- contract = presets / "mavis" / "platform-contract.json"
44
+ def released_contract_stacks(platform: str) -> list[str]:
45
+ contract = presets / platform / "platform-contract.json"
46
46
  if not contract.is_file():
47
47
  return []
48
48
  try:
@@ -71,7 +71,7 @@ failures: list[str] = []
71
71
  checks = 0
72
72
 
73
73
  for platform in PLATFORMS:
74
- stacks = STACKS if platform != "mavis" else released_mavis_stacks()
74
+ stacks = STACKS if platform not in {"mavis", "opencode"} else released_contract_stacks(platform)
75
75
  for stack in stacks:
76
76
  agents_dir = presets / platform / stack / "agents"
77
77
  if not agents_dir.is_dir():
@@ -8,7 +8,7 @@
8
8
  ".claude/agents/README.md": "10e8eac494611ad037fc2be118ed6f2e4ae3fca77a8f82171f5c0d3ae7f37249",
9
9
  ".claude/agents/accessibility-reviewer.md": "87030cc8a0c4ed7224b784655002c2efe0629ca39253dfa20379a64ad942c332",
10
10
  ".claude/agents/api-contract-reviewer.md": "930374aea3b8aae083ceceac7efc74034b231ed2337608f4b19afa563becb1c4",
11
- ".claude/agents/build-verifier.md": "f132292fdab6751df833eed5719c3fa35d223131d0c82b934174b3a5d05a51ed",
11
+ ".claude/agents/build-verifier.md": "709f5d1099d15356a9e74b9f465762dd038dda72a266eccdf3a5dc99d021d2a4",
12
12
  ".claude/agents/ci-investigator.md": "ea6b26e97498edd5696514bcea543bf88f47a1c123664a45dd6d3563f28ece8c",
13
13
  ".claude/agents/code-reviewer.md": "3bb4d752922b33ab741a5032d778c0e5d26f065fb4e8f67af9e31d9487f53366",
14
14
  ".claude/agents/codebase-analyzer.md": "cae5586fd9a5fc0ea12a7578c5a9f5a4c72229b1e5c4f1eeb9ff9b6b243becf0",
@@ -72,7 +72,7 @@
72
72
  ".claude/rules/tooling-and-review/README.md": "27a956fa7fa68c58cd17a9b598580b06cb2241aadef29e3b647a31ab418d99ec",
73
73
  ".claude/rules/tooling-and-review/agent-team-intake.md": "4880f2d91c221c5be3d75650973403402a4705d9bd66178e3ca4b163a3704ebc",
74
74
  ".claude/rules/tooling-and-review/agent-team-orchestrator.md": "95d7f915a03d6317f32d0de77879aee2b41db71c629c4baf18f36ad6316d7fe9",
75
- ".claude/rules/tooling-and-review/ai-local-overlay.md": "2b4c7db3c68a2645c96d35e682a6e6f61ecabca97cc009128af825724bbfe2fb",
75
+ ".claude/rules/tooling-and-review/ai-local-overlay.md": "beb18a5047085e0376797e5240156f0423027a02b2e66e3e24b59ef4433b9a06",
76
76
  ".claude/rules/tooling-and-review/anti-sycophancy-discipline.md": "6d359631dde6dbe6588397aa1108752cfdad9764d90d6d9da31606a463b72226",
77
77
  ".claude/rules/tooling-and-review/code-quality.md": "9aee7805102788d8a9d21504127f2fe94209d98abd1c880719097a42fb70bf94",
78
78
  ".claude/rules/tooling-and-review/code-review-mr.md": "2e2353585d9ce54b799463031e17fc7cf8d81b6dc1670e385d8f6065c53ce862",
@@ -81,11 +81,11 @@
81
81
  ".claude/rules/tooling-and-review/mockup-review.md": "6ace9a2be32595c2307bdb2d1c6b9952d3fa37e3ca8bb7b2398d86110fec7a8c",
82
82
  ".claude/rules/tooling-and-review/package-manager.md": "1e80459ac986a61eee45a6d0aef67177aa449279784848397c32ac0511b0c208",
83
83
  ".claude/rules/tooling-and-review/post-change-lint.md": "9fc3151e022866f7a14ca5bbfaa4b2889f7ea049cfde545633ba5daaf319398e",
84
- ".claude/rules/tooling-and-review/preset-layering.md": "f759db43eeb6e0d60f202bf8d039e876f9ef5e30d1d8347a72a27f359223c164",
85
- ".claude/rules/tooling-and-review/preset-no-cross-stack-leakage.md": "fd096345109e52f1f648d69c192b77d75da50f919bd5ca6c0f00170654657506",
86
- ".claude/rules/tooling-and-review/preset-pr-checklist.md": "eb8b58e17d9524c4606ee3484dbed575504b1ff1ed52abeb877557312ef7a43d",
87
- ".claude/rules/tooling-and-review/preset-token-budget.md": "1814407172ed4a88420406df13ca66af176a5a0c6282e37382d7ec0045506e13",
88
- ".claude/rules/tooling-and-review/preset-twin-sync.md": "f9c97f5027c87cdf7e52d309746c7efc1b5c55551c5844bbcb4d581f1e8cac22",
84
+ ".claude/rules/tooling-and-review/preset-layering.md": "8f1fc67a0ff5f8194507c039fa6ae461c23cf7a227ceb08a5f9d7da1e281034f",
85
+ ".claude/rules/tooling-and-review/preset-no-cross-stack-leakage.md": "0e8ea45b160d0ee0b56c64bb74d9ae6190c47e8a41139e83040dc2d9bd8e7dd0",
86
+ ".claude/rules/tooling-and-review/preset-pr-checklist.md": "a8a65497c25e5418e2b7d27fb806a00349e8e749d8e50531872b957782e9f0f7",
87
+ ".claude/rules/tooling-and-review/preset-token-budget.md": "5faea90cd9037b84e42aeb11da7ee37c4dccbe47fae28e5f081cd4418f6f539e",
88
+ ".claude/rules/tooling-and-review/preset-twin-sync.md": "456768516f288fb83a398cdd8b8cd81c9a563f433150f1b8ec0a35411ae1226a",
89
89
  ".claude/rules/tooling-and-review/security-next.md": "3e5c733925a0040928a9fbb7d841e69a471a03c5cb02d2fa986b81d29d7e31ca",
90
90
  ".claude/rules/tooling-and-review/technical-retro.md": "afed167d7f9dc26b25ce47368998df665cc779550b101041ab3ee5bcdd81a96b",
91
91
  ".claude/rules/tooling-and-review/ux-design.md": "732e9f290155ac5255bde9bf3fe6430ca1b1fd9c0ccbf5ea4467a3ccc22f12b5",
@@ -122,7 +122,7 @@
122
122
  ".claude/team/fixtures/ux-design-standalone.json": "528aeb9db4c310d1b5e3eacae0ad39a4fd1dd49c04154b064e7027981488a07a",
123
123
  ".claude/team/tasks/.gitkeep": "aca85ed348a49b11d68188be6ab245b521e5a706bbe0fbca1a28abfdc7155893",
124
124
  ".mcp.json": "f68a14db68a491a982975a3e6cc985057072a68eebdcedf33d8ac2a4a3b284ca",
125
- "AGENTS.md": "faa7f59b3ab5f5fd85174045edba9e0e7382d8e2afe1086f7713993d70c958ea",
125
+ "AGENTS.md": "8c90523299194b7796390550c66e7d3442daa385fe5647761deea05e4cab7dc3",
126
126
  "docs/specs/INDEX.md": "cab59f2093e6aaa376246a56d53e4268cb59070a6b10e87a94895192c35f422d",
127
127
  "docs/specs/_templates/feature.md": "83c47b90334c555fb5144749837e2ddfe1cae5bb89031f885ba98671075d59e7",
128
128
  "docs/specs/_templates/page.md": "a668c584e28edd52afaf3f9f8f898a75e43cb82fa12822c7a016033441e12e54"
@@ -9,7 +9,7 @@
9
9
  ".cursor/agents/README.md": "73b159dd8708f6b6e6bb16f3f85246fb2cc4d517ba6eafd757d2f48b137f6ac0",
10
10
  ".cursor/agents/accessibility-reviewer.md": "bdcd98ba07edc5dc8556030b16b6d4c88a55af8ada600559ac453ad0962f7845",
11
11
  ".cursor/agents/api-contract-reviewer.md": "51ce103a07db0ab786ebed5423fa2be0e448e94d8f17b59faf47d3ecaebdf08f",
12
- ".cursor/agents/build-verifier.md": "c9d235bcf200f34db41729edd9a1d4855b27a4f6096c27905169395395ba5d4e",
12
+ ".cursor/agents/build-verifier.md": "d35e763877cf25995181ec2c29b13bce6a02febd7fc7071cc69a72024bda3682",
13
13
  ".cursor/agents/ci-investigator.md": "7db8c393877b8ede733d814de82c3d4cc96438705dd0fbeed9188193e02d7129",
14
14
  ".cursor/agents/code-reviewer.md": "a1dfb4badd5e03292fde5ce4c004a9b4977b92a562833a3c068a0ef528c52ca7",
15
15
  ".cursor/agents/codebase-analyzer.md": "cae5586fd9a5fc0ea12a7578c5a9f5a4c72229b1e5c4f1eeb9ff9b6b243becf0",
@@ -48,7 +48,7 @@
48
48
  ".cursor/rules/README.md": "6aaaabd87ecb0ee398ac02ee39a5d6ca610c48bb437650cf127d9a6a0a13bf45",
49
49
  ".cursor/rules/agent-team-intake.mdc": "7d8925e60ffecb4ae0c95236f013b750e4fbe14782a4de4729db71c5667fa958",
50
50
  ".cursor/rules/agent-team-orchestrator.mdc": "cc3f67a610a08c858e94430b7a101425a926d15af7d75940b45383c04370f17f",
51
- ".cursor/rules/ai-local-overlay.mdc": "29dcb6ec804ecdf04ba8bfc7a97f6fa32d54ea2c8a5543385f8e0197faa202dc",
51
+ ".cursor/rules/ai-local-overlay.mdc": "28fc63869066e4af448032049adff4d65770b5b79ea68b6bec51ca6c4f7d0b58",
52
52
  ".cursor/rules/anti-sycophancy-discipline.mdc": "20c9dc8da47240f3f5b6a1c355d0bd83265cf7f1e080a6025b824d1b7ac2d36d",
53
53
  ".cursor/rules/api-services.mdc": "22214ec8a30bdc04d8099b7e97daf7baa0b772eb401ce95750faf10f19dd8770",
54
54
  ".cursor/rules/architecture-boundaries-ui.mdc": "8697315b586b4d9e993fb5895617cf23943ad3b4a87f592a975df3c3f31f5bb8",
@@ -72,11 +72,11 @@
72
72
  ".cursor/rules/package-manager.mdc": "ff639e2957f359d60adbc8daf2fbdecf9d7be0b6b9664860697a50b1eb855aab",
73
73
  ".cursor/rules/playwright-agents.mdc": "088c4aaf50c5d6fa1846ec73f32878ec51f795bf3eafb0daccdb6355e56e7e65",
74
74
  ".cursor/rules/post-change-lint.mdc": "2f9398a6ae81636d7a401e5fc38bb300579e10d29df9a375a3ed592dfb028529",
75
- ".cursor/rules/preset-layering.mdc": "6a193863d9cd02598282e4697aac077fec7839dbf24fc56d5eab3064e2eb79d0",
76
- ".cursor/rules/preset-no-cross-stack-leakage.mdc": "b7301230d4d07abd38f7be3f592b5d5385968d4a401050cdbb9a0e9898cb911c",
77
- ".cursor/rules/preset-pr-checklist.mdc": "c77157ee16a83a4b156a27487da3ae98351fbd8198eda1f6e8605439a5f7bd9b",
78
- ".cursor/rules/preset-token-budget.mdc": "845755627c744d33ae0e6790d1d57b8605cbc56fee68d371cb6f20cd2573ed86",
79
- ".cursor/rules/preset-twin-sync.mdc": "a731e0337de8c3a59749755883a0488e60b45d8e3d9bfabae5f9659c35f0eda1",
75
+ ".cursor/rules/preset-layering.mdc": "07a30ba09fde4fc90d162c3df12b1e75a038508ee8da6a14eecd7c13b9a95ba2",
76
+ ".cursor/rules/preset-no-cross-stack-leakage.mdc": "34906de78c3e925f46d95717109c6fafee6db29242263dedae695015567862a6",
77
+ ".cursor/rules/preset-pr-checklist.mdc": "94dc1cd164414b13f4445e55e1c443c56fc99dcd6cb22971078d9153a45516e7",
78
+ ".cursor/rules/preset-token-budget.mdc": "36ab13d54ba172853ccaac048464006744e8148c3ae545b446284e02a6334a0e",
79
+ ".cursor/rules/preset-twin-sync.mdc": "40e85050f2d2119f66868eefdd10a85b3ca91a5f9412a32ccdc713f8ba2ef24b",
80
80
  ".cursor/rules/product-specs-authoring.mdc": "590bbfe9f1d3c6b9acd363cd9658f9bb509702071410ef753a49d9a0a90122d2",
81
81
  ".cursor/rules/product-specs.mdc": "6f0e09b2f6c1b9ed63e23c50c9e2a4a481e7b102f821d1c94a0077bd2cf606ff",
82
82
  ".cursor/rules/public-imports.mdc": "efa02180236610417b5f08b63e4d4babc982bc9c6aa81fa464e0be948109fe26",
@@ -124,7 +124,7 @@
124
124
  ".cursor/team/fixtures/mockup-review-standalone.json": "edda49b1298e94f9a33496317e9f6ccbbb047659e044e05ba35669bfe800eda6",
125
125
  ".cursor/team/fixtures/ux-design-standalone.json": "528aeb9db4c310d1b5e3eacae0ad39a4fd1dd49c04154b064e7027981488a07a",
126
126
  ".cursor/team/tasks/.gitkeep": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
127
- "AGENTS.md": "59acefbf75e74132a8391531797b3c81e0ea6f9592dea7aed637fdcbf7f74a39",
127
+ "AGENTS.md": "de61cddb2fb50d27150ba5427a9c445373d62fe6be136b9da07b130e6fe12c2e",
128
128
  "docs/specs/INDEX.md": "cab59f2093e6aaa376246a56d53e4268cb59070a6b10e87a94895192c35f422d",
129
129
  "docs/specs/_templates/feature.md": "83c47b90334c555fb5144749837e2ddfe1cae5bb89031f885ba98671075d59e7",
130
130
  "docs/specs/_templates/page.md": "a668c584e28edd52afaf3f9f8f898a75e43cb82fa12822c7a016033441e12e54"