# Vibgrate CLI — Full Documentation > [Code Drift Intelligence](https://vibgrate.com/code-drift-intelligence) across ~19 ecosystems — Node, .NET, Python, Java, Go, Rust, and more For a quick overview, see the [README](./README.md). This document covers everything in detail. --- ## Table of Contents - [How It Works](#how-it-works) - [Choosing a rollout model: one-off vs CI](#choosing-a-rollout-model-one-off-vs-ci) - [Commands Reference](#commands-reference) - [vg baseline](#vg-baseline) - [vg bisect](#vg-bisect) - [vg drift](#vg-drift) - [vg evidence](#vg-evidence) - [vg fix](#vg-fix) - [vg init](#vg-init) - [vg report](#vg-report) - [vg review](#vg-review) - [vg sbom](#vg-sbom) - [vg scan](#vg-scan) - [Vulnerabilities and exposure attribution](#vulnerabilities-and-exposure-attribution) - [vg update](#vg-update) - [vg why](#vg-why) - [Workspace auth & cloud upload](#workspace-auth--cloud-upload) - [vg dsn create](#vg-dsn-create) - [vg login](#vg-login) - [vg logout](#vg-logout) - [vg push](#vg-push) - [Code Graph Commands](#code-graph-commands) - [vg ask](#vg-ask) - [vg build](#vg-build) - [vg watch](#vg-watch) - [vg bundle](#vg-bundle) - [vg code](#vg-code) - [vg embed](#vg-embed) - [vg export](#vg-export) - [vg facts](#vg-facts) - [vg guide](#vg-guide) - [vg impact](#vg-impact) - [vg install / vg uninstall](#vg-install) - [vg lib](#vg-lib) - [vg locale](#vg-locale) - [vg map / vg hubs / vg areas / vg oddities](#vg-map--vg-hubs--vg-areas--vg-oddities) - [vg llm-host](#vg-llm-host) - [vg models](#vg-models) - [vg module](#vg-module) - [vg path](#vg-path) - [vg savings](#vg-savings) - [vg serve](#vg-serve) - [vg share](#vg-share) - [vg show](#vg-show) - [vg status](#vg-status) - [vg tests](#vg-tests) - [vg tree](#vg-tree) - [vg unknowns](#vg-unknowns) - [Context compression](#context-compression) - [vg serve --compress](#vg-serve---compress) - [vg install --compress / vg uninstall](#vg-install---compress--vg-uninstall) - [vg savings / vg show savings](#vg-savings--vg-show-savings) - [vg install --learn](#vg-install---learn) - [vg serve memory](#vg-serve-memory) - [vg serve compress / vg serve retrieve](#vg-serve-compress--vg-serve-retrieve) - [Configuration reference (`VG_*`)](#configuration-reference-vg_) - [SDK](#sdk) - [Holistic Code Specification (vg hcs)](#holistic-code-specification-vg-hcs) - [vg hcs extract](#vg-hcs-extract) - [vg hcs digest](#vg-hcs-digest) - [vg hcs map](#vg-hcs-map) - [vg hcs gate](#vg-hcs-gate) - [vg hcs validate](#vg-hcs-validate) - [Diagnostics, IDE & runtime](#diagnostics-ide--runtime) - [vg daemon](#vg-daemon) - [vg doctor](#vg-doctor) - [vg lsp](#vg-lsp) - [vg policy](#vg-policy) - [DriftScore](#driftscore) - [Drift Baselines & Fitness Functions](#drift-baselines--fitness-functions) - [How the Score Is Calculated](#how-the-score-is-calculated) - [Risk Levels](#risk-levels) - [Score Components](#score-components) - [Output Formats](#output-formats) - [Text](#text) - [JSON Artifact](#json-artifact) - [SARIF](#sarif) - [Markdown](#markdown) - [Configuration](#configuration) - [vibgrate.config.ts](#vibgrateconfigts) - [Thresholds](#thresholds) - [Scanner Toggles](#scanner-toggles) - [Extended Scanners](#extended-scanners) - [Platform Matrix](#platform-matrix) - [Dependency Risk](#dependency-risk) - [Dependency Graph & Duplication](#dependency-graph--duplication) - [SBOM-ready Supply Chain Inventory](#sbom-ready-supply-chain-inventory) - [Tooling Inventory](#tooling-inventory) - [Build & Deploy Surface Area](#build--deploy-surface-area) - [TypeScript Modernity](#typescript-modernity) - [Breaking Change Exposure](#breaking-change-exposure) - [File Hotspots](#file-hotspots) - [Security Posture](#security-posture) - [Security Scanners](#security-scanners) - [Service Dependencies](#service-dependencies) - [Database Schema](#database-schema) - [Architecture Layers](#architecture-layers) - [Code Quality Metrics](#code-quality-metrics) - [OWASP Category Mapping](#owasp-category-mapping) - [CI Integration](#ci-integration) - [GitHub Actions](#github-actions) - [Azure DevOps](#azure-devops) - [GitLab CI](#gitlab-ci) - [Generic Pipelines](#generic-pipelines) - [Vibgrate Cloud Upload](#vibgrate-cloud-upload) - [DSN Tokens](#dsn-tokens) - [Data Residency](#data-residency) - [Privacy & Security](#privacy--security) - [Exit Codes](#exit-codes) - [Programmatic API](#programmatic-api) --- ## How It Works Vibgrate recursively scans your repository for `package.json` (Node/TypeScript), `.sln`/`.csproj` (.NET), Python manifests, and Java build manifests. For each project it discovers, it: 1. **Detects** the runtime version, target framework, and all dependencies 2. **Queries** the npm/NuGet registry for latest stable versions (with built-in caching and concurrency control) 3. **Computes** how far behind each component is — major version lag, EOL proximity, dependency age distribution 4. **Generates** a deterministic [DriftScore](https://vibgrate.com/driftscore) (0–100) 5. **Produces** findings, a full JSON artifact, and optional SARIF output Core drift analysis does not execute source code. Optional security scanners can run lightweight secret heuristics and local toolchain checks. [Vibgrate Cloud](https://vibgrate.com/cloud) upload remains optional. --- ## Choosing a rollout model: one-off vs CI Most teams adopt Vibgrate in two steps: 1. **One-off scan** to establish a baseline and identify immediate upgrade priorities. 2. **CI integration** to continuously detect drift regression on every pull request/build. | Mode | Benefits | Typical command | | ------------------ | --------------------------------------------------------------------------- | --------------------------------------------------------- | | One-off scan | Fast snapshot of current upgrade debt, useful for audits and planning | `npx @vibgrate/cli scan` | | CI-integrated scan | Continuous governance with automated failure thresholds and SARIF surfacing | `npx @vibgrate/cli scan --format sarif --fail-on error` | In practice, one-off scans tell you where you are today; CI keeps you from drifting back tomorrow. --- ## Feature coverage and practical usage guide This section summarizes what the CLI supports today and how to use each capability effectively. ### Supported project ecosystems Vibgrate evaluates **upgrade drift** in depth for: - **Node.js / TypeScript** (`package.json`, lockfiles) - **.NET** (`.sln`, `.csproj`) - **Python** (`requirements.txt`, `pyproject.toml`-style manifests) - **Java** (`pom.xml`, Gradle-style manifests) **Known-vulnerability detection** (`--vulns`) and **dependency attribution** (`vg why`, exposure windows) additionally cover npm / pnpm / yarn, pip / poetry / pipenv, cargo, composer, bundler, go, pub, hex, NuGet, and Maven/Gradle — read from each project's lockfile. ### End-to-end workflow (recommended) 1. Run an initial scan. 2. Save a baseline on your main branch. 3. Enforce drift gates in CI. 4. Export/report artifacts for stakeholders. Example: ```bash # Step 1: first scan vg scan # Step 2: baseline vg baseline # Step 3: policy in CI vg scan --baseline .vibgrate/baseline.json --drift-budget 40 --drift-worsening 5 --fail-on error # Step 4: produce report vg report --in .vibgrate/scan_result.json --format md ``` Expected results: - Teams get a stable score trend instead of one-time snapshots. - CI fails early when drift budgets are exceeded (exit code `2`). - Markdown/JSON/SARIF outputs are ready for engineering and governance workflows. ## Commands Reference Drift scoring, baselines, reports, supply-chain evidence, and related local tooling. **Typical path:** `vg init` → `vg scan` → `vg baseline` → `vg report` → `vg fix` ### vg baseline Create a drift baseline snapshot for delta comparison. ```bash vg baseline [path] ``` Runs a full scan and saves the result to `.vibgrate/baseline.json`. Use this as the starting point for tracking drift over time. --- ### vg bisect Pinpoint the commit where a dependency crossed a version line. Where `vg why` narrates every version change, `vg bisect` answers one targeted question: *when did we cross this line?* — for example, when a vulnerable dependency was finally patched past the fixed version, or when a major was adopted. ```bash vg bisect ``` `` is a version or a semver range. A bare version means "reached or surpassed" — `vg bisect lodash 4.17.21` is the same as `vg bisect lodash '>=4.17.21'`. It reads the same lockfile history `vg why` uses (npm / pnpm / yarn, pip / poetry, cargo, composer, bundler, go, pub, hex, NuGet, and Maven/Gradle), offline and without checking out any commit. It reports the commit that first reached the constraint — author, date, and the version before and after — or tells you the line was never crossed and shows the latest version in history (so an unadopted fix is obvious). Later flips, such as a downgrade that re-introduced the old version, are listed too. Add `--assert` to turn it into a CI gate: the command exits non-zero when the current version does not satisfy the constraint, so a pipeline step can block a merge until the fix is adopted. ```bash vg bisect lodash 4.17.21 --assert # fails the build until lodash is patched to >= 4.17.21 ``` Exit codes: `0` when the query resolves, `2` when `--assert` finds the constraint unsatisfied, `3` when the package has no version history, `5` for an invalid version or range. --- ### vg drift What is outdated across your dependencies — a fast, offline currency check. ```bash vg drift ``` Reads each project's lockfile and reports which dependencies have drifted behind their latest known version. Offline by default (uses the last-known catalog); add `--online` to check live registries for current latest versions. | Flag | Description | |------|-------------| | `--online` | Check live registries for the latest versions instead of the offline catalog | | `--fail-on ` | CI gate: exit non-zero when drift is found at this level (`major`, `minor`, or `standards`) | Add `--json` for machine-readable output. --- ### vg evidence Vibgrate Evidence — signed, reproducible regulatory evidence. Register products with digital elements, freeze a shipped release into an immutable component manifest, then answer "which shipped products contain this vulnerability, at which versions, in which markets, still in support?" as evidence a third party can verify offline. Reporting duties are modelled as **regimes** (jurisdiction-neutral): the EU Cyber Resilience Act (`--regime cra`) is the first; DORA incident reporting (`--regime dora-incident`) ships too. A new jurisdiction is a regime profile, not a new command. ```bash vg evidence init [--regime ] [--coordinator ] [--responsible ] [--filing-authority] [--ooo ] vg evidence regimes vg evidence product add [--markets DE,FR] [--classification ] [--in-scope] [--rationale ] [--bind ] [--until ] vg evidence product list vg evidence product show vg evidence release [--from ] [--image ] [--buildkit-metadata ] [--provenance ] [--ship-date ] [--build-id ] [--digest ] [--markets DE,FR] vg evidence exposure [--regime ] [--advisory ] [--offline] [--as-of ] [--products ] [--include-eol] [--format table|json] [--pack --stage ] [--bundle ] [--tsa ] vg evidence readiness [--regime ] [--format table|json] vg evidence support-period [--from ] [--until ] vg evidence pack [--regime ] [--stage ] [--advisory ] [--offline] [--out ] vg evidence drill [--regime ] [--scenario ] [--elapsed ] vg evidence watch [--regime ] [--since ] [--webhook ] [--format table|json] vg evidence verify [--pub ] vg evidence push [--result ] [--regime ] [--dsn ] [--no-releases] vg evidence export [--out ] [--regime ] ``` | Command | Description | |---------|-------------| | `vg evidence init` | Set org, coordinator CSIRT, and the person with filing authority | | `vg evidence regimes` | List available reporting regimes and their clocks | | `vg evidence product add` | Register a product with digital elements (PDE) | | `vg evidence release` | Freeze a shipped release into an immutable component manifest | | `vg evidence exposure` | Which shipped products contain a vulnerability — with signed evidence | | `vg evidence readiness` | Deterministic gap report against the regime's obligations | | `vg evidence drill` | Timed dry-run of the determination against a simulated advisory | | `vg evidence pack` | Build the submission pack a human pastes into the reporting platform | | `vg evidence watch` | Check CISA KEV for new exposure against your shipped components | | `vg evidence verify` | Verify an evidence bundle offline — no account, no network | | `vg evidence push` | Push the product registry, every frozen release manifest, and an optional signed exposure bundle to Vibgrate Cloud, which verifies the signature | | `vg evidence export` | Air-gap bundle of all evidence state | `release` freezes the manifest from a Vibgrate scan artifact or an SBOM — CycloneDX, SPDX, or either one wrapped in an in-toto / DSSE **SBOM attestation**. Where the artefact is a container image, it can also take the facts straight from what BuildKit wrote instead of values typed in by hand: `--buildkit-metadata` reads the `docker buildx build --metadata-file` output for the image digest and build reference; `--provenance` reads a SLSA provenance attestation for the source repository, commit, and base images; and `--image ` asks Docker for the image's digest, `org.opencontainers.image.*` labels, and any attached provenance and SBOM attestations (an attached SBOM becomes the manifest when `--from` is not given). `--image` runs `docker image inspect` and `docker buildx imagetools inspect`; the second contacts the image's registry when the reference is not present locally. Those facts are stored under `build` in the frozen manifest. A typed `--digest` that disagrees with what the build wrote is an error, never a silent preference, and attestation signatures are recorded as unverified — verify them with `cosign`. `push` sends the product registry, every frozen release manifest (components, artefact digest, and the build facts read from BuildKit), and — when `--result` points at a bundle directory — the exposure result with its DSSE envelope and RFC 3161 token. Vibgrate Cloud verifies the signature itself and records the outcome as `intact`, `failed` or `absent`; it never takes a flag's word for it (the old `--signed` flag is accepted and ignored). Bodies above the 10 MB limit drop component lists from the oldest releases first and say so; `--no-releases` omits the manifests entirely. In Vibgrate Cloud → Govern ▸ Evidence the frozen releases appear with their chain of custody, every component is searchable across releases, and each ledger entry offers its archived bundle for download. `exposure` matches against manifests **frozen at ship time**, not the current tree, and never guesses: a bound product with no frozen manifest returns `undetermined` with a reason. It runs fully `--offline` against a local advisory file, and can emit a signed evidence bundle (`--bundle `) that `vg evidence verify` checks offline with honest `verified` / `unverified` / `failed` states. Pass `--tsa ` to anchor the bundle to a trusted **RFC 3161** timestamp (`timestamp.tsr`), fully verifiable with `openssl ts -verify`. `watch` joins the CISA **Known Exploited Vulnerabilities (KEV)** catalog to the components in your frozen manifests (via OSV) and reports any KEV-listed vulnerability that affects a shipped release — alerting via stdout or `--webhook`. It **surfaces the KEV listing**; whether a vulnerability is "actively exploited" for a filing is your determination, not the tool's. **Exit codes** (CI-usable): `0` no exposure · `2` exposure found · `3` undetermined (manual review) · `1` operational error. No language model touches any figure in the evidence path, and every determination carries an evidence-not-compliance disclaimer. Vibgrate Evidence produces evidence to support your obligations under a regime; it does not determine compliance and is not legal advice. --- ### vg fix Turn a drift scan into ranked, risk-tiered upgrade plans and **apply** the one you choose — bringing packages up to date with confidence. `vg fix` uses the hosted Vibgrate planner, so it needs a login: run `vg login` (or set `VIBGRATE_DSN`). The CLI only measures your project locally — your source never leaves your machine; only dependency versions and the aggregate usage signals the planner needs are sent. ```bash vg login # once, to authenticate vg fix # analyse, then choose/apply a plan vg fix --dry-run # show exactly what would change, apply nothing vg fix --plan safe --yes # apply a specific plan non-interactively (CI) vg fix --no-apply # only print the plans vg fix --format json # machine-readable report for CI or an agent (no apply) vg fix --kind patch --yes # group and apply just the patch-level bumps vg fix --packages "lodash,chalk" --yes # group and apply exactly this batch ``` **Grouping, instead of one PR per package.** `--packages` and `--kind` narrow planning to a specific batch — every patch bump, every minor bump, or an explicit package list — so a CI job or an editor integration can open one PR per batch instead of a constant stream of single-package PRs. The two compose: `--packages "a,b,c" --kind minor` plans+applies only the minor bumps among those three. Each batch still goes through the full hosted planner, so cross-package conflicts *within that batch* (e.g. two packages that would pin a shared peer dependency to incompatible ranges once bumped together) are still caught — a conflict blocks apply unless you pass `--force`. **Applying.** When there's more than one plan, `vg fix` shows them and asks which to apply; with a single plan it applies it directly. Applying runs your project's own package manager (pnpm/npm/yarn/bun, pip, cargo, go, composer, dotnet, dart, …) to pin each target version — editing the manifest and installing in one step. Ecosystems without a clean one-shot pin (e.g. Maven/Gradle) are reported for a manual edit rather than skipped silently. Changes are local and git-reversible; use `--dry-run` to preview, `--no-apply` to never touch the project, `--yes`/ `--plan` for non-interactive runs. `--format json`/`md` are report-only. It reads the last scan artifact (`.vibgrate/scan_result.json`); if there isn't one it runs a drift scan first, skipping the code map. Every drifted dependency — across all supported ecosystems (npm, PyPI, Go, Cargo, Maven/Gradle, NuGet, Composer, RubyGems, pub, Hex, …) — is sent to the planner, which builds three plans and names the categorical best one: - **Low-risk** — patch and minor updates only, limited to lightly-used packages with no breaking-change signals and no dependency conflicts. - **Balanced** — the low-risk set plus single, clean major upgrades. - **Full** — everything to latest stable, except upgrades that are mutually incompatible at those versions. The analysis runs in two phases. A fast pass classifies every upgrade (patch / minor / major) and measures its blast radius from how heavily the package is used in your source. When major upgrades are involved it goes deeper: it checks npm peer dependencies to find packages that **cannot** upgrade together (e.g. `react-dom@18` needs `react@18`), scans the intervening releases for breaking-change signals, and considers the API surface — the classes and functions your code imports — that a new version must preserve. Security is folded in with **real-world exploitability**. Each upgrade is checked against [OSV](https://vibgrate.com/glossary/osv) in both directions (advisories **remediated** vs. **introduced**), and current-version advisories are cross-referenced with the [**CISA KEV**](https://vibgrate.com/glossary/kev) (known- exploited) list and [**FIRST EPSS**](https://vibgrate.com/glossary/epss) (exploit-probability) scores. A package with a known-exploited advisory is treated as must-fix, so the recommendation prioritises "fix these few" over churning everything. Advisories with no upgrade path in any plan are called out as unresolved. Each plan also shows an **expected DriftScore** — the estimated score after the plan lands — so you can weigh drift-reduction payoff against risk (e.g. *Low-risk: 58 → 54; Full: 58 → 31*). Where a package has a known upgrade **playbook**, the plan surfaces its codemod (e.g. `ng update`). The recommendation is deterministic. When known-exploited or high/critical advisories are open, `vg fix` recommends the lowest-risk plan that clears them — so if a patch closes a critical CVE, that's the plan it points you to rather than a sweeping major bump. With nothing severe outstanding, it prefers the least disruptive plan. | Flag | Meaning | |---|---| | `--format ` | Output format (default `text`; `json`/`md` are report-only, no apply). | | `--in ` | Scan artifact to read (default `.vibgrate/scan_result.json`, resolved against the analysed path). | | `--dsn ` | DSN token (or use `VIBGRATE_DSN` / `vg login`). | | `--region ` | Override data residency region (`us`, `eu`). | | `--plan ` | Apply a specific plan non-interactively (`safe`/`balanced`/`aggressive`). | | `--yes` | Apply the recommended plan without prompting. | | `--dry-run` | Show what would change without applying. | | `--no-apply` | Only print the plans; never modify the project. | | `--repository-name ` | Override the repository name recorded for this plan. | | `--packages ` | Plan/apply only these packages (comma-separated) — for grouping a specific batch instead of every drifted dependency. | | `--kind ` | Plan/apply only upgrades of this semver bump kind. Combine with `--packages` to target one Dependabot-style batch. | | `--force` | Apply a plan even if the planner flagged a blocking cross-package conflict within it. | | `--fail-on-vulns ` | Exit non-zero if the recommended plan leaves an advisory at or above this severity unresolved. | Exit codes: `0` on success, `2` when `--fail-on-vulns` finds an unresolved advisory at or above the threshold or an apply step fails. --- ### vg init Initialise Vibgrate in a project. ```bash vg init [path] [--baseline] [--yes] ``` | Flag | Description | | ------------ | ------------------------------------------- | | `--baseline` | Create an initial drift baseline after init | | `--yes` | Skip confirmation prompts | Creates: - `.vibgrate/` directory - `vibgrate.config.ts` with sensible defaults --- ### vg report Generate a human-readable report from a scan artifact. ```bash vg report [--in ] [--format md|text|json] ``` | Flag | Default | Description | | ---------- | ---------------------------- | -------------------------------------- | | `--in` | `.vibgrate/scan_result.json` | Input artifact file | | `--format` | `text` | Output format: `md`, `text`, or `json` | --- ### vg review **Vibgrate Review** — architecture and security-control review of the change you just made, run locally from the code map. Where `vg scan` answers "how far behind is the stack?", `vg review` answers a different question about the same repository: *did this change move the system toward its declared architecture, weaken a security control, or create an implication the author did not account for?* ```bash vg review # the working tree + index, vs HEAD vg review --in-place # the same, stated explicitly vg review --local # deterministic scanners; no hosted model vg review --loop # review → deterministic patch → re-review vg review --base origin/main # merge-base of HEAD and the base branch vg review explain arch-01 # the evidence behind one finding vg review findings-from-diff # deterministic graph/policy findings only vg review propose blast: --model forge --json ``` | Flag | Default | Description | | --------------------- | ------- | -------------------------------------------------------------------------- | | `--in-place` | off | Review the working tree as-is (already the default without `--base`) | | `--local` (global) | off | Deterministic pass on this machine — never a hosted model (implies `--offline`) | | `--loop` | off | Explicit review → apply deterministic patches → re-review. Never automatic. CLI only. | | `--base ` | — | Review HEAD against the merge-base with `` | | `--format ` | `text` | `text`, `json` (the receipt), `sarif` (security findings only), `md` | | `-o, --out ` | — | Write the formatted result to a file | | `--push` | off | Send the receipt to Vibgrate Cloud (needs a DSN) | | `--fail-on ` | from config | Gate CI on this decision: `none`, `fail`, or `needs_review` | | `--explain` | off | Add local-model explanations. **Requires a local model; fails closed (exit 6) without one** | | `--include-spans` | off | Include evidence line ranges in the pushed receipt | | `--include-snippets` | off | Include capped source snippets in the pushed receipt (explicit opt-in) | | `--offline` (global) | off | Never touch the network — no push, no model fetch | | `--write-context` | off | Write `.vibgrate/review-context.md` — committed agent memory | | `--inject-context [file]` | off | Update the managed review block inside `CLAUDE.md` (or a named file) | `vg review` reads the code map, so run `vg` (or `vg build`) in the repository first. Without a map it exits `6` — never `0`. #### Decisions Policy owns the decision, and it is the only layer that writes one. Findings — from the scanners or from the optional local model — have nowhere to put a verdict. | Decision | Meaning | | -------------- | ----------------------------------------------------------------------------- | | `pass` | No material delta, or every finding is target-aligned | | `fail` | An unresolved **protected** finding, or a high-severity finding above the confidence threshold | | `needs_review` | Findings a human should look at | | `undetermined` | Not enough evidence to decide — deliberately not a pass | **Gating is opt-in, exactly as it is for [`vg scan`](#vg-scan).** By default `vg review` reports the decision, writes it to the receipt, and exits `0` — even on `fail`. It breaks the build only when you ask it to: | Gate | Set by | Exits `2` on | | --- | --- | --- | | `none` | the default | never | | `fail` | `--fail-on fail`, or `enforcement = "enforced"` with `fail_on = "fail"` | `fail` | | `needs_review` | `--fail-on needs_review`, or the same in config | `fail`, `needs_review`, `undetermined` | A gate failure exits `2` (`GATE_FAILED`) — the same code `vg scan`, `vg drift` and `vg hcs gate` use. It is deliberately **not** `1`: across this CLI `1` means the command itself errored, and a policy verdict of `fail` is the command working correctly. Machines should branch on `decision` in the receipt, which is unambiguous; the exit code exists for CI. `enforcement = "advisory"` means no gate at all. That is what makes the setting real rather than a label on the receipt — an advisory repository reports honestly and never blocks a merge. **Protected findings** — an unguarded entrypoint, a removed guard, a validated taint flow, a known-vulnerable dependency — carry `protected_finding: true`. While one is unresolved, policy cannot emit `pass`: not via an approved exception, not via low confidence, not via the quick path, and not via anything the model says. Turn a rule off in `.vibgrate/review.toml` if it does not apply to your repository; that is the only way to stop it gating. #### What it looks for | Finding | What it means | |---|---| | `unguarded_entrypoint` | **Protected.** A mutating route has no authorization guard, where its peers do | | `guard_removed` | **Protected.** A guard was deleted and nothing equivalent remains | | `known_vulnerable_dependency` | **Protected.** A changed manifest declares a package with a known advisory | | `correctness` (producer `blast_radius`) | Blast-radius fact: a changed symbol has cross-file callers or dependents — the same reverse-reachability `vg impact` reports. Severity stays at or below medium. Stable `id` (`blast:{node_id}` / `blast:{path}:{name}`) is the finding_key. | | `correctness` (producer `architecture`) | Architecture-policy on a changed file (layer skip / boundary, peer deviation, duplicate implementation, uncovered change). Stable `id` is `arch:{rule}:{path}` using the architecture pack's rule string when one exists. Severity is `low`, `medium`, or `high` — never `critical` from version lag. | Two of these deserve a note, because they are what a linter cannot do: **Peers are graph areas and roles, not folders.** A directory is a filing decision; an area is a structural one. Recent files count roughly twice what year-old ones do, so a large abandoned wing does not get to dictate "the convention". Where peers are too evenly split to have one, Review says *no convention* instead of naming a plurality winner. **A majority is never treated as correct.** Peers establish what is *normal*; only `target_pattern` establishes what is *right*. A file that goes through the service layer while all its peers bypass it is the first one to improve — Review will not flag it. That is the difference between this and a consistency scanner, which by construction scores your best file worst. **Routes get three answers, not two.** Every route is `auth`, `not-auth`, or `unsure`, and `unsure` is never promoted. A guard Review does not recognise, or a file it could not parse, becomes an honest unknown — never "this route is open". Below four classified peer routes a finding is advisory and cannot gate. #### Findings from a change — `vg review findings-from-diff` The same deterministic scanners `vg review` runs, printed as the `vg.review.findings.v1` document — graph blast-radius facts for changed symbols, plus architecture and security-control findings on the change set. No hosted model. Use this when you want the findings (and their ids) without the signed receipt ceremony. ```bash vg review findings-from-diff vg review findings-from-diff --base origin/main vg review findings-from-diff --diff pr.patch --format json vg review propose blast: --model forge --json ``` `--diff` reads a unified diff (`-` is stdin). The patch names the files and hunks; the code map still has to be built (`vg` or `vg build`). `--format json` writes the findings document plus a `publishable` array of correctness rows (`kind: "correctness"`, stable `id` used as `finding_key`, producer metadata `blast_radius` or `architecture`) for App ingest. When capsule verification already emitted a fact for a finding's path, that evidence id is cited on the finding and on the publishable `receipts` array — no second receipt system. Suggested-fix on those rows is an honest skip — there is no computed PatchIR for blast-radius or architecture-policy rows. Propose is dry-run unless you pass `--apply --yes` on a topic branch — never the default branch. Local Code Mode ids are `spark`, `flow`, and `forge`; hosted Review uses `relay:`. This path does not post a comment or a check run. #### Before you write it — `assess_change` `vg review` asks "was that change sound?". The MCP tool `assess_change` asks "would this be sound?", while an agent is still deciding — the only point at which the fix is free. ```jsonc // one call, from any MCP client wired to `vg serve` { "file": "src/routes/invoices.ts", "content": "" } ``` It returns the conflicts, anything the proposal duplicates, the convention its peers follow with example files to open, and the declared target. One tool, not a family of them: a family pushes the judgement into the model, and in this product the model never decides. #### Agent memory — `--write-context` `vg review --write-context` writes `.vibgrate/review-context.md`: the declared target, the conventions with exemplars, what is currently open, and what to do. Commit it, and the next agent starts from the answer. It tells the agent to follow the **declared target**, never "the majority" — otherwise a repository mid-migration ends up with memory instructing every agent to perpetuate the legacy it is migrating away from. `--inject-context` keeps the same content in a marked block inside `CLAUDE.md`, leaving everything a human wrote in that file untouched. #### Configuration — `.vibgrate/review.toml` ```toml [review] enforcement = "advisory" # advisory | enforced fail_on = "fail" # fail | needs_review target_pattern = "layered" # the architecture you say you want [review.protected] unguarded_entrypoint = true known_vulnerable_dependency = true validated_taint = true ``` Read from the **trusted base branch** when `--base` is given, so a pull request cannot weaken the policy applied to itself. Team markdown packs live under `.vibgrate/review/` — the same tree the GitHub App reads. `ignore.md` drops matching finding paths; `policy.md` is attached to the human report; `merge.md` is evaluated locally (docs-only may approve; a change to `merge.md` itself is refused); `checks/*.md` each produce one CLI pass or a skipped-with-reason line. Custom checks have no extra correctness engine on the CLI either. `--loop` applies only deterministic `package.json` version bumps it can compute (from a known current → latest pair). It does not rewrite lockfiles, does not open a hosted branch, and never starts unless you pass the flag. Declaring `target_pattern` is what turns a layering observation into a *regression*. Without it, a dependency that skips a tier is reported as a medium finding about the repository's own majority — because a majority is not the same thing as a decision, and Review will not treat it as one. #### What it does not claim Review reports change integrity. It does not prove code is secure, correct, or free of vulnerabilities; it does not replace Semgrep, CodeQL, a compiler, or your tests; and **absence of findings is not a certification**. #### Privacy Source stays on the machine. `--push` sends the receipt — decisions, claims, evidence ids, paths, digests, versions — never the analysis capsule and never source text. Line ranges are opt-in (`--include-spans`); snippets are a second, explicit opt-in (`--include-snippets`) and are capped. `--offline`, or simply having no DSN, keeps the receipt on disk. Ready-to-use workflows: `examples/github-actions/vibgrate-review.yml` and `vibgrate-review-sarif.yml`. --- ### vg sbom Export [SBOMs](https://vibgrate.com/glossary/sbom) from an existing scan artifact or compare two artifacts. ```bash vg sbom export [--in ] [--format cyclonedx|spdx] [--out ] [--root ] [--no-transitive] vg sbom delta --from --to [--out ] vg sbom vex [--from ] [--statement ...] [--product ] [--out ] ``` | Command | Description | |---------|-------------| | `vg sbom export` | Emit CycloneDX or SPDX JSON from a scan artifact | | `vg sbom delta` | Compare dependencies between two artifacts (added/removed/changed + drift delta) | | `vg sbom vex` | Emit a spec-compliant OpenVEX document (exploitability statements) for attestation | Use this to treat SBOMs as operational intelligence instead of static compliance output. `vg sbom export` reports the full resolved dependency tree, not just what's declared in the manifest: it reads `package-lock.json` / `pnpm-lock.yaml` / `yarn.lock` (npm, pnpm, and yarn) from `--root` (defaults to the current directory) and folds every transitive package in alongside the directly-scanned ones. Each component carries a `vibgrate:scope` property (`direct` or `transitive`) so consumers can still tell the two apart. Pass `--no-transitive` to report only the manifest-declared dependencies, matching pre-existing output. Every component also carries a [purl](https://github.com/package-url/purl-spec) (`pkg:npm/@`, scoped names as their own namespace segment) — as the CycloneDX `purl` field and `bom-ref`, and as the SPDX `externalRefs` PACKAGE-MANAGER reference — so a vulnerability scanner can match components without re-deriving an identifier. When the lockfile format resolves real dependency edges (npm `package-lock.json` v2/v3 today; pnpm and yarn report components without edges), the SBOM also carries the resolved dependency graph: CycloneDX's top-level `dependencies` array, or SPDX `DEPENDS_ON` relationships. Where edges aren't resolvable, that section is left out entirely rather than shipping a graph that claims "no dependencies" when the truth is "not tracked". `vg sbom vex` is input-agnostic: it assembles a complete OpenVEX document from the statements you supply (`--from ` and/or repeatable `--statement`), so it works regardless of which scanner flagged the components. A zero-statement document is valid and honest — it asserts no known affected components. --- ### vg scan The primary command. Scans your project for upgrade drift. ```bash vg scan [path] [--vulns] [--full] [--format text|json|sarif|md] [--out ] [--fail-on warn|error|architecture-finding|architecture-warning] [--offline] [--package-manifest ] [--no-local-artifacts] [--max-privacy] [--baseline ] [--drift-budget ] [--drift-worsening ] [--changed-only] [--concurrency ] ``` | Flag | Default | Description | |------|---------|-------------| | `--vulns` | — | Also detect known vulnerabilities (OSV online; offline via `--package-manifest` advisories) | | `--full` | — | Comprehensive scan: enables `--vulns` and reports banned dependencies when a standards policy exists | | `--format` | `text` | Output format: `text`, `json`, `sarif`, or `md` | | `--out ` | — | Write output to a file | | `--fail-on ` | — | Exit with code 2 if findings at this level exist. `warn` / `error` gate on drift findings. `architecture-finding` (hard boundary violations) and `architecture-warning` (violations and warnings) gate on the architecture module's boundary findings, judged under the policy pack in force — `hexagonal-v1` unless `.vibgrate/architecture.toml`, `VIBGRATE_ARCHITECTURE_POLICY` or `vg build --policy` says `layered-v1`. The output names the pack whether the gate passes or fails; each failing row is `file:line symbol violation: … (rule)`. Pick the pack before turning this on: see [Architecture policy packs](./docs/architecture-policies.md) | | `--baseline ` | — | Compare against a previous baseline | | `--changed-only` | — | Only scan changed files | | `--concurrency ` | `8` | Max concurrent npm registry calls | | `--drift-budget ` | — | Fitness gate: fail if drift score is above this budget | | `--drift-worsening ` | — | Fitness gate: fail if drift worsens by more than % vs baseline | | `--push` | — | Upload scan artifact to Vibgrate Cloud after a successful scan | | `--dsn ` | `VIBGRATE_DSN` env | DSN used for `--push` authentication | | `--region ` | — | Override data residency (`us`, `eu`) during push | | `--strict` | — | Fail scan command if push fails | | `--ui-purpose` | — | Enable optional UI-purpose evidence extraction | | `--offline` | — | Disable network calls and disable upload/push behavior | | `--package-manifest ` | — | JSON or ZIP package-version manifest used for offline/latest lookups (latest bundle: `https://github.com/vibgrate/manifests/latest-packages.zip`) | | `--no-local-artifacts` | — | Do not write `.vibgrate/*.json` scan artifacts to disk | | `--max-privacy` | — | Hardened privacy mode with minimal scanners and no local artifacts | | `--no-graph` | — | Skip building the local code map that scan produces after scoring drift | | `--project-scan-timeout ` | `180` | Per-project scan timeout | | `--repository-name ` | directory / `package.json` name | Override the repository name recorded for this scan | | `--force` | — | Always create a fresh ingest, even when the repository is unchanged since the last scan | | `--quiet` | — | Suppress promotional output; scan results are unaffected | By default, the scan writes `.vibgrate/scan_result.json`. Use `--no-local-artifacts` or `--max-privacy` to suppress local JSON artifact files. For offline drift scoring, pass `--package-manifest ` with a downloaded manifest bundle such as `https://github.com/vibgrate/manifests/latest-packages.zip`. Examples: ```bash # Standard text scan vg scan # JSON output for automation vg scan --format json --out scan.json # CI gate with baseline regression protection vg scan --baseline .vibgrate/baseline.json --drift-budget 40 --drift-worsening 5 --fail-on error # Upload result in the same command vg scan --push --strict ``` Expected results: - Clear score/risk output in terminal (or JSON/SARIF when selected). - Exit code `2` when configured quality gates are exceeded. - When `--push` is enabled, artifact upload is attempted after scan completion. **Plan limits never block the scan.** If your workspace is at a plan limit that gates ingestion — repository cap, scan credits, VM minutes — the CLI warns with the reason (and the upgrade link), disables the upload, and runs the **full local scan** anyway, repeating the warning after the results so it isn't lost in the output. Local scoring never depended on the cloud, and now neither does it depend on your plan. Pass `--strict` to keep the old behaviour and fail the command instead, which is usually what you want in CI. --- ### Vulnerabilities and exposure attribution `vg scan --vulns` matches your installed dependencies against the public OSV database and records each known vulnerability — advisory id and CVE, severity, CVSS, and the fixing version — in the scan artifact, as findings, and in SARIF. Supply advisories in a `--package-manifest` bundle to run it offline. In a git repository the scan also attributes each finding: the commit, author, and date that introduced the vulnerable version, and how long you have been exposed. These exposure windows aggregate into remediation metrics framed around the [EU Cyber Resilience Act (CRA)](https://vibgrate.com/compliance/cra): open counts by severity, mean and maximum time exposed, and per-severity SLA breaches (defaults: critical 7 days, high 30, moderate 90, low 180). The metrics are descriptive — they show whether remediation keeps pace; they are not a compliance certification. The scan also reconstructs **closed** exposure windows from history — a vulnerable version that was later bumped out of the affected range or removed from the lockfile entirely — and reports real remediation time (MTTR) from them: measured, not estimated. Offline, a package-version manifest extends this to advisories that are fully fixed today, so a dependency that is clean now but was once vulnerable still counts toward your remediation record. Detection and attribution read each project's lockfile, so they cover npm / pnpm / yarn, pip / poetry / pipenv, cargo, composer, bundler, go, pub, hex, NuGet, and Maven/Gradle. ```bash # Online detection against OSV vg scan --vulns # Air-gapped: advisories supplied in the manifest bundle vg scan --vulns --offline --package-manifest ./package-versions.zip # Everything in one run: drift + vulnerabilities + a banned-dependency report vg scan --full ``` --- ### vg update Check for and install updates. ```bash vg update [--check] [--pm ] ``` | Flag | Description | | ----------- | ------------------------------------------------------ | | `--check` | Only check for updates, don't install | | `--pm` | Force a package manager (`npm`, `pnpm`, `yarn`, `bun`) | | `--global` | Update the global installation | | `-y, --yes` | Skip confirmation prompts | **On Windows**, updating a global install replaces files the running `vg` process has open — Windows locks loaded native modules (`.node`), so npm fails with `EBUSY: resource busy or locked`. `vg update` handles this: it stops the vgd daemon and any older `vg serve` first, and if the install still hits a locked file it offers to finish the update from a detached script that waits for `vg` to exit. Pass `-y` to accept that without a prompt. The script writes a transcript whose path is printed; check the result with `vg --version`. --- ### vg why Explain a dependency from git history: who added it, every version since, and any open vulnerabilities it carries. ```bash vg why ``` `vg why` reads your lockfile's history, so it works across npm / pnpm / yarn, pip / poetry, cargo, composer, bundler, go, pub, hex, NuGet, and Maven/Gradle projects. For Maven/Gradle the history comes from a resolved `gradle.lockfile`, or a `pom.xml`'s pinned direct-dependency versions (versions managed by a BOM/`dependencyManagement` aren't resolved). Open vulnerabilities and their introduction attribution come from your most recent `vg scan --vulns`. --- ## Workspace auth & cloud upload Sign in to Vibgrate Cloud, manage DSN tokens for CI, and push scan results. Local drift scoring does not require this — nothing leaves your machine until you push. **Typical path:** `vg login` → `vg dsn create` → `vg push` → `vg logout` ### vg dsn create Generate an HMAC-signed DSN token for API authentication. ```bash vg dsn create --workspace [--region ] [--ingest ] [--write ] ``` | Flag | Default | Description | | ------------- | ---------- | --------------------------------------------------------------------------- | | `--workspace` | _required_ | Your workspace ID, or `new` to auto-generate a workspace | | `--region` | `us` | Data residency region (`us`, `eu`) | | `--ingest` | — | Custom ingest API URL (overrides `--region`) | | `--write` | — | Write DSN to a file (add to `.gitignore`!) | When using `--workspace new`, the CLI auto-generates a workspace ID and provisions the DSN with the Vibgrate API. Rate limited to 1 new DSN per 5 minutes per IP address. --- ### vg login Authenticate the CLI with your Vibgrate workspace through the browser. Credentials are stored locally so `vg fix` and `vg push` can reach the hosted planner and Vibgrate Cloud. ```bash vg login ``` | Flag | Default | Description | |------|---------|-------------| | `--region ` | `us` | Data-residency region (`us`, `eu`) | | `--ingest ` | — | Custom ingest API URL (overrides `--region`) | | `--no-browser` | — | Print the URL to open instead of launching a browser (headless / SSH) | --- ### vg logout Clear stored Vibgrate login credentials from this machine. ```bash vg logout ``` --- ### vg push Upload scan results to the Vibgrate Cloud API. ```bash vg push [--dsn ] [--file ] [--region ] [--strict] ``` | Flag | Default | Description | | ---------- | ---------------------------- | ------------------------------------------- | | `--dsn` | `VIBGRATE_DSN` env | DSN token for authentication | | `--file` | `.vibgrate/scan_result.json` | Scan artifact to upload | | `--region` | — | Override data residency region (`us`, `eu`) | | `--strict` | — | Fail hard on upload errors | Upload is always optional. Best-effort by default — use `--strict` in CI if you want the pipeline to fail on upload errors. --- ## Code Graph Commands Build and query the deterministic code map. **Typical path:** `vg build` → `vg status` → `vg ask` → `vg impact` → `vg share` ### vg ask Ask the code map a question using hybrid lexical + structural + semantic search. ```bash vg ask "" ``` A local ONNX embedding model is downloaded once on first use, then cached and fully offline. Degrades gracefully to lexical-only under `--offline` or `--no-semantic`. > **Semantic search is opt-in.** The embedding backend (`fastembed`, which pulls a native ONNX runtime) is declared as an **optional dependency**: package managers install it by default, but if it's absent — e.g. you installed with `--omit=optional`, or it failed to build on your platform — `vg ask` and `vg embed` transparently fall back to lexical + structural search. Nothing else in the CLI needs it, so `vg build`, `vg show`, `vg impact`, drift reporting, and MCP serving all run without it. If you never use semantic `ask`, you can install lean: `npm i @vibgrate/cli --omit=optional`. A host application that bundles the CLI without optional dependencies (Vibgrate for VS Code does this) can supply the backend from its own directory by setting `VIBGRATE_EMBEDDER_PATH` to a folder whose `node_modules` contains `fastembed`; when set, that copy is used first. Before answering, `ask` checks whether files changed since the map was last built and, if so, rebuilds it incrementally first (only the changed files re-parse) — so answers always reflect the code as it is now. The check is stat-based and costs almost nothing when nothing changed; `--no-refresh` opts out. | Flag | Default | Description | |------|---------|-------------| | `` | — | Your question | | `-b, --budget ` | `2000` | Approx token budget for returned context | | `--no-semantic` | — | Lexical only; skip the local embedding pass | | `--no-refresh` | — | Answer from the map as built; skip the auto-rebuild when files changed | --- ### vg build Build or update the code map incrementally. ```bash vg build [paths...] ``` Maps source code into a graph artifact, enabling all downstream queries (`vg show`, `vg ask`, `vg impact`, etc.). | Flag | Default | Description | |------|---------|-------------| | `[paths...]` | `.` | Folders or files to map | | `--only ` | — | Restrict to languages (e.g. `ts,py,go`) | | `--exclude ` | — | Extra ignore glob (repeatable) | | `--jobs ` | auto | Worker count (`1` = single-threaded) | | `--scip ` | auto-detect | Ingest a SCIP index for precise resolution | | `--no-scip` | — | Ignore any SCIP index | | `--no-tsc` | — | Skip the TypeScript resolver (heuristic floor only) | | `--no-html` | — | Do not write `graph.html` | | `--no-report` | — | Do not write `GRAPH_REPORT.md` | | `--no-warm` | — | Do not warm the semantic index after building | | `--grammars ` | — | Grammar `.wasm` directory for offline/air-gapped use | | `-o, --export ` | — | Also write the map to a file (format from extension) | **Local by default — no git churn.** The first time vg writes into `.vibgrate/` it also creates `.vibgrate/.gitignore`, keeping the graph artifacts (`graph.json`, `graph.html`, `GRAPH_REPORT.md`, `facts.jsonl`, `mcp-navigation.json`) and the cache out of git — so builds, auto-refreshes, and MCP use never leave your branch dirty. Run `vg share` when you want the map committed for your team (it rewrites that ignore file). vg never touches an existing `.vibgrate/.gitignore`, so edit it (or leave it empty) to manage the ignores yourself. --- ### vg watch Rebuild the code map when source files change (debounced). Useful for long agent sessions. ```bash vg watch vg watch src/ --fast ``` | Flag | Default | Description | |------|---------|-------------| | `[paths...]` | `.` | Folders to watch | | `--debounce ` | `400` | Settle time after a change before rebuild | | `--fast` | — | Skip precise TypeScript resolve on rebuilds | | `--no-html` | — | Do not rewrite `graph.html` | | `--no-report` | — | Do not rewrite `GRAPH_REPORT.md` | ### vg bundle Build an air-gapped bundle — grammars, the code graph, and the library catalog — for use on a machine with no network. ```bash vg bundle ``` | Flag | Default | Description | |------|---------|-------------| | `--offline` | — | Build using only locally-available assets | | `-o, --out ` | `vg-bundle` | Output directory for the bundle | Add `--json` for machine-readable output. --- ### vg code A coding **agent** grounded in the deterministic code graph: its search tool resolves symbols and relations from the map, not text matches from a grep. It runs on a local model or a hosted one, and every change it makes to your working tree is approved before it lands. ```bash vg code # guided: pick a model, then describe tasks vg code "add a --timeout flag to the scan command" ``` #### Does it write to my disk? Yes — through approved steps, and only those. This is the one thing to be clear about before you run it: | You run | What happens | |---|---| | `vg code` or `vg code ""` at a terminal | The **agent loop**. Read-only steps (search, read, list, impact) run without prompting. Every edit and every command **asks you first**, and writes when you approve. | | `… --auto` | The same loop with no prompts. A denylist blocks catastrophic commands. For CI and scripted runs. | | `… --single` | The **one-shot planner**: one proposed edit, no tool loop, no commands. **Dry-run by default** — it prints the diff and writes nothing unless you pass `--apply` *and* `--yes`. | | `… --mock ` | The one-shot path driven by a scripted reply instead of a model (offline; tests, CI, benchmarks). | The agent loop is the default. `--apply` and `--yes` belong to `--single` / `--mock` only — in the agent loop, consent is the per-step approval instead. Without a TTY and without `--auto`, `vg code` refuses to start rather than writing unattended. **How the loop works.** The model is given tools and works in steps — search the code graph, read files, check a symbol's blast radius, edit, create/delete files, and run your tests or build — until the task is done. `--max-steps ` caps the loop (default 24). **Guided mode.** Run `vg code` with no instruction at an interactive terminal and it walks you through everything: it builds the code map, then asks where the model should run — a local model, or one of the current top providers (Claude, GPT, Grok, Gemini, …) surfaced live from the catalog — and which model, with an "enter a slug myself" option at every step. Before pulling any local model it runs a memory pre-flight (estimated footprint vs free RAM/VRAM and already-loaded models) and won't pull a model your machine can't run; then it drops into an agent session where you describe tasks and approve each change. For scripts and CI, pass an instruction with `--auto` (or `--mock`) — the agent only prompts at a TTY, so automation never blocks. While a session is active, Vibgrate Graph (`vg serve`) runs as a separate process for the life of the session and is stopped when you exit. Every graph-backed call is attributed to VG Code and the model in use, so `vg savings` reports token/$ savings **per model**. **A full local session (no cloud):** ```bash # one-time: a local coding model (or: vg models install) vg models pull qwen2.5-coder:7b # start a guided agent session — pick "Local model" → qwen2.5-coder:7b vg code ``` ```text VG Code · graph-grounded coding · v2026.x ✔ Code map built ✔ Model catalog loaded ? Where should the model run? ❯ Local model (on your machine) 1 found Anthropic (Claude) via OpenRouter … ✔ qwen2.5-coder:7b ◆ Ready — ollama/qwen2.5-coder:7b · graph 48213. Describe a task; empty line to exit. code › add a --timeout flag to the scan command and use it → search_code(query: --timeout flag scan command) scanCommand (function) src/commands/scan.ts:12 → read_file(path: src/commands/scan.ts) src/commands/scan.ts (40 lines): … → graph_impact(symbol: runScan) 3 symbol(s) depend on runScan: … I'll add the option and thread it through. → edit_file(path: src/commands/scan.ts, …) --- a/src/commands/scan.ts +++ b/src/commands/scan.ts @@ … + .option('--timeout ', 'abort the scan after milliseconds') ? Apply edit to src/commands/scan.ts? [Y/n] y ✔ edited src/commands/scan.ts → run_command(command: npm test -- scan) ? Run `npm test -- scan`? [y/N] y ✔ exit 0 … 12 passing → finish(summary: added a --timeout flag to scan and covered it with tests) ✔ added a --timeout flag to scan and covered it with tests +6 -1 across 1 file(s) · via ollama/qwen2.5-coder:7b ``` Run it non-interactively with `vg code "add a --timeout flag to scan" --provider ollama --model qwen2.5-coder:7b --auto`, or against a hosted model with `--provider openrouter --model anthropic/claude-3.5-sonnet` (set `OPENROUTER_API_KEY`). **In a session** you can type slash-commands: | Command | What it does | |---|---| | `/undo` | Revert the files changed by the last task | | `/diff` | Show the last change | | `/model` | Switch model without leaving the session | | `/cost` | Running token/$ cost for the session (local models are free) | | `/compact` | Condense the session so far into one checkpoint recap | | `/clear` | Explains that each task already starts fresh — nothing to clear | | `/help` | List the commands | | `/exit` | Quit (an empty line, `exit`, or `quit` also work) | **More session controls:** - `--stream` streams the model's output live as it's generated. - `--verify [command]` runs your tests after the agent finishes and, if they fail, feeds the failures back so it fixes them (uses the `testCommand` from config if you don't name one). - `--continue [id]` resumes a session — your most recent one, or the id you name — recapping what was already done for the model and restoring `/undo`. - `--reasoning-effort low|medium|high` tells a reasoning-capable model how hard to think (models without the knob ignore it). - `--worktree` runs the session in an **isolated git worktree** under `.vibgrate/worktrees`, so an agent can work without touching your checkout. Bare `--worktree` creates one; `--worktree ` reuses it. Inspect and land the result with `--worktree-diff ` (print the delta as a patch), `--worktree-apply ` (apply it onto the main tree via `git apply --3way`), and `--worktree-remove `. - `--security-tier ` sets shell isolation for `run_command`: `L0` runs on the host, `L1` uses Seatbelt or bubblewrap where available (`L2`/`L3` are reserved). - A live **token/$ meter** shows after each task and via `/cost` (cost is shown when the model's price is known; local models are free). - **External MCP tools:** list servers under `mcpServers` in `.vibgrate/code.json` and the agent can call their tools (namespaced `mcp____`); read-only tools run freely, anything else is approved like a built-in mutating tool. VG Code also **adopts the standard MCP config files** already in your repo — `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.vscode/mcp.json` (VS Code) — and merges them with your `.vibgrate/code.json` (which wins on any name clash), so servers you've already configured for another tool work here with no extra setup. Both local (`command`) and remote (`url`) servers are supported. **Tools the agent has.** Searching is the code graph (`search_code`) — not a grep. Read-only tools run without prompting; the rest are approved per step (or auto-approved under `--auto`). | Tool | What it does | Approval | |---|---|---| | `search_code` | Search the code graph: symbols and relations, plus a literal sweep for exact phrases and URLs | free | | `read_file` / `list_files` | Read a file (or a line range); list files known to the map | free | | `graph_impact` | Blast radius of changing a symbol — callers, importers, subtypes | free | | `library_docs` | Version-correct docs for a dependency this project actually installs | free | | `set_progress` | Maintain the task checklist shown during the run | free | | `inspect_task` / `inspect_change` / `verify_change` | Governance steps: assess the task, review a pending change, verify the result | free | | `edit_file` / `create_file` / `delete_file` | Change the working tree | **approved** | | `apply_patch` | Apply a validated PatchIR multi-op edit transactionally | **approved** per file | | `run_command` | Run tests, builds, or any other shell command | **approved** | | `read_notebook` / `edit_notebook_cell` | Read a Jupyter notebook; edit one cell | read free · edit **approved** | | `web_fetch` / `web_search` | Fetch a URL or search the web — results are size-capped, secret-redacted, and treated as untrusted | **approved** | | `browser_*` | Drive a browser: start, navigate, snapshot, click, type, stop | start/navigate **approved** | | `spawn_subagent` | Delegate a sub-task to a nested agent (optionally in a worktree) | **approved** | | `ask_user` | Ask you a question mid-task | free | | `finish` / `abort` | End the task with a summary, or give up | free | | `mcp____` | Tools from your configured MCP servers | free if read-only, else **approved** | **Safety.** The agent never sends a secrets file (`.env`, keys, credentials) to the model, and redacts stray credential shapes from any file it reads. Under `--auto`, a denylist blocks catastrophic commands (filesystem wipes, `curl … | sh`, force-push, …); interactively you see and approve each command yourself. **Configure once** in `.vibgrate/code.json` so you can then just run `vg code` (flags still override): ```json { "provider": "ollama", "model": "qwen2.5-coder:7b", "testCommand": "npm test", "auto": false, "denyCommands": ["deploy", "kubectl\\s+delete"], "maxSteps": 24, "mcpServers": { "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp"] } } } ``` | Key | Default | Notes | |---|---|---| | `provider` | auto | `vibgrate-relay`, `ollama`, `lmstudio`, `foundry-local`, `openrouter`, `litellm`, `openai`, `together`, `llama-cpp` | | `model` | — | Model id/slug (or set `VG_CODE_MODEL`) | | `auto` | `false` | Run autonomously (auto-approve) by default | | `testCommand` | — | The project's test command, surfaced to the agent and used by `--verify` | | `denyCommands` | — | Extra regex/substring rules blocked on top of the built-in denylist | | `contextWindow` | model default | Override the usable context window (tokens) for compaction sizing | | `maxSteps` | `24` | Default step cap for the agent loop | | `capsule` | — | Prefer a source-bearing Task Capsule for first context (`--capsule` / `--no-capsule`) | | `securityTier` | `L0` | Shell isolation: `L0` host, `L1` Seatbelt/bubblewrap where available (`L2`/`L3` reserved) | | `modelProfile` | derived | Overrides merged onto the model-derived profile: `mode`, `capsuleBudgetTokens`, `maxRepairRounds`, `constrainedDecoding`, `securityTier` | | `mcpServers` | — | External MCP servers (name → launch spec), merged with `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`; this file wins on a name clash | A missing or malformed file is simply "no config", never an error. **How an edit is built.** It assembles a small, high-signal context from the map (the relevant symbols, their relations, the blast radius of changing them, and any hard constraints), asks the model for a minimal edit, and applies that edit through a deterministic merge so the change lands exactly where it was meant to. Before an edit is written, its replacement body is scanned against the graph's identifier trie: an edit that references a symbol the graph does not know — and that is not already local to the target file — is **blocked, not merely flagged**. **The one-shot path.** `--single` skips the tool loop and proposes a single edit. That path is dry-run by default; `--apply` walks the full inspect → assess → dry-run → approve → execute → verify → log lifecycle, and still requires your explicit `--yes` (or an interactive confirmation) — there is no write-without-consent path. ```bash vg code "rename readCfg to readConfig everywhere it is called" --single --apply --yes ``` Pick a backend with `--provider` and `--model`. No model is bundled, and nothing is installed until you first use a backend that needs it: - **Local** — a Code Mode pack, `--provider ollama`, `--provider lmstudio`, `--provider foundry-local`, or `--provider llama-cpp --model-path ` (or `--local` to force on-device only). - **Vibgrate Relay** — the first-party hosted router that supplements your local models: `--provider vibgrate-relay`, authenticated with `VIBGRATE_RELAY_TOKEN` instead of a per-provider API key. Set `VIBGRATE_RELAY_URL` to point at staging or a self-hosted deployment. - **Other hosted** — any OpenAI-compatible endpoint: `--provider openrouter` / `litellm` / `openai` / `together`. API keys are read from the environment only (e.g. `OPENROUTER_API_KEY`), never passed as flags. With no `--provider`, `vg code` chooses from what you have already configured, best first: **Vibgrate Relay** when `VIBGRATE_RELAY_TOKEN` is set (with local fallback if Relay is unreachable), then another configured hosted key, then a locally-pulled model. It never dials a cloud endpoint you didn't set up. | Flag | Default | Description | |------|---------|-------------| | `` | — | What to change, in plain language. Omit it at a TTY for guided mode. | | `--provider ` | auto | `vibgrate-relay`, `ollama`, `lmstudio`, `foundry-local`, `openrouter`, `litellm`, `openai`, `together`, `llama-cpp` | | `--model ` | — | Model id (or set `VG_CODE_MODEL`) | | `--mode ` | auto-fit | Code Mode: `spark` \| `flow` \| `forge` — preferred over raw model names | | `--model-path ` | — | GGUF path for `--provider llama-cpp` (weights are never auto-downloaded) | | `-f, --file ` | — | Restrict the edit surface to this file (repeatable) | | `-b, --budget ` | `3000` | Approx context token budget | | `--auto` | — | Autonomous: auto-approve every edit and command (denylist still applies) | | `--max-steps ` | `24` | Cap the number of agent steps | | `--single` | — | One-shot planner (single edit, no tool loop) instead of the agent | | `--apply` | — | `--single`/`--mock` only: write the change (still requires `--yes` or a confirmation) | | `--yes` | — | Consent to write, or to a first-use package install, non-interactively | | `--stream` | — | Stream the model output live | | `--verify [command]` | — | After the agent finishes, run tests and feed failures back for repair (uses `testCommand` when no command is given) | | `--continue [id]` | — | Resume the most recent session, or the given session id (recap + restore `/undo`) | | `--reasoning-effort ` | model default | How hard a reasoning-capable model should think: `low` \| `medium` \| `high` | | `--worktree [id]` | — | Run the session in an isolated git worktree under `.vibgrate/worktrees` | | `--worktree-diff` / `--worktree-apply` / `--worktree-remove ` | — | One-shot worktree review flow: print the delta, apply it onto the main tree (`git apply --3way`), or remove the checkout | | `--capsule` / `--no-capsule` | config | Use (or disable) a source-bearing Task Capsule for first context | | `--security-tier ` | `L0` | Shell isolation: `L0` host, `L1` Seatbelt/bubblewrap where available | | `--restore-checkpoint ` | — | One-shot: restore the given `--file` paths from a checkpoint commit, then exit (for host UIs) | | `--stream-json` | — | Machine protocol: NDJSON agent events on stdout, approval decisions on stdin (host UIs such as the VS Code panel) | | `--session` | — | With `--stream-json`: stay open for further turns instead of exiting after one | | `--mock ` | — | Use a scripted reply instead of a model (offline; tests/CI/benchmarks) | | `-o, --out ` | — | Write the JSON result to a file (for CI/benchmarks) | | `--local` (global) | — | On-device model backends only — never a hosted model. Implies `--offline` | `--stream-json` is the protocol **Vibgrate for VS Code** speaks to the CLI: the panel renders the agent's steps, approves or denies each mutating tool call over stdin, and with `--session` keeps one warm graph across turns. It is a supported surface for any host UI, not just ours. Add `--json` for the full machine-readable result (proposed changes, diffs, and the verification summary), or `--out ` to write it for CI. Requires a map — run `vg` first if you have not built one. #### Tool results are compressed Inside the agent loop there is no wire to sit on, so `vg code` applies [context compression](#context-compression) itself: a bulky `run_command`, `search_code` or `web_fetch` result is compressed once on the way into the transcript instead of being re-billed on every later step. Reads an edit is computed from, and failed results, are never touched. The original stays in the local retrievable store and the model gets a `vg_retrieve` tool, so it can pull back the whole result or just the lines it needs (`grep`, `lines`, `head`, `tail`, capped by `VG_CODE_RETRIEVE_MAX_TOKENS`). Tokens are counted in the routed model's tokenizer; the run result reports what it saved (`compression` in `--json`) and `vg savings` counts it under the `vg-code` client. Set `VG_CODE_COMPRESS=0` to turn it off. --- ### vg embed Precompute the semantic index so the next `vg ask` is instant. ```bash vg embed ``` Local ONNX model downloaded once into a shared cache (`~/.cache/vibgrate/models`). Per-repo vectors stored in `.vibgrate/cache/`. | Flag | Description | |------|-------------| | `--where` | Show where the model is cached and its size | | `--clear` | Remove the downloaded model from shared cache | --- ### vg export Export the code map in various formats. ```bash vg export [file] ``` Format is inferred from the file extension. Use `-` for stdout. | Extension | Format | |-----------|--------| | `.json` | JSON | | `.ndjson` | Newline-delimited JSON | | `.graphml` | GraphML | | `.dot` | Graphviz DOT | | `.cypher` | Neo4j Cypher | | `.md` | Markdown | | `.html` | HTML visualization | | `.cdx.json` | CycloneDX SBOM / AI-BOM | | `.spdx.json` | SPDX | --- ### vg facts Deterministic open facts for a node (contract, invariant, characterization). ```bash vg facts ``` Epistemic-typed: declared/static → observed/derived. Open facts (contract / invariant / characterization) ship on every build. | Flag | Description | |------|-------------| | `` | Node to inspect | | `--pick ` | Pick the nth candidate when ambiguous | --- ### vg guide Cited, relevant standards and practices for a node — the free standards pack. ```bash vg guide ``` | Flag | Description | |------|-------------| | `` | Node to inspect | | `--pick ` | Pick the nth candidate when ambiguous | --- ### vg impact What breaks if you change it — deterministic structural blast radius. ```bash vg impact ``` Reverse reachability with decay confidence. With `--tests`, returns exactly the tests to run before shipping. | Flag | Default | Description | |------|---------|-------------| | `` | — | Node to assess | | `--depth ` | `4` | Max traversal depth | | `--tests` | — | Also surface the tests covering the affected set | | `--fail-on-untested` | — | Exit 2 if any affected node is untested (CI gate) | | `--pick ` | — | Pick the nth candidate when ambiguous | --- ### vg install Add Vibgrate AI Context to your AI assistant(s) — skill, MCP wiring, and advisory nudge. ```bash vg install [tools...] vg install --all vg install --detect vg install --list vg uninstall vg uninstall cursor --purge ``` Idempotent and repo-local (changes can be committed and shared with your team). **Supported assistant ids:** `claude`, `cursor`, `windsurf`, `vscode`, `codex`, `gemini`, `grok`, `opencode`, `kilo`, `aider`, `factory`, `trae`, `kiro`, `amp`, `kimi`, `codebuddy`, `copilot-cli`, `pi`, `devin`, `hermes`, `openclaw`, `agents` Run `vg install --list` for the live support matrix (ids can grow over time) — it shows, per assistant, whether the install writes an MCP registration, a skill, and a nudge. **Where the MCP server is registered.** Each host reads its own config file, so `vg install` writes the one that host actually loads: `.mcp.json` (Claude Code), `.cursor/mcp.json`, `.windsurf/mcp.json`, `.vscode/mcp.json`, and `.grok/config.toml` (a `[mcp_servers.vg]` table — what `grok mcp add --scope project` writes). Assistants without an MCP entry in the matrix get the skill and nudge, and their AI reaches the graph through the `vg` CLI instead. Existing entries, sections, and comments in these files are preserved — only the `vg` entry is written. | Flag | Description | |------|-------------| | `[tools...]` | Assistant ids to install for | | `--all` | Install for every supported assistant | | `--detect` | Detect assistants in use (repo footprint, home config, PATH) and install for those; with `--list`, only report what was detected | | `--list` | Show the support matrix and exit | | `--no-hook` | Skip the advisory nudge | | `--compress [url]` | Also route the assistant through the local compression listener and start it — see [vg install --compress](#vg-install---compress--vg-uninstall) | | `--compress-scope ` | Where `--compress` writes: `project` (default) or `user` | | `--login` | Copilot only: GitHub device-flow sign-in before the routing is written | | `--learn` / `--apply` | Turn past agent sessions into guardrails in the assistant's instructions file — see [vg install --learn](#vg-install---learn) | **`vg uninstall` flags:** | Flag | Description | |------|-------------| | `` | Assistant ids to remove (required) | | `--purge` | Also delete the skill file | | `--force` | Restore a routed config file even when another live session still holds it | `vg uninstall` only removes AI-assistant wiring. To remove the CLI package from the machine, use your package manager (`npm uninstall -g @vibgrate/cli`, etc.). --- ### vg lib Version-correct library docs — from the hosted catalog or local ingestion. ```bash vg lib # List the catalog vg lib # Show docs for a library (pinned to your lockfile version) vg lib add # Ingest docs from a local source vg lib publish # Upload private library docs to the hosted catalog vg lib resolve # Resolve name → catalog id + version vg lib refresh # Re-ingest all local sources ``` | Flag | Default | Description | |------|---------|-------------| | `--name ` | — | Library name (for `add`) | | `--version ` | — | Pin the doc version (for `add`/`publish`) | | `-b, --budget ` | — | Trim docs to ~N tokens | | `--readme ` | `./README.md` | README path (for `publish`) | | `--dts ` | — | TypeScript declaration path (for `publish`) | | `--language ` | — | Primary language (for `publish`) | | `--region ` | `us` | Data-residency region for the hosted catalog | | `--ingest ` | — | Hosted catalog URL override (wins over `--region`) | --- ### vg locale **Vibgrate Localize** — managed localisation for your application. Locale projects, keys, and translations live in Vibgrate Cloud and sync with the locale files in your repo. `vg localize` is a backward-friendly alias; `vg locale` is the canonical form. ```bash vg locale config --project [--target-languages fr-FR,de-DE] [--namespaces common,checkout] vg locale push [--dry-run] [--source-only] vg locale pull [--language ] [--dry-run] [--include-machine] vg locale save-missing [--dry-run] vg locale add [value] [--namespace ] [--language ] vg locale remove [--namespace ] vg locale get [--namespace ] [--language ] vg locale status vg locale format [--dry-run] ``` | Command | Description | |---------|-------------| | `vg locale config` | Create or update `vibgrate.locale.yaml` (no credentials are stored in it) | | `vg locale push` | Upload local locale files to Vibgrate Cloud (`vg locale sync` is an alias) | | `vg locale pull` | Write translations from Vibgrate Cloud into local files (`vg locale download` is an alias) | | `vg locale save-missing` | Create only the keys Cloud does not have yet — never overwrites a translation | | `vg locale add` | Add or update a single key | | `vg locale remove` | Remove one or more keys | | `vg locale get` | Show one key and its translations | | `vg locale status` | Coverage and health per language | | `vg locale format` | Re-format local locale files in place | **Local vs Cloud.** `config` and `format` only touch the filesystem — they work on any plan and make no network call. Everything else reads or writes Vibgrate Cloud and needs a **Team plan or above**. `vg locale push --dry-run` also works with no credentials, so you can preview what a push would send before you have logged in. **Configuration** lives in `vibgrate.locale.yaml`, which is safe to commit — the DSN is never written to it and resolves at call time from `--dsn`, `VIBGRATE_DSN`, or your stored login, exactly as `vg push` does. ```yaml project: acme-web sourceLanguage: en-US targetLanguages: [fr-FR, de-DE] format: nested path: src/locales/{language}/{namespace}.json namespaces: [common, checkout] version: latest ``` **Formats.** `json`, `nested`, `flat`, `yaml`, and `yml` are supported today; the professional formats (`po`, `xliff`, `xliff2`, `android`, `strings`, `resx`, `csv`, `arb`, `properties`) report that they are not available yet rather than failing as unknown. Every codec round-trips, and writes are key-sorted so pulling twice never produces a spurious diff. **Machine translation is never silently accepted.** `pull` writes only translations a person has accepted; pass `--include-machine` to also write machine and needs-review entries. --- ### vg map / vg hubs / vg areas / vg oddities Map-level insights — read-only views over the committed graph. ```bash vg map # Overview: areas, hubs, untested hotspots vg hubs # Most-depended-on code (centrality outliers) vg areas # Natural groupings (communities), each labelled and sized vg oddities # Surprising cross-area links (architectural smells) ``` | Command | Flag | Default | Description | |---------|------|---------|-------------| | `vg hubs` | `-n, --limit ` | `20` | How many hubs to show | | `vg areas` | `-n, --limit ` | `30` | How many areas to show | | `vg oddities` | `-n, --limit ` | `20` | How many oddities to show | --- ### vg llm-host Thin **enterprise inference process** (ADR-005). Code Modes and install stay on `vg models`; this host only loads weights and decodes over a local socket when isolation is `process`. ```bash vg llm-host status vg llm-host serve # listen on the default runtime socket vg llm-host serve --socket /tmp/vg-llm.sock --yes ``` Default isolation is **embedded** (same process as the agent). Set `VIBGRATE_INFERENCE_ISOLATION=process` to use the host. Management never moves into the host process. **First-party weights.** Code Mode packs are first-party GGUF only: `vg models install` downloads the catalogued refs into the Vibgrate weight store under the cache directory (HTTPS hosts allowlisted, including Hugging Face LFS/Xet CDNs). Catalog entries carry **sha256 pins**; a download that does not match is rejected. Ollama is used only for models you select explicitly (`vg models pull `, `--provider ollama`) — never for a Code Mode pack. **Foundry Local.** On Windows (or any host running Microsoft Foundry Local), use `--provider foundry-local --model ` with the OpenAI-compatible server (default `http://127.0.0.1:5272/v1`, override with `FOUNDRY_LOCAL_BASE_URL`). `vg models status` lists models when the server responds. **Warm local inference (Approach B default).** Code Mode packs (channel 2026.08.1) install first-party GGUF weights and run on embedded llama.cpp only — there is no Ollama fallback in a pack. When a GGUF is on disk (weight store or `~/models`), `vg code` prefers embedded llama.cpp automatically. Set `VG_PREFER_OLLAMA=1` to try your own Ollama models first outside Code Modes. Spark constrained decoding is **fail-closed**: if the binding cannot attach a PatchIR grammar, generation errors instead of free-text (opt-in raw string GBNF only via `VG_ALLOW_GRAMMAR_STRING_FALLBACK=1`). **Identifier enforce (before apply).** Edits and `apply_patch` that invent identifiers not present in the code graph are **blocked** (not merely annotated). Identifiers already in the target file (locals, params, existing helpers) and tokens only in comments/strings are allowed. After an approved edit, the session trie updates so new symbols become legal. **Native logit mask (P1).** When node-llama-cpp exposes `TokenBias`, the warm host **boosts** tokens for graph identifiers and can **suppress** vocabulary tokens that are complete identifiers absent from the graph. Bias is cached per session/trie. Without `TokenBias`, generation still runs (post-scan annotation + enforce-before-apply remain). **Dynamic open-identifier sampler (P2).** When enabled (`VG_LLM_ON_TOKEN=1` or `VG_LLM_CUSTOM_SAMPLER=1`, or a binding that declares the hook), the host tracks whether generation is inside a code identifier and rejects tokens that invent graph-unknown names mid-decode. Composed with grammar + TokenBias on the same `prompt()` call. **Warm KV prefix reuse (P1/P2).** Prompt segments are content-hashed; after the first turn, stable system/capsule blocks are warm. A multi-turn **cursor** skips already-evaluated leading blocks and only re-evaluates the delta when the binding supports evaluate-without-generate, so multi-turn TTFT does not re-prefill the full capsule every step. **Speculative drafts (P2).** Graph-verbatim draft candidates are **ranked** against the user ask; the host tries accept in score order (evaluate-without-generate when available). **Shared host in vgd.** The daemon protocol includes `host-status`, `host-load`, `host-unload`, and `host-generate` so a long-lived process can keep a warm model for CLI and IDE clients (same session pool as embedded). Clients may pass a **client id** on load so unload is **refcounted** — CLI and VS Code can share one warm model without the first exit killing the session. **Hardware default mode.** `vg models mode --apply-recommend` pins the Code Mode recommended from free RAM/VRAM and repo size when no default is set. `vg doctor` surfaces the same recommendation under `localInference`. **Coding metrics (release).** `vg models coding-metrics` builds a `coding-metrics/0` report: host-bench arms + gates + offline Fusion FCS/ZNS trajectory pack. Default host mode is **simulate** (no GPU). CI runs simulate+gate on PRs; CLI publish opens a website data PR under `data/benchmarks-coding/` (human review before public claims), same pattern as CLI release benchmarks. **Host bench (P3).** `vg models host-bench` runs Approach B measurement arms (Ollama baseline, embedded warm, grammar, identifier enforce, TokenBias, dynamic sampler, KV delta). Default **simulate** exercises the warm host without a GPU; `--mock` is registry shells only; `--live --model-path ` is operator hardware. Add `--gate` to evaluate release gates (exit code **2** on hard failure). Use `--json` for CI artifacts. --- ### vg models **Code Modes** (Spark / Flow / Forge) for VG Code, plus the **local model fleet** (Ollama, LM Studio, and on-disk `gguf` files). The default view is outcome-oriented: which mode fits this machine and repo, which pack backs it, and whether it is ready. **Named install commands run by default** (same polarity as the rest of `vg`: the command does what it says). Pass `--dry-run` to print the plan only. Status/resolve never download. Destructive `rm` confirms on a TTY; non-interactive remove needs `--yes`. `install` / `pull` install the **full provider dependency closure** (e.g. runtime npm deps for llama-cpp, then the pinned GGUF from the first-party weight store; `pull` fetches your named model via Ollama). Third-party apps such as Ollama itself are never auto-installed — install them separately if the plan lists them as blocked. ```bash vg models # Code Modes status + fleet summary vg models --raw # local models only vg models status vg models mode [spark|flow|forge] vg models resolve [mode] # pack + model + fit (no download) vg models install [mode] # install the pack (add --dry-run to preview) vg models pin vg models unpin vg models packs vg models pull # download (add --dry-run to preview) vg models uninstall # uninstall (TTY confirm; --yes for CI; --dry-run to preview) vg models host-bench # Approach B measurement arms (default: simulate, no GPU) vg models coding-metrics # host-bench + Fusion FCS/ZNS report (coding-metrics/0) vg models catalog ``` | Mode | Intent | |------|--------| | **Spark** | Fast, small footprint — quick edits and tight memory | | **Flow** | Balanced default for day-to-day coding | | **Forge** | Heavier pack when you have headroom and want more capacity | | Subcommand / flag | Description | |-------------------|-------------| | `--raw` | Skip Code Modes; list discovered local models only | | `mode [spark\|flow\|forge]` | Show or set the default Code Mode (`--auto` clears a fixed default) | | `resolve [mode]` | Resolve pack + underlying model + fit without downloading | | `install [mode]` | Resolve and install the pack (`--dry-run` for plan only) | | `pin ` / `unpin ` | Pin or clear a reproducible pack (e.g. `flow@2026.07.1`) | | `packs` | List qualified Code Mode packs | | `pull ` | Download via local runtime (default Ollama; `--dry-run` for plan only) | | `uninstall ` | Uninstall a local model — Ollama, LM Studio, or GGUF (`--dry-run` plan; TTY confirm or `--yes`; `--runtime` optional auto-detect) | | `host-bench` | Approach B measurement arms (`--simulate` default, `--mock`, `--live --model-path`, `--gate`) | | `coding-metrics` | Unified host-bench + Fusion FCS/ZNS report (`--out`, `--gate`, `--version`); publish path for release | | `catalog` | Live hosted model catalog (cached; not used under `--offline`) | | `--json` | Machine-readable JSON on stdout | ```bash vg models pull qwen2.5-coder:7b vg models pull qwen2.5-coder:7b --dry-run # plan only ``` | Flag | Default | Description | |------|---------|-------------| | `` | — | Model to pull or uninstall, e.g. `qwen2.5-coder:7b` or a `.gguf` basename | | `--runtime ` | pull: `ollama` · uninstall: auto | Runtime (`ollama`, `lm-studio`, `gguf`). Uninstall auto-detects from the installed fleet when omitted. | | `--dry-run` | — | Print the plan only; do not download or uninstall | | `--yes` | — | Skip interactive confirms (required for non-interactive `uninstall`) | --- ### vg module Manage the **optional local modules** — separately-licensed engines the CLI can load through a narrow seam. Two are supported today: `relevance` (semantic ranking kernel) and `hcs` (the [HCS](#holistic-code-specification-vg-hcs) engine behind `vg hcs`). ```bash vg module status # what is installed, and whether it loads vg module install hcs # fetch + unpack (prompts once, unless --yes) vg module install relevance --force vg module remove hcs ``` Installation fetches the module from the npm registry **as a plain tarball**, verifies its integrity, and unpacks it into the Vibgrate cache. Your project is never touched, and nothing from the tarball executes at install time. State is per-user, not per-repo, and your answer to the prompt is recorded — a decline is remembered, so nothing re-prompts on every run. | Subcommand | Description | |------------|-------------| | `status` | Installed modules, their versions, whether the seam can load them, and your recorded consent | | `install ` | Install a module (`--yes` to skip the prompt, `--force` to reinstall) | | `remove ` | Delete an installed module | Set `VIBGRATE_NO_KERNEL=1` to disable optional modules entirely — installs are refused and commands that need an engine exit with `6` ([`ENGINE_UNAVAILABLE`](#exit-codes)) rather than silently degrading. `vg module status` reports the disabled state. Add `--json` to any subcommand for machine-readable output. --- ### vg path Show how A connects to B — shortest path in the call graph. ```bash vg path ``` | Flag | Description | |------|-------------| | `` | Source node | | `` | Target node | | `--pick-a ` | Pick the nth candidate for A | | `--pick-b ` | Pick the nth candidate for B | --- ### vg savings A local, privacy-safe report of the tokens and dollars saved by querying the map instead of grepping and reading whole files. ```bash vg savings ``` Reads the counts-only usage ledger recorded when you run `vg serve --savings` (or pass `--client` on CLI navigation calls). Nothing leaves your machine — the figures are estimates. | Flag | Default | Description | |------|---------|-------------| | `--days ` | `30` | Reporting window in days | | `--clear` | — | Delete the recorded usage data for this repo (the ledger under `.vibgrate/cache/`, plus the opt-in stats-share upload state and per-install id) | | `--reset` | — | Delete the context-compression ledger (global, under the Vibgrate data directory) | Add `--json` for machine-readable output. **Context compression.** When `vg serve --compress`, `vg code` or the SDK wrappers have saved anything, the report gains a second section: requests, tokens before / after and estimated dollars for today, the last 7 days and the last 30 days, split by model, client and project, plus the size of the retrievable store. The ledger is an append-only JSONL file with numbers only — never message content — kept for 30 days. See [Context compression](#context-compression). --- ### vg serve Start [Vibgrate AI Context](https://vibgrate.com/library) — a local-first [MCP](https://vibgrate.com/glossary/model-context-protocol) serving your code map, drift, and version-correct docs to your AI assistant (fully offline under `--offline`). ```bash vg serve ``` | Flag | Default | Description | |------|---------|-------------| | `--http` | — | Serve over streamable HTTP instead of stdio | | `--port ` | `7437` | Port for `--http` | | `--host ` | `127.0.0.1` | Host for `--http` | | `--savings` | — | Record local, counts-only usage savings (opt-in) | | `--share-stats` | — | Also upload the counts-only usage ledger to Vibgrate to improve the local MCP (opt-in; off by default; implies `--savings`; disabled under `--offline`) | | `--dedup` | — | Collapse a node's heavy relation lists on repeat reads within a session, to save tokens (opt-in) | | `--no-refresh` | — | Serve the map as built; skip the auto-rebuild when files change | | `--compress`, `--compress-only`, `--background`, `--compress-port`, `--compress-mode`, `--profile`, `[agent…]` | — | Context compression in the same process — see [vg serve --compress](#vg-serve---compress) | Via stdio (default), your AI assistant spawns the server. Via `--http`, it runs as a local HTTP endpoint for browser or shared access. **A live status display shows what the server is doing for you.** While `vg serve` runs in a terminal, a status block on stderr updates in place: uptime, which AI clients are connected (detected from the MCP handshake), calls and average response time per tool, and — for the navigation tools with a grep/read baseline — the context tokens served vs the estimated tokens a grep-and-read agent would have burned instead, with the estimated saving labelled as such. Outside a terminal (when your assistant spawns the server) it degrades to a quiet one-line heartbeat in the server logs every 15 minutes, and only when there has been activity. The display is in-memory only and always on — nothing is written to disk or uploaded (recording and sharing below stay opt-in) — and `--quiet` turns it off. **Usage stats — local by default, sharing is opt-in.** `--savings` records a *counts-only* ledger under `.vibgrate/` — per navigation call: which tool, how it resolved (complete/partial/miss), the vg-vs-grep token figures, whether it came over the MCP (`mcp`) or the `vg` CLI (`cli`), and a coarse client label (which AI). `vg savings` reports it locally; nothing leaves your machine. `--share-stats` additionally uploads that same counts-only ledger to Vibgrate periodically, so we can see how the local MCP is used and improve it. It **never** sends code, file paths, question text, repo identity, or any credential — only counts, outcomes, token figures, the vg version, your OS/arch, and a random per-install id. It's off unless you pass the flag, is disabled entirely under `--offline`, and the endpoint can be overridden with `VIBGRATE_STATS_ENDPOINT`. **Attributing CLI calls.** The MCP path detects the calling client automatically from the connection handshake. For CLI calls, pass `--client=` (e.g. `vg "how does auth work" --client=claude`) so the call is attributed in `vg savings` and any shared stats — this is what `vg install` writes into each assistant's skill. Without `--client`, a bare `vg ask` records nothing. **The map stays fresh while you (or your AI) edit code.** Each tool call runs a cheap stat-only freshness check against the last build; when files really changed, the server rebuilds the map incrementally in-process — only changed files re-parse — and answers from the updated graph. Probes are debounced with a self-tuning cadence (2s floor, scaling with measured probe cost so probing never exceeds a few percent of serve time even on very large repos), rebuilds are single-flight and cross-process locked, and touch-only changes (a `git checkout`, a re-save with identical content) are recognized by content hash and never trigger a rebuild. There is no filesystem watcher: freshness is checked exactly when it matters — at query time. (`vg daemon` is a separate optional process for multi-workspace IDE/agent sessions; `vg serve` does not require it.) The server also hot-reloads `graph.json` whenever it changes on disk, so an external `vg` build is picked up on the next call too. The server exposes read-only tools your assistant can call over the code map and dependency data, including: - `query_graph`, `get_node`, `find_path`, `impact_of`, `tests_for` — navigate and reason about the code map. - `check_drift` — offline dependency inventory; pass `attribute: true` to add git "who added this / who set the version" attribution. - `list_vulnerabilities`, `vuln_attribution` — known vulnerabilities and their exposure attribution from the last `vg scan --vulns`. - `upgrade_impact` — what an upgrade will cost: version distance, how many files import the package, the vulnerabilities it fixes, and — with `changelog: true` — online breaking-change notes between your version and the latest. - `resolve_library`, `library_docs` — version-correct, drift-annotated library docs. All tools are read-only. The server is local-first: it always answers from your machine when it can, and its only network touches are the embedder's one-time model fetch, `upgrade_impact`'s `changelog`, and `library_docs`' fall-through to the hosted catalog when the local docs for a library are thin or missing. `--offline` is the hard airgap — it disables all three. --- ### vg share Make the code map committable and auto-updating for your team. ```bash vg share ``` Installs a pre-commit hook, deterministic merge driver, and `.gitignore` so the map stays fresh without any manual steps. This rewrites the default `.vibgrate/.gitignore` (which ignores the graph artifacts, `graph.json` included) so `graph.json` is committed while the cache and volatile reports stay ignored. | Flag | Description | |------|-------------| | `--undo` | Reverse what `vg share` installed | | `--reports` | Also commit `graph.html` / `GRAPH_REPORT.md` (default: gitignored) | --- ### vg show Explain a single node: what it is, what it calls, what calls it. ```bash vg show ``` | Flag | Description | |------|-------------| | `` | Qualified name, short name, `file:line`, glob, or id | | `--pick ` | Pick the nth candidate when ambiguous | Outputs the qualified name, kind, file location, signature, importance score, area, extends relationships, callees, and callers. For functions and methods it also prints the architecture classification the Architecture module wrote at build time — role, purposes, a one-line description, and any boundary violation — when that module is loaded (`vg module install arch`; installed by default). #### vg show arch Open a local, interactive architecture map of the same graph in your browser. ```bash vg show arch # vg · arch http://127.0.0.1:7420 vg show arch --focus UserService # open on one symbol vg show arch --no-open --json # print the URL and counts; keep serving ``` | Flag | Default | Description | |------|---------|-------------| | `--port ` | `7420` | Port | | `--host ` | `127.0.0.1` | Bind address (loopback by default; binding elsewhere prints a warning) | | `--focus ` | — | Open the map on this symbol | | `--no-open` | — | Print the URL without opening a browser | The map opens on the **workspace** (one card per package), then drills into a **column slice** (UI / endpoint → application → store). Same-file functions collapse; tests stay hidden; at most 120 cards. Filters (**by job**, **by cluster**, **who calls whom**, **missing steps**, **problems**) apply inside that zoom. Overlay toggles (vulns, drift, ownership, churn) paint on that same canvas — the Health tab opens them; it is not a blank page. Missing scan, CODEOWNERS, or git history is omitted, never a healthy zero, and the map does not invent an Architecture Health Score. With the Architecture module off it is the raw graph’s kinds in the same columns — never a guess. The page is served inline from loopback with no external assets, and `q` / Ctrl-C stops it. `vg show chart` is the pre-rename spelling and still works as a silent alias for one release. The VS Code architecture board hosts the same page and payload. See [docs/show-arch.md](./docs/show-arch.md). #### vg show savings Print (and with `--open`, open) the URL of the local savings page served by `vg serve --compress` — see [vg savings / vg show savings](#vg-savings--vg-show-savings). --- ### vg status Graph freshness, counts, and staleness — compared against the working tree. ```bash vg status ``` Outputs: map path, generation timestamp, node/edge/area counts, languages, cluster method, resolver rungs used, cache status, and stale file count. When a build has run on this machine, staleness is exact (per-file stat + content hash against the last build's snapshot — edits, adds, and removes); otherwise it falls back to comparing the file set. --- ### vg tests Which tests cover a node (call/coverage linkage). ```bash vg tests ``` `--missing` flips to show untested nodes nearby. `--run` prints (or `--exec` runs) the minimal command to exercise exactly those tests. | Flag | Description | |------|-------------| | `` | Node to inspect | | `--missing` | Show untested nodes nearby instead | | `--run` | Print the command to run exactly these tests | | `--exec` | Run that command | | `--pick ` | Pick the nth candidate when ambiguous | --- ### vg tree The call tree rooted at a node. ```bash vg tree ``` Callees by default; `--callers` to invert. Depth-bounded and cycle-safe. | Flag | Default | Description | |------|---------|-------------| | `` | — | Root node | | `--callers` | — | Show callers instead of callees | | `--depth ` | `3` | Max depth | | `--pick ` | — | Pick the nth candidate when ambiguous | --- ### vg unknowns What the graph cannot resolve, ranked by blast radius — the unresolved references most worth teaching the map about. ```bash vg unknowns ``` Surfaces the symbols and imports the resolver could not tie to a definition, ordered by how much depends on them, so you can see where a SCIP index or a targeted `--only` language pass would most improve resolution. | Flag | Default | Description | |------|---------|-------------| | `-n, --limit ` | `20` | How many to show | Add `--json` for machine-readable output. --- ## Context compression Every turn, an AI coding agent re-sends its whole conversation to the model — the test log it already read, the JSON payload it already parsed, the grep output it already acted on. Vibgrate CLI compresses that context **before** it reaches the model and keeps every original retrievable on your machine, so the model can pull back exactly the lines it needs instead of re-paying for all of them. Compression is not a second product with its own commands. It is a mode of the local runtime you already start (`vg serve`), a flag on the installer you already use (`vg install`), and a section of the report you already read (`vg savings`). Inside `vg code` it is on by default. ```bash vg serve --compress # serve the map *and* compress context, one process vg install claude --compress # point Claude Code at it and start the listener (undo: `vg uninstall claude`) vg savings # what it saved: today / 7 days / 30 days ``` **How a request is compressed.** The pipeline walks each message and each content block and decides per block: 1. **Exclusions first.** System prompts, user text, assistant text, blocks with `cache_control`, blocks already carrying a marker, retrieval results, error outputs, file reads (`Read`, `cat`, `head`, …) and edits are left alone by default. In `cache` mode only the newest turn (the part after the last assistant message) is eligible, so your provider's prompt cache keeps hitting; `token` mode makes every eligible block a candidate. The last `protectRecent` messages keep their code intact. 2. **Route by shape.** A detector classifies the block — `json`, `source_code`, `search_results`, `build_output`, `git_diff`, `html`, `tabular`, `structured_config`, `plain_text` — with a confidence floor per type, and hands it to the matching compressor. 3. **Lossless first.** Repeated lines, grep and directory headings, path headings, diff index lines and config boilerplate fold into byte-reversible markers that `unfold` restores exactly. Lossy compression runs on top only when it beats the fold by at least `VG_COMPRESS_LOSSY_MIN_EXTRA_SAVINGS` (default 15%). 4. **Lossy, shape-aware.** JSON arrays keep a head / middle / tail sample plus every error-looking item, rare-status outliers and items matching your question, and render the dropped rows as a compact schema line. Logs collapse near-duplicates and keep errors, stack traces, first and last lines. Grep output is capped per file with matches for your question kept. Diffs keep every changed line and cap context. Source code keeps signatures, imports, exports and docstrings and collapses long bodies (verified to still parse with the bundled grammars). Prose is compressed extractively — a deterministic sentence score, no model. 5. **Guarded.** Anchors (errors, ids, hashes, URLs, test names) may never be dropped; a result that is not smaller, or that fails the recoverability check, is rejected and the original forwarded. A compressor that throws is a passthrough. A per-request deadline (`VG_COMPRESS_DEADLINE_MS`) forwards the original if compression runs long. 6. **Retrievable.** Each lossy block stores its original (secrets redacted) in a local store with a 30-minute TTL and adds a marker — `<>` inside arrays, or a trailing `Retrieve original: hash=… (before → after tokens)` line. When a request carries markers, the proxy adds a `vg_retrieve` tool; if the model calls it, the proxy answers from the store without a client round-trip (up to 3 rounds per request) and, for streaming clients, keeps the connection alive with heartbeats while it does. Across turns the pipeline also replaces verbatim repeats of earlier tool output with a pointer (cross-turn dedup), marks file reads that a later edit made stale (read lifecycle), holds fresh reads byte-exact for a few turns before they become eligible (maturation), and — opt-in — compacts prior-turn reasoning on models that bill it. **Savings profiles.** `coding` (default) is cache-mode with reads and edits byte-exact; `balanced` lets older reads compress; `aggressive` is token-mode over everything eligible, for long autonomous runs; `general` is for non-coding chat. Pick one with `--profile` or `VG_COMPRESS_PROFILE`. **Privacy.** Everything runs on your machine. The proxy binds to loopback unless you pass a token, forwards your provider credentials untouched, strips its own headers before forwarding, never logs message bodies unless you ask (`VG_PROXY_LOG_MESSAGES`), and redacts secret shapes before anything — stored originals, ledgers, logs, memory — is written to disk. Files are created `0600`. There is no beacon and no update check unless you opt in, and `DO_NOT_TRACK` / `VIBGRATE_TELEMETRY=0` win over any opt-in. --- ### vg serve --compress `vg serve` is the local runtime: it serves your code map to an AI over MCP. `--compress` adds a second listener to the *same process* — an Anthropic- and OpenAI-compatible endpoint that shrinks context on its way to the model. One runtime, two listeners; there is no separate server to start, supervise or stop. It speaks the Anthropic Messages API (`/v1/messages`), OpenAI Chat Completions (`/v1/chat/completions`) and Responses (`/v1/responses`), streaming and non-streaming, and passes `count_tokens`, `embeddings` and `models` through untouched. ```bash vg serve --compress # MCP on stdio + compression on 127.0.0.1:8787 vg serve --http --compress # MCP over HTTP as well vg serve --compress --profile aggressive # compress harder vg serve --compress-only # compression only, no code map needed vg serve --compress --background # start (or reuse) the listener as a background process, then return vg serve --compress claude --model claude-sonnet-5 # one session, environment only ``` | Flag | Default | Description | |------|---------|-------------| | `--compress` | off | Turn on context compression: the listener, plus the compression MCP tools | | `--compress-port ` | `8787` | Port for the compression listener (independent of `--port`, which is MCP's) | | `--compress-only` | off | Compression **without** a code map: none is built, none is required, and only the tools that answer without one are listed | | `--compress-mode ` | `cache` | `cache` (newest turn only, prompt-cache safe) or `token` (maximum removal) | | `--profile

