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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ backward compatible.

### Fixed

- **`vg report --format` rejects values other than `md`, `text`, and `json`.**
An unknown value, including `html`, exits `5` with a usage error that names
the value and lists the valid ones. Stdout is empty. `text` stays the
default. There is no HTML report. Markdown, text, and JSON field coverage
is documented on `vg report`.

- **`vg sbom export` merges multi-project lockfiles by ecosystem, name, and
version.** The same package from two projects stays one component, and
`vibgrate:projects` lists every project that contributed it. A sub-project
Expand Down
125 changes: 120 additions & 5 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ For a quick overview, see the [README](./README.md). This document covers everyt
- [vg fix](#vg-fix)
- [vg init](#vg-init)
- [vg report](#vg-report)
- [Report format coverage](#report-format-coverage)
- [vg review](#vg-review)
- [vg sbom](#vg-sbom)
- [Choosing CycloneDX or SPDX](#choosing-cyclonedx-or-spdx)
Expand Down Expand Up @@ -458,16 +459,130 @@ Creates:

### vg report

Generate a human-readable report from a scan artifact.
Render a saved scan artifact. There is no HTML report. The formats are `md`,
`text`, and `json`. `text` is the default. `json` prints the artifact unchanged
and is the only stable machine-readable contract. `sarif` is not a `vg report`
format; write that with `vg scan --format sarif`.

```bash
vg report [--in <file>] [--format md|text|json]
```

| Flag | Default | Description |
| ---------- | ---------------------------- | -------------------------------------- |
| `--in` | `.vibgrate/scan_result.json` | Input artifact file |
| `--format` | `text` | Output format: `md`, `text`, or `json` |
| Flag | Default | Description |
| ---------- | ---------------------------- | --------------------------------------------------------------------------- |
| `--in` | `.vibgrate/scan_result.json` | Input artifact file |
| `--format` | `text` | `md`, `text`, or `json`. Any other value is a usage error (exit `5`) |

An unknown value is rejected before the file is read. The process exits `5`,
writes nothing to stdout, and prints one stderr line that names the value and
lists the valid ones:

```text
error: unknown --format "html" (expected md, text, json)
```

The comparison is exact. `HTML`, `sarif`, and `md ` are unknown. Omitting
`--format` stays on `text`.

#### Report format coverage

`--format md` is `formatMarkdown` (`src/reporting/formatters/markdown.ts`).
`--format text` is `formatText` (`src/reporting/formatters/text.ts`). Both read
the same artifact. A section below is omitted when its guard is false. Lists
follow artifact order except where a sort is named. Colours in the text report
are terminal styling; under `NO_COLOR` the same words are plain. A null score
is `n/a` in both formats. A measured `0` stays `0`.

The same artifact, three times:

```bash
vg report --in .vibgrate/scan_result.json --format md
vg report --in .vibgrate/scan_result.json --format text
vg report --in .vibgrate/scan_result.json --format json
```

##### Markdown (`formatMarkdown`)

Always:

| Section | Fields |
| --- | --- |
| Title | `# Vibgrate Drift Report` |
| Metrics table | **DriftScore** — `n/a`, or `{drift.score}/100` plus `_(lower is better; 0 = no drift)_`. **Risk Level** — `drift.riskLevel` uppercased, or `n/a` when it is missing. **Projects** — `projects.length`. **Scanned** — `timestamp`; when set, `durationMs` as seconds with one decimal, `filesScanned` as `{n} files`, and `treeSummary` as `{totalFiles} workspace files · {totalDirs} dirs`. **VCS** — `vcs.type`, only when `vcs` is set. **Branch** — `vcs.branch`, when set. **Commit** — `` `vcs.shortSha` ``, only when `vcs.sha` is set |
| Score Breakdown | `drift.components.runtimeScore`, `frameworkScore`, `dependencyScore`, `eolScore` |
| Projects | One `### {name} ({type})` per project. **Runtime**, when `runtime` is set: the runtime, then ` — {runtimeMajorsBehind} major(s) behind` when that count is greater than 0, otherwise ` — current`. **Frameworks**, when the list is non-empty: `name`, `currentVersion` or `?`, `latestVersion` or `?`, and `majorsBehind` as `current` (`0`), `{n} behind`, or `unknown` (`null`). **Dependencies**, when `current + oneBehind + twoPlusBehind + unknown` is greater than 0: those four `dependencyAgeBuckets` counts |

When the artifact carries the data:

| Section | Guard and fields |
| --- | --- |
| Product Purpose Signals | `extended.uiPurpose`. **Frameworks** — `detectedFrameworks`, or `unknown`. **Evidence Items** — `topEvidence.length`, plus `(capped from {evidenceCount})` when `capped` is set. **Top Evidence** — the first 10 of `topEvidence`: `kind`, `value`, `file`. **Unknowns** — the first 5 of `unknownSignals` |
| Recommended Standards | `extended.standards.recommended` is non-empty. **Detected purpose** — `projectPurposes` as `{project} → {category}`, when that list is non-empty. **Compliance framework coverage** — `frameworks`: `name`, `recommendedMembers`, `totalMembers`. **Top standards to consider** — the first 10 of `recommended`: `name`, `reason`, and `_(compliance)_` when `complianceRelevant` is set |
| Findings | `findings` is non-empty. Table of `level` (error, warning, or anything else), `ruleId`, the stored `message`, and `location` |
| Drift Delta | `delta` is set. The signed number, `vs baseline`, and `_(worsened)_` when positive, `_(improved)_` when negative, or neither when zero |
| Baseline suppressions | `baselineComparison.compared` is true. `{n} finding suppressed by baseline` or `{n} findings suppressed by baseline`, where `n` is `baselineComparison.suppressed.length` |

##### Text (`formatText`)

Always, including an artifact with no projects:

| Section | Fields |
| --- | --- |
| Banner | The word `vibgrate` and `Code Intelligence Engine v` plus the running CLI version. This version is not a field of the artifact |
| Title | `Vibgrate Drift Report` |
| DriftScore Summary | **DriftScore** — `n/a` or `{drift.score}/100`. **Risk Level** — `LOW`, `MODERATE`, or `HIGH` for `low`, `moderate`, and `high`; the raw string for any other non-empty level; `n/a` when missing. **Projects** — `projects.length`. **VCS**, when `vcs` is set — `vcs.type`, then `vcs.branch` and `vcs.shortSha` when those are set |
| Score Breakdown | The same four component scores as Markdown, each drawn as a bar when measured |
| Footer | `Scanned at {timestamp}`; when set, `durationMs` as seconds with one decimal, `{n} file scanned` or `{n} files scanned`, `{treeSummary.totalFiles} workspace files`, and `{treeSummary.totalDirs} dirs` |

Per project, in artifact order:

| Line | Guard and fields |
| --- | --- |
| Heading | `name`, `type`, `path` |
| Runtime | `runtime` when set, and `runtimeMajorsBehind` as `{n} major` or `majors behind` when greater than 0, otherwise `current` |
| Target | `targetFramework` when set |
| Frameworks | Same version fields as Markdown (`name`, `currentVersion`, `latestVersion`, `majorsBehind`) |
| Dependencies | The four `dependencyAgeBuckets` counts when their sum is greater than 0 |

When set:

| Section | Guard and fields |
| --- | --- |
| Drift Delta | `delta` is set. The signed number and `(vs baseline)`. Zero prints `0`. No worsened or improved word |
| Baseline suppressions | Same sentence as Markdown, when `baselineComparison.compared` is true |
| Tech Stack | `extended.toolingInventory` has a category with items. Category label, then each item's `name`. Labels: Frontend, Meta-frameworks, Bundlers, CSS / UI, Backend, ORM / Database, Testing, Lint & Format, API & Messaging, Observability, Payment, Auth, Email, Cloud, Databases, Messaging, CRM & Comms, Storage, Search & AI. An unlisted category key is printed as stored |
| Services & Integrations | `extended.serviceDependencies`, same category labels. Each item's `name`, and `version` when set |
| Breaking Change Exposure | `extended.breakingChangeExposure` has a deprecated package or a legacy polyfill. `exposureScore`, `deprecatedPackages`, `legacyPolyfills`, a peer-conflict line when `peerConflictsDetected` is set, `overallRecommendation`, and up to 3 `projectIntelligence` rows that have packages (project `project` and `recommendation`; up to 2 packages: `package`, `currentVersion`, `targetVersion`, `usage.touchedPercent`, `automatable`) |
| TypeScript | `extended.tsModernity.typescriptVersion` is set. The version, `strict` (`strict` is omitted when it is neither true nor false), `moduleType`, and `target` |
| Build & Deploy | `extended.buildDeploy` has CI entries, a Dockerfile, or a package manager. `ci`, Docker `dockerfileCount` and `baseImages`, `packageManagers`, `monorepoTools`, `iac` — each line only when that list or count is non-empty |
| Product Purpose Signals | `extended.uiPurpose`. `detectedFrameworks` or `unknown`. Evidence count is `topEvidence.length`, and `of {evidenceCount} (capped)` when `capped` is set. **Top Signals** — the first 8 of `topEvidence`: `kind`, `value`, `file`. **Unknowns** — the first 4 of `unknownSignals` |
| Security Posture | `extended.securityPosture`. `lockfilePresent`, `gitignoreCoversEnv`, `gitignoreCoversNodeModules`. `multipleLockfileTypes` and `envFilesTracked` only when true |
| Platform | `extended.platformMatrix` has a native module or a Docker base image. `nativeModules` when non-empty. `osAssumptions` when non-empty. `dockerBaseImages` only decides whether the section exists; it is not its own line |
| Code Quality | `extended.codeQuality`. `filesAnalyzed`, `functionsAnalyzed`, `avgCyclomaticComplexity`, `avgFunctionLength`, `maxNestingDepth`, `circularDependencies`, `deadCodePercent`. **God files** — the first 3 of `godFiles`: `path`, `lines` |
| Database Schema | `extended.databaseSchema`. `providers`, model count, enum count. **Sources** when models use more than one `source`, sorted by source name. **Models** — the first 5 `name`s, then a remaining count |
| Dependency Graph | `extended.dependencyGraph.lockfileType` is set. `lockfileType`, `totalUnique`, `totalInstalled`. Duplicated count when `duplicatedPackages` is non-empty. Phantom count when `phantomDependencies` is non-empty |
| Findings | `findings` is non-empty. Counts of `error`, `warning`, and `note`. Each row is the message from `humanFindingText`, then `ruleId` and `location`, then a hint when that helper returns one. The helper drops a stored ` — no fix available` and adds `fix available (...)` from `details.fixedVersions` only when the message does not already contain `fix available` |
| Top Priority Actions | Up to five derived lines, highest severity first. Each line has a title and an explanation; **Impact** is printed only for the runtime, framework, and dependency-rot actions. The triggers are: a runtime `runtimeMajorsBehind` of 3 or more; a framework `majorsBehind` of 3 or more; a project whose `twoPlusBehind / (current + oneBehind + twoPlusBehind)` is at least 40 percent; a framework exactly 2 majors behind; any deprecated package or legacy polyfill on `breakingChangeExposure`; 10 or more `phantomDependencies`; `securityPosture.envFilesTracked` or a missing lockfile; 3 or more `duplicatedPackages` that each have 3 or more versions. Explanations quote project `path`, runtime `runtime` / `runtimeLatest`, framework versions, dependency `package` / `resolvedVersion` or `currentSpec` / `latestStable` / `majorsBehind`, and the matching posture or graph fields |
| Architecture Layers | `extended.architecture`. `archetype`, `archetypeConfidence`, `totalClassified`, `unclassified`. **Folders** — the first 12 of `folders`: `path`, `layer`, `confidence`, `fileCount`. **Coverage**, when set — `ratio`, `classified`, `unclassified`, and `bySource` counts sorted by count descending then source name. **Unclassified source** — `unclassifiedFiles.length` when that list is non-empty. **Unclassified by folder** — the first 8 of `unclassifiedFolders`: `count`, `path`. **Layers** with `fileCount` greater than 0: `layer`, `fileCount`, `driftScore`, `riskLevel`. **Boundary violations** — violations whose `rule` does not start with `graph-conflict:`, first 8: `fromFile`, `toFile`, `rule`. **Layer conflicts** — the `graph-conflict:` violations, first 8, same three fields. A capped violation list is marked when `violationsCapped` is set |
| Solution Drift Summary | `solutions` is non-empty. `name`, `projectPaths.length`, and `drift.score` or `n/a` |

`relationshipDiagram` is not rendered. The text formatter leaves that diagram in the JSON artifact.

##### Differences

- Markdown is a short table. Text is the terminal report: banner and CLI version, project `path` and `targetFramework`, the extended sections above, findings with a fix hint, derived priority actions, architecture, solutions, score bars, and the scanned-at footer.
- Markdown prints **Recommended Standards**. Text does not.
- Text prints Tech Stack, Services, Breaking Change Exposure, TypeScript, Build & Deploy, Security Posture, Platform, Code Quality, Database Schema, and Dependency Graph. Markdown does not.
- Product purpose: Markdown shows 10 evidence rows and 5 unknowns (`Evidence Items`, `Top Evidence`). Text shows 8 and 4 (`Evidence`, `Top Signals`).
- Findings: Markdown prints the stored `message` in a table. Text prints counts plus `humanFindingText` (the stored message, a removed ` — no fix available`, and the fix hint).
- Drift delta: Markdown adds `_(worsened)_` or `_(improved)_`. Text prints the signed number only.
- DriftScore: Markdown puts the lower-is-better gloss in the opening cell. Text puts the score in **DriftScore Summary** without that gloss.
- VCS: Markdown uses separate VCS, Branch, and Commit rows, and Commit requires `vcs.sha`. Text uses one line and can show `shortSha` without requiring `sha`.
- Runtime wording: Markdown always says `major(s)`. Text says `major` or `majors`.

##### JSON

`--format json` prints `JSON.stringify` of the loaded artifact with a two-space indent. Keys, values, and omissions are unchanged. Parse this when a script needs a field. Markdown and text drop fields, cap lists, and add derived lines; they are not a stable contract.

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ All HCS computation runs in an optional, separately-licensed engine module that
| `vg evidence` | Signed, reproducible regulatory evidence — jurisdiction-neutral regimes (EU CRA first, DORA incident reporting too): `init`, `product`, `release`, `exposure`, `readiness`, `drill`, `watch`, `pack`, `verify`, `push`, `export` |
| `vg fix` | Ranked, risk-tiered upgrade plans from the hosted planner — then apply the one you choose |
| `vg init [path]` | Initialise config and `.vibgrate/` |
| `vg report` | Generate a report from a scan artifact |
| `vg report` | Render a saved scan artifact as text (default), Markdown, or JSON. There is no HTML report. Field coverage is in [DOCS.md](./DOCS.md#report-format-coverage) |
| `vg review` | **Vibgrate Review** — architecture + security-control review of the current change, locally (`--in-place`, `--local`, `--loop`). Deterministic blast-radius findings from the code graph via `vg review findings-from-diff`; `vg review propose <id>` attaches a PatchIR dry-run (same `--base` / `--in-place` / `--diff`, `--findings`, or `.vibgrate/review-propose-handoff.json`). One decision (`pass` / `needs_review` / `fail` / `undetermined`) in a signed receipt (Ed25519 over the receipt digest; `vg review verify <receipt.json>` checks it offline); protected findings cannot be blessed into a pass. Reports change integrity, not a proof of security. Builds or refreshes the code map itself when it is missing or stale (`--no-auto-build` opts out) |
| `vg sbom export` / `delta` / `vex` | Export CycloneDX/SPDX SBOM, diff two artifacts, or emit an OpenVEX document |
| `vg scan [path]` | Scan for upgrade drift |
Expand Down
7 changes: 4 additions & 3 deletions plugins/claude/vg/skills/vg/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: vg
description: Query the local code graph (vg) for structure, impact, and navigation instead of grepping/reading many files.
---

<!-- vg:v3 · managed by `vg install` — auto-refreshed when these instructions evolve; remove this line to opt out -->
<!-- vg:v4 · managed by `vg install` — auto-refreshed when these instructions evolve; remove this line to opt out -->

# vg — the code map

Expand Down Expand Up @@ -37,8 +37,9 @@ measured and the tools improved):
## Dependencies & library docs

- **Upgrade drift:** `vg drift` lists what is outdated across dependencies
(offline; `--online` for currency). `vg scan` scores upgrade drift and
`vg report` renders it (text | json | sarif | md).
(offline; `--online` for currency). `vg scan` scores upgrade drift
(`text`, `json`, `sarif`, or `md`). `vg report` renders a saved scan
artifact as `text` (the default), `md`, or `json`. There is no HTML report.
- **Version-correct docs:** `vg lib <name>` returns drift-annotated, version-
specific usage docs for a library — inject these instead of guessing an API.

Expand Down
7 changes: 4 additions & 3 deletions skills/vg/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: vg
description: Query the local code graph (vg) for structure, impact, and navigation instead of grepping/reading many files.
---

<!-- vg:v3 · managed by `vg install` — auto-refreshed when these instructions evolve; remove this line to opt out -->
<!-- vg:v4 · managed by `vg install` — auto-refreshed when these instructions evolve; remove this line to opt out -->

# vg — the code map

Expand Down Expand Up @@ -37,8 +37,9 @@ measured and the tools improved):
## Dependencies & library docs

- **Upgrade drift:** `vg drift` lists what is outdated across dependencies
(offline; `--online` for currency). `vg scan` scores upgrade drift and
`vg report` renders it (text | json | sarif | md).
(offline; `--online` for currency). `vg scan` scores upgrade drift
(`text`, `json`, `sarif`, or `md`). `vg report` renders a saved scan
artifact as `text` (the default), `md`, or `json`. There is no HTML report.
- **Version-correct docs:** `vg lib <name>` returns drift-annotated, version-
specific usage docs for a library — inject these instead of guessing an API.

Expand Down
11 changes: 7 additions & 4 deletions src/install/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ export const NUDGE_END = '<!-- vg:end -->';
* History: v1 = the unversioned originals (detected by their headings);
* v2 = versioned markers + the strong MCP-first recommendation;
* v3 = library-docs workflow resolves by package name only, and an
* `entityCount` of 0 means read `node_modules` rather than retry.
* `entityCount` of 0 means read `node_modules` rather than retry;
* v4 = `vg report` formats are text (default), md, and json. There is no
* HTML report, and sarif belongs to `vg scan`.
*/
export const INSTALL_CONTENT_VERSION = 3;
export const INSTALL_CONTENT_VERSION = 4;

const VERSION_MARKER_RE = /<!--\s*vg:v(\d+)\b[^>]*-->/;

Expand Down Expand Up @@ -90,8 +92,9 @@ measured and the tools improved):
## Dependencies & library docs

- **Upgrade drift:** \`vg drift\` lists what is outdated across dependencies
(offline; \`--online\` for currency). \`vg scan\` scores upgrade drift and
\`vg report\` renders it (text | json | sarif | md).
(offline; \`--online\` for currency). \`vg scan\` scores upgrade drift
(\`text\`, \`json\`, \`sarif\`, or \`md\`). \`vg report\` renders a saved scan
artifact as \`text\` (the default), \`md\`, or \`json\`. There is no HTML report.
- **Version-correct docs:** \`vg lib <name>\` returns drift-annotated, version-
specific usage docs for a library — inject these instead of guessing an API.

Expand Down
Loading
Loading