@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 +554 -292
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +558 -320
- package/dist/mcp/index.js.map +4 -4
- package/package.json +1 -1
- package/server.json +1 -1
- package/templates/CLEO-INJECTION.md +11 -0
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
|
-
*
|
|
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
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
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
|
|
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
|
|
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/
|
|
20997
|
-
|
|
20998
|
-
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
|
|
21002
|
-
|
|
21003
|
-
|
|
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 (
|
|
21024
|
-
|
|
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
|
-
|
|
21027
|
-
|
|
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
|
-
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
|
|
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(() => (
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
27294
|
-
const isNodeProject =
|
|
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:
|
|
27299
|
-
message:
|
|
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) =>
|
|
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 ?
|
|
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 =
|
|
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();
|