Skip to content
Agent integrations

Agent integrations

You write the door once. multivac projects it into whatever harness the person in front of it happens to use.

The canonical door is AGENTS.md at the repo root — in the brain and in every declared repo. Everything else on this page is a projection of that one file. Adding a harness is an entry in src/adapters/registry.ts, shipped inside the package: doors and doctor dispatch on the entry’s kind, never on its name, so a new harness is data and nothing else. Every entry records the vendor doc it was verified against.

Select them in .multivac/config.yml:

doors: [agents, claude, cursor, opencode, codex, windsurf, gemini, copilot]

Then mvac doors. That is the whole adoption step — never re-run init.

The four kinds

kindwhat doors writesentries
canonicalAGENTS.md itself — the source every other kind projects fromagents
nativenothing. The harness already reads AGENTS.md; a second file would be a paraphraseopencode, codex, windsurf
symlinka second name for the same bytes: <door> → AGENTS.mdclaude, gemini
stuba small tool-owned file, optional frontmatter, then the managed block, pointing at AGENTS.mdcursor, copilot

There is no kind for “cannot be owned”. A harness whose door multivac cannot write gets no entry, because an entry is how this tool says supported — it appears in --provider’s legal values, in the table above, and in the count of what multivac integrates with. aider had one for a while, carrying a note that explained at length why none of it applied; it read as support to anyone who did not open it. An unknown name already gets the list of what is supported, which is the answer that helps.

Everything multivac writes into a file it does not fully own lands between <!-- multivac:begin --> and <!-- multivac:end -->. Content outside that block is yours and is never touched — including in a stub door, which reads the file before it writes it and adds its frontmatter only when creating the file (MV-108). Before MV-108 the stub kind wrote its file whole, and that sentence was false for .github/copilot-instructions.md and .cursor/rules/multivac.mdc.

What one run looks like

$ mvac doors
brain: door + hooks updated
$ mvac doctor
doors      agents: AGENTS.md ok · claude: CLAUDE.md ok (symlink) · cursor: .cursor/rules/multivac.mdc ok · opencode: AGENTS.md ok (read natively) · codex: AGENTS.md ok (read natively) · windsurf: AGENTS.md ok (read natively) · gemini: GEMINI.md ok (symlink) · copilot: .github/copilot-instructions.md ok

agents

file writtenAGENTS.md
kindcanonical
skill
hook config
sourcehttps://agents.md/

The canonical door. Every other target projects from this file, and doors writes it whether or not agents is in your list. In the brain it carries the law summary and the active-invariant count; in a consumer repo it carries where the brain is mounted, what binds, and that a change may cross repos.

init is what creates it; doors refreshes its managed block.

claude

file writtenCLAUDE.md
kindsymlinkAGENTS.md
skill.claude/skills/multivac/
hook config.claude/settings.jsonhooks.SessionStart and hooks.PostToolUsemvac verify, wrapped per event so a red run reaches the model (MV-112)
detected byan existing CLAUDE.md
sourcehttps://code.claude.com/docs/en/memory

Claude Code reads CLAUDE.md, not AGENTS.md; its own docs give ln -s AGENTS.md CLAUDE.md as the way to share one file, so that is exactly what doors creates.

This is the only entry with all three artifact classes. Besides the symlink, doors copies the packaged skill into .claude/skills/multivac/ and merges multivac’s hook entries into .claude/settings.json, preserving every key and entry it does not own:

{
  "hooks": {
    "SessionStart": [
      { "hooks": [ { "type": "command", "command": "mvac verify 2>&1 || true" } ] }
    ],
    "PostToolUse": [
      {
        "hooks": [ { "type": "command", "command": "mvac verify >&2 || exit 2" } ],
        "matcher": "Edit|Write|MultiEdit"
      }
    ]
  }
}

SessionStart catches a lying brain before the agent conceives code on top of it; PostToolUse re-checks after every edit. Both run the default policy, not --strict.

The skill directory is a mirror, not an accretion: every run deletes anything under .claude/skills/multivac/ that the package no longer ships — including a file you put there yourself, because nothing on disk says who wrote it (MV-73). Your own skills live beside it: doors never touches a sibling under .claude/skills/, only the one directory it writes. What multivac owns here is the individual command, matched exactly — never an entry that merely mentions it — and the set it owns is the three commands multivac has written, so a brain projected before MV-112 is upgraded in place rather than gaining a second gate beside the mute one. The wrapping differs per event because Claude Code reads each hook back on one channel only; the contract and its reasons are in hooks. mvac verify --strict is your hook and stays untouched, commands you add beside multivac’s stay in place, and the matcher on an entry is yours. The rule and the notice it prints are in hooks.

