verelease 0.1.2-alpha.2 → 0.1.2-alpha.3

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.
package/dist/bin.js CHANGED
@@ -1,12 +1,201 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/bin.ts
4
+ import { defineCommand, runMain } from "citty";
5
+ import { readFile as readFile9 } from "fs/promises";
6
+ import { fileURLToPath } from "url";
7
+ import { join as join15, dirname } from "path";
8
+
3
9
  // src/cli.ts
4
- import { resolve as resolve2, join as join13 } from "path";
5
- import { writeFile as writeFile5, mkdir as mkdir6, readFile as readFile7, unlink, rm as rm4 } from "fs/promises";
10
+ import { resolve as resolve2, join as join14 } from "path";
11
+ import { writeFile as writeFile6, mkdir as mkdir6, readFile as readFile8, rm as rm4 } from "fs/promises";
6
12
  import { mkdtemp as mkdtemp5 } from "fs/promises";
7
13
  import { tmpdir as tmpdir5 } from "os";
8
14
  import { existsSync } from "fs";
9
- import { createInterface } from "readline/promises";
15
+ import * as prompts2 from "@clack/prompts";
16
+ import colors2 from "picocolors";
17
+ import * as Effect3 from "effect/Effect";
18
+ import * as Exit2 from "effect/Exit";
19
+
20
+ // ../core/dist/errors.js
21
+ import * as Data from "effect/Data";
22
+ var NpmAuthError = class extends Data.TaggedError("NpmAuthError") {
23
+ };
24
+ var BaselineFetchError = class extends Data.TaggedError("BaselineFetchError") {
25
+ };
26
+ var TestExecutionError = class extends Data.TaggedError("TestExecutionError") {
27
+ };
28
+ var ManifestMutationError = class extends Data.TaggedError("ManifestMutationError") {
29
+ };
30
+ var LockfileSyncError = class extends Data.TaggedError("LockfileSyncError") {
31
+ };
32
+ var PublishError = class extends Data.TaggedError("PublishError") {
33
+ };
34
+ var RegistryCollisionError = class extends Data.TaggedError("RegistryCollisionError") {
35
+ };
36
+
37
+ // ../core/dist/transaction.js
38
+ import * as Effect from "effect/Effect";
39
+ import * as Exit from "effect/Exit";
40
+ import { readFile, writeFile, unlink, stat } from "fs/promises";
41
+ import { join } from "path";
42
+ async function fileExists(path) {
43
+ try {
44
+ const s = await stat(path);
45
+ return s.isFile();
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ function executeReleaseTransaction(options, use) {
51
+ const { plan, cwd, lockfileAdapter, onRollback, onStep } = options;
52
+ const acquire = Effect.tryPromise({
53
+ try: async () => {
54
+ onStep?.("Creating manifest backups...");
55
+ const filesToTouch = /* @__PURE__ */ new Set();
56
+ for (const pkgPlan of plan.packages) {
57
+ if (pkgPlan.bump !== "none") {
58
+ const pkgJson = join(pkgPlan.directory, "package.json");
59
+ const jsrJson = join(pkgPlan.directory, "jsr.json");
60
+ const denoJson = join(pkgPlan.directory, "deno.json");
61
+ if (await fileExists(pkgJson))
62
+ filesToTouch.add(pkgJson);
63
+ if (await fileExists(jsrJson))
64
+ filesToTouch.add(jsrJson);
65
+ if (await fileExists(denoJson))
66
+ filesToTouch.add(denoJson);
67
+ }
68
+ for (const update of pkgPlan.dependentUpdates) {
69
+ const depPkgPlan = plan.packages.find((p) => p.name === update.dependentName);
70
+ if (depPkgPlan) {
71
+ const depPkgJson = join(depPkgPlan.directory, "package.json");
72
+ if (await fileExists(depPkgJson))
73
+ filesToTouch.add(depPkgJson);
74
+ }
75
+ }
76
+ }
77
+ const backups = [];
78
+ for (const filePath of filesToTouch) {
79
+ const originalContent = await readFile(filePath, "utf8");
80
+ backups.push({ filePath, originalContent });
81
+ }
82
+ onStep?.("Applying version bumps to manifests...");
83
+ const versionMap = /* @__PURE__ */ new Map();
84
+ for (const pkgPlan of plan.packages) {
85
+ if (pkgPlan.bump !== "none") {
86
+ versionMap.set(pkgPlan.name, pkgPlan.proposedVersion);
87
+ }
88
+ }
89
+ const depUpdateMap = /* @__PURE__ */ new Map();
90
+ for (const pkgPlan of plan.packages) {
91
+ for (const depUp of pkgPlan.dependentUpdates) {
92
+ if (!depUpdateMap.has(depUp.dependentName)) {
93
+ depUpdateMap.set(depUp.dependentName, []);
94
+ }
95
+ depUpdateMap.get(depUp.dependentName).push({
96
+ dependencyName: depUp.dependencyName,
97
+ newRange: depUp.newRange,
98
+ type: depUp.dependencyType
99
+ });
100
+ }
101
+ }
102
+ const modifiedManifests = [];
103
+ for (const pkgPlan of plan.packages) {
104
+ const pkgJsonPath = join(pkgPlan.directory, "package.json");
105
+ if (filesToTouch.has(pkgJsonPath)) {
106
+ const content = await readFile(pkgJsonPath, "utf8");
107
+ const parsed = JSON.parse(content);
108
+ if (versionMap.has(pkgPlan.name)) {
109
+ parsed.version = versionMap.get(pkgPlan.name);
110
+ }
111
+ const depUpdates = depUpdateMap.get(pkgPlan.name);
112
+ if (depUpdates) {
113
+ for (const up of depUpdates) {
114
+ const sec = parsed[up.type];
115
+ if (sec && sec[up.dependencyName]) {
116
+ sec[up.dependencyName] = up.newRange;
117
+ }
118
+ }
119
+ }
120
+ await writeFile(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
121
+ modifiedManifests.push(pkgJsonPath);
122
+ }
123
+ const jsrJsonPath = join(pkgPlan.directory, "jsr.json");
124
+ if (filesToTouch.has(jsrJsonPath)) {
125
+ const content = await readFile(jsrJsonPath, "utf8");
126
+ const parsed = JSON.parse(content);
127
+ if (versionMap.has(pkgPlan.name)) {
128
+ parsed.version = versionMap.get(pkgPlan.name);
129
+ }
130
+ await writeFile(jsrJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
131
+ modifiedManifests.push(jsrJsonPath);
132
+ }
133
+ const denoJsonPath = join(pkgPlan.directory, "deno.json");
134
+ if (filesToTouch.has(denoJsonPath)) {
135
+ const content = await readFile(denoJsonPath, "utf8");
136
+ const parsed = JSON.parse(content);
137
+ if (versionMap.has(pkgPlan.name)) {
138
+ parsed.version = versionMap.get(pkgPlan.name);
139
+ }
140
+ await writeFile(denoJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
141
+ modifiedManifests.push(denoJsonPath);
142
+ }
143
+ }
144
+ onStep?.(`Regenerating lockfile with ${lockfileAdapter.packageManager}...`);
145
+ const lockRes = await lockfileAdapter.regenerateLockfile(cwd);
146
+ if (!lockRes.success) {
147
+ for (const b of backups) {
148
+ await writeFile(b.filePath, b.originalContent, "utf8");
149
+ }
150
+ throw new Error(`Lockfile generation failed: ${lockRes.output}`);
151
+ }
152
+ const changelogBackups = /* @__PURE__ */ new Map();
153
+ const scope = {
154
+ plan,
155
+ modifiedManifests,
156
+ changelogBackups,
157
+ backupChangelog(path, original) {
158
+ changelogBackups.set(path, original);
159
+ }
160
+ };
161
+ return { scope, backups };
162
+ },
163
+ catch: (err) => new ManifestMutationError({
164
+ message: err instanceof Error ? err.message : String(err)
165
+ })
166
+ });
167
+ const release = (resource, exit) => Effect.promise(async () => {
168
+ if (Exit.isFailure(exit)) {
169
+ onRollback?.("Rolling back manifests to previous versions...");
170
+ for (const b of resource.backups) {
171
+ try {
172
+ await writeFile(b.filePath, b.originalContent, "utf8");
173
+ } catch {
174
+ }
175
+ }
176
+ onRollback?.("Restored manifests.");
177
+ onRollback?.("Restoring changelog files...");
178
+ for (const [chPath, original] of resource.scope.changelogBackups) {
179
+ try {
180
+ if (original === null) {
181
+ await unlink(chPath);
182
+ } else {
183
+ await writeFile(chPath, original, "utf8");
184
+ }
185
+ } catch {
186
+ }
187
+ }
188
+ onRollback?.("Restored changelog files.");
189
+ onRollback?.("Synchronizing workspace lockfile...");
190
+ try {
191
+ await lockfileAdapter.regenerateLockfile(cwd);
192
+ onRollback?.("Synchronized lockfile.");
193
+ } catch {
194
+ }
195
+ }
196
+ });
197
+ return Effect.acquireUseRelease(acquire, ({ scope }) => use(scope), release);
198
+ }
10
199
 
11
200
  // ../workspace/dist/graph.js
12
201
  var PackageGraphImpl = class {
@@ -160,12 +349,12 @@ var PackageGraphImpl = class {
160
349
  };
161
350
 
162
351
  // ../workspace/dist/resolver.js
163
- import { readFile, stat, readdir } from "fs/promises";
164
- import { join, relative, resolve } from "path";
352
+ import { readFile as readFile2, stat as stat2, readdir } from "fs/promises";
353
+ import { join as join2, relative, resolve } from "path";
165
354
  import { parse as parseYaml } from "yaml";
166
- async function fileExists(path) {
355
+ async function fileExists2(path) {
167
356
  try {
168
- const s = await stat(path);
357
+ const s = await stat2(path);
169
358
  return s.isFile();
170
359
  } catch {
171
360
  return false;
@@ -173,7 +362,7 @@ async function fileExists(path) {
173
362
  }
174
363
  async function dirExists(path) {
175
364
  try {
176
- const s = await stat(path);
365
+ const s = await stat2(path);
177
366
  return s.isDirectory();
178
367
  } catch {
179
368
  return false;
@@ -182,25 +371,25 @@ async function dirExists(path) {
182
371
  var DefaultWorkspaceResolver = class {
183
372
  name = "default-workspace-resolver";
184
373
  async supports(rootDir) {
185
- const pnpmWorkspace = join(rootDir, "pnpm-workspace.yaml");
186
- const packageJson = join(rootDir, "package.json");
187
- return await fileExists(pnpmWorkspace) || await fileExists(packageJson);
374
+ const pnpmWorkspace = join2(rootDir, "pnpm-workspace.yaml");
375
+ const packageJson = join2(rootDir, "package.json");
376
+ return await fileExists2(pnpmWorkspace) || await fileExists2(packageJson);
188
377
  }
189
378
  async resolve(rootDir) {
190
379
  const absRootDir = resolve(rootDir);
191
- const pnpmWorkspacePath = join(absRootDir, "pnpm-workspace.yaml");
192
- const rootPkgJsonPath = join(absRootDir, "package.json");
380
+ const pnpmWorkspacePath = join2(absRootDir, "pnpm-workspace.yaml");
381
+ const rootPkgJsonPath = join2(absRootDir, "package.json");
193
382
  let packageManager = "unknown";
194
383
  let patterns = [];
195
- if (await fileExists(pnpmWorkspacePath)) {
384
+ if (await fileExists2(pnpmWorkspacePath)) {
196
385
  packageManager = "pnpm";
197
- const content = await readFile(pnpmWorkspacePath, "utf8");
386
+ const content = await readFile2(pnpmWorkspacePath, "utf8");
198
387
  const parsed = parseYaml(content);
199
388
  if (parsed && Array.isArray(parsed.packages)) {
200
389
  patterns = parsed.packages;
201
390
  }
202
- } else if (await fileExists(rootPkgJsonPath)) {
203
- const content = await readFile(rootPkgJsonPath, "utf8");
391
+ } else if (await fileExists2(rootPkgJsonPath)) {
392
+ const content = await readFile2(rootPkgJsonPath, "utf8");
204
393
  const parsed = JSON.parse(content);
205
394
  if (Array.isArray(parsed.workspaces)) {
206
395
  patterns = parsed.workspaces;
@@ -233,17 +422,17 @@ var DefaultWorkspaceResolver = class {
233
422
  const matchedDirs = /* @__PURE__ */ new Set();
234
423
  for (const pattern of patterns) {
235
424
  if (pattern.endsWith("/*")) {
236
- const baseDir = join(absRootDir, pattern.slice(0, -2));
425
+ const baseDir = join2(absRootDir, pattern.slice(0, -2));
237
426
  if (await dirExists(baseDir)) {
238
427
  const entries = await readdir(baseDir, { withFileTypes: true });
239
428
  for (const entry of entries) {
240
429
  if (entry.isDirectory()) {
241
- matchedDirs.add(join(baseDir, entry.name));
430
+ matchedDirs.add(join2(baseDir, entry.name));
242
431
  }
243
432
  }
244
433
  }
245
434
  } else {
246
- const exactDir = join(absRootDir, pattern);
435
+ const exactDir = join2(absRootDir, pattern);
247
436
  if (await dirExists(exactDir)) {
248
437
  matchedDirs.add(exactDir);
249
438
  }
@@ -251,10 +440,10 @@ var DefaultWorkspaceResolver = class {
251
440
  }
252
441
  const packages = [];
253
442
  for (const dir of matchedDirs) {
254
- const manifestPath = join(dir, "package.json");
255
- if (await fileExists(manifestPath)) {
443
+ const manifestPath = join2(dir, "package.json");
444
+ if (await fileExists2(manifestPath)) {
256
445
  try {
257
- const content = await readFile(manifestPath, "utf8");
446
+ const content = await readFile2(manifestPath, "utf8");
258
447
  const manifest = JSON.parse(content);
259
448
  if (manifest.name && manifest.version) {
260
449
  packages.push({
@@ -450,8 +639,8 @@ var DefaultSemVerDecisionEngine = class {
450
639
  };
451
640
 
452
641
  // ../contracts/dist/store.js
453
- import { readFile as readFile2, writeFile, mkdir, readdir as readdir2 } from "fs/promises";
454
- import { join as join2 } from "path";
642
+ import { readFile as readFile3, writeFile as writeFile2, mkdir, readdir as readdir2 } from "fs/promises";
643
+ import { join as join3 } from "path";
455
644
  import semver3 from "semver";
456
645
  var FileContractStore = class {
457
646
  directory;
@@ -471,9 +660,9 @@ var FileContractStore = class {
471
660
  const files = await readdir2(this.directory, { recursive: true });
472
661
  for (const file of files) {
473
662
  if (typeof file === "string" && file.endsWith(".json")) {
474
- const fullPath = join2(this.directory, file);
663
+ const fullPath = join3(this.directory, file);
475
664
  try {
476
- const content = await readFile2(fullPath, "utf8");
665
+ const content = await readFile3(fullPath, "utf8");
477
666
  const parsed = JSON.parse(content);
478
667
  if (!packageName || parsed.package === packageName) {
479
668
  contracts.push(parsed);
@@ -494,8 +683,8 @@ var FileContractStore = class {
494
683
  await this.ensureDir();
495
684
  const sanitizedPackage = contract.package.replace(/[^a-zA-Z0-9_-]/g, "_");
496
685
  const sanitizedId = contract.id.replace(/[^a-zA-Z0-9_-]/g, "_");
497
- const filePath = join2(this.directory, `${sanitizedPackage}__${sanitizedId}.json`);
498
- await writeFile(filePath, JSON.stringify(contract, null, 2), "utf8");
686
+ const filePath = join3(this.directory, `${sanitizedPackage}__${sanitizedId}.json`);
687
+ await writeFile2(filePath, JSON.stringify(contract, null, 2), "utf8");
499
688
  }
500
689
  async getActiveContracts(packageName, targetVersion) {
501
690
  const all = await this.listContracts(packageName);
@@ -519,20 +708,20 @@ var FileContractStore = class {
519
708
  import { execFile } from "child_process";
520
709
  import { promisify } from "util";
521
710
  import { mkdtemp, rm, mkdir as mkdir2 } from "fs/promises";
522
- import { join as join3 } from "path";
711
+ import { join as join4 } from "path";
523
712
  import { tmpdir } from "os";
524
713
  import * as tar from "tar";
525
714
  var execFileAsync = promisify(execFile);
526
715
  var CandidatePacker = class {
527
- async pack(packageDir, packageName, version) {
528
- const tempDir = await mkdtemp(join3(tmpdir(), "verelease-candidate-"));
716
+ async pack(packageDir, packageName, version2) {
717
+ const tempDir = await mkdtemp(join4(tmpdir(), "verelease-candidate-"));
529
718
  const tarballDest = tempDir;
530
719
  const { stdout } = await execFileAsync("npm", ["pack", "--pack-destination", tarballDest], {
531
720
  cwd: packageDir
532
721
  });
533
722
  const tarballFilename = stdout.trim().split("\n").pop().trim();
534
- const tarballPath = join3(tarballDest, tarballFilename);
535
- const unpackedDir = join3(tempDir, "unpacked");
723
+ const tarballPath = join4(tarballDest, tarballFilename);
724
+ const unpackedDir = join4(tempDir, "unpacked");
536
725
  await mkdir2(unpackedDir, { recursive: true });
537
726
  await tar.x({
538
727
  file: tarballPath,
@@ -542,14 +731,14 @@ var CandidatePacker = class {
542
731
  });
543
732
  return {
544
733
  packageName,
545
- version,
734
+ version: version2,
546
735
  tarballPath,
547
736
  unpackedDir
548
737
  };
549
738
  }
550
739
  async cleanup(packResult) {
551
740
  try {
552
- const parentDir = join3(packResult.unpackedDir, "..");
741
+ const parentDir = join4(packResult.unpackedDir, "..");
553
742
  await rm(parentDir, { recursive: true, force: true });
554
743
  } catch {
555
744
  }
@@ -557,15 +746,15 @@ var CandidatePacker = class {
557
746
  };
558
747
 
559
748
  // ../contracts/dist/runner.js
560
- import { mkdir as mkdir3, symlink, rm as rm2, writeFile as writeFile2, copyFile } from "fs/promises";
561
- import { join as join5, basename } from "path";
749
+ import { mkdir as mkdir3, symlink, rm as rm2, writeFile as writeFile3, copyFile } from "fs/promises";
750
+ import { join as join6, basename } from "path";
562
751
  import { tmpdir as tmpdir2 } from "os";
563
752
  import { mkdtemp as mkdtemp2 } from "fs/promises";
564
753
 
565
754
  // ../test-adapter-node/dist/node-test.js
566
755
  import { spawn } from "child_process";
567
- import { readFile as readFile3 } from "fs/promises";
568
- import { join as join4 } from "path";
756
+ import { readFile as readFile4 } from "fs/promises";
757
+ import { join as join5 } from "path";
569
758
  var NodeTestAdapter = class {
570
759
  options;
571
760
  name = "node:test";
@@ -592,7 +781,7 @@ var NodeTestAdapter = class {
592
781
  let testFiles = ctx.testFiles && ctx.testFiles.length > 0 ? ctx.testFiles : [];
593
782
  if (loaderArgs.length === 0 || testFiles.length === 0) {
594
783
  try {
595
- const pkgRaw = await readFile3(join4(ctx.packagePath, "package.json"), "utf8");
784
+ const pkgRaw = await readFile4(join5(ctx.packagePath, "package.json"), "utf8");
596
785
  const pkg = JSON.parse(pkgRaw);
597
786
  const testScript = pkg.scripts?.test || "";
598
787
  if (loaderArgs.length === 0 && (testScript.includes("--import tsx") || testScript.includes("tsx"))) {
@@ -766,22 +955,22 @@ var ContractRunner = class {
766
955
  }
767
956
  async runContract(contract, candidate) {
768
957
  const startTime = Date.now();
769
- const consumerEnvDir = await mkdtemp2(join5(tmpdir2(), "verelease-consumer-"));
770
- const nodeModulesDir = join5(consumerEnvDir, "node_modules");
958
+ const consumerEnvDir = await mkdtemp2(join6(tmpdir2(), "verelease-consumer-"));
959
+ const nodeModulesDir = join6(consumerEnvDir, "node_modules");
771
960
  try {
772
- await writeFile2(join5(consumerEnvDir, "package.json"), JSON.stringify({ name: "consumer-fixture", type: "module" }, null, 2), "utf8");
773
- let targetPkgLink = join5(nodeModulesDir, candidate.packageName);
961
+ await writeFile3(join6(consumerEnvDir, "package.json"), JSON.stringify({ name: "consumer-fixture", type: "module" }, null, 2), "utf8");
962
+ let targetPkgLink = join6(nodeModulesDir, candidate.packageName);
774
963
  if (candidate.packageName.startsWith("@")) {
775
964
  const [scope, pkgName] = candidate.packageName.split("/");
776
- const scopeDir = join5(nodeModulesDir, scope);
965
+ const scopeDir = join6(nodeModulesDir, scope);
777
966
  await mkdir3(scopeDir, { recursive: true });
778
- targetPkgLink = join5(scopeDir, pkgName);
967
+ targetPkgLink = join6(scopeDir, pkgName);
779
968
  } else {
780
969
  await mkdir3(nodeModulesDir, { recursive: true });
781
970
  }
782
971
  await symlink(candidate.unpackedDir, targetPkgLink, "junction");
783
972
  const isolatedTestName = basename(contract.testFile);
784
- const isolatedTestPath = join5(consumerEnvDir, isolatedTestName);
973
+ const isolatedTestPath = join6(consumerEnvDir, isolatedTestName);
785
974
  await copyFile(contract.testFile, isolatedTestPath);
786
975
  const testRun = await this.testAdapter.run({
787
976
  packagePath: consumerEnvDir,
@@ -1118,11 +1307,11 @@ var ApiDiffEngine = class {
1118
1307
  };
1119
1308
 
1120
1309
  // ../api-diff/dist/analyzer.js
1121
- import { readFile as readFile4, stat as stat2 } from "fs/promises";
1122
- import { join as join6 } from "path";
1123
- async function fileExists2(path) {
1310
+ import { readFile as readFile5, stat as stat3 } from "fs/promises";
1311
+ import { join as join7 } from "path";
1312
+ async function fileExists3(path) {
1124
1313
  try {
1125
- const s = await stat2(path);
1314
+ const s = await stat3(path);
1126
1315
  return s.isFile();
1127
1316
  } catch {
1128
1317
  return false;
@@ -1141,11 +1330,11 @@ var ApiDiffAnalyzer = class {
1141
1330
  if (!candidateEntrypoint) {
1142
1331
  return [];
1143
1332
  }
1144
- const candidateSource = await readFile4(candidateEntrypoint, "utf8");
1333
+ const candidateSource = await readFile5(candidateEntrypoint, "utf8");
1145
1334
  const candidateSnapshot = this.extractor.extractFromSource(candidateSource, candidateEntrypoint);
1146
1335
  let baselineSnapshot;
1147
- if (context.baselinePath && await fileExists2(context.baselinePath)) {
1148
- const baseSource = await readFile4(context.baselinePath, "utf8");
1336
+ if (context.baselinePath && await fileExists3(context.baselinePath)) {
1337
+ const baseSource = await readFile5(context.baselinePath, "utf8");
1149
1338
  if (context.baselinePath.endsWith(".json")) {
1150
1339
  baselineSnapshot = JSON.parse(baseSource);
1151
1340
  } else {
@@ -1159,14 +1348,14 @@ var ApiDiffAnalyzer = class {
1159
1348
  }
1160
1349
  async findEntrypoint(pkgDir) {
1161
1350
  const candidates = [
1162
- join6(pkgDir, "src", "index.ts"),
1163
- join6(pkgDir, "src", "index.js"),
1164
- join6(pkgDir, "index.ts"),
1165
- join6(pkgDir, "index.js"),
1166
- join6(pkgDir, "dist", "index.d.ts")
1351
+ join7(pkgDir, "src", "index.ts"),
1352
+ join7(pkgDir, "src", "index.js"),
1353
+ join7(pkgDir, "index.ts"),
1354
+ join7(pkgDir, "index.js"),
1355
+ join7(pkgDir, "dist", "index.d.ts")
1167
1356
  ];
1168
1357
  for (const c of candidates) {
1169
- if (await fileExists2(c)) {
1358
+ if (await fileExists3(c)) {
1170
1359
  return c;
1171
1360
  }
1172
1361
  }
@@ -1177,14 +1366,14 @@ var ApiDiffAnalyzer = class {
1177
1366
  // ../api-diff/dist/baseline.js
1178
1367
  import { execFile as execFile2 } from "child_process";
1179
1368
  import { promisify as promisify2 } from "util";
1180
- import { mkdtemp as mkdtemp3, mkdir as mkdir4, stat as stat3 } from "fs/promises";
1181
- import { join as join7 } from "path";
1369
+ import { mkdtemp as mkdtemp3, mkdir as mkdir4, stat as stat4 } from "fs/promises";
1370
+ import { join as join8 } from "path";
1182
1371
  import { tmpdir as tmpdir3 } from "os";
1183
1372
  import * as tar2 from "tar";
1184
1373
  var execFileAsync2 = promisify2(execFile2);
1185
- async function fileExists3(path) {
1374
+ async function fileExists4(path) {
1186
1375
  try {
1187
- const s = await stat3(path);
1376
+ const s = await stat4(path);
1188
1377
  return s.isFile();
1189
1378
  } catch {
1190
1379
  return false;
@@ -1199,20 +1388,20 @@ var BaselineFetcher = class {
1199
1388
  * or the extracted package has no recognizable entry point.
1200
1389
  */
1201
1390
  async fetch(packageName, parentTmpDir) {
1202
- const version = await this.getLatestVersion(packageName);
1203
- if (!version)
1391
+ const version2 = await this.getLatestVersion(packageName);
1392
+ if (!version2)
1204
1393
  return null;
1205
- const tempDir = parentTmpDir ? join7(parentTmpDir, packageName.replace(/\//g, "__")) : await mkdtemp3(join7(tmpdir3(), "verelease-baseline-"));
1394
+ const tempDir = parentTmpDir ? join8(parentTmpDir, packageName.replace(/\//g, "__")) : await mkdtemp3(join8(tmpdir3(), "verelease-baseline-"));
1206
1395
  await mkdir4(tempDir, { recursive: true });
1207
1396
  let tarballFilename;
1208
1397
  try {
1209
- const { stdout } = await execFileAsync2("npm", ["pack", `${packageName}@${version}`, "--pack-destination", tempDir], { timeout: 3e4 });
1398
+ const { stdout } = await execFileAsync2("npm", ["pack", `${packageName}@${version2}`, "--pack-destination", tempDir], { timeout: 3e4 });
1210
1399
  tarballFilename = stdout.trim().split("\n").pop().trim();
1211
1400
  } catch {
1212
1401
  return null;
1213
1402
  }
1214
- const tarballPath = join7(tempDir, tarballFilename);
1215
- const extractedDir = join7(tempDir, "extracted");
1403
+ const tarballPath = join8(tempDir, tarballFilename);
1404
+ const extractedDir = join8(tempDir, "extracted");
1216
1405
  await mkdir4(extractedDir, { recursive: true });
1217
1406
  try {
1218
1407
  await tar2.x({
@@ -1227,7 +1416,7 @@ var BaselineFetcher = class {
1227
1416
  const entryPointPath = await this.findEntrypoint(extractedDir);
1228
1417
  if (!entryPointPath)
1229
1418
  return null;
1230
- return { version, entryPointPath, extractedDir };
1419
+ return { version: version2, entryPointPath, extractedDir };
1231
1420
  }
1232
1421
  async getLatestVersion(packageName) {
1233
1422
  try {
@@ -1242,16 +1431,16 @@ var BaselineFetcher = class {
1242
1431
  }
1243
1432
  async findEntrypoint(pkgDir) {
1244
1433
  const candidates = [
1245
- join7(pkgDir, "dist", "index.d.ts"),
1246
- join7(pkgDir, "dist", "index.d.mts"),
1247
- join7(pkgDir, "src", "index.ts"),
1248
- join7(pkgDir, "src", "index.js"),
1249
- join7(pkgDir, "index.d.ts"),
1250
- join7(pkgDir, "index.ts"),
1251
- join7(pkgDir, "index.js")
1434
+ join8(pkgDir, "dist", "index.d.ts"),
1435
+ join8(pkgDir, "dist", "index.d.mts"),
1436
+ join8(pkgDir, "src", "index.ts"),
1437
+ join8(pkgDir, "src", "index.js"),
1438
+ join8(pkgDir, "index.d.ts"),
1439
+ join8(pkgDir, "index.ts"),
1440
+ join8(pkgDir, "index.js")
1252
1441
  ];
1253
1442
  for (const candidate of candidates) {
1254
- if (await fileExists3(candidate)) {
1443
+ if (await fileExists4(candidate)) {
1255
1444
  return candidate;
1256
1445
  }
1257
1446
  }
@@ -1260,140 +1449,18 @@ var BaselineFetcher = class {
1260
1449
  };
1261
1450
 
1262
1451
  // ../manifest/dist/mutator.js
1263
- import { readFile as readFile5, writeFile as writeFile3, stat as stat4 } from "fs/promises";
1264
- import { join as join8 } from "path";
1265
- async function fileExists4(path) {
1266
- try {
1267
- const s = await stat4(path);
1268
- return s.isFile();
1269
- } catch {
1270
- return false;
1271
- }
1272
- }
1273
- var TransactionalManifestMutator = class {
1274
- async applyPlan(plan) {
1275
- const backups = [];
1276
- const modifiedFiles = [];
1277
- const filesToTouch = /* @__PURE__ */ new Set();
1278
- for (const pkgPlan of plan.packages) {
1279
- if (pkgPlan.bump !== "none") {
1280
- const pkgJson = join8(pkgPlan.directory, "package.json");
1281
- const jsrJson = join8(pkgPlan.directory, "jsr.json");
1282
- const denoJson = join8(pkgPlan.directory, "deno.json");
1283
- if (await fileExists4(pkgJson))
1284
- filesToTouch.add(pkgJson);
1285
- if (await fileExists4(jsrJson))
1286
- filesToTouch.add(jsrJson);
1287
- if (await fileExists4(denoJson))
1288
- filesToTouch.add(denoJson);
1289
- }
1290
- for (const update of pkgPlan.dependentUpdates) {
1291
- const depPkgPlan = plan.packages.find((p) => p.name === update.dependentName);
1292
- if (depPkgPlan) {
1293
- const depPkgJson = join8(depPkgPlan.directory, "package.json");
1294
- if (await fileExists4(depPkgJson))
1295
- filesToTouch.add(depPkgJson);
1296
- }
1297
- }
1298
- }
1299
- for (const filePath of filesToTouch) {
1300
- const originalContent = await readFile5(filePath, "utf8");
1301
- backups.push({ filePath, originalContent });
1302
- }
1303
- try {
1304
- const versionMap = /* @__PURE__ */ new Map();
1305
- for (const pkgPlan of plan.packages) {
1306
- if (pkgPlan.bump !== "none") {
1307
- versionMap.set(pkgPlan.name, pkgPlan.proposedVersion);
1308
- }
1309
- }
1310
- const depUpdateMap = /* @__PURE__ */ new Map();
1311
- for (const pkgPlan of plan.packages) {
1312
- for (const depUp of pkgPlan.dependentUpdates) {
1313
- if (!depUpdateMap.has(depUp.dependentName)) {
1314
- depUpdateMap.set(depUp.dependentName, []);
1315
- }
1316
- depUpdateMap.get(depUp.dependentName).push({
1317
- dependencyName: depUp.dependencyName,
1318
- newRange: depUp.newRange,
1319
- type: depUp.dependencyType
1320
- });
1321
- }
1322
- }
1323
- for (const pkgPlan of plan.packages) {
1324
- const pkgJsonPath = join8(pkgPlan.directory, "package.json");
1325
- if (filesToTouch.has(pkgJsonPath)) {
1326
- const content = await readFile5(pkgJsonPath, "utf8");
1327
- const parsed = JSON.parse(content);
1328
- if (versionMap.has(pkgPlan.name)) {
1329
- parsed.version = versionMap.get(pkgPlan.name);
1330
- }
1331
- const depUpdates = depUpdateMap.get(pkgPlan.name);
1332
- if (depUpdates) {
1333
- for (const up of depUpdates) {
1334
- const sec = parsed[up.type];
1335
- if (sec && sec[up.dependencyName]) {
1336
- sec[up.dependencyName] = up.newRange;
1337
- }
1338
- }
1339
- }
1340
- await writeFile3(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
1341
- modifiedFiles.push(pkgJsonPath);
1342
- }
1343
- const jsrJsonPath = join8(pkgPlan.directory, "jsr.json");
1344
- if (filesToTouch.has(jsrJsonPath)) {
1345
- const content = await readFile5(jsrJsonPath, "utf8");
1346
- const parsed = JSON.parse(content);
1347
- if (versionMap.has(pkgPlan.name)) {
1348
- parsed.version = versionMap.get(pkgPlan.name);
1349
- }
1350
- await writeFile3(jsrJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
1351
- modifiedFiles.push(jsrJsonPath);
1352
- }
1353
- const denoJsonPath = join8(pkgPlan.directory, "deno.json");
1354
- if (filesToTouch.has(denoJsonPath)) {
1355
- const content = await readFile5(denoJsonPath, "utf8");
1356
- const parsed = JSON.parse(content);
1357
- if (versionMap.has(pkgPlan.name)) {
1358
- parsed.version = versionMap.get(pkgPlan.name);
1359
- }
1360
- await writeFile3(denoJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
1361
- modifiedFiles.push(denoJsonPath);
1362
- }
1363
- }
1364
- } catch (err) {
1365
- for (const b of backups) {
1366
- await writeFile3(b.filePath, b.originalContent, "utf8");
1367
- }
1368
- throw err;
1369
- }
1370
- const transaction = {
1371
- id: `tx-${Date.now()}`,
1372
- plan,
1373
- backups,
1374
- modifiedFiles,
1375
- appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
1376
- async rollback() {
1377
- for (const b of backups) {
1378
- await writeFile3(b.filePath, b.originalContent, "utf8");
1379
- }
1380
- },
1381
- async commit() {
1382
- }
1383
- };
1384
- return transaction;
1385
- }
1386
- };
1452
+ import { readFile as readFile6, writeFile as writeFile4, stat as stat5 } from "fs/promises";
1453
+ import { join as join9 } from "path";
1387
1454
 
1388
1455
  // ../manifest/dist/lockfile.js
1389
1456
  import { execFile as execFile3 } from "child_process";
1390
1457
  import { promisify as promisify3 } from "util";
1391
- import { stat as stat5 } from "fs/promises";
1392
- import { join as join9 } from "path";
1458
+ import { stat as stat6 } from "fs/promises";
1459
+ import { join as join10 } from "path";
1393
1460
  var execFileAsync3 = promisify3(execFile3);
1394
1461
  async function fileExists5(path) {
1395
1462
  try {
1396
- const s = await stat5(path);
1463
+ const s = await stat6(path);
1397
1464
  return s.isFile();
1398
1465
  } catch {
1399
1466
  return false;
@@ -1446,11 +1513,11 @@ var NpmLockfileAdapter = class {
1446
1513
  var AutoLockfileAdapter = class {
1447
1514
  packageManager = "npm";
1448
1515
  async regenerateLockfile(rootDirectory) {
1449
- if (await fileExists5(join9(rootDirectory, "pnpm-lock.yaml"))) {
1516
+ if (await fileExists5(join10(rootDirectory, "pnpm-lock.yaml"))) {
1450
1517
  this.packageManager = "pnpm";
1451
1518
  return new PnpmLockfileAdapter().regenerateLockfile(rootDirectory);
1452
1519
  }
1453
- if (await fileExists5(join9(rootDirectory, "package-lock.json"))) {
1520
+ if (await fileExists5(join10(rootDirectory, "package-lock.json"))) {
1454
1521
  this.packageManager = "npm";
1455
1522
  return new NpmLockfileAdapter().regenerateLockfile(rootDirectory);
1456
1523
  }
@@ -1461,6 +1528,10 @@ var AutoLockfileAdapter = class {
1461
1528
  // ../publisher-npm/dist/publisher.js
1462
1529
  import { execFile as execFile4, spawn as spawn2 } from "child_process";
1463
1530
  import { promisify as promisify4 } from "util";
1531
+ import * as Effect2 from "effect/Effect";
1532
+ import * as Schedule from "effect/Schedule";
1533
+ import * as prompts from "@clack/prompts";
1534
+ import colors from "picocolors";
1464
1535
  var execFileAsync4 = promisify4(execFile4);
1465
1536
  function openBrowser(url) {
1466
1537
  const platform = process.platform;
@@ -1507,20 +1578,90 @@ var NpmPublishProvider = class {
1507
1578
  }
1508
1579
  return channel;
1509
1580
  }
1581
+ /**
1582
+ * Check npm authentication status without throwing.
1583
+ */
1584
+ async checkAuth() {
1585
+ try {
1586
+ const { stdout } = await execFileAsync4("npm", ["whoami"]);
1587
+ const username = stdout.trim();
1588
+ return { authenticated: true, username };
1589
+ } catch (err) {
1590
+ return {
1591
+ authenticated: false,
1592
+ error: err.stderr || err.message || "401 Unauthorized"
1593
+ };
1594
+ }
1595
+ }
1596
+ /**
1597
+ * Proactively verifies npm authentication.
1598
+ * If unauthenticated and interactive, prompts the user to log in now,
1599
+ * runs `npm login` interactively, verifies the login, and continues.
1600
+ */
1601
+ async ensureAuth(options = {}) {
1602
+ const isInteractive = options.interactive ?? (this.options.interactive || !process.env.CI && process.stdin.isTTY);
1603
+ const status = await this.checkAuth();
1604
+ if (status.authenticated && status.username) {
1605
+ return status.username;
1606
+ }
1607
+ if (!isInteractive) {
1608
+ throw new NpmAuthError({
1609
+ message: "You are not logged in to npm (npm whoami failed with 401). Run 'npm login' or configure NPM_TOKEN / NODE_AUTH_TOKEN in CI."
1610
+ });
1611
+ }
1612
+ prompts.log.warn(colors.yellow("You are not currently logged in to npm."));
1613
+ const shouldLogin = await prompts.confirm({
1614
+ message: "Would you like to log in to npm now?",
1615
+ initialValue: true
1616
+ });
1617
+ if (prompts.isCancel(shouldLogin) || !shouldLogin) {
1618
+ throw new NpmAuthError({
1619
+ message: "npm authentication cancelled. Run 'npm login' before releasing."
1620
+ });
1621
+ }
1622
+ prompts.log.info(colors.cyan("Launching npm login in your terminal..."));
1623
+ await new Promise((resolve3, reject) => {
1624
+ const child = spawn2("npm", ["login"], { stdio: "inherit" });
1625
+ child.on("close", (code) => {
1626
+ if (code === 0) {
1627
+ resolve3();
1628
+ } else {
1629
+ reject(new NpmAuthError({
1630
+ message: `npm login exited with code ${code}`,
1631
+ code: code ?? void 0
1632
+ }));
1633
+ }
1634
+ });
1635
+ child.on("error", (err) => {
1636
+ reject(new NpmAuthError({
1637
+ message: `Failed to launch npm login: ${err.message}`
1638
+ }));
1639
+ });
1640
+ });
1641
+ const recheck = await this.checkAuth();
1642
+ if (recheck.authenticated && recheck.username) {
1643
+ prompts.log.success(colors.green(`Authenticated as ${colors.bold(recheck.username)} on npm.`));
1644
+ return recheck.username;
1645
+ }
1646
+ throw new NpmAuthError({
1647
+ message: "npm login completed, but npm authentication could not be verified."
1648
+ });
1649
+ }
1510
1650
  async prepare(ctx) {
1511
1651
  try {
1512
1652
  await execFileAsync4("npm", ["pack", "--dry-run"], {
1513
1653
  cwd: ctx.packagePlan.directory
1514
1654
  });
1515
1655
  } catch (err) {
1516
- throw new Error(`Failed packing verification for package ${ctx.packagePlan.name}: ${err.message || String(err)}`);
1656
+ throw new PublishError({
1657
+ packageName: ctx.packagePlan.name,
1658
+ message: `Failed packing verification for package ${ctx.packagePlan.name}: ${err.message || String(err)}`
1659
+ });
1517
1660
  }
1518
- if (!ctx.dryRun && !process.env.CI) {
1519
- try {
1520
- await execFileAsync4("npm", ["whoami"]);
1521
- } catch {
1522
- throw new Error(`You are not logged in to npm (npm whoami failed with 401). Please run 'npm login' first.`);
1523
- }
1661
+ if (!ctx.dryRun) {
1662
+ await this.ensureAuth({
1663
+ interactive: this.options.interactive ?? (!process.env.CI && process.stdin.isTTY)
1664
+ });
1524
1665
  }
1525
1666
  }
1526
1667
  async publish(ctx) {
@@ -1545,8 +1686,7 @@ var NpmPublishProvider = class {
1545
1686
  }
1546
1687
  const isInteractive = this.options.interactive ?? (!ctx.dryRun && !process.env.CI && process.stdin.isTTY);
1547
1688
  if (isInteractive) {
1548
- console.log(`
1549
- \u{1F510} Interactive terminal detected for npm publish:`);
1689
+ prompts.log.info(colors.cyan("Terminal ready for npm publish authentication:"));
1550
1690
  console.log(` \u2022 WebAuthn / Passkey / Touch ID: press ENTER when prompted to open browser.`);
1551
1691
  console.log(` \u2022 Authenticator App: enter your 6-digit TOTP code if prompted.
1552
1692
  `);
@@ -1619,25 +1759,22 @@ ${err.message || ""}`;
1619
1759
  if (authUrlMatch && doneUrlMatch && !ctx.dryRun) {
1620
1760
  const authUrl = authUrlMatch[0];
1621
1761
  const doneUrl = doneUrlMatch[0];
1622
- console.log(`
1623
- \x1B[36m\u{1F511} WebAuthn / Security Key 2FA Detected!\x1B[0m`);
1762
+ prompts.log.info(colors.cyan(colors.bold("\u{1F511} WebAuthn / Security Key 2FA Detected!")));
1624
1763
  console.log(` Your npm account uses Touch ID / security key (WebAuthn) instead of a 6-digit code.`);
1625
1764
  console.log(` Opening browser to authenticate:`);
1626
- console.log(` \u{1F449} \x1B[4m${authUrl}\x1B[0m
1765
+ console.log(` \u{1F449} ${colors.underline(authUrl)}
1627
1766
  `);
1628
1767
  openBrowser(authUrl);
1629
- console.log(`\u23F3 Waiting for Touch ID / security key approval in your browser (up to 90s)...`);
1768
+ prompts.log.info("Waiting for Touch ID / security key approval in your browser (up to 90s)...");
1630
1769
  const token = await pollForWebAuthToken(doneUrl, 9e4);
1631
1770
  if (token) {
1632
- console.log(`\x1B[32m\u2713 Touch ID / Passkey approved! Completing publish...\x1B[0m
1633
- `);
1771
+ prompts.log.success(colors.green("\u2713 Touch ID / Passkey approved! Completing publish..."));
1634
1772
  return this.publish({
1635
1773
  ...ctx,
1636
1774
  extraArgs: [...ctx.extraArgs || [], "--otp", token]
1637
1775
  });
1638
1776
  } else {
1639
- console.error(`\x1B[31m\u274C WebAuthn approval timed out or was cancelled.\x1B[0m
1640
- `);
1777
+ prompts.log.error(colors.red("\u274C WebAuthn approval timed out or was cancelled."));
1641
1778
  }
1642
1779
  }
1643
1780
  return {
@@ -1653,17 +1790,29 @@ ${err.message || ""}`;
1653
1790
  };
1654
1791
  }
1655
1792
  }
1793
+ /**
1794
+ * Resiliently fetches published versions using EffectTS retry schedule
1795
+ */
1656
1796
  async getPublishedVersions(packageName) {
1797
+ const fetchVersions = Effect2.tryPromise({
1798
+ try: async () => {
1799
+ const { stdout } = await execFileAsync4("npm", ["view", packageName, "versions", "--json"]);
1800
+ const parsed = JSON.parse(stdout.trim());
1801
+ if (Array.isArray(parsed)) {
1802
+ return parsed;
1803
+ }
1804
+ if (typeof parsed === "string") {
1805
+ return [parsed];
1806
+ }
1807
+ return [];
1808
+ },
1809
+ catch: (err) => err
1810
+ });
1811
+ const withRetry = Effect2.retry(fetchVersions, {
1812
+ schedule: Schedule.recurs(2)
1813
+ });
1657
1814
  try {
1658
- const { stdout } = await execFileAsync4("npm", ["view", packageName, "versions", "--json"]);
1659
- const parsed = JSON.parse(stdout.trim());
1660
- if (Array.isArray(parsed)) {
1661
- return parsed;
1662
- }
1663
- if (typeof parsed === "string") {
1664
- return [parsed];
1665
- }
1666
- return [];
1815
+ return await Effect2.runPromise(withRetry);
1667
1816
  } catch {
1668
1817
  return [];
1669
1818
  }
@@ -1671,12 +1820,12 @@ ${err.message || ""}`;
1671
1820
  };
1672
1821
 
1673
1822
  // ../config/dist/config.js
1674
- import { stat as stat6 } from "fs/promises";
1675
- import { join as join10 } from "path";
1823
+ import { stat as stat7 } from "fs/promises";
1824
+ import { join as join11 } from "path";
1676
1825
  import { pathToFileURL } from "url";
1677
1826
  async function fileExists6(path) {
1678
1827
  try {
1679
- const s = await stat6(path);
1828
+ const s = await stat7(path);
1680
1829
  return s.isFile();
1681
1830
  } catch {
1682
1831
  return false;
@@ -1694,7 +1843,7 @@ async function loadConfig(rootDir) {
1694
1843
  "autoversion.config.mjs"
1695
1844
  ];
1696
1845
  for (const name of candidateNames) {
1697
- const fullPath = join10(rootDir, name);
1846
+ const fullPath = join11(rootDir, name);
1698
1847
  if (await fileExists6(fullPath)) {
1699
1848
  try {
1700
1849
  const fileUrl = pathToFileURL(fullPath).href;
@@ -1740,8 +1889,8 @@ var MigrationScriptSchema = z.object({
1740
1889
  });
1741
1890
 
1742
1891
  // ../ai/dist/changelog.js
1743
- function renderChangelogMarkdown(version, date, data) {
1744
- const parts = [`# ${version} (${date})`, ""];
1892
+ function renderChangelogMarkdown(version2, date, data) {
1893
+ const parts = [`# ${version2} (${date})`, ""];
1745
1894
  if (data.breakingChanges.length > 0) {
1746
1895
  parts.push("## \u26A0\uFE0F Breaking Changes");
1747
1896
  for (const item of data.breakingChanges) {
@@ -1949,10 +2098,10 @@ walkAndTransform(targetDir);
1949
2098
  }
1950
2099
 
1951
2100
  // ../ai/dist/validation.js
1952
- import { cp, mkdtemp as mkdtemp4, rm as rm3, writeFile as writeFile4, mkdir as mkdir5, symlink as symlink2, readdir as readdir3 } from "fs/promises";
2101
+ import { cp, mkdtemp as mkdtemp4, rm as rm3, writeFile as writeFile5, mkdir as mkdir5, symlink as symlink2, readdir as readdir3 } from "fs/promises";
1953
2102
  import { execFile as execFile5 } from "child_process";
1954
2103
  import { promisify as promisify5 } from "util";
1955
- import { join as join11 } from "path";
2104
+ import { join as join12 } from "path";
1956
2105
  import { tmpdir as tmpdir4 } from "os";
1957
2106
  import * as tar3 from "tar";
1958
2107
  var execFileAsync5 = promisify5(execFile5);
@@ -2165,8 +2314,8 @@ var CommandTestAdapter = class {
2165
2314
  };
2166
2315
 
2167
2316
  // ../test-adapter/dist/test-suite-analyzer.js
2168
- import { readFile as readFile6, readdir as readdir4 } from "fs/promises";
2169
- import { join as join12 } from "path";
2317
+ import { readFile as readFile7, readdir as readdir4 } from "fs/promises";
2318
+ import { join as join13 } from "path";
2170
2319
  var TestSuiteAnalyzer = class {
2171
2320
  name = "test-suite-analyzer";
2172
2321
  adapters;
@@ -2183,7 +2332,7 @@ var TestSuiteAnalyzer = class {
2183
2332
  const { packageName, packagePath } = context;
2184
2333
  let manifest;
2185
2334
  try {
2186
- const raw = await readFile6(join12(packagePath, "package.json"), "utf8");
2335
+ const raw = await readFile7(join13(packagePath, "package.json"), "utf8");
2187
2336
  manifest = JSON.parse(raw);
2188
2337
  } catch {
2189
2338
  return [];
@@ -2784,30 +2933,21 @@ var DenoTestAdapter = class {
2784
2933
 
2785
2934
  // src/cli.ts
2786
2935
  import semver4 from "semver";
2787
- async function promptConfirm(question, defaultYes = false) {
2788
- const rl = createInterface({
2789
- input: process.stdin,
2790
- output: process.stdout
2791
- });
2792
- try {
2793
- const answer = (await rl.question(question)).trim().toLowerCase();
2794
- if (!answer) return defaultYes;
2795
- return answer === "y" || answer === "yes";
2796
- } finally {
2797
- rl.close();
2798
- }
2799
- }
2800
2936
  var VereleaseCli = class {
2801
2937
  cwd;
2802
2938
  constructor(options = {}) {
2803
2939
  this.cwd = resolve2(options.cwd || process.cwd());
2804
2940
  }
2805
2941
  async createPlan(options = {}) {
2942
+ const isInteractive = process.stdout.isTTY && !options.json;
2943
+ const spin = isInteractive ? prompts2.spinner() : void 0;
2944
+ spin?.start("Resolving workspace package graph");
2806
2945
  const config = await loadConfig(this.cwd);
2807
2946
  const resolver = new DefaultWorkspaceResolver();
2808
2947
  const discovery = await resolver.resolve(this.cwd);
2948
+ spin?.stop(`Discovered ${colors2.bold(discovery.packages.length)} workspace package${discovery.packages.length === 1 ? "" : "s"}`);
2809
2949
  const contractStore = new FileContractStore(
2810
- join13(this.cwd, config.contracts?.directory || ".verelease/contracts")
2950
+ join14(this.cwd, config.contracts?.directory || ".verelease/contracts")
2811
2951
  );
2812
2952
  const contractAnalyzer = new ContractCompatibilityAnalyzer(contractStore);
2813
2953
  const apiDiffAnalyzer = new ApiDiffAnalyzer();
@@ -2826,8 +2966,9 @@ var VereleaseCli = class {
2826
2966
  const npmPublisher = new NpmPublishProvider();
2827
2967
  const evidenceMap = /* @__PURE__ */ new Map();
2828
2968
  const initialBumps = /* @__PURE__ */ new Map();
2829
- const baselineTmpDir = await mkdtemp5(join13(tmpdir5(), "verelease-baseline-"));
2969
+ const baselineTmpDir = await mkdtemp5(join14(tmpdir5(), "verelease-baseline-"));
2830
2970
  try {
2971
+ spin?.start("Evaluating compatibility evidence (API diffs, tests & contracts)");
2831
2972
  for (const pkg of discovery.packages) {
2832
2973
  const evidence = [];
2833
2974
  try {
@@ -2930,10 +3071,12 @@ var VereleaseCli = class {
2930
3071
  reasons
2931
3072
  });
2932
3073
  }
3074
+ spin?.stop("Compatibility evidence evaluated");
2933
3075
  } finally {
2934
3076
  await rm4(baselineTmpDir, { recursive: true, force: true }).catch(() => {
2935
3077
  });
2936
3078
  }
3079
+ spin?.start("Propagating dependency updates across workspace graph");
2937
3080
  const propagator = new VersionPropagator(discovery.graph);
2938
3081
  const { bumps: finalBumps, dependencyUpdates } = propagator.propagate(initialBumps);
2939
3082
  const publishOrder = discovery.graph.getTopologicalOrder();
@@ -2973,6 +3116,7 @@ var VereleaseCli = class {
2973
3116
  publish: !pkg.isPrivate && bumpTarget.bump !== "none"
2974
3117
  });
2975
3118
  }
3119
+ spin?.stop(`Calculated release plan [Channel: ${colors2.cyan(options.channel || "stable")}]`);
2976
3120
  const plan = {
2977
3121
  schemaVersion: 1,
2978
3122
  id: `plan-${Date.now()}`,
@@ -2993,6 +3137,11 @@ var VereleaseCli = class {
2993
3137
  return { plan, evidenceMap };
2994
3138
  }
2995
3139
  async runPlanCommand(options = {}) {
3140
+ if (!options.json) {
3141
+ prompts2.intro(
3142
+ colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" evidence-based release plan")
3143
+ );
3144
+ }
2996
3145
  const { plan } = await this.createPlan(options);
2997
3146
  if (options.json) {
2998
3147
  console.log(JSON.stringify(plan, null, 2));
@@ -3000,198 +3149,228 @@ var VereleaseCli = class {
3000
3149
  }
3001
3150
  this.printPlan(plan);
3002
3151
  try {
3003
- const verelDir = join13(this.cwd, ".verelease");
3152
+ const verelDir = join14(this.cwd, ".verelease");
3004
3153
  await mkdir6(verelDir, { recursive: true });
3005
- await writeFile5(
3006
- join13(verelDir, "release-plan.json"),
3154
+ await writeFile6(
3155
+ join14(verelDir, "release-plan.json"),
3007
3156
  JSON.stringify(plan, null, 2),
3008
3157
  "utf8"
3009
3158
  );
3010
3159
  } catch {
3011
3160
  }
3161
+ prompts2.outro(
3162
+ plan.summary.bumpedPackages === 0 ? colors2.green("\u2728 All packages are up to date.") : colors2.cyan(
3163
+ `Plan ready: run ${colors2.bold(`verelease release --channel ${plan.channel}`)} to execute.`
3164
+ )
3165
+ );
3012
3166
  return plan;
3013
3167
  }
3014
3168
  async runReleaseCommand(options = {}) {
3169
+ if (!options.json) {
3170
+ prompts2.intro(
3171
+ colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(
3172
+ ` release pipeline [${colors2.bold(options.channel || "stable")}]` + (options.dryRun ? colors2.yellow(" (DRY RUN)") : "")
3173
+ )
3174
+ );
3175
+ }
3015
3176
  const { plan, evidenceMap } = await this.createPlan(options);
3016
- console.log(`
3017
- \u{1F680} Verelease Pipeline [Channel: ${plan.channel}, Dry-run: ${!!options.dryRun}]
3018
- `);
3019
3177
  this.printPlan(plan);
3020
3178
  if (plan.summary.bumpedPackages === 0) {
3021
- console.log("\n\u2728 No packages require release.");
3179
+ prompts2.outro(colors2.green("\u2728 No packages require release."));
3022
3180
  return;
3023
3181
  }
3024
3182
  const publishable = plan.packages.filter((p) => p.publish && p.bump !== "none");
3025
3183
  const internalBumps = plan.packages.filter((p) => !p.publish && p.bump !== "none");
3026
- const promptDescription = publishable.length > 0 ? `publishing ${publishable.map((p) => `${p.name}@${p.proposedVersion}`).join(", ")}` + (internalBumps.length > 0 ? ` and bumping ${internalBumps.length} internal package(s)` : "") : `bumping ${internalBumps.length} internal package(s)`;
3027
- const shouldPrompt = !options.dryRun && !options.yes && !process.env.CI && process.stdin.isTTY;
3028
- if (shouldPrompt) {
3029
- const confirmed = await promptConfirm(
3030
- `
3031
- ? Proceed with ${promptDescription}? [y/N] `
3032
- );
3033
- if (!confirmed) {
3034
- console.log("\n\u274C Release cancelled by user. No manifests, lockfiles, or packages were modified.\n");
3184
+ const promptDescription = publishable.length > 0 ? `publishing ${publishable.map((p) => colors2.cyan(`${p.name}@${p.proposedVersion}`)).join(", ")}` + (internalBumps.length > 0 ? ` and bumping ${internalBumps.length} internal package(s)` : "") : `bumping ${internalBumps.length} internal package(s)`;
3185
+ const isInteractive = !options.dryRun && !options.yes && !process.env.CI && process.stdin.isTTY;
3186
+ const npmPublisher = new NpmPublishProvider();
3187
+ if (publishable.length > 0 && !options.dryRun) {
3188
+ const authStatus = await npmPublisher.checkAuth();
3189
+ if (!authStatus.authenticated) {
3190
+ if (isInteractive) {
3191
+ await npmPublisher.ensureAuth({ interactive: true });
3192
+ } else {
3193
+ throw new NpmAuthError({
3194
+ message: "You are not logged in to npm. Run 'npm login' or configure NPM_TOKEN / NODE_AUTH_TOKEN in CI."
3195
+ });
3196
+ }
3197
+ }
3198
+ }
3199
+ if (isInteractive) {
3200
+ const confirmed = await prompts2.confirm({
3201
+ message: `Proceed with ${promptDescription}?`,
3202
+ initialValue: true
3203
+ });
3204
+ if (prompts2.isCancel(confirmed) || !confirmed) {
3205
+ prompts2.cancel("Release cancelled by user. No manifests, lockfiles, or packages were modified.");
3035
3206
  return;
3036
3207
  }
3037
3208
  }
3038
- const mutator = new TransactionalManifestMutator();
3209
+ if (options.dryRun) {
3210
+ prompts2.log.info(colors2.yellow("DRY RUN: Manifest mutations, changelogs, git tags, and registry publishing skipped."));
3211
+ prompts2.outro(colors2.green("Dry run completed successfully."));
3212
+ return;
3213
+ }
3039
3214
  const lockfileAdapter = new AutoLockfileAdapter();
3040
- const npmPublisher = new NpmPublishProvider();
3041
3215
  const ai = new TanStackReleaseAI();
3042
3216
  const git = new GitManager(this.cwd);
3043
- let transaction;
3044
- const changelogBackups = /* @__PURE__ */ new Map();
3045
- if (!options.dryRun) {
3046
- console.log("\n\u{1F4E6} Applying version bumps to manifests...");
3047
- transaction = await mutator.applyPlan(plan);
3048
- console.log(`\u2713 Updated ${transaction.modifiedFiles.length} manifest files`);
3049
- console.log(`
3050
- \u{1F512} Regenerating lockfile using ${lockfileAdapter.packageManager}...`);
3051
- const lockRes = await lockfileAdapter.regenerateLockfile(this.cwd);
3052
- if (!lockRes.success) {
3053
- console.error(`\u274C Lockfile regeneration failed: ${lockRes.output}`);
3054
- await transaction.rollback();
3055
- console.log("\u21A9 Rolled back manifest updates");
3056
- process.exit(1);
3057
- }
3058
- console.log("\u2713 Lockfile regenerated successfully");
3059
- console.log("\n\u{1F916} Generating changelogs & migration documentation via AI layer...");
3060
- for (const pkgPlan of plan.packages) {
3061
- if (pkgPlan.bump === "none") continue;
3062
- const evidence = evidenceMap.get(pkgPlan.name) || [];
3063
- const changelog = await ai.generateChangelog({
3064
- packageName: pkgPlan.name,
3065
- previousVersion: pkgPlan.currentVersion,
3066
- newVersion: pkgPlan.proposedVersion,
3067
- evidence
3068
- });
3069
- const changelogPath = join13(pkgPlan.directory, "CHANGELOG.md");
3070
- let existingChangelog = null;
3071
- try {
3072
- existingChangelog = await readFile7(changelogPath, "utf8");
3073
- } catch {
3074
- existingChangelog = null;
3217
+ const spin = process.stdout.isTTY ? prompts2.spinner() : void 0;
3218
+ const releaseProgram = executeReleaseTransaction(
3219
+ {
3220
+ plan,
3221
+ cwd: this.cwd,
3222
+ lockfileAdapter,
3223
+ onStep: (msg) => {
3224
+ spin?.message(msg);
3225
+ },
3226
+ onRollback: (msg) => {
3227
+ prompts2.log.warn(colors2.yellow(`\u21A9 ${msg}`));
3075
3228
  }
3076
- changelogBackups.set(changelogPath, existingChangelog);
3077
- await writeFile5(
3078
- changelogPath,
3079
- `${changelog.markdown}
3229
+ },
3230
+ (scope) => Effect3.gen(function* () {
3231
+ spin?.start("Generating changelogs & migration documentation via AI layer");
3232
+ for (const pkgPlan of plan.packages) {
3233
+ if (pkgPlan.bump === "none") continue;
3234
+ const evidence = evidenceMap.get(pkgPlan.name) || [];
3235
+ const changelog = yield* Effect3.promise(
3236
+ () => ai.generateChangelog({
3237
+ packageName: pkgPlan.name,
3238
+ previousVersion: pkgPlan.currentVersion,
3239
+ newVersion: pkgPlan.proposedVersion,
3240
+ evidence
3241
+ })
3242
+ );
3243
+ const changelogPath = join14(pkgPlan.directory, "CHANGELOG.md");
3244
+ let existingChangelog = null;
3245
+ try {
3246
+ existingChangelog = yield* Effect3.promise(() => readFile8(changelogPath, "utf8"));
3247
+ } catch {
3248
+ existingChangelog = null;
3249
+ }
3250
+ scope.backupChangelog(changelogPath, existingChangelog);
3251
+ yield* Effect3.promise(
3252
+ () => writeFile6(
3253
+ changelogPath,
3254
+ `${changelog.markdown}
3080
3255
 
3081
3256
  ${existingChangelog || ""}`.trim() + "\n",
3082
- "utf8"
3083
- );
3084
- console.log(` \u2713 Updated ${pkgPlan.name} CHANGELOG.md`);
3085
- if (pkgPlan.bump === "major") {
3086
- const breakingEvidence = evidence.filter((e) => e.severity === "major");
3087
- const guide = await ai.generateMigrationGuide({
3088
- packageName: pkgPlan.name,
3089
- previousVersion: pkgPlan.currentVersion,
3090
- newVersion: pkgPlan.proposedVersion,
3091
- breakingChanges: breakingEvidence
3092
- });
3093
- const migrationsDir = join13(pkgPlan.directory, "docs", "migrations");
3094
- await mkdir6(migrationsDir, { recursive: true });
3095
- const guidePath = join13(
3096
- migrationsDir,
3097
- `${pkgPlan.currentVersion.split(".")[0]}-to-${pkgPlan.proposedVersion.split(".")[0]}.md`
3257
+ "utf8"
3258
+ )
3098
3259
  );
3099
- await writeFile5(guidePath, guide.markdown, "utf8");
3100
- console.log(` \u2713 Generated migration guide: ${guidePath}`);
3101
- }
3102
- }
3103
- if (options.tag) {
3104
- try {
3105
- console.log("\n\u{1F3F7}\uFE0F Creating Git release commit and tags...");
3106
- await git.createReleaseCommit([], `chore(release): publish version ${plan.id}`);
3107
- for (const pkgPlan of plan.packages) {
3108
- if (pkgPlan.publish) {
3109
- const tagName = git.formatTagName({
3260
+ if (pkgPlan.bump === "major") {
3261
+ const breakingEvidence = evidence.filter((e) => e.severity === "major");
3262
+ const guide = yield* Effect3.promise(
3263
+ () => ai.generateMigrationGuide({
3110
3264
  packageName: pkgPlan.name,
3111
- version: pkgPlan.proposedVersion
3112
- });
3113
- await git.createTag(tagName);
3114
- console.log(` \u2713 Created git tag: ${tagName}`);
3115
- }
3265
+ previousVersion: pkgPlan.currentVersion,
3266
+ newVersion: pkgPlan.proposedVersion,
3267
+ breakingChanges: breakingEvidence
3268
+ })
3269
+ );
3270
+ const migrationsDir = join14(pkgPlan.directory, "docs", "migrations");
3271
+ yield* Effect3.promise(async () => {
3272
+ await mkdir6(migrationsDir, { recursive: true });
3273
+ const guidePath = join14(
3274
+ migrationsDir,
3275
+ `${pkgPlan.currentVersion.split(".")[0]}-to-${pkgPlan.proposedVersion.split(".")[0]}.md`
3276
+ );
3277
+ await writeFile6(guidePath, guide.markdown, "utf8");
3278
+ });
3116
3279
  }
3117
- } catch (err) {
3118
- console.warn(` \u26A0\uFE0F Git tagging warning: ${err.message || String(err)}`);
3119
3280
  }
3120
- }
3121
- } else {
3122
- console.log("\n[DRY RUN] Skipped manifest, lockfile, changelog mutations, and git tags.");
3123
- }
3124
- console.log("\n\u{1F4E4} Publishing packages in dependency order:");
3125
- for (const pkgName of plan.publishOrder) {
3126
- const pkgPlan = plan.packages.find((p) => p.name === pkgName);
3127
- if (!pkgPlan || !pkgPlan.publish) continue;
3128
- console.log(`
3129
- \u2192 Publishing ${pkgPlan.name}@${pkgPlan.proposedVersion}...`);
3130
- const result = await npmPublisher.publish({
3131
- packagePlan: pkgPlan,
3132
- fullPlan: plan,
3133
- dryRun: !!options.dryRun,
3134
- extraArgs: options.otp ? ["--otp", options.otp] : void 0
3135
- });
3136
- if (result.published) {
3137
- console.log(` \u2713 ${pkgPlan.name}@${pkgPlan.proposedVersion} published [tag: ${result.distTag}]`);
3138
- } else {
3139
- console.error(` \u274C Failed to publish ${pkgPlan.name}: ${result.error}`);
3140
- if (!options.dryRun) {
3141
- console.log("\n\u21A9 Rolling back manifest updates due to publish failure...");
3142
- if (transaction) {
3143
- await transaction.rollback();
3144
- console.log(" \u2713 Restored manifests to previous versions");
3145
- }
3146
- for (const [chPath, original] of changelogBackups) {
3281
+ spin?.stop("Changelogs and documentation generated");
3282
+ if (options.tag) {
3283
+ spin?.start("Creating Git release commit and tags");
3284
+ yield* Effect3.promise(async () => {
3147
3285
  try {
3148
- if (original === null) {
3149
- await unlink(chPath);
3150
- } else {
3151
- await writeFile5(chPath, original, "utf8");
3286
+ await git.createReleaseCommit([], `chore(release): publish version ${plan.id}`);
3287
+ for (const pkgPlan of plan.packages) {
3288
+ if (pkgPlan.publish) {
3289
+ const tagName = git.formatTagName({
3290
+ packageName: pkgPlan.name,
3291
+ version: pkgPlan.proposedVersion
3292
+ });
3293
+ await git.createTag(tagName);
3294
+ }
3152
3295
  }
3153
- } catch {
3296
+ } catch (err) {
3297
+ prompts2.log.warn(`Git tagging warning: ${err.message || String(err)}`);
3154
3298
  }
3299
+ });
3300
+ spin?.stop("Git commit and tags created");
3301
+ }
3302
+ spin?.start("Publishing packages to npm registry");
3303
+ for (const pkgName of plan.publishOrder) {
3304
+ const pkgPlan = plan.packages.find((p) => p.name === pkgName);
3305
+ if (!pkgPlan || !pkgPlan.publish) continue;
3306
+ spin?.message(`Publishing ${colors2.bold(pkgPlan.name)}@${pkgPlan.proposedVersion}`);
3307
+ const result = yield* Effect3.promise(
3308
+ () => npmPublisher.publish({
3309
+ packagePlan: pkgPlan,
3310
+ fullPlan: plan,
3311
+ dryRun: !!options.dryRun,
3312
+ extraArgs: options.otp ? ["--otp", options.otp] : void 0
3313
+ })
3314
+ );
3315
+ if (!result.published) {
3316
+ return yield* Effect3.fail(
3317
+ new PublishError({
3318
+ packageName: pkgPlan.name,
3319
+ message: result.error || "npm publish failed"
3320
+ })
3321
+ );
3155
3322
  }
3156
- console.log(" \u2713 Restored changelog files");
3157
- await lockfileAdapter.regenerateLockfile(this.cwd);
3158
- console.log(" \u2713 Synchronized lockfile\n");
3159
- process.exit(1);
3323
+ prompts2.log.success(
3324
+ colors2.green(
3325
+ `Published ${colors2.bold(pkgPlan.name)}@${pkgPlan.proposedVersion} [tag: ${colors2.cyan(result.distTag)}]`
3326
+ )
3327
+ );
3160
3328
  }
3161
- }
3329
+ spin?.stop("All packages published successfully");
3330
+ })
3331
+ );
3332
+ const exit = await Effect3.runPromiseExit(releaseProgram);
3333
+ if (Exit2.isFailure(exit)) {
3334
+ prompts2.log.error(
3335
+ colors2.red("Publish failed. Transactional rollback has restored all files to their clean state.")
3336
+ );
3337
+ process.exit(1);
3162
3338
  }
3163
- console.log("\n\u{1F389} Release completed successfully!");
3339
+ prompts2.outro(colors2.green(colors2.bold("\u{1F389} Release completed successfully!")));
3164
3340
  }
3165
3341
  async runExplainCommand(packageName) {
3342
+ prompts2.intro(
3343
+ colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(` explanation for ${colors2.bold(packageName)}`)
3344
+ );
3166
3345
  const { plan, evidenceMap } = await this.createPlan();
3167
3346
  const pkgPlan = plan.packages.find((p) => p.name === packageName);
3168
3347
  if (!pkgPlan) {
3169
- console.error(`Package "${packageName}" not found in workspace.`);
3348
+ prompts2.log.error(`Package "${packageName}" not found in workspace.`);
3170
3349
  return;
3171
3350
  }
3172
3351
  const evidence = evidenceMap.get(packageName) || [];
3173
- console.log(`
3174
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
3175
- console.log(` EXPLANATION FOR ${packageName}`);
3176
- console.log(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
3177
- `);
3178
- console.log(`Current Version: ${pkgPlan.currentVersion}`);
3179
- console.log(`Proposed Version: ${pkgPlan.proposedVersion} [${pkgPlan.bump.toUpperCase()}]`);
3180
- console.log(`Confidence: ${(pkgPlan.confidence * 100).toFixed(0)}%`);
3181
- console.log(`Requires Review: ${pkgPlan.requiresApproval ? "YES" : "NO"}
3182
- `);
3183
- console.log(`Evidence items (${evidence.length}):`);
3352
+ const badge = pkgPlan.bump === "major" ? colors2.red("\u{1F534} MAJOR") : pkgPlan.bump === "minor" ? colors2.yellow("\u{1F7E1} MINOR") : pkgPlan.bump === "patch" ? colors2.green("\u{1F7E2} PATCH") : colors2.dim("\u26AA NONE");
3353
+ const lines = [
3354
+ `${colors2.bold("Current Version:")} ${pkgPlan.currentVersion}`,
3355
+ `${colors2.bold("Proposed Version:")} ${colors2.cyan(colors2.bold(pkgPlan.proposedVersion))} [${badge}]`,
3356
+ `${colors2.bold("Confidence:")} ${(pkgPlan.confidence * 100).toFixed(0)}%`,
3357
+ `${colors2.bold("Requires Review:")} ${pkgPlan.requiresApproval ? colors2.yellow("YES") : colors2.green("NO")}`,
3358
+ "",
3359
+ `${colors2.bold(`Evidence Items (${evidence.length}):`)}`
3360
+ ];
3184
3361
  for (const ev of evidence) {
3185
- console.log(` \u2022 [${ev.source.toUpperCase()}] (${ev.severity.toUpperCase()}) ${ev.title}`);
3186
- console.log(` ${ev.description}
3187
- `);
3362
+ lines.push(` \u2022 [${colors2.cyan(ev.source.toUpperCase())}] (${colors2.bold(ev.severity.toUpperCase())}) ${ev.title}`);
3363
+ lines.push(` ${colors2.dim(ev.description)}`);
3188
3364
  }
3189
3365
  if (pkgPlan.reasons.length > 0) {
3190
- console.log("Decision reasons:");
3366
+ lines.push("");
3367
+ lines.push(colors2.bold("Decision Reasons:"));
3191
3368
  for (const reason of pkgPlan.reasons) {
3192
- console.log(` ${reason}`);
3369
+ lines.push(` \u2022 ${reason}`);
3193
3370
  }
3194
3371
  }
3372
+ prompts2.note(lines.join("\n"), `Evidence Breakdown`);
3373
+ prompts2.outro("Explanation complete.");
3195
3374
  }
3196
3375
  async runGuideCommand(options = {}) {
3197
3376
  if (options.json) {
@@ -3206,11 +3385,11 @@ ${existingChangelog || ""}`.trim() + "\n",
3206
3385
  "Pass --yes to bypass interactive prompts in CI"
3207
3386
  ],
3208
3387
  commands: {
3209
- plan: "verel plan [--channel <channel>] [--json]",
3210
- release: "verel release [--channel <channel>] [-y, --yes]",
3211
- explain: "verel explain <pkg>",
3212
- guide: "verel guide [--json]",
3213
- init: "verel init"
3388
+ plan: "verelease plan [--channel <channel>] [--json]",
3389
+ release: "verelease release [--channel <channel>] [-y, --yes]",
3390
+ explain: "verelease explain <pkg>",
3391
+ guide: "verelease guide [--json]",
3392
+ init: "verelease init"
3214
3393
  },
3215
3394
  configuration: "verelease.config.ts",
3216
3395
  contractsDirectory: ".verelease/contracts",
@@ -3222,37 +3401,32 @@ ${existingChangelog || ""}`.trim() + "\n",
3222
3401
  );
3223
3402
  return;
3224
3403
  }
3225
- console.log(`
3226
- # Verelease Agent Guide
3227
-
3228
- Verelease is an evidence-based SemVer release engine for JavaScript & TypeScript.
3229
-
3230
- ## Core Rules for AI Coding Agents
3231
- 1. **NEVER manually edit "version" in package.json**: Always let Verelease determine versions.
3232
- 2. **Inspect first**: Run \`pnpm verel plan\` to preview proposed version bumps without modifying files.
3233
- 3. **Diagnose bumps**: Run \`pnpm verel explain <pkg>\` to understand the evidence behind any bump.
3234
- 4. **Automated releases**: In CI or automated workflows, run \`pnpm verel release --yes\` to bypass prompts.
3235
- 5. **Interactive releases**: When run locally, Verelease asks for [y/N] confirmation and supports Touch ID / WebAuthn.
3236
-
3237
- ## Command Reference
3238
- - \`pnpm verel plan\` Preview release plan for stable channel
3239
- - \`pnpm verel plan --channel alpha\` Preview pre-release bumps for alpha channel
3240
- - \`pnpm verel release\` Interactive release pipeline
3241
- - \`pnpm verel release -y\` Non-interactive release (for agents/CI)
3242
- - \`pnpm verel explain <package>\` Inspect compatibility evidence and decision reasons
3243
- - \`pnpm verel init\` Initialize config, contracts, and AGENTS.md
3244
-
3245
- ## Configuration
3246
- Config is loaded from \`verelease.config.ts\` or \`verelease.config.js\`.
3247
- Contract test suites live in \`.verelease/contracts/\`.
3248
- `);
3404
+ prompts2.intro(colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" agent guide"));
3405
+ const content = [
3406
+ colors2.bold("Core Rules for AI Coding Agents:"),
3407
+ `1. ${colors2.bold("NEVER manually edit 'version' in package.json")}: Always let Verelease determine versions.`,
3408
+ `2. ${colors2.bold("Inspect first")}: Run ${colors2.cyan("verelease plan")} to preview proposed version bumps.`,
3409
+ `3. ${colors2.bold("Diagnose bumps")}: Run ${colors2.cyan("verelease explain <pkg>")} to inspect compatibility evidence.`,
3410
+ `4. ${colors2.bold("Automated releases")}: In CI or agents, run ${colors2.cyan("verelease release --yes")} to bypass prompts.`,
3411
+ `5. ${colors2.bold("Interactive releases")}: Locally, Verelease prompts [y/N] and supports WebAuthn / Touch ID.`,
3412
+ "",
3413
+ colors2.bold("Command Reference:"),
3414
+ `\u2022 ${colors2.cyan("verelease plan")} Preview release plan for stable channel`,
3415
+ `\u2022 ${colors2.cyan("verelease plan --channel alpha")} Preview pre-release bumps for alpha channel`,
3416
+ `\u2022 ${colors2.cyan("verelease release")} Interactive release pipeline`,
3417
+ `\u2022 ${colors2.cyan("verelease release -y")} Non-interactive release (for agents/CI)`,
3418
+ `\u2022 ${colors2.cyan("verelease explain <package>")} Inspect evidence and decision reasons`,
3419
+ `\u2022 ${colors2.cyan("verelease init")} Initialize config, contracts, and AGENTS.md`
3420
+ ].join("\n");
3421
+ prompts2.note(content, "Agent Guide & Workflow");
3422
+ prompts2.outro("Guide complete.");
3249
3423
  }
3250
3424
  async runInitCommand(options = {}) {
3251
- const configPath = join13(this.cwd, "verelease.config.ts");
3252
- const contractsDir = join13(this.cwd, ".verelease", "contracts");
3253
- const agentsPath = join13(this.cwd, "AGENTS.md");
3254
- const pkgJsonPath = join13(this.cwd, "package.json");
3255
- console.log("\n\u{1F680} Initializing Verelease in current project...\n");
3425
+ const configPath = join14(this.cwd, "verelease.config.ts");
3426
+ const contractsDir = join14(this.cwd, ".verelease", "contracts");
3427
+ const agentsPath = join14(this.cwd, "AGENTS.md");
3428
+ const pkgJsonPath = join14(this.cwd, "package.json");
3429
+ prompts2.intro(colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" project initialization"));
3256
3430
  if (!existsSync(configPath)) {
3257
3431
  const sampleConfig = `import { defineConfig } from "verelease/config";
3258
3432
 
@@ -3275,21 +3449,21 @@ export default defineConfig({
3275
3449
  },
3276
3450
  });
3277
3451
  `;
3278
- await writeFile5(configPath, sampleConfig, "utf8");
3279
- console.log(" \u2713 Created verelease.config.ts");
3452
+ await writeFile6(configPath, sampleConfig, "utf8");
3453
+ prompts2.log.success(colors2.green("Created verelease.config.ts"));
3280
3454
  } else {
3281
- console.log(" \u2022 verelease.config.ts already exists");
3455
+ prompts2.log.info("verelease.config.ts already exists");
3282
3456
  }
3283
3457
  if (!existsSync(contractsDir)) {
3284
3458
  await mkdir6(contractsDir, { recursive: true });
3285
- await writeFile5(
3286
- join13(contractsDir, "README.md"),
3459
+ await writeFile6(
3460
+ join14(contractsDir, "README.md"),
3287
3461
  "# Verelease Contracts\n\nPlace consumer-driven contract definitions (.json or .yaml) here.\n",
3288
3462
  "utf8"
3289
3463
  );
3290
- console.log(" \u2713 Created .verelease/contracts/");
3464
+ prompts2.log.success(colors2.green("Created .verelease/contracts/"));
3291
3465
  } else {
3292
- console.log(" \u2022 .verelease/contracts/ already exists");
3466
+ prompts2.log.info(".verelease/contracts/ already exists");
3293
3467
  }
3294
3468
  const agentGuidance = `
3295
3469
  ## Releases & Versioning (Verelease)
@@ -3301,24 +3475,24 @@ This repository uses [Verelease](https://github.com/ivanjeremic/verelease) for a
3301
3475
  - **NEVER manually edit "version" in package.json.**
3302
3476
  `;
3303
3477
  if (!existsSync(agentsPath)) {
3304
- await writeFile5(agentsPath, `# Agent Guidelines
3478
+ await writeFile6(agentsPath, `# Agent Guidelines
3305
3479
  ${agentGuidance}
3306
3480
  `, "utf8");
3307
- console.log(" \u2713 Created AGENTS.md with Verelease instructions");
3481
+ prompts2.log.success(colors2.green("Created AGENTS.md with Verelease instructions"));
3308
3482
  } else {
3309
- const currentContent = await readFile7(agentsPath, "utf8");
3483
+ const currentContent = await readFile8(agentsPath, "utf8");
3310
3484
  if (!currentContent.includes("Verelease")) {
3311
- await writeFile5(agentsPath, `${currentContent.trim()}
3485
+ await writeFile6(agentsPath, `${currentContent.trim()}
3312
3486
  ${agentGuidance}
3313
3487
  `, "utf8");
3314
- console.log(" \u2713 Appended Verelease instructions to AGENTS.md");
3488
+ prompts2.log.success(colors2.green("Appended Verelease instructions to AGENTS.md"));
3315
3489
  } else {
3316
- console.log(" \u2022 AGENTS.md already contains Verelease instructions");
3490
+ prompts2.log.info("AGENTS.md already contains Verelease instructions");
3317
3491
  }
3318
3492
  }
3319
3493
  if (existsSync(pkgJsonPath)) {
3320
3494
  try {
3321
- const pkg = JSON.parse(await readFile7(pkgJsonPath, "utf8"));
3495
+ const pkg = JSON.parse(await readFile8(pkgJsonPath, "utf8"));
3322
3496
  pkg.scripts = pkg.scripts || {};
3323
3497
  let modified = false;
3324
3498
  if (!pkg.scripts.plan) {
@@ -3330,127 +3504,219 @@ ${agentGuidance}
3330
3504
  modified = true;
3331
3505
  }
3332
3506
  if (modified) {
3333
- await writeFile5(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
3334
- console.log(" \u2713 Added 'plan' and 'release' scripts to package.json");
3507
+ await writeFile6(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
3508
+ prompts2.log.success(colors2.green("Added 'plan' and 'release' scripts to package.json"));
3335
3509
  }
3336
3510
  } catch {
3337
3511
  }
3338
3512
  }
3339
- console.log("\n\u2728 Verelease successfully initialized!\n");
3513
+ prompts2.outro(colors2.green(colors2.bold("\u2728 Verelease successfully initialized!")));
3340
3514
  }
3341
3515
  printPlan(plan) {
3342
- console.log(`
3343
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
3344
- console.log(` VERELEASE RELEASE PLAN`);
3345
- console.log(` Channel: ${plan.channel} | Packages: ${plan.summary.totalPackages} | Bumps: ${plan.summary.bumpedPackages}`);
3346
- console.log(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
3347
- `);
3516
+ const lines = [];
3348
3517
  for (const pkg of plan.packages) {
3349
- const bumpColor = pkg.bump === "major" ? "\u{1F534} MAJOR" : pkg.bump === "minor" ? "\u{1F7E1} MINOR" : pkg.bump === "patch" ? "\u{1F7E2} PATCH" : "\u26AA NONE";
3350
- console.log(`${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.proposedVersion} [${bumpColor}]`);
3351
- console.log(` Confidence: ${(pkg.confidence * 100).toFixed(0)}% | Publish: ${pkg.publish ? "YES" : "NO"}`);
3518
+ const badge = pkg.bump === "major" ? colors2.red("\u{1F534} MAJOR") : pkg.bump === "minor" ? colors2.yellow("\u{1F7E1} MINOR") : pkg.bump === "patch" ? colors2.green("\u{1F7E2} PATCH") : colors2.dim("\u26AA NONE");
3519
+ const pubTag = pkg.publish ? colors2.cyan(" [npm publish]") : colors2.dim(" [internal]");
3520
+ lines.push(
3521
+ `${badge} ${colors2.bold(pkg.name)} ${colors2.dim(pkg.currentVersion)} \u2192 ${colors2.cyan(colors2.bold(pkg.proposedVersion))}${pubTag}`
3522
+ );
3352
3523
  if (pkg.reasons.length > 0) {
3353
- console.log(" Reasons:");
3354
- for (const reason of pkg.reasons) {
3355
- console.log(` ${reason}`);
3524
+ for (const reason of pkg.reasons.slice(0, 2)) {
3525
+ lines.push(` ${colors2.dim("\u2022")} ${colors2.dim(reason)}`);
3356
3526
  }
3357
3527
  }
3358
3528
  if (pkg.dependentUpdates.length > 0) {
3359
- console.log(" Dependent Updates:");
3360
- for (const dep of pkg.dependentUpdates) {
3361
- console.log(` ${dep.dependentName} depends on ${dep.dependencyName} (${dep.previousRange} \u2192 ${dep.newRange})`);
3529
+ for (const dep of pkg.dependentUpdates.slice(0, 2)) {
3530
+ lines.push(
3531
+ ` ${colors2.dim("\u21B3")} ${colors2.dim(`${dep.dependentName} depends on ${dep.dependencyName} (${dep.previousRange} \u2192 ${dep.newRange})`)}`
3532
+ );
3362
3533
  }
3363
3534
  }
3364
- console.log("");
3535
+ lines.push("");
3365
3536
  }
3537
+ const summaryLine = [
3538
+ `${colors2.bold(plan.summary.totalPackages)} packages`,
3539
+ `${colors2.green(colors2.bold(plan.summary.bumpedPackages))} bumps`,
3540
+ `(${colors2.green(`${plan.summary.patchBumps} patch`)}, ${colors2.yellow(`${plan.summary.minorBumps} minor`)}, ${colors2.red(`${plan.summary.majorBumps} major`)})`,
3541
+ `Channel: ${colors2.cyan(plan.channel)}`
3542
+ ].join(" | ");
3543
+ lines.push(summaryLine);
3544
+ prompts2.note(lines.join("\n"), `Release Plan Summary`);
3366
3545
  }
3367
3546
  };
3368
3547
 
3369
3548
  // src/bin.ts
3370
- async function main() {
3371
- const args = process.argv.slice(2);
3372
- const command = args[0] || "plan";
3373
- if (args.includes("--help") || args.includes("-h") || command === "help") {
3374
- console.log(`
3375
- Usage: verelease [command] [options]
3376
-
3377
- Commands:
3378
- plan Preview the release plan without making changes (default)
3379
- release Apply version bumps, generate changelogs, and publish
3380
- explain <pkg> Explain compatibility evidence and bump decision for a package
3381
- guide Show agent-optimized cheat sheet and machine-readable instructions
3382
- init Initialize verelease.config.ts, contracts, and AGENTS.md
3383
-
3384
- Options:
3385
- --channel <name> Release channel (e.g. stable, alpha, beta, rc) [default: stable]
3386
- --bump <type> Manual bump override (patch | minor | major)
3387
- --dry-run Simulate pipeline without modifying manifests or publishing
3388
- -y, --yes Skip interactive confirmation prompt
3389
- --otp <code> One-time password for npm 2FA
3390
- --tag Create git commit and tags for release
3391
- --json Output machine-readable JSON
3392
- -h, --help Show this help message
3393
- `);
3394
- return;
3549
+ var __dirname2 = dirname(fileURLToPath(import.meta.url));
3550
+ async function getCliVersion() {
3551
+ try {
3552
+ const pkgJsonPath = join15(__dirname2, "../package.json");
3553
+ const content = await readFile9(pkgJsonPath, "utf8");
3554
+ const parsed = JSON.parse(content);
3555
+ return parsed.version || "0.1.0";
3556
+ } catch {
3557
+ return "0.1.0";
3395
3558
  }
3396
- const options = {
3397
- dryRun: args.includes("--dry-run"),
3398
- json: args.includes("--json"),
3399
- tag: args.includes("--tag"),
3400
- yes: args.includes("--yes") || args.includes("-y")
3559
+ }
3560
+ var commonArgs = {
3561
+ channel: {
3562
+ type: "string",
3563
+ description: "Release channel (e.g. stable, alpha, beta, rc)",
3564
+ default: "stable"
3565
+ },
3566
+ bump: {
3567
+ type: "string",
3568
+ description: "Manual bump override (patch | minor | major)"
3569
+ },
3570
+ dryRun: {
3571
+ type: "boolean",
3572
+ description: "Simulate pipeline without modifying manifests or publishing"
3573
+ },
3574
+ yes: {
3575
+ type: "boolean",
3576
+ alias: "y",
3577
+ description: "Skip interactive confirmation prompt"
3578
+ },
3579
+ json: {
3580
+ type: "boolean",
3581
+ description: "Output machine-readable JSON"
3582
+ },
3583
+ tag: {
3584
+ type: "boolean",
3585
+ description: "Create git commit and tags for release"
3586
+ },
3587
+ otp: {
3588
+ type: "string",
3589
+ description: "One-time password for npm 2FA"
3590
+ },
3591
+ cwd: {
3592
+ type: "string",
3593
+ description: "Working directory path"
3594
+ }
3595
+ };
3596
+ function toCliOptions(args) {
3597
+ return {
3598
+ channel: args.channel,
3599
+ defaultBump: args.bump,
3600
+ dryRun: Boolean(args.dryRun),
3601
+ yes: Boolean(args.yes),
3602
+ json: Boolean(args.json),
3603
+ tag: Boolean(args.tag),
3604
+ otp: args.otp,
3605
+ cwd: args.cwd
3401
3606
  };
3402
- const channelIdx = args.indexOf("--channel");
3403
- if (channelIdx !== -1 && args[channelIdx + 1]) {
3404
- options.channel = args[channelIdx + 1];
3607
+ }
3608
+ var subcommandExecuted = false;
3609
+ var planCommand = defineCommand({
3610
+ meta: {
3611
+ name: "plan",
3612
+ description: "Preview the release plan without making changes"
3613
+ },
3614
+ args: commonArgs,
3615
+ async run({ args }) {
3616
+ subcommandExecuted = true;
3617
+ const opts = toCliOptions(args);
3618
+ const cli = new VereleaseCli(opts);
3619
+ await cli.runPlanCommand(opts);
3405
3620
  }
3406
- const bumpIdx = args.indexOf("--bump");
3407
- if (bumpIdx !== -1 && args[bumpIdx + 1]) {
3408
- options.defaultBump = args[bumpIdx + 1];
3621
+ });
3622
+ var releaseCommand = defineCommand({
3623
+ meta: {
3624
+ name: "release",
3625
+ description: "Apply version bumps, generate changelogs, and publish"
3626
+ },
3627
+ args: commonArgs,
3628
+ async run({ args }) {
3629
+ subcommandExecuted = true;
3630
+ const opts = toCliOptions(args);
3631
+ const cli = new VereleaseCli(opts);
3632
+ await cli.runReleaseCommand(opts);
3409
3633
  }
3410
- const cwdIdx = args.indexOf("--cwd");
3411
- if (cwdIdx !== -1 && args[cwdIdx + 1]) {
3412
- options.cwd = args[cwdIdx + 1];
3634
+ });
3635
+ var explainCommand = defineCommand({
3636
+ meta: {
3637
+ name: "explain",
3638
+ description: "Explain compatibility evidence and bump decision for a package"
3639
+ },
3640
+ args: {
3641
+ package: {
3642
+ type: "positional",
3643
+ description: "Package name to explain (e.g. @verelease/core)",
3644
+ required: false
3645
+ },
3646
+ ...commonArgs
3647
+ },
3648
+ async run({ args }) {
3649
+ subcommandExecuted = true;
3650
+ const opts = toCliOptions(args);
3651
+ const cli = new VereleaseCli(opts);
3652
+ const targetPkg = args.package || args._ && args._[0];
3653
+ if (!targetPkg) {
3654
+ console.error("\n\u274C Please specify a package name to explain, e.g.: verelease explain @verelease/core\n");
3655
+ process.exit(1);
3656
+ }
3657
+ await cli.runExplainCommand(targetPkg);
3658
+ }
3659
+ });
3660
+ var guideCommand = defineCommand({
3661
+ meta: {
3662
+ name: "guide",
3663
+ description: "Show agent-optimized cheat sheet and machine-readable instructions"
3664
+ },
3665
+ args: {
3666
+ json: {
3667
+ type: "boolean",
3668
+ description: "Output machine-readable JSON"
3669
+ },
3670
+ cwd: {
3671
+ type: "string",
3672
+ description: "Working directory path"
3673
+ }
3674
+ },
3675
+ async run({ args }) {
3676
+ subcommandExecuted = true;
3677
+ const cli = new VereleaseCli({ cwd: args.cwd });
3678
+ await cli.runGuideCommand({ json: Boolean(args.json) });
3413
3679
  }
3414
- const otpIdx = args.indexOf("--otp");
3415
- if (otpIdx !== -1 && args[otpIdx + 1]) {
3416
- options.otp = args[otpIdx + 1];
3680
+ });
3681
+ var initCommand = defineCommand({
3682
+ meta: {
3683
+ name: "init",
3684
+ description: "Initialize verelease.config.ts, contracts, and AGENTS.md"
3685
+ },
3686
+ args: {
3687
+ cwd: {
3688
+ type: "string",
3689
+ description: "Working directory path"
3690
+ }
3691
+ },
3692
+ async run({ args }) {
3693
+ subcommandExecuted = true;
3694
+ const cli = new VereleaseCli({ cwd: args.cwd });
3695
+ await cli.runInitCommand({ cwd: args.cwd });
3417
3696
  }
3418
- const cli = new VereleaseCli(options);
3419
- try {
3420
- switch (command) {
3421
- case "plan":
3422
- await cli.runPlanCommand(options);
3423
- break;
3424
- case "release":
3425
- await cli.runReleaseCommand(options);
3426
- break;
3427
- case "version":
3428
- await cli.runReleaseCommand({ ...options, dryRun: false });
3429
- break;
3430
- case "guide":
3431
- await cli.runGuideCommand(options);
3432
- break;
3433
- case "init":
3434
- await cli.runInitCommand(options);
3435
- break;
3436
- case "explain":
3437
- const targetPkg = args[1];
3438
- if (!targetPkg) {
3439
- console.error("Please specify a package name to explain, e.g.: verel explain @acme/core");
3440
- process.exit(1);
3441
- }
3442
- await cli.runExplainCommand(targetPkg);
3443
- break;
3444
- default:
3445
- console.log(`Unknown command: ${command}`);
3446
- console.log(`Run 'verelease --help' for available commands and options.`);
3447
- process.exit(1);
3448
- }
3449
- } catch (err) {
3450
- console.error(`
3451
- \u274C Error: ${err.message || String(err)}`);
3452
- process.exit(1);
3697
+ });
3698
+ var version = await getCliVersion();
3699
+ var mainCommand = defineCommand({
3700
+ meta: {
3701
+ name: "verelease",
3702
+ version,
3703
+ description: "Evidence-based semantic versioning release engine"
3704
+ },
3705
+ args: commonArgs,
3706
+ subCommands: {
3707
+ plan: planCommand,
3708
+ release: releaseCommand,
3709
+ version: releaseCommand,
3710
+ explain: explainCommand,
3711
+ guide: guideCommand,
3712
+ init: initCommand
3713
+ },
3714
+ async run({ args }) {
3715
+ if (subcommandExecuted) return;
3716
+ const opts = toCliOptions(args);
3717
+ const cli = new VereleaseCli(opts);
3718
+ await cli.runPlanCommand(opts);
3453
3719
  }
3454
- }
3455
- main();
3720
+ });
3721
+ void runMain(mainCommand);
3456
3722
  //# sourceMappingURL=bin.js.map