@bonesofspring/ai-rules 0.2.23 → 0.2.25
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 +16 -0
- package/README.md +20 -5
- package/bin/cli.js +121 -15
- package/bin/mavis-install.js +5 -1
- package/bin/opencode-install.js +535 -0
- package/package.json +10 -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 +4 -3
- package/presets/_shared/core/meta/preset-token-budget.md +3 -0
- package/presets/_shared/core/meta/preset-twin-sync.md +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -0
- package/presets/claude/next/REPO_AGENTS.md +34 -8
- 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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -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 +16 -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 +4 -3
- package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/go/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/java/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +16 -0
- package/presets/cursor/next/REPO_AGENTS.md +26 -8
- 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 +4 -3
- package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/next/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +16 -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 +4 -3
- package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
- package/presets/cursor/svelte/rules/preset-twin-sync.mdc +16 -0
- package/presets/mavis/next/MAVIS.md +1 -0
- package/presets/mavis/next/REPO_AGENTS.md +28 -48
- package/presets/mavis/next/adapter-map.json +3 -3
- package/presets/mavis/next/agents/build-verifier.md +3 -1
- package/presets/mavis/next/rules/agent-team-orchestrator.md +2 -2
- package/presets/mavis/next/rules/ai-local-overlay.md +1 -1
- package/presets/mavis/next/rules/preset-layering.md +3 -3
- package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +1 -1
- package/presets/mavis/next/rules/preset-pr-checklist.md +4 -3
- package/presets/mavis/next/rules/preset-token-budget.md +3 -0
- package/presets/mavis/next/rules/preset-twin-sync.md +16 -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 +74 -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 +5 -1
- package/scripts/check-chain-hook-coverage.sh +1 -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 +30 -1
- package/scripts/check-preset-structure.sh +45 -0
- package/scripts/check-task-router-intents.sh +4 -4
- package/scripts/fixtures/cli-golden/claude/next.json +8 -8
- package/scripts/fixtures/cli-golden/cursor/next.json +8 -8
- package/scripts/fixtures/cli-golden/mavis/next.json +10 -10
- package/scripts/fixtures/cli-golden/opencode/next.json +125 -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 +4 -101
- package/scripts/generate-opencode-next.mjs +730 -0
- package/scripts/lib/discover-hooks.mjs +23 -1
- package/scripts/sync-dogfood-mavis.sh +90 -0
- package/scripts/sync-dogfood-opencode.sh +85 -0
- package/scripts/test-agent-task-metrics-hooks.mjs +4 -1
- package/scripts/test-chain-team-phases-coverage.mjs +6 -1
- package/scripts/test-cli-init-clean.mjs +180 -2
- package/scripts/test-opencode-context-budget.mjs +53 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### Quad-install Next: shared root `AGENTS.md`
|
|
6
|
+
|
|
7
|
+
- Cursor, Claude, Mavis, and OpenCode **next** presets now ship the **same** `REPO_AGENTS.md` (presence-based load + per-tool pipeline caveat + local overlay). Last `init` still overwrites the file; content no longer depends on which tool ran last.
|
|
8
|
+
- `clean <tool>` leaves root `AGENTS.md` when another tool install is still present.
|
|
9
|
+
- `init multi --preset next` installs all four platforms in one command (Mavis/OpenCode skipped on stacks they have not released).
|
|
10
|
+
- Consumers: `npx @bonesofspring/ai-rules init multi --preset next` on Next repos.
|
|
11
|
+
|
|
12
|
+
### OpenCode platform (experimental Next-only)
|
|
13
|
+
|
|
14
|
+
- 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/**`.
|
|
15
|
+
- 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.
|
|
16
|
+
- 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).
|
|
17
|
+
- 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`.
|
|
18
|
+
- 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.
|
|
19
|
+
- Consumers: re-run `npx @bonesofspring/ai-rules init opencode --preset next` after publish. Cursor/Claude/Mavis installs unchanged.
|
|
20
|
+
|
|
5
21
|
### Mavis platform (MiniMax Code, experimental Next-only)
|
|
6
22
|
|
|
7
23
|
- 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/**`.
|
package/README.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# @bonesofspring/ai-rules
|
|
2
2
|
|
|
3
|
-
Presets for Cursor, Claude Code,
|
|
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
|
|
|
7
7
|
```bash
|
|
8
8
|
# from the project root
|
|
9
|
-
npx @bonesofspring/ai-rules init
|
|
10
|
-
npx @bonesofspring/ai-rules init claude --preset next
|
|
11
|
-
npx @bonesofspring/ai-rules init mavis --preset next
|
|
9
|
+
npx @bonesofspring/ai-rules init multi --preset next
|
|
12
10
|
npx @bonesofspring/ai-rules clean cursor
|
|
13
11
|
npx @bonesofspring/ai-rules clean mavis
|
|
12
|
+
npx @bonesofspring/ai-rules clean opencode
|
|
14
13
|
```
|
|
15
14
|
|
|
16
15
|
Global: `npm i -g @bonesofspring/ai-rules`, then `ai-rules ...`.
|
|
@@ -36,7 +35,7 @@ If `.cursor` does not appear, check the command output for errors and confirm yo
|
|
|
36
35
|
- optional dirs when present: `agents/`, `hooks/`, `skills/`, `team/`
|
|
37
36
|
- Cursor root files: `hooks.json`, `BUGBOT.md`, `AGENTS.md`, `MCP.md`, `mcp.json`
|
|
38
37
|
- Claude: `CLAUDE.md`, `MCP.md` under `.claude/`; project `.mcp.json` at repo root
|
|
39
|
-
- **
|
|
38
|
+
- **Shared root `AGENTS.md`:** removed only when no other tool install remains (Cursor, Claude, Mavis, or OpenCode markers still on disk)
|
|
40
39
|
|
|
41
40
|
Any other files you added under `.cursor/` or `.claude/` (notes, local overrides, IDE state) are **left intact**. Older releases that deleted the whole tool directory are no longer the behavior.
|
|
42
41
|
|
|
@@ -58,6 +57,22 @@ Where things go on `init`:
|
|
|
58
57
|
|
|
59
58
|
**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
59
|
|
|
60
|
+
**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/**`. All four next platforms ship the **same** root `AGENTS.md` (last `init` still overwrites, content does not change). See [`docs/OPENCODE.md`](docs/OPENCODE.md), [`presets/opencode/README.md`](presets/opencode/README.md), and [`docs/OPENCODE-COMPATIBILITY.md`](docs/OPENCODE-COMPATIBILITY.md).
|
|
61
|
+
|
|
62
|
+
### Multi-tool on one branch (Next)
|
|
63
|
+
|
|
64
|
+
Install all four from the app root (`init multi` is the one-shot), then commit the four trees plus root `AGENTS.md`. Living exceptions belong in **`.ai.local/`** (init never touches it).
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx @bonesofspring/ai-rules init multi --preset next
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Per-tool: `init cursor|claude|mavis|opencode --preset next`. On stacks other than `next`, `init multi` still installs Cursor and Claude and **skips** Mavis/OpenCode until those platforms list the stack in `releasedStacks`.
|
|
71
|
+
|
|
72
|
+
**Shared:** stack rules (packaged per tool), root `AGENTS.md`, `.ai.local/` overlay.
|
|
73
|
+
|
|
74
|
+
**Not shared:** `/task` pipelines. Each tool writes `.tool/team/tasks/<slug>/` (`status.json` is not cross-tool). Cursor auto-chains via `hooks.json` / `subagentStop`. Claude ships `chain-team-phases.sh` without `hooks.json`. Mavis and OpenCode are semi-auto: `printf '{"status":"completed"}' | bash .tool/hooks/chain-team-phases.sh`. Mavis/OpenCode remain Next-only.
|
|
75
|
+
|
|
61
76
|
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).
|
|
62
77
|
|
|
63
78
|
## Product specs (`docs/specs/`)
|
package/bin/cli.js
CHANGED
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { cmdInitMavis, cmdCleanMavis } = require('./mavis-install');
|
|
8
|
+
const { cmdInitOpencode, cmdCleanOpencode } = require('./opencode-install');
|
|
8
9
|
|
|
9
10
|
/** Single path segment: letters, digits, dot, underscore, hyphen; no separators or "..". */
|
|
10
11
|
const PRESET_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
11
12
|
const DEFAULT_PRESET = 'next';
|
|
12
|
-
const TOOLS = new Set(['cursor', 'claude', 'mavis']);
|
|
13
|
+
const TOOLS = new Set(['cursor', 'claude', 'mavis', 'opencode']);
|
|
14
|
+
/** Meta-target: `init multi` runs every platform that has released the preset. */
|
|
15
|
+
const INIT_TARGETS = new Set([...TOOLS, 'multi']);
|
|
16
|
+
const MULTI_INSTALL_ORDER = ['cursor', 'claude', 'mavis', 'opencode'];
|
|
13
17
|
|
|
14
18
|
const PRESETS = {
|
|
15
19
|
cursor: {
|
|
@@ -52,9 +56,9 @@ function printHelp() {
|
|
|
52
56
|
console.log(`@bonesofspring/ai-rules
|
|
53
57
|
|
|
54
58
|
Usage:
|
|
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>]
|
|
59
|
+
ai-rules init <cursor|claude|mavis|opencode|multi> [--preset <name>]
|
|
60
|
+
ai-rules init <cursor|claude|mavis|opencode|multi> <preset-name>
|
|
61
|
+
ai-rules clean <cursor|claude|mavis|opencode> [--cwd <dir>]
|
|
58
62
|
|
|
59
63
|
Options:
|
|
60
64
|
--preset <name> Preset name (default: ${DEFAULT_PRESET})
|
|
@@ -68,9 +72,11 @@ Notes:
|
|
|
68
72
|
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.
|
|
69
73
|
Claude init copies rules, commands, skills, agents, hooks, team, CLAUDE.md, MCP.md, and mcp.json → .mcp.json (project root).
|
|
70
74
|
Mavis init copies into .mavis/ (ai-rules-owned) plus REPO_AGENTS.md → <cwd>/AGENTS.md; MCP merges into .mavis/mcp.json only (never ~/.minimax/**).
|
|
71
|
-
|
|
75
|
+
OpenCode init copies into .opencode/ plus REPO_AGENTS.md → <cwd>/AGENTS.md; MCP merges into <cwd>/opencode.json (never ~/.config/opencode/**).
|
|
76
|
+
All init targets also copy REPO_AGENTS.md → <cwd>/AGENTS.md (repo-root cross-tool agents file; overwrite; all next platforms ship the same file).
|
|
77
|
+
init multi installs every platform that has released the preset (cursor, claude, plus mavis/opencode when listed in releasedStacks). Not a clean target.
|
|
72
78
|
When <cwd>/docs/specs/ is absent, init also seeds INDEX.md stub + _templates/* from package assets (never overwrites an existing INDEX).
|
|
73
|
-
clean removes tool dirs/files
|
|
79
|
+
clean removes tool dirs/files. Shared <cwd>/AGENTS.md is removed only when no other tool install remains.
|
|
74
80
|
Command forms: "init cursor --preset next", "init cursor next", "--preset next init cursor", "cursor init --preset next".
|
|
75
81
|
|
|
76
82
|
Examples:
|
|
@@ -96,7 +102,10 @@ Examples:
|
|
|
96
102
|
npx @bonesofspring/ai-rules init claude --preset php-hexagonal
|
|
97
103
|
npx @bonesofspring/ai-rules init claude --preset php-laravel
|
|
98
104
|
npx @bonesofspring/ai-rules init mavis --preset next
|
|
105
|
+
npx @bonesofspring/ai-rules init opencode --preset next
|
|
106
|
+
npx @bonesofspring/ai-rules init multi --preset next
|
|
99
107
|
npx @bonesofspring/ai-rules clean mavis
|
|
108
|
+
npx @bonesofspring/ai-rules clean opencode
|
|
100
109
|
npx @bonesofspring/ai-rules clean cursor
|
|
101
110
|
`);
|
|
102
111
|
}
|
|
@@ -154,7 +163,7 @@ function parseArgs(argv) {
|
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
/**
|
|
157
|
-
* Supports: init cursor, cursor init, init cursor next (positional preset).
|
|
166
|
+
* Supports: init cursor, cursor init, init cursor next (positional preset), init multi.
|
|
158
167
|
*/
|
|
159
168
|
function normalizeInitClean(parsed) {
|
|
160
169
|
const positional = [...parsed._];
|
|
@@ -162,11 +171,11 @@ function normalizeInitClean(parsed) {
|
|
|
162
171
|
let tool;
|
|
163
172
|
let presetFromPositional;
|
|
164
173
|
|
|
165
|
-
if (positional.length >= 2 &&
|
|
174
|
+
if (positional.length >= 2 && INIT_TARGETS.has(positional[0]) && positional[1] === 'init') {
|
|
166
175
|
command = 'init';
|
|
167
176
|
tool = positional[0];
|
|
168
177
|
presetFromPositional = positional[2];
|
|
169
|
-
} else if (positional.length >= 2 && positional[0] === 'init' &&
|
|
178
|
+
} else if (positional.length >= 2 && positional[0] === 'init' && INIT_TARGETS.has(positional[1])) {
|
|
170
179
|
command = 'init';
|
|
171
180
|
tool = positional[1];
|
|
172
181
|
presetFromPositional = positional[2];
|
|
@@ -387,6 +396,23 @@ function removeFileIfExists(filePath) {
|
|
|
387
396
|
}
|
|
388
397
|
}
|
|
389
398
|
|
|
399
|
+
/** Markers that mean a tool install is still present (directory leftovers do not count). */
|
|
400
|
+
const TOOL_INSTALL_MARKERS = {
|
|
401
|
+
'.cursor': ['.cursor/AGENTS.md', '.cursor/hooks.json', '.cursor/rules'],
|
|
402
|
+
'.claude': ['.claude/CLAUDE.md', '.claude/rules'],
|
|
403
|
+
'.mavis': ['.mavis/.ai-rules-install.json', '.mavis/AGENTS.md'],
|
|
404
|
+
'.opencode': ['.opencode/.ai-rules-install.json', '.opencode/AGENTS.md'],
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
function otherToolInstallPresent(cwd, exceptRoot) {
|
|
408
|
+
return Object.entries(TOOL_INSTALL_MARKERS).some(([root, markers]) => {
|
|
409
|
+
if (root === exceptRoot) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return markers.some((rel) => fs.existsSync(path.join(cwd, rel)));
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
390
416
|
function listPresetNames(presetsRoot) {
|
|
391
417
|
if (!fs.existsSync(presetsRoot)) {
|
|
392
418
|
return [];
|
|
@@ -487,6 +513,53 @@ function resolvePresetName(tool, preset, packageRoot) {
|
|
|
487
513
|
return { name, base, presetsRoot };
|
|
488
514
|
}
|
|
489
515
|
|
|
516
|
+
function loadReleasedStacks(platform) {
|
|
517
|
+
if (platform === 'cursor' || platform === 'claude') {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const contractPath = path.join(getPackageRoot(), 'presets', platform, 'platform-contract.json');
|
|
521
|
+
if (!fs.existsSync(contractPath)) {
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
const contract = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
|
|
526
|
+
return Array.isArray(contract.releasedStacks) ? contract.releasedStacks : [];
|
|
527
|
+
} catch {
|
|
528
|
+
return [];
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function cmdInitMulti(preset, cwdOption) {
|
|
533
|
+
const presetName = preset === undefined || preset === '' ? DEFAULT_PRESET : preset;
|
|
534
|
+
const invalid = validatePresetName(presetName);
|
|
535
|
+
if (invalid) {
|
|
536
|
+
console.error(invalid);
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const installed = [];
|
|
541
|
+
const skipped = [];
|
|
542
|
+
for (const tool of MULTI_INSTALL_ORDER) {
|
|
543
|
+
const released = loadReleasedStacks(tool);
|
|
544
|
+
if (released && released.length > 0 && !released.includes(presetName)) {
|
|
545
|
+
skipped.push(tool);
|
|
546
|
+
console.log(
|
|
547
|
+
`Skipping ${tool}: preset "${presetName}" is not released yet (released: ${released.join(', ')})`,
|
|
548
|
+
);
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
console.log(`\n=== init ${tool} --preset ${presetName} ===`);
|
|
552
|
+
cmdInit(tool, presetName, cwdOption);
|
|
553
|
+
installed.push(tool);
|
|
554
|
+
}
|
|
555
|
+
if (installed.length === 0) {
|
|
556
|
+
console.error(`multi: no platforms installed for preset "${presetName}".`);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
const skipNote = skipped.length > 0 ? `; skipped ${skipped.join(', ')}` : '';
|
|
560
|
+
console.log(`\nDone. multi installed ${installed.join(', ')}${skipNote}`);
|
|
561
|
+
}
|
|
562
|
+
|
|
490
563
|
function cmdInit(tool, preset, cwdOption) {
|
|
491
564
|
if (tool === 'mavis') {
|
|
492
565
|
cmdInitMavis({
|
|
@@ -501,9 +574,21 @@ function cmdInit(tool, preset, cwdOption) {
|
|
|
501
574
|
});
|
|
502
575
|
return;
|
|
503
576
|
}
|
|
577
|
+
if (tool === 'opencode') {
|
|
578
|
+
cmdInitOpencode({
|
|
579
|
+
preset,
|
|
580
|
+
cwdOption,
|
|
581
|
+
helpers: {
|
|
582
|
+
resolveTargetCwd,
|
|
583
|
+
seedDocsSpecsIfAbsent,
|
|
584
|
+
getPackageRoot,
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
504
589
|
const cfg = PRESETS[tool];
|
|
505
590
|
if (!cfg) {
|
|
506
|
-
console.error(`Unknown tool: ${tool}. Use cursor, claude, or
|
|
591
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, mavis, or opencode.`);
|
|
507
592
|
process.exit(1);
|
|
508
593
|
}
|
|
509
594
|
|
|
@@ -685,13 +770,20 @@ function cmdClean(tool, cwdOption) {
|
|
|
685
770
|
if (tool === 'mavis') {
|
|
686
771
|
cmdCleanMavis({
|
|
687
772
|
cwdOption,
|
|
688
|
-
helpers: { resolveTargetCwd },
|
|
773
|
+
helpers: { resolveTargetCwd, otherToolInstallPresent },
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (tool === 'opencode') {
|
|
778
|
+
cmdCleanOpencode({
|
|
779
|
+
cwdOption,
|
|
780
|
+
helpers: { resolveTargetCwd, otherToolInstallPresent },
|
|
689
781
|
});
|
|
690
782
|
return;
|
|
691
783
|
}
|
|
692
784
|
const cfg = PRESETS[tool];
|
|
693
785
|
if (!cfg) {
|
|
694
|
-
console.error(`Unknown tool: ${tool}. Use cursor, claude, or
|
|
786
|
+
console.error(`Unknown tool: ${tool}. Use cursor, claude, mavis, or opencode.`);
|
|
695
787
|
process.exit(1);
|
|
696
788
|
}
|
|
697
789
|
const cwd = resolveTargetCwd(cwdOption);
|
|
@@ -728,6 +820,10 @@ function cmdClean(tool, cwdOption) {
|
|
|
728
820
|
}
|
|
729
821
|
|
|
730
822
|
for (const destRel of Object.values(cfg.repoRootFiles || {})) {
|
|
823
|
+
if (destRel === 'AGENTS.md' && otherToolInstallPresent(cwd, toolRoot)) {
|
|
824
|
+
console.log('Kept shared AGENTS.md (another tool install is still present)');
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
731
827
|
removeFileIfExists(path.join(cwd, destRel));
|
|
732
828
|
removedParts.push(destRel);
|
|
733
829
|
}
|
|
@@ -760,16 +856,26 @@ function main() {
|
|
|
760
856
|
const { command, tool, preset } = normalizeInitClean(parsed);
|
|
761
857
|
|
|
762
858
|
if (command === 'init') {
|
|
763
|
-
if (!tool || !
|
|
764
|
-
console.error(
|
|
859
|
+
if (!tool || !INIT_TARGETS.has(tool)) {
|
|
860
|
+
console.error(
|
|
861
|
+
'Missing or invalid tool. Use: init cursor|claude|mavis|opencode|multi [--preset next]',
|
|
862
|
+
);
|
|
765
863
|
process.exit(1);
|
|
766
864
|
}
|
|
865
|
+
if (tool === 'multi') {
|
|
866
|
+
cmdInitMulti(preset, parsed.cwd);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
767
869
|
cmdInit(tool, preset, parsed.cwd);
|
|
768
870
|
return;
|
|
769
871
|
}
|
|
770
872
|
if (command === 'clean') {
|
|
873
|
+
if (tool === 'multi') {
|
|
874
|
+
console.error('clean multi is not supported. Clean each tool: cursor, claude, mavis, opencode.');
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
771
877
|
if (!tool || !TOOLS.has(tool)) {
|
|
772
|
-
console.error('Missing or invalid tool. Use: clean cursor|claude|mavis');
|
|
878
|
+
console.error('Missing or invalid tool. Use: clean cursor|claude|mavis|opencode');
|
|
773
879
|
process.exit(1);
|
|
774
880
|
}
|
|
775
881
|
cmdClean(tool, parsed.cwd);
|
package/bin/mavis-install.js
CHANGED
|
@@ -374,7 +374,7 @@ function removeEmptyDirs(dir) {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
function cmdCleanMavis({ cwdOption, helpers }) {
|
|
377
|
-
const { resolveTargetCwd } = helpers;
|
|
377
|
+
const { resolveTargetCwd, otherToolInstallPresent } = helpers;
|
|
378
378
|
const cwd = resolveTargetCwd(cwdOption);
|
|
379
379
|
const manifestPath = path.join(cwd, MANIFEST_REL);
|
|
380
380
|
if (!fs.existsSync(manifestPath)) {
|
|
@@ -409,6 +409,10 @@ function cmdCleanMavis({ cwdOption, helpers }) {
|
|
|
409
409
|
continue;
|
|
410
410
|
}
|
|
411
411
|
const current = sha256File(abs);
|
|
412
|
+
if (rel === 'AGENTS.md' && otherToolInstallPresent && otherToolInstallPresent(cwd, '.mavis')) {
|
|
413
|
+
console.log('Kept shared AGENTS.md (another tool install is still present)');
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
412
416
|
if (current === installedHash) {
|
|
413
417
|
fs.unlinkSync(abs);
|
|
414
418
|
} else {
|