node-modules-hunter 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/cli.js +1219 -1018
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ nmh stats -g # global installs across npm/pnpm/yarn/bun
|
|
|
47
47
|
nmh why <pkg> [dir…] # reverse depend: where is <pkg> and what requires it — read-only
|
|
48
48
|
nmh treemap [dir] # single-file interactive HTML treemap → nmh-treemap.html
|
|
49
49
|
nmh update --check # v1.2: compare installed vs registry — nothing installs
|
|
50
|
+
nmh update --check beta # v1.4: compare against a specific dist-tag (or: --tag beta)
|
|
50
51
|
nmh update --info # v1.2: full registry detail (npm view style)
|
|
51
52
|
nmh update -y # install the newer version without asking (-n declines, --force skips the ask)
|
|
52
53
|
|
|
@@ -92,6 +93,8 @@ Search patterns: plain text is a case-insensitive substring (`nmh search lodash`
|
|
|
92
93
|
| `--reinstall` | With `stats` (focused view): naive npm-ci-style estimate of how long re-installing everything takes (pkg × 60 ms + bytes / 50 MB/s + 3 s per native-family build); assumptions surfaced in the JSON, labeled naif |
|
|
93
94
|
| `--animate` / `--no-animate` | Spinning braille loader while measuring (time-driven — rotates on its own, v1.1.1): auto = real TTY only, `--animate` forces it even when piped; always suppressed under `--json`; `.nmhrc animate:false` mutes it |
|
|
94
95
|
| `--update-check` | Opt-in: peek npm for a newer release (non-blocking, 24 h cache, notice on stderr; also `updateCheck` in `.nmhrc`) |
|
|
96
|
+
| `--tag <name>` | **v1.4** — update only: read a specific npm dist-tag (`nmh update --tag beta`); `--check <tag>` names it inline; unknown tags refuse with the available list, downgrades confirm with NO as the default |
|
|
97
|
+
| `--update-tag <name>` | **v1.4** — config family: persist the default update dist-tag (`updateTag` in `.nmhrc`) — what `nmh update` / `--check` read when no `--tag` is given |
|
|
95
98
|
| `--config <file>` | Use this config file instead of the `.nmhrc` lookup (cwd → `$HOME`) |
|
|
96
99
|
| `--language <lang>` | **v1.3** — UI language: `en` \| `id` \| `zh` \| `ru` \| `es`; also via `.nmhrc` `language` and `NMH_LANG` (see [UI language](#ui-language-v130--i18n)) |
|
|
97
100
|
| `--json` | Machine-readable output (pipe into `jq`) |
|
|
@@ -234,7 +237,7 @@ Only bare package specifiers count (`node:fs`, `./util`, `#alias` skipped; subpa
|
|
|
234
237
|
|
|
235
238
|
```bash
|
|
236
239
|
$ nmh stats ./my-repo --update-check # opt-in npm peek, 24 h cache
|
|
237
|
-
ℹ update
|
|
240
|
+
ℹ update available: 0.4.0 → 0.5.0 — npm i -g node-modules-hunter (…)
|
|
238
241
|
```
|
|
239
242
|
|
|
240
243
|
### Vision & hygiene extras (v0.5)
|
|
@@ -359,6 +362,7 @@ The classic question — *which of these `node_modules` can I actually kill?*
|
|
|
359
362
|
- **`--fast-size`** swaps the byte-exact walker for one `du -sk` per entry (KiB granularity — sizes are always multiples of 1024); anything `du` can't measure falls back to the exact walker silently. Scan-only knob: delete runs always measure exactly so the freed-bytes number stays honest. Windows has no `du`, so it always walks.
|
|
360
363
|
- **Default skip-list flip**: discovery now hides name-based system noise (`.Trash`, `.Trashes`, `.Trash-<uid>`, `Library`, `*.app`) wherever it sits in the tree; pass **`--no-skip`** to restore the bare full-tree v1.0 audit. `.git` stays skipped always (even with `--no-skip`), and direct targets are never filtered — the skip only shapes what discovery finds.
|
|
361
364
|
- **`nmh update` is consented (v1.2)**: the plain run shows the facts and asks before installing (`@clack` confirm on a TTY; a piped/`--json` run needs `-y` to install or `-n` to decline — without an answer it refuses with exit `1`, the same protocol as deletes). `--check` is the compact compare for scripts/CI (exit 0 whether or not an update exists, never installs), `--info` is the full `npm view`-style read-out (description, tarball + size + file count, engines, license, publish time), and the two are mutually exclusive. `--force` skips the ask when an update exists — an already-latest install is checked and reported, never blindly reinstalled. Non-npm-global installs keep the honest how-to note. `--json` payload: `{ current, latest, updated, method }` frozen + additive `{ updateAvailable, detail? }`.
|
|
365
|
+
- **Dist-tag support (v1.4)**: `--tag <name>` (or `--check <tag>`, or the persisted `.nmhrc` `updateTag` key via `nmh config set --update-tag <name>`) reads any published dist-tag instead of `latest`. A tag pointing below the installed version is a **downgrade** — same consent gate as an upgrade, but the TTY confirm defaults to **NO** (overwriting a newer install is never the obvious move); non-TTY still needs `-y`. Unknown tags refuse loudly with the available list (`--tag nope` → exit 1), malformed names are validated against the npm dist-tag shape. `--info` lists every published tag (`dist-tags latest=1.4.0, beta=1.5.0-beta.1`). `--json` payload stays additive: `{ current, latest, updated, method, updateAvailable, detail? }` + new `{ tag, target, direction: 'upgrade'|'downgrade'|'current', distTags? }`. The wizard menu gains a **🔁 Update nmh** entry, and the wizard boot now peeks the registry by default (24 h cache, `.nmhrc` `updateCheck: false` opts out).
|
|
362
366
|
- **`--json` stays additive**: new optional keys `idleDays`, `activitySource`, `backend`, `trashBatch`, `trashFallback` appear only when a v1.1 feature is active; every v1.0 payload key is unchanged (the frozen v1.0 contract, extended minor-version-safely).
|
|
363
367
|
|
|
364
368
|
## Exit codes
|
|
@@ -417,6 +421,7 @@ For settings you always repeat, drop a `.nmhrc` (or `.nmhrc.json`) next to your
|
|
|
417
421
|
"animate": false,
|
|
418
422
|
"updateCheck": true,
|
|
419
423
|
"language": "en",
|
|
424
|
+
"updateTag": "latest",
|
|
420
425
|
"logDir": "~/.cache/nmh-logs"
|
|
421
426
|
}
|
|
422
427
|
```
|
|
@@ -438,7 +443,7 @@ Hand-editing JSON is optional now:
|
|
|
438
443
|
| `nmh config remove …` | Drop listed items; the **bare flag clears the whole list** |
|
|
439
444
|
| `nmh config unset …` | Delete keys from the file (back to defaults) |
|
|
440
445
|
|
|
441
|
-
List keys use self-describing flag names: `--default-exclude` (→ the `exclude` key) and `--default-keep` (→ `keep`). Scalars keep their `.nmhrc` names: `--depth`, `--default-mode`, `--log-dir`, `--animate`, `--color`, `--update-check` (negations like `--no-animate` work).
|
|
446
|
+
List keys use self-describing flag names: `--default-exclude` (→ the `exclude` key) and `--default-keep` (→ `keep`). Scalars keep their `.nmhrc` names: `--depth`, `--default-mode`, `--log-dir`, `--animate`, `--color`, `--update-check`, `--update-tag` (v1.4 — the `updateTag` dist-tag, validated against the npm tag-name shape) (negations like `--no-animate` work).
|
|
442
447
|
|
|
443
448
|
```bash
|
|
444
449
|
nmh config add --default-exclude=.npm,.nvm,self # the classic homedir silencer
|
|
@@ -470,10 +475,11 @@ NMH_LANG=ru nmh scan # 3. the environment (NMH_LANG, then LC_ALL/LAN
|
|
|
470
475
|
- Output that exists before the language is known (the very first help/version paint, config-load errors) renders English by design; everything after the config read is localized.
|
|
471
476
|
- The `--json` payloads and the exit-code table are **language-independent contracts** — keys and codes never change with the locale.
|
|
472
477
|
- Grammar of the CLI itself is universal: command names, flags, manifest field names (`dependencies`/`devDependencies`), and technical labels (`runtime`/`system`/`io`) stay as-is in every locale.
|
|
478
|
+
- **v1.3.1**: `zh` and `ru` carry their real original translations (the v1.3.0 release shipped them as English placeholders while the 1:1 catalog gate kept the key sets aligned), and `nmh help` now documents the `--language <lang>` flag itself — going forward, **every new flag, command or feature must appear in the help manual in the same release**.
|
|
473
479
|
|
|
474
480
|
## Roadmap
|
|
475
481
|
|
|
476
|
-
v0.1 shipped the MVP (scan · size · delete · wizard · guards · PM detection). v0.2 added **search, map, `--depend`, `-r` recursive audit and the `--pm` / `--exclude` / `--keep` / `--depth` / `--expand` filters**. v0.3 added the **`stats` suite (dup factor, waste, top-N, per-package tree), `stats -g` global analysis across PMs, report export (`--save-report`/`--report`), `--animate` and the `.nmhrc` config file**. v0.4 added the **analytics deep-cut: baselines (`--save-baseline`/`--compare`/`--history`), dedup suggestions with per-copy declared ranges, `nmh why`, the real `--by type` breakdown, the `--phantom`/`--dead` best-effort import audit and the opt-in `--update-check`**. v0.5 added the **vision & hygiene extras: license scan (`--licenses`/`--by licenses` with the copyleft-in-prod ⚠), the experimental Bloat Score (`--score`) and CO2 estimate (`--co2`), the deprecated check (`--online`, offline manifests + lazy cached registry fallback), the `nmh treemap` single-file HTML export and `--save-report` for map/search/stats**. v0.6 added the **lockfile-grade graph: `--graph` parses npm/yarn/pnpm/bun lockfiles (zero new dependencies) into a real dependency graph with a BFS depth histogram, `nmh why` upgrades to lockfile-grade required-by (transitive requirers, no more false "orphans"), and `--reinstall` estimates the npm-ci-style reinstall time**. v0.7 added the **full PRD §7.5 wizard menu (delete all / pick / search / map / detailed report / settings, with loop-backs), a serious banner box (TTY) + compact one-liner (non-TTY, `--json`-clean), and a richer `nmh version` (node + abi + OS/arch/kernel + tty, `--json`)**. The v0.7.1 patch re-scheduled the folder walker to a slot-per-readdir work queue: the old recursive limiter could deadlock (circular wait) on wide bushy trees like a 12-package pnpm workspace — freezing the scan with **zero output and exit code 0 mid-walk**. Trees of that shape are covered by a regression test now. The same patch hardened measurement: children are classified with `fs.lstat` instead of dirent `d_type`, because some filesystems (Android FUSE/sdcardfs) misreport hardlinked pnpm-store files as symlinks — one `typescript/lib` dir had 112 of 125 dirents lying, which made a whole pnpm virtual store measure 650 KB while `du` saw 66 MB — and entry sizes now dedupe hardlinks per entry (`du -sb`-style), keeping walk numbers in line with `du` (91 → 63 MB on the same tree). v0.8 makes the no-arg wizard scope-aware: when the cwd has no node_modules within depth 3, it climbs once to the nearest manifest ancestor (package.json, any PM lockfile, or `pnpm-workspace.yaml`, ≤8 hops — empty manifest dirs are skipped so the monorepo root still wins) and the whole menu runs there; the scope is frozen for the session (rescans never re-climb), a TTY note reveals the lift, non-TTY gets a warn line, and `--json` stays a pure payload. The v0.9 minor introduces **`nmh config` (info · set · add · remove · unset)** — manage the `.nmhrc` from the CLI instead of hand-editing JSON (`--default-exclude`/`--default-keep` list discipline with dedupe and clear, loader-grade validation, unknown-key preservation on write, `$HOME/.nmhrc` default target) — and the **`self` token** for `exclude`/`keep`: a scan-level guard that drops every entry inside the folder nmh runs from, wholesale, whatever target you point at (your own project can never be hunted by accident). v0.9.1 is a **live-audit hardening patch**: a scripted fixture farm × ~140 invoked combination runs surfaced five defects that are now fixed — delete guards additionally refuse a `node_modules` sitting *directly inside* `/` or `$HOME` (previously a `--dir ~ --delete -y` could sweep it away), the wizard's non-TTY fallback scan now honors `exclude`/`keep` from config and flags, a bare `--depth` failure message no longer swallows the value into `true`, the `--depend` guard messages are reconciled as map-only with actionable hints, and a failed `treemap --out` write exits 1 with a friendly 🛑 instead of a raw errno string. Audited-and-confirmed-as-designed along the way: positional targets are never comma-split (only `--dir` is), `--save-report` write failures warn and keep the exit code, `nmh search` matches top-level package names per PRD §7.3, and `nmh why` takes pkg-first arguments. v0.9.2 is an **independent-review hardening patch**: it closes the biggest hole the v0.9.1 audit missed — a TTY `--json --delete` used to execute with **zero confirmation** (machine output has no interactive prompt slot, and the non-TTY guard didn't cover the TTY branch; a pseudo-TTY reproduction proved rc=0 with the victim gone) — now `--json --delete` requires `--yes`/`--force` on every shell, TTY included. The same pass fixed the config family under `--quiet --json` (its payload was the one survivor the sweep missed — printed empty with rc 0), turned a silent bare/multi `--size` into a loud usage error like its sibling guards, and cleaned the last 10 pre-existing `tsc` errors (a leftover `depth` sentinel union) so `npm run typecheck` is finally clean. v0.9.3 is a **no-behavior-change cleanliness patch** from the same review: six dead symbols removed (`measurePackageRefs`, `SizedPackageRef`, `readLicense`, `listHasSelf`, `isVerbose`, `formatCount`), the write-only `MeasureProgress.currentEntry` field dropped, the twin `DepsAudit` interfaces collapsed to one canonical definition (re-exported from `core/result.ts`), the `self` guard moved to its semantic home (`core/guards.ts`), `ConfigError` now carries a machine-readable `code` instead of message string-matching, scan's private report writer now shares `writeReportSnapshot` with map/search/stats, the `analyze`/`info` stats aliases are documented, and `npm run typecheck` stays at zero errors. v0.9.4 is a **hardening patch for the review findings that change data/behavior**: `stripJsonC` is now fully quote-aware (a trailing comma inside a string value is no longer eaten — silent corruption fixed), `makeGlobMatcher` caps pathological patterns (512 chars / 8 `**`) up front, a delete op aimed at `/` refuses before scanning, the deprecated `truncated` flag means "a 201st item was refused" instead of "exactly at the cap", recovery logs get a random suffix (no same-millisecond overwrites), deprecated notes are ANSI-stripped at the source, `nmh why` reads its manifests/locks with bounded concurrency of 4, the `$HOME` guard ignores letter-case on macOS, the `.nmhrc` ancestor trust is documented in the README, and the self-guard-vs-symlinked-cwd claim is verified as a non-issue (Node resolves `process.cwd()` physically). v1.0.0 is the **stable-API release**: every `--json` payload is a frozen contract guarded by the 245-test suite, the exit-code table (0/1/2/3/4) is locked, `npm run typecheck` and `npm audit` are both clean, and the package ships to npm with a `v1.0.0` git tag once the remote is configured. v1.1.0 is the **stale-hunting edition**: `--stale <days>` grades entries by newest-of git commit → manifest mtime → folder mtime and drops no-evidence stragglers, `--fast-size` measures with `du -sk` (exact-walker fallback), results gain an `Idle` column, a proportional Share bar and `--sort size|path|idle|mtime`, discovery hides system noise by default (`--no-skip` restores the bare audit), `nmh update` self-updates npm-global installs, and `--trash=system|nmh` picks the backend with a restorable `~/.nmh/trash` fallback — all with zero new dependencies and additive-only JSON keys, guarded by the 267-test suite. The v1.1.1 patch is a **polish-and-hardening pass driven by three user reports and a full live-exercise matrix** (26 sandbox fixtures × 223 real combinations): the measuring loader is a **time-driven spinner** (braille frames rotate on their own timer at 70 ms instead of per-update repaints, spinning on TTY, silent in pipes unless `--animate on`), `nmh help` renders a **complete manual** (own renderer — 14 commands + 5 stats subcommand views + grouped options + examples + the safety/exit footer, and the previously-missing `-h`/`--help`/`-v`/`--version` aliases finally work instead of slipping into the default scan), and ANSI colors are **TTY-only by design** (picocolors' `!!env.CI` guess had painted unknown escape bytes into piped GitHub Actions output and shredded substring assertions — CI is 9/9 green for the first time; local color comes from a real TTY or explicit `--color`/`FORCE_COLOR`). The same matrix surfaced two defects the suite had missed: an unknown first word (`nmh scanx`) used to fall through to a silent full-cwd scan with exit 0 — any non-path, non-command token is now a loud usage error (and a bare cwd-relative dir like `nmh node_modules` now correctly targets that folder); and `nmh config set` wrote `$HOME/.nmhrc` while scans walked cwd → `/` and never read it when the cwd lived outside the home tree — the home file is now the documented fallback (chain first). Meanwhile `os.homedir()` on Windows reads `USERPROFILE` (test helper pins both), `execFile('npm')` needs `shell: true` on win32 (`stats -g` exited 2 there), and system-trash-dependent flows run hermetically via `nmh trash` fallback. Suite: 44 files / 279 tests green. v1.2.0 is the **consent-and-targeting edition**: `nmh update` stops installing on faith — the plain run shows the facts and asks (`-y`/`-n` on pipes, `--force` to skip the ask, and an already-latest install is never reinstalled), while `--check` and `--info` are read-only registry views (compact compare vs full `npm view` detail, mutually exclusive); and the wizard becomes a first-class command — `nmh wizard --dir=./a,./b` scopes the menu to explicit targets (no auto-climb), `nmh --wizard` is the flag form, and the `--` router (`nmh wizard -- stats --dir=./a`) opens a read-only view through the full dispatcher before the menu; pipes refuse every explicit wizard form with exit 1 and directions while the bare `nmh` fallback scan stays untouched. The update messages were also unified to Indonesian (the smoke matrix caught `--check` speaking English next to an Indonesian default). Suite: 46 files / 295 tests green. The v1.2.1 hotfix repairs what the wild caught the same day: modern npm prints `npm view <pkg> --json` as an **abbreviated packument array** (no top-level `version` key), which made every `nmh update` flavor exit "cannot read the latest version" — version detection is back on the plain-text `npm view <pkg> version` spawn, the `--info` detail is a separate best-effort fetch with shape normalization (array / object / garbage all tolerated — facts minus detail, never a failed run), and the new `NMH_UPDATE_VIEW_JSON` seam keeps both shapes hermetically testable. Suite: 46 files / 296 tests green. v1.3.0 is the **full-i18n edition**: the CLI now speaks five languages — `en` (default & fallback), `id`, `zh`, `ru`, `es` — through a single catalog (`t(key, params)` in `src/i18n/`, `{placeholder}` interpolation, zero hard-coded user-facing strings left in `src/` outside the i18n module, help manual / wizard menus / table headers / guard & delete messages / treemap HTML included). The language resolves as `--language` > `.nmhrc` `language` key > `NMH_LANG` > `LC_ALL`/`LANG` > `en` (unknown flag values refused loudly with exit 1, unknown env values fall back to `en` silently), a CI completeness gate keeps every locale's key set and `{placeholder}` slots 1:1 with the English source, and both frozen contracts — `--json` payload keys and the exit-code table — are language-independent. Suite: 48 files / 315 tests green.
|
|
482
|
+
v0.1 shipped the MVP (scan · size · delete · wizard · guards · PM detection). v0.2 added **search, map, `--depend`, `-r` recursive audit and the `--pm` / `--exclude` / `--keep` / `--depth` / `--expand` filters**. v0.3 added the **`stats` suite (dup factor, waste, top-N, per-package tree), `stats -g` global analysis across PMs, report export (`--save-report`/`--report`), `--animate` and the `.nmhrc` config file**. v0.4 added the **analytics deep-cut: baselines (`--save-baseline`/`--compare`/`--history`), dedup suggestions with per-copy declared ranges, `nmh why`, the real `--by type` breakdown, the `--phantom`/`--dead` best-effort import audit and the opt-in `--update-check`**. v0.5 added the **vision & hygiene extras: license scan (`--licenses`/`--by licenses` with the copyleft-in-prod ⚠), the experimental Bloat Score (`--score`) and CO2 estimate (`--co2`), the deprecated check (`--online`, offline manifests + lazy cached registry fallback), the `nmh treemap` single-file HTML export and `--save-report` for map/search/stats**. v0.6 added the **lockfile-grade graph: `--graph` parses npm/yarn/pnpm/bun lockfiles (zero new dependencies) into a real dependency graph with a BFS depth histogram, `nmh why` upgrades to lockfile-grade required-by (transitive requirers, no more false "orphans"), and `--reinstall` estimates the npm-ci-style reinstall time**. v0.7 added the **full PRD §7.5 wizard menu (delete all / pick / search / map / detailed report / settings, with loop-backs), a serious banner box (TTY) + compact one-liner (non-TTY, `--json`-clean), and a richer `nmh version` (node + abi + OS/arch/kernel + tty, `--json`)**. The v0.7.1 patch re-scheduled the folder walker to a slot-per-readdir work queue: the old recursive limiter could deadlock (circular wait) on wide bushy trees like a 12-package pnpm workspace — freezing the scan with **zero output and exit code 0 mid-walk**. Trees of that shape are covered by a regression test now. The same patch hardened measurement: children are classified with `fs.lstat` instead of dirent `d_type`, because some filesystems (Android FUSE/sdcardfs) misreport hardlinked pnpm-store files as symlinks — one `typescript/lib` dir had 112 of 125 dirents lying, which made a whole pnpm virtual store measure 650 KB while `du` saw 66 MB — and entry sizes now dedupe hardlinks per entry (`du -sb`-style), keeping walk numbers in line with `du` (91 → 63 MB on the same tree). v0.8 makes the no-arg wizard scope-aware: when the cwd has no node_modules within depth 3, it climbs once to the nearest manifest ancestor (package.json, any PM lockfile, or `pnpm-workspace.yaml`, ≤8 hops — empty manifest dirs are skipped so the monorepo root still wins) and the whole menu runs there; the scope is frozen for the session (rescans never re-climb), a TTY note reveals the lift, non-TTY gets a warn line, and `--json` stays a pure payload. The v0.9 minor introduces **`nmh config` (info · set · add · remove · unset)** — manage the `.nmhrc` from the CLI instead of hand-editing JSON (`--default-exclude`/`--default-keep` list discipline with dedupe and clear, loader-grade validation, unknown-key preservation on write, `$HOME/.nmhrc` default target) — and the **`self` token** for `exclude`/`keep`: a scan-level guard that drops every entry inside the folder nmh runs from, wholesale, whatever target you point at (your own project can never be hunted by accident). v0.9.1 is a **live-audit hardening patch**: a scripted fixture farm × ~140 invoked combination runs surfaced five defects that are now fixed — delete guards additionally refuse a `node_modules` sitting *directly inside* `/` or `$HOME` (previously a `--dir ~ --delete -y` could sweep it away), the wizard's non-TTY fallback scan now honors `exclude`/`keep` from config and flags, a bare `--depth` failure message no longer swallows the value into `true`, the `--depend` guard messages are reconciled as map-only with actionable hints, and a failed `treemap --out` write exits 1 with a friendly 🛑 instead of a raw errno string. Audited-and-confirmed-as-designed along the way: positional targets are never comma-split (only `--dir` is), `--save-report` write failures warn and keep the exit code, `nmh search` matches top-level package names per PRD §7.3, and `nmh why` takes pkg-first arguments. v0.9.2 is an **independent-review hardening patch**: it closes the biggest hole the v0.9.1 audit missed — a TTY `--json --delete` used to execute with **zero confirmation** (machine output has no interactive prompt slot, and the non-TTY guard didn't cover the TTY branch; a pseudo-TTY reproduction proved rc=0 with the victim gone) — now `--json --delete` requires `--yes`/`--force` on every shell, TTY included. The same pass fixed the config family under `--quiet --json` (its payload was the one survivor the sweep missed — printed empty with rc 0), turned a silent bare/multi `--size` into a loud usage error like its sibling guards, and cleaned the last 10 pre-existing `tsc` errors (a leftover `depth` sentinel union) so `npm run typecheck` is finally clean. v0.9.3 is a **no-behavior-change cleanliness patch** from the same review: six dead symbols removed (`measurePackageRefs`, `SizedPackageRef`, `readLicense`, `listHasSelf`, `isVerbose`, `formatCount`), the write-only `MeasureProgress.currentEntry` field dropped, the twin `DepsAudit` interfaces collapsed to one canonical definition (re-exported from `core/result.ts`), the `self` guard moved to its semantic home (`core/guards.ts`), `ConfigError` now carries a machine-readable `code` instead of message string-matching, scan's private report writer now shares `writeReportSnapshot` with map/search/stats, the `analyze`/`info` stats aliases are documented, and `npm run typecheck` stays at zero errors. v0.9.4 is a **hardening patch for the review findings that change data/behavior**: `stripJsonC` is now fully quote-aware (a trailing comma inside a string value is no longer eaten — silent corruption fixed), `makeGlobMatcher` caps pathological patterns (512 chars / 8 `**`) up front, a delete op aimed at `/` refuses before scanning, the deprecated `truncated` flag means "a 201st item was refused" instead of "exactly at the cap", recovery logs get a random suffix (no same-millisecond overwrites), deprecated notes are ANSI-stripped at the source, `nmh why` reads its manifests/locks with bounded concurrency of 4, the `$HOME` guard ignores letter-case on macOS, the `.nmhrc` ancestor trust is documented in the README, and the self-guard-vs-symlinked-cwd claim is verified as a non-issue (Node resolves `process.cwd()` physically). v1.0.0 is the **stable-API release**: every `--json` payload is a frozen contract guarded by the 245-test suite, the exit-code table (0/1/2/3/4) is locked, `npm run typecheck` and `npm audit` are both clean, and the package ships to npm with a `v1.0.0` git tag once the remote is configured. v1.1.0 is the **stale-hunting edition**: `--stale <days>` grades entries by newest-of git commit → manifest mtime → folder mtime and drops no-evidence stragglers, `--fast-size` measures with `du -sk` (exact-walker fallback), results gain an `Idle` column, a proportional Share bar and `--sort size|path|idle|mtime`, discovery hides system noise by default (`--no-skip` restores the bare audit), `nmh update` self-updates npm-global installs, and `--trash=system|nmh` picks the backend with a restorable `~/.nmh/trash` fallback — all with zero new dependencies and additive-only JSON keys, guarded by the 267-test suite. The v1.1.1 patch is a **polish-and-hardening pass driven by three user reports and a full live-exercise matrix** (26 sandbox fixtures × 223 real combinations): the measuring loader is a **time-driven spinner** (braille frames rotate on their own timer at 70 ms instead of per-update repaints, spinning on TTY, silent in pipes unless `--animate on`), `nmh help` renders a **complete manual** (own renderer — 14 commands + 5 stats subcommand views + grouped options + examples + the safety/exit footer, and the previously-missing `-h`/`--help`/`-v`/`--version` aliases finally work instead of slipping into the default scan), and ANSI colors are **TTY-only by design** (picocolors' `!!env.CI` guess had painted unknown escape bytes into piped GitHub Actions output and shredded substring assertions — CI is 9/9 green for the first time; local color comes from a real TTY or explicit `--color`/`FORCE_COLOR`). The same matrix surfaced two defects the suite had missed: an unknown first word (`nmh scanx`) used to fall through to a silent full-cwd scan with exit 0 — any non-path, non-command token is now a loud usage error (and a bare cwd-relative dir like `nmh node_modules` now correctly targets that folder); and `nmh config set` wrote `$HOME/.nmhrc` while scans walked cwd → `/` and never read it when the cwd lived outside the home tree — the home file is now the documented fallback (chain first). Meanwhile `os.homedir()` on Windows reads `USERPROFILE` (test helper pins both), `execFile('npm')` needs `shell: true` on win32 (`stats -g` exited 2 there), and system-trash-dependent flows run hermetically via `nmh trash` fallback. Suite: 44 files / 279 tests green. v1.2.0 is the **consent-and-targeting edition**: `nmh update` stops installing on faith — the plain run shows the facts and asks (`-y`/`-n` on pipes, `--force` to skip the ask, and an already-latest install is never reinstalled), while `--check` and `--info` are read-only registry views (compact compare vs full `npm view` detail, mutually exclusive); and the wizard becomes a first-class command — `nmh wizard --dir=./a,./b` scopes the menu to explicit targets (no auto-climb), `nmh --wizard` is the flag form, and the `--` router (`nmh wizard -- stats --dir=./a`) opens a read-only view through the full dispatcher before the menu; pipes refuse every explicit wizard form with exit 1 and directions while the bare `nmh` fallback scan stays untouched. The update messages were also unified to Indonesian (the smoke matrix caught `--check` speaking English next to an Indonesian default). Suite: 46 files / 295 tests green. The v1.2.1 hotfix repairs what the wild caught the same day: modern npm prints `npm view <pkg> --json` as an **abbreviated packument array** (no top-level `version` key), which made every `nmh update` flavor exit "cannot read the latest version" — version detection is back on the plain-text `npm view <pkg> version` spawn, the `--info` detail is a separate best-effort fetch with shape normalization (array / object / garbage all tolerated — facts minus detail, never a failed run), and the new `NMH_UPDATE_VIEW_JSON` seam keeps both shapes hermetically testable. Suite: 46 files / 296 tests green. v1.3.0 is the **full-i18n edition**: the CLI now speaks five languages — `en` (default & fallback), `id`, `zh`, `ru`, `es` — through a single catalog (`t(key, params)` in `src/i18n/`, `{placeholder}` interpolation, zero hard-coded user-facing strings left in `src/` outside the i18n module, help manual / wizard menus / table headers / guard & delete messages / treemap HTML included). The language resolves as `--language` > `.nmhrc` `language` key > `NMH_LANG` > `LC_ALL`/`LANG` > `en` (unknown flag values refused loudly with exit 1, unknown env values fall back to `en` silently), a CI completeness gate keeps every locale's key set and `{placeholder}` slots 1:1 with the English source, and both frozen contracts — `--json` payload keys and the exit-code table — are language-independent. Suite: 48 files / 315 tests green. The v1.3.1 patch **completes the five-language promise**: `zh` and `ru` are filled with their real Mandarin/Russian translations (all 480 keys, the same 1:1 key + `{placeholder}` gate applies), and `nmh help` documents the `--language <lang>` flag — codifying the rule that every new flag, command or feature ships with its help entry in the same release. Suite: 48 files / 319 tests green. v1.4.0 is the **update-zoom edition**: `nmh update` reads any npm **dist-tag** — `--tag <name>` / `--check [tag]` / the persisted `.nmhrc` `updateTag` key (`nmh config set --update-tag beta`) — with version compares that finally understand **prereleases** (a triple tie is broken prerelease-wise, so `1.4.0` beats `1.4.0-beta.1` and `beta.2` beats `beta.1`); a tag pointing below the installed version is a **downgrade** with the same consent gate as an upgrade but a NO-default confirm (non-TTY still needs `-y`), unknown tags refuse loudly with the available list, `--info` lists every published tag, and the `--json` payload grows additively (`tag` / `target` / `direction` / `distTags?` — the frozen keys untouched). The wizard menu gains **🔁 Update nmh**, the wizard boot peeks the registry by default (24 h cache, `updateCheck: false` opts out), and the English catalog was swept to be 100% English (a handful of inherited keys still held Indonesian values; key names are frozen — only values changed). Suite: 48 files / 325 tests green.
|
|
477
483
|
|
|
478
484
|
## Development
|
|
479
485
|
|