@williamthorsen/release-kit 10.3.0 → 10.3.2

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 (40) hide show
  1. package/.readyup/kits/default.js +2 -2
  2. package/.readyup/kits/npm-auto-publish.js +1 -1
  3. package/.readyup/manifest.json +4 -4
  4. package/CHANGELOG.md +14 -0
  5. package/README.md +1 -19
  6. package/dist/esm/bin/release-kit.js +6 -5
  7. package/dist/esm/buildChangelogEntries.js +2 -1
  8. package/dist/esm/buildDependencyGraph.js +3 -6
  9. package/dist/esm/changelogJsonFile.js +2 -1
  10. package/dist/esm/changelogOverrides.js +3 -2
  11. package/dist/esm/checkWorkTypesDrift.js +5 -4
  12. package/dist/esm/createGithubReleaseCommand.js +2 -1
  13. package/dist/esm/deriveWorkspaceConfig.js +2 -2
  14. package/dist/esm/discoverWorkspaces.js +2 -1
  15. package/dist/esm/getCommitsSinceTarget.js +3 -5
  16. package/dist/esm/init/initCommand.js +4 -3
  17. package/dist/esm/init/scaffold.js +2 -1
  18. package/dist/esm/loadConfig.js +3 -2
  19. package/dist/esm/loadValidatedConfig.js +2 -1
  20. package/dist/esm/planReleaseNotesPreviews.js +2 -1
  21. package/dist/esm/planVersionBump.js +3 -6
  22. package/dist/esm/prepareCommand.js +8 -7
  23. package/dist/esm/previewTagPrefixes.js +2 -1
  24. package/dist/esm/publishCommand.js +3 -2
  25. package/dist/esm/pushCommand.js +2 -1
  26. package/dist/esm/readCurrentVersion.js +2 -1
  27. package/dist/esm/releaseFiles.js +2 -1
  28. package/dist/esm/releasePrepareMono.js +2 -5
  29. package/dist/esm/resolveCommandTags.js +3 -2
  30. package/dist/esm/resolveReleaseNotesConfig.js +2 -1
  31. package/dist/esm/sync-labels/generateCommand.js +3 -2
  32. package/dist/esm/sync-labels/initCommand.js +2 -1
  33. package/dist/esm/sync-labels/presets.js +2 -2
  34. package/dist/esm/sync-labels/syncCommand.js +2 -1
  35. package/dist/esm/syncWorkTypes.js +5 -4
  36. package/dist/esm/tagCommand.js +2 -1
  37. package/dist/esm/validateOverridesCommand.js +5 -7
  38. package/dist/esm/workTypesUtils.d.ts +0 -1
  39. package/dist/esm/workTypesUtils.js +0 -3
  40. package/package.json +15 -5
