@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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### OpenCode platform (experimental Next-only)
|
|
6
|
+
|
|
7
|
+
- New platform **`opencode`**: `npx @bonesofspring/ai-rules init opencode --preset next` installs **`.opencode/**`** plus root `AGENTS.md` and merges MCP into **`opencode.json`**. `clean opencode` is manifest/hash-safe. The CLI never writes `~/.config/opencode/**`.
|
|
8
|
+
- Source: `presets/opencode/next/` derived from `presets/cursor/next` (`adapter-map.json`, `generate-opencode-next.mjs`). `platform-contract.json` `releasedStacks: ["next"]`; nine other stacks deferred.
|
|
9
|
+
- Native OpenCode discovery for agents, skills, and slash commands. Rules under `.opencode/rules/` are lazy-loaded from `AGENTS.md` (no `alwaysApply`/`globs` engine). Pipeline hook is manual `chain-team-phases.sh` (no `hooks.json` plugin in v1).
|
|
10
|
+
- Docs: [`docs/OPENCODE.md`](docs/OPENCODE.md) (consumer), [`docs/OPENCODE-COMPATIBILITY.md`](docs/OPENCODE-COMPATIBILITY.md) (docs-floor evidence). Gates: `check-opencode-*`, `test-opencode-context-budget` wired into `check:preset-structure`.
|
|
11
|
+
- Review follow-up: team-agent write-scope lead-in is role-specific; consumer `OPENCODE.md` no longer uses broken relative doc links; orchestrator uses OpenCode `mode`/`permission` wording; `build-verifier` preset-structure lists OpenCode/Mavis derive gates; Cursor/Claude `REPO_AGENTS.md` keep OpenCode/Mavis pointers so mixed-tool `init` does not drop them.
|
|
12
|
+
- Consumers: re-run `npx @bonesofspring/ai-rules init opencode --preset next` after publish. Cursor/Claude/Mavis installs unchanged.
|
|
13
|
+
|
|
14
|
+
### Mavis platform (MiniMax Code, experimental Next-only)
|
|
15
|
+
|
|
16
|
+
- 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/**`.
|
|
17
|
+
- 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).
|
|
18
|
+
- 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`.
|
|
19
|
+
- 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`.
|
|
20
|
+
- Consumers: re-run `npx @bonesofspring/ai-rules init mavis --preset next` after publish. Cursor/Claude installs unchanged.
|
|
21
|
+
|
|
5
22
|
### Chain-team-phases hook coverage + pipeline race fixes
|
|
6
23
|
|
|
7
24
|
- `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, **Mavis / MiniMax Code (experimental Next-only)**, and **OpenCode (experimental Next-only)**:
|
|
4
4
|
|
|
5
5
|
Node 18+.
|
|
6
6
|
|
|
@@ -8,7 +8,11 @@ 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
|
|
12
|
+
npx @bonesofspring/ai-rules init opencode --preset next
|
|
11
13
|
npx @bonesofspring/ai-rules clean cursor
|
|
14
|
+
npx @bonesofspring/ai-rules clean mavis
|
|
15
|
+
npx @bonesofspring/ai-rules clean opencode
|
|
12
16
|
```
|
|
13
17
|
|
|
14
18
|
Global: `npm i -g @bonesofspring/ai-rules`, then `ai-rules ...`.
|
|
@@ -54,6 +58,10 @@ Where things go on `init`:
|
|
|
54
58
|
| **repo-root agents** | `REPO_AGENTS.md` → **`<cwd>/AGENTS.md`** | same |
|
|
55
59
|
| **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
60
|
|
|
61
|
+
**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).
|
|
62
|
+
|
|
63
|
+
**OpenCode (experimental Next-only):** `ai-rules init opencode --preset next` writes native **`.opencode/**`** plus root `AGENTS.md` and merges MCP into **`opencode.json`**. `clean opencode` is manifest-owned. The CLI never writes `~/.config/opencode/**`. Last `init` wins on root `AGENTS.md` if several tools are installed. See [`docs/OPENCODE.md`](docs/OPENCODE.md), [`presets/opencode/README.md`](presets/opencode/README.md), and [`docs/OPENCODE-COMPATIBILITY.md`](docs/OPENCODE-COMPATIBILITY.md).
|
|
64
|
+
|
|
57
65
|
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
66
|
|
|
59
67
|
## Product specs (`docs/specs/`)
|
|
@@ -121,6 +129,6 @@ Presets are developed in this monorepo under `presets/`. If you add or change ru
|
|
|
121
129
|
- **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
130
|
- **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
131
|
- **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
|
|
132
|
+
- **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
133
|
|
|
126
134
|
MIT.
|
package/bin/cli.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { cmdInitMavis, cmdCleanMavis } = require('./mavis-install');
|
|
8
|
+
const { cmdInitOpencode, cmdCleanOpencode } = require('./opencode-install');
|
|
7
9
|
|
|
8
10
|
/** Single path segment: letters, digits, dot, underscore, hyphen; no separators or "..". */
|
|
9
11
|
const PRESET_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
10
12
|
const DEFAULT_PRESET = 'next';
|
|
11
|
-
const TOOLS = new Set(['cursor', 'claude']);
|
|
13
|
+
const TOOLS = new Set(['cursor', 'claude', 'mavis', 'opencode']);
|
|
12
14
|
|
|
13
15
|
const PRESETS = {
|
|
14
16
|
cursor: {
|
|
@@ -51,9 +53,9 @@ function printHelp() {
|
|
|
51
53
|
console.log(`@bonesofspring/ai-rules
|
|
52
54
|
|
|
53
55
|
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>]
|
|
56
|
+
ai-rules init <cursor|claude|mavis|opencode> [--preset <name>]
|
|
57
|
+
ai-rules init <cursor|claude|mavis|opencode> <preset-name>
|
|
58
|
+
ai-rules clean <cursor|claude|mavis|opencode> [--cwd <dir>]
|
|
57
59
|
|
|
58
60
|
Options:
|
|
59
61
|
--preset <name> Preset name (default: ${DEFAULT_PRESET})
|
|
@@ -66,7 +68,9 @@ Notes:
|
|
|
66
68
|
copied to a timestamped *.bak.* then replaced with the preset file. JSONC is not supported).
|
|
67
69
|
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
70
|
Claude init copies rules, commands, skills, agents, hooks, team, CLAUDE.md, MCP.md, and mcp.json → .mcp.json (project root).
|
|
69
|
-
|
|
71
|
+
Mavis init copies into .mavis/ (ai-rules-owned) plus REPO_AGENTS.md → <cwd>/AGENTS.md; MCP merges into .mavis/mcp.json only (never ~/.minimax/**).
|
|
72
|
+
OpenCode init copies into .opencode/ plus REPO_AGENTS.md → <cwd>/AGENTS.md; MCP merges into <cwd>/opencode.json (never ~/.config/opencode/**).
|
|
73
|
+
All init targets also copy REPO_AGENTS.md → <cwd>/AGENTS.md (repo-root cross-tool agents file; overwrite).
|
|
70
74
|
When <cwd>/docs/specs/ is absent, init also seeds INDEX.md stub + _templates/* from package assets (never overwrites an existing INDEX).
|
|
71
75
|
clean removes tool dirs/files and <cwd>/AGENTS.md (shared across tools — re-init the other tool to restore).
|
|
72
76
|
Command forms: "init cursor --preset next", "init cursor next", "--preset next init cursor", "cursor init --preset next".
|
|
@@ -93,6 +97,10 @@ Examples:
|
|
|
93
97
|
npx @bonesofspring/ai-rules init claude --preset mcp-ts
|
|
94
98
|
npx @bonesofspring/ai-rules init claude --preset php-hexagonal
|
|
95
99
|
npx @bonesofspring/ai-rules init claude --preset php-laravel
|
|
100
|
+
npx @bonesofspring/ai-rules init mavis --preset next
|
|
101
|
+
npx @bonesofspring/ai-rules init opencode --preset next
|
|
102
|
+
npx @bonesofspring/ai-rules clean mavis
|
|
103
|
+
npx @bonesofspring/ai-rules clean opencode
|
|
96
104
|
npx @bonesofspring/ai-rules clean cursor
|
|
97
105
|
`);
|
|
98
106
|
}
|
|
@@ -484,9 +492,34 @@ function resolvePresetName(tool, preset, packageRoot) {
|
|
|
484
492
|
}
|
|
485
493
|
|
|
486
494
|
function cmdInit(tool, preset, cwdOption) {
|
|
495
|
+
if (tool === 'mavis') {
|
|
496
|
+
cmdInitMavis({
|
|
497
|
+
preset,
|
|
498
|
+
cwdOption,
|
|
499
|
+
helpers: {
|
|
500
|
+
resolveTargetCwd,
|
|
501
|
+
seedDocsSpecsIfAbsent,
|
|
502
|
+
installMcpJson,
|
|
503
|
+
getPackageRoot,
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (tool === 'opencode') {
|
|
509
|
+
cmdInitOpencode({
|
|
510
|
+
preset,
|
|
511
|
+
cwdOption,
|
|
512
|
+
helpers: {
|
|
513
|
+
resolveTargetCwd,
|
|
514
|
+
seedDocsSpecsIfAbsent,
|
|
515
|
+
getPackageRoot,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
487
520
|
const cfg = PRESETS[tool];
|
|
488
521
|
if (!cfg) {
|
|
489
|
-
console.error(`Unknown tool: ${tool}. Use cursor or
|
|
522
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, mavis, or opencode.`);
|
|
490
523
|
process.exit(1);
|
|
491
524
|
}
|
|
492
525
|
|
|
@@ -665,9 +698,23 @@ function rollbackInit(createdPaths, toolRootExisted, toolRootAbs) {
|
|
|
665
698
|
}
|
|
666
699
|
|
|
667
700
|
function cmdClean(tool, cwdOption) {
|
|
701
|
+
if (tool === 'mavis') {
|
|
702
|
+
cmdCleanMavis({
|
|
703
|
+
cwdOption,
|
|
704
|
+
helpers: { resolveTargetCwd },
|
|
705
|
+
});
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (tool === 'opencode') {
|
|
709
|
+
cmdCleanOpencode({
|
|
710
|
+
cwdOption,
|
|
711
|
+
helpers: { resolveTargetCwd },
|
|
712
|
+
});
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
668
715
|
const cfg = PRESETS[tool];
|
|
669
716
|
if (!cfg) {
|
|
670
|
-
console.error(`Unknown tool: ${tool}. Use cursor or
|
|
717
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, mavis, or opencode.`);
|
|
671
718
|
process.exit(1);
|
|
672
719
|
}
|
|
673
720
|
const cwd = resolveTargetCwd(cwdOption);
|
|
@@ -737,7 +784,7 @@ function main() {
|
|
|
737
784
|
|
|
738
785
|
if (command === 'init') {
|
|
739
786
|
if (!tool || !TOOLS.has(tool)) {
|
|
740
|
-
console.error('Missing or invalid tool. Use: init cursor|claude [--preset next]');
|
|
787
|
+
console.error('Missing or invalid tool. Use: init cursor|claude|mavis|opencode [--preset next]');
|
|
741
788
|
process.exit(1);
|
|
742
789
|
}
|
|
743
790
|
cmdInit(tool, preset, parsed.cwd);
|
|
@@ -745,7 +792,7 @@ function main() {
|
|
|
745
792
|
}
|
|
746
793
|
if (command === 'clean') {
|
|
747
794
|
if (!tool || !TOOLS.has(tool)) {
|
|
748
|
-
console.error('Missing or invalid tool. Use: clean cursor|claude');
|
|
795
|
+
console.error('Missing or invalid tool. Use: clean cursor|claude|mavis|opencode');
|
|
749
796
|
process.exit(1);
|
|
750
797
|
}
|
|
751
798
|
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 };
|