Vinaya CLI

Vinaya’s command reference.

documented against @attalabs/vinaya@0.19.2

help

$ vinaya help

Show this help text

version

$ vinaya version

Print the CLI version

Options

--jsonEnveloped JSON output (schema: 1)

init

$ npx @attalabs/vinaya init

Install 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.

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.

Options

--dry-runPrint the full diff without installing anything
--yesSkip the confirmation prompt

init product

$ vinaya init product

Register a project in .vinaya/projects.md (and its project:<name> label) in an already-initialized repo

Writes (or appends to) .vinaya/projects.md — the registry Vinaya Studio's tranche board resolves a project's board link against. Idempotent: re-running with the same name updates nothing.

Also creates a project:<name> label (create-if-absent) — informational only, not read by any shipped check or by Studio.

A missing GitHub remote skips the label (the one forge-reaching op) but still writes the registry row, which is a pure local file write.

Options

--path <path>The project's home folder — declared, not derived. Defaults to the repo root.

check

$ vinaya check

Run one check, or every registered check

Each spawned check's child process sees only a fixed baseline (PATH, LANG, HOME, HTTPS_PROXY, HTTP_PROXY, NO_PROXY, TMPDIR) plus whatever its CheckSpec['env'] declaration explicitly forwards — never the full parent environment. A required (true) or unsatisfied anyOf declaration missing from the caller's environment synthesizes a CheckError before the check ever spawns. Declare env (a core check's own registration, or vinaya.config.json's checks.<name>.env for a custom one) for any check that reads process.env/Bun.env/Deno.env directly — vinaya doctor carries the permanent diagnostic for one that doesn't.

--plan composes with --json. It requires zero env vars and never prints an env value — only how each one resolves (passthrough, optional, literal, or anyOf). A FAIL_CLOSED entry (a bare key with no namespace matching no core check) always renders inline rather than being dropped, and exits non-zero. --plan previews what the next minor's execution will enforce — vinaya check itself still runs the flat, unvalidated registry this release.

--local exists because a requiresOpenPr check (the core closes-n/test-plan, or a custom check declaring the same field) can only evaluate for real once a pull request exists — the generated pre-commit/pre-push hooks pass it so the first commit on a fresh task branch is never asked to satisfy a PR-body field before a PR can possibly exist. CI's vinaya-checks.yml omits it, so these checks always run for real once a PR is open.

Options

--allRun every registered check instead of one named check
--jsonEnveloped JSON output (schema: 1)
--diff-onlyScope diff-declared checks to changed files
--localSkip every requiresOpenPr check (closes-n, test-plan) — set by the generated hooks, never by CI
--parallel[=n]Concurrency cap (default: cpu-derived)
--planPrint the resolved check registry (default/overridden/additive) without running anything

new check

$ vinaya new check

Scaffold a custom check into ./scripts/vinaya-checks/

pr create

$ vinaya pr create

Open a pull request after full brief-schema validation

Runs the config-defined brief-schema gate (briefSchema.pr in vinaya.config.json) LOCALLY before any gh write — prevention, not detection. On any failure it refuses with the versioned CheckError contract (one JSON line per finding on stderr, exit 1) whose agent_recovery_prompt names the exact corrective command.

Options

--titlePR title (validated against the forge-title grammar)
--body-filePath to the PR body (stream-safe; the same bytes are validated and sent)
--labelLabel(s) to apply (repeatable, comma-separated)
--validate-onlyRun every gate and report PASS without opening the PR
--jsonEnveloped JSON output (schema: 1)

pr edit

$ vinaya pr edit

Edit an existing pull request (<n>) after full brief-schema validation

The target PR's real head branch and changed files are fetched from the forge to build the validation context — a failed fetch is a hard refusal, never a fall-back to the local checkout.

Options

--titleNew PR title (validated against the forge-title grammar)
--body-filePath to the new PR body (stream-safe; same bytes validated and sent)
--validate-onlyRun every gate and report PASS without editing the PR
--jsonEnveloped JSON output (schema: 1)

issue create

$ vinaya issue create

Open an issue after full brief-schema validation

A task Issue (any vinaya/tranche:* label) must carry the full Planner rationale (briefSchema.issue); non-task Issues pass through unvalidated.

Options

--titleIssue title (validated on task Issues)
--body-filePath to the Issue body (stream-safe; same bytes validated and sent)
--labelLabel(s) to apply; a `vinaya/tranche:*` label marks a task Issue
--validate-onlyRun every gate and report PASS without opening the Issue
--jsonEnveloped JSON output (schema: 1)