@@ -1,6 +1,6 @@
1
1
  /** @noformat — @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.24.0";
3
+ export const __readyupVersion = "0.27.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/default.ts
@@ -52,7 +52,7 @@ function detectRepoType() {
52
52
 
53
53
  // .readyup/kits/default.ts
54
54
  function getMinVersion() {
55
- const picked = { "version": "10.3.0" };
55
+ const picked = { "version": "10.3.2" };
56
56
  if (typeof picked["version"] !== "string") {
57
57
  throw new TypeError("release-kit/package.json: 'version' must be a string");
58
58
  }
@@ -1,6 +1,6 @@
1
1
  /** @noformat — @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.24.0";
3
+ export const __readyupVersion = "0.27.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/npm-auto-publish.ts
@@ -7,10 +7,10 @@
7
7
  ],
8
8
  "name": "default",
9
9
  "path": "kits/default.js",
10
- "readyupVersion": "0.24.0",
10
+ "readyupVersion": "0.27.0",
11
11
  "source": "kits/default.ts",
12
12
  "sourceHash": "fe239d01",
13
- "targetHash": "9bd8fb5d"
13
+ "targetHash": "6b363809"
14
14
  },
15
15
  {
16
16
  "checklists": [
@@ -19,10 +19,10 @@
19
19
  ],
20
20
  "name": "npm-auto-publish",
21
21
  "path": "kits/npm-auto-publish.js",
22
- "readyupVersion": "0.24.0",
22
+ "readyupVersion": "0.27.0",
23
23
  "source": "kits/npm-auto-publish.ts",
24
24
  "sourceHash": "ac484613",
25
- "targetHash": "f790e7d5"
25
+ "targetHash": "2e324215"
26
26
  }
27
27
  ]
28
28
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 10.3.2 — 2026-08-13
6
+
7
+ ### ♻️ Refactoring
8
+
9
+ - Adopt toolbelt.errors for error-message extraction and cause-chaining (#652)
10
+
11
+ Improves error reporting by adopting standardized ways of capturing and describing errors across the codebase.
12
+
13
+ ### ⚙️ Tooling
14
+
15
+ - Upgrade the lint config and clear its violations (#671)
16
+
17
+ Upgrades `@williamthorsen/eslint-config-typescript`, along with other config packages, and clears lint violations surfaced by the newly activated rules.
18
+
5
19
  ## 10.3.0 — 2026-08-08
6
20
 
7
21
  ### 🎉 Features
package/README.md CHANGED
@@ -4,25 +4,7 @@ Version-bumping and changelog-generation toolkit for release workflows.
4
4
 
5
5
  Provides a self-contained CLI that auto-discovers workspaces from `pnpm-workspace.yaml`, parses conventional commits, determines version bumps, updates `package.json` files, and generates changelogs from `git-cliff --context` output rendered in-process (with optional [editorial overrides](#editorial-overrides)).
6
6
 
7
- <!-- section:release-notes -->
8
- ## Release notes — v10.3.0 (2026-08-08)
9
-
10
- ### 🎉 Features
11
-
12
- - Stop reporting absence checks that pass (#610)
13
-
14
- A ReadyUp check that checks for a problem (such as an unwanted file or a stale config reference) now stays silent unless it finds one. The `nmr`, `release-kit`, and `v11y-check` ReadyUp kits previously printed a line for every such check on every run.
15
-
16
- - Stop reporting non-issues in the readyup kits (#613)
17
-
18
- Modifies the `release-kit` and `nmr` ReadyUp kits to avoid verbose reporting when an initial check determines that further checks aren't applicable. In particular, a repo with no `release-kit` config file gets a single line in the `release-kit` report, and a repo that publishes nothing now bypasses the `npm-auto-publish` kit instead of failing it.
19
-
20
- ### 🐛 Bug fixes
21
-
22
- - Publish build output atomically so a failed build cannot destroy it (#631)
23
-
24
- Fixes an issue where a failed or still-running `nmr build` could leave a package with no build output at all, breaking the tooling that a subsequent build depends on. Separately, the error reported when a package's build output is missing now names the correct recovery command.
25
- <!-- /section:release-notes -->
7
+ <!-- section:release-notes --><!-- /section:release-notes -->
26
8
 
27
9
  ## Installation
28
10
 
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { parseArgsOrExit, readPackageVersion, reportError } from '@williamthorsen/nmr-core';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { checkWorkTypesDrift } from "../checkWorkTypesDrift.js";
4
5
  import { commitCommand } from "../commitCommand.js";
5
6
  import { createGithubReleaseCommand } from "../createGithubReleaseCommand.js";
@@ -258,7 +259,6 @@ Options:
258
259
  }
259
260
  const args = process.argv.slice(2);
260
261
  const command = args[0];
261
- const flags = args.slice(1);
262
262
  if (command === '--version' || command === '-V') {
263
263
  console.info(VERSION);
264
264
  process.exit(0);
@@ -267,6 +267,7 @@ if (command === undefined || ['--help', '-h'].includes(command)) {
267
267
  showUsage();
268
268
  process.exit(0);
269
269
  }
270
+ const flags = args.slice(1);
270
271
  if (command === 'prepare') {
271
272
  if (flags.some((f) => f === '--help' || f === '-h')) {
272
273
  showPrepareHelp();
@@ -284,7 +285,7 @@ if (command === 'commit') {
284
285
  commitCommand(flags);
285
286
  }
286
287
  catch (error) {
287
- reportError(error instanceof Error ? error.message : String(error));
288
+ reportError(describeError(error));
288
289
  process.exit(1);
289
290
  }
290
291
  process.exit(0);
@@ -349,11 +350,11 @@ if (command === 'init') {
349
350
  }
350
351
  if (command === 'sync-labels') {
351
352
  const subcommand = flags[0];
352
- const subflags = flags.slice(1);
353
353
  if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
354
354
  showSyncLabelsHelp();
355
355
  process.exit(0);
356
356
  }
357
+ const subflags = flags.slice(1);
357
358
  if (subcommand === 'init') {
358
359
  if (subflags.some((f) => f === '--help' || f === '-h')) {
359
360
  showSyncLabelsInitHelp();
@@ -397,11 +398,11 @@ if (command === 'sync-labels') {
397
398
  }
398
399
  if (command === 'overrides') {
399
400
  const subcommand = flags[0];
400
- const subflags = flags.slice(1);
401
401
  if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
402
402
  showOverridesHelp();
403
403
  process.exit(0);
404
404
  }
405
+ const subflags = flags.slice(1);
405
406
  if (subcommand === 'validate') {
406
407
  if (subflags.some((f) => f === '--help' || f === '-h')) {
407
408
  showOverridesValidateHelp();
@@ -426,11 +427,11 @@ if (command === 'overrides') {
426
427
  }
427
428
  if (command === 'work-types') {
428
429
  const subcommand = flags[0];
429
- const subflags = flags.slice(1);
430
430
  if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
431
431
  showWorkTypesHelp();
432
432
  process.exit(0);
433
433
  }
434
+ const subflags = flags.slice(1);
434
435
  if (subcommand === 'check') {
435
436
  if (subflags.some((f) => f === '--help' || f === '-h')) {
436
437
  showWorkTypesCheckHelp();
@@ -1,3 +1,4 @@
1
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
1
2
  import { extractVersion } from "./changelogJsonUtils.js";
2
3
  import { DEFAULT_WORK_TYPES } from "./defaults.js";
3
4
  import { COMMIT_PREPROCESSOR_PATTERNS, PIPE_SCOPE_SOURCE } from "./parseCommitMessage.js";
@@ -31,7 +32,7 @@ export function buildChangelogEntries(config, tag, options) {
31
32
  return transformReleases(releases, devOnlySections);
32
33
  }
33
34
  catch (error) {
34
- throw new Error(`Failed to build changelog entries for tag ${tag}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
35
+ throw chainError(`Failed to build changelog entries for tag ${tag}`, error);
35
36
  }
36
37
  }
37
38
  function parseCliffContext(json) {
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
2
3
  function isPackageJsonSubset(value) {
3
4
  return typeof value === 'object' && value !== null;
4
5
  }
@@ -51,18 +52,14 @@ function readPackageJsonSubset(filePath) {
51
52
  content = readFileSync(filePath, 'utf8');
52
53
  }
53
54
  catch (error) {
54
- throw new Error(`Failed to read ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
55
- cause: error,
56
- });
55
+ throw chainError(`Failed to read ${filePath}`, error);
57
56
  }
58
57
  let parsed;
59
58
  try {
60
59
  parsed = JSON.parse(content);
61
60
  }
62
61
  catch (error) {
63
- throw new Error(`Failed to parse JSON in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
64
- cause: error,
65
- });
62
+ throw chainError(`Failed to parse JSON in ${filePath}`, error);
66
63
  }
67
64
  if (!isPackageJsonSubset(parsed)) {
68
65
  throw new Error(`Invalid package.json at ${filePath}`);
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import stringify from 'json-stringify-pretty-compact';
4
5
  import semver from 'semver';
5
6
  import { isChangelogEntry } from "./changelogJsonUtils.js";
@@ -30,7 +31,7 @@ function readExistingEntries(filePath) {
30
31
  return parsed.filter(isChangelogEntry);
31
32
  }
32
33
  catch (error) {
33
- console.warn(`Warning: could not parse existing ${filePath}: ${error instanceof Error ? error.message : String(error)}; treating as empty`);
34
+ console.warn(`Warning: could not parse existing ${filePath}: ${describeError(error)}; treating as empty`);
34
35
  return [];
35
36
  }
36
37
  }
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { isRecord } from "./typeGuards.js";
4
5
  const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
5
6
  const VALID_AUDIENCE_VALUES = new Set(['all', 'dev', 'skip']);
@@ -15,7 +16,7 @@ export function loadChangelogOverrides(path) {
15
16
  }
16
17
  catch (error) {
17
18
  return {
18
- errors: [`Failed to read override file ${path}: ${error instanceof Error ? error.message : String(error)}`],
19
+ errors: [`Failed to read override file ${path}: ${describeError(error)}`],
19
20
  };
20
21
  }
21
22
  let parsed;
@@ -24,7 +25,7 @@ export function loadChangelogOverrides(path) {
24
25
  }
25
26
  catch (error) {
26
27
  return {
27
- errors: [`Failed to parse override file ${path}: ${error instanceof Error ? error.message : String(error)}`],
28
+ errors: [`Failed to parse override file ${path}: ${describeError(error)}`],
28
29
  };
29
30
  }
30
31
  const result = validateChangelogOverrides(parsed);
@@ -2,7 +2,8 @@ import { readFileSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { formatErrorLine } from '@williamthorsen/nmr-core';
5
- import { buildFetchInit, errorMessage, hasExpectedTopLevelShape } from "./workTypesUtils.js";
5
+ import { describeError } from '@williamthorsen/toolbelt.errors';
6
+ import { buildFetchInit, hasExpectedTopLevelShape } from "./workTypesUtils.js";
6
7
  export const UPSTREAM_WORK_TYPES_URL = 'https://raw.githubusercontent.com/williamthorsen/codeassembly/main/packages/agents/content/skills/_data/work-types.json';
7
8
  function resolveDefaultLocalPath() {
8
9
  const moduleDir = dirname(fileURLToPath(import.meta.url));
@@ -20,7 +21,7 @@ export async function checkWorkTypesDrift(dependencies = {}) {
20
21
  catch (error) {
21
22
  return {
22
23
  exitCode: 3,
23
- message: `Local work-types.json is not valid JSON: ${errorMessage(error)}`,
24
+ message: `Local work-types.json is not valid JSON: ${describeError(error)}`,
24
25
  };
25
26
  }
26
27
  const fetchInit = buildFetchInit();
@@ -31,7 +32,7 @@ export async function checkWorkTypesDrift(dependencies = {}) {
31
32
  catch (error) {
32
33
  return {
33
34
  exitCode: 2,
34
- message: formatErrorLine(`Failed to fetch upstream work-types.json: ${errorMessage(error)}`),
35
+ message: formatErrorLine(`Failed to fetch upstream work-types.json: ${describeError(error)}`),
35
36
  };
36
37
  }
37
38
  if (response.status === 404) {
@@ -54,7 +55,7 @@ export async function checkWorkTypesDrift(dependencies = {}) {
54
55
  catch (error) {
55
56
  return {
56
57
  exitCode: 3,
57
- message: `Upstream work-types.json is not valid JSON: ${errorMessage(error)}`,
58
+ message: `Upstream work-types.json is not valid JSON: ${describeError(error)}`,
58
59
  };
59
60
  }
60
61
  if (!hasExpectedTopLevelShape(upstreamJson)) {
@@ -1,4 +1,5 @@
1
1
  import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { createGithubReleases } from "./createGithubRelease.js";
3
4
  import { formatPrivateSkip } from "./formatPrivateSkip.js";
4
5
  import { parseRequestedTags } from "./parseRequestedTags.js";
@@ -28,7 +29,7 @@ export async function createGithubReleaseCommand(argv) {
28
29
  outcome = createGithubReleases(publishableTags, changelogJsonOutputPath, dryRun, sectionOrder);
29
30
  }
30
31
  catch (error) {
31
- reportError(`Failed to create GitHub Releases: ${error instanceof Error ? error.message : String(error)}`);
32
+ reportError(`Failed to create GitHub Releases: ${describeError(error)}`);
32
33
  process.exit(1);
33
34
  }
34
35
  if (outcome.skipped.length > 0) {
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { basename } from 'node:path';
3
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
3
4
  import { isRecord } from "./typeGuards.js";
4
5
  export function deriveWorkspaceConfig(workspacePath) {
5
6
  const dir = basename(workspacePath);
@@ -9,8 +10,7 @@ export function deriveWorkspaceConfig(workspacePath) {
9
10
  parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
10
11
  }
11
12
  catch (error) {
12
- const message = error instanceof Error ? error.message : String(error);
13
- throw new Error(`Failed to read ${packageJsonPath}: ${message}`, { cause: error });
13
+ throw chainError(`Failed to read ${packageJsonPath}`, error);
14
14
  }
15
15
  const name = isRecord(parsed) ? parsed['name'] : undefined;
16
16
  if (typeof name !== 'string' || name.length === 0) {
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { glob } from 'glob';
4
5
  import { parse } from 'yaml';
5
6
  import { isRecord } from "./typeGuards.js";
@@ -13,7 +14,7 @@ export async function discoverWorkspaces() {
13
14
  content = readFileSync(workspaceFile, 'utf8');
14
15
  }
15
16
  catch (error) {
16
- console.warn(`Warning: Failed to read ${workspaceFile}: ${error instanceof Error ? error.message : String(error)}`);
17
+ console.warn(`Warning: Failed to read ${workspaceFile}: ${describeError(error)}`);
17
18
  return undefined;
18
19
  }
19
20
  const parsed = parse(content);
@@ -1,12 +1,10 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import { GIT_OUTPUT_LIMIT } from '@williamthorsen/nmr-core';
3
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
3
4
  const FIELD_SEPARATOR = '\u{1F}';
4
5
  function isNoTagError(err) {
5
6
  return typeof err === 'object' && err !== null && 'status' in err && err.status === 128;
6
7
  }
7
- function errorMessage(err) {
8
- return err instanceof Error ? err.message : String(err);
9
- }
10
8
  function findLatestTag(tagPrefixes) {
11
9
  if (tagPrefixes.length === 0) {
12
10
  throw new Error('findLatestTag: tagPrefixes must contain at least one entry');
@@ -24,7 +22,7 @@ function findLatestTag(tagPrefixes) {
24
22
  if (isNoTagError(error)) {
25
23
  return undefined;
26
24
  }
27
- throw new Error(`Failed to run 'git describe': ${errorMessage(error)}`, { cause: error });
25
+ throw chainError(`Failed to run 'git describe'`, error);
28
26
  }
29
27
  }
30
28
  const RELEASE_COMMIT_PREFIX = 'release:';
@@ -59,7 +57,7 @@ export function getCommitsSinceTarget(tagPrefixes, paths) {
59
57
  }).trim();
60
58
  }
61
59
  catch (error) {
62
- throw new Error(`Failed to run 'git log' for range '${range}': ${errorMessage(error)}`, { cause: error });
60
+ throw chainError(`Failed to run 'git log' for range '${range}'`, error);
63
61
  }
64
62
  if (logOutput === '') {
65
63
  return { tag, commits: [] };
@@ -1,4 +1,5 @@
1
1
  import { printError, printStep, printSuccess, reportWriteResult } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { hasPackageJson, isGitRepo, usesPnpm } from "./checks.js";
3
4
  import { detectRepoType } from "./detectRepoType.js";
4
5
  import { scaffoldFiles } from "./scaffold.js";
@@ -29,7 +30,7 @@ export function initCommand({ dryRun, force, withConfig }) {
29
30
  eligible = checkEligibility();
30
31
  }
31
32
  catch (error) {
32
- printError(`Eligibility check failed: ${error instanceof Error ? error.message : String(error)}`);
33
+ printError(`Eligibility check failed: ${describeError(error)}`);
33
34
  return 1;
34
35
  }
35
36
  if (!eligible)
@@ -40,7 +41,7 @@ export function initCommand({ dryRun, force, withConfig }) {
40
41
  repoType = detectRepoType();
41
42
  }
42
43
  catch (error) {
43
- printError(`Failed to detect repo type: ${error instanceof Error ? error.message : String(error)}`);
44
+ printError(`Failed to detect repo type: ${describeError(error)}`);
44
45
  return 1;
45
46
  }
46
47
  printSuccess(`Detected: ${repoType}`);
@@ -50,7 +51,7 @@ export function initCommand({ dryRun, force, withConfig }) {
50
51
  results = scaffoldFiles({ repoType, dryRun, overwrite: force, withConfig });
51
52
  }
52
53
  catch (error) {
53
- printError(`Failed to scaffold files: ${error instanceof Error ? error.message : String(error)}`);
54
+ printError(`Failed to scaffold files: ${describeError(error)}`);
54
55
  return 1;
55
56
  }
56
57
  for (const result of results) {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import { findPackageRoot, writeFileWithCheck } from '@williamthorsen/nmr-core';
4
+ import { describeError } from '@williamthorsen/toolbelt.errors';
4
5
  import { createGithubReleaseWorkflow, publishWorkflow, releaseConfigScript, releaseWorkflow } from "./templates.js";
5
6
  export function copyCliffTemplate(dryRun, overwrite) {
6
7
  const destPath = '.config/git-cliff.toml';
@@ -14,7 +15,7 @@ export function copyCliffTemplate(dryRun, overwrite) {
14
15
  content = readFileSync(templatePath, 'utf8');
15
16
  }
16
17
  catch (error) {
17
- const message = error instanceof Error ? error.message : String(error);
18
+ const message = describeError(error);
18
19
  return { filePath: destPath, outcome: 'failed', error: `Failed to read template ${templatePath}: ${message}` };
19
20
  }
20
21
  return writeFileWithCheck(destPath, content, { dryRun, overwrite });
@@ -9,6 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
9
9
  import { existsSync, readFileSync } from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { pathToFileURL } from 'node:url';
12
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
12
13
  import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_PROJECT_TAG_PREFIX, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from "./defaults.js";
13
14
  import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
14
15
  import { isRecord } from "./typeGuards.js";
@@ -23,14 +24,14 @@ export function readRootPackageVersion() {
23
24
  contents = readFileSync(absolutePath, 'utf8');
24
25
  }
25
26
  catch (error) {
26
- throw new Error(`Failed to read root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
27
+ throw chainError(`Failed to read root ${ROOT_PACKAGE_JSON_PATH}`, error);
27
28
  }
28
29
  let parsed;
29
30
  try {
30
31
  parsed = JSON.parse(contents);
31
32
  }
32
33
  catch (error) {
33
- throw new Error(`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
34
+ throw chainError(`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}`, error);
34
35
  }
35
36
  if (!isRecord(parsed)) {
36
37
  return { exists: true, version: undefined };
@@ -1,4 +1,5 @@
1
1
  import { reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { loadConfig } from "./loadConfig.js";
3
4
  import { validateConfig } from "./validateConfig.js";
4
5
  export async function loadValidatedConfig() {
@@ -7,7 +8,7 @@ export async function loadValidatedConfig() {
7
8
  raw = await loadConfig();
8
9
  }
9
10
  catch (error) {
10
- const message = error instanceof Error ? error.message : String(error);
11
+ const message = describeError(error);
11
12
  reportError(`Failed to load config: ${message}`);
12
13
  return { status: 'invalid' };
13
14
  }
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { extractVersion } from "./changelogJsonUtils.js";
4
5
  import { renderInjectedReadmeFromEntries } from "./injectReleaseNotesIntoReadme.js";
5
6
  export function planReleaseNotesPreviews(options) {
@@ -36,7 +37,7 @@ function readWorkspaceReadme(readmePath, warnings) {
36
37
  return { content: readFileSync(readmePath, 'utf8'), exists: true, unreadable: false };
37
38
  }
38
39
  catch (error) {
39
- const message = error instanceof Error ? error.message : String(error);
40
+ const message = describeError(error);
40
41
  warnings.push(`failed to read ${readmePath}: ${message}; skipping injected-README preview`);
41
42
  return { content: '', exists: false, unreadable: true };
42
43
  }
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
2
3
  import { bumpVersion } from "./bumpVersion.js";
3
4
  function isPackageJson(value) {
4
5
  return typeof value === 'object' && value !== null && 'version' in value && typeof value.version === 'string';
@@ -34,18 +35,14 @@ function readPackageJson(filePath) {
34
35
  content = readFileSync(filePath, 'utf8');
35
36
  }
36
37
  catch (error) {
37
- throw new Error(`Failed to read ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
38
- cause: error,
39
- });
38
+ throw chainError(`Failed to read ${filePath}`, error);
40
39
  }
41
40
  let parsed;
42
41
  try {
43
42
  parsed = JSON.parse(content);
44
43
  }
45
44
  catch (error) {
46
- throw new Error(`Failed to parse JSON in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
47
- cause: error,
48
- });
45
+ throw chainError(`Failed to parse JSON in ${filePath}`, error);
49
46
  }
50
47
  if (!isPackageJson(parsed)) {
51
48
  throw new Error(`No valid 'version' field found in ${filePath}`);
@@ -1,5 +1,6 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
4
5
  import { buildDependencyGraph } from "./buildDependencyGraph.js";
5
6
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
@@ -87,7 +88,7 @@ export async function prepareCommand(argv) {
87
88
  assertCleanWorkingTree();
88
89
  }
89
90
  catch (error) {
90
- reportError(error instanceof Error ? error.message : String(error));
91
+ reportError(describeError(error));
91
92
  process.exit(1);
92
93
  }
93
94
  }
@@ -97,7 +98,7 @@ export async function prepareCommand(argv) {
97
98
  discoveredPaths = await discoverWorkspaces();
98
99
  }
99
100
  catch (error) {
100
- reportError(`Failed to discover workspaces: ${error instanceof Error ? error.message : String(error)}`);
101
+ reportError(`Failed to discover workspaces: ${describeError(error)}`);
101
102
  process.exit(1);
102
103
  }
103
104
  if (discoveredPaths === undefined) {
@@ -127,7 +128,7 @@ function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion,
127
128
  config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
128
129
  }
129
130
  catch (error) {
130
- reportError(`Failed to resolve workspaces: ${error instanceof Error ? error.message : String(error)}`);
131
+ reportError(`Failed to resolve workspaces: ${describeError(error)}`);
131
132
  process.exit(1);
132
133
  }
133
134
  if (setVersion !== undefined && config.project !== undefined) {
@@ -190,7 +191,7 @@ async function loadAndValidateConfig() {
190
191
  rawConfig = await loadConfig();
191
192
  }
192
193
  catch (error) {
193
- reportError(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
194
+ reportError(`Failed to load config: ${describeError(error)}`);
194
195
  process.exit(1);
195
196
  }
196
197
  if (rawConfig === undefined) {
@@ -215,7 +216,7 @@ function runAndReport(computePlan, dryRun) {
215
216
  plan = computePlan();
216
217
  }
217
218
  catch (error) {
218
- reportError(error instanceof Error ? error.message : String(error));
219
+ reportError(describeError(error));
219
220
  process.stderr.write('No files were written; the working tree is unchanged.\n');
220
221
  process.exit(1);
221
222
  }
@@ -228,7 +229,7 @@ function runAndReport(computePlan, dryRun) {
228
229
  applyReleasePlan(plan);
229
230
  }
230
231
  catch (error) {
231
- reportError(error instanceof Error ? error.message : String(error));
232
+ reportError(describeError(error));
232
233
  process.exit(1);
233
234
  }
234
235
  const formatError = runFormatCommand(plan.formatCommand);
@@ -249,7 +250,7 @@ function runFormatCommand(formatCommand) {
249
250
  return undefined;
250
251
  }
251
252
  catch (error) {
252
- return error instanceof Error ? error.message : String(error);
253
+ return describeError(error);
253
254
  }
254
255
  }
255
256
  function reportPlanFiles(plan, dryRun) {
@@ -1,6 +1,7 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import { basename } from 'node:path';
3
3
  import { GIT_OUTPUT_LIMIT } from '@williamthorsen/nmr-core';
4
+ import { describeError } from '@williamthorsen/toolbelt.errors';
4
5
  import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
5
6
  import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
6
7
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
@@ -49,7 +50,7 @@ function buildPreviewRow(workspacePath, overridesByDir) {
49
50
  derivedPrefix = deriveWorkspaceConfig(workspacePath).tagPrefix;
50
51
  }
51
52
  catch (error) {
52
- derivationError = error instanceof Error ? error.message : String(error);
53
+ derivationError = describeError(error);
53
54
  }
54
55
  const derivedTagCount = derivedPrefix === null ? 0 : countTagsMatching(derivedPrefix);
55
56
  const declaredIdentities = overridesByDir.get(dir) ?? [];
@@ -1,6 +1,7 @@
1
1
  import { writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
4
+ import { describeError } from '@williamthorsen/toolbelt.errors';
4
5
  import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
5
6
  import { detectPackageManager } from "./detectPackageManager.js";
6
7
  import { formatPrivateSkip } from "./formatPrivateSkip.js";
@@ -23,7 +24,7 @@ export async function publishCommand(argv) {
23
24
  assertCleanWorkingTree();
24
25
  }
25
26
  catch (error) {
26
- reportError(error instanceof Error ? error.message : String(error));
27
+ reportError(describeError(error));
27
28
  process.exit(1);
28
29
  }
29
30
  }
@@ -73,7 +74,7 @@ export async function publishCommand(argv) {
73
74
  console.warn(` ${t}`);
74
75
  }
75
76
  }
76
- reportError(error instanceof Error ? error.message : String(error));
77
+ reportError(describeError(error));
77
78
  process.exit(1);
78
79
  }
79
80
  }
@@ -1,4 +1,5 @@
1
1
  import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { parseRequestedTags } from "./parseRequestedTags.js";
3
4
  import { pushRelease } from "./pushRelease.js";
4
5
  import { resolveCommandTags } from "./resolveCommandTags.js";
@@ -34,7 +35,7 @@ export async function pushCommand(argv) {
34
35
  }
35
36
  }
36
37
  catch (error) {
37
- reportError(error instanceof Error ? error.message : String(error));
38
+ reportError(describeError(error));
38
39
  process.exit(1);
39
40
  }
40
41
  }
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  function hasVersionField(value) {
3
4
  return typeof value === 'object' && value !== null && 'version' in value && typeof value.version === 'string';
4
5
  }
@@ -11,7 +12,7 @@ export function readCurrentVersion(filePath) {
11
12
  }
12
13
  }
13
14
  catch (error) {
14
- console.warn(`Failed to read current version from ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
15
+ console.warn(`Failed to read current version from ${filePath}: ${describeError(error)}`);
15
16
  }
16
17
  return undefined;
17
18
  }
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  export const RELEASE_TAGS_FILE = 'tmp/.release-tags';
4
5
  export const RELEASE_SUMMARY_FILE = 'tmp/.release-summary';
5
6
  export function resolveReleaseTagsPath() {
@@ -23,7 +24,7 @@ function describeTagsReadFailure(error) {
23
24
  if (getErrnoCode(error) === 'ENOENT') {
24
25
  return `No tags file found at ${tagsPath}. Run \`release-kit prepare\` first.`;
25
26
  }
26
- const detail = error instanceof Error ? error.message : String(error);
27
+ const detail = describeError(error);
27
28
  return `Cannot read the tags file at ${tagsPath}: ${detail}`;
28
29
  }
29
30
  function getErrnoCode(error) {
@@ -1,4 +1,5 @@
1
1
  import { join as joinPath } from 'node:path';
2
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
2
3
  import { buildChangelogEntries } from "./buildChangelogEntries.js";
3
4
  import { buildDependencyGraph } from "./buildDependencyGraph.js";
4
5
  import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
@@ -402,16 +403,12 @@ function planFormatCommand(config, tags, modifiedFiles) {
402
403
  function findWorkspace(workspaces, dir) {
403
404
  return workspaces.find((w) => w.dir === dir);
404
405
  }
405
- function wrapStageError(stageLabel, error) {
406
- const message = error instanceof Error ? error.message : String(error);
407
- return new Error(`${stageLabel}: ${message}`, { cause: error });
408
- }
409
406
  function tryStage(stageLabel, fn) {
410
407
  try {
411
408
  return fn();
412
409
  }
413
410
  catch (error) {
414
- throw wrapStageError(stageLabel, error);
411
+ throw chainError(stageLabel, error);
415
412
  }
416
413
  }
417
414
  function workspaceStageLabel(dir) {
@@ -1,4 +1,5 @@
1
1
  import { reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
3
4
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
4
5
  import { resolveReleaseTags } from "./resolveReleaseTags.js";
@@ -8,7 +9,7 @@ export async function resolveCommandTags(tags) {
8
9
  discoveredPaths = await discoverWorkspaces();
9
10
  }
10
11
  catch (error) {
11
- reportError(`Failed to discover workspaces: ${error instanceof Error ? error.message : String(error)}`);
12
+ reportError(`Failed to discover workspaces: ${describeError(error)}`);
12
13
  process.exit(1);
13
14
  }
14
15
  let workspaces;
@@ -22,7 +23,7 @@ export async function resolveCommandTags(tags) {
22
23
  }
23
24
  }
24
25
  catch (error) {
25
- reportError(`Failed to resolve workspaces: ${error instanceof Error ? error.message : String(error)}`);
26
+ reportError(`Failed to resolve workspaces: ${describeError(error)}`);
26
27
  process.exit(1);
27
28
  }
28
29
  let resolvedTags;
@@ -1,4 +1,5 @@
1
1
  import { reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG } from "./defaults.js";
3
4
  import { loadConfig, resolveWorkTypes } from "./loadConfig.js";
4
5
  import { validateConfig } from "./validateConfig.js";
@@ -9,7 +10,7 @@ export async function resolveReleaseNotesConfig(options = {}) {
9
10
  rawConfig = await loadConfig();
10
11
  }
11
12
  catch (error) {
12
- const message = error instanceof Error ? error.message : String(error);
13
+ const message = describeError(error);
13
14
  if (strictLoad) {
14
15
  reportError(`Failed to load config: ${message}`);
15
16
  process.exit(1);
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname } from 'node:path';
3
3
  import { reportError } from '@williamthorsen/nmr-core';
4
+ import { describeError } from '@williamthorsen/toolbelt.errors';
4
5
  import { stringify } from 'yaml';
5
6
  import { CONFIG_FILE_PATH } from "../loadConfig.js";
6
7
  import { loadValidatedConfig } from "../loadValidatedConfig.js";
@@ -46,7 +47,7 @@ export async function generateCommand({ check = false } = {}) {
46
47
  labels = resolveLabels(repoLabels);
47
48
  }
48
49
  catch (error) {
49
- const message = error instanceof Error ? error.message : String(error);
50
+ const message = describeError(error);
50
51
  reportError(`Failed to resolve labels: ${message}`);
51
52
  return 1;
52
53
  }
@@ -64,7 +65,7 @@ export async function generateCommand({ check = false } = {}) {
64
65
  writeFileSync(LABELS_OUTPUT_PATH, content, 'utf8');
65
66
  }
66
67
  catch (error) {
67
- const message = error instanceof Error ? error.message : String(error);
68
+ const message = describeError(error);
68
69
  reportError(`Failed to write ${LABELS_OUTPUT_PATH}: ${message}`);
69
70
  return 1;
70
71
  }
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { reportWriteResult, writeFileWithCheck } from '@williamthorsen/nmr-core';
3
+ import { describeError } from '@williamthorsen/toolbelt.errors';
3
4
  import { discoverWorkspaces } from "../discoverWorkspaces.js";
4
5
  import { CONFIG_FILE_PATH } from "../loadConfig.js";
5
6
  import { loadValidatedConfig } from "../loadValidatedConfig.js";
@@ -20,7 +21,7 @@ export async function syncLabelsInitCommand({ dryRun, force }) {
20
21
  workspacePaths = await discoverWorkspaces();
21
22
  }
22
23
  catch (error) {
23
- const message = error instanceof Error ? error.message : String(error);
24
+ const message = describeError(error);
24
25
  process.stderr.write(` Failed to discover workspaces: ${message}\n`);
25
26
  return 1;
26
27
  }
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { findPackageRoot } from '@williamthorsen/nmr-core';
5
+ import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
5
6
  import { parse } from 'yaml';
6
7
  import { isRecord } from "../typeGuards.js";
7
8
  export function hashPresetFile(presetName) {
@@ -22,8 +23,7 @@ export function loadPreset(presetName) {
22
23
  content = readFileSync(presetPath, 'utf8');
23
24
  }
24
25
  catch (error) {
25
- const message = error instanceof Error ? error.message : String(error);
26
- throw new Error(`Failed to read preset "${presetName}": ${message}`, { cause: error });
26
+ throw chainError(`Failed to read preset "${presetName}"`, error);
27
27
  }
28
28
  const parsed = parse(content);
29
29
  if (!Array.isArray(parsed)) {
@@ -1,6 +1,7 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { GIT_OUTPUT_LIMIT, reportError } from '@williamthorsen/nmr-core';
4
+ import { describeError } from '@williamthorsen/toolbelt.errors';
4
5
  import { checkRetiredSyncLabelsConfig } from "./retiredConfig.js";
5
6
  const WORKFLOW_FILE = '.github/workflows/sync-labels.yaml';
6
7
  function checkGhAvailable() {
@@ -28,7 +29,7 @@ export function syncLabelsCommand() {
28
29
  execSync('gh workflow run sync-labels.yaml', { stdio: 'inherit' });
29
30
  }
30
31
  catch (error) {
31
- const message = error instanceof Error ? error.message : String(error);
32
+ const message = describeError(error);
32
33
  reportError(`Failed to trigger workflow: ${message}`);
33
34
  return 1;
34
35
  }
@@ -2,9 +2,10 @@ import { readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { formatErrorLine } from '@williamthorsen/nmr-core';
5
+ import { describeError } from '@williamthorsen/toolbelt.errors';
5
6
  import { UPSTREAM_WORK_TYPES_URL } from "./checkWorkTypesDrift.js";
6
7
  import { isRecord } from "./typeGuards.js";
7
- import { buildFetchInit, errorMessage, hasExpectedTopLevelShape } from "./workTypesUtils.js";
8
+ import { buildFetchInit, hasExpectedTopLevelShape } from "./workTypesUtils.js";
8
9
  function resolveDefaultLocalPath() {
9
10
  const moduleDir = dirname(fileURLToPath(import.meta.url));
10
11
  return resolve(moduleDir, 'work-types.json');
@@ -35,7 +36,7 @@ export async function syncWorkTypes(dependencies = {}) {
35
36
  catch (error) {
36
37
  return {
37
38
  exitCode: 2,
38
- message: formatErrorLine(`Failed to fetch upstream work-types.json: ${errorMessage(error)}`),
39
+ message: formatErrorLine(`Failed to fetch upstream work-types.json: ${describeError(error)}`),
39
40
  };
40
41
  }
41
42
  if (!response.ok) {
@@ -52,7 +53,7 @@ export async function syncWorkTypes(dependencies = {}) {
52
53
  catch (error) {
53
54
  return {
54
55
  exitCode: 3,
55
- message: `Upstream work-types.json is not valid JSON: ${errorMessage(error)}`,
56
+ message: `Upstream work-types.json is not valid JSON: ${describeError(error)}`,
56
57
  };
57
58
  }
58
59
  if (!hasExpectedTopLevelShape(upstreamJson)) {
@@ -83,7 +84,7 @@ export async function syncWorkTypes(dependencies = {}) {
83
84
  catch (error) {
84
85
  return {
85
86
  exitCode: 2,
86
- message: formatErrorLine(`Failed to write ${localPath}: ${errorMessage(error)}`),
87
+ message: formatErrorLine(`Failed to write ${localPath}: ${describeError(error)}`),
87
88
  };
88
89
  }
89
90
  return {
@@ -1,4 +1,5 @@
1
1
  import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { createTags } from "./createTags.js";
3
4
  const tagFlagSchema = {
4
5
  dryRun: { long: '--dry-run', type: 'boolean' },
@@ -10,7 +11,7 @@ export function tagCommand(argv) {
10
11
  createTags({ dryRun, noGitChecks });
11
12
  }
12
13
  catch (error) {
13
- reportError(error instanceof Error ? error.message : String(error));
14
+ reportError(describeError(error));
14
15
  process.exit(1);
15
16
  }
16
17
  }
@@ -1,4 +1,5 @@
1
1
  import { formatErrorLine } from '@williamthorsen/nmr-core';
2
+ import { describeError } from '@williamthorsen/toolbelt.errors';
2
3
  import { buildChangelogEntries } from "./buildChangelogEntries.js";
3
4
  import { resolveOverridePath, validateAllChangelogOverrides, } from "./changelogOverrides.js";
4
5
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
@@ -16,21 +17,21 @@ export async function validateOverridesCommand(dependencies = {}) {
16
17
  rawConfig = await load();
17
18
  }
18
19
  catch (error) {
19
- return { exitCode: 2, message: formatErrorLine(`Failed to load config: ${errorMessage(error)}`) };
20
+ return { exitCode: 2, message: formatErrorLine(`Failed to load config: ${describeError(error)}`) };
20
21
  }
21
22
  let userConfig;
22
23
  try {
23
24
  userConfig = validateLoadedConfig(rawConfig);
24
25
  }
25
26
  catch (error) {
26
- return { exitCode: 2, message: errorMessage(error) };
27
+ return { exitCode: 2, message: describeError(error) };
27
28
  }
28
29
  let discoveredPaths;
29
30
  try {
30
31
  discoveredPaths = await discover();
31
32
  }
32
33
  catch (error) {
33
- return { exitCode: 2, message: formatErrorLine(`Failed to discover workspaces: ${errorMessage(error)}`) };
34
+ return { exitCode: 2, message: formatErrorLine(`Failed to discover workspaces: ${describeError(error)}`) };
34
35
  }
35
36
  let inputs;
36
37
  try {
@@ -40,7 +41,7 @@ export async function validateOverridesCommand(dependencies = {}) {
40
41
  : buildMonorepoInputs(discoveredPaths, userConfig, buildEntries);
41
42
  }
42
43
  catch (error) {
43
- return { exitCode: 2, message: formatErrorLine(`Failed to resolve overrides scope: ${errorMessage(error)}`) };
44
+ return { exitCode: 2, message: formatErrorLine(`Failed to resolve overrides scope: ${describeError(error)}`) };
44
45
  }
45
46
  const result = validate(inputs);
46
47
  return formatValidateOverridesResult(result);
@@ -70,9 +71,6 @@ function formatSummaryLine(errorCount, warningCount) {
70
71
  function pluralize(count, noun) {
71
72
  return count === 1 ? `${count} ${noun}` : `${count} ${noun}s`;
72
73
  }
73
- function errorMessage(error) {
74
- return error instanceof Error ? error.message : String(error);
75
- }
76
74
  function defaultBuildEntries(config, tagPattern, includePaths) {
77
75
  const options = {};
78
76
  if (tagPattern !== undefined) {
@@ -2,5 +2,4 @@ export declare function hasExpectedTopLevelShape(value: unknown): value is {
2
2
  tiers: unknown[];
3
3
  types: unknown[];
4
4
  };
5
- export declare function errorMessage(error: unknown): string;
6
5
  export declare function buildFetchInit(): RequestInit | undefined;
@@ -7,9 +7,6 @@ export function hasExpectedTopLevelShape(value) {
7
7
  }
8
8
  return Array.isArray(value.tiers) && Array.isArray(value.types);
9
9
  }
10
- export function errorMessage(error) {
11
- return error instanceof Error ? error.message : String(error);
12
- }
13
10
  export function buildFetchInit() {
14
11
  const token = process.env['GITHUB_TOKEN'];
15
12
  if (token === undefined || token === '') {
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "10.3.0",
3
+ "version": "10.3.2",
4
4
  "description": "Version-bumping and changelog-generation toolkit for release workflows",
5
- "keywords": [],
5
+ "keywords": [
6
+ "changelog",
7
+ "monorepo",
8
+ "release",
9
+ "semver",
10
+ "versioning"
11
+ ],
6
12
  "homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/release-kit#readme",
7
13
  "bugs": {
8
14
  "url": "https://github.com/williamthorsen/node-monorepo-tools/issues"
@@ -14,6 +20,9 @@
14
20
  },
15
21
  "license": "ISC",
16
22
  "author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
23
+ "sideEffects": [
24
+ "./dist/esm/bin/*.js"
25
+ ],
17
26
  "type": "module",
18
27
  "exports": {
19
28
  ".": {
@@ -39,16 +48,17 @@
39
48
  "CHANGELOG.md"
40
49
  ],
41
50
  "dependencies": {
51
+ "@williamthorsen/toolbelt.errors": "0.4.0",
42
52
  "glob": "13.0.6",
43
53
  "json-stringify-pretty-compact": "4.0.0",
44
54
  "semver": "7.8.5",
45
55
  "yaml": "2.9.0",
46
56
  "zod": "4.4.3",
47
- "@williamthorsen/nmr-core": "0.9.3"
57
+ "@williamthorsen/nmr-core": "0.10.0"
48
58
  },
49
59
  "devDependencies": {
50
- "@types/semver": "7.7.1",
51
- "readyup": "0.24.0",
60
+ "@types/semver": "7.8.0",
61
+ "readyup": "0.27.0",
52
62
  "smol-toml": "1.7.1"
53
63
  },
54
64
  "engines": {