@williamthorsen/nmr 0.38.0 → 0.40.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.
Files changed (112) hide show
  1. package/.meta/changelog.json +212 -4
  2. package/.readyup/kits/default.js +124 -104
  3. package/.readyup/manifest.json +12 -7
  4. package/CHANGELOG.md +108 -0
  5. package/README.md +41 -47
  6. package/agents/README.md +8 -8
  7. package/agents/guidance/rulebooks/nmr.md +8 -8
  8. package/bin/ensure-prepublish-hooks.js +1 -1
  9. package/bin/nmr-clean.js +1 -1
  10. package/bin/nmr-compile.js +1 -1
  11. package/bin/nmr-fmt.js +1 -1
  12. package/bin/nmr-report-catalog.js +1 -1
  13. package/bin/nmr-report-overrides.js +1 -1
  14. package/bin/nmr-taze.js +1 -1
  15. package/bin/nmr.js +1 -1
  16. package/dist/esm/UserError.d.ts +1 -2
  17. package/dist/esm/check-cache.d.ts +61 -63
  18. package/dist/esm/check-cache.js +24 -24
  19. package/dist/esm/cli-ensure-prepublish-hooks.js +4 -4
  20. package/dist/esm/commands/build-output.d.ts +37 -38
  21. package/dist/esm/commands/build-output.js +9 -9
  22. package/dist/esm/commands/build.d.ts +13 -14
  23. package/dist/esm/commands/build.js +67 -65
  24. package/dist/esm/commands/clean.d.ts +6 -6
  25. package/dist/esm/commands/clean.js +11 -11
  26. package/dist/esm/commands/ensure-prepublish-hooks.d.ts +7 -7
  27. package/dist/esm/commands/ensure-prepublish-hooks.js +41 -41
  28. package/dist/esm/commands/fmt.d.ts +8 -8
  29. package/dist/esm/commands/fmt.js +33 -33
  30. package/dist/esm/commands/report-catalog.d.ts +5 -7
  31. package/dist/esm/commands/report-catalog.js +10 -8
  32. package/dist/esm/commands/report-overrides.d.ts +0 -2
  33. package/dist/esm/commands/report-overrides.js +8 -8
  34. package/dist/esm/commands/taze.d.ts +9 -8
  35. package/dist/esm/config.d.ts +5 -5
  36. package/dist/esm/config.js +27 -27
  37. package/dist/esm/context.d.ts +1 -2
  38. package/dist/esm/context.js +8 -8
  39. package/dist/esm/default-scripts.d.ts +5 -6
  40. package/dist/esm/defineConfig.d.ts +1 -1
  41. package/dist/esm/git-ignored-paths.d.ts +9 -0
  42. package/dist/esm/git-ignored-paths.js +10 -0
  43. package/dist/esm/glyphs.d.ts +3 -3
  44. package/dist/esm/help.d.ts +4 -4
  45. package/dist/esm/help.js +21 -21
  46. package/dist/esm/helpers/clampToBytes.d.ts +4 -5
  47. package/dist/esm/helpers/clampToBytes.js +3 -3
  48. package/dist/esm/helpers/config-path.d.ts +0 -2
  49. package/dist/esm/helpers/createBoundedBuffer.d.ts +3 -3
  50. package/dist/esm/helpers/createBoundedBuffer.js +2 -2
  51. package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
  52. package/dist/esm/helpers/duration.d.ts +10 -11
  53. package/dist/esm/helpers/duration.js +7 -7
  54. package/dist/esm/helpers/filter-selection.d.ts +19 -19
  55. package/dist/esm/helpers/filter-selection.js +12 -12
  56. package/dist/esm/helpers/findClosestName.d.ts +1 -1
  57. package/dist/esm/helpers/findClosestName.js +15 -15
  58. package/dist/esm/helpers/hook-name.d.ts +2 -5
  59. package/dist/esm/helpers/package-json.d.ts +11 -10
  60. package/dist/esm/helpers/package-json.js +26 -25
  61. package/dist/esm/helpers/readStringValues.d.ts +2 -3
  62. package/dist/esm/helpers/reportClosing.d.ts +8 -9
  63. package/dist/esm/helpers/transcript.d.ts +15 -15
  64. package/dist/esm/helpers/transcript.js +14 -14
  65. package/dist/esm/helpers/type-guards.d.ts +1 -0
  66. package/dist/esm/output-style.d.ts +12 -13
  67. package/dist/esm/prettier.d.ts +2 -2
  68. package/dist/esm/prettier.js +5 -5
  69. package/dist/esm/recording.d.ts +25 -20
  70. package/dist/esm/recording.js +28 -21
  71. package/dist/esm/replay-assembly.d.ts +10 -10
  72. package/dist/esm/report-format.d.ts +15 -20
  73. package/dist/esm/report-format.js +7 -7
  74. package/dist/esm/reportCliFailure.d.ts +2 -3
  75. package/dist/esm/resolve-scripts.d.ts +4 -5
  76. package/dist/esm/resolveBinStyles.d.ts +4 -5
  77. package/dist/esm/resolveBinStyles.js +3 -3
  78. package/dist/esm/resolver.d.ts +16 -20
  79. package/dist/esm/resolver.js +15 -15
  80. package/dist/esm/runCli.d.ts +6 -6
  81. package/dist/esm/runCli.js +261 -195
  82. package/dist/esm/runner.d.ts +20 -20
  83. package/dist/esm/runner.js +18 -18
  84. package/dist/esm/steps.d.ts +51 -38
  85. package/dist/esm/steps.js +32 -27
  86. package/dist/esm/taze.d.ts +1 -1
  87. package/dist/esm/tests.d.ts +23 -18
  88. package/dist/esm/tests.js +21 -9
  89. package/dist/esm/tiers.d.ts +28 -24
  90. package/dist/esm/tiers.js +13 -9
  91. package/dist/esm/types.d.ts +5 -5
  92. package/dist/esm/verbosity.d.ts +23 -21
  93. package/dist/esm/verbosity.js +7 -7
  94. package/dist/esm/verdict.d.ts +39 -32
  95. package/dist/esm/verdict.js +63 -50
  96. package/dist/esm/vitest-source-resolution.d.ts +11 -11
  97. package/dist/esm/vitest-source-resolution.js +49 -47
  98. package/dist/esm/vitest.d.ts +27 -26
  99. package/dist/esm/vitest.js +90 -33
  100. package/dist/esm/workspace.d.ts +6 -22
  101. package/dist/esm/workspace.js +9 -67
  102. package/docs/check-cache.md +57 -57
  103. package/docs/prettier.md +10 -10
  104. package/docs/reporting.md +48 -44
  105. package/docs/scripts.md +29 -29
  106. package/docs/upgrades.md +18 -18
  107. package/docs/utilities.md +13 -13
  108. package/docs/vitest.md +68 -66
  109. package/docs/workspace.md +27 -4
  110. package/package.json +6 -6
  111. package/dist/esm/helpers/workspace-patterns.d.ts +0 -8
  112. package/dist/esm/helpers/workspace-patterns.js +0 -29
