@williamthorsen/release-kit 2.0.0 → 2.3.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 (75) hide show
  1. package/CHANGELOG.md +77 -58
  2. package/README.md +33 -42
  3. package/bin/release-kit.js +2 -0
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/bin/release-kit.js +158 -5
  6. package/dist/esm/bumpAllVersions.d.ts +2 -2
  7. package/dist/esm/bumpAllVersions.js +1 -4
  8. package/dist/esm/component.d.ts +1 -1
  9. package/dist/esm/component.js +2 -3
  10. package/dist/esm/createTags.d.ts +5 -0
  11. package/dist/esm/createTags.js +73 -0
  12. package/dist/esm/detectPackageManager.d.ts +2 -0
  13. package/dist/esm/detectPackageManager.js +44 -0
  14. package/dist/esm/findPackageRoot.d.ts +1 -0
  15. package/dist/esm/findPackageRoot.js +17 -0
  16. package/dist/esm/format.d.ts +3 -0
  17. package/dist/esm/format.js +14 -0
  18. package/dist/esm/generateChangelogs.d.ts +2 -2
  19. package/dist/esm/generateChangelogs.js +14 -13
  20. package/dist/esm/hasPrettierConfig.d.ts +1 -0
  21. package/dist/esm/hasPrettierConfig.js +42 -0
  22. package/dist/esm/index.d.ts +12 -2
  23. package/dist/esm/index.js +11 -2
  24. package/dist/esm/init/checks.d.ts +0 -2
  25. package/dist/esm/init/checks.js +0 -17
  26. package/dist/esm/init/initCommand.d.ts +3 -1
  27. package/dist/esm/init/initCommand.js +35 -40
  28. package/dist/esm/init/prompt.d.ts +0 -1
  29. package/dist/esm/init/prompt.js +3 -14
  30. package/dist/esm/init/scaffold.d.ts +4 -2
  31. package/dist/esm/init/scaffold.js +20 -48
  32. package/dist/esm/init/templates.js +14 -4
  33. package/dist/esm/loadConfig.js +0 -6
  34. package/dist/esm/prepareCommand.d.ts +10 -0
  35. package/dist/esm/prepareCommand.js +97 -21
  36. package/dist/esm/publish.d.ts +7 -0
  37. package/dist/esm/publish.js +49 -0
  38. package/dist/esm/publishCommand.d.ts +1 -0
  39. package/dist/esm/publishCommand.js +54 -0
  40. package/dist/esm/releasePrepare.d.ts +2 -2
  41. package/dist/esm/releasePrepare.js +46 -17
  42. package/dist/esm/releasePrepareMono.d.ts +2 -2
  43. package/dist/esm/releasePrepareMono.js +54 -24
  44. package/dist/esm/reportPrepare.d.ts +2 -0
  45. package/dist/esm/reportPrepare.js +120 -0
  46. package/dist/esm/resolveCliffConfigPath.d.ts +1 -0
  47. package/dist/esm/resolveCliffConfigPath.js +25 -0
  48. package/dist/esm/resolveReleaseTags.d.ts +6 -0
  49. package/dist/esm/resolveReleaseTags.js +42 -0
  50. package/dist/esm/runReleasePrepare.d.ts +1 -1
  51. package/dist/esm/runReleasePrepare.js +4 -3
  52. package/dist/esm/sync-labels/generateCommand.d.ts +4 -0
  53. package/dist/esm/sync-labels/generateCommand.js +51 -0
  54. package/dist/esm/sync-labels/initCommand.d.ts +6 -0
  55. package/dist/esm/sync-labels/initCommand.js +62 -0
  56. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +3 -0
  57. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +59 -0
  58. package/dist/esm/sync-labels/presets.d.ts +2 -0
  59. package/dist/esm/sync-labels/presets.js +40 -0
  60. package/dist/esm/sync-labels/resolveLabels.d.ts +2 -0
  61. package/dist/esm/sync-labels/resolveLabels.js +43 -0
  62. package/dist/esm/sync-labels/scaffold.d.ts +6 -0
  63. package/dist/esm/sync-labels/scaffold.js +25 -0
  64. package/dist/esm/sync-labels/syncCommand.d.ts +1 -0
  65. package/dist/esm/sync-labels/syncCommand.js +33 -0
  66. package/dist/esm/sync-labels/templates.d.ts +4 -0
  67. package/dist/esm/sync-labels/templates.js +50 -0
  68. package/dist/esm/sync-labels/types.d.ts +9 -0
  69. package/dist/esm/sync-labels/types.js +0 -0
  70. package/dist/esm/tagCommand.d.ts +1 -0
  71. package/dist/esm/tagCommand.js +20 -0
  72. package/dist/esm/types.d.ts +29 -1
  73. package/dist/esm/validateConfig.js +11 -6
  74. package/package.json +12 -5
  75. package/presets/labels/common.yaml +52 -0
package/CHANGELOG.md CHANGED
@@ -2,129 +2,148 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [release-kit-v2.0.0] - 2026-03-16
5
+ ## [release-kit-v2.3.0] - 2026-03-28
6
6
 
7
7
  ### Features
8
8
 