issue edit

$ vinaya issue edit

Edit an existing issue (<n>) after full brief-schema validation

The target Issue's actual labels are fetched from the forge and unioned with argv to decide task-Issue applicability — a failed fetch is a hard refusal.

Options

--titleNew Issue title (validated on task Issues)
--body-filePath to the new Issue body (stream-safe; same bytes validated and sent)
--validate-onlyRun every gate and report PASS without editing the Issue
--jsonEnveloped JSON output (schema: 1)

doctor

$ vinaya doctor

Diagnose 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).

Options

--jsonEnveloped JSON output (schema: 1)

upgrade

$ vinaya upgrade

Regenerate hooks, workflow, and config to the current contract version (diff-and-confirm)

Options

--dry-runPrint the full diff without regenerating anything
--yesSkip the confirmation prompt

archive

$ vinaya archive

Run the post-merge Archivist directly: provenance + Issue close-out for a merged task PR

Resolves the merge commit's associated PR via gh, and — if it's a task PR without a provenance comment yet — posts the provenance block and closes the linked Issue. Idempotent: re-running against an already-archived PR is a no-op.

The same logic the generated vinaya-archivist.yml workflow's post-merge job runs on every push to main — callable directly for a one-off run or local verification.

Options

--merge-shaThe merge commit to resolve (defaults to the current HEAD)

archive tranche

$ vinaya archive tranche

Close a tranche — the tranche-level bookend to `init product`, closing the Milestone via the CLI

Refuses if any task Issue attached to the named tranche is still open, naming each one — closing a tranche with unresolved work is never silently allowed.

Once every task Issue is closed, prompts for confirmation (unless --yes) and closes the GitHub Milestone.

Options

--yesSkip the confirmation prompt

audit

$ vinaya audit

Run the ring-2 dead-branch-push and direct-main-push detection checks directly

Dead-branch-push is never-red — a notification channel that flags (label + PR comment) any task/* branch whose tip commit lands after its own PR already resolved. Direct-main-push is a real pass/fail — it opens an incident Issue and exits 1 if a commit on main has no associated merged PR.

The same logic the generated vinaya-archivist.yml workflow's daily-drift and direct-main-push-detection jobs run on schedule / on every push to main — callable directly for a one-off run or local verification.

Options

--onlyScope to one check: 'dead-branches' or 'direct-push'
--shaThe commit to check for direct-main-push (defaults to the current HEAD)
--jsonEnveloped JSON output

eject

$ vinaya eject

Remove every Vinaya-installed artifact, restoring the repo to stock

Options

--dry-runPrint the full removal diff without removing anything
--yesSkip the confirmation prompt

demo break

$ vinaya demo break

Run a guided refusal-then-fix demo on an isolated, discardable branch

Creates a collision-safe vinaya/demo-break-<id> branch off the current one, stages a deliberately incomplete draft brief, and attempts a real git commit — the repo's actually-installed pre-commit hook refuses it with its real output, not a scripted string. Applies the minimal fix, commits again, then switches back and deletes the demo branch.

Safe to run twice: refuses on a dirty working tree, refuses from a detached HEAD, and recovers automatically from a prior crashed run before starting a fresh one — never leaves the original branch touched or a stray demo branch behind.

Options

--keepSkip cleanup and leave the demo branch checked out to inspect

waiver

$ vinaya waiver

Apply the actor-verified 'vinaya/waiver:docs' or 'vinaya/waiver:review' label after prompting for a reason

Applies the label via gh pr edit --add-label, under the invoking human's own authenticated gh identity — this command never fabricates an actor. A waiver is never an agent-emittable string: not a PR body field, not a commit trailer, not a comment — the label plus its own labeling-timeline actor is the only mechanism ring 1 honors.

--print-only prints the exact gh pr edit/gh pr comment commands and runs nothing — for a human who wants to run them itself, or a CI/non-interactive context where an agent session should never be the one applying its own waiver.

Options

--reasonThe waiver rationale, posted as a PR comment (prompted for if omitted)
--print-onlyPrint the exact `gh` commands instead of running them

studio

$ vinaya studio

Launch local Vinaya Studio against this repo — works standalone via npx, not just inside a Vinaya workspace checkout

quickstart

$ vinaya quickstart

Guided 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.