@bonesofspring/ai-rules
Presets for Cursor, Claude Code, Mavis (MiniMax mcode), OpenCode, and ZCode. Node 18+.
npx @bonesofspring/ai-rules init multi --preset next
npx @bonesofspring/ai-rules update # reinstall tools already recorded in this repo
npx @bonesofspring/ai-rules update --merge-ai-local # fast-forward files whose recorded base still matches; edits and pre-record files stay put
npx @bonesofspring/ai-rules clean cursor # never deletes .ai.local/
Run init from the app root (--cwd if needed). Default --preset is next. Forms: init cursor --preset next, init cursor next, init cursor. Global: npm i -g @bonesofspring/ai-rules.
init overwrites matching files. MCP is the exception: mcpServers is merged when the existing file is valid JSON (extra servers kept; shipped keys updated). Re-init overwrites preset scalar fields on a shipped server, such as command. When the preset entry omits env or headers, the consumer values are kept; a nested key the preset does define replaces that key. An existing config that is not a valid JSON object fails init with exit 1 and is left untouched — nothing is backed up or replaced. .claude/settings.json is merged too (permission arrays are append-if-missing, preset hooks added per command). When the preset denies bare Bash(git commit) or Bash(git push), re-init forces those stems into permissions.deny and removes the same stem from allow and ask, even if the consumer file already allowed them. Every other permission entry stays consumer-wins and is not inverted. It is the third merged-config path alongside opencode.json and .mcp.json; ZCode adds the fourth — the nested mcp.servers key of the consumer-owned .zcode/config.json (consumer hooks/plugins keys and custom servers stay; clean strips exactly the recorded server ids; this preset never writes a hooks key). See docs/ZCODE.md. Deleting a shipped server is not sticky — the next init restores it.
What init writes
| Cursor | Claude | Mavis | OpenCode | ZCode | |
|---|---|---|---|---|---|
| Rules / commands / agents / skills / hooks | .cursor/** | .claude/** | .mavis/** | .opencode/** | .zcode/** |
| Tool map | .cursor/AGENTS.md | .claude/CLAUDE.md | .mavis/AGENTS.md | .opencode/AGENTS.md | root AGENTS.md is the map (loaded natively; no per-tool copy) |
| MCP | .cursor/mcp.json (merge) | .mcp.json (merge) | .mavis/mcp.json (merge) | opencode.json (merge) | .zcode/config.json → nested mcp.servers (merge) |
| Root agents | REPO_AGENTS.md → AGENTS.md (same file on all five; ZCode loads it natively) | ||||
/task artifacts | .ai.local/team/ (shared; templates refreshed; tasks/<slug>/ never overwritten) | ||||
| Template drift | .ai.local/.template-base.json (shared; one record per tree of files that hold a shipped template byte; update prints one drift line, update --merge-ai-local fast-forwards a file only when its recorded base still matches and reports the rest) | ||||
| Self-learning engine | .ai.local/scripts/ (shared; thirteen scripts — the engine, manifest.mjs, and the six team-db*.mjs projection modules; existing files never overwritten) | ||||
| Specs seed | docs/specs/ copied only if the directory is absent |
The engine is the in-task self-learning loop: .ai.local/signals.jsonl → .ai.local/rule-changes.jsonl →
.ai.local/conventions.md / .ai.local/rules-library/. The chain hook runs it after every producer receipt; run it
yourself when the hook is disabled:
node .ai.local/scripts/run.mjs # derive → append → improve
node .ai.local/scripts/run.mjs --status # read-only: per-signature state, last capture, envelope
node .ai.local/scripts/run.mjs --strict # read-only check: exit 1 when capture is broken
init seeds the engine with skip-if-exists — a modified run.mjs stays yours and is reported as kept; there is no
--force. Re-running init does not update a seeded module, so delete the file to pick up a newer engine. The seeded
tree is byte-identical to presets/_shared/ai-local/scripts/, and .ai.local/.gitignore (created when absent, missing
required lines appended to an existing file) ignores *.lock, *.tmp, .rollback/, team.db, team.db-wal,
team.db-shm, and team-db-errors.log; the root .gitignore is never edited.
The same seeding ships manifest.mjs, the writer for .ai.local/team/tasks/<slug>/artifact-manifest.json:
node .ai.local/scripts/manifest.mjs init --slug <slug>
node .ai.local/scripts/manifest.mjs upsert --slug <slug> --id <id> --producer <agent> \
--outcome completed --paths brief.md --acs AC-1 --attempt <attemptId>
node .ai.local/scripts/manifest.mjs check --slug <slug>
Producers merge their receipt with it — the manifest is a whole-file merge target, never a fragment edit.
SQLite projection
The same seeded tree carries a rebuildable projection of .ai.local/team/ into
.ai.local/team/team.db (SQLite, WAL). The files stay authoritative: the database is
derived, git-ignored, and safe to delete. The two write seams (the chain hook after its
file writes, and manifest.mjs after a receipt is printed) run a bounded 5 s child; a
failure is written to team-db-errors.log and never fails a file write.
node .ai.local/scripts/team-db.mjs init # create/migrate the database
node .ai.local/scripts/team-db.mjs migrate-tasks # create when absent, then write only the delta
node .ai.local/scripts/team-db.mjs sync # full backfill + reconciliation
node .ai.local/scripts/team-db.mjs sync --slug <slug> # the bounded hook-shaped projection
node .ai.local/scripts/team-db.mjs check # schema, fingerprint, integrity
node .ai.local/scripts/team-db.mjs serve # http://127.0.0.1:4178 + dashboard
AI_RULES_TEAM_DB_DISABLE=1 node .ai.local/scripts/team-db.mjs sync # no-op: file-only mode
Requires Node 22.13.0+ for node:sqlite; older runtimes stay file-only with one line and exit 0.
migrate-tasks is the one-command path: it creates the database when it is absent and writes
only the delta against an existing one, so a fresh workspace and a populated database converge
in one invocation.
team-db.mjs serve binds loopback only, is read-only apart from POST /api/v1/sync, and
serves the OpenAPI 3.1 document at /openapi.json. Details, environment names, retention,
and rollback: docs/TEAM-DB.md.
Living exceptions: .ai.local/rules/ and .ai.local/conventions.md (init does not overwrite them). The only exception is opt-in and hash-gated: update --merge-ai-local fast-forwards a file whose recorded base in .ai.local/.template-base.json still matches, keeps an edited file byte-identical, and diverts its unified diff to .ai.local/.rollback/template-drift/ (never a .md, never a conflict marker). Plain update only prints one drift line and records the files that already hold the shipped template; your own bytes never become a base. Overlay does not load .ai.local/team/** as rules; see docs/LOCAL-OVERLAY.md.
clean <tool> removes only CLI-managed paths under that tool root. It does not wipe the whole .cursor/ / .claude/ tree, and it never deletes .ai.local/. Root AGENTS.md is removed only when no other tool install remains.
Mavis never writes ~/.minimax/**. OpenCode never writes ~/.config/opencode/**. Guides: docs/MAVIS.md, docs/OPENCODE.md.
Shared vs per-tool
Shared: stack semantics, root AGENTS.md, .ai.local/ overlay, /task slugs at .ai.local/team/tasks/<slug>/ (one status.json / metrics.json).
Per-tool: how the chain runs. Cursor auto-chains via hooks.json / subagentStop. Claude, Mavis, OpenCode, and ZCode use bash .<tool>/hooks/chain-team-phases.sh after a producer receipt (Mavis/OpenCode/ZCode: semi-auto).
init multi --preset <stack> installs every platform that lists the stack in releasedStacks (all 11 stacks on all five platforms). Commit the five trees, root AGENTS.md, and .ai.local/team/ templates if you want them in the clone.
update reinstalls each tool whose <tool>/.ai-rules-install.json is already present, using the preset field init wrote (platform + preset). Slash command /update runs that CLI. A tool without a manifest stays uninstalled, and the recorded preset stays as stored. To switch preset, run init <tool> --preset <name>.
On session start (Cursor and Claude hooks; Mavis, OpenCode, and ZCode on the first reply) the preset compares that recorded packageVersion with the latest @bonesofspring/ai-rules release. When a newer release exists, the reply opens with a level-2 heading and a blockquote naming the installed version, the latest version, and /update. That block stays out of the task answer. /update then says the update finished successfully, or that it did not, without quoting the exit code.
Stacks
next · nuxt · svelte · mol · ios-swift · android-kotlin · go · java · mcp-ts · php-hexagonal · php-laravel
npx @bonesofspring/ai-rules init multi --preset nuxt
npx @bonesofspring/ai-rules init multi --preset mol
Help: ai-rules --help.
Skills of note: write-adr (all except mcp-ts); spec-authoring / /spec-start; mockup-review and ux-design on UI stacks; figma-fidelity (post-implementation Figma-fidelity gate, next); react-performance (next), vue-performance (nuxt), svelte-performance (svelte). The mol stack ($mol reactive micro-modular UI) ships a batteries-first corpus — mol-app-core, mol-reactivity, mol-view-tree, mol-components always-on, mol-state / mol-build / mol-testing / mol-theming on demand.
Product specs and OpenSpec
docs/specs/** — consume (default), generate, or bootstrap via task-analyst + skill spec-authoring. Empty docs/specs/ before init skips the seed: delete the folder and re-init.
openspec/** is a separate tree owned by agent openspec-author (all stacks except mcp-ts). Not a substitute for docs/specs/.
Optional check: yarn check:product-specs [<repo-root>] (WARN) or --strict. The default root is the repository root; a tree with no docs/specs/domains/<domain>/<spec>.md fails under --strict rather than passing for having inspected nothing. yarn check:product-specs:self-test asserts the parser's three verdicts.
Anti-hallucination
Every installed preset ships requestable anti-hallucination-contract (not always-on). Writer roles — implementer, unit-test, e2e, qa, and code-reviewer — also load that stack's floor stem. yarn check:preset-structure fails if a role or skill binding is removed. Register a new skill family in SKILL_CONTRACT_CLASS (writer or reader). Details: docs/PRESET-CONTRIBUTION.md.
Contributing
- Layers:
presets/_shared/README.md - Guide:
docs/PRESET-CONTRIBUTION.md - Overlay:
docs/LOCAL-OVERLAY.md - MCP setup:
docs/MCP-SETUP.md - Dogfood:
scripts/sync-dogfood-cursor.sh,scripts/sync-dogfood-claude.sh,scripts/sync-dogfood-mavis.sh,scripts/sync-dogfood-opencode.sh— seeds.ai.local/team, preserves slugs - Gates:
yarn check:preset-structure
_shared is not an installable --preset.
MIT.