9
- - #20 release-kit|feat!: Move reusable release workflow into repo (#26)
9
+ - #8 feat: Add shared writeFileWithCheck utility and overwrite reporting (#66)
10
10
 
11
- Move the reusable release workflow from `williamthorsen/.github` into this repository as `.github/workflows/release-workflow.yaml`, versioned independently via `release-workflow-v{major}` tags. Update `release-kit init` templates to reference the new workflow location. The workflow no longer requires pnpm: it installs release-kit globally and runs it directly.
11
+ Extracts three duplicated `writeIfAbsent` implementations and two duplicated terminal helper sets into shared utilities in `@williamthorsen/node-monorepo-core`, then migrates all consumers (`release-kit init`, `preflight init`, `sync-labels`) to use them. All init commands now report which files were created, overwritten, skipped, or failed including when `--force` replaces existing files.
12
12
 
13
- - #22 release-kit|feat: Add --force flag to release-kit prepare (#25)
13
+ - #11 release-kit|feat: Separate tag-write errors from release preparation errors (#67)
14
14
 
15
- Add a `--force` flag that allows release preparation to proceed even when no release-worthy commits are found.
15
+ When tag-file writing fails, the error message now reads "Error writing release tags:" instead of the misleading "Error preparing release:", which only appeared because both operations shared a single try/catch.
16
16
 
17
- - #7 release-kit|feat!: Slim down release workflow by removing unnecessary pnpm install (#21)
17
+ Refactors `writeReleaseTags` to use the shared `writeFileWithCheck` utility from `@node-monorepo-tools/core` instead of raw `mkdirSync`/`writeFileSync`. The function now returns a structured `WriteResult` instead of throwing, and contains no `console` calls — all presentation moves to `runAndReport`.
18
18
 
19
- Remove pnpm setup steps from the reusable release workflow. Release-kit is self-contained and invokes git-cliff and prettier via `npx`, so no local dependency installation is needed.
19
+ ### Tests
20
20
 
21
- ### Refactoring
21
+ - #14 release-kit|tests: Add eligibility check failure and short-circuit tests (#63)
22
22
 
23
- - #6 release-kit|refactor: Clean up release-kit post-migration issues (#19)
23
+ Adds 4 unit tests to `initCommand.unit.test.ts` covering the remaining `checkEligibility` orchestration gaps: individual failure exit codes for `hasPackageJson` and `usesPnpm`, and short-circuit verification ensuring downstream checks are skipped when an earlier check fails.
24
24
 
25
- ## [release-kit-v1.0.1] - 2026-03-12
25
+ - #13 release-kit|tests: Add cliff.toml.template alignment test (#64)
26
26
 
27
- ### Bug fixes
27
+ Adds a unit test that enforces bidirectional alignment between `DEFAULT_WORK_TYPES` and the bundled `cliff.toml.template` commit parsers. The test parses the TOML template using `smol-toml`, then verifies that every canonical type name and alias is matched by a parser with the correct group heading, and that every parser group maps to a known work type header.
28
28
 
29
- - #34 release-kit|fix: Fix failure to find consumer's config (#35)
29
+ - #12 release-kit|tests: Add releasePrepare coverage for bumpOverride, tagPrefix, and dry-run tags (#65)
30
30
 
