Skip to content

JSON output (--json)

Machine-readable output for scripts and CI. Each command writes exactly one JSON object to stdout.

bash
nodehunter scan . --json
nodehunter size ~/work --json
nodehunter list --json
nodehunter find react --json
nodehunter why react --json
nodehunter inspect --json
nodehunter delete . --json --yes

Human-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

json
{
  "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

FieldMeaning
okWhether the command completed successfully
commandCanonical command id (scan, size, list, find, why, inspect, delete)
meta.apiVersionSDK semver backing the JSON contract
meta.schemaVersionEnvelope shape version (integer)
meta.cwdHost working directory when the command ran
reportDiscovery/analysis facts (absent if the command failed before analysis)
report.versionProjectAnalysisReport schema version
report.countsWalker counters — source of truth for matched
report.rollupsDerived helpers (largest, totalHuman) on sized runs
summary.timings.wallMsTotal wall-clock time for the command
summary.timings.analysisCore walk/size/total phase timings (when available)
summary.labelHuman label when different from command (e.g. delete (refused))
summary.countsHost action counts only (deleted, …) — not discovery duplicates
summary.notesNon-fatal host messages (declined confirm, refused delete, …)
summary.tipsContextual guidance (level + text) — same content as human [tip] / [notice] channels
workspaceWorkspace query payload on list, find, why, inspect (see below)

Commands

CommandreportworkspaceNotes
scanDiscovery onlytotalSizeBytes is null
sizeDiscovery + per-match sizesIncludes rollups and stats; human lists capped unless --full
listDiscovery (physical sizes for projects)List payloadProjects: physical total; --package: per-install sizeBytes (attributed)
findDiscoveryFind payloadPer-install sizeBytes on aggregate (attributed)
whyDiscoveryWhy payloadPackage footprint with attributed installation sizes
inspectDiscoveryInspect payloadFocus-specific analysis (--focus duplicates today)
deleteDiscovery previewsummary.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):

json
{
  "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>:

json
{
  "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>:

json
{
  "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>:

json
{
  "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.

json
{
  "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:

json
{
  "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

bash
nodehunter scan . --json --json-pretty false   # compact (default when pretty unset)
nodehunter scan . --json --json-pretty true    # indented

Versioning

  • Bump meta.schemaVersion when the top-level envelope changes (currently 3 — adds optional workspace).
  • Bump report.version when analysis fields change.
  • Check release notes before upgrading SDK consumers.