Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ backward compatible.

### Fixed

- **`vg scan --package-manifest` stops when the manifest cannot be read.** A
missing path, a file this process cannot read, or content that is not a
package-version manifest (JSON, or a ZIP containing `package-versions.json`,
`manifest.json`, or `index.json`) now ends the command with a non-zero exit
and an error that names the path and what to pass instead. The scan does not
continue without the manifest, and the message does not include the file's
contents.

- **`vg sbom export` no longer turns a package name that cannot be a Package URL
into a purl-shaped string.** A space, a non-ASCII character, or an empty path
segment used to be percent-encoded (`pkg:npm/foo%20bar@1.0.0`) and shipped as
if it were a real purl. The component stays in the CycloneDX and SPDX
documents. The purl (and the SPDX purl externalRef) is left off, the row is
marked `vibgrate:purlStatus=unavailable`, and the command prints a warning
that names the package and ecosystem. The same rule applies to npm components
in `vg export`'s CycloneDX output. An ecosystem this exporter does not know
is not reported as npm.

- **`vg show arch` clipped the map to a fixed viewport.** Columns that ran off the
bottom of the window could not be scrolled or zoomed; the canvas is now a
pannable, zoomable map (scroll or drag, pinch / Ctrl-scroll, + / −).
Expand Down
56 changes: 54 additions & 2 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,15 @@ Every component also carries a [purl](https://github.com/package-url/purl-spec)
(`pkg:npm/<name>@<version>`, 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
identifier. When a package name cannot be a Package URL (a space, a non-ASCII
character, or an empty path segment), that component stays in the document and
the purl is omitted. CycloneDX sets `vibgrate:purlStatus` to `unavailable` and
records the reason on `vibgrate:purlWarning`. SPDX omits the purl externalRef,
records `purlStatus=unavailable` on the package annotation, and repeats the
reason in a second annotation. `vg sbom export` prints the same warning on
stderr. The warning names the package and its ecosystem.

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
Expand Down Expand Up @@ -1994,11 +2002,24 @@ vg path handler insert --calls
| `--calls` | Follow call edges only; show the call-site line of each hop |
| `--pick-a <n>` | Pick the nth candidate for A |
| `--pick-b <n>` | Pick the nth candidate for B |
| `--diagram` | Draw the path as a pinned call path instead of text |
| `--format <fmt>` | With `--diagram`: `md` (default) or `json` (a `vg.review.doc.v1` document with `kind: "explain"`) |

With `--json`, `steps` lists each hop's edge kind, resolver, call-site line and
`awaited` flag. The `find_path` MCP tool returns the same as `hops`, and takes
`calls_only: true` for the call-only path.

With `--diagram`, the path becomes a document you can read or hand to an agent:

```bash
vg path placeOrder audit --calls --diagram
```

- **What it is:** each hop in order, with the line that makes it and whether the call is awaited.
- **How it works:** one call path, caller first. Each step is linked to the lines that declare it, and each hop to its call site.

Every element comes from the code map and is pinned to lines in the working tree. A step with no code in the repository (a library function) is left out of the call path and named in the notes.

---

### vg savings
Expand Down Expand Up @@ -2111,6 +2132,37 @@ vg show src/orders/service.ts:42 --diagram --pick 1 --format json

Every element is pinned to lines in the working tree and comes from the code map, so nothing is marked new or edited and the call path has no before side. When nothing in the code map calls the code, the flow leads instead. The document is the same `vg.review.doc.v1` that `vg review doc` writes, so the same renderers and checks apply. In VS Code, **Vibgrate: Explain This Code with Diagrams** opens it for the function under the cursor.

#### vg show flow

What a function does, step by step, as one pinned flow diagram: the explain view of `vg show <name> --diagram` with only its flows.

```bash
vg show flow UpdateProductCommandHandler.Handle
vg show flow src/orders/service.ts:42 --format json
```

Each step is a statement the code map recorded (a query, a write, a call, a branch or an error path), linked to its line. When the code map records no steps for the function, `vg show flow` says so and exits 3. It needs the Architecture module (`vg module install arch`).

Agents get the same documents over MCP: under `vg serve --review`, the `review_doc` tool's `explain` op takes `symbol` (and `to` for a call path). It saves nothing unless asked to keep it.

#### vg show scratchpad

One explain scratchpad per repository, for understanding code rather than reviewing a change. Every explanation you keep lands on top, newest first:

```bash
vg show OrderService.save --diagram --keep
vg path placeOrder audit --calls --diagram --keep
vg show scratchpad
vg show scratchpad --clear
```

- **Newest on top.** Each entry starts with a heading. Keeping the same explanation again moves it to the top instead of adding a copy. The 30 newest entries are kept.
- **Agents write here too.** The `review_doc` tool's `explain` op with `keep: true` adds an entry. Ops `get`, `patch`, `check` and `clear` with `doc_id: "scratchpad"` read it, redraw a block by id, and empty it. What an agent writes is marked as written by an agent.
- **Pinned to the working tree.** Code moves under a scratchpad, so a block whose lines no longer exist is reported, not deleted. A patch is refused only when it breaks something that was fine.
- **Local.** It is stored in `.vibgrate/review-docs/scratchpad.json`, never committed, and deleted after 365 days without an update.

In VS Code, **Vibgrate: Explain This Code with Diagrams** keeps its result on the scratchpad, and **Vibgrate: Open Explain Scratchpad** opens it. The tab updates as an agent writes to it.

#### vg show arch

Open a local, interactive architecture map of the same graph in your browser.
Expand Down Expand Up @@ -2814,7 +2866,7 @@ This makes drift a formal quality gate (fitness function), not just reporting.

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.
**Lower score = healthier upgrade posture.** 0 means no drift (fully current); 100 means maximum drift. Higher is worse. A component that was not measured is `null` in JSON and `n/a` in text, not 0. When nothing was measured, the overall score is null as well, and `--drift-budget` does not compare it.

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

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,9 @@ Under each set, commands are listed A–Z. A short **typical path** (usual order
| `vg map` / `vg hubs` / `vg areas` / `vg oddities` | Map insights: overview, most-depended-on code, natural groupings, cross-area smells |
| `vg models` | Code Modes (Spark / Flow / Forge) + local fleet (Ollama / LM Studio / gguf); `install` / `pull` by default (`--dry-run` to preview) |
| `vg module` | Manage optional local modules (`relevance`, `hcs`): `status`, `install`, `remove` |
| `vg path <from> <to>` | How A connects to B (shortest path) |
| `vg path <from> <to>` | How A connects to B (shortest path); `--diagram` draws it as a pinned call path |
| `vg show flow <entry>` | What a function does, step by step, as a pinned flow diagram |
| `vg show scratchpad` | The explain scratchpad: explanations kept with `--keep` or by an agent, newest on top |
| `vg savings` | Local report of tokens/$ saved — the grep baseline for map queries, and context compression by window, model and client (estimates) |
| `vg watch` | Rebuild the map when files change |
| `vg serve` | Start **Vibgrate AI Context** (local-first MCP: code map + drift + version-correct docs) |
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ inputs:
image-tag:
description: 'Scanner image tag to run (defaults to a pinned, tested release).'
required: false
default: '2026.1003.1' # vibgrate:cli-version — stamped by scripts/stamp-release-pins.mjs
default: '2026.1003.3' # vibgrate:cli-version — stamped by scripts/stamp-release-pins.mjs
verify:
description: 'Verify the image cosign signature + provenance before running (requires cosign on the runner).'
required: false
Expand Down
2 changes: 1 addition & 1 deletion charts/vibgrate/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type: application
# stamped to the released @vibgrate/cli calendar version by
# scripts/stamp-release-pins.mjs (via the marker on the appVersion line below).
version: 0.1.2
appVersion: "2026.1003.1" # vibgrate:cli-version — stamped by scripts/stamp-release-pins.mjs
appVersion: "2026.1003.3" # vibgrate:cli-version — stamped by scripts/stamp-release-pins.mjs
home: https://vibgrate.com
icon: https://vibgrate.com/web-app-manifest-512x512.png
sources:
Expand Down
4 changes: 3 additions & 1 deletion docs/public/SCORING-METHODOLOGY-PUBLIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ RiskScore's job).

Four weighted pillars, computed on a health scale and emitted as drift (0 = no
drift). Weight is redistributed across whichever pillars have data, so a scan
with no runtime metadata is not unfairly penalised.
with no runtime metadata is not unfairly penalised. A pillar with no input is
`null` (shown as `n/a`), not drift 0. When no pillar has data, the overall
DriftScore is null rather than 0.

| Pillar | Weight | Input |
|---|---:|---|
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vibgrate/cli",
"version": "2026.1003.1",
"version": "2026.1003.3",
"description": "vg — local codebase intelligence CLI + MCP server for AI coding agents: deterministic code graph, drift reporting, and version-correct library docs (Apache-2.0)",
"//mcpName": "Official MCP registry ownership proof: the registry fetches the published npm package and requires this field to match the com.vibgrate/ai-context server entry (see docs/marketing/mcp-registry/README.md). Must ship in the published @vibgrate/cli package.json.",
"mcpName": "com.vibgrate/ai-context",
Expand Down
4 changes: 2 additions & 2 deletions packaging/homebrew-tap/Formula/vg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
class Vg < Formula
desc "Deterministic, no-API-key code graph for AI assistants (vg)"
homepage "https://vibgrate.com"
url "https://registry.npmjs.org/@vibgrate/cli/-/cli-2026.1003.1.tgz"
sha256 "656f2b6ffc7fb8d025805949e5ed2aea22d5cf3141f2fe4117fe758e71933a7a"
url "https://registry.npmjs.org/@vibgrate/cli/-/cli-2026.914.1.tgz"
sha256 "21c164080d1ba33dc53d604a8754ffa0079daa9c8b771a9053c224a2c43877bf"
license "Apache-2.0"
depends_on "node"

Expand Down
2 changes: 1 addition & 1 deletion packaging/scoop-bucket/vg.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2026.1003.1",
"version": "2026.914.1",
"description": "Deterministic, no-API-key code graph for AI assistants (vg)",
"homepage": "https://vibgrate.com",
"license": "Apache-2.0",
Expand Down
52 changes: 52 additions & 0 deletions releases/v2026.1003.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Vibgrate CLI 2026.1003.3

_Released 2026-10-03_

This release of the Vibgrate CLI includes several important fixes and new features aimed at improving the user experience. Notably, it addresses issues with DriftScore reporting, configuration file handling, and introduces new diagramming capabilities for code analysis.

## What changed

### New

- `vg path <from> <to> --diagram` and `vg show flow <entry>` offer new ways to visualize code interactions.
- An explain scratchpad has been introduced for understanding code, with the newest entries displayed on top.

### Fixed

- `vg scan` now correctly distinguishes between an unmeasured DriftScore and a real score of 0.
- A typo in `.vibgrate/config.yml` now prevents commands from crashing and provides clear error messages.
- Commands now stop with clear errors for truncated or invalid code maps, and provide guidance to rebuild.
- `vg scan --package-manifest` exits with an error when the manifest file is missing or unreadable.
- `vg sbom export` now correctly handles components with names that cannot be represented as Package URLs.
- `vg scan` and `vg build` will stop for truncated lockfiles and provide instructions to regenerate them.
- `vg scan --vulns` now reports issues with unparseable CVSS vectors separately from missing scores.
- Warnings are issued for declared licenses that cannot be read as SPDX, including the license text.
- `vg build` and `vg scan` now skip empty or whitespace-only exclude patterns, improving project visibility.

## Benchmarks

Two-arm benchmark of this release against 2026.1003.1, interleaved on one runner against the pinned corpus (236 metrics compared).

| Metric | Previous | This release |
| --- | --- | --- |
| Languages with extraction | 19 count | 19 count |
| Definitions extracted (corpus total) | 26401 count | 26401 count |
| Call edges extracted (corpus total) | 18653 count | 18653 count |
| Locate accuracy (top-1) | 0.94 ratio | 0.94 ratio |
| Dependency detection (authored manifest truth) | 0.96 ratio | 0.96 ratio |
| CLI startup (--version, median) | 608.20 ms | 615.20 ms |

2 regression(s) — published, not omitted:
- Tasks passed on both arms: 36 → 34 (-5.6%)
- Comparable-task rate (both arms passed / total): 0.95 → 0.89 (-5.6%)

Full report and methodology: https://vibgrate.com/cli/benchmarks

## Install or update

```sh
npm install -g @vibgrate/cli
vg
```

Full changelog: https://vibgrate.com/changelog/cli/2026.1003.3
7 changes: 5 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { registerPolicy } from './commands/policy.js';
import { registerLlmHost } from './commands/llm-host.js';
import { registerHcs } from './commands/hcs.js';
import { registerReview } from './commands/review.js';
import { ConfigFileError } from './core-open/config.js';
import { LockfileParseError } from './core-open/utils/lockfile-parse.js';
import { CliError, ExitCode } from './util/exit.js';
import { c, info, disableColor, exitAfterFlush } from './util/output.js';

Expand Down Expand Up @@ -431,10 +433,11 @@ function handleError(err: unknown): never {
/* stdout closed */
}
};
if (err instanceof CliError) {
if (err instanceof CliError || err instanceof LockfileParseError || err instanceof ConfigFileError) {
const code = err instanceof CliError ? err.code : ExitCode.ERROR;
emitHostError(err.message);
info(c.red(`error: ${err.message}`));
return exitAfterFlush(err.code);
return exitAfterFlush(code);
}
const message = err instanceof Error ? err.message : String(err);
const correlation = Math.random().toString(36).slice(2, 10);
Expand Down
Loading
Loading