Install
npx multivac@latest initThat is the whole thing. No install, no clone, no global.
Requirements
| requirement | why |
|---|---|
| Node.js ≥ 24 | declared in engines; the CLI is ESM and uses modern node: APIs |
| git | verify shells out to git ls-files; the brain and every repo are git-native |
Nothing else. Three runtime dependencies — picomatch, yaml and citty —
and that count is itself law: the third arrived as a design change with the row
and the constitution moved before the package, and a fourth faces the same.
Try it, then keep it
npx fetches and runs without installing anything, which is the right shape
for the first command you ever run against a repo:
npx multivac@latest init # write the brain
npx multivac@latest doctor # what is declared, what was foundOnce you know you want it, put it on your PATH so the hooks find it too:
npm i -g multivac@latest
# or: pnpm add -g multivac@latestThe hooks care which one you did. The shims run the most specific multivac
they find: this repository’s own build when the repository is multivac, then the
multivac it declares (npx --no-install multivac), then mvac on PATH.
npx --no-install resolves a package already present in the project, not one it
has to fetch — so a global install, or multivac as a devDependency of the brain,
both arm the floor. npx multivac@latest typed by hand does not, because
nothing persists.
Published on npm, MIT, and small enough to read: npx multivac@latest fetches
the current release. The CLI surface below is what ships today, and the parts
still moving say so where they appear.
Or from source
For working on multivac itself, or to run an unreleased commit:
git clone https://github.com/ulm0/multivac
cd multivac
corepack pnpm install
pnpm run build
pnpm link --global # optional; every command also works as `node /path/to/multivac/dist/cli.js`The repo develops with pnpm and says so if you reach for another one:
$ npm install
multivac develops with pnpm — run: corepack pnpm installThat guard is scoped to the repo. Installing the published package with npm
or npx is entirely fine — a tool that refused its own users’ package manager
would be a tool nobody installs.
Two names, one binary
{
"bin": {
"multivac": "dist/cli.js",
"mvac": "dist/cli.js"
}
}multivac and mvac are the same file. The docs use them interchangeably:
multivac in prose where it reads better, mvac in shell blocks where it is
shorter. The hooks look for mvac last — the shims try this repository’s
own dist/cli.js (with its node_modules beside it, and only when the
repository is multivac), then npx --no-install multivac, then mvac on
PATH. Inside the clone the build hits; in a repository that neither is nor
declares multivac, the linked mvac does. With none of the three the shim
warns on stderr and exits 0, verifying nothing.
See Hooks.
Check it
$ mvac --versionIt prints the version in the package you installed. What holds the published version honest is the release job: it refuses to publish under a tag that disagrees with the manifest.
$ mvac --help
multivac <command> [args]
commands:
init scaffold the brain: everything multivac owns under .multivac/
seed deterministic boundary inventory -> .multivac/seed-report.md
verify check anchors against the declared repos (deterministic, offline)
count dry-run an anchor leg: match count + per-file breakdown, verify's own matcher
doors project doors + install git hooks into the brain and declared repos
doctor what is declared, what was found, what is degraded, how to fix it
repos list declared repos; `repos sync [--shallow]` clones the missing, fetches the rest; `repos check` verifies them offline
change new/plan/apply/land/close — the ecosystem change lifecycle
roadmap the changes that have not started yet — list them, record one
help help <topic|command> — `help anchor` prints the anchor grammar on one screenpackage.json said at the commit you cloned, and the commit is the identity —
a build between releases carries the previous release’s number.Every machine needs its own runner
The hook files travel with the clone — they are versioned under
.multivac/hooks/, and multivac doors points core.hooksPath at them in each
new clone — unless the repo’s core.hooksPath already names a directory of its
own, or it has .husky/ and no core.hooksPath: then the shims go beside the
repo’s own hooks and core.hooksPath stays as it was — but the binary does not. On a machine where none of the
three runners resolves, the shim prints one warning to stderr and exits 0:
the commit lands, unverified. That is deliberate, and it is why the install
above is per machine and why multivac doctor names the runner it found, or
says INACTIVE. See Hooks.
Next
Scaffold a brain: Getting started.