@varlock/bumpy 0.0.2 → 1.1.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 (43) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/config-schema.json +327 -0
  3. package/dist/add-BmNL5VwL.mjs +323 -0
  4. package/dist/{ai-CQhUyHAG.mjs → ai-sMYUf3lP.mjs} +21 -4
  5. package/dist/{apply-release-plan-D6TSrcwX.mjs → apply-release-plan-0kH62jhu.mjs} +35 -26
  6. package/dist/bump-file-DVqR3k67.mjs +157 -0
  7. package/dist/{changelog-github-Du62krXi.mjs → changelog-github-DkACMj0j.mjs} +23 -21
  8. package/dist/check-BjWF6SJm.mjs +65 -0
  9. package/dist/{ci-D6LQbR38.mjs → ci-DY58ugIi.mjs} +138 -91
  10. package/dist/{ci-setup-C6FlOfW5.mjs → ci-setup-BQwktQEe.mjs} +3 -3
  11. package/dist/cli.mjs +36 -41
  12. package/dist/commit-message-BwsowSds.mjs +23 -0
  13. package/dist/{config-BkwIEaQg.mjs → config-B-Qg3DZH.mjs} +30 -24
  14. package/dist/fs-DYR2XuFE.mjs +81 -0
  15. package/dist/generate-DX46X-rW.mjs +186 -0
  16. package/dist/{git-CGHVXXKw.mjs → git-YDedMddc.mjs} +54 -2
  17. package/dist/index.d.mts +68 -39
  18. package/dist/index.mjs +9 -9
  19. package/dist/init-DkTPs_WQ.mjs +196 -0
  20. package/dist/{names-Ck8cun7B.mjs → names-C-TuOPbd.mjs} +1 -1
  21. package/dist/{js-yaml-DpZfOoD4.mjs → package-manager-Clsmr-9r.mjs} +79 -1
  22. package/dist/picomatch-DMmqYjgq.mjs +1870 -0
  23. package/dist/{publish-D_7RqEYL.mjs → publish-CGB4TIKD.mjs} +26 -25
  24. package/dist/{publish-pipeline-ChnqW8nR.mjs → publish-pipeline-CXuqce1N.mjs} +24 -19
  25. package/dist/release-plan-JNir7bSM.mjs +264 -0
  26. package/dist/{semver-BTzYh8vc.mjs → semver-BJzWIuRz.mjs} +13 -3
  27. package/dist/{shell-Dj7JRD_q.mjs → shell-CY7OD48z.mjs} +20 -2
  28. package/dist/{status--Q8yAxQ4.mjs → status-EGYqULJg.mjs} +26 -22
  29. package/dist/{version-cAUkfYPx.mjs → version-BcfidiVX.mjs} +23 -22
  30. package/dist/{workspace-CxEKakDm.mjs → workspace-DWXlwcH4.mjs} +3 -3
  31. package/package.json +16 -1
  32. package/skills/add-change/SKILL.md +18 -14
  33. package/dist/add-BjyVIUlr.mjs +0 -175
  34. package/dist/changeset-UCZdSRDv.mjs +0 -108
  35. package/dist/check-jIwike9F.mjs +0 -51
  36. package/dist/fs-0AtnPUUe.mjs +0 -51
  37. package/dist/generate-Btrsn1qi.mjs +0 -177
  38. package/dist/init-B0q3wEQW.mjs +0 -22
  39. package/dist/migrate-CfQNwD0T.mjs +0 -121
  40. package/dist/package-manager-DcI5TdDE.mjs +0 -80
  41. package/dist/release-plan-BEzwApuK.mjs +0 -173
  42. /package/dist/{clack-CDRCHrC-.mjs → clack-C6bVkGxf.mjs} +0 -0
  43. /package/dist/{dep-graph-E-9-eQ2J.mjs → dep-graph-DiLeAhl9.mjs} +0 -0
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "bumpy",
3
3
  "version": "0.0.1",
