@williamthorsen/release-kit 15.0.3 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.meta/changelog.json +66 -0
  2. package/.readyup/kits/default.js +13 -13
  3. package/.readyup/kits/npm-auto-publish.js +4 -4
  4. package/.readyup/manifest.json +12 -12
  5. package/CHANGELOG.md +19 -0
  6. package/README.md +7 -1
  7. package/dist/esm/applyChangelogOverrides.js +6 -6
  8. package/dist/esm/assertReleasesOnNpm.js +11 -11
  9. package/dist/esm/assertTaggedBaseline.js +5 -5
  10. package/dist/esm/bin/release-kit.js +16 -16
  11. package/dist/esm/buildChangelogEntries.d.ts +19 -19
  12. package/dist/esm/buildChangelogEntries.js +40 -36
  13. package/dist/esm/buildChangelogItem.js +24 -24
  14. package/dist/esm/buildDependencyGraph.js +23 -23
  15. package/dist/esm/buildReleasedResult.js +21 -19
  16. package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
  17. package/dist/esm/bumpVersion.js +8 -8
  18. package/dist/esm/changelogArchive.d.ts +1 -1
  19. package/dist/esm/changelogArchive.js +2 -2
  20. package/dist/esm/changelogJsonFile.js +5 -5
  21. package/dist/esm/changelogJsonUtils.js +3 -3
  22. package/dist/esm/changelogOverrides.d.ts +5 -5
  23. package/dist/esm/changelogOverrides.js +23 -23
  24. package/dist/esm/classifyChangelogCommit.js +4 -4
  25. package/dist/esm/commitCommand.js +3 -3
  26. package/dist/esm/compareVersions.js +13 -13
  27. package/dist/esm/configSchema.d.ts +2 -2
  28. package/dist/esm/configSchema.js +1 -1
  29. package/dist/esm/createGithubRelease.d.ts +4 -4
  30. package/dist/esm/createGithubRelease.js +10 -10
  31. package/dist/esm/createGithubReleaseCommand.js +9 -9
  32. package/dist/esm/createTags.d.ts +2 -2
  33. package/dist/esm/createTags.js +8 -8
  34. package/dist/esm/decideDirectReleases.d.ts +5 -5
  35. package/dist/esm/decideDirectReleases.js +25 -25
  36. package/dist/esm/decideRelease.d.ts +1 -1
  37. package/dist/esm/decideRelease.js +2 -2
  38. package/dist/esm/deriveDirectoryConfig.d.ts +1 -1
  39. package/dist/esm/deriveDirectoryConfig.js +18 -18
  40. package/dist/esm/deriveWorkspaceConfig.js +4 -4
  41. package/dist/esm/detectPackageManager.js +5 -5
  42. package/dist/esm/detectUndeclaredTagPrefixes.js +6 -6
  43. package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
  44. package/dist/esm/enumerateReleaseWindows.js +7 -7
  45. package/dist/esm/findTransitiveDependents.js +5 -5
  46. package/dist/esm/findUnroutedEntryScopes.js +11 -11
  47. package/dist/esm/format.d.ts +1 -1
  48. package/dist/esm/format.js +1 -1
  49. package/dist/esm/formatPrepareFiles.d.ts +3 -3
  50. package/dist/esm/formatPrepareFiles.js +10 -10
  51. package/dist/esm/formatPrepareWarnings.js +6 -6
  52. package/dist/esm/hasPrettierConfig.js +2 -2
  53. package/dist/esm/init/checks.js +5 -3
  54. package/dist/esm/init/detectRepoType.js +3 -3
  55. package/dist/esm/init/initCommand.d.ts +4 -4
  56. package/dist/esm/init/initCommand.js +7 -7
  57. package/dist/esm/init/parseJsonRecord.d.ts +1 -1
  58. package/dist/esm/init/parseJsonRecord.js +4 -4
  59. package/dist/esm/init/scaffold.d.ts +4 -4
  60. package/dist/esm/init/scaffold.js +15 -15
  61. package/dist/esm/init/templates.d.ts +10 -10
  62. package/dist/esm/init/templates.js +5 -5
  63. package/dist/esm/init/updateTemplatesCommand.d.ts +2 -2
  64. package/dist/esm/init/updateTemplatesCommand.js +14 -12
  65. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +1 -1
  66. package/dist/esm/injectReleaseNotesIntoReadme.js +6 -6
  67. package/dist/esm/injectSection.js +8 -8
  68. package/dist/esm/loadChangelogOverrides.d.ts +1 -1
  69. package/dist/esm/loadChangelogOverrides.js +9 -9
  70. package/dist/esm/loadConfig.d.ts +3 -3
  71. package/dist/esm/loadConfig.js +10 -10
  72. package/dist/esm/loadValidatedConfig.js +4 -4
  73. package/dist/esm/mergeConfig.d.ts +1 -1
  74. package/dist/esm/mergeConfig.js +27 -27
  75. package/dist/esm/monorepoConfigAssertions.js +15 -15
  76. package/dist/esm/npmRegistry.js +4 -4
  77. package/dist/esm/packsPath.js +3 -3
  78. package/dist/esm/parseChangeRecordBlock.js +33 -33
  79. package/dist/esm/parseCommitMessage.js +5 -5
  80. package/dist/esm/planChangelogFiles.js +8 -8
  81. package/dist/esm/planReleaseNotesPreviews.js +10 -10
  82. package/dist/esm/planVersionBump.js +24 -16
  83. package/dist/esm/prepareArgs.d.ts +9 -9
  84. package/dist/esm/prepareArgs.js +11 -11
  85. package/dist/esm/prepareCommand.js +23 -23
  86. package/dist/esm/prepareComposite.d.ts +4 -4
  87. package/dist/esm/prepareComposite.js +16 -16
  88. package/dist/esm/{releasePrepareTargets.d.ts → prepareReleaseTargets.d.ts} +9 -9
  89. package/dist/esm/{releasePrepareTargets.js → prepareReleaseTargets.js} +40 -40
  90. package/dist/esm/prepareRunContext.d.ts +1 -1
  91. package/dist/esm/prepareRunContext.js +5 -5
  92. package/dist/esm/prepareTarget.d.ts +3 -3
  93. package/dist/esm/prepareTarget.js +41 -39
  94. package/dist/esm/previewTagPrefixes.js +19 -19
  95. package/dist/esm/propagateBumps.js +8 -8
  96. package/dist/esm/publish.d.ts +2 -2
  97. package/dist/esm/publish.js +5 -5
  98. package/dist/esm/publishCommand.js +20 -20
  99. package/dist/esm/pushCommand.js +8 -8
  100. package/dist/esm/pushRelease.d.ts +1 -1
  101. package/dist/esm/pushRelease.js +2 -2
  102. package/dist/esm/readChangelogSections.d.ts +1 -1
  103. package/dist/esm/readChangelogSections.js +16 -16
  104. package/dist/esm/releasePlan.js +25 -25
  105. package/dist/esm/renderChangelogMarkdown.d.ts +4 -4
  106. package/dist/esm/renderChangelogMarkdown.js +18 -15
  107. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  108. package/dist/esm/renderReleaseNotes.js +16 -16
  109. package/dist/esm/reportPrepare.js +34 -34
  110. package/dist/esm/resolveCommandTags.js +9 -9
  111. package/dist/esm/resolveReleaseDeclaration.d.ts +2 -2
  112. package/dist/esm/resolveReleaseDeclaration.js +22 -22
  113. package/dist/esm/resolveReleaseTags.js +17 -15
  114. package/dist/esm/resolveReleaseTargets.js +2 -2
  115. package/dist/esm/showTagPrefixesCommand.js +4 -4
  116. package/dist/esm/sync-labels/generateCommand.d.ts +3 -3
  117. package/dist/esm/sync-labels/generateCommand.js +10 -10
  118. package/dist/esm/sync-labels/initCommand.d.ts +3 -3
  119. package/dist/esm/sync-labels/initCommand.js +15 -15
  120. package/dist/esm/sync-labels/presets.js +3 -3
  121. package/dist/esm/sync-labels/resolveLabels.js +13 -13
  122. package/dist/esm/sync-labels/templates.d.ts +2 -2
  123. package/dist/esm/sync-labels/templates.js +1 -1
  124. package/dist/esm/tagCommand.js +4 -4
  125. package/dist/esm/topologicalSort.d.ts +2 -2
  126. package/dist/esm/topologicalSort.js +7 -7
  127. package/dist/esm/types.d.ts +3 -3
  128. package/dist/esm/validateAllChangelogOverrides.js +9 -9
  129. package/dist/esm/validateConfig.d.ts +1 -1
  130. package/dist/esm/validateConfig.js +60 -50
  131. package/dist/esm/validateOverridesCommand.js +2 -2
  132. package/docs/changelogs.md +5 -5
  133. package/docs/configuration.md +1 -1
  134. package/docs/work-types.md +1 -1
  135. package/package.json +1 -1