31
- Resolves the config file path to an absolute path using `process.cwd()` before passing it to both `existsSync` and `jiti.import()`. Previously, `jiti.import()` received a bare relative path which it resolved against `import.meta.url` (the package's install location), making it impossible for consumers to load their config files. Also fixes root tsconfig includes to cover `.config/` and removes a stale duplicate comment.
31
+ Adds three unit tests to `releasePrepare.unit.test.ts` covering previously untested code paths: the `bumpOverride` bypass of commit-based bump detection, custom `tagPrefix` propagation into tags, and tag computation in dry-run mode.
32
32
 
33
- ## [release-kit-v1.0.0] - 2026-03-12
33
+ ## [release-kit-v2.2.0] - 2026-03-27
34
34
 
35
35
  ### Features
36
36
 
37
- - #28 release-kit|feat!: Migrate to CLI-driven release preparation with auto-discovery (#31)
37
+ - #27 release-kit|feat: Auto-detect Prettier for CHANGELOG formatting (#36)
38
38
 
39
- Replaces release-kit's script-based release preparation with a self-contained CLI (`npx @williamthorsen/release-kit prepare`) that auto-discovers workspaces from `pnpm-workspace.yaml`. Adds workspace auto-discovery, TypeScript config loading via jiti, config validation, and a `component()` factory that accepts full workspace-relative paths. Refactors the type system: `WorkTypeConfig` becomes a record keyed by type name, version-bump rules move to a separate `VersionPatterns` structure, and `ComponentConfig` gains a `dir` field for canonical directory identity.
39
+ When `formatCommand` is not configured, release-kit now auto-detects whether the repo uses Prettier by checking for config files (`.prettierrc*`, `prettier.config.*`) or a `"prettier"` key in root `package.json`. If found, it defaults to `npx prettier --write` on generated files. If not found, formatting is skipped.
40
40
 
41
- ### Refactoring
41
+ - #28 release-kit|feat: Add tag-creation command (#40)
42
42
 
43
- - #28 release-kit|refactor: Adjust location of config & tags file
43
+ Adds a `release-kit tag` CLI command that reads computed tag names from the `tmp/.release-tags` file produced by `prepare` and creates annotated git tags. The command supports `--dry-run` (preview without creating tags) and `--no-git-checks` (skip dirty working tree validation). The `createTags` function and its options type are exported for programmatic use.
44
44
 
45
- ### Tooling
45
+ - #29 release-kit|feat: Add publish command (#42)
46
46
 
47
- - #28 release-kit|tooling: Remove legacy release-kit scripts
47
+ Adds a `release-kit publish` subcommand that derives packages to publish from git tags on HEAD and delegates to the repo's detected package manager. Also cleans up the `.release-tags` file after tag creation.
48
48
 
49
- ## [release-kit-v0.3.0] - 2026-03-11
49
+ - #41 release-kit|feat: Remove tagPrefix customization from component config (#49)
50
50
 
51
- ### Features
51
+ Removes the ability to customize `tagPrefix` per component, enforcing the deterministic `{dir}-v` convention universally. The internal `tagPrefix` property on `ComponentConfig` and `ReleaseConfig` is preserved — only the override/customization entry points are removed. Existing configs that still include `tagPrefix` now receive a clear deprecation error.
52
52
 
53
- - Release-kit|feat: Extract CLI runner into release-kit and co-locate scripts with workflow
53
+ - #54 release-kit|feat: Add styled terminal output to prepare command (#55)
54
54
 
55
- Move release script logic (arg parsing, validation, component filtering) into a reusable `runReleasePrepare` function in the release-kit package. Consuming repos now provide only their config and call `runReleasePrepare(config)`.
55
+ Adds ANSI formatting and emoji markers to the `release-kit prepare` command output. Progress chatter is dimmed, key results (version bumps, release tags, completion status) are highlighted with bold text and emoji, and monorepo components are separated by box-drawing section headers.
56
56
 
57
- Relocate release-prepare.ts and release.config.ts from scripts/ to .github/scripts/ so they live alongside the workflow they serve.
57
+ - #59 feat: Extract nmr CLI from core package (#61)
58
58
 
59
- - #20 release-kit|feat: Add release-kit init CLI command for automated repo setup (#22)
59
+ Extracts all nmr CLI code from `packages/core` into a new `packages/nmr` package (`@williamthorsen/nmr`). Core is reduced to an empty shared-library shell ready for cross-cutting utilities. All internal references are rewired and the full build/test pipeline passes.
60
60
 
61
- Add an interactive `npx release-kit init` CLI command that checks repo eligibility, detects monorepo vs single-package layout, scaffolds workflow/scripts/config files, and updates `package.json` with release scripts.
61
+ Scopes: core, nmr
62
62
 
63
- Also expand `runReleasePrepare` to polymorphically handle both `MonorepoReleaseConfig` and `ReleaseConfig`, and update the esbuild plugin to preserve shebangs during compilation.
63
+ ### Refactoring
64
64
 
65
- - #24 release-kit|feat: Return computed tags from release prepare and write .release-tags (#27)
65
+ - #43 refactor: Replace dist bin targets with thin wrapper scripts (#48)
66
66
 
67
- `releasePrepare` and `releasePrepareMono` now return `string[]` of computed tag names instead of `void`. `runReleasePrepare` writes these tags to a `.release-tags` file (one tag per line) so the CI workflow can read them instead of independently deriving tag names from `git diff`. In dry-run mode the file is not written. This makes `tagPrefix` the single source of truth for tag names, eliminating the mismatch between TypeScript-computed tags and workflow-derived tags.
67
+ The `bin` entries in `packages/core` and `packages/release-kit` pointed directly into `dist/esm/`, causing `pnpm install` to emit "Failed to create bin" warnings in fresh worktrees where `dist/` does not yet exist. Each bin entry now points to a committed wrapper script in `bin/` that dynamically imports the real entry point. The `files` field in both packages includes `bin` so the wrappers are published.
68
68
 
69
- ## [strings-v3.1.1] - 2026-03-10
69
+ - #53 release-kit|refactor: Separate presentation from logic in prepare workflow (#57)
70
70
 
71
- ### Tooling
71
+ Extracts all `console.info` calls from the prepare workflow's logic functions (`bumpAllVersions`, `generateChangelogs`, `releasePrepare`, `releasePrepareMono`) into a dedicated `reportPrepare` formatter. Logic functions now return structured result types (`BumpResult`, `ComponentPrepareResult`, `PrepareResult`). The legacy `runReleasePrepare` entry point is retired, with its utilities absorbed into `prepareCommand`.
72
72
 
73
- - \*|tooling: Change package registry from github to npmjs
73
+ ### Tests
74
74
 
75
- ## [tools-v3.0.1] - 2026-03-10
75
+ - #17 release-kit|tests: Cover multi-changelogPaths and error paths (#44)
76
76
 
77
- ### Tooling
77
+ Add three tests for previously untested code paths:
78
78
 
79
- - \*|tooling: Change package registry from github to npmjs
79
+ - `releasePrepareMono`: component with two `changelogPaths` entries, asserting `git-cliff` is invoked once per path with the correct `--output` target.
80
+ - `getCommitsSinceTarget`: `git describe` failure with a non-128 exit status propagates as a wrapped error instead of being swallowed.
81
+ - `getCommitsSinceTarget`: `git log` failure is wrapped and re-thrown with the commit range in the message.
80
82
 
81
- ## [release-kit-v0.2.1] - 2026-03-09
83
+ Also adds a `findAllCliffOutputPaths()` test helper that collects the `--output` arg from every `git-cliff` mock call.
82
84
 
83
85
  ### Tooling
84
86
 
85
- - Root|tooling: Make release-kit public
87
+ - #37 root|tooling: Adopt nmr to run monorepo and workspace scripts (#38)
86
88
 
87
- ## [release-kit-v0.2.0] - 2026-03-09
89
+ Replaces the legacy workspace script runner and ~25 root `package.json` scripts with `nmr`, the monorepo's own context-aware script runner. Root scripts are reduced to 4 (`prepare`, `postinstall`, `ci`, `bootstrap`), packages use direct build commands for bootstrap, and release-kit declares tier-3 test overrides for its integration test configs.
88
90
 
89
- ### Documentation
91
+ ## [release-workflow-v1] - 2026-03-19
90
92
 
91
- - Release-kit|docs: Rewrite README as adoption guide
93
+ ### Dependencies
92
94
 
93
- Replace minimal API docs with end-to-end adoption guide covering single-package and monorepo configurations, release scripts, GitHub Actions workflows that commit directly to `main`.
95
+ - Root|deps: Add release-kit as root devDependency
94
96
 
95
- ### Tooling
97
+ Make `npx release-kit` and `pnpm exec release-kit` resolve within
98
+ this repo by adding a `workspace:*` dependency that symlinks the bin.
99
+
100
+ ### Features
101
+
102
+ - #23 release-kit|feat: Add sync-labels command (#33)
96
103
 
97
- - #13 root|tooling: Migrate from changesets to release-kit (#16)
104
+ Add a `release-kit sync-labels` command group with three subcommands (`init`, `generate`, `sync`) for declarative GitHub label management in monorepos. Bundle a reusable GitHub Actions workflow and composable label presets with the release-kit package. Introduce a `findPackageRoot` utility to replace fragile hardcoded path resolutions across the codebase.
98
105
 
99
- Replaces the `@changesets/cli`-based release workflow with the in-house `release-kit` package, adding `git-cliff` for changelog generation, a monorepo release config for all 13 packages, and a CLI wrapper script. Removes all changeset infrastructure and creates per-package baseline version tags.
106
+ - #34 release-kit|feat: Report up-to-date status for unchanged init files (#35)
100
107
 
101
- - #10 root|tooling: Publish release-kit to GitHub Package Registry (#17)
108
+ `release-kit init` now compares existing file content against the default before reporting status. When an existing file is identical to the default (after normalizing trailing whitespace), it reports `✅ (up to date)` instead of the misleading `⚠️ (already exists)`.
102
109
 
103
- Adds release infrastructure for the toolbelt monorepo: a GitHub Actions `workflow_dispatch` workflow that automates the full release cycle (prepare, commit, tag, push) on `main`, convenience `release:prepare` scripts in the release-kit package, and a `RELEASING.md` documenting the workflow-based release process.
110
+ - Release-workflow|feat: Accept force input
104
111
 
105
- - #10 root|tooling: Streamline release-kit adoption
112
+ Pass `--force` to the prepare command so callers can force a version
113
+ bump even when there are no release-worthy changes.
106
114
 
107
- ## [tools-v3.0.0] - 2026-03-08
115
+ ## [release-kit-v2.1.0] - 2026-03-17
108
116
 
109
117
  ### Features
110
118
 
111
- - #7 release|feat: Create release-kit package (#9)
119
+ - #7 release-kit|feat!: Slim down release workflow by removing unnecessary pnpm install (#21)
112
120
 
113
- Creates the `@williamthorsen/release-kit` package in the `toolbelt` monorepo, extracting version-bumping and changelog-generation logic from `skypilot-site` and `devtools/afg` into a reusable library. The package provides functions for parsing conventional commits, determining semver bump types, updating `package.json` versions across workspaces, and generating changelogs via `git-cliff`.
121
+ Make release-kit self-contained by invoking git-cliff via `npx --yes` instead of requiring it on PATH, and by appending modified file paths to the format command so lightweight formatters like `npx prettier --write` work without a full `pnpm install`. Update init templates, README, and consuming repo config/workflow to reference workflow v3.
114
122
 
115
- Add contextual error messages to all I/O operations: file reads/writes in bumpAllVersions, execSync calls in generateChangelogs and releasePrepare, and git commands in getCommitsSinceTarget.
123
+ - #22 release-kit|feat: Add --force flag to release-kit prepare (#25)
116
124
 
117
- Differentiate expected "no tag" errors from real failures in git describe. Replace string-based commit separator with null-byte to prevent collisions with commit message content. Log git log failures before returning empty results.
125
+ Add a `--force` flag to `release-kit prepare` that bypasses the "no commits since last tag" check in monorepo mode, allowing version bumping and changelog generation to proceed even when no new commits are found since the last release tag. The flag requires `--bump` since there are no commits to infer bump type from. The local release workflow gains a `force` boolean input for future use.
118
126
 
119
- Add tests for uppercase/mixed-case type resolution, workspace+breaking combo parsing, breaking-on-first-commit early return, and empty workTypes. Strengthen alias tests to use toStrictEqual for full shape
120
- verification.
127
+ - #20 release-kit|feat!: Move reusable release workflow into repo (#26)
128
+
129
+ Moves the reusable release workflow from `williamthorsen/.github` into this repo as `release-workflow.yaml`, stripping all pnpm-related steps since release-kit now runs git-cliff and prettier via `npx` internally. Updates this repo's caller workflow to use a relative path and update init templates to reference the new location. Establishes a naming convention (`{name}-workflow.yaml` for reusable, `{name}.yaml` for callers) and independent versioning strategy (`{name}-workflow-v{major}` tags), documented in `.github/workflows/README.md`.
121
130
 
122
- Simplify determineBumpType by replacing the redundant isKeyOf guard on RELEASE_PRIORITY with a direct lookup, since bump is already typed as ReleaseType. Simplify parseCommitMessage by replacing mutable object construction with a conditional spread for the optional workspace field.
131
+ - #30 release-kit|feat: Allow git-cliff to be used without config (#31)
123
132
 
124
- Add workspaceAliases field to ReleaseConfig and integrate into parseCommitMessage for resolving workspace shorthand names to canonical names. Replace execSync with execFileSync using argument arrays in generateChangelogs and getCommitsSinceTarget to prevent shell injection from paths with special characters. Remove redundant length-check guard in bumpAllVersions, keeping the undefined guard that also narrows the type.
133
+ Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` `cliff.toml` bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.
125
134
 
126
135
  ### Refactoring
127
136
 
128
- - Release-kit|refactor: Inline isKeyOf and remove toolbelt.objects dependency
137
+ - #6 release-kit|refactor: Clean up release-kit post-migration issues (#19)
138
+
139
+ Addresses five code quality issues and a test coverage gap identified during the release-kit migration (#5). Extracts a duplicated `isRecord` type guard into a shared module, eliminates a double-read in `bumpAllVersions`, improves error handling in `usesPnpm` by replacing a silent catch with a structured error boundary, removes an unreachable `'feature'` pattern from version defaults, and adds an integration test for scaffold template path resolution.
140
+
141
+ ## [release-kit-v1.0.1] - 2026-03-14
142
+
143
+ ### Features
144
+
145
+ - #5 release-kit|feat: Migrate release-kit from toolbelt (#18)
146
+
147
+ Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.
129
148
 
130
149
  <!-- generated by git-cliff -->
package/README.md CHANGED
@@ -13,17 +13,14 @@ pnpm add -D @williamthorsen/release-kit
13
13
  ## Quick start
14
14
 
15
15
  ```bash
16
- # 1. Set up release-kit in your repo (scaffolds workflow + optional config)
16
+ # 1. Set up release-kit in your repo (scaffolds the release workflow)
17
17
  npx @williamthorsen/release-kit init
18
18
 
19
19
  # 2. Preview what a release would do
20
20
  npx @williamthorsen/release-kit prepare --dry-run
21
-
22
- # 3. Copy cliff.toml.template to your repo root (if init didn't create one)
23
- cp node_modules/@williamthorsen/release-kit/cliff.toml.template cliff.toml
24
21
  ```
25
22
 
26
- That's it for most repos. The CLI auto-discovers workspaces and applies sensible defaults. Customize only what you need via `.config/release-kit.config.ts`.
23
+ That's it for most repos. The CLI auto-discovers workspaces and applies sensible defaults. The bundled `cliff.toml.template` is used automatically — no need to copy it. Customize only what you need via `.config/release-kit.config.ts`.
27
24
 
28
25
  ## How it works
29
26
 
@@ -31,7 +28,7 @@ That's it for most repos. The CLI auto-discovers workspaces and applies sensible
31
28
  2. **Config loading**: loads `.config/release-kit.config.ts` (if present) via [jiti](https://github.com/unjs/jiti) and merges it with discovered defaults.
32
29
  3. **Commit analysis**: for each component, finds commits since the last version tag, parses them for type and scope, and determines the appropriate version bump.
33
30
  4. **Version bump + changelog**: bumps `package.json` versions and generates changelogs via `git-cliff`.
34
- 5. **Release tags file**: writes computed tags to `/tmp/release-kit/.release-tags` for CI consumption.
31
+ 5. **Release tags file**: writes computed tags to `tmp/.release-tags` for the release workflow to read when tagging and pushing.
35
32
 
36
33
  ## CLI reference
37
34
 
@@ -51,29 +48,25 @@ Options:
51
48
 
52
49
  Component names for `--only` match the package directory name (e.g., `arrays`, `release-kit`).
53
50
 
54
- **Self-hosting note**: in a repo where `release-kit` is a workspace package (e.g., this monorepo), `npx` and `pnpm exec` may not resolve the binary. Run the built entry point directly:
55
-
56
- ```bash
57
- node packages/release-kit/dist/esm/bin/release-kit.js prepare --dry-run
58
- ```
59
-
60
51
  ### `release-kit init`
61
52
 
62
- Initialize release-kit in the current repository. Scaffolds a GitHub Actions workflow and an optional config file.
53
+ Initialize release-kit in the current repository. By default, scaffolds only the GitHub Actions workflow file. Use `--with-config` to also scaffold configuration files.
63
54
 
64
55
  ```
65
56
  Usage: release-kit init [options]
66
57
 
67
58
  Options:
68
- --dry-run Preview changes without writing files
69
- --help, -h Show help
59
+ --with-config Also scaffold .config/release-kit.config.ts and .config/git-cliff.toml
60
+ --force Overwrite existing files instead of skipping them
61
+ --dry-run Preview changes without writing files
62
+ --help, -h Show help
70
63
  ```
71
64
 
72
65
  Scaffolded files:
73
66
 
74
- - `.config/release-kit.config.ts` — starter config with commented-out customization examples
75
67
  - `.github/workflows/release.yaml` — workflow that delegates to a reusable release workflow
76
- - `cliff.toml` — copied from the bundled template (prompted if missing)
68
+ - `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
69
+ - `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
77
70
 
78
71
  ## Configuration
79
72
 
@@ -105,14 +98,14 @@ The config file supports both `export default config` and `export const config =
105
98
 
106
99
  ### `ReleaseKitConfig` reference
107
100
 
108
- | Field | Type | Description |
109
- | ------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------- |
110
- | `cliffConfigPath` | `string` | Path to `cliff.toml` (defaults to `'cliff.toml'`) |
111
- | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
112
- | `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
113
- | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
114
- | `workspaceAliases` | `Record<string, string>` | Maps shorthand workspace names to canonical names in commits |
115
- | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
101
+ | Field | Type | Description |
102
+ | ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
103
+ | `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` `cliff.toml` → bundled template |
104
+ | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
105
+ | `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
106
+ | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
107
+ | `workspaceAliases` | `Record<string, string>` | Maps shorthand workspace names to canonical names in commits |
108
+ | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
116
109
 
117
110
  All fields are optional.
118
111
 
@@ -121,7 +114,6 @@ All fields are optional.
121
114
  ```typescript
122
115
  interface ComponentOverride {
123
116
  dir: string; // Package directory name (e.g., 'arrays')
124
- tagPrefix?: string; // Custom git tag prefix (defaults to '${dir}-v')
125
117
  shouldExclude?: boolean; // If true, exclude from release processing
126
118
  }
127
119
  ```
@@ -185,12 +177,9 @@ component('packages/arrays');
185
177
  // changelogPaths: ['packages/arrays'],
186
178
  // paths: ['packages/arrays/**'],
187
179
  // }
188
-
189
- // Custom tag prefix
190
- component('libs/core', 'core-v');
191
180
  ```
192
181
 
193
- The `dir` field is derived from `path.basename()`, so `packages/arrays` and `libs/arrays` both produce `dir: 'arrays'`.
182
+ The `dir` field is derived from `path.basename()`, so `packages/arrays` and `libs/arrays` both produce `dir: 'arrays'`. The `tagPrefix` is always `${dir}-v` — it cannot be customized.
194
183
 
195
184
  ## GitHub Actions workflow
196
185
 
@@ -259,7 +248,7 @@ jobs:
259
248
  if: steps.check.outputs.changed == 'true'
260
249
  id: tags
261
250
  run: |
262
- TAGS=$(cat /tmp/release-kit/.release-tags | tr '\n' ' ')
251
+ TAGS=$(cat tmp/.release-tags | tr '\n' ' ')
263
252
  echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
264
253
  echo "Releasing: $TAGS"
265
254
 
@@ -297,7 +286,7 @@ And the tag step with:
297
286
  if: steps.check.outputs.changed == 'true'
298
287
  id: tags
299
288
  run: |
300
- TAG=$(cat /tmp/release-kit/.release-tags)
289
+ TAG=$(cat tmp/.release-tags)
301
290
  echo "tag=$TAG" >> "$GITHUB_OUTPUT"
302
291
  ```
303
292
 
@@ -316,19 +305,20 @@ Or use the GitHub UI: Actions > Release > Run workflow.
316
305
 
317
306
  ## cliff.toml setup
318
307
 
319
- The package includes a `cliff.toml.template` with a generic git-cliff configuration that:
308
+ The package includes a bundled `cliff.toml.template` that is used automatically when no custom config is found. The resolution order is:
309
+
310
+ 1. Explicit `cliffConfigPath` in `.config/release-kit.config.ts`
311
+ 2. `.config/git-cliff.toml`
312
+ 3. `cliff.toml` (repo root)
313
+ 4. Bundled `cliff.toml.template` (automatic fallback)
314
+
315
+ The bundled template provides a generic git-cliff configuration that:
320
316
 
321
317
  - Strips issue-ticket prefixes matching `^[A-Z]+-\d+\s+` (e.g., `TOOL-123 `, `AFG-456 `)
322
318
  - Handles both `type: description` and `workspace|type: description` commit formats
323
319
  - Groups commits by work type into changelog sections
324
320
 
325
- Copy it to your repo root:
326
-
327
- ```bash
328
- cp node_modules/@williamthorsen/release-kit/cliff.toml.template cliff.toml
329
- ```
330
-
331
- Then customize as needed for your project.
321
+ To customize, scaffold a local copy with `release-kit init --with-config` and edit `.config/git-cliff.toml`.
332
322
 
333
323
  ## External dependencies
334
324
 
@@ -386,5 +376,6 @@ If your format command does not accept file arguments, update it to one that doe
386
376
  3. Delete the `.changeset/` directory.
387
377
  4. Run `npx @williamthorsen/release-kit init` to scaffold workflow and config files.
388
378
  5. Remove `changeset:*` scripts from `package.json` (no replacement needed — the CLI handles everything).
389
- 6. Copy `cliff.toml.template` to your repo root as `cliff.toml` (if `init` didn't create one).
390
- 7. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag arrays-v1.0.0`).
379
+ 6. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag arrays-v1.0.0`).
380
+
381
+ No cliff config copy is needed — the bundled template is used automatically. To customize, run `release-kit init --with-config`.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/esm/bin/release-kit.js');
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- f3265b81405ed17fde593f32d16e47fc2fc43f6a87805009ff7d808d5c61a2af
1
+ a59da8f9c8d7a95b776af266dfb8998307bdabf5e0cb68c9ca2a17b9655a5524
@@ -1,29 +1,87 @@
1
1
  #!/usr/bin/env node
2
2
  import { initCommand } from "../init/initCommand.js";
3
3
  import { prepareCommand } from "../prepareCommand.js";
4
+ import { publishCommand } from "../publishCommand.js";
5
+ import { generateCommand } from "../sync-labels/generateCommand.js";
6
+ import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
7
+ import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
8
+ import { tagCommand } from "../tagCommand.js";
4
9
  function showUsage() {
5
10
  console.info(`
6
11
  Usage: release-kit <command> [options]
7
12
 
8
13
  Commands:
9
14
  prepare Run release preparation (auto-discovers workspaces)
15
+ tag Create annotated git tags from the tags file
16
+ publish Publish packages with release tags on HEAD
10
17
  init Initialize release-kit in the current repository
18
+ sync-labels Manage GitHub label synchronization
11
19
 
12
20
  Options:
13
21
  --dry-run Preview changes without writing files
14
22
  --help, -h Show this help message
15
23
  `);
16
24
  }
25
+ function showSyncLabelsHelp() {
26
+ console.info(`
27
+ Usage: release-kit sync-labels <subcommand> [options]
28
+
29
+ Manage GitHub label synchronization via preset and custom label definitions.
30
+
31
+ Subcommands:
32
+ init Scaffold caller workflow and config, then generate labels
33
+ generate Regenerate .github/labels.yaml from config
34
+ sync Trigger the sync-labels workflow via gh CLI
35
+
36
+ Options:
37
+ --help, -h Show this help message
38
+ `);
39
+ }
40
+ function showSyncLabelsInitHelp() {
41
+ console.info(`
42
+ Usage: release-kit sync-labels init [options]
43
+
44
+ Scaffold the sync-labels caller workflow and config file, auto-discover workspaces
45
+ for scope labels, then generate .github/labels.yaml.
46
+
47
+ Options:
48
+ --dry-run Preview changes without writing files
49
+ --force Overwrite existing files instead of skipping them
50
+ --help, -h Show this help message
51
+ `);
52
+ }
53
+ function showSyncLabelsGenerateHelp() {
54
+ console.info(`
55
+ Usage: release-kit sync-labels generate
56
+
57
+ Regenerate .github/labels.yaml from .config/sync-labels.config.ts.
58
+
59
+ Options:
60
+ --help, -h Show this help message
61
+ `);
62
+ }
63
+ function showSyncLabelsSyncHelp() {
64
+ console.info(`
65
+ Usage: release-kit sync-labels sync
66
+
67
+ Trigger the sync-labels GitHub Actions workflow via the gh CLI.
68
+
69
+ Options:
70
+ --help, -h Show this help message
71
+ `);
72
+ }
17
73
  function showInitHelp() {
18
74
  console.info(`
19
75
  Usage: release-kit init [options]
20
76
 
21
77
  Initialize release-kit in the current repository.
22
- Scaffolds workflow and config files.
78
+ By default, scaffolds only the GitHub Actions workflow file.
23
79
 
24
80
  Options:
25
- --dry-run Preview changes without writing files
26
- --help, -h Show this help message
81
+ --with-config Also scaffold .config/release-kit.config.ts and .config/git-cliff.toml
82
+ --force Overwrite existing files instead of skipping them
83
+ --dry-run Preview changes without writing files
84
+ --help, -h Show this help message
27
85
  `);
28
86
  }
29
87
  function showPrepareHelp() {
@@ -39,6 +97,31 @@ Options:
39
97
  --help, -h Show this help message
40
98
  `);
41
99
  }
100
+ function showTagHelp() {
101
+ console.info(`
102
+ Usage: release-kit tag [options]
103
+
104
+ Create annotated git tags from the tags file produced by \`prepare\`.
105
+
106
+ Options:
107
+ --dry-run Preview without creating tags
108
+ --no-git-checks Skip dirty working tree check
109
+ --help, -h Show this help message
110
+ `);
111
+ }
112
+ function showPublishHelp() {
113
+ console.info(`
114
+ Usage: release-kit publish [options]
115
+
116
+ Publish packages that have release tags on HEAD.
117
+
118
+ Options:
119
+ --dry-run Preview without publishing
120
+ --no-git-checks Skip git checks (pnpm only)
121
+ --only=name1,name2 Only publish the named packages (comma-separated, monorepo only)
122
+ --help, -h Show this help message
123
+ `);
124
+ }
42
125
  const args = process.argv.slice(2);
