@williamthorsen/nmr 0.39.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.
- package/.meta/changelog.json +128 -4
- package/.readyup/kits/default.js +47 -32
- package/.readyup/manifest.json +12 -7
- package/CHANGELOG.md +38 -0
- package/README.md +41 -73
- package/agents/README.md +8 -8
- package/agents/guidance/rulebooks/nmr.md +8 -8
- package/bin/ensure-prepublish-hooks.js +1 -1
- package/bin/nmr-clean.js +1 -1
- package/bin/nmr-compile.js +1 -1
- package/bin/nmr-fmt.js +1 -1
- package/bin/nmr-report-catalog.js +1 -1
- package/bin/nmr-report-overrides.js +1 -1
- package/bin/nmr-taze.js +1 -1
- package/bin/nmr.js +1 -1
- package/dist/esm/UserError.d.ts +1 -2
- package/dist/esm/check-cache.d.ts +59 -61
- package/dist/esm/commands/build-output.d.ts +35 -36
- package/dist/esm/commands/build.d.ts +13 -14
- package/dist/esm/commands/build.js +3 -3
- package/dist/esm/commands/clean.d.ts +6 -6
- package/dist/esm/commands/ensure-prepublish-hooks.d.ts +5 -5
- package/dist/esm/commands/fmt.d.ts +8 -8
- package/dist/esm/commands/fmt.js +1 -1
- package/dist/esm/commands/report-catalog.d.ts +5 -7
- package/dist/esm/commands/report-catalog.js +1 -1
- package/dist/esm/commands/report-overrides.d.ts +0 -2
- package/dist/esm/commands/report-overrides.js +1 -1
- package/dist/esm/commands/taze.d.ts +9 -8
- package/dist/esm/config.d.ts +5 -5
- package/dist/esm/config.js +3 -3
- package/dist/esm/context.d.ts +1 -2
- package/dist/esm/default-scripts.d.ts +5 -6
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/git-ignored-paths.d.ts +9 -0
- package/dist/esm/git-ignored-paths.js +10 -0
- package/dist/esm/glyphs.d.ts +3 -3
- package/dist/esm/help.d.ts +2 -2
- package/dist/esm/helpers/clampToBytes.d.ts +4 -5
- package/dist/esm/helpers/config-path.d.ts +0 -2
- package/dist/esm/helpers/createBoundedBuffer.d.ts +2 -2
- package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
- package/dist/esm/helpers/duration.d.ts +8 -9
- package/dist/esm/helpers/filter-selection.d.ts +19 -19
- package/dist/esm/helpers/findClosestName.d.ts +1 -1
- package/dist/esm/helpers/hook-name.d.ts +2 -5
- package/dist/esm/helpers/package-json.d.ts +9 -8
- package/dist/esm/helpers/readStringValues.d.ts +2 -3
- package/dist/esm/helpers/reportClosing.d.ts +8 -9
- package/dist/esm/helpers/transcript.d.ts +13 -13
- package/dist/esm/helpers/type-guards.d.ts +1 -0
- package/dist/esm/output-style.d.ts +12 -13
- package/dist/esm/prettier.d.ts +2 -2
- package/dist/esm/recording.d.ts +20 -15
- package/dist/esm/recording.js +7 -7
- package/dist/esm/replay-assembly.d.ts +10 -10
- package/dist/esm/report-format.d.ts +14 -19
- package/dist/esm/reportCliFailure.d.ts +2 -3
- package/dist/esm/resolve-scripts.d.ts +4 -5
- package/dist/esm/resolveBinStyles.d.ts +4 -5
- package/dist/esm/resolver.d.ts +14 -18
- package/dist/esm/runCli.d.ts +6 -6
- package/dist/esm/runCli.js +21 -21
- package/dist/esm/runner.d.ts +19 -19
- package/dist/esm/steps.d.ts +43 -42
- package/dist/esm/taze.d.ts +1 -1
- package/dist/esm/tests.d.ts +18 -13
- package/dist/esm/tests.js +2 -2
- package/dist/esm/tiers.d.ts +27 -23
- package/dist/esm/tiers.js +6 -2
- package/dist/esm/types.d.ts +5 -5
- package/dist/esm/verbosity.d.ts +22 -20
- package/dist/esm/verdict.d.ts +33 -33
- package/dist/esm/verdict.js +1 -1
- package/dist/esm/vitest-source-resolution.d.ts +11 -11
- package/dist/esm/vitest-source-resolution.js +2 -2
- package/dist/esm/vitest.d.ts +25 -24
- package/dist/esm/vitest.js +16 -8
- package/dist/esm/workspace.d.ts +4 -4
- package/docs/check-cache.md +57 -57
- package/docs/prettier.md +10 -10
- package/docs/reporting.md +46 -46
- package/docs/scripts.md +29 -29
- package/docs/upgrades.md +18 -18
- package/docs/utilities.md +13 -13
- package/docs/vitest.md +64 -62
- package/docs/workspace.md +19 -19
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -2,53 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
# @williamthorsen/nmr
|
|
4
4
|
|
|
5
|
-
Context-aware script runner for pnpm monorepos.
|
|
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.
|
|
8
|
+
## Release notes — v0.40.0 (2026-10-01)
|
|
9
9
|
|
|
10
10
|
### 🎉 Features
|
|
11
11
|
|
|
12
|
-
-
|
|
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
|
-
|
|
15
|
-
- Adds a no-op verdict for a command that the drop leaves with no step at all.
|
|
16
|
-
- Replaces the single refusal for a workspace that selects no package with one message per cause.
|
|
16
|
+
### 🐛 Bug fixes
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- 🚨 **Breaking:** Prefix the booleans and reject unrecognized Vitest option keys (#849)
|
|
21
|
-
|
|
22
|
-
- Renames two boolean options of `defineVitestConfig` and `defineRootVitestConfig`, the factories that build a repo's Vitest config: `isolateGit` becomes `shouldIsolateGit`, and `resolveFromSource` becomes `shouldResolveFromSource`.
|
|
23
|
-
- 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.
|
|
24
|
-
- 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`.
|
|
25
|
-
|
|
26
|
-
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.
|
|
27
|
-
|
|
28
|
-
- 🚨 **Breaking:** End nmr's source value names with the kind of the value (#850)
|
|
29
|
-
|
|
30
|
-
- 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.
|
|
31
|
-
- Adds a guard to `checkTestFileConventions` and `reportTestFileConventions` that throws a `TypeError` naming `excludedBasenames` when a call still sets `exclude`.
|
|
32
|
-
|
|
33
|
-
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.
|
|
34
|
-
|
|
35
|
-
- 🚨 **Breaking:** Hoist the pnpm-workspace resolver into nmr-core as a total API (#864)
|
|
36
|
-
|
|
37
|
-
- 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.
|
|
38
|
-
- 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.
|
|
39
|
-
- Removes `diagnoseEmptyWorkspace` and `EmptyWorkspaceDiagnosis` from `@williamthorsen/nmr/workspace`, since `resolveWorkspace` reports the same cause in one call.
|
|
40
|
-
|
|
41
|
-
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.
|
|
42
|
-
|
|
43
|
-
- 🚨 **Breaking:** Adopt the shared workspace resolver in release-kit, honoring exclusions (#865)
|
|
44
|
-
|
|
45
|
-
- 🚨 **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`.
|
|
46
|
-
- 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.
|
|
47
|
-
- 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.
|
|
48
|
-
- 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.
|
|
49
|
-
- 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.
|
|
50
|
-
|
|
51
|
-
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.
|
|
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)
|
|
52
20
|
<!-- /section:release-notes -->
|
|
53
21
|
|
|
54
22
|
## Installation
|
|
@@ -61,16 +29,16 @@ pnpm add -D @williamthorsen/nmr
|
|
|
61
29
|
|
|
62
30
|
### Making `nmr` resolvable
|
|
63
31
|
|
|
64
|
-
nmr installs as a workspace bin, so the bare `nmr` command works only when
|
|
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:
|
|
65
33
|
|
|
66
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.
|
|
67
|
-
- **`pnpm exec nmr <command>`**. Works
|
|
35
|
+
- **`pnpm exec nmr <command>`**. Works without any setup: pnpm resolves the bin from the workspace root.
|
|
68
36
|
|
|
69
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.
|
|
70
38
|
|
|
71
39
|
## Quick start
|
|
72
40
|
|
|
73
|
-
nmr works
|
|
41
|
+
nmr works without any configuration. It includes built-in scripts for common monorepo tasks.
|
|
74
42
|
|
|
75
43
|
From a package directory:
|
|
76
44
|
|
|
@@ -86,7 +54,7 @@ From the monorepo root:
|
|
|
86
54
|
nmr test # Run root tests + recursive workspace tests
|
|
87
55
|
nmr build # Build all packages
|
|
88
56
|
nmr ci # Build, then run the strict checks
|
|
89
|
-
nmr prepush # Run everything the remote runs, before
|
|
57
|
+
nmr prepush # Run everything the remote runs, before a push
|
|
90
58
|
```
|
|
91
59
|
|
|
92
60
|
Target packages from anywhere:
|
|
@@ -96,19 +64,19 @@ nmr --filter core test # Test only the package whose manifest name is `core`
|
|
|
96
64
|
nmr --recursive lint # Lint all workspace packages
|
|
97
65
|
```
|
|
98
66
|
|
|
99
|
-
`--recursive`, and a `--filter` that selects more than one package, give
|
|
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.
|
|
100
68
|
|
|
101
|
-
Position determines ownership:
|
|
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.
|
|
102
70
|
|
|
103
|
-
`nmr --help` lists every flag and command, with the shell command each command resolves
|
|
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.
|
|
104
72
|
|
|
105
|
-
nmr detects
|
|
73
|
+
nmr detects the directory from which it is invoked and selects the right scripts automatically. See [context-aware resolution](#context-aware-resolution) below.
|
|
106
74
|
|
|
107
75
|
## Context-aware resolution
|
|
108
76
|
|
|
109
|
-
nmr's key feature is that the same command runs different scripts depending on where
|
|
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.
|
|
110
78
|
|
|
111
|
-
| Where
|
|
79
|
+
| Where `nmr` is invoked | Registry used | Working directory | `nmr test` runs |
|
|
112
80
|
| --------------------------------- | ----------------- | ----------------- | -------------------------------------------------------------------- |
|
|
113
81
|
| Monorepo root | Root scripts | Monorepo root | `root:test`, then `-R test` |
|
|
114
82
|
| Inside a workspace package | Workspace scripts | The package root | `pnpm exec vitest --project unit --project tool` (that package only) |
|
|
@@ -126,21 +94,21 @@ nmr --workspace-root check
|
|
|
126
94
|
Two consequences:
|
|
127
95
|
|
|
128
96
|
- `nmr --workspace-root clean` sweeps every workspace package, as `nmr clean` does from the root.
|
|
129
|
-
- Passthrough paths resolve against the working directory:
|
|
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.
|
|
130
98
|
|
|
131
99
|
## Three-tier override system
|
|
132
100
|
|
|
133
101
|
Scripts resolve through three tiers. Higher tiers override lower ones:
|
|
134
102
|
|
|
135
|
-
1. **Built-in defaults
|
|
136
|
-
2. **Repo-wide config
|
|
137
|
-
3. **Per-package overrides
|
|
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`
|
|
138
106
|
|
|
139
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.
|
|
140
108
|
|
|
141
109
|
## Pre and post hooks
|
|
142
110
|
|
|
143
|
-
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:
|
|
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.
|
|
144
112
|
|
|
145
113
|
Extend `nmr build` with a pre-build step for every workspace package:
|
|
146
114
|
|
|
@@ -159,7 +127,7 @@ export default defineConfig({
|
|
|
159
127
|
|
|
160
128
|
## Configuration
|
|
161
129
|
|
|
162
|
-
Create `.config/nmr.config.ts` in the monorepo root to add or override scripts. A package may
|
|
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):
|
|
163
131
|
|
|
164
132
|
```ts
|
|
165
133
|
import { defineConfig } from '@williamthorsen/nmr/config';
|
|
@@ -178,7 +146,7 @@ export default defineConfig({
|
|
|
178
146
|
|
|
179
147
|
## What nmr reports
|
|
180
148
|
|
|
181
|
-
Every command nmr runs reports one line naming the scope it ran
|
|
149
|
+
Every command that nmr runs reports one line naming the scope at which it ran, the command, the outcome, and the timing:
|
|
182
150
|
|
|
183
151
|
```console
|
|
184
152
|
✅ nmr-core: test: passed in 12.4s
|
|
@@ -189,9 +157,9 @@ Every command nmr runs reports one line naming the scope it ran at, the command,
|
|
|
189
157
|
|
|
190
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.
|
|
191
159
|
|
|
192
|
-
**Verdicts print in every verbosity.**
|
|
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.
|
|
193
161
|
|
|
194
|
-
[What nmr reports](docs/reporting.md) covers how verdicts nest, the `--json` format, the output style, and
|
|
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.
|
|
195
163
|
|
|
196
164
|
## Check-result cache
|
|
197
165
|
|
|
@@ -220,7 +188,7 @@ import { defineVitestConfig } from '@williamthorsen/nmr/vitest';
|
|
|
220
188
|
export default defineVitestConfig();
|
|
221
189
|
```
|
|
222
190
|
|
|
223
|
-
`@williamthorsen/nmr/tests` exports a check that fails a test run on a test file outside `__tests__` or one that
|
|
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.
|
|
224
192
|
|
|
225
193
|
### Shared Prettier config
|
|
226
194
|
|
|
@@ -241,7 +209,7 @@ export default definePrettierConfig();
|
|
|
241
209
|
|
|
242
210
|
### Workspace introspection
|
|
243
211
|
|
|
244
|
-
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:
|
|
245
213
|
|
|
246
214
|
```ts
|
|
247
215
|
// __tests__/packages.test.ts
|
|
@@ -254,11 +222,11 @@ for (const packageDir of getWorkspacePackageDirs(monorepoRoot)) {
|
|
|
254
222
|
}
|
|
255
223
|
```
|
|
256
224
|
|
|
257
|
-
[Workspace introspection](docs/workspace.md) covers what each lookup returns and
|
|
225
|
+
[Workspace introspection](docs/workspace.md) covers what each lookup returns and the cases in which it diverges from pnpm.
|
|
258
226
|
|
|
259
227
|
## Agent guidance
|
|
260
228
|
|
|
261
|
-
nmr
|
|
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`:
|
|
262
230
|
|
|
263
231
|
```yaml
|
|
264
232
|
packages:
|
|
@@ -266,7 +234,7 @@ packages:
|
|
|
266
234
|
- '@williamthorsen/nmr'
|
|
267
235
|
```
|
|
268
236
|
|
|
269
|
-
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
|
|
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.
|
|
270
238
|
|
|
271
239
|
Wiring the sync to `postinstall` keeps it current without a hand-run command:
|
|
272
240
|
|
|
@@ -282,7 +250,7 @@ Wiring the sync to `postinstall` keeps it current without a hand-run command:
|
|
|
282
250
|
|
|
283
251
|
## Conformance checks
|
|
284
252
|
|
|
285
|
-
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
|
|
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.
|
|
286
254
|
|
|
287
255
|
Add `readyup` as a devDependency, then name nmr in its config:
|
|
288
256
|
|
|
@@ -296,20 +264,20 @@ export default defineRdyConfig({
|
|
|
296
264
|
```
|
|
297
265
|
|
|
298
266
|
```bash
|
|
299
|
-
rdy run --packages # every kit each listed package publishes
|
|
267
|
+
rdy run --packages # every kit that each listed package publishes
|
|
300
268
|
rdy run --from npm:@williamthorsen/nmr # nmr's kit alone, without the config entry
|
|
301
269
|
rdy list --from npm:@williamthorsen/nmr # what nmr publishes
|
|
302
270
|
```
|
|
303
271
|
|
|
304
|
-
`--packages` is the form that survives nmr publishing further kits. Both need `readyup` 0.23 or later, and `@williamthorsen/nmr` as a _direct_ devDependency:
|
|
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.
|
|
305
273
|
|
|
306
274
|
## Documentation
|
|
307
275
|
|
|
308
|
-
- [Scripts and configuration](docs/scripts.md):
|
|
309
|
-
- [What nmr reports](docs/reporting.md):
|
|
310
|
-
- [Check-result cache](docs/check-cache.md):
|
|
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
|
|
311
279
|
- [Dependency upgrades](docs/upgrades.md): `nmr upgrade`, the upgrade policy, `report-catalog`, `report-overrides`, and `nmr-taze`
|
|
312
280
|
- [Standalone utilities](docs/utilities.md): `nmr-clean`, `nmr-compile`, `nmr-fmt`, and `ensure-prepublish-hooks`
|
|
313
|
-
- [Shared Vitest config](docs/vitest.md):
|
|
314
|
-
- [Shared Prettier config](docs/prettier.md):
|
|
315
|
-
- [Workspace introspection](docs/workspace.md):
|
|
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`
|
|
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
|
|
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
|
|
12
|
-
2. **The obvious action goes wrong without it.** Guidance that prevents a silent mistake
|
|
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
|
|
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
|
-
|
|
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`
|
|
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
|
|
10
|
-
- Scope follows cwd, and bare `nmr` lists both registries:
|
|
11
|
-
- Never `npx 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,
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
}
|
package/bin/nmr-compile.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|
package/dist/esm/UserError.d.ts
CHANGED
|
@@ -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.
|
|
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);
|