@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
package/dist/index.d.mts CHANGED
@@ -1,20 +1,16 @@
1
1
  //#region src/types.d.ts
2
2
  type BumpType = 'major' | 'minor' | 'patch';
3
- type BumpTypeWithIsolated = BumpType | 'minor-isolated' | 'patch-isolated';
3
+ type BumpTypeWithNone = BumpType | 'none';
4
4
  declare const BUMP_LEVELS: Record<BumpType, number>;
5
5
  declare function bumpLevel(type: BumpType): number;
6
- declare function parseIsolatedBump(type: BumpTypeWithIsolated): {
7
- bump: BumpType;
8
- isolated: boolean;
9
- };
10
6
  declare function maxBump(a: BumpType | undefined, b: BumpType): BumpType;
11
7
  interface DependencyBumpRule {
12
8
  /** What bump level in the dependency triggers propagation */
13
- trigger: BumpType | 'none';
9
+ trigger: BumpType;
14
10
  /** What bump to apply to the dependent */
15
11
  bumpAs: BumpType | 'match';
16
12
  }
17
- declare const DEFAULT_BUMP_RULES: Record<string, DependencyBumpRule>;
13
+ declare const DEFAULT_BUMP_RULES: Record<string, DependencyBumpRule | false>;
18
14
  type DepType = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies';
19
15
  declare const DEP_TYPES: DepType[];
