JSON output (--json)
Machine-readable output for scripts and CI. Each command writes exactly one JSON object to stdout.
nodehunter scan . --json
nodehunter size ~/work --json
nodehunter list --json
nodehunter find react --json
nodehunter why react --json
nodehunter inspect --json
nodehunter delete . --json --yesHuman-oriented logs ([info], [walk], banners) go to stderr and are fully suppressed with -j / --json or -s / --silent. Use the envelope (ok, summary.notes) and exit code instead.
Envelope shape
{
"ok": true,
"command": "size",
"meta": {
"apiVersion": "0.0.1",
"schemaVersion": 3,
"cwd": "/home/you/project"
},
"report": {
"version": 1,
"scannedAt": "2026-06-17T12:00:00.000Z",
"root": "/home/you/project",
"counts": {
"matched": 2,
"ignored": 1,
"symlinksSkipped": 0
},
"totalSizeBytes": 99550745,
"matches": [
{
"path": "/home/you/project/node_modules",
"depth": 1,
"sizeBytes": 99550732,
"sizeProvider": "du"
}
],
"meta": {
"sizeProvider": "du",
"timings": { "walkMs": 20, "sizeMs": 500 }
},
"rollups": {
"largest": { "path": "…", "sizeBytes": 99550732 },
"totalHuman": "94.9 MB"
}
},
"summary": {
"timings": {
"wallMs": 530,
"analysis": { "walkMs": 20, "sizeMs": 500 }
},
"tips": [
{ "level": "tip", "text": "Largest project: `/home/you/Projects/foo` — run `nodehunter delete \"/home/you/Projects/foo\"` to reclaim space there." }
]
}
}Field guide
| Field | Meaning |
|---|---|
ok | Whether the command completed successfully |
command | Canonical command id (scan, size, list, find, why, inspect, delete) |
meta.apiVersion | SDK semver backing the JSON contract |
meta.schemaVersion | Envelope shape version (integer) |
meta.cwd | Host working directory when the command ran |
report | Discovery/analysis facts (absent if the command failed before analysis) |
report.version | ProjectAnalysisReport schema version |
report.counts | Walker counters — source of truth for matched |
report.rollups | Derived helpers (largest, totalHuman) on sized runs |
summary.timings.wallMs | Total wall-clock time for the command |
summary.timings.analysis | Core walk/size/total phase timings (when available) |
summary.label | Human label when different from command (e.g. delete (refused)) |
summary.counts | Host action counts only (deleted, …) — not discovery duplicates |
summary.notes | Non-fatal host messages (declined confirm, refused delete, …) |
summary.tips | Contextual guidance (level + text) — same content as human [tip] / [notice] channels |
workspace | Workspace query payload on list, find, why, inspect (see below) |
Commands
| Command | report | workspace | Notes |
|---|---|---|---|
scan | Discovery only | — | totalSizeBytes is null |
size | Discovery + per-match sizes | — | Includes rollups and stats; human lists capped unless --full |
list | Discovery (physical sizes for projects) | List payload | Projects: physical total; --package: per-install sizeBytes (attributed) |
find | Discovery | Find payload | Per-install sizeBytes on aggregate (attributed) |
why | Discovery | Why payload | Package footprint with attributed installation sizes |
inspect | Discovery | Inspect payload | Focus-specific analysis (--focus duplicates today) |
delete | Discovery preview | — | summary.counts.deleted when deletion runs |
workspace block (list, find, why, inspect)
list, find, why, and inspect include a workspace object alongside report (when discovery succeeded):
list — projects (default):
{
"ok": true,
"command": "list",
"meta": { "apiVersion": "0.0.1", "schemaVersion": 3, "cwd": "…" },
"report": { "…": "ProjectAnalysisReport" },
"workspace": {
"version": 1,
"mode": "projects",
"items": [
{
"projectPath": "/home/you/project",
"nodeModulesPath": "/home/you/project/node_modules",
"sizeBytes": 99550732
}
]
},
"summary": { "timings": { "wallMs": 120 } }
}list --package <name>:
{
"workspace": {
"version": 1,
"mode": "packages",
"filter": { "name": "react" },
"items": [
{
"name": "react",
"version": "18.2.0",
"path": "/home/you/project/node_modules/react",
"projectPath": "/home/you/project",
"sizeBytes": 442368
}
]
}
}find <package>:
{
"workspace": {
"version": 1,
"name": "react",
"found": true,
"aggregate": {
"name": "react",
"occurrences": 2,
"versions": ["18.2.0", "18.3.0"],
"totalSizeBytes": 884736,
"installations": [ { "name": "react", "version": "18.2.0", "path": "…", "projectPath": "…", "sizeBytes": 442368 } ]
}
}
}When no installs match, find sets "found": false and omits aggregate.
why <package>:
{
"workspace": {
"version": 1,
"name": "react",
"found": true,
"explain": {
"name": "react",
"projects": [{ "projectPath": "/home/you/project", "nodeModulesPath": "…" }],
"versions": ["18.2.0", "18.3.0"],
"installations": [
{ "name": "react", "version": "18.2.0", "path": "…", "projectPath": "…", "sizeBytes": 442368 }
],
"totalSizeBytes": 884736
}
}
}When no installs match, why sets "found": false and omits explain.
inspect --focus duplicates:
Registered presets: duplicates, versions, largest, frameworks, drift, risks — all available today.
{
"workspace": {
"version": 1,
"focus": "duplicates",
"entries": [
{
"name": "react",
"versions": ["18.2.0", "18.3.0"],
"occurrences": 2,
"installations": [ { "name": "react", "version": "18.2.0", "path": "…", "projectPath": "…" } ]
}
]
}
}Per-installation sizeBytes and aggregate totalSizeBytes are attributed (SizeProvider on each install path). They are not guaranteed to equal physical node_modules totals from size — especially on pnpm/npm layouts.
size stats block
Every sized size run includes a stats aggregate alongside the full report:
{
"ok": true,
"command": "size",
"meta": { "apiVersion": "0.0.1", "schemaVersion": 3, "cwd": "…" },
"report": { "…": "same ProjectAnalysisReport as a normal size run" },
"stats": {
"version": 1,
"matched": 26,
"totalSizeBytes": 1234567890,
"totalHuman": "1.1 GB",
"averageSizeBytes": 47483380,
"averageHuman": "45.3 MB",
"top": [
{
"path": "/home/you/Projects/foo/node_modules",
"projectPath": "/home/you/Projects/foo",
"sizeBytes": 500000000,
"sizeHuman": "476.8 MB"
}
]
},
"summary": { "timings": { "wallMs": 2800 } }
}Pretty printing
nodehunter scan . --json --json-pretty false # compact (default when pretty unset)
nodehunter scan . --json --json-pretty true # indentedVersioning
- Bump
meta.schemaVersionwhen the top-level envelope changes (currently 3 — adds optionalworkspace). - Bump
report.versionwhen analysis fields change. - Check release notes before upgrading SDK consumers.