@williamthorsen/release-kit 11.0.0 → 13.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 (189) hide show
  1. package/.meta/changelog.json +274 -2
  2. package/.readyup/kits/default.js +18 -33
  3. package/.readyup/kits/npm-auto-publish.js +24 -23
  4. package/.readyup/manifest.json +16 -11
  5. package/CHANGELOG.md +98 -0
  6. package/README.md +57 -69
  7. package/bin/release-kit.js +1 -1
  8. package/dist/esm/assertCleanWorkingTree.d.ts +1 -5
  9. package/dist/esm/assertTaggedBaseline.d.ts +29 -0
  10. package/dist/esm/assertTaggedBaseline.js +41 -0
  11. package/dist/esm/attachChangelogDiagnostics.d.ts +4 -0
  12. package/dist/esm/attachChangelogDiagnostics.js +14 -0
  13. package/dist/esm/bin/release-kit.js +88 -24
  14. package/dist/esm/buildChangelogEntries.d.ts +69 -17
  15. package/dist/esm/buildChangelogEntries.js +222 -120
  16. package/dist/esm/buildDependencyGraph.d.ts +6 -6
  17. package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -8
  18. package/dist/esm/buildReleaseSummary.d.ts +2 -8
  19. package/dist/esm/buildReleaseSummary.js +2 -2
  20. package/dist/esm/buildSyntheticChangelogEntry.d.ts +3 -5
  21. package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
  22. package/dist/esm/bumpVersion.d.ts +1 -8
  23. package/dist/esm/changelogJsonFile.d.ts +5 -7
  24. package/dist/esm/changelogJsonFile.js +2 -17
  25. package/dist/esm/changelogJsonUtils.d.ts +7 -10
  26. package/dist/esm/changelogOverrides.d.ts +79 -100
  27. package/dist/esm/changelogOverrides.js +138 -58
  28. package/dist/esm/classifyChangelogCommit.d.ts +36 -0
  29. package/dist/esm/classifyChangelogCommit.js +34 -0
  30. package/dist/esm/commitCommand.d.ts +2 -4
  31. package/dist/esm/compareVersions.d.ts +10 -4
  32. package/dist/esm/compareVersions.js +16 -0
  33. package/dist/esm/configFlagSchema.d.ts +1 -1
  34. package/dist/esm/createGithubRelease.d.ts +7 -19
  35. package/dist/esm/createGithubReleaseCommand.d.ts +5 -5
  36. package/dist/esm/createGithubReleaseCommand.js +9 -9
  37. package/dist/esm/createTags.d.ts +2 -3
  38. package/dist/esm/decideRelease.d.ts +11 -41
  39. package/dist/esm/decideRelease.js +5 -27
  40. package/dist/esm/defaults.d.ts +5 -24
  41. package/dist/esm/defaults.js +7 -5
  42. package/dist/esm/defineConfig.d.ts +1 -1
  43. package/dist/esm/deleteFileIfExists.d.ts +1 -1
  44. package/dist/esm/deriveReleaseNotesConfig.d.ts +20 -0
  45. package/dist/esm/deriveReleaseNotesConfig.js +21 -0
  46. package/dist/esm/deriveWorkspaceConfig.d.ts +3 -5
  47. package/dist/esm/detectPackageManager.d.ts +2 -4
  48. package/dist/esm/detectUndeclaredTagPrefixes.d.ts +4 -6
  49. package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
  50. package/dist/esm/determineBumpType.d.ts +11 -11
  51. package/dist/esm/determineBumpType.js +6 -7
  52. package/dist/esm/discoverWorkspaces.d.ts +14 -15
  53. package/dist/esm/discoverWorkspaces.js +12 -12
  54. package/dist/esm/enterRepoRoot.d.ts +12 -0
  55. package/dist/esm/enterRepoRoot.js +16 -0
  56. package/dist/esm/enumerateReleaseWindows.d.ts +50 -0
  57. package/dist/esm/enumerateReleaseWindows.js +124 -0
  58. package/dist/esm/extractMigration.d.ts +6 -8
  59. package/dist/esm/findUnroutedEntryScopes.d.ts +16 -0
  60. package/dist/esm/findUnroutedEntryScopes.js +57 -0
  61. package/dist/esm/formatExcludedSkip.d.ts +3 -0
  62. package/dist/esm/formatExcludedSkip.js +3 -0
  63. package/dist/esm/formatPrivateSkip.d.ts +1 -6
  64. package/dist/esm/generateChangelogs.d.ts +13 -26
  65. package/dist/esm/generateChangelogs.js +0 -14
  66. package/dist/esm/glyphs.d.ts +2 -2
  67. package/dist/esm/help/prepareHelp.d.ts +3 -4
  68. package/dist/esm/help/prepareHelp.js +7 -8
  69. package/dist/esm/index.d.ts +1 -1
  70. package/dist/esm/init/checks.d.ts +3 -3
  71. package/dist/esm/init/checks.js +4 -1
  72. package/dist/esm/init/detectRepoType.d.ts +2 -3
  73. package/dist/esm/init/hasProvenance.d.ts +2 -0
  74. package/dist/esm/init/hasProvenance.js +3 -0
  75. package/dist/esm/init/initCommand.d.ts +3 -6
  76. package/dist/esm/init/initCommand.js +11 -11
  77. package/dist/esm/init/parseJsonRecord.d.ts +1 -4
  78. package/dist/esm/init/scaffold.d.ts +1 -3
  79. package/dist/esm/init/scaffold.js +2 -30
  80. package/dist/esm/init/templates.d.ts +9 -11
  81. package/dist/esm/init/templates.js +12 -11
  82. package/dist/esm/init/updateTemplatesCommand.d.ts +13 -0
  83. package/dist/esm/init/updateTemplatesCommand.js +64 -0
  84. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +12 -17
  85. package/dist/esm/injectSection.d.ts +3 -3
  86. package/dist/esm/loadConfig.d.ts +16 -14
  87. package/dist/esm/loadConfig.js +2 -5
  88. package/dist/esm/loadValidatedConfig.d.ts +16 -19
  89. package/dist/esm/loadValidatedConfig.js +6 -1
  90. package/dist/esm/packsPath.d.ts +9 -9
  91. package/dist/esm/parseChangeRecordBlock.d.ts +37 -0
  92. package/dist/esm/parseChangeRecordBlock.js +134 -0
  93. package/dist/esm/parseCommitMessage.d.ts +42 -32
  94. package/dist/esm/parseCommitMessage.js +33 -41
  95. package/dist/esm/parseRequestedTags.d.ts +3 -3
  96. package/dist/esm/planReleaseNotesPreviews.d.ts +6 -6
  97. package/dist/esm/planVersionBump.d.ts +5 -3
  98. package/dist/esm/planVersionBump.js +3 -0
  99. package/dist/esm/prepareCommand.d.ts +4 -7
  100. package/dist/esm/prepareCommand.js +17 -18
  101. package/dist/esm/previewTagPrefixes.d.ts +8 -10
  102. package/dist/esm/propagateBumps.d.ts +6 -7
  103. package/dist/esm/publish.d.ts +1 -1
  104. package/dist/esm/publishCommand.d.ts +4 -3
  105. package/dist/esm/publishCommand.js +8 -8
  106. package/dist/esm/pushCommand.d.ts +4 -3
  107. package/dist/esm/pushCommand.js +8 -2
  108. package/dist/esm/pushRelease.d.ts +2 -2
  109. package/dist/esm/readChangelogSections.d.ts +33 -0
  110. package/dist/esm/readChangelogSections.js +80 -0
  111. package/dist/esm/releaseFiles.d.ts +5 -7
  112. package/dist/esm/releasePlan.d.ts +11 -13
  113. package/dist/esm/releasePrepare.d.ts +25 -25
  114. package/dist/esm/releasePrepare.js +80 -80
  115. package/dist/esm/releasePrepareMono.d.ts +9 -7
  116. package/dist/esm/releasePrepareMono.js +114 -99
  117. package/dist/esm/releasePrepareProject.d.ts +19 -29
  118. package/dist/esm/releasePrepareProject.js +46 -45
  119. package/dist/esm/renderChangelogMarkdown.d.ts +10 -4
  120. package/dist/esm/renderChangelogMarkdown.js +27 -2
  121. package/dist/esm/renderReleaseNotes.d.ts +3 -11
  122. package/dist/esm/renderReleaseNotes.js +2 -6
  123. package/dist/esm/reportPrepare.d.ts +4 -5
  124. package/dist/esm/reportPrepare.js +67 -42
  125. package/dist/esm/resolveCommandTags.d.ts +15 -11
  126. package/dist/esm/resolveCommandTags.js +35 -5
  127. package/dist/esm/resolveConfigFlag.d.ts +7 -0
  128. package/dist/esm/resolveConfigFlag.js +4 -0
  129. package/dist/esm/resolveReleaseTags.d.ts +6 -8
  130. package/dist/esm/resolveReleaseTags.js +1 -1
  131. package/dist/esm/resolveStylesOrExit.d.ts +2 -2
  132. package/dist/esm/showTagPrefixesCommand.d.ts +7 -10
  133. package/dist/esm/showTagPrefixesCommand.js +4 -4
  134. package/dist/esm/stripEmojiPrefix.d.ts +1 -0
  135. package/dist/esm/stripScope.d.ts +2 -2
  136. package/dist/esm/stripScope.js +3 -3
  137. package/dist/esm/sync-labels/generateCommand.d.ts +11 -10
  138. package/dist/esm/sync-labels/generateCommand.js +11 -8
  139. package/dist/esm/sync-labels/initCommand.d.ts +6 -7
  140. package/dist/esm/sync-labels/initCommand.js +2 -2
  141. package/dist/esm/sync-labels/presets.d.ts +5 -4
  142. package/dist/esm/sync-labels/presets.js +9 -9
  143. package/dist/esm/sync-labels/resolveLabels.d.ts +7 -7
  144. package/dist/esm/sync-labels/resolveLabels.js +1 -1
  145. package/dist/esm/sync-labels/retiredConfig.d.ts +4 -5
  146. package/dist/esm/sync-labels/syncCommand.d.ts +1 -4
  147. package/dist/esm/sync-labels/templates.d.ts +3 -3
  148. package/dist/esm/sync-labels/templates.js +3 -3
  149. package/dist/esm/sync-labels/typeLabels.d.ts +14 -0
  150. package/dist/esm/sync-labels/typeLabels.js +38 -0
  151. package/dist/esm/sync-labels/types.d.ts +1 -1
  152. package/dist/esm/tagCommand.d.ts +1 -1
  153. package/dist/esm/typeGuards.d.ts +2 -2
  154. package/dist/esm/types.d.ts +185 -116
  155. package/dist/esm/types.js +2 -2
  156. package/dist/esm/validateConfig.d.ts +2 -11
  157. package/dist/esm/validateConfig.js +5 -1
  158. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -12
  159. package/dist/esm/validateOverridesCommand.d.ts +19 -21
  160. package/dist/esm/validateOverridesCommand.js +16 -23
  161. package/docs/api.md +6 -6
  162. package/docs/changelogs.md +90 -28
  163. package/docs/configuration.md +30 -29
  164. package/docs/editorial-overrides.md +90 -31
  165. package/docs/labels.md +21 -19
  166. package/docs/project-releases.md +14 -14
  167. package/docs/publishing.md +5 -5
  168. package/docs/readiness-checks.md +3 -3
  169. package/docs/releasing.md +54 -24
  170. package/docs/work-types.md +46 -58
  171. package/package.json +7 -7
  172. package/presets/labels/common.yaml +1 -41
  173. package/schemas/changelog-overrides.json +39 -0
  174. package/cliff.toml.template +0 -65
  175. package/dist/esm/collectPolicyViolations.d.ts +0 -15
  176. package/dist/esm/collectPolicyViolations.js +0 -12
  177. package/dist/esm/determineBumpFromCommits.d.ts +0 -17
  178. package/dist/esm/determineBumpFromCommits.js +0 -28
  179. package/dist/esm/getCommitsSinceTarget.d.ts +0 -21
  180. package/dist/esm/getCommitsSinceTarget.js +0 -66
  181. package/dist/esm/resolveCliffConfigPath.d.ts +0 -11
  182. package/dist/esm/resolveCliffConfigPath.js +0 -20
  183. package/dist/esm/resolveReleaseNotesConfig.d.ts +0 -26
  184. package/dist/esm/resolveReleaseNotesConfig.js +0 -29
  185. package/dist/esm/runGitCliff.d.ts +0 -38
  186. package/dist/esm/runGitCliff.js +0 -25
  187. package/dist/esm/workTypesData.d.ts +0 -50
  188. package/dist/esm/workTypesData.js +0 -145
  189. package/schemas/label-map.json +0 -24