If CLAUDE.md already exists as a regular file, doors refuses to replace it and says what to do:

api: notice: CLAUDE.md exists as a regular file — merge it into AGENTS.md and remove it to get the symlink

On Windows without developer mode the symlink is not permitted; the notice tells you to put @AGENTS.md as the first line of CLAUDE.md instead.

cursor

file written.cursor/rules/multivac.mdc
kindstub, with frontmatter
skill
hook config
detected byan existing .cursor/ directory
sourcehttps://cursor.com/docs/context/rules

Cursor reads AGENTS.md at the project root, so this target is optional. Take it when you want the door pinned into every chat rather than read opportunistically: only .mdc files under .cursor/rules carry the frontmatter that sets alwaysApply.

The file in full:

---
description: multivac door — ecosystem law, brain location
alwaysApply: true
---

<!-- multivac:begin -->
Read `AGENTS.md` at the repo root — the multivac door: what is law here, where the brain lives. Run `multivac verify` before you commit.
<!-- multivac:end -->

opencode

file writtennone — AGENTS.md is the integration
kindnative
detected byan existing opencode.json
sourcehttps://opencode.ai/docs/rules/

opencode reads AGENTS.md at the project root and up the tree. There is nothing to project: extra instruction files would go under instructions in opencode.json, which multivac does not own.

Declaring opencode in doors: changes no bytes on disk. It changes doctor, which now reports the canonical door on opencode’s behalf:

opencode: AGENTS.md ok (read natively)

If AGENTS.md is missing, the fix doctor names for a native entry is multivac init ., not multivac doors — because init is what creates the canonical file.

codex

file writtennone — AGENTS.md is the integration
kindnative
detected byan existing .codex/ directory
sourcehttps://learn.chatgpt.com/docs/agent-configuration/agents-md

Codex reads AGENTS.md from the git root down to the working directory, concatenated, nearest last. Nothing to project; its own config is .codex/config.toml, which multivac does not write.

windsurf

file writtennone — AGENTS.md is the integration
kindnative
detected byan existing .windsurf/ directory
sourcehttps://docs.windsurf.com/windsurf/cascade/agents-md

Cascade treats a root AGENTS.md as an always-on rule, and one in a subdirectory as a glob rule for that directory. The legacy .windsurf/rules/*.md mechanism still works but needs its own frontmatter, so multivac stays with the canonical file.

gemini

file writtenGEMINI.md
kindsymlinkAGENTS.md
detected byan existing .gemini/ directory
sourcehttps://geminicli.com/docs/cli/gemini-md/

Gemini CLI reads GEMINI.md by default. It can be pointed at AGENTS.md with context.fileName in .gemini/settings.json — but the symlink needs no settings file and no merge into someone else’s JSON, so that is what multivac projects.

copilot

file written.github/copilot-instructions.md
kindstub, no frontmatter
detected byan existing .github/copilot-instructions.md
sourcehttps://docs.github.com/en/copilot/reference/custom-instructions-support

Copilot reads AGENTS.md in some surfaces — the cloud agent, VS Code chat, Copilot CLI — but .github/copilot-instructions.md is the one path supported everywhere. It takes plain markdown with no frontmatter, so the stub is the managed block alone:

<!-- multivac:begin -->
Read `AGENTS.md` at the repo root — the multivac door: what is law here, where the brain lives. Run `multivac verify` before you commit.
<!-- multivac:end -->

Three artifact classes

Across all entries, multivac writes at most three kinds of thing, and they differ by when the agent reads them:

classloadedcarries
dooralways — first read of the sessionpointers and law: where the brain is, what binds, run verify
hooksnever read — they fireenforcement: git pre-commit/pre-push, harness hooks
skillon demandthe operating manual: anchor grammar, the change lifecycle, the interview

Only claude currently has all three. Git hooks are installed for every repo doors reaches, regardless of which harness entries you declared — see Hooks.

Detection

With no --provider flag, init probes for the detect path of every registry entry and writes what it found as a commented proposal, never as an enabled key:

doors: [agents]
# detected claude, cursor artifacts — to project the door there, use:
# doors: [agents, claude, cursor]

Detect, then ask. multivac never enables an integration because a directory happened to exist.