@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/cli/index.js CHANGED
@@ -4439,6 +4439,118 @@ var init_sqlite = __esm({
4439
4439
  }
4440
4440
  });
4441
4441
 
4442
+ // src/core/metrics/provider-detection.ts
4443
+ var provider_detection_exports = {};
4444
+ __export(provider_detection_exports, {
4445
+ detectRuntimeProviderContext: () => detectRuntimeProviderContext,
4446
+ resetRuntimeProviderContextCache: () => resetRuntimeProviderContextCache,
4447
+ selectRuntimeProviderContext: () => selectRuntimeProviderContext
4448
+ });
4449
+ import { basename as basename3 } from "node:path";
4450
+ import {
4451
+ detectProjectProviders,
4452
+ getProvider,
4453
+ resolveAlias
4454
+ } from "@cleocode/caamp";
4455
+ function inferProviderFromVendor(vendor) {
4456
+ const value = (vendor ?? "").trim().toLowerCase();
4457
+ if (!value) return void 0;
4458
+ if (value.includes("anthropic") || value.includes("claude")) return "anthropic";
4459
+ if (value.includes("openai") || value.includes("codex") || value.includes("chatgpt"))
4460
+ return "openai";
4461
+ if (value.includes("google") || value.includes("gemini")) return "google";
4462
+ if (value.includes("xai") || value.includes("grok")) return "xai";
4463
+ return void 0;
4464
+ }
4465
+ function getRuntimeHints(snapshot) {
4466
+ const argv = snapshot.argv ?? process.argv;
4467
+ const env = snapshot.env ?? process.env;
4468
+ const hints = /* @__PURE__ */ new Set();
4469
+ const bin = basename3(argv[1] ?? argv[0] ?? "").replace(/\.[^.]+$/, "");
4470
+ if (bin) hints.add(bin);
4471
+ if (env["CLAUDE_CODE_ENABLE_TELEMETRY"] || env["CLAUDE_CODE_ENTRYPOINT"]) {
4472
+ hints.add("claude-code");
4473
+ hints.add("claude");
4474
+ }
4475
+ if (env["OPENCODE_AGENT"] || env["OPENCODE"]) {
4476
+ hints.add("opencode");
4477
+ }
4478
+ if (env["CURSOR_TRACE_ID"] || env["CURSOR_AGENT"]) {
4479
+ hints.add("cursor");
4480
+ }
4481
+ return Array.from(hints);
4482
+ }
4483
+ function pickDetectionByHint(detections, hints) {
4484
+ for (const hint of hints) {
4485
+ const resolved = resolveAlias(hint);
4486
+ const direct = detections.find(
4487
+ (entry) => entry.provider.id === resolved || entry.provider.id === hint
4488
+ );
4489
+ if (direct) return direct;
4490
+ const byAlias = detections.find(
4491
+ (entry) => entry.provider.aliases.includes(hint) || entry.provider.agentFlag === hint || entry.provider.toolName.toLowerCase() === hint.toLowerCase()
4492
+ );
4493
+ if (byAlias) return byAlias;
4494
+ const provider = getProvider(resolved);
4495
+ if (provider) {
4496
+ return {
4497
+ provider,
4498
+ installed: true,
4499
+ methods: [],
4500
+ projectDetected: false
4501
+ };
4502
+ }
4503
+ }
4504
+ return null;
4505
+ }
4506
+ function selectRuntimeProviderContext(detections, snapshot = {}) {
4507
+ const hints = getRuntimeHints(snapshot);
4508
+ const hinted = pickDetectionByHint(detections, hints);
4509
+ const projectMatches = detections.filter((entry) => entry.projectDetected);
4510
+ const installed = detections.filter((entry) => entry.installed);
4511
+ const selected = hinted ?? (projectMatches.length === 1 ? projectMatches[0] : null) ?? (installed.length === 1 ? installed[0] : null);
4512
+ if (!selected) {
4513
+ return {
4514
+ runtimeCandidates: installed.map((entry) => entry.provider.id)
4515
+ };
4516
+ }
4517
+ return {
4518
+ runtimeProviderId: selected.provider.id,
4519
+ runtimeToolName: selected.provider.toolName,
4520
+ runtimeVendor: selected.provider.vendor,
4521
+ runtimeInstructionFile: selected.provider.instructFile,
4522
+ runtimeProjectDetected: selected.projectDetected,
4523
+ runtimeDetectionMethods: selected.methods,
4524
+ runtimeCandidates: installed.map((entry) => entry.provider.id),
4525
+ inferredModelProvider: inferProviderFromVendor(selected.provider.vendor)
4526
+ };
4527
+ }
4528
+ function detectRuntimeProviderContext(snapshot = {}) {
4529
+ if (!snapshot.cwd && !snapshot.argv && !snapshot.env && cachedRuntimeProvider) {
4530
+ return cachedRuntimeProvider;
4531
+ }
4532
+ try {
4533
+ const detections = detectProjectProviders(snapshot.cwd ?? process.cwd());
4534
+ const context = selectRuntimeProviderContext(detections, snapshot);
4535
+ if (!snapshot.cwd && !snapshot.argv && !snapshot.env) {
4536
+ cachedRuntimeProvider = context;
4537
+ }
4538
+ return context;
4539
+ } catch {
4540
+ return {};
4541
+ }
4542
+ }
4543
+ function resetRuntimeProviderContextCache() {
4544
+ cachedRuntimeProvider = null;
4545
+ }
4546
+ var cachedRuntimeProvider;
4547
+ var init_provider_detection = __esm({
4548
+ "src/core/metrics/provider-detection.ts"() {
4549
+ "use strict";
4550
+ cachedRuntimeProvider = null;
4551
+ }
4552
+ });
4553
+
4442
4554
  // src/store/parsers.ts
4443
4555
  function safeParseJson(str) {
4444
4556
  if (!str) return void 0;
@@ -8482,14 +8594,16 @@ async function writeMemoryBridge(projectRoot, config) {
8482
8594
  }
8483
8595
  writeFileSync4(bridgePath, content, "utf-8");
8484
8596
  return { path: bridgePath, written: true };
8485
- } catch {
8597
+ } catch (err) {
8598
+ console.error("[CLEO] Failed to write memory bridge:", err instanceof Error ? err.message : String(err));
8486
8599
  return { path: bridgePath, written: false };
8487
8600
  }
8488
8601
  }
8489
8602
  async function refreshMemoryBridge(projectRoot) {
8490
8603
  try {
8491
8604
  await writeMemoryBridge(projectRoot);
8492
- } catch {
8605
+ } catch (err) {
8606
+ console.error("[CLEO] Memory bridge refresh failed:", err instanceof Error ? err.message : String(err));
8493
8607
  }
8494
8608
  }