@@ -1,4 +1,70 @@
1
1
  [
2
+ {
3
+ "version": "16.0.0",
4
+ "date": "2026-10-28",
5
+ "sections": [
6
+ {
7
+ "title": "🎉 Features",
8
+ "audience": "all",
9
+ "items": [
10
+ {
11
+ "description": "Renames the `changelogMarkdown.keepVersions` config key to `changelogMarkdown.keptVersionCount` and makes config validation reject the old key with an error that names the replacement. (#1134)",
12
+ "breaking": true,
13
+ "migration": "Rename `changelogMarkdown.keepVersions` to `changelogMarkdown.keptVersionCount` in `.config/release-kit.config.ts`, keeping the same value.",
14
+ "hash": "3d44603845a461295a5a62eee60cf151b5291f1a",
15
+ "entry": 1
16
+ }
17
+ ]
18
+ },
19
+ {
20
+ "title": "♻️ Refactoring",
21
+ "audience": "dev",
22
+ "items": [
23
+ {
24
+ "description": "Renames the boolean options, fields, and parameters in `release-kit` to take an `is`, `has`, `does`, or `should` prefix, such as `dryRun` to `isDryRun` and `exists` to `doesExist`, and leaves every CLI flag spelling unchanged. (#1132)",
25
+ "hash": "dcb78f0657c580eb7c7be94859e4185ae9f3d3be",
26
+ "entry": 1
27
+ },
28
+ {
29
+ "description": "Renames the values and type fields declared in `release-kit`'s source files from the first alphabetically through `prepareTarget.ts` so that each name ends with its kind, such as `parsedJson` for `parsed` and `unreleasedReading` for the history field `unreleased`, without changing behavior. (#1133)",
30
+ "hash": "eae8b6458dca1927393d64626d4731fc7c9f9df1",
31
+ "entry": 1
32
+ },
33
+ {
34
+ "description": "Renames values and type fields across release-kit's source so that each name ends with its kind, such as `applied` to `appliedWrites` and `TargetPrepareOptions.only` to `onlyTargetIds`. (#1134)",
35
+ "hash": "3d44603845a461295a5a62eee60cf151b5291f1a",
36
+ "entry": 2
37
+ },
38
+ {
39
+ "description": "Renames `release-kit`'s source functions and test helpers so that each name starts with a transitive verb that states its action, for example `releasePrepareTargets` to `prepareReleaseTargets` (with its module and test files), `publishWorkflow` to `renderPublishWorkflow`, and `toChangelogEntries` to `convertToChangelogEntries`, without changing the exports of `.` or `./config`. (#1137)",
40
+ "hash": "a84d9ce0652754a417b85c3f9cd14a6e400c7b51",
41
+ "entry": 1
42
+ },
43
+ {
44
+ "description": "Renames identifiers in `release-kit` so that each measurement ends with its unit, such as `timestamp` becoming `timestampSec` on `ReleaseWindow` and `SUBJECT_COLUMN_BUDGET` becoming `SUBJECT_BUDGET_COLUMNS`, and so that obscure abbreviations are spelled out, such as `pkg` becoming `packageJson` and `dep` becoming `dependency`. (#1138)",
45
+ "hash": "f75938c0bfe9365a76b15aa6b4b9d195046c8c78",
46
+ "entry": 1
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "title": "🧪 Tests",
52
+ "audience": "dev",
53
+ "items": [
54
+ {
55
+ "description": "Renames the values declared in release-kit's `src/__tests__` files, from the first one alphabetically through `prepareCommand.unit.test.ts`, so that each name ends with its kind, such as `underlying` to `underlyingError` and `unreleased` to `unreleasedReading`. (#1135)",
56
+ "hash": "b98d26e24e86835ce6edc78f53a964bcc84472fb",
57
+ "entry": 1
58
+ },
59
+ {
60
+ "description": "Suffixes the values declared in release-kit's remaining tests with their kind, such as `known` to `knownFlags`, `silent` to `silencedConsole`, and `actual` to `actualModule`, in the `src/__tests__` files after `prepareCommand.unit.test.ts` and the tests under `src/init/__tests__` and `src/sync-labels/__tests__`. (#1136)",
61
+ "hash": "1e947eb5c2d168ffd3d1cf9cf8b0b4e720eaf991",
62
+ "entry": 1
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ },
2
68
  {
3
69
  "version": "15.0.3",
4
70
  "date": "2026-10-27",
@@ -24,14 +24,14 @@ function isRecord(value) {
24
24
  }
25
25
 
26
26
  // src/init/parseJsonRecord.ts
27
- function parseJsonRecord(raw) {
28
- let parsed;
27
+ function parseJsonRecord(jsonText) {
28
+ let parsedJson;
29
29
  try {
30
- parsed = JSON.parse(raw);
30
+ parsedJson = JSON.parse(jsonText);
31
31
  } catch {
32
32
  return void 0;
33
33
  }
34
- return isRecord(parsed) ? parsed : void 0;
34
+ return isRecord(parsedJson) ? parsedJson : void 0;
35
35
  }
36
36
 
37
37
  // src/init/detectRepoType.ts
@@ -40,9 +40,9 @@ function detectRepoType() {
40
40
  return "monorepo";
41
41
  }
42
42
  if (existsSync("package.json")) {
43
- const raw = readFileSync("package.json", "utf8");
44
- const pkg = parseJsonRecord(raw);
45
- if (pkg !== void 0 && Array.isArray(pkg["workspaces"])) {
43
+ const manifestJson = readFileSync("package.json", "utf8");
44
+ const packageJson = parseJsonRecord(manifestJson);
45
+ if (packageJson !== void 0 && Array.isArray(packageJson["workspaces"])) {
46
46
  return "monorepo";
47
47
  }
48
48
  }
@@ -67,9 +67,9 @@ function packsPath(filesField, path) {
67
67
  var GLOB_TOKEN_PATTERN = /\*\*\/|\*\*|\*|\?|[^*?]+/g;
68
68
  function compileEntry(entry) {
69
69
  if (entry.startsWith("!")) return void 0;
70
- const normalized = entry.replace(/^\.?\//, "").replace(/\/+$/, "");
71
- if (normalized === "") return void 0;
72
- return new RegExp(`^${translateGlob(normalized)}$`);
70
+ const normalizedEntry = entry.replace(/^\.?\//, "").replace(/\/+$/, "");
71
+ if (normalizedEntry === "") return void 0;
72
+ return new RegExp(`^${translateGlob(normalizedEntry)}$`);
73
73
  }
74
74
  function isStringArray(value) {
75
75
  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
@@ -105,7 +105,7 @@ function translateGlob(pattern) {
105
105
  function changelogArchivesAreEnabled() {
106
106
  const content = readFile(".config/release-kit.config.ts");
107
107
  if (content === void 0) return false;
108
- return /changelogMarkdown\s*:\s*\{[^}]*keepVersions\s*:/.test(content);
108
+ return /changelogMarkdown\s*:\s*\{[^}]*keptVersionCount\s*:/.test(content);
109
109
  }
110
110
  function changelogJsonIsEnabled() {
111
111
  const content = readFile(".config/release-kit.config.ts");
@@ -234,7 +234,7 @@ var CREATE_GITHUB_RELEASE_WORKFLOW_HASH_SINGLE = "d6de961de913c67b08dfa7e07f7349
234
234
 
235
235
  // .readyup/kits/default.ts
236
236
  function getMinVersion() {
237
- const picked = { "version": "15.0.3" };
237
+ const picked = { "version": "16.0.0" };
238
238
  if (typeof picked["version"] !== "string") {
239
239
  throw new TypeError("release-kit/package.json: 'version' must be a string");
240
240
  }
@@ -405,7 +405,7 @@ var default_default = defineRdyKit({
405
405
  severity: "warn",
406
406
  skip: () => {
407
407
  if (!hasPublishablePackages()) return "no publishable packages";
408
- return !changelogArchivesAreEnabled() && "changelogMarkdown.keepVersions is not set";
408
+ return !changelogArchivesAreEnabled() && "changelogMarkdown.keptVersionCount is not set";
409
409
  },
410
410
  check: () => packagesShipChangelogArchives(),
411
411
  fix: 'Add "changelogs/" to the files field of each affected package.json, so that the links from CHANGELOG.md to its archive files resolve in the published package'
@@ -60,16 +60,16 @@ function lookUpNpmPackage(packageName, registry) {
60
60
  return classifyNpmPackageLookup(runNpmJson(args));
61
61
  }
62
62
  function readNpmError(stdout) {
63
- let parsed;
63
+ let parsedJson;
64
64
  try {
65
- parsed = JSON.parse(stdout);
65
+ parsedJson = JSON.parse(stdout);
66
66
  } catch {
67
67
  return void 0;
68
68
  }
69
- if (!isRecord(parsed) || !isRecord(parsed["error"]) || typeof parsed["error"]["code"] !== "string") {
69
+ if (!isRecord(parsedJson) || !isRecord(parsedJson["error"]) || typeof parsedJson["error"]["code"] !== "string") {
70
70
  return void 0;
71
71
  }
72
- const { code, summary } = parsed["error"];
72
+ const { code, summary } = parsedJson["error"];
73
73
  return { code, summary: typeof summary === "string" ? summary : "" };
74
74
  }
75
75
  function runNpmJson(args) {
@@ -7,7 +7,7 @@
7
7
  "checklist": "release-kit",
8
8
  "checks": [
9
9
  "@williamthorsen/release-kit in devDependencies",
10
- "@williamthorsen/release-kit >= 15.0.3",
10
+ "@williamthorsen/release-kit >= 16.0.0",
11
11
  "release.yaml workflow exists",
12
12
  "release.yaml matches template",
13
13
  "release.yaml does not reference deprecated tag ref",
@@ -43,12 +43,12 @@
43
43
  "esbuildVersion": "0.28.2",
44
44
  "inputs": [
45
45
  {
46
- "hash": "ad3666b8",
46
+ "hash": "56566d0b",
47
47
  "kind": "module",
48
48
  "path": "kits/default.ts"
49
49
  },
50
50
  {
51
- "hash": "45fc235f",
51
+ "hash": "dafe3ffb",
52
52
  "kind": "module",
53
53
  "path": "lib/changelog-checks.ts"
54
54
  },
@@ -63,7 +63,7 @@
63
63
  "path": "lib/workflow-hashes.ts"
64
64
  },
65
65
  {
66
- "hash": "5c98064e",
66
+ "hash": "de7d08de",
67
67
  "kind": "inline",
68
68
  "path": "../package.json",
69
69
  "paths": [
@@ -71,22 +71,22 @@
71
71
  ]
72
72
  },
73
73
  {
74
- "hash": "40404aa0",
74
+ "hash": "694186f4",
75
75
  "kind": "module",
76
76
  "path": "../src/changelogArchive.ts"
77
77
  },
78
78
  {
79
- "hash": "43bb0452",
79
+ "hash": "65fa2f33",
80
80
  "kind": "module",
81
81
  "path": "../src/init/detectRepoType.ts"
82
82
  },
83
83
  {
84
- "hash": "68354f97",
84
+ "hash": "9ca0f6bf",
85
85
  "kind": "module",
86
86
  "path": "../src/init/parseJsonRecord.ts"
87
87
  },
88
88
  {
89
- "hash": "1c9cbce9",
89
+ "hash": "2210df36",
90
90
  "kind": "module",
91
91
  "path": "../src/packsPath.ts"
92
92
  },
@@ -100,8 +100,8 @@
100
100
  "path": "kits/default.js",
101
101
  "readyupVersion": "0.44.0",
102
102
  "source": "kits/default.ts",
103
- "sourceHash": "ad3666b8",
104
- "targetHash": "35d9bb56"
103
+ "sourceHash": "56566d0b",
104
+ "targetHash": "43d5913f"
105
105
  },
106
106
  {
107
107
  "checklistChecks": [
@@ -145,7 +145,7 @@
145
145
  "path": "../src/init/hasProvenance.ts"
146
146
  },
147
147
  {
148
- "hash": "3e39b9be",
148
+ "hash": "bb8746b3",
149
149
  "kind": "module",
150
150
  "path": "../src/npmRegistry.ts"
151
151
  },
@@ -160,7 +160,7 @@
160
160
  "readyupVersion": "0.44.0",
161
161
  "source": "kits/npm-auto-publish.ts",
162
162
  "sourceHash": "682fdd4a",
163
- "targetHash": "fac6bc8b"
163
+ "targetHash": "84f31b6d"
164
164
  }
165
165
  ]
166
166
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 16.0.0 — 2026-10-28
6
+
7
+ ### 🎉 Features
8
+
9
+ - 🚨 **Breaking:** Renames the `changelogMarkdown.keepVersions` config key to `changelogMarkdown.keptVersionCount` and makes config validation reject the old key with an error that names the replacement. (#1134)
10
+
11
+ ### ♻️ Refactoring
12
+
13
+ - Renames the boolean options, fields, and parameters in `release-kit` to take an `is`, `has`, `does`, or `should` prefix, such as `dryRun` to `isDryRun` and `exists` to `doesExist`, and leaves every CLI flag spelling unchanged. (#1132)
14
+ - Renames the values and type fields declared in `release-kit`'s source files from the first alphabetically through `prepareTarget.ts` so that each name ends with its kind, such as `parsedJson` for `parsed` and `unreleasedReading` for the history field `unreleased`, without changing behavior. (#1133)
15
+ - Renames values and type fields across release-kit's source so that each name ends with its kind, such as `applied` to `appliedWrites` and `TargetPrepareOptions.only` to `onlyTargetIds`. (#1134)
16
+ - Renames `release-kit`'s source functions and test helpers so that each name starts with a transitive verb that states its action, for example `releasePrepareTargets` to `prepareReleaseTargets` (with its module and test files), `publishWorkflow` to `renderPublishWorkflow`, and `toChangelogEntries` to `convertToChangelogEntries`, without changing the exports of `.` or `./config`. (#1137)
17
+ - Renames identifiers in `release-kit` so that each measurement ends with its unit, such as `timestamp` becoming `timestampSec` on `ReleaseWindow` and `SUBJECT_COLUMN_BUDGET` becoming `SUBJECT_BUDGET_COLUMNS`, and so that obscure abbreviations are spelled out, such as `pkg` becoming `packageJson` and `dep` becoming `dependency`. (#1138)
18
+
19
+ ### 🧪 Tests
20
+
21
+ - Renames the values declared in release-kit's `src/__tests__` files, from the first one alphabetically through `prepareCommand.unit.test.ts`, so that each name ends with its kind, such as `underlying` to `underlyingError` and `unreleased` to `unreleasedReading`. (#1135)
22
+ - Suffixes the values declared in release-kit's remaining tests with their kind, such as `known` to `knownFlags`, `silent` to `silencedConsole`, and `actual` to `actualModule`, in the `src/__tests__` files after `prepareCommand.unit.test.ts` and the tests under `src/init/__tests__` and `src/sync-labels/__tests__`. (#1136)
23
+
5
24
  ## 15.0.3 — 2026-10-27
6
25
 
7
26
  ### ♻️ Refactoring
package/README.md CHANGED
@@ -6,7 +6,13 @@ Version-bumping and changelog-generation toolkit for release workflows.
6
6
 
7
7
  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 the same commit history that decides each bump (with optional [editorial overrides](docs/editorial-overrides.md)).
8
8
 
9
- <!-- section:release-notes --><!-- /section:release-notes -->
9
+ <!-- section:release-notes -->
10
+ ## Release notes — v16.0.0 (2026-10-28)
11
+
12
+ ### 🎉 Features
13
+
14
+ - 🚨 **Breaking:** Renames the `changelogMarkdown.keepVersions` config key to `changelogMarkdown.keptVersionCount` and makes config validation reject the old key with an error that names the replacement. (#1134)
15
+ <!-- /section:release-notes -->
10
16
 
11
17
  ## Installation
12
18
 
@@ -55,10 +55,10 @@ export function resolveOverrideKey(overrideKey, positionsByHash) {
55
55
  if (hashes.length > 1) {
56
56
  return { kind: 'ambiguous', hashes };
57
57
  }
58
- const available = [...(positionsByHash.get(hash) ?? [])];
58
+ const availablePositions = [...(positionsByHash.get(hash) ?? [])];
59
59
  const positions = entry === undefined
60
- ? available.toSorted((left, right) => (left ?? 0) - (right ?? 0))
61
- : available.filter((position) => position === entry);
60
+ ? availablePositions.toSorted((left, right) => (left ?? 0) - (right ?? 0))
61
+ : availablePositions.filter((position) => position === entry);
62
62
  return positions.length === 0 ? { kind: 'none' } : { kind: 'items', hash, positions };
63
63
  }
64
64
  function applyOverridesToItems(items, itemIdToOverride) {
@@ -133,10 +133,10 @@ function resolveOverrideKeys(positionsByHash, overrides) {
133
133
  }
134
134
  const { hash, positions } = resolution;
135
135
  if (parseOverrideKey(overrideKey).entry === undefined && positions.length > 1) {
136
- const fieldsSet = ['description', 'body'].filter((field) => override[field] !== undefined);
137
- if (fieldsSet.length > 0) {
136
+ const setFields = ['description', 'body'].filter((field) => override[field] !== undefined);
137
+ if (setFields.length > 0) {
138
138
  const ordinalKeys = positions.map((position) => `${overrideKey}:${position}`).join(', ');
139
- errors.push(`Override key '${overrideKey}' sets ${fieldsSet.join(' and ')} on a commit with several items; use ${ordinalKeys}`);
139
+ errors.push(`Override key '${overrideKey}' sets ${setFields.join(' and ')} on a commit with several items; use ${ordinalKeys}`);
140
140
  continue;
141
141
  }
142
142
  }
@@ -4,8 +4,8 @@ import { isPublishableManifest } from "./isPublishableManifest.js";
4
4
  import { lookUpNpmPackage } from "./npmRegistry.js";
5
5
  import { isRecord } from "./typeGuards.js";
6
6
  export function assertReleasesOnNpm(workspacePaths) {
7
- const unpublished = [];
8
- const unverifiable = [];
7
+ const unpublishedCandidates = [];
8
+ const unverifiableCandidates = [];
9
9
  for (const workspacePath of workspacePaths) {
10
10
  const candidate = buildNpmCandidate(workspacePath);
11
11
  if (candidate === undefined) {
@@ -13,16 +13,16 @@ export function assertReleasesOnNpm(workspacePaths) {
13
13
  }
14
14
  const lookup = lookUpNpmPackage(candidate.name, candidate.registry);
15
15
  if (lookup.status === 'unpublished') {
16
- unpublished.push(candidate);
16
+ unpublishedCandidates.push(candidate);
17
17
  }
18
18
  else if (lookup.status === 'unverifiable') {
19
- unverifiable.push({ candidate, detail: lookup.detail });
19
+ unverifiableCandidates.push({ candidate, detail: lookup.detail });
20
20
  }
21
21
  }
22
- if (unpublished.length === 0 && unverifiable.length === 0) {
22
+ if (unpublishedCandidates.length === 0 && unverifiableCandidates.length === 0) {
23
23
  return;
24
24
  }
25
- throw new Error(describeRefusal(unpublished, unverifiable));
25
+ throw new Error(describeRefusal(unpublishedCandidates, unverifiableCandidates));
26
26
  }
27
27
  function buildNpmCandidate(workspacePath) {
28
28
  const manifest = JSON.parse(readFileSync(join(workspacePath, 'package.json'), 'utf8'));
@@ -37,19 +37,19 @@ function buildNpmCandidate(workspacePath) {
37
37
  ...(typeof registry === 'string' && { registry }),
38
38
  };
39
39
  }
40
- function describeRefusal(unpublished, unverifiable) {
40
+ function describeRefusal(unpublishedCandidates, unverifiableCandidates) {
41
41
  const sections = ['Cannot prepare the release: npm must already have every package that the release publishes.'];
42
- if (unpublished.length > 0) {
42
+ if (unpublishedCandidates.length > 0) {
43
43
  sections.push([
44
44
  'Not on npm yet:',
45
- ...unpublished.map(({ name, workspacePath }) => ` - ${name}: run "npm publish --access public" from ${workspacePath}`),
45
+ ...unpublishedCandidates.map(({ name, workspacePath }) => ` - ${name}: run "npm publish --access public" from ${workspacePath}`),
46
46
  'Then register each one as a trusted publisher; "rdy run --from npm:@williamthorsen/release-kit npm-auto-publish" prints the command.',
47
47
  ].join('\n'));
48
48
  }
49
- if (unverifiable.length > 0) {
49
+ if (unverifiableCandidates.length > 0) {
50
50
  sections.push([
51
51
  'Could not confirm on npm:',
52
- ...unverifiable.map(({ candidate, detail }) => ` - ${candidate.name}: ${detail}`),
52
+ ...unverifiableCandidates.map(({ candidate, detail }) => ` - ${candidate.name}: ${detail}`),
53
53
  ].join('\n'));
54
54
  }
55
55
  return sections.join('\n\n');
@@ -4,10 +4,10 @@ import { readChangelogEntries } from "./changelogJsonUtils.js";
4
4
  import { readCurrentVersion } from "./planVersionBump.js";
5
5
  import { readChangelogSections } from "./readChangelogSections.js";
6
6
  export function assertTaggedBaseline(findings) {
7
- const untagged = findings.filter((finding) => finding !== undefined);
8
- if (untagged.length === 0)
7
+ const untaggedFindings = findings.filter((finding) => finding !== undefined);
8
+ if (untaggedFindings.length === 0)
9
9
  return;
10
- const lines = untagged.map(({ label, version, tag }) => ` - ${label}: ${version} (create tag ${tag})`);
10
+ const lines = untaggedFindings.map(({ label, version, tag }) => ` - ${label}: ${version} (create tag ${tag})`);
11
11
  throw new Error([
12
12
  'The current version is recorded in the changelog, but its tag is not the previous tag reachable from HEAD:',
13
13
  ...lines,
@@ -31,9 +31,9 @@ export function findUntaggedBaseline(target, config) {
31
31
  function readRecordedVersions(changelogPaths, config) {
32
32
  const versions = new Set();
33
33
  for (const changelogPath of changelogPaths) {
34
- const { versioned } = readChangelogSections(join(changelogPath, 'CHANGELOG.md'));
34
+ const { versionedSections } = readChangelogSections(join(changelogPath, 'CHANGELOG.md'));
35
35
  const entries = readChangelogEntries(resolveChangelogJsonPath(config, changelogPath)) ?? [];
36
- for (const { version } of [...versioned, ...entries]) {
36
+ for (const { version } of [...versionedSections, ...entries]) {
37
37
  versions.add(version);
38
38
  }
39
39
  }
@@ -127,12 +127,12 @@ if (command === 'init') {
127
127
  }
128
128
  enterRepoRootOrExit();
129
129
  const initFlagSchema = {
130
- dryRun: { long: '--dry-run', type: 'boolean' },
131
- force: { long: '--force', type: 'boolean' },
132
- withConfig: { long: '--with-config', type: 'boolean' },
130
+ isDryRun: { long: '--dry-run', type: 'boolean' },
131
+ shouldForce: { long: '--force', type: 'boolean' },
132
+ shouldScaffoldConfig: { long: '--with-config', type: 'boolean' },
133
133
  };
134
- const { dryRun, force, withConfig } = parseArgsOrExit(flags, initFlagSchema).flags;
135
- const exitCode = initCommand({ dryRun, force, styles, withConfig });
134
+ const { isDryRun, shouldForce, shouldScaffoldConfig } = parseArgsOrExit(flags, initFlagSchema).flags;
135
+ const exitCode = initCommand({ isDryRun, shouldForce, styles, shouldScaffoldConfig });
136
136
  process.exit(exitCode);
137
137
  }
138
138
  if (command === 'update-templates') {
@@ -142,10 +142,10 @@ if (command === 'update-templates') {
142
142
  }
143
143
  enterRepoRootOrExit();
144
144
  const updateTemplatesFlagSchema = {
145
- dryRun: { long: '--dry-run', type: 'boolean' },
145
+ isDryRun: { long: '--dry-run', type: 'boolean' },
146
146
  };
147
- const { dryRun } = parseArgsOrExit(flags, updateTemplatesFlagSchema).flags;
148
- const exitCode = updateTemplatesCommand({ dryRun, styles });
147
+ const { isDryRun } = parseArgsOrExit(flags, updateTemplatesFlagSchema).flags;
148
+ const exitCode = updateTemplatesCommand({ isDryRun, styles });
149
149
  process.exit(exitCode);
150
150
  }
151
151
  if (command === 'sync-labels') {
@@ -163,15 +163,15 @@ if (command === 'sync-labels') {
163
163
  const { invocationDir } = enterRepoRootOrExit();
164
164
  const syncLabelsInitFlagSchema = {
165
165
  ...configFlagSchema,
166
- dryRun: { long: '--dry-run', type: 'boolean' },
167
- force: { long: '--force', type: 'boolean' },
166
+ isDryRun: { long: '--dry-run', type: 'boolean' },
167
+ shouldForce: { long: '--force', type: 'boolean' },
168
168
  };
169
- const { config, dryRun, force } = parseArgsOrExit(subflags, syncLabelsInitFlagSchema).flags;
169
+ const { config, isDryRun, shouldForce } = parseArgsOrExit(subflags, syncLabelsInitFlagSchema).flags;
170
170
  const configPath = resolveConfigFlag(config, invocationDir);
171
171
  const exitCode = await syncLabelsInitCommand({
172
172
  ...(configPath !== undefined && { configPath }),
173
- dryRun,
174
- force,
173
+ isDryRun,
174
+ shouldForce,
175
175
  styles,
176
176
  });
177
177
  process.exit(exitCode);
@@ -184,11 +184,11 @@ if (command === 'sync-labels') {
184
184
  const { invocationDir } = enterRepoRootOrExit();
185
185
  const generateFlagSchema = {
186
186
  ...configFlagSchema,
187
- check: { long: '--check', type: 'boolean' },
187
+ shouldCheck: { long: '--check', type: 'boolean' },
188
188
  };
189
- const { check, config } = parseArgsOrExit(subflags, generateFlagSchema).flags;
189
+ const { shouldCheck, config } = parseArgsOrExit(subflags, generateFlagSchema).flags;
190
190
  const configPath = resolveConfigFlag(config, invocationDir);
191
- const exitCode = await generateCommand({ check, ...(configPath !== undefined && { configPath }), styles });
191
+ const exitCode = await generateCommand({ shouldCheck, ...(configPath !== undefined && { configPath }), styles });
192
192
  process.exit(exitCode);
193
193
  }
194
194
  if (subcommand === 'sync') {
@@ -5,12 +5,30 @@ import type { ChangelogEntry, ChangelogSection, ReleaseSettings, ReleaseType } f
5
5
  /**
6
6
  * Builds structured changelog entries from the release windows of git history, naming the unreleased window `tag`.
7
7
  *
8
- * Composes `readReleaseHistory` and `toChangelogEntries` for a caller that knows the tag before it reads.
8
+ * Composes `readReleaseHistory` and `convertToChangelogEntries` for a caller that knows the tag before it reads.
9
9
  */
10
10
  export declare function buildChangelogEntries(config: ReleaseHistoryConfig, tag: string, options: GenerateChangelogOptions): {
11
11
  entries: ChangelogEntry[];
12
12
  diagnostics: ChangelogDiagnostics;
13
13
  };
14
+ /** Labels the unreleased window of `history` with `tag` and returns the entries of every window, newest first. */
15
+ export declare function convertToChangelogEntries(history: ReleaseHistory, tag: string): ChangelogEntry[];
16
+ /** One read of a scope's release windows. */
17
+ export interface ReleaseHistory {
18
+ /** The newest matching tag that HEAD reaches; undefined when none does. */
19
+ previousTag: string | undefined;
20
+ /** Entries of the released windows, newest first; a window that doesn't yield any item contributes none. */
21
+ releasedEntries: ChangelogEntry[];
22
+ unreleasedReading: UnreleasedWindowReading;
23
+ }
24
+ /** The configuration that `readReleaseHistory` reads. */
25
+ export type ReleaseHistoryConfig = Pick<ReleaseSettings, 'breakingPolicies' | 'changelogJson' | 'scopeAliases' | 'versionPatterns' | 'workTypes'>;
26
+ /**
27
+ * Returns the entries that a direct release tagged `tag` records: those of `convertToChangelogEntries`, or, when the
28
+ * unreleased window doesn't yield any item, the synthetic "Forced version bump." entry dated `today` ahead of the
29
+ * released entries.
30
+ */
31
+ export declare function convertToReleaseEntries(history: ReleaseHistory, tag: string, today: string): ChangelogEntry[];
14
32
  /**
15
33
  * Reads a scope's release windows once and returns the changelog items of every window, with what the unreleased
16
34
  * window decides: its commits, its bump, and its diagnostics.
@@ -21,16 +39,6 @@ export declare function buildChangelogEntries(config: ReleaseHistoryConfig, tag:
21
39
  * only for the unreleased window, since the released windows were reported when they were prepared.
22
40
  */
23
41
  export declare function readReleaseHistory(config: ReleaseHistoryConfig, options: GenerateChangelogOptions): ReleaseHistory;
24
- /** One read of a scope's release windows. */
25
- export interface ReleaseHistory {
26
- /** The newest matching tag that HEAD reaches; undefined when none does. */
27
- previousTag: string | undefined;
28
- /** Entries of the released windows, newest first; a window that doesn't yield any item contributes none. */
29
- releasedEntries: ChangelogEntry[];
30
- unreleased: UnreleasedWindowReading;
31
- }
32
- /** The configuration that `readReleaseHistory` reads. */
33
- export type ReleaseHistoryConfig = Pick<ReleaseSettings, 'breakingPolicies' | 'changelogJson' | 'scopeAliases' | 'versionPatterns' | 'workTypes'>;
34
42
  /**
35
43
  * Strips a section title down to its bare name.
36
44
  *
@@ -39,14 +47,6 @@ export type ReleaseHistoryConfig = Pick<ReleaseSettings, 'breakingPolicies' | 'c
39
47
  * through this helper lets the two forms match.
40
48
  */
41
49
  export declare function stripGroupDecorations(group: string): string;
42
- /** Labels the unreleased window of `history` with `tag` and returns the entries of every window, newest first. */
43
- export declare function toChangelogEntries(history: ReleaseHistory, tag: string): ChangelogEntry[];
44
- /**
45
- * Returns the entries that a direct release tagged `tag` records: those of `toChangelogEntries`, or, when the
46
- * unreleased window doesn't yield any item, the synthetic "Forced version bump." entry dated `today` ahead of the
47
- * released entries.
48
- */
49
- export declare function toReleaseEntries(history: ReleaseHistory, tag: string, today: string): ChangelogEntry[];
50
50
  /** The unreleased window: its changelog sections and commits, and the bump and diagnostics that they determine. */
51
51
  export interface UnreleasedWindowReading {
52
52
  /** The highest bump that the window's items call for; undefined when the window doesn't yield any item. */