@@ -1,3 +1,4 @@
1
+ import type { BreakingPolicy } from '@williamthorsen/change-grammar';
1
2
  import { z } from 'zod';
2
3
  /** Semver release type for version bumping. */
3
4
  export type ReleaseType = 'major' | 'minor' | 'patch';
@@ -11,38 +12,41 @@ export interface ChangelogItem {
11
12
  /**
12
13
  * Whether this item represents a breaking change.
13
14
  *
14
- * `true` when the commit subject carries the `!` prefix (e.g. `feat!:` or `drop(scope)!:`) and
15
- * the commit's work-type policy permits it, so the item agrees with the version bump.
16
- * The `BREAKING CHANGE:` body footer is intentionally NOT considered here. Renderers prefix
17
- * breaking-item bullets with the marker constructed from `WORK_TYPES_DATA.markers.breaking`
18
- * (rendered as `🚨 **Breaking:** ` with the canonical SSOT values) to surface them prominently
19
- * in release notes.
15
+ * `true` when the commit subject has the `!` marker (e.g. `feat!:` or `drop(scope)!:`), or the
16
+ * change-record entry from which the item derives sets `breaking: true`, and the work-type policy
17
+ * permits it, so that the item agrees with the version bump. The `BREAKING CHANGE:` body footer
18
+ * does not set it.
20
19
  */
21
20
  breaking?: boolean;
22
21
  /**
23
- * Migration instruction for a consumer, taken from the `Migration:` paragraph of `body`.
22
+ * Migration instruction for a consumer, taken from the `Migration:` paragraph of `body`, or from
23
+ * the `migration` of the change-record entry from which the item derives.
24
24
  *
25
- * Derived from `body` wherever `body` is set, and absent where the body carries no labeled
26
- * paragraph. Independent of `breaking`: a `deprecate` commit cannot carry `!` under the default
27
- * breaking policy and still calls for a migration. `body` keeps the paragraph, so
28
- * `CHANGELOG.md` goes on rendering it in place.
25
+ * On a title-derived item, derived from `body` whenever `body` is set, and absent when the body
26
+ * does not contain any labeled paragraph. Independent of `breaking`: A `deprecate` commit cannot
27
+ * take `!` under the default breaking policy and still calls for a migration. `body` keeps the
28
+ * paragraph, so `CHANGELOG.md` goes on rendering it in place.
29
29
  */
30
30
  migration?: string;
31
31
  /**
32
- * Full git commit SHA when known. Captured from git-cliff's `--context` output and
32
+ * Full git commit SHA when known. Captured from the commit's release window and
33
33
  * persisted in `changelog.json` so that override files can target items by hash. Synthetic
34
34
  * propagation entries (`buildSyntheticChangelogEntry`, `buildEmptyReleaseEntry`) leave
35
- * this field absent — they have no underlying commit.
35
+ * this field absent, because they do not have any underlying commit.
36
36
  */
37
37
  hash?: string;
38
+ /**
39
+ * 1-based position of the change-record entry from which this item derives, counted over the block's entries as
40
+ * written. Absent for an item derived from the commit's title.
41
+ */
42
+ entry?: number;
38
43
  }