` | `coding` | `coding` / `balanced` / `aggressive` / `general` | | `--background` | off | Start the compression listener as a background process — or reuse the healthy one already on the port — and return. No code map, no MCP; stop it with `vg serve stop`. This is what `vg install --compress` runs for you | | `[-- …]` | — | Run one agent session through the listener (started for you if it is not running), then restore the environment | Everything else is a setting rather than a flag. There are around 130 `VG_*` knobs — upstream URLs, spend caps, rate limits, tokens for off-loopback binding, output shaping, logging — and putting each on the command line would turn `--help` into a manual. Read them with `vg serve config` and change one with `vg serve config set KEY VALUE`; hot knobs take effect on the next request. **Why `--compress` gates the MCP tools too.** Every tool schema an MCP server advertises is re-sent on every agent step, so a capability listed "just in case" is a standing tax on people who never use it. `compress_content`, `retrieve_original` and `compression_stats` are therefore listed only when you asked for compression. They remain callable either way. **Sharing a listener.** If a healthy listener is already on the port, a second `vg serve --compress` attaches to it instead of failing — several assistants each spawning their own `vg serve` is the normal case, not an error. Subcommands: `vg serve status [--compress-port ] [--json]` (what is listening, which agents are routed, Copilot sign-in state), `vg serve stop [--compress-port ]`, `vg serve config [--json]` with `set ` / `unset `. Local endpoints: `/` (the savings page, see `vg show savings`), `/health`, `/ready`, `/version`, `/api/stats`, `/api/savings`, `/api/proxy/clients`, `/metrics` (Prometheus text) — these answer on the listener's own bind and, when a token is set, require it off-loopback. The admin endpoints `/api/settings` (GET / POST), `/api/ccr/`, `/api/proxy/shutdown`, `/api/cache/clear` and `/api/stats/reset` answer loopback callers only and return `404` to anyone else. Sidecar endpoints for your own code: `POST /v1/compress` (a `{ messages, … }` request body in → the same body compressed, plus accounting) and `POST /v1/retrieve`. Gemini-native calls (`/v1beta/models/…:generateContent`, `:streamGenerateContent?alt=sse`) pass through with their query string intact — forwarded, not yet compressed. Every response carries `x-vg-tokens-before`, `x-vg-tokens-after`, `x-vg-tokens-saved`, `x-vg-usd-saved` and `x-vg-transforms` headers so a client can see what happened to its request. --- ### vg install --compress / vg uninstall `vg install` is the one verb that writes an AI assistant's configuration, so routing an assistant through compression is a flag on it — not a separate verb that edits the same files a second way. `vg uninstall` is the one revert. ```bash vg install claude --compress # write Claude Code's base URL, repo-local, and start the listener vg install codex --compress --compress-scope user # write the home config instead vg install copilot-cli --compress --login # device-flow sign-in, then route vg serve status # what is routed right now, and by whom vg uninstall cursor # put its config back, byte-for-byte ``` One command is a working setup. After writing the routing, `--compress` makes sure the listener it points at is running: it starts `vg serve --compress --background` (or reuses the healthy listener already on the port) and prints the URL. For Claude Code it also adds a `SessionStart` hook to the same `.claude/settings.json` that runs `vg serve --compress --background --quiet`, so the routing keeps working after a reboot — each new session restarts the listener if it is gone. The hook needs `vg` on your `PATH` (`npm i -g @vibgrate/cli`); with `npx` it is skipped and said so. `vg uninstall claude` removes the hook with the routing. Pointing at a listener vg does not manage — an explicit `--compress ` or `VG_PROXY_URL` — starts nothing. **Supported agents:** `claude`, `codex`, `cursor`, `aider`, `copilot-cli`, `opencode`, `cline`, `continue`, `goose`, `openhands`, `gemini`, `qwen`, `kimi`, `grok`, `crush`, `amp`, `factory` (Droid), `kiro`, `vibe`, `zcode`, `vscode` (Claude Code in VS Code). Agents that read a config file (Codex `config.toml`, Claude Code / Droid `settings.json`, OpenCode and Crush JSON, Continue and Goose YAML) get an atomic edit with a `.vg-backup` beside it, a marker recording exactly which fields changed, and an owner file so two concurrent sessions never undo each other. Agents that only read an environment variable (Cursor, Aider, Cline, OpenHands, Kimi, Grok, Qwen Code, Gemini CLI, Amp, Kiro, Mistral Vibe, ZCode) have no file to write durably — run them with `vg serve --compress `; the install reports that rather than silently skipping. | Flag | Description | |------|-------------| | `--compress [url]` | Route this assistant; the URL defaults to `VG_PROXY_URL`, else the host/port knobs | | `--compress-scope ` | `project` (repo-local, the team-shareable default) or `user` (home config) | | `--login` | Copilot only: GitHub device-flow sign-in before writing. The token is stored `0600` at `VG_COPILOT_AUTH_FILE` and never printed | | `--force` (on `uninstall`) | Restore a file another live session still holds | **One session instead of durable config.** `vg serve --compress ` runs a single agent through the listener using the environment only: nothing is written, and everything is restored when the child exits. The child inherits your terminal, gets `VG_WRAP_ACTIVE=1`, receives forwarded `SIGTERM` / `SIGHUP`, and its exit code becomes vg's. --- ### vg savings / vg show savings `vg savings` reports tokens and dollars saved — a compression section alongside the grep-baseline numbers for map queries. `vg show savings` opens the same numbers as a live local page, next to `vg show arch` for the code graph. ```bash vg savings # today / 7 days / 30 days, by model, client, project vg savings --compression # just the compression section vg savings --benchmark # measure the compressors on built-in fixtures vg show savings --open # the live page in your browser ``` `--benchmark` runs the pipeline over one fixture per content type and reports p50 / p95 latency and the kept ratio. Nothing is sent anywhere; this is how to check that a change to the compressors did not regress before you rely on it. `--iterations `, `--fixture ` and `--model ` tune the run. The page is served inline by the compression listener — no external assets — and shows savings for today / 7 days / 30 days, the split by model, client and project, recent requests with their transforms, the output-verbosity estimate with its confidence interval, the retrievable store, and an editor for the hot-reloadable settings. --- ### vg install --learn Turn your past agent sessions into guardrails the assistant reads next time. The outcome is "agent instructions written", which is `vg install`'s job, so it is a mode of the installer rather than a verb of its own. ```bash vg install claude --learn # preview: the block it would write, as a diff vg install claude --learn --apply # write it vg install codex --learn --since 14d --learn-target AGENTS.md --apply ``` It scans the session logs of Claude Code, Codex, Gemini CLI, Grok, OpenCode, Cursor, Copilot and Aider for this project — narrowed to the assistants you named — detects loops (the same failing command run again and again, edit / undo cycles, retry storms), repeated errors and missing-context patterns, and renders a short block between `` and `` markers. Re-running replaces the block and leaves the rest of the file untouched. The analyzer is deterministic and needs no model; set `VG_LEARN_CLI` to hand the digest to a local CLI of your choice instead. | Flag | Default | Description | |------|---------|-------------| | `--learn` | — | Preview the guardrails; nothing is written | | `--apply` | — | Write the block (and save the learned output-verbosity profile) | | `--since ` | `7d` | How far back to scan | | `--min-evidence ` | `2` | Occurrences before a non-loop pattern becomes a rule | | `--learn-target ` | the assistant's own | `CLAUDE.local.md`, `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `GROK.md`, or any path | | `--all-projects` | — | Scan sessions from every project, not just this repo | --- ### vg serve memory Project-scoped memory shared across your AI agents: facts, preferences, rules, decisions, gotchas, commands and snippets, with an evidence count so a rule is promoted only after it has been seen enough times (`VG_MEMORY_MIN_EVIDENCE`, default 3). It nests under `vg serve` because serve is what injects it — the code graph remains vg's memory of the *code*; this is the small store of things a session learned that the map cannot know. ```bash vg serve memory add "Run tests with pnpm test, never npm" --kind rule --tags testing vg serve memory search "how do we run tests" vg serve memory list --scope project vg serve memory export > memories.jsonl vg serve memory import memories.jsonl ``` Scopes are `project` (keyed by the git top-level, never injected when there is no repository), `user` and `global`. Storage is a JSONL file per scope under Vibgrate's data directory (`0600`), searched with a lexical ranker — no model, works offline. `vg serve --memory` injects the top `VG_MEMORY_TOP_K` matches into each request and exposes `memory_search` / `memory_save` to the model. Text is redacted for secret shapes before it is stored. --- ### vg serve compress / vg serve retrieve The debug paths. In normal use nobody runs these: the listener already compressed the tool output, and a model that needs an original calls `retrieve_original` (or the `vg_retrieve` tool the listener injects). They are here for when you want to see exactly what the pipeline does to a payload. ```bash cat test-output.log | vg serve compress --tool Bash vg serve compress transcript.json --model claude-sonnet-4-5 --mode token vg serve compress big-array.json --dry-run --stats --json vg serve retrieve 3f9a1c2b7e4d # whole original vg serve retrieve 3f9a1c2b7e4d --grep "Error" # matching lines only vg serve retrieve 3f9a1c2b7e4d --lines 120-180 vg serve retrieve 3f9a1c2b7e4d --json-path "[3].status" vg serve retrieve --list # what is retrievable right now vg serve retrieve --purge # drop expired entries ``` `vg serve compress` detects its input automatically: a JSON array of chat messages (OpenAI or Anthropic shape) or a request body with `messages` goes through the message pipeline and comes back in the same shape; anything else is treated as one tool output and routed by content type. Compressed content goes to stdout, accounting to stderr; `--json` returns both in one object. Its other flags — `--format`, `--profile`, `--mode`, `--lossless`, `--no-ccr`, `--query`, `--tool`, `--target-ratio`, `--dry-run`, `--stats` — mirror the pipeline's own options. `vg serve retrieve` accepts a bare hash, a whole `<>` marker, or a `hash=…` fragment. Entries expire after `VG_CCR_TTL_SECONDS` (default 1800); an expired hash exits `3`. `--max-tokens` caps the slice; `--head` / `--tail` take the first / last N lines. Add `--json` for `{ content, found, truncated, … }`. --- ### Configuration reference (`VG_*`) Every setting is an environment variable; `vg serve config` prints them all with their current value and source, and `vg serve config set KEY VALUE` persists one in `settings.json` (applied only when the variable is not already set in the environment). Precedence: flag → environment → `settings.json` → profile default. Knobs marked *hot* take effect on the next request without a restart. | Variable | Default | What it does | |---|---|---| | `VG_COMPRESS` | `true` | Master switch (off = passthrough) *hot* | | `VG_COMPRESS_MODE` | `cache` | `cache` or `token` *hot* | | `VG_COMPRESS_PROFILE` | `coding` | Savings profile *hot* | | `VG_COMPRESS_MIN_TOKENS` | `500` | Per-message floor before anything is attempted *hot* | | `VG_COMPRESS_PROTECT_RECENT` | `3` | Keep code intact in the last N messages *hot* | | `VG_COMPRESS_PROTECT_READS` | `true` | File reads stay byte-exact *hot* | | `VG_COMPRESS_PROTECT_TOOL_RESULTS` | — | Tool names never lossy-compressed *hot* | | `VG_COMPRESS_EXCLUDE_TOOLS` | — | Tool names skipped entirely *hot* | | `VG_COMPRESS_LOSSLESS` | `false` | Folds only *hot* | | `VG_COMPRESS_LOSSY_MIN_EXTRA_SAVINGS` | `0.15` | How much lossy must beat lossless by *hot* | | `VG_COMPRESS_DEDUPE` | `true` | Cross-turn dedup *hot* | | `VG_COMPRESS_READ_LIFECYCLE` | `true` | Mark reads a later edit made stale *hot* | | `VG_COMPRESS_THINKING_COMPACT` | `false` | Compact prior-turn reasoning where it is billed *hot* | | `VG_COMPRESS_TOOL_PROFILES` | — | JSON map: tool → `{skipCompression, losslessOnly, maxItemsAfterCrush, bias, preserveKeywords}` *hot* | | `VG_COMPRESS_DEADLINE_MS` | `2000` | Per-request budget; over it, the original is forwarded *hot* | | `VG_CODE_COMPRESS` | `true` | Compress bulky tool results inside the `vg code` loop *hot* | | `VG_CODE_COMPRESS_MIN_CHARS` | `4000` | Size a `vg code` tool result must reach before it is compressed *hot* | | `VG_CODE_RETRIEVE_MAX_TOKENS` | `4000` | Cap on one `vg_retrieve` answer inside `vg code`; narrow with `grep` / `lines` / `head` / `tail` *hot* | | `VG_MODEL_LIMITS` / `VG_MODEL_ALIAS_MAP` / `VG_MODEL_PRICES` | — | Context-window, alias and pricing overrides (also `models.json` in the data dir) *hot* | | `VG_CCR` | `true` | Keep originals retrievable *hot* | | `VG_CCR_TTL_SECONDS` | `1800` | How long an original stays retrievable | | `VG_CCR_MAX_ENTRIES` | `1000` | Store size (LRU) | | `VG_CCR_BACKEND` | `disk` | `disk` (shared across processes) or `memory` | | `VG_PROXY_HOST` / `VG_PROXY_PORT` / `VG_PROXY_TOKEN` | `127.0.0.1` / `8787` / — | Bind and auth | | `VG_PROXY_ANTHROPIC_API_URL` / `VG_PROXY_OPENAI_API_URL` | provider defaults | Upstreams | | `VG_PROXY_ALLOWED_BASE_URLS` | — | Allow-list of upstream base URLs | | `VG_PROXY_RPM` / `VG_PROXY_TPM` / `VG_PROXY_BUDGET` | `0` | Limits (0 = off) *hot* | | `VG_PROXY_MODEL_ROUTES` | — | `requested=served,…` model rewrites *hot* | | `VG_PROXY_TOOL_SEARCH` | `true` | Defer large tool lists behind a search tool *hot* | | `VG_PROXY_SYSTEM_COMPACT` | `true` | Compact boilerplate in long system prompts *hot* | | `VG_PROXY_LOG_FILE` / `VG_PROXY_LOG_MESSAGES` | — / `false` | Request log; bodies only when asked *hot* | | `VG_PROXY_METRICS` | `true` | Expose `/metrics` | | `VG_OUTPUT_SHAPER` | `false` | Verbosity steering + effort clamp *hot* | | `VG_OUTPUT_VERBOSITY_LEVEL` | `L2` | `L1` (lightest) … `L4` *hot* | | `VG_OUTPUT_HOLDOUT` | `0.1` | Fraction of turns left unsteered to measure the saving *hot* | | `VG_MEMORY` / `VG_MEMORY_TOP_K` / `VG_MEMORY_MIN_EVIDENCE` | `false` / `5` / `3` | Memory injection *hot* | | `VG_LEARN_TARGET` / `VG_LEARN_CLI` | `CLAUDE.local.md` / — | `vg install --learn` defaults | | `VG_WRAP_PROXY_TIMEOUT` / `VG_WRAP_QUIET` | `15` / `false` | One-session run behaviour | | `VG_CONTEXT_DIR` | data dir `/context` | Where all of this state lives | The full list — about 130 variables including the per-subsystem tuning knobs — is what `vg serve config` prints. --- ### SDK The same pipeline is available programmatically from `@vibgrate/cli`, offline, with no proxy running. ```typescript import { compress, withCompression, compressionMiddleware, CompressionStore } from '@vibgrate/cli'; // One call over a message array (OpenAI or Anthropic shape; same shape back). const result = await compress(messages, { model: 'claude-sonnet-5', mode: 'token' }); console.log(result.tokensBefore, result.tokensAfter, result.transformsApplied); // Wrap an SDK client: compresses on the way in, answers vg_retrieve calls itself. const anthropic = withCompression(new Anthropic(), { profile: 'coding' }); const openai = withCompression(new OpenAI()); // Vercel AI SDK middleware shape. const model = wrapLanguageModel({ model: baseModel, middleware: compressionMiddleware() }); ``` Also exported: `CompressionSession` (per-conversation state: frozen verdicts, dedup, read lifecycle), `SharedContext` (compressed hand-offs between agents), `ContentRouter` and the individual compressors, `detectContentType`, `tokenizerFor`, `modelInfo`, `priceFor`, `costUsd`, the savings ledger (`appendSavingsEvent`, `rollupSavings`) and `MemoryStore`. --- ## Holistic Code Specification (vg hcs) `vg hcs` turns source into a stream of **deterministic code facts** — one NDJSON line per fact — and then renders, maps, gates, and validates that stream. Same code in, same facts out, on every machine: the whole pipeline is reproducible, so a fact stream is something you can commit, diff, and gate CI on. Extraction currently covers **Rust, Ruby, PHP, Dart, Swift, Scala, C++, COBOL, and VB6** — including the legacy stacks that rarely have any machine-readable specification at all. The engine reports its own supported set, and `--language` accepts anything in it; an unsupported value lists the valid ones. **The engine is an optional module.** All HCS computation (fact parsing, decoding, scoring, diffing, rendering) runs inside `@vibgrate/hcs-engine`, a separately-licensed WASM sandbox that makes **no network calls and spawns no processes**. The CLI side is plumbing only — read input, call the engine, write output — so every host gets byte-identical results. The module is auto-provisioned on first use (one-line notice), or install it up front with [`vg module install hcs`](#vg-module). When the engine is missing and cannot be fetched, every `vg hcs` subcommand exits **`6` (`ENGINE_UNAVAILABLE`)** — deliberately distinct from `2` (`GATE_FAILED`), so CI can never read "engine missing" as a gate verdict. **Typical path:** `vg hcs extract` → `vg hcs digest` (read it) / `vg hcs map` (see the system) / `vg hcs gate` (guard CI). ```bash vg hcs extract -o facts.ndjson # facts for the current tree vg hcs digest --in facts.ndjson --format md # a readable specification vg hcs map --facts facts.ndjson --format mermaid vg hcs gate --baseline main.ndjson --facts facts.ndjson ``` ### vg hcs extract Extract facts from source into an NDJSON stream. Test files are excluded by default; the walk skips vendored, build, and dependency directories, and files above 2 MiB. ```bash vg hcs extract [dir] -o facts.ndjson vg hcs extract src --language rust --include-tests ``` **Incremental by default.** When the output stream already exists, extraction is incremental: the engine plans from the previous stream's file-hash index, the CLI re-extracts only added and changed files, and the engine merges — reusing unchanged facts byte-for-byte. So a repeated `-o` run costs only the delta, with output identical to a full extraction. Point `--previous` at a different stream to keep the state file separate from the output, or pass `--full` to re-extract everything from scratch (the merged result still refreshes the index, so the next run stays incremental). | Flag | Default | Description | |------|---------|-------------| | `[dir]` | `.` | Directory to extract from | | `--language ` | all supported | Extract only this language | | `--include-tests` | — | Include test files (excluded by default) | | `-o, --out ` | stdout | Write the NDJSON stream to a file | | `--previous ` | the `--out` file | Previous stream to update incrementally (missing file ⇒ full extraction) | | `--full` | — | Ignore the previous stream and re-extract every file | ### vg hcs digest Render a fact stream as a human-readable specification. ```bash vg hcs extract | vg hcs digest --format md -o SPEC.md vg hcs digest --in facts.ndjson --format html --level full --title "Payments service" ``` | Flag | Default | Description | |------|---------|-------------| | `--in ` | stdin | NDJSON fact stream to read | | `-o, --out ` | stdout | Write output to a file | | `--format ` | `md` | `md`, `json`, or `html` | | `--level ` | `concise` | `concise` or `full` | | `--title ` | — | Override the document title | ### vg hcs map Build the **System Map** from a fact stream — the components, their relations, and the structure they imply. ```bash vg hcs map --facts facts.ndjson --format mermaid vg hcs map --facts facts.ndjson --profile migration -o map.json ``` | Flag | Default | Description | |------|---------|-------------| | `--facts <file>` | stdin | NDJSON fact stream to read | | `-o, --out <file>` | stdout | Write output to a file | | `--format <format>` | `json` | `json`, `md`, or `mermaid` | | `--profile <profile>` | — | Consumer projection: `integration`, `migration`, or `governance` | Pass the global `--generated-at <iso>` to pin the artifact timestamp for byte-deterministic output. ### vg hcs gate The **governance gate**: diff two fact streams and fail on material structural regressions. This is the CI-facing command. ```bash vg hcs gate --baseline main.ndjson --facts pr.ndjson --format sarif -o hcs.sarif vg hcs gate --baseline main.ndjson --facts pr.ndjson --policy .vibgrate/hcs-gate.json ``` | Flag | Default | Description | |------|---------|-------------| | `--baseline <file>` | *required* | Baseline NDJSON facts (the "before" stream) | | `--facts <file>` | stdin | Current NDJSON facts (the "after" stream) | | `--policy <file>` | — | Gate policy JSON — thresholds and disabled rules | | `-o, --out <file>` | stdout | Write the result to a file | | `--format <format>` | `text` | `text`, `json`, or `sarif` | Exit codes: `0` when the gate passes, **`2`** on a material structural regression, `6` when the engine is unavailable. ### vg hcs validate Validate a fact stream against the HCS spec. Exits with the spec's Appendix-I conformance code, so a malformed stream is caught before anything downstream consumes it. ```bash vg hcs validate facts.ndjson vg hcs validate facts.ndjson --json ``` Add `--json` for the full machine-readable report (every error with its code and fact id). --- ## Diagnostics, IDE & runtime Setup health, IDE language server, local workspace daemon, and context-policy pins. **Typical path:** `vg doctor` → `vg lsp` → `vg daemon` ### vg daemon Local workspace daemon for multi-root graph sessions used by IDE extensions and coding agents. Tracks registered repository roots, can load a built map into an in-memory active graph, and answers structural queries and impact over a local socket. Does not rewrite your source tree. Most developers never need this directly — Vibgrate for VS Code and `vg code` attach when needed. Use the CLI for explicit control, multi-root federation, or scripting. ```bash vg daemon status vg daemon ensure vg daemon start vg daemon stop vg daemon restart vg daemon register vg daemon list vg daemon federation vg daemon publish vg daemon query "<text>" vg daemon impact <symbol> vg daemon graphs ``` | Subcommand | Description | |------------|-------------| | `status` | Whether the daemon is running and how many workspaces it tracks | | `start` | Run in the foreground (Ctrl-C to stop) | | `ensure` | Start in the background if not already running (idempotent; for hosts and agents) | | `stop` | Stop the running daemon (idempotent — succeeds if none is running) | | `restart` | Stop the daemon if running, then start it in the background (`vg update` does this automatically after installing a new version) | | `register [root]` | Register the current (or given) repository with a running daemon | | `list` | List registered workspaces | | `federation [root]` | Register a multi-root federation from `.vibgrate/federation.json` (or primary cwd) | | `publish [root]` | Load the workspace code map into the daemon active graph (run `vg build` first). The daemon reads the map from disk itself, binary snapshot first — nothing heavy crosses the socket | | `query <query...>` | Lexical/structural query against the active graph | | `impact <symbol>` | Blast radius for a symbol in the active graph | | `graphs` | List multi-branch graph slots currently resident | | Flag | Description | |------|-------------| | `--socket <path>` | Override the local socket path | | `--repository-id <id>` | On `query` / `impact` / `graphs`: target a workspace id from `vg daemon list` | | `--git-ref <ref>` | On `publish` / `query` / `impact`: branch or SHA | | `--limit <n>` | On `query`: max matches (default 12) | | `--depth <n>` | On `impact`: max dependency depth (default 4) | | `--json` | Machine-readable JSON on stdout | Typical host flow: ```bash vg build vg daemon ensure vg daemon publish vg daemon query "payment service" ``` --- ### vg doctor One read-only diagnostic pass over setup: which config file won, which credential source won (secrets never printed), whether a code map exists and how fresh it is, hosted catalog reachability, what `vg install` would register as the MCP launch, telemetry opt-outs, **local inference** (Code Mode recommendation from free RAM/VRAM, weight catalog pin status, warm host pool size, isolation / sampler env), and **context compression** (is a proxy running and on which port, the active profile and mode, retrievable-store size and expiry, memory scopes, which agents are currently wrapped, and any `VG_*` value that fails validation). Prints state; changes nothing. ```bash vg doctor vg doctor --json vg doctor --local ``` | Flag | Description | |------|-------------| | `--json` | Machine-readable JSON on stdout | | `--offline` | Skip the hosted reachability probe | | `-C, --cwd <dir>` | Run as if started in that directory | --- ### vg lsp Start the Vibgrate language server over **stdio** — the shared engine behind **Vibgrate for VS Code** and other thin IDE clients. Editors spawn this; humans rarely run it by hand. ```bash vg lsp vg lsp --diagnostics vg lsp --no-graph vg lsp --no-semantic vg lsp --local ``` | Flag | Description | |------|-------------| | `--diagnostics` | Also publish Problems-panel diagnostics (EOL runtime, unmaintained packages, license change). **Off by default** — drift is not a defect, and the Problems panel is not filled by default. | | `--no-graph` | Skip the local code graph entirely: no background build; graph queries report it as turned off | | `--no-semantic` | Never use semantic search for graph queries (lexical only; embedding model is not downloaded) | | `--offline` | Never touch the network (air-gapped editor sessions) | The process owns stdin/stdout until the client sends `shutdown` + `exit`. Speaks standard LSP plus a custom `vibgrate/score` notification carrying the DriftScore and its **band** (never a colour) so clients can theme correctly. --- ### vg policy Show the production **context-policy** pin used by VG Code ranking, and verify a signed `context-policy-patch/0` JSON file before any release that would bump it. Learning never mutates production policy from a single task. This is not the hosted workspace policy UI in Vibgrate Cloud (banned packages, drift budgets). For dependency bans in CI, use `vg drift --fail-on standards` with a committed standards file. ```bash vg policy vg policy --json vg policy verify ./context-policy-patch.json ``` | Subcommand | Description | |------------|-------------| | *(default)* | Print production pin and ranking version | | `verify <file>` | Verify a `context-policy-patch/0` JSON file (hash + optional signature + production gate) | Exit non-zero from `verify` when the production gate is not ready (so CI can block a premature bump). --- ## Drift Baselines & Fitness Functions Vibgrate stores scan state under `.vibgrate/`: - `.vibgrate/scan_result.json`: latest scan artifact - `.vibgrate/baseline.json`: explicit baseline snapshot (`vg baseline`) - `<project>/.vibgrate/project_score.json`: per-project score snapshots Recommended workflow: 1. Create baseline once on main branch: ```bash vg baseline ``` 2. In CI, run scan with comparison and gates: ```bash vg scan --baseline .vibgrate/baseline.json --drift-budget 40 --drift-worsening 5 ``` 3. When planned upgrades land, refresh baseline: ```bash vg baseline ``` This makes drift a formal quality gate (fitness function), not just reporting. ## DriftScore ### How the Score Is Calculated The DriftScore is a deterministic, versioned metric (0–100) that represents how far behind your codebase is relative to the current stable ecosystem baseline. **Lower score = healthier upgrade posture.** 0 means no drift (fully current); 100 means maximum drift. Higher is worse. The methodology is published: see the [public scoring specification](./docs/public/SCORING-METHODOLOGY-PUBLIC.md) in this repository and the overview at [vibgrate.com/driftscore](https://vibgrate.com/driftscore). ### Risk Levels | Score | Risk Level | | ------ | ------------------------------------ | | 0–30 | **Low** — You're in good shape | | 31–60 | **Moderate** — Some attention needed | | 61–100 | **High** — Significant upgrade debt | ### Score Components The overall score is a weighted combination of four components: | Component | What It Measures | | ---------------- | --------------------------------------------------------------------------------- | | **Runtime** | Node.js or .NET runtime major version lag | | **Frameworks** | Major version distance for core frameworks (React, Next, NestJS, ASP.NET, etc.) | | **Dependencies** | Age distribution across all dependencies (current vs 1 major behind vs 2+ behind) | | **EOL Risk** | Proximity to end-of-life for runtimes and frameworks | --- ## Output Formats ### Text The default output. A coloured, human-readable report showing: - Overall drift score and risk level - Score component breakdown with visual bars - Per-project details: runtime lag, framework versions, dependency distribution - Findings with severity icons ### JSON Artifact The full scan artifact in JSON format. Contains all raw data, scores, findings, and VCS metadata. Stable schema (`schemaVersion: "1.0"`). This is the same artifact saved to `.vibgrate/scan_result.json`. ### SARIF [Static Analysis Results Interchange Format](https://sarifweb.azurewebsites.net/) — compatible with GitHub Code Scanning and Azure DevOps. Contains findings only (not all metrics). Ideal for integrating drift findings directly into your PR review workflow. ### Markdown A clean Markdown report suitable for PRs, wikis, or documentation. --- ## Configuration ### vibgrate.config.ts Run `vg init` to generate the config file, or create one manually: ```typescript import type { VibgrateConfig } from "@vibgrate/cli"; const config: VibgrateConfig = { exclude: ["legacy/**"], thresholds: { failOnError: { eolDays: 180, frameworkMajorLag: 3, dependencyTwoPlusPercent: 50, }, warn: { frameworkMajorLag: 2, dependencyTwoPlusPercent: 30, }, }, scanners: { platformMatrix: { enabled: true }, dependencyRisk: { enabled: true }, dependencyGraph: { enabled: true }, toolingInventory: { enabled: true }, buildDeploy: { enabled: true }, tsModernity: { enabled: true }, breakingChangeExposure: { enabled: true }, fileHotspots: { enabled: true }, securityPosture: { enabled: true }, securityScanners: { enabled: true }, serviceDependencies: { enabled: true }, databaseSchema: { enabled: true }, }, }; export default config; ``` Also supports `vibgrate.config.js` and `vibgrate.config.json`. ### Thresholds Control when findings are raised and when the CLI should fail. | Threshold | Default | Triggers | | -------------------------------------- | ------- | ------------------------------------------------------------- | | `failOnError.eolDays` | 180 | Error finding when runtime EOL is within N days | | `failOnError.frameworkMajorLag` | 3 | Error finding when any framework is N+ majors behind | | `failOnError.dependencyTwoPlusPercent` | 50 | Error finding when N+% of dependencies are 2+ majors behind | | `warn.frameworkMajorLag` | 2 | Warning finding when any framework is N+ majors behind | | `warn.dependencyTwoPlusPercent` | 30 | Warning finding when N+% of dependencies are 2+ majors behind | ### Scanner Toggles Each extended scanner can be individually disabled. Set `scanners: false` to disable all extended scanners (the core drift scan always runs). ### Resource safeguards (environment variables) Building the code map holds every parse table, node, and edge in memory, so on a pathological corpus (a vendored 200 MB bundle, a million-file tree) an unguarded build could exhaust memory and crash the process. The build ships with safeguards on by default; each is tunable via an environment variable, and `0` always means "disabled". | Variable | Default | What it does | | --------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `VG_MAX_FILE_BYTES` | `2097152` (2 MiB) | Per-file source cap. Larger files (almost always generated/minified) are skipped with a warning; they stay freshness-tracked. | | `VG_MAX_FILES` | `100000` | Corpus file-count ceiling. Exceeding it stops the build with guidance (scope with paths, `--exclude`, or `--only`) instead of an OOM. | | `VG_TSC_MAX_FILES` | `10000` | Max TS/JS files handed to the in-process TypeScript resolver (the largest single memory consumer). Above it, the heuristic rung is used. | | `VG_MEMORY_BUDGET_MB` | 90% of the Node heap ceiling | Heap budget checked at phase boundaries. Exceeding it stops the build with a clear, catchable error before V8 hard-crashes. | | `VG_JOBS` | CPU cores − 1 | Default parse worker count when `--jobs` isn't passed. Fewer workers = lower peak memory (each worker loads its own grammar set). | | `VG_WORKER_HEAP_MB` | platform default | Per-worker old-generation heap cap, so one runaway parse can't take the whole machine. | Skips are deterministic functions of the input (file size, file count) — never of observed memory — so identical input still produces an identical `graph.json`. To give the build more room instead of limiting it, raise the Node heap: `NODE_OPTIONS=--max-old-space-size=8192`. --- ## Extended Scanners Beyond the core drift score, Vibgrate runs a suite of extended scanners that collect high-value migration intelligence. All scanners: - Are **read-only** — they never write files or execute project code - Run **in parallel** — failures in one scanner never affect the others - Can be **individually toggled** in the config - Collect **zero sensitive data** — no secrets, no PII, and no credentials, even the ones that do open source files (below) The core drift score is manifest/lockfile-only. Several extended scanners, and the code graph (`vg build`/`vg map`/`vg share`/`vg serve`), go further and open your source files locally — that is how they work, not an accident: | Reads source locally | What it extracts | Keeps raw text? | |---|---|---| | **Code graph** (`vg build`/`vg map`) | Symbol names, call edges, file paths, hubs/areas — the graph itself | No — never a source line, only structural graph facts | | **Code Quality** (`codeQuality`) | Cyclomatic complexity, function length, nesting depth, dead code, "god files" | No — computed metrics only | | **Breaking Change Exposure** (`breakingChangeExposure`) | Import/usage-pattern hit counts for majorly-outdated packages | No — counts only | | **Database Schema** (`databaseSchema`) | Table/model names, column names and types, relation/key flags from SQL/Prisma/Drizzle/TypeORM files | No — never a query, row, or credential | | **UI Purpose** (`uiPurpose`) | Route/nav/title/CTA copy, for feature detection | **Yes** — short evidence samples of the literal UI text are kept locally (never business logic, never a full file) | None of this is executed, and **nothing above leaves your machine** unless you run `vg share`/`vg push` or scan with a DSN configured — and even then, what uploads is the computed/structural output in the table above, never a raw source file. Each of these is individually toggleable; set the matching `scanners.<name>.enabled` to `false` (or `scanners: false` for all extended scanners, which does not affect the code graph) if you don't want the read to happen at all. See [Scanner Toggles](#scanner-toggles) above and each scanner's own section below. The one exception where code truly leaves your machine is the **remediation agent**: when you ask it to write a fix, it clones your repository into an isolated virtual machine Vibgrate controls, makes the change, and hands you a pull request. That only happens when you ask for it. See [vibgrate.com/subprocessors](https://vibgrate.com/subprocessors) for who processes what. ### Platform Matrix Collects platform and architecture signals that predict where builds will break when moving CI runners, containers, or CPU architectures. - `engines.node` and `engines.npm`/`engines.pnpm` ranges - `.nvmrc` / `.node-version` files - .NET `TargetFramework` and SDK versions - Native module risk packages (`sharp`, `bcrypt`, `node-gyp`, etc.) - OS-assumption scripts in `package.json` - Dockerfile base images (FROM lines only) ### Dependency Risk Extends dependency analysis with risk classification signals: - Deprecated packages (npm `deprecated` field) - Native module detection - Platform-specific package flags ### Dependency Graph & Duplication Parses lockfiles (pnpm, npm, yarn, .NET) to build a workspace-wide dependency graph: - Total unique vs. installed dependency counts - Duplicated packages (multiple versions of the same package) - Phantom dependencies (used but not declared) ### SBOM-ready Supply Chain Inventory Vibgrate artifacts include dependency graph and package inventory data that can be used for supply-chain governance workflows: - Lockfile-derived package counts (`totalUnique`, `totalInstalled`) - Duplicate-version hotspots to prioritize remediation - Phantom dependency evidence (`phantomDependencies` + details) - Inventory metadata that pairs well with internal SBOM pipelines Vibgrate supports both direct SBOM export (`vg sbom export`) and raw inventory consumption from `scan_result.json`, so teams can choose either built-in output or custom SBOM pipelines. Example: ```bash vg sbom export --in .vibgrate/scan_result.json --format spdx --out sbom.spdx.json ``` Expected result: - A standards-based SBOM file (`spdx` or `cyclonedx`) is written for downstream governance tooling. ### Tooling Inventory Maps the full technology stack across your workspace by detecting package names in dependencies: | Category | Examples | | --------------- | ------------------------------------ | | Frontend | React, Vue, Angular, Svelte, Solid | | Meta-frameworks | Next.js, Nuxt, Astro, Remix | | Bundlers | Vite, webpack, esbuild, Rollup | | Backend | Express, Fastify, NestJS, Hono | | ORM / DB | Prisma, Drizzle, TypeORM, EF Core | | Testing | Vitest, Jest, Playwright, xUnit | | Observability | Sentry, OpenTelemetry, Pino, Winston | ### Build & Deploy Surface Area Detects CI/CD, containerisation, and infrastructure-as-code: - CI systems (GitHub Actions, GitLab CI, Azure DevOps, Jenkins, CircleCI) - Docker and Docker Compose - IaC (Terraform, Bicep, CloudFormation, Pulumi) - Release tooling (Changesets, semantic-release, GitVersion) - Package managers and monorepo tools ### TypeScript Modernity Reads `tsconfig.json` compiler options to assess strictness and modernity: - TypeScript version - `strict`, `noImplicitAny`, `strictNullChecks` flags - Module system (`module`, `moduleResolution`, `target`) - ESM vs CJS classification - `exports` field presence ### Breaking Change Exposure Flags packages and patterns known to cause upgrade pain: - Deprecated packages (e.g. `request`, `node-sass`, `tslint`, `moment`) - Legacy Node API polyfills no longer needed on Node 18+ (e.g. `node-fetch`, `abort-controller`) - Peer dependency conflicts - Exposure score (0–100) ### File Hotspots Lightweight complexity analysis using filesystem metadata only (never reads file contents): - File counts by extension - Largest files by size (path + bytes) - Directory depth distribution - Most-used packages across the workspace ### Security Posture Structural security hygiene indicators (not a secret scanner): - Lockfile presence and consistency - `.gitignore` coverage for `.env` files and `node_modules` - `.env` files tracked outside `.gitignore` - Audit severity counts (via `npm audit --json`) ### Security Scanners Security scanner orchestration and readiness analysis for local policy and secret-scanning workflows: - Scanner engine discovery (installed vs missing) - Version freshness checks to flag stale scanner engines/signatures - Local config discovery for scanner policy files - Cache-backed heuristic secret signals to add value even when binaries are unavailable > This scanner does not guarantee full secret detection or rule coverage by itself; it reports toolchain status and lightweight in-repo indicators so teams can decide how to harden CI enforcement. ### Service Dependencies Maps external service and platform dependencies by detecting SDK packages: | Category | Examples | | ------------- | -------------------------------- | | Payment | Stripe, Braintree, PayPal | | Auth | Auth0, Clerk, Firebase, Passport | | Cloud SDKs | AWS, Azure, Google Cloud | | Databases | PostgreSQL, MongoDB, Redis | | Messaging | SQS, SNS, Kafka, BullMQ | | Observability | Sentry, DataDog, New Relic | ### Database Schema Extracts structural database-schema facts across five sources — Prisma (`schema.prisma`), raw SQL migrations (`.sql` files), SQL Server database projects (`.sqlproj`), Drizzle (`pgTable`/`mysqlTable`/`sqliteTable`), and TypeORM (`@Entity()` classes) — merged into one report: - Table/model names, per-field name and type, and relation/list/optional/id/unique flags - Enum names and values (Prisma) - Datasource providers (e.g. `postgresql`, `mysql`) — never the connection-string `url` - Files scanned, with a per-project breakdown Only structural facts are ever extracted — never a raw source line, a query, or a connection string/credential (any `scheme://user:pass@host` line is stripped as defense in depth even though hand-written SQL rarely embeds one). Reading these facts means opening `.sql`/`.prisma`/ORM source files locally — see the table at the top of this section for how this compares to the code graph and the other scanners that also read source. It's on by default; disable it with `scanners.databaseSchema.enabled: false` in `vibgrate.config.ts` (see [Scanner Toggles](#scanner-toggles)). Like every extended scanner, results only leave your machine when you run `vg push` or scan with a DSN configured — and the models/fields/files arrays are capped before upload so a large-monorepo schema can't balloon the payload. ### Architecture Layers Classifies source files into architectural layers and reports drift by layer to make refactors more predictable: - Archetype detection (e.g. Next.js, NestJS, Express, serverless, monorepo, CLI) - Layer-level file counts and confidence scoring - Per-layer package drift scores and risk levels - Layer-specific tech stack and service dependency attribution ### Code Quality Metrics Fast AST-based quality checks to identify upgrade friction hotspots: - Files/functions analyzed - Cyclomatic complexity averages - Function length and nesting depth signals - Circular dependencies and dead-code estimate - "God file" detection for oversized high-complexity modules ### OWASP Category Mapping Maps security findings into OWASP Top 10 categories for security triage inside existing drift reports: - Supports `fast` and `cache-input` modes - Categorizes findings with severity and CWE metadata - Emits per-category counts in JSON output - Designed for CI visibility without requiring a separate report format --- ## CI Integration ### GitHub Actions Use the maintained templates in this package for copy-paste setup: - `examples/github-actions/driftscore-ci.yml` (JSON artifact + drift gate) - `examples/github-actions/driftscore-sarif.yml` (SARIF upload to code scanning) - `examples/github-actions/vulnerabilities-sarif.yml` (vulnerability gate + SARIF upload) - `docs/ci/github-actions.md` (integration notes) ```yaml steps: - name: Vibgrate Scan run: npx @vibgrate/cli scan --format sarif --out vibgrate.sarif --fail-on error - name: Upload SARIF uses: github/codeql-action/upload-sarif@v3 with: sarif_file: vibgrate.sarif # Optional: push metrics to Vibgrate Cloud - name: Push Vibgrate Metrics env: VIBGRATE_DSN: ${{ secrets.VIBGRATE_DSN }} run: npx @vibgrate/cli push --file .vibgrate/scan_result.json ``` To gate pull requests on **known vulnerabilities** and surface them in the Security tab, the maintained `vibgrate/cli` Action does the scan, gate, and SARIF upload in one step (needs `permissions: security-events: write`): ```yaml steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # full history → exposure attribution + remediation MTTR - uses: vibgrate/cli@v1 with: vulns: true fail-on: error # critical/high block the merge upload-sarif: true category: vibgrate-vulns ``` ### Azure DevOps ```yaml steps: - script: npx @vibgrate/cli scan --format sarif --out vibgrate.sarif --fail-on error displayName: Vibgrate Scan - task: PublishBuildArtifacts@1 inputs: PathtoPublish: vibgrate.sarif ArtifactName: VibgrateSARIF ``` ### GitLab CI ```yaml vibgrate: script: - npx @vibgrate/cli scan --format sarif --out vibgrate.sarif --fail-on error artifacts: reports: sast: vibgrate.sarif ``` ### Generic Pipelines Vibgrate works in any CI environment. The CLI: - Requires no login or authentication - Returns meaningful exit codes (see below) - Produces standard SARIF output - Works entirely offline (push is opt-in) --- ## Vibgrate Cloud Upload ### DSN Tokens Vibgrate uses HMAC-signed DSN tokens for authenticated uploads. The DSN format: ``` vibgrate+https://<key_id>:<secret>@<ingest_host>/<workspace_id> ``` Set `VIBGRATE_DSN` as a secret in your CI environment. Uploads are always optional — the CLI provides full value locally without any server connection. ### Data Residency Vibgrate supports region-specific ingest endpoints: | Region | Endpoint | | ------------ | ------------------------ | | US (default) | `us.ingest.vibgrate.com` | | EU | `eu.ingest.vibgrate.com` | Use `--region eu` on `push` or `dsn create` to route data to the EU endpoint. --- ## Privacy & Security Vibgrate is built with a privacy-first architecture. Here's what it **never** does: | Category | Hard guarantee | | ------------------ | -------------------------------------------------- | | Source code | Never read beyond config/manifest files | | Secrets | Never scanned for, never extracted | | Environment values | Never read — only `.env` file existence is flagged | | Git identity data | Never accessed — `git log` is never invoked | | File contents | Only structured config fields are extracted | | Network endpoints | Never parsed from config files | What it **does** collect: - Package names and version numbers (from `package.json`, `.csproj`, lockfiles) - Config structure flags (e.g. `strict: true` from `tsconfig.json`) - File names and sizes (paths and metadata, never contents) - Public npm/NuGet registry metadata (latest versions, deprecation flags) - CI/Docker/IaC file presence and structural counts --- ## `--offline` vs `--local` Two different questions, so two flags: | Flag | Question it answers | Effect | | --- | --- | --- | | `--offline` | May this reach the network? | No model download, no catalog fetch, no upload. Available on every command. | | `--local` | Where does inference run? | On-device backends only, never a hosted model. Consumed by [`vg code`](#vg-code). | **`--local` implies `--offline`**, so anything that already passes `--local` keeps its exact behaviour. The converse does not hold, and deliberately so: forcing on-device inference is a choice you may want on a fully connected machine — for privacy, cost, or latency — and `--offline` would not say it. `vg scan --offline` and `vg evidence --offline` mean the same thing they always have. --- ## Exit Codes CI and agents branch on these, so they are a stable contract. | Code | Name | Meaning | | ---- | --------------------- | ---------------------------------------------------------------------------------------- | | `0` | `OK` | Success | | `1` | `ERROR` | Runtime error | | `2` | `GATE_FAILED` | A gate failed: `--fail-on` threshold exceeded, a drift budget breached, `vg hcs gate` regression, `vg bisect --assert` unsatisfied | | `3` | `NOT_FOUND` | The thing asked for does not exist (unknown symbol, no version history, …) | | `4` | `NON_DETERMINISTIC` | A verification found output that is not reproducible | | `5` | `USAGE_ERROR` | Bad invocation: unknown command, invalid flag value, missing argument | | `6` | `ENGINE_UNAVAILABLE` | A required optional module is not installed and could not be fetched (see [`vg module`](#vg-module)) | `6` is deliberately distinct from `2`: a CI gate must never read "engine missing" as a gate verdict. The same rule is why [`vg review`](#vg-review) exits `6` when there is no code map, and why `--explain` exits `6` rather than quietly producing a review no model contributed to. --- ## Programmatic API The package exports its core types for programmatic use: ```typescript import type { VibgrateConfig, ScanArtifact, DriftScore, Finding, } from "@vibgrate/cli"; ``` --- ## Requirements - **Node.js** >= 22.0.0 - Works on macOS, Linux, and Windows --- ## Links - [Website](https://vibgrate.com) - [Vibgrate CLI — live demo and simulator](https://vibgrate.com/cli) - [CLI benchmarks](https://vibgrate.com/cli/benchmarks) · [methodology](https://vibgrate.com/cli/benchmarks/methodology) · [token savings](https://vibgrate.com/cli/benchmarks/token-savings) - [DriftScore](https://vibgrate.com/driftscore) - [Vibgrate AI Context (local-first MCP)](https://vibgrate.com/library) - [Vibgrate Graph](https://vibgrate.com/graph) - [Vibgrate Cloud](https://vibgrate.com/cloud) · [create a free workspace](https://dash.vibgrate.com) - [Vibgrate Cloud MCP](https://vibgrate.com/mcp) - [AI agent skills](https://vibgrate.com/skills) - [Glossary](https://vibgrate.com/glossary) - [Help center](https://vibgrate.com/help) - [Changelog](https://vibgrate.com/changelog) - [npm](https://www.npmjs.com/package/@vibgrate/cli) --- Copyright © 2026 Vibgrate. All rights reserved. See [LICENSE](https://vibgrate.com/license) for terms.