Output format
Two formats: a human-readable text report (default) and machine-readable JSON (--json). Both emit one logical entry per target. If you parse the output programmatically (CI gate, dashboard, AI agent), prefer --json — the text format is stable but optimized for reading.
All results — text and --json — are written to stdout. Progress and diagnostics go to stderr, so a redirect like dependency-audit . > result.json captures only the result. While auditing, a one-line version banner (dependency-audit vX.Y.Z) followed by a live progress line (current phase, deps materialized) is drawn on stderr, but only when stderr is an interactive terminal — under a pipe, a file, or CI it is silent, leaving stderr empty on a clean run. (For a machine-readable record of the producing version, read the version field of the --json envelope below.)
Text format
Section titled “Text format”The report is a sequence of per-target blocks, then a summary line. Each line is prefixed with two spaces and a status symbol:
| Symbol | Meaning |
|---|---|
✓ | The target is clean — no findings and no notices. |
✗ | A finding (a real problem that fails the audit). |
ℹ | A notice (a coverage gap — no/unreachable type surface; does not fail). |
– | An ignored finding (suppressed by a rule; does not fail). Ends with — ignored. |
? | An unchecked specifier (dynamic/opaque — surfaced, not resolved). |
↷ | A skipped target — a non-package path (does not affect the exit code). |
⚠ | An error — the target could not be audited at all. |
→ | A remediation suggestion (continuation of the preceding ✗/– line). |
↳ | A root-cause note: the finding is caused by another target audited in the run — a continuation under a finding, or (with --collapse-root-cause) a muted, non-failing finding row. |
When stdout is a color-capable terminal, severity is also carried by color — red for findings/errors, yellow for notices/unchecked, green for clean, and muted (dim) for ignored/skipped/secondary detail. Color is auto-disabled when the output isn’t a TTY (e.g. piped to a file or another program), with one exception: under GitHub Actions (GITHUB_ACTIONS=true) color is emitted anyway, since its log viewer renders ANSI even though the step’s stdout is a pipe. NO_COLOR and FORCE_COLOR are honored throughout and override that exception. The symbols above are the source of truth; color is purely a visual aid (and never emitted under --json), so parsers should key on the symbols, not the color.
Per-target block
Section titled “Per-target block”<packageName>@<version> <target> [resolved: <tarball-url>] # only for a fetched spec/URL [integrity: <sri>] # only for a fetched spec/URL <one or more status lines, in this order:> ✓ no undeclared imports # only when fully clean ℹ <surface> <message> # notices ✗ <surface> [<kind>] <specifier> (<file>) # findings → <suggestion> ↳ caused by <producer-target> (<notice>) … # only when correlated to a producer in the run ↳ <surface> [<kind>] <specifier> (<file>) — root cause: <producer> (<notice>) # only under --collapse-root-cause – <surface> [<kind>] <specifier> (<file>) — ignored # ignored findings ? unchecked <specifier> (<reason>; <file>) # unchecked specifiers<packageName>falls back to the target string if the manifest has noname;@<version>is omitted if the manifest has noversion.<surface>istypesorruntime;<kind>is a finding kind;<specifier>is the full bare specifier as written (e.g.react/jsx-runtime), not just the owning package;<file>is the package-relative path where it was first seen.- Columns are space-padded for alignment; treat runs of whitespace as a single separator when parsing.
Error / skip block
Section titled “Error / skip block”<target> ⚠ error <message> # could not be audited<target> ↷ skipped <reason> # a non-package path (neutral)Findings recap
Section titled “Findings recap”When the run has at least one failing finding, a consolidated recap is printed just above the summary line, so the actual problems sit at the foot of a long log (e.g. a CI run) instead of buried in the per-target blocks. Each row names its owning package, so it reads standalone; collapsed findings (under --collapse-root-cause) are omitted, since they don’t fail the run. The recap is omitted entirely on a clean run.
Findings: ✗ <packageName> <surface> [<kind>] <specifier> (<file>)Summary line
Section titled “Summary line”<N> package[s], <F> finding[s][, <K> collapsed][, <G> ignored][, <C> notice[s]][, <S> skipped][, <E> error[s]].The collapsed, ignored, notice, skipped, and error clauses appear only when their count is non-zero. <F> counts findings that fail the run — non-ignored, and (under --collapse-root-cause) non-collapsed.
JSON format (--json)
Section titled “JSON format (--json)”A { tool, version, results } envelope. tool is always "dependency-audit" and version is the producing CLI version — recorded so a saved audit artifact stays reproducible as the resolution behavior evolves. results is a JSON array with one element per target, in input order; each element is an AuditResult, an error entry (could not audit), or a skip entry (a non-package path).
{ "tool": "dependency-audit", "version": "0.2.1", // the producing CLI version "results": [ /* one AuditResult / error entry / skip entry per target */ ],}Error and skip entries
Section titled “Error and skip entries”{ "target": "<target>", "error": "<message>" }{ "target": "<target>", "skipped": "<reason>" }Distinguish the three by key: an error key → could not audit (exit 2); a skipped key → a neutral non-package path; otherwise it is an AuditResult (use ok).
AuditResult
Section titled “AuditResult”{ "target": "./packages/my-lib", // the target as passed in "source": { // how it was acquired "kind": "directory", // "directory" | "tarball" | "spec" "resolved": { // present only for a fetched spec/URL "name": "my-lib", "version": "1.2.3", "tarball": "https://registry.npmjs.org/my-lib/-/my-lib-1.2.3.tgz", "integrity": "sha512-…", }, }, "packageName": "my-lib", // string | undefined (from the manifest) "packageVersion": "1.2.3", // string | undefined "ok": false, // true when findings is empty (notices do not affect ok) "findings": [ { "specifier": "react", // the bare specifier as written "packageName": "react", // the normalized owning package "surface": "types", // "types" | "runtime" "kind": "undeclared", // see Findings & notices // "reason": "condition-mismatch", // only on some `unresolved` runtime findings // "causedBy": { "target": "./packages/producer", "packageName": "@scope/producer", "notice": "types-unreachable" }, // only when correlated to a producer in the run // "leakedVia": ["some-dep"], // declared dep(s) that also expose this type (likely leaked in; types `undeclared` finding) "firstSeenIn": "dist/index.d.ts", // package-relative path "suggestion": "declare \"@types/react\" …", }, ], "ignored": [ /* Finding[] suppressed by an ignore rule, same shape */ ], "usedIgnoreRules": [ { "package": "optional-plugin" }, // the run's ignore rules that suppressed something in this target ], "unchecked": [ { "specifier": "…", "reason": "dynamic specifier", "firstSeenIn": "dist/index.js" }, ], "notices": [{ "kind": "types-not-built", "surface": "types", "message": "…" }], "resolvedDeps": [ { "name": "react", "range": "^18.0.0", "version": "18.3.1" }, // version: string | undefined if it could not be materialized ],}Field reference
Section titled “Field reference”| Field | Type | Notes |
|---|---|---|
target | string | The target as passed on the CLI / to audit(). |
source.kind | "directory" | "tarball" | "spec" | How the target was acquired. |
source.resolved | object | absent | Present only for a fetched spec/URL; records the moving target’s identity + SRI. |
packageName | string | undefined | From the target manifest. |
packageVersion | string | undefined | From the target manifest. |
ok | boolean | true ⇔ findings is empty. Notices and unchecked do not affect ok. |
findings | Finding[] | Non-ignored problems. See Findings. |
ignored | Finding[] | Findings suppressed by a rule, echoed for auditability. |
usedIgnoreRules | IgnoreRule[] | The ignore rules that suppressed at least one finding in this target — a rule unused across every target of a run is stale (the CLI warns; see Stale ignore rules). |
unchecked | UncheckedSpecifier[] | Dynamic/opaque specifiers static analysis could not resolve. |
notices | Notice[] | Coverage gaps. See Notices. |
resolvedDeps | ResolvedDependency[] | Every declared dep and the version materialized (undefined if it could not be fetched/linked). |
Consuming the JSON
Section titled “Consuming the JSON”Iterate payload.results (each entry below is one of its elements); read payload.version if you need the producing tool version.
- Pass/fail per target:
entry.error !== undefined→ could not audit;entry.skipped !== undefined→ a non-package path (neutral, ignore for pass/fail); elseentry.ok→ pass/fail on findings. - Coverage: count
entry.noticesto report “audited N, M with no analyzable type surface”. Treat as failure only if you opt into--require-types(CLI) or checknotices.lengthyourself (API). - Aggregate exit semantics mirror the CLI: any error entry → 2; else any
ok === false→ 1; else 0. Skip entries are neutral. (--collapse-root-causeis a CLI-only exit/presentation flag — it does not changeentry.ok, so under it the CLI can exit 0 while anentry.okisfalse; a JSON consumer that wants the same behavior treats a finding withcausedByas non-failing itself.)