About getting started
Installing Vinaya puts the harness into your repository: its git hooks, its CI workflow and a starter vinaya.. From then on, every commit, push and pull request runs the same checks, and every governed act lands in the Log.
The Vinaya process comes with it, plugged in by default.
How it works
- 1
Install
Run
vinaya initin a git repository. It shows you the complete diff first and writes nothing until you confirm. Add--dry-runto see the diff and stop. - 2
Check the install
Run
vinaya doctor. It reports what is missing or out of date and never writes. - 3
Keep it current
Run
vinaya upgradeafter a new version. It regenerates only the files Vinaya owns and keeps your settings. - 4
Remove it
Run
vinaya eject. It removes exactly whatinitinstalled, and lists the labels it created for you to remove by hand.
Vinaya records what it installed in the managed part of vinaya.. That record is how upgrade and eject know what is theirs, so neither touches your own files. See Setup commands for every file and flag.
The commands
quickstart
$ npx @attalabs/vinaya quickstartGuided wizard: init, doc-owners, project, commit, demo break, doctor, push — one command
Calls init's own diff-and-confirm flow unchanged (pausing on Enter before the diff prints, so its own step header isn't scrolled off by a long diff), then Y/n-prompts through the workarounds a guest used to run by hand: binding .vinaya/doc-owners pairs (bad input offers a retry instead of silently skipping, and a pointer that doesn't exist on disk is refused outright — both loop across as many pairs as the guest wants, not just one), registering tracked projects (init product, same retry/loop shape), committing the install, running demo break as proof the gates actually work (default yes — the one step this wizard makes hardest to skip), running doctor, and pushing. Each declined prompt skips only that step; the install commit itself is never prompt-gated — it just no-ops when there is genuinely nothing to commit.
Never reimplements or edits init/init product/demo break/doctor — it only calls their existing, unmodified entry points in sequence.
init
$ npx @attalabs/vinaya initInstall Vinaya's git hooks, CI workflow, and starter config (diff-and-confirm, non-destructive)
It detects your repo, prints the complete diff of every intended change, and waits for your confirmation before installing anything. --dry-run prints that same diff and installs nothing. Nothing ever runs automatically on package install.
It installs one CI workflow that runs vinaya check --all --diff-only, alongside your existing workflows — refusing to overwrite rather than touching foreign content already at that path. Git hook stubs invoke the vinaya binary directly; if a hook already exists, it appends a delimited managed block, shown verbatim in the diff first, rather than overwriting it.
vinaya.config.json is seeded with a starter ruleset extracted from Vinaya's own battle-tested gates, not invented defaults. Issue and PR templates carrying the brief schema are added alongside your own; tier and needs:*-input labels are created only if they don't already exist — your existing labels are never modified.
Two empty folders — vinaya/checks/ and vinaya/roles/ — are scaffolded alongside the config, each held open by a placeholder file (git does not track empty directories). vinaya new noop-check and vinaya new role write their real output into these folders later; both placeholders are recorded in the ownership manifest, so eject removes them exactly like everything else init created.
The generated workflows reach the vinaya binary through npx @attalabs/vinaya, with no build step — unless your repo vendors the CLI itself (a workspace member declaring the name @attalabs/vinaya), in which case npx would resolve to that unbuilt local member instead of the registry. init detects that at generation time and writes workflows that build and run your own copy by path instead, so your CI exercises the code in the pull request. upgrade and doctor make the same determination, so regenerating is stable.
The recommended branch-protection command is printed for you to run yourself — it is never applied, and your PATH is never touched. eject removes exactly the managed block it owns, or a whole file only if init created it.
--dry-runPrint the full diff without installing anything--yesSkip the confirmation promptupgrade
$ vinaya upgradeRegenerate hooks, workflow, and config to the current contract version (diff-and-confirm)
When claude is a selected agent vendor, also retrofits the Claude Code Stop hook (.claude/hooks/track-transcript.sh, .claude/settings.json) the same way init installs it on a fresh repo — for a repo that ran init before this hook existed, closing the gap that left token rows reading —/—/— with no operator ever told to re-run init. Never overwrites a foreign .claude/settings.json (refuse-if-foreign, same as init); appends to a foreign Stop-hook script rather than replacing it (managed-block, same discipline as the git hooks).
--dry-runPrint the full diff without regenerating anything--yesSkip the confirmation promptdoctor
$ vinaya doctorDiagnose hook, workflow, and config health — report only, never mutates
Carries the same env-declaration diagnostic vinaya check warns with — permanently, at info severity, not just ahead of the spawn-default flip — plus a warn-severity lint over suspicious env literal forms (a stray "true"/"false" string, or a high-entropy literal that reads like a leaked secret committed to config).
Also carries the two permanent checks-classification diagnostics: a config key that REPLACES a core check (warn), and a bare un-namespaced key that is REJECTED (error, naming the rename requirement). These are the reason a config vinaya check now refuses outright is still diagnosable — the refusal runs nothing, so doctor is the surface that explains why.
--jsonEnveloped JSON output (schema: 1)eject
$ vinaya ejectRemove every Vinaya-installed artifact, restoring the repo to stock
--dry-runPrint the full removal diff without removing anything--yesSkip the confirmation promptConfiguration
vinaya init seeds a starter vinaya.: both rings flags set to true, an empty checks, a starter briefSchema, then managed. An empty file is already a full harness, so add a key only to change what runs. See Configuration.