@cleocode/cleo 2026.3.17 → 2026.3.19

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/cli/index.js CHANGED
@@ -1803,6 +1803,16 @@ var init_registry = __esm({
1803
1803
  sessionRequired: false,
1804
1804
  requiredParams: ["version"]
1805
1805
  },
1806
+ {
1807
+ gateway: "query",
1808
+ domain: "pipeline",
1809
+ operation: "release.channel.show",
1810
+ description: "Show the current release channel based on git branch (latest/beta/alpha)",
1811
+ tier: 0,
1812
+ idempotent: true,
1813
+ sessionRequired: false,
1814
+ requiredParams: []
1815
+ },
1806
1816
  {
1807
1817
  gateway: "mutate",
1808
1818
  domain: "pipeline",
@@ -1863,6 +1873,16 @@ var init_registry = __esm({
1863
1873
  sessionRequired: false,
1864
1874
  requiredParams: []
1865
1875
  },
1876
+ {
1877
+ gateway: "mutate",
1878
+ domain: "pipeline",
1879
+ operation: "release.cancel",
1880
+ description: "pipeline.release.cancel (mutate)",
1881
+ tier: 0,
1882
+ idempotent: false,
1883
+ sessionRequired: false,
1884
+ requiredParams: ["version"]
1885
+ },
1866
1886
  {
1867
1887
  gateway: "mutate",
1868
1888
  domain: "pipeline",
@@ -19690,10 +19710,10 @@ async function systemLog(projectRoot, filters) {
19690
19710
  }
19691
19711
  async function queryAuditLogSqlite(projectRoot, filters) {
19692
19712
  try {
19693
- const { join: join77 } = await import("node:path");
19694
- const { existsSync: existsSync72 } = await import("node:fs");
19695
- const dbPath = join77(projectRoot, ".cleo", "tasks.db");
19696
- if (!existsSync72(dbPath)) {
19713
+ const { join: join78 } = await import("node:path");
19714
+ const { existsSync: existsSync73 } = await import("node:fs");
19715
+ const dbPath = join78(projectRoot, ".cleo", "tasks.db");
19716
+ if (!existsSync73(dbPath)) {
19697
19717
  const offset = filters?.offset ?? 0;
19698
19718
  const limit = filters?.limit ?? 20;
19699
19719
  return {
@@ -20549,10 +20569,10 @@ async function readProjectMeta(projectPath) {
20549
20569
  }
20550
20570
  async function readProjectId(projectPath) {
20551
20571
  try {
20552
- const { readFileSync: readFileSync53, existsSync: existsSync72 } = await import("node:fs");
20572
+ const { readFileSync: readFileSync54, existsSync: existsSync73 } = await import("node:fs");
20553
20573
  const infoPath = join33(projectPath, ".cleo", "project-info.json");
20554
- if (!existsSync72(infoPath)) return "";
20555
- const data = JSON.parse(readFileSync53(infoPath, "utf-8"));
20574
+ if (!existsSync73(infoPath)) return "";
20575
+ const data = JSON.parse(readFileSync54(infoPath, "utf-8"));
20556
20576
  return typeof data.projectId === "string" ? data.projectId : "";
20557
20577
  } catch {
20558
20578
  return "";
@@ -28477,7 +28497,8 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
28477
28497
  }
28478
28498
  function buildEntry(task) {
28479
28499
  const cleanTitle = capitalize(stripConventionalPrefix(task.title));
28480
- const desc6 = task.description?.trim();
28500
+ const safeDesc = task.description?.replace(/\r?\n/g, " ").replace(/\s{2,}/g, " ").trim();
28501
+ const desc6 = safeDesc;
28481
28502
  const shouldIncludeDesc = (() => {
28482
28503
  if (!desc6 || desc6.length === 0) return false;
28483
28504
  const titleNorm = cleanTitle.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim();
@@ -28494,23 +28515,29 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
28494
28515
  }
28495
28516
  function categorizeTask(task) {
28496
28517
  if (task.type === "epic") return "changes";
28497
- const labels = task.labels ?? [];
28498
- const titleLower = stripConventionalPrefix(task.title).toLowerCase();
28499
- const rawTitleLower = task.title.toLowerCase();
28518
+ const taskType = (task.type ?? "").toLowerCase();
28519
+ if (taskType === "test") return "tests";
28520
+ if (taskType === "fix" || taskType === "bugfix") return "fixes";
28521
+ if (taskType === "feat" || taskType === "feature") return "features";
28522
+ if (taskType === "docs" || taskType === "doc") return "docs";
28523
+ if (taskType === "chore" || taskType === "refactor") return "chores";
28500
28524
  if (/^feat(\([^)]+\))?:/.test(task.title.toLowerCase())) return "features";
28501
28525
  if (/^fix(\([^)]+\))?:/.test(task.title.toLowerCase())) return "fixes";
28502
28526
  if (/^docs?(\([^)]+\))?:/.test(task.title.toLowerCase())) return "docs";
28503
28527
  if (/^test(\([^)]+\))?:/.test(task.title.toLowerCase())) return "tests";
28504
28528
  if (/^(chore|refactor|style|ci|build|perf)(\([^)]+\))?:/.test(task.title.toLowerCase())) return "chores";
28505
- if (labels.some((l) => ["feat", "feature", "enhancement", "add"].includes(l.toLowerCase()))) return "features";
28529
+ const labels = task.labels ?? [];
28530
+ if (labels.some((l) => ["test", "testing"].includes(l.toLowerCase()))) return "tests";
28506
28531
  if (labels.some((l) => ["fix", "bug", "bugfix", "regression"].includes(l.toLowerCase()))) return "fixes";
28532
+ if (labels.some((l) => ["feat", "feature", "enhancement", "add"].includes(l.toLowerCase()))) return "features";
28507
28533
  if (labels.some((l) => ["docs", "documentation"].includes(l.toLowerCase()))) return "docs";
28508
- if (labels.some((l) => ["test", "testing"].includes(l.toLowerCase()))) return "tests";
28509
28534
  if (labels.some((l) => ["chore", "refactor", "cleanup", "maintenance"].includes(l.toLowerCase()))) return "chores";
28510
- if (titleLower.startsWith("add ") || titleLower.includes("implement") || titleLower.startsWith("create ") || titleLower.startsWith("introduce ")) return "features";
28535
+ const titleLower = stripConventionalPrefix(task.title).toLowerCase();
28536
+ const rawTitleLower = task.title.toLowerCase();
28537
+ if (titleLower.startsWith("test") || titleLower.includes("test") && titleLower.includes("add")) return "tests";
28511
28538
  if (titleLower.includes("bug") || titleLower.startsWith("fix") || titleLower.includes("regression") || titleLower.includes("broken")) return "fixes";
28539
+ if (titleLower.startsWith("add ") || titleLower.includes("implement") || titleLower.startsWith("create ") || titleLower.startsWith("introduce ")) return "features";
28512
28540
  if (titleLower.startsWith("doc") || titleLower.includes("documentation") || titleLower.includes("readme") || titleLower.includes("changelog")) return "docs";
28513
- if (titleLower.startsWith("test") || titleLower.includes("test") && titleLower.includes("add")) return "tests";
28514
28541
  if (titleLower.startsWith("chore") || titleLower.includes("refactor") || titleLower.includes("cleanup") || titleLower.includes("migrate") || titleLower.includes("upgrade") || titleLower.includes("remove ") || titleLower.startsWith("audit")) return "chores";
28515
28542
  if (rawTitleLower.startsWith("feat")) return "features";
28516
28543
  return "changes";
@@ -28521,6 +28548,10 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
28521
28548
  if (task.type === "epic") continue;
28522
28549
  if (task.labels?.some((l) => l.toLowerCase() === "epic")) continue;
28523
28550
  if (/^epic:/i.test(task.title.trim())) continue;
28551
+ const labelsLower = (task.labels ?? []).map((l) => l.toLowerCase());
28552
+ if (labelsLower.some((l) => ["research", "internal", "spike", "audit"].includes(l))) continue;
28553
+ if (["spike", "research"].includes((task.type ?? "").toLowerCase())) continue;
28554
+ if (/^(research|investigate|audit|spike)\s/i.test(task.title.trim())) continue;
28524
28555
  const category = categorizeTask(task);
28525
28556
  const entry = buildEntry(task);
28526
28557
  if (category === "features") features.push(entry);
@@ -28578,7 +28609,7 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
28578
28609
  }
28579
28610
  const { customBlocks } = parseChangelogBlocks(existingChangelogContent);
28580
28611
  const changelogBody = sections.slice(2).join("\n");
28581
- await writeChangelogSection(normalizedVersion, changelogBody, customBlocks, changelogPath);
28612
+ await writeChangelogSection(normalizedVersion.replace(/^v/, ""), changelogBody, customBlocks, changelogPath);
28582
28613
  return {
28583
28614
  version: normalizedVersion,
28584
28615
  changelog,
@@ -28651,7 +28682,7 @@ async function tagRelease(version, cwd) {
28651
28682
  await db.update(releaseManifests).set({ status: "tagged", taggedAt }).where(eq14(releaseManifests.version, normalizedVersion)).run();
28652
28683
  return { version: normalizedVersion, status: "tagged", taggedAt };
28653
28684
  }
28654
- async function runReleaseGates(version, loadTasksFn, cwd) {
28685
+ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
28655
28686
  if (!version) {
28656
28687
  throw new Error("version is required");
28657
28688
  }
@@ -28699,23 +28730,31 @@ async function runReleaseGates(version, loadTasksFn, cwd) {
28699
28730
  message: existsSync49(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
28700
28731
  });
28701
28732
  }
28702
- let workingTreeClean = true;
28703
- let dirtyFiles = [];
28704
- try {
28705
- const porcelain = execFileSync7("git", ["status", "--porcelain"], {
28706
- cwd: projectRoot,
28707
- encoding: "utf-8",
28708
- stdio: "pipe"
28733
+ if (opts?.dryRun) {
28734
+ gates.push({
28735
+ name: "clean_working_tree",
28736
+ status: "passed",
28737
+ message: "Skipped in dry-run mode"
28738
+ });
28739
+ } else {
28740
+ let workingTreeClean = true;
28741
+ let dirtyFiles = [];
28742
+ try {
28743
+ const porcelain = execFileSync7("git", ["status", "--porcelain"], {
28744
+ cwd: projectRoot,
28745
+ encoding: "utf-8",
28746
+ stdio: "pipe"
28747
+ });
28748
+ dirtyFiles = porcelain.split("\n").filter((l) => l.trim()).filter((l) => !l.startsWith("?? ")).map((l) => l.slice(3).trim()).filter((f) => f !== "CHANGELOG.md" && f !== "VERSION" && f !== "package.json");
28749
+ workingTreeClean = dirtyFiles.length === 0;
28750
+ } catch {
28751
+ }
28752
+ gates.push({
28753
+ name: "clean_working_tree",
28754
+ status: workingTreeClean ? "passed" : "failed",
28755
+ message: workingTreeClean ? "Working tree clean (excluding CHANGELOG.md, VERSION, package.json)" : `Uncommitted changes in: ${dirtyFiles.slice(0, 5).join(", ")}${dirtyFiles.length > 5 ? ` (+${dirtyFiles.length - 5} more)` : ""}`
28709
28756
  });
28710
- dirtyFiles = porcelain.split("\n").filter((l) => l.trim()).map((l) => l.slice(3).trim()).filter((f) => f !== "CHANGELOG.md" && f !== "VERSION" && f !== "package.json");
28711
- workingTreeClean = dirtyFiles.length === 0;
28712
- } catch {
28713
28757
  }
28714
- gates.push({
28715
- name: "clean_working_tree",
28716
- status: workingTreeClean ? "passed" : "failed",
28717
- message: workingTreeClean ? "Working tree clean (excluding CHANGELOG.md, VERSION, package.json)" : `Uncommitted changes in: ${dirtyFiles.slice(0, 5).join(", ")}${dirtyFiles.length > 5 ? ` (+${dirtyFiles.length - 5} more)` : ""}`
28718
- });
28719
28758
  const isPreRelease = normalizedVersion.includes("-");
28720
28759
  let currentBranch = "";
28721
28760
  try {
@@ -28775,6 +28814,28 @@ async function runReleaseGates(version, loadTasksFn, cwd) {
28775
28814
  metadata
28776
28815
  };
28777
28816
  }
28817
+ async function cancelRelease(version, projectRoot) {
28818
+ if (!version) {
28819
+ throw new Error("version is required");
28820
+ }
28821
+ const normalizedVersion = normalizeVersion(version);
28822
+ const db = await getDb(projectRoot);
28823
+ const rows = await db.select().from(releaseManifests).where(eq14(releaseManifests.version, normalizedVersion)).limit(1).all();
28824
+ if (rows.length === 0) {
28825
+ return { success: false, message: `Release ${normalizedVersion} not found`, version: normalizedVersion };
28826
+ }
28827
+ const status = rows[0].status;
28828
+ const cancellableStates = ["draft", "prepared"];
28829
+ if (!cancellableStates.includes(status)) {
28830
+ return {
28831
+ success: false,
28832
+ message: `Cannot cancel a release in '${status}' state. Use 'release rollback' instead.`,
28833
+ version: normalizedVersion
28834
+ };
28835
+ }
28836
+ await db.delete(releaseManifests).where(eq14(releaseManifests.version, normalizedVersion)).run();
28837
+ return { success: true, message: `Release ${normalizedVersion} cancelled and removed`, version: normalizedVersion };
28838
+ }
28778
28839
  async function rollbackRelease(version, reason, cwd) {
28779
28840
  if (!version) {
28780
28841
  throw new Error("version is required");
@@ -28849,7 +28910,8 @@ async function pushRelease(version, remote, cwd, opts) {
28849
28910
  encoding: "utf-8",
28850
28911
  stdio: ["pipe", "pipe", "pipe"]
28851
28912
  });
28852
- if (statusOutput.trim().length > 0) {
28913
+ const trackedDirty = statusOutput.split("\n").filter((l) => l.trim() && !l.startsWith("?? ")).join("\n");
28914
+ if (trackedDirty.trim().length > 0) {
28853
28915
  throw new Error(
28854
28916
  "Git working tree is not clean. Commit or stash changes before pushing (config: release.push.requireCleanTree=true)."
28855
28917
  );
@@ -28981,6 +29043,179 @@ var init_guards = __esm({
28981
29043
  }
28982
29044
  });
28983
29045
 
29046
+ // src/core/release/version-bump.ts
29047
+ import { existsSync as existsSync50, readFileSync as readFileSync39, writeFileSync as writeFileSync8 } from "node:fs";
29048
+ import { join as join48 } from "node:path";
29049
+ function readConfigValueSync2(path, defaultValue, cwd) {
29050
+ try {
29051
+ const configPath = join48(getCleoDir(cwd), "config.json");
29052
+ if (!existsSync50(configPath)) return defaultValue;
29053
+ const config = JSON.parse(readFileSync39(configPath, "utf-8"));
29054
+ const keys = path.split(".");
29055
+ let value = config;
29056
+ for (const key of keys) {
29057
+ if (value == null || typeof value !== "object") return defaultValue;
29058
+ value = value[key];
29059
+ }
29060
+ return value ?? defaultValue;
29061
+ } catch {
29062
+ return defaultValue;
29063
+ }
29064
+ }
29065
+ function validateVersionFormat(version) {
29066
+ return VERSION_WITH_PRERELEASE.test(version);
29067
+ }
29068
+ function getVersionBumpConfig(cwd) {
29069
+ try {
29070
+ const raw = readConfigValueSync2("release.versionBump.files", [], cwd);
29071
+ return raw.map((entry) => ({
29072
+ file: entry.path ?? entry.file ?? "",
29073
+ strategy: entry.strategy,
29074
+ field: entry.jsonPath?.replace(/^\./, "") ?? entry.field,
29075
+ key: entry.key,
29076
+ section: entry.section,
29077
+ pattern: entry.sedPattern ?? entry.pattern
29078
+ })).filter((t) => t.file !== "");
29079
+ } catch {
29080
+ return [];
29081
+ }
29082
+ }
29083
+ function bumpFile(target, newVersion, projectRoot) {
29084
+ const filePath = join48(projectRoot, target.file);
29085
+ if (!existsSync50(filePath)) {
29086
+ return {
29087
+ file: target.file,
29088
+ strategy: target.strategy,
29089
+ success: false,
29090
+ error: `File not found: ${target.file}`
29091
+ };
29092
+ }
29093
+ try {
29094
+ const content = readFileSync39(filePath, "utf-8");
29095
+ let previousVersion;
29096
+ let newContent;
29097
+ switch (target.strategy) {
29098
+ case "plain": {
29099
+ previousVersion = content.trim();
29100
+ newContent = newVersion + "\n";
29101
+ break;
29102
+ }
29103
+ case "json": {
29104
+ const field = target.field ?? "version";
29105
+ const json = JSON.parse(content);
29106
+ previousVersion = getNestedField(json, field);
29107
+ setNestedField(json, field, newVersion);
29108
+ newContent = JSON.stringify(json, null, 2) + "\n";
29109
+ break;
29110
+ }
29111
+ case "toml": {
29112
+ const key = target.key ?? "version";
29113
+ const versionRegex = new RegExp(`^(${key}\\s*=\\s*")([^"]+)(")`, "m");
29114
+ const match = content.match(versionRegex);
29115
+ previousVersion = match?.[2];
29116
+ newContent = content.replace(versionRegex, `$1${newVersion}$3`);
29117
+ break;
29118
+ }
29119
+ case "sed": {
29120
+ const pattern = target.pattern ?? "";
29121
+ if (!pattern.includes("{{VERSION}}")) {
29122
+ return {
29123
+ file: target.file,
29124
+ strategy: target.strategy,
29125
+ success: false,
29126
+ error: "sed strategy requires {{VERSION}} placeholder in pattern"
29127
+ };
29128
+ }
29129
+ const regex = new RegExp(pattern.replace("{{VERSION}}", "([\\d.]+)"));
29130
+ const match = content.match(regex);
29131
+ previousVersion = match?.[1];
29132
+ newContent = content.replace(
29133
+ regex,
29134
+ pattern.replace("{{VERSION}}", newVersion)
29135
+ );
29136
+ break;
29137
+ }
29138
+ default:
29139
+ return {
29140
+ file: target.file,
29141
+ strategy: target.strategy,
29142
+ success: false,
29143
+ error: `Unknown strategy: ${target.strategy}`
29144
+ };
29145
+ }
29146
+ writeFileSync8(filePath, newContent, "utf-8");
29147
+ return {
29148
+ file: target.file,
29149
+ strategy: target.strategy,
29150
+ success: true,
29151
+ previousVersion,
29152
+ newVersion
29153
+ };
29154
+ } catch (err) {
29155
+ return {
29156
+ file: target.file,
29157
+ strategy: target.strategy,
29158
+ success: false,
29159
+ error: String(err)
29160
+ };
29161
+ }
29162
+ }
29163
+ function bumpVersionFromConfig(newVersion, options = {}, cwd) {
29164
+ if (!validateVersionFormat(newVersion)) {
29165
+ throw new CleoError(
29166
+ 6 /* VALIDATION_ERROR */,
29167
+ `Invalid version: '${newVersion}' (expected X.Y.Z or YYYY.M.patch)`
29168
+ );
29169
+ }
29170
+ const targets = getVersionBumpConfig(cwd);
29171
+ if (targets.length === 0) {
29172
+ throw new CleoError(
29173
+ 1 /* GENERAL_ERROR */,
29174
+ "No version bump targets configured. Add release.versionBump.files to .cleo/config.json"
29175
+ );
29176
+ }
29177
+ const projectRoot = getProjectRoot(cwd);
29178
+ const results = [];
29179
+ if (options.dryRun) {
29180
+ for (const target of targets) {
29181
+ results.push({
29182
+ file: target.file,
29183
+ strategy: target.strategy,
29184
+ success: true,
29185
+ newVersion
29186
+ });
29187
+ }
29188
+ return { results, allSuccess: true };
29189
+ }
29190
+ for (const target of targets) {
29191
+ results.push(bumpFile(target, newVersion, projectRoot));
29192
+ }
29193
+ const allSuccess = results.every((r) => r.success);
29194
+ return { results, allSuccess };
29195
+ }
29196
+ function getNestedField(obj, path) {
29197
+ return path.split(".").reduce((acc, key) => acc?.[key], obj);
29198
+ }
29199
+ function setNestedField(obj, path, value) {
29200
+ const parts = path.split(".");
29201
+ let current = obj;
29202
+ for (let i = 0; i < parts.length - 1; i++) {
29203
+ if (typeof current[parts[i]] !== "object") current[parts[i]] = {};
29204
+ current = current[parts[i]];
29205
+ }
29206
+ current[parts[parts.length - 1]] = value;
29207
+ }
29208
+ var VERSION_WITH_PRERELEASE;
29209
+ var init_version_bump = __esm({
29210
+ "src/core/release/version-bump.ts"() {
29211
+ "use strict";
29212
+ init_paths();
29213
+ init_errors();
29214
+ init_exit_codes();
29215
+ VERSION_WITH_PRERELEASE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
29216
+ }
29217
+ });
29218
+
28984
29219
  // src/dispatch/engines/release-engine.ts
28985
29220
  import { execFileSync as execFileSync8 } from "node:child_process";
28986
29221
  function isAgentContext() {
@@ -29103,6 +29338,23 @@ async function releaseRollback(version, reason, projectRoot) {
29103
29338
  return engineError(code, message);
29104
29339
  }
29105
29340
  }
29341
+ async function releaseCancel(version, projectRoot) {
29342
+ if (!version) {
29343
+ return engineError("E_INVALID_INPUT", "version is required");
29344
+ }
29345
+ try {
29346
+ const result = await cancelRelease(version, projectRoot);
29347
+ if (!result.success) {
29348
+ const code = result.message.includes("not found") ? "E_NOT_FOUND" : "E_INVALID_STATE";
29349
+ return engineError(code, result.message);
29350
+ }
29351
+ return { success: true, data: result };
29352
+ } catch (err) {
29353
+ const message = err.message;
29354
+ const code = message.includes("not found") ? "E_NOT_FOUND" : "E_CANCEL_FAILED";
29355
+ return engineError(code, message);
29356
+ }
29357
+ }
29106
29358
  async function releasePush(version, remote, projectRoot, opts) {
29107
29359
  if (isAgentContext()) {
29108
29360
  const hasEntry = await hasManifestEntry(version, projectRoot);
@@ -29146,7 +29398,7 @@ async function releasePush(version, remote, projectRoot, opts) {
29146
29398
  }
29147
29399
  }
29148
29400
  async function releaseShip(params, projectRoot) {
29149
- const { version, epicId, remote, dryRun = false } = params;
29401
+ const { version, epicId, remote, dryRun = false, bump = true } = params;
29150
29402
  if (!version) {
29151
29403
  return engineError("E_INVALID_INPUT", "version is required");
29152
29404
  }
@@ -29154,30 +29406,51 @@ async function releaseShip(params, projectRoot) {
29154
29406
  return engineError("E_INVALID_INPUT", "epicId is required");
29155
29407
  }
29156
29408
  const cwd = projectRoot ?? resolveProjectRoot();
29409
+ const steps = [];
29157
29410
  const logStep = (n, total, label, done, error) => {
29411
+ let msg;
29158
29412
  if (done === void 0) {
29159
- console.log(`[Step ${n}/${total}] ${label}...`);
29413
+ msg = `[Step ${n}/${total}] ${label}...`;
29160
29414
  } else if (done) {
29161
- console.log(` \u2713 ${label}`);
29415
+ msg = ` \u2713 ${label}`;
29162
29416
  } else {
29163
- console.log(` \u2717 ${label}: ${error ?? "failed"}`);
29417
+ msg = ` \u2717 ${label}: ${error ?? "failed"}`;
29164
29418
  }
29419
+ steps.push(msg);
29420
+ console.log(msg);
29165
29421
  };
29422
+ const bumpTargets = getVersionBumpConfig(cwd);
29423
+ const shouldBump = bump && bumpTargets.length > 0;
29166
29424
  try {
29167
- logStep(1, 7, "Validate release gates");
29425
+ if (shouldBump) {
29426
+ logStep(0, 8, "Bump version files");
29427
+ if (!dryRun) {
29428
+ const bumpResults = bumpVersionFromConfig(version, { dryRun: false }, cwd);
29429
+ if (!bumpResults.allSuccess) {
29430
+ const failed = bumpResults.results.filter((r) => !r.success).map((r) => r.file);
29431
+ steps.push(` ! Version bump partial: failed for ${failed.join(", ")}`);
29432
+ } else {
29433
+ logStep(0, 8, "Bump version files", true);
29434
+ }
29435
+ } else {
29436
+ logStep(0, 8, "Bump version files", true);
29437
+ }
29438
+ }
29439
+ logStep(1, 8, "Validate release gates");
29168
29440
  const gatesResult = await runReleaseGates(
29169
29441
  version,
29170
29442
  () => loadTasks2(projectRoot),
29171
- projectRoot
29443
+ projectRoot,
29444
+ { dryRun }
29172
29445
  );
29173
29446
  if (gatesResult && !gatesResult.allPassed) {
29174
29447
  const failedGates = gatesResult.gates.filter((g) => g.status === "failed");
29175
- logStep(1, 7, "Validate release gates", false, failedGates.map((g) => g.name).join(", "));
29448
+ logStep(1, 8, "Validate release gates", false, failedGates.map((g) => g.name).join(", "));
29176
29449
  return engineError("E_LIFECYCLE_GATE_FAILED", `Release gates failed for ${version}: ${failedGates.map((g) => g.name).join(", ")}`, {
29177
29450
  details: { gates: gatesResult.gates, failedCount: gatesResult.failedCount }
29178
29451
  });
29179
29452
  }
29180
- logStep(1, 7, "Validate release gates", true);
29453
+ logStep(1, 8, "Validate release gates", true);
29181
29454
  let resolvedChannel = "latest";
29182
29455
  let currentBranchForPR = "HEAD";
29183
29456
  try {
@@ -29197,7 +29470,7 @@ async function releaseShip(params, projectRoot) {
29197
29470
  if (gateMetadata?.currentBranch) {
29198
29471
  currentBranchForPR = gateMetadata.currentBranch;
29199
29472
  }
29200
- logStep(2, 7, "Check epic completeness");
29473
+ logStep(2, 8, "Check epic completeness");
29201
29474
  let releaseTaskIds = [];
29202
29475
  try {
29203
29476
  const manifest = await showManifestRelease(version, projectRoot);
@@ -29208,13 +29481,13 @@ async function releaseShip(params, projectRoot) {
29208
29481
  const epicCheck = await checkEpicCompleteness(releaseTaskIds, projectRoot, epicAccessor);
29209
29482
  if (epicCheck.hasIncomplete) {
29210
29483
  const incomplete = epicCheck.epics.filter((e) => e.missingChildren.length > 0).map((e) => `${e.epicId}: missing ${e.missingChildren.map((c) => c.id).join(", ")}`).join("; ");
29211
- logStep(2, 7, "Check epic completeness", false, incomplete);
29484
+ logStep(2, 8, "Check epic completeness", false, incomplete);
29212
29485
  return engineError("E_LIFECYCLE_GATE_FAILED", `Epic completeness check failed: ${incomplete}`, {
29213
29486
  details: { epics: epicCheck.epics }
29214
29487
  });
29215
29488
  }
29216
- logStep(2, 7, "Check epic completeness", true);
29217
- logStep(3, 7, "Check task double-listing");
29489
+ logStep(2, 8, "Check epic completeness", true);
29490
+ logStep(3, 8, "Check task double-listing");
29218
29491
  const allReleases = await listManifestReleases(projectRoot);
29219
29492
  const existingReleases = (allReleases.releases ?? []).filter((r) => r.version !== version);
29220
29493
  const doubleCheck = checkDoubleListing(
@@ -29223,39 +29496,38 @@ async function releaseShip(params, projectRoot) {
29223
29496
  );
29224
29497
  if (doubleCheck.hasDoubleListing) {
29225
29498
  const dupes = doubleCheck.duplicates.map((d) => `${d.taskId} (in ${d.releases.join(", ")})`).join("; ");
29226
- logStep(3, 7, "Check task double-listing", false, dupes);
29499
+ logStep(3, 8, "Check task double-listing", false, dupes);
29227
29500
  return engineError("E_VALIDATION", `Double-listing detected: ${dupes}`, {
29228
29501
  details: { duplicates: doubleCheck.duplicates }
29229
29502
  });
29230
29503
  }
29231
- logStep(3, 7, "Check task double-listing", true);
29232
- logStep(4, 7, "Generate CHANGELOG");
29233
- const changelogResult = await generateReleaseChangelog(
29234
- version,
29235
- () => loadTasks2(projectRoot),
29236
- projectRoot
29237
- );
29238
- const changelogPath = `${cwd}/CHANGELOG.md`;
29239
- const generatedContent = changelogResult.changelog ?? "";
29240
- logStep(4, 7, "Generate CHANGELOG", true);
29504
+ logStep(3, 8, "Check task double-listing", true);
29241
29505
  const loadedConfig = loadReleaseConfig(cwd);
29242
29506
  const pushMode = getPushMode(loadedConfig);
29243
29507
  const gitflowCfg = getGitFlowConfig(loadedConfig);
29244
29508
  const targetBranch = targetBranchFromGates ?? gitflowCfg.branches.main;
29245
29509
  if (dryRun) {
29510
+ logStep(4, 8, "Generate CHANGELOG");
29511
+ logStep(4, 8, "Generate CHANGELOG", true);
29246
29512
  const wouldCreatePR = requiresPRFromGates || pushMode === "pr";
29513
+ const filesToStagePreview = ["CHANGELOG.md", ...shouldBump ? bumpTargets.map((t) => t.file) : []];
29514
+ const wouldDo = [];
29515
+ if (shouldBump) {
29516
+ wouldDo.push(`bump version files: ${bumpTargets.map((t) => t.file).join(", ")} \u2192 ${version}`);
29517
+ }
29518
+ wouldDo.push(
29519
+ `write CHANGELOG.md: ## [${version}] - ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} (preview only, not written in dry-run)`,
29520
+ `git add ${filesToStagePreview.join(" ")}`,
29521
+ `git commit -m "release: ship v${version} (${epicId})"`,
29522
+ `git tag -a v${version} -m "Release v${version}"`
29523
+ );
29247
29524
  const dryRunOutput = {
29248
29525
  version,
29249
29526
  epicId,
29250
29527
  dryRun: true,
29251
29528
  channel: resolvedChannel,
29252
29529
  pushMode,
29253
- wouldDo: [
29254
- `write CHANGELOG section for ${version} (${generatedContent.length} chars)`,
29255
- "git add CHANGELOG.md",
29256
- `git commit -m "release: ship v${version} (${epicId})"`,
29257
- `git tag -a v${version} -m "Release v${version}"`
29258
- ]
29530
+ wouldDo
29259
29531
  };
29260
29532
  if (wouldCreatePR) {
29261
29533
  const ghAvailable = isGhCliAvailable();
@@ -29272,15 +29544,24 @@ async function releaseShip(params, projectRoot) {
29272
29544
  dryRunOutput["wouldCreatePR"] = false;
29273
29545
  }
29274
29546
  dryRunOutput["wouldDo"].push("markReleasePushed(...)");
29275
- return { success: true, data: dryRunOutput };
29547
+ return { success: true, data: { ...dryRunOutput, steps } };
29276
29548
  }
29277
- logStep(5, 7, "Commit release");
29549
+ logStep(4, 8, "Generate CHANGELOG");
29550
+ await generateReleaseChangelog(
29551
+ version,
29552
+ () => loadTasks2(projectRoot),
29553
+ projectRoot
29554
+ );
29555
+ const changelogPath = `${cwd}/CHANGELOG.md`;
29556
+ logStep(4, 8, "Generate CHANGELOG", true);
29557
+ logStep(5, 8, "Commit release");
29278
29558
  const gitCwd = { cwd, encoding: "utf-8", stdio: "pipe" };
29559
+ const filesToStage = ["CHANGELOG.md", ...shouldBump ? bumpTargets.map((t) => t.file) : []];
29279
29560
  try {
29280
- execFileSync8("git", ["add", "CHANGELOG.md"], gitCwd);
29561
+ execFileSync8("git", ["add", ...filesToStage], gitCwd);
29281
29562
  } catch (err) {
29282
29563
  const msg = err.message ?? String(err);
29283
- logStep(5, 7, "Commit release", false, `git add failed: ${msg}`);
29564
+ logStep(5, 8, "Commit release", false, `git add failed: ${msg}`);
29284
29565
  return engineError("E_GENERAL", `git add failed: ${msg}`);
29285
29566
  }
29286
29567
  try {
@@ -29291,26 +29572,26 @@ async function releaseShip(params, projectRoot) {
29291
29572
  );
29292
29573
  } catch (err) {
29293
29574
  const msg = err.stderr ?? err.message ?? String(err);
29294
- logStep(5, 7, "Commit release", false, `git commit failed: ${msg}`);
29575
+ logStep(5, 8, "Commit release", false, `git commit failed: ${msg}`);
29295
29576
  return engineError("E_GENERAL", `git commit failed: ${msg}`);
29296
29577
  }
29297
- logStep(5, 7, "Commit release", true);
29578
+ logStep(5, 8, "Commit release", true);
29298
29579
  let commitSha;
29299
29580
  try {
29300
29581
  commitSha = execFileSync8("git", ["rev-parse", "HEAD"], gitCwd).toString().trim();
29301
29582
  } catch {
29302
29583
  }
29303
- logStep(6, 7, "Tag release");
29584
+ logStep(6, 8, "Tag release");
29304
29585
  const gitTag = `v${version.replace(/^v/, "")}`;
29305
29586
  try {
29306
29587
  execFileSync8("git", ["tag", "-a", gitTag, "-m", `Release ${gitTag}`], gitCwd);
29307
29588
  } catch (err) {
29308
29589
  const msg = err.stderr ?? err.message ?? String(err);
29309
- logStep(6, 7, "Tag release", false, `git tag failed: ${msg}`);
29590
+ logStep(6, 8, "Tag release", false, `git tag failed: ${msg}`);
29310
29591
  return engineError("E_GENERAL", `git tag failed: ${msg}`);
29311
29592
  }
29312
- logStep(6, 7, "Tag release", true);
29313
- logStep(7, 7, "Push / create PR");
29593
+ logStep(6, 8, "Tag release", true);
29594
+ logStep(7, 8, "Push / create PR");
29314
29595
  let prResult = null;
29315
29596
  const pushResult = await pushRelease(version, remote, projectRoot, {
29316
29597
  explicitPush: true,
@@ -29337,24 +29618,34 @@ async function releaseShip(params, projectRoot) {
29337
29618
  projectRoot: cwd
29338
29619
  });
29339
29620
  if (prResult.mode === "created") {
29340
- console.log(` \u2713 Push / create PR`);
29341
- console.log(` PR created: ${prResult.prUrl}`);
29342
- console.log(` \u2192 Next: merge the PR, then CI will publish to npm @${resolvedChannel}`);
29621
+ const m1 = ` \u2713 Push / create PR`;
29622
+ const m2 = ` PR created: ${prResult.prUrl}`;
29623
+ const m3 = ` \u2192 Next: merge the PR, then CI will publish to npm @${resolvedChannel}`;
29624
+ steps.push(m1, m2, m3);
29625
+ console.log(m1);
29626
+ console.log(m2);
29627
+ console.log(m3);
29343
29628
  } else if (prResult.mode === "skipped") {
29344
- console.log(` \u2713 Push / create PR`);
29345
- console.log(` PR already exists: ${prResult.prUrl}`);
29629
+ const m1 = ` \u2713 Push / create PR`;
29630
+ const m2 = ` PR already exists: ${prResult.prUrl}`;
29631
+ steps.push(m1, m2);
29632
+ console.log(m1);
29633
+ console.log(m2);
29346
29634
  } else {
29347
- console.log(` ! Push / create PR \u2014 manual PR required:`);
29348
- console.log(prResult.instructions);
29635
+ const m1 = ` ! Push / create PR \u2014 manual PR required:`;
29636
+ const m2 = prResult.instructions ?? "";
29637
+ steps.push(m1, m2);
29638
+ console.log(m1);
29639
+ console.log(m2);
29349
29640
  }
29350
29641
  } else {
29351
29642
  try {
29352
29643
  execFileSync8("git", ["push", remote ?? "origin", "--follow-tags"], gitCwd);
29353
- logStep(7, 7, "Push / create PR", true);
29644
+ logStep(7, 8, "Push / create PR", true);
29354
29645
  } catch (err) {
29355
29646
  const execError = err;
29356
29647
  const msg = (execError.stderr ?? execError.message ?? "").slice(0, 500);
29357
- logStep(7, 7, "Push / create PR", false, `git push failed: ${msg}`);
29648
+ logStep(7, 8, "Push / create PR", false, `git push failed: ${msg}`);
29358
29649
  return engineError("E_GENERAL", `git push failed: ${msg}`, {
29359
29650
  details: { exitCode: execError.status }
29360
29651
  });
@@ -29372,6 +29663,7 @@ async function releaseShip(params, projectRoot) {
29372
29663
  pushedAt,
29373
29664
  changelog: changelogPath,
29374
29665
  channel: resolvedChannel,
29666
+ steps,
29375
29667
  ...prResult ? {
29376
29668
  pr: {
29377
29669
  mode: prResult.mode,
@@ -29396,13 +29688,14 @@ var init_release_engine = __esm({
29396
29688
  init_github_pr();
29397
29689
  init_channel();
29398
29690
  init_release_config();
29691
+ init_version_bump();
29399
29692
  init_error();
29400
29693
  }
29401
29694
  });
29402
29695
 
29403
29696
  // src/dispatch/engines/template-parser.ts
29404
- import { readFileSync as readFileSync39, readdirSync as readdirSync12, existsSync as existsSync50 } from "fs";
29405
- import { join as join48 } from "path";
29697
+ import { readFileSync as readFileSync40, readdirSync as readdirSync12, existsSync as existsSync51 } from "fs";
29698
+ import { join as join49 } from "path";
29406
29699
  import { parse as parseYaml } from "yaml";
29407
29700
  function deriveSubcommand(filename) {
29408
29701
  let stem = filename.replace(/\.ya?ml$/i, "");
@@ -29416,8 +29709,8 @@ function deriveSubcommand(filename) {
29416
29709
  return firstWord.toLowerCase();
29417
29710
  }
29418
29711
  function parseTemplateFile(templateDir, filename) {
29419
- const filePath = join48(templateDir, filename);
29420
- const raw = readFileSync39(filePath, "utf-8");
29712
+ const filePath = join49(templateDir, filename);
29713
+ const raw = readFileSync40(filePath, "utf-8");
29421
29714
  const parsed = parseYaml(raw);
29422
29715
  const name = typeof parsed.name === "string" ? parsed.name : filename;
29423
29716
  const titlePrefix = typeof parsed.title === "string" ? parsed.title : "";
@@ -29466,8 +29759,8 @@ function parseTemplateFile(templateDir, filename) {
29466
29759
  };
29467
29760
  }
29468
29761
  function parseIssueTemplates(projectRoot) {
29469
- const templateDir = join48(projectRoot, ".github", "ISSUE_TEMPLATE");
29470
- if (!existsSync50(templateDir)) {
29762
+ const templateDir = join49(projectRoot, ".github", "ISSUE_TEMPLATE");
29763
+ if (!existsSync51(templateDir)) {
29471
29764
  return engineError("E_NOT_FOUND", `Issue template directory not found: ${templateDir}`);
29472
29765
  }
29473
29766
  let files;
@@ -31146,26 +31439,26 @@ var init_check = __esm({
31146
31439
  });
31147
31440
 
31148
31441
  // src/core/adrs/validate.ts
31149
- import { readFileSync as readFileSync40, readdirSync as readdirSync13, existsSync as existsSync51 } from "node:fs";
31150
- import { join as join49 } from "node:path";
31442
+ import { readFileSync as readFileSync41, readdirSync as readdirSync13, existsSync as existsSync52 } from "node:fs";
31443
+ import { join as join50 } from "node:path";
31151
31444
  import AjvModule3 from "ajv";
31152
31445
  async function validateAllAdrs(projectRoot) {
31153
- const adrsDir = join49(projectRoot, ".cleo", "adrs");
31154
- const schemaPath = join49(projectRoot, "schemas", "adr-frontmatter.schema.json");
31155
- if (!existsSync51(schemaPath)) {
31446
+ const adrsDir = join50(projectRoot, ".cleo", "adrs");
31447
+ const schemaPath = join50(projectRoot, "schemas", "adr-frontmatter.schema.json");
31448
+ if (!existsSync52(schemaPath)) {
31156
31449
  return {
31157
31450
  valid: false,
31158
31451
  errors: [{ file: "schemas/adr-frontmatter.schema.json", field: "schema", message: "Schema file not found" }],
31159
31452
  checked: 0
31160
31453
  };
31161
31454
  }
31162
- if (!existsSync51(adrsDir)) {
31455
+ if (!existsSync52(adrsDir)) {
31163
31456
  return { valid: true, errors: [], checked: 0 };
31164
31457
  }
31165
- const schema = JSON.parse(readFileSync40(schemaPath, "utf-8"));
31458
+ const schema = JSON.parse(readFileSync41(schemaPath, "utf-8"));
31166
31459
  const ajv = new Ajv3({ allErrors: true });
31167
31460
  const validate = ajv.compile(schema);
31168
- const files = readdirSync13(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join49(adrsDir, f));
31461
+ const files = readdirSync13(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join50(adrsDir, f));
31169
31462
  const errors = [];
31170
31463
  for (const filePath of files) {
31171
31464
  const record = parseAdrFile(filePath, projectRoot);
@@ -31192,15 +31485,15 @@ var init_validate = __esm({
31192
31485
  });
31193
31486
 
31194
31487
  // src/core/adrs/list.ts
31195
- import { readdirSync as readdirSync14, existsSync as existsSync52 } from "node:fs";
31196
- import { join as join50 } from "node:path";
31488
+ import { readdirSync as readdirSync14, existsSync as existsSync53 } from "node:fs";
31489
+ import { join as join51 } from "node:path";
31197
31490
  async function listAdrs(projectRoot, opts) {
31198
- const adrsDir = join50(projectRoot, ".cleo", "adrs");
31199
- if (!existsSync52(adrsDir)) {
31491
+ const adrsDir = join51(projectRoot, ".cleo", "adrs");
31492
+ if (!existsSync53(adrsDir)) {
31200
31493
  return { adrs: [], total: 0 };
31201
31494
  }
31202
31495
  const files = readdirSync14(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
31203
- const records = files.map((f) => parseAdrFile(join50(adrsDir, f), projectRoot));
31496
+ const records = files.map((f) => parseAdrFile(join51(adrsDir, f), projectRoot));
31204
31497
  const filtered = records.filter((r) => {
31205
31498
  if (opts?.status && r.frontmatter.Status !== opts.status) return false;
31206
31499
  if (opts?.since && r.frontmatter.Date < opts.since) return false;
@@ -31225,14 +31518,14 @@ var init_list2 = __esm({
31225
31518
  });
31226
31519
 
31227
31520
  // src/core/adrs/show.ts
31228
- import { existsSync as existsSync53, readdirSync as readdirSync15 } from "node:fs";
31229
- import { join as join51 } from "node:path";
31521
+ import { existsSync as existsSync54, readdirSync as readdirSync15 } from "node:fs";
31522
+ import { join as join52 } from "node:path";
31230
31523
  async function showAdr(projectRoot, adrId) {
31231
- const adrsDir = join51(projectRoot, ".cleo", "adrs");
31232
- if (!existsSync53(adrsDir)) return null;
31524
+ const adrsDir = join52(projectRoot, ".cleo", "adrs");
31525
+ if (!existsSync54(adrsDir)) return null;
31233
31526
  const files = readdirSync15(adrsDir).filter((f) => f.startsWith(adrId) && f.endsWith(".md"));
31234
31527
  if (files.length === 0) return null;
31235
- const filePath = join51(adrsDir, files[0]);
31528
+ const filePath = join52(adrsDir, files[0]);
31236
31529
  return parseAdrFile(filePath, projectRoot);
31237
31530
  }
31238
31531
  var init_show2 = __esm({
@@ -31243,8 +31536,8 @@ var init_show2 = __esm({
31243
31536
  });
31244
31537
 
31245
31538
  // src/core/adrs/find.ts
31246
- import { readdirSync as readdirSync16, existsSync as existsSync54 } from "node:fs";
31247
- import { join as join52 } from "node:path";
31539
+ import { readdirSync as readdirSync16, existsSync as existsSync55 } from "node:fs";
31540
+ import { join as join53 } from "node:path";
31248
31541
  function normalise(s) {
31249
31542
  return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
31250
31543
  }
@@ -31261,8 +31554,8 @@ function matchedTerms(target, terms) {
31261
31554
  return terms.filter((term) => t.includes(term));
31262
31555
  }
31263
31556
  async function findAdrs(projectRoot, query, opts) {
31264
- const adrsDir = join52(projectRoot, ".cleo", "adrs");
31265
- if (!existsSync54(adrsDir)) {
31557
+ const adrsDir = join53(projectRoot, ".cleo", "adrs");
31558
+ if (!existsSync55(adrsDir)) {
31266
31559
  return { adrs: [], query, total: 0 };
31267
31560
  }
31268
31561
  const files = readdirSync16(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
@@ -31271,7 +31564,7 @@ async function findAdrs(projectRoot, query, opts) {
31271
31564
  const filterKeywords = opts?.keywords ? parseTags(opts.keywords) : null;
31272
31565
  const results = [];
31273
31566
  for (const file of files) {
31274
- const record = parseAdrFile(join52(adrsDir, file), projectRoot);
31567
+ const record = parseAdrFile(join53(adrsDir, file), projectRoot);
31275
31568
  const fm = record.frontmatter;
31276
31569
  if (opts?.status && fm.Status !== opts.status) continue;
31277
31570
  if (filterTopics && filterTopics.length > 0) {
@@ -31349,12 +31642,12 @@ var init_adrs = __esm({
31349
31642
  });
31350
31643
 
31351
31644
  // src/core/admin/sync.ts
31352
- import { join as join53 } from "node:path";
31645
+ import { join as join54 } from "node:path";
31353
31646
  import { rm as rm2, rmdir, stat as stat2 } from "node:fs/promises";
31354
31647
  async function getSyncStatus(projectRoot) {
31355
31648
  try {
31356
31649
  const cleoDir = getCleoDir(projectRoot);
31357
- const stateFile = join53(cleoDir, "sync", "todowrite-session.json");
31650
+ const stateFile = join54(cleoDir, "sync", "todowrite-session.json");
31358
31651
  const sessionState = await readJson(stateFile);
31359
31652
  if (!sessionState) {
31360
31653
  return {
@@ -31398,8 +31691,8 @@ async function getSyncStatus(projectRoot) {
31398
31691
  async function clearSyncState(projectRoot, dryRun) {
31399
31692
  try {
31400
31693
  const cleoDir = getCleoDir(projectRoot);
31401
- const syncDir = join53(cleoDir, "sync");
31402
- const stateFile = join53(syncDir, "todowrite-session.json");
31694
+ const syncDir = join54(cleoDir, "sync");
31695
+ const stateFile = join54(syncDir, "todowrite-session.json");
31403
31696
  let exists = false;
31404
31697
  try {
31405
31698
  await stat2(stateFile);
@@ -32166,8 +32459,8 @@ var init_import_tasks = __esm({
32166
32459
  // src/core/snapshot/index.ts
32167
32460
  import { createHash as createHash6 } from "node:crypto";
32168
32461
  import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir10 } from "node:fs/promises";
32169
- import { existsSync as existsSync55 } from "node:fs";
32170
- import { join as join54, dirname as dirname14 } from "node:path";
32462
+ import { existsSync as existsSync56 } from "node:fs";
32463
+ import { join as join55, dirname as dirname14 } from "node:path";
32171
32464
  function toSnapshotTask(task) {
32172
32465
  return {
32173
32466
  id: task.id,
@@ -32220,7 +32513,7 @@ async function exportSnapshot(cwd) {
32220
32513
  }
32221
32514
  async function writeSnapshot(snapshot, outputPath) {
32222
32515
  const dir = dirname14(outputPath);
32223
- if (!existsSync55(dir)) {
32516
+ if (!existsSync56(dir)) {
32224
32517
  await mkdir10(dir, { recursive: true });
32225
32518
  }
32226
32519
  await writeFile10(outputPath, JSON.stringify(snapshot, null, 2) + "\n");
@@ -32236,7 +32529,7 @@ async function readSnapshot(inputPath) {
32236
32529
  function getDefaultSnapshotPath(cwd) {
32237
32530
  const cleoDir = getCleoDirAbsolute(cwd);
32238
32531
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
32239
- return join54(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
32532
+ return join55(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
32240
32533
  }
32241
32534
  async function importSnapshot(snapshot, cwd) {
32242
32535
  const accessor = await getAccessor(cwd);
@@ -32534,15 +32827,15 @@ var init_archive_stats2 = __esm({
32534
32827
  });
32535
32828
 
32536
32829
  // src/core/project-info.ts
32537
- import { readFileSync as readFileSync41, existsSync as existsSync56 } from "node:fs";
32538
- import { join as join55 } from "node:path";
32830
+ import { readFileSync as readFileSync42, existsSync as existsSync57 } from "node:fs";
32831
+ import { join as join56 } from "node:path";
32539
32832
  function getProjectInfoSync(cwd) {
32540
32833
  const projectRoot = cwd ?? process.cwd();
32541
32834
  const cleoDir = getCleoDirAbsolute(projectRoot);
32542
- const infoPath = join55(cleoDir, "project-info.json");
32543
- if (!existsSync56(infoPath)) return null;
32835
+ const infoPath = join56(cleoDir, "project-info.json");
32836
+ if (!existsSync57(infoPath)) return null;
32544
32837
  try {
32545
- const raw = readFileSync41(infoPath, "utf-8");
32838
+ const raw = readFileSync42(infoPath, "utf-8");
32546
32839
  const data = JSON.parse(raw);
32547
32840
  if (typeof data.projectHash !== "string" || data.projectHash.length === 0) {
32548
32841
  return null;
@@ -32629,8 +32922,8 @@ var init_defaults = __esm({
32629
32922
  });
32630
32923
 
32631
32924
  // src/mcp/lib/config.ts
32632
- import { readFileSync as readFileSync42, existsSync as existsSync57 } from "fs";
32633
- import { join as join56 } from "path";
32925
+ import { readFileSync as readFileSync43, existsSync as existsSync58 } from "fs";
32926
+ import { join as join57 } from "path";
32634
32927
  function loadFromEnv(key) {
32635
32928
  const envKey = `${ENV_PREFIX}${key.toUpperCase()}`;
32636
32929
  return process.env[envKey];
@@ -32651,12 +32944,12 @@ function parseEnvValue2(key, value) {
32651
32944
  }
32652
32945
  function loadFromFile(projectRoot) {
32653
32946
  const root = projectRoot || process.cwd();
32654
- const configPath = join56(root, ".cleo", "config.json");
32655
- if (!existsSync57(configPath)) {
32947
+ const configPath = join57(root, ".cleo", "config.json");
32948
+ if (!existsSync58(configPath)) {
32656
32949
  return {};
32657
32950
  }
32658
32951
  try {
32659
- const content = readFileSync42(configPath, "utf-8");
32952
+ const content = readFileSync43(configPath, "utf-8");
32660
32953
  const config = JSON.parse(content);
32661
32954
  const result = {};
32662
32955
  if (config.mcp) {
@@ -32996,8 +33289,8 @@ __export(session_grade_exports, {
32996
33289
  gradeSession: () => gradeSession,
32997
33290
  readGrades: () => readGrades
32998
33291
  });
32999
- import { join as join57 } from "node:path";
33000
- import { existsSync as existsSync58 } from "node:fs";
33292
+ import { join as join58 } from "node:path";
33293
+ import { existsSync as existsSync59 } from "node:fs";
33001
33294
  import { readFile as readFile14, appendFile, mkdir as mkdir11 } from "node:fs/promises";
33002
33295
  async function gradeSession(sessionId, cwd) {
33003
33296
  const sessionEntries = await queryAudit({ sessionId });
@@ -33177,9 +33470,9 @@ function detectDuplicateCreates(entries) {
33177
33470
  async function appendGradeResult(result, cwd) {
33178
33471
  try {
33179
33472
  const cleoDir = getCleoDirAbsolute(cwd);
33180
- const metricsDir = join57(cleoDir, "metrics");
33473
+ const metricsDir = join58(cleoDir, "metrics");
33181
33474
  await mkdir11(metricsDir, { recursive: true });
33182
- const gradesPath = join57(metricsDir, "GRADES.jsonl");
33475
+ const gradesPath = join58(metricsDir, "GRADES.jsonl");
33183
33476
  const line = JSON.stringify({ ...result, evaluator: "auto" }) + "\n";
33184
33477
  await appendFile(gradesPath, line, "utf8");
33185
33478
  } catch {
@@ -33188,8 +33481,8 @@ async function appendGradeResult(result, cwd) {
33188
33481
  async function readGrades(sessionId, cwd) {
33189
33482
  try {
33190
33483
  const cleoDir = getCleoDirAbsolute(cwd);
33191
- const gradesPath = join57(cleoDir, "metrics", "GRADES.jsonl");
33192
- if (!existsSync58(gradesPath)) return [];
33484
+ const gradesPath = join58(cleoDir, "metrics", "GRADES.jsonl");
33485
+ if (!existsSync59(gradesPath)) return [];
33193
33486
  const content = await readFile14(gradesPath, "utf8");
33194
33487
  const results = content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
33195
33488
  return sessionId ? results.filter((r) => r.sessionId === sessionId) : results;
@@ -35543,6 +35836,7 @@ var init_pipeline2 = __esm({
35543
35836
  "release.push",
35544
35837
  "release.gates.run",
35545
35838
  "release.rollback",
35839
+ "release.cancel",
35546
35840
  "release.ship",
35547
35841
  "manifest.append",
35548
35842
  "manifest.archive",
@@ -35907,6 +36201,20 @@ var init_pipeline2 = __esm({
35907
36201
  const result = await releaseRollback(version, reason, this.projectRoot);
35908
36202
  return this.wrapEngineResult(result, "mutate", "release.rollback", startTime);
35909
36203
  }
36204
+ case "cancel": {
36205
+ const version = params?.version;
36206
+ if (!version) {
36207
+ return this.errorResponse(
36208
+ "mutate",
36209
+ "release.cancel",
36210
+ "E_INVALID_INPUT",
36211
+ "version is required",
36212
+ startTime
36213
+ );
36214
+ }
36215
+ const result = await releaseCancel(version, this.projectRoot);
36216
+ return this.wrapEngineResult(result, "mutate", "release.cancel", startTime);
36217
+ }
35910
36218
  case "ship": {
35911
36219
  const version = params?.version;
35912
36220
  const epicId = params?.epicId;
@@ -35921,8 +36229,9 @@ var init_pipeline2 = __esm({
35921
36229
  }
35922
36230
  const remote = params?.remote;
35923
36231
  const dryRun = params?.dryRun;
36232
+ const bump = params?.bump;
35924
36233
  const result = await releaseShip(
35925
- { version, epicId, remote, dryRun },
36234
+ { version, epicId, remote, dryRun, bump },
35926
36235
  this.projectRoot
35927
36236
  );
35928
36237
  return this.wrapEngineResult(result, "mutate", "release.ship", startTime);
@@ -36448,7 +36757,7 @@ var init_build_config = __esm({
36448
36757
  "use strict";
36449
36758
  BUILD_CONFIG = {
36450
36759
  "name": "@cleocode/cleo",
36451
- "version": "2026.3.17",
36760
+ "version": "2026.3.19",
36452
36761
  "description": "CLEO V2 - TypeScript task management CLI for AI coding agents",
36453
36762
  "repository": {
36454
36763
  "owner": "kryptobaseddev",
@@ -36457,7 +36766,7 @@ var init_build_config = __esm({
36457
36766
  "url": "https://github.com/kryptobaseddev/cleo.git",
36458
36767
  "issuesUrl": "https://github.com/kryptobaseddev/cleo/issues"
36459
36768
  },
36460
- "buildDate": "2026-03-07T07:08:25.337Z",
36769
+ "buildDate": "2026-03-07T20:22:23.104Z",
36461
36770
  "templates": {
36462
36771
  "issueTemplatesDir": "templates/issue-templates"
36463
36772
  }
@@ -36466,8 +36775,8 @@ var init_build_config = __esm({
36466
36775
  });
36467
36776
 
36468
36777
  // src/core/issue/template-parser.ts
36469
- import { existsSync as existsSync59, readFileSync as readFileSync43, readdirSync as readdirSync17, writeFileSync as writeFileSync8 } from "node:fs";
36470
- import { join as join58, basename as basename10 } from "node:path";
36778
+ import { existsSync as existsSync60, readFileSync as readFileSync44, readdirSync as readdirSync17, writeFileSync as writeFileSync9 } from "node:fs";
36779
+ import { join as join59, basename as basename10 } from "node:path";
36471
36780
  function extractYamlField(content, field) {
36472
36781
  const regex = new RegExp(`^${field}:\\s*["']?(.+?)["']?\\s*$`, "m");
36473
36782
  const match = content.match(regex);
@@ -36499,9 +36808,9 @@ function extractYamlArray(content, field) {
36499
36808
  return items;
36500
36809
  }
36501
36810
  function parseTemplateFile2(filePath) {
36502
- if (!existsSync59(filePath)) return null;
36811
+ if (!existsSync60(filePath)) return null;
36503
36812
  try {
36504
- const content = readFileSync43(filePath, "utf-8");
36813
+ const content = readFileSync44(filePath, "utf-8");
36505
36814
  const fileName = basename10(filePath);
36506
36815
  const stem = fileName.replace(/\.ya?ml$/, "");
36507
36816
  const name = extractYamlField(content, "name");
@@ -36518,12 +36827,12 @@ function parseTemplateFile2(filePath) {
36518
36827
  function parseIssueTemplates2(projectDir) {
36519
36828
  try {
36520
36829
  const packageRoot = getPackageRoot();
36521
- const packagedTemplateDir = join58(packageRoot, PACKAGED_TEMPLATE_DIR);
36522
- if (existsSync59(packagedTemplateDir)) {
36830
+ const packagedTemplateDir = join59(packageRoot, PACKAGED_TEMPLATE_DIR);
36831
+ if (existsSync60(packagedTemplateDir)) {
36523
36832
  const templates3 = [];
36524
36833
  for (const file of readdirSync17(packagedTemplateDir)) {
36525
36834
  if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
36526
- const template = parseTemplateFile2(join58(packagedTemplateDir, file));
36835
+ const template = parseTemplateFile2(join59(packagedTemplateDir, file));
36527
36836
  if (template) templates3.push(template);
36528
36837
  }
36529
36838
  if (templates3.length > 0) return templates3;
@@ -36531,12 +36840,12 @@ function parseIssueTemplates2(projectDir) {
36531
36840
  } catch {
36532
36841
  }
36533
36842
  const dir = projectDir ?? getProjectRoot();
36534
- const templateDir = join58(dir, TEMPLATE_DIR);
36535
- if (!existsSync59(templateDir)) return [];
36843
+ const templateDir = join59(dir, TEMPLATE_DIR);
36844
+ if (!existsSync60(templateDir)) return [];
36536
36845
  const templates2 = [];
36537
36846
  for (const file of readdirSync17(templateDir)) {
36538
36847
  if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
36539
- const template = parseTemplateFile2(join58(templateDir, file));
36848
+ const template = parseTemplateFile2(join59(templateDir, file));
36540
36849
  if (template) templates2.push(template);
36541
36850
  }
36542
36851
  return templates2;
@@ -36545,10 +36854,10 @@ function getTemplateConfig(cwd) {
36545
36854
  const projectDir = cwd ?? getProjectRoot();
36546
36855
  const liveTemplates = parseIssueTemplates2(projectDir);
36547
36856
  if (liveTemplates.length > 0) return liveTemplates;
36548
- const cachePath = join58(getCleoDir(cwd), CACHE_FILE);
36549
- if (existsSync59(cachePath)) {
36857
+ const cachePath = join59(getCleoDir(cwd), CACHE_FILE);
36858
+ if (existsSync60(cachePath)) {
36550
36859
  try {
36551
- const cached = JSON.parse(readFileSync43(cachePath, "utf-8"));
36860
+ const cached = JSON.parse(readFileSync44(cachePath, "utf-8"));
36552
36861
  if (cached.templates?.length > 0) return cached.templates;
36553
36862
  } catch {
36554
36863
  }
@@ -37607,8 +37916,8 @@ var init_tools = __esm({
37607
37916
  });
37608
37917
 
37609
37918
  // src/core/nexus/query.ts
37610
- import { join as join59, basename as basename11 } from "node:path";
37611
- import { existsSync as existsSync60, readFileSync as readFileSync44 } from "node:fs";
37919
+ import { join as join60, basename as basename11 } from "node:path";
37920
+ import { existsSync as existsSync61, readFileSync as readFileSync45 } from "node:fs";
37612
37921
  import { z as z3 } from "zod";
37613
37922
  function validateSyntax(query) {
37614
37923
  if (!query) return false;
@@ -37644,9 +37953,9 @@ function getCurrentProject() {
37644
37953
  return process.env["NEXUS_CURRENT_PROJECT"];
37645
37954
  }
37646
37955
  try {
37647
- const infoPath = join59(process.cwd(), ".cleo", "project-info.json");
37648
- if (existsSync60(infoPath)) {
37649
- const data = JSON.parse(readFileSync44(infoPath, "utf-8"));
37956
+ const infoPath = join60(process.cwd(), ".cleo", "project-info.json");
37957
+ if (existsSync61(infoPath)) {
37958
+ const data = JSON.parse(readFileSync45(infoPath, "utf-8"));
37650
37959
  if (typeof data.name === "string" && data.name.length > 0) {
37651
37960
  return data.name;
37652
37961
  }
@@ -37682,7 +37991,7 @@ async function resolveProjectPath2(projectName) {
37682
37991
  return project.path;
37683
37992
  }
37684
37993
  async function readProjectTasks(projectPath) {
37685
- const tasksDbPath = join59(projectPath, ".cleo", "tasks.db");
37994
+ const tasksDbPath = join60(projectPath, ".cleo", "tasks.db");
37686
37995
  try {
37687
37996
  const accessor = await getAccessor(projectPath);
37688
37997
  const taskFile = await accessor.loadTaskFile();
@@ -38096,8 +38405,8 @@ var init_deps2 = __esm({
38096
38405
 
38097
38406
  // src/core/nexus/sharing/index.ts
38098
38407
  import { readFile as readFile15, writeFile as writeFile11 } from "node:fs/promises";
38099
- import { existsSync as existsSync61, readdirSync as readdirSync18, statSync as statSync8 } from "node:fs";
38100
- import { join as join60, relative as relative4 } from "node:path";
38408
+ import { existsSync as existsSync62, readdirSync as readdirSync18, statSync as statSync8 } from "node:fs";
38409
+ import { join as join61, relative as relative4 } from "node:path";
38101
38410
  function matchesPattern(filePath, pattern) {
38102
38411
  const normalizedPath = filePath.replace(/^\/+|\/+$/g, "");
38103
38412
  const normalizedPattern = pattern.replace(/^\/+|\/+$/g, "");
@@ -38122,7 +38431,7 @@ function collectCleoFiles(cleoDir) {
38122
38431
  const entries = readdirSync18(dir);
38123
38432
  for (const entry of entries) {
38124
38433
  if (entry === ".git") continue;
38125
- const fullPath = join60(dir, entry);
38434
+ const fullPath = join61(dir, entry);
38126
38435
  const relPath = relative4(cleoDir, fullPath);
38127
38436
  try {
38128
38437
  const stat5 = statSync8(fullPath);
@@ -38182,7 +38491,7 @@ function generateGitignoreEntries(sharing) {
38182
38491
  async function syncGitignore(cwd) {
38183
38492
  const config = await loadConfig(cwd);
38184
38493
  const projectRoot = getProjectRoot(cwd);
38185
- const gitignorePath = join60(projectRoot, ".gitignore");
38494
+ const gitignorePath = join61(projectRoot, ".gitignore");
38186
38495
  const entries = generateGitignoreEntries(config.sharing);
38187
38496
  const managedSection = [
38188
38497
  "",
@@ -38192,7 +38501,7 @@ async function syncGitignore(cwd) {
38192
38501
  ""
38193
38502
  ].join("\n");
38194
38503
  let content = "";
38195
- if (existsSync61(gitignorePath)) {
38504
+ if (existsSync62(gitignorePath)) {
38196
38505
  content = await readFile15(gitignorePath, "utf-8");
38197
38506
  }
38198
38507
  const startIdx = content.indexOf(GITIGNORE_START);
@@ -40114,13 +40423,13 @@ var init_field_context = __esm({
40114
40423
  });
40115
40424
 
40116
40425
  // src/core/sessions/context-alert.ts
40117
- import { existsSync as existsSync62, readFileSync as readFileSync45, writeFileSync as writeFileSync9 } from "node:fs";
40118
- import { join as join61 } from "node:path";
40426
+ import { existsSync as existsSync63, readFileSync as readFileSync46, writeFileSync as writeFileSync10 } from "node:fs";
40427
+ import { join as join62 } from "node:path";
40119
40428
  function getCurrentSessionId(cwd) {
40120
40429
  if (process.env.CLEO_SESSION) return process.env.CLEO_SESSION;
40121
- const sessionFile = join61(getCleoDir(cwd), ".current-session");
40122
- if (existsSync62(sessionFile)) {
40123
- return readFileSync45(sessionFile, "utf-8").trim() || null;
40430
+ const sessionFile = join62(getCleoDir(cwd), ".current-session");
40431
+ if (existsSync63(sessionFile)) {
40432
+ return readFileSync46(sessionFile, "utf-8").trim() || null;
40124
40433
  }
40125
40434
  return null;
40126
40435
  }
@@ -41183,7 +41492,7 @@ async function stopTask2(sessionId, cwd) {
41183
41492
  }
41184
41493
  async function workHistory(sessionId, limit = 50, cwd) {
41185
41494
  const db = await getDb(cwd);
41186
- const rows = await db.select().from(taskWorkHistory).where(eq16(taskWorkHistory.sessionId, sessionId)).orderBy(desc5(taskWorkHistory.setAt)).limit(limit).all();
41495
+ const rows = await db.select().from(taskWorkHistory).where(eq16(taskWorkHistory.sessionId, sessionId)).orderBy(desc5(taskWorkHistory.setAt), desc5(taskWorkHistory.id)).limit(limit).all();
41187
41496
  return rows.map((r) => ({
41188
41497
  taskId: r.taskId,
41189
41498
  setAt: r.setAt,
@@ -41367,14 +41676,14 @@ __export(logger_exports, {
41367
41676
  logFileExists: () => logFileExists,
41368
41677
  readMigrationLog: () => readMigrationLog
41369
41678
  });
41370
- import { existsSync as existsSync65, mkdirSync as mkdirSync16, statSync as statSync9, appendFileSync as appendFileSync4 } from "node:fs";
41371
- import { join as join68, dirname as dirname16, relative as relative6 } from "node:path";
41679
+ import { existsSync as existsSync66, mkdirSync as mkdirSync16, statSync as statSync9, appendFileSync as appendFileSync4 } from "node:fs";
41680
+ import { join as join69, dirname as dirname16, relative as relative6 } from "node:path";
41372
41681
  function createMigrationLogger(cleoDir, config) {
41373
41682
  return new MigrationLogger(cleoDir, config);
41374
41683
  }
41375
41684
  function readMigrationLog(logPath) {
41376
- const { readFileSync: readFileSync53 } = __require("node:fs");
41377
- const content = readFileSync53(logPath, "utf-8");
41685
+ const { readFileSync: readFileSync54 } = __require("node:fs");
41686
+ const content = readFileSync54(logPath, "utf-8");
41378
41687
  return content.split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
41379
41688
  }
41380
41689
  function logFileExists(logPath) {
@@ -41389,14 +41698,14 @@ function logFileExists(logPath) {
41389
41698
  function getLatestMigrationLog(cleoDir) {
41390
41699
  try {
41391
41700
  const { readdirSync: readdirSync21, statSync: statSync10 } = __require("node:fs");
41392
- const logsDir = join68(cleoDir, "logs");
41393
- if (!existsSync65(logsDir)) {
41701
+ const logsDir = join69(cleoDir, "logs");
41702
+ if (!existsSync66(logsDir)) {
41394
41703
  return null;
41395
41704
  }
41396
41705
  const files = readdirSync21(logsDir).filter((f) => f.startsWith("migration-") && f.endsWith(".jsonl")).map((f) => ({
41397
41706
  name: f,
41398
- path: join68(logsDir, f),
41399
- mtime: statSync10(join68(logsDir, f)).mtime.getTime()
41707
+ path: join69(logsDir, f),
41708
+ mtime: statSync10(join69(logsDir, f)).mtime.getTime()
41400
41709
  })).sort((a, b) => b.mtime - a.mtime);
41401
41710
  return files.length > 0 ? files[0].path : null;
41402
41711
  } catch {
@@ -41426,9 +41735,9 @@ var init_logger2 = __esm({
41426
41735
  consoleOutput: config.consoleOutput ?? false
41427
41736
  };
41428
41737
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
41429
- this.logPath = join68(cleoDir, "logs", `migration-${timestamp}.jsonl`);
41738
+ this.logPath = join69(cleoDir, "logs", `migration-${timestamp}.jsonl`);
41430
41739
  const logsDir = dirname16(this.logPath);
41431
- if (!existsSync65(logsDir)) {
41740
+ if (!existsSync66(logsDir)) {
41432
41741
  mkdirSync16(logsDir, { recursive: true });
41433
41742
  }
41434
41743
  this.startTime = Date.now();
@@ -41517,7 +41826,7 @@ var init_logger2 = __esm({
41517
41826
  sourcePath: relative6(this.cleoDir, sourcePath),
41518
41827
  ...additionalData
41519
41828
  };
41520
- if (existsSync65(sourcePath)) {
41829
+ if (existsSync66(sourcePath)) {
41521
41830
  try {
41522
41831
  const stats2 = statSync9(sourcePath);
41523
41832
  data.sourceSize = stats2.size;
@@ -41527,7 +41836,7 @@ var init_logger2 = __esm({
41527
41836
  }
41528
41837
  if (targetPath) {
41529
41838
  data.targetPath = relative6(this.cleoDir, targetPath);
41530
- if (existsSync65(targetPath)) {
41839
+ if (existsSync66(targetPath)) {
41531
41840
  try {
41532
41841
  const stats2 = statSync9(targetPath);
41533
41842
  data.targetSize = stats2.size;
@@ -41596,15 +41905,15 @@ var init_logger2 = __esm({
41596
41905
  */
41597
41906
  cleanupOldLogs() {
41598
41907
  try {
41599
- const logsDir = join68(this.cleoDir, "logs");
41600
- if (!existsSync65(logsDir)) {
41908
+ const logsDir = join69(this.cleoDir, "logs");
41909
+ if (!existsSync66(logsDir)) {
41601
41910
  return;
41602
41911
  }
41603
41912
  const { readdirSync: readdirSync21, unlinkSync: unlinkSync5 } = __require("node:fs");
41604
41913
  const files = readdirSync21(logsDir).filter((f) => f.startsWith("migration-") && f.endsWith(".jsonl")).map((f) => ({
41605
41914
  name: f,
41606
- path: join68(logsDir, f),
41607
- mtime: statSync9(join68(logsDir, f)).mtime.getTime()
41915
+ path: join69(logsDir, f),
41916
+ mtime: statSync9(join69(logsDir, f)).mtime.getTime()
41608
41917
  })).sort((a, b) => b.mtime - a.mtime);
41609
41918
  const filesToRemove = files.slice(this.config.maxLogFiles);
41610
41919
  for (const file of filesToRemove) {
@@ -41700,8 +42009,8 @@ __export(state_exports, {
41700
42009
  verifySourceIntegrity: () => verifySourceIntegrity
41701
42010
  });
41702
42011
  import { readFile as readFile19, writeFile as writeFile13, unlink as unlink5 } from "node:fs/promises";
41703
- import { join as join69 } from "node:path";
41704
- import { existsSync as existsSync66 } from "node:fs";
42012
+ import { join as join70 } from "node:path";
42013
+ import { existsSync as existsSync67 } from "node:fs";
41705
42014
  import { createHash as createHash8 } from "node:crypto";
41706
42015
  async function computeFileChecksum(filePath) {
41707
42016
  try {
@@ -41723,8 +42032,8 @@ async function countRecords(filePath, key) {
41723
42032
  async function createMigrationState(cleoDir, sourceFiles) {
41724
42033
  const files = sourceFiles ?? {};
41725
42034
  if (!files.todoJson) {
41726
- const todoPath = join69(cleoDir, "todo.json");
41727
- if (existsSync66(todoPath)) {
42035
+ const todoPath = join70(cleoDir, "todo.json");
42036
+ if (existsSync67(todoPath)) {
41728
42037
  files.todoJson = {
41729
42038
  path: todoPath,
41730
42039
  checksum: await computeFileChecksum(todoPath),
@@ -41733,8 +42042,8 @@ async function createMigrationState(cleoDir, sourceFiles) {
41733
42042
  }
41734
42043
  }
41735
42044
  if (!files.sessionsJson) {
41736
- const sessionsPath = join69(cleoDir, "sessions.json");
41737
- if (existsSync66(sessionsPath)) {
42045
+ const sessionsPath = join70(cleoDir, "sessions.json");
42046
+ if (existsSync67(sessionsPath)) {
41738
42047
  files.sessionsJson = {
41739
42048
  path: sessionsPath,
41740
42049
  checksum: await computeFileChecksum(sessionsPath),
@@ -41743,8 +42052,8 @@ async function createMigrationState(cleoDir, sourceFiles) {
41743
42052
  }
41744
42053
  }
41745
42054
  if (!files.archiveJson) {
41746
- const archivePath = join69(cleoDir, "todo-archive.json");
41747
- if (existsSync66(archivePath)) {
42055
+ const archivePath = join70(cleoDir, "todo-archive.json");
42056
+ if (existsSync67(archivePath)) {
41748
42057
  files.archiveJson = {
41749
42058
  path: archivePath,
41750
42059
  checksum: await computeFileChecksum(archivePath),
@@ -41772,7 +42081,7 @@ async function createMigrationState(cleoDir, sourceFiles) {
41772
42081
  return state;
41773
42082
  }
41774
42083
  async function writeMigrationState(cleoDir, state) {
41775
- const statePath = join69(cleoDir, STATE_FILENAME);
42084
+ const statePath = join70(cleoDir, STATE_FILENAME);
41776
42085
  const tempPath = `${statePath}.tmp`;
41777
42086
  await writeFile13(tempPath, JSON.stringify(state, null, 2));
41778
42087
  await writeFile13(statePath, await readFile19(tempPath));
@@ -41847,7 +42156,7 @@ async function addMigrationWarning(cleoDir, warning) {
41847
42156
  }
41848
42157
  async function loadMigrationState(cleoDir) {
41849
42158
  try {
41850
- const statePath = join69(cleoDir, STATE_FILENAME);
42159
+ const statePath = join70(cleoDir, STATE_FILENAME);
41851
42160
  const content = await readFile19(statePath, "utf-8");
41852
42161
  return JSON.parse(content);
41853
42162
  } catch {
@@ -41889,7 +42198,7 @@ async function failMigration(cleoDir, error) {
41889
42198
  }
41890
42199
  async function clearMigrationState(cleoDir) {
41891
42200
  try {
41892
- const statePath = join69(cleoDir, STATE_FILENAME);
42201
+ const statePath = join70(cleoDir, STATE_FILENAME);
41893
42202
  await unlink5(statePath);
41894
42203
  } catch {
41895
42204
  }
@@ -41929,7 +42238,7 @@ async function verifySourceIntegrity(cleoDir) {
41929
42238
  const missing = [];
41930
42239
  for (const [key, fileInfo] of Object.entries(state.sourceFiles)) {
41931
42240
  if (!fileInfo) continue;
41932
- if (!existsSync66(fileInfo.path)) {
42241
+ if (!existsSync67(fileInfo.path)) {
41933
42242
  missing.push(key);
41934
42243
  continue;
41935
42244
  }
@@ -41960,8 +42269,8 @@ __export(migration_sqlite_exports, {
41960
42269
  migrateJsonToSqlite: () => migrateJsonToSqlite2,
41961
42270
  migrateJsonToSqliteAtomic: () => migrateJsonToSqliteAtomic
41962
42271
  });
41963
- import { existsSync as existsSync67, readFileSync as readFileSync48 } from "node:fs";
41964
- import { join as join70, dirname as dirname17 } from "node:path";
42272
+ import { existsSync as existsSync68, readFileSync as readFileSync49 } from "node:fs";
42273
+ import { join as join71, dirname as dirname17 } from "node:path";
41965
42274
  import { mkdirSync as mkdirSync17 } from "node:fs";
41966
42275
  import { drizzle as drizzle4 } from "drizzle-orm/sqlite-proxy";
41967
42276
  import { migrate as migrate4 } from "drizzle-orm/sqlite-proxy/migrator";
@@ -41999,26 +42308,26 @@ function countJsonRecords(cleoDir) {
41999
42308
  let tasks2 = 0;
42000
42309
  let archived = 0;
42001
42310
  let sessions2 = 0;
42002
- const todoPath = join70(cleoDir, "todo.json");
42003
- if (existsSync67(todoPath)) {
42311
+ const todoPath = join71(cleoDir, "todo.json");
42312
+ if (existsSync68(todoPath)) {
42004
42313
  try {
42005
- const data = JSON.parse(readFileSync48(todoPath, "utf-8"));
42314
+ const data = JSON.parse(readFileSync49(todoPath, "utf-8"));
42006
42315
  tasks2 = (data.tasks ?? []).length;
42007
42316
  } catch {
42008
42317
  }
42009
42318
  }
42010
- const archivePath = join70(cleoDir, "todo-archive.json");
42011
- if (existsSync67(archivePath)) {
42319
+ const archivePath = join71(cleoDir, "todo-archive.json");
42320
+ if (existsSync68(archivePath)) {
42012
42321
  try {
42013
- const data = JSON.parse(readFileSync48(archivePath, "utf-8"));
42322
+ const data = JSON.parse(readFileSync49(archivePath, "utf-8"));
42014
42323
  archived = (data.tasks ?? data.archivedTasks ?? []).length;
42015
42324
  } catch {
42016
42325
  }
42017
42326
  }
42018
- const sessionsPath = join70(cleoDir, "sessions.json");
42019
- if (existsSync67(sessionsPath)) {
42327
+ const sessionsPath = join71(cleoDir, "sessions.json");
42328
+ if (existsSync68(sessionsPath)) {
42020
42329
  try {
42021
- const data = JSON.parse(readFileSync48(sessionsPath, "utf-8"));
42330
+ const data = JSON.parse(readFileSync49(sessionsPath, "utf-8"));
42022
42331
  sessions2 = (data.sessions ?? []).length;
42023
42332
  } catch {
42024
42333
  }
@@ -42095,13 +42404,13 @@ async function migrateJsonToSqliteAtomic(cwd, tempDbPath, logger) {
42095
42404
  }
42096
42405
  }
42097
42406
  async function runMigrationDataImport(db, cleoDir, result, logger) {
42098
- const todoPath = join70(cleoDir, "todo.json");
42099
- if (existsSync67(todoPath)) {
42407
+ const todoPath = join71(cleoDir, "todo.json");
42408
+ if (existsSync68(todoPath)) {
42100
42409
  try {
42101
42410
  logger?.info("import", "read-todo", "Reading todo.json", {
42102
42411
  path: todoPath.replace(cleoDir, ".")
42103
42412
  });
42104
- const todoData = JSON.parse(readFileSync48(todoPath, "utf-8"));
42413
+ const todoData = JSON.parse(readFileSync49(todoPath, "utf-8"));
42105
42414
  const tasks2 = topoSortTasks(todoData.tasks ?? []);
42106
42415
  const totalTasks = tasks2.length;
42107
42416
  logger?.info("import", "tasks-start", `Starting import of ${totalTasks} tasks`, {
@@ -42170,13 +42479,13 @@ async function runMigrationDataImport(db, cleoDir, result, logger) {
42170
42479
  result.warnings.push("todo.json not found, skipping task import");
42171
42480
  logger?.warn("import", "todo-missing", "todo.json not found, skipping task import");
42172
42481
  }
42173
- const archivePath = join70(cleoDir, "todo-archive.json");
42174
- if (existsSync67(archivePath)) {
42482
+ const archivePath = join71(cleoDir, "todo-archive.json");
42483
+ if (existsSync68(archivePath)) {
42175
42484
  try {
42176
42485
  logger?.info("import", "read-archive", "Reading todo-archive.json", {
42177
42486
  path: archivePath.replace(cleoDir, ".")
42178
42487
  });
42179
- const archiveData = JSON.parse(readFileSync48(archivePath, "utf-8"));
42488
+ const archiveData = JSON.parse(readFileSync49(archivePath, "utf-8"));
42180
42489
  const archivedTasks = topoSortTasks(archiveData.tasks ?? archiveData.archivedTasks ?? []);
42181
42490
  const totalArchived = archivedTasks.length;
42182
42491
  logger?.info("import", "archive-start", `Starting import of ${totalArchived} archived tasks`, {
@@ -42229,13 +42538,13 @@ async function runMigrationDataImport(db, cleoDir, result, logger) {
42229
42538
  logger?.error("import", "parse-archive", errorMsg);
42230
42539
  }
42231
42540
  }
42232
- const sessionsPath = join70(cleoDir, "sessions.json");
42233
- if (existsSync67(sessionsPath)) {
42541
+ const sessionsPath = join71(cleoDir, "sessions.json");
42542
+ if (existsSync68(sessionsPath)) {
42234
42543
  try {
42235
42544
  logger?.info("import", "read-sessions", "Reading sessions.json", {
42236
42545
  path: sessionsPath.replace(cleoDir, ".")
42237
42546
  });
42238
- const sessionsData = JSON.parse(readFileSync48(sessionsPath, "utf-8"));
42547
+ const sessionsData = JSON.parse(readFileSync49(sessionsPath, "utf-8"));
42239
42548
  const sessions2 = sessionsData.sessions ?? [];
42240
42549
  const totalSessions = sessions2.length;
42241
42550
  logger?.info("import", "sessions-start", `Starting import of ${totalSessions} sessions`, {
@@ -42367,10 +42676,10 @@ async function migrateJsonToSqlite2(cwd, options) {
42367
42676
  return result;
42368
42677
  }
42369
42678
  const db = await getDb(cwd);
42370
- const todoPath = join70(cleoDir, "todo.json");
42371
- if (existsSync67(todoPath)) {
42679
+ const todoPath = join71(cleoDir, "todo.json");
42680
+ if (existsSync68(todoPath)) {
42372
42681
  try {
42373
- const todoData = JSON.parse(readFileSync48(todoPath, "utf-8"));
42682
+ const todoData = JSON.parse(readFileSync49(todoPath, "utf-8"));
42374
42683
  const tasks2 = topoSortTasks(todoData.tasks ?? []);
42375
42684
  for (const task of tasks2) {
42376
42685
  try {
@@ -42419,10 +42728,10 @@ async function migrateJsonToSqlite2(cwd, options) {
42419
42728
  } else {
42420
42729
  result.warnings.push("todo.json not found, skipping task import");
42421
42730
  }
42422
- const archivePath = join70(cleoDir, "todo-archive.json");
42423
- if (existsSync67(archivePath)) {
42731
+ const archivePath = join71(cleoDir, "todo-archive.json");
42732
+ if (existsSync68(archivePath)) {
42424
42733
  try {
42425
- const archiveData = JSON.parse(readFileSync48(archivePath, "utf-8"));
42734
+ const archiveData = JSON.parse(readFileSync49(archivePath, "utf-8"));
42426
42735
  const archivedTasks = topoSortTasks(archiveData.tasks ?? archiveData.archivedTasks ?? []);
42427
42736
  for (const task of archivedTasks) {
42428
42737
  try {
@@ -42459,11 +42768,11 @@ async function migrateJsonToSqlite2(cwd, options) {
42459
42768
  result.errors.push(`Failed to parse todo-archive.json: ${String(err)}`);
42460
42769
  }
42461
42770
  }
42462
- const sessionsPath = join70(cleoDir, "sessions.json");
42463
- if (existsSync67(sessionsPath)) {
42771
+ const sessionsPath = join71(cleoDir, "sessions.json");
42772
+ if (existsSync68(sessionsPath)) {
42464
42773
  try {
42465
42774
  const sessionsData = JSON.parse(
42466
- readFileSync48(sessionsPath, "utf-8")
42775
+ readFileSync49(sessionsPath, "utf-8")
42467
42776
  );
42468
42777
  const sessions2 = sessionsData.sessions ?? [];
42469
42778
  for (const session of sessions2) {
@@ -42531,8 +42840,8 @@ var init_migration_sqlite = __esm({
42531
42840
 
42532
42841
  // src/cli/index.ts
42533
42842
  import { Command, Help } from "commander";
42534
- import { readFileSync as readFileSync52 } from "node:fs";
42535
- import { join as join76 } from "node:path";
42843
+ import { readFileSync as readFileSync53 } from "node:fs";
42844
+ import { join as join77 } from "node:path";
42536
42845
 
42537
42846
  // src/cli/commands/add.ts
42538
42847
  init_cli();
@@ -43147,12 +43456,13 @@ function registerReleaseCommand(program2) {
43147
43456
  notes: opts["notes"]
43148
43457
  }, { command: "release" });
43149
43458
  });
43150
- release2.command("ship <version>").description("Ship a release: gates \u2192 changelog \u2192 commit \u2192 tag \u2192 push").requiredOption("--epic <id>", "Epic task ID for commit message (e.g. T5576)").option("--dry-run", "Preview all actions without writing anything").option("--no-push", "Commit and tag but skip git push").option("--remote <remote>", "Git remote to push to (default: origin)").action(async (version, opts) => {
43459
+ release2.command("ship <version>").description("Ship a release: gates \u2192 changelog \u2192 commit \u2192 tag \u2192 push").requiredOption("--epic <id>", "Epic task ID for commit message (e.g. T5576)").option("--dry-run", "Preview all actions without writing anything").option("--no-push", "Commit and tag but skip git push").option("--no-bump", "Skip version file bumping (default: bump if configured)").option("--remote <remote>", "Git remote to push to (default: origin)").action(async (version, opts) => {
43151
43460
  await dispatchFromCli("mutate", "pipeline", "release.ship", {
43152
43461
  version,
43153
43462
  epicId: opts["epic"],
43154
43463
  dryRun: opts["dryRun"],
43155
43464
  push: opts["push"] !== false,
43465
+ bump: opts["bump"] !== false,
43156
43466
  remote: opts["remote"]
43157
43467
  }, { command: "release" });
43158
43468
  });
@@ -43165,6 +43475,9 @@ function registerReleaseCommand(program2) {
43165
43475
  release2.command("changelog <version>").description("Generate changelog for a release").action(async (version) => {
43166
43476
  await dispatchFromCli("mutate", "pipeline", "release.changelog", { version }, { command: "release" });
43167
43477
  });
43478
+ release2.command("cancel <version>").description("Cancel and remove a release in draft or prepared state").action(async (version) => {
43479
+ await dispatchFromCli("mutate", "pipeline", "release.cancel", { version }, { command: "release" });
43480
+ });
43168
43481
  }
43169
43482
 
43170
43483
  // src/cli/commands/env.ts
@@ -43382,14 +43695,14 @@ init_errors();
43382
43695
  init_exit_codes();
43383
43696
  init_paths();
43384
43697
  init_json();
43385
- import { join as join62 } from "node:path";
43698
+ import { join as join63 } from "node:path";
43386
43699
  var VALID_CATEGORIES = ["write", "read", "sync", "maintenance"];
43387
43700
  var VALID_RELEVANCE = ["critical", "high", "medium", "low"];
43388
43701
  async function locateCommandsIndex() {
43389
43702
  const cleoHome = getCleoHome();
43390
43703
  const paths = [
43391
- join62(cleoHome, "docs", "commands", "COMMANDS-INDEX.json"),
43392
- join62(process.cwd(), "docs", "commands", "COMMANDS-INDEX.json")
43704
+ join63(cleoHome, "docs", "commands", "COMMANDS-INDEX.json"),
43705
+ join63(process.cwd(), "docs", "commands", "COMMANDS-INDEX.json")
43393
43706
  ];
43394
43707
  for (const p of paths) {
43395
43708
  const data = await readJson(p);
@@ -43470,9 +43783,9 @@ init_errors();
43470
43783
  init_json();
43471
43784
  init_paths();
43472
43785
  import { readdir as readdir2, readFile as readFile16 } from "node:fs/promises";
43473
- import { join as join63 } from "node:path";
43786
+ import { join as join64 } from "node:path";
43474
43787
  async function getScriptNames(projectRoot) {
43475
- const scriptsDir = join63(projectRoot, "scripts");
43788
+ const scriptsDir = join64(projectRoot, "scripts");
43476
43789
  try {
43477
43790
  const files = await readdir2(scriptsDir);
43478
43791
  return files.filter((f) => f.endsWith(".sh")).map((f) => f.replace(".sh", "")).sort();
@@ -43481,7 +43794,7 @@ async function getScriptNames(projectRoot) {
43481
43794
  }
43482
43795
  }
43483
43796
  async function getIndexedCommands(projectRoot) {
43484
- const indexPath = join63(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
43797
+ const indexPath = join64(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
43485
43798
  const index5 = await readJson(indexPath);
43486
43799
  if (!index5) return [];
43487
43800
  return index5.commands.map((c) => c.name).sort();
@@ -43514,7 +43827,7 @@ async function runGapCheck(_projectRoot, filterId) {
43514
43827
  const reviewFiles = files.filter((f) => f.endsWith(".md"));
43515
43828
  for (const file of reviewFiles) {
43516
43829
  if (filterId && !file.includes(filterId)) continue;
43517
- const filePath = join63(reviewDir, file);
43830
+ const filePath = join64(reviewDir, file);
43518
43831
  const content = await readFile16(filePath, "utf-8");
43519
43832
  const taskMatch = file.match(/^(T\d+)/);
43520
43833
  const taskId = taskMatch ? taskMatch[1] : "UNKNOWN";
@@ -43618,7 +43931,7 @@ init_paths();
43618
43931
  init_json();
43619
43932
  init_data_accessor();
43620
43933
  import { readFile as readFile17, rm as rm3, stat as stat3 } from "node:fs/promises";
43621
- import { join as join64 } from "node:path";
43934
+ import { join as join65 } from "node:path";
43622
43935
  function parseTaskId(content) {
43623
43936
  const match = content.match(/^\[T(\d+)\]/);
43624
43937
  return match ? `T${match[1]}` : null;
@@ -43674,7 +43987,7 @@ function registerExtractCommand(program2) {
43674
43987
  const accessor = await getAccessor();
43675
43988
  const taskData = await accessor.loadTaskFile();
43676
43989
  const cleoDir = getCleoDir();
43677
- const stateFile = join64(cleoDir, "sync", "todowrite-session.json");
43990
+ const stateFile = join65(cleoDir, "sync", "todowrite-session.json");
43678
43991
  let sessionState = null;
43679
43992
  try {
43680
43993
  sessionState = await readJson(stateFile);
@@ -43862,15 +44175,15 @@ init_exit_codes();
43862
44175
  init_paths();
43863
44176
  import { spawn as spawn2, execFileSync as execFileSync12 } from "node:child_process";
43864
44177
  import { readFile as readFile18, writeFile as writeFile12, mkdir as mkdir12, rm as rm4, stat as stat4 } from "node:fs/promises";
43865
- import { join as join65 } from "node:path";
44178
+ import { join as join66 } from "node:path";
43866
44179
  var DEFAULT_PORT = 3456;
43867
44180
  var DEFAULT_HOST = "127.0.0.1";
43868
44181
  function getWebPaths() {
43869
44182
  const cleoHome = getCleoHome();
43870
44183
  return {
43871
- pidFile: join65(cleoHome, "web-server.pid"),
43872
- configFile: join65(cleoHome, "web-server.json"),
43873
- logFile: join65(cleoHome, "logs", "web-server.log")
44184
+ pidFile: join66(cleoHome, "web-server.pid"),
44185
+ configFile: join66(cleoHome, "web-server.json"),
44186
+ logFile: join66(cleoHome, "logs", "web-server.log")
43874
44187
  };
43875
44188
  }
43876
44189
  function isProcessRunning(pid) {
@@ -43914,14 +44227,14 @@ function registerWebCommand(program2) {
43914
44227
  throw new CleoError(1 /* GENERAL_ERROR */, `Server already running (PID: ${status.pid})`);
43915
44228
  }
43916
44229
  const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
43917
- const distMcpDir = join65(projectRoot, "dist", "mcp");
43918
- await mkdir12(join65(getCleoHome(), "logs"), { recursive: true });
44230
+ const distMcpDir = join66(projectRoot, "dist", "mcp");
44231
+ await mkdir12(join66(getCleoHome(), "logs"), { recursive: true });
43919
44232
  await writeFile12(configFile, JSON.stringify({
43920
44233
  port,
43921
44234
  host,
43922
44235
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
43923
44236
  }));
43924
- const webIndexPath = join65(distMcpDir, "index.js");
44237
+ const webIndexPath = join66(distMcpDir, "index.js");
43925
44238
  try {
43926
44239
  await stat4(webIndexPath);
43927
44240
  } catch {
@@ -44145,13 +44458,13 @@ init_renderers();
44145
44458
  init_errors();
44146
44459
  init_exit_codes();
44147
44460
  init_paths();
44148
- import { existsSync as existsSync63, readFileSync as readFileSync46, writeFileSync as writeFileSync10, mkdirSync as mkdirSync15 } from "node:fs";
44461
+ import { existsSync as existsSync64, readFileSync as readFileSync47, writeFileSync as writeFileSync11, mkdirSync as mkdirSync15 } from "node:fs";
44149
44462
  import { execFileSync as execFileSync13 } from "node:child_process";
44150
- import { join as join66, dirname as dirname15 } from "node:path";
44463
+ import { join as join67, dirname as dirname15 } from "node:path";
44151
44464
  function getChangelogSource(cwd) {
44152
44465
  const configPath = getConfigPath(cwd);
44153
44466
  try {
44154
- const config = JSON.parse(readFileSync46(configPath, "utf-8"));
44467
+ const config = JSON.parse(readFileSync47(configPath, "utf-8"));
44155
44468
  return config?.release?.changelog?.source ?? "CHANGELOG.md";
44156
44469
  } catch {
44157
44470
  return "CHANGELOG.md";
@@ -44160,7 +44473,7 @@ function getChangelogSource(cwd) {
44160
44473
  function getEnabledPlatforms(cwd) {
44161
44474
  const configPath = getConfigPath(cwd);
44162
44475
  try {
44163
- const config = JSON.parse(readFileSync46(configPath, "utf-8"));
44476
+ const config = JSON.parse(readFileSync47(configPath, "utf-8"));
44164
44477
  const outputs = config?.release?.changelog?.outputs ?? [];
44165
44478
  return outputs.filter((o) => o.enabled);
44166
44479
  } catch {
@@ -44287,11 +44600,11 @@ function registerGenerateChangelogCommand(program2) {
44287
44600
  const targetPlatform = opts["platform"];
44288
44601
  const dryRun = !!opts["dryRun"];
44289
44602
  const sourceFile = getChangelogSource();
44290
- const sourcePath = join66(getProjectRoot(), sourceFile);
44291
- if (!existsSync63(sourcePath)) {
44603
+ const sourcePath = join67(getProjectRoot(), sourceFile);
44604
+ if (!existsSync64(sourcePath)) {
44292
44605
  throw new CleoError(4 /* NOT_FOUND */, `Changelog source not found: ${sourcePath}`);
44293
44606
  }
44294
- const sourceContent = readFileSync46(sourcePath, "utf-8");
44607
+ const sourceContent = readFileSync47(sourcePath, "utf-8");
44295
44608
  const repoSlug = getGitHubRepoSlug();
44296
44609
  const results = [];
44297
44610
  if (targetPlatform) {
@@ -44300,9 +44613,9 @@ function registerGenerateChangelogCommand(program2) {
44300
44613
  const outputPath = platformConfig?.path ?? getDefaultOutputPath(targetPlatform);
44301
44614
  const content = generateForPlatform(targetPlatform, sourceContent, repoSlug, limit);
44302
44615
  if (!dryRun) {
44303
- const fullPath = join66(getProjectRoot(), outputPath);
44616
+ const fullPath = join67(getProjectRoot(), outputPath);
44304
44617
  mkdirSync15(dirname15(fullPath), { recursive: true });
44305
- writeFileSync10(fullPath, content, "utf-8");
44618
+ writeFileSync11(fullPath, content, "utf-8");
44306
44619
  }
44307
44620
  results.push({ platform: targetPlatform, path: outputPath, written: !dryRun });
44308
44621
  } else {
@@ -44321,9 +44634,9 @@ function registerGenerateChangelogCommand(program2) {
44321
44634
  limit
44322
44635
  );
44323
44636
  if (!dryRun) {
44324
- const fullPath = join66(getProjectRoot(), platformConfig.path);
44637
+ const fullPath = join67(getProjectRoot(), platformConfig.path);
44325
44638
  mkdirSync15(dirname15(fullPath), { recursive: true });
44326
- writeFileSync10(fullPath, content, "utf-8");
44639
+ writeFileSync11(fullPath, content, "utf-8");
44327
44640
  }
44328
44641
  results.push({
44329
44642
  platform: platformConfig.platform,
@@ -45076,22 +45389,22 @@ function registerPlanCommand(program2) {
45076
45389
  }
45077
45390
 
45078
45391
  // src/core/otel/index.ts
45079
- import { readFileSync as readFileSync47, existsSync as existsSync64, writeFileSync as writeFileSync11, copyFileSync as copyFileSync4 } from "node:fs";
45080
- import { join as join67 } from "node:path";
45392
+ import { readFileSync as readFileSync48, existsSync as existsSync65, writeFileSync as writeFileSync12, copyFileSync as copyFileSync4 } from "node:fs";
45393
+ import { join as join68 } from "node:path";
45081
45394
  function getProjectRoot2() {
45082
45395
  let dir = process.cwd();
45083
45396
  while (dir !== "/") {
45084
- if (existsSync64(join67(dir, ".cleo", "config.json"))) return dir;
45085
- dir = join67(dir, "..");
45397
+ if (existsSync65(join68(dir, ".cleo", "config.json"))) return dir;
45398
+ dir = join68(dir, "..");
45086
45399
  }
45087
45400
  return process.cwd();
45088
45401
  }
45089
45402
  function getTokenFilePath() {
45090
- return join67(getProjectRoot2(), ".cleo", "metrics", "TOKEN_USAGE.jsonl");
45403
+ return join68(getProjectRoot2(), ".cleo", "metrics", "TOKEN_USAGE.jsonl");
45091
45404
  }
45092
45405
  function readJsonlFile(filePath) {
45093
- if (!existsSync64(filePath)) return [];
45094
- const content = readFileSync47(filePath, "utf-8").trim();
45406
+ if (!existsSync65(filePath)) return [];
45407
+ const content = readFileSync48(filePath, "utf-8").trim();
45095
45408
  if (!content) return [];
45096
45409
  return content.split("\n").map((line) => JSON.parse(line));
45097
45410
  }
@@ -45173,10 +45486,10 @@ async function getRealTokenUsage(_opts) {
45173
45486
  }
45174
45487
  async function clearOtelData() {
45175
45488
  const tokenFile = getTokenFilePath();
45176
- if (existsSync64(tokenFile)) {
45489
+ if (existsSync65(tokenFile)) {
45177
45490
  const backup = `${tokenFile}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
45178
45491
  copyFileSync4(tokenFile, backup);
45179
- writeFileSync11(tokenFile, "");
45492
+ writeFileSync12(tokenFile, "");
45180
45493
  return { message: "Token tracking cleared", backup };
45181
45494
  }
45182
45495
  return { message: "No token file to clear" };
@@ -45538,8 +45851,8 @@ init_storage_preflight();
45538
45851
  init_paths();
45539
45852
  init_storage_preflight();
45540
45853
  init_agent_outputs();
45541
- import { existsSync as existsSync68, readFileSync as readFileSync49, writeFileSync as writeFileSync12, mkdirSync as mkdirSync18, readdirSync as readdirSync19, copyFileSync as copyFileSync5 } from "node:fs";
45542
- import { join as join71 } from "node:path";
45854
+ import { existsSync as existsSync69, readFileSync as readFileSync50, writeFileSync as writeFileSync13, mkdirSync as mkdirSync18, readdirSync as readdirSync19, copyFileSync as copyFileSync5 } from "node:fs";
45855
+ import { join as join72 } from "node:path";
45543
45856
 
45544
45857
  // src/store/index.ts
45545
45858
  init_atomic();
@@ -45575,8 +45888,8 @@ async function runUpgrade(options = {}) {
45575
45888
  return { success: false, upToDate: false, dryRun: isDryRun, actions, applied: 0, errors: [String(err)] };
45576
45889
  }
45577
45890
  const cleoDir = getCleoDirAbsolute(options.cwd);
45578
- const dbPath = join71(cleoDir, "tasks.db");
45579
- const dbExists2 = existsSync68(dbPath);
45891
+ const dbPath = join72(cleoDir, "tasks.db");
45892
+ const dbExists2 = existsSync69(dbPath);
45580
45893
  const legacyRecordCount = preflight.details.todoJsonTaskCount + preflight.details.archiveJsonTaskCount + preflight.details.sessionsJsonCount;
45581
45894
  const needsMigration = !dbExists2 && legacyRecordCount > 0;
45582
45895
  const needsCleanup = dbExists2 && preflight.migrationNeeded;
@@ -45592,7 +45905,7 @@ async function runUpgrade(options = {}) {
45592
45905
  let migrationLock = null;
45593
45906
  try {
45594
45907
  const cleoDir2 = getCleoDirAbsolute(options.cwd);
45595
- const dbPath2 = join71(cleoDir2, "tasks.db");
45908
+ const dbPath2 = join72(cleoDir2, "tasks.db");
45596
45909
  try {
45597
45910
  migrationLock = await acquireLock(dbPath2, { stale: 3e4, retries: 0 });
45598
45911
  } catch {
@@ -45617,23 +45930,23 @@ async function runUpgrade(options = {}) {
45617
45930
  } = await Promise.resolve().then(() => (init_state(), state_exports));
45618
45931
  const logger = new MigrationLogger2(cleoDir2);
45619
45932
  await createMigrationState2(cleoDir2, {
45620
- todoJson: { path: join71(cleoDir2, "todo.json"), checksum: "" },
45621
- sessionsJson: { path: join71(cleoDir2, "sessions.json"), checksum: "" },
45622
- archiveJson: { path: join71(cleoDir2, "todo-archive.json"), checksum: "" }
45933
+ todoJson: { path: join72(cleoDir2, "todo.json"), checksum: "" },
45934
+ sessionsJson: { path: join72(cleoDir2, "sessions.json"), checksum: "" },
45935
+ archiveJson: { path: join72(cleoDir2, "todo-archive.json"), checksum: "" }
45623
45936
  });
45624
45937
  await updateMigrationPhase2(cleoDir2, "backup");
45625
45938
  logger.info("init", "start", "Migration state initialized");
45626
- const dbBackupPath = join71(cleoDir2, "backups", "safety", `tasks.db.pre-migration.${Date.now()}`);
45627
- const dbTempPath = join71(cleoDir2, "tasks.db.migrating");
45628
- if (existsSync68(dbPath2)) {
45629
- const backupDir = join71(cleoDir2, "backups", "safety");
45630
- if (!existsSync68(backupDir)) {
45939
+ const dbBackupPath = join72(cleoDir2, "backups", "safety", `tasks.db.pre-migration.${Date.now()}`);
45940
+ const dbTempPath = join72(cleoDir2, "tasks.db.migrating");
45941
+ if (existsSync69(dbPath2)) {
45942
+ const backupDir = join72(cleoDir2, "backups", "safety");
45943
+ if (!existsSync69(backupDir)) {
45631
45944
  mkdirSync18(backupDir, { recursive: true });
45632
45945
  }
45633
45946
  copyFileSync5(dbPath2, dbBackupPath);
45634
45947
  const { createHash: createHash9 } = await import("node:crypto");
45635
- const origChecksum = createHash9("sha256").update(readFileSync49(dbPath2)).digest("hex");
45636
- const backupChecksum = createHash9("sha256").update(readFileSync49(dbBackupPath)).digest("hex");
45948
+ const origChecksum = createHash9("sha256").update(readFileSync50(dbPath2)).digest("hex");
45949
+ const backupChecksum = createHash9("sha256").update(readFileSync50(dbBackupPath)).digest("hex");
45637
45950
  if (origChecksum !== backupChecksum) {
45638
45951
  throw new Error(
45639
45952
  `Backup verification failed: checksum mismatch. Aborting migration to prevent data loss.`
@@ -45648,14 +45961,14 @@ async function runUpgrade(options = {}) {
45648
45961
  }
45649
45962
  logger.info("backup", "verified", "Backup integrity verified", { checksum: origChecksum });
45650
45963
  }
45651
- if (existsSync68(dbTempPath)) {
45964
+ if (existsSync69(dbTempPath)) {
45652
45965
  const { unlinkSync: unlinkSync5 } = await import("node:fs");
45653
45966
  unlinkSync5(dbTempPath);
45654
45967
  }
45655
- const configPath = join71(cleoDir2, "config.json");
45968
+ const configPath = join72(cleoDir2, "config.json");
45656
45969
  let configBackup = null;
45657
- if (existsSync68(configPath)) {
45658
- configBackup = readFileSync49(configPath, "utf-8");
45970
+ if (existsSync69(configPath)) {
45971
+ configBackup = readFileSync50(configPath, "utf-8");
45659
45972
  }
45660
45973
  const { resetDbState: resetDbState2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
45661
45974
  resetDbState2();
@@ -45681,10 +45994,10 @@ async function runUpgrade(options = {}) {
45681
45994
  resetDbState2();
45682
45995
  if (result.success) {
45683
45996
  const totalImported = result.tasksImported + result.archivedImported;
45684
- if (totalImported === 0 && existsSync68(dbBackupPath)) {
45997
+ if (totalImported === 0 && existsSync69(dbBackupPath)) {
45685
45998
  copyFileSync5(dbBackupPath, dbPath2);
45686
45999
  if (configBackup) {
45687
- writeFileSync12(configPath, configBackup);
46000
+ writeFileSync13(configPath, configBackup);
45688
46001
  }
45689
46002
  actions.push({
45690
46003
  action: "storage_migration",
@@ -45695,9 +46008,9 @@ async function runUpgrade(options = {}) {
45695
46008
  errors.push("Migration imported 0 tasks \u2014 restored from backup to prevent data loss");
45696
46009
  } else {
45697
46010
  let config = {};
45698
- if (existsSync68(configPath)) {
46011
+ if (existsSync69(configPath)) {
45699
46012
  try {
45700
- config = JSON.parse(readFileSync49(configPath, "utf-8"));
46013
+ config = JSON.parse(readFileSync50(configPath, "utf-8"));
45701
46014
  } catch {
45702
46015
  }
45703
46016
  }
@@ -45705,7 +46018,7 @@ async function runUpgrade(options = {}) {
45705
46018
  config.storage = {};
45706
46019
  }
45707
46020
  config.storage.engine = "sqlite";
45708
- writeFileSync12(configPath, JSON.stringify(config, null, 2));
46021
+ writeFileSync13(configPath, JSON.stringify(config, null, 2));
45709
46022
  actions.push({
45710
46023
  action: "storage_migration",
45711
46024
  status: "applied",
@@ -45723,11 +46036,11 @@ async function runUpgrade(options = {}) {
45723
46036
  logger.info("complete", "finish", "Migration completed successfully");
45724
46037
  }
45725
46038
  } else {
45726
- if (existsSync68(dbBackupPath)) {
46039
+ if (existsSync69(dbBackupPath)) {
45727
46040
  copyFileSync5(dbBackupPath, dbPath2);
45728
46041
  }
45729
46042
  if (configBackup) {
45730
- writeFileSync12(configPath, configBackup);
46043
+ writeFileSync13(configPath, configBackup);
45731
46044
  }
45732
46045
  await updateMigrationPhase2(cleoDir2, "failed");
45733
46046
  for (const error of result.errors) {
@@ -45746,12 +46059,12 @@ async function runUpgrade(options = {}) {
45746
46059
  } catch (err) {
45747
46060
  try {
45748
46061
  const cleoDir2 = getCleoDirAbsolute(options.cwd);
45749
- const dbPath2 = join71(cleoDir2, "tasks.db");
45750
- const safetyDir = join71(cleoDir2, "backups", "safety");
45751
- if (existsSync68(safetyDir)) {
46062
+ const dbPath2 = join72(cleoDir2, "tasks.db");
46063
+ const safetyDir = join72(cleoDir2, "backups", "safety");
46064
+ if (existsSync69(safetyDir)) {
45752
46065
  const backups = readdirSync19(safetyDir).filter((f) => f.startsWith("tasks.db.pre-migration.")).sort().reverse();
45753
- if (backups.length > 0 && !existsSync68(dbPath2)) {
45754
- copyFileSync5(join71(safetyDir, backups[0]), dbPath2);
46066
+ if (backups.length > 0 && !existsSync69(dbPath2)) {
46067
+ copyFileSync5(join72(safetyDir, backups[0]), dbPath2);
45755
46068
  }
45756
46069
  }
45757
46070
  } catch {
@@ -45786,7 +46099,7 @@ async function runUpgrade(options = {}) {
45786
46099
  details: preflight.summary
45787
46100
  });
45788
46101
  }
45789
- if (existsSync68(dbPath)) {
46102
+ if (existsSync69(dbPath)) {
45790
46103
  try {
45791
46104
  const { runAllRepairs: runAllRepairs2 } = await Promise.resolve().then(() => (init_repair(), repair_exports));
45792
46105
  const repairActions = await runAllRepairs2(options.cwd, isDryRun);
@@ -45796,8 +46109,8 @@ async function runUpgrade(options = {}) {
45796
46109
  } catch {
45797
46110
  }
45798
46111
  }
45799
- if (existsSync68(dbPath)) {
45800
- const legacySequenceFiles = [".sequence", ".sequence.json"].filter((f) => existsSync68(join71(cleoDir, f)));
46112
+ if (existsSync69(dbPath)) {
46113
+ const legacySequenceFiles = [".sequence", ".sequence.json"].filter((f) => existsSync69(join72(cleoDir, f)));
45801
46114
  if (legacySequenceFiles.length > 0) {
45802
46115
  if (isDryRun) {
45803
46116
  actions.push({
@@ -45826,7 +46139,7 @@ async function runUpgrade(options = {}) {
45826
46139
  }
45827
46140
  if (needsCleanup) {
45828
46141
  const staleJsonFiles = ["todo.json", "sessions.json", "todo-archive.json", ".sequence", ".sequence.json"];
45829
- const foundStale = staleJsonFiles.filter((f) => existsSync68(join71(cleoDir, f)));
46142
+ const foundStale = staleJsonFiles.filter((f) => existsSync69(join72(cleoDir, f)));
45830
46143
  if (foundStale.length > 0) {
45831
46144
  if (isDryRun) {
45832
46145
  actions.push({
@@ -45836,15 +46149,15 @@ async function runUpgrade(options = {}) {
45836
46149
  });
45837
46150
  } else {
45838
46151
  try {
45839
- const backupDir = join71(cleoDir, ".backups", `legacy-json-${Date.now()}`);
46152
+ const backupDir = join72(cleoDir, ".backups", `legacy-json-${Date.now()}`);
45840
46153
  mkdirSync18(backupDir, { recursive: true });
45841
46154
  for (const f of foundStale) {
45842
- const src = join71(cleoDir, f);
45843
- copyFileSync5(src, join71(backupDir, f));
46155
+ const src = join72(cleoDir, f);
46156
+ copyFileSync5(src, join72(backupDir, f));
45844
46157
  }
45845
46158
  const { unlinkSync: unlinkSync5 } = await import("node:fs");
45846
46159
  for (const f of foundStale) {
45847
- unlinkSync5(join71(cleoDir, f));
46160
+ unlinkSync5(join72(cleoDir, f));
45848
46161
  }
45849
46162
  actions.push({
45850
46163
  action: "stale_json_cleanup",
@@ -45864,7 +46177,7 @@ async function runUpgrade(options = {}) {
45864
46177
  if (options.includeGlobal) {
45865
46178
  try {
45866
46179
  const globalDir = getCleoHome();
45867
- const globalPreflight = checkStorageMigration(join71(globalDir, ".."));
46180
+ const globalPreflight = checkStorageMigration(join72(globalDir, ".."));
45868
46181
  if (globalPreflight.migrationNeeded) {
45869
46182
  actions.push({
45870
46183
  action: "global_storage_check",
@@ -45885,8 +46198,8 @@ async function runUpgrade(options = {}) {
45885
46198
  try {
45886
46199
  const projectRoot = getProjectRoot(options.cwd);
45887
46200
  if (isDryRun) {
45888
- const gitignorePath = join71(cleoDir, ".gitignore");
45889
- if (!existsSync68(gitignorePath)) {
46201
+ const gitignorePath = join72(cleoDir, ".gitignore");
46202
+ if (!existsSync69(gitignorePath)) {
45890
46203
  actions.push({ action: "gitignore_integrity", status: "preview", details: "Would create .cleo/.gitignore from template" });
45891
46204
  } else {
45892
46205
  actions.push({ action: "gitignore_integrity", status: "preview", details: "Would verify .cleo/.gitignore matches template" });
@@ -45929,12 +46242,12 @@ async function runUpgrade(options = {}) {
45929
46242
  try {
45930
46243
  const projectRootForContext = getProjectRoot(options.cwd);
45931
46244
  if (isDryRun) {
45932
- const contextPath = join71(cleoDir, "project-context.json");
45933
- if (!existsSync68(contextPath)) {
46245
+ const contextPath = join72(cleoDir, "project-context.json");
46246
+ if (!existsSync69(contextPath)) {
45934
46247
  actions.push({ action: "project_context_detection", status: "preview", details: "Would detect and create project-context.json" });
45935
46248
  } else {
45936
46249
  try {
45937
- const context = JSON.parse(readFileSync49(contextPath, "utf-8"));
46250
+ const context = JSON.parse(readFileSync50(contextPath, "utf-8"));
45938
46251
  if (context.detectedAt) {
45939
46252
  const daysSince = (Date.now() - new Date(context.detectedAt).getTime()) / (1e3 * 60 * 60 * 24);
45940
46253
  if (daysSince > 30) {
@@ -46095,7 +46408,7 @@ async function runUpgrade(options = {}) {
46095
46408
  init_runtime();
46096
46409
  import { readFile as readFile20 } from "node:fs/promises";
46097
46410
  import * as readline from "node:readline";
46098
- import { join as join72 } from "node:path";
46411
+ import { join as join73 } from "node:path";
46099
46412
  import { execFile as execFile7 } from "node:child_process";
46100
46413
  import { promisify as promisify9 } from "node:util";
46101
46414
  init_build_config();
@@ -46104,7 +46417,7 @@ var GITHUB_REPO = BUILD_CONFIG.repository.fullName;
46104
46417
  async function getCurrentVersion() {
46105
46418
  const cleoHome = getCleoHome();
46106
46419
  try {
46107
- const content = await readFile20(join72(cleoHome, "VERSION"), "utf-8");
46420
+ const content = await readFile20(join73(cleoHome, "VERSION"), "utf-8");
46108
46421
  return (content.split("\n")[0] ?? "unknown").trim();
46109
46422
  } catch {
46110
46423
  return "unknown";
@@ -46151,7 +46464,7 @@ async function writeRuntimeVersionMetadata(mode, source, version) {
46151
46464
  `installed=${(/* @__PURE__ */ new Date()).toISOString()}`
46152
46465
  ];
46153
46466
  await import("node:fs/promises").then(
46154
- ({ writeFile: writeFile15, mkdir: mkdir14 }) => mkdir14(cleoHome, { recursive: true }).then(() => writeFile15(join72(cleoHome, "VERSION"), `${lines.join("\n")}
46467
+ ({ writeFile: writeFile15, mkdir: mkdir14 }) => mkdir14(cleoHome, { recursive: true }).then(() => writeFile15(join73(cleoHome, "VERSION"), `${lines.join("\n")}
46155
46468
  `, "utf-8"))
46156
46469
  );
46157
46470
  }
@@ -46510,14 +46823,14 @@ function registerVerifyCommand(program2) {
46510
46823
 
46511
46824
  // src/cli/commands/detect-drift.ts
46512
46825
  init_renderers();
46513
- import { readFileSync as readFileSync50, existsSync as existsSync69, readdirSync as readdirSync20 } from "node:fs";
46514
- import { join as join73, dirname as dirname18 } from "node:path";
46826
+ import { readFileSync as readFileSync51, existsSync as existsSync70, readdirSync as readdirSync20 } from "node:fs";
46827
+ import { join as join74, dirname as dirname18 } from "node:path";
46515
46828
  import { fileURLToPath as fileURLToPath5 } from "node:url";
46516
46829
  function findProjectRoot() {
46517
46830
  const currentFile = fileURLToPath5(import.meta.url);
46518
46831
  let currentDir = dirname18(currentFile);
46519
46832
  while (currentDir !== "/") {
46520
- if (existsSync69(join73(currentDir, "package.json"))) {
46833
+ if (existsSync70(join74(currentDir, "package.json"))) {
46521
46834
  return currentDir;
46522
46835
  }
46523
46836
  const parent = dirname18(currentDir);
@@ -46555,16 +46868,16 @@ function registerDetectDriftCommand(program2) {
46555
46868
  };
46556
46869
  const safeRead = (path) => {
46557
46870
  try {
46558
- return readFileSync50(path, "utf-8");
46871
+ return readFileSync51(path, "utf-8");
46559
46872
  } catch {
46560
46873
  return "";
46561
46874
  }
46562
46875
  };
46563
46876
  try {
46564
- const specPath = join73(projectRoot, "docs", "specs", "CLEO-OPERATIONS-REFERENCE.md");
46565
- const queryPath = join73(projectRoot, "src", "mcp", "gateways", "query.ts");
46566
- const mutatePath = join73(projectRoot, "src", "mcp", "gateways", "mutate.ts");
46567
- if (!existsSync69(specPath)) {
46877
+ const specPath = join74(projectRoot, "docs", "specs", "CLEO-OPERATIONS-REFERENCE.md");
46878
+ const queryPath = join74(projectRoot, "src", "mcp", "gateways", "query.ts");
46879
+ const mutatePath = join74(projectRoot, "src", "mcp", "gateways", "mutate.ts");
46880
+ if (!existsSync70(specPath)) {
46568
46881
  addCheck("Gateway-to-spec sync", "fail", "CLEO-OPERATIONS-REFERENCE.md missing", [{
46569
46882
  severity: "error",
46570
46883
  category: "spec",
@@ -46572,7 +46885,7 @@ function registerDetectDriftCommand(program2) {
46572
46885
  file: specPath,
46573
46886
  recommendation: "Create docs/specs/CLEO-OPERATIONS-REFERENCE.md with canonical operation definitions"
46574
46887
  }]);
46575
- } else if (!existsSync69(queryPath) || !existsSync69(mutatePath)) {
46888
+ } else if (!existsSync70(queryPath) || !existsSync70(mutatePath)) {
46576
46889
  addCheck("Gateway-to-spec sync", "fail", "MCP gateway files missing", [{
46577
46890
  severity: "error",
46578
46891
  category: "implementation",
@@ -46626,16 +46939,16 @@ function registerDetectDriftCommand(program2) {
46626
46939
  }]);
46627
46940
  }
46628
46941
  try {
46629
- const cliDir = join73(projectRoot, "src", "cli", "commands");
46630
- const coreDir = join73(projectRoot, "src", "core");
46631
- if (!existsSync69(cliDir)) {
46942
+ const cliDir = join74(projectRoot, "src", "cli", "commands");
46943
+ const coreDir = join74(projectRoot, "src", "core");
46944
+ if (!existsSync70(cliDir)) {
46632
46945
  addCheck("CLI-to-core sync", "fail", "CLI commands directory missing", [{
46633
46946
  severity: "error",
46634
46947
  category: "structure",
46635
46948
  message: "src/cli/commands/ directory not found",
46636
46949
  recommendation: "Verify TypeScript source structure is intact"
46637
46950
  }]);
46638
- } else if (!existsSync69(coreDir)) {
46951
+ } else if (!existsSync70(coreDir)) {
46639
46952
  addCheck("CLI-to-core sync", "fail", "Core directory missing", [{
46640
46953
  severity: "error",
46641
46954
  category: "structure",
@@ -46650,8 +46963,8 @@ function registerDetectDriftCommand(program2) {
46650
46963
  addCheck("CLI-to-core sync", "fail", `Error: ${e.message}`);
46651
46964
  }
46652
46965
  try {
46653
- const domainsDir = join73(projectRoot, "src", "mcp", "domains");
46654
- if (!existsSync69(domainsDir)) {
46966
+ const domainsDir = join74(projectRoot, "src", "mcp", "domains");
46967
+ if (!existsSync70(domainsDir)) {
46655
46968
  addCheck("Domain handler coverage", "fail", "MCP domains directory missing", [{
46656
46969
  severity: "error",
46657
46970
  category: "structure",
@@ -46666,8 +46979,8 @@ function registerDetectDriftCommand(program2) {
46666
46979
  addCheck("Domain handler coverage", "fail", `Error: ${e.message}`);
46667
46980
  }
46668
46981
  try {
46669
- const matrixPath = join73(projectRoot, "src", "dispatch", "lib", "capability-matrix.ts");
46670
- if (!existsSync69(matrixPath)) {
46982
+ const matrixPath = join74(projectRoot, "src", "dispatch", "lib", "capability-matrix.ts");
46983
+ if (!existsSync70(matrixPath)) {
46671
46984
  addCheck("Capability matrix", "fail", "Capability matrix missing", [{
46672
46985
  severity: "error",
46673
46986
  category: "configuration",
@@ -46681,8 +46994,8 @@ function registerDetectDriftCommand(program2) {
46681
46994
  addCheck("Capability matrix", "fail", `Error: ${e.message}`);
46682
46995
  }
46683
46996
  try {
46684
- const schemaPath = join73(projectRoot, "src", "store", "schema.ts");
46685
- if (!existsSync69(schemaPath)) {
46997
+ const schemaPath = join74(projectRoot, "src", "store", "schema.ts");
46998
+ if (!existsSync70(schemaPath)) {
46686
46999
  addCheck("Schema validation", "fail", "Schema definition missing", [{
46687
47000
  severity: "error",
46688
47001
  category: "data-model",
@@ -46707,10 +47020,10 @@ function registerDetectDriftCommand(program2) {
46707
47020
  addCheck("Schema validation", "fail", `Error: ${e.message}`);
46708
47021
  }
46709
47022
  try {
46710
- const visionPath = join73(projectRoot, "docs", "concepts", "CLEO-VISION.md");
46711
- const specPath = join73(projectRoot, "docs", "specs", "PORTABLE-BRAIN-SPEC.md");
47023
+ const visionPath = join74(projectRoot, "docs", "concepts", "CLEO-VISION.md");
47024
+ const specPath = join74(projectRoot, "docs", "specs", "PORTABLE-BRAIN-SPEC.md");
46712
47025
  const issues = [];
46713
- if (!existsSync69(visionPath)) {
47026
+ if (!existsSync70(visionPath)) {
46714
47027
  issues.push({
46715
47028
  severity: "error",
46716
47029
  category: "vision",
@@ -46719,7 +47032,7 @@ function registerDetectDriftCommand(program2) {
46719
47032
  recommendation: "Create docs/concepts/CLEO-VISION.md with project vision"
46720
47033
  });
46721
47034
  }
46722
- if (!existsSync69(specPath)) {
47035
+ if (!existsSync70(specPath)) {
46723
47036
  issues.push({
46724
47037
  severity: "error",
46725
47038
  category: "spec",
@@ -46756,8 +47069,8 @@ function registerDetectDriftCommand(program2) {
46756
47069
  addCheck("Canonical identity", "fail", `Error: ${e.message}`);
46757
47070
  }
46758
47071
  try {
46759
- const injectionPath = join73(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
46760
- if (!existsSync69(injectionPath)) {
47072
+ const injectionPath = join74(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
47073
+ if (!existsSync70(injectionPath)) {
46761
47074
  addCheck("Agent injection", "fail", "Agent injection template missing", [{
46762
47075
  severity: "error",
46763
47076
  category: "agent-support",
@@ -46782,8 +47095,8 @@ function registerDetectDriftCommand(program2) {
46782
47095
  addCheck("Agent injection", "fail", `Error: ${e.message}`);
46783
47096
  }
46784
47097
  try {
46785
- const exitCodesPath = join73(projectRoot, "src", "types", "exit-codes.ts");
46786
- if (!existsSync69(exitCodesPath)) {
47098
+ const exitCodesPath = join74(projectRoot, "src", "types", "exit-codes.ts");
47099
+ if (!existsSync70(exitCodesPath)) {
46787
47100
  addCheck("Exit codes", "fail", "Exit codes definition missing", [{
46788
47101
  severity: "error",
46789
47102
  category: "protocol",
@@ -47022,8 +47335,8 @@ function registerRemoteCommand(program2) {
47022
47335
  init_renderers();
47023
47336
  init_paths();
47024
47337
  import { mkdir as mkdir13, writeFile as writeFile14, readFile as readFile21 } from "node:fs/promises";
47025
- import { existsSync as existsSync70, readFileSync as readFileSync51 } from "node:fs";
47026
- import { join as join74, resolve as resolve10, dirname as dirname19 } from "node:path";
47338
+ import { existsSync as existsSync71, readFileSync as readFileSync52 } from "node:fs";
47339
+ import { join as join75, resolve as resolve10, dirname as dirname19 } from "node:path";
47027
47340
  import { homedir as homedir4 } from "node:os";
47028
47341
  import { fileURLToPath as fileURLToPath6 } from "node:url";
47029
47342
  function registerInstallGlobalCommand(program2) {
@@ -47033,17 +47346,17 @@ function registerInstallGlobalCommand(program2) {
47033
47346
  const warnings = [];
47034
47347
  try {
47035
47348
  const cleoHome = getCleoHome();
47036
- const globalTemplatesDir = join74(cleoHome, "templates");
47349
+ const globalTemplatesDir = join75(cleoHome, "templates");
47037
47350
  if (!isDryRun) {
47038
47351
  await mkdir13(globalTemplatesDir, { recursive: true });
47039
47352
  }
47040
47353
  try {
47041
47354
  const thisFile = fileURLToPath6(import.meta.url);
47042
47355
  const packageRoot = resolve10(dirname19(thisFile), "..", "..", "..");
47043
- const templatePath = join74(packageRoot, "templates", "CLEO-INJECTION.md");
47044
- if (existsSync70(templatePath)) {
47045
- const content = readFileSync51(templatePath, "utf-8");
47046
- const globalPath = join74(globalTemplatesDir, "CLEO-INJECTION.md");
47356
+ const templatePath = join75(packageRoot, "templates", "CLEO-INJECTION.md");
47357
+ if (existsSync71(templatePath)) {
47358
+ const content = readFileSync52(templatePath, "utf-8");
47359
+ const globalPath = join75(globalTemplatesDir, "CLEO-INJECTION.md");
47047
47360
  if (!isDryRun) {
47048
47361
  await writeFile14(globalPath, content);
47049
47362
  }
@@ -47053,12 +47366,12 @@ function registerInstallGlobalCommand(program2) {
47053
47366
  warnings.push("Could not refresh CLEO-INJECTION.md template");
47054
47367
  }
47055
47368
  const globalAgentsDir = getAgentsHome();
47056
- const globalAgentsMd = join74(globalAgentsDir, "AGENTS.md");
47369
+ const globalAgentsMd = join75(globalAgentsDir, "AGENTS.md");
47057
47370
  try {
47058
47371
  const { inject, getInstalledProviders: getInstalledProviders3, injectAll: injectAll2, buildInjectionContent: buildInjectionContent2 } = await import("@cleocode/caamp");
47059
47372
  if (!isDryRun) {
47060
47373
  await mkdir13(globalAgentsDir, { recursive: true });
47061
- if (existsSync70(globalAgentsMd)) {
47374
+ if (existsSync71(globalAgentsMd)) {
47062
47375
  const content = await readFile21(globalAgentsMd, "utf8");
47063
47376
  const stripped = content.replace(/\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g, "");
47064
47377
  if (stripped !== content) {
@@ -47077,8 +47390,8 @@ function registerInstallGlobalCommand(program2) {
47077
47390
  const injectionContent = buildInjectionContent2({ references: ["@~/.agents/AGENTS.md"] });
47078
47391
  if (!isDryRun) {
47079
47392
  for (const provider of providers) {
47080
- const instructFilePath = join74(provider.pathGlobal, provider.instructFile);
47081
- if (existsSync70(instructFilePath)) {
47393
+ const instructFilePath = join75(provider.pathGlobal, provider.instructFile);
47394
+ if (existsSync71(instructFilePath)) {
47082
47395
  const fileContent = await readFile21(instructFilePath, "utf8");
47083
47396
  const stripped = fileContent.replace(/\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g, "");
47084
47397
  if (stripped !== fileContent) {
@@ -47093,7 +47406,7 @@ function registerInstallGlobalCommand(program2) {
47093
47406
  }
47094
47407
  } else {
47095
47408
  for (const p of providers) {
47096
- const displayPath = join74(p.pathGlobal, p.instructFile).replace(homedir4(), "~");
47409
+ const displayPath = join75(p.pathGlobal, p.instructFile).replace(homedir4(), "~");
47097
47410
  created.push(`${displayPath} (would update CAAMP block)`);
47098
47411
  }
47099
47412
  }
@@ -47299,7 +47612,7 @@ init_paths();
47299
47612
  init_paths();
47300
47613
  init_brain_sqlite();
47301
47614
  init_brain_search();
47302
- import { existsSync as existsSync71 } from "node:fs";
47615
+ import { existsSync as existsSync72 } from "node:fs";
47303
47616
  import { createRequire as createRequire5 } from "node:module";
47304
47617
  var _require5 = createRequire5(import.meta.url);
47305
47618
  var { DatabaseSync: DatabaseSync3 } = _require5("node:sqlite");
@@ -47343,7 +47656,7 @@ async function migrateClaudeMem(projectRoot, options = {}) {
47343
47656
  errors: [],
47344
47657
  dryRun
47345
47658
  };
47346
- if (!existsSync71(sourcePath)) {
47659
+ if (!existsSync72(sourcePath)) {
47347
47660
  throw new Error(
47348
47661
  `claude-mem database not found at: ${sourcePath}
47349
47662
  Expected location: ~/.claude-mem/claude-mem.db
@@ -47745,10 +48058,10 @@ init_config();
47745
48058
  // src/cli/logger-bootstrap.ts
47746
48059
  init_logger();
47747
48060
  init_project_info();
47748
- import { join as join75 } from "node:path";
48061
+ import { join as join76 } from "node:path";
47749
48062
  function initCliLogger(cwd, loggingConfig) {
47750
48063
  const projectInfo = getProjectInfoSync(cwd);
47751
- initLogger(join75(cwd, ".cleo"), loggingConfig, projectInfo?.projectHash);
48064
+ initLogger(join76(cwd, ".cleo"), loggingConfig, projectInfo?.projectHash);
47752
48065
  }
47753
48066
 
47754
48067
  // src/cli/index.ts
@@ -47981,8 +48294,8 @@ Upgrade options:
47981
48294
  }
47982
48295
  function getPackageVersion() {
47983
48296
  try {
47984
- const moduleRoot = join76(import.meta.dirname ?? "", "..", "..");
47985
- const pkg = JSON.parse(readFileSync52(join76(moduleRoot, "package.json"), "utf-8"));
48297
+ const moduleRoot = join77(import.meta.dirname ?? "", "..", "..");
48298
+ const pkg = JSON.parse(readFileSync53(join77(moduleRoot, "package.json"), "utf-8"));
47986
48299
  return pkg.version ?? "0.0.0";
47987
48300
  } catch {
47988
48301
  return "0.0.0";
@@ -48093,7 +48406,7 @@ program.hook("preAction", async () => {
48093
48406
  const config = await loadConfig();
48094
48407
  initCliLogger(process.cwd(), config.logging);
48095
48408
  const { pruneAuditLog: pruneAuditLog2 } = await Promise.resolve().then(() => (init_audit_prune(), audit_prune_exports));
48096
- pruneAuditLog2(join76(process.cwd(), ".cleo"), config.logging).catch(() => {
48409
+ pruneAuditLog2(join77(process.cwd(), ".cleo"), config.logging).catch(() => {
48097
48410
  });
48098
48411
  } catch {
48099
48412
  }
@@ -48133,7 +48446,7 @@ program.hook("preAction", (thisCommand) => {
48133
48446
  }
48134
48447
  });
48135
48448
  if (process.argv[2] === "mcp") {
48136
- const mcpPath = join76(import.meta.dirname ?? "", "..", "mcp", "index.js");
48449
+ const mcpPath = join77(import.meta.dirname ?? "", "..", "mcp", "index.js");
48137
48450
  const { spawn: spawn3 } = await import("node:child_process");
48138
48451
  const child = spawn3(
48139
48452
  process.execPath,