@bonesofspring/ai-rules 0.2.22 → 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 (429) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +10 -2
  3. package/bin/cli.js +56 -9
  4. package/bin/mavis-install.js +441 -0
  5. package/bin/opencode-install.js +531 -0
  6. package/package.json +17 -3
  7. package/presets/_shared/core/agent-team/agent-team-orchestrator.md +2 -2
  8. package/presets/_shared/core/meta/preset-layering.md +3 -3
  9. package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +1 -1
  10. package/presets/_shared/core/meta/preset-pr-checklist.md +5 -3
  11. package/presets/_shared/core/meta/preset-token-budget.md +3 -0
  12. package/presets/_shared/core/meta/preset-twin-sync.md +32 -0
  13. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-layering.md +3 -3
  14. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  15. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  16. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-token-budget.md +3 -0
  17. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +32 -0
  18. package/presets/claude/go/rules/tooling-and-review/preset-layering.md +3 -3
  19. package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  20. package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  21. package/presets/claude/go/rules/tooling-and-review/preset-token-budget.md +3 -0
  22. package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +32 -0
  23. package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +3 -3
  24. package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  25. package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  26. package/presets/claude/ios-swift/rules/tooling-and-review/preset-token-budget.md +3 -0
  27. package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +32 -0
  28. package/presets/claude/java/rules/tooling-and-review/preset-layering.md +3 -3
  29. package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  30. package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  31. package/presets/claude/java/rules/tooling-and-review/preset-token-budget.md +3 -0
  32. package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +32 -0
  33. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +3 -3
  34. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  35. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  36. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-token-budget.md +3 -0
  37. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +32 -0
  38. package/presets/claude/next/REPO_AGENTS.md +10 -5
  39. package/presets/claude/next/agents/build-verifier.md +3 -1
  40. package/presets/claude/next/rules/tooling-and-review/ai-local-overlay.md +1 -1
  41. package/presets/claude/next/rules/tooling-and-review/preset-layering.md +3 -3
  42. package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  43. package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  44. package/presets/claude/next/rules/tooling-and-review/preset-token-budget.md +3 -0
  45. package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +32 -0
  46. package/presets/claude/nuxt/rules/tooling-and-review/preset-layering.md +3 -3
  47. package/presets/claude/nuxt/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  48. package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  49. package/presets/claude/nuxt/rules/tooling-and-review/preset-token-budget.md +3 -0
  50. package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +32 -0
  51. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +3 -3
  52. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  53. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  54. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +3 -0
  55. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +32 -0
  56. package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +3 -3
  57. package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  58. package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  59. package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +3 -0
  60. package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +32 -0
  61. package/presets/claude/svelte/rules/tooling-and-review/preset-layering.md +3 -3
  62. package/presets/claude/svelte/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  63. package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +5 -3
  64. package/presets/claude/svelte/rules/tooling-and-review/preset-token-budget.md +3 -0
  65. package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +32 -0
  66. package/presets/cursor/android-kotlin/rules/preset-layering.mdc +3 -3
  67. package/presets/cursor/android-kotlin/rules/preset-no-cross-stack-leakage.mdc +1 -1
  68. package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +5 -3
  69. package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
  70. package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +32 -0
  71. package/presets/cursor/go/rules/preset-layering.mdc +3 -3
  72. package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +1 -1
  73. package/presets/cursor/go/rules/preset-pr-checklist.mdc +5 -3
  74. package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
  75. package/presets/cursor/go/rules/preset-twin-sync.mdc +32 -0
  76. package/presets/cursor/ios-swift/rules/preset-layering.mdc +3 -3
  77. package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +1 -1
  78. package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +5 -3
  79. package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
  80. package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +32 -0
  81. package/presets/cursor/java/rules/preset-layering.mdc +3 -3
  82. package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +1 -1
  83. package/presets/cursor/java/rules/preset-pr-checklist.mdc +5 -3
  84. package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
  85. package/presets/cursor/java/rules/preset-twin-sync.mdc +32 -0
  86. package/presets/cursor/mcp-ts/rules/preset-layering.mdc +3 -3
  87. package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +1 -1
  88. package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +5 -3
  89. package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
  90. package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +32 -0
  91. package/presets/cursor/next/REPO_AGENTS.md +10 -5
  92. package/presets/cursor/next/agents/build-verifier.md +3 -1
  93. package/presets/cursor/next/rules/ai-local-overlay.mdc +1 -1
  94. package/presets/cursor/next/rules/preset-layering.mdc +3 -3
  95. package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +1 -1
  96. package/presets/cursor/next/rules/preset-pr-checklist.mdc +5 -3
  97. package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
  98. package/presets/cursor/next/rules/preset-twin-sync.mdc +32 -0
  99. package/presets/cursor/nuxt/rules/preset-layering.mdc +3 -3
  100. package/presets/cursor/nuxt/rules/preset-no-cross-stack-leakage.mdc +1 -1
  101. package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +5 -3
  102. package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
  103. package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +32 -0
  104. package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +3 -3
  105. package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +1 -1
  106. package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +5 -3
  107. package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
  108. package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +32 -0
  109. package/presets/cursor/php-laravel/rules/preset-layering.mdc +3 -3
  110. package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +1 -1
  111. package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +5 -3
  112. package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
  113. package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +32 -0
  114. package/presets/cursor/svelte/rules/preset-layering.mdc +3 -3
  115. package/presets/cursor/svelte/rules/preset-no-cross-stack-leakage.mdc +1 -1
  116. package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +5 -3
  117. package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
  118. package/presets/cursor/svelte/rules/preset-twin-sync.mdc +32 -0
  119. package/presets/mavis/README.md +60 -0
  120. package/presets/mavis/next/MAVIS.md +67 -0
  121. package/presets/mavis/next/MCP.md +36 -0
  122. package/presets/mavis/next/README.md +15 -0
  123. package/presets/mavis/next/REPO_AGENTS.md +96 -0
  124. package/presets/mavis/next/adapter-map.json +959 -0
  125. package/presets/mavis/next/agents/README.md +206 -0
  126. package/presets/mavis/next/agents/accessibility-reviewer.md +61 -0
  127. package/presets/mavis/next/agents/api-contract-reviewer.md +57 -0
  128. package/presets/mavis/next/agents/build-verifier.md +112 -0
  129. package/presets/mavis/next/agents/ci-investigator.md +63 -0
  130. package/presets/mavis/next/agents/code-reviewer.md +61 -0
  131. package/presets/mavis/next/agents/codebase-analyzer.md +21 -0
  132. package/presets/mavis/next/agents/debugger.md +80 -0
  133. package/presets/mavis/next/agents/feature-developer.md +101 -0
  134. package/presets/mavis/next/agents/migration-specialist.md +67 -0
  135. package/presets/mavis/next/agents/mockup-reviewer.md +53 -0
  136. package/presets/mavis/next/agents/openspec-author.md +157 -0
  137. package/presets/mavis/next/agents/performance-auditor.md +68 -0
  138. package/presets/mavis/next/agents/playwright-test-generator.md +28 -0
  139. package/presets/mavis/next/agents/playwright-test-healer.md +28 -0
  140. package/presets/mavis/next/agents/playwright-test-planner.md +21 -0
  141. package/presets/mavis/next/agents/qa-tester.md +52 -0
  142. package/presets/mavis/next/agents/security-reviewer.md +54 -0
  143. package/presets/mavis/next/agents/solution-architect.md +68 -0
  144. package/presets/mavis/next/agents/task-analyst.md +166 -0
  145. package/presets/mavis/next/agents/task-router.md +178 -0
  146. package/presets/mavis/next/agents/tech-writer.md +57 -0
  147. package/presets/mavis/next/agents/unit-test-generator.md +31 -0
  148. package/presets/mavis/next/agents/unit-test-healer.md +32 -0
  149. package/presets/mavis/next/agents/unit-test-planner.md +54 -0
  150. package/presets/mavis/next/commands/README.md +49 -0
  151. package/presets/mavis/next/commands/spec-start.md +24 -0
  152. package/presets/mavis/next/commands/task-continue.md +50 -0
  153. package/presets/mavis/next/commands/task.md +50 -0
  154. package/presets/mavis/next/commands/technical-retro.md +80 -0
  155. package/presets/mavis/next/hooks/README.md +22 -0
  156. package/presets/mavis/next/hooks/chain-team-phases.sh +845 -0
  157. package/presets/mavis/next/mcp.json +19 -0
  158. package/presets/mavis/next/rules/README.md +16 -0
  159. package/presets/mavis/next/rules/agent-team-intake.md +11 -0
  160. package/presets/mavis/next/rules/agent-team-orchestrator.md +210 -0
  161. package/presets/mavis/next/rules/ai-local-overlay.md +37 -0
  162. package/presets/mavis/next/rules/anti-sycophancy-discipline.md +23 -0
  163. package/presets/mavis/next/rules/api-services.md +72 -0
  164. package/presets/mavis/next/rules/architecture-boundaries-ui.md +9 -0
  165. package/presets/mavis/next/rules/architecture-boundaries.md +86 -0
  166. package/presets/mavis/next/rules/arrow-functions.md +25 -0
  167. package/presets/mavis/next/rules/code-quality-and-refactoring.md +40 -0
  168. package/presets/mavis/next/rules/code-review-mr.md +40 -0
  169. package/presets/mavis/next/rules/css-property-order-stylelint.md +5 -0
  170. package/presets/mavis/next/rules/design-guidance.md +99 -0
  171. package/presets/mavis/next/rules/feature-delivery-workflow.md +49 -0
  172. package/presets/mavis/next/rules/http-client.md +55 -0
  173. package/presets/mavis/next/rules/layer-barrel-exports.md +68 -0
  174. package/presets/mavis/next/rules/mcp-usage.md +86 -0
  175. package/presets/mavis/next/rules/mockup-review.md +107 -0
  176. package/presets/mavis/next/rules/navigation-router-stack.md +56 -0
  177. package/presets/mavis/next/rules/navigation-router-ui.md +10 -0
  178. package/presets/mavis/next/rules/next-app-core.md +29 -0
  179. package/presets/mavis/next/rules/next-app-router.md +44 -0
  180. package/presets/mavis/next/rules/no-props-spread.md +56 -0
  181. package/presets/mavis/next/rules/no-type-assertion-as-import-export.md +51 -0
  182. package/presets/mavis/next/rules/package-manager.md +11 -0
  183. package/presets/mavis/next/rules/playwright-agents.md +67 -0
  184. package/presets/mavis/next/rules/post-change-lint.md +35 -0
  185. package/presets/mavis/next/rules/preset-layering.md +27 -0
  186. package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +90 -0
  187. package/presets/mavis/next/rules/preset-pr-checklist.md +36 -0
  188. package/presets/mavis/next/rules/preset-token-budget.md +39 -0
  189. package/presets/mavis/next/rules/preset-twin-sync.md +81 -0
  190. package/presets/mavis/next/rules/product-specs-authoring.md +112 -0
  191. package/presets/mavis/next/rules/product-specs.md +56 -0
  192. package/presets/mavis/next/rules/public-imports.md +50 -0
  193. package/presets/mavis/next/rules/react-a11y-coding.md +31 -0
  194. package/presets/mavis/next/rules/react-next-anti-hallucination.md +60 -0
  195. package/presets/mavis/next/rules/react-ui.md +105 -0
  196. package/presets/mavis/next/rules/reference-features.md +45 -0
  197. package/presets/mavis/next/rules/security-next.md +45 -0
  198. package/presets/mavis/next/rules/store-rtk.md +78 -0
  199. package/presets/mavis/next/rules/technical-retro.md +11 -0
  200. package/presets/mavis/next/rules/tests-e2e-structure.md +72 -0
  201. package/presets/mavis/next/rules/tests-unit.md +72 -0
  202. package/presets/mavis/next/rules/types-jsdoc.md +43 -0
  203. package/presets/mavis/next/rules/ux-design.md +99 -0
  204. package/presets/mavis/next/skills/README.md +21 -0
  205. package/presets/mavis/next/skills/ci-investigation/SKILL.md +39 -0
  206. package/presets/mavis/next/skills/code-review/SKILL.md +39 -0
  207. package/presets/mavis/next/skills/debug-investigation/SKILL.md +26 -0
  208. package/presets/mavis/next/skills/feature-delivery/SKILL.md +34 -0
  209. package/presets/mavis/next/skills/mockup-review/SKILL.md +24 -0
  210. package/presets/mavis/next/skills/playwright-e2e/SKILL.md +28 -0
  211. package/presets/mavis/next/skills/react-performance/SKILL.md +39 -0
  212. package/presets/mavis/next/skills/spec-authoring/SKILL.md +21 -0
  213. package/presets/mavis/next/skills/spec-discovery/SKILL.md +53 -0
  214. package/presets/mavis/next/skills/technical-retro/SKILL.md +49 -0
  215. package/presets/mavis/next/skills/unit-testing/SKILL.md +29 -0
  216. package/presets/mavis/next/skills/ux-design/SKILL.md +31 -0
  217. package/presets/mavis/next/skills/write-adr/SKILL.md +36 -0
  218. package/presets/mavis/next/team/README.md +148 -0
  219. package/presets/mavis/next/team/conventions.md +21 -0
  220. package/presets/mavis/next/team/fixtures/artifact-manifest.example.json +79 -0
  221. package/presets/mavis/next/team/fixtures/bugfix-standard.json +25 -0
  222. package/presets/mavis/next/team/fixtures/feature-full.json +29 -0
  223. package/presets/mavis/next/team/fixtures/feature-light.json +27 -0
  224. package/presets/mavis/next/team/fixtures/feature-with-figma-url.json +17 -0
  225. package/presets/mavis/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  226. package/presets/mavis/next/team/fixtures/memlog.example.md +9 -0
  227. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  228. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  229. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  230. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  231. package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  232. package/presets/mavis/next/team/fixtures/metrics.example.json +54 -0
  233. package/presets/mavis/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  234. package/presets/mavis/next/team/fixtures/mockup-review-standalone.json +14 -0
  235. package/presets/mavis/next/team/fixtures/ux-design-standalone.json +15 -0
  236. package/presets/mavis/next/team/tasks/.gitkeep +0 -0
  237. package/presets/mavis/platform-contract.json +131 -0
  238. package/presets/opencode/README.md +61 -0
  239. package/presets/opencode/next/MCP.md +36 -0
  240. package/presets/opencode/next/OPENCODE.md +65 -0
  241. package/presets/opencode/next/README.md +16 -0
  242. package/presets/opencode/next/REPO_AGENTS.md +97 -0
  243. package/presets/opencode/next/adapter-map.json +959 -0
  244. package/presets/opencode/next/agents/README.md +201 -0
  245. package/presets/opencode/next/agents/accessibility-reviewer.md +73 -0
  246. package/presets/opencode/next/agents/api-contract-reviewer.md +69 -0
  247. package/presets/opencode/next/agents/build-verifier.md +124 -0
  248. package/presets/opencode/next/agents/ci-investigator.md +71 -0
  249. package/presets/opencode/next/agents/code-reviewer.md +73 -0
  250. package/presets/opencode/next/agents/codebase-analyzer.md +33 -0
  251. package/presets/opencode/next/agents/debugger.md +88 -0
  252. package/presets/opencode/next/agents/feature-developer.md +109 -0
  253. package/presets/opencode/next/agents/migration-specialist.md +79 -0
  254. package/presets/opencode/next/agents/mockup-reviewer.md +65 -0
  255. package/presets/opencode/next/agents/openspec-author.md +169 -0
  256. package/presets/opencode/next/agents/performance-auditor.md +80 -0
  257. package/presets/opencode/next/agents/playwright-test-generator.md +36 -0
  258. package/presets/opencode/next/agents/playwright-test-healer.md +36 -0
  259. package/presets/opencode/next/agents/playwright-test-planner.md +33 -0
  260. package/presets/opencode/next/agents/qa-tester.md +60 -0
  261. package/presets/opencode/next/agents/security-reviewer.md +66 -0
  262. package/presets/opencode/next/agents/solution-architect.md +80 -0
  263. package/presets/opencode/next/agents/task-analyst.md +178 -0
  264. package/presets/opencode/next/agents/task-router.md +190 -0
  265. package/presets/opencode/next/agents/tech-writer.md +69 -0
  266. package/presets/opencode/next/agents/unit-test-generator.md +39 -0
  267. package/presets/opencode/next/agents/unit-test-healer.md +40 -0
  268. package/presets/opencode/next/agents/unit-test-planner.md +66 -0
  269. package/presets/opencode/next/commands/README.md +49 -0
  270. package/presets/opencode/next/commands/spec-start.md +32 -0
  271. package/presets/opencode/next/commands/task-continue.md +58 -0
  272. package/presets/opencode/next/commands/task.md +58 -0
  273. package/presets/opencode/next/commands/technical-retro.md +88 -0
  274. package/presets/opencode/next/hooks/README.md +22 -0
  275. package/presets/opencode/next/hooks/chain-team-phases.sh +845 -0
  276. package/presets/opencode/next/opencode.json +33 -0
  277. package/presets/opencode/next/rules/README.md +20 -0
  278. package/presets/opencode/next/rules/agent-team-intake.md +11 -0
  279. package/presets/opencode/next/rules/agent-team-orchestrator.md +214 -0
  280. package/presets/opencode/next/rules/ai-local-overlay.md +37 -0
  281. package/presets/opencode/next/rules/anti-sycophancy-discipline.md +23 -0
  282. package/presets/opencode/next/rules/api-services.md +72 -0
  283. package/presets/opencode/next/rules/architecture-boundaries-ui.md +9 -0
  284. package/presets/opencode/next/rules/architecture-boundaries.md +86 -0
  285. package/presets/opencode/next/rules/arrow-functions.md +25 -0
  286. package/presets/opencode/next/rules/code-quality-and-refactoring.md +40 -0
  287. package/presets/opencode/next/rules/code-review-mr.md +40 -0
  288. package/presets/opencode/next/rules/css-property-order-stylelint.md +5 -0
  289. package/presets/opencode/next/rules/design-guidance.md +99 -0
  290. package/presets/opencode/next/rules/feature-delivery-workflow.md +49 -0
  291. package/presets/opencode/next/rules/http-client.md +55 -0
  292. package/presets/opencode/next/rules/layer-barrel-exports.md +68 -0
  293. package/presets/opencode/next/rules/mcp-usage.md +86 -0
  294. package/presets/opencode/next/rules/mockup-review.md +107 -0
  295. package/presets/opencode/next/rules/navigation-router-stack.md +56 -0
  296. package/presets/opencode/next/rules/navigation-router-ui.md +10 -0
  297. package/presets/opencode/next/rules/next-app-core.md +29 -0
  298. package/presets/opencode/next/rules/next-app-router.md +44 -0
  299. package/presets/opencode/next/rules/no-props-spread.md +56 -0
  300. package/presets/opencode/next/rules/no-type-assertion-as-import-export.md +51 -0
  301. package/presets/opencode/next/rules/package-manager.md +11 -0
  302. package/presets/opencode/next/rules/playwright-agents.md +67 -0
  303. package/presets/opencode/next/rules/post-change-lint.md +35 -0
  304. package/presets/opencode/next/rules/preset-layering.md +27 -0
  305. package/presets/opencode/next/rules/preset-no-cross-stack-leakage.md +90 -0
  306. package/presets/opencode/next/rules/preset-pr-checklist.md +36 -0
  307. package/presets/opencode/next/rules/preset-token-budget.md +39 -0
  308. package/presets/opencode/next/rules/preset-twin-sync.md +81 -0
  309. package/presets/opencode/next/rules/product-specs-authoring.md +112 -0
  310. package/presets/opencode/next/rules/product-specs.md +56 -0
  311. package/presets/opencode/next/rules/public-imports.md +50 -0
  312. package/presets/opencode/next/rules/react-a11y-coding.md +31 -0
  313. package/presets/opencode/next/rules/react-next-anti-hallucination.md +60 -0
  314. package/presets/opencode/next/rules/react-ui.md +105 -0
  315. package/presets/opencode/next/rules/reference-features.md +45 -0
  316. package/presets/opencode/next/rules/security-next.md +45 -0
  317. package/presets/opencode/next/rules/store-rtk.md +78 -0
  318. package/presets/opencode/next/rules/technical-retro.md +11 -0
  319. package/presets/opencode/next/rules/tests-e2e-structure.md +72 -0
  320. package/presets/opencode/next/rules/tests-unit.md +72 -0
  321. package/presets/opencode/next/rules/types-jsdoc.md +43 -0
  322. package/presets/opencode/next/rules/ux-design.md +99 -0
  323. package/presets/opencode/next/skills/README.md +21 -0
  324. package/presets/opencode/next/skills/ci-investigation/SKILL.md +44 -0
  325. package/presets/opencode/next/skills/code-review/SKILL.md +44 -0
  326. package/presets/opencode/next/skills/debug-investigation/SKILL.md +59 -0
  327. package/presets/opencode/next/skills/feature-delivery/SKILL.md +39 -0
  328. package/presets/opencode/next/skills/mockup-review/SKILL.md +29 -0
  329. package/presets/opencode/next/skills/playwright-e2e/SKILL.md +33 -0
  330. package/presets/opencode/next/skills/react-performance/SKILL.md +44 -0
  331. package/presets/opencode/next/skills/spec-authoring/SKILL.md +26 -0
  332. package/presets/opencode/next/skills/spec-discovery/SKILL.md +58 -0
  333. package/presets/opencode/next/skills/technical-retro/SKILL.md +54 -0
  334. package/presets/opencode/next/skills/unit-testing/SKILL.md +34 -0
  335. package/presets/opencode/next/skills/ux-design/SKILL.md +36 -0
  336. package/presets/opencode/next/skills/write-adr/SKILL.md +41 -0
  337. package/presets/opencode/next/team/README.md +148 -0
  338. package/presets/opencode/next/team/conventions.md +21 -0
  339. package/presets/opencode/next/team/fixtures/artifact-manifest.example.json +79 -0
  340. package/presets/opencode/next/team/fixtures/bugfix-standard.json +25 -0
  341. package/presets/opencode/next/team/fixtures/feature-full.json +29 -0
  342. package/presets/opencode/next/team/fixtures/feature-light.json +27 -0
  343. package/presets/opencode/next/team/fixtures/feature-with-figma-url.json +17 -0
  344. package/presets/opencode/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  345. package/presets/opencode/next/team/fixtures/memlog.example.md +9 -0
  346. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  347. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  348. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  349. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  350. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  351. package/presets/opencode/next/team/fixtures/metrics.example.json +54 -0
  352. package/presets/opencode/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  353. package/presets/opencode/next/team/fixtures/mockup-review-standalone.json +14 -0
  354. package/presets/opencode/next/team/fixtures/ux-design-standalone.json +15 -0
  355. package/presets/opencode/next/team/tasks/.gitkeep +0 -0
  356. package/presets/opencode/platform-contract.json +131 -0
  357. package/scripts/capture-cli-golden.mjs +48 -0
  358. package/scripts/check-chain-hook-coverage.sh +2 -0
  359. package/scripts/check-mavis-adapter-map.mjs +294 -0
  360. package/scripts/check-mavis-local-overlay.mjs +56 -0
  361. package/scripts/check-mavis-platform-contract.mjs +388 -0
  362. package/scripts/check-mavis-token-budget.mjs +48 -0
  363. package/scripts/check-opencode-adapter-map.mjs +294 -0
  364. package/scripts/check-opencode-local-overlay.mjs +56 -0
  365. package/scripts/check-opencode-platform-contract.mjs +388 -0
  366. package/scripts/check-opencode-token-budget.mjs +48 -0
  367. package/scripts/check-preset-leakage.sh +64 -3
  368. package/scripts/check-preset-structure.sh +108 -0
  369. package/scripts/check-shared-core-drift.sh +18 -0
  370. package/scripts/check-task-router-intents.sh +15 -2
  371. package/scripts/fixtures/cli-golden/claude/next.json +130 -0
  372. package/scripts/fixtures/cli-golden/cursor/next.json +132 -0
  373. package/scripts/fixtures/cli-golden/mavis/next.json +125 -0
  374. package/scripts/fixtures/cli-golden/opencode/next.json +125 -0
  375. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  376. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  377. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  378. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/MAVIS.md +1 -0
  379. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/adapter-map.json +26 -0
  380. package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/rules/sample.md +2 -0
  381. package/scripts/fixtures/mavis-adapter-map/valid/cursor/AGENTS.md +1 -0
  382. package/scripts/fixtures/mavis-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  383. package/scripts/fixtures/mavis-adapter-map/valid/mavis/MAVIS.md +1 -0
  384. package/scripts/fixtures/mavis-adapter-map/valid/mavis/adapter-map.json +26 -0
  385. package/scripts/fixtures/mavis-adapter-map/valid/mavis/rules/sample.md +2 -0
  386. package/scripts/fixtures/mavis-platform-contract/invalid-minimax-destination.json +131 -0
  387. package/scripts/fixtures/mavis-platform-contract/invalid-missing-capability.json +126 -0
  388. package/scripts/fixtures/mavis-platform-contract/invalid-released-unknown-stack.json +133 -0
  389. package/scripts/fixtures/mavis-platform-contract/invalid-unknown-native-path.json +131 -0
  390. package/scripts/fixtures/mavis-platform-contract/invalid-version-pin.json +132 -0
  391. package/scripts/fixtures/mavis-platform-contract/valid.json +131 -0
  392. package/scripts/fixtures/mavis-probe/AGENTS.md +6 -0
  393. package/scripts/fixtures/mavis-probe/agents/probe-agent.md +6 -0
  394. package/scripts/fixtures/mavis-probe/fake-mcode.mjs +98 -0
  395. package/scripts/fixtures/mavis-probe/output.schema.json +28 -0
  396. package/scripts/fixtures/mavis-probe/rules/probe.md +5 -0
  397. package/scripts/fixtures/mavis-probe/skills/probe/SKILL.md +11 -0
  398. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  399. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  400. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  401. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/OPENCODE.md +1 -0
  402. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/adapter-map.json +26 -0
  403. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/rules/sample.md +2 -0
  404. package/scripts/fixtures/opencode-adapter-map/valid/cursor/AGENTS.md +1 -0
  405. package/scripts/fixtures/opencode-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  406. package/scripts/fixtures/opencode-adapter-map/valid/opencode/OPENCODE.md +1 -0
  407. package/scripts/fixtures/opencode-adapter-map/valid/opencode/adapter-map.json +26 -0
  408. package/scripts/fixtures/opencode-adapter-map/valid/opencode/rules/sample.md +2 -0
  409. package/scripts/fixtures/opencode-platform-contract/invalid-home-destination.json +131 -0
  410. package/scripts/fixtures/opencode-platform-contract/invalid-missing-capability.json +126 -0
  411. package/scripts/fixtures/opencode-platform-contract/invalid-released-unknown-stack.json +133 -0
  412. package/scripts/fixtures/opencode-platform-contract/invalid-unknown-native-path.json +131 -0
  413. package/scripts/fixtures/opencode-platform-contract/invalid-version-pin.json +132 -0
  414. package/scripts/fixtures/opencode-platform-contract/valid.json +131 -0
  415. package/scripts/generate-mavis-next.mjs +706 -0
  416. package/scripts/generate-opencode-next.mjs +831 -0
  417. package/scripts/lib/cli-golden.mjs +50 -0
  418. package/scripts/lib/discover-hooks.mjs +94 -0
  419. package/scripts/probe-mavis-capabilities.mjs +480 -0
  420. package/scripts/sync-dogfood-mavis.sh +90 -0
  421. package/scripts/sync-dogfood-opencode.sh +85 -0
  422. package/scripts/test-agent-task-metrics-hooks.mjs +25 -16
  423. package/scripts/test-chain-team-phases-coverage.mjs +15 -7
  424. package/scripts/test-cli-init-clean.mjs +367 -0
  425. package/scripts/test-cli-mcp-merge.mjs +23 -0
  426. package/scripts/test-mavis-context-budget.mjs +55 -0
  427. package/scripts/test-mavis-orchestration.mjs +649 -0
  428. package/scripts/test-opencode-context-budget.mjs +53 -0
  429. package/scripts/test-probe-mavis-capabilities.mjs +205 -0
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Validate presets/mavis/platform-contract.json against architecture §3.4.
4
+ *
5
+ * Usage:
6
+ * node scripts/check-mavis-platform-contract.mjs
7
+ * node scripts/check-mavis-platform-contract.mjs --contract <path>
8
+ * node scripts/check-mavis-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
+ 'mavis',
20
+ 'platform-contract.json',
21
+ );
22
+ const fixtureDirectory = path.join(
23
+ scriptDirectory,
24
+ 'fixtures',
25
+ 'mavis-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: 'MAVIS.md', destination: '.mavis/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
+ /(?:^|[\\/])\.minimax(?:[\\/]|$)|(?:^|[\\/])\.minimax-code(?:[\\/]|$)|~\/\.minimax|\$HOME\/\.minimax/i;
77
+ const VERSION_PIN_KEYS = new Set([
78
+ 'requiredCliVersion',
79
+ 'requiredDesktopVersion',
80
+ 'minCliVersion',
81
+ 'pinnedVersion',
82
+ 'requiredVersion',
83
+ ]);
84
+ const EVIDENCE_MARKER = 'MAVIS-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 !== 'mavis') {
100
+ fail('id must be "mavis"');
101
+ }
102
+ if (contract.runtimeExecutable !== 'mcode') {
103
+ fail('runtimeExecutable must be "mcode"');
104
+ }
105
+ if (contract.sourceRoot !== 'presets/mavis') {
106
+ fail('sourceRoot must be "presets/mavis"');
107
+ }
108
+ if (contract.managedRoot !== '.mavis') {
109
+ fail('managedRoot must be ".mavis"');
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', 'mavis', 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('.mavis/')
160
+ ) {
161
+ fail(
162
+ `entryFiles[${index}].destination must be AGENTS.md or under .mavis/`,
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('.mavis/')) {
194
+ fail(
195
+ `directoryMappings[${index}].destination must start with .mavis/`,
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 !== 'mcp.json') {
206
+ fail('mcp.source must be "mcp.json"');
207
+ }
208
+ if (mcp.fallbackDestination !== '.mavis/mcp.json') {
209
+ fail('mcp.fallbackDestination must be ".mavis/mcp.json"');
210
+ }
211
+ if (mcp.mode !== 'fallback-manual-import') {
212
+ fail('mcp.mode must be "fallback-manual-import"');
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 a MiniMax-native or 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-minimax-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 Mavis 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 { loadReleasedMavisStacks } 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 = loadReleasedMavisStacks(presetsRoot);
16
+ if (stacks.length === 0) {
17
+ console.log('ok mavis 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, 'mavis', stack, 'adapter-map.json');
24
+ if (!fs.existsSync(mapPath)) {
25
+ console.error(`FAIL mavis/${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 mavis/${stack}: ${rootRules.length} root-entry rules (cap ${ROOT_ENTRY_CAP})`,
39
+ );
40
+ failed += 1;
41
+ } else {
42
+ console.log(
43
+ `ok mavis/${stack}: ${rootRules.length} root-entry rules (cap ${ROOT_ENTRY_CAP})`,
44
+ );
45
+ }
46
+ }
47
+
48
+ process.exit(failed === 0 ? 0 : 1);