43
126
  const command = args[0];
44
127
  const flags = args.slice(1);
@@ -54,20 +137,90 @@ if (command === "prepare") {
54
137
  await prepareCommand(flags);
55
138
  process.exit(0);
56
139
  }
140
+ if (command === "tag") {
141
+ if (flags.some((f) => f === "--help" || f === "-h")) {
142
+ showTagHelp();
143
+ process.exit(0);
144
+ }
145
+ tagCommand(flags);
146
+ process.exit(0);
147
+ }
148
+ if (command === "publish") {
149
+ if (flags.some((f) => f === "--help" || f === "-h")) {
150
+ showPublishHelp();
151
+ process.exit(0);
152
+ }
153
+ await publishCommand(flags);
154
+ process.exit(0);
155
+ }
57
156
  if (command === "init") {
58
157
  if (flags.some((f) => f === "--help" || f === "-h")) {
59
158
  showInitHelp();
60
159
  process.exit(0);
61
160
  }
62
- const unknownFlags = flags.filter((f) => f !== "--dry-run" && f !== "--help" && f !== "-h");
161
+ const knownInitFlags = /* @__PURE__ */ new Set(["--dry-run", "--force", "--with-config", "--help", "-h"]);
162
+ const unknownFlags = flags.filter((f) => !knownInitFlags.has(f));
63
163
  if (unknownFlags.length > 0) {
64
164
  console.error(`Error: Unknown option: ${unknownFlags[0]}`);
65
165
  process.exit(1);
66
166
  }
67
167
  const dryRun = flags.includes("--dry-run");
68
- const exitCode = await initCommand({ dryRun });
168
+ const force = flags.includes("--force");
169
+ const withConfig = flags.includes("--with-config");
170
+ const exitCode = initCommand({ dryRun, force, withConfig });
69
171
  process.exit(exitCode);
70
172
  }