39
44
  /** A grouped section within a changelog entry (e.g., "Features", "Bug fixes"). */
40
45
  export interface ChangelogSection {
41
46
  /**
42
- * Section title carrying the leading emoji prefix used by `cliff.toml.template` group
43
- * definitions (e.g. `"🐛 Bug fixes"`). The `<!-- NN -->` canonical-order HTML comment
44
- * is stripped during transform; the emoji remains. Callers that match against `title`
45
- * (e.g. `sectionOrder` configs) must include the emoji prefix.
47
+ * Section title, read from the work type of the commit or of its change-record entry. A default
48
+ * title begins with the type's emoji (e.g. `"🐛 Bug fixes"`), which an exact match against
49
+ * `title`, such as a `sectionOrder` list, must include.
46
50
  */
47
51
  title: string;
48
52
  audience: ChangelogAudience;
@@ -61,13 +65,12 @@ export interface ChangelogJsonConfig {
61
65
  devOnlySections: string[];
62
66
  }
63
67
  /**
64
- * Editorial override for a single changelog item, keyed by commit hash (or hash prefix) in
65
- * the override file. All fields are optional; an entry with no fields is a validation error.
68
+ * Editorial override for changelog items, keyed in the override file by `<hash>` (every item of the commit) or
69
+ * `<hash>:<n>` (the item derived from the commit's `n`th change-record entry), where the hash is lowercase hex
70
+ * matched as a prefix. All fields are optional; an entry without any fields is a validation error.
66
71
  *
67
- * `audience` declares the full forward-compatible vocabulary, but only `'skip'` is currently
68
- * supported at runtime. `'all'` and `'dev'` reclassification is reserved for a v2 follow-up
69
- * and rejected by the validator with an explicit "not yet supported" error so the on-disk
70
- * format remains stable through the v1→v2 transition.
72
+ * `audience` admits `'all'` and `'dev'`, but the validator rejects both with a "not yet supported"
73
+ * error; only `'skip'` takes effect.
71
74
  */
72
75
  export interface ChangelogOverride {
73
76
  audience?: 'all' | 'dev' | 'skip';
@@ -75,22 +78,20 @@ export interface ChangelogOverride {
75
78
  body?: string;
76
79
  breaking?: boolean;
77
80
  }
78
- /** On-disk shape of `.meta/changelog-overrides.json`: a flat record keyed by commit hash or prefix. */
81
+ /**
82
+ * Overrides in `.meta/changelog-overrides.json`: a flat record keyed by `<hash>` or `<hash>:<n>`. The file may also contain
83
+ * a top-level `$schema` string, which the loader drops.
84
+ */
79
85
  export type ChangelogOverridesFile = Record<string, ChangelogOverride>;
80
86
  /** Configuration for release notes consumption (README injection). */
81
87
  export interface ReleaseNotesConfig {
82
88
  shouldInjectIntoReadme: boolean;
83
89
  }
84
- /**
85
- * Project-release config after merging defaults. Mirrors the `ReleaseNotesConfig`/
86
- * `ResolvedReleaseNotesConfig`-style split: optional fields in `ProjectConfig` become
87
- * required in `ResolvedProjectConfig`.
88
- */
90
+ /** Project-release config after merging defaults: `ProjectConfig` with every field required. */
89
91
  export interface ResolvedProjectConfig {
90
92
  /**
91
- * Resolved commit window for the project release: the git pathspecs and git-cliff include-paths
92
- * that select which commits the stage considers. Defaults to the union of every contributing
93
- * workspace's `paths`.
93
+ * Resolved commit window for the project release: the git pathspecs that select which commits
94
+ * the stage considers. Defaults to the union of every contributing workspace's `paths`.
94
95
  */
95
96
  paths: string[];
96
97
  /** Resolved tag prefix for project-level tags. */
@@ -102,11 +103,8 @@ export interface PropagationSource {
102
103
  newVersion: string;
103
104
  }
104
105
  /**
105
- * A `!`-policy violation detected while parsing commits during release preparation.
106
- *
107
- * Surfaces from `releasePrepare`, `releasePrepareMono`, and `releasePrepareProject` via the
108
- * `policyViolations` field on each per-workspace/project result. The policy itself is enforced
109
- * tolerantly at release time — violations are collected and reported, never fail the release.
106
+ * A `!`-policy violation detected while reading the unreleased window during release preparation. Violations are
107
+ * reported and never fail the release.
110
108
  */
111
109
  export interface PolicyViolation {
112
110
  /** Full hash of the offending commit. */
@@ -115,17 +113,63 @@ export interface PolicyViolation {
115
113
  commitSubject: string;
116
114
  /** Resolved canonical work type whose policy was violated. */
117
115
  type: string;
118
- /** Where the violation was detected: `'prefix'` for `!`, `'body'` for `BREAKING CHANGE:` footer. */
119
- surface: 'prefix' | 'body';
116
+ /**
117
+ * Where the violation was detected: `'prefix'` for `!`, `'body'` for `BREAKING CHANGE:` footer, `'entry'` for a
118
+ * change-record entry's `breaking`.
119
+ */
120
+ surface: 'prefix' | 'body' | 'entry';
121
+ /** 1-based position of the offending change-record entry; present only for surface `'entry'`. */
122
+ entryPosition?: number;
123
+ }
124
+ /** A commit whose `change-record` block could not be read, so that its changelog item came from its title. */
125
+ export interface MalformedChangeRecordBlock {
126
+ /** Full hash of the commit. */
127
+ commitHash: string;
128
+ /** First line of the commit message. */
129
+ commitSubject: string;
130
+ /** The defect that stopped the read. */
131
+ reason: string;
120
132
  }
121
133
  /**
122
- * Result of preparing a single workspace (package) for release when a release was produced.
123
- *
124
- * `currentVersion`, `newVersion`, `tag`, `bumpedFiles`, and `changelogFiles` are always
125
- * populated. `releaseType` stays optional because it is left undefined for
126
- * `--set-version` workspaces. `parsedCommitCount` stays optional because the legacy
127
- * single-package bump-override path does not populate it. `commits` stays optional
128
- * because propagation-only releases have no direct commits.
134
+ * A change-record entry whose type the work-type taxonomy does not declare, so that it did not yield any changelog
135
+ * item.
136
+ */
137
+ export interface UndeclaredEntryType {
138
+ /** Full hash of the commit. */
139
+ commitHash: string;
140
+ /** First line of the commit message. */
141
+ commitSubject: string;
142
+ /** 1-based position of the entry in its block. */
143
+ entryPosition: number;
144
+ /** The type as the entry declares it. */
145
+ type: string;
146
+ }
147
+ /** A change-record entry scope that does not name any workspace whose unreleased window contains its commit. */
148
+ export interface UnroutedEntryScope {
149
+ /** Full hash of the commit. */
150
+ commitHash: string;
151
+ /** First line of the commit message. */
152
+ commitSubject: string;
153
+ /** 1-based position of the entry in its block. */
154
+ entryPosition: number;
155
+ /** The scope as the entry declares it. */
156
+ scope: string;
157
+ }
158
+ /**
159
+ * What `prepare` did with the existing `##` sections of one `CHANGELOG.md` that the regenerated file does not render
160
+ * from entries.
161
+ */
162
+ export interface ChangelogPreservation {
163
+ /** The `CHANGELOG.md` path. */
164
+ file: string;
165
+ /** Versions whose sections were kept verbatim, because neither the built entries nor `changelog.json` contain them. */
166
+ preservedVersions: string[];
167
+ /** Headings of the sections that do not name any version, which the regenerated file does not contain. */
168
+ droppedUnversionedHeadings: string[];
169
+ }
170
+ /**
171
+ * Result of preparing a single workspace (package) for release when a release was produced. `releaseType` is absent
172
+ * for `--set-version`.
129
173
  */
130
174
  export interface ReleasedWorkspaceResult {
131
175
  status: 'released';
@@ -134,18 +178,21 @@ export interface ReleasedWorkspaceResult {
134
178
  previousTag?: string;
135
179
  commitCount: number;
136
180
  /**
137
- * Count of commits that parsed into a recognized work type. Always populated for
138
- * results produced by the unified `decideRelease` algorithm (the monorepo path),
139
- * including the no-commits case where it is `0`. May be absent for results
140
- * produced by the legacy single-package executor's bump-override path. Use
141
- * `bumpOverride` (not `parsedCommitCount === undefined`) as the signal for "the
142
- * user supplied --bump=X".
181
+ * Count of commits that yield at least one changelog item; `0` when the unreleased window does
182
+ * not contain any commits or when none does. Absent for `--set-version` and propagation-only
183
+ * workspaces. `bumpOverride`, not this count, signals that the user supplied `--bump=X`.
143
184
  */
144
185
  parsedCommitCount?: number;
145
- /** Commits that could not be parsed into a recognized work type. */
186
+ /** Commits that do not yield any changelog item and are not accounted for by any exclusion or diagnostic. */
146
187
  unparseableCommits?: Commit[];
147
- /** Policy violations detected while parsing this workspace's commits; omitted when none. */
188
+ /** Policy violations of the unreleased window's titles and change-record entries; omitted when none. */
148
189
  policyViolations?: PolicyViolation[];
190
+ /** Commits of the unreleased window whose `change-record` block could not be read; omitted when none. */
191
+ malformedBlocks?: MalformedChangeRecordBlock[];
192
+ /** Change-record entries of the unreleased window whose type is undeclared; omitted when none. */
193
+ undeclaredEntryTypes?: UndeclaredEntryType[];
194
+ /** Change-record entry scopes of the unreleased window that route their entry nowhere; omitted when none. */
195
+ unroutedEntryScopes?: UnroutedEntryScope[];
149
196
  releaseType?: ReleaseType;
150
197
  currentVersion: string;
151
198
  newVersion: string;
@@ -154,24 +201,28 @@ export interface ReleasedWorkspaceResult {
154
201
  changelogFiles: string[];
155
202
  /** Release-notes preview files; present only under `--with-release-notes`. */
156
203
  previewFiles?: string[];
157
- /** Raw commits associated with this workspace (present for direct releases, absent for propagation-only). */
204
+ /**
205
+ * Per `CHANGELOG.md`, the existing sections that were kept or dropped; omitted when `prepare` did not keep or drop
206
+ * any.
207
+ */
208
+ changelogPreservation?: ChangelogPreservation[];
209
+ /** Raw commits of the workspace's unreleased window. */
158
210
  commits?: Commit[];
159
211
  /**
160
- * Present when `--bump=X` was supplied and selected the release level for this workspace.
161
- * Distinguishes a bump-override release from a `--force` fallback or a natural-bump release;
162
- * consumed by the renderer to label the release accurately.
212
+ * Present when `--bump=X` was supplied and selected the release level for this workspace, which
213
+ * distinguishes a bump-override release from a `--force` fallback or a natural-bump release.
163
214
  */
164
215
  bumpOverride?: ReleaseType;
165
216
  /** Dependencies that triggered a propagated bump (present for propagated or mixed workspaces). */
166
217
  propagatedFrom?: PropagationSource[];
218
+ /** Present when propagation alone set the bump, the workspace's own commits having called for none. */
219
+ propagatedOnly?: true;
167
220
  /** Present when this workspace was written via `--set-version`; the explicit version that was applied. */
168
221
  setVersion?: string;
169
222
  }
170
223
  /**
171
- * Result of preparing a single workspace (package) for release when the release was skipped.
172
- *
173
- * Carries diagnostic data (`commitCount`, `parsedCommitCount`, `unparseableCommits`,
174
- * `previousTag`) plus the human-readable `skipReason`. Release-only fields are absent.
224
+ * Result of preparing a single workspace (package) for release when the release was skipped: the diagnostic fields
225
+ * and a human-readable `skipReason`, without the release-only fields.
175
226
  */
176
227
  export interface SkippedWorkspaceResult {
177
228
  status: 'skipped';
@@ -180,14 +231,20 @@ export interface SkippedWorkspaceResult {
180
231
  previousTag?: string;
181
232
  commitCount: number;
182
233
  /**
183
- * Count of commits that parsed into a recognized work type. May be absent for results
184
- * produced by the legacy single-package executor's bump-override path.
234
+ * Count of commits that yield at least one changelog item; `0` when the unreleased window does not contain any
235
+ * commits or when none does.
185
236
  */
186
- parsedCommitCount?: number;
187
- /** Commits that could not be parsed into a recognized work type. */
237
+ parsedCommitCount: number;
238
+ /** Commits that do not yield any changelog item and are not accounted for by any exclusion or diagnostic. */
188
239
  unparseableCommits?: Commit[];
189
- /** Policy violations detected while parsing this workspace's commits; omitted when none. */
240
+ /** Policy violations of the unreleased window's titles and change-record entries; omitted when none. */
190
241
  policyViolations?: PolicyViolation[];
242
+ /** Commits of the unreleased window whose `change-record` block could not be read; omitted when none. */
243
+ malformedBlocks?: MalformedChangeRecordBlock[];
244
+ /** Change-record entries of the unreleased window whose type is undeclared; omitted when none. */
245
+ undeclaredEntryTypes?: UndeclaredEntryType[];
246
+ /** Change-record entry scopes of the unreleased window that route their entry nowhere; omitted when none. */
247
+ unroutedEntryScopes?: UnroutedEntryScope[];
191
248
  skipReason: string;
192
249
  }
193
250
  /**
@@ -200,26 +257,27 @@ export type WorkspacePrepareResult = ReleasedWorkspaceResult | SkippedWorkspaceR
200
257
  /**
201
258
  * Result of preparing a project-level release when a release was produced.
202
259
  *
203
- * Mirrors `ReleasedWorkspaceResult` minus the workspace-only fields (`name`,
204
- * `propagatedFrom`, `setVersion`). Project releases never propagate and never use
205
- * `--set-version`, so `releaseType`, `currentVersion`, `newVersion`, `tag`, and
206
- * `commits` are all required.
260
+ * Mirrors `ReleasedWorkspaceResult` without the workspace-only fields. Project releases never propagate and never
261
+ * use `--set-version`, so `releaseType`, `parsedCommitCount`, and `commits` are required.
207
262
  */
208
263
  export interface ReleasedProjectResult {
209
264
  status: 'released';
210
265
  previousTag?: string;
211
266
  commitCount: number;
212
267
  /**
213
- * Count of commits that parsed into a recognized work type. Always populated by the
214
- * unified `decideRelease` algorithm — `0` when there are no commits or when none
215
- * parse. Use `bumpOverride` (not `parsedCommitCount === 0`) as the signal for "the
216
- * user supplied --bump=X".
268
+ * Count of commits that yield at least one changelog item; `0` when the unreleased window does
269
+ * not contain any commits or when none does. `bumpOverride`, not a zero count, signals that the
270
+ * user supplied `--bump=X`.
217
271
  */
218
272
  parsedCommitCount: number;
219
- /** Commits that could not be parsed into a recognized work type. */
273
+ /** Commits that do not yield any changelog item and are not accounted for by any exclusion or diagnostic. */
220
274
  unparseableCommits?: Commit[];
221
- /** Policy violations detected while parsing the project's commits; omitted when none. */
275
+ /** Policy violations of the unreleased window's titles and change-record entries; omitted when none. */
222
276
  policyViolations?: PolicyViolation[];
277
+ /** Commits of the unreleased window whose `change-record` block could not be read; omitted when none. */
278
+ malformedBlocks?: MalformedChangeRecordBlock[];
279
+ /** Change-record entries of the unreleased window whose type is undeclared; omitted when none. */
280
+ undeclaredEntryTypes?: UndeclaredEntryType[];
223
281
  releaseType: ReleaseType;
224
282
  currentVersion: string;
225
283
  newVersion: string;
@@ -228,35 +286,40 @@ export interface ReleasedProjectResult {
228
286
  changelogFiles: string[];
229
287
  /** Release-notes preview files; present only under `--with-release-notes`. */
230
288
  previewFiles?: string[];
289
+ /**
290
+ * Per `CHANGELOG.md`, the existing sections that were kept or dropped; omitted when `prepare` did not keep or drop
291
+ * any.
292
+ */
293
+ changelogPreservation?: ChangelogPreservation[];
231
294
  /** Raw commits in the project's contributing-paths window since the last project tag. */
232
295
  commits: Commit[];
233
296
  /**
234
- * Present when `--bump=X` was supplied and selected the release level for the project.
235
- * Distinguishes a bump-override release from a `--force` fallback or a natural-bump release;
236
- * consumed by the renderer to label the release accurately.
297
+ * Present when `--bump=X` was supplied and selected the release level for the project, which
298
+ * distinguishes a bump-override release from a `--force` fallback or a natural-bump release.
237
299
  */
238
300
  bumpOverride?: ReleaseType;
239
301
  }
240
302
  /**
241
- * Result of preparing a project-level release when the release was skipped.
242
- *
243
- * Carries diagnostic data (`commitCount`, `parsedCommitCount`, `unparseableCommits`,
244
- * `previousTag`) plus the human-readable `skipReason`. Release-only fields are absent.
303
+ * Result of preparing a project-level release when the release was skipped: the diagnostic fields and a
304
+ * human-readable `skipReason`, without the release-only fields.
245
305
  */
246
306
  export interface SkippedProjectResult {
247
307
  status: 'skipped';
248
308
  previousTag?: string;
249
309
  commitCount: number;
250
310
  /**
251
- * Count of commits that parsed into a recognized work type. Always populated by the
252
- * unified `decideRelease` algorithm — `0` when there are no commits or when none
253
- * parse.
311
+ * Count of commits that yield at least one changelog item; `0` when the unreleased window does not contain any
312
+ * commits or when none does.
254
313
  */
255
314
  parsedCommitCount: number;
256
- /** Commits that could not be parsed into a recognized work type. */
315
+ /** Commits that do not yield any changelog item and are not accounted for by any exclusion or diagnostic. */
257
316
  unparseableCommits?: Commit[];
258
- /** Policy violations detected while parsing the project's commits; omitted when none. */
317
+ /** Policy violations of the unreleased window's titles and change-record entries; omitted when none. */
259
318
  policyViolations?: PolicyViolation[];
319
+ /** Commits of the unreleased window whose `change-record` block could not be read; omitted when none. */
320
+ malformedBlocks?: MalformedChangeRecordBlock[];
321
+ /** Change-record entries of the unreleased window whose type is undeclared; omitted when none. */
322
+ undeclaredEntryTypes?: UndeclaredEntryType[];
260
323
  skipReason: string;
261
324
  }
262
325
  /**
@@ -264,8 +327,8 @@ export interface SkippedProjectResult {
264
327
  *
265
328
  * Discriminated by `status`: `ReleasedProjectResult` for produced releases,
266
329
  * `SkippedProjectResult` for skips. `PrepareResult.project === undefined` means either that
267
- * no project block is configured or that `--only` narrowed the run, which skips the stage
268
- * before it can produce a result; the skip is surfaced as a run warning instead.
330
+ * the config does not declare a `project` block or that `--only` narrowed the run, which skips
331
+ * the stage before it can produce a result; `prepare` reports the skip as a run warning instead.
269
332
  */
270
333
  export type ProjectPrepareResult = ReleasedProjectResult | SkippedProjectResult;
271
334
  /** Aggregate result of the prepare workflow for both single-package and monorepo modes. */
@@ -276,7 +339,7 @@ export interface PrepareResult {
276
339
  command: string;
277
340
  files: string[];
278
341
  } | undefined;
279
- /** Warnings surfaced during preparation (e.g., circular dependency detection). */
342
+ /** Warnings reported during preparation (e.g., circular dependency detection). */
280
343
  warnings?: string[] | undefined;
281
344
  /** Result of the project-level release stage (present only when `config.project` is configured and the stage ran). */
282
345
  project?: ProjectPrepareResult | undefined;
@@ -287,6 +350,12 @@ export interface WorkTypeConfig {
287
350
  header: string;
288
351
  /** Optional aliases that map to this work type (e.g., 'feature' -> 'feat'). */
289
352
  aliases?: string[] | undefined;
353
+ /**
354
+ * Keeps commits and change-record entries of this type out of every changelog. They do not yield
355
+ * any item, so they contribute nothing to the version bump, and the release report does not
356
+ * list them as unparseable.
357
+ */
358
+ excludedFromChangelog?: boolean | undefined;
290
359
  }
291
360
  /**
292
361
  * Defines which commit types trigger major or minor version bumps.
@@ -311,15 +380,15 @@ export declare const legacyIdentitySchema: z.ZodObject<{
311
380
  tagPrefix: z.ZodString;
312
381
  }, z.core.$strict>;
313
382
  /**
314
- * A single historical identity snapshot for a workspace. Both fields are required — a full
315
- * tuple stays unambiguous across any number of future renames.
383
+ * A single historical identity snapshot for a workspace. Both fields are required, because a full
384
+ * tuple stays unambiguous across any number of renames.
316
385
  */
317
386
  export type LegacyIdentity = z.infer<typeof legacyIdentitySchema>;
318
387
  /**
319
388
  * Schema for a package that once lived in this repo but has since been extracted or removed.
320
389
  *
321
390
  * Unlike `legacyIdentitySchema`, retired packages are never consulted for baseline lookup
322
- * or changelog attribution — they acknowledge historical tag prefixes and suppress
391
+ * or changelog attribution; they acknowledge historical tag prefixes and suppress
323
392
  * undeclared-candidate warnings.
324
393
  */
325
394
  export declare const retiredPackageSchema: z.ZodObject<{
@@ -361,6 +430,7 @@ export declare const releaseNotesInputSchema: z.ZodObject<{
361
430
  export declare const workTypeConfigSchema: z.ZodObject<{
362
431
  header: z.ZodString;
363
432
  aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
433
+ excludedFromChangelog: z.ZodOptional<z.ZodBoolean>;
364
434
  }, z.core.$strict>;
365
435
  /** Schema for the `versionPatterns` block. Both arrays are required when the field is provided. */
366
436
  export declare const versionPatternsSchema: z.ZodObject<{
@@ -369,9 +439,8 @@ export declare const versionPatternsSchema: z.ZodObject<{
369
439
  }, z.core.$strict>;
370
440
  /** Schema for a single `breakingPolicies` value. */
371
441
  export declare const breakingPolicyValueSchema: z.ZodEnum<{
372
- forbidden: "forbidden";
373
442
  optional: "optional";
374
- required: "required";
443
+ forbidden: "forbidden";
375
444
  }>;
376
445
  /** Schema for a single label's spec in the `repoLabels.labels` record. */
377
446
  export declare const labelSpecSchema: z.ZodObject<{
@@ -381,7 +450,7 @@ export declare const labelSpecSchema: z.ZodObject<{
381
450
  /**
382
451
  * A label's color and optional description; the name is the record key in `repoLabels.labels`.
383
452
  * An omitted description resolves to empty, which the generated labels file declares explicitly so that a sync
384
- * clears whatever description the label carries today.
453
+ * clears whatever description the label has on the repo.
385
454
  */
386
455
  export type LabelSpec = z.infer<typeof labelSpecSchema>;
387
456
  /**
@@ -389,10 +458,10 @@ export type LabelSpec = z.infer<typeof labelSpecSchema>;
389
458
  * the GitHub repo, distinct from labels applied to PRs and issues).
390
459
  *
391
460
  * Resolution is an ordered fold with last-writer-wins: presets in `extends` order, then the `labels` record,
392
- * where an entry adds a label, replaces one an earlier layer defined, or removes it (`null`). Names match
461
+ * in which an entry adds a label, replaces one that an earlier layer defined, or removes it (`null`). Names match
393
462
  * case-insensitively, as GitHub matches them, and the replacing layer's spelling wins.
394
463
  * Two misstatements are resolve-time errors (`resolveLabels`), not schema errors: `labels` keys that differ only in
395
- * case, and a `null` naming a label that no earlier layer defined.
464
+ * case, and a `null` naming a label not defined by any earlier layer.
396
465
  */
397
466
  export declare const repoLabelsSchema: z.ZodObject<{
398
467
  extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -406,16 +475,14 @@ export type RepoLabelsConfig = z.infer<typeof repoLabelsSchema>;
406
475
  /** Schema for the consumer-facing config file shape (`.config/release-kit.config.ts`). */
407
476
  export declare const releaseKitConfigSchema: z.ZodObject<{
408
477
  breakingPolicies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
409
- forbidden: "forbidden";
410
478
  optional: "optional";
411
- required: "required";
479
+ forbidden: "forbidden";
412
480
  }>>>;
413
481
  changelogJson: z.ZodOptional<z.ZodObject<{
414
482
  enabled: z.ZodOptional<z.ZodBoolean>;
415
483
  outputPath: z.ZodOptional<z.ZodString>;
416
484
  devOnlySections: z.ZodOptional<z.ZodArray<z.ZodString>>;
417
485
  }, z.core.$strict>>;
418
- cliffConfigPath: z.ZodOptional<z.ZodString>;
419
486
  formatCommand: z.ZodOptional<z.ZodString>;
420
487
  project: z.ZodOptional<z.ZodObject<{
421
488
  paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -452,6 +519,7 @@ export declare const releaseKitConfigSchema: z.ZodObject<{
452
519
  workTypes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
453
520
  header: z.ZodString;
454
521
  aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
522
+ excludedFromChangelog: z.ZodOptional<z.ZodBoolean>;
455
523
  }, z.core.$strict>>>;
456
524
  }, z.core.$strict>;
457
525
  /**
@@ -461,14 +529,16 @@ export declare const releaseKitConfigSchema: z.ZodObject<{
461
529
  export type ReleaseKitConfig = z.infer<typeof releaseKitConfigSchema>;
462
530
  /** A raw commit from the git log. */
463
531
  export interface Commit {
464
- /** The full commit message (first line). */
532
+ /** The commit message, subject and body together. */
465
533
  message: string;
534
+ /** The first line of the commit message. */
535
+ subject: string;
466
536
  /** The commit hash. */
467
537
  hash: string;
468
538
  }
469
539
  /** A commit that has been parsed to extract structured metadata. */
470
540
  export interface ParsedCommit {
471
- /** The original commit message. */
541
+ /** The original commit message, subject and body together. */
472
542
  message: string;
473
543
  /** The commit hash. */
474
544
  hash: string;
@@ -489,12 +559,11 @@ export interface WorkspaceConfig {
489
559
  name: string;
490
560
  /** The git tag prefix for this workspace (e.g., 'nmr-core-v'), derived from the unscoped `package.json` name. */
491
561
  tagPrefix: string;
492
- /** Workspace-relative path to the package root (e.g., `packages/core`). */
562
+ /** Repo-relative path to the package root (e.g., `packages/core`). */
493
563
  workspacePath: string;
494
564
  /**
495
565
  * Whether this workspace can be published to a registry. `true` when `package.json#private`
496
- * is absent or `false`; `false` only when `private === true`. Consumed by `release-kit publish`
497
- * to filter unpublishable workspaces; other commands ignore this field.
566
+ * is absent or `false`; `false` only when `private === true`.
498
567
  */
499
568
  isPublishable: boolean;
500
569
  /** Paths to package.json files to bump. */
@@ -523,15 +592,13 @@ export interface MonorepoReleaseConfig {
523
592
  * When provided, replaces the default entirely.
524
593
  * Pass `{}` to disable enforcement (parser falls back to `'optional'` for missing types).
525
594
  */
526
- breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
595
+ breakingPolicies?: Record<string, BreakingPolicy>;
527
596
  /**
528
597
  * Shell command to run after all changelogs are generated (e.g., 'pnpm run fmt').
529
598
  * Modified file paths (package.json files and CHANGELOGs) are appended as space-separated
530
599
  * arguments. Paths are repo-relative; file paths containing spaces are not supported.
531
600
  */
532
601
  formatCommand?: string;
533
- /** Path to the cliff.toml file; defaults to 'cliff.toml' when absent. */
534
- cliffConfigPath?: string;
535
602
  /**
536
603
  * Maps scope shorthand names to their canonical names.
537
604
  * When a commit uses `shorthand|type: description` or `type(shorthand): description`,
@@ -543,11 +610,13 @@ export interface MonorepoReleaseConfig {
543
610
  /** Controls release notes consumption (README injection). */
544
611
  releaseNotes: ReleaseNotesConfig;
545
612
  /**
546
- * Project-level release config. Present iff the consumer declared `project: {}`.
547
- * When present, `releasePrepareMono` runs an additional project-release stage after the per-workspace loop.
613
+ * Project-level release config. Present when the config declares a `project` block, in which case
614
+ * `prepare` runs a project-release stage after the per-workspace loop.
548
615
  */
549
616
  project?: ResolvedProjectConfig;
550
617
  }
618
+ /** Monorepo config as seen by the prepare workflow, which does not read release-notes configuration. */
619
+ export type MonorepoPrepareConfig = Omit<MonorepoReleaseConfig, 'releaseNotes'>;
551
620
  /** Configuration for the release workflow. */
552
621
  export interface ReleaseConfig {
553
622
  /** The git tag prefix used to identify version tags (e.g., 'v'). */
@@ -565,15 +634,13 @@ export interface ReleaseConfig {
565
634
  * provided, replaces the default entirely. Pass `{}` to disable enforcement (parser falls
566
635
  * back to `'optional'` for missing types).
567
636
  */
568
- breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
637
+ breakingPolicies?: Record<string, BreakingPolicy>;
569
638
  /**
570
639
  * Shell command to run after changelog generation (e.g., 'pnpm run fmt').
571
640
  * Modified file paths (package.json files and CHANGELOGs) are appended as space-separated arguments.
572
641
  * Paths are repo-relative; file paths containing spaces are not supported.
573
642
  */
574
643
  formatCommand?: string;
575
- /** Path to the cliff.toml file; defaults to 'cliff.toml' when absent. */
576
- cliffConfigPath?: string;
577
644
  /**
578
645
  * Maps scope shorthand names to their canonical names.
579
646
  * When a commit uses `shorthand|type: description` or `type(shorthand): description`,
@@ -585,3 +652,5 @@ export interface ReleaseConfig {
585
652
  /** Controls release notes consumption (README injection). */
586
653
  releaseNotes: ReleaseNotesConfig;
587
654
  }
655
+ /** Single-package config as seen by the prepare workflow, which does not read release-notes configuration. */
656
+ export type PrepareConfig = Omit<ReleaseConfig, 'releaseNotes'>;
package/dist/esm/types.js CHANGED
@@ -41,6 +41,7 @@ export const workTypeConfigSchema = z
41
41
  .object({
42
42
  header: z.string(),
43
43
  aliases: z.array(z.string()).optional(),
44
+ excludedFromChangelog: z.boolean().optional(),
44
45
  })
45
46
  .strict();
46
47
  export const versionPatternsSchema = z
@@ -49,7 +50,7 @@ export const versionPatternsSchema = z
49
50
  minor: z.array(z.string()),
50
51
  })
51
52
  .strict();
52
- export const breakingPolicyValueSchema = z.enum(['forbidden', 'optional', 'required']);
53
+ export const breakingPolicyValueSchema = z.enum(['forbidden', 'optional']);
53
54
  export const labelSpecSchema = z
54
55
  .object({
55
56
  color: z.string().min(1),
@@ -66,7 +67,6 @@ export const releaseKitConfigSchema = z
66
67
  .object({
67
68
  breakingPolicies: z.record(z.string(), breakingPolicyValueSchema).optional(),
68
69
  changelogJson: changelogJsonInputSchema.optional(),
69
- cliffConfigPath: z.string().min(1).optional(),
70
70
  formatCommand: z.string().min(1).optional(),
71
71
  project: projectConfigSchema.optional(),
72
72
  releaseNotes: releaseNotesInputSchema.optional(),
@@ -1,16 +1,7 @@
1
1
  import { type ReleaseKitConfig } from './types.js';
2
2
  /**
3
- * Validate a raw config object loaded from `.config/release-kit.config.ts`.
4
- *
5
- * Single source of truth: `releaseKitConfigSchema` in `types.ts`, with
6
- * `ReleaseKitConfig = z.infer<typeof releaseKitConfigSchema>`. Adding a field to the type
7
- * without updating the schema is impossible because the type *is* derived from the
8
- * schema.
9
- *
10
- * Pipeline: (1) preprocess strips deprecated keys and emits migration-guidance errors;
11
- * (2) `releaseKitConfigSchema.safeParse` does shape validation; (3) post-parse cross-field
12
- * checks (full-tuple duplicates, retired-vs-legacy collisions) operate on the typed
13
- * result.
3
+ * Validates a raw config object loaded from `.config/release-kit.config.ts`. Returns `{}` with the errors for a config
4
+ * that fails the schema parse, and runs the cross-field checks on one that passes.
14
5
  */
15
6
  export declare function validateConfig(raw: unknown): {
16
7
  config: ReleaseKitConfig;