@bonesofspring/ai-rules 0.2.21 → 0.2.23
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 +8 -0
- package/README.md +6 -2
- package/bin/cli.js +32 -8
- package/bin/mavis-install.js +441 -0
- package/package.json +9 -2
- package/presets/_shared/core/agent-team/agent-artifact-contracts.md +2 -0
- package/presets/_shared/core/meta/preset-pr-checklist.md +1 -0
- package/presets/_shared/core/meta/preset-twin-sync.md +17 -1
- package/presets/claude/android-kotlin/agents/build-verifier.md +2 -0
- package/presets/claude/android-kotlin/agents/code-reviewer.md +2 -0
- package/presets/claude/android-kotlin/agents/debugger.md +1 -0
- package/presets/claude/android-kotlin/agents/feature-developer.md +3 -0
- package/presets/claude/android-kotlin/agents/qa-tester.md +2 -0
- package/presets/claude/android-kotlin/agents/task-router.md +4 -0
- package/presets/claude/android-kotlin/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/go/agents/build-verifier.md +4 -0
- package/presets/claude/go/agents/code-reviewer.md +4 -0
- package/presets/claude/go/agents/debugger.md +4 -0
- package/presets/claude/go/agents/feature-developer.md +5 -0
- package/presets/claude/go/agents/qa-tester.md +4 -0
- package/presets/claude/go/agents/task-router.md +4 -0
- package/presets/claude/go/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/ios-swift/agents/build-verifier.md +2 -0
- package/presets/claude/ios-swift/agents/code-reviewer.md +2 -0
- package/presets/claude/ios-swift/agents/debugger.md +1 -0
- package/presets/claude/ios-swift/agents/feature-developer.md +3 -0
- package/presets/claude/ios-swift/agents/qa-tester.md +2 -0
- package/presets/claude/ios-swift/agents/task-router.md +4 -0
- package/presets/claude/ios-swift/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/java/agents/build-verifier.md +4 -0
- package/presets/claude/java/agents/code-reviewer.md +4 -0
- package/presets/claude/java/agents/debugger.md +4 -0
- package/presets/claude/java/agents/feature-developer.md +5 -0
- package/presets/claude/java/agents/qa-tester.md +4 -0
- package/presets/claude/java/agents/task-router.md +4 -0
- package/presets/claude/java/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/mcp-ts/agents/build-verifier.md +4 -0
- package/presets/claude/mcp-ts/agents/feature-developer.md +5 -0
- package/presets/claude/mcp-ts/agents/task-router.md +1 -1
- package/presets/claude/mcp-ts/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/next/agents/build-verifier.md +2 -0
- package/presets/claude/next/agents/code-reviewer.md +2 -0
- package/presets/claude/next/agents/debugger.md +4 -0
- package/presets/claude/next/agents/feature-developer.md +3 -0
- package/presets/claude/next/agents/qa-tester.md +4 -0
- package/presets/claude/next/agents/unit-test-generator.md +1 -1
- package/presets/claude/next/agents/unit-test-planner.md +1 -1
- package/presets/claude/next/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/next/rules/api-and-data/http-client.md +1 -1
- package/presets/claude/next/rules/architecture/reference-features.md +1 -1
- package/presets/claude/next/rules/testing/README.md +1 -1
- package/presets/claude/next/rules/testing/tests-e2e-structure.md +2 -0
- package/presets/claude/next/rules/testing/tests-unit.md +3 -5
- package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/next/rules/ui-and-accessibility/react-ui.md +1 -1
- package/presets/claude/next/skills/playwright-e2e/SKILL.md +5 -3
- package/presets/claude/next/skills/unit-testing/SKILL.md +6 -4
- package/presets/claude/nuxt/agents/build-verifier.md +2 -0
- package/presets/claude/nuxt/agents/code-reviewer.md +2 -0
- package/presets/claude/nuxt/agents/debugger.md +4 -0
- package/presets/claude/nuxt/agents/feature-developer.md +3 -0
- package/presets/claude/nuxt/agents/qa-tester.md +4 -0
- package/presets/claude/nuxt/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/php-hexagonal/agents/build-verifier.md +4 -0
- package/presets/claude/php-hexagonal/agents/code-reviewer.md +4 -0
- package/presets/claude/php-hexagonal/agents/debugger.md +4 -0
- package/presets/claude/php-hexagonal/agents/feature-developer.md +5 -0
- package/presets/claude/php-hexagonal/agents/qa-tester.md +4 -0
- package/presets/claude/php-hexagonal/agents/task-router.md +4 -0
- package/presets/claude/php-hexagonal/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/php-laravel/agents/build-verifier.md +4 -0
- package/presets/claude/php-laravel/agents/code-reviewer.md +4 -0
- package/presets/claude/php-laravel/agents/debugger.md +4 -0
- package/presets/claude/php-laravel/agents/feature-developer.md +5 -0
- package/presets/claude/php-laravel/agents/qa-tester.md +4 -0
- package/presets/claude/php-laravel/agents/task-router.md +1 -0
- package/presets/claude/php-laravel/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/claude/svelte/agents/build-verifier.md +2 -0
- package/presets/claude/svelte/agents/code-reviewer.md +2 -0
- package/presets/claude/svelte/agents/debugger.md +4 -0
- package/presets/claude/svelte/agents/feature-developer.md +3 -0
- package/presets/claude/svelte/agents/qa-tester.md +4 -0
- package/presets/claude/svelte/hooks/chain-team-phases.sh +51 -11
- package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +1 -0
- package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +17 -1
- package/presets/cursor/android-kotlin/agents/build-verifier.md +2 -0
- package/presets/cursor/android-kotlin/agents/code-reviewer.md +2 -0
- package/presets/cursor/android-kotlin/agents/debugger.md +1 -0
- package/presets/cursor/android-kotlin/agents/feature-developer.md +3 -0
- package/presets/cursor/android-kotlin/agents/qa-tester.md +2 -0
- package/presets/cursor/android-kotlin/agents/task-router.md +4 -0
- package/presets/cursor/android-kotlin/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/go/agents/build-verifier.md +4 -0
- package/presets/cursor/go/agents/code-reviewer.md +4 -0
- package/presets/cursor/go/agents/debugger.md +4 -0
- package/presets/cursor/go/agents/feature-developer.md +5 -0
- package/presets/cursor/go/agents/qa-tester.md +4 -0
- package/presets/cursor/go/agents/task-router.md +4 -0
- package/presets/cursor/go/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/go/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/go/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/ios-swift/agents/build-verifier.md +2 -0
- package/presets/cursor/ios-swift/agents/code-reviewer.md +2 -0
- package/presets/cursor/ios-swift/agents/debugger.md +1 -0
- package/presets/cursor/ios-swift/agents/feature-developer.md +3 -0
- package/presets/cursor/ios-swift/agents/qa-tester.md +2 -0
- package/presets/cursor/ios-swift/agents/task-router.md +4 -0
- package/presets/cursor/ios-swift/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/java/agents/build-verifier.md +4 -0
- package/presets/cursor/java/agents/code-reviewer.md +4 -0
- package/presets/cursor/java/agents/debugger.md +4 -0
- package/presets/cursor/java/agents/feature-developer.md +5 -0
- package/presets/cursor/java/agents/qa-tester.md +4 -0
- package/presets/cursor/java/agents/task-router.md +4 -0
- package/presets/cursor/java/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/java/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/java/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/mcp-ts/agents/build-verifier.md +4 -0
- package/presets/cursor/mcp-ts/agents/feature-developer.md +5 -0
- package/presets/cursor/mcp-ts/agents/task-router.md +1 -1
- package/presets/cursor/mcp-ts/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/next/agents/build-verifier.md +2 -0
- package/presets/cursor/next/agents/code-reviewer.md +2 -0
- package/presets/cursor/next/agents/debugger.md +4 -0
- package/presets/cursor/next/agents/feature-developer.md +3 -0
- package/presets/cursor/next/agents/qa-tester.md +4 -0
- package/presets/cursor/next/agents/unit-test-generator.md +1 -1
- package/presets/cursor/next/agents/unit-test-planner.md +1 -1
- package/presets/cursor/next/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/next/rules/http-client.mdc +1 -1
- package/presets/cursor/next/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/next/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/next/rules/react-ui.mdc +1 -1
- package/presets/cursor/next/rules/reference-features.mdc +1 -1
- package/presets/cursor/next/rules/tests-e2e-structure.mdc +2 -0
- package/presets/cursor/next/rules/tests-unit.mdc +3 -5
- package/presets/cursor/next/skills/playwright-e2e/SKILL.md +5 -3
- package/presets/cursor/next/skills/unit-testing/SKILL.md +6 -4
- package/presets/cursor/nuxt/agents/build-verifier.md +2 -0
- package/presets/cursor/nuxt/agents/code-reviewer.md +2 -0
- package/presets/cursor/nuxt/agents/debugger.md +4 -0
- package/presets/cursor/nuxt/agents/feature-developer.md +3 -0
- package/presets/cursor/nuxt/agents/qa-tester.md +4 -0
- package/presets/cursor/nuxt/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/php-hexagonal/agents/build-verifier.md +4 -0
- package/presets/cursor/php-hexagonal/agents/code-reviewer.md +4 -0
- package/presets/cursor/php-hexagonal/agents/debugger.md +4 -0
- package/presets/cursor/php-hexagonal/agents/feature-developer.md +5 -0
- package/presets/cursor/php-hexagonal/agents/qa-tester.md +4 -0
- package/presets/cursor/php-hexagonal/agents/task-router.md +4 -0
- package/presets/cursor/php-hexagonal/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/php-laravel/agents/build-verifier.md +4 -0
- package/presets/cursor/php-laravel/agents/code-reviewer.md +4 -0
- package/presets/cursor/php-laravel/agents/debugger.md +4 -0
- package/presets/cursor/php-laravel/agents/feature-developer.md +5 -0
- package/presets/cursor/php-laravel/agents/qa-tester.md +4 -0
- package/presets/cursor/php-laravel/agents/task-router.md +1 -0
- package/presets/cursor/php-laravel/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +17 -1
- package/presets/cursor/svelte/agents/build-verifier.md +2 -0
- package/presets/cursor/svelte/agents/code-reviewer.md +2 -0
- package/presets/cursor/svelte/agents/debugger.md +4 -0
- package/presets/cursor/svelte/agents/feature-developer.md +3 -0
- package/presets/cursor/svelte/agents/qa-tester.md +4 -0
- package/presets/cursor/svelte/hooks/chain-team-phases.sh +51 -11
- package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +1 -0
- package/presets/cursor/svelte/rules/preset-twin-sync.mdc +17 -1
- 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 +94 -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 +110 -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 +35 -0
- package/presets/mavis/next/rules/preset-token-budget.md +36 -0
- package/presets/mavis/next/rules/preset-twin-sync.md +65 -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/scripts/capture-cli-golden.mjs +44 -0
- package/scripts/check-chain-hook-coverage.sh +1 -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-preset-leakage.sh +35 -3
- package/scripts/check-preset-structure.sh +73 -0
- package/scripts/check-preset-token-budget.sh +100 -0
- package/scripts/check-shared-core-drift.sh +18 -0
- package/scripts/check-task-router-intents.sh +154 -0
- 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/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/generate-mavis-next.mjs +704 -0
- package/scripts/lib/cli-golden.mjs +50 -0
- package/scripts/lib/discover-hooks.mjs +72 -0
- package/scripts/probe-mavis-capabilities.mjs +480 -0
- package/scripts/test-agent-task-metrics-hooks.mjs +120 -16
- package/scripts/test-chain-team-phases-coverage.mjs +110 -8
- package/scripts/test-cli-init-clean.mjs +256 -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-probe-mavis-capabilities.mjs +205 -0
- package/scripts/test-task-router-intents-fixtures.mjs +109 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### Mavis platform (MiniMax Code, experimental Next-only)
|
|
6
|
+
|
|
7
|
+
- New platform **`mavis`** (`mcode` runtime): `npx @bonesofspring/ai-rules init mavis --preset next` installs **`.mavis/**`** plus root `AGENTS.md`. `clean mavis` is manifest/hash-safe. MCP merges to **`.mavis/mcp.json`** only — the CLI never writes `~/.minimax/**`.
|
|
8
|
+
- Source: `presets/mavis/next/` derived from `presets/cursor/next` (`adapter-map.json`, `generate-mavis-next.mjs`). `platform-contract.json` `releasedStacks: ["next"]`; nine other stacks deferred (WP-F).
|
|
9
|
+
- Semi-auto orchestration: `chain-team-phases.sh` on `.mavis/team`; no shipped `hooks.json`. Human gates via `task-continue` → hook `end_human_gate`. Agents upsert `artifact-manifest.json` only — hook owns `status.json` / `metrics.json`.
|
|
10
|
+
- Docs: [`docs/MAVIS.md`](docs/MAVIS.md) (consumer), [`docs/MAVIS-COMPATIBILITY.md`](docs/MAVIS-COMPATIBILITY.md) (probe evidence). Gates: `check-mavis-*`, `test-mavis-*` wired into `check:preset-structure`.
|
|
11
|
+
- Consumers: re-run `npx @bonesofspring/ai-rules init mavis --preset next` after publish. Cursor/Claude installs unchanged.
|
|
12
|
+
|
|
5
13
|
### Chain-team-phases hook coverage + pipeline race fixes
|
|
6
14
|
|
|
7
15
|
- `chain-team-phases.sh` (all 20 stack × platform hooks) now covers every agent that ships in its stack's `agents/` directory:
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @bonesofspring/ai-rules
|
|
2
2
|
|
|
3
|
-
Presets for Cursor
|
|
3
|
+
Presets for Cursor, Claude Code, and **Mavis / MiniMax Code (experimental Next-only)**:
|
|
4
4
|
|
|
5
5
|
Node 18+.
|
|
6
6
|
|
|
@@ -8,7 +8,9 @@ Node 18+.
|
|
|
8
8
|
# from the project root
|
|
9
9
|
npx @bonesofspring/ai-rules init cursor --preset next
|
|
10
10
|
npx @bonesofspring/ai-rules init claude --preset next
|
|
11
|
+
npx @bonesofspring/ai-rules init mavis --preset next
|
|
11
12
|
npx @bonesofspring/ai-rules clean cursor
|
|
13
|
+
npx @bonesofspring/ai-rules clean mavis
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
Global: `npm i -g @bonesofspring/ai-rules`, then `ai-rules ...`.
|
|
@@ -54,6 +56,8 @@ Where things go on `init`:
|
|
|
54
56
|
| **repo-root agents** | `REPO_AGENTS.md` → **`<cwd>/AGENTS.md`** | same |
|
|
55
57
|
| **docs/specs seed** | When `<cwd>/docs/specs/` is **absent**, copy INDEX stub + `_templates/*` from `presets/_shared/assets/docs-specs/` (never overwrite existing INDEX) | same |
|
|
56
58
|
|
|
59
|
+
**Mavis (MiniMax Code, experimental Next-only):** `ai-rules init mavis --preset next` writes ai-rules-owned **`.mavis/**`** plus root `AGENTS.md`. MCP merges into **`.mavis/mcp.json`** only — the CLI never writes `~/.minimax/**`. `clean mavis` is manifest-owned: unchanged hashes are removed; modified files stay. See [`docs/MAVIS.md`](docs/MAVIS.md), [`presets/mavis/README.md`](presets/mavis/README.md), and [`docs/MAVIS-COMPATIBILITY.md`](docs/MAVIS-COMPATIBILITY.md).
|
|
60
|
+
|
|
57
61
|
Skills of note: `write-adr` (all stacks); `spec-authoring` (Generate/Bootstrap `docs/specs`); `mockup-review` (**next** / **nuxt** / **svelte** / **ios-swift** / **android-kotlin** — Figma mockup reviewer via agent `mockup-reviewer`); `ux-design` (**next** / **nuxt** / **svelte** / **ios-swift** / **android-kotlin** — behavioral UX via `task-analyst`, no agent); `react-performance` (**next only**); `vue-performance` (**nuxt only**); `svelte-performance` (**svelte only**). Optional command `/spec-start`. Living gotchas: `team/conventions.md`. Opt-in rule samples eval: `yarn check:rule-eval` (workflow `rule-eval.yml`; not part of `check:preset-structure`). Optional consumer specs check: `yarn check:product-specs` (WARN default; `--strict` FAIL).
|
|
58
62
|
|
|
59
63
|
## Product specs (`docs/specs/`)
|
|
@@ -121,6 +125,6 @@ Presets are developed in this monorepo under `presets/`. If you add or change ru
|
|
|
121
125
|
- **Dogfood / drift scripts:** [`scripts/sync-dogfood-cursor.sh`](scripts/sync-dogfood-cursor.sh) (also syncs root `AGENTS.md` from `REPO_AGENTS.md`; seeds `team/conventions.md` if absent), [`scripts/check-shared-core-drift.sh`](scripts/check-shared-core-drift.sh). Published npm `files` include `scripts/` so consumers can run `check-product-specs.sh` from the package.
|
|
122
126
|
- **Preset structure CI gates** (exit non-zero on FAIL): `yarn check:preset-structure` (or `bash scripts/check-preset-structure.sh`) — twin map, session-start budget (Option A), leakage, shared-core drift (`AI_RULES_DRIFT_FAIL=1`)
|
|
123
127
|
- **Opt-in rule eval:** `yarn check:rule-eval` + `.github/workflows/rule-eval.yml` (`workflow_dispatch` + paths-scoped PR) — **not** wired into `check:preset-structure`
|
|
124
|
-
- **CLI note:** `_shared` is not an installable `--preset`; `init` copies
|
|
128
|
+
- **CLI note:** `_shared` is not an installable `--preset`; `init` copies `presets/cursor/<name>/`, `presets/claude/<name>/`, or `presets/mavis/<name>/` when the stack is in `releasedStacks`
|
|
125
129
|
|
|
126
130
|
MIT.
|
package/bin/cli.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { cmdInitMavis, cmdCleanMavis } = require('./mavis-install');
|
|
7
8
|
|
|
8
9
|
/** Single path segment: letters, digits, dot, underscore, hyphen; no separators or "..". */
|
|
9
10
|
const PRESET_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
10
11
|
const DEFAULT_PRESET = 'next';
|
|
11
|
-
const TOOLS = new Set(['cursor', 'claude']);
|
|
12
|
+
const TOOLS = new Set(['cursor', 'claude', 'mavis']);
|
|
12
13
|
|
|
13
14
|
const PRESETS = {
|
|
14
15
|
cursor: {
|
|
@@ -51,9 +52,9 @@ function printHelp() {
|
|
|
51
52
|
console.log(`@bonesofspring/ai-rules
|
|
52
53
|
|
|
53
54
|
Usage:
|
|
54
|
-
ai-rules init <cursor|claude> [--preset <name>]
|
|
55
|
-
ai-rules init <cursor|claude> <preset-name>
|
|
56
|
-
ai-rules clean <cursor|claude> [--cwd <dir>]
|
|
55
|
+
ai-rules init <cursor|claude|mavis> [--preset <name>]
|
|
56
|
+
ai-rules init <cursor|claude|mavis> <preset-name>
|
|
57
|
+
ai-rules clean <cursor|claude|mavis> [--cwd <dir>]
|
|
57
58
|
|
|
58
59
|
Options:
|
|
59
60
|
--preset <name> Preset name (default: ${DEFAULT_PRESET})
|
|
@@ -66,6 +67,7 @@ Notes:
|
|
|
66
67
|
copied to a timestamped *.bak.* then replaced with the preset file. JSONC is not supported).
|
|
67
68
|
Cursor init copies rules, commands, agents, hooks, skills, team, hooks.json, BUGBOT.md, AGENTS.md → .cursor/AGENTS.md, MCP.md, mcp.json → .cursor/mcp.json.
|
|
68
69
|
Claude init copies rules, commands, skills, agents, hooks, team, CLAUDE.md, MCP.md, and mcp.json → .mcp.json (project root).
|
|
70
|
+
Mavis init copies into .mavis/ (ai-rules-owned) plus REPO_AGENTS.md → <cwd>/AGENTS.md; MCP merges into .mavis/mcp.json only (never ~/.minimax/**).
|
|
69
71
|
Both tools also copy REPO_AGENTS.md → <cwd>/AGENTS.md (repo-root cross-tool agents file; overwrite).
|
|
70
72
|
When <cwd>/docs/specs/ is absent, init also seeds INDEX.md stub + _templates/* from package assets (never overwrites an existing INDEX).
|
|
71
73
|
clean removes tool dirs/files and <cwd>/AGENTS.md (shared across tools — re-init the other tool to restore).
|
|
@@ -93,6 +95,8 @@ Examples:
|
|
|
93
95
|
npx @bonesofspring/ai-rules init claude --preset mcp-ts
|
|
94
96
|
npx @bonesofspring/ai-rules init claude --preset php-hexagonal
|
|
95
97
|
npx @bonesofspring/ai-rules init claude --preset php-laravel
|
|
98
|
+
npx @bonesofspring/ai-rules init mavis --preset next
|
|
99
|
+
npx @bonesofspring/ai-rules clean mavis
|
|
96
100
|
npx @bonesofspring/ai-rules clean cursor
|
|
97
101
|
`);
|
|
98
102
|
}
|
|
@@ -484,9 +488,22 @@ function resolvePresetName(tool, preset, packageRoot) {
|
|
|
484
488
|
}
|
|
485
489
|
|
|
486
490
|
function cmdInit(tool, preset, cwdOption) {
|
|
491
|
+
if (tool === 'mavis') {
|
|
492
|
+
cmdInitMavis({
|
|
493
|
+
preset,
|
|
494
|
+
cwdOption,
|
|
495
|
+
helpers: {
|
|
496
|
+
resolveTargetCwd,
|
|
497
|
+
seedDocsSpecsIfAbsent,
|
|
498
|
+
installMcpJson,
|
|
499
|
+
getPackageRoot,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
487
504
|
const cfg = PRESETS[tool];
|
|
488
505
|
if (!cfg) {
|
|
489
|
-
console.error(`Unknown tool: ${tool}. Use cursor or
|
|
506
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, or mavis.`);
|
|
490
507
|
process.exit(1);
|
|
491
508
|
}
|
|
492
509
|
|
|
@@ -665,9 +682,16 @@ function rollbackInit(createdPaths, toolRootExisted, toolRootAbs) {
|
|
|
665
682
|
}
|
|
666
683
|
|
|
667
684
|
function cmdClean(tool, cwdOption) {
|
|
685
|
+
if (tool === 'mavis') {
|
|
686
|
+
cmdCleanMavis({
|
|
687
|
+
cwdOption,
|
|
688
|
+
helpers: { resolveTargetCwd },
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
668
692
|
const cfg = PRESETS[tool];
|
|
669
693
|
if (!cfg) {
|
|
670
|
-
console.error(`Unknown tool: ${tool}. Use cursor or
|
|
694
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, or mavis.`);
|
|
671
695
|
process.exit(1);
|
|
672
696
|
}
|
|
673
697
|
const cwd = resolveTargetCwd(cwdOption);
|
|
@@ -737,7 +761,7 @@ function main() {
|
|
|
737
761
|
|
|
738
762
|
if (command === 'init') {
|
|
739
763
|
if (!tool || !TOOLS.has(tool)) {
|
|
740
|
-
console.error('Missing or invalid tool. Use: init cursor|claude [--preset next]');
|
|
764
|
+
console.error('Missing or invalid tool. Use: init cursor|claude|mavis [--preset next]');
|
|
741
765
|
process.exit(1);
|
|
742
766
|
}
|
|
743
767
|
cmdInit(tool, preset, parsed.cwd);
|
|
@@ -745,7 +769,7 @@ function main() {
|
|
|
745
769
|
}
|
|
746
770
|
if (command === 'clean') {
|
|
747
771
|
if (!tool || !TOOLS.has(tool)) {
|
|
748
|
-
console.error('Missing or invalid tool. Use: clean cursor|claude');
|
|
772
|
+
console.error('Missing or invalid tool. Use: clean cursor|claude|mavis');
|
|
749
773
|
process.exit(1);
|
|
750
774
|
}
|
|
751
775
|
cmdClean(tool, parsed.cwd);
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest-owned Mavis init/clean. Does not write ~/.minimax/**.
|
|
3
|
+
*/
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const MANIFEST_REL = path.join('.mavis', '.ai-rules-install.json');
|
|
9
|
+
const CONTRACT_REL = path.join('presets', 'mavis', '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_MAVIS_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(`Mavis 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 buildPlan({ contract, sourceBase, cwd }) {
|
|
78
|
+
const files = [];
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
const addFile = (source, destRel) => {
|
|
81
|
+
if (seen.has(destRel)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
seen.add(destRel);
|
|
85
|
+
files.push({
|
|
86
|
+
source,
|
|
87
|
+
dest: path.join(cwd, destRel),
|
|
88
|
+
rel: destRel,
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
for (const entry of contract.entryFiles || []) {
|
|
93
|
+
const source = path.join(sourceBase, entry.source);
|
|
94
|
+
if (fs.existsSync(source)) {
|
|
95
|
+
addFile(source, entry.destination);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const mapping of contract.directoryMappings || []) {
|
|
100
|
+
const sourceDir = path.join(sourceBase, mapping.source);
|
|
101
|
+
if (!fs.existsSync(sourceDir)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
for (const rel of walkFiles(sourceDir)) {
|
|
105
|
+
addFile(path.join(sourceDir, rel), `${mapping.destination}/${rel}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const mcpMd = path.join(sourceBase, 'MCP.md');
|
|
110
|
+
if (fs.existsSync(mcpMd)) {
|
|
111
|
+
addFile(mcpMd, path.join(contract.managedRoot, 'MCP.md'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const mcpSource = path.join(sourceBase, contract.mcp.source);
|
|
115
|
+
const mcp = fs.existsSync(mcpSource)
|
|
116
|
+
? {
|
|
117
|
+
source: mcpSource,
|
|
118
|
+
dest: path.join(cwd, contract.mcp.fallbackDestination),
|
|
119
|
+
rel: contract.mcp.fallbackDestination,
|
|
120
|
+
}
|
|
121
|
+
: null;
|
|
122
|
+
|
|
123
|
+
for (const skip of SKIP_STACK_ROOT) {
|
|
124
|
+
const skipped = path.join(sourceBase, skip);
|
|
125
|
+
if (fs.existsSync(skipped)) {
|
|
126
|
+
const accidental = files.find((item) => item.source === skipped);
|
|
127
|
+
if (accidental) {
|
|
128
|
+
throw new Error(`refusing to install stack-root ${skip}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { files, mcp };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function capabilityModes(contract) {
|
|
137
|
+
const modes = {};
|
|
138
|
+
for (const [key, value] of Object.entries(contract.nativeCapabilities || {})) {
|
|
139
|
+
modes[key] = value.status;
|
|
140
|
+
}
|
|
141
|
+
return modes;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function shippedMcpIds(mcpSource) {
|
|
145
|
+
if (!mcpSource || !fs.existsSync(mcpSource)) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const parsed = loadJson(mcpSource);
|
|
149
|
+
if (!parsed || typeof parsed.mcpServers !== 'object' || parsed.mcpServers === null) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
return Object.keys(parsed.mcpServers);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function previousManifest(cwd) {
|
|
156
|
+
const manifestPath = path.join(cwd, MANIFEST_REL);
|
|
157
|
+
if (!fs.existsSync(manifestPath)) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
return loadJson(manifestPath);
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function restoreRollback(overwritten, created, rollbackRoot, mavisRoot, mavisExisted) {
|
|
168
|
+
for (const item of overwritten) {
|
|
169
|
+
try {
|
|
170
|
+
if (fs.existsSync(item.backup)) {
|
|
171
|
+
fs.mkdirSync(path.dirname(item.dest), { recursive: true });
|
|
172
|
+
fs.copyFileSync(item.backup, item.dest);
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
// Best-effort restore; caller already reports the failure.
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const createdPaths = [...created].sort((a, b) => b.length - a.length);
|
|
179
|
+
for (const dest of createdPaths) {
|
|
180
|
+
try {
|
|
181
|
+
if (!fs.existsSync(dest)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const stat = fs.lstatSync(dest);
|
|
185
|
+
if (stat.isDirectory()) {
|
|
186
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
187
|
+
} else {
|
|
188
|
+
fs.unlinkSync(dest);
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
// Best-effort.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (fs.existsSync(rollbackRoot)) {
|
|
195
|
+
fs.rmSync(rollbackRoot, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
if (!mavisExisted && fs.existsSync(mavisRoot)) {
|
|
198
|
+
fs.rmSync(mavisRoot, { recursive: true, force: true });
|
|
199
|
+
} else if (mavisExisted && fs.existsSync(mavisRoot)) {
|
|
200
|
+
removeEmptyDirs(mavisRoot);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function cmdInitMavis({ preset, cwdOption, helpers }) {
|
|
205
|
+
const { resolveTargetCwd, seedDocsSpecsIfAbsent, installMcpJson, getPackageRoot } = helpers;
|
|
206
|
+
const packageRoot = getPackageRoot();
|
|
207
|
+
const cwd = resolveTargetCwd(cwdOption);
|
|
208
|
+
const contract = loadContract(packageRoot);
|
|
209
|
+
const stack = preset || 'next';
|
|
210
|
+
const releasedStacks = Array.isArray(contract.releasedStacks) ? contract.releasedStacks : [];
|
|
211
|
+
if (releasedStacks.length > 0 && !releasedStacks.includes(stack)) {
|
|
212
|
+
console.error(`Preset "${stack}" for mavis is not released yet.`);
|
|
213
|
+
console.error(`Released stacks: ${releasedStacks.join(', ')}`);
|
|
214
|
+
console.error('Additional stacks ship after the pilot human gate (WP-F).');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
const sourceBase = path.join(packageRoot, contract.sourceRoot, stack);
|
|
218
|
+
if (!fs.existsSync(sourceBase)) {
|
|
219
|
+
console.error(`Preset "${stack}" for mavis not found.`);
|
|
220
|
+
console.error(`Looked in: ${sourceBase}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const mavisRoot = path.join(cwd, contract.managedRoot);
|
|
225
|
+
const mavisExisted = fs.existsSync(mavisRoot);
|
|
226
|
+
const docsSpecsDir = path.join(cwd, 'docs', 'specs');
|
|
227
|
+
const docsSpecsExisted = fs.existsSync(docsSpecsDir);
|
|
228
|
+
const prior = previousManifest(cwd);
|
|
229
|
+
const runId = new Date().toISOString().replace(/[:.]/g, '-');
|
|
230
|
+
const rollbackRoot = path.join(mavisRoot, '.rollback', runId);
|
|
231
|
+
const created = new Set();
|
|
232
|
+
const overwritten = [];
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
throwIfPhase('validate');
|
|
236
|
+
const plan = buildPlan({ contract, sourceBase, cwd });
|
|
237
|
+
for (const item of plan.files) {
|
|
238
|
+
if (!fs.existsSync(item.source)) {
|
|
239
|
+
throw new Error(`Source missing: ${item.source}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
throwIfPhase('snapshot');
|
|
244
|
+
for (const item of plan.files) {
|
|
245
|
+
if (!fs.existsSync(item.dest)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const backup = path.join(rollbackRoot, item.rel);
|
|
249
|
+
fs.mkdirSync(path.dirname(backup), { recursive: true });
|
|
250
|
+
fs.copyFileSync(item.dest, backup);
|
|
251
|
+
overwritten.push({ dest: item.dest, backup });
|
|
252
|
+
const priorHash = prior && prior.managedFiles ? prior.managedFiles[item.rel] : null;
|
|
253
|
+
const currentHash = sha256File(item.dest);
|
|
254
|
+
if (priorHash && priorHash !== currentHash) {
|
|
255
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
256
|
+
const reported = `${item.dest}.bak.${stamp}`;
|
|
257
|
+
fs.copyFileSync(item.dest, reported);
|
|
258
|
+
console.warn(`Modified managed file ${item.rel} backed up to ${reported} before overwrite.`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (plan.mcp && fs.existsSync(plan.mcp.dest)) {
|
|
262
|
+
const backup = path.join(rollbackRoot, plan.mcp.rel);
|
|
263
|
+
fs.mkdirSync(path.dirname(backup), { recursive: true });
|
|
264
|
+
fs.copyFileSync(plan.mcp.dest, backup);
|
|
265
|
+
overwritten.push({ dest: plan.mcp.dest, backup });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
throwIfPhase('write');
|
|
269
|
+
for (const item of plan.files) {
|
|
270
|
+
if (!fs.existsSync(item.dest)) {
|
|
271
|
+
created.add(item.dest);
|
|
272
|
+
}
|
|
273
|
+
writeAtomicCopy(item.source, item.dest);
|
|
274
|
+
console.log(`Copied ${path.basename(item.source)} → ${item.rel}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
throwIfPhase('mcp');
|
|
278
|
+
if (plan.mcp) {
|
|
279
|
+
if (!fs.existsSync(plan.mcp.dest)) {
|
|
280
|
+
created.add(plan.mcp.dest);
|
|
281
|
+
}
|
|
282
|
+
installMcpJson(plan.mcp.source, plan.mcp.dest, plan.mcp.rel);
|
|
283
|
+
console.log(
|
|
284
|
+
'MCP fallback staged at .mavis/mcp.json. Import it in MiniMax Code MCP JSON mode; do not write ~/.minimax/mcp.json from this CLI.',
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
throwIfPhase('docs');
|
|
289
|
+
const seeded = seedDocsSpecsIfAbsent(packageRoot, cwd);
|
|
290
|
+
if (seeded && !docsSpecsExisted) {
|
|
291
|
+
created.add(docsSpecsDir);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
throwIfPhase('manifest');
|
|
295
|
+
const managedFiles = {};
|
|
296
|
+
for (const item of plan.files) {
|
|
297
|
+
managedFiles[item.rel] = sha256File(item.dest);
|
|
298
|
+
}
|
|
299
|
+
if (plan.mcp && fs.existsSync(plan.mcp.dest)) {
|
|
300
|
+
managedFiles[plan.mcp.rel] = sha256File(plan.mcp.dest);
|
|
301
|
+
}
|
|
302
|
+
const manifest = {
|
|
303
|
+
schemaVersion: 1,
|
|
304
|
+
packageVersion: packageVersion(packageRoot),
|
|
305
|
+
platform: 'mavis',
|
|
306
|
+
preset: stack,
|
|
307
|
+
installedAt: new Date().toISOString(),
|
|
308
|
+
managedFiles,
|
|
309
|
+
managedDirectories: (contract.directoryMappings || []).map((entry) => entry.destination),
|
|
310
|
+
shippedMcpServerIds: plan.mcp ? shippedMcpIds(plan.mcp.source) : [],
|
|
311
|
+
docsSeed: seeded
|
|
312
|
+
? { seeded: true }
|
|
313
|
+
: { seeded: false, reason: docsSpecsExisted ? 'directory-exists' : 'assets-missing' },
|
|
314
|
+
capabilityModes: capabilityModes(contract),
|
|
315
|
+
};
|
|
316
|
+
const manifestAbs = path.join(cwd, MANIFEST_REL);
|
|
317
|
+
if (!fs.existsSync(manifestAbs)) {
|
|
318
|
+
created.add(manifestAbs);
|
|
319
|
+
}
|
|
320
|
+
writeAtomicText(manifestAbs, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
321
|
+
|
|
322
|
+
if (fs.existsSync(rollbackRoot)) {
|
|
323
|
+
fs.rmSync(rollbackRoot, { recursive: true, force: true });
|
|
324
|
+
}
|
|
325
|
+
const rollbackParent = path.join(mavisRoot, '.rollback');
|
|
326
|
+
if (fs.existsSync(rollbackParent) && fs.readdirSync(rollbackParent).length === 0) {
|
|
327
|
+
fs.rmdirSync(rollbackParent);
|
|
328
|
+
}
|
|
329
|
+
} catch (error) {
|
|
330
|
+
restoreRollback(overwritten, created, rollbackRoot, mavisRoot, mavisExisted);
|
|
331
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
332
|
+
console.error(`init failed: ${message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
console.log(`Done. .mavis installed at ${mavisRoot} (preset: ${stack})`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isPlainObject(value) {
|
|
340
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function stripShippedMcp(mcpAbs, shippedIds) {
|
|
344
|
+
if (!fs.existsSync(mcpAbs) || shippedIds.length === 0) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
let parsed;
|
|
348
|
+
try {
|
|
349
|
+
parsed = loadJson(mcpAbs);
|
|
350
|
+
} catch {
|
|
351
|
+
console.warn(`Preserved ${mcpAbs} (not valid JSON); shipped MCP keys not stripped.`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (!isPlainObject(parsed) || !isPlainObject(parsed.mcpServers)) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const nextServers = { ...parsed.mcpServers };
|
|
358
|
+
for (const id of shippedIds) {
|
|
359
|
+
delete nextServers[id];
|
|
360
|
+
}
|
|
361
|
+
writeAtomicText(mcpAbs, `${JSON.stringify({ ...parsed, mcpServers: nextServers }, null, 2)}\n`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function removeEmptyDirs(dir) {
|
|
365
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
for (const name of fs.readdirSync(dir)) {
|
|
369
|
+
removeEmptyDirs(path.join(dir, name));
|
|
370
|
+
}
|
|
371
|
+
if (fs.readdirSync(dir).length === 0) {
|
|
372
|
+
fs.rmdirSync(dir);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function cmdCleanMavis({ cwdOption, helpers }) {
|
|
377
|
+
const { resolveTargetCwd } = helpers;
|
|
378
|
+
const cwd = resolveTargetCwd(cwdOption);
|
|
379
|
+
const manifestPath = path.join(cwd, MANIFEST_REL);
|
|
380
|
+
if (!fs.existsSync(manifestPath)) {
|
|
381
|
+
console.error(
|
|
382
|
+
'clean mavis: missing .mavis/.ai-rules-install.json; nothing removed (fail-safe).',
|
|
383
|
+
);
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
let manifest;
|
|
387
|
+
try {
|
|
388
|
+
manifest = loadJson(manifestPath);
|
|
389
|
+
} catch {
|
|
390
|
+
console.error(
|
|
391
|
+
'clean mavis: invalid .mavis/.ai-rules-install.json; nothing removed (fail-safe).',
|
|
392
|
+
);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
if (!manifest || manifest.platform !== 'mavis' || !isPlainObject(manifest.managedFiles)) {
|
|
396
|
+
console.error(
|
|
397
|
+
'clean mavis: install manifest is not a Mavis ownership record; nothing removed.',
|
|
398
|
+
);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const preserved = [];
|
|
403
|
+
for (const [rel, installedHash] of Object.entries(manifest.managedFiles)) {
|
|
404
|
+
if (rel === MANIFEST_REL) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const abs = path.join(cwd, rel);
|
|
408
|
+
if (!fs.existsSync(abs)) {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const current = sha256File(abs);
|
|
412
|
+
if (current === installedHash) {
|
|
413
|
+
fs.unlinkSync(abs);
|
|
414
|
+
} else {
|
|
415
|
+
preserved.push(rel);
|
|
416
|
+
console.warn(`Preserved modified managed file ${rel}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (preserved.includes('.mavis/mcp.json') && Array.isArray(manifest.shippedMcpServerIds)) {
|
|
421
|
+
stripShippedMcp(path.join(cwd, '.mavis', 'mcp.json'), manifest.shippedMcpServerIds);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
for (const dirRel of [...(manifest.managedDirectories || [])].sort((a, b) => b.length - a.length)) {
|
|
425
|
+
removeEmptyDirs(path.join(cwd, dirRel));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
fs.unlinkSync(manifestPath);
|
|
429
|
+
removeEmptyDirs(path.join(cwd, '.mavis'));
|
|
430
|
+
if (fs.existsSync(path.join(cwd, '.mavis')) && fs.readdirSync(path.join(cwd, '.mavis')).length === 0) {
|
|
431
|
+
fs.rmdirSync(path.join(cwd, '.mavis'));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
console.log(
|
|
435
|
+
preserved.length > 0
|
|
436
|
+
? `clean mavis: removed unchanged managed files; preserved ${preserved.join(', ')}`
|
|
437
|
+
: 'clean mavis: removed unchanged managed files',
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = { cmdInitMavis, cmdCleanMavis, MANIFEST_REL };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bonesofspring/ai-rules",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.23",
|
|
4
4
|
"description": "Presets of Cursor and Claude rules/commands for Revy Ross personal use",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Revy Ross",
|
|
@@ -38,8 +38,15 @@
|
|
|
38
38
|
"check:product-specs:dogfood": "bash scripts/check-product-specs.sh ../../dogfood/mcp-ts-sample",
|
|
39
39
|
"check:rule-eval": "node scripts/check-rule-eval.mjs",
|
|
40
40
|
"test:cli-mcp-merge": "node scripts/test-cli-mcp-merge.mjs",
|
|
41
|
+
"test:cli-init-clean": "node scripts/test-cli-init-clean.mjs",
|
|
41
42
|
"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"
|
|
43
|
+
"test:agent-task-metrics-hooks": "node scripts/test-agent-task-metrics-hooks.mjs",
|
|
44
|
+
"check:mavis-platform-contract": "node scripts/check-mavis-platform-contract.mjs --self-test",
|
|
45
|
+
"check:mavis-adapter-map": "node scripts/check-mavis-adapter-map.mjs --self-test",
|
|
46
|
+
"check:mavis-token-budget": "node scripts/check-mavis-token-budget.mjs",
|
|
47
|
+
"check:mavis-local-overlay": "node scripts/check-mavis-local-overlay.mjs",
|
|
48
|
+
"test:mavis-context-budget": "node scripts/test-mavis-context-budget.mjs",
|
|
49
|
+
"test:mavis-orchestration": "node scripts/test-mavis-orchestration.mjs"
|
|
43
50
|
},
|
|
44
51
|
"engines": {
|
|
45
52
|
"node": ">=18"
|
|
@@ -78,6 +78,8 @@ A producer finishes its step by upserting only its owned artifact entry and term
|
|
|
78
78
|
|
|
79
79
|
`blocked` is terminal for the producer attempt but non-advancing for the pipeline: the hook persists `status.state` / `phase` as `blocked` and emits no next-agent handoff. A receipt must either carry the active `attemptId` or belong to the step's assigned output entry. Its terminal timestamp must be at or after the active attempt's `startedAt`; otherwise the hook keeps the attempt active in `awaiting_artifact_receipt` and does not advance or retry.
|
|
80
80
|
|
|
81
|
+
> **Receipt binding (owner prompts).** Each owning-agent prompt (`feature-developer`, `build-verifier`, `code-reviewer`, `debugger`, `qa-tester` across `cursor/` and `claude/` platforms × 10 stacks) carries the same one-paragraph callout under `## On completion` (or appended as `## Receipt binding` when no `On completion` section exists). When writing the terminal receipt, set `entry.receipt.attemptId = <attemptId supplied by the orchestrator>`. The hook at `chain-team-phases.sh:readBoundReceiptOutcome` rejects entries whose `attemptId` does not match the current attempt, or whose `Date.parse(timestamps.completedAt) < Date.parse(attemptStartedAt)`. A missing or stale attemptId causes `awaiting_artifact_receipt` stalls.
|
|
82
|
+
|
|
81
83
|
## Ownership and writes
|
|
82
84
|
|
|
83
85
|
- Agents own their output artifacts and their manifest entries/receipts. They merge by stable `id` and preserve all foreign entries.
|
|
@@ -7,6 +7,7 @@ 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)
|
|
10
11
|
- [ ] **Mapping:** `claude/<stack>/rules/README.md` mapping table is current
|
|
11
12
|
- [ ] **Token budget:** Cursor `alwaysApply` ≤ 4 (4th slot reserved for `ai-local-overlay`); Claude session-start exactly 3; meta rules not always-on
|
|
12
13
|
- [ ] **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`.
|