@williamthorsen/release-kit 10.4.4 → 10.6.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 (105) hide show
  1. package/.meta/changelog.json +1502 -0
  2. package/.readyup/kits/default.js +120 -4
  3. package/.readyup/kits/npm-auto-publish.js +11 -3
  4. package/.readyup/manifest.json +16 -11
  5. package/CHANGELOG.md +57 -0
  6. package/README.md +64 -4
  7. package/dist/esm/assertCleanWorkingTree.d.ts +5 -0
  8. package/dist/esm/buildChangelogEntries.d.ts +22 -0
  9. package/dist/esm/buildChangelogEntries.js +5 -0
  10. package/dist/esm/buildDependencyGraph.d.ts +16 -0
  11. package/dist/esm/buildEmptyReleaseEntry.d.ts +8 -0
  12. package/dist/esm/buildReleaseSummary.d.ts +10 -0
  13. package/dist/esm/buildSyntheticChangelogEntry.d.ts +6 -0
  14. package/dist/esm/bumpVersion.d.ts +8 -0
  15. package/dist/esm/changelogJsonFile.d.ts +11 -0
  16. package/dist/esm/changelogJsonUtils.d.ts +16 -2
  17. package/dist/esm/changelogJsonUtils.js +0 -13
  18. package/dist/esm/changelogOverrides.d.ts +143 -0
  19. package/dist/esm/changelogOverrides.js +8 -0
  20. package/dist/esm/checkWorkTypesDrift.d.ts +25 -0
  21. package/dist/esm/collectPolicyViolations.d.ts +9 -0
  22. package/dist/esm/commitCommand.d.ts +6 -0
  23. package/dist/esm/compareVersions.d.ts +7 -0
  24. package/dist/esm/createGithubRelease.d.ts +30 -0
  25. package/dist/esm/createGithubReleaseCommand.d.ts +7 -0
  26. package/dist/esm/createTags.d.ts +5 -0
  27. package/dist/esm/decideRelease.d.ts +37 -0
  28. package/dist/esm/defaults.d.ts +25 -0
  29. package/dist/esm/defineConfig.d.ts +9 -0
  30. package/dist/esm/deleteFileIfExists.d.ts +1 -0
  31. package/dist/esm/deriveWorkspaceConfig.d.ts +9 -0
  32. package/dist/esm/detectPackageManager.d.ts +6 -0
  33. package/dist/esm/detectUndeclaredTagPrefixes.d.ts +18 -0
  34. package/dist/esm/determineBumpFromCommits.d.ts +5 -0
  35. package/dist/esm/determineBumpType.d.ts +12 -0
  36. package/dist/esm/discoverWorkspaces.d.ts +12 -0
  37. package/dist/esm/extractMigration.d.ts +14 -0
  38. package/dist/esm/extractMigration.js +18 -0
  39. package/dist/esm/format.d.ts +3 -0
  40. package/dist/esm/formatPrivateSkip.d.ts +6 -0
  41. package/dist/esm/generateChangelogs.d.ts +25 -0
  42. package/dist/esm/getCommitsSinceTarget.d.ts +16 -0
  43. package/dist/esm/hasPrettierConfig.d.ts +6 -0
  44. package/dist/esm/help/prepareHelp.d.ts +6 -0
  45. package/dist/esm/init/checks.d.ts +4 -0
  46. package/dist/esm/init/detectRepoType.d.ts +7 -0
  47. package/dist/esm/init/initCommand.d.ts +6 -0
  48. package/dist/esm/init/parseJsonRecord.d.ts +4 -0
  49. package/dist/esm/init/scaffold.d.ts +3 -1
  50. package/dist/esm/init/templates.d.ts +13 -0
  51. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +39 -0
  52. package/dist/esm/injectSection.d.ts +6 -0
  53. package/dist/esm/loadConfig.d.ts +46 -0
  54. package/dist/esm/loadValidatedConfig.d.ts +8 -0
  55. package/dist/esm/packsPath.d.ts +15 -0
  56. package/dist/esm/packsPath.js +47 -0
  57. package/dist/esm/parseCommitMessage.d.ts +42 -0
  58. package/dist/esm/parseRequestedTags.d.ts +7 -0
  59. package/dist/esm/planReleaseNotesPreviews.d.ts +20 -0
  60. package/dist/esm/planVersionBump.d.ts +12 -0
  61. package/dist/esm/prepareCommand.d.ts +10 -0
  62. package/dist/esm/previewTagPrefixes.d.ts +21 -1
  63. package/dist/esm/propagateBumps.d.ts +15 -0
  64. package/dist/esm/publish.d.ts +1 -0
  65. package/dist/esm/publishCommand.d.ts +4 -0
  66. package/dist/esm/pushCommand.d.ts +4 -0
  67. package/dist/esm/pushRelease.d.ts +6 -0
  68. package/dist/esm/readCurrentVersion.d.ts +1 -0
  69. package/dist/esm/releaseFiles.d.ts +22 -0
  70. package/dist/esm/releasePlan.d.ts +23 -0
  71. package/dist/esm/releasePrepare.d.ts +40 -0
  72. package/dist/esm/releasePrepare.js +0 -3
  73. package/dist/esm/releasePrepareMono.d.ts +8 -0
  74. package/dist/esm/releasePrepareMono.js +0 -4
  75. package/dist/esm/releasePrepareProject.d.ts +42 -0
  76. package/dist/esm/renderChangelogMarkdown.d.ts +17 -0
  77. package/dist/esm/renderReleaseNotes.d.ts +25 -0
  78. package/dist/esm/reportPrepare.d.ts +14 -0
  79. package/dist/esm/reportPrepare.js +4 -2
  80. package/dist/esm/resolveCliffConfigPath.d.ts +10 -0
  81. package/dist/esm/resolveCommandTags.d.ts +13 -1
  82. package/dist/esm/resolveReleaseNotesConfig.d.ts +14 -0
  83. package/dist/esm/resolveReleaseTags.d.ts +22 -0
  84. package/dist/esm/runGitCliff.d.ts +37 -1
  85. package/dist/esm/runGitCliff.js +4 -10
  86. package/dist/esm/showTagPrefixesCommand.d.ts +11 -0
  87. package/dist/esm/stripScope.d.ts +7 -0
  88. package/dist/esm/sync-labels/generateCommand.d.ts +17 -0
  89. package/dist/esm/sync-labels/initCommand.d.ts +9 -0
  90. package/dist/esm/sync-labels/presets.d.ts +6 -0
  91. package/dist/esm/sync-labels/resolveLabels.d.ts +10 -0
  92. package/dist/esm/sync-labels/retiredConfig.d.ts +8 -0
  93. package/dist/esm/sync-labels/syncCommand.d.ts +6 -0
  94. package/dist/esm/sync-labels/templates.d.ts +14 -0
  95. package/dist/esm/sync-labels/types.d.ts +4 -0
  96. package/dist/esm/syncWorkTypes.d.ts +18 -0
  97. package/dist/esm/tagCommand.d.ts +1 -0
  98. package/dist/esm/typeGuards.d.ts +2 -0
  99. package/dist/esm/types.d.ts +302 -0
  100. package/dist/esm/validateConfig.d.ts +14 -1
  101. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +21 -0
  102. package/dist/esm/validateOverridesCommand.d.ts +30 -0
  103. package/dist/esm/workTypesData.d.ts +27 -0
  104. package/dist/esm/workTypesUtils.d.ts +17 -0
  105. package/package.json +12 -11
