@cleocode/cleo 2026.3.29 → 2026.3.30

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/mcp/index.js CHANGED
@@ -4599,6 +4599,118 @@ var init_handoff = __esm({
4599
4599
  }
4600
4600
  });
4601
4601
 
4602
+ // src/core/metrics/provider-detection.ts
4603
+ var provider_detection_exports = {};
4604
+ __export(provider_detection_exports, {
4605
+ detectRuntimeProviderContext: () => detectRuntimeProviderContext,
4606
+ resetRuntimeProviderContextCache: () => resetRuntimeProviderContextCache,
4607
+ selectRuntimeProviderContext: () => selectRuntimeProviderContext
4608
+ });
4609
+ import { basename as basename2 } from "node:path";
4610
+ import {
4611
+ detectProjectProviders,
4612
+ getProvider,
4613
+ resolveAlias
4614
+ } from "@cleocode/caamp";
4615
+ function inferProviderFromVendor(vendor) {
4616
+ const value = (vendor ?? "").trim().toLowerCase();
4617
+ if (!value) return void 0;
4618
+ if (value.includes("anthropic") || value.includes("claude")) return "anthropic";
4619
+ if (value.includes("openai") || value.includes("codex") || value.includes("chatgpt"))
4620
+ return "openai";
4621
+ if (value.includes("google") || value.includes("gemini")) return "google";
4622
+ if (value.includes("xai") || value.includes("grok")) return "xai";
4623
+ return void 0;
4624
+ }
4625
+ function getRuntimeHints(snapshot) {
4626
+ const argv = snapshot.argv ?? process.argv;
4627
+ const env = snapshot.env ?? process.env;
4628
+ const hints = /* @__PURE__ */ new Set();
4629
+ const bin = basename2(argv[1] ?? argv[0] ?? "").replace(/\.[^.]+$/, "");
4630
+ if (bin) hints.add(bin);
4631
+ if (env["CLAUDE_CODE_ENABLE_TELEMETRY"] || env["CLAUDE_CODE_ENTRYPOINT"]) {
4632
+ hints.add("claude-code");
4633
+ hints.add("claude");
4634
+ }
4635
+ if (env["OPENCODE_AGENT"] || env["OPENCODE"]) {
4636
+ hints.add("opencode");
4637
+ }
4638
+ if (env["CURSOR_TRACE_ID"] || env["CURSOR_AGENT"]) {
4639
+ hints.add("cursor");
4640
+ }
4641
+ return Array.from(hints);
4642
+ }
4643
+ function pickDetectionByHint(detections, hints) {
4644
+ for (const hint of hints) {
4645
+ const resolved = resolveAlias(hint);
4646
+ const direct = detections.find(
4647
+ (entry) => entry.provider.id === resolved || entry.provider.id === hint
4648
+ );
4649
+ if (direct) return direct;
4650
+ const byAlias = detections.find(
4651
+ (entry) => entry.provider.aliases.includes(hint) || entry.provider.agentFlag === hint || entry.provider.toolName.toLowerCase() === hint.toLowerCase()
4652
+ );
4653
+ if (byAlias) return byAlias;
4654
+ const provider = getProvider(resolved);
4655
+ if (provider) {
4656
+ return {
4657
+ provider,
4658
+ installed: true,
4659
+ methods: [],
4660
+ projectDetected: false
4661
+ };
4662
+ }
4663
+ }
4664
+ return null;
4665
+ }
4666
+ function selectRuntimeProviderContext(detections, snapshot = {}) {
4667
+ const hints = getRuntimeHints(snapshot);
4668
+ const hinted = pickDetectionByHint(detections, hints);
4669
+ const projectMatches = detections.filter((entry) => entry.projectDetected);
4670
+ const installed = detections.filter((entry) => entry.installed);
4671
+ const selected = hinted ?? (projectMatches.length === 1 ? projectMatches[0] : null) ?? (installed.length === 1 ? installed[0] : null);
4672
+ if (!selected) {
4673
+ return {
4674
+ runtimeCandidates: installed.map((entry) => entry.provider.id)
4675
+ };
4676
+ }
4677
+ return {
4678
+ runtimeProviderId: selected.provider.id,
4679
+ runtimeToolName: selected.provider.toolName,
4680
+ runtimeVendor: selected.provider.vendor,
4681
+ runtimeInstructionFile: selected.provider.instructFile,
4682
+ runtimeProjectDetected: selected.projectDetected,
4683
+ runtimeDetectionMethods: selected.methods,
4684
+ runtimeCandidates: installed.map((entry) => entry.provider.id),
4685
+ inferredModelProvider: inferProviderFromVendor(selected.provider.vendor)
4686
+ };
4687
+ }
4688
+ function detectRuntimeProviderContext(snapshot = {}) {
4689
+ if (!snapshot.cwd && !snapshot.argv && !snapshot.env && cachedRuntimeProvider) {
4690
+ return cachedRuntimeProvider;
4691
+ }
4692
+ try {
4693
+ const detections = detectProjectProviders(snapshot.cwd ?? process.cwd());
4694
+ const context = selectRuntimeProviderContext(detections, snapshot);
4695
+ if (!snapshot.cwd && !snapshot.argv && !snapshot.env) {
4696
+ cachedRuntimeProvider = context;
4697
+ }
4698
+ return context;
4699
+ } catch {
4700
+ return {};
4701
+ }
4702
+ }
4703
+ function resetRuntimeProviderContextCache() {
4704
+ cachedRuntimeProvider = null;
4705
+ }
4706
+ var cachedRuntimeProvider;
4707
+ var init_provider_detection = __esm({
4708
+ "src/core/metrics/provider-detection.ts"() {
4709
+ "use strict";
4710
+ cachedRuntimeProvider = null;
4711
+ }
4712
+ });
4713
+
4602
4714
  // src/store/data-safety.ts
4603
4715
  import { eq as eq8 } from "drizzle-orm";
4604
4716
  async function checkTaskExists(taskId, cwd, config = {}) {
@@ -7067,14 +7179,16 @@ async function writeMemoryBridge(projectRoot, config) {
7067
7179
  }
7068
7180
  writeFileSync2(bridgePath, content, "utf-8");
7069
7181
  return { path: bridgePath, written: true };
7070
- } catch {
7182
+ } catch (err) {
7183
+ console.error("[CLEO] Failed to write memory bridge:", err instanceof Error ? err.message : String(err));
7071
7184
  return { path: bridgePath, written: false };
7072
7185
  }
7073
7186
  }
7074
7187
  async function refreshMemoryBridge(projectRoot) {
7075
7188
  try {
7076
7189
  await writeMemoryBridge(projectRoot);
7077
- } catch {
7190
+ } catch (err) {
7191
+ console.error("[CLEO] Memory bridge refresh failed:", err instanceof Error ? err.message : String(err));
7078
7192
  }
7079
7193
  }