20
16
  interface PublishConfig {
@@ -36,20 +32,39 @@ interface PublishConfig {
36
32
  interface BumpyConfig {
37
33
  baseBranch: string;
38
34
  access: 'public' | 'restricted';
39
- commit: boolean;
40
- changelog: string | [string, Record<string, unknown>];
35
+ /**
36
+ * Customize the commit message used when versioning.
37
+ * A string starting with "./" is treated as a path to a module that exports
38
+ * a function receiving the release plan and returning a message string.
39
+ * Any other string is used as a static commit message.
40
+ * Omit to use the default: "Version packages\n\npkg@version..."
41
+ */
42
+ versionCommitMessage?: string;
43
+ changelog: false | string | [string, Record<string, unknown>];
41
44
  fixed: string[][];
42
45
  linked: string[][];
46
+ /** Glob patterns to filter which changed files count toward marking a package as changed */
47
+ changedFilePatterns: string[];
43
48
  /** Package names/globs to exclude from version management */
44
49
  ignore: string[];
45
50
  /** Package names/globs to explicitly include (overrides private + ignore) */
46
51
  include: string[];
47
- updateInternalDependencies: 'patch' | 'minor' | 'out-of-range' | 'none';
48
- dependencyBumpRules: Partial<Record<DepType, DependencyBumpRule>>;
52
+ updateInternalDependencies: 'patch' | 'minor' | 'out-of-range';
53
+ dependencyBumpRules: Partial<Record<DepType, DependencyBumpRule | false>>;
49
54
  privatePackages: {
50
55
  version: boolean;
51
56
  tag: boolean;
52
57
  };
58
+ /**
59
+ * Allow per-package custom commands (buildCommand, publishCommand, checkPublished)
60
+ * defined in package.json "bumpy" fields.
61
+ * Commands defined in the root config's `packages` map are always trusted.
62
+ *
63
+ * true = allow all packages to define custom commands
64
+ * string[] = allow only matching package names/globs
65
+ * false = only root-config commands are allowed (default)
66
+ */
67
+ allowCustomCommands: boolean | string[];
53
68
  packages: Record<string, PackageConfig>;
54
69
  publish: PublishConfig;
55
70
  /**
@@ -84,26 +99,27 @@ interface PackageConfig {
84
99
  skipNpmPublish?: boolean;
85
100
  /** Command to check if a version is already published. Should output the published version string. */
86
101
  checkPublished?: string;
87
- dependencyBumpRules?: Partial<Record<DepType, DependencyBumpRule>>;
88
- specificDependencyRules?: Record<string, DependencyBumpRule>;
102
+ /** Glob patterns to filter which changed files count toward marking this package as changed */
103
+ changedFilePatterns?: string[];
104
+ dependencyBumpRules?: Partial<Record<DepType, DependencyBumpRule | false>>;
89
105
  cascadeTo?: Record<string, DependencyBumpRule>;
90
106
  }
91
107
  declare const DEFAULT_PUBLISH_CONFIG: PublishConfig;
92
108
  declare const DEFAULT_CONFIG: BumpyConfig;
93
- interface ChangesetReleaseSimple {
109
+ interface BumpFileReleaseSimple {
94
110
  name: string;
95
- type: BumpTypeWithIsolated;
111
+ type: BumpTypeWithNone;
96
112
  }
97
- interface ChangesetReleaseCascade {
113
+ interface BumpFileReleaseCascade {
98
114
  name: string;
99
- type: BumpTypeWithIsolated;
115
+ type: BumpTypeWithNone;
100
116
  cascade: Record<string, BumpType>;
101
117
  }
102
- type ChangesetRelease = ChangesetReleaseSimple | ChangesetReleaseCascade;
103
- declare function hasCascade(r: ChangesetRelease): r is ChangesetReleaseCascade;
104
- interface Changeset {
118
+ type BumpFileRelease = BumpFileReleaseSimple | BumpFileReleaseCascade;
119
+ declare function hasCascade(r: BumpFileRelease): r is BumpFileReleaseCascade;
120
+ interface BumpFile {
105
121
  id: string;
106
- releases: ChangesetRelease[];
122
+ releases: BumpFileRelease[];
107
123
  summary: string;
108
124
  }
109
125
  interface WorkspacePackage {
@@ -131,13 +147,14 @@ interface PlannedRelease {
131
147
  type: BumpType;
132
148
  oldVersion: string;
133
149
  newVersion: string;
134
- changesets: string[];
150
+ bumpFiles: string[];
135
151
  isDependencyBump: boolean;
136
152
  isCascadeBump: boolean;
137
153
  }
138
154
  interface ReleasePlan {
139
- changesets: Changeset[];
155
+ bumpFiles: BumpFile[];
140
156
  releases: PlannedRelease[];
157
+ warnings: string[];
141
158
  }
142
159
  //#endregion
143
160
  //#region src/core/config.d.ts
@@ -175,30 +192,35 @@ declare class DependencyGraph {
175
192
  topologicalSort(packages: Map<string, WorkspacePackage>): string[];
176
193
  }
177
194
  //#endregion
178
- //#region src/core/changeset.d.ts
179
- /** Read all changeset files from .bumpy/ directory, sorted by git creation order */
180
- declare function readChangesets(rootDir: string): Promise<Changeset[]>;
181
- /** Parse changeset content (for testing) */
182
- declare function parseChangeset(content: string, id: string): Changeset | null;
183
- /** Write a changeset file */
184
- declare function writeChangeset(rootDir: string, filename: string, releases: ChangesetRelease[], summary: string): Promise<string>;
195
+ //#region src/core/bump-file.d.ts
196
+ /** Read all bump files from .bumpy/ directory, sorted by git creation order */
197
+ declare function readBumpFiles(rootDir: string): Promise<BumpFile[]>;
198
+ /** Parse bump file content (for testing) */
199
+ declare function parseBumpFile(content: string, id: string): BumpFile | null;
200
+ /** Write a bump file */
201
+ declare function writeBumpFile(rootDir: string, filename: string, releases: BumpFileRelease[], summary: string): Promise<string>;
185
202
  //#endregion
186
203
  //#region src/core/release-plan.d.ts
187
204
  /**
188
- * Build a release plan from pending changesets, the dependency graph, and config.
205
+ * Build a release plan from pending bump files, the dependency graph, and config.
189
206
  * This is the core algorithm of bumpy.
207
+ *
208
+ * The propagation loop runs three phases until stable:
209
+ * Phase A — fix out-of-range dependencies (always runs)
210
+ * Phase B — enforce fixed/linked group constraints
211
+ * Phase C — apply cascades and proactive propagation rules
190
212
  */
191
- declare function assembleReleasePlan(changesets: Changeset[], packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig): ReleasePlan;
213
+ declare function assembleReleasePlan(bumpFiles: BumpFile[], packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig): ReleasePlan;
192
214
  //#endregion
193
215
  //#region src/core/apply-release-plan.d.ts
194
- /** Apply the release plan: bump versions, update changelogs, delete changesets */
216
+ /** Apply the release plan: bump versions, update changelogs, delete bump files */
195
217
  declare function applyReleasePlan(releasePlan: ReleasePlan, packages: Map<string, WorkspacePackage>, rootDir: string, config: BumpyConfig): Promise<void>;
196
218
  //#endregion
197
219
  //#region src/core/changelog.d.ts
198
220
  interface ChangelogContext {
199
221
  release: PlannedRelease;
200
- /** Changesets that contributed to this release */
201
- changesets: Changeset[];
222
+ /** Bump files that contributed to this release */
223
+ bumpFiles: BumpFile[];
202
224
  /** ISO date string (YYYY-MM-DD) */
203
225
  date: string;
204
226
  }
@@ -215,7 +237,7 @@ declare const defaultFormatter: ChangelogFormatter;
215
237
  */
216
238
  declare function loadFormatter(changelog: BumpyConfig['changelog'], rootDir: string): Promise<ChangelogFormatter>;
217
239
  /** Generate a changelog entry using the configured formatter */
218
- declare function generateChangelogEntry(release: PlannedRelease, changesets: Changeset[], formatter?: ChangelogFormatter, date?: string): Promise<string>;
240
+ declare function generateChangelogEntry(release: PlannedRelease, bumpFiles: BumpFile[], formatter?: ChangelogFormatter, date?: string): Promise<string>;
219
241
  /** Prepend a new entry to an existing CHANGELOG.md content */
220
242
  declare function prependToChangelog(existingContent: string, newEntry: string): string;
221
243
  //#endregion
@@ -223,14 +245,21 @@ declare function prependToChangelog(existingContent: string, newEntry: string):
223
245
  interface GithubChangelogOptions {
224
246
  /** "owner/repo" — auto-detected from gh CLI if not provided */
225
247
  repo?: string;
248
+ /** Whether to include "Thanks @user" messages for contributors (default: true) */
249
+ thankContributors?: boolean;
226
250
  /** GitHub usernames (without @) to skip "Thanks" messages for (e.g. internal team members) */
227
251
  internalAuthors?: string[];
228
252
  }
229
253
  //#endregion
230
254
  //#region src/core/semver.d.ts
231
255
  declare function bumpVersion(version: string, type: BumpType): string;
232
- /** Check if a version satisfies a range */
233
- declare function satisfies(version: string, range: string): boolean;
256
+ /**
257
+ * Check if a version satisfies a range.
258
+ * @param version - The version to check
259
+ * @param range - The version range (may include workspace: or catalog: protocol)
260
+ * @param currentVersion - The dependency's current version, used to resolve workspace:^ and workspace:~
261
+ */
262
+ declare function satisfies(version: string, range: string, currentVersion?: string): boolean;
234
263
  /** Strip workspace: protocol from version ranges */
235
264
  declare function stripProtocol(range: string): string;
236
265
  //#endregion
@@ -259,4 +288,4 @@ interface PublishResult {
259
288
  */
260
289
  declare function publishPackages(releasePlan: ReleasePlan, packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig, rootDir: string, opts?: PublishOptions, catalogs?: CatalogMap, detectedPm?: PackageManager): Promise<PublishResult>;
261
290
  //#endregion
262
- export { BUMP_LEVELS, BumpType, BumpTypeWithIsolated, BumpyConfig, type ChangelogContext, type ChangelogFormatter, Changeset, ChangesetRelease, ChangesetReleaseCascade, ChangesetReleaseSimple, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseChangeset, parseIsolatedBump, prependToChangelog, publishPackages, readChangesets, satisfies, stripProtocol, writeChangeset };
291
+ export { BUMP_LEVELS, BumpFile, BumpFileRelease, BumpFileReleaseCascade, BumpFileReleaseSimple, BumpType, BumpTypeWithNone, BumpyConfig, type ChangelogContext, type ChangelogFormatter, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { a as loadConfig, c as BUMP_LEVELS, d as DEFAULT_PUBLISH_CONFIG, f as DEP_TYPES, g as parseIsolatedBump, h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, n as findRoot, p as bumpLevel, r as getBumpyDir, s as matchGlob, u as DEFAULT_CONFIG } from "./config-BkwIEaQg.mjs";
2
- import { t as discoverPackages } from "./workspace-CxEKakDm.mjs";
3
- import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
4
- import { i as writeChangeset, n as parseChangeset, r as readChangesets } from "./changeset-UCZdSRDv.mjs";
5
- import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-BTzYh8vc.mjs";
6
- import { t as assembleReleasePlan } from "./release-plan-BEzwApuK.mjs";
7
- import { a as prependToChangelog, i as loadFormatter, n as defaultFormatter, r as generateChangelogEntry, t as applyReleasePlan } from "./apply-release-plan-D6TSrcwX.mjs";
8
- import { t as publishPackages } from "./publish-pipeline-ChnqW8nR.mjs";
9
- export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseChangeset, parseIsolatedBump, prependToChangelog, publishPackages, readChangesets, satisfies, stripProtocol, writeChangeset };
1
+ import { a as loadConfig, c as BUMP_LEVELS, d as DEFAULT_PUBLISH_CONFIG, f as DEP_TYPES, h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, n as findRoot, p as bumpLevel, r as getBumpyDir, s as matchGlob, u as DEFAULT_CONFIG } from "./config-B-Qg3DZH.mjs";
2
+ import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
3
+ import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
4
+ import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-DVqR3k67.mjs";
5
+ import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-BJzWIuRz.mjs";
6
+ import { t as assembleReleasePlan } from "./release-plan-JNir7bSM.mjs";
7
+ import { a as prependToChangelog, i as loadFormatter, n as defaultFormatter, r as generateChangelogEntry, t as applyReleasePlan } from "./apply-release-plan-0kH62jhu.mjs";
8
+ import { t as publishPackages } from "./publish-pipeline-CXuqce1N.mjs";
9
+ export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
@@ -0,0 +1,196 @@
1
+ import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
+ import { a as readJson, d as writeText, i as listFiles, n as exists, o as readText, t as ensureDir, u as writeJson } from "./fs-DYR2XuFE.mjs";
3
+ import { t as detectPackageManager } from "./package-manager-Clsmr-9r.mjs";
4
+ import { t as run } from "./shell-CY7OD48z.mjs";
5
+ import { c as ot, o as gt, s as mt, t as unwrap } from "./clack-C6bVkGxf.mjs";
6
+ import { resolve } from "node:path";
7
+ import { readdir, rename, rm } from "node:fs/promises";
8
+ //#region ../../.bumpy/README.md
9
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
10
+ var README_default = "# 🐸 Bumpy\n\nThis directory is used by [bumpy](https://bumpy.varlock.dev) to manage versioning and changelogs.\n\nBumpy is a modern versioning tool for JavaScript/TypeScript projects (monorepos and single packages). It uses **bump files** — small markdown files in this directory — to declare pending version changes. These files are consumed during the release process to compute version bumps, update changelogs, and publish packages.\n\n## How it works\n\n1. When you make a change that should trigger a release, create a bump file (typically one per PR)\n2. Bump files accumulate on your main branch until you're ready to release\n3. At release time, bumpy merges all pending bumps into a release plan, updates versions and changelogs, and publishes packages\n\n## Creating bump files\n\n### Interactive\n\n```bash\nbunx bumpy add\n```\n\n### Non-interactive (useful for AI-assisted development)\n\n```bash\nbunx bumpy add --packages \"package-name:minor,other-package:patch\" --message \"Description of changes\" --name \"my-change\"\n```\n\n### By hand\n\nCreate a `.md` file in this directory with YAML frontmatter mapping package names to bump levels (`major`, `minor`, or `patch`), and a markdown body for the changelog entry:\n\n```markdown\n---\n'package-name': minor\n---\n\nAdded a new feature.\n```\n\n### From conventional commits\n\n```bash\nbunx bumpy generate\n```\n\n### Empty bump files\n\nFor PRs that intentionally don't need a release (docs, CI, etc.):\n\n```bash\nbunx bumpy add --empty --name \"docs-update\"\n```\n\n## Keeping bump files up to date\n\nAs a PR evolves, make sure its bump file stays in sync. If the scope of changes grows (e.g., a patch becomes a new feature), update the bump level and description to match. Reviewers and AI assistants should treat the bump file as part of the PR — just like tests and docs.\n\n## Files in this directory\n\n- `_config.json` — bumpy configuration\n- `README.md` — this file\n- `*.md` (other than README.md) — pending bump files\n\n📖 Full documentation: https://bumpy.varlock.dev\n";
11
+ //#endregion
12
+ //#region src/commands/init.ts
13
+ const PM_RUNNER = {
14
+ bun: "bunx bumpy",
15
+ pnpm: "pnpm bumpy",
16
+ yarn: "yarn bumpy",
17
+ npm: "npx bumpy"
18
+ };
19
+ const PM_ADD_DEV = {
20
+ bun: "bun add -d",
21
+ pnpm: "pnpm add -Dw",
22
+ yarn: "yarn add -D -W",
23
+ npm: "npm install -D"
24
+ };
25
+ const PM_REMOVE = {
26
+ bun: "bun remove",
27
+ pnpm: "pnpm remove -w",
28
+ yarn: "yarn remove -W",
29
+ npm: "npm uninstall"
30
+ };
31
+ async function initCommand(rootDir, opts = {}) {
32
+ const bumpyDir = resolve(rootDir, ".bumpy");
33
+ const changesetDir = resolve(rootDir, ".changeset");
34
+ const hasChangeset = await exists(changesetDir);
35
+ if (await exists(resolve(bumpyDir, "_config.json"))) {
36
+ log.info("🐸 Detected .bumpy/ directory - looks like we're ready to go!");
37
+ return;
38
+ }
39
+ const pm = await detectPackageManager(rootDir);
40
+ if (!opts.force) mt(import_picocolors.default.bgCyan(import_picocolors.default.black(" bumpy init ")));
41
+ if (hasChangeset) {
42
+ log.step("🦋 Detected .changeset/ directory — migrating to .bumpy/ 🐸");
43
+ await rename(changesetDir, bumpyDir);
44
+ log.dim(" Renamed .changeset/ → .bumpy/");
45
+ const oldConfigPath = resolve(bumpyDir, "config.json");
46
+ if (await exists(oldConfigPath)) {
47
+ const bumpyConfig = migrateChangesetConfig(await readJson(oldConfigPath));
48
+ await writeJson(resolve(bumpyDir, "_config.json"), bumpyConfig);
49
+ await rm(oldConfigPath);
50
+ log.dim(" Migrated config.json → _config.json");
51
+ const migratedFields = Object.keys(bumpyConfig).filter((k) => k !== "$schema");
52
+ if (migratedFields.length > 0) log.dim(" Migrated fields: " + migratedFields.join(", "));
53
+ } else await writeJson(resolve(bumpyDir, "_config.json"), makeDefaultConfig());
54
+ const readmeContent = README_default.replaceAll("bunx bumpy", PM_RUNNER[pm] || "npx bumpy");
55
+ await writeText(resolve(bumpyDir, "README.md"), readmeContent);
56
+ log.dim(" Replaced README.md");
57
+ const pendingFiles = (await readdir(bumpyDir)).filter((f) => f.endsWith(".md") && f !== "README.md");
58
+ if (pendingFiles.length > 0) log.dim(` Kept ${pendingFiles.length} pending bump file(s)`);
59
+ if (await isPackageInstalled(rootDir, "@changesets/cli")) {
60
+ if (opts.force) await uninstallPackage(pm, "@changesets/cli", rootDir);
61
+ else if (unwrap(await ot({
62
+ message: "Uninstall @changesets/cli?",
63
+ initialValue: true
64
+ }))) await uninstallPackage(pm, "@changesets/cli", rootDir);
65
+ }
66
+ await warnChangesetWorkflows(rootDir, pm);
67
+ } else {
68
+ await ensureDir(bumpyDir);
69
+ await writeJson(resolve(bumpyDir, "_config.json"), makeDefaultConfig());
70
+ const readmeContent = README_default.replaceAll("bunx bumpy", PM_RUNNER[pm] || "npx bumpy");
71
+ await writeText(resolve(bumpyDir, "README.md"), readmeContent);
72
+ log.success("Initialized .bumpy/ directory");
73
+ log.dim(" Created .bumpy/_config.json");
74
+ log.dim(" Created .bumpy/README.md");
75
+ }
76
+ if (!await isPackageInstalled(rootDir, "@varlock/bumpy")) {
77
+ if (opts.force) await installPackage(pm, "@varlock/bumpy", rootDir);
78
+ else if (unwrap(await ot({
79
+ message: "Install @varlock/bumpy as a dev dependency?",
80
+ initialValue: true
81
+ }))) await installPackage(pm, "@varlock/bumpy", rootDir);
82
+ }
83
+ if (!opts.force) gt(import_picocolors.default.green("bumpy is ready!"));
84
+ else if (hasChangeset) log.success("Migration complete!");
85
+ }
86
+ function makeDefaultConfig() {
87
+ return {
88
+ $schema: "../node_modules/@varlock/bumpy/config-schema.json",
89
+ baseBranch: "main",
90
+ changelog: "default"
91
+ };
92
+ }
93
+ function migrateChangesetConfig(csConfig) {
94
+ const bumpyConfig = makeDefaultConfig();
95
+ for (const field of [
96
+ "baseBranch",
97
+ "access",
98
+ "fixed",
99
+ "linked",
100
+ "ignore",
101
+ "updateInternalDependencies",
102
+ "privatePackages"
103
+ ]) if (csConfig[field] !== void 0) bumpyConfig[field] = csConfig[field];
104
+ return bumpyConfig;
105
+ }
106
+ async function isPackageInstalled(rootDir, pkgName) {
107
+ try {
108
+ const pkg = await readJson(resolve(rootDir, "package.json"));
109
+ return !!(pkg.devDependencies?.[pkgName] || pkg.dependencies?.[pkgName]);
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+ async function installPackage(pm, pkgName, rootDir) {
115
+ const cmd = `${PM_ADD_DEV[pm] || "npm install -D"} ${pkgName}`;
116
+ log.step(`Installing ${pkgName}...`);
117
+ try {
118
+ run(cmd, { cwd: rootDir });
119
+ log.dim(` ${cmd}`);
120
+ } catch (err) {
121
+ log.warn(`Failed to install ${pkgName}: ${err instanceof Error ? err.message : err}`);
122
+ log.dim(` Run manually: ${cmd}`);
123
+ }
124
+ }
125
+ async function uninstallPackage(pm, pkgName, rootDir) {
126
+ const cmd = `${PM_REMOVE[pm] || "npm uninstall"} ${pkgName}`;
127
+ log.step(`Uninstalling ${pkgName}...`);
128
+ try {
129
+ run(cmd, { cwd: rootDir });
130
+ log.dim(` ${cmd}`);
131
+ } catch (err) {
132
+ log.warn(`Failed to uninstall ${pkgName}: ${err instanceof Error ? err.message : err}`);
133
+ log.dim(` Run manually: ${cmd}`);
134
+ }
135
+ }
136
+ /** Patterns to detect in workflow files, with suggested replacements */
137
+ const CHANGESET_PATTERNS = [
138
+ {
139
+ pattern: /changesets\/action/,
140
+ replacement: "see https://bumpy.varlock.dev/ci for bumpy CI setup"
141
+ },
142
+ {
143
+ pattern: /changeset publish/,
144
+ replacement: "bumpy publish"
145
+ },
146
+ {
147
+ pattern: /changeset version/,
148
+ replacement: "bumpy version"
149
+ },
150
+ {
151
+ pattern: /changeset status/,
152
+ replacement: "bumpy status"
153
+ },
154
+ {
155
+ pattern: /@changesets\//,
156
+ replacement: "replace with @varlock/bumpy"
157
+ }
158
+ ];
159
+ async function warnChangesetWorkflows(rootDir, pm) {
160
+ const workflowDir = resolve(rootDir, ".github", "workflows");
161
+ if (!await exists(workflowDir)) return;
162
+ const yamlFiles = (await listFiles(workflowDir)).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
163
+ if (yamlFiles.length === 0) return;
164
+ const runner = PM_RUNNER[pm] || "npx bumpy";
165
+ const hits = [];
166
+ for (const file of yamlFiles) {
167
+ const lines = (await readText(resolve(workflowDir, file))).split("\n");
168
+ const fileMatches = [];
169
+ for (let i = 0; i < lines.length; i++) {
170
+ const line = lines[i];
171
+ for (const { pattern, replacement } of CHANGESET_PATTERNS) {
172
+ const match = line.match(pattern);
173
+ if (match) fileMatches.push({
174
+ line: i + 1,
175
+ found: match[0],
176
+ suggestion: replacement
177
+ });
178
+ }
179
+ }
180
+ if (fileMatches.length > 0) hits.push({
181
+ file,
182
+ matches: fileMatches
183
+ });
184
+ }
185
+ if (hits.length === 0) return;
186
+ console.log();
187
+ log.warn("Found changeset references in GitHub workflows:");
188
+ for (const { file, matches } of hits) {
189
+ log.dim(` .github/workflows/${file}`);
190
+ for (const { line, found, suggestion } of matches) log.dim(` L${line}: ${import_picocolors.default.red(found)} → ${import_picocolors.default.green(suggestion)}`);
191
+ }
192
+ console.log();
193
+ log.dim(` Run ${import_picocolors.default.cyan(`${runner} ci setup`)} for help configuring CI workflows.`);
194
+ }
195
+ //#endregion
196
+ export { initCommand };
@@ -1,5 +1,5 @@
1
1
  //#region src/utils/names.ts
2
- /** Generate a random adjective-noun name for changeset files */
2
+ /** Generate a random adjective-noun name for bump files */
3
3
  function randomName() {
4
4
  const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
5
5
  return `${pick(ADJECTIVES)}-${pick(ADJECTIVES)}-${pick(NOUNS)}`;
@@ -1,3 +1,5 @@
1
+ import { a as readJson, n as exists, o as readText } from "./fs-DYR2XuFE.mjs";
2
+ import { resolve } from "node:path";
1
3
  //#region ../../node_modules/.bun/js-yaml@4.1.1/node_modules/js-yaml/dist/js-yaml.mjs
2
4
  /*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */
3
5
  function isNothing(subject) {
@@ -2028,4 +2030,80 @@ var jsYaml = {
2028
2030
  safeDump: renamed("safeDump", "dump")
2029
2031
  };
2030
2032
  //#endregion
2031
- export { jsYaml as t };
2033
+ //#region src/utils/package-manager.ts
2034
+ /** Detect the package manager, extract workspace globs, and load catalogs */
2035
+ async function detectWorkspaces(rootDir) {
2036
+ const pm = await detectPackageManager(rootDir);
2037
+ return {
2038
+ packageManager: pm,
2039
+ globs: await getWorkspaceGlobs(rootDir, pm),
2040
+ catalogs: await loadCatalogs(rootDir, pm)
2041
+ };
2042
+ }
2043
+ async function detectPackageManager(rootDir) {
2044
+ if (await exists(resolve(rootDir, "bun.lock")) || await exists(resolve(rootDir, "bun.lockb"))) return "bun";
2045
+ if (await exists(resolve(rootDir, "pnpm-lock.yaml"))) return "pnpm";
2046
+ if (await exists(resolve(rootDir, "yarn.lock"))) return "yarn";
2047
+ try {
2048
+ const pkg = await readJson(resolve(rootDir, "package.json"));
2049
+ if (typeof pkg.packageManager === "string") {
2050
+ const name = pkg.packageManager.split("@")[0];
2051
+ if (name === "pnpm" || name === "yarn" || name === "bun") return name;
2052
+ }
2053
+ } catch {}
2054
+ return "npm";
2055
+ }
2056
+ async function getWorkspaceGlobs(rootDir, pm) {
2057
+ if (pm === "pnpm") {
2058
+ const wsFile = resolve(rootDir, "pnpm-workspace.yaml");
2059
+ if (await exists(wsFile)) {
2060
+ const content = await readText(wsFile);
2061
+ const parsed = jsYaml.load(content);
2062
+ if (parsed?.packages) return parsed.packages;
2063
+ }
2064
+ }
2065
+ try {
2066
+ const workspaces = (await readJson(resolve(rootDir, "package.json"))).workspaces;
2067
+ if (Array.isArray(workspaces)) return workspaces;
2068
+ if (workspaces && typeof workspaces === "object" && "packages" in workspaces) {
2069
+ const pkgs = workspaces.packages;
2070
+ if (Array.isArray(pkgs)) return pkgs;
2071
+ }
2072
+ } catch {}
2073
+ return [];
2074
+ }
2075
+ /** Load catalog definitions from pnpm-workspace.yaml or root package.json */
2076
+ async function loadCatalogs(rootDir, pm) {
2077
+ const catalogs = /* @__PURE__ */ new Map();
2078
+ if (pm === "pnpm") {
2079
+ const wsFile = resolve(rootDir, "pnpm-workspace.yaml");
2080
+ if (await exists(wsFile)) {
2081
+ const content = await readText(wsFile);
2082
+ const parsed = jsYaml.load(content);
2083
+ if (parsed?.catalog) catalogs.set("", parsed.catalog);
2084
+ if (parsed?.catalogs) for (const [name, deps] of Object.entries(parsed.catalogs)) catalogs.set(name, deps);
2085
+ }
2086
+ }
2087
+ try {
2088
+ const pkg = await readJson(resolve(rootDir, "package.json"));
2089
+ if (pkg.catalog && typeof pkg.catalog === "object") catalogs.set("", pkg.catalog);
2090
+ if (pkg.catalogs && typeof pkg.catalogs === "object") for (const [name, deps] of Object.entries(pkg.catalogs)) catalogs.set(name, deps);
2091
+ const workspaces = pkg.workspaces;
2092
+ if (workspaces && typeof workspaces === "object" && !Array.isArray(workspaces)) {
2093
+ const ws = workspaces;
2094
+ if (ws.catalog && typeof ws.catalog === "object") catalogs.set("", ws.catalog);
2095
+ if (ws.catalogs && typeof ws.catalogs === "object") for (const [name, deps] of Object.entries(ws.catalogs)) catalogs.set(name, deps);
2096
+ }
2097
+ } catch {}
2098
+ return catalogs;
2099
+ }
2100
+ /** Resolve a specific dependency's catalog: reference */
2101
+ function resolveCatalogDep(depName, range, catalogs) {
2102
+ if (!range.startsWith("catalog:")) return null;
2103
+ const catalogName = range.slice(8).trim() || "";
2104
+ const catalog = catalogs.get(catalogName);
2105
+ if (!catalog) return null;
2106
+ return catalog[depName] ?? null;
2107
+ }
2108
+ //#endregion
2109
+ export { jsYaml as i, detectWorkspaces as n, resolveCatalogDep as r, detectPackageManager as t };