@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.
- package/CHANGELOG.md +17 -0
- package/README.md +10 -2
- package/bin/cli.js +56 -9
- package/bin/mavis-install.js +441 -0
- package/bin/opencode-install.js +531 -0
- package/package.json +17 -3
- package/presets/_shared/core/agent-team/agent-team-orchestrator.md +2 -2
- package/presets/_shared/core/meta/preset-layering.md +3 -3
- package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +1 -1
- package/presets/_shared/core/meta/preset-pr-checklist.md +5 -3
- package/presets/_shared/core/meta/preset-token-budget.md +3 -0
- package/presets/_shared/core/meta/preset-twin-sync.md +32 -0
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/go/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/go/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/java/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/java/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/next/REPO_AGENTS.md +10 -5
- package/presets/claude/next/agents/build-verifier.md +3 -1
- package/presets/claude/next/rules/tooling-and-review/ai-local-overlay.md +1 -1
- package/presets/claude/next/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/next/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/nuxt/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/nuxt/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/nuxt/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/claude/svelte/rules/tooling-and-review/preset-layering.md +3 -3
- package/presets/claude/svelte/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
- package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +5 -3
- package/presets/claude/svelte/rules/tooling-and-review/preset-token-budget.md +3 -0
- package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +32 -0
- package/presets/cursor/android-kotlin/rules/preset-layering.mdc +3 -3
- package/presets/cursor/android-kotlin/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/go/rules/preset-layering.mdc +3 -3
- package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/go/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/go/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/ios-swift/rules/preset-layering.mdc +3 -3
- package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/java/rules/preset-layering.mdc +3 -3
- package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/java/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/java/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/mcp-ts/rules/preset-layering.mdc +3 -3
- package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/next/REPO_AGENTS.md +10 -5
- package/presets/cursor/next/agents/build-verifier.md +3 -1
- package/presets/cursor/next/rules/ai-local-overlay.mdc +1 -1
- package/presets/cursor/next/rules/preset-layering.mdc +3 -3
- package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/next/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/next/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/nuxt/rules/preset-layering.mdc +3 -3
- package/presets/cursor/nuxt/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +3 -3
- package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/php-laravel/rules/preset-layering.mdc +3 -3
- package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +32 -0
- package/presets/cursor/svelte/rules/preset-layering.mdc +3 -3
- package/presets/cursor/svelte/rules/preset-no-cross-stack-leakage.mdc +1 -1
- package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +5 -3
- package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/svelte/rules/preset-twin-sync.mdc +32 -0
- package/presets/mavis/README.md +60 -0
- package/presets/mavis/next/MAVIS.md +67 -0
- package/presets/mavis/next/MCP.md +36 -0
- package/presets/mavis/next/README.md +15 -0
- package/presets/mavis/next/REPO_AGENTS.md +96 -0
- package/presets/mavis/next/adapter-map.json +959 -0
- package/presets/mavis/next/agents/README.md +206 -0
- package/presets/mavis/next/agents/accessibility-reviewer.md +61 -0
- package/presets/mavis/next/agents/api-contract-reviewer.md +57 -0
- package/presets/mavis/next/agents/build-verifier.md +112 -0
- package/presets/mavis/next/agents/ci-investigator.md +63 -0
- package/presets/mavis/next/agents/code-reviewer.md +61 -0
- package/presets/mavis/next/agents/codebase-analyzer.md +21 -0
- package/presets/mavis/next/agents/debugger.md +80 -0
- package/presets/mavis/next/agents/feature-developer.md +101 -0
- package/presets/mavis/next/agents/migration-specialist.md +67 -0
- package/presets/mavis/next/agents/mockup-reviewer.md +53 -0
- package/presets/mavis/next/agents/openspec-author.md +157 -0
- package/presets/mavis/next/agents/performance-auditor.md +68 -0
- package/presets/mavis/next/agents/playwright-test-generator.md +28 -0
- package/presets/mavis/next/agents/playwright-test-healer.md +28 -0
- package/presets/mavis/next/agents/playwright-test-planner.md +21 -0
- package/presets/mavis/next/agents/qa-tester.md +52 -0
- package/presets/mavis/next/agents/security-reviewer.md +54 -0
- package/presets/mavis/next/agents/solution-architect.md +68 -0
- package/presets/mavis/next/agents/task-analyst.md +166 -0
- package/presets/mavis/next/agents/task-router.md +178 -0
- package/presets/mavis/next/agents/tech-writer.md +57 -0
- package/presets/mavis/next/agents/unit-test-generator.md +31 -0
- package/presets/mavis/next/agents/unit-test-healer.md +32 -0
- package/presets/mavis/next/agents/unit-test-planner.md +54 -0
- package/presets/mavis/next/commands/README.md +49 -0
- package/presets/mavis/next/commands/spec-start.md +24 -0
- package/presets/mavis/next/commands/task-continue.md +50 -0
- package/presets/mavis/next/commands/task.md +50 -0
- package/presets/mavis/next/commands/technical-retro.md +80 -0
- package/presets/mavis/next/hooks/README.md +22 -0
- package/presets/mavis/next/hooks/chain-team-phases.sh +845 -0
- package/presets/mavis/next/mcp.json +19 -0
- package/presets/mavis/next/rules/README.md +16 -0
- package/presets/mavis/next/rules/agent-team-intake.md +11 -0
- package/presets/mavis/next/rules/agent-team-orchestrator.md +210 -0
- package/presets/mavis/next/rules/ai-local-overlay.md +37 -0
- package/presets/mavis/next/rules/anti-sycophancy-discipline.md +23 -0
- package/presets/mavis/next/rules/api-services.md +72 -0
- package/presets/mavis/next/rules/architecture-boundaries-ui.md +9 -0
- package/presets/mavis/next/rules/architecture-boundaries.md +86 -0
- package/presets/mavis/next/rules/arrow-functions.md +25 -0
- package/presets/mavis/next/rules/code-quality-and-refactoring.md +40 -0
- package/presets/mavis/next/rules/code-review-mr.md +40 -0
- package/presets/mavis/next/rules/css-property-order-stylelint.md +5 -0
- package/presets/mavis/next/rules/design-guidance.md +99 -0
- package/presets/mavis/next/rules/feature-delivery-workflow.md +49 -0
- package/presets/mavis/next/rules/http-client.md +55 -0
- package/presets/mavis/next/rules/layer-barrel-exports.md +68 -0
- package/presets/mavis/next/rules/mcp-usage.md +86 -0
- package/presets/mavis/next/rules/mockup-review.md +107 -0
- package/presets/mavis/next/rules/navigation-router-stack.md +56 -0
- package/presets/mavis/next/rules/navigation-router-ui.md +10 -0
- package/presets/mavis/next/rules/next-app-core.md +29 -0
- package/presets/mavis/next/rules/next-app-router.md +44 -0
- package/presets/mavis/next/rules/no-props-spread.md +56 -0
- package/presets/mavis/next/rules/no-type-assertion-as-import-export.md +51 -0
- package/presets/mavis/next/rules/package-manager.md +11 -0
- package/presets/mavis/next/rules/playwright-agents.md +67 -0
- package/presets/mavis/next/rules/post-change-lint.md +35 -0
- package/presets/mavis/next/rules/preset-layering.md +27 -0
- package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +90 -0
- package/presets/mavis/next/rules/preset-pr-checklist.md +36 -0
- package/presets/mavis/next/rules/preset-token-budget.md +39 -0
- package/presets/mavis/next/rules/preset-twin-sync.md +81 -0
- package/presets/mavis/next/rules/product-specs-authoring.md +112 -0
- package/presets/mavis/next/rules/product-specs.md +56 -0
- package/presets/mavis/next/rules/public-imports.md +50 -0
- package/presets/mavis/next/rules/react-a11y-coding.md +31 -0
- package/presets/mavis/next/rules/react-next-anti-hallucination.md +60 -0
- package/presets/mavis/next/rules/react-ui.md +105 -0
- package/presets/mavis/next/rules/reference-features.md +45 -0
- package/presets/mavis/next/rules/security-next.md +45 -0
- package/presets/mavis/next/rules/store-rtk.md +78 -0
- package/presets/mavis/next/rules/technical-retro.md +11 -0
- package/presets/mavis/next/rules/tests-e2e-structure.md +72 -0
- package/presets/mavis/next/rules/tests-unit.md +72 -0
- package/presets/mavis/next/rules/types-jsdoc.md +43 -0
- package/presets/mavis/next/rules/ux-design.md +99 -0
- package/presets/mavis/next/skills/README.md +21 -0
- package/presets/mavis/next/skills/ci-investigation/SKILL.md +39 -0
- package/presets/mavis/next/skills/code-review/SKILL.md +39 -0
- package/presets/mavis/next/skills/debug-investigation/SKILL.md +26 -0
- package/presets/mavis/next/skills/feature-delivery/SKILL.md +34 -0
- package/presets/mavis/next/skills/mockup-review/SKILL.md +24 -0
- package/presets/mavis/next/skills/playwright-e2e/SKILL.md +28 -0
- package/presets/mavis/next/skills/react-performance/SKILL.md +39 -0
- package/presets/mavis/next/skills/spec-authoring/SKILL.md +21 -0
- package/presets/mavis/next/skills/spec-discovery/SKILL.md +53 -0
- package/presets/mavis/next/skills/technical-retro/SKILL.md +49 -0
- package/presets/mavis/next/skills/unit-testing/SKILL.md +29 -0
- package/presets/mavis/next/skills/ux-design/SKILL.md +31 -0
- package/presets/mavis/next/skills/write-adr/SKILL.md +36 -0
- package/presets/mavis/next/team/README.md +148 -0
- package/presets/mavis/next/team/conventions.md +21 -0
- package/presets/mavis/next/team/fixtures/artifact-manifest.example.json +79 -0
- package/presets/mavis/next/team/fixtures/bugfix-standard.json +25 -0
- package/presets/mavis/next/team/fixtures/feature-full.json +29 -0
- package/presets/mavis/next/team/fixtures/feature-light.json +27 -0
- package/presets/mavis/next/team/fixtures/feature-with-figma-url.json +17 -0
- package/presets/mavis/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
- package/presets/mavis/next/team/fixtures/memlog.example.md +9 -0
- package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
- package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
- package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
- package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
- package/presets/mavis/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
- package/presets/mavis/next/team/fixtures/metrics.example.json +54 -0
- package/presets/mavis/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
- package/presets/mavis/next/team/fixtures/mockup-review-standalone.json +14 -0
- package/presets/mavis/next/team/fixtures/ux-design-standalone.json +15 -0
- package/presets/mavis/next/team/tasks/.gitkeep +0 -0
- package/presets/mavis/platform-contract.json +131 -0
- package/presets/opencode/README.md +61 -0
- package/presets/opencode/next/MCP.md +36 -0
- package/presets/opencode/next/OPENCODE.md +65 -0
- package/presets/opencode/next/README.md +16 -0
- package/presets/opencode/next/REPO_AGENTS.md +97 -0
- package/presets/opencode/next/adapter-map.json +959 -0
- package/presets/opencode/next/agents/README.md +201 -0
- package/presets/opencode/next/agents/accessibility-reviewer.md +73 -0
- package/presets/opencode/next/agents/api-contract-reviewer.md +69 -0
- package/presets/opencode/next/agents/build-verifier.md +124 -0
- package/presets/opencode/next/agents/ci-investigator.md +71 -0
- package/presets/opencode/next/agents/code-reviewer.md +73 -0
- package/presets/opencode/next/agents/codebase-analyzer.md +33 -0
- package/presets/opencode/next/agents/debugger.md +88 -0
- package/presets/opencode/next/agents/feature-developer.md +109 -0
- package/presets/opencode/next/agents/migration-specialist.md +79 -0
- package/presets/opencode/next/agents/mockup-reviewer.md +65 -0
- package/presets/opencode/next/agents/openspec-author.md +169 -0
- package/presets/opencode/next/agents/performance-auditor.md +80 -0
- package/presets/opencode/next/agents/playwright-test-generator.md +36 -0
- package/presets/opencode/next/agents/playwright-test-healer.md +36 -0
- package/presets/opencode/next/agents/playwright-test-planner.md +33 -0
- package/presets/opencode/next/agents/qa-tester.md +60 -0
- package/presets/opencode/next/agents/security-reviewer.md +66 -0
- package/presets/opencode/next/agents/solution-architect.md +80 -0
- package/presets/opencode/next/agents/task-analyst.md +178 -0
- package/presets/opencode/next/agents/task-router.md +190 -0
- package/presets/opencode/next/agents/tech-writer.md +69 -0
- package/presets/opencode/next/agents/unit-test-generator.md +39 -0
- package/presets/opencode/next/agents/unit-test-healer.md +40 -0
- package/presets/opencode/next/agents/unit-test-planner.md +66 -0
- package/presets/opencode/next/commands/README.md +49 -0
- package/presets/opencode/next/commands/spec-start.md +32 -0
- package/presets/opencode/next/commands/task-continue.md +58 -0
- package/presets/opencode/next/commands/task.md +58 -0
- package/presets/opencode/next/commands/technical-retro.md +88 -0
- package/presets/opencode/next/hooks/README.md +22 -0
- package/presets/opencode/next/hooks/chain-team-phases.sh +845 -0
- package/presets/opencode/next/opencode.json +33 -0
- package/presets/opencode/next/rules/README.md +20 -0
- package/presets/opencode/next/rules/agent-team-intake.md +11 -0
- package/presets/opencode/next/rules/agent-team-orchestrator.md +214 -0
- package/presets/opencode/next/rules/ai-local-overlay.md +37 -0
- package/presets/opencode/next/rules/anti-sycophancy-discipline.md +23 -0
- package/presets/opencode/next/rules/api-services.md +72 -0
- package/presets/opencode/next/rules/architecture-boundaries-ui.md +9 -0
- package/presets/opencode/next/rules/architecture-boundaries.md +86 -0
- package/presets/opencode/next/rules/arrow-functions.md +25 -0
- package/presets/opencode/next/rules/code-quality-and-refactoring.md +40 -0
- package/presets/opencode/next/rules/code-review-mr.md +40 -0
- package/presets/opencode/next/rules/css-property-order-stylelint.md +5 -0
- package/presets/opencode/next/rules/design-guidance.md +99 -0
- package/presets/opencode/next/rules/feature-delivery-workflow.md +49 -0
- package/presets/opencode/next/rules/http-client.md +55 -0
- package/presets/opencode/next/rules/layer-barrel-exports.md +68 -0
- package/presets/opencode/next/rules/mcp-usage.md +86 -0
- package/presets/opencode/next/rules/mockup-review.md +107 -0
- package/presets/opencode/next/rules/navigation-router-stack.md +56 -0
- package/presets/opencode/next/rules/navigation-router-ui.md +10 -0
- package/presets/opencode/next/rules/next-app-core.md +29 -0
- package/presets/opencode/next/rules/next-app-router.md +44 -0
- package/presets/opencode/next/rules/no-props-spread.md +56 -0
- package/presets/opencode/next/rules/no-type-assertion-as-import-export.md +51 -0
- package/presets/opencode/next/rules/package-manager.md +11 -0
- package/presets/opencode/next/rules/playwright-agents.md +67 -0
- package/presets/opencode/next/rules/post-change-lint.md +35 -0
- package/presets/opencode/next/rules/preset-layering.md +27 -0
- package/presets/opencode/next/rules/preset-no-cross-stack-leakage.md +90 -0
- package/presets/opencode/next/rules/preset-pr-checklist.md +36 -0
- package/presets/opencode/next/rules/preset-token-budget.md +39 -0
- package/presets/opencode/next/rules/preset-twin-sync.md +81 -0
- package/presets/opencode/next/rules/product-specs-authoring.md +112 -0
- package/presets/opencode/next/rules/product-specs.md +56 -0
- package/presets/opencode/next/rules/public-imports.md +50 -0
- package/presets/opencode/next/rules/react-a11y-coding.md +31 -0
- package/presets/opencode/next/rules/react-next-anti-hallucination.md +60 -0
- package/presets/opencode/next/rules/react-ui.md +105 -0
- package/presets/opencode/next/rules/reference-features.md +45 -0
- package/presets/opencode/next/rules/security-next.md +45 -0
- package/presets/opencode/next/rules/store-rtk.md +78 -0
- package/presets/opencode/next/rules/technical-retro.md +11 -0
- package/presets/opencode/next/rules/tests-e2e-structure.md +72 -0
- package/presets/opencode/next/rules/tests-unit.md +72 -0
- package/presets/opencode/next/rules/types-jsdoc.md +43 -0
- package/presets/opencode/next/rules/ux-design.md +99 -0
- package/presets/opencode/next/skills/README.md +21 -0
- package/presets/opencode/next/skills/ci-investigation/SKILL.md +44 -0
- package/presets/opencode/next/skills/code-review/SKILL.md +44 -0
- package/presets/opencode/next/skills/debug-investigation/SKILL.md +59 -0
- package/presets/opencode/next/skills/feature-delivery/SKILL.md +39 -0
- package/presets/opencode/next/skills/mockup-review/SKILL.md +29 -0
- package/presets/opencode/next/skills/playwright-e2e/SKILL.md +33 -0
- package/presets/opencode/next/skills/react-performance/SKILL.md +44 -0
- package/presets/opencode/next/skills/spec-authoring/SKILL.md +26 -0
- package/presets/opencode/next/skills/spec-discovery/SKILL.md +58 -0
- package/presets/opencode/next/skills/technical-retro/SKILL.md +54 -0
- package/presets/opencode/next/skills/unit-testing/SKILL.md +34 -0
- package/presets/opencode/next/skills/ux-design/SKILL.md +36 -0
- package/presets/opencode/next/skills/write-adr/SKILL.md +41 -0
- package/presets/opencode/next/team/README.md +148 -0
- package/presets/opencode/next/team/conventions.md +21 -0
- package/presets/opencode/next/team/fixtures/artifact-manifest.example.json +79 -0
- package/presets/opencode/next/team/fixtures/bugfix-standard.json +25 -0
- package/presets/opencode/next/team/fixtures/feature-full.json +29 -0
- package/presets/opencode/next/team/fixtures/feature-light.json +27 -0
- package/presets/opencode/next/team/fixtures/feature-with-figma-url.json +17 -0
- package/presets/opencode/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
- package/presets/opencode/next/team/fixtures/memlog.example.md +9 -0
- package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
- package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
- package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
- package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
- package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
- package/presets/opencode/next/team/fixtures/metrics.example.json +54 -0
- package/presets/opencode/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
- package/presets/opencode/next/team/fixtures/mockup-review-standalone.json +14 -0
- package/presets/opencode/next/team/fixtures/ux-design-standalone.json +15 -0
- package/presets/opencode/next/team/tasks/.gitkeep +0 -0
- package/presets/opencode/platform-contract.json +131 -0
- package/scripts/capture-cli-golden.mjs +48 -0
- package/scripts/check-chain-hook-coverage.sh +2 -0
- package/scripts/check-mavis-adapter-map.mjs +294 -0
- package/scripts/check-mavis-local-overlay.mjs +56 -0
- package/scripts/check-mavis-platform-contract.mjs +388 -0
- package/scripts/check-mavis-token-budget.mjs +48 -0
- package/scripts/check-opencode-adapter-map.mjs +294 -0
- package/scripts/check-opencode-local-overlay.mjs +56 -0
- package/scripts/check-opencode-platform-contract.mjs +388 -0
- package/scripts/check-opencode-token-budget.mjs +48 -0
- package/scripts/check-preset-leakage.sh +64 -3
- package/scripts/check-preset-structure.sh +108 -0
- package/scripts/check-shared-core-drift.sh +18 -0
- package/scripts/check-task-router-intents.sh +15 -2
- package/scripts/fixtures/cli-golden/claude/next.json +130 -0
- package/scripts/fixtures/cli-golden/cursor/next.json +132 -0
- package/scripts/fixtures/cli-golden/mavis/next.json +125 -0
- package/scripts/fixtures/cli-golden/opencode/next.json +125 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/MAVIS.md +1 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/adapter-map.json +26 -0
- package/scripts/fixtures/mavis-adapter-map/invalid-unmapped/mavis/rules/sample.md +2 -0
- package/scripts/fixtures/mavis-adapter-map/valid/cursor/AGENTS.md +1 -0
- package/scripts/fixtures/mavis-adapter-map/valid/cursor/rules/sample.mdc +2 -0
- package/scripts/fixtures/mavis-adapter-map/valid/mavis/MAVIS.md +1 -0
- package/scripts/fixtures/mavis-adapter-map/valid/mavis/adapter-map.json +26 -0
- package/scripts/fixtures/mavis-adapter-map/valid/mavis/rules/sample.md +2 -0
- package/scripts/fixtures/mavis-platform-contract/invalid-minimax-destination.json +131 -0
- package/scripts/fixtures/mavis-platform-contract/invalid-missing-capability.json +126 -0
- package/scripts/fixtures/mavis-platform-contract/invalid-released-unknown-stack.json +133 -0
- package/scripts/fixtures/mavis-platform-contract/invalid-unknown-native-path.json +131 -0
- package/scripts/fixtures/mavis-platform-contract/invalid-version-pin.json +132 -0
- package/scripts/fixtures/mavis-platform-contract/valid.json +131 -0
- package/scripts/fixtures/mavis-probe/AGENTS.md +6 -0
- package/scripts/fixtures/mavis-probe/agents/probe-agent.md +6 -0
- package/scripts/fixtures/mavis-probe/fake-mcode.mjs +98 -0
- package/scripts/fixtures/mavis-probe/output.schema.json +28 -0
- package/scripts/fixtures/mavis-probe/rules/probe.md +5 -0
- package/scripts/fixtures/mavis-probe/skills/probe/SKILL.md +11 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/OPENCODE.md +1 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/adapter-map.json +26 -0
- package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/rules/sample.md +2 -0
- package/scripts/fixtures/opencode-adapter-map/valid/cursor/AGENTS.md +1 -0
- package/scripts/fixtures/opencode-adapter-map/valid/cursor/rules/sample.mdc +2 -0
- package/scripts/fixtures/opencode-adapter-map/valid/opencode/OPENCODE.md +1 -0
- package/scripts/fixtures/opencode-adapter-map/valid/opencode/adapter-map.json +26 -0
- package/scripts/fixtures/opencode-adapter-map/valid/opencode/rules/sample.md +2 -0
- package/scripts/fixtures/opencode-platform-contract/invalid-home-destination.json +131 -0
- package/scripts/fixtures/opencode-platform-contract/invalid-missing-capability.json +126 -0
- package/scripts/fixtures/opencode-platform-contract/invalid-released-unknown-stack.json +133 -0
- package/scripts/fixtures/opencode-platform-contract/invalid-unknown-native-path.json +131 -0
- package/scripts/fixtures/opencode-platform-contract/invalid-version-pin.json +132 -0
- package/scripts/fixtures/opencode-platform-contract/valid.json +131 -0
- package/scripts/generate-mavis-next.mjs +706 -0
- package/scripts/generate-opencode-next.mjs +831 -0
- package/scripts/lib/cli-golden.mjs +50 -0
- package/scripts/lib/discover-hooks.mjs +94 -0
- package/scripts/probe-mavis-capabilities.mjs +480 -0
- package/scripts/sync-dogfood-mavis.sh +90 -0
- package/scripts/sync-dogfood-opencode.sh +85 -0
- package/scripts/test-agent-task-metrics-hooks.mjs +25 -16
- package/scripts/test-chain-team-phases-coverage.mjs +15 -7
- package/scripts/test-cli-init-clean.mjs +367 -0
- package/scripts/test-cli-mcp-merge.mjs +23 -0
- package/scripts/test-mavis-context-budget.mjs +55 -0
- package/scripts/test-mavis-orchestration.mjs +649 -0
- package/scripts/test-opencode-context-budget.mjs +53 -0
- package/scripts/test-probe-mavis-capabilities.mjs +205 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest-owned OpenCode init/clean. Does not write ~/.config/opencode/**.
|
|
3
|
+
*/
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const MANIFEST_REL = path.join('.opencode', '.ai-rules-install.json');
|
|
9
|
+
const CONTRACT_REL = path.join('presets', 'opencode', 'platform-contract.json');
|
|
10
|
+
const SKIP_STACK_ROOT = new Set(['adapter-map.json', 'README.md']);
|
|
11
|
+
|
|
12
|
+
function sha256File(filePath) {
|
|
13
|
+
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function failPhase() {
|
|
17
|
+
return process.env.AI_RULES_OPENCODE_FAIL_PHASE || '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function throwIfPhase(name) {
|
|
21
|
+
if (failPhase() === name) {
|
|
22
|
+
throw new Error(`injected failure at ${name}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function loadJson(filePath) {
|
|
27
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function walkFiles(root) {
|
|
31
|
+
const files = [];
|
|
32
|
+
const visit = (dir) => {
|
|
33
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
34
|
+
const full = path.join(dir, entry.name);
|
|
35
|
+
if (entry.isDirectory()) {
|
|
36
|
+
visit(full);
|
|
37
|
+
} else if (entry.isFile()) {
|
|
38
|
+
files.push(path.relative(root, full).split(path.sep).join('/'));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
if (fs.existsSync(root)) {
|
|
43
|
+
visit(root);
|
|
44
|
+
}
|
|
45
|
+
return files.sort();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function writeAtomicCopy(source, dest) {
|
|
49
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
50
|
+
const tmp = `${dest}.ai-rules-tmp`;
|
|
51
|
+
fs.copyFileSync(source, tmp);
|
|
52
|
+
if (dest.endsWith('.sh')) {
|
|
53
|
+
fs.chmodSync(tmp, 0o755);
|
|
54
|
+
}
|
|
55
|
+
fs.renameSync(tmp, dest);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function writeAtomicText(dest, text) {
|
|
59
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
60
|
+
const tmp = `${dest}.ai-rules-tmp`;
|
|
61
|
+
fs.writeFileSync(tmp, text);
|
|
62
|
+
fs.renameSync(tmp, dest);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function loadContract(packageRoot) {
|
|
66
|
+
const contractPath = path.join(packageRoot, CONTRACT_REL);
|
|
67
|
+
if (!fs.existsSync(contractPath)) {
|
|
68
|
+
throw new Error(`OpenCode platform contract missing: ${contractPath}`);
|
|
69
|
+
}
|
|
70
|
+
return loadJson(contractPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function packageVersion(packageRoot) {
|
|
74
|
+
return loadJson(path.join(packageRoot, 'package.json')).version;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isPlainObject(value) {
|
|
78
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function ownKeys(object) {
|
|
82
|
+
return Object.keys(object).filter((key) => Object.hasOwn(object, key));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildPlan({ contract, sourceBase, cwd }) {
|
|
86
|
+
const files = [];
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const addFile = (source, destRel) => {
|
|
89
|
+
if (seen.has(destRel)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
seen.add(destRel);
|
|
93
|
+
files.push({
|
|
94
|
+
source,
|
|
95
|
+
dest: path.join(cwd, destRel),
|
|
96
|
+
rel: destRel,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
for (const entry of contract.entryFiles || []) {
|
|
101
|
+
const source = path.join(sourceBase, entry.source);
|
|
102
|
+
if (fs.existsSync(source)) {
|
|
103
|
+
addFile(source, entry.destination);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const mapping of contract.directoryMappings || []) {
|
|
108
|
+
const sourceDir = path.join(sourceBase, mapping.source);
|
|
109
|
+
if (!fs.existsSync(sourceDir)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
for (const rel of walkFiles(sourceDir)) {
|
|
113
|
+
addFile(path.join(sourceDir, rel), `${mapping.destination}/${rel}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const mcpMd = path.join(sourceBase, 'MCP.md');
|
|
118
|
+
if (fs.existsSync(mcpMd)) {
|
|
119
|
+
addFile(mcpMd, path.join(contract.managedRoot, 'MCP.md'));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const mcpSource = path.join(sourceBase, contract.mcp.source);
|
|
123
|
+
const mcp = fs.existsSync(mcpSource)
|
|
124
|
+
? {
|
|
125
|
+
source: mcpSource,
|
|
126
|
+
dest: path.join(cwd, contract.mcp.fallbackDestination),
|
|
127
|
+
rel: contract.mcp.fallbackDestination,
|
|
128
|
+
}
|
|
129
|
+
: null;
|
|
130
|
+
|
|
131
|
+
for (const skip of SKIP_STACK_ROOT) {
|
|
132
|
+
const skipped = path.join(sourceBase, skip);
|
|
133
|
+
if (fs.existsSync(skipped)) {
|
|
134
|
+
const accidental = files.find((item) => item.source === skipped);
|
|
135
|
+
if (accidental) {
|
|
136
|
+
throw new Error(`refusing to install stack-root ${skip}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { files, mcp };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function capabilityModes(contract) {
|
|
145
|
+
const modes = {};
|
|
146
|
+
for (const [key, value] of Object.entries(contract.nativeCapabilities || {})) {
|
|
147
|
+
modes[key] = value.status;
|
|
148
|
+
}
|
|
149
|
+
return modes;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function shippedMcpIds(mcpSource) {
|
|
153
|
+
if (!mcpSource || !fs.existsSync(mcpSource)) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
const parsed = loadJson(mcpSource);
|
|
157
|
+
if (!parsed || typeof parsed.mcp !== 'object' || parsed.mcp === null) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
return Object.keys(parsed.mcp);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function previousManifest(cwd) {
|
|
164
|
+
const manifestPath = path.join(cwd, MANIFEST_REL);
|
|
165
|
+
if (!fs.existsSync(manifestPath)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
return loadJson(manifestPath);
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function mergeServerEntry(existingEntry, presetEntry) {
|
|
176
|
+
if (!isPlainObject(existingEntry) || !isPlainObject(presetEntry)) {
|
|
177
|
+
return presetEntry;
|
|
178
|
+
}
|
|
179
|
+
const merged = { ...existingEntry, ...presetEntry };
|
|
180
|
+
for (const nested of ['env', 'headers', 'environment']) {
|
|
181
|
+
const existingNested = existingEntry[nested];
|
|
182
|
+
const presetNested = presetEntry[nested];
|
|
183
|
+
if (isPlainObject(existingNested) && isPlainObject(presetNested)) {
|
|
184
|
+
merged[nested] = { ...existingNested, ...presetNested };
|
|
185
|
+
} else if (isPlainObject(existingNested) && presetNested === undefined) {
|
|
186
|
+
merged[nested] = existingNested;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return merged;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function mergeOpencodeConfig(presetObj, existingObj) {
|
|
193
|
+
const preset = isPlainObject(presetObj) ? presetObj : {};
|
|
194
|
+
const existing = isPlainObject(existingObj) ? existingObj : {};
|
|
195
|
+
const existingServersInvalid = Object.hasOwn(existing, 'mcp') && !isPlainObject(existing.mcp);
|
|
196
|
+
const presetMcp = isPlainObject(preset.mcp) ? preset.mcp : {};
|
|
197
|
+
const existingMcp =
|
|
198
|
+
!existingServersInvalid && isPlainObject(existing.mcp) ? existing.mcp : {};
|
|
199
|
+
const extrasKept = ownKeys(existingMcp).filter((key) => !Object.hasOwn(presetMcp, key));
|
|
200
|
+
const mergedMcp = { ...existingMcp };
|
|
201
|
+
for (const key of ownKeys(presetMcp)) {
|
|
202
|
+
mergedMcp[key] = mergeServerEntry(existingMcp[key], presetMcp[key]);
|
|
203
|
+
}
|
|
204
|
+
const merged = { ...existing };
|
|
205
|
+
if (!merged.$schema && preset.$schema) {
|
|
206
|
+
merged.$schema = preset.$schema;
|
|
207
|
+
}
|
|
208
|
+
merged.mcp = mergedMcp;
|
|
209
|
+
return { merged, extrasKept, existingServersInvalid };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function backupFile(destAbs) {
|
|
213
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
214
|
+
const bak = `${destAbs}.bak.${stamp}`;
|
|
215
|
+
fs.copyFileSync(destAbs, bak);
|
|
216
|
+
return bak;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function installOpencodeJson(mcpFrom, mcpDestAbs, destLabel) {
|
|
220
|
+
const presetObj = loadJson(mcpFrom);
|
|
221
|
+
if (!fs.existsSync(mcpDestAbs)) {
|
|
222
|
+
fs.mkdirSync(path.dirname(mcpDestAbs), { recursive: true });
|
|
223
|
+
fs.copyFileSync(mcpFrom, mcpDestAbs);
|
|
224
|
+
console.log(`Copied ${path.basename(mcpFrom)} → ${destLabel}`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
let existingObj;
|
|
228
|
+
try {
|
|
229
|
+
existingObj = loadJson(mcpDestAbs);
|
|
230
|
+
} catch {
|
|
231
|
+
const bak = backupFile(mcpDestAbs);
|
|
232
|
+
fs.copyFileSync(mcpFrom, mcpDestAbs);
|
|
233
|
+
console.warn(`Existing ${destLabel} was not valid JSON; backed up to ${bak} and replaced.`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (!isPlainObject(existingObj)) {
|
|
237
|
+
const bak = backupFile(mcpDestAbs);
|
|
238
|
+
fs.copyFileSync(mcpFrom, mcpDestAbs);
|
|
239
|
+
console.warn(`Existing ${destLabel} was not a JSON object; backed up to ${bak} and replaced.`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const { merged, extrasKept, existingServersInvalid } = mergeOpencodeConfig(
|
|
243
|
+
presetObj,
|
|
244
|
+
existingObj,
|
|
245
|
+
);
|
|
246
|
+
if (existingServersInvalid) {
|
|
247
|
+
const bak = backupFile(mcpDestAbs);
|
|
248
|
+
console.warn(
|
|
249
|
+
`Existing ${destLabel} mcp was not a JSON object; backed up to ${bak} and merging as empty extras.`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
writeAtomicText(mcpDestAbs, `${JSON.stringify(merged, null, 2)}\n`);
|
|
253
|
+
if (extrasKept.length > 0) {
|
|
254
|
+
console.log(
|
|
255
|
+
`Merged ${path.basename(mcpFrom)} → ${destLabel} (kept extra servers: ${extrasKept.join(', ')})`,
|
|
256
|
+
);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(`Merged ${path.basename(mcpFrom)} → ${destLabel}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function restoreRollback(overwritten, created, rollbackRoot, managedRoot, rootExisted) {
|
|
263
|
+
for (const item of overwritten) {
|
|
264
|
+
try {
|
|
265
|
+
if (fs.existsSync(item.backup)) {
|
|
266
|
+
fs.mkdirSync(path.dirname(item.dest), { recursive: true });
|
|
267
|
+
fs.copyFileSync(item.backup, item.dest);
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
// Best-effort restore; caller already reports the failure.
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const createdPaths = [...created].sort((a, b) => b.length - a.length);
|
|
274
|
+
for (const dest of createdPaths) {
|
|
275
|
+
try {
|
|
276
|
+
if (!fs.existsSync(dest)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const stat = fs.lstatSync(dest);
|
|
280
|
+
if (stat.isDirectory()) {
|
|
281
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
282
|
+
} else {
|
|
283
|
+
fs.unlinkSync(dest);
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
// Best-effort.
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (fs.existsSync(rollbackRoot)) {
|
|
290
|
+
fs.rmSync(rollbackRoot, { recursive: true, force: true });
|
|
291
|
+
}
|
|
292
|
+
if (!rootExisted && fs.existsSync(managedRoot)) {
|
|
293
|
+
fs.rmSync(managedRoot, { recursive: true, force: true });
|
|
294
|
+
} else if (rootExisted && fs.existsSync(managedRoot)) {
|
|
295
|
+
removeEmptyDirs(managedRoot);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function cmdInitOpencode({ preset, cwdOption, helpers }) {
|
|
300
|
+
const { resolveTargetCwd, seedDocsSpecsIfAbsent, getPackageRoot } = helpers;
|
|
301
|
+
const packageRoot = getPackageRoot();
|
|
302
|
+
const cwd = resolveTargetCwd(cwdOption);
|
|
303
|
+
const contract = loadContract(packageRoot);
|
|
304
|
+
const stack = preset || 'next';
|
|
305
|
+
const releasedStacks = Array.isArray(contract.releasedStacks) ? contract.releasedStacks : [];
|
|
306
|
+
if (releasedStacks.length > 0 && !releasedStacks.includes(stack)) {
|
|
307
|
+
console.error(`Preset "${stack}" for opencode is not released yet.`);
|
|
308
|
+
console.error(`Released stacks: ${releasedStacks.join(', ')}`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
const sourceBase = path.join(packageRoot, contract.sourceRoot, stack);
|
|
312
|
+
if (!fs.existsSync(sourceBase)) {
|
|
313
|
+
console.error(`Preset "${stack}" for opencode not found.`);
|
|
314
|
+
console.error(`Looked in: ${sourceBase}`);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const managedRoot = path.join(cwd, contract.managedRoot);
|
|
319
|
+
const rootExisted = fs.existsSync(managedRoot);
|
|
320
|
+
const docsSpecsDir = path.join(cwd, 'docs', 'specs');
|
|
321
|
+
const docsSpecsExisted = fs.existsSync(docsSpecsDir);
|
|
322
|
+
const prior = previousManifest(cwd);
|
|
323
|
+
const runId = new Date().toISOString().replace(/[:.]/g, '-');
|
|
324
|
+
const rollbackRoot = path.join(managedRoot, '.rollback', runId);
|
|
325
|
+
const created = new Set();
|
|
326
|
+
const overwritten = [];
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
throwIfPhase('validate');
|
|
330
|
+
const plan = buildPlan({ contract, sourceBase, cwd });
|
|
331
|
+
for (const item of plan.files) {
|
|
332
|
+
if (!fs.existsSync(item.source)) {
|
|
333
|
+
throw new Error(`Source missing: ${item.source}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
throwIfPhase('snapshot');
|
|
338
|
+
for (const item of plan.files) {
|
|
339
|
+
if (!fs.existsSync(item.dest)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const backup = path.join(rollbackRoot, item.rel);
|
|
343
|
+
fs.mkdirSync(path.dirname(backup), { recursive: true });
|
|
344
|
+
fs.copyFileSync(item.dest, backup);
|
|
345
|
+
overwritten.push({ dest: item.dest, backup });
|
|
346
|
+
const priorHash = prior && prior.managedFiles ? prior.managedFiles[item.rel] : null;
|
|
347
|
+
const currentHash = sha256File(item.dest);
|
|
348
|
+
if (priorHash && priorHash !== currentHash) {
|
|
349
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
350
|
+
const reported = `${item.dest}.bak.${stamp}`;
|
|
351
|
+
fs.copyFileSync(item.dest, reported);
|
|
352
|
+
console.warn(`Modified managed file ${item.rel} backed up to ${reported} before overwrite.`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (plan.mcp && fs.existsSync(plan.mcp.dest)) {
|
|
356
|
+
const backup = path.join(rollbackRoot, plan.mcp.rel);
|
|
357
|
+
fs.mkdirSync(path.dirname(backup), { recursive: true });
|
|
358
|
+
fs.copyFileSync(plan.mcp.dest, backup);
|
|
359
|
+
overwritten.push({ dest: plan.mcp.dest, backup });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
throwIfPhase('write');
|
|
363
|
+
for (const item of plan.files) {
|
|
364
|
+
if (!fs.existsSync(item.dest)) {
|
|
365
|
+
created.add(item.dest);
|
|
366
|
+
}
|
|
367
|
+
writeAtomicCopy(item.source, item.dest);
|
|
368
|
+
console.log(`Copied ${path.basename(item.source)} → ${item.rel}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
throwIfPhase('mcp');
|
|
372
|
+
if (plan.mcp) {
|
|
373
|
+
if (!fs.existsSync(plan.mcp.dest)) {
|
|
374
|
+
created.add(plan.mcp.dest);
|
|
375
|
+
}
|
|
376
|
+
installOpencodeJson(plan.mcp.source, plan.mcp.dest, plan.mcp.rel);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
throwIfPhase('docs');
|
|
380
|
+
const seeded = seedDocsSpecsIfAbsent(packageRoot, cwd);
|
|
381
|
+
if (seeded && !docsSpecsExisted) {
|
|
382
|
+
created.add(docsSpecsDir);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
throwIfPhase('manifest');
|
|
386
|
+
const managedFiles = {};
|
|
387
|
+
for (const item of plan.files) {
|
|
388
|
+
managedFiles[item.rel] = sha256File(item.dest);
|
|
389
|
+
}
|
|
390
|
+
if (plan.mcp && fs.existsSync(plan.mcp.dest)) {
|
|
391
|
+
managedFiles[plan.mcp.rel] = sha256File(plan.mcp.dest);
|
|
392
|
+
}
|
|
393
|
+
const manifest = {
|
|
394
|
+
schemaVersion: 1,
|
|
395
|
+
packageVersion: packageVersion(packageRoot),
|
|
396
|
+
platform: 'opencode',
|
|
397
|
+
preset: stack,
|
|
398
|
+
installedAt: new Date().toISOString(),
|
|
399
|
+
managedFiles,
|
|
400
|
+
managedDirectories: (contract.directoryMappings || []).map((entry) => entry.destination),
|
|
401
|
+
shippedMcpServerIds: plan.mcp ? shippedMcpIds(plan.mcp.source) : [],
|
|
402
|
+
docsSeed: seeded
|
|
403
|
+
? { seeded: true }
|
|
404
|
+
: { seeded: false, reason: docsSpecsExisted ? 'directory-exists' : 'assets-missing' },
|
|
405
|
+
capabilityModes: capabilityModes(contract),
|
|
406
|
+
};
|
|
407
|
+
const manifestAbs = path.join(cwd, MANIFEST_REL);
|
|
408
|
+
if (!fs.existsSync(manifestAbs)) {
|
|
409
|
+
created.add(manifestAbs);
|
|
410
|
+
}
|
|
411
|
+
writeAtomicText(manifestAbs, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
412
|
+
|
|
413
|
+
if (fs.existsSync(rollbackRoot)) {
|
|
414
|
+
fs.rmSync(rollbackRoot, { recursive: true, force: true });
|
|
415
|
+
}
|
|
416
|
+
const rollbackParent = path.join(managedRoot, '.rollback');
|
|
417
|
+
if (fs.existsSync(rollbackParent) && fs.readdirSync(rollbackParent).length === 0) {
|
|
418
|
+
fs.rmdirSync(rollbackParent);
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
restoreRollback(overwritten, created, rollbackRoot, managedRoot, rootExisted);
|
|
422
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
423
|
+
console.error(`init failed: ${message}`);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
console.log(`Done. .opencode installed at ${managedRoot} (preset: ${stack})`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function stripShippedMcp(mcpAbs, shippedIds) {
|
|
431
|
+
if (!fs.existsSync(mcpAbs) || shippedIds.length === 0) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
let parsed;
|
|
435
|
+
try {
|
|
436
|
+
parsed = loadJson(mcpAbs);
|
|
437
|
+
} catch {
|
|
438
|
+
console.warn(`Preserved ${mcpAbs} (not valid JSON); shipped MCP keys not stripped.`);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (!isPlainObject(parsed) || !isPlainObject(parsed.mcp)) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const nextServers = { ...parsed.mcp };
|
|
445
|
+
for (const id of shippedIds) {
|
|
446
|
+
delete nextServers[id];
|
|
447
|
+
}
|
|
448
|
+
writeAtomicText(mcpAbs, `${JSON.stringify({ ...parsed, mcp: nextServers }, null, 2)}\n`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function removeEmptyDirs(dir) {
|
|
452
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
for (const name of fs.readdirSync(dir)) {
|
|
456
|
+
removeEmptyDirs(path.join(dir, name));
|
|
457
|
+
}
|
|
458
|
+
if (fs.readdirSync(dir).length === 0) {
|
|
459
|
+
fs.rmdirSync(dir);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function cmdCleanOpencode({ cwdOption, helpers }) {
|
|
464
|
+
const { resolveTargetCwd } = helpers;
|
|
465
|
+
const cwd = resolveTargetCwd(cwdOption);
|
|
466
|
+
const manifestPath = path.join(cwd, MANIFEST_REL);
|
|
467
|
+
if (!fs.existsSync(manifestPath)) {
|
|
468
|
+
console.error(
|
|
469
|
+
'clean opencode: missing .opencode/.ai-rules-install.json; nothing removed (fail-safe).',
|
|
470
|
+
);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
let manifest;
|
|
474
|
+
try {
|
|
475
|
+
manifest = loadJson(manifestPath);
|
|
476
|
+
} catch {
|
|
477
|
+
console.error(
|
|
478
|
+
'clean opencode: invalid .opencode/.ai-rules-install.json; nothing removed (fail-safe).',
|
|
479
|
+
);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
if (!manifest || manifest.platform !== 'opencode' || !isPlainObject(manifest.managedFiles)) {
|
|
483
|
+
console.error(
|
|
484
|
+
'clean opencode: install manifest is not an OpenCode ownership record; nothing removed.',
|
|
485
|
+
);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const preserved = [];
|
|
490
|
+
for (const [rel, installedHash] of Object.entries(manifest.managedFiles)) {
|
|
491
|
+
if (rel === MANIFEST_REL) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const abs = path.join(cwd, rel);
|
|
495
|
+
if (!fs.existsSync(abs)) {
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const current = sha256File(abs);
|
|
499
|
+
if (current === installedHash) {
|
|
500
|
+
fs.unlinkSync(abs);
|
|
501
|
+
} else {
|
|
502
|
+
preserved.push(rel);
|
|
503
|
+
console.warn(`Preserved modified managed file ${rel}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (preserved.includes('opencode.json') && Array.isArray(manifest.shippedMcpServerIds)) {
|
|
508
|
+
stripShippedMcp(path.join(cwd, 'opencode.json'), manifest.shippedMcpServerIds);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
for (const dirRel of [...(manifest.managedDirectories || [])].sort((a, b) => b.length - a.length)) {
|
|
512
|
+
removeEmptyDirs(path.join(cwd, dirRel));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
fs.unlinkSync(manifestPath);
|
|
516
|
+
removeEmptyDirs(path.join(cwd, '.opencode'));
|
|
517
|
+
if (
|
|
518
|
+
fs.existsSync(path.join(cwd, '.opencode')) &&
|
|
519
|
+
fs.readdirSync(path.join(cwd, '.opencode')).length === 0
|
|
520
|
+
) {
|
|
521
|
+
fs.rmdirSync(path.join(cwd, '.opencode'));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
console.log(
|
|
525
|
+
preserved.length > 0
|
|
526
|
+
? `clean opencode: removed unchanged managed files; preserved ${preserved.join(', ')}`
|
|
527
|
+
: 'clean opencode: removed unchanged managed files',
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
module.exports = { cmdInitOpencode, cmdCleanOpencode, MANIFEST_REL };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bonesofspring/ai-rules",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Presets of Cursor and
|
|
3
|
+
"version": "0.2.24",
|
|
4
|
+
"description": "Presets of Cursor, Claude, Mavis, and OpenCode rules/commands for Revy Ross personal use",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Revy Ross",
|
|
7
7
|
"repository": {
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"keywords": [
|
|
12
12
|
"cursor",
|
|
13
13
|
"claude",
|
|
14
|
+
"opencode",
|
|
15
|
+
"mavis",
|
|
14
16
|
"rules",
|
|
15
17
|
"ai",
|
|
16
18
|
"bonesofspring"
|
|
@@ -38,8 +40,20 @@
|
|
|
38
40
|
"check:product-specs:dogfood": "bash scripts/check-product-specs.sh ../../dogfood/mcp-ts-sample",
|
|
39
41
|
"check:rule-eval": "node scripts/check-rule-eval.mjs",
|
|
40
42
|
"test:cli-mcp-merge": "node scripts/test-cli-mcp-merge.mjs",
|
|
43
|
+
"test:cli-init-clean": "node scripts/test-cli-init-clean.mjs",
|
|
41
44
|
"test:chain-coverage": "node scripts/test-chain-team-phases-coverage.mjs",
|
|
42
|
-
"test:agent-task-metrics-hooks": "node scripts/test-agent-task-metrics-hooks.mjs"
|
|
45
|
+
"test:agent-task-metrics-hooks": "node scripts/test-agent-task-metrics-hooks.mjs",
|
|
46
|
+
"check:mavis-platform-contract": "node scripts/check-mavis-platform-contract.mjs --self-test",
|
|
47
|
+
"check:mavis-adapter-map": "node scripts/check-mavis-adapter-map.mjs --self-test",
|
|
48
|
+
"check:mavis-token-budget": "node scripts/check-mavis-token-budget.mjs",
|
|
49
|
+
"check:mavis-local-overlay": "node scripts/check-mavis-local-overlay.mjs",
|
|
50
|
+
"test:mavis-context-budget": "node scripts/test-mavis-context-budget.mjs",
|
|
51
|
+
"test:mavis-orchestration": "node scripts/test-mavis-orchestration.mjs",
|
|
52
|
+
"check:opencode-platform-contract": "node scripts/check-opencode-platform-contract.mjs --self-test",
|
|
53
|
+
"check:opencode-adapter-map": "node scripts/check-opencode-adapter-map.mjs --self-test",
|
|
54
|
+
"check:opencode-token-budget": "node scripts/check-opencode-token-budget.mjs",
|
|
55
|
+
"check:opencode-local-overlay": "node scripts/check-opencode-local-overlay.mjs",
|
|
56
|
+
"test:opencode-context-budget": "node scripts/test-opencode-context-budget.mjs"
|
|
43
57
|
},
|
|
44
58
|
"engines": {
|
|
45
59
|
"node": ">=18"
|
|
@@ -192,9 +192,9 @@ If `pipeline.json` is missing, fall back to fixed phases: analysis → developme
|
|
|
192
192
|
|
|
193
193
|
Do **not** run `/task` + router for:
|
|
194
194
|
|
|
195
|
-
- Pure questions (
|
|
195
|
+
- Pure questions («как работает X» (how does X work), «объясни» (explain)) — prefer agent **`codebase-analyzer`** for deep traces; inline OK for trivial one-liners. <!-- i18n-keep: E4 -->
|
|
196
196
|
- Typo / one-file fix / trivial config with no architecture risk.
|
|
197
|
-
- User explicitly says
|
|
197
|
+
- User explicitly says «без pipeline» (no pipeline), «просто сделай» (just do it), or continues an active slug. <!-- i18n-keep: E4 -->
|
|
198
198
|
- Single-line `docs-only` with no code impact.
|
|
199
199
|
|
|
200
200
|
Borderline work requests — see **agent-team-intake**.
|
|
@@ -7,14 +7,14 @@ Rules for **preset authors** (`packages/ai-rules/presets/**`). Not always-on in
|
|
|
7
7
|
| Layer | Where | What to put |
|
|
8
8
|
|-------|-------|-------------|
|
|
9
9
|
| **Core** | `presets/_shared/core/**` | Stack-agnostic prose + checklists; no frontmatter; no paths `app/**`, `Features/**`, Playwright, xcodebuild |
|
|
10
|
-
| **Stack** | `presets/{cursor\|claude}/<stack>/` | Domain/tooling, stack always-on trio, stack examples in **Stack notes** |
|
|
11
|
-
| **Platform** | Cursor `.mdc` / Claude `.md` | Frontmatter (`alwaysApply`/`globs` vs `paths:`), platform-only (`BUGBOT.md`, `CLAUDE.md`) |
|
|
10
|
+
| **Stack** | `presets/{cursor\|claude\|mavis\|opencode}/<stack>/` | Domain/tooling, stack always-on trio, stack examples in **Stack notes** |
|
|
11
|
+
| **Platform** | Cursor `.mdc` / Claude `.md` / Mavis `.md` / OpenCode `.md` | Frontmatter (`alwaysApply`/`globs` vs `paths:` vs OpenCode `mode`/`permission`), platform-only (`BUGBOT.md`, `CLAUDE.md`) |
|
|
12
12
|
|
|
13
13
|
## Where to put a new rule
|
|
14
14
|
|
|
15
15
|
1. Shared principles for all stacks → **core** + adapters with `<!-- shared-core: … -->`.
|
|
16
16
|
2. Only one of the installable stacks (`next` / `nuxt` / `svelte` / `ios-swift` / `android-kotlin` / `go` / `java` / `mcp-ts` / `php-hexagonal` / `php-laravel`) → **stack-specific** (not in `_shared`).
|
|
17
|
-
3. Only Cursor or
|
|
17
|
+
3. Only Cursor, Claude, Mavis, or OpenCode packaging → platform artifact / frontmatter; do not duplicate semantics.
|
|
18
18
|
|
|
19
19
|
## SoT
|
|
20
20
|
|
|
@@ -85,4 +85,4 @@ Required instead: package-manager–discovered typecheck/test (`mcp-ts-tooling`,
|
|
|
85
85
|
|
|
86
86
|
## Chain fork
|
|
87
87
|
|
|
88
|
-
`hooks/chain-team-phases.sh` must differ **byte-wise** pairwise among `next`, `nuxt`, `svelte`, `ios-swift`, `android-kotlin`, `go`, `java`, `mcp-ts`, `php-hexagonal`, and `php-laravel` (per platform).
|
|
88
|
+
`hooks/chain-team-phases.sh` must differ **byte-wise** pairwise among `next`, `nuxt`, `svelte`, `ios-swift`, `android-kotlin`, `go`, `java`, `mcp-ts`, `php-hexagonal`, and `php-laravel` (per platform). Released Mavis/OpenCode stacks must also differ from the Cursor and Claude twins of the same stack.
|
|
@@ -7,17 +7,19 @@ Checklist for PRs that touch `packages/ai-rules/presets/**` (and dogfood `.curso
|
|
|
7
7
|
- [ ] **SoT:** edits in `packages/…` (not only root `.cursor/`)
|
|
8
8
|
- [ ] **Core:** if `_shared/core/**` changed — all `<!-- shared-core: … -->` consumers updated (or documented intentional fork)
|
|
9
9
|
- [ ] **Twin:** Cursor change → Claude twin **same PR** (**FAIL** if missing on next, nuxt, svelte, ios-swift, android-kotlin, go, java, mcp-ts, php-hexagonal, and php-laravel — see `preset-twin-sync`)
|
|
10
|
+
- [ ] **Mavis derive:** mapped `cursor/<stack>/` change → `mavis/<stack>/` + `adapter-map.json` **same PR** when the stack is in `releasedStacks` (run `generate-mavis-next.mjs` + `check-mavis-adapter-map.mjs` for `next`; see `preset-twin-sync` § Mavis derivation)
|
|
11
|
+
- [ ] **OpenCode derive:** mapped `cursor/<stack>/` change → `opencode/<stack>/` + `adapter-map.json` **same PR** when the stack is in `releasedStacks` (run `generate-opencode-next.mjs` + `check-opencode-adapter-map.mjs` for `next`; see `preset-twin-sync` § OpenCode derivation)
|
|
10
12
|
- [ ] **Mapping:** `claude/<stack>/rules/README.md` mapping table is current
|
|
11
13
|
- [ ] **Token budget:** Cursor `alwaysApply` ≤ 4 (4th slot reserved for `ai-local-overlay`); Claude session-start exactly 3; meta rules not always-on
|
|
12
14
|
- [ ] **Local overlay coverage:** every installable stack ships `ai-local-overlay.mdc` (Cursor) and `ai-local-overlay.md` (Claude) with `MANDATORY` + acknowledgement format + `docs/LOCAL-OVERLAY.md` pointer; Claude twin listed in `claude/<stack>/rules/README.md` mapping table. Run `packages/ai-rules/scripts/check-local-overlay.sh`.
|
|
13
15
|
- [ ] **Leakage:** no foreign stack agent stems / handoff strings (`preset-no-cross-stack-leakage`)
|
|
14
16
|
- [ ] **Hook-only lifecycle:** `status.json` / `metrics.json` writes happen only inside `hooks/`; agents read but never rewrite lifecycle fields. Run `packages/ai-rules/scripts/check-hook-only-lifecycle.sh`.
|
|
15
|
-
- [ ] **Chain fork:** the byte-wise MD5 uniqueness of `chain-team-phases.sh`
|
|
17
|
+
- [ ] **Chain fork:** the byte-wise MD5 uniqueness of `chain-team-phases.sh` holds pairwise among the 10 stacks × Cursor/Claude platforms, and each released Mavis/OpenCode stack hook must also differ from the Cursor and Claude twins of the same stack. Run `packages/ai-rules/scripts/check-chain-hook-coverage.sh` and `packages/ai-rules/scripts/test-chain-team-phases-coverage.mjs`.
|
|
16
18
|
- [ ] **Twin depth:** domain ≥15 lines; thin aliases documented
|
|
17
19
|
- [ ] **README:** stack `rules/README.md` core→stack mapping / catalog updated if stems added
|
|
18
20
|
- [ ] **CHANGELOG / package README:** note when public preset paths or install surface change
|
|
19
21
|
- [ ] **i18n anchors:** kept-marked Cyrillic fragments in `tests-unit.md(c)` stay consistent with their English counterparts. Run `packages/ai-rules/scripts/check-preset-i18n.sh`.
|
|
20
|
-
- [ ] **Dogfood:** run `packages/ai-rules/scripts/sync-dogfood-cursor.sh` (or document intentional skip
|
|
22
|
+
- [ ] **Dogfood:** run `packages/ai-rules/scripts/sync-dogfood-cursor.sh` (and `sync-dogfood-mavis.sh` / `sync-dogfood-opencode.sh` when those platforms changed), or document an intentional skip, when maintaining this monorepo
|
|
21
23
|
- [ ] **shared-core drift:** for embed-mode stems, run `packages/ai-rules/scripts/check-shared-core-drift.sh` (or document intentional fork)
|
|
22
24
|
|
|
23
25
|
## Pointers
|
|
@@ -27,6 +29,6 @@ Checklist for PRs that touch `packages/ai-rules/presets/**` (and dogfood `.curso
|
|
|
27
29
|
- Budget: `preset-token-budget`
|
|
28
30
|
- Leakage: `preset-no-cross-stack-leakage`
|
|
29
31
|
- Validation: stack `agents/build-verifier.md` → preset-structure section
|
|
30
|
-
- Dogfood
|
|
32
|
+
- Dogfood scripts: `packages/ai-rules/scripts/sync-dogfood-cursor.sh`, `sync-dogfood-mavis.sh`, `sync-dogfood-opencode.sh`
|
|
31
33
|
- Drift script: `packages/ai-rules/scripts/check-shared-core-drift.sh`
|
|
32
34
|
- Orchestrator: when editing presets, load this checklist + twin-sync
|
|
@@ -8,6 +8,8 @@ Invariants for rule loading in an **installed** preset (consumer app).
|
|
|
8
8
|
|----------|-----------|-----|
|
|
9
9
|
| Cursor | `alwaysApply: true` | **≤ 4** |
|
|
10
10
|
| Claude | rules **without** `paths:` | **exactly 3** |
|
|
11
|
+
| Mavis | `adapter-map` rules with `loadMode: root-entry` | **≤ 4** |
|
|
12
|
+
| OpenCode | `adapter-map` rules with `loadMode: root-entry` | **≤ 4** |
|
|
11
13
|
|
|
12
14
|
**Cursor 4th slot is reserved for `ai-local-overlay`** (consumer-owned `.ai.local/` rules). If you do not need local overlay, demote that rule to `globs: ["**/.ai.local/**"]` and revert to ≤3.
|
|
13
15
|
|
|
@@ -16,6 +18,7 @@ Invariants for rule loading in an **installed** preset (consumer app).
|
|
|
16
18
|
Session-start body budget:
|
|
17
19
|
- Claude trio: ≤140 lines total (stretch ≤120).
|
|
18
20
|
- Cursor 4-set: ≤170 lines total (4 rules including `ai-local-overlay`).
|
|
21
|
+
- Mavis / OpenCode: ≤4 root-entry rule files; combined root `AGENTS.md` + platform entry ≤170 nonempty lines. Do not dump the full rule catalog into `AGENTS.md`.
|
|
19
22
|
|
|
20
23
|
## Meta / author rules
|
|
21
24
|
|