@williamthorsen/nmr 0.23.0 → 0.25.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.
@@ -0,0 +1,479 @@
1
+ /** @noformat — @generated. Do not edit. Compiled by rdy. */
2
+ /* eslint-disable */
3
+ export const __readyupVersion = "0.23.0";
4
+
5
+
6
+ // .readyup/kits/default.ts
7
+ import { existsSync, globSync, readdirSync as readdirSync2, readFileSync } from "node:fs";
8
+ import { basename, join, sep } from "node:path";
9
+ import { defineRdyKit } from "readyup";
10
+ import {
11
+ fileContains,
12
+ fileExists,
13
+ hasDevDependency,
14
+ hasMinDevDependencyVersion,
15
+ hasPackageJsonField,
16
+ isRecord,
17
+ readFile,
18
+ readPackageJson
19
+ } from "readyup/check-utils";
20
+
21
+ // src/default-scripts.ts
22
+ var GATE_PROJECTS = "--project unit --project tool";
23
+ var workspaceScripts = {
24
+ build: ["compile"],
25
+ check: ["typecheck", "fmt:check", "lint:check", "test"],
26
+ "check:strict": ["typecheck", "fmt:check", "lint:strict", "test:coverage"],
27
+ clean: "nmr-clean",
28
+ compile: "nmr-compile",
29
+ fix: ["lint", "fmt"],
30
+ "fix:check": ["fmt:check", "lint:check"],
31
+ fmt: "nmr-fmt --write",
32
+ "fmt:check": "nmr-fmt --check",
33
+ lint: "eslint --fix .",
34
+ "lint:check": "eslint .",
35
+ "lint:strict": "strict-lint",
36
+ test: `pnpm exec vitest ${GATE_PROJECTS}`,
37
+ "test:all": "pnpm exec vitest",
38
+ "test:coverage": `pnpm exec vitest ${GATE_PROJECTS} --coverage`,
39
+ "test:tool": "pnpm exec vitest --project tool",
40
+ "test:unit": "pnpm exec vitest --project unit",
41
+ "test:watch": `pnpm exec vitest ${GATE_PROJECTS} --watch`,
42
+ typecheck: "tsgo --noEmit",
43
+ // `--include-locked` is required rather than stylistic: a repo that pins exactly (pnpm's
44
+ // `savePrefix: ''`) has no dependency taze considers unlocked, so without it nothing is reported.
45
+ upgrade: "nmr-taze --include-locked",
46
+ "view-coverage": "open coverage/index.html"
47
+ };
48
+ var rootScripts = {
49
+ audit: ["audit:prod", "audit:dev"],
50
+ "audit:dev": "pnpm exec v11y --dev",
51
+ "audit:prod": "pnpm exec v11y --prod",
52
+ build: "pnpm --recursive exec nmr build",
53
+ check: ["typecheck", "fmt:check", "lint:check", "test"],
54
+ "check:agent-files": "nmr-sync-agent-files --check",
55
+ "check:strict": ["typecheck", "fmt:check", "lint:strict", "test:coverage", "check:agent-files"],
56
+ // Excludes the audit, which has a workflow of its own.
57
+ ci: ["build", "check:strict"],
58
+ clean: "nmr-clean",
59
+ fix: ["lint", "fmt"],
60
+ "fix:check": ["fmt:check", "lint:check"],
61
+ // Identical to the workspace entries: the bin scopes its own selection to the working directory, so
62
+ // neither registry needs the `sh -c '… "${@:-.}"' --` wrapper that gave the root form a default path.
63
+ fmt: "nmr-fmt --write",
64
+ "fmt:check": "nmr-fmt --check",
65
+ lint: "nmr root:lint && pnpm --recursive exec nmr lint",
66
+ "lint:check": "nmr root:lint:check && pnpm --recursive exec nmr lint:check",
67
+ "lint:strict": "nmr root:lint:strict && pnpm --recursive exec nmr lint:strict",
68
+ // Everything the remote runs: the code-quality gate plus the audit workflow.
69
+ prepush: ["ci", "audit"],
70
+ "report-overrides": "nmr-report-overrides",
71
+ "root:check": ["root:typecheck", "fmt:check", "root:lint:check", "root:test"],
72
+ "root:lint": "eslint --fix --ignore-pattern 'packages/**' .",
73
+ "root:lint:check": "eslint --ignore-pattern 'packages/**' .",
74
+ "root:lint:strict": "strict-lint --ignore-pattern 'packages/**' .",
75
+ "root:test": `vitest --config ./vitest.root.config.ts ${GATE_PROJECTS}`,
76
+ "root:test:all": "vitest --config ./vitest.root.config.ts",
77
+ "root:test:tool": "vitest --config ./vitest.root.config.ts --project tool",
78
+ "root:test:unit": "vitest --config ./vitest.root.config.ts --project unit",
79
+ "root:typecheck": "tsgo --noEmit",
80
+ "root:upgrade": "nmr-taze --include-locked",
81
+ "sync-agent-files": "nmr-sync-agent-files",
82
+ test: "nmr root:test && pnpm --recursive exec nmr test",
83
+ "test:all": "nmr root:test:all && pnpm --recursive exec nmr test:all",
84
+ // Chains `root:test`, not a `root:test:coverage`: the root config reports no coverage of its own.
85
+ "test:coverage": "nmr root:test && pnpm --recursive exec nmr test:coverage",
86
+ "test:tool": "nmr root:test:tool && pnpm --recursive exec nmr test:tool",
87
+ "test:unit": "nmr root:test:unit && pnpm --recursive exec nmr test:unit",
88
+ // Omitting `--config` is deliberate: bare `vitest` at the monorepo root resolves the root `vitest.config.ts`,
89
+ // covering the whole tree in one process. A chain like the others would never advance past its first watcher.
90
+ "test:watch": `vitest ${GATE_PROJECTS} --watch`,
91
+ typecheck: "nmr root:typecheck && pnpm --recursive exec nmr typecheck",
92
+ // A string rather than a composite because passthrough args attach to the chain's last command — as a
93
+ // composite, `nmr upgrade major` would hand `major` to the override report instead of the upgrade tool.
94
+ //
95
+ // Overrides print first, as context for the report that follows: a pinned transitive dependency is why
96
+ // an expected upgrade may be missing from it. taze handles pnpm workspaces natively, so this needs no
97
+ // `pnpm --recursive` fan-out: one process covers the root and every package.
98
+ upgrade: "nmr-report-overrides && nmr-taze --include-locked --recursive"
99
+ };
100
+
101
+ // src/resolve-scripts.ts
102
+ function getDefaultRootScripts() {
103
+ return { ...rootScripts };
104
+ }
105
+
106
+ // src/tiers.ts
107
+ import { readdirSync } from "node:fs";
108
+ import path from "node:path";
109
+ var TEST_DIR = "__tests__";
110
+ var TEST_EXTENSIONS = "{ts,tsx}";
111
+ var TEST_GLOB_PREFIX = `**/${TEST_DIR}/**`;
112
+ var TEST_FILE_PATTERN = /\.test\.tsx?$/;
113
+ var ALL_TEST_PATTERNS = [`${TEST_GLOB_PREFIX}/*.test.${TEST_EXTENSIONS}`];
114
+ function findTestFiles(rootDir) {
115
+ const found = [];
116
+ collectTestFiles(rootDir, "", false, found);
117
+ return found.toSorted();
118
+ }
119
+ function hasTierInfix(filePath) {
120
+ const tiers = TIER_NAMES;
121
+ return tiers.includes(path.basename(filePath).split(".").at(-3) ?? "");
122
+ }
123
+ var TEST_COLLECTION_EXCLUDE = [".git", "coverage", "dist", "node_modules"];
124
+ var TIER_NAMES = ["unit", "tool", "localhost", "remote"];
125
+ function collectTestFiles(dir, relativeDir, inTestDir, found) {
126
+ const entries = readdirSync(dir, { withFileTypes: true });
127
+ for (const entry of entries) {
128
+ const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
129
+ if (entry.isDirectory()) {
130
+ if (TEST_COLLECTION_EXCLUDE.includes(entry.name)) continue;
131
+ collectTestFiles(path.join(dir, entry.name), relativePath, inTestDir || entry.name === TEST_DIR, found);
132
+ } else if (inTestDir && TEST_FILE_PATTERN.test(entry.name)) {
133
+ found.push(relativePath);
134
+ }
135
+ }
136
+ }
137
+
138
+ // .readyup/kits/default.ts
139
+ var default_default = defineRdyKit({
140
+ checklists: [
141
+ {
142
+ name: "nmr",
143
+ checks: [
144
+ // -- Setup ---------------------------------------------------------------
145
+ {
146
+ name: "@williamthorsen/nmr in devDependencies",
147
+ severity: "error",
148
+ check: () => hasDevDependency("@williamthorsen/nmr"),
149
+ fix: "pnpm add --save-dev @williamthorsen/nmr",
150
+ checks: [
151
+ {
152
+ get name() {
153
+ return `@williamthorsen/nmr >= ${getMinVersion()}`;
154
+ },
155
+ severity: "error",
156
+ check: () => hasMinDevDependencyVersion("@williamthorsen/nmr", getMinVersion(), {
157
+ exempt: (range) => range.startsWith("workspace:")
158
+ }),
159
+ get fix() {
160
+ return `pnpm add --save-dev @williamthorsen/nmr@^${getMinVersion()}`;
161
+ }
162
+ }
163
+ ]
164
+ },
165
+ {
166
+ name: "pnpm-workspace.yaml exists",
167
+ severity: "error",
168
+ check: () => fileExists("pnpm-workspace.yaml"),
169
+ fix: "Create pnpm-workspace.yaml with workspace package globs"
170
+ },
171
+ {
172
+ name: "package.json has packageManager field",
173
+ severity: "warn",
174
+ check: () => hasPackageJsonField("packageManager"),
175
+ fix: 'Add "packageManager" field to package.json (e.g., "pnpm@10.33.0")'
176
+ },
177
+ {
178
+ name: ".tool-versions does not list pnpm",
179
+ severity: "warn",
180
+ check: toolVersionsHasNoPnpm,
181
+ fix: "Remove pnpm from .tool-versions \u2014 manage via packageManager field and corepack"
182
+ },
183
+ {
184
+ name: ".config/nmr.config.ts uses defineConfig",
185
+ severity: "recommend",
186
+ skip: () => !fileExists(".config/nmr.config.ts") ? "no nmr config file" : false,
187
+ check: () => fileContains(".config/nmr.config.ts", /defineConfig/),
188
+ fix: "Wrap your config export with defineConfig() from @williamthorsen/nmr/config for type safety"
189
+ },
190
+ // -- Root script cleanup -------------------------------------------------
191
+ {
192
+ name: "root package.json has no nmr-provided scripts",
193
+ severity: "warn",
194
+ check: noRedundantRootScripts,
195
+ fix: "Remove scripts from root package.json that nmr provides as built-in root scripts \u2014 invoke via nmr directly"
196
+ },
197
+ {
198
+ name: "root:lint:strict does not use echo fallback",
199
+ severity: "warn",
200
+ skip: () => !scriptExists("root:lint:strict") ? "no root:lint:strict script" : false,
201
+ check: () => !scriptMatches("root:lint:strict", /\becho\b/),
202
+ fix: "Replace the echo fallback in root:lint:strict \u2014 strict-lint now supports path arguments"
203
+ },
204
+ // -- Workspace build readiness -------------------------------------------
205
+ {
206
+ name: "all workspace packages can build",
207
+ severity: "warn",
208
+ check: allWorkspacePackagesCanBuild,
209
+ fix: `Add "build": ":" to packages that don't need a build, or ensure packages that use the default nmr build have a tsconfig.json and a src/ directory`
210
+ },
211
+ // -- Vitest projects -----------------------------------------------------
212
+ {
213
+ name: "no retired Vitest config variants",
214
+ severity: "error",
215
+ check: () => noRetiredVitestConfigs(),
216
+ fix: "Delete every vitest.standalone.config.* and vitest.integration.config.*. nmr's test scripts select Vitest projects instead of naming config files"
217
+ },
218
+ {
219
+ name: "vitest.config.ts builds on @williamthorsen/nmr/vitest",
220
+ severity: "error",
221
+ check: () => vitestConfigBuildsOnSharedConfig(),
222
+ fix: "Replace vitest.config.ts with: import { defineVitestConfig } from '@williamthorsen/nmr/vitest'; export default defineVitestConfig();"
223
+ },
224
+ {
225
+ name: "vitest.root.config.ts builds on @williamthorsen/nmr/vitest",
226
+ severity: "error",
227
+ check: () => vitestRootConfigBuildsOnSharedConfig(),
228
+ fix: "Replace vitest.root.config.ts with: import { defineRootVitestConfig } from '@williamthorsen/nmr/vitest'; export default defineRootVitestConfig({ monorepoRoot: import.meta.dirname });"
229
+ },
230
+ {
231
+ name: "every test file names its isolation tier",
232
+ severity: "error",
233
+ check: () => everyTestFileNamesItsTier(),
234
+ fix: `Rename each to <subject>[.<aspect>].<tier>.test.ts, naming one of ${TIER_NAMES.join(", ")}. Use tool for a test that reaches a program the environment supplies, which is where a retired .int. or .integration. file belongs. Only the segment before .test. selects a project, so an untiered file runs under the residual unit project and reports success`
235
+ },
236
+ {
237
+ name: "no package re-exports the ancestor Vitest config",
238
+ severity: "recommend",
239
+ check: () => noReExportOnlyVitestConfigs(),
240
+ fix: "Delete these files. Vitest resolves config by walking up from the run root, so a per-package re-export is redundant"
241
+ },
242
+ // -- Shared Prettier config ----------------------------------------------
243
+ {
244
+ name: "Prettier config builds on @williamthorsen/nmr/prettier",
245
+ severity: "error",
246
+ check: () => prettierConfigBuildsOnSharedConfig(),
247
+ fix: "Replace the Prettier config with: import { definePrettierConfig } from '@williamthorsen/nmr/prettier'; export default definePrettierConfig();"
248
+ },
249
+ // -- Audit dependency --------------------------------------------------------
250
+ {
251
+ name: "v11y-check in devDependencies",
252
+ severity: "warn",
253
+ check: () => hasDevDependency("v11y-check"),
254
+ fix: "pnpm add --save-dev v11y-check"
255
+ },
256
+ {
257
+ name: "code-quality workflow does not use nmr prepush",
258
+ severity: "warn",
259
+ skip: () => !fileExists(".github/workflows/code-quality.yaml") ? "no code-quality workflow" : false,
260
+ check: codeQualityWorkflowDoesNotUseNmrPrepush,
261
+ fix: 'Change the check-command in .github/workflows/code-quality.yaml from "pnpm exec nmr prepush" to "pnpm exec nmr ci", which runs the same checks without the audit that audit.yaml already runs'
262
+ },
263
+ // -- Legacy script runner ------------------------------------------------
264
+ {
265
+ name: "scripts/run-workspace-script.ts does not exist",
266
+ severity: "error",
267
+ check: () => !fileExists("scripts/run-workspace-script.ts"),
268
+ fix: "Delete scripts/run-workspace-script.ts \u2014 nmr replaces this custom script runner"
269
+ },
270
+ {
271
+ name: 'no workspace packages reference run-workspace-script or "pnpm run ws"',
272
+ severity: "error",
273
+ check: noWorkspaceRunScriptReferences,
274
+ fix: 'Remove "ws" script entries and replace any "pnpm run ws" invocations with nmr in each packages/*/package.json'
275
+ }
276
+ ]
277
+ }
278
+ ]
279
+ });
280
+ var SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([".git", "coverage", "dist", "node_modules"]);
281
+ var CONFIG_EXTENSIONS = "{ts,mts,cts,js,mjs,cjs}";
282
+ var SHARED_VITEST_MODULE = "@williamthorsen/nmr/vitest";
283
+ var SHARED_PRETTIER_MODULE = "@williamthorsen/nmr/prettier";
284
+ var INERT_PRETTIER_CONFIGS = [".prettierrc", ".prettierrc.{json,json5,yaml,yml,toml}"];
285
+ var RE_EXPORT_LINE_PATTERN = /^export\s*(?:\{\s*default\s*\}|\*)\s*from\s*['"]\.\.\/[^'"]*['"];?$/;
286
+ function allWorkspacePackagesCanBuild() {
287
+ const packagesDir = join(process.cwd(), "packages");
288
+ if (!existsSync(packagesDir)) return true;
289
+ const entries = readdirSync2(packagesDir, { withFileTypes: true });
290
+ const failing = [];
291
+ for (const entry of entries) {
292
+ if (!entry.isDirectory()) continue;
293
+ const pkgPath = `packages/${entry.name}/package.json`;
294
+ const content = readFile(pkgPath);
295
+ if (!content) continue;
296
+ const hasBuildOverride = /"build"\s*:/.test(content);
297
+ const hasDefaultBuildInputs = fileExists(`packages/${entry.name}/tsconfig.json`) && existsSync(join(packagesDir, entry.name, "src"));
298
+ if (!hasBuildOverride && !hasDefaultBuildInputs) {
299
+ failing.push(entry.name);
300
+ }
301
+ }
302
+ if (failing.length === 0) return true;
303
+ return {
304
+ ok: false,
305
+ detail: `missing build override or tsconfig.json + src/: ${failing.join(", ")}`
306
+ };
307
+ }
308
+ function checkNoMatchingFiles(patterns, cwd) {
309
+ const found = findFiles(patterns, cwd);
310
+ if (found.length === 0) return true;
311
+ return { ok: false, detail: formatPaths(found) };
312
+ }
313
+ function checkRootVitestConfig(baseName, exportName, cwd) {
314
+ const matches = findFiles([`${baseName}.${CONFIG_EXTENSIONS}`], cwd);
315
+ if (matches.length === 0) {
316
+ return { ok: false, detail: `${baseName}.ts is missing` };
317
+ }
318
+ const stale = matches.filter(
319
+ (relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), exportName, SHARED_VITEST_MODULE)
320
+ );
321
+ if (stale.length === 0) return true;
322
+ return { ok: false, detail: `does not import ${exportName} from ${SHARED_VITEST_MODULE}: ${stale.join(", ")}` };
323
+ }
324
+ function prettierConfigBuildsOnSharedConfig(cwd = process.cwd()) {
325
+ const configs = findFiles(
326
+ [`.prettierrc.${CONFIG_EXTENSIONS}`, `prettier.config.${CONFIG_EXTENSIONS}`],
327
+ //
328
+ cwd
329
+ );
330
+ if (configs.length === 0) {
331
+ return { ok: false, detail: describeMissingPrettierConfig(cwd) };
332
+ }
333
+ const stale = configs.filter(
334
+ (relativePath) => !importsSharedExport(readFileIn(cwd, relativePath), "definePrettierConfig", SHARED_PRETTIER_MODULE)
335
+ );
336
+ if (stale.length === 0) return true;
337
+ return {
338
+ ok: false,
339
+ detail: `does not import definePrettierConfig from ${SHARED_PRETTIER_MODULE}: ${stale.join(", ")}`
340
+ };
341
+ }
342
+ function describeMissingPrettierConfig(cwd) {
343
+ const inert = findFiles(INERT_PRETTIER_CONFIGS, cwd);
344
+ if (inert.length > 0) return `holds no code to call the factory: ${inert.join(", ")}`;
345
+ if (hasPrettierConfigKey(cwd)) {
346
+ return 'holds no code to call the factory: the "prettier" key in package.json';
347
+ }
348
+ return ".prettierrc.js is missing";
349
+ }
350
+ function hasPrettierConfigKey(cwd) {
351
+ const manifest = readFileIn(cwd, "package.json");
352
+ if (manifest === void 0) return false;
353
+ try {
354
+ const parsed = JSON.parse(manifest);
355
+ return isRecord(parsed) && parsed.prettier !== void 0;
356
+ } catch {
357
+ return false;
358
+ }
359
+ }
360
+ function codeQualityWorkflowDoesNotUseNmrPrepush() {
361
+ const content = readFile(".github/workflows/code-quality.yaml");
362
+ if (content === void 0) return true;
363
+ return !/check-command:\s*pnpm exec nmr prepush(\s|$)/.test(content);
364
+ }
365
+ function everyTestFileNamesItsTier(cwd = process.cwd()) {
366
+ const untiered = findTestFiles(cwd).filter((path2) => !hasTierInfix(path2));
367
+ if (untiered.length === 0) return true;
368
+ return { ok: false, detail: formatPaths(untiered) };
369
+ }
370
+ function findFiles(patterns, cwd) {
371
+ return globSync(patterns, { cwd, exclude: (path2) => SCAN_EXCLUDE_DIRS.has(basename(path2)) }).map((path2) => path2.split(sep).join("/")).toSorted();
372
+ }
373
+ function formatPaths(paths) {
374
+ return `${paths.length} found:
375
+ ${paths.map((path2) => ` ${path2}`).join("\n")}`;
376
+ }
377
+ function getMinVersion() {
378
+ const picked = { "version": "0.25.0" };
379
+ if (typeof picked.version !== "string") {
380
+ throw new TypeError("nmr/package.json: 'version' must be a string");
381
+ }
382
+ return picked.version;
383
+ }
384
+ function importsSharedExport(content, exportName, moduleSpecifier) {
385
+ if (content === void 0) return false;
386
+ const pattern = new RegExp(String.raw`import\s*\{[^}]*\b${exportName}\b[^}]*\}\s*from\s*['"]${moduleSpecifier}['"]`);
387
+ return pattern.test(content);
388
+ }
389
+ function isReExportOnly(content) {
390
+ if (content === void 0) return false;
391
+ const statements = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
392
+ return statements.length > 0 && statements.every((line) => RE_EXPORT_LINE_PATTERN.test(line));
393
+ }
394
+ function noReExportOnlyVitestConfigs(cwd = process.cwd()) {
395
+ const nonRootConfigs = findFiles([`**/vitest.config.${CONFIG_EXTENSIONS}`], cwd).filter((path2) => path2.includes("/"));
396
+ const reExports = nonRootConfigs.filter((path2) => isReExportOnly(readFileIn(cwd, path2)));
397
+ if (reExports.length === 0) return true;
398
+ return { ok: false, detail: formatPaths(reExports) };
399
+ }
400
+ function noRedundantRootScripts() {
401
+ const pkg = readPackageJson();
402
+ if (!pkg) return true;
403
+ const scripts = pkg.scripts;
404
+ if (!isRecord(scripts)) return true;
405
+ const builtInNames = Object.keys(getDefaultRootScripts());
406
+ const redundant = Object.keys(scripts).filter((name) => builtInNames.includes(name));
407
+ if (redundant.length === 0) return true;
408
+ return {
409
+ ok: false,
410
+ detail: `redundant: ${redundant.join(", ")}`
411
+ };
412
+ }
413
+ function noRetiredVitestConfigs(cwd = process.cwd()) {
414
+ return checkNoMatchingFiles(
415
+ [`**/vitest.standalone.config.${CONFIG_EXTENSIONS}`, `**/vitest.integration.config.${CONFIG_EXTENSIONS}`],
416
+ cwd
417
+ );
418
+ }
419
+ function noWorkspaceRunScriptReferences() {
420
+ const packagesDir = join(process.cwd(), "packages");
421
+ if (!existsSync(packagesDir)) return true;
422
+ const legacyPattern = /run-workspace-script|"pnpm\s+run\s+ws\b/;
423
+ const entries = readdirSync2(packagesDir, { withFileTypes: true });
424
+ const matches = [];
425
+ for (const entry of entries) {
426
+ if (!entry.isDirectory()) continue;
427
+ const content = readFile(`packages/${entry.name}/package.json`);
428
+ if (content && legacyPattern.test(content)) {
429
+ matches.push(entry.name);
430
+ }
431
+ }
432
+ if (matches.length === 0) return true;
433
+ return {
434
+ ok: false,
435
+ detail: `found in: ${matches.join(", ")}`
436
+ };
437
+ }
438
+ function readFileIn(cwd, relativePath) {
439
+ try {
440
+ return readFileSync(join(cwd, relativePath), "utf8");
441
+ } catch {
442
+ return void 0;
443
+ }
444
+ }
445
+ function scriptExists(name) {
446
+ const pkg = readPackageJson();
447
+ if (!pkg) return false;
448
+ const scripts = pkg.scripts;
449
+ return isRecord(scripts) && name in scripts;
450
+ }
451
+ function scriptMatches(name, pattern) {
452
+ const pkg = readPackageJson();
453
+ if (!pkg) return false;
454
+ const scripts = pkg.scripts;
455
+ if (!isRecord(scripts)) return false;
456
+ const value = scripts[name];
457
+ return typeof value === "string" && pattern.test(value);
458
+ }
459
+ function toolVersionsHasNoPnpm() {
460
+ const content = readFile(".tool-versions");
461
+ if (content === void 0) return true;
462
+ return !/^pnpm\s/m.test(content);
463
+ }
464
+ function vitestConfigBuildsOnSharedConfig(cwd = process.cwd()) {
465
+ return checkRootVitestConfig("vitest.config", "defineVitestConfig", cwd);
466
+ }
467
+ function vitestRootConfigBuildsOnSharedConfig(cwd = process.cwd()) {
468
+ return checkRootVitestConfig("vitest.root.config", "defineRootVitestConfig", cwd);
469
+ }
470
+ export {
471
+ codeQualityWorkflowDoesNotUseNmrPrepush,
472
+ default_default as default,
473
+ everyTestFileNamesItsTier,
474
+ noReExportOnlyVitestConfigs,
475
+ noRetiredVitestConfigs,
476
+ prettierConfigBuildsOnSharedConfig,
477
+ vitestConfigBuildsOnSharedConfig,
478
+ vitestRootConfigBuildsOnSharedConfig
479
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "version": 1,
3
+ "kits": [
4
+ {
5
+ "checklists": [
6
+ "nmr"
7
+ ],
8
+ "name": "default",
9
+ "path": "kits/default.js",
10
+ "readyupVersion": "0.23.0",
11
+ "source": "kits/default.ts",
12
+ "sourceHash": "f646e7fb",
13
+ "targetHash": "514c9b6f"
14
+ }
15
+ ]
16
+ }
package/AGENTS.md CHANGED
@@ -7,4 +7,6 @@ source: '@williamthorsen/nmr@0.0.0-source'
7
7
  - 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.