7080
7194
  async function getLastHandoffSafe(projectRoot) {
@@ -8499,6 +8613,10 @@ async function ensureInjection(projectRoot) {
8499
8613
  if (existsSync23(projectContextPath)) {
8500
8614
  agentsMdLines.push("@.cleo/project-context.json");
8501
8615
  }
8616
+ const memoryBridgePath = join26(projectRoot, ".cleo", "memory-bridge.md");
8617
+ if (existsSync23(memoryBridgePath)) {
8618
+ agentsMdLines.push("@.cleo/memory-bridge.md");
8619
+ }
8502
8620
  const contributorBlock = buildContributorInjectionBlock(projectRoot);
8503
8621
  if (contributorBlock) {
8504
8622
  agentsMdLines.push(contributorBlock);
@@ -8699,6 +8817,7 @@ __export(scaffold_exports, {
8699
8817
  CLEO_GITIGNORE_FALLBACK: () => CLEO_GITIGNORE_FALLBACK,
8700
8818
  REQUIRED_CLEO_SUBDIRS: () => REQUIRED_CLEO_SUBDIRS,
8701
8819
  REQUIRED_GLOBAL_SUBDIRS: () => REQUIRED_GLOBAL_SUBDIRS,
8820
+ checkBrainDb: () => checkBrainDb,
8702
8821
  checkCleoGitRepo: () => checkCleoGitRepo,
8703
8822
  checkCleoStructure: () => checkCleoStructure,
8704
8823
  checkConfig: () => checkConfig,
@@ -8706,10 +8825,12 @@ __export(scaffold_exports, {
8706
8825
  checkGlobalHome: () => checkGlobalHome,
8707
8826
  checkGlobalTemplates: () => checkGlobalTemplates,
8708
8827
  checkLogDir: () => checkLogDir,
8828
+ checkMemoryBridge: () => checkMemoryBridge,
8709
8829
  checkProjectContext: () => checkProjectContext,
8710
8830
  checkProjectInfo: () => checkProjectInfo,
8711
8831
  checkSqliteDb: () => checkSqliteDb,
8712
8832
  createDefaultConfig: () => createDefaultConfig,
8833
+ ensureBrainDb: () => ensureBrainDb,
8713
8834
  ensureCleoGitRepo: () => ensureCleoGitRepo,
8714
8835
  ensureCleoStructure: () => ensureCleoStructure,
8715
8836
  ensureConfig: () => ensureConfig,
@@ -9324,6 +9445,79 @@ function checkSqliteDb(projectRoot) {
9324
9445
  fix: null
9325
9446
  };
9326
9447
  }
9448
+ async function ensureBrainDb(projectRoot) {
9449
+ const cleoDir = getCleoDirAbsolute(projectRoot);
9450
+ const dbPath = join27(cleoDir, "brain.db");
9451
+ if (existsSync24(dbPath)) {
9452
+ return { action: "skipped", path: dbPath, details: "brain.db already exists" };
9453
+ }
9454
+ try {
9455
+ const { getBrainDb: getBrainDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
9456
+ await getBrainDb2(projectRoot);
9457
+ return { action: "created", path: dbPath, details: "Brain database initialized" };
9458
+ } catch (err) {
9459
+ return {
9460
+ action: "skipped",
9461
+ path: dbPath,
9462
+ details: `Failed to initialize brain.db: ${err instanceof Error ? err.message : String(err)}`
9463
+ };
9464
+ }
9465
+ }
9466
+ function checkBrainDb(projectRoot) {
9467
+ const cleoDir = getCleoDirAbsolute(projectRoot);
9468
+ const dbPath = join27(cleoDir, "brain.db");
9469
+ if (!existsSync24(dbPath)) {
9470
+ return {
9471
+ id: "brain_db",
9472
+ category: "scaffold",
9473
+ status: "failed",
9474
+ message: "brain.db not found",
9475
+ details: { path: dbPath, exists: false },
9476
+ fix: "cleo init"
9477
+ };
9478
+ }
9479
+ const stat3 = statSync3(dbPath);
9480
+ if (stat3.size === 0) {
9481
+ return {
9482
+ id: "brain_db",
9483
+ category: "scaffold",
9484
+ status: "warning",
9485
+ message: "brain.db exists but is empty (0 bytes)",
9486
+ details: { path: dbPath, exists: true, size: 0 },
9487
+ fix: "cleo upgrade"
9488
+ };
9489
+ }
9490
+ return {
9491
+ id: "brain_db",
9492
+ category: "scaffold",
9493
+ status: "passed",
9494
+ message: `brain.db exists (${stat3.size} bytes)`,
9495
+ details: { path: dbPath, exists: true, size: stat3.size },
9496
+ fix: null
9497
+ };
9498
+ }
9499
+ function checkMemoryBridge(projectRoot) {
9500
+ const cleoDir = getCleoDirAbsolute(projectRoot);
9501
+ const bridgePath = join27(cleoDir, "memory-bridge.md");
9502
+ if (!existsSync24(bridgePath)) {
9503
+ return {
9504
+ id: "memory_bridge",
9505
+ category: "scaffold",
9506
+ status: "warning",
9507
+ message: "memory-bridge.md not found",
9508
+ details: { path: bridgePath, exists: false },
9509
+ fix: "cleo init or cleo refresh-memory"
9510
+ };
9511
+ }
9512
+ return {
9513
+ id: "memory_bridge",
9514
+ category: "scaffold",
9515
+ status: "passed",
9516
+ message: "memory-bridge.md exists",
9517
+ details: { path: bridgePath, exists: true },
9518
+ fix: null
9519
+ };
9520
+ }
9327
9521
  async function ensureGlobalHome() {
9328
9522
  const cleoHome = getCleoHome();
9329
9523
  const alreadyExists = existsSync24(cleoHome);
@@ -10404,6 +10598,28 @@ var init_registry2 = __esm({
10404
10598
  }
10405
10599
  });
10406
10600
 
10601
+ // src/core/adapters/adapter-registry.ts
10602
+ var ADAPTER_REGISTRY;
10603
+ var init_adapter_registry = __esm({
10604
+ "src/core/adapters/adapter-registry.ts"() {
10605
+ "use strict";
10606
+ ADAPTER_REGISTRY = {
10607
+ "claude-code": async () => {
10608
+ const { ClaudeCodeAdapter } = await import("@cleocode/adapter-claude-code");
10609
+ return new ClaudeCodeAdapter();
10610
+ },
10611
+ "opencode": async () => {
10612
+ const { OpenCodeAdapter } = await import("@cleocode/adapter-opencode");
10613
+ return new OpenCodeAdapter();
10614
+ },
10615
+ "cursor": async () => {
10616
+ const { CursorAdapter } = await import("@cleocode/adapter-cursor");
10617
+ return new CursorAdapter();
10618
+ }
10619
+ };
10620
+ }
10621
+ });
10622
+
10407
10623
  // src/core/adapters/discovery.ts
10408
10624
  import { execFileSync as execFileSync2 } from "node:child_process";
10409
10625
  import { existsSync as existsSync28, readdirSync as readdirSync12, readFileSync as readFileSync18 } from "node:fs";
@@ -10469,13 +10685,16 @@ var log4, AdapterManager;
10469
10685
  var init_manager = __esm({
10470
10686
  "src/core/adapters/manager.ts"() {
10471
10687
  "use strict";
10688
+ init_registry();
10472
10689
  init_logger();
10690
+ init_adapter_registry();
10473
10691
  init_discovery();
10474
10692
  log4 = getLogger("adapter-manager");
10475
10693
  AdapterManager = class _AdapterManager {
10476
10694
  static instance = null;
10477
10695
  adapters = /* @__PURE__ */ new Map();
10478
10696
  manifests = /* @__PURE__ */ new Map();
10697
+ hookCleanups = /* @__PURE__ */ new Map();
10479
10698
  activeId = null;
10480
10699
  projectRoot;
10481
10700
  constructor(projectRoot) {
@@ -10519,7 +10738,7 @@ var init_manager = __esm({
10519
10738
  }
10520
10739
  /**
10521
10740
  * Load and initialize an adapter by manifest ID.
10522
- * The adapter module is dynamically imported from the manifest's entryPoint.
10741
+ * Uses the static adapter registry for reliable bundled operation.
10523
10742
  */
10524
10743
  async activate(adapterId) {
10525
10744
  const manifest = this.manifests.get(adapterId);
@@ -10531,20 +10750,18 @@ var init_manager = __esm({
10531
10750
  this.activeId = adapterId;
10532
10751
  return existing;
10533
10752
  }
10534
- const { resolve: resolve10 } = await import("node:path");
10535
- const entryPath = resolve10(
10536
- this.projectRoot,
10537
- "packages",
10538
- "adapters",
10539
- manifest.provider,
10540
- manifest.entryPoint
10541
- );
10753
+ const factory = ADAPTER_REGISTRY[adapterId];
10754
+ if (!factory) {
10755
+ throw new Error(`No adapter registered in static registry: ${adapterId}`);
10756
+ }
10542
10757
  try {
10543
- const mod = await import(entryPath);
10544
- const adapter = typeof mod.default === "function" ? new mod.default() : typeof mod.createAdapter === "function" ? await mod.createAdapter() : mod.default;
10758
+ const adapter = await factory();
10545
10759
  await adapter.initialize(this.projectRoot);
10546
10760
  this.adapters.set(adapterId, adapter);
10547
10761
  this.activeId = adapterId;
10762
+ if (adapter.hooks) {
10763
+ await this.wireAdapterHooks(adapterId, adapter);
10764
+ }
10548
10765
  log4.info({ adapterId, provider: manifest.provider }, "Adapter activated");
10549
10766
  return adapter;
10550
10767
  } catch (err) {
@@ -10626,6 +10843,7 @@ var init_manager = __esm({
10626
10843
  async dispose() {
10627
10844
  for (const [id, adapter] of this.adapters) {
10628
10845
  try {
10846
+ await this.cleanupAdapterHooks(id, adapter);
10629
10847
  await adapter.dispose();
10630
10848
  log4.info({ adapterId: id }, "Adapter disposed");
10631
10849
  } catch (err) {
@@ -10633,6 +10851,7 @@ var init_manager = __esm({
10633
10851
  }
10634
10852
  }
10635
10853
  this.adapters.clear();
10854
+ this.hookCleanups.clear();
10636
10855
  this.activeId = null;
10637
10856
  }
10638
10857
  /** Dispose a single adapter. */
@@ -10640,6 +10859,7 @@ var init_manager = __esm({
10640
10859
  const adapter = this.adapters.get(adapterId);
10641
10860
  if (!adapter) return;
10642
10861
  try {
10862
+ await this.cleanupAdapterHooks(adapterId, adapter);
10643
10863
  await adapter.dispose();
10644
10864
  } catch (err) {
10645
10865
  log4.error({ adapterId, err }, "Failed to dispose adapter");
@@ -10649,6 +10869,51 @@ var init_manager = __esm({
10649
10869
  this.activeId = null;
10650
10870
  }
10651
10871
  }
10872
+ /**
10873
+ * Wire an adapter's hook event map into CLEO's HookRegistry.
10874
+ * Creates bridging handlers at priority 50 for each mapped event.
10875
+ */
10876
+ async wireAdapterHooks(adapterId, adapter) {
10877
+ if (!adapter.hooks) return;
10878
+ try {
10879
+ await adapter.hooks.registerNativeHooks(this.projectRoot);
10880
+ } catch (err) {
10881
+ log4.error({ adapterId, err }, "Failed to register native hooks");
10882
+ }
10883
+ const eventMap = adapter.hooks.getEventMap?.();
10884
+ if (!eventMap) return;
10885
+ const cleanups = [];
10886
+ for (const [_providerEvent, caampEvent] of Object.entries(eventMap)) {
10887
+ const hookId = `adapter-${adapterId}-${caampEvent}`;
10888
+ const unregister = hooks.register({
10889
+ id: hookId,
10890
+ event: caampEvent,
10891
+ priority: 50,
10892
+ handler: async (_projectRoot, payload) => {
10893
+ log4.debug({ adapterId, event: caampEvent, payload }, "Adapter hook dispatched");
10894
+ }
10895
+ });
10896
+ cleanups.push(unregister);
10897
+ }
10898
+ this.hookCleanups.set(adapterId, cleanups);
10899
+ }
10900
+ /**
10901
+ * Clean up hook registrations for an adapter.
10902
+ */
10903
+ async cleanupAdapterHooks(adapterId, adapter) {
10904
+ const cleanups = this.hookCleanups.get(adapterId);
10905
+ if (cleanups) {
10906
+ for (const fn of cleanups) {
10907
+ fn();
10908
+ }
10909
+ this.hookCleanups.delete(adapterId);
10910
+ }
10911
+ try {
10912
+ await adapter.hooks?.unregisterNativeHooks();
10913
+ } catch (err) {
10914
+ log4.error({ adapterId, err }, "Failed to unregister native hooks");
10915
+ }
10916
+ }
10652
10917
  };
10653
10918
  }
10654
10919
  });
@@ -10656,6 +10921,7 @@ var init_manager = __esm({
10656
10921
  // src/core/adapters/index.ts
10657
10922
  var adapters_exports = {};
10658
10923
  __export(adapters_exports, {
10924
+ ADAPTER_REGISTRY: () => ADAPTER_REGISTRY,
10659
10925
  AdapterManager: () => AdapterManager,
10660
10926
  detectProvider: () => detectProvider,
10661
10927
  discoverAdapterManifests: () => discoverAdapterManifests
@@ -10663,6 +10929,7 @@ __export(adapters_exports, {
10663
10929
  var init_adapters = __esm({
10664
10930
  "src/core/adapters/index.ts"() {
10665
10931
  "use strict";
10932
+ init_adapter_registry();
10666
10933
  init_manager();
10667
10934
  init_discovery();
10668
10935
  }
@@ -12893,6 +13160,13 @@ async function startSession(options, cwd, accessor) {
12893
13160
  );
12894
13161
  }
12895
13162
  }
13163
+ let detectedProviderId = null;
13164
+ try {
13165
+ const { detectRuntimeProviderContext: detectRuntimeProviderContext2 } = await Promise.resolve().then(() => (init_provider_detection(), provider_detection_exports));
13166
+ const ctx = detectRuntimeProviderContext2();
13167
+ detectedProviderId = ctx.runtimeProviderId ?? null;
13168
+ } catch {
13169
+ }
12896
13170
  const session = {
12897
13171
  id: generateSessionId(),
12898
13172
  name: options.name,
@@ -12907,7 +13181,7 @@ async function startSession(options, cwd, accessor) {
12907
13181
  notes: [],
12908
13182
  tasksCompleted: [],
12909
13183
  tasksCreated: [],
12910
- providerId: options.providerId ?? null
13184
+ providerId: options.providerId ?? detectedProviderId ?? null
12911
13185
  };
12912
13186
  if (options.grade) {
12913
13187
  session.notes = ["[grade-mode:enabled]", ...session.notes ?? []];
@@ -12917,13 +13191,22 @@ async function startSession(options, cwd, accessor) {
12917
13191
  }
12918
13192
  sessions2.push(session);
12919
13193
  await saveSessions(sessions2, cwd, accessor);
13194
+ if (session.providerId) {
13195
+ Promise.resolve().then(() => (init_adapters(), adapters_exports)).then(({ AdapterManager: AdapterManager2 }) => {
13196
+ const mgr = AdapterManager2.getInstance(cwd ?? process.cwd());
13197
+ mgr.discover();
13198
+ return mgr.activate(session.providerId);
13199
+ }).catch(() => {
13200
+ });
13201
+ }
12920
13202
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
12921
13203
  hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
12922
13204
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12923
13205
  sessionId: session.id,
12924
13206
  name: options.name,
12925
13207
  scope,
12926
- agent: options.agent
13208
+ agent: options.agent,
13209
+ providerId: session.providerId ?? void 0
12927
13210
  }).catch(() => {
12928
13211
  });
12929
13212
  return session;
@@ -12959,7 +13242,8 @@ async function endSession(options = {}, cwd, accessor) {
12959
13242
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12960
13243
  sessionId: session.id,
12961
13244
  duration,
12962
- tasksCompleted: session.tasksCompleted || []
13245
+ tasksCompleted: session.tasksCompleted || [],
13246
+ providerId: session.providerId ?? void 0
12963
13247
  }).catch(() => {
12964
13248
  });
12965
13249
  const { bridgeSessionToMemory: bridgeSessionToMemory2 } = await Promise.resolve().then(() => (init_session_memory_bridge(), session_memory_bridge_exports));
@@ -13329,7 +13613,7 @@ async function initializeDefaultAdapters() {
13329
13613
  }
13330
13614
  }
13331
13615
  var SpawnAdapterRegistry, spawnRegistry;
13332
- var init_adapter_registry = __esm({
13616
+ var init_adapter_registry2 = __esm({
13333
13617
  "src/core/spawn/adapter-registry.ts"() {
13334
13618
  "use strict";
13335
13619
  SpawnAdapterRegistry = class {
@@ -14577,6 +14861,18 @@ async function startupHealthCheck(projectRoot) {
14577
14861
  } else {
14578
14862
  checks.push({ check: "log_dir", status: "pass", message: "Log directory present" });
14579
14863
  }
14864
+ const brainDbCheck = checkBrainDb(root);
14865
+ checks.push({
14866
+ check: "brain_db",
14867
+ status: brainDbCheck.status === "passed" ? "pass" : "warn",
14868
+ message: brainDbCheck.message
14869
+ });
14870
+ const memBridgeCheck = checkMemoryBridge(root);
14871
+ checks.push({
14872
+ check: "memory_bridge",
14873
+ status: memBridgeCheck.status === "passed" ? "pass" : "warn",
14874
+ message: memBridgeCheck.message
14875
+ });
14580
14876
  const hasFailures = failures.length > 0;
14581
14877
  const state = hasFailures && !projectHealthy ? "needs_upgrade" : "healthy";
14582
14878
  return {
@@ -20993,137 +21289,41 @@ async function resolveProviderFromModelRegistry(model) {
20993
21289
  return resolveProviderFromModelIndex(index5, model);
20994
21290
  }
20995
21291
 
20996
- // src/core/metrics/provider-detection.ts
20997
- import { basename as basename2 } from "node:path";
20998
- import {
20999
- detectProjectProviders,
21000
- getProvider,
21001
- resolveAlias
21002
- } from "@cleocode/caamp";
21003
- function inferProviderFromVendor(vendor) {
21004
- const value = (vendor ?? "").trim().toLowerCase();
21005
- if (!value) return void 0;
21006
- if (value.includes("anthropic") || value.includes("claude")) return "anthropic";
21007
- if (value.includes("openai") || value.includes("codex") || value.includes("chatgpt"))
21292
+ // src/core/metrics/token-service.ts
21293
+ init_provider_detection();
21294
+ function normalizeProvider(provider, model, runtimeProvider) {
21295
+ const value = (provider ?? "").trim().toLowerCase();
21296
+ const modelValue = (model ?? "").trim().toLowerCase();
21297
+ const runtimeValue = (runtimeProvider ?? "").trim().toLowerCase();
21298
+ if (value) return value;
21299
+ if (modelValue.startsWith("gpt") || modelValue.startsWith("o1") || modelValue.startsWith("o3") || modelValue.startsWith("text-embedding")) {
21008
21300
  return "openai";
21009
- if (value.includes("google") || value.includes("gemini")) return "google";
21010
- if (value.includes("xai") || value.includes("grok")) return "xai";
21011
- return void 0;
21012
- }
21013
- function getRuntimeHints(snapshot) {
21014
- const argv = snapshot.argv ?? process.argv;
21015
- const env = snapshot.env ?? process.env;
21016
- const hints = /* @__PURE__ */ new Set();
21017
- const bin = basename2(argv[1] ?? argv[0] ?? "").replace(/\.[^.]+$/, "");
21018
- if (bin) hints.add(bin);
21019
- if (env["CLAUDE_CODE_ENABLE_TELEMETRY"] || env["CLAUDE_CODE_ENTRYPOINT"]) {
21020
- hints.add("claude-code");
21021
- hints.add("claude");
21022
21301
  }
21023
- if (env["OPENCODE_AGENT"] || env["OPENCODE"]) {
21024
- hints.add("opencode");
21302
+ if (modelValue.includes("claude")) return "anthropic";
21303
+ if (modelValue.includes("gemini")) return "google";
21304
+ if (runtimeValue) return runtimeValue;
21305
+ return "unknown";
21306
+ }
21307
+ async function resolveMeasurementProvider(input) {
21308
+ const runtime = detectRuntimeProviderContext({ cwd: input.cwd });
21309
+ const explicit = (input.provider ?? "").trim().toLowerCase();
21310
+ if (explicit) {
21311
+ return { provider: explicit, source: "explicit" };
21025
21312
  }
21026
- if (env["CURSOR_TRACE_ID"] || env["CURSOR_AGENT"]) {
21027
- hints.add("cursor");
21313
+ const fromRegistry = await resolveProviderFromModelRegistry(input.model);
21314
+ if (fromRegistry.provider) {
21315
+ return {
21316
+ provider: fromRegistry.provider,
21317
+ source: fromRegistry.source,
21318
+ candidates: fromRegistry.candidates
21319
+ };
21028
21320
  }
21029
- return Array.from(hints);
21030
- }
21031
- function pickDetectionByHint(detections, hints) {
21032
- for (const hint of hints) {
21033
- const resolved = resolveAlias(hint);
21034
- const direct = detections.find(
21035
- (entry) => entry.provider.id === resolved || entry.provider.id === hint
21036
- );
21037
- if (direct) return direct;
21038
- const byAlias = detections.find(
21039
- (entry) => entry.provider.aliases.includes(hint) || entry.provider.agentFlag === hint || entry.provider.toolName.toLowerCase() === hint.toLowerCase()
21040
- );
21041
- if (byAlias) return byAlias;
21042
- const provider = getProvider(resolved);
21043
- if (provider) {
21044
- return {
21045
- provider,
21046
- installed: true,
21047
- methods: [],
21048
- projectDetected: false
21049
- };
21050
- }
21051
- }
21052
- return null;
21053
- }
21054
- function selectRuntimeProviderContext(detections, snapshot = {}) {
21055
- const hints = getRuntimeHints(snapshot);
21056
- const hinted = pickDetectionByHint(detections, hints);
21057
- const projectMatches = detections.filter((entry) => entry.projectDetected);
21058
- const installed = detections.filter((entry) => entry.installed);
21059
- const selected = hinted ?? (projectMatches.length === 1 ? projectMatches[0] : null) ?? (installed.length === 1 ? installed[0] : null);
21060
- if (!selected) {
21061
- return {
21062
- runtimeCandidates: installed.map((entry) => entry.provider.id)
21063
- };
21064
- }
21065
- return {
21066
- runtimeProviderId: selected.provider.id,
21067
- runtimeToolName: selected.provider.toolName,
21068
- runtimeVendor: selected.provider.vendor,
21069
- runtimeInstructionFile: selected.provider.instructFile,
21070
- runtimeProjectDetected: selected.projectDetected,
21071
- runtimeDetectionMethods: selected.methods,
21072
- runtimeCandidates: installed.map((entry) => entry.provider.id),
21073
- inferredModelProvider: inferProviderFromVendor(selected.provider.vendor)
21074
- };
21075
- }
21076
- var cachedRuntimeProvider = null;
21077
- function detectRuntimeProviderContext(snapshot = {}) {
21078
- if (!snapshot.cwd && !snapshot.argv && !snapshot.env && cachedRuntimeProvider) {
21079
- return cachedRuntimeProvider;
21080
- }
21081
- try {
21082
- const detections = detectProjectProviders(snapshot.cwd ?? process.cwd());
21083
- const context = selectRuntimeProviderContext(detections, snapshot);
21084
- if (!snapshot.cwd && !snapshot.argv && !snapshot.env) {
21085
- cachedRuntimeProvider = context;
21086
- }
21087
- return context;
21088
- } catch {
21089
- return {};
21090
- }
21091
- }
21092
-
21093
- // src/core/metrics/token-service.ts
21094
- function normalizeProvider(provider, model, runtimeProvider) {
21095
- const value = (provider ?? "").trim().toLowerCase();
21096
- const modelValue = (model ?? "").trim().toLowerCase();
21097
- const runtimeValue = (runtimeProvider ?? "").trim().toLowerCase();
21098
- if (value) return value;
21099
- if (modelValue.startsWith("gpt") || modelValue.startsWith("o1") || modelValue.startsWith("o3") || modelValue.startsWith("text-embedding")) {
21100
- return "openai";
21101
- }
21102
- if (modelValue.includes("claude")) return "anthropic";
21103
- if (modelValue.includes("gemini")) return "google";
21104
- if (runtimeValue) return runtimeValue;
21105
- return "unknown";
21106
- }
21107
- async function resolveMeasurementProvider(input) {
21108
- const runtime = detectRuntimeProviderContext({ cwd: input.cwd });
21109
- const explicit = (input.provider ?? "").trim().toLowerCase();
21110
- if (explicit) {
21111
- return { provider: explicit, source: "explicit" };
21112
- }
21113
- const fromRegistry = await resolveProviderFromModelRegistry(input.model);
21114
- if (fromRegistry.provider) {
21115
- return {
21116
- provider: fromRegistry.provider,
21117
- source: fromRegistry.source,
21118
- candidates: fromRegistry.candidates
21119
- };
21120
- }
21121
- const fallback = normalizeProvider(void 0, input.model, runtime.inferredModelProvider);
21122
- return {
21123
- provider: fallback,
21124
- source: fallback === "unknown" ? "unknown" : runtime.inferredModelProvider ? "runtime-vendor" : "heuristic",
21125
- candidates: fromRegistry.candidates
21126
- };
21321
+ const fallback = normalizeProvider(void 0, input.model, runtime.inferredModelProvider);
21322
+ return {
21323
+ provider: fallback,
21324
+ source: fallback === "unknown" ? "unknown" : runtime.inferredModelProvider ? "runtime-vendor" : "heuristic",
21325
+ candidates: fromRegistry.candidates
21326
+ };
21127
21327
  }
21128
21328
  function buildRuntimeMetadata(input) {
21129
21329
  const runtime = detectRuntimeProviderContext({ cwd: input.cwd });
@@ -22205,6 +22405,7 @@ init_agent_outputs();
22205
22405
  init_paths();
22206
22406
  init_scaffold();
22207
22407
  init_schema_management();
22408
+ init_memory_bridge();
22208
22409
  import { existsSync as existsSync29, readdirSync as readdirSync13, readFileSync as readFileSync19 } from "node:fs";
22209
22410
  import { copyFile as copyFile3, lstat, mkdir as mkdir9, readFile as readFile8, symlink, unlink as unlink3 } from "node:fs/promises";
22210
22411
  import { basename as basename5, dirname as dirname10, join as join33 } from "node:path";
@@ -22378,6 +22579,14 @@ async function initProject(opts = {}) {
22378
22579
  } catch (err) {
22379
22580
  created.push(`tasks.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
22380
22581
  }
22582
+ try {
22583
+ const brainResult = await ensureBrainDb(projRoot);
22584
+ if (brainResult.action === "created") {
22585
+ created.push("brain.db");
22586
+ }
22587
+ } catch (err) {
22588
+ created.push(`brain.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
22589
+ }
22381
22590
  if (force) {
22382
22591
  const gitignoreResult = await ensureGitignore(projRoot);
22383
22592
  if (gitignoreResult.action === "skipped") {
@@ -22500,11 +22709,34 @@ async function initProject(opts = {}) {
22500
22709
  const detected = mgr.detectActive();
22501
22710
  if (detected.length > 0) {
22502
22711
  created.push(`adapters: active provider detected (${detected.join(", ")})`);
22712
+ for (const adapterId of detected) {
22713
+ try {
22714
+ const adapter = await mgr.activate(adapterId);
22715
+ const installResult = await adapter.install.install({
22716
+ projectDir: projRoot
22717
+ });
22718
+ if (installResult.success) {
22719
+ created.push(`adapter install (${adapterId}): installed`);
22720
+ } else {
22721
+ warnings.push(`adapter install (${adapterId}): failed`);
22722
+ }
22723
+ } catch (err) {
22724
+ warnings.push(`adapter activate/install (${adapterId}): ${err instanceof Error ? err.message : String(err)}`);
22725
+ }
22726
+ }
22503
22727
  }
22504
22728
  }
22505
22729
  } catch (err) {
22506
22730
  warnings.push(`Adapter discovery: ${err instanceof Error ? err.message : String(err)}`);
22507
22731
  }
22732
+ try {
22733
+ const bridgeResult = await writeMemoryBridge(projRoot);
22734
+ if (bridgeResult.written) {
22735
+ created.push("memory-bridge.md");
22736
+ }
22737
+ } catch (err) {
22738
+ warnings.push(`Memory bridge: ${err instanceof Error ? err.message : String(err)}`);
22739
+ }
22508
22740
  const rootGitignoreResult = await removeCleoFromRootGitignore(projRoot);
22509
22741
  if (rootGitignoreResult.removed) {
22510
22742
  warnings.push(
@@ -25570,7 +25802,7 @@ async function orchestrateValidate(taskId, projectRoot) {
25570
25802
  async function orchestrateSpawnExecute(taskId, adapterId, protocolType, projectRoot, _tier) {
25571
25803
  const cwd = projectRoot ?? process.cwd();
25572
25804
  try {
25573
- const { initializeDefaultAdapters: initializeDefaultAdapters2, spawnRegistry: spawnRegistry2 } = await Promise.resolve().then(() => (init_adapter_registry(), adapter_registry_exports));
25805
+ const { initializeDefaultAdapters: initializeDefaultAdapters2, spawnRegistry: spawnRegistry2 } = await Promise.resolve().then(() => (init_adapter_registry2(), adapter_registry_exports));
25574
25806
  await initializeDefaultAdapters2();
25575
25807
  let adapter;
25576
25808
  if (adapterId) {
@@ -26699,7 +26931,10 @@ async function checkEpicCompleteness(releaseTaskIds, cwd, accessor) {
26699
26931
  if (!epic) continue;
26700
26932
  const allChildren = data.tasks.filter((t) => t.parentId === epicId && t.id !== epicId);
26701
26933
  const includedSet = new Set(includedTasks);
26702
- const missingChildren = allChildren.filter((t) => !includedSet.has(t.id) && !releaseSet.has(t.id)).map((t) => ({ id: t.id, title: t.title, status: t.status }));
26934
+ const parentIds = new Set(data.tasks.filter((t) => t.parentId).map((t) => t.parentId));
26935
+ const missingChildren = allChildren.filter(
26936
+ (t) => t.status === "done" && !parentIds.has(t.id) && !includedSet.has(t.id) && !releaseSet.has(t.id)
26937
+ ).map((t) => ({ id: t.id, title: t.title, status: t.status }));
26703
26938
  if (missingChildren.length > 0) hasIncomplete = true;
26704
26939
  epics.push({
26705
26940
  epicId,
@@ -26854,9 +27089,9 @@ init_json();
26854
27089
  init_sqlite();
26855
27090
  init_tasks_schema();
26856
27091
  import { execFileSync as execFileSync5 } from "node:child_process";
26857
- import { existsSync as existsSync41, renameSync as renameSync7 } from "node:fs";
27092
+ import { existsSync as existsSync42, renameSync as renameSync7 } from "node:fs";
26858
27093
  import { readFile as readFile11 } from "node:fs/promises";
26859
- import { join as join42 } from "node:path";
27094
+ import { join as join43 } from "node:path";
26860
27095
  import { and as and6, count as count4, desc as desc5, eq as eq15 } from "drizzle-orm";
26861
27096
  init_paths();
26862
27097
 
@@ -26951,6 +27186,170 @@ function escapeRegex(str) {
26951
27186
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26952
27187
  }
26953
27188
 
27189
+ // src/core/release/version-bump.ts
27190
+ init_exit_codes();
27191
+ init_errors();
27192
+ init_paths();
27193
+ import { existsSync as existsSync41, readFileSync as readFileSync26, writeFileSync as writeFileSync6 } from "node:fs";
27194
+ import { join as join42 } from "node:path";
27195
+ function readConfigValueSync2(path, defaultValue, cwd) {
27196
+ try {
27197
+ const configPath = join42(getCleoDir(cwd), "config.json");
27198
+ if (!existsSync41(configPath)) return defaultValue;
27199
+ const config = JSON.parse(readFileSync26(configPath, "utf-8"));
27200
+ const keys = path.split(".");
27201
+ let value = config;
27202
+ for (const key of keys) {
27203
+ if (value == null || typeof value !== "object") return defaultValue;
27204
+ value = value[key];
27205
+ }
27206
+ return value ?? defaultValue;
27207
+ } catch {
27208
+ return defaultValue;
27209
+ }
27210
+ }
27211
+ var VERSION_WITH_PRERELEASE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
27212
+ function validateVersionFormat(version) {
27213
+ return VERSION_WITH_PRERELEASE.test(version);
27214
+ }
27215
+ function getVersionBumpConfig(cwd) {
27216
+ try {
27217
+ const raw = readConfigValueSync2("release.versionBump.files", [], cwd);
27218
+ return raw.map((entry) => ({
27219
+ file: entry.path ?? entry.file ?? "",
27220
+ strategy: entry.strategy,
27221
+ field: entry.jsonPath?.replace(/^\./, "") ?? entry.field,
27222
+ key: entry.key,
27223
+ section: entry.section,
27224
+ pattern: entry.sedPattern ?? entry.pattern
27225
+ })).filter((t) => t.file !== "");
27226
+ } catch {
27227
+ return [];
27228
+ }
27229
+ }
27230
+ function bumpFile(target, newVersion, projectRoot) {
27231
+ const filePath = join42(projectRoot, target.file);
27232
+ if (!existsSync41(filePath)) {
27233
+ return {
27234
+ file: target.file,
27235
+ strategy: target.strategy,
27236
+ success: false,
27237
+ error: `File not found: ${target.file}`
27238
+ };
27239
+ }
27240
+ try {
27241
+ const content = readFileSync26(filePath, "utf-8");
27242
+ let previousVersion;
27243
+ let newContent;
27244
+ switch (target.strategy) {
27245
+ case "plain": {
27246
+ previousVersion = content.trim();
27247
+ newContent = newVersion + "\n";
27248
+ break;
27249
+ }
27250
+ case "json": {
27251
+ const field = target.field ?? "version";
27252
+ const json = JSON.parse(content);
27253
+ previousVersion = getNestedField(json, field);
27254
+ setNestedField(json, field, newVersion);
27255
+ newContent = JSON.stringify(json, null, 2) + "\n";
27256
+ break;
27257
+ }
27258
+ case "toml": {
27259
+ const key = target.key ?? "version";
27260
+ const versionRegex = new RegExp(`^(${key}\\s*=\\s*")([^"]+)(")`, "m");
27261
+ const match = content.match(versionRegex);
27262
+ previousVersion = match?.[2];
27263
+ newContent = content.replace(versionRegex, `$1${newVersion}$3`);
27264
+ break;
27265
+ }
27266
+ case "sed": {
27267
+ const pattern = target.pattern ?? "";
27268
+ if (!pattern.includes("{{VERSION}}")) {
27269
+ return {
27270
+ file: target.file,
27271
+ strategy: target.strategy,
27272
+ success: false,
27273
+ error: "sed strategy requires {{VERSION}} placeholder in pattern"
27274
+ };
27275
+ }
27276
+ const regex = new RegExp(pattern.replace("{{VERSION}}", "([\\d.]+)"));
27277
+ const match = content.match(regex);
27278
+ previousVersion = match?.[1];
27279
+ newContent = content.replace(regex, pattern.replace("{{VERSION}}", newVersion));
27280
+ break;
27281
+ }
27282
+ default:
27283
+ return {
27284
+ file: target.file,
27285
+ strategy: target.strategy,
27286
+ success: false,
27287
+ error: `Unknown strategy: ${target.strategy}`
27288
+ };
27289
+ }
27290
+ writeFileSync6(filePath, newContent, "utf-8");
27291
+ return {
27292
+ file: target.file,
27293
+ strategy: target.strategy,
27294
+ success: true,
27295
+ previousVersion,
27296
+ newVersion
27297
+ };
27298
+ } catch (err) {
27299
+ return {
27300
+ file: target.file,
27301
+ strategy: target.strategy,
27302
+ success: false,
27303
+ error: String(err)
27304
+ };
27305
+ }
27306
+ }
27307
+ function bumpVersionFromConfig(newVersion, options = {}, cwd) {
27308
+ if (!validateVersionFormat(newVersion)) {
27309
+ throw new CleoError(
27310
+ 6 /* VALIDATION_ERROR */,
27311
+ `Invalid version: '${newVersion}' (expected X.Y.Z or YYYY.M.patch)`
27312
+ );
27313
+ }
27314
+ const targets = getVersionBumpConfig(cwd);
27315
+ if (targets.length === 0) {
27316
+ throw new CleoError(
27317
+ 1 /* GENERAL_ERROR */,
27318
+ "No version bump targets configured. Add release.versionBump.files to .cleo/config.json"
27319
+ );
27320
+ }
27321
+ const projectRoot = getProjectRoot(cwd);
27322
+ const results = [];
27323
+ if (options.dryRun) {
27324
+ for (const target of targets) {
27325
+ results.push({
27326
+ file: target.file,
27327
+ strategy: target.strategy,
27328
+ success: true,
27329
+ newVersion
27330
+ });
27331
+ }
27332
+ return { results, allSuccess: true };
27333
+ }
27334
+ for (const target of targets) {
27335
+ results.push(bumpFile(target, newVersion, projectRoot));
27336
+ }
27337
+ const allSuccess = results.every((r) => r.success);
27338
+ return { results, allSuccess };
27339
+ }
27340
+ function getNestedField(obj, path) {
27341
+ return path.split(".").reduce((acc, key) => acc?.[key], obj);
27342
+ }
27343
+ function setNestedField(obj, path, value) {
27344
+ const parts = path.split(".");
27345
+ let current = obj;
27346
+ for (let i = 0; i < parts.length - 1; i++) {
27347
+ if (typeof current[parts[i]] !== "object") current[parts[i]] = {};
27348
+ current = current[parts[i]];
27349
+ }
27350
+ current[parts[parts.length - 1]] = value;
27351
+ }
27352
+
26954
27353
  // src/core/release/release-manifest.ts
26955
27354
  function normalizeLimit2(limit) {
26956
27355
  return typeof limit === "number" && limit > 0 ? limit : void 0;
@@ -27176,7 +27575,7 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
27176
27575
  }
27177
27576
  const changelog = sections.join("\n");
27178
27577
  await db.update(releaseManifests).set({ changelog }).where(eq15(releaseManifests.version, normalizedVersion)).run();
27179
- const changelogPath = join42(cwd ?? process.cwd(), "CHANGELOG.md");
27578
+ const changelogPath = join43(cwd ?? process.cwd(), "CHANGELOG.md");
27180
27579
  let existingChangelogContent = "";
27181
27580
  try {
27182
27581
  existingChangelogContent = await readFile11(changelogPath, "utf8");
@@ -27290,13 +27689,13 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
27290
27689
  message: incompleteTasks.length === 0 ? "All tasks completed" : `${incompleteTasks.length} tasks not completed: ${incompleteTasks.join(", ")}`
27291
27690
  });
27292
27691
  const projectRoot = cwd ?? getProjectRoot();
27293
- const distPath = join42(projectRoot, "dist", "cli", "index.js");
27294
- const isNodeProject = existsSync41(join42(projectRoot, "package.json"));
27692
+ const distPath = join43(projectRoot, "dist", "cli", "index.js");
27693
+ const isNodeProject = existsSync42(join43(projectRoot, "package.json"));
27295
27694
  if (isNodeProject) {
27296
27695
  gates.push({
27297
27696
  name: "build_artifact",
27298
- status: existsSync41(distPath) ? "passed" : "failed",
27299
- message: existsSync41(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
27697
+ status: existsSync42(distPath) ? "passed" : "failed",
27698
+ message: existsSync42(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
27300
27699
  });
27301
27700
  }
27302
27701
  if (opts?.dryRun) {
@@ -27306,6 +27705,8 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
27306
27705
  message: "Skipped in dry-run mode"
27307
27706
  });
27308
27707
  } else {
27708
+ const bumpTargets = getVersionBumpConfig(cwd);
27709
+ const allowedDirty = /* @__PURE__ */ new Set(["CHANGELOG.md", ...bumpTargets.map((t) => t.file)]);
27309
27710
  let workingTreeClean = true;
27310
27711
  let dirtyFiles = [];
27311
27712
  try {
@@ -27314,14 +27715,15 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
27314
27715
  encoding: "utf-8",
27315
27716
  stdio: "pipe"
27316
27717
  });
27317
- 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");
27718
+ dirtyFiles = porcelain.split("\n").filter((l) => l.trim()).filter((l) => !l.startsWith("?? ")).map((l) => l.slice(3).trim()).filter((f) => !allowedDirty.has(f));
27318
27719
  workingTreeClean = dirtyFiles.length === 0;
27319
27720
  } catch {
27320
27721
  }
27722
+ const excludeList = [...allowedDirty].join(", ");
27321
27723
  gates.push({
27322
27724
  name: "clean_working_tree",
27323
27725
  status: workingTreeClean ? "passed" : "failed",
27324
- 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)` : ""}`
27726
+ message: workingTreeClean ? `Working tree clean (excluding ${excludeList})` : `Uncommitted changes in: ${dirtyFiles.slice(0, 5).join(", ")}${dirtyFiles.length > 5 ? ` (+${dirtyFiles.length - 5} more)` : ""}`
27325
27727
  });
27326
27728
  }
27327
27729
  const isPreRelease = normalizedVersion.includes("-");
@@ -27436,7 +27838,7 @@ async function rollbackRelease(version, reason, cwd) {
27436
27838
  };
27437
27839
  }
27438
27840
  async function readPushPolicy(cwd) {
27439
- const configPath = join42(getCleoDirAbsolute(cwd), "config.json");
27841
+ const configPath = join43(getCleoDirAbsolute(cwd), "config.json");
27440
27842
  const config = await readJson(configPath);
27441
27843
  if (!config) return void 0;
27442
27844
  const release2 = config.release;
@@ -27534,170 +27936,6 @@ async function markReleasePushed(version, pushedAt, cwd, provenance) {
27534
27936
  }).where(eq15(releaseManifests.version, normalizedVersion)).run();
27535
27937
  }
27536
27938
 
27537
- // src/core/release/version-bump.ts
27538
- init_exit_codes();
27539
- init_errors();
27540
- init_paths();
27541
- import { existsSync as existsSync42, readFileSync as readFileSync26, writeFileSync as writeFileSync6 } from "node:fs";
27542
- import { join as join43 } from "node:path";
27543
- function readConfigValueSync2(path, defaultValue, cwd) {
27544
- try {
27545
- const configPath = join43(getCleoDir(cwd), "config.json");
27546
- if (!existsSync42(configPath)) return defaultValue;
27547
- const config = JSON.parse(readFileSync26(configPath, "utf-8"));
27548
- const keys = path.split(".");
27549
- let value = config;
27550
- for (const key of keys) {
27551
- if (value == null || typeof value !== "object") return defaultValue;
27552
- value = value[key];
27553
- }
27554
- return value ?? defaultValue;
27555
- } catch {
27556
- return defaultValue;
27557
- }
27558
- }
27559
- var VERSION_WITH_PRERELEASE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
27560
- function validateVersionFormat(version) {
27561
- return VERSION_WITH_PRERELEASE.test(version);
27562
- }
27563
- function getVersionBumpConfig(cwd) {
27564
- try {
27565
- const raw = readConfigValueSync2("release.versionBump.files", [], cwd);
27566
- return raw.map((entry) => ({
27567
- file: entry.path ?? entry.file ?? "",
27568
- strategy: entry.strategy,
27569
- field: entry.jsonPath?.replace(/^\./, "") ?? entry.field,
27570
- key: entry.key,
27571
- section: entry.section,
27572
- pattern: entry.sedPattern ?? entry.pattern
27573
- })).filter((t) => t.file !== "");
27574
- } catch {
27575
- return [];
27576
- }
27577
- }
27578
- function bumpFile(target, newVersion, projectRoot) {
27579
- const filePath = join43(projectRoot, target.file);
27580
- if (!existsSync42(filePath)) {
27581
- return {
27582
- file: target.file,
27583
- strategy: target.strategy,
27584
- success: false,
27585
- error: `File not found: ${target.file}`
27586
- };
27587
- }
27588
- try {
27589
- const content = readFileSync26(filePath, "utf-8");
27590
- let previousVersion;
27591
- let newContent;
27592
- switch (target.strategy) {
27593
- case "plain": {
27594
- previousVersion = content.trim();
27595
- newContent = newVersion + "\n";
27596
- break;
27597
- }
27598
- case "json": {
27599
- const field = target.field ?? "version";
27600
- const json = JSON.parse(content);
27601
- previousVersion = getNestedField(json, field);
27602
- setNestedField(json, field, newVersion);
27603
- newContent = JSON.stringify(json, null, 2) + "\n";
27604
- break;
27605
- }
27606
- case "toml": {
27607
- const key = target.key ?? "version";
27608
- const versionRegex = new RegExp(`^(${key}\\s*=\\s*")([^"]+)(")`, "m");
27609
- const match = content.match(versionRegex);
27610
- previousVersion = match?.[2];
27611
- newContent = content.replace(versionRegex, `$1${newVersion}$3`);
27612
- break;
27613
- }
27614
- case "sed": {
27615
- const pattern = target.pattern ?? "";
27616
- if (!pattern.includes("{{VERSION}}")) {
27617
- return {
27618
- file: target.file,
27619
- strategy: target.strategy,
27620
- success: false,
27621
- error: "sed strategy requires {{VERSION}} placeholder in pattern"
27622
- };
27623
- }
27624
- const regex = new RegExp(pattern.replace("{{VERSION}}", "([\\d.]+)"));
27625
- const match = content.match(regex);
27626
- previousVersion = match?.[1];
27627
- newContent = content.replace(regex, pattern.replace("{{VERSION}}", newVersion));
27628
- break;
27629
- }
27630
- default:
27631
- return {
27632
- file: target.file,
27633
- strategy: target.strategy,
27634
- success: false,
27635
- error: `Unknown strategy: ${target.strategy}`
27636
- };
27637
- }
27638
- writeFileSync6(filePath, newContent, "utf-8");
27639
- return {
27640
- file: target.file,
27641
- strategy: target.strategy,
27642
- success: true,
27643
- previousVersion,
27644
- newVersion
27645
- };
27646
- } catch (err) {
27647
- return {
27648
- file: target.file,
27649
- strategy: target.strategy,
27650
- success: false,
27651
- error: String(err)
27652
- };
27653
- }
27654
- }
27655
- function bumpVersionFromConfig(newVersion, options = {}, cwd) {
27656
- if (!validateVersionFormat(newVersion)) {
27657
- throw new CleoError(
27658
- 6 /* VALIDATION_ERROR */,
27659
- `Invalid version: '${newVersion}' (expected X.Y.Z or YYYY.M.patch)`
27660
- );
27661
- }
27662
- const targets = getVersionBumpConfig(cwd);
27663
- if (targets.length === 0) {
27664
- throw new CleoError(
27665
- 1 /* GENERAL_ERROR */,
27666
- "No version bump targets configured. Add release.versionBump.files to .cleo/config.json"
27667
- );
27668
- }
27669
- const projectRoot = getProjectRoot(cwd);
27670
- const results = [];
27671
- if (options.dryRun) {
27672
- for (const target of targets) {
27673
- results.push({
27674
- file: target.file,
27675
- strategy: target.strategy,
27676
- success: true,
27677
- newVersion
27678
- });
27679
- }
27680
- return { results, allSuccess: true };
27681
- }
27682
- for (const target of targets) {
27683
- results.push(bumpFile(target, newVersion, projectRoot));
27684
- }
27685
- const allSuccess = results.every((r) => r.success);
27686
- return { results, allSuccess };
27687
- }
27688
- function getNestedField(obj, path) {
27689
- return path.split(".").reduce((acc, key) => acc?.[key], obj);
27690
- }
27691
- function setNestedField(obj, path, value) {
27692
- const parts = path.split(".");
27693
- let current = obj;
27694
- for (let i = 0; i < parts.length - 1; i++) {
27695
- if (typeof current[parts[i]] !== "object") current[parts[i]] = {};
27696
- current = current[parts[i]];
27697
- }
27698
- current[parts[parts.length - 1]] = value;
27699
- }
27700
-
27701
27939
  // src/dispatch/engines/release-engine.ts
27702
27940
  init_data_accessor();
27703
27941
  init_error();