@@ -1,18 +1,5 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { isRecord, isUnknownArray } from "./typeGuards.js";
3
- export function isChangelogItem(value) {
4
- if (!isRecord(value))
5
- return false;
6
- if (typeof value['description'] !== 'string')
7
- return false;
8
- if (value['body'] !== undefined && typeof value['body'] !== 'string')
9
- return false;
10
- if (value['breaking'] !== undefined && typeof value['breaking'] !== 'boolean')
11
- return false;
12
- if (value['hash'] !== undefined && typeof value['hash'] !== 'string')
13
- return false;
14
- return true;
15
- }
16
3
  export function isChangelogEntry(value) {
17
4
  return (isRecord(value) &&
18
5
  typeof value['version'] === 'string' &&
@@ -1,53 +1,196 @@
1
1
  import type { ChangelogEntry, ChangelogOverride, WorkspaceConfig } from './types.js';
2
+ /** Result of loading an override file: either parsed overrides or a list of structured errors. */
2
3
  export type LoadChangelogOverridesResult = {
3
4
  overrides: Map<string, ChangelogOverride>;
4
5
  } | {
5
6
  errors: string[];
6
7
  };
8
+ /**
9
+ * Load and validate the editorial overrides file at `path`.
10
+ *
11
+ * - Missing file resolves to an empty map (no-op default; matches "absent file → unchanged behavior").
12
+ * - Malformed JSON, wrong top-level shape, or any per-entry validation failure surfaces as
13
+ * structured errors. Callers decide how to surface them.
14
+ *
15
+ * Returns either `{ overrides: Map }` on success or `{ errors: string[] }` on failure. Pure
16
+ * except for the single file read; performs no other I/O.
17
+ */
7
18
  export declare function loadChangelogOverrides(path: string): LoadChangelogOverridesResult;
19
+ /**
20
+ * Validate a parsed override record. Returns the parsed `Map` along with any error messages.
21
+ *
22
+ * Each error names the offending key (e.g. `overrides['abc']: 'audience' must be one of …`)
23
+ * so the consumer can locate it in their override file.
24
+ */
8
25
  export declare function validateChangelogOverrides(raw: unknown): {
9
26
  overrides: Map<string, ChangelogOverride>;
10
27
  errors: string[];
11
28
  };
29
+ /**
30
+ * Format the standard "stale override key" warning. Callers compute the set of stale keys
31
+ * (keys that didn't match anywhere) and use this helper to surface them uniformly. Centralized
32
+ * so single-package and monorepo flows produce identical warning text.
33
+ */
12
34
  export declare function formatStaleOverrideKeyWarning(key: string): string;
35
+ /**
36
+ * Apply overrides to a `ChangelogEntry[]`, returning a new array. Pure: no mutation, no I/O.
37
+ *
38
+ * Match algorithm: each override key is treated as a string-prefix against `ChangelogItem.hash`.
39
+ * - 0 matches → key is recorded as unmatched in this batch (no warning emitted here).
40
+ * - 1 match → apply each present override field to the matched item, record key as matched.
41
+ * - 2+ matches → error (ambiguous prefix).
42
+ *
43
+ * `matchedKeys` lists the override keys that resolved to exactly one item in this batch.
44
+ * Callers are responsible for computing stale-key warnings: in a monorepo run, an override
45
+ * may target a commit that lives in another workspace, so the per-batch zero-match signal is
46
+ * insufficient on its own. Single-package and monorepo orchestrators format warnings using
47
+ * `formatStaleOverrideKeyWarning` after aggregating `matchedKeys` across all apply calls.
48
+ *
49
+ * v1 audience semantics:
50
+ * - `'skip'` removes the matched item from its containing section. Empty sections are pruned.
51
+ * Versions with zero sections still appear (matches existing "empty workspace" behavior).
52
+ * - `'all'` and `'dev'` are validated out before reaching the applier.
53
+ *
54
+ * Items without a `hash` (synthetic propagation entries) are never matched and pass through.
55
+ *
56
+ * The function does not throw; warnings/errors accumulate and are returned alongside the
57
+ * transformed entries so the caller decides whether to abort or log-and-continue.
58
+ */
13
59
  export declare function applyChangelogOverrides(entries: ChangelogEntry[], overrides: Map<string, ChangelogOverride>): {
14
60
  entries: ChangelogEntry[];
15
61
  warnings: string[];
16
62
  errors: string[];
17
63
  matchedKeys: string[];
18
64
  };
65
+ /**
66
+ * Resolve the conventional override-file path for a given scope root.
67
+ *
68
+ * `scopeRoot` is the directory at which a scope's other artifacts live (e.g., `'.'` for the
69
+ * project, `'packages/foo'` for a workspace). The returned path is repo-relative.
70
+ */
19
71
  export declare function resolveOverridePath(scopeRoot: string): string;
72
+ /** Result of {@link loadOverridesForScopes}: per-scope maps plus accumulated load/validation errors. */
20
73
  export interface LoadOverridesForScopesResult {
74
+ /** Project-tier (root) overrides. Empty map when the project file is absent. */
21
75
  project: Map<string, ChangelogOverride>;
76
+ /** Workspace-tier overrides keyed by `workspacePath` (e.g., `'packages/foo'`). Empty map when absent. */
22
77
  perWorkspace: Map<string, Map<string, ChangelogOverride>>;
78
+ /** All load and validation errors across every requested scope, prefixed with the offending file path. */
23
79
  errors: string[];
24
80
  }
81
+ /**
82
+ * Load and validate every requested override file in one pass.
83
+ *
84
+ * Iterates the configured scope roots, calls {@link loadChangelogOverrides} for each, and
85
+ * aggregates errors across all files (rather than throwing on the first failure) so a
86
+ * consumer who edits multiple files at once sees every problem in one report. Missing files
87
+ * resolve to empty maps — matches the existing single-file behavior.
88
+ *
89
+ * The caller is expected to surface a combined error when `errors.length > 0` and abort the
90
+ * release before any workspace begins writing.
91
+ */
25
92
  export declare function loadOverridesForScopes(scopes: {
26
93
  project?: string;
27
94
  workspaces?: string[];
28
95
  }): LoadOverridesForScopesResult;
96
+ /**
97
+ * Compose root and workspace override maps into a single effective map for one workspace.
98
+ *
99
+ * Byte-equal-key shadowing: when a workspace key string-equals a root key, the workspace
100
+ * entry wins entirely (no field-level merge) and supplants the root entry in the result.
101
+ * Different-prefix keys that happen to resolve to the same commit do NOT shadow here — they
102
+ * fall through to the existing ambiguous-prefix error in {@link applyChangelogOverrides}.
103
+ *
104
+ * Pure: never mutates inputs.
105
+ */
29
106
  export declare function composeOverrides(rootEntries: Map<string, ChangelogOverride>, workspaceEntries: Map<string, ChangelogOverride> | undefined): Map<string, ChangelogOverride>;
107
+ /**
108
+ * Shared override-application context threaded through the per-workspace and project apply
109
+ * sites. Bundles the loaded per-scope maps with the run-scoped warning aggregators so each
110
+ * call site can both consume the maps it needs and report into the same warning channel.
111
+ *
112
+ * `globalMatchedRootKeys` tracks ROOT keys matched in any apply call. Workspace-sourced
113
+ * matched keys are not added here — they're tracked locally per workspace because their
114
+ * stale-key semantics are local (a workspace key that doesn't match in its own workspace is
115
+ * unambiguously stale and is warned immediately, not aggregated to end-of-run).
116
+ */
30
117
  export interface OverrideContext {
31
118
  project: Map<string, ChangelogOverride>;
32
119
  perWorkspace: Map<string, Map<string, ChangelogOverride>>;
33
120
  overrideWarnings: string[];
34
121
  globalMatchedRootKeys: Set<string>;
35
122
  }
123
+ /**
124
+ * Load all per-scope override files (root + every workspace) in one upfront pass, validate
125
+ * them, and bundle the results with run-scoped warning aggregators. Aborts the release with
126
+ * a clear error when any file is malformed — checked-in editorial config that does not parse
127
+ * is a bug, and we want the prepare run to fail before any workspace begins writing.
128
+ */
36
129
  export declare function createOverrideContext(workspaces: WorkspaceConfig[]): OverrideContext;
130
+ /** Per-scope input to {@link validateAllChangelogOverrides}. */
37
131
  export interface ChangelogOverrideScope {
132
+ /** Path to the override file (relative to the repo root). Used to load the file and to attribute findings. */
38
133
  filePath: string;
134
+ /** Commit hashes in this scope's history window. Each override key is matched against these. */
39
135
  hashes: readonly string[];
40
136
  }
137
+ /** Inputs to {@link validateAllChangelogOverrides}. */
41
138
  export interface ValidateAllChangelogOverridesInputs {
139
+ /**
140
+ * Project-tier scope (the override file at the repo root).
141
+ *
142
+ * The file at `filePath` is loaded once and used in two ways:
143
+ * - Its overrides are composed into every workspace's apply (root-tier overrides apply globally).
144
+ * - When `hashes` is provided, the project map is also applied directly to that hash universe
145
+ * (project release in monorepo mode, or the package's history in single-package mode).
146
+ *
147
+ * Omit when no project file exists (rare — most repos have a root file even if empty).
148
+ */
42
149
  project?: {
43
150
  filePath: string;
44
151
  hashes?: readonly string[];
45
152
  };
153
+ /** Per-workspace scopes. Each workspace's file applies only to its own hash universe. */
46
154
  workspaces?: readonly ChangelogOverrideScope[];
47
155
  }
156
+ /** Result of {@link validateAllChangelogOverrides}: aggregated errors and warnings, each prefixed with the file path it pertains to. */
48
157
  export interface ValidateAllChangelogOverridesResult {
49
158
  errors: string[];
50
159
  warnings: string[];
51
160
  }
161
+ /**
162
+ * End-to-end health check across every override file and scope. Pure: takes already-collected
163
+ * hash universes and returns aggregated findings. The CLI command and any other consumer
164
+ * (programmatic library callers, future composite checks) wrap this with discovery and I/O.
165
+ *
166
+ * When invoked via the standard `validateOverridesCommand` entry point, each scope's
167
+ * `hashes` is built by `buildChangelogEntries` — the same path `release-kit prepare` walks —
168
+ * so the match-set is byte-equal to what `prepare` would compute. The tier asymmetry is part
169
+ * of that contract: workspace-tier keys are stale if they don't match in their own workspace;
170
+ * root-tier keys are stale only if they don't match in any scope (no workspace AND not the
171
+ * project release window). Library callers that construct `inputs` directly are responsible
172
+ * for supplying the same hash universes if they want this guarantee.
173
+ *
174
+ * Every returned string is prefixed with the relative override-file path it pertains to so
175
+ * consumers can locate the offending file without further structuring.
176
+ */
52
177
  export declare function validateAllChangelogOverrides(inputs: ValidateAllChangelogOverridesInputs): ValidateAllChangelogOverridesResult;
178
+ /**
179
+ * Apply the composed (root + workspace) override map to a workspace's changelog entries and
180
+ * report stale-key warnings tier-by-tier.
181
+ *
182
+ * The composed map is applied in a single {@link applyChangelogOverrides} call. Matched keys
183
+ * are demultiplexed by source via `Map.has` lookups on the original (uncomposed) maps so the
184
+ * stale-key semantics stay tier-aware:
185
+ *
186
+ * - Workspace-sourced keys (those present in the per-workspace map, regardless of whether
187
+ * the root map also has the same byte-equal key) that did NOT match are unambiguously
188
+ * stale in their own apply context — push an immediate stale warning naming the key.
189
+ * - Root-sourced matched keys (present in root, absent from this workspace's map) are added
190
+ * to `globalMatchedRootKeys` so the orchestrator's end-of-run loop can dedupe across
191
+ * batches and warn only on root keys that matched nowhere.
192
+ *
193
+ * Throws when any apply call surfaces an error (e.g. ambiguous prefix). Returns the applied
194
+ * result so the caller can consume `applied.entries` for downstream rendering.
195
+ */
53
196
  export declare function applyWorkspaceOverrides(newEntries: ChangelogEntry[], workspacePath: string, overrideContext: OverrideContext): ReturnType<typeof applyChangelogOverrides>;
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { describeError } from '@williamthorsen/toolbelt.errors';
4
+ import { extractMigration } from "./extractMigration.js";
4
5
  import { isRecord } from "./typeGuards.js";
5
6
  const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
6
7
  const VALID_AUDIENCE_VALUES = new Set(['all', 'dev', 'skip']);
@@ -206,6 +207,13 @@ function applyOverrideToItem(item, override) {
206
207
  }
207
208
  if (override.body !== undefined) {
208
209
  result.body = override.body;
210
+ const migration = extractMigration(override.body);
211
+ if (migration === undefined) {
212
+ delete result.migration;
213
+ }
214
+ else {
215
+ result.migration = migration;
216
+ }
209
217
  }
210
218
  if (override.breaking !== undefined) {
211
219
  result.breaking = override.breaking;
@@ -1,11 +1,36 @@
1
+ /** URL of the upstream canonical `work-types.json` published by codeassembly. */
1
2
  export declare const UPSTREAM_WORK_TYPES_URL = "https://raw.githubusercontent.com/williamthorsen/codeassembly/main/packages/agents/content/skills/_data/work-types.json";
3
+ /** Outcome of a drift check, paired with a human-readable message. */
2
4
  export interface DriftCheckResult {
5
+ /**
6
+ * Process exit code semantics:
7
+ * - `0` — match (or upstream missing/transitional, with a warning).
8
+ * - `1` — drift detected.
9
+ * - `2` — network error.
10
+ * - `3` — schema mismatch (upstream JSON does not parse or fails the schema invariants).
11
+ */
3
12
  exitCode: 0 | 1 | 2 | 3;
4
13
  message: string;
5
14
  }
15
+ /** Minimal injection seam so unit tests can substitute a deterministic fetcher. */
6
16
  export interface CheckWorkTypesDriftDependencies {
17
+ /** Absolute path of the local `work-types.json`. Defaults to the bundled file. */
7
18
  localPath?: string;
19
+ /** HTTP fetcher. Defaults to global `fetch` (Node 18+). */
8
20
  fetch?: typeof globalThis.fetch;
21
+ /** Override the upstream URL (used by tests; production callers should leave default). */
9
22
  upstreamUrl?: string;
10
23
  }
24
+ /**
25
+ * Compare the bundled `work-types.json` against the upstream codeassembly canonical and
26
+ * report drift.
27
+ *
28
+ * Failure modes:
29
+ * - Network error → exit 2 with a diagnostic.
30
+ * - Upstream 404 → exit 0 with a transitional warning (codeassembly hasn't published the
31
+ * JSON yet; the check is informational while migration is in flight).
32
+ * - Schema mismatch → exit 3 with a diagnostic.
33
+ * - Drift → exit 1 with a unified-diff-style message.
34
+ * - Match → exit 0.
35
+ */
11
36
  export declare function checkWorkTypesDrift(dependencies?: CheckWorkTypesDriftDependencies): Promise<DriftCheckResult>;
@@ -1,5 +1,14 @@
1
1
  import type { PolicyViolationHandler } from './parseCommitMessage.js';
2
2
  import type { PolicyViolation } from './types.js';
3
+ /**
4
+ * Build a fresh `policyViolations` accumulator paired with the `onPolicyViolation` callback
5
+ * that pushes structured entries into it.
6
+ *
7
+ * Used by all three release-prepare orchestrators (`releasePrepare`, `releasePrepareMono`,
8
+ * `releasePrepareProject`) to collect parser-side `!`-policy violations into a result-attachable
9
+ * array. Centralizing the callback shape keeps the `commitSubject` extraction rule (`firstLine
10
+ * post-split`) consistent across orchestrators.
11
+ */
3
12
  export declare function createPolicyViolationCollector(): {
4
13
  violations: PolicyViolation[];
5
14
  onPolicyViolation: PolicyViolationHandler;
@@ -1 +1,7 @@
1
+ /**
2
+ * Orchestrate the CLI `commit` command.
3
+ *
4
+ * Reads the tags and summary files produced by `prepare`, stages all
5
+ * changes, and creates a release commit with a formatted message.
6
+ */
1
7
  export declare function commitCommand(argv: string[]): void;
@@ -1 +1,8 @@
1
+ /**
2
+ * Return true when `target` is strictly greater than `current`, comparing major, minor, then patch.
3
+ *
4
+ * Equal versions return false. Comparison is numeric (not lexical), so `0.10.0` is greater than `0.9.0`.
5
+ *
6
+ * @throws If either version is not canonical `N.N.N` semver.
7
+ */
1
8
  export declare function isForwardVersion(current: string, target: string): boolean;
@@ -1,24 +1,54 @@
1
+ /** Options for creating a GitHub Release. */
1
2
  export interface CreateGithubReleaseOptions {
2
3
  tag: string;
3
4
  changelogJsonPath: string;
4
5
  dryRun: boolean;
6
+ /** Section titles in priority order. When omitted, entry order is preserved. */
5
7
  sectionOrder?: string[];
6
8
  }
9
+ /**
10
+ * Discriminated reason a single tag was skipped.
11
+ *
12
+ * `'no-entry'` is the umbrella for all "data missing" sub-cases (changelog file not found,
13
+ * JSON unparseable, version not in changelog) — distinguishing them programmatically adds no
14
+ * value to the consumer, while the existing `console.warn` messages already discriminate them
15
+ * for human diagnostics.
16
+ */
7
17
  export type CreateReleaseSkipReason = 'no-entry' | 'no-audience-content' | 'empty-body';
18
+ /** Discriminated outcome of attempting to create a single GitHub Release. */
8
19
  export type CreateReleaseResult = {
9
20
  status: 'created';
10
21
  } | {
11
22
  status: 'skipped';
12
23
  reason: CreateReleaseSkipReason;
13
24
  };
25
+ /**
26
+ * Create a GitHub Release from changelog.json using the `gh` CLI.
27
+ *
28
+ * Reads the changelog JSON, finds the entry matching the tag's version, renders all-audience
29
+ * release notes, and creates the release. Returns a discriminated `{status: 'skipped', reason}`
30
+ * (with a warning) when the changelog entry is missing, the entry has no all-audience content,
31
+ * or the rendered body is empty. Throws when the `gh` CLI invocation itself fails so callers
32
+ * can surface the failure rather than exit 0 silently.
33
+ */
14
34
  export declare function createGithubRelease(options: CreateGithubReleaseOptions): CreateReleaseResult;
35
+ /** Outcome of a `createGithubReleases` invocation. */
15
36
  export interface CreateGithubReleasesOutcome {
37
+ /** Tags for which a Release was created (or would be, under `--dry-run`). */
16
38
  created: string[];
39
+ /** Tags that were skipped, paired with the discriminated reason for each skip. */
17
40
  skipped: Array<{
18
41
  tag: string;
19
42
  reason: CreateReleaseSkipReason;
20
43
  }>;
21
44
  }
45
+ /**
46
+ * Create GitHub Releases for each provided tag.
47
+ *
48
+ * Per-tag soft-fails (missing changelog entry, no all-audience content, empty rendered body) are
49
+ * recorded in `skipped` with their discriminated reason and do not throw. Hard failures from the
50
+ * `gh` CLI itself throw and short-circuit the loop so callers can surface them.
51
+ */
22
52
  export declare function createGithubReleases(tags: Array<{
23
53
  tag: string;
24
54
  workspacePath: string;
@@ -1 +1,8 @@
1
+ /**
2
+ * Orchestrate the CLI `create-github-release` command: Create GitHub Releases from changelog.json
3
+ * for tags on HEAD (or a comma-separated `--tags` subset), without requiring npm publish.
4
+ *
5
+ * Private/unpublishable workspaces are skipped with a warning and never get a Release, matching
6
+ * `release-kit publish`; an all-private tag set is a clean no-op.
7
+ */
1
8
  export declare function createGithubReleaseCommand(argv: string[]): Promise<void>;
@@ -2,4 +2,9 @@ export interface CreateTagsOptions {
2
2
  dryRun: boolean;
3
3
  noGitChecks: boolean;
4
4
  }
5
+ /**
6
+ * Read tag names from the tags file produced by `prepare` and create annotated git tags.
7
+ *
8
+ * Returns the list of tag names that were created (or would be created in dry-run mode).
9
+ */
5
10
  export declare function createTags(options: CreateTagsOptions): string[];
@@ -1,19 +1,30 @@
1
1
  import { type PolicyViolationHandler } from './parseCommitMessage.js';
2
2
  import type { Commit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.js';
3
+ /** Inputs to the unified release decision used by both pipelines. */
3
4
  export interface DecideReleaseArgs {
4
5
  commits: readonly Commit[];
6
+ /** True when `--force` was passed; treats absence of a natural bump as patch. Defaults to false. */
5
7
  force?: boolean | undefined;
8
+ /** Explicit `--bump=X` override. When set, the level is X regardless of the natural bump. */
6
9
  bumpOverride: ReleaseType | undefined;
7
10
  workTypes: Record<string, WorkTypeConfig>;
8
11
  versionPatterns: VersionPatterns;
9
12
  scopeAliases: Record<string, string> | undefined;
13
+ /** Per-canonical-type breaking-policy lookup. When provided, drives `!` policy enforcement in `parseCommitMessage`. */
10
14
  breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
15
+ /** Receives policy-violation notifications from `parseCommitMessage`. */
11
16
  onPolicyViolation?: PolicyViolationHandler;
17
+ /**
18
+ * Skip wordings to use when no commits exist (`noCommits`) or when commits exist but
19
+ * none parse to a bump-worthy work type (`noBumpWorthy`). Both pipelines pass their
20
+ * own pre-rendered wordings (with `{name}` / `since` substitutions already applied).
21
+ */
12
22
  skipReasons: {
13
23
  noCommits: string;
14
24
  noBumpWorthy: string;
15
25
  };
16
26
  }
27
+ /** Outcome of `decideRelease`: either release at a chosen level or skip with a reason. */
17
28
  export type DecideReleaseResult = {
18
29
  outcome: 'release';
19
30
  releaseType: ReleaseType;
@@ -25,4 +36,30 @@ export type DecideReleaseResult = {
25
36
  parsedCommitCount: number;
26
37
  unparseableCommits: Commit[] | undefined;
27
38
  };
39
+ /**
40
+ * Apply the unified release-decision algorithm shared by both `releasePrepareProject`
41
+ * and the per-workspace path in `releasePrepareMono`.
42
+ *
43
+ * Algorithm:
44
+ * shouldRelease = naturalBump !== undefined OR force === true
45
+ * releaseLevel = bumpOverride ?? naturalBump ?? 'patch'
46
+ *
47
+ * "natural bump" is the raw output of `determineBumpType` over the parsed commits — it
48
+ * is `undefined` when no commits exist or when none of the parsed commits map to a
49
+ * bump-worthy work type. Note that this differs from `determineBumpFromCommits`, which
50
+ * applies a patch floor whenever any commits exist; the patch floor is intentionally
51
+ * not applied here so that `--bump=X` and `--force` remain orthogonal (the floor is
52
+ * applied only when `--force` is set, by virtue of the `naturalBump ?? 'patch'`
53
+ * fallback in the release-level expression above).
54
+ *
55
+ * `parsedCommitCount` and `unparseableCommits` are returned in both branches so callers
56
+ * can surface diagnostic data uniformly regardless of skip/release outcome.
57
+ *
58
+ * Scope: this is the canonical algorithm for the orthogonal-flag model in the
59
+ * monorepo path (`releasePrepareProject` + per-workspace `determineDirectBumps`). The
60
+ * single-package executor (`releasePrepare`) deliberately retains
61
+ * `determineBumpFromCommits` and the legacy patch-floor semantics; `prepareCommand`
62
+ * rejects `--force` without `--bump` in single-package mode to surface that gap. A
63
+ * future ticket can unify the single-package path; see #313's external plan.
64
+ */
28
65
  export declare function decideRelease(args: DecideReleaseArgs): DecideReleaseResult;
@@ -1,13 +1,38 @@
1
1
  import type { ChangelogJsonConfig, ReleaseNotesConfig, VersionPatterns, WorkTypeConfig } from './types.js';
2
+ /** Re-export the runtime taxonomy so consumers don't have to depend on `workTypesData.ts` directly. */
2
3
  export type { WorkTypesData } from './workTypesData.js';
3
4
  export { WORK_TYPES_DATA } from './workTypesData.js';
5
+ /**
6
+ * Compose the rendered section heading for a work-type entry as `${emoji} ${label}`.
7
+ *
8
+ * Single-sourced here so callers (DEFAULT_WORK_TYPES derivation, devOnlySections
9
+ * derivation, cliff-template drift comparison) cannot disagree on the composition rule.
10
+ */
4
11
  export declare function composeHeader(entry: {
5
12
  emoji: string;
6
13
  label: string;
7
14
  }): string;
15
+ /**
16
+ * Default work types ordered by canonical priority.
17
+ *
18
+ * Derived from `work-types.json` (the canonical SSOT, mirrored at runtime by
19
+ * `workTypesData.ts`). To change the taxonomy, edit `work-types.json` and update
20
+ * `workTypesData.ts` to match — a drift test enforces lockstep.
21
+ */
8
22
  export declare const DEFAULT_WORK_TYPES: Record<string, WorkTypeConfig>;
23
+ /**
24
+ * Per-canonical-type breaking-policy lookup derived from the canonical taxonomy.
25
+ *
26
+ * Pass this to `parseCommitMessage` (or an equivalent caller) so the parser knows which
27
+ * types tolerate `!` and which trigger a policy-violation warning. Missing entries default
28
+ * to `'optional'` to preserve back-compat for consumers that supply custom work-types.
29
+ */
9
30
  export declare const DEFAULT_BREAKING_POLICIES: Record<string, 'forbidden' | 'optional' | 'required'>;
31
+ /** Default version bump patterns. */
10
32
  export declare const DEFAULT_VERSION_PATTERNS: VersionPatterns;
33
+ /** Default configuration for structured changelog JSON generation. */
11
34
  export declare const DEFAULT_CHANGELOG_JSON_CONFIG: ChangelogJsonConfig;
35
+ /** Default configuration for release notes consumption. */
12
36
  export declare const DEFAULT_RELEASE_NOTES_CONFIG: ReleaseNotesConfig;
37
+ /** Default tag prefix for project-level releases. */
13
38
  export declare const DEFAULT_PROJECT_TAG_PREFIX = "v";
@@ -1,3 +1,12 @@
1
1
  import type { ReleaseKitConfig } from './types.js';
2
2
  export type { LabelSpec, ReleaseKitConfig, RepoLabelsConfig } from './types.js';
3
+ /**
4
+ * Type-safe identity function for configuration files.
5
+ *
6
+ * Usage in `.config/release-kit.config.ts`:
7
+ * ```ts
8
+ * import { defineConfig } from '@williamthorsen/release-kit/config';
9
+ * export default defineConfig({ ... });
10
+ * ```
11
+ */
3
12
  export declare function defineConfig(config: ReleaseKitConfig): ReleaseKitConfig;
@@ -1 +1,2 @@
1
+ /** Delete a file, silently ignoring the case where it does not exist. */
1
2
  export declare function deleteFileIfExists(filePath: string): void;
@@ -1,2 +1,11 @@
1
1
  import type { WorkspaceConfig } from './types.js';
2
+ /**
3
+ * Derives a workspace configuration from a workspace-relative path.
4
+ *
5
+ * Reads `package.json` at the workspace path to derive the tag identifier from the
6
+ * package's `name` field (with any leading `@scope/` stripped). The `dir` field remains
7
+ * the basename of the path — it is the stable internal identifier used for `--only`,
8
+ * config overrides, and dependency-graph lookups. The `tagPrefix` is `${unscopedName}-v`,
9
+ * so tags reflect the package identity rather than the directory layout.
10
+ */
2
11
  export declare function deriveWorkspaceConfig(workspacePath: string): WorkspaceConfig;
@@ -1,2 +1,8 @@
1
1
  export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'yarn-berry';
2
+ /**
3
+ * Detect the repo's package manager by checking the `packageManager` field in root `package.json`,
4
+ * then falling back to lockfile detection, then defaulting to `npm`.
5
+ *
6
+ * Yarn v2+ is returned as `'yarn-berry'` to distinguish it from Yarn Classic.
7
+ */
2
8
  export declare function detectPackageManager(): PackageManager;
@@ -1,7 +1,25 @@
1
+ /** A candidate tag prefix found in the repo that is not in the known-prefix set. */
1
2
  export interface UndeclaredTagPrefix {
3
+ /** The extracted prefix, including the trailing `-v` (e.g., `'core-v'`). */
2
4
  prefix: string;
5
+ /** Count of tags matching this prefix. */
3
6
  tagCount: number;
7
+ /** Up to `EXAMPLE_TAG_LIMIT` example tag names under this prefix. */
4
8
  exampleTags: string[];
9
+ /**
10
+ * Heuristic `dir` to suggest when offering a paste-ready config snippet; the prefix with
11
+ * the trailing `-v` stripped. The operator may need to remap this to the actual workspace
12
+ * directory basename.
13
+ */
5
14
  suggestedDir: string;
6
15
  }
16
+ /**
17
+ * Scan local git tags for release-shaped tags whose prefix is not in the known set.
18
+ *
19
+ * Reads only the local tag list; callers expecting to see recently-fetched remote tags
20
+ * should `git fetch --tags` first. Returns an empty array when the repo has no tags
21
+ * or no candidate-shaped tags outside the known set.
22
+ *
23
+ * @param knownPrefixes - Union of derived and declared prefixes across all workspaces.
24
+ */
7
25
  export declare function detectUndeclaredTagPrefixes(knownPrefixes: readonly string[]): UndeclaredTagPrefix[];
@@ -1,12 +1,17 @@
1
1
  import { type PolicyViolationHandler } from './parseCommitMessage.js';
2
2
  import type { Commit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.js';
3
+ /** Aggregate result of parsing commits and determining the bump type. */
3
4
  export interface BumpDetermination {
4
5
  releaseType: ReleaseType | undefined;
5
6
  parsedCommitCount: number;
6
7
  unparseableCommits: Commit[] | undefined;
7
8
  }
9
+ /** Optional configuration for parser-policy plumbing. */
8
10
  export interface DetermineBumpOptions {
11
+ /** Per-canonical-type breaking-policy lookup (drives `!` policy enforcement). */
9
12
  breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
13
+ /** Receives policy-violation notifications from `parseCommitMessage`. */
10
14
  onPolicyViolation?: PolicyViolationHandler;
11
15
  }
16
+ /** Parse commits, determine bump type, and apply patch floor when commits exist but none parsed. */
12
17
  export declare function determineBumpFromCommits(commits: Commit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns, scopeAliases: Record<string, string> | undefined, options?: DetermineBumpOptions): BumpDetermination;
@@ -1,2 +1,14 @@
1
1
  import type { ParsedCommit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.js';
2
+ /**
3
+ * Determines the overall bump type from a set of parsed commits.
4
+ *
5
+ * Uses `versionPatterns` to decide which commit types trigger major/minor bumps.
6
+ * The `'!'` sentinel in `versionPatterns.major` means "any breaking commit triggers major".
7
+ * Any recognized commit type not listed in major or minor patterns defaults to patch.
8
+ *
9
+ * @param commits - The parsed commits to analyze.
10
+ * @param workTypes - The work type configurations to verify recognized types.
11
+ * @param versionPatterns - Rules for determining major and minor bumps.
12
+ * @returns The determined release type, or undefined if no commits match a known work type.
13
+ */
2
14
  export declare function determineBumpType(commits: readonly ParsedCommit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns): ReleaseType | undefined;
@@ -1 +1,13 @@
1
+ /**
2
+ * Reads `pnpm-workspace.yaml` and resolves its `packages` globs to discover workspace directories.
3
+ *
4
+ * Returns an array of workspace-relative directory paths (e.g., `['packages/arrays', 'packages/strings']`).
5
+ * Only directories containing a `package.json` are included.
6
+ *
7
+ * Note: The returned paths are full relative paths from the repo root (e.g., `packages/arrays`,
8
+ * `libs/core`). These paths are passed directly to `deriveWorkspaceConfig()`, which uses them for all
9
+ * file path construction and derives the display name via `path.basename()`.
10
+ *
11
+ * @returns The discovered workspace paths, or `undefined` if no workspace config is found.
12
+ */
1
13
  export declare function discoverWorkspaces(): Promise<string[] | undefined>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Extracts the instruction from a commit body's `Migration:` paragraph.
3
+ *
4
+ * The first paragraph opening with the label wins: the lede doctrine specifies one labeled
5
+ * paragraph per body, so a second is ignored rather than appended. The match is anchored and
6
+ * case-sensitive, so `migration:` and `**Migration:**` yield nothing; the doctrine calls the
7
+ * label literal, and tolerating variants would let the form drift.
8
+ *
9
+ * Newlines inside the paragraph are preserved. The first character is capitalized, which is
10
+ * inert for the imperative the doctrine requires and repairs the lowercase continuation of a
11
+ * paragraph written before it. Returns `undefined` when no paragraph carries the label, or when
12
+ * the label is followed by nothing.
13
+ */
14
+ export declare function extractMigration(body: string | undefined): string | undefined;
@@ -0,0 +1,18 @@
1
+ const PARAGRAPH_SEPARATOR = /\n(?:[ \t]*\n)+/;
2
+ const MIGRATION_LABEL = /^Migration:[ \t]*/;
3
+ export function extractMigration(body) {
4
+ if (body === undefined) {
5
+ return undefined;
6
+ }
7
+ for (const paragraph of body.split(PARAGRAPH_SEPARATOR)) {
8
+ if (!MIGRATION_LABEL.test(paragraph)) {
9
+ continue;
10
+ }
11
+ const instruction = paragraph.replace(MIGRATION_LABEL, '').trim();
12
+ if (instruction.length === 0) {
13
+ return undefined;
14
+ }
15
+ return instruction.charAt(0).toUpperCase() + instruction.slice(1);
16
+ }
17
+ return undefined;
18
+ }
@@ -1,3 +1,6 @@
1
+ /** Wrap text in ANSI bold. */
1
2
  export declare function bold(text: string): string;
3
+ /** Wrap text in ANSI dim. */
2
4
  export declare function dim(text: string): string;
5
+ /** Render a section header for separating workspaces in CLI output. */
3
6
  export declare function sectionHeader(name: string): string;