8
8
  - 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.
9
9
  - Never `npx nmr`: inside a git worktree it can resolve a different nmr from outside the tree. Fall back to `pnpm exec nmr`.
10
- - The rest is in `node_modules/@williamthorsen/nmr/README.md`: pre/post hooks (every `nmr X` auto-wraps as `nmr X:pre && nmr X && nmr X:post`), script overrides and their `""`/`":"` skip values, `nmr upgrade` ceilings (report-only until `nmr upgrade --write`), and the `nmr-compile` build (there is no repo-local build script).
10
+ - `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 both. Neither installs or binds a git hook.
11
+ - A check that already passed on the current working tree skips, printing `⏭️ <command>: passed …`. That is a real pass, not a stale one: any edit, addition, or deletion re-runs the command. A skipped command produces no files, so where you need an artifact rather than an exit status (a fresh `coverage/`), run `nmr --no-cache <command>`.
12
+ - The rest is in `node_modules/@williamthorsen/nmr/README.md`: pre/post hooks (every `nmr X` auto-wraps as `nmr X:pre && nmr X && nmr X:post`), script overrides and their `""`/`":"` skip values, `nmr upgrade` ceilings (report-only until `nmr upgrade --write`), the `nmr-compile` build (there is no repo-local build script), and the check-result cache's key, its `checkCache` config, and every condition that disables it.