8495
8609
  async function getLastHandoffSafe(projectRoot) {
@@ -9914,6 +10028,10 @@ async function ensureInjection(projectRoot) {
9914
10028
  if (existsSync26(projectContextPath)) {
9915
10029
  agentsMdLines.push("@.cleo/project-context.json");
9916
10030
  }
10031
+ const memoryBridgePath = join28(projectRoot, ".cleo", "memory-bridge.md");
10032
+ if (existsSync26(memoryBridgePath)) {
10033
+ agentsMdLines.push("@.cleo/memory-bridge.md");
10034
+ }
9917
10035
  const contributorBlock = buildContributorInjectionBlock(projectRoot);
9918
10036
  if (contributorBlock) {
9919
10037
  agentsMdLines.push(contributorBlock);
@@ -10114,6 +10232,7 @@ __export(scaffold_exports, {
10114
10232
  CLEO_GITIGNORE_FALLBACK: () => CLEO_GITIGNORE_FALLBACK,
10115
10233
  REQUIRED_CLEO_SUBDIRS: () => REQUIRED_CLEO_SUBDIRS,
10116
10234
  REQUIRED_GLOBAL_SUBDIRS: () => REQUIRED_GLOBAL_SUBDIRS,
10235
+ checkBrainDb: () => checkBrainDb,
10117
10236
  checkCleoGitRepo: () => checkCleoGitRepo,
10118
10237
  checkCleoStructure: () => checkCleoStructure,
10119
10238
  checkConfig: () => checkConfig,
@@ -10121,10 +10240,12 @@ __export(scaffold_exports, {
10121
10240
  checkGlobalHome: () => checkGlobalHome,
10122
10241
  checkGlobalTemplates: () => checkGlobalTemplates,
10123
10242
  checkLogDir: () => checkLogDir,
10243
+ checkMemoryBridge: () => checkMemoryBridge,
10124
10244
  checkProjectContext: () => checkProjectContext,
10125
10245
  checkProjectInfo: () => checkProjectInfo,
10126
10246
  checkSqliteDb: () => checkSqliteDb,
10127
10247
  createDefaultConfig: () => createDefaultConfig,
10248
+ ensureBrainDb: () => ensureBrainDb,
10128
10249
  ensureCleoGitRepo: () => ensureCleoGitRepo,
10129
10250
  ensureCleoStructure: () => ensureCleoStructure,
10130
10251
  ensureConfig: () => ensureConfig,
@@ -10739,6 +10860,79 @@ function checkSqliteDb(projectRoot) {
10739
10860
  fix: null
10740
10861
  };
10741
10862
  }
10863
+ async function ensureBrainDb(projectRoot) {
10864
+ const cleoDir = getCleoDirAbsolute(projectRoot);
10865
+ const dbPath = join29(cleoDir, "brain.db");
10866
+ if (existsSync27(dbPath)) {
10867
+ return { action: "skipped", path: dbPath, details: "brain.db already exists" };
10868
+ }
10869
+ try {
10870
+ const { getBrainDb: getBrainDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
10871
+ await getBrainDb2(projectRoot);
10872
+ return { action: "created", path: dbPath, details: "Brain database initialized" };
10873
+ } catch (err) {
10874
+ return {
10875
+ action: "skipped",
10876
+ path: dbPath,
10877
+ details: `Failed to initialize brain.db: ${err instanceof Error ? err.message : String(err)}`
10878
+ };
10879
+ }
10880
+ }
10881
+ function checkBrainDb(projectRoot) {
10882
+ const cleoDir = getCleoDirAbsolute(projectRoot);
10883
+ const dbPath = join29(cleoDir, "brain.db");
10884
+ if (!existsSync27(dbPath)) {
10885
+ return {
10886
+ id: "brain_db",
10887
+ category: "scaffold",
10888
+ status: "failed",
10889
+ message: "brain.db not found",
10890
+ details: { path: dbPath, exists: false },
10891
+ fix: "cleo init"
10892
+ };
10893
+ }
10894
+ const stat5 = statSync4(dbPath);
10895
+ if (stat5.size === 0) {
10896
+ return {
10897
+ id: "brain_db",
10898
+ category: "scaffold",
10899
+ status: "warning",
10900
+ message: "brain.db exists but is empty (0 bytes)",
10901
+ details: { path: dbPath, exists: true, size: 0 },
10902
+ fix: "cleo upgrade"
10903
+ };
10904
+ }
10905
+ return {
10906
+ id: "brain_db",
10907
+ category: "scaffold",
10908
+ status: "passed",
10909
+ message: `brain.db exists (${stat5.size} bytes)`,
10910
+ details: { path: dbPath, exists: true, size: stat5.size },
10911
+ fix: null
10912
+ };
10913
+ }
10914
+ function checkMemoryBridge(projectRoot) {
10915
+ const cleoDir = getCleoDirAbsolute(projectRoot);
10916
+ const bridgePath = join29(cleoDir, "memory-bridge.md");
10917
+ if (!existsSync27(bridgePath)) {
10918
+ return {
10919
+ id: "memory_bridge",
10920
+ category: "scaffold",
10921
+ status: "warning",
10922
+ message: "memory-bridge.md not found",
10923
+ details: { path: bridgePath, exists: false },
10924
+ fix: "cleo init or cleo refresh-memory"
10925
+ };
10926
+ }
10927
+ return {
10928
+ id: "memory_bridge",
10929
+ category: "scaffold",
10930
+ status: "passed",
10931
+ message: "memory-bridge.md exists",
10932
+ details: { path: bridgePath, exists: true },
10933
+ fix: null
10934
+ };
10935
+ }
10742
10936
  async function ensureGlobalHome() {
10743
10937
  const cleoHome = getCleoHome();
10744
10938
  const alreadyExists = existsSync27(cleoHome);
@@ -11924,6 +12118,28 @@ var init_registry2 = __esm({
11924
12118
  }
11925
12119
  });
11926
12120
 
12121
+ // src/core/adapters/adapter-registry.ts
12122
+ var ADAPTER_REGISTRY;
12123
+ var init_adapter_registry = __esm({
12124
+ "src/core/adapters/adapter-registry.ts"() {
12125
+ "use strict";
12126
+ ADAPTER_REGISTRY = {
12127
+ "claude-code": async () => {
12128
+ const { ClaudeCodeAdapter } = await import("@cleocode/adapter-claude-code");
12129
+ return new ClaudeCodeAdapter();
12130
+ },
12131
+ "opencode": async () => {
12132
+ const { OpenCodeAdapter } = await import("@cleocode/adapter-opencode");
12133
+ return new OpenCodeAdapter();
12134
+ },
12135
+ "cursor": async () => {
12136
+ const { CursorAdapter } = await import("@cleocode/adapter-cursor");
12137
+ return new CursorAdapter();
12138
+ }
12139
+ };
12140
+ }
12141
+ });
12142
+
11927
12143
  // src/core/adapters/discovery.ts
11928
12144
  import { execFileSync as execFileSync3 } from "node:child_process";
11929
12145
  import { existsSync as existsSync31, readdirSync as readdirSync13, readFileSync as readFileSync22 } from "node:fs";
@@ -11993,13 +12209,16 @@ var log4, AdapterManager;
11993
12209
  var init_manager = __esm({
11994
12210
  "src/core/adapters/manager.ts"() {
11995
12211
  "use strict";
12212
+ init_registry();
11996
12213
  init_logger();
12214
+ init_adapter_registry();
11997
12215
  init_discovery();
11998
12216
  log4 = getLogger("adapter-manager");
11999
12217
  AdapterManager = class _AdapterManager {
12000
12218
  static instance = null;
12001
12219
  adapters = /* @__PURE__ */ new Map();
12002
12220
  manifests = /* @__PURE__ */ new Map();
12221
+ hookCleanups = /* @__PURE__ */ new Map();
12003
12222
  activeId = null;
12004
12223
  projectRoot;
12005
12224
  constructor(projectRoot) {
@@ -12043,7 +12262,7 @@ var init_manager = __esm({
12043
12262
  }
12044
12263
  /**
12045
12264
  * Load and initialize an adapter by manifest ID.
12046
- * The adapter module is dynamically imported from the manifest's entryPoint.
12265
+ * Uses the static adapter registry for reliable bundled operation.
12047
12266
  */
12048
12267
  async activate(adapterId) {
12049
12268
  const manifest = this.manifests.get(adapterId);
@@ -12055,20 +12274,18 @@ var init_manager = __esm({
12055
12274
  this.activeId = adapterId;
12056
12275
  return existing;
12057
12276
  }
12058
- const { resolve: resolve12 } = await import("node:path");
12059
- const entryPath = resolve12(
12060
- this.projectRoot,
12061
- "packages",
12062
- "adapters",
12063
- manifest.provider,
12064
- manifest.entryPoint
12065
- );
12277
+ const factory = ADAPTER_REGISTRY[adapterId];
12278
+ if (!factory) {
12279
+ throw new Error(`No adapter registered in static registry: ${adapterId}`);
12280
+ }
12066
12281
  try {
12067
- const mod = await import(entryPath);
12068
- const adapter = typeof mod.default === "function" ? new mod.default() : typeof mod.createAdapter === "function" ? await mod.createAdapter() : mod.default;
12282
+ const adapter = await factory();
12069
12283
  await adapter.initialize(this.projectRoot);
12070
12284
  this.adapters.set(adapterId, adapter);
12071
12285
  this.activeId = adapterId;
12286
+ if (adapter.hooks) {
12287
+ await this.wireAdapterHooks(adapterId, adapter);
12288
+ }
12072
12289
  log4.info({ adapterId, provider: manifest.provider }, "Adapter activated");
12073
12290
  return adapter;
12074
12291
  } catch (err) {
@@ -12150,6 +12367,7 @@ var init_manager = __esm({
12150
12367
  async dispose() {
12151
12368
  for (const [id, adapter] of this.adapters) {
12152
12369
  try {
12370
+ await this.cleanupAdapterHooks(id, adapter);
12153
12371
  await adapter.dispose();
12154
12372
  log4.info({ adapterId: id }, "Adapter disposed");
12155
12373
  } catch (err) {
@@ -12157,6 +12375,7 @@ var init_manager = __esm({
12157
12375
  }
12158
12376
  }
12159
12377
  this.adapters.clear();
12378
+ this.hookCleanups.clear();
12160
12379
  this.activeId = null;
12161
12380
  }
12162
12381
  /** Dispose a single adapter. */
@@ -12164,6 +12383,7 @@ var init_manager = __esm({
12164
12383
  const adapter = this.adapters.get(adapterId);
12165
12384
  if (!adapter) return;
12166
12385
  try {
12386
+ await this.cleanupAdapterHooks(adapterId, adapter);
12167
12387
  await adapter.dispose();
12168
12388
  } catch (err) {
12169
12389
  log4.error({ adapterId, err }, "Failed to dispose adapter");
@@ -12173,6 +12393,51 @@ var init_manager = __esm({
12173
12393
  this.activeId = null;
12174
12394
  }
12175
12395
  }
12396
+ /**
12397
+ * Wire an adapter's hook event map into CLEO's HookRegistry.
12398
+ * Creates bridging handlers at priority 50 for each mapped event.
12399
+ */
12400
+ async wireAdapterHooks(adapterId, adapter) {
12401
+ if (!adapter.hooks) return;
12402
+ try {
12403
+ await adapter.hooks.registerNativeHooks(this.projectRoot);
12404
+ } catch (err) {
12405
+ log4.error({ adapterId, err }, "Failed to register native hooks");
12406
+ }
12407
+ const eventMap = adapter.hooks.getEventMap?.();
12408
+ if (!eventMap) return;
12409
+ const cleanups = [];
12410
+ for (const [_providerEvent, caampEvent] of Object.entries(eventMap)) {
12411
+ const hookId = `adapter-${adapterId}-${caampEvent}`;
12412
+ const unregister = hooks.register({
12413
+ id: hookId,
12414
+ event: caampEvent,
12415
+ priority: 50,
12416
+ handler: async (_projectRoot, payload) => {
12417
+ log4.debug({ adapterId, event: caampEvent, payload }, "Adapter hook dispatched");
12418
+ }
12419
+ });
12420
+ cleanups.push(unregister);
12421
+ }
12422
+ this.hookCleanups.set(adapterId, cleanups);
12423
+ }
12424
+ /**
12425
+ * Clean up hook registrations for an adapter.
12426
+ */
12427
+ async cleanupAdapterHooks(adapterId, adapter) {
12428
+ const cleanups = this.hookCleanups.get(adapterId);
12429
+ if (cleanups) {
12430
+ for (const fn of cleanups) {
12431
+ fn();
12432
+ }
12433
+ this.hookCleanups.delete(adapterId);
12434
+ }
12435
+ try {
12436
+ await adapter.hooks?.unregisterNativeHooks();
12437
+ } catch (err) {
12438
+ log4.error({ adapterId, err }, "Failed to unregister native hooks");
12439
+ }
12440
+ }
12176
12441
  };
12177
12442
  }
12178
12443
  });
@@ -12180,6 +12445,7 @@ var init_manager = __esm({
12180
12445
  // src/core/adapters/index.ts
12181
12446
  var adapters_exports = {};
12182
12447
  __export(adapters_exports, {
12448
+ ADAPTER_REGISTRY: () => ADAPTER_REGISTRY,
12183
12449
  AdapterManager: () => AdapterManager,
12184
12450
  detectProvider: () => detectProvider,
12185
12451
  discoverAdapterManifests: () => discoverAdapterManifests
@@ -12187,6 +12453,7 @@ __export(adapters_exports, {
12187
12453
  var init_adapters = __esm({
12188
12454
  "src/core/adapters/index.ts"() {
12189
12455
  "use strict";
12456
+ init_adapter_registry();
12190
12457
  init_manager();
12191
12458
  init_discovery();
12192
12459
  }
@@ -12410,6 +12677,14 @@ async function initProject(opts = {}) {
12410
12677
  } catch (err) {
12411
12678
  created.push(`tasks.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
12412
12679
  }
12680
+ try {
12681
+ const brainResult = await ensureBrainDb(projRoot);
12682
+ if (brainResult.action === "created") {
12683
+ created.push("brain.db");
12684
+ }
12685
+ } catch (err) {
12686
+ created.push(`brain.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
12687
+ }
12413
12688
  if (force) {
12414
12689
  const gitignoreResult = await ensureGitignore(projRoot);
12415
12690
  if (gitignoreResult.action === "skipped") {
@@ -12532,11 +12807,34 @@ async function initProject(opts = {}) {
12532
12807
  const detected = mgr.detectActive();
12533
12808
  if (detected.length > 0) {
12534
12809
  created.push(`adapters: active provider detected (${detected.join(", ")})`);
12810
+ for (const adapterId of detected) {
12811
+ try {
12812
+ const adapter = await mgr.activate(adapterId);
12813
+ const installResult = await adapter.install.install({
12814
+ projectDir: projRoot
12815
+ });
12816
+ if (installResult.success) {
12817
+ created.push(`adapter install (${adapterId}): installed`);
12818
+ } else {
12819
+ warnings.push(`adapter install (${adapterId}): failed`);
12820
+ }
12821
+ } catch (err) {
12822
+ warnings.push(`adapter activate/install (${adapterId}): ${err instanceof Error ? err.message : String(err)}`);
12823
+ }
12824
+ }
12535
12825
  }
12536
12826
  }
12537
12827
  } catch (err) {
12538
12828
  warnings.push(`Adapter discovery: ${err instanceof Error ? err.message : String(err)}`);
12539
12829
  }
12830
+ try {
12831
+ const bridgeResult = await writeMemoryBridge(projRoot);
12832
+ if (bridgeResult.written) {
12833
+ created.push("memory-bridge.md");
12834
+ }
12835
+ } catch (err) {
12836
+ warnings.push(`Memory bridge: ${err instanceof Error ? err.message : String(err)}`);
12837
+ }
12540
12838
  const rootGitignoreResult = await removeCleoFromRootGitignore(projRoot);
12541
12839
  if (rootGitignoreResult.removed) {
12542
12840
  warnings.push(
@@ -12598,6 +12896,7 @@ var init_init = __esm({
12598
12896
  init_paths();
12599
12897
  init_scaffold();
12600
12898
  init_schema_management();
12899
+ init_memory_bridge();
12601
12900
  }
12602
12901
  });
12603
12902
 
@@ -14546,6 +14845,13 @@ async function startSession(options, cwd, accessor) {
14546
14845
  );
14547
14846
  }
14548
14847
  }
14848
+ let detectedProviderId = null;
14849
+ try {
14850
+ const { detectRuntimeProviderContext: detectRuntimeProviderContext2 } = await Promise.resolve().then(() => (init_provider_detection(), provider_detection_exports));
14851
+ const ctx = detectRuntimeProviderContext2();
14852
+ detectedProviderId = ctx.runtimeProviderId ?? null;
14853
+ } catch {
14854
+ }
14549
14855
  const session = {
14550
14856
  id: generateSessionId(),
14551
14857
  name: options.name,
@@ -14560,7 +14866,7 @@ async function startSession(options, cwd, accessor) {
14560
14866
  notes: [],
14561
14867
  tasksCompleted: [],
14562
14868
  tasksCreated: [],
14563
- providerId: options.providerId ?? null
14869
+ providerId: options.providerId ?? detectedProviderId ?? null
14564
14870
  };
14565
14871
  if (options.grade) {
14566
14872
  session.notes = ["[grade-mode:enabled]", ...session.notes ?? []];
@@ -14570,13 +14876,22 @@ async function startSession(options, cwd, accessor) {
14570
14876
  }
14571
14877
  sessions2.push(session);
14572
14878
  await saveSessions(sessions2, cwd, accessor);
14879
+ if (session.providerId) {
14880
+ Promise.resolve().then(() => (init_adapters(), adapters_exports)).then(({ AdapterManager: AdapterManager2 }) => {
14881
+ const mgr = AdapterManager2.getInstance(cwd ?? process.cwd());
14882
+ mgr.discover();
14883
+ return mgr.activate(session.providerId);
14884
+ }).catch(() => {
14885
+ });
14886
+ }
14573
14887
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
14574
14888
  hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
14575
14889
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
14576
14890
  sessionId: session.id,
14577
14891
  name: options.name,
14578
14892
  scope,
14579
- agent: options.agent
14893
+ agent: options.agent,
14894
+ providerId: session.providerId ?? void 0
14580
14895
  }).catch(() => {
14581
14896
  });
14582
14897
  return session;
@@ -14612,7 +14927,8 @@ async function endSession(options = {}, cwd, accessor) {
14612
14927
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
14613
14928
  sessionId: session.id,
14614
14929
  duration,
14615
- tasksCompleted: session.tasksCompleted || []
14930
+ tasksCompleted: session.tasksCompleted || [],
14931
+ providerId: session.providerId ?? void 0
14616
14932
  }).catch(() => {
14617
14933
  });
14618
14934
  const { bridgeSessionToMemory: bridgeSessionToMemory2 } = await Promise.resolve().then(() => (init_session_memory_bridge(), session_memory_bridge_exports));
@@ -15130,7 +15446,7 @@ async function initializeDefaultAdapters() {
15130
15446
  }
15131
15447
  }
15132
15448
  var SpawnAdapterRegistry, spawnRegistry;
15133
- var init_adapter_registry = __esm({
15449
+ var init_adapter_registry2 = __esm({
15134
15450
  "src/core/spawn/adapter-registry.ts"() {
15135
15451
  "use strict";
15136
15452
  SpawnAdapterRegistry = class {
@@ -16359,6 +16675,18 @@ async function startupHealthCheck(projectRoot) {
16359
16675
  } else {
16360
16676
  checks.push({ check: "log_dir", status: "pass", message: "Log directory present" });
16361
16677
  }
16678
+ const brainDbCheck = checkBrainDb(root);
16679
+ checks.push({
16680
+ check: "brain_db",
16681
+ status: brainDbCheck.status === "passed" ? "pass" : "warn",
16682
+ message: brainDbCheck.message
16683
+ });
16684
+ const memBridgeCheck = checkMemoryBridge(root);
16685
+ checks.push({
16686
+ check: "memory_bridge",
16687
+ status: memBridgeCheck.status === "passed" ? "pass" : "warn",
16688
+ message: memBridgeCheck.message
16689
+ });
16362
16690
  const hasFailures = failures.length > 0;
16363
16691
  const state = hasFailures && !projectHealthy ? "needs_upgrade" : "healthy";
16364
16692
  return {
@@ -20625,104 +20953,8 @@ async function resolveProviderFromModelRegistry(model) {
20625
20953
  return resolveProviderFromModelIndex(index5, model);
20626
20954
  }
20627
20955
 
20628
- // src/core/metrics/provider-detection.ts
20629
- import { basename as basename3 } from "node:path";
20630
- import {
20631
- detectProjectProviders,
20632
- getProvider,
20633
- resolveAlias
20634
- } from "@cleocode/caamp";
20635
- function inferProviderFromVendor(vendor) {
20636
- const value = (vendor ?? "").trim().toLowerCase();
20637
- if (!value) return void 0;
20638
- if (value.includes("anthropic") || value.includes("claude")) return "anthropic";
20639
- if (value.includes("openai") || value.includes("codex") || value.includes("chatgpt"))
20640
- return "openai";
20641
- if (value.includes("google") || value.includes("gemini")) return "google";
20642
- if (value.includes("xai") || value.includes("grok")) return "xai";
20643
- return void 0;
20644
- }
20645
- function getRuntimeHints(snapshot) {
20646
- const argv = snapshot.argv ?? process.argv;
20647
- const env = snapshot.env ?? process.env;
20648
- const hints = /* @__PURE__ */ new Set();
20649
- const bin = basename3(argv[1] ?? argv[0] ?? "").replace(/\.[^.]+$/, "");
20650
- if (bin) hints.add(bin);
20651
- if (env["CLAUDE_CODE_ENABLE_TELEMETRY"] || env["CLAUDE_CODE_ENTRYPOINT"]) {
20652
- hints.add("claude-code");
20653
- hints.add("claude");
20654
- }
20655
- if (env["OPENCODE_AGENT"] || env["OPENCODE"]) {
20656
- hints.add("opencode");
20657
- }
20658
- if (env["CURSOR_TRACE_ID"] || env["CURSOR_AGENT"]) {
20659
- hints.add("cursor");
20660
- }
20661
- return Array.from(hints);
20662
- }
20663
- function pickDetectionByHint(detections, hints) {
20664
- for (const hint of hints) {
20665
- const resolved = resolveAlias(hint);
20666
- const direct = detections.find(
20667
- (entry) => entry.provider.id === resolved || entry.provider.id === hint
20668
- );
20669
- if (direct) return direct;
20670
- const byAlias = detections.find(
20671
- (entry) => entry.provider.aliases.includes(hint) || entry.provider.agentFlag === hint || entry.provider.toolName.toLowerCase() === hint.toLowerCase()
20672
- );
20673
- if (byAlias) return byAlias;
20674
- const provider = getProvider(resolved);
20675
- if (provider) {
20676
- return {
20677
- provider,
20678
- installed: true,
20679
- methods: [],
20680
- projectDetected: false
20681
- };
20682
- }
20683
- }
20684
- return null;
20685
- }
20686
- function selectRuntimeProviderContext(detections, snapshot = {}) {
20687
- const hints = getRuntimeHints(snapshot);
20688
- const hinted = pickDetectionByHint(detections, hints);
20689
- const projectMatches = detections.filter((entry) => entry.projectDetected);
20690
- const installed = detections.filter((entry) => entry.installed);
20691
- const selected = hinted ?? (projectMatches.length === 1 ? projectMatches[0] : null) ?? (installed.length === 1 ? installed[0] : null);
20692
- if (!selected) {
20693
- return {
20694
- runtimeCandidates: installed.map((entry) => entry.provider.id)
20695
- };
20696
- }
20697
- return {
20698
- runtimeProviderId: selected.provider.id,
20699
- runtimeToolName: selected.provider.toolName,
20700
- runtimeVendor: selected.provider.vendor,
20701
- runtimeInstructionFile: selected.provider.instructFile,
20702
- runtimeProjectDetected: selected.projectDetected,
20703
- runtimeDetectionMethods: selected.methods,
20704
- runtimeCandidates: installed.map((entry) => entry.provider.id),
20705
- inferredModelProvider: inferProviderFromVendor(selected.provider.vendor)
20706
- };
20707
- }
20708
- var cachedRuntimeProvider = null;
20709
- function detectRuntimeProviderContext(snapshot = {}) {
20710
- if (!snapshot.cwd && !snapshot.argv && !snapshot.env && cachedRuntimeProvider) {
20711
- return cachedRuntimeProvider;
20712
- }
20713
- try {
20714
- const detections = detectProjectProviders(snapshot.cwd ?? process.cwd());
20715
- const context = selectRuntimeProviderContext(detections, snapshot);
20716
- if (!snapshot.cwd && !snapshot.argv && !snapshot.env) {
20717
- cachedRuntimeProvider = context;
20718
- }
20719
- return context;
20720
- } catch {
20721
- return {};
20722
- }
20723
- }
20724
-
20725
20956
  // src/core/metrics/token-service.ts
20957
+ init_provider_detection();
20726
20958
  function normalizeProvider(provider, model, runtimeProvider) {
20727
20959
  const value = (provider ?? "").trim().toLowerCase();
20728
20960
  const modelValue = (model ?? "").trim().toLowerCase();
@@ -28570,7 +28802,7 @@ async function orchestrateValidate(taskId, projectRoot) {
28570
28802
  async function orchestrateSpawnExecute(taskId, adapterId, protocolType, projectRoot, _tier) {
28571
28803
  const cwd = projectRoot ?? process.cwd();
28572
28804
  try {
28573
- const { initializeDefaultAdapters: initializeDefaultAdapters2, spawnRegistry: spawnRegistry2 } = await Promise.resolve().then(() => (init_adapter_registry(), adapter_registry_exports));
28805
+ const { initializeDefaultAdapters: initializeDefaultAdapters2, spawnRegistry: spawnRegistry2 } = await Promise.resolve().then(() => (init_adapter_registry2(), adapter_registry_exports));
28574
28806
  await initializeDefaultAdapters2();
28575
28807
  let adapter;
28576
28808
  if (adapterId) {
@@ -29699,7 +29931,10 @@ async function checkEpicCompleteness(releaseTaskIds, cwd, accessor) {
29699
29931
  if (!epic) continue;
29700
29932
  const allChildren = data.tasks.filter((t) => t.parentId === epicId && t.id !== epicId);
29701
29933
  const includedSet = new Set(includedTasks);
29702
- const missingChildren = allChildren.filter((t) => !includedSet.has(t.id) && !releaseSet.has(t.id)).map((t) => ({ id: t.id, title: t.title, status: t.status }));
29934
+ const parentIds = new Set(data.tasks.filter((t) => t.parentId).map((t) => t.parentId));
29935
+ const missingChildren = allChildren.filter(
29936
+ (t) => t.status === "done" && !parentIds.has(t.id) && !includedSet.has(t.id) && !releaseSet.has(t.id)
29937
+ ).map((t) => ({ id: t.id, title: t.title, status: t.status }));
29703
29938
  if (missingChildren.length > 0) hasIncomplete = true;
29704
29939
  epics.push({
29705
29940
  epicId,
@@ -29856,9 +30091,9 @@ init_tasks_schema();
29856
30091
  init_pagination();
29857
30092
  init_paths();
29858
30093
  import { execFileSync as execFileSync5 } from "node:child_process";
29859
- import { existsSync as existsSync42, renameSync as renameSync7 } from "node:fs";
30094
+ import { existsSync as existsSync43, renameSync as renameSync7 } from "node:fs";
29860
30095
  import { readFile as readFile11 } from "node:fs/promises";
29861
- import { join as join43 } from "node:path";
30096
+ import { join as join44 } from "node:path";
29862
30097
  import { and as and6, count as count4, desc as desc5, eq as eq15 } from "drizzle-orm";
29863
30098
 
29864
30099
  // src/core/release/changelog-writer.ts
@@ -29952,6 +30187,170 @@ function escapeRegex(str) {
29952
30187
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29953
30188
  }
29954
30189
 
30190
+ // src/core/release/version-bump.ts
30191
+ init_exit_codes();
30192
+ init_errors();
30193
+ init_paths();
30194
+ import { existsSync as existsSync42, readFileSync as readFileSync29, writeFileSync as writeFileSync7 } from "node:fs";
30195
+ import { join as join43 } from "node:path";
30196
+ function readConfigValueSync2(path, defaultValue, cwd) {
30197
+ try {
30198
+ const configPath = join43(getCleoDir(cwd), "config.json");
30199
+ if (!existsSync42(configPath)) return defaultValue;
30200
+ const config = JSON.parse(readFileSync29(configPath, "utf-8"));
30201
+ const keys = path.split(".");
30202
+ let value = config;
30203
+ for (const key of keys) {
30204
+ if (value == null || typeof value !== "object") return defaultValue;
30205
+ value = value[key];
30206
+ }
30207
+ return value ?? defaultValue;
30208
+ } catch {
30209
+ return defaultValue;
30210
+ }
30211
+ }
30212
+ var VERSION_WITH_PRERELEASE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
30213
+ function validateVersionFormat(version) {
30214
+ return VERSION_WITH_PRERELEASE.test(version);
30215
+ }
30216
+ function getVersionBumpConfig(cwd) {
30217
+ try {
30218
+ const raw = readConfigValueSync2("release.versionBump.files", [], cwd);
30219
+ return raw.map((entry) => ({
30220
+ file: entry.path ?? entry.file ?? "",
30221
+ strategy: entry.strategy,
30222
+ field: entry.jsonPath?.replace(/^\./, "") ?? entry.field,
30223
+ key: entry.key,
30224
+ section: entry.section,
30225
+ pattern: entry.sedPattern ?? entry.pattern
30226
+ })).filter((t) => t.file !== "");
30227
+ } catch {
30228
+ return [];
30229
+ }
30230
+ }
30231
+ function bumpFile(target, newVersion, projectRoot) {
30232
+ const filePath = join43(projectRoot, target.file);
30233
+ if (!existsSync42(filePath)) {
30234
+ return {
30235
+ file: target.file,
30236
+ strategy: target.strategy,
30237
+ success: false,
30238
+ error: `File not found: ${target.file}`
30239
+ };
30240
+ }
30241
+ try {
30242
+ const content = readFileSync29(filePath, "utf-8");
30243
+ let previousVersion;
30244
+ let newContent;
30245
+ switch (target.strategy) {
30246
+ case "plain": {
30247
+ previousVersion = content.trim();
30248
+ newContent = newVersion + "\n";
30249
+ break;
30250
+ }
30251
+ case "json": {
30252
+ const field = target.field ?? "version";
30253
+ const json = JSON.parse(content);
30254
+ previousVersion = getNestedField(json, field);
30255
+ setNestedField(json, field, newVersion);
30256
+ newContent = JSON.stringify(json, null, 2) + "\n";
30257
+ break;
30258
+ }
30259
+ case "toml": {
30260
+ const key = target.key ?? "version";
30261
+ const versionRegex = new RegExp(`^(${key}\\s*=\\s*")([^"]+)(")`, "m");
30262
+ const match = content.match(versionRegex);
30263
+ previousVersion = match?.[2];
30264
+ newContent = content.replace(versionRegex, `$1${newVersion}$3`);
30265
+ break;
30266
+ }
30267
+ case "sed": {
30268
+ const pattern = target.pattern ?? "";
30269
+ if (!pattern.includes("{{VERSION}}")) {
30270
+ return {
30271
+ file: target.file,
30272
+ strategy: target.strategy,
30273
+ success: false,
30274
+ error: "sed strategy requires {{VERSION}} placeholder in pattern"
30275
+ };
30276
+ }
30277
+ const regex = new RegExp(pattern.replace("{{VERSION}}", "([\\d.]+)"));
30278
+ const match = content.match(regex);
30279
+ previousVersion = match?.[1];
30280
+ newContent = content.replace(regex, pattern.replace("{{VERSION}}", newVersion));
30281
+ break;
30282
+ }
30283
+ default:
30284
+ return {
30285
+ file: target.file,
30286
+ strategy: target.strategy,
30287
+ success: false,
30288
+ error: `Unknown strategy: ${target.strategy}`
30289
+ };
30290
+ }
30291
+ writeFileSync7(filePath, newContent, "utf-8");
30292
+ return {
30293
+ file: target.file,
30294
+ strategy: target.strategy,
30295
+ success: true,
30296
+ previousVersion,
30297
+ newVersion
30298
+ };
30299
+ } catch (err) {
30300
+ return {
30301
+ file: target.file,
30302
+ strategy: target.strategy,
30303
+ success: false,
30304
+ error: String(err)
30305
+ };
30306
+ }
30307
+ }
30308
+ function bumpVersionFromConfig(newVersion, options = {}, cwd) {
30309
+ if (!validateVersionFormat(newVersion)) {
30310
+ throw new CleoError(
30311
+ 6 /* VALIDATION_ERROR */,
30312
+ `Invalid version: '${newVersion}' (expected X.Y.Z or YYYY.M.patch)`
30313
+ );
30314
+ }
30315
+ const targets = getVersionBumpConfig(cwd);
30316
+ if (targets.length === 0) {
30317
+ throw new CleoError(
30318
+ 1 /* GENERAL_ERROR */,
30319
+ "No version bump targets configured. Add release.versionBump.files to .cleo/config.json"
30320
+ );
30321
+ }
30322
+ const projectRoot = getProjectRoot(cwd);
30323
+ const results = [];
30324
+ if (options.dryRun) {
30325
+ for (const target of targets) {
30326
+ results.push({
30327
+ file: target.file,
30328
+ strategy: target.strategy,
30329
+ success: true,
30330
+ newVersion
30331
+ });
30332
+ }
30333
+ return { results, allSuccess: true };
30334
+ }
30335
+ for (const target of targets) {
30336
+ results.push(bumpFile(target, newVersion, projectRoot));
30337
+ }
30338
+ const allSuccess = results.every((r) => r.success);
30339
+ return { results, allSuccess };
30340
+ }
30341
+ function getNestedField(obj, path) {
30342
+ return path.split(".").reduce((acc, key) => acc?.[key], obj);
30343
+ }
30344
+ function setNestedField(obj, path, value) {
30345
+ const parts = path.split(".");
30346
+ let current = obj;
30347
+ for (let i = 0; i < parts.length - 1; i++) {
30348
+ if (typeof current[parts[i]] !== "object") current[parts[i]] = {};
30349
+ current = current[parts[i]];
30350
+ }
30351
+ current[parts[parts.length - 1]] = value;
30352
+ }
30353
+
29955
30354
  // src/core/release/release-manifest.ts
29956
30355
  function normalizeLimit2(limit) {
29957
30356
  return typeof limit === "number" && limit > 0 ? limit : void 0;
@@ -30177,7 +30576,7 @@ async function generateReleaseChangelog(version, loadTasksFn, cwd) {
30177
30576
  }
30178
30577
  const changelog = sections.join("\n");
30179
30578
  await db.update(releaseManifests).set({ changelog }).where(eq15(releaseManifests.version, normalizedVersion)).run();
30180
- const changelogPath = join43(cwd ?? process.cwd(), "CHANGELOG.md");
30579
+ const changelogPath = join44(cwd ?? process.cwd(), "CHANGELOG.md");
30181
30580
  let existingChangelogContent = "";
30182
30581
  try {
30183
30582
  existingChangelogContent = await readFile11(changelogPath, "utf8");
@@ -30291,13 +30690,13 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
30291
30690
  message: incompleteTasks.length === 0 ? "All tasks completed" : `${incompleteTasks.length} tasks not completed: ${incompleteTasks.join(", ")}`
30292
30691
  });
30293
30692
  const projectRoot = cwd ?? getProjectRoot();
30294
- const distPath = join43(projectRoot, "dist", "cli", "index.js");
30295
- const isNodeProject = existsSync42(join43(projectRoot, "package.json"));
30693
+ const distPath = join44(projectRoot, "dist", "cli", "index.js");
30694
+ const isNodeProject = existsSync43(join44(projectRoot, "package.json"));
30296
30695
  if (isNodeProject) {
30297
30696
  gates.push({
30298
30697
  name: "build_artifact",
30299
- status: existsSync42(distPath) ? "passed" : "failed",
30300
- message: existsSync42(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
30698
+ status: existsSync43(distPath) ? "passed" : "failed",
30699
+ message: existsSync43(distPath) ? "dist/cli/index.js present" : "dist/ not built \u2014 run: npm run build"
30301
30700
  });
30302
30701
  }
30303
30702
  if (opts?.dryRun) {
@@ -30307,6 +30706,8 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
30307
30706
  message: "Skipped in dry-run mode"
30308
30707
  });
30309
30708
  } else {
30709
+ const bumpTargets = getVersionBumpConfig(cwd);
30710
+ const allowedDirty = /* @__PURE__ */ new Set(["CHANGELOG.md", ...bumpTargets.map((t) => t.file)]);
30310
30711
  let workingTreeClean = true;
30311
30712
  let dirtyFiles = [];
30312
30713
  try {
@@ -30315,14 +30716,15 @@ async function runReleaseGates(version, loadTasksFn, cwd, opts) {
30315
30716
  encoding: "utf-8",
30316
30717
  stdio: "pipe"
30317
30718
  });
30318
- 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");
30719
+ dirtyFiles = porcelain.split("\n").filter((l) => l.trim()).filter((l) => !l.startsWith("?? ")).map((l) => l.slice(3).trim()).filter((f) => !allowedDirty.has(f));
30319
30720
  workingTreeClean = dirtyFiles.length === 0;
30320
30721
  } catch {
30321
30722
  }
30723
+ const excludeList = [...allowedDirty].join(", ");
30322
30724
  gates.push({
30323
30725
  name: "clean_working_tree",
30324
30726
  status: workingTreeClean ? "passed" : "failed",
30325
- 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)` : ""}`
30727
+ message: workingTreeClean ? `Working tree clean (excluding ${excludeList})` : `Uncommitted changes in: ${dirtyFiles.slice(0, 5).join(", ")}${dirtyFiles.length > 5 ? ` (+${dirtyFiles.length - 5} more)` : ""}`
30326
30728
  });
30327
30729
  }
30328
30730
  const isPreRelease = normalizedVersion.includes("-");
@@ -30437,7 +30839,7 @@ async function rollbackRelease(version, reason, cwd) {
30437
30839
  };
30438
30840
  }
30439
30841
  async function readPushPolicy(cwd) {
30440
- const configPath = join43(getCleoDirAbsolute(cwd), "config.json");
30842
+ const configPath = join44(getCleoDirAbsolute(cwd), "config.json");
30441
30843
  const config = await readJson(configPath);
30442
30844
  if (!config) return void 0;
30443
30845
  const release2 = config.release;
@@ -30535,170 +30937,6 @@ async function markReleasePushed(version, pushedAt, cwd, provenance) {
30535
30937
  }).where(eq15(releaseManifests.version, normalizedVersion)).run();
30536
30938
  }
30537
30939
 
30538
- // src/core/release/version-bump.ts
30539
- init_exit_codes();
30540
- init_errors();
30541
- init_paths();
30542
- import { existsSync as existsSync43, readFileSync as readFileSync29, writeFileSync as writeFileSync7 } from "node:fs";
30543
- import { join as join44 } from "node:path";
30544
- function readConfigValueSync2(path, defaultValue, cwd) {
30545
- try {
30546
- const configPath = join44(getCleoDir(cwd), "config.json");
30547
- if (!existsSync43(configPath)) return defaultValue;
30548
- const config = JSON.parse(readFileSync29(configPath, "utf-8"));
30549
- const keys = path.split(".");
30550
- let value = config;
30551
- for (const key of keys) {
30552
- if (value == null || typeof value !== "object") return defaultValue;
30553
- value = value[key];
30554
- }
30555
- return value ?? defaultValue;
30556
- } catch {
30557
- return defaultValue;
30558
- }
30559
- }
30560
- var VERSION_WITH_PRERELEASE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
30561
- function validateVersionFormat(version) {
30562
- return VERSION_WITH_PRERELEASE.test(version);
30563
- }
30564
- function getVersionBumpConfig(cwd) {
30565
- try {
30566
- const raw = readConfigValueSync2("release.versionBump.files", [], cwd);
30567
- return raw.map((entry) => ({
30568
- file: entry.path ?? entry.file ?? "",
30569
- strategy: entry.strategy,
30570
- field: entry.jsonPath?.replace(/^\./, "") ?? entry.field,
30571
- key: entry.key,
30572
- section: entry.section,
30573
- pattern: entry.sedPattern ?? entry.pattern
30574
- })).filter((t) => t.file !== "");
30575
- } catch {
30576
- return [];
30577
- }
30578
- }
30579
- function bumpFile(target, newVersion, projectRoot) {
30580
- const filePath = join44(projectRoot, target.file);
30581
- if (!existsSync43(filePath)) {
30582
- return {
30583
- file: target.file,
30584
- strategy: target.strategy,
30585
- success: false,
30586
- error: `File not found: ${target.file}`
30587
- };
30588
- }
30589
- try {
30590
- const content = readFileSync29(filePath, "utf-8");
30591
- let previousVersion;
30592
- let newContent;
30593
- switch (target.strategy) {
30594
- case "plain": {
30595
- previousVersion = content.trim();
30596
- newContent = newVersion + "\n";
30597
- break;
30598
- }
30599
- case "json": {
30600
- const field = target.field ?? "version";
30601
- const json = JSON.parse(content);
30602
- previousVersion = getNestedField(json, field);
30603
- setNestedField(json, field, newVersion);
30604
- newContent = JSON.stringify(json, null, 2) + "\n";
30605
- break;
30606
- }
30607
- case "toml": {
30608
- const key = target.key ?? "version";
30609
- const versionRegex = new RegExp(`^(${key}\\s*=\\s*")([^"]+)(")`, "m");
30610
- const match = content.match(versionRegex);
30611
- previousVersion = match?.[2];
30612
- newContent = content.replace(versionRegex, `$1${newVersion}$3`);
30613
- break;
30614
- }
30615
- case "sed": {
30616
- const pattern = target.pattern ?? "";
30617
- if (!pattern.includes("{{VERSION}}")) {
30618
- return {
30619
- file: target.file,
30620
- strategy: target.strategy,
30621
- success: false,
30622
- error: "sed strategy requires {{VERSION}} placeholder in pattern"
30623
- };
30624
- }
30625
- const regex = new RegExp(pattern.replace("{{VERSION}}", "([\\d.]+)"));
30626
- const match = content.match(regex);
30627
- previousVersion = match?.[1];
30628
- newContent = content.replace(regex, pattern.replace("{{VERSION}}", newVersion));
30629
- break;
30630
- }
30631
- default:
30632
- return {
30633
- file: target.file,
30634
- strategy: target.strategy,
30635
- success: false,
30636
- error: `Unknown strategy: ${target.strategy}`
30637
- };
30638
- }
30639
- writeFileSync7(filePath, newContent, "utf-8");
30640
- return {
30641
- file: target.file,
30642
- strategy: target.strategy,
30643
- success: true,
30644
- previousVersion,
30645
- newVersion
30646
- };
30647
- } catch (err) {
30648
- return {
30649
- file: target.file,
30650
- strategy: target.strategy,
30651
- success: false,
30652
- error: String(err)
30653
- };
30654
- }
30655
- }
30656
- function bumpVersionFromConfig(newVersion, options = {}, cwd) {
30657
- if (!validateVersionFormat(newVersion)) {
30658
- throw new CleoError(
30659
- 6 /* VALIDATION_ERROR */,
30660
- `Invalid version: '${newVersion}' (expected X.Y.Z or YYYY.M.patch)`
30661
- );
30662
- }
30663
- const targets = getVersionBumpConfig(cwd);
30664
- if (targets.length === 0) {
30665
- throw new CleoError(
30666
- 1 /* GENERAL_ERROR */,
30667
- "No version bump targets configured. Add release.versionBump.files to .cleo/config.json"
30668
- );
30669
- }
30670
- const projectRoot = getProjectRoot(cwd);
30671
- const results = [];
30672
- if (options.dryRun) {
30673
- for (const target of targets) {
30674
- results.push({
30675
- file: target.file,
30676
- strategy: target.strategy,
30677
- success: true,
30678
- newVersion
30679
- });
30680
- }
30681
- return { results, allSuccess: true };
30682
- }
30683
- for (const target of targets) {
30684
- results.push(bumpFile(target, newVersion, projectRoot));
30685
- }
30686
- const allSuccess = results.every((r) => r.success);
30687
- return { results, allSuccess };
30688
- }
30689
- function getNestedField(obj, path) {
30690
- return path.split(".").reduce((acc, key) => acc?.[key], obj);
30691
- }
30692
- function setNestedField(obj, path, value) {
30693
- const parts = path.split(".");
30694
- let current = obj;
30695
- for (let i = 0; i < parts.length - 1; i++) {
30696
- if (typeof current[parts[i]] !== "object") current[parts[i]] = {};
30697
- current = current[parts[i]];
30698
- }
30699
- current[parts[parts.length - 1]] = value;
30700
- }
30701
-
30702
30940
  // src/dispatch/engines/release-engine.ts
30703
30941
  init_data_accessor();
30704
30942
  init_error();
@@ -45215,7 +45453,7 @@ import { execFileSync as execFileSync13 } from "node:child_process";
45215
45453
  // src/config/build-config.ts
45216
45454
  var BUILD_CONFIG = {
45217
45455
  "name": "@cleocode/cleo",
45218
- "version": "2026.3.29",
45456
+ "version": "2026.3.30",
45219
45457
  "description": "CLEO V2 - TypeScript task management CLI for AI coding agents",
45220
45458
  "repository": {
45221
45459
  "owner": "kryptobaseddev",
@@ -45224,7 +45462,7 @@ var BUILD_CONFIG = {
45224
45462
  "url": "https://github.com/kryptobaseddev/cleo.git",
45225
45463
  "issuesUrl": "https://github.com/kryptobaseddev/cleo/issues"
45226
45464
  },
45227
- "buildDate": "2026-03-16T14:27:12.409Z",
45465
+ "buildDate": "2026-03-16T17:27:58.735Z",
45228
45466
  "templates": {
45229
45467
  "issueTemplatesDir": "templates/issue-templates"
45230
45468
  }
@@ -47933,6 +48171,30 @@ async function runUpgrade(options = {}) {
47933
48171
  }
47934
48172
  } catch {
47935
48173
  }
48174
+ try {
48175
+ const { ensureBrainDb: ensureBrainDb2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
48176
+ const brainResult = await ensureBrainDb2(projectRootForMaint);
48177
+ if (brainResult.action !== "skipped") {
48178
+ actions.push({
48179
+ action: "ensure_brain_db",
48180
+ status: "applied",
48181
+ details: brainResult.details ?? "brain.db initialized"
48182
+ });
48183
+ }
48184
+ } catch {
48185
+ }
48186
+ try {
48187
+ const { writeMemoryBridge: writeMemoryBridge2 } = await Promise.resolve().then(() => (init_memory_bridge(), memory_bridge_exports));
48188
+ const bridgeResult = await writeMemoryBridge2(projectRootForMaint);
48189
+ if (bridgeResult.written) {
48190
+ actions.push({
48191
+ action: "memory_bridge",
48192
+ status: "applied",
48193
+ details: "memory-bridge.md regenerated"
48194
+ });
48195
+ }
48196
+ } catch {
48197
+ }
47936
48198
  try {
47937
48199
  const rootGitignoreResult = await removeCleoFromRootGitignore(projectRootForMaint);
47938
48200
  if (rootGitignoreResult.removed) {