173
+ if (command === "sync-labels") {
174
+ const subcommand = flags[0];
175
+ const subflags = flags.slice(1);
176
+ if (subcommand === "--help" || subcommand === "-h" || subcommand === void 0) {
177
+ showSyncLabelsHelp();
178
+ process.exit(0);
179
+ }
180
+ if (subcommand === "init") {
181
+ if (subflags.some((f) => f === "--help" || f === "-h")) {
182
+ showSyncLabelsInitHelp();
183
+ process.exit(0);
184
+ }
185
+ const knownFlags = /* @__PURE__ */ new Set(["--dry-run", "--force", "--help", "-h"]);
186
+ const unknownFlags = subflags.filter((f) => !knownFlags.has(f));
187
+ if (unknownFlags.length > 0) {
188
+ console.error(`Error: Unknown option: ${unknownFlags[0]}`);
189
+ process.exit(1);
190
+ }
191
+ const dryRun = subflags.includes("--dry-run");
192
+ const force = subflags.includes("--force");
193
+ const exitCode = await syncLabelsInitCommand({ dryRun, force });
194
+ process.exit(exitCode);
195
+ }
196
+ if (subcommand === "generate") {
197
+ if (subflags.some((f) => f === "--help" || f === "-h")) {
198
+ showSyncLabelsGenerateHelp();
199
+ process.exit(0);
200
+ }
201
+ if (subflags.length > 0) {
202
+ console.error(`Error: Unknown option: ${subflags[0]}`);
203
+ process.exit(1);
204
+ }
205
+ const exitCode = await generateCommand();
206
+ process.exit(exitCode);
207
+ }
208
+ if (subcommand === "sync") {
209
+ if (subflags.some((f) => f === "--help" || f === "-h")) {
210
+ showSyncLabelsSyncHelp();
211
+ process.exit(0);
212
+ }
213
+ if (subflags.length > 0) {
214
+ console.error(`Error: Unknown option: ${subflags[0]}`);
215
+ process.exit(1);
216
+ }
217
+ const exitCode = syncLabelsCommand();
218
+ process.exit(exitCode);
219
+ }
220
+ console.error(`Error: Unknown subcommand: ${subcommand}`);
221
+ showSyncLabelsHelp();
222
+ process.exit(1);
223
+ }
71
224
  console.error(`Error: Unknown command: ${command}`);
72
225
  showUsage();
73
226
  process.exit(1);
@@ -1,2 +1,2 @@
1
- import type { ReleaseType } from './types.ts';
2
- export declare function bumpAllVersions(packageFiles: readonly string[], releaseType: ReleaseType, dryRun: boolean): string;
1
+ import type { BumpResult, ReleaseType } from './types.ts';
2
+ export declare function bumpAllVersions(packageFiles: readonly string[], releaseType: ReleaseType, dryRun: boolean): BumpResult;