package/CHANGELOG.md CHANGED
@@ -2,6 +2,114 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.40.0 — 2026-10-01
6
+
7
+ ### 🎉 Features
8
+
9
+ - Makes `checkTestFileConventions` and the readyup kit's tier and placement checks skip untracked paths that git ignores, so that local build output such as a stale `.netlify/` directory no longer fails the check. (#898)
10
+ - Excludes untracked paths that git ignores from collection in `defineVitestConfig` and `defineRootVitestConfig`, so that Vitest does not run a test file that the conventions check skips. (#898)
11
+ - Rewords the `nmr --log` refusal for a run that the check-result cache does not cover to `the check-result cache does not apply here (NMR_DEBUG=1 reports why)`, and renames the matching `docs/check-cache.md` heading to "When the cache does not apply", which changes its anchor. (#923)
12
+
13
+ ### 🐛 Bug fixes
14
+
15
+ - Rewords the messages printed by `nmr`, its bin wrappers, and its readyup kit, which used figures such as "sits under" and "holds", em-dashes, and "no" as a determiner, so that a diagnosis such as "-F/--filter did not match any workspace" states its condition directly. (#932)
16
+ - Fixes the issue that the `nmr` rulebook recommended `-q` without stating that nmr reads its own flags only before the command name, which led agents to run `nmr ci -q` and fail when `nmr-fmt` rejected the flag. (#948)
17
+
18
+ ### ♻️ Refactoring
19
+
20
+ - Replaces every `.sort()` call that suppressed `unicorn/no-array-sort` with `toSorted()` and drops the spread copy wherever the source was already an array. (#925)
21
+
22
+ ### 🧪 Tests
23
+
24
+ - Stubs `GIT_CEILING_DIRECTORIES` to the fixture's parent in every test that expects git to find no repository, so that a repository above the temp directory can no longer change the test's result. (#923)
25
+ - Moves the `everyBinTargetIsACommittedWrapper` tests from `kit-bin-checks.unit.test.ts` to `kit-bin-checks.tool.test.ts`, because they spawn git through readyup's `listTrackedFiles`. (#923)
26
+
27
+ ### ⚙️ Tooling
28
+
29
+ - Adds a check to `bootstrap.tool.test.ts` that fails when a package's `prepare` runs the build bootstrap without either depending on nmr-core or passing `--conditions nmr-source`, which would make `pnpm install` fail on a fresh clone. (#903)
30
+
31
+ ### 📦 Dependencies
32
+
33
+ - Adds `esbuild` 0.28.2 to the pnpm catalog and declares it as a devDependency of `nmr`, `release-kit`, and `v11y-check`, because `readyup` declares it only as an optional peer and `rdy compile` failed in those packages once a regenerated lockfile stopped installing it. (#944)
34
+
35
+ ### 📚 Documentation
36
+
37
+ - Revises the comments in `packages/nmr/src` and `packages/nmr/.readyup/kits/default.ts` against the comment-discipline rules, correcting comments that misstated the code, such as the Levenshtein distance quoted in `runCli.ts` and a `root-tests-config` kit check that does not exist. (#919)
38
+ - Corrects the truncation example in `packages/nmr/docs/reporting.md`, replacing 90 seconds, which renders as `1m 30s` whether it is truncated or rounded, with 119.9 seconds, which truncates to `1m 59s`. (#919)
39
+ - Rewrites the sentences in the README, the guides under `docs/`, the agent rulebook, and the code comments of `@williamthorsen/nmr` that broke the repository's writing rules on plain speech, em-dashes, second-person address, and relative clauses. (#932)
40
+ - Replaces "where" in two doc comments in `packages/nmr/src/check-cache.ts` and in the fixture comment in two tier-budget tests, because each use introduced a consequence rather than a place. (#945)
41
+ - Writes `-q` as `nmr -q <command>` in `docs/reporting.md` and in the README's verbosity paragraph. (#948)
42
+
43
+ ## 0.39.0 — 2026-09-22
44
+
45
+ ### 🎉 Features
46
+
47
+ - Report why a workspace selects no package, and no-op nmr's own -R steps (#841)
48
+
49
+ - Drops a `-R` step that nmr composed into a script where the workspace holds no package, so `typecheck` and the five `test` scripts run there instead of failing the run.
50
+ - Adds a no-op verdict for a command that the drop leaves with no step at all.
51
+ - Replaces the single refusal for a workspace that selects no package with one message per cause.
52
+
53
+ Migration: In a repo that declares a workspace package pattern and deliberately holds no package, delete the `.config/nmr.config.ts` entries added to work around the `-R` refusal, the `build: ':'` override included.
54
+
55
+ - 🚨 **Breaking:** Prefix the booleans and reject unrecognized Vitest option keys (#849)
56
+
57
+ - Renames two boolean options of `defineVitestConfig` and `defineRootVitestConfig`, the factories that build a repo's Vitest config: `isolateGit` becomes `shouldIsolateGit`, and `resolveFromSource` becomes `shouldResolveFromSource`.
58
+ - Makes both factories reject an option key outside the recognized set, naming the offending keys and the recognized ones, rather than ignoring a misspelled or retired key and letting its option fall back to the default with nothing in the run reporting it.
59
+ - Adds a retired-key check that runs ahead of the unrecognized-key check, reporting the new name for a config that still sets `isolateGit` or `resolveFromSource`.
60
+
61
+ Migration: In each options object passed to `defineVitestConfig` or `defineRootVitestConfig`, rename `isolateGit` to `shouldIsolateGit` and `resolveFromSource` to `shouldResolveFromSource`, and correct any other key that the factory now rejects. `defineRootVitestConfig` recognizes `monorepoRoot` on the last layer alone, so a config that sets it on an earlier layer now throws where the old code ignored it.
62
+
63
+ - 🚨 **Breaking:** End nmr's source value names with the kind of the value (#850)
64
+
65
+ - Renames two fields of `TestFileConventionsReport`, `misplaced` and `untiered`, to `misplacedFiles` and `untieredFiles`, and renames `TestFileConventionsOptions`'s `exclude` to `excludedBasenames`, in the `@williamthorsen/nmr/tests` entry point.
66
+ - Adds a guard to `checkTestFileConventions` and `reportTestFileConventions` that throws a `TypeError` naming `excludedBasenames` when a call still sets `exclude`.
67
+
68
+ Migration: Read `report.misplacedFiles` and `report.untieredFiles` in place of `report.misplaced` and `report.untiered`, and pass `excludedBasenames` in place of `exclude`. A call that still sets `exclude` throws rather than running with no exclusions, so the failure surfaces where Vitest loads the call site.
69
+
70
+ - 🚨 **Breaking:** Hoist the pnpm-workspace resolver into nmr-core as a total API (#864)
71
+
72
+ - Adds a `./workspace` subpath export to `@williamthorsen/nmr-core`, whose `resolveWorkspace` returns a pnpm workspace's package directories, or the cause of an empty result, as a value rather than as a thrown error.
73
+ - Reports a `pnpm-workspace.yaml` that the YAML parser rejects under a cause of its own, `unreadable-manifest`, so `nmr -R build` names the syntax error and its remedy in place of failing with the parser's own error.
74
+ - Removes `diagnoseEmptyWorkspace` and `EmptyWorkspaceDiagnosis` from `@williamthorsen/nmr/workspace`, since `resolveWorkspace` reports the same cause in one call.
75
+
76
+ Migration: Replace each `diagnoseEmptyWorkspace` call with `resolveWorkspace`, which reports the cause on the empty result it already returns, and rename the `no-manifest` cause to `no-package`. `EmptyWorkspaceCause` also gains `unreadable-manifest`, so an exhaustive switch over it stops compiling until it handles the new member; every other export of `@williamthorsen/nmr/workspace` keeps its signature and its `UserError` throws.
77
+
78
+ - 🚨 **Breaking:** Adopt the shared workspace resolver in release-kit, honoring exclusions (#865)
79
+
80
+ - 🚨 **Breaking:** Narrows `no-pattern` in `@williamthorsen/nmr-core`'s `EmptyWorkspaceCause`, moving an absent or empty `packages` list onto `no-packages-list` and a value that is not a list of strings onto `unreadable-packages`.
81
+ - Stops `release-kit` from tagging, publishing, and label-syncing a package that a `!` entry in `pnpm-workspace.yaml` excludes, which its previous `glob`-based resolution could not remove from the match.
82
+ - Replaces the release of the root package alone with a failure naming the condition that emptied the workspace, when `pnpm-workspace.yaml` does not hold valid YAML or declares a workspace that does not resolve to a package.
83
+ - Replaces the `no-pattern` remedy that `nmr` printed for a `pnpm-workspace.yaml` declaring no `packages` list, and for one declaring a `packages` value that is not a list of strings.
84
+ - Raises `engines.node` to `>=24.16.0` in `@williamthorsen/nmr-core` and `@williamthorsen/release-kit`, because Node 24.0 through 24.15 ignores the `followSymlinks` option that the workspace matcher passes to `globSync` and drops a symlinked package directory from the resolution without reporting it.
85
+
86
+ Migration: Add a `no-packages-list` arm and an `unreadable-packages` arm to any `switch` that covers `EmptyWorkspaceCause` exhaustively. `no-pattern` now names only a list that the resolver read in full and whose entries all failed to become a positive pattern, so move the handling of an absent `packages` key, an empty list, and a value that is not a list of strings out of the `no-pattern` arm into the two new ones. Route `no-packages-list` to single-package behavior rather than to a failure path: pnpm resolves such a manifest to the root package alone.
87
+
88
+ ### ♻️ Refactoring
89
+
90
+ - Align nmr's function names with naming conventions (#852)
91
+
92
+ - Renames the functions in `packages/nmr` to align with naming conventions.
93
+
94
+ - Suffix nmr's unit-measuring numbers and expand its abbreviations (#854)
95
+
96
+ - Renames every unit-measuring number and abbreviated identifier under `packages/nmr/src`, adding `Ms`, `Bytes`, and `Offset` suffixes to the quantities and expanding `pkg`, `Len`, and `Idx` to `package`, `Length`, and `Index`. The renames are not part of any public API.
97
+
98
+ ### 🧪 Tests
99
+
100
+ - End nmr's test value names with the kind of the value (#851)
101
+
102
+ - Aligns the name of every non-boolean value in `packages/nmr/src/**/__tests__` with naming conventions.
103
+ - Renames `inline` to `inlineBody` in the repo-root `__tests__/reusable-workflow-runtime.app.unit.test.ts`.
104
+
105
+ ### 👷 CI
106
+
107
+ - Make the release workflow Corepack-agnostic (#842)
108
+
109
+ - Removes the `Disable Corepack` step from `release.reusable.yaml`, which Node 25's removal of Corepack from the distribution would make fail with `command not found` once `actions/setup-node` moves past Node 24, and keeps `COREPACK_ENABLE_STRICT: '0'`, which guards the source-build path where the pnpm `packageManager` pin survives.
110
+ - Adds a case to `reusable-workflow-runtime.app.unit.test.ts` that fails the test run on any reusable workflow whose `run:` commands invoke `corepack`, enumerated from `.github/workflows` so that a workflow added later is covered without editing a list.
111
+ - Drops the Corepack recommendation from the README's pnpm prerequisite, which now points at pnpm's installation page, and from the fix text of the `.tool-versions does not list pnpm` check in nmr's readyup kit, which now names the `packageManager` field.
112
+
5
113
  ## 0.38.0 — 2026-09-18
6
114
 
7
115
  ### 🎉 Features
package/README.md CHANGED
@@ -2,27 +2,21 @@
2
2
 
3
3
  # @williamthorsen/nmr
4
4
 
5
- Context-aware script runner for pnpm monorepos. Ships an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
5
+ Context-aware script runner for pnpm monorepos. Includes an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
6
6
 
7
7
  <!-- section:release-notes -->
8
- ## Release notes — v0.38.0 (2026-09-18)
8
+ ## Release notes — v0.40.0 (2026-10-01)
9
9
 
10
10
  ### 🎉 Features
11
11
 
12
- - Give nmr plain output in pipes and CI logs (#838)
12
+ - Makes `checkTestFileConventions` and the readyup kit's tier and placement checks skip untracked paths that git ignores, so that local build output such as a stale `.netlify/` directory no longer fails the check. (#898)
13
+ - Excludes untracked paths that git ignores from collection in `defineVitestConfig` and `defineRootVitestConfig`, so that Vitest does not run a test file that the conventions check skips. (#898)
14
+ - Rewords the `nmr --log` refusal for a run that the check-result cache does not cover to `the check-result cache does not apply here (NMR_DEBUG=1 reports why)`, and renames the matching `docs/check-cache.md` heading to "When the cache does not apply", which changes its anchor. (#923)
13
15
 
14
- - Stops `nmr` from printing emoji to a pipe or a CI log: When the output stream is not a terminal or `CI` is set, a verdict reads `PASS`, `FAIL`, `SKIP`, or `NOOP` and a command icon is omitted, so that a log can be searched for a verdict.
15
- - Adds `--output-style <auto|plain|rich>` to `nmr` and the `NMR_OUTPUT_STYLE` variable, each forcing either style; the flag outranks the variable, a value that names no style exits 1 with a usage error, and neither enters a check-cache key: A rich run and a plain run share a recorded pass.
16
- - Replaces several rich glyphs, taking the status markers from the set that release-kit and v11y-check print so that one status has one glyph across the three tools: a recalled pass shows ⏩ in place of ⏭️, a skipped empty override shows ⚪ in place of ⛔, a warning and the `--log` refusal show 🟠 in place of ⚠️ and 📭, the `--log` header shows 💾 in place of 📼, and `nmr-ensure-prepublish-hooks` reports ✅ and ❌ in place of ✓ and ✗.
16
+ ### 🐛 Bug fixes
17
17
 
18
- ### 🪦 Removed
19
-
20
- - 🚨 **Breaking:** Rename the declinesArgs step declaration to shouldDeclineArguments (#822)
21
-
22
- - Makes a `.config/nmr.config.ts` that still declares `declinesArgs` on a composite script's `{ run, declinesArgs }` step fail to load, with an error that names `shouldDeclineArguments` as the replacement.
23
- - Renames the matching field of the `StepSpec` type exported by `@williamthorsen/nmr/config` and `@williamthorsen/nmr/scripts`.
24
-
25
- Migration: Rename `declinesArgs` to `shouldDeclineArguments` in each composite script step of `.config/nmr.config.ts`, and in any code that sets or reads the field on a `StepSpec`.
18
+ - Rewords the messages printed by `nmr`, its bin wrappers, and its readyup kit, which used figures such as "sits under" and "holds", em-dashes, and "no" as a determiner, so that a diagnosis such as "-F/--filter did not match any workspace" states its condition directly. (#932)
19
+ - Fixes the issue that the `nmr` rulebook recommended `-q` without stating that nmr reads its own flags only before the command name, which led agents to run `nmr ci -q` and fail when `nmr-fmt` rejected the flag. (#948)
26
20
  <!-- /section:release-notes -->
27
21
 
28
22
  ## Installation
@@ -35,16 +29,16 @@ pnpm add -D @williamthorsen/nmr
35
29
 
36
30
  ### Making `nmr` resolvable
37
31
 
38
- nmr installs as a workspace bin, so the bare `nmr` command works only when your shell can find `<root>/node_modules/.bin/nmr`. Choose one:
32
+ nmr installs as a workspace bin, so the bare `nmr` command works only when the shell can find `<root>/node_modules/.bin/nmr`. Choose one:
39
33
 
40
34
  - **[direnv](https://direnv.net/)** (recommended for contributors). Add `PATH_add node_modules/.bin` to the repo's `.envrc`, and bare `nmr` works from any subdirectory.
41
- - **`pnpm exec nmr <command>`**. Works with no setup: pnpm resolves the bin from the workspace root.
35
+ - **`pnpm exec nmr <command>`**. Works without any setup: pnpm resolves the bin from the workspace root.
42
36
 
43
37
  > **Note:** Avoid `npx nmr`. Inside a git worktree, `npx` can resolve a different nmr binary from outside the working tree, so the command succeeds while running the wrong code.
44
38
 
45
39
  ## Quick start
46
40
 
47
- nmr works out of the box with no configuration. It ships with built-in scripts for common monorepo tasks.
41
+ nmr works without any configuration. It includes built-in scripts for common monorepo tasks.
48
42
 
49
43
  From a package directory:
50
44
 
@@ -60,7 +54,7 @@ From the monorepo root:
60
54
  nmr test # Run root tests + recursive workspace tests
61
55
  nmr build # Build all packages
62
56
  nmr ci # Build, then run the strict checks
63
- nmr prepush # Run everything the remote runs, before you push
57
+ nmr prepush # Run everything the remote runs, before a push
64
58
  ```
65
59
 
66
60
  Target packages from anywhere:
@@ -70,19 +64,19 @@ nmr --filter core test # Test only the package whose manifest name is `core`
70
64
  nmr --recursive lint # Lint all workspace packages
71
65
  ```
72
66
 
73
- `--recursive`, and a `--filter` that selects more than one package, give each package no terminal input. An interactive command such as `test:watch` needs a filter that selects a single package.
67
+ `--recursive`, and a `--filter` that selects more than one package, do not give any package terminal input. An interactive command such as `test:watch` needs a filter that selects a single package.
74
68
 
75
- Position determines ownership: flags before the command name are nmr's own, and everything after the command name is forwarded untouched to the resolved command.
69
+ Position determines ownership: Flags before the command name are nmr's own, and everything after the command name is forwarded untouched to the resolved command.
76
70
 
77
- `nmr --help` lists every flag and command, with the shell command each command resolves to. Per-package `package.json` files need no script entries.
71
+ `nmr --help` lists every flag and command, with the shell command to which each command resolves. Per-package `package.json` files do not need any script entries.
78
72
 
79
- nmr detects where you are and selects the right scripts automatically — see [context-aware resolution](#context-aware-resolution) below.
73
+ nmr detects the directory from which it is invoked and selects the right scripts automatically. See [context-aware resolution](#context-aware-resolution) below.
80
74
 
81
75
  ## Context-aware resolution
82
76
 
83
- nmr's key feature is that the same command runs different scripts depending on where you invoke it. It walks up from your current directory to find `pnpm-workspace.yaml`, then checks whether your CWD is inside a workspace package directory.
77
+ nmr's key feature is that the same command runs different scripts depending on where it is invoked. It walks up from the current directory to find `pnpm-workspace.yaml`, then checks whether the current directory is inside a workspace package directory.
84
78
 
85
- | Where you run `nmr` | Registry used | Working directory | `nmr test` runs |
79
+ | Where `nmr` is invoked | Registry used | Working directory | `nmr test` runs |
86
80
  | --------------------------------- | ----------------- | ----------------- | -------------------------------------------------------------------- |
87
81
  | Monorepo root | Root scripts | Monorepo root | `root:test`, then `-R test` |
88
82
  | Inside a workspace package | Workspace scripts | The package root | `pnpm exec vitest --project unit --project tool` (that package only) |
@@ -100,21 +94,21 @@ nmr --workspace-root check
100
94
  Two consequences:
101
95
 
102
96
  - `nmr --workspace-root clean` sweeps every workspace package, as `nmr clean` does from the root.
103
- - Passthrough paths resolve against the working directory: from `packages/nmr/src/`, `nmr --workspace-root fmt pnpm-workspace.yaml` formats the file at the monorepo root.
97
+ - Passthrough paths resolve against the working directory: From `packages/nmr/src/`, `nmr --workspace-root fmt pnpm-workspace.yaml` formats the file at the monorepo root.
104
98
 
105
99
  ## Three-tier override system
106
100
 
107
101
  Scripts resolve through three tiers. Higher tiers override lower ones:
108
102
 
109
- 1. **Built-in defaults** — scripts shipped with this package
110
- 2. **Repo-wide config** — additions and overrides in `.config/nmr.config.ts`
111
- 3. **Per-package overrides** — scripts in a package's `package.json`
103
+ 1. **Built-in defaults**: Scripts included in this package
104
+ 2. **Repo-wide config**: Additions and overrides in `.config/nmr.config.ts`
105
+ 3. **Per-package overrides**: Scripts in a package's `package.json`
112
106
 
113
107
  [Scripts and configuration](docs/scripts.md#three-tier-override-system) shows a worked example, how to skip a script for one package, and what nmr does with a `package.json` entry that re-invokes its own command.
114
108
 
115
109
  ## Pre and post hooks
116
110
 
117
- Every `nmr X` invocation auto-wraps as the equivalent of `nmr X:pre && nmr X && nmr X:post`. Hooks are first-class scripts that resolve through the same three tiers as any other script (built-in defaults, then `.config/nmr.config.ts`, then per-package `package.json`). Wrapping is uniform: nested invocations from composite expansion get their own hook treatment. A failing hook ends the sequence, and its exit code propagates.
111
+ Every `nmr X` invocation auto-wraps as the equivalent of `nmr X:pre && nmr X && nmr X:post`. Hooks are first-class scripts that resolve through the same three tiers as any other script (built-in defaults, then `.config/nmr.config.ts`, then per-package `package.json`). Wrapping is uniform: Nested invocations from composite expansion get their own hook treatment. A failing hook ends the sequence, and its exit code propagates.
118
112
 
119
113
  Extend `nmr build` with a pre-build step for every workspace package:
120
114
 
@@ -133,7 +127,7 @@ export default defineConfig({
133
127
 
134
128
  ## Configuration
135
129
 
136
- Create `.config/nmr.config.ts` in the monorepo root to add or override scripts. A package may carry one too, for [build settings of its own](docs/scripts.md#package-level-configuration):
130
+ Create `.config/nmr.config.ts` in the monorepo root to add or override scripts. A package may have one too, for [build settings of its own](docs/scripts.md#package-level-configuration):
137
131
 
138
132
  ```ts
139
133
  import { defineConfig } from '@williamthorsen/nmr/config';
@@ -152,7 +146,7 @@ export default defineConfig({
152
146
 
153
147
  ## What nmr reports
154
148
 
155
- Every command nmr runs reports one line naming the scope it ran at, the command, the outcome, and the timing:
149
+ Every command that nmr runs reports one line naming the scope at which it ran, the command, the outcome, and the timing:
156
150
 
157
151
  ```console
158
152
  ✅ nmr-core: test: passed in 12.4s
@@ -163,9 +157,9 @@ Every command nmr runs reports one line naming the scope it ran at, the command,
163
157
 
164
158
  **A pipe and a CI log get words rather than emoji.** The same four lines read `PASS`, `FAIL`, `SKIP`, and `NOOP` there, which `grep FAIL` finds. `--output-style` and `NMR_OUTPUT_STYLE` each force either style.
165
159
 
166
- **Verdicts print in every verbosity.** `-q` withholds the output of the commands nmr runs, never nmr's own words: a passing quiet run reports its verdicts and nothing else. A command whose override resolved to `""` or `":"` reports the skip rather than exiting 0 in silence, which is what separates it from a command that passed. An `NMR_RUN_IF_PRESENT` miss reports nothing, having no command to report on.
160
+ **Verdicts print in every verbosity.** `nmr -q <command>` withholds the output of the commands that nmr runs, never nmr's own words: A passing quiet run reports its verdicts and nothing else. A command that ran nothing reports the skip rather than exiting 0 in silence, which separates it from a command that passed: an override resolving to `""` or `":"`, and a fan-out step that a package-free workspace left nothing to do. An `NMR_RUN_IF_PRESENT` miss reports nothing, because it does not have a command to report on.
167
161
 
168
- [What nmr reports](docs/reporting.md) covers how verdicts nest, the `--json` format, the output style, and where the verbosity of a run comes from.
162
+ [What nmr reports](docs/reporting.md) covers how verdicts nest, the `--json` format, the output style, and what sets the verbosity of a run.
169
163
 
170
164
  ## Check-result cache
171
165
 
@@ -194,7 +188,7 @@ import { defineVitestConfig } from '@williamthorsen/nmr/vitest';
194
188
  export default defineVitestConfig();
195
189
  ```
196
190
 
197
- `@williamthorsen/nmr/tests` exports a check that fails a test run on a test file outside `__tests__` or one that names no tier. [Shared Vitest config](docs/vitest.md) covers the test tiers, that check, what the factory supplies, and customizing it by scope.
191
+ `@williamthorsen/nmr/tests` exports a check that fails a test run on a test file outside `__tests__` or one that does not name a tier. [Shared Vitest config](docs/vitest.md) covers the test tiers, that check, what the factory supplies, and customizing it by scope.
198
192
 
199
193
  ### Shared Prettier config
200
194
 
@@ -215,7 +209,7 @@ export default definePrettierConfig();
215
209
 
216
210
  ### Workspace introspection
217
211
 
218
- Repo-wide tests and scripts often need to know where the monorepo root is, or which directories its workspace packages live in. The `@williamthorsen/nmr/workspace` subpath publishes the two pnpm-workspace lookups nmr uses internally:
212
+ Repo-wide tests and scripts often need to know where the monorepo root is, or which directories its workspace packages live in. The `@williamthorsen/nmr/workspace` subpath publishes the two pnpm-workspace lookups that nmr uses internally:
219
213
 
220
214
  ```ts
221
215
  // __tests__/packages.test.ts
@@ -228,11 +222,11 @@ for (const packageDir of getWorkspacePackageDirs(monorepoRoot)) {
228
222
  }
229
223
  ```
230
224
 
231
- [Workspace introspection](docs/workspace.md) covers what each lookup returns and where it diverges from pnpm.
225
+ [Workspace introspection](docs/workspace.md) covers what each lookup returns and the cases in which it diverges from pnpm.
232
226
 
233
227
  ## Agent guidance
234
228
 
235
- nmr ships the rules an agent needs in order to invoke it, as [CodeAssembly](https://github.com/williamthorsen/codeassembly) package content. Adopt it by naming the package in the consuming repo's `.agents/codeassembly.yaml`:
229
+ nmr includes the rules that an agent needs in order to invoke it, as [CodeAssembly](https://github.com/williamthorsen/codeassembly) package content. Adopt it by naming the package in the consuming repo's `.agents/codeassembly.yaml`:
236
230
 
237
231
  ```yaml
238
232
  packages:
@@ -240,7 +234,7 @@ packages:
240
234
  - '@williamthorsen/nmr'
241
235
  ```
242
236
 
243
- Then add `codeassembly` as a devDependency and run `codeassembly sync`. The guidance is injected into the machine-local guidance file each harness loads at launch; nothing is copied into the repo or committed there. It resolves from the installed package, so upgrading nmr updates it with no second step.
237
+ Then add `codeassembly` as a devDependency and run `codeassembly sync`. The guidance is injected into the machine-local guidance file that each harness loads at launch; nothing is copied into the repo or committed there. It resolves from the installed package, so upgrading nmr updates it without a second step.
244
238
 
245
239
  Wiring the sync to `postinstall` keeps it current without a hand-run command:
246
240
 
@@ -256,7 +250,7 @@ Wiring the sync to `postinstall` keeps it current without a hand-run command:
256
250
 
257
251
  ## Conformance checks
258
252
 
259
- nmr publishes a `readyup` kit that checks a consuming repo against the current release: the shared Vitest and Prettier configs, the workspace layout, the root script registry, the test-file conventions ([tier names](docs/vitest.md#test-tiers) and `__tests__` placement), that the repo's own suite gates them, that every `bin` target is a committed wrapper rather than build output that pnpm cannot link at install time, that `files` covers the output each wrapper loads, that no root install script runs `lefthook install` without `lefthook check-install`, and that no `package.json` in the tree declares a `pnpm` field, which pnpm 11 reads no key from. Those settings belong in `pnpm-workspace.yaml`, and `pnpx codemod run pnpm-v10-to-v11` moves them. The kit ships inside the package, so it checks against the nmr version installed rather than whatever a repository ref happens to point at, and a tier added or renamed in nmr reaches the repo on upgrade. A repo that declares the [conventions check](docs/vitest.md#gating-the-test-file-conventions) gates both conventions in its own test run, and the kit then leaves the test files to that check.
253
+ nmr publishes a `readyup` kit that checks a consuming repo against the current release: the shared Vitest and Prettier configs, the workspace layout, the root script registry, the test-file conventions ([tier names](docs/vitest.md#test-tiers) and `__tests__` placement), that the repo's own suite gates them, that every `bin` target is a committed wrapper rather than build output that pnpm cannot link at install time, that `files` covers the output that each wrapper loads, that the root install scripts do not run `lefthook install` without `lefthook check-install`, and that the `package.json` files in the tree do not declare a `pnpm` field, from which pnpm 11 does not read any key. Those settings belong in `pnpm-workspace.yaml`, and `pnpx codemod run pnpm-v10-to-v11` moves them. The kit is included in the package, so it checks against the nmr version installed rather than whatever a repository ref happens to point at, and a tier added or renamed in nmr takes effect in the repo when it upgrades nmr. A repo that declares the [conventions check](docs/vitest.md#gating-the-test-file-conventions) gates both conventions in its own test run, and the kit then leaves the test files to that check.
260
254
 
261
255
  Add `readyup` as a devDependency, then name nmr in its config:
262
256
 
@@ -270,20 +264,20 @@ export default defineRdyConfig({
270
264
  ```
271
265
 
272
266
  ```bash
273
- rdy run --packages # every kit each listed package publishes
267
+ rdy run --packages # every kit that each listed package publishes
274
268
  rdy run --from npm:@williamthorsen/nmr # nmr's kit alone, without the config entry
275
269
  rdy list --from npm:@williamthorsen/nmr # what nmr publishes
276
270
  ```
277
271
 
278
- `--packages` is the form that survives nmr publishing further kits. Both need `readyup` 0.23 or later, and `@williamthorsen/nmr` as a _direct_ devDependency: a strict pnpm layout links nothing else into the project, so a transitive copy is unreachable.
272
+ `--packages` is the form that survives nmr publishing further kits. Both need `readyup` 0.23 or later, and `@williamthorsen/nmr` as a _direct_ devDependency: A strict pnpm layout links nothing else into the project, so a transitive copy is unreachable.
279
273
 
280
274
  ## Documentation
281
275
 
282
- - [Scripts and configuration](docs/scripts.md): override tiers, script values, hooks, `defineConfig` fields, `devBin`, and the default commands
283
- - [What nmr reports](docs/reporting.md): verdicts, `--json`, output style, and verbosity
284
- - [Check-result cache](docs/check-cache.md): what is skipped, what a hit requires, and replaying, reading, and bypassing recorded runs
276
+ - [Scripts and configuration](docs/scripts.md): Override tiers, script values, hooks, `defineConfig` fields, `devBin`, and the default commands
277
+ - [What nmr reports](docs/reporting.md): Verdicts, `--json`, output style, and verbosity
278
+ - [Check-result cache](docs/check-cache.md): What is skipped, what a hit requires, and replaying, reading, and bypassing recorded runs
285
279
  - [Dependency upgrades](docs/upgrades.md): `nmr upgrade`, the upgrade policy, `report-catalog`, `report-overrides`, and `nmr-taze`
286
280
  - [Standalone utilities](docs/utilities.md): `nmr-clean`, `nmr-compile`, `nmr-fmt`, and `ensure-prepublish-hooks`
287
- - [Shared Vitest config](docs/vitest.md): test tiers, test selections, the conventions check, and customization
288
- - [Shared Prettier config](docs/prettier.md): what it formats and how to customize it
289
- - [Workspace introspection](docs/workspace.md): the two workspace lookups
281
+ - [Shared Vitest config](docs/vitest.md): Test tiers, test selections, the conventions check, and customization
282
+ - [Shared Prettier config](docs/prettier.md): What it formats and how to customize it
283
+ - [Workspace introspection](docs/workspace.md): The two workspace lookups
package/agents/README.md CHANGED
@@ -4,21 +4,21 @@ This directory is a CodeAssembly content root, declared by the `codeassembly.con
4
4
 
5
5
  ## The ambient cheatsheet
6
6
 
7
- `guidance/rulebooks/nmr.md` carries `delivery: ambient`, so its body is injected at launch in every consuming repo. Every line is paid for on every task, whether or not it is relevant. It is a cheatsheet, not a manual.
7
+ `guidance/rulebooks/nmr.md` declares `delivery: ambient`, so its body is injected at launch in every consuming repo. Every line consumes launch tokens on every task, whether or not it is relevant. It is a cheatsheet, not a manual.
8
8
 
9
- A line earns a place in it only if both hold:
9
+ A line belongs in it only if both hold:
10
10
 
11
- 1. **It is absent from nmr's own output.** Bare `nmr` already prints the flags, every command, and the shell command each resolves to; failing checks already name their own fix. Repeating any of that costs launch tokens to say something the agent will be told anyway, at the moment it matters.
12
- 2. **The obvious action goes wrong without it.** Guidance that prevents a silent mistake earns its place; guidance that prevents a mistake the next error message would diagnose does not.
11
+ 1. **It is absent from nmr's own output.** Bare `nmr` already prints the flags, every command, and the shell command to which each resolves; failing checks already name their own fix. Repeating any of that spends launch tokens to say something the agent will be told anyway, at the moment it matters.
12
+ 2. **The obvious action goes wrong without it.** Guidance that prevents a silent mistake belongs in it; guidance that prevents a mistake that the next error message would diagnose does not.
13
13
 
14
- Everything else belongs in the topic document under `packages/nmr/docs/` that covers it, reachable on demand at `node_modules/@williamthorsen/nmr/docs/`, with a pointer from the cheatsheet naming the topic and its document so an agent knows where to look. When adding a feature, document it there and extend that pointer rather than the cheatsheet.
14
+ Everything else belongs in the topic document under `packages/nmr/docs/` that covers it, reachable on demand at `node_modules/@williamthorsen/nmr/docs/`, with a pointer from the cheatsheet naming the topic and its document so that an agent knows where to look. When adding a feature, document it there and extend that pointer rather than the cheatsheet.
15
15
 
16
16
  ## Constraints on rulebook bodies
17
17
 
18
- Rulebook bodies carry no links to other rulebooks. CodeAssembly's renderer rewrites link targets under `skills/` and `scripts/` and rejects everything else, so a cross-reference names the deployed `consult-<slug>` in prose and declares the edge under `dependencies:`.
18
+ Rulebook bodies do not contain any links to other rulebooks. CodeAssembly's renderer rewrites link targets under `skills/` and `scripts/` and rejects everything else, so a cross-reference names the deployed `consult-<slug>` in prose and declares the edge under `dependencies:`.
19
19
 
20
- Anchor-only links are checked against the body they appear in, so a fragment naming a heading that lives in another document fails the run. Check `](#` before moving a section between bodies.
20
+ Because anchor-only links are checked against the body in which they appear, a fragment naming a heading in another document fails the run. Check `](#` before moving a section between bodies.
21
21
 
22
22
  ## Publishing
23
23
 
24
- The directory is published because `files` in `packages/nmr/package.json` lists it. Dropping that entry fails nothing locally, because this repo resolves the guidance through a `workspace:*` self-link that `files` does not govern. `packaging.tool.test.ts` is what catches it.
24
+ The directory is published because `files` in `packages/nmr/package.json` lists it. Dropping that entry fails nothing locally, because this repo resolves the guidance through a `workspace:*` self-link that `files` does not govern. `packaging.tool.test.ts` catches it.
@@ -6,12 +6,12 @@ delivery: ambient
6
6
 
7
7
  # nmr: agent guidance
8
8
 
9
- - Use `nmr <command>`, not `pnpm run <command>`, for anything nmr provides. Run bare `nmr` to list every command and the shell command it resolves to; a wrong guess reports only `Unknown command`, with no list.
10
- - Scope follows cwd, and bare `nmr` lists both registries: from the repo root a command covers root files and every workspace; from inside a package, that package alone. `nmr -F <pkg> <command>` targets one package from anywhere; `nmr root:<command>` targets root files alone, which isolates a failure to root code.
11
- - Never `npx nmr`: inside a git worktree it can resolve a different nmr from outside the tree. Fall back to `pnpm exec nmr`.
9
+ - Use `nmr <command>`, not `pnpm run <command>`, for anything nmr provides. Run bare `nmr` to list every command and the shell command to which it resolves; a wrong guess reports only `Unknown command`, without a list.
10
+ - Scope follows cwd, and bare `nmr` lists both registries: From the repo root a command covers root files and every workspace; from inside a package, that package alone. `nmr -F <pkg> <command>` targets one package from anywhere; `nmr root:<command>` targets root files alone, which isolates a failure to root code.
11
+ - Never `npx nmr`: Inside a git worktree it can resolve a different nmr from outside the tree. Fall back to `pnpm exec nmr`.
12
12
  - `nmr ci` runs what a code-quality workflow runs (build, then strict checks) and not the dependency audit, which belongs to a workflow of its own; `nmr prepush` runs the audit first, then `ci`, so a vulnerability stops the run before the long gate. Neither installs or binds a git hook.
13
- - A check that already passed on the current working tree skips, printing `SKIP <scope>: <command>: passed …` and, where the recalled run left one, `— replayed: <excerpt>` of what that run concluded. Any edit, addition, or deletion re-runs it, so read the replayed excerpt rather than paying for the answer again: never pass `--no-cache` to get a "real" run, which costs the command's full runtime to reach the same answer. Its one use is a file rather than an exit status -- a skipped command writes none, so a fresh `coverage/` needs `nmr --no-cache test:coverage`. A composite replays its constituents' excerpts, each attributed as `<scope>: <command>: <excerpt>` and cut at the line's ceiling where there are too many to fit. A skip carrying no excerpt is one whose run wrote to a terminal, or one whose constituents recorded none.
14
- - `nmr --log <command>` prints the whole run behind that excerpt -- up to 256 KiB, led by when it was recorded, how long it ran, and the command string that produced it -- so output you need in full is a recording to read rather than a run to pay for again. It runs nothing, and it is held to the tree rather than to the terminal: a recording a piped run made prints for a reader at one. A composite prints its constituents' excerpts, so the transcript of one of them is `nmr -F <pkg> --log <command>` or that constituent's own name. Nothing to show is said on one line and exits non-zero, naming which it is.
15
- - Every command nmr runs reports one line -- `PASS <scope>: <command>: passed in 12.4s`, `FAIL … failed in 1.2s (exit 1)`, `SKIP … passed 4m ago on this tree`, or `NOOP … skipped, the override is empty` -- and a composite reports alongside every command it expands into. Those are the words a pipe and a CI log get; a terminal gets ✅, ❌, ⏩, and ⚪ in their place, and `--output-style rich` or `NMR_OUTPUT_STYLE=rich` forces the emoji on any stream. The lines print in every verbosity, so `-q` is the low-noise way to run rather than a silent one: it withholds the output of the commands nmr runs and keeps nmr's own verdicts, which is how a pass, a cached skip, and a `""`/`":"` override stay distinguishable. Expect a run to be quiet already without the flag: nmr recognizes a known agent harness in the environment and takes quiet from that, as it does from `NMR_COMMAND_VERBOSITY` or the repo's `output.commandVerbosity`. A command whose output you need in full is one to run with `NMR_COMMAND_VERBOSITY=full`, which outranks both.
16
- - Every test file lives under a `__tests__` directory and names its isolation tier last: `<subject>[.<aspect>].<tier>.test.ts`, where any earlier segment is free-form documentation. A tier is the furthest thing the test reaches -- `tool` (a program the environment supplies), `localhost` (a service on this machine), `remote` (a machine that isn't this one), or `unit`, the residual claiming everything else. A file outside `__tests__`, or one whose tier segment is missing or misspelt, fails the test run in a repo that declares nmr's exported conventions check, which reports both. In a repo that has not, neither fails anything -- the first is collected by nothing and the second runs under `unit` -- and `rdy run --packages` reports both and warns that the check is missing.
17
- - The rest is in `node_modules/@williamthorsen/nmr/docs/`: `scripts.md` for pre/post hooks (every `nmr X` auto-wraps as `nmr X:pre && nmr X && nmr X:post`) and script overrides and their `""`/`":"` skip values; `upgrades.md` for `nmr upgrade` ceilings (report-only until `nmr upgrade --write`); `utilities.md` for the `nmr-compile` build (there is no repo-local build script); `vitest.md` for the test tiers' timeout budgets, the `tiers` seam that raises one, and which script reaches which tier; and `check-cache.md` for the check-result cache's key, the retention key a replayed excerpt is additionally held to, the retention ceiling `--log` prints within, its `checkCache` config, and every condition that disables it.
13
+ - A check that already passed on the current working tree skips, printing `SKIP <scope>: <command>: passed …` and, when the recalled run left one, `— replayed: <excerpt>` of what that run concluded. Any edit, addition, or deletion re-runs it. Read the replayed excerpt rather than running the check again: Never pass `--no-cache` to get a "real" run, which takes the command's full runtime to reach the same answer. Its one use is a file rather than an exit status -- a skipped command writes none, so a fresh `coverage/` needs `nmr --no-cache test:coverage`. A composite replays its constituents' excerpts, each attributed as `<scope>: <command>: <excerpt>` and cut at the line's ceiling when there are too many to fit. A skip without an excerpt is one whose run wrote to a terminal, or one whose constituents recorded none.
14
+ - `nmr --log <command>` prints the whole run behind that excerpt -- up to 256 KiB, led by when it was recorded, how long it ran, and the command string that produced it. Output that you need in full is a recording to read rather than a command to run again. It runs nothing, and it is held to the tree rather than to the terminal: A recording made by a piped run prints for a reader at one. A composite prints its constituents' excerpts, not their transcripts, so the transcript of one of them is `nmr -F <pkg> --log <command>` or that constituent's own name. When there is nothing to show, `--log` says so on one line, names which case it is, and exits non-zero.
15
+ - Every command that nmr runs reports one line -- `PASS <scope>: <command>: passed in 12.4s`, `FAIL … failed in 1.2s (exit 1)`, `SKIP … passed 4m ago on this tree`, or `NOOP … skipped, the override is empty` -- and a composite reports alongside every command into which it expands. Those are the words that a pipe and a CI log get; a terminal gets ✅, ❌, ⏩, and ⚪ in their place, and `--output-style rich` or `NMR_OUTPUT_STYLE=rich` forces the emoji on any stream. The lines print in every verbosity, so `nmr -q <command>` is the low-noise way to run rather than a silent one: It withholds the output of the commands that nmr runs and keeps nmr's own verdicts, which is how a pass, a cached skip, and a `""`/`":"` override stay distinguishable. nmr reads its own flags only before the command name and passes everything after the name to the commands that run: `nmr ci -q` hands `-q` to `nmr-fmt`, which rejects it. Expect a run to be quiet already without the flag: nmr recognizes a known agent harness in the environment and takes quiet from that, as it does from `NMR_COMMAND_VERBOSITY` or the repo's `output.commandVerbosity`. A command whose output you need in full is one to run with `NMR_COMMAND_VERBOSITY=full`, which outranks both.
16
+ - Every test file lives under a `__tests__` directory and names its isolation tier last: `<subject>[.<aspect>].<tier>.test.ts`, where any earlier segment is free-form documentation. A tier is the furthest thing that the test reaches -- `tool` (a program supplied by the environment), `localhost` (a service on this machine), `remote` (a machine that isn't this one), or `unit`, the residual claiming everything else. A file outside `__tests__`, or one whose tier segment is missing or misspelt, fails the test run in a repo that declares nmr's exported conventions check, which reports both. In a repo that has not, neither fails anything -- the first is collected by nothing and the second runs under `unit` -- and `rdy run --packages` reports both and warns that the check is missing.
17
+ - The rest is in `node_modules/@williamthorsen/nmr/docs/`: `scripts.md` for pre/post hooks (every `nmr X` auto-wraps as `nmr X:pre && nmr X && nmr X:post`) and script overrides and their `""`/`":"` skip values; `upgrades.md` for `nmr upgrade` ceilings (report-only until `nmr upgrade --write`); `utilities.md` for the `nmr-compile` build (the repo does not define a build script of its own); `vitest.md` for the test tiers' timeout budgets, the `tiers` seam that raises one, and which script reaches which tier; and `check-cache.md` for the check-result cache's key, the retention key to which a replayed excerpt is additionally held, the retention ceiling within which `--log` prints, its `checkCache` config, and every condition that disables it.
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-ensure-prepublish-hooks.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('ensure-prepublish-hooks: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('ensure-prepublish-hooks: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`ensure-prepublish-hooks: failed to load: ${error.message}\n`);
9
9
  }
package/bin/nmr-clean.js CHANGED
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-clean.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-clean: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-clean: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-clean: failed to load: ${error.message}\n`);
9
9
  }
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-build.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-compile: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-compile: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-compile: failed to load: ${error.message}\n`);
9
9
  }
package/bin/nmr-fmt.js CHANGED
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-fmt.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-fmt: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-fmt: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-fmt: failed to load: ${error.message}\n`);
9
9
  }
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-report-catalog.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-report-catalog: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-report-catalog: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-report-catalog: failed to load: ${error.message}\n`);
9
9
  }
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-report-overrides.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-report-overrides: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-report-overrides: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-report-overrides: failed to load: ${error.message}\n`);
9
9
  }
package/bin/nmr-taze.js CHANGED
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli-taze.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr-taze: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr-taze: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr-taze: failed to load: ${error.message}\n`);
9
9
  }
package/bin/nmr.js CHANGED
@@ -3,7 +3,7 @@ try {
3
3
  await import('../dist/esm/cli.js');
4
4
  } catch (error) {
5
5
  if (error.code === 'ERR_MODULE_NOT_FOUND') {
6
- process.stderr.write('nmr: build output not found — run `pnpm run bootstrap` first\n');
6
+ process.stderr.write('nmr: build output not found; run `pnpm run bootstrap` first\n');
7
7
  } else {
8
8
  process.stderr.write(`nmr: failed to load: ${error.message}\n`);
9
9
  }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * A failure in what the user declared -- a config file, a `package.json` -- rather than a fault in nmr.
3
3
  *
4
- * The CLI boundary prints one of these as its message alone. A stack trace records where nmr noticed the
5
- * problem, which says nothing about the file the reader has to edit.
4
+ * The CLI boundary prints one of these as its message alone.
6
5
  */
7
6
  export declare class UserError extends Error {
8
7
  constructor(message: string);