4
- "description": "AI-assisted changeset creation for bumpy monorepo versioning",
4
+ "description": "AI-assisted bump file creation for bumpy monorepo versioning",
5
5
  "author": {
6
6
  "name": "DMNO",
7
7
  "url": "https://github.com/dmno-dev"
8
8
  },
9
9
  "repository": "https://github.com/dmno-dev/bumpy",
10
10
  "license": "MIT",
11
- "keywords": ["monorepo", "versioning", "changelog", "changesets"],
11
+ "keywords": ["monorepo", "versioning", "changelog", "bump-files"],
12
12
  "skills": "./skills/"
13
13
  }
@@ -0,0 +1,327 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://raw.githubusercontent.com/dmno-dev/bumpy/main/packages/bumpy/config-schema.json",
4
+ "title": "Bumpy Configuration",
5
+ "description": "Configuration for @varlock/bumpy — monorepo versioning and changelog tool",
6
+ "type": "object",
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "description": "Path or URL to the JSON schema"
11
+ },
12
+ "baseBranch": {
13
+ "type": "string",
14
+ "description": "Branch used for release comparisons",
15
+ "default": "main"
16
+ },
17
+ "access": {
18
+ "type": "string",
19
+ "enum": ["public", "restricted"],
20
+ "description": "Default npm publish access level",
21
+ "default": "public"
22
+ },
23
+ "versionCommitMessage": {
24
+ "type": "string",
25
+ "description": "Customize the commit message used when versioning. A plain string is used as-is. A path starting with \"./\" or \"../\" is loaded as a module that exports a function receiving the release plan and returning a message string."
26
+ },
27
+ "changelog": {
28
+ "description": "Changelog formatter — \"default\", \"github\", or path to a custom formatter. Can also be a tuple of [formatter, options]. Set to false to disable changelog generation.",
29
+ "default": "default",
30
+ "oneOf": [
31
+ { "type": "boolean", "const": false },
32
+ { "type": "string" },
33
+ {
34
+ "type": "array",
35
+ "items": [{ "type": "string" }, { "type": "object" }],
36
+ "minItems": 2,
37
+ "maxItems": 2
38
+ }
39
+ ]
40
+ },
41
+ "changedFilePatterns": {
42
+ "type": "array",
43
+ "description": "Glob patterns to filter which changed files count toward marking a package as changed. Can be overridden per-package.",
44
+ "items": { "type": "string" },
45
+ "default": ["**"]
46
+ },
47
+ "fixed": {
48
+ "type": "array",
49
+ "description": "Package groups that always bump together to the same version. Each element is an array of package name globs.",
50
+ "items": {
51
+ "type": "array",
52
+ "items": { "type": "string" }
53
+ },
54
+ "default": []
55
+ },
56
+ "linked": {
57
+ "type": "array",
58
+ "description": "Package groups that share the highest bump level. Each element is an array of package name globs.",
59
+ "items": {
60
+ "type": "array",
61
+ "items": { "type": "string" }
62
+ },
63
+ "default": []
64
+ },
65
+ "ignore": {
66
+ "type": "array",
67
+ "description": "Package name globs to exclude from versioning",
68
+ "items": { "type": "string" },
69
+ "default": []
70
+ },
71
+ "include": {
72
+ "type": "array",
73
+ "description": "Package name globs to explicitly include (overrides ignore and privatePackages)",
74
+ "items": { "type": "string" },
75
+ "default": []
76
+ },
77
+ "updateInternalDependencies": {
78
+ "type": "string",
79
+ "enum": ["patch", "minor", "out-of-range"],
80
+ "description": "When to update internal dependency version ranges",
81
+ "default": "out-of-range"
82
+ },
83
+ "dependencyBumpRules": {
84
+ "type": "object",
85
+ "description": "Controls how bumps propagate through dependency types",
86
+ "properties": {
87
+ "dependencies": { "$ref": "#/$defs/dependencyBumpRule" },
88
+ "devDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
89
+ "peerDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
90
+ "optionalDependencies": { "$ref": "#/$defs/dependencyBumpRule" }
91
+ },
92
+ "additionalProperties": false
93
+ },
94
+ "privatePackages": {
95
+ "type": "object",
96
+ "description": "Whether to version and/or create git tags for private packages",
97
+ "properties": {
98
+ "version": {
99
+ "type": "boolean",
100
+ "description": "Whether to version private packages",
101
+ "default": false
102
+ },
103
+ "tag": {
104
+ "type": "boolean",
105
+ "description": "Whether to create git tags for private packages",
106
+ "default": false
107
+ }
108
+ },
109
+ "additionalProperties": false
110
+ },
111
+ "allowCustomCommands": {
112
+ "description": "Allow per-package custom commands (buildCommand, publishCommand, checkPublished) defined in package.json \"bumpy\" fields. true = allow all, string[] = allow matching package name globs, false = only root-config commands allowed.",
113
+ "default": false,
114
+ "oneOf": [
115
+ { "type": "boolean" },
116
+ {
117
+ "type": "array",
118
+ "items": { "type": "string" }
119
+ }
120
+ ]
121
+ },
122
+ "packages": {
123
+ "type": "object",
124
+ "description": "Per-package config overrides, keyed by package name or glob pattern",
125
+ "additionalProperties": { "$ref": "#/$defs/packageConfig" }
126
+ },
127
+ "publish": {
128
+ "type": "object",
129
+ "description": "Publishing pipeline configuration",
130
+ "properties": {
131
+ "packManager": {
132
+ "type": "string",
133
+ "enum": ["auto", "npm", "pnpm", "bun", "yarn"],
134
+ "description": "Package manager to use for packing. \"auto\" detects from lockfile.",
135
+ "default": "auto"
136
+ },
137
+ "publishManager": {
138
+ "type": "string",
139
+ "enum": ["npm", "pnpm", "bun", "yarn"],
140
+ "description": "Command to use for publishing. npm supports OIDC/provenance.",
141
+ "default": "npm"
142
+ },
143
+ "publishArgs": {
144
+ "type": "array",
145
+ "items": { "type": "string" },
146
+ "description": "Extra args appended to the publish command (e.g., \"--provenance\")",
147
+ "default": []
148
+ },
149
+ "protocolResolution": {
150
+ "type": "string",
151
+ "enum": ["pack", "in-place", "none"],
152
+ "description": "How to handle workspace:/catalog: protocol resolution. \"pack\" = use PM's pack to build a clean tarball, \"in-place\" = rewrite package.json before publish, \"none\" = don't resolve.",
153
+ "default": "pack"
154
+ }
155
+ },
156
+ "additionalProperties": false
157
+ },
158
+ "aggregateRelease": {
159
+ "description": "GitHub release creation (requires gh CLI). false = individual release per package, true = single aggregated release, or an object with enabled and optional title (supports {{date}}).",
160
+ "default": false,
161
+ "oneOf": [
162
+ { "type": "boolean" },
163
+ {
164
+ "type": "object",
165
+ "properties": {
166
+ "enabled": {
167
+ "type": "boolean",
168
+ "description": "Whether to create an aggregated release"
169
+ },
170
+ "title": {
171
+ "type": "string",
172
+ "description": "Custom title for the aggregated release (supports {{date}})"
173
+ }
174
+ },
175
+ "required": ["enabled"],
176
+ "additionalProperties": false
177
+ }
178
+ ]
179
+ },
180
+ "gitUser": {
181
+ "type": "object",
182
+ "description": "Git identity used for CI commits",
183
+ "properties": {
184
+ "name": {
185
+ "type": "string",
186
+ "description": "Git user name",
187
+ "default": "bumpy-bot"
188
+ },
189
+ "email": {
190
+ "type": "string",
191
+ "description": "Git user email",
192
+ "default": "276066384+bumpy-bot@users.noreply.github.com"
193
+ }
194
+ },
195
+ "additionalProperties": false
196
+ },
197
+ "versionPr": {
198
+ "type": "object",
199
+ "description": "Customize the version PR created by bumpy ci release",
200
+ "properties": {
201
+ "title": {
202
+ "type": "string",
203
+ "description": "PR title",
204
+ "default": "🐸 Versioned release"
205
+ },
206
+ "branch": {
207
+ "type": "string",
208
+ "description": "Branch name for the version PR",
209
+ "default": "bumpy/version-packages"
210
+ },
211
+ "preamble": {
212
+ "type": "string",
213
+ "description": "HTML/markdown content prepended to the PR body"
214
+ }
215
+ },
216
+ "additionalProperties": false
217
+ }
218
+ },
219
+ "additionalProperties": false,
220
+ "$defs": {
221
+ "bumpType": {
222
+ "type": "string",
223
+ "enum": ["major", "minor", "patch"]
224
+ },
225
+ "dependencyBumpRule": {
226
+ "oneOf": [
227
+ {
228
+ "type": "object",
229
+ "properties": {
230
+ "trigger": {
231
+ "$ref": "#/$defs/bumpType",
232
+ "description": "Minimum bump level that triggers propagation"
233
+ },
234
+ "bumpAs": {
235
+ "description": "What level to bump the dependent (\"match\" mirrors the triggering level)",
236
+ "oneOf": [{ "$ref": "#/$defs/bumpType" }, { "type": "string", "const": "match" }]
237
+ }
238
+ },
239
+ "required": ["trigger", "bumpAs"],
240
+ "additionalProperties": false
241
+ },
242
+ {
243
+ "type": "boolean",
244
+ "const": false,
245
+ "description": "Set to false to disable propagation for this dependency type"
246
+ }
247
+ ]
248
+ },
249
+ "packageConfig": {
250
+ "type": "object",
251
+ "description": "Per-package configuration",
252
+ "properties": {
253
+ "managed": {
254
+ "type": "boolean",
255
+ "description": "Explicitly opt this package in or out of version management"
256
+ },
257
+ "access": {
258
+ "type": "string",
259
+ "enum": ["public", "restricted"],
260
+ "description": "Override the global access level"
261
+ },
262
+ "publishCommand": {
263
+ "description": "Custom command(s) to publish this package (replaces npm publish)",
264
+ "oneOf": [
265
+ { "type": "string" },
266
+ {
267
+ "type": "array",
268
+ "items": { "type": "string" }
269
+ }
270
+ ]
271
+ },
272
+ "buildCommand": {
273
+ "type": "string",
274
+ "description": "Command to run before publishing"
275
+ },
276
+ "registry": {
277
+ "type": "string",
278
+ "description": "Custom npm registry URL"
279
+ },
280
+ "skipNpmPublish": {
281
+ "type": "boolean",
282
+ "description": "Don't publish to npm (still creates git tags)"
283
+ },
284
+ "checkPublished": {
285
+ "type": "string",
286
+ "description": "Custom command that outputs the currently published version"
287
+ },
288
+ "changedFilePatterns": {
289
+ "type": "array",
290
+ "description": "Glob patterns to filter which changed files count toward marking this package as changed (overrides root setting)",
291
+ "items": { "type": "string" }
292
+ },
293
+ "dependencyBumpRules": {
294
+ "type": "object",
295
+ "description": "Per-package override for dependency propagation rules",
296
+ "properties": {
297
+ "dependencies": { "$ref": "#/$defs/dependencyBumpRule" },
298
+ "devDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
299
+ "peerDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
300
+ "optionalDependencies": { "$ref": "#/$defs/dependencyBumpRule" }
301
+ },
302
+ "additionalProperties": false
303
+ },
304
+ "cascadeTo": {
305
+ "type": "object",
306
+ "description": "Explicit cascade targets — glob pattern mapped to { trigger, bumpAs }",
307
+ "additionalProperties": {
308
+ "type": "object",
309
+ "properties": {
310
+ "trigger": {
311
+ "$ref": "#/$defs/bumpType",
312
+ "description": "Minimum bump level that triggers the cascade"
313
+ },
314
+ "bumpAs": {
315
+ "description": "What level to bump the target",
316
+ "oneOf": [{ "$ref": "#/$defs/bumpType" }, { "type": "string", "const": "match" }]
317
+ }
318
+ },
319
+ "required": ["trigger", "bumpAs"],
320
+ "additionalProperties": false
321
+ }
322
+ }
323
+ },
324
+ "additionalProperties": false
325
+ }
326
+ }
327
+ }
@@ -0,0 +1,323 @@
1
+ import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
+ import { n as exists, t as ensureDir } from "./fs-DYR2XuFE.mjs";
3
+ import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir, s as matchGlob } from "./config-B-Qg3DZH.mjs";
4
+ import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
5
+ import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
6
+ import { i as writeBumpFile } from "./bump-file-DVqR3k67.mjs";
7
+ import { r as getChangedFiles } from "./git-YDedMddc.mjs";
8
+ import { c as ot, d as yt, i as _t, l as pt, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-C6bVkGxf.mjs";
9
+ import { n as slugify, t as randomName } from "./names-C-TuOPbd.mjs";
10
+ import { t as require_picomatch } from "./picomatch-DMmqYjgq.mjs";
11
+ import { relative, resolve } from "node:path";
12
+ import * as readline from "node:readline";
13
+ //#region src/prompts/bump-select.ts
14
+ var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
15
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
16
+ const LEVELS = [
17
+ "none",
18
+ "patch",
19
+ "minor",
20
+ "major"
21
+ ];
22
+ /**
23
+ * Custom interactive prompt for selecting bump levels for multiple packages.
24
+ * - Up/Down arrows to navigate between packages
25
+ * - Left/Right arrows to change the bump level
26
+ * - Changed packages default to "patch", unchanged to "none"
27
+ * - Enter to confirm
28
+ * - Ctrl+C / Escape to cancel
29
+ */
30
+ async function bumpSelectPrompt(items) {
31
+ const changedEntries = items.map((item, idx) => ({
32
+ item,
33
+ idx
34
+ })).filter(({ item }) => item.changed);
35
+ const unchangedEntries = items.map((item, idx) => ({
36
+ item,
37
+ idx
38
+ })).filter(({ item }) => !item.changed);
39
+ const displayOrder = [...changedEntries, ...unchangedEntries];
40
+ let cursor = 0;
41
+ const levels = items.map((item) => item.changed ? "patch" : "none");
42
+ return new Promise((resolve) => {
43
+ const { stdin, stdout } = process;
44
+ const rl = readline.createInterface({
45
+ input: stdin,
46
+ terminal: true
47
+ });
48
+ stdout.write("\x1B[?25l");
49
+ let renderedLines = 0;
50
+ function render(final = false) {
51
+ if (renderedLines > 0) {
52
+ stdout.write(`\x1B[${renderedLines}A`);
53
+ stdout.write("\x1B[0J");
54
+ }
55
+ const lines = [];
56
+ if (final) {
57
+ lines.push(`${import_picocolors.default.green("◇")} Bump levels selected`);
58
+ const selected = displayOrder.filter(({ idx }) => levels[idx] !== "none");
59
+ if (selected.length === 0) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("(none)")}`);
60
+ else for (const { item, idx } of selected) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan(item.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(levels[idx])}`);
61
+ lines.push(import_picocolors.default.dim("│"));
62
+ } else {
63
+ lines.push(`${import_picocolors.default.cyan("◆")} Select bump levels`);
64
+ lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("↑/↓ navigate · ←/→ change level · enter to confirm")}`);
65
+ lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("0 clear current · x clear all · r reset all to defaults")}`);
66
+ lines.push(import_picocolors.default.dim("│"));
67
+ let displayIdx = 0;
68
+ if (changedEntries.length > 0) {
69
+ lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.underline("Changed")}`);
70
+ for (const { item, idx } of changedEntries) {
71
+ lines.push(formatRow(item, levels[idx], cursor === displayIdx));
72
+ displayIdx++;
73
+ }
74
+ if (unchangedEntries.length > 0) lines.push(import_picocolors.default.dim("│"));
75
+ }
76
+ if (unchangedEntries.length > 0) {
77
+ lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.underline("Unchanged")}`);
78
+ for (const { item, idx } of unchangedEntries) {
79
+ lines.push(formatRow(item, levels[idx], cursor === displayIdx));
80
+ displayIdx++;
81
+ }
82
+ }
83
+ lines.push(import_picocolors.default.dim("│"));
84
+ const selectedCount = levels.filter((l) => l !== "none").length;
85
+ lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`${selectedCount} package${selectedCount !== 1 ? "s" : ""} selected`)}`);
86
+ lines.push(`${import_picocolors.default.dim("└")}`);
87
+ }
88
+ const output = lines.join("\n") + "\n";
89
+ stdout.write(output);
90
+ renderedLines = lines.length;
91
+ }
92
+ function cleanup() {
93
+ rl.close();
94
+ stdout.write("\x1B[?25h");
95
+ if (stdin.isTTY) stdin.setRawMode(false);
96
+ }
97
+ function finish(result) {
98
+ render(true);
99
+ cleanup();
100
+ resolve(result);
101
+ }
102
+ if (stdin.isTTY) stdin.setRawMode(true);
103
+ stdin.resume();
104
+ render();
105
+ stdin.on("keypress", (_str, key) => {
106
+ if (!key) return;
107
+ if (key.name === "escape" || key.ctrl && key.name === "c") {
108
+ cleanup();
109
+ if (renderedLines > 0) {
110
+ stdout.write(`\x1B[${renderedLines}A`);
111
+ stdout.write("\x1B[0J");
112
+ }
113
+ stdout.write(`${import_picocolors.default.red("■")} Cancelled\n`);
114
+ resolve(Symbol("cancel"));
115
+ return;
116
+ }
117
+ if (key.name === "return") {
118
+ const results = [];
119
+ for (let i = 0; i < items.length; i++) if (levels[i] !== "none") results.push({
120
+ name: items[i].name,
121
+ type: levels[i]
122
+ });
123
+ finish(results);
124
+ return;
125
+ }
126
+ if (key.name === "up" || key.name === "k") cursor = (cursor - 1 + displayOrder.length) % displayOrder.length;
127
+ else if (key.name === "down" || key.name === "j") cursor = (cursor + 1) % displayOrder.length;
128
+ else if (key.name === "right" || key.name === "l") {
129
+ const entry = displayOrder[cursor];
130
+ const currentLevel = LEVELS.indexOf(levels[entry.idx]);
131
+ if (currentLevel < LEVELS.length - 1) levels[entry.idx] = LEVELS[currentLevel + 1];
132
+ } else if (key.name === "left" || key.name === "h") {
133
+ const entry = displayOrder[cursor];
134
+ const currentLevel = LEVELS.indexOf(levels[entry.idx]);
135
+ if (currentLevel > 0) levels[entry.idx] = LEVELS[currentLevel - 1];
136
+ } else if (_str === "0" || key.name === "backspace") {
137
+ const entry = displayOrder[cursor];
138
+ levels[entry.idx] = "none";
139
+ } else if (_str === "r") for (let i = 0; i < items.length; i++) levels[i] = items[i].changed ? "patch" : "none";
140
+ else if (_str === "x") for (let i = 0; i < items.length; i++) levels[i] = "none";
141
+ render();
142
+ });
143
+ readline.emitKeypressEvents(stdin, rl);
144
+ });
145
+ }
146
+ function formatRow(item, level, focused) {
147
+ return `${import_picocolors.default.dim("│")} ${focused ? import_picocolors.default.cyan("›") : " "} ${focused ? import_picocolors.default.cyan(item.name) : item.name} ${import_picocolors.default.dim(`(${item.version})`)} ${formatLevel(level, focused)}`;
148
+ }
149
+ function formatLevel(level, focused) {
150
+ if (!focused) {
151
+ if (level === "none") return import_picocolors.default.dim("·");
152
+ if (level === "major") return import_picocolors.default.red(level);
153
+ if (level === "minor") return import_picocolors.default.yellow(level);
154
+ return import_picocolors.default.green(level);
155
+ }
156
+ return `◄ ${LEVELS.map((l) => {
157
+ if (l === level) {
158
+ if (l === "none") return import_picocolors.default.bold(import_picocolors.default.dim("[none]"));
159
+ if (l === "major") return import_picocolors.default.bold(import_picocolors.default.red(`[${l}]`));
160
+ if (l === "minor") return import_picocolors.default.bold(import_picocolors.default.yellow(`[${l}]`));
161
+ return import_picocolors.default.bold(import_picocolors.default.green(`[${l}]`));
162
+ }
163
+ return import_picocolors.default.dim(l);
164
+ }).join(import_picocolors.default.dim(" · "))} ►`;
165
+ }
166
+ //#endregion
167
+ //#region src/commands/add.ts
168
+ const CASCADE_CHOICES = [
169
+ {
170
+ label: "patch",
171
+ value: "patch"
172
+ },
173
+ {
174
+ label: "minor",
175
+ value: "minor"
176
+ },
177
+ {
178
+ label: "major",
179
+ value: "major"
180
+ }
181
+ ];
182
+ async function addCommand(rootDir, opts) {
183
+ const config = await loadConfig(rootDir);
184
+ const bumpyDir = getBumpyDir(rootDir);
185
+ await ensureDir(bumpyDir);
186
+ if (opts.empty) {
187
+ const filename = opts.name ? slugify(opts.name) : randomName();
188
+ const filePath = resolve(bumpyDir, `${filename}.md`);
189
+ const { writeText } = await import("./fs-DYR2XuFE.mjs").then((n) => n.r);
190
+ await writeText(filePath, "---\n---\n");
191
+ log.success(`🐸 Created empty bump file: .bumpy/${filename}.md`);
192
+ return;
193
+ }
194
+ let releases;
195
+ let summary;
196
+ let filename;
197
+ if (opts.packages) {
198
+ releases = parsePackagesFlag(opts.packages);
199
+ summary = opts.message || "";
200
+ filename = opts.name ? slugify(opts.name) : randomName();
201
+ } else {
202
+ mt(import_picocolors.default.bgCyan(import_picocolors.default.black(" bumpy add ")));
203
+ const pkgs = await discoverPackages(rootDir, config);
204
+ const depGraph = new DependencyGraph(pkgs);
205
+ if (pkgs.size === 0) {
206
+ pt("No managed packages found in this workspace.");
207
+ process.exit(1);
208
+ }
209
+ const baseBranch = config.baseBranch;
210
+ const changedFiles = getChangedFiles(rootDir, baseBranch);
211
+ const matchers = /* @__PURE__ */ new Map();
212
+ for (const [name, pkg] of pkgs) {
213
+ const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
214
+ matchers.set(name, (0, import_picomatch.default)(patterns));
215
+ }
216
+ const changedPackageNames = /* @__PURE__ */ new Set();
217
+ for (const file of changedFiles) for (const [name, pkg] of pkgs) {
218
+ const pkgRelDir = relative(rootDir, pkg.dir);
219
+ if (file.startsWith(pkgRelDir + "/")) {
220
+ const relToPackage = file.slice(pkgRelDir.length + 1);
221
+ if (matchers.get(name)(relToPackage)) changedPackageNames.add(name);
222
+ }
223
+ }
224
+ const bumpSelectResult = await bumpSelectPrompt([...pkgs.values()].map((pkg) => ({
225
+ name: pkg.name,
226
+ version: pkg.version,
227
+ changed: changedPackageNames.has(pkg.name)
228
+ })));
229
+ if (typeof bumpSelectResult === "symbol") {
230
+ pt("Aborted");
231
+ process.exit(0);
232
+ }
233
+ const bumpSelections = bumpSelectResult;
234
+ if (bumpSelections.length === 0) {
235
+ pt("No packages selected.");
236
+ process.exit(0);
237
+ }
238
+ releases = [];
239
+ for (const { name, type: bumpType } of bumpSelections) {
240
+ const release = {
241
+ name,
242
+ type: bumpType
243
+ };
244
+ {
245
+ const dependents = depGraph.getDependents(name);
246
+ const cascadeTargets = pkgs.get(name).bumpy?.cascadeTo;
247
+ if (dependents.length > 0 || cascadeTargets) {
248
+ if (unwrap(await ot({
249
+ message: `${import_picocolors.default.cyan(name)} has ${import_picocolors.default.bold(String(dependents.length))} dependents. Specify explicit cascades?`,
250
+ initialValue: false
251
+ }))) {
252
+ const allTargets = /* @__PURE__ */ new Set();
253
+ for (const d of dependents) allTargets.add(d.name);
254
+ if (cascadeTargets) {
255
+ for (const pattern of Object.keys(cascadeTargets)) for (const [pName] of pkgs) if (matchGlob(pName, pattern)) allTargets.add(pName);
256
+ }
257
+ const cascadeSelected = unwrap(await yt({
258
+ message: "Which packages should cascade?",
259
+ options: [...allTargets].map((n) => ({
260
+ label: n,
261
+ value: n
262
+ })),
263
+ required: false
264
+ }));
265
+ if (cascadeSelected.length > 0) {
266
+ const cascadeBump = unwrap(await _t({
267
+ message: "Cascade bump type",
268
+ options: CASCADE_CHOICES
269
+ }));
270
+ const cascade = {};
271
+ for (const target of cascadeSelected) cascade[target] = cascadeBump;
272
+ release.cascade = cascade;
273
+ }
274
+ }
275
+ }
276
+ }
277
+ releases.push(release);
278
+ }
279
+ summary = unwrap(await Ot({
280
+ message: "Summary (what changed and why)",
281
+ placeholder: "A short description of the change",
282
+ validate: (value) => {
283
+ if (!value || !value.trim()) return "Summary is required";
284
+ }
285
+ }));
286
+ const defaultName = randomName();
287
+ filename = slugify(unwrap(await Ot({
288
+ message: "Bump file name",
289
+ placeholder: defaultName,
290
+ defaultValue: defaultName,
291
+ validate: (value) => {
292
+ if (!value) return void 0;
293
+ if (!slugify(value)) return "Name must contain at least one alphanumeric character";
294
+ }
295
+ }))) || defaultName;
296
+ }
297
+ if (await exists(resolve(bumpyDir, `${filename}.md`))) filename = `${filename}-${Date.now()}`;
298
+ await writeBumpFile(rootDir, filename, releases, summary);
299
+ if (opts.packages) {
300
+ log.success(`🐸 Created bump file: .bumpy/${filename}.md`);
301
+ for (const r of releases) log.dim(` ${r.name}: ${r.type}${formatCascade(r)}`);
302
+ } else {
303
+ wt(releases.map((r) => `${import_picocolors.default.cyan(r.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(r.type)}${formatCascade(r)}`).join("\n"), "Bump file");
304
+ gt(import_picocolors.default.green(`🐸 Created .bumpy/${filename}.md`));
305
+ }
306
+ }
307
+ function formatCascade(r) {
308
+ if (!("cascade" in r) || Object.keys(r.cascade).length === 0) return "";
309
+ const parts = Object.entries(r.cascade).map(([k, v]) => `${k}:${v}`);
310
+ return import_picocolors.default.dim(` (cascade: ${parts.join(", ")})`);
311
+ }
312
+ function parsePackagesFlag(input) {
313
+ return input.split(",").map((entry) => {
314
+ const [name, type] = entry.trim().split(":");
315
+ if (!name || !type) throw new Error(`Invalid package format: "${entry}". Expected "name:bumpType"`);
316
+ return {
317
+ name: name.trim(),
318
+ type: type.trim()
319
+ };
320
+ });
321
+ }
322
+ //#endregion
323
+ export { addCommand };