zelari-code 1.1.0 → 1.2.0
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/app.js +2 -2
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/checkpoint/checkpointManager.js +198 -0
- package/dist/cli/checkpoint/checkpointManager.js.map +1 -0
- package/dist/cli/components/StatusBar.js +9 -1
- package/dist/cli/components/StatusBar.js.map +1 -1
- package/dist/cli/diagnostics/engine.js +256 -0
- package/dist/cli/diagnostics/engine.js.map +1 -0
- package/dist/cli/hooks/chatStats.js +5 -1
- package/dist/cli/hooks/chatStats.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +17 -0
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +1105 -337
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/modelPricing.js +29 -5
- package/dist/cli/modelPricing.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +37 -1
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/slashCommands.js +24 -1
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/checkpoint.js +42 -0
- package/dist/cli/slashHandlers/checkpoint.js.map +1 -0
- package/dist/cli/slashHandlers/updater.js +18 -0
- package/dist/cli/slashHandlers/updater.js.map +1 -1
- package/dist/cli/toolRegistry.js +126 -19
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/taskTool.js +145 -0
- package/dist/cli/tools/taskTool.js.map +1 -0
- package/dist/cli/updater.js +83 -5
- package/dist/cli/updater.js.map +1 -1
- package/dist/cli/zelariMission.js +10 -0
- package/dist/cli/zelariMission.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -308,7 +308,7 @@ async function refreshGrokToken(options) {
|
|
|
308
308
|
return parseTokenResponseBody(obj, accessToken);
|
|
309
309
|
}
|
|
310
310
|
async function openBrowser(url2) {
|
|
311
|
-
const { spawn:
|
|
311
|
+
const { spawn: spawn7 } = await import("node:child_process");
|
|
312
312
|
const cmd = (() => {
|
|
313
313
|
switch (process.platform) {
|
|
314
314
|
case "darwin":
|
|
@@ -321,7 +321,7 @@ async function openBrowser(url2) {
|
|
|
321
321
|
})();
|
|
322
322
|
return new Promise((resolve, reject) => {
|
|
323
323
|
try {
|
|
324
|
-
const child =
|
|
324
|
+
const child = spawn7(cmd.bin, cmd.args, {
|
|
325
325
|
stdio: "ignore",
|
|
326
326
|
detached: true
|
|
327
327
|
});
|
|
@@ -1535,10 +1535,10 @@ function mergeDefs(...defs) {
|
|
|
1535
1535
|
function cloneDef(schema) {
|
|
1536
1536
|
return mergeDefs(schema._zod.def);
|
|
1537
1537
|
}
|
|
1538
|
-
function getElementAtPath(obj,
|
|
1539
|
-
if (!
|
|
1538
|
+
function getElementAtPath(obj, path27) {
|
|
1539
|
+
if (!path27)
|
|
1540
1540
|
return obj;
|
|
1541
|
-
return
|
|
1541
|
+
return path27.reduce((acc, key) => acc?.[key], obj);
|
|
1542
1542
|
}
|
|
1543
1543
|
function promiseAllObject(promisesObj) {
|
|
1544
1544
|
const keys = Object.keys(promisesObj);
|
|
@@ -1866,11 +1866,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1866
1866
|
}
|
|
1867
1867
|
return false;
|
|
1868
1868
|
}
|
|
1869
|
-
function prefixIssues(
|
|
1869
|
+
function prefixIssues(path27, issues) {
|
|
1870
1870
|
return issues.map((iss) => {
|
|
1871
1871
|
var _a3;
|
|
1872
1872
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1873
|
-
iss.path.unshift(
|
|
1873
|
+
iss.path.unshift(path27);
|
|
1874
1874
|
return iss;
|
|
1875
1875
|
});
|
|
1876
1876
|
}
|
|
@@ -2088,16 +2088,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2088
2088
|
}
|
|
2089
2089
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2090
2090
|
const fieldErrors = { _errors: [] };
|
|
2091
|
-
const processError = (error52,
|
|
2091
|
+
const processError = (error52, path27 = []) => {
|
|
2092
2092
|
for (const issue2 of error52.issues) {
|
|
2093
2093
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2094
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2094
|
+
issue2.errors.map((issues) => processError({ issues }, [...path27, ...issue2.path]));
|
|
2095
2095
|
} else if (issue2.code === "invalid_key") {
|
|
2096
|
-
processError({ issues: issue2.issues }, [...
|
|
2096
|
+
processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
|
|
2097
2097
|
} else if (issue2.code === "invalid_element") {
|
|
2098
|
-
processError({ issues: issue2.issues }, [...
|
|
2098
|
+
processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
|
|
2099
2099
|
} else {
|
|
2100
|
-
const fullpath = [...
|
|
2100
|
+
const fullpath = [...path27, ...issue2.path];
|
|
2101
2101
|
if (fullpath.length === 0) {
|
|
2102
2102
|
fieldErrors._errors.push(mapper(issue2));
|
|
2103
2103
|
} else {
|
|
@@ -2124,17 +2124,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2124
2124
|
}
|
|
2125
2125
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2126
2126
|
const result = { errors: [] };
|
|
2127
|
-
const processError = (error52,
|
|
2127
|
+
const processError = (error52, path27 = []) => {
|
|
2128
2128
|
var _a3, _b;
|
|
2129
2129
|
for (const issue2 of error52.issues) {
|
|
2130
2130
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2131
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2131
|
+
issue2.errors.map((issues) => processError({ issues }, [...path27, ...issue2.path]));
|
|
2132
2132
|
} else if (issue2.code === "invalid_key") {
|
|
2133
|
-
processError({ issues: issue2.issues }, [...
|
|
2133
|
+
processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
|
|
2134
2134
|
} else if (issue2.code === "invalid_element") {
|
|
2135
|
-
processError({ issues: issue2.issues }, [...
|
|
2135
|
+
processError({ issues: issue2.issues }, [...path27, ...issue2.path]);
|
|
2136
2136
|
} else {
|
|
2137
|
-
const fullpath = [...
|
|
2137
|
+
const fullpath = [...path27, ...issue2.path];
|
|
2138
2138
|
if (fullpath.length === 0) {
|
|
2139
2139
|
result.errors.push(mapper(issue2));
|
|
2140
2140
|
continue;
|
|
@@ -2166,8 +2166,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2166
2166
|
}
|
|
2167
2167
|
function toDotPath(_path) {
|
|
2168
2168
|
const segs = [];
|
|
2169
|
-
const
|
|
2170
|
-
for (const seg of
|
|
2169
|
+
const path27 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2170
|
+
for (const seg of path27) {
|
|
2171
2171
|
if (typeof seg === "number")
|
|
2172
2172
|
segs.push(`[${seg}]`);
|
|
2173
2173
|
else if (typeof seg === "symbol")
|
|
@@ -15670,13 +15670,13 @@ function resolveRef(ref, ctx) {
|
|
|
15670
15670
|
if (!ref.startsWith("#")) {
|
|
15671
15671
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15672
15672
|
}
|
|
15673
|
-
const
|
|
15674
|
-
if (
|
|
15673
|
+
const path27 = ref.slice(1).split("/").filter(Boolean);
|
|
15674
|
+
if (path27.length === 0) {
|
|
15675
15675
|
return ctx.rootSchema;
|
|
15676
15676
|
}
|
|
15677
15677
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15678
|
-
if (
|
|
15679
|
-
const key =
|
|
15678
|
+
if (path27[0] === defsKey) {
|
|
15679
|
+
const key = path27[1];
|
|
15680
15680
|
if (!key || !ctx.defs[key]) {
|
|
15681
15681
|
throw new Error(`Reference not found: ${ref}`);
|
|
15682
15682
|
}
|
|
@@ -17706,11 +17706,11 @@ var init_tools = __esm({
|
|
|
17706
17706
|
if (!ctx.addDocument)
|
|
17707
17707
|
return "Knowledge vault tool not available.";
|
|
17708
17708
|
const title = args["title"] || "New Document";
|
|
17709
|
-
const
|
|
17709
|
+
const path27 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17710
17710
|
const content = args["content"] || "";
|
|
17711
17711
|
const tags = args["tags"] || [];
|
|
17712
17712
|
ctx.addDocument({
|
|
17713
|
-
path:
|
|
17713
|
+
path: path27,
|
|
17714
17714
|
title,
|
|
17715
17715
|
content,
|
|
17716
17716
|
format: "markdown",
|
|
@@ -17719,7 +17719,7 @@ var init_tools = __esm({
|
|
|
17719
17719
|
workspaceId: ctx.workspaceId
|
|
17720
17720
|
});
|
|
17721
17721
|
ctx.addActivity("vault", "created document", title);
|
|
17722
|
-
return `Document "${title}" created at "${
|
|
17722
|
+
return `Document "${title}" created at "${path27}".`;
|
|
17723
17723
|
}
|
|
17724
17724
|
}
|
|
17725
17725
|
];
|
|
@@ -18725,11 +18725,174 @@ ${cached2}`;
|
|
|
18725
18725
|
}
|
|
18726
18726
|
});
|
|
18727
18727
|
|
|
18728
|
+
// packages/core/dist/core/providerStream.js
|
|
18729
|
+
function toLegacyMessages(messages) {
|
|
18730
|
+
return messages.map((m) => ({ role: m.role, content: m.content }));
|
|
18731
|
+
}
|
|
18732
|
+
function toLegacyTools(tools) {
|
|
18733
|
+
return tools.map((t) => ({
|
|
18734
|
+
type: "function",
|
|
18735
|
+
function: { name: t.name, description: t.description, parameters: t.parameters }
|
|
18736
|
+
}));
|
|
18737
|
+
}
|
|
18738
|
+
function wrapLegacyStream(legacyStream, params, _messages, _tools) {
|
|
18739
|
+
return async function* (providerParams) {
|
|
18740
|
+
const accumulatedChunks = [];
|
|
18741
|
+
const legacyParams = {
|
|
18742
|
+
apiKey: params.apiKey,
|
|
18743
|
+
model: providerParams.model,
|
|
18744
|
+
provider: providerParams.provider,
|
|
18745
|
+
messages: toLegacyMessages(providerParams.messages),
|
|
18746
|
+
tools: toLegacyTools(providerParams.tools),
|
|
18747
|
+
temperature: 0.7,
|
|
18748
|
+
stream: true,
|
|
18749
|
+
customBaseUrl: params.customBaseUrl,
|
|
18750
|
+
customAuthStyle: params.customAuthStyle,
|
|
18751
|
+
onChunk: (chunk) => {
|
|
18752
|
+
accumulatedChunks.push(chunk);
|
|
18753
|
+
},
|
|
18754
|
+
onRequestId: void 0,
|
|
18755
|
+
signal: providerParams.signal
|
|
18756
|
+
};
|
|
18757
|
+
const queue = [];
|
|
18758
|
+
let resolveNext = null;
|
|
18759
|
+
let done = false;
|
|
18760
|
+
let error51 = null;
|
|
18761
|
+
const promise2 = legacyStream(legacyParams).then((result) => {
|
|
18762
|
+
for (const tc of result.toolCalls) {
|
|
18763
|
+
queue.push({ kind: "tool_call", toolCallId: tc.id, toolName: tc.name, args: tc.args });
|
|
18764
|
+
}
|
|
18765
|
+
queue.push({ kind: "finish", reason: "stop" });
|
|
18766
|
+
}).catch((err) => {
|
|
18767
|
+
error51 = err;
|
|
18768
|
+
}).finally(() => {
|
|
18769
|
+
done = true;
|
|
18770
|
+
if (resolveNext) {
|
|
18771
|
+
resolveNext();
|
|
18772
|
+
resolveNext = null;
|
|
18773
|
+
}
|
|
18774
|
+
});
|
|
18775
|
+
let lastEmittedIndex = 0;
|
|
18776
|
+
while (!done || queue.length > 0 || lastEmittedIndex < accumulatedChunks.length) {
|
|
18777
|
+
while (lastEmittedIndex < accumulatedChunks.length) {
|
|
18778
|
+
yield { kind: "text", delta: accumulatedChunks[lastEmittedIndex] };
|
|
18779
|
+
lastEmittedIndex++;
|
|
18780
|
+
}
|
|
18781
|
+
while (queue.length > 0) {
|
|
18782
|
+
const d = queue.shift();
|
|
18783
|
+
yield d;
|
|
18784
|
+
}
|
|
18785
|
+
if (error51) {
|
|
18786
|
+
throw error51;
|
|
18787
|
+
}
|
|
18788
|
+
if (done)
|
|
18789
|
+
break;
|
|
18790
|
+
await new Promise((resolve) => {
|
|
18791
|
+
resolveNext = resolve;
|
|
18792
|
+
});
|
|
18793
|
+
}
|
|
18794
|
+
await promise2;
|
|
18795
|
+
};
|
|
18796
|
+
}
|
|
18797
|
+
var init_providerStream = __esm({
|
|
18798
|
+
"packages/core/dist/core/providerStream.js"() {
|
|
18799
|
+
"use strict";
|
|
18800
|
+
}
|
|
18801
|
+
});
|
|
18802
|
+
|
|
18803
|
+
// packages/core/dist/core/sessionJsonl.js
|
|
18804
|
+
import { promises as fs8 } from "node:fs";
|
|
18805
|
+
import path10 from "node:path";
|
|
18806
|
+
import os3 from "node:os";
|
|
18807
|
+
async function readSession(filePath) {
|
|
18808
|
+
try {
|
|
18809
|
+
const content = await fs8.readFile(filePath, "utf-8");
|
|
18810
|
+
const events = [];
|
|
18811
|
+
for (const line of content.split("\n")) {
|
|
18812
|
+
const trimmed = line.trim();
|
|
18813
|
+
if (!trimmed)
|
|
18814
|
+
continue;
|
|
18815
|
+
try {
|
|
18816
|
+
const parsed = JSON.parse(trimmed);
|
|
18817
|
+
if (parsed && typeof parsed === "object" && "event" in parsed) {
|
|
18818
|
+
events.push(parsed.event);
|
|
18819
|
+
}
|
|
18820
|
+
} catch {
|
|
18821
|
+
}
|
|
18822
|
+
}
|
|
18823
|
+
return events;
|
|
18824
|
+
} catch (err) {
|
|
18825
|
+
if (err.code === "ENOENT")
|
|
18826
|
+
return [];
|
|
18827
|
+
throw err;
|
|
18828
|
+
}
|
|
18829
|
+
}
|
|
18830
|
+
function defaultBaseDir() {
|
|
18831
|
+
return path10.join(os3.tmpdir(), "zelari-code", "sessions");
|
|
18832
|
+
}
|
|
18833
|
+
var SessionJsonlWriter;
|
|
18834
|
+
var init_sessionJsonl = __esm({
|
|
18835
|
+
"packages/core/dist/core/sessionJsonl.js"() {
|
|
18836
|
+
"use strict";
|
|
18837
|
+
SessionJsonlWriter = class {
|
|
18838
|
+
filePath;
|
|
18839
|
+
onError;
|
|
18840
|
+
constructor(sessionId, options = {}) {
|
|
18841
|
+
const baseDir = options.baseDir ?? defaultBaseDir();
|
|
18842
|
+
this.filePath = path10.join(baseDir, `${sessionId}.jsonl`);
|
|
18843
|
+
this.onError = options.onError ?? console.error;
|
|
18844
|
+
}
|
|
18845
|
+
/** Absolute path to the session JSONL file. */
|
|
18846
|
+
get path() {
|
|
18847
|
+
return this.filePath;
|
|
18848
|
+
}
|
|
18849
|
+
/** Append a BrainEvent as one JSON line. Creates the file + parent dirs if missing. */
|
|
18850
|
+
async append(event) {
|
|
18851
|
+
try {
|
|
18852
|
+
await fs8.mkdir(path10.dirname(this.filePath), { recursive: true });
|
|
18853
|
+
const line = JSON.stringify({
|
|
18854
|
+
ts: event.ts,
|
|
18855
|
+
sessionId: event.sessionId,
|
|
18856
|
+
event
|
|
18857
|
+
}) + "\n";
|
|
18858
|
+
await fs8.appendFile(this.filePath, line, { encoding: "utf-8", mode: 420 });
|
|
18859
|
+
} catch (err) {
|
|
18860
|
+
this.onError(`[sessionJsonl] failed to append event to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
18861
|
+
}
|
|
18862
|
+
}
|
|
18863
|
+
/** Close the writer (no-op currently, but reserved for future buffered mode). */
|
|
18864
|
+
async close() {
|
|
18865
|
+
}
|
|
18866
|
+
};
|
|
18867
|
+
}
|
|
18868
|
+
});
|
|
18869
|
+
|
|
18870
|
+
// packages/core/dist/harness/index.js
|
|
18871
|
+
var harness_exports = {};
|
|
18872
|
+
__export(harness_exports, {
|
|
18873
|
+
AgentHarness: () => AgentHarness,
|
|
18874
|
+
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
18875
|
+
hashToolCall: () => hashToolCall,
|
|
18876
|
+
normalizeTextToolArgs: () => normalizeTextToolArgs,
|
|
18877
|
+
parseTextToolCalls: () => parseTextToolCalls,
|
|
18878
|
+
readSession: () => readSession,
|
|
18879
|
+
wrapLegacyStream: () => wrapLegacyStream
|
|
18880
|
+
});
|
|
18881
|
+
var init_harness = __esm({
|
|
18882
|
+
"packages/core/dist/harness/index.js"() {
|
|
18883
|
+
"use strict";
|
|
18884
|
+
init_AgentHarness();
|
|
18885
|
+
init_providerStream();
|
|
18886
|
+
init_sessionJsonl();
|
|
18887
|
+
}
|
|
18888
|
+
});
|
|
18889
|
+
|
|
18728
18890
|
// src/cli/provider/openai-compatible.ts
|
|
18729
18891
|
var openai_compatible_exports = {};
|
|
18730
18892
|
__export(openai_compatible_exports, {
|
|
18731
18893
|
PROVIDER_ENDPOINTS: () => PROVIDER_ENDPOINTS,
|
|
18732
18894
|
openaiCompatibleProvider: () => openaiCompatibleProvider,
|
|
18895
|
+
parseCachedPromptTokens: () => parseCachedPromptTokens,
|
|
18733
18896
|
providerConfigFor: () => providerConfigFor,
|
|
18734
18897
|
providerFromEnv: () => providerFromEnv,
|
|
18735
18898
|
resolveActiveProvider: () => resolveActiveProvider,
|
|
@@ -18738,6 +18901,17 @@ __export(openai_compatible_exports, {
|
|
|
18738
18901
|
function resolveActiveProvider() {
|
|
18739
18902
|
return getProviderConfig().activeProviderId;
|
|
18740
18903
|
}
|
|
18904
|
+
function parseCachedPromptTokens(usage) {
|
|
18905
|
+
if (!usage || typeof usage !== "object") return 0;
|
|
18906
|
+
const candidates = [
|
|
18907
|
+
usage.prompt_tokens_details?.cached_tokens,
|
|
18908
|
+
usage.prompt_cache_hit_tokens
|
|
18909
|
+
];
|
|
18910
|
+
for (const c of candidates) {
|
|
18911
|
+
if (typeof c === "number" && Number.isFinite(c) && c >= 0) return c;
|
|
18912
|
+
}
|
|
18913
|
+
return 0;
|
|
18914
|
+
}
|
|
18741
18915
|
function resolveBaseUrl(providerId) {
|
|
18742
18916
|
const custom2 = getCustomEndpoint(providerId);
|
|
18743
18917
|
if (custom2) return custom2;
|
|
@@ -18849,9 +19023,15 @@ function openaiCompatibleProvider(config2) {
|
|
|
18849
19023
|
const promptTokens = typeof parsed.usage.prompt_tokens === "number" ? parsed.usage.prompt_tokens : 0;
|
|
18850
19024
|
const completionTokens = typeof parsed.usage.completion_tokens === "number" ? parsed.usage.completion_tokens : 0;
|
|
18851
19025
|
const totalTokens = typeof parsed.usage.total_tokens === "number" ? parsed.usage.total_tokens : promptTokens + completionTokens;
|
|
19026
|
+
const cachedPromptTokens = parseCachedPromptTokens(parsed.usage);
|
|
18852
19027
|
yield {
|
|
18853
19028
|
kind: "usage",
|
|
18854
|
-
usage: {
|
|
19029
|
+
usage: {
|
|
19030
|
+
promptTokens,
|
|
19031
|
+
completionTokens,
|
|
19032
|
+
totalTokens,
|
|
19033
|
+
...cachedPromptTokens > 0 ? { cachedPromptTokens } : {}
|
|
19034
|
+
}
|
|
18855
19035
|
};
|
|
18856
19036
|
}
|
|
18857
19037
|
if (typeof delta?.content === "string" && delta.content.length > 0) {
|
|
@@ -19925,7 +20105,7 @@ var init_parseCssMotion = __esm({
|
|
|
19925
20105
|
});
|
|
19926
20106
|
|
|
19927
20107
|
// packages/core/dist/council/verification/citeVerify.js
|
|
19928
|
-
import { existsSync as
|
|
20108
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
|
|
19929
20109
|
import { join } from "node:path";
|
|
19930
20110
|
function extractCitations(text) {
|
|
19931
20111
|
const out = [];
|
|
@@ -19949,7 +20129,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
19949
20129
|
const results = [];
|
|
19950
20130
|
for (const cite of extractCitations(synthesisText)) {
|
|
19951
20131
|
const abs = join(projectRoot, cite.file);
|
|
19952
|
-
if (!
|
|
20132
|
+
if (!existsSync8(abs)) {
|
|
19953
20133
|
results.push({
|
|
19954
20134
|
id: "synthesis.cite-invalid",
|
|
19955
20135
|
severity: "error",
|
|
@@ -20225,14 +20405,14 @@ var init_synthesisAudit = __esm({
|
|
|
20225
20405
|
});
|
|
20226
20406
|
|
|
20227
20407
|
// packages/core/dist/council/verification/runChecks.js
|
|
20228
|
-
import { existsSync as
|
|
20408
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
20229
20409
|
import { join as join2 } from "node:path";
|
|
20230
20410
|
function loadNfrSpec(zelariRoot) {
|
|
20231
|
-
const
|
|
20232
|
-
if (!
|
|
20411
|
+
const path27 = join2(zelariRoot, "nfr-spec.json");
|
|
20412
|
+
if (!existsSync9(path27))
|
|
20233
20413
|
return null;
|
|
20234
20414
|
try {
|
|
20235
|
-
const raw = JSON.parse(readFileSync6(
|
|
20415
|
+
const raw = JSON.parse(readFileSync6(path27, "utf8"));
|
|
20236
20416
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
20237
20417
|
return null;
|
|
20238
20418
|
return raw;
|
|
@@ -20243,11 +20423,11 @@ function loadNfrSpec(zelariRoot) {
|
|
|
20243
20423
|
function resolveTargets(projectRoot, spec) {
|
|
20244
20424
|
const found = [];
|
|
20245
20425
|
for (const rel2 of spec.targets) {
|
|
20246
|
-
if (
|
|
20426
|
+
if (existsSync9(join2(projectRoot, rel2))) {
|
|
20247
20427
|
found.push(rel2);
|
|
20248
20428
|
}
|
|
20249
20429
|
}
|
|
20250
|
-
if (found.length === 0 &&
|
|
20430
|
+
if (found.length === 0 && existsSync9(join2(projectRoot, "index.html"))) {
|
|
20251
20431
|
return ["index.html"];
|
|
20252
20432
|
}
|
|
20253
20433
|
return found;
|
|
@@ -20303,7 +20483,7 @@ function checkDeadCssHooks(html, relFile) {
|
|
|
20303
20483
|
}
|
|
20304
20484
|
function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
20305
20485
|
const planPath = join2(zelariRoot, "plan.json");
|
|
20306
|
-
if (!
|
|
20486
|
+
if (!existsSync9(planPath) || keywords.length === 0)
|
|
20307
20487
|
return [];
|
|
20308
20488
|
let plan;
|
|
20309
20489
|
try {
|
|
@@ -20343,11 +20523,11 @@ function checkPlanReality(projectRoot, zelariRoot, targets, keywords) {
|
|
|
20343
20523
|
}
|
|
20344
20524
|
function checkReadmeStale(projectRoot, targets) {
|
|
20345
20525
|
const readmePath = join2(projectRoot, "README.md");
|
|
20346
|
-
if (!
|
|
20526
|
+
if (!existsSync9(readmePath) || targets.length === 0)
|
|
20347
20527
|
return [];
|
|
20348
20528
|
const readme = readFileSync6(readmePath, "utf8");
|
|
20349
20529
|
const htmlPath = join2(projectRoot, targets[0]);
|
|
20350
|
-
if (!
|
|
20530
|
+
if (!existsSync9(htmlPath))
|
|
20351
20531
|
return [];
|
|
20352
20532
|
const html = readFileSync6(htmlPath, "utf8");
|
|
20353
20533
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
@@ -20473,7 +20653,7 @@ var init_runChecks = __esm({
|
|
|
20473
20653
|
});
|
|
20474
20654
|
|
|
20475
20655
|
// packages/core/dist/council/verification/microGate.js
|
|
20476
|
-
import { existsSync as
|
|
20656
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
|
|
20477
20657
|
import { join as join3 } from "node:path";
|
|
20478
20658
|
function checkDeadHooksInHtml(html) {
|
|
20479
20659
|
const warnings = [];
|
|
@@ -20503,7 +20683,7 @@ function checkDeadHooksInHtml(html) {
|
|
|
20503
20683
|
}
|
|
20504
20684
|
function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
20505
20685
|
const abs = join3(projectRoot, relPath);
|
|
20506
|
-
if (!
|
|
20686
|
+
if (!existsSync10(abs) || !/\.html?$/i.test(relPath))
|
|
20507
20687
|
return [];
|
|
20508
20688
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
20509
20689
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -20947,7 +21127,7 @@ var init_inlineJsAutofix = __esm({
|
|
|
20947
21127
|
});
|
|
20948
21128
|
|
|
20949
21129
|
// packages/core/dist/agents/councilApi.js
|
|
20950
|
-
import { existsSync as
|
|
21130
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
20951
21131
|
import { join as join5 } from "node:path";
|
|
20952
21132
|
function parseClarificationRequest(text) {
|
|
20953
21133
|
const start = text.indexOf(QUESTION_MARKER);
|
|
@@ -21468,7 +21648,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21468
21648
|
const zelariRoot = `${chairmanProjectRoot}/.zelari`;
|
|
21469
21649
|
const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
|
|
21470
21650
|
for (const rel2 of spec.targets) {
|
|
21471
|
-
if (!
|
|
21651
|
+
if (!existsSync11(join5(chairmanProjectRoot, rel2)))
|
|
21472
21652
|
continue;
|
|
21473
21653
|
changedTargetFiles.add(rel2);
|
|
21474
21654
|
for (const w of runChairmanMicroGate({ projectRoot: chairmanProjectRoot, relPath: rel2, zelariRoot })) {
|
|
@@ -21488,7 +21668,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
|
|
|
21488
21668
|
const zelariRootReplay = `${chairmanProjectRoot}/.zelari`;
|
|
21489
21669
|
const specReplay = loadNfrSpec(zelariRootReplay) ?? DEFAULT_NFR_SPEC;
|
|
21490
21670
|
for (const rel2 of specReplay.targets) {
|
|
21491
|
-
if (!
|
|
21671
|
+
if (!existsSync11(join5(chairmanProjectRoot, rel2)))
|
|
21492
21672
|
continue;
|
|
21493
21673
|
changedTargetFiles.add(rel2);
|
|
21494
21674
|
for (const w of runChairmanMicroGate({
|
|
@@ -22421,9 +22601,9 @@ var init_types2 = __esm({
|
|
|
22421
22601
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
22422
22602
|
import { join as join8 } from "node:path";
|
|
22423
22603
|
function readLessonsDeduped(zelariRoot) {
|
|
22424
|
-
const
|
|
22604
|
+
const path27 = join8(zelariRoot, LESSONS_FILE);
|
|
22425
22605
|
try {
|
|
22426
|
-
const raw = readFileSync11(
|
|
22606
|
+
const raw = readFileSync11(path27, "utf8");
|
|
22427
22607
|
const byId = /* @__PURE__ */ new Map();
|
|
22428
22608
|
for (const line of raw.split(/\r?\n/)) {
|
|
22429
22609
|
if (!line.trim())
|
|
@@ -22524,8 +22704,8 @@ function keywordsFrom(check2, signature) {
|
|
|
22524
22704
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
22525
22705
|
}
|
|
22526
22706
|
function writeLesson(zelariRoot, lesson) {
|
|
22527
|
-
const
|
|
22528
|
-
appendFileSync2(
|
|
22707
|
+
const path27 = join9(zelariRoot, LESSONS_FILE);
|
|
22708
|
+
appendFileSync2(path27, `${JSON.stringify(lesson)}
|
|
22529
22709
|
`, "utf8");
|
|
22530
22710
|
}
|
|
22531
22711
|
function findSimilar(lessons, signature) {
|
|
@@ -23062,7 +23242,7 @@ var init_council = __esm({
|
|
|
23062
23242
|
import {
|
|
23063
23243
|
mkdirSync as mkdirSync6,
|
|
23064
23244
|
writeFileSync as writeFileSync10,
|
|
23065
|
-
existsSync as
|
|
23245
|
+
existsSync as existsSync12,
|
|
23066
23246
|
accessSync,
|
|
23067
23247
|
constants,
|
|
23068
23248
|
realpathSync
|
|
@@ -23089,7 +23269,7 @@ function hashProject(projectPath) {
|
|
|
23089
23269
|
}
|
|
23090
23270
|
function isWritableDir(dir) {
|
|
23091
23271
|
try {
|
|
23092
|
-
if (!
|
|
23272
|
+
if (!existsSync12(dir)) return false;
|
|
23093
23273
|
accessSync(dir, constants.W_OK);
|
|
23094
23274
|
return true;
|
|
23095
23275
|
} catch {
|
|
@@ -23098,9 +23278,9 @@ function isWritableDir(dir) {
|
|
|
23098
23278
|
}
|
|
23099
23279
|
function ensureWorkspaceDir(workspaceDir) {
|
|
23100
23280
|
mkdirSync6(workspaceDir, { recursive: true });
|
|
23101
|
-
if (workspaceDir.endsWith("/.zelari") &&
|
|
23281
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync12(join11(workspaceDir, "..", ".git"))) {
|
|
23102
23282
|
const gitignorePath = join11(workspaceDir, ".gitignore");
|
|
23103
|
-
if (!
|
|
23283
|
+
if (!existsSync12(gitignorePath)) {
|
|
23104
23284
|
writeFileSync10(gitignorePath, "*\n!.gitignore\n");
|
|
23105
23285
|
}
|
|
23106
23286
|
}
|
|
@@ -23135,8 +23315,8 @@ __export(workspaceSummary_exports, {
|
|
|
23135
23315
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
23136
23316
|
buildZelariReadHint: () => buildZelariReadHint
|
|
23137
23317
|
});
|
|
23138
|
-
import { existsSync as
|
|
23139
|
-
import { join as join12, relative } from "node:path";
|
|
23318
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync, statSync as statSync3 } from "node:fs";
|
|
23319
|
+
import { join as join12, relative as relative2 } from "node:path";
|
|
23140
23320
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
23141
23321
|
const { maxEntries = 30 } = options;
|
|
23142
23322
|
const name = safeProjectName(projectRoot);
|
|
@@ -23164,7 +23344,7 @@ function formatTaskLine(t) {
|
|
|
23164
23344
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
23165
23345
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
23166
23346
|
const planPath = join12(zelariRoot, "plan.json");
|
|
23167
|
-
if (!
|
|
23347
|
+
if (!existsSync13(planPath)) return null;
|
|
23168
23348
|
let plan;
|
|
23169
23349
|
try {
|
|
23170
23350
|
plan = JSON.parse(readFileSync12(planPath, "utf8"));
|
|
@@ -23297,7 +23477,7 @@ function pickNextTask(open) {
|
|
|
23297
23477
|
}
|
|
23298
23478
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
23299
23479
|
const planPath = join12(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
23300
|
-
if (!
|
|
23480
|
+
if (!existsSync13(planPath)) return "";
|
|
23301
23481
|
return [
|
|
23302
23482
|
"# Council workspace detected (.zelari/)",
|
|
23303
23483
|
"This project has a council workspace: .zelari/plan.json holds the plan (phases, tasks, milestones) and .zelari/plan-tasks/ holds one detail file per task.",
|
|
@@ -23313,7 +23493,7 @@ function safeProjectName(root) {
|
|
|
23313
23493
|
}
|
|
23314
23494
|
function readPackageJson(projectRoot) {
|
|
23315
23495
|
const p3 = join12(projectRoot, "package.json");
|
|
23316
|
-
if (!
|
|
23496
|
+
if (!existsSync13(p3)) return null;
|
|
23317
23497
|
try {
|
|
23318
23498
|
return JSON.parse(readFileSync12(p3, "utf8"));
|
|
23319
23499
|
} catch {
|
|
@@ -23349,7 +23529,7 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
23349
23529
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
23350
23530
|
break;
|
|
23351
23531
|
}
|
|
23352
|
-
const rel2 =
|
|
23532
|
+
const rel2 = relative2(projectRoot, join12(projectRoot, entry.name));
|
|
23353
23533
|
if (entry.isDirectory()) {
|
|
23354
23534
|
let inner = "";
|
|
23355
23535
|
try {
|
|
@@ -23406,7 +23586,7 @@ __export(storage_exports, {
|
|
|
23406
23586
|
import {
|
|
23407
23587
|
readFileSync as readFileSync13,
|
|
23408
23588
|
writeFileSync as writeFileSync11,
|
|
23409
|
-
existsSync as
|
|
23589
|
+
existsSync as existsSync14,
|
|
23410
23590
|
mkdirSync as mkdirSync7,
|
|
23411
23591
|
readdirSync as readdirSync2,
|
|
23412
23592
|
renameSync as renameSync2
|
|
@@ -23665,32 +23845,32 @@ var init_storage = __esm({
|
|
|
23665
23845
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
23666
23846
|
Storage = class {
|
|
23667
23847
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
23668
|
-
read(
|
|
23669
|
-
if (!
|
|
23670
|
-
throw new Error(`File not found: ${
|
|
23848
|
+
read(path27) {
|
|
23849
|
+
if (!existsSync14(path27)) {
|
|
23850
|
+
throw new Error(`File not found: ${path27}`);
|
|
23671
23851
|
}
|
|
23672
|
-
const md = readFileSync13(
|
|
23852
|
+
const md = readFileSync13(path27, "utf8");
|
|
23673
23853
|
return parseFrontmatter(md);
|
|
23674
23854
|
}
|
|
23675
23855
|
/** Read a Markdown file; returns null if not found. */
|
|
23676
|
-
readIfExists(
|
|
23677
|
-
if (!
|
|
23678
|
-
return this.read(
|
|
23856
|
+
readIfExists(path27) {
|
|
23857
|
+
if (!existsSync14(path27)) return null;
|
|
23858
|
+
return this.read(path27);
|
|
23679
23859
|
}
|
|
23680
23860
|
/**
|
|
23681
23861
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
23682
23862
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
23683
23863
|
*/
|
|
23684
|
-
write(
|
|
23685
|
-
mkdirSync7(dirname(
|
|
23686
|
-
const tmp =
|
|
23864
|
+
write(path27, meta3, body) {
|
|
23865
|
+
mkdirSync7(dirname(path27), { recursive: true });
|
|
23866
|
+
const tmp = path27 + ".tmp-" + process.pid;
|
|
23687
23867
|
const md = serializeFrontmatter(meta3, body);
|
|
23688
23868
|
writeFileSync11(tmp, md, "utf8");
|
|
23689
|
-
renameSync2(tmp,
|
|
23869
|
+
renameSync2(tmp, path27);
|
|
23690
23870
|
}
|
|
23691
23871
|
/** List all .md files in a directory (non-recursive). */
|
|
23692
23872
|
listMarkdown(dir) {
|
|
23693
|
-
if (!
|
|
23873
|
+
if (!existsSync14(dir)) return [];
|
|
23694
23874
|
return readdirSync2(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join13(dir, f));
|
|
23695
23875
|
}
|
|
23696
23876
|
};
|
|
@@ -23730,14 +23910,14 @@ __export(stubs_exports, {
|
|
|
23730
23910
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
23731
23911
|
});
|
|
23732
23912
|
import {
|
|
23733
|
-
existsSync as
|
|
23913
|
+
existsSync as existsSync15,
|
|
23734
23914
|
readdirSync as readdirSync3,
|
|
23735
23915
|
writeFileSync as writeFileSync12,
|
|
23736
23916
|
readFileSync as readFileSync14,
|
|
23737
23917
|
mkdirSync as mkdirSync8,
|
|
23738
23918
|
renameSync as renameSync3
|
|
23739
23919
|
} from "node:fs";
|
|
23740
|
-
import { join as join14, basename as basename2, dirname as dirname2, relative as
|
|
23920
|
+
import { join as join14, basename as basename2, dirname as dirname2, relative as relative3 } from "node:path";
|
|
23741
23921
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
23742
23922
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
23743
23923
|
return {
|
|
@@ -23751,7 +23931,7 @@ function planJsonPath(ctx) {
|
|
|
23751
23931
|
}
|
|
23752
23932
|
function readPlan(ctx) {
|
|
23753
23933
|
const jsonPath = planJsonPath(ctx);
|
|
23754
|
-
if (
|
|
23934
|
+
if (existsSync15(jsonPath)) {
|
|
23755
23935
|
try {
|
|
23756
23936
|
const parsed = JSON.parse(
|
|
23757
23937
|
readFileSync14(jsonPath, "utf8")
|
|
@@ -23764,8 +23944,8 @@ function readPlan(ctx) {
|
|
|
23764
23944
|
} catch {
|
|
23765
23945
|
}
|
|
23766
23946
|
}
|
|
23767
|
-
const
|
|
23768
|
-
const doc = ctx.storage.readIfExists(
|
|
23947
|
+
const path27 = workspaceFile(ctx.rootDir, "plan");
|
|
23948
|
+
const doc = ctx.storage.readIfExists(path27);
|
|
23769
23949
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
23770
23950
|
const meta3 = doc.meta;
|
|
23771
23951
|
return {
|
|
@@ -23846,7 +24026,7 @@ function renderPlanBody(summary) {
|
|
|
23846
24026
|
}
|
|
23847
24027
|
function nextAdrId(ctx) {
|
|
23848
24028
|
const decisionsDir = join14(ctx.rootDir, "decisions");
|
|
23849
|
-
if (!
|
|
24029
|
+
if (!existsSync15(decisionsDir)) return "001";
|
|
23850
24030
|
const existing = readdirSync3(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
23851
24031
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
23852
24032
|
return String(max + 1).padStart(3, "0");
|
|
@@ -23930,7 +24110,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
23930
24110
|
dueDate: input.dueDate,
|
|
23931
24111
|
targetVersion: version2
|
|
23932
24112
|
});
|
|
23933
|
-
const
|
|
24113
|
+
const path27 = join14(ctx.rootDir, "milestones", `${id}.md`);
|
|
23934
24114
|
const meta3 = {
|
|
23935
24115
|
kind: "milestone",
|
|
23936
24116
|
id,
|
|
@@ -23947,7 +24127,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
23947
24127
|
`Target version: ${version2}`,
|
|
23948
24128
|
""
|
|
23949
24129
|
].join("\n");
|
|
23950
|
-
ctx.storage.write(
|
|
24130
|
+
ctx.storage.write(path27, meta3, body);
|
|
23951
24131
|
return { id, created: true };
|
|
23952
24132
|
}
|
|
23953
24133
|
function readPlanSummary(ctx) {
|
|
@@ -24151,7 +24331,7 @@ function addIdeaStub(ctx) {
|
|
|
24151
24331
|
const tags = args["tags"] ?? [];
|
|
24152
24332
|
const category = args["category"] ?? "General";
|
|
24153
24333
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
24154
|
-
const
|
|
24334
|
+
const path27 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
24155
24335
|
const meta3 = {
|
|
24156
24336
|
kind: "adr",
|
|
24157
24337
|
status: "proposed",
|
|
@@ -24177,7 +24357,7 @@ function addIdeaStub(ctx) {
|
|
|
24177
24357
|
...consequences.map((c) => `- ${c}`),
|
|
24178
24358
|
""
|
|
24179
24359
|
].join("\n");
|
|
24180
|
-
ctx.storage.write(
|
|
24360
|
+
ctx.storage.write(path27, meta3, body);
|
|
24181
24361
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
24182
24362
|
});
|
|
24183
24363
|
}
|
|
@@ -24259,14 +24439,14 @@ function createDocumentStub(ctx) {
|
|
|
24259
24439
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
24260
24440
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
24261
24441
|
}
|
|
24262
|
-
const
|
|
24442
|
+
const path27 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
24263
24443
|
const meta3 = {
|
|
24264
24444
|
kind: "doc",
|
|
24265
24445
|
id: slug,
|
|
24266
24446
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
24267
24447
|
tags
|
|
24268
24448
|
};
|
|
24269
|
-
ctx.storage.write(
|
|
24449
|
+
ctx.storage.write(path27, meta3, content);
|
|
24270
24450
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
24271
24451
|
});
|
|
24272
24452
|
}
|
|
@@ -24302,7 +24482,7 @@ function searchDocumentsStub(ctx) {
|
|
|
24302
24482
|
];
|
|
24303
24483
|
const results = [];
|
|
24304
24484
|
for (const file2 of files) {
|
|
24305
|
-
if (!
|
|
24485
|
+
if (!existsSync15(file2)) continue;
|
|
24306
24486
|
const raw = readFileSync14(file2, "utf8");
|
|
24307
24487
|
const content = raw.toLowerCase();
|
|
24308
24488
|
let idx = -1;
|
|
@@ -24330,7 +24510,7 @@ function searchDocumentsStub(ctx) {
|
|
|
24330
24510
|
const end = Math.min(raw.length, idx + matchLen + 40);
|
|
24331
24511
|
const snippet = "\u2026" + raw.slice(start, end).replace(/\n/g, " ").trim() + "\u2026";
|
|
24332
24512
|
results.push({
|
|
24333
|
-
path:
|
|
24513
|
+
path: relative3(ctx.rootDir, file2) || basename2(file2),
|
|
24334
24514
|
snippet
|
|
24335
24515
|
});
|
|
24336
24516
|
if (results.length >= limit) break;
|
|
@@ -24493,15 +24673,38 @@ __export(updater_exports, {
|
|
|
24493
24673
|
compareSemver: () => compareSemver,
|
|
24494
24674
|
fetchLatestVersion: () => fetchLatestVersion,
|
|
24495
24675
|
getCurrentVersion: () => getCurrentVersion,
|
|
24496
|
-
performUpdate: () => performUpdate
|
|
24676
|
+
performUpdate: () => performUpdate,
|
|
24677
|
+
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
24497
24678
|
});
|
|
24498
24679
|
import { createRequire } from "node:module";
|
|
24499
|
-
import { spawn as
|
|
24500
|
-
import
|
|
24680
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
24681
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
24682
|
+
import path16 from "node:path";
|
|
24501
24683
|
import { fileURLToPath } from "node:url";
|
|
24684
|
+
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
24685
|
+
const dir = path16.dirname(execPath);
|
|
24686
|
+
const candidates = [
|
|
24687
|
+
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
24688
|
+
path16.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
24689
|
+
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
24690
|
+
path16.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
24691
|
+
];
|
|
24692
|
+
for (const candidate of candidates) {
|
|
24693
|
+
try {
|
|
24694
|
+
if (existsSync16(candidate)) return candidate;
|
|
24695
|
+
} catch {
|
|
24696
|
+
}
|
|
24697
|
+
}
|
|
24698
|
+
return null;
|
|
24699
|
+
}
|
|
24700
|
+
function looksLikeBrokenShim(exitCode, output) {
|
|
24701
|
+
if (exitCode === 127) return true;
|
|
24702
|
+
const h = output.toLowerCase();
|
|
24703
|
+
return h.includes("shim target not found") || h.includes("is not recognized");
|
|
24704
|
+
}
|
|
24502
24705
|
function getCurrentVersion() {
|
|
24503
24706
|
try {
|
|
24504
|
-
const pkgPath =
|
|
24707
|
+
const pkgPath = path16.resolve(__dirname2, "..", "..", "package.json");
|
|
24505
24708
|
const pkg = require2(pkgPath);
|
|
24506
24709
|
return pkg.version;
|
|
24507
24710
|
} catch {
|
|
@@ -24561,13 +24764,27 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
24561
24764
|
updateAvailable: cmp < 0
|
|
24562
24765
|
};
|
|
24563
24766
|
}
|
|
24564
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
24767
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn3, resolveNpmCli = resolveBundledNpmCli) {
|
|
24768
|
+
const args = ["install", "-g", `${packageName}@latest`];
|
|
24769
|
+
const primary = await runNpm(executor, args, "shim");
|
|
24770
|
+
if (primary.ok) return primary;
|
|
24771
|
+
const npmCli = resolveNpmCli();
|
|
24772
|
+
if (npmCli && looksLikeBrokenShim(primary.exitCode, primary.output)) {
|
|
24773
|
+
const fallback = await runNpm(executor, args, "bundled", npmCli);
|
|
24774
|
+
return {
|
|
24775
|
+
...fallback,
|
|
24776
|
+
output: `[update] npm shim failed (${primary.error ?? "exit " + primary.exitCode}); retried via bundled npm (${npmCli}).
|
|
24777
|
+
${fallback.output}`
|
|
24778
|
+
};
|
|
24779
|
+
}
|
|
24780
|
+
return primary;
|
|
24781
|
+
}
|
|
24782
|
+
function runNpm(executor, args, mode, npmCliPath) {
|
|
24565
24783
|
return new Promise((resolve) => {
|
|
24566
|
-
const args = ["install", "-g", `${packageName}@latest`];
|
|
24567
24784
|
let stdout = "";
|
|
24568
24785
|
let stderr = "";
|
|
24569
24786
|
const stdio = ["ignore", "pipe", "pipe"];
|
|
24570
|
-
const child = process.platform === "win32" ? executor(buildCmdLine("npm", args), { stdio, shell: true }) : executor("npm", args, { stdio });
|
|
24787
|
+
const child = mode === "bundled" && npmCliPath ? executor(process.execPath, [npmCliPath, ...args], { stdio }) : process.platform === "win32" ? executor(buildCmdLine("npm", args), { stdio, shell: true }) : executor("npm", args, { stdio });
|
|
24571
24788
|
child.stdout?.on("data", (chunk) => {
|
|
24572
24789
|
stdout += chunk.toString();
|
|
24573
24790
|
});
|
|
@@ -24599,13 +24816,13 @@ var init_updater = __esm({
|
|
|
24599
24816
|
"use strict";
|
|
24600
24817
|
init_cmdline();
|
|
24601
24818
|
require2 = createRequire(import.meta.url);
|
|
24602
|
-
__dirname2 =
|
|
24819
|
+
__dirname2 = path16.dirname(fileURLToPath(import.meta.url));
|
|
24603
24820
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
24604
24821
|
}
|
|
24605
24822
|
});
|
|
24606
24823
|
|
|
24607
24824
|
// src/cli/mcp/mcpClient.ts
|
|
24608
|
-
import { spawn as
|
|
24825
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
24609
24826
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
24610
24827
|
var init_mcpClient = __esm({
|
|
24611
24828
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -24633,10 +24850,10 @@ var init_mcpClient = __esm({
|
|
|
24633
24850
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
24634
24851
|
windowsHide: true
|
|
24635
24852
|
};
|
|
24636
|
-
const child = process.platform === "win32" ?
|
|
24853
|
+
const child = process.platform === "win32" ? spawn4(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
24637
24854
|
...spawnOpts,
|
|
24638
24855
|
shell: true
|
|
24639
|
-
}) :
|
|
24856
|
+
}) : spawn4(this.config.command, this.config.args ?? [], spawnOpts);
|
|
24640
24857
|
this.child = child;
|
|
24641
24858
|
child.stdout.setEncoding("utf8");
|
|
24642
24859
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -24780,7 +24997,7 @@ __export(mcpManager_exports, {
|
|
|
24780
24997
|
readMcpConfig: () => readMcpConfig,
|
|
24781
24998
|
registerMcpTools: () => registerMcpTools
|
|
24782
24999
|
});
|
|
24783
|
-
import { existsSync as
|
|
25000
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
|
|
24784
25001
|
import { join as join15 } from "node:path";
|
|
24785
25002
|
import { homedir as homedir4 } from "node:os";
|
|
24786
25003
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
@@ -24791,7 +25008,7 @@ function readMcpConfig(projectRoot = process.cwd()) {
|
|
|
24791
25008
|
// later = higher precedence
|
|
24792
25009
|
];
|
|
24793
25010
|
for (const p3 of paths) {
|
|
24794
|
-
if (!
|
|
25011
|
+
if (!existsSync17(p3)) continue;
|
|
24795
25012
|
try {
|
|
24796
25013
|
const parsed = JSON.parse(readFileSync15(p3, "utf8"));
|
|
24797
25014
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
@@ -24925,11 +25142,11 @@ var planDetect_exports = {};
|
|
|
24925
25142
|
__export(planDetect_exports, {
|
|
24926
25143
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
24927
25144
|
});
|
|
24928
|
-
import { existsSync as
|
|
25145
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
|
|
24929
25146
|
import { join as join16 } from "node:path";
|
|
24930
25147
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
24931
25148
|
const planPath = join16(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
24932
|
-
if (!
|
|
25149
|
+
if (!existsSync18(planPath)) return false;
|
|
24933
25150
|
try {
|
|
24934
25151
|
const parsed = JSON.parse(readFileSync16(planPath, "utf8"));
|
|
24935
25152
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
@@ -25028,15 +25245,15 @@ __export(agentsMd_exports, {
|
|
|
25028
25245
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
25029
25246
|
updateAgentsMd: () => updateAgentsMd
|
|
25030
25247
|
});
|
|
25031
|
-
import { existsSync as
|
|
25248
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
25032
25249
|
import { createHash as createHash2 } from "node:crypto";
|
|
25033
25250
|
import { join as join17 } from "node:path";
|
|
25034
25251
|
import { readFile } from "node:fs/promises";
|
|
25035
25252
|
async function readPackageJson2(projectRoot) {
|
|
25036
|
-
const
|
|
25037
|
-
if (!
|
|
25253
|
+
const path27 = join17(projectRoot, "package.json");
|
|
25254
|
+
if (!existsSync19(path27)) return null;
|
|
25038
25255
|
try {
|
|
25039
|
-
return JSON.parse(await readFile(
|
|
25256
|
+
return JSON.parse(await readFile(path27, "utf8"));
|
|
25040
25257
|
} catch {
|
|
25041
25258
|
return null;
|
|
25042
25259
|
}
|
|
@@ -25059,7 +25276,7 @@ async function genTechStack(ctx) {
|
|
|
25059
25276
|
}
|
|
25060
25277
|
async function genDecisions(ctx) {
|
|
25061
25278
|
const decisionsDir = join17(ctx.rootDir, "decisions");
|
|
25062
|
-
if (!
|
|
25279
|
+
if (!existsSync19(decisionsDir)) return "_No ADRs yet._";
|
|
25063
25280
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
25064
25281
|
const accepted = [];
|
|
25065
25282
|
const proposed = [];
|
|
@@ -25085,7 +25302,7 @@ async function genDecisions(ctx) {
|
|
|
25085
25302
|
async function genConventions(ctx) {
|
|
25086
25303
|
const lines = [];
|
|
25087
25304
|
const claudeMd = join17(ctx.projectRoot, "CLAUDE.MD");
|
|
25088
|
-
if (
|
|
25305
|
+
if (existsSync19(claudeMd)) {
|
|
25089
25306
|
const content = readFileSync17(claudeMd, "utf8");
|
|
25090
25307
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
25091
25308
|
if (match) {
|
|
@@ -25118,9 +25335,9 @@ async function genBuild(ctx) {
|
|
|
25118
25335
|
].join("\n");
|
|
25119
25336
|
}
|
|
25120
25337
|
async function genOpenQuestions(ctx) {
|
|
25121
|
-
const
|
|
25122
|
-
if (!
|
|
25123
|
-
const content = readFileSync17(
|
|
25338
|
+
const path27 = join17(ctx.rootDir, "risks.md");
|
|
25339
|
+
if (!existsSync19(path27)) return "_No open questions._";
|
|
25340
|
+
const content = readFileSync17(path27, "utf8");
|
|
25124
25341
|
const lines = content.split("\n");
|
|
25125
25342
|
const questions = [];
|
|
25126
25343
|
let currentTitle = "";
|
|
@@ -25195,7 +25412,7 @@ function titleCase(id) {
|
|
|
25195
25412
|
}
|
|
25196
25413
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
25197
25414
|
const agentsPath = join17(projectRoot, "AGENTS.MD");
|
|
25198
|
-
if (
|
|
25415
|
+
if (existsSync19(agentsPath)) {
|
|
25199
25416
|
const content = readFileSync17(agentsPath, "utf8");
|
|
25200
25417
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
25201
25418
|
if (!hasAnyMarker) {
|
|
@@ -25211,7 +25428,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25211
25428
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
25212
25429
|
}
|
|
25213
25430
|
let manualContent = "";
|
|
25214
|
-
if (
|
|
25431
|
+
if (existsSync19(agentsPath)) {
|
|
25215
25432
|
const { manualBlocks } = parseAgentsMd(readFileSync17(agentsPath, "utf8"));
|
|
25216
25433
|
manualContent = manualBlocks.after;
|
|
25217
25434
|
} else {
|
|
@@ -25228,7 +25445,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25228
25445
|
""
|
|
25229
25446
|
].join("\n");
|
|
25230
25447
|
}
|
|
25231
|
-
const oldContent =
|
|
25448
|
+
const oldContent = existsSync19(agentsPath) ? readFileSync17(agentsPath, "utf8") : "";
|
|
25232
25449
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
25233
25450
|
const changedSections = [];
|
|
25234
25451
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -25365,8 +25582,8 @@ var init_completeDesign = __esm({
|
|
|
25365
25582
|
});
|
|
25366
25583
|
|
|
25367
25584
|
// src/cli/workspace/projectSmoke.ts
|
|
25368
|
-
import { spawn as
|
|
25369
|
-
import { existsSync as
|
|
25585
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
25586
|
+
import { existsSync as existsSync20, readFileSync as readFileSync18 } from "node:fs";
|
|
25370
25587
|
import { join as join18 } from "node:path";
|
|
25371
25588
|
function pickSmokeScript(scripts) {
|
|
25372
25589
|
if (!scripts) return null;
|
|
@@ -25380,7 +25597,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25380
25597
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
25381
25598
|
}
|
|
25382
25599
|
const pkgPath = join18(projectRoot, "package.json");
|
|
25383
|
-
if (!
|
|
25600
|
+
if (!existsSync20(pkgPath)) {
|
|
25384
25601
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
25385
25602
|
}
|
|
25386
25603
|
let scripts = {};
|
|
@@ -25396,7 +25613,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25396
25613
|
}
|
|
25397
25614
|
return await new Promise((resolveRun) => {
|
|
25398
25615
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
25399
|
-
const child =
|
|
25616
|
+
const child = spawn5(npmCmd, ["run", script], {
|
|
25400
25617
|
cwd: projectRoot,
|
|
25401
25618
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25402
25619
|
env: process.env,
|
|
@@ -25468,8 +25685,8 @@ __export(postCouncilHook_exports, {
|
|
|
25468
25685
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
25469
25686
|
runPostCouncilHook: () => runPostCouncilHook
|
|
25470
25687
|
});
|
|
25471
|
-
import { spawn as
|
|
25472
|
-
import { existsSync as
|
|
25688
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
25689
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
|
|
25473
25690
|
import { join as join19 } from "node:path";
|
|
25474
25691
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
25475
25692
|
if (options?.runMode === "implementation") {
|
|
@@ -25483,7 +25700,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25483
25700
|
}
|
|
25484
25701
|
const planJsonPath2 = join19(ctx.rootDir, "plan.json");
|
|
25485
25702
|
const scriptPath = join19(ctx.projectRoot, "complete-design.mjs");
|
|
25486
|
-
if (!
|
|
25703
|
+
if (!existsSync21(planJsonPath2)) {
|
|
25487
25704
|
return {
|
|
25488
25705
|
ran: false,
|
|
25489
25706
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -25499,7 +25716,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25499
25716
|
if (phaseCount === 0) {
|
|
25500
25717
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
25501
25718
|
}
|
|
25502
|
-
if (!
|
|
25719
|
+
if (!existsSync21(scriptPath)) {
|
|
25503
25720
|
try {
|
|
25504
25721
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
25505
25722
|
return {
|
|
@@ -25517,7 +25734,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25517
25734
|
}
|
|
25518
25735
|
}
|
|
25519
25736
|
return await new Promise((resolveRun) => {
|
|
25520
|
-
const child =
|
|
25737
|
+
const child = spawn6(process.execPath, [scriptPath], {
|
|
25521
25738
|
cwd: ctx.projectRoot,
|
|
25522
25739
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25523
25740
|
env: process.env
|
|
@@ -25683,8 +25900,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
25683
25900
|
sources: scope.sources
|
|
25684
25901
|
} : void 0
|
|
25685
25902
|
});
|
|
25686
|
-
const
|
|
25687
|
-
completionHook = { ran: true, path:
|
|
25903
|
+
const path27 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
25904
|
+
completionHook = { ran: true, path: path27, completion };
|
|
25688
25905
|
} catch (err) {
|
|
25689
25906
|
completionHook = {
|
|
25690
25907
|
ran: true,
|
|
@@ -25720,12 +25937,12 @@ var buildLessonsSummary_exports = {};
|
|
|
25720
25937
|
__export(buildLessonsSummary_exports, {
|
|
25721
25938
|
buildLessonsSummary: () => buildLessonsSummary
|
|
25722
25939
|
});
|
|
25723
|
-
import { existsSync as
|
|
25940
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
25724
25941
|
import { join as join20 } from "node:path";
|
|
25725
25942
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
25726
25943
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
25727
25944
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
25728
|
-
if (!
|
|
25945
|
+
if (!existsSync22(join20(zelariRoot, "lessons.jsonl"))) return null;
|
|
25729
25946
|
const lessons = recallLessons(zelariRoot, {
|
|
25730
25947
|
maxLessons: 5,
|
|
25731
25948
|
maxBytes: 2048,
|
|
@@ -25748,12 +25965,12 @@ __export(councilFeedback_exports, {
|
|
|
25748
25965
|
});
|
|
25749
25966
|
import {
|
|
25750
25967
|
promises as fs12,
|
|
25751
|
-
existsSync as
|
|
25968
|
+
existsSync as existsSync23,
|
|
25752
25969
|
readFileSync as readFileSync20,
|
|
25753
25970
|
writeFileSync as writeFileSync14,
|
|
25754
25971
|
mkdirSync as mkdirSync9
|
|
25755
25972
|
} from "node:fs";
|
|
25756
|
-
import
|
|
25973
|
+
import path17 from "node:path";
|
|
25757
25974
|
import os7 from "node:os";
|
|
25758
25975
|
var FeedbackStore;
|
|
25759
25976
|
var init_councilFeedback = __esm({
|
|
@@ -25764,7 +25981,7 @@ var init_councilFeedback = __esm({
|
|
|
25764
25981
|
now;
|
|
25765
25982
|
entries = [];
|
|
25766
25983
|
constructor(options = {}) {
|
|
25767
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
25984
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path17.join(os7.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
25768
25985
|
this.now = options.now ?? Date.now;
|
|
25769
25986
|
this.load();
|
|
25770
25987
|
}
|
|
@@ -25857,7 +26074,7 @@ var init_councilFeedback = __esm({
|
|
|
25857
26074
|
}
|
|
25858
26075
|
// --- persistence ---------------------------------------------------------
|
|
25859
26076
|
load() {
|
|
25860
|
-
if (!
|
|
26077
|
+
if (!existsSync23(this.file)) return;
|
|
25861
26078
|
try {
|
|
25862
26079
|
const raw = readFileSync20(this.file, "utf-8");
|
|
25863
26080
|
const parsed = JSON.parse(raw);
|
|
@@ -25870,7 +26087,7 @@ var init_councilFeedback = __esm({
|
|
|
25870
26087
|
}
|
|
25871
26088
|
}
|
|
25872
26089
|
save() {
|
|
25873
|
-
mkdirSync9(
|
|
26090
|
+
mkdirSync9(path17.dirname(this.file), { recursive: true });
|
|
25874
26091
|
writeFileSync14(
|
|
25875
26092
|
this.file,
|
|
25876
26093
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -25905,7 +26122,7 @@ __export(fileBackend_exports, {
|
|
|
25905
26122
|
});
|
|
25906
26123
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
25907
26124
|
import { promises as fs13 } from "node:fs";
|
|
25908
|
-
import * as
|
|
26125
|
+
import * as path18 from "node:path";
|
|
25909
26126
|
function tokenize(text) {
|
|
25910
26127
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
25911
26128
|
}
|
|
@@ -25943,8 +26160,8 @@ var init_fileBackend = __esm({
|
|
|
25943
26160
|
logPath = "";
|
|
25944
26161
|
memoryDir = "";
|
|
25945
26162
|
async init(projectRoot) {
|
|
25946
|
-
this.memoryDir =
|
|
25947
|
-
this.logPath =
|
|
26163
|
+
this.memoryDir = path18.join(projectRoot, ".zelari", "memory");
|
|
26164
|
+
this.logPath = path18.join(this.memoryDir, "log.jsonl");
|
|
25948
26165
|
await fs13.mkdir(this.memoryDir, { recursive: true });
|
|
25949
26166
|
}
|
|
25950
26167
|
async add(content, metadata = {}, graph) {
|
|
@@ -26014,6 +26231,142 @@ var init_fileBackend = __esm({
|
|
|
26014
26231
|
}
|
|
26015
26232
|
});
|
|
26016
26233
|
|
|
26234
|
+
// src/cli/checkpoint/checkpointManager.ts
|
|
26235
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
26236
|
+
import { promisify } from "node:util";
|
|
26237
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
26238
|
+
import { tmpdir } from "node:os";
|
|
26239
|
+
import path19 from "node:path";
|
|
26240
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
26241
|
+
async function git(cwd, args, env) {
|
|
26242
|
+
const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
|
|
26243
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
26244
|
+
env: env ? { ...process.env, ...env } : process.env
|
|
26245
|
+
});
|
|
26246
|
+
return stdout;
|
|
26247
|
+
}
|
|
26248
|
+
async function gitSafe(cwd, args, env) {
|
|
26249
|
+
try {
|
|
26250
|
+
return (await git(cwd, args, env)).trim();
|
|
26251
|
+
} catch {
|
|
26252
|
+
return null;
|
|
26253
|
+
}
|
|
26254
|
+
}
|
|
26255
|
+
async function isGitRepo(cwd) {
|
|
26256
|
+
return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
26257
|
+
}
|
|
26258
|
+
async function withTempIndex(fn) {
|
|
26259
|
+
const dir = mkdtempSync(path19.join(tmpdir(), "zelari-ckpt-"));
|
|
26260
|
+
const indexFile = path19.join(dir, "index");
|
|
26261
|
+
try {
|
|
26262
|
+
return await fn(indexFile);
|
|
26263
|
+
} finally {
|
|
26264
|
+
rmSync(dir, { recursive: true, force: true });
|
|
26265
|
+
}
|
|
26266
|
+
}
|
|
26267
|
+
async function snapshotTree(cwd) {
|
|
26268
|
+
const head = await gitSafe(cwd, ["rev-parse", "HEAD"]);
|
|
26269
|
+
return withTempIndex(async (indexFile) => {
|
|
26270
|
+
const env = { GIT_INDEX_FILE: indexFile };
|
|
26271
|
+
if (head) await git(cwd, ["read-tree", "HEAD"], env);
|
|
26272
|
+
await git(cwd, ["add", "-A"], env);
|
|
26273
|
+
const tree = (await git(cwd, ["write-tree"], env)).trim();
|
|
26274
|
+
return { tree, head };
|
|
26275
|
+
});
|
|
26276
|
+
}
|
|
26277
|
+
async function createCheckpoint(cwd, label = "checkpoint") {
|
|
26278
|
+
if (!await isGitRepo(cwd)) {
|
|
26279
|
+
return { ok: false, error: "not a git repository \u2014 checkpoints require git" };
|
|
26280
|
+
}
|
|
26281
|
+
try {
|
|
26282
|
+
const { tree, head } = await snapshotTree(cwd);
|
|
26283
|
+
const id = randomUUID3().slice(0, 8);
|
|
26284
|
+
const createdAt = Date.now();
|
|
26285
|
+
const message = `zelari-checkpoint ${id}: ${label}`;
|
|
26286
|
+
const commitArgs = ["commit-tree", tree, "-m", message];
|
|
26287
|
+
if (head) commitArgs.push("-p", head);
|
|
26288
|
+
const commit = (await git(cwd, commitArgs)).trim();
|
|
26289
|
+
await git(cwd, ["update-ref", `${REF_PREFIX}${id}`, commit]);
|
|
26290
|
+
return { ok: true, value: { id, label, tree, head, commit, createdAt } };
|
|
26291
|
+
} catch (err) {
|
|
26292
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
26293
|
+
}
|
|
26294
|
+
}
|
|
26295
|
+
async function listCheckpoints(cwd) {
|
|
26296
|
+
const out = await gitSafe(cwd, [
|
|
26297
|
+
"for-each-ref",
|
|
26298
|
+
"--sort=-creatordate",
|
|
26299
|
+
"--format=%(refname) %(objectname) %(creatordate:unix) %(contents:subject)",
|
|
26300
|
+
REF_PREFIX
|
|
26301
|
+
]);
|
|
26302
|
+
if (!out) return [];
|
|
26303
|
+
const checkpoints = [];
|
|
26304
|
+
for (const line of out.split("\n")) {
|
|
26305
|
+
if (!line.trim()) continue;
|
|
26306
|
+
const [refname, commit, unix, ...subjectParts] = line.split(" ");
|
|
26307
|
+
if (!refname?.startsWith(REF_PREFIX)) continue;
|
|
26308
|
+
const id = refname.slice(REF_PREFIX.length);
|
|
26309
|
+
const subject = subjectParts.join(" ");
|
|
26310
|
+
const label = subject.replace(/^zelari-checkpoint\s+\S+:\s*/, "") || "checkpoint";
|
|
26311
|
+
const tree = await gitSafe(cwd, ["rev-parse", `${commit}^{tree}`]) ?? "";
|
|
26312
|
+
checkpoints.push({
|
|
26313
|
+
id,
|
|
26314
|
+
label,
|
|
26315
|
+
tree,
|
|
26316
|
+
head: null,
|
|
26317
|
+
commit: commit ?? "",
|
|
26318
|
+
createdAt: (Number(unix) || 0) * 1e3
|
|
26319
|
+
});
|
|
26320
|
+
}
|
|
26321
|
+
return checkpoints;
|
|
26322
|
+
}
|
|
26323
|
+
async function latestCheckpoint(cwd) {
|
|
26324
|
+
return (await listCheckpoints(cwd))[0] ?? null;
|
|
26325
|
+
}
|
|
26326
|
+
async function restoreCheckpoint(cwd, id) {
|
|
26327
|
+
if (!await isGitRepo(cwd)) {
|
|
26328
|
+
return { ok: false, error: "not a git repository \u2014 checkpoints require git" };
|
|
26329
|
+
}
|
|
26330
|
+
try {
|
|
26331
|
+
const target = id ? (await listCheckpoints(cwd)).find((c) => c.id === id) : await latestCheckpoint(cwd);
|
|
26332
|
+
if (!target) {
|
|
26333
|
+
return { ok: false, error: id ? `checkpoint "${id}" not found` : "no checkpoints to restore" };
|
|
26334
|
+
}
|
|
26335
|
+
const snapTree = target.tree || await gitSafe(cwd, ["rev-parse", `${target.commit}^{tree}`]);
|
|
26336
|
+
if (!snapTree) return { ok: false, error: "could not resolve checkpoint tree" };
|
|
26337
|
+
const current = await snapshotTree(cwd);
|
|
26338
|
+
const addedOut = await gitSafe(cwd, [
|
|
26339
|
+
"diff",
|
|
26340
|
+
"--name-only",
|
|
26341
|
+
"--diff-filter=A",
|
|
26342
|
+
snapTree,
|
|
26343
|
+
current.tree
|
|
26344
|
+
]);
|
|
26345
|
+
const added = addedOut ? addedOut.split("\n").filter((l) => l.trim()) : [];
|
|
26346
|
+
await git(cwd, ["read-tree", snapTree]);
|
|
26347
|
+
await git(cwd, ["checkout-index", "-a", "-f"]);
|
|
26348
|
+
const deleted = [];
|
|
26349
|
+
for (const rel2 of added) {
|
|
26350
|
+
try {
|
|
26351
|
+
rmSync(path19.join(cwd, rel2), { force: true });
|
|
26352
|
+
deleted.push(rel2);
|
|
26353
|
+
} catch {
|
|
26354
|
+
}
|
|
26355
|
+
}
|
|
26356
|
+
return { ok: true, value: { id: target.id, deleted } };
|
|
26357
|
+
} catch (err) {
|
|
26358
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
26359
|
+
}
|
|
26360
|
+
}
|
|
26361
|
+
var execFileAsync, REF_PREFIX;
|
|
26362
|
+
var init_checkpointManager = __esm({
|
|
26363
|
+
"src/cli/checkpoint/checkpointManager.ts"() {
|
|
26364
|
+
"use strict";
|
|
26365
|
+
execFileAsync = promisify(execFile2);
|
|
26366
|
+
REF_PREFIX = "refs/zelari/checkpoints/";
|
|
26367
|
+
}
|
|
26368
|
+
});
|
|
26369
|
+
|
|
26017
26370
|
// src/cli/zelariMission.ts
|
|
26018
26371
|
var zelariMission_exports = {};
|
|
26019
26372
|
__export(zelariMission_exports, {
|
|
@@ -26023,9 +26376,9 @@ __export(zelariMission_exports, {
|
|
|
26023
26376
|
resolveMaxStall: () => resolveMaxStall,
|
|
26024
26377
|
runZelariMission: () => runZelariMission
|
|
26025
26378
|
});
|
|
26026
|
-
import { randomUUID as
|
|
26379
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
26027
26380
|
import { promises as fs14 } from "node:fs";
|
|
26028
|
-
import * as
|
|
26381
|
+
import * as path20 from "node:path";
|
|
26029
26382
|
function resolveMaxIterations(env = process.env) {
|
|
26030
26383
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
26031
26384
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -26041,10 +26394,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
26041
26394
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
26042
26395
|
}
|
|
26043
26396
|
async function writeMissionState(projectRoot, state2) {
|
|
26044
|
-
const dir =
|
|
26397
|
+
const dir = path20.join(projectRoot, ".zelari");
|
|
26045
26398
|
await fs14.mkdir(dir, { recursive: true });
|
|
26046
26399
|
await fs14.writeFile(
|
|
26047
|
-
|
|
26400
|
+
path20.join(dir, "mission-state.json"),
|
|
26048
26401
|
JSON.stringify(state2, null, 2) + "\n",
|
|
26049
26402
|
"utf8"
|
|
26050
26403
|
);
|
|
@@ -26085,7 +26438,7 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
26085
26438
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
26086
26439
|
const maxIter = deps.maxIterations ?? resolveMaxIterations(deps.env);
|
|
26087
26440
|
const maxStall = resolveMaxStall(deps.env);
|
|
26088
|
-
const missionId = deps.missionId ?? `m_${
|
|
26441
|
+
const missionId = deps.missionId ?? `m_${randomUUID4().slice(0, 8)}`;
|
|
26089
26442
|
const startedAt = now().toISOString();
|
|
26090
26443
|
const state2 = {
|
|
26091
26444
|
missionId,
|
|
@@ -26100,6 +26453,14 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
26100
26453
|
};
|
|
26101
26454
|
await deps.memory.init(deps.projectRoot);
|
|
26102
26455
|
await writeMissionState(deps.projectRoot, state2);
|
|
26456
|
+
if ((deps.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
26457
|
+
const cp = await createCheckpoint(deps.projectRoot, `zelari mission ${missionId}`);
|
|
26458
|
+
if (cp.ok) {
|
|
26459
|
+
deps.emit(
|
|
26460
|
+
`[zelari] checkpoint ${cp.value.id} creato \u2014 se la missione va storta ripristina con \`/rollback ${cp.value.id}\`.`
|
|
26461
|
+
);
|
|
26462
|
+
}
|
|
26463
|
+
}
|
|
26103
26464
|
const designFirst = brief.phases[0]?.mode === "design-phase";
|
|
26104
26465
|
let noWriteStreak = 0;
|
|
26105
26466
|
for (let i = 1; i <= maxIter; i++) {
|
|
@@ -26167,6 +26528,7 @@ var init_zelariMission = __esm({
|
|
|
26167
26528
|
"src/cli/zelariMission.ts"() {
|
|
26168
26529
|
"use strict";
|
|
26169
26530
|
init_fileBackend();
|
|
26531
|
+
init_checkpointManager();
|
|
26170
26532
|
DEFAULT_MAX_ITER = 10;
|
|
26171
26533
|
DEFAULT_MAX_STALL = 2;
|
|
26172
26534
|
}
|
|
@@ -26178,26 +26540,26 @@ __export(doctor_exports, {
|
|
|
26178
26540
|
runDoctor: () => runDoctor
|
|
26179
26541
|
});
|
|
26180
26542
|
import { execSync } from "node:child_process";
|
|
26181
|
-
import { existsSync as
|
|
26543
|
+
import { existsSync as existsSync28, readFileSync as readFileSync23, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
26182
26544
|
import { createRequire as createRequire2 } from "node:module";
|
|
26183
26545
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
26184
|
-
import
|
|
26546
|
+
import path26 from "node:path";
|
|
26185
26547
|
function findPackageRoot(start) {
|
|
26186
26548
|
let dir = start;
|
|
26187
26549
|
for (let i = 0; i < 6; i += 1) {
|
|
26188
|
-
const candidate =
|
|
26189
|
-
if (
|
|
26550
|
+
const candidate = path26.join(dir, "package.json");
|
|
26551
|
+
if (existsSync28(candidate)) {
|
|
26190
26552
|
try {
|
|
26191
26553
|
const pkg = JSON.parse(readFileSync23(candidate, "utf8"));
|
|
26192
26554
|
if (pkg.name === "zelari-code") return dir;
|
|
26193
26555
|
} catch {
|
|
26194
26556
|
}
|
|
26195
26557
|
}
|
|
26196
|
-
const parent =
|
|
26558
|
+
const parent = path26.dirname(dir);
|
|
26197
26559
|
if (parent === dir) break;
|
|
26198
26560
|
dir = parent;
|
|
26199
26561
|
}
|
|
26200
|
-
return
|
|
26562
|
+
return path26.resolve(__dirname3, "..", "..", "..");
|
|
26201
26563
|
}
|
|
26202
26564
|
function tryExec(cmd) {
|
|
26203
26565
|
try {
|
|
@@ -26211,7 +26573,7 @@ function tryExec(cmd) {
|
|
|
26211
26573
|
}
|
|
26212
26574
|
function readPackageJson3() {
|
|
26213
26575
|
try {
|
|
26214
|
-
const pkgPath =
|
|
26576
|
+
const pkgPath = path26.join(packageRoot, "package.json");
|
|
26215
26577
|
return JSON.parse(readFileSync23(pkgPath, "utf8"));
|
|
26216
26578
|
} catch {
|
|
26217
26579
|
return null;
|
|
@@ -26227,8 +26589,8 @@ function checkShim(pkgName) {
|
|
|
26227
26589
|
}
|
|
26228
26590
|
const isWin = process.platform === "win32";
|
|
26229
26591
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
26230
|
-
const shimPath =
|
|
26231
|
-
if (!
|
|
26592
|
+
const shimPath = path26.join(prefix, shimName);
|
|
26593
|
+
if (!existsSync28(shimPath)) {
|
|
26232
26594
|
return FAIL(
|
|
26233
26595
|
`shim not found at ${shimPath}
|
|
26234
26596
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -26255,8 +26617,8 @@ function checkShim(pkgName) {
|
|
|
26255
26617
|
fix: npm install -g ${pkgName}@latest --force`
|
|
26256
26618
|
);
|
|
26257
26619
|
}
|
|
26258
|
-
const resolved =
|
|
26259
|
-
const expected =
|
|
26620
|
+
const resolved = path26.resolve(path26.dirname(shimPath), target);
|
|
26621
|
+
const expected = path26.join(
|
|
26260
26622
|
prefix,
|
|
26261
26623
|
"node_modules",
|
|
26262
26624
|
pkgName,
|
|
@@ -26295,8 +26657,8 @@ function checkNode(pkg) {
|
|
|
26295
26657
|
return OK(`node ${raw}`);
|
|
26296
26658
|
}
|
|
26297
26659
|
function checkBundle() {
|
|
26298
|
-
const bundle =
|
|
26299
|
-
if (!
|
|
26660
|
+
const bundle = path26.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
26661
|
+
if (!existsSync28(bundle)) {
|
|
26300
26662
|
return FAIL(
|
|
26301
26663
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
26302
26664
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -26316,7 +26678,7 @@ function checkRuntimeDeps() {
|
|
|
26316
26678
|
const missing = [];
|
|
26317
26679
|
for (const dep of required2) {
|
|
26318
26680
|
try {
|
|
26319
|
-
const localReq = createRequire2(
|
|
26681
|
+
const localReq = createRequire2(path26.join(packageRoot, "package.json"));
|
|
26320
26682
|
localReq.resolve(dep);
|
|
26321
26683
|
} catch {
|
|
26322
26684
|
missing.push(dep);
|
|
@@ -26398,7 +26760,7 @@ var init_doctor = __esm({
|
|
|
26398
26760
|
"src/cli/utils/doctor.ts"() {
|
|
26399
26761
|
"use strict";
|
|
26400
26762
|
require3 = createRequire2(import.meta.url);
|
|
26401
|
-
__dirname3 =
|
|
26763
|
+
__dirname3 = path26.dirname(fileURLToPath2(import.meta.url));
|
|
26402
26764
|
packageRoot = findPackageRoot(__dirname3);
|
|
26403
26765
|
OK = (message) => ({
|
|
26404
26766
|
ok: true,
|
|
@@ -26845,6 +27207,81 @@ function StreamingTail(m) {
|
|
|
26845
27207
|
// src/cli/components/StatusBar.tsx
|
|
26846
27208
|
import React6 from "react";
|
|
26847
27209
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
27210
|
+
|
|
27211
|
+
// src/cli/modelPricing.ts
|
|
27212
|
+
var PRICES_PER_MILLION = {
|
|
27213
|
+
// xAI Grok
|
|
27214
|
+
"grok-4": { input: 3, output: 15 },
|
|
27215
|
+
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
27216
|
+
"grok-3": { input: 3, output: 15 },
|
|
27217
|
+
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
27218
|
+
"grok-2-vision": { input: 2, output: 10 },
|
|
27219
|
+
// GLM / Z.AI
|
|
27220
|
+
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
27221
|
+
"glm-4.5": { input: 0.5, output: 2 },
|
|
27222
|
+
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
27223
|
+
"glm-z1": { input: 0.5, output: 2 },
|
|
27224
|
+
// MiniMax
|
|
27225
|
+
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
27226
|
+
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
27227
|
+
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
27228
|
+
// DeepSeek (global platform) — estimated list prices; override via
|
|
27229
|
+
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
27230
|
+
// DeepSeek prompt-cache hits are ~10× cheaper than a cache miss.
|
|
27231
|
+
"deepseek-v4-flash": { input: 0.14, output: 0.28, cachedInput: 0.014 },
|
|
27232
|
+
"deepseek-v4-pro": { input: 0.55, output: 2.19, cachedInput: 0.055 },
|
|
27233
|
+
// OpenAI (for openai-compatible fallback)
|
|
27234
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
27235
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
27236
|
+
"gpt-4-turbo": { input: 10, output: 30 },
|
|
27237
|
+
"o1-preview": { input: 15, output: 60 },
|
|
27238
|
+
"o1-mini": { input: 3, output: 12 }
|
|
27239
|
+
};
|
|
27240
|
+
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
27241
|
+
var DEFAULT_CACHE_DISCOUNT = 0.25;
|
|
27242
|
+
function envOverride(model) {
|
|
27243
|
+
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
27244
|
+
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
27245
|
+
return parseRateOverride(raw);
|
|
27246
|
+
}
|
|
27247
|
+
function parseRateOverride(raw) {
|
|
27248
|
+
if (!raw) return null;
|
|
27249
|
+
const [inStr, outStr] = raw.split("/");
|
|
27250
|
+
const input = Number.parseFloat(inStr);
|
|
27251
|
+
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
27252
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
27253
|
+
return { input, output };
|
|
27254
|
+
}
|
|
27255
|
+
function getModelRate(model) {
|
|
27256
|
+
if (!model) return DEFAULT_RATE;
|
|
27257
|
+
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
27258
|
+
}
|
|
27259
|
+
function calculateCost(model, promptTokens, completionTokens, cachedPromptTokens = 0) {
|
|
27260
|
+
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
27261
|
+
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
27262
|
+
const rate = getModelRate(model);
|
|
27263
|
+
const cached2 = Number.isFinite(cachedPromptTokens) && cachedPromptTokens > 0 ? Math.min(cachedPromptTokens, promptTokens) : 0;
|
|
27264
|
+
const uncachedPrompt = promptTokens - cached2;
|
|
27265
|
+
const cachedRate = rate.cachedInput ?? rate.input * DEFAULT_CACHE_DISCOUNT;
|
|
27266
|
+
const inputCost = uncachedPrompt / 1e6 * rate.input + cached2 / 1e6 * cachedRate;
|
|
27267
|
+
const outputCost = completionTokens / 1e6 * rate.output;
|
|
27268
|
+
return Number((inputCost + outputCost).toFixed(6));
|
|
27269
|
+
}
|
|
27270
|
+
function formatCost(usd) {
|
|
27271
|
+
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
27272
|
+
if (usd === 0) return "$0.0000";
|
|
27273
|
+
if (usd < 1e-4) return "<$0.0001";
|
|
27274
|
+
return `$${usd.toFixed(4)}`;
|
|
27275
|
+
}
|
|
27276
|
+
function formatTokens(tokens) {
|
|
27277
|
+
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
27278
|
+
if (tokens < 1e3) return `${tokens}`;
|
|
27279
|
+
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
27280
|
+
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
27281
|
+
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
27282
|
+
}
|
|
27283
|
+
|
|
27284
|
+
// src/cli/components/StatusBar.tsx
|
|
26848
27285
|
function StatusBar({
|
|
26849
27286
|
model,
|
|
26850
27287
|
provider,
|
|
@@ -26855,7 +27292,9 @@ function StatusBar({
|
|
|
26855
27292
|
mode = "agent",
|
|
26856
27293
|
cwd,
|
|
26857
27294
|
elapsedMs = null,
|
|
26858
|
-
lastMs = null
|
|
27295
|
+
lastMs = null,
|
|
27296
|
+
costUsd = 0,
|
|
27297
|
+
cachedTokens = 0
|
|
26859
27298
|
}) {
|
|
26860
27299
|
return /* @__PURE__ */ React6.createElement(Box5, { paddingX: 1, width: "100%", justifyContent: "space-between", gap: 2 }, /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 2 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, /* @__PURE__ */ React6.createElement(Text6, { color: sessionActive ? "green" : "gray" }, sessionActive ? "\u25CF" : "\u25CB"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " "), /* @__PURE__ */ React6.createElement(
|
|
26861
27300
|
Text6,
|
|
@@ -26864,7 +27303,7 @@ function StatusBar({
|
|
|
26864
27303
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
26865
27304
|
},
|
|
26866
27305
|
mode === "council" ? "\u26EC council" : mode === "zelari" ? "\u26A1 zelari" : "\u23F5 agent"
|
|
26867
|
-
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (shift+tab)"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, provider), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, null, model), cwd ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, cwd)) : null)), /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 1 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, busy && elapsedMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Spinner, { color: "yellow" }), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " ", formatDuration(elapsedMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : lastMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "last ", formatDuration(lastMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, queueCount > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "queue ", queueCount), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "session ", sessionId))));
|
|
27306
|
+
), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (shift+tab)"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, provider), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, null, model), cwd ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, cwd)) : null)), /* @__PURE__ */ React6.createElement(Box5, { flexShrink: 1 }, /* @__PURE__ */ React6.createElement(Text6, { wrap: "truncate" }, busy && elapsedMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Spinner, { color: "yellow" }), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " ", formatDuration(elapsedMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : lastMs !== null ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "last ", formatDuration(lastMs)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, queueCount > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "queue ", queueCount), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, costUsd > 0 ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, formatCost(costUsd)), cachedTokens > 0 ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (", formatTokens(cachedTokens), " cached)") : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 ")) : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "session ", sessionId))));
|
|
26868
27307
|
}
|
|
26869
27308
|
|
|
26870
27309
|
// src/cli/components/SelectList.tsx
|
|
@@ -29270,75 +29709,13 @@ registerCodingSkill(regressionTest);
|
|
|
29270
29709
|
|
|
29271
29710
|
// src/cli/app.tsx
|
|
29272
29711
|
init_providerConfig();
|
|
29273
|
-
|
|
29274
|
-
// packages/core/dist/harness/index.js
|
|
29275
|
-
init_AgentHarness();
|
|
29276
|
-
|
|
29277
|
-
// packages/core/dist/core/sessionJsonl.js
|
|
29278
|
-
import { promises as fs8 } from "node:fs";
|
|
29279
|
-
import path10 from "node:path";
|
|
29280
|
-
import os3 from "node:os";
|
|
29281
|
-
var SessionJsonlWriter = class {
|
|
29282
|
-
filePath;
|
|
29283
|
-
onError;
|
|
29284
|
-
constructor(sessionId, options = {}) {
|
|
29285
|
-
const baseDir = options.baseDir ?? defaultBaseDir();
|
|
29286
|
-
this.filePath = path10.join(baseDir, `${sessionId}.jsonl`);
|
|
29287
|
-
this.onError = options.onError ?? console.error;
|
|
29288
|
-
}
|
|
29289
|
-
/** Absolute path to the session JSONL file. */
|
|
29290
|
-
get path() {
|
|
29291
|
-
return this.filePath;
|
|
29292
|
-
}
|
|
29293
|
-
/** Append a BrainEvent as one JSON line. Creates the file + parent dirs if missing. */
|
|
29294
|
-
async append(event) {
|
|
29295
|
-
try {
|
|
29296
|
-
await fs8.mkdir(path10.dirname(this.filePath), { recursive: true });
|
|
29297
|
-
const line = JSON.stringify({
|
|
29298
|
-
ts: event.ts,
|
|
29299
|
-
sessionId: event.sessionId,
|
|
29300
|
-
event
|
|
29301
|
-
}) + "\n";
|
|
29302
|
-
await fs8.appendFile(this.filePath, line, { encoding: "utf-8", mode: 420 });
|
|
29303
|
-
} catch (err) {
|
|
29304
|
-
this.onError(`[sessionJsonl] failed to append event to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
29305
|
-
}
|
|
29306
|
-
}
|
|
29307
|
-
/** Close the writer (no-op currently, but reserved for future buffered mode). */
|
|
29308
|
-
async close() {
|
|
29309
|
-
}
|
|
29310
|
-
};
|
|
29311
|
-
async function readSession(filePath) {
|
|
29312
|
-
try {
|
|
29313
|
-
const content = await fs8.readFile(filePath, "utf-8");
|
|
29314
|
-
const events = [];
|
|
29315
|
-
for (const line of content.split("\n")) {
|
|
29316
|
-
const trimmed = line.trim();
|
|
29317
|
-
if (!trimmed)
|
|
29318
|
-
continue;
|
|
29319
|
-
try {
|
|
29320
|
-
const parsed = JSON.parse(trimmed);
|
|
29321
|
-
if (parsed && typeof parsed === "object" && "event" in parsed) {
|
|
29322
|
-
events.push(parsed.event);
|
|
29323
|
-
}
|
|
29324
|
-
} catch {
|
|
29325
|
-
}
|
|
29326
|
-
}
|
|
29327
|
-
return events;
|
|
29328
|
-
} catch (err) {
|
|
29329
|
-
if (err.code === "ENOENT")
|
|
29330
|
-
return [];
|
|
29331
|
-
throw err;
|
|
29332
|
-
}
|
|
29333
|
-
}
|
|
29334
|
-
function defaultBaseDir() {
|
|
29335
|
-
return path10.join(os3.tmpdir(), "zelari-code", "sessions");
|
|
29336
|
-
}
|
|
29712
|
+
init_harness();
|
|
29337
29713
|
|
|
29338
29714
|
// src/cli/hooks/useSession.ts
|
|
29339
29715
|
import { useState as useState5, useRef as useRef3, useCallback, useEffect as useEffect4 } from "react";
|
|
29340
29716
|
|
|
29341
29717
|
// src/cli/sessionManager.ts
|
|
29718
|
+
init_harness();
|
|
29342
29719
|
import { promises as fs9, existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync, statSync } from "node:fs";
|
|
29343
29720
|
import path11 from "node:path";
|
|
29344
29721
|
import os4 from "node:os";
|
|
@@ -29460,6 +29837,9 @@ async function loadSessionEvents(id) {
|
|
|
29460
29837
|
return readSession(filePath);
|
|
29461
29838
|
}
|
|
29462
29839
|
|
|
29840
|
+
// src/cli/hooks/useSession.ts
|
|
29841
|
+
init_harness();
|
|
29842
|
+
|
|
29463
29843
|
// src/cli/hooks/eventsToMessages.ts
|
|
29464
29844
|
function eventsToMessages(events) {
|
|
29465
29845
|
const out = [];
|
|
@@ -29740,6 +30120,7 @@ ${lines.join("\n")}`;
|
|
|
29740
30120
|
}
|
|
29741
30121
|
|
|
29742
30122
|
// src/cli/hooks/useChatTurn.ts
|
|
30123
|
+
init_harness();
|
|
29743
30124
|
import { useState as useState6, useRef as useRef4, useCallback as useCallback2 } from "react";
|
|
29744
30125
|
|
|
29745
30126
|
// src/cli/metrics.ts
|
|
@@ -30082,33 +30463,317 @@ function safeStringify(value) {
|
|
|
30082
30463
|
}
|
|
30083
30464
|
}
|
|
30084
30465
|
|
|
30466
|
+
// src/cli/diagnostics/engine.ts
|
|
30467
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
30468
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
30469
|
+
import path15 from "node:path";
|
|
30470
|
+
function parseEslintJson(stdout, _file2) {
|
|
30471
|
+
const json2 = safeJson(stdout);
|
|
30472
|
+
if (!Array.isArray(json2)) return [];
|
|
30473
|
+
const out = [];
|
|
30474
|
+
for (const fileResult of json2) {
|
|
30475
|
+
if (!fileResult || typeof fileResult !== "object") continue;
|
|
30476
|
+
const fr = fileResult;
|
|
30477
|
+
const filePath = typeof fr.filePath === "string" ? fr.filePath : _file2;
|
|
30478
|
+
if (!Array.isArray(fr.messages)) continue;
|
|
30479
|
+
for (const m of fr.messages) {
|
|
30480
|
+
if (!m || typeof m !== "object") continue;
|
|
30481
|
+
const msg = m;
|
|
30482
|
+
out.push({
|
|
30483
|
+
file: filePath,
|
|
30484
|
+
line: typeof msg.line === "number" ? msg.line : 0,
|
|
30485
|
+
...typeof msg.column === "number" ? { column: msg.column } : {},
|
|
30486
|
+
// ESLint severity: 2 = error, 1 = warning.
|
|
30487
|
+
severity: msg.severity === 2 ? "error" : "warning",
|
|
30488
|
+
message: typeof msg.message === "string" ? msg.message : "(no message)",
|
|
30489
|
+
...typeof msg.ruleId === "string" && msg.ruleId ? { code: msg.ruleId } : {},
|
|
30490
|
+
source: "eslint"
|
|
30491
|
+
});
|
|
30492
|
+
}
|
|
30493
|
+
}
|
|
30494
|
+
return out;
|
|
30495
|
+
}
|
|
30496
|
+
function parseRuffJson(stdout, _file2) {
|
|
30497
|
+
const json2 = safeJson(stdout);
|
|
30498
|
+
if (!Array.isArray(json2)) return [];
|
|
30499
|
+
const out = [];
|
|
30500
|
+
for (const issue2 of json2) {
|
|
30501
|
+
if (!issue2 || typeof issue2 !== "object") continue;
|
|
30502
|
+
const it = issue2;
|
|
30503
|
+
const code = typeof it.code === "string" ? it.code : void 0;
|
|
30504
|
+
out.push({
|
|
30505
|
+
file: typeof it.filename === "string" ? it.filename : _file2,
|
|
30506
|
+
line: typeof it.location?.row === "number" ? it.location.row : 0,
|
|
30507
|
+
...typeof it.location?.column === "number" ? { column: it.location.column } : {},
|
|
30508
|
+
// Ruff has no severity field; E999 is a syntax error, everything else
|
|
30509
|
+
// is a lint warning.
|
|
30510
|
+
severity: code === "E999" ? "error" : "warning",
|
|
30511
|
+
message: typeof it.message === "string" ? it.message : "(no message)",
|
|
30512
|
+
...code ? { code } : {},
|
|
30513
|
+
source: "ruff"
|
|
30514
|
+
});
|
|
30515
|
+
}
|
|
30516
|
+
return out;
|
|
30517
|
+
}
|
|
30518
|
+
function safeJson(s) {
|
|
30519
|
+
const trimmed = s.trim();
|
|
30520
|
+
if (!trimmed) return null;
|
|
30521
|
+
try {
|
|
30522
|
+
return JSON.parse(trimmed);
|
|
30523
|
+
} catch {
|
|
30524
|
+
return null;
|
|
30525
|
+
}
|
|
30526
|
+
}
|
|
30527
|
+
var DEFAULT_PROVIDERS = [
|
|
30528
|
+
{
|
|
30529
|
+
name: "eslint",
|
|
30530
|
+
bin: "eslint",
|
|
30531
|
+
extensions: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"],
|
|
30532
|
+
args: (file2) => ["--format", "json", file2],
|
|
30533
|
+
parse: parseEslintJson
|
|
30534
|
+
},
|
|
30535
|
+
{
|
|
30536
|
+
name: "ruff",
|
|
30537
|
+
bin: "ruff",
|
|
30538
|
+
extensions: [".py"],
|
|
30539
|
+
args: (file2) => ["check", "--output-format", "json", file2],
|
|
30540
|
+
parse: parseRuffJson
|
|
30541
|
+
}
|
|
30542
|
+
];
|
|
30543
|
+
function providerForFile(file2, providers = DEFAULT_PROVIDERS) {
|
|
30544
|
+
const ext = path15.extname(file2).toLowerCase();
|
|
30545
|
+
return providers.find((p3) => p3.extensions.includes(ext)) ?? null;
|
|
30546
|
+
}
|
|
30547
|
+
function resolveBin(bin, cwd) {
|
|
30548
|
+
const suffixes = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
|
|
30549
|
+
let dir = cwd;
|
|
30550
|
+
for (let i = 0; i < 6; i += 1) {
|
|
30551
|
+
for (const suffix of suffixes) {
|
|
30552
|
+
const candidate = path15.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
|
|
30553
|
+
if (existsSync7(candidate)) return candidate;
|
|
30554
|
+
}
|
|
30555
|
+
const parent = path15.dirname(dir);
|
|
30556
|
+
if (parent === dir) break;
|
|
30557
|
+
dir = parent;
|
|
30558
|
+
}
|
|
30559
|
+
return bin;
|
|
30560
|
+
}
|
|
30561
|
+
var defaultRunner = (cmd, args, opts) => new Promise((resolve) => {
|
|
30562
|
+
let stdout = "";
|
|
30563
|
+
let stderr = "";
|
|
30564
|
+
let settled = false;
|
|
30565
|
+
const done = (r) => {
|
|
30566
|
+
if (settled) return;
|
|
30567
|
+
settled = true;
|
|
30568
|
+
resolve(r);
|
|
30569
|
+
};
|
|
30570
|
+
let child;
|
|
30571
|
+
try {
|
|
30572
|
+
child = process.platform === "win32" ? spawn2(`${cmd} ${args.join(" ")}`, { cwd: opts.cwd, shell: true }) : spawn2(cmd, args, { cwd: opts.cwd });
|
|
30573
|
+
} catch {
|
|
30574
|
+
done({ code: null, stdout: "", stderr: "" });
|
|
30575
|
+
return;
|
|
30576
|
+
}
|
|
30577
|
+
const timer = setTimeout(() => {
|
|
30578
|
+
try {
|
|
30579
|
+
child.kill();
|
|
30580
|
+
} catch {
|
|
30581
|
+
}
|
|
30582
|
+
done({ code: null, stdout, stderr });
|
|
30583
|
+
}, opts.timeoutMs);
|
|
30584
|
+
child.stdout?.on("data", (c) => {
|
|
30585
|
+
stdout += c.toString();
|
|
30586
|
+
});
|
|
30587
|
+
child.stderr?.on("data", (c) => {
|
|
30588
|
+
stderr += c.toString();
|
|
30589
|
+
});
|
|
30590
|
+
child.on("error", () => {
|
|
30591
|
+
clearTimeout(timer);
|
|
30592
|
+
done({ code: null, stdout: "", stderr: "" });
|
|
30593
|
+
});
|
|
30594
|
+
child.on("close", (code) => {
|
|
30595
|
+
clearTimeout(timer);
|
|
30596
|
+
done({ code, stdout, stderr });
|
|
30597
|
+
});
|
|
30598
|
+
});
|
|
30599
|
+
async function runDiagnosticsForFile(file2, options = {}) {
|
|
30600
|
+
const provider = providerForFile(file2, options.providers);
|
|
30601
|
+
if (!provider) return [];
|
|
30602
|
+
const cwd = options.cwd ?? process.cwd();
|
|
30603
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
30604
|
+
const runner = options.runner ?? defaultRunner;
|
|
30605
|
+
try {
|
|
30606
|
+
const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
|
|
30607
|
+
const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
|
|
30608
|
+
return provider.parse(result.stdout, file2);
|
|
30609
|
+
} catch {
|
|
30610
|
+
return [];
|
|
30611
|
+
}
|
|
30612
|
+
}
|
|
30613
|
+
function formatDiagnostics(diagnostics, opts = {}) {
|
|
30614
|
+
if (diagnostics.length === 0) return "";
|
|
30615
|
+
const maxLines = opts.maxLines ?? 20;
|
|
30616
|
+
const rank = { error: 0, warning: 1, info: 2 };
|
|
30617
|
+
const sorted = [...diagnostics].sort(
|
|
30618
|
+
(a, b) => rank[a.severity] - rank[b.severity] || a.line - b.line
|
|
30619
|
+
);
|
|
30620
|
+
const errors = sorted.filter((d) => d.severity === "error").length;
|
|
30621
|
+
const warnings = sorted.filter((d) => d.severity === "warning").length;
|
|
30622
|
+
const header = `\u26A0 ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"} (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) \u2014 fix before continuing:`;
|
|
30623
|
+
const shown = sorted.slice(0, maxLines).map((d) => {
|
|
30624
|
+
const loc = opts.relativeTo ? relative(opts.relativeTo, d.file) : d.file;
|
|
30625
|
+
const pos = d.column ? `${d.line}:${d.column}` : `${d.line}`;
|
|
30626
|
+
const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
|
|
30627
|
+
const code = d.code ? ` [${d.code}]` : "";
|
|
30628
|
+
return ` ${loc}:${pos} ${tag}${code}: ${d.message} (${d.source})`;
|
|
30629
|
+
});
|
|
30630
|
+
const overflow = sorted.length > maxLines ? [` \u2026 and ${sorted.length - maxLines} more`] : [];
|
|
30631
|
+
return [header, ...shown, ...overflow].join("\n");
|
|
30632
|
+
}
|
|
30633
|
+
function relative(from, to) {
|
|
30634
|
+
try {
|
|
30635
|
+
const rel2 = path15.relative(from, to);
|
|
30636
|
+
return rel2 && !rel2.startsWith("..") ? rel2 : to;
|
|
30637
|
+
} catch {
|
|
30638
|
+
return to;
|
|
30639
|
+
}
|
|
30640
|
+
}
|
|
30641
|
+
|
|
30642
|
+
// src/cli/tools/taskTool.ts
|
|
30643
|
+
init_zod();
|
|
30644
|
+
init_toolTypes();
|
|
30645
|
+
var SUBAGENT_SYSTEM_PROMPT = [
|
|
30646
|
+
"You are a focused sub-agent spawned to answer ONE bounded question or",
|
|
30647
|
+
"complete ONE small research task for a parent coding agent.",
|
|
30648
|
+
"",
|
|
30649
|
+
"You have READ-ONLY tools (read files, list files, search, fetch URLs).",
|
|
30650
|
+
"You cannot edit files or run shell commands \u2014 do not attempt to.",
|
|
30651
|
+
"",
|
|
30652
|
+
"Work efficiently: gather only what you need, then STOP and reply with a",
|
|
30653
|
+
"concise, self-contained conclusion the parent can act on. Include concrete",
|
|
30654
|
+
"references (file paths, line numbers, symbol names) but do not paste large",
|
|
30655
|
+
"file dumps. Do not ask the parent follow-up questions \u2014 deliver your best",
|
|
30656
|
+
"answer with what you can find."
|
|
30657
|
+
].join("\n");
|
|
30658
|
+
var TaskArgsSchema = external_exports.object({
|
|
30659
|
+
description: external_exports.string().min(1).describe("A 3-6 word label for the sub-task (for logs/UI)."),
|
|
30660
|
+
prompt: external_exports.string().min(1).describe(
|
|
30661
|
+
"The full, self-contained instruction for the sub-agent. It has no access to this conversation, so include all context it needs."
|
|
30662
|
+
)
|
|
30663
|
+
});
|
|
30664
|
+
async function runSubAgent(harness) {
|
|
30665
|
+
let current = "";
|
|
30666
|
+
let lastCompleted = "";
|
|
30667
|
+
let error51;
|
|
30668
|
+
for await (const ev of harness.run()) {
|
|
30669
|
+
switch (ev.type) {
|
|
30670
|
+
case "message_start":
|
|
30671
|
+
current = "";
|
|
30672
|
+
break;
|
|
30673
|
+
case "message_delta":
|
|
30674
|
+
current += ev.delta;
|
|
30675
|
+
break;
|
|
30676
|
+
case "message_end":
|
|
30677
|
+
if (current.trim()) lastCompleted = current;
|
|
30678
|
+
current = "";
|
|
30679
|
+
break;
|
|
30680
|
+
case "error":
|
|
30681
|
+
error51 = ev.message;
|
|
30682
|
+
break;
|
|
30683
|
+
default:
|
|
30684
|
+
break;
|
|
30685
|
+
}
|
|
30686
|
+
}
|
|
30687
|
+
const result = (lastCompleted || current).trim();
|
|
30688
|
+
return { result, ...error51 ? { error: error51 } : {} };
|
|
30689
|
+
}
|
|
30690
|
+
function createTaskTool(deps) {
|
|
30691
|
+
return {
|
|
30692
|
+
name: "task",
|
|
30693
|
+
description: 'Delegate a focused, READ-ONLY research/exploration sub-task to an isolated sub-agent that has its own fresh context and returns only a concise conclusion. Use it to keep your own context lean \u2014 e.g. "find where feature X is implemented and summarize how it works" \u2014 on large codebases. The sub-agent cannot edit files or run shell commands. Provide a fully self-contained `prompt` (it cannot see this conversation).',
|
|
30694
|
+
// The sub-agent reads the workspace and may hit the network (fetch/search),
|
|
30695
|
+
// but never writes or runs shell commands.
|
|
30696
|
+
permissions: ["read", "network"],
|
|
30697
|
+
// Sub-agents run a full (bounded) agent loop — allow generous wall time.
|
|
30698
|
+
timeoutMs: 3e5,
|
|
30699
|
+
inputSchema: TaskArgsSchema,
|
|
30700
|
+
execute: async (args, ctx) => {
|
|
30701
|
+
let sub;
|
|
30702
|
+
try {
|
|
30703
|
+
sub = await deps.createSubAgentContext();
|
|
30704
|
+
} catch (err) {
|
|
30705
|
+
return typedErr(`task: could not initialize sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
30706
|
+
}
|
|
30707
|
+
if (!sub) {
|
|
30708
|
+
return typedErr("task: no provider configured for the sub-agent (set an API key / run /login).");
|
|
30709
|
+
}
|
|
30710
|
+
const config2 = {
|
|
30711
|
+
model: sub.model,
|
|
30712
|
+
provider: sub.provider,
|
|
30713
|
+
messages: [
|
|
30714
|
+
{ role: "system", content: SUBAGENT_SYSTEM_PROMPT },
|
|
30715
|
+
{ role: "user", content: args.prompt }
|
|
30716
|
+
],
|
|
30717
|
+
tools: sub.tools,
|
|
30718
|
+
toolRegistry: sub.registry,
|
|
30719
|
+
providerStream: sub.providerStream,
|
|
30720
|
+
cwd: ctx.cwd,
|
|
30721
|
+
// Guard against a single turn ballooning context on "explore" tasks.
|
|
30722
|
+
maxToolCallsPerTurn: 5
|
|
30723
|
+
};
|
|
30724
|
+
let harness;
|
|
30725
|
+
try {
|
|
30726
|
+
if (deps.harnessFactory) {
|
|
30727
|
+
harness = deps.harnessFactory(config2);
|
|
30728
|
+
} else {
|
|
30729
|
+
const { AgentHarness: AgentHarness2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
|
|
30730
|
+
harness = new AgentHarness2(config2);
|
|
30731
|
+
}
|
|
30732
|
+
} catch (err) {
|
|
30733
|
+
return typedErr(`task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
30734
|
+
}
|
|
30735
|
+
const { result, error: error51 } = await runSubAgent(harness);
|
|
30736
|
+
if (!result) {
|
|
30737
|
+
return typedErr(`task: sub-agent produced no output${error51 ? ` (${error51})` : ""}.`);
|
|
30738
|
+
}
|
|
30739
|
+
return typedOk({ result });
|
|
30740
|
+
}
|
|
30741
|
+
};
|
|
30742
|
+
}
|
|
30743
|
+
|
|
30085
30744
|
// src/cli/toolRegistry.ts
|
|
30745
|
+
init_openai_compatible();
|
|
30086
30746
|
function createBuiltinToolRegistry(options = {}) {
|
|
30087
30747
|
const root = options.root ?? process.cwd();
|
|
30088
30748
|
const audit = options.audit ?? new AuditLogger();
|
|
30089
30749
|
const sessionId = options.sessionId ?? "cli";
|
|
30750
|
+
const diagnosticsOn = options.diagnostics ?? process.env.ZELARI_DIAGNOSTICS !== "0";
|
|
30751
|
+
const withDiag = (t) => diagnosticsOn ? wrapWithDiagnostics(t, root, options.diagnosticsRunner) : t;
|
|
30090
30752
|
const safeReadFile = wrapWithSandbox(readFileTool, ["path"], root, audit, sessionId);
|
|
30091
|
-
const safeWriteFile = wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId);
|
|
30092
|
-
const safeEditFile = wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId);
|
|
30753
|
+
const safeWriteFile = withDiag(wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId));
|
|
30754
|
+
const safeEditFile = withDiag(wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId));
|
|
30093
30755
|
const safeGrepContent = wrapWithSandbox(grepContentTool, ["path"], root, audit, sessionId);
|
|
30094
30756
|
const safeListFiles = wrapWithSandbox(listFilesTool, ["path"], root, audit, sessionId);
|
|
30095
30757
|
const safeShowDiff = wrapWithSandbox(showDiffTool, ["path"], root, audit, sessionId);
|
|
30096
|
-
const safeApplyDiff = wrapWithSandbox(applyDiffTool, ["path"], root, audit, sessionId);
|
|
30758
|
+
const safeApplyDiff = withDiag(wrapWithSandbox(applyDiffTool, ["path"], root, audit, sessionId));
|
|
30097
30759
|
const safeBash = wrapWithShellSafety(bashTool, audit, sessionId);
|
|
30098
30760
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
30099
30761
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
30100
30762
|
const registry3 = new ToolRegistry();
|
|
30763
|
+
const readOnly = options.readOnly === true;
|
|
30101
30764
|
registry3.register(safeReadFile);
|
|
30102
|
-
registry3.register(safeWriteFile);
|
|
30103
|
-
registry3.register(safeEditFile);
|
|
30104
|
-
registry3.register(safeBash);
|
|
30105
30765
|
registry3.register(safeGrepContent);
|
|
30106
30766
|
registry3.register(safeListFiles);
|
|
30107
30767
|
registry3.register(safeShowDiff);
|
|
30108
|
-
registry3.register(safeApplyDiff);
|
|
30109
30768
|
registry3.register(safeFetchUrl);
|
|
30110
30769
|
registry3.register(safeWebSearch);
|
|
30111
|
-
|
|
30770
|
+
if (!readOnly) {
|
|
30771
|
+
registry3.register(safeWriteFile);
|
|
30772
|
+
registry3.register(safeEditFile);
|
|
30773
|
+
registry3.register(safeBash);
|
|
30774
|
+
registry3.register(safeApplyDiff);
|
|
30775
|
+
}
|
|
30776
|
+
const summary = readOnly ? [safeReadFile, safeGrepContent, safeListFiles, safeShowDiff, safeFetchUrl, safeWebSearch] : [
|
|
30112
30777
|
safeReadFile,
|
|
30113
30778
|
safeWriteFile,
|
|
30114
30779
|
safeEditFile,
|
|
@@ -30119,11 +30784,44 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
30119
30784
|
safeApplyDiff,
|
|
30120
30785
|
safeFetchUrl,
|
|
30121
30786
|
safeWebSearch
|
|
30122
|
-
]
|
|
30787
|
+
];
|
|
30788
|
+
const tools = summary.map((t) => ({
|
|
30123
30789
|
name: t.name,
|
|
30124
30790
|
description: t.description,
|
|
30125
30791
|
permissions: t.permissions ?? []
|
|
30126
30792
|
}));
|
|
30793
|
+
if (!readOnly && options.enableTask !== false) {
|
|
30794
|
+
const taskTool = createTaskTool({
|
|
30795
|
+
createSubAgentContext: async () => {
|
|
30796
|
+
const cfg = await providerFromEnv();
|
|
30797
|
+
if (!cfg) return null;
|
|
30798
|
+
const { registry: subRegistry } = createBuiltinToolRegistry({
|
|
30799
|
+
root,
|
|
30800
|
+
audit,
|
|
30801
|
+
sessionId,
|
|
30802
|
+
readOnly: true,
|
|
30803
|
+
diagnostics: false
|
|
30804
|
+
});
|
|
30805
|
+
return {
|
|
30806
|
+
providerStream: openaiCompatibleProvider(cfg),
|
|
30807
|
+
model: cfg.model,
|
|
30808
|
+
provider: "openai-compatible",
|
|
30809
|
+
registry: subRegistry,
|
|
30810
|
+
tools: subRegistry.toOpenAITools().map((t) => ({
|
|
30811
|
+
name: t.function.name,
|
|
30812
|
+
description: t.function.description,
|
|
30813
|
+
parameters: t.function.parameters
|
|
30814
|
+
}))
|
|
30815
|
+
};
|
|
30816
|
+
}
|
|
30817
|
+
});
|
|
30818
|
+
registry3.register(taskTool);
|
|
30819
|
+
tools.push({
|
|
30820
|
+
name: taskTool.name,
|
|
30821
|
+
description: taskTool.description,
|
|
30822
|
+
permissions: taskTool.permissions ?? []
|
|
30823
|
+
});
|
|
30824
|
+
}
|
|
30127
30825
|
return { registry: registry3, tools };
|
|
30128
30826
|
}
|
|
30129
30827
|
function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
@@ -30173,6 +30871,36 @@ function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
|
30173
30871
|
}
|
|
30174
30872
|
};
|
|
30175
30873
|
}
|
|
30874
|
+
function wrapWithDiagnostics(original, root, runner) {
|
|
30875
|
+
return {
|
|
30876
|
+
...original,
|
|
30877
|
+
execute: async (rawArgs, ctx) => {
|
|
30878
|
+
const result = await original.execute(rawArgs, ctx);
|
|
30879
|
+
if (!result.ok) return result;
|
|
30880
|
+
if (rawArgs.dryRun === true) return result;
|
|
30881
|
+
const value = result.value;
|
|
30882
|
+
const filePath = value && typeof value === "object" && typeof value.path === "string" ? value.path : void 0;
|
|
30883
|
+
if (!filePath) return result;
|
|
30884
|
+
try {
|
|
30885
|
+
const timeoutMs = Number(process.env.ZELARI_DIAGNOSTICS_TIMEOUT_MS) || 5e3;
|
|
30886
|
+
const diags = await runDiagnosticsForFile(filePath, {
|
|
30887
|
+
cwd: root,
|
|
30888
|
+
timeoutMs,
|
|
30889
|
+
...runner ? { runner } : {}
|
|
30890
|
+
});
|
|
30891
|
+
const formatted = formatDiagnostics(diags, { relativeTo: root });
|
|
30892
|
+
if (formatted) {
|
|
30893
|
+
return {
|
|
30894
|
+
ok: true,
|
|
30895
|
+
value: { ...value, diagnostics: formatted }
|
|
30896
|
+
};
|
|
30897
|
+
}
|
|
30898
|
+
} catch {
|
|
30899
|
+
}
|
|
30900
|
+
return result;
|
|
30901
|
+
}
|
|
30902
|
+
};
|
|
30903
|
+
}
|
|
30176
30904
|
function wrapWithAudit(original, audit, sessionId) {
|
|
30177
30905
|
return {
|
|
30178
30906
|
...original,
|
|
@@ -30337,69 +31065,16 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
30337
31065
|
});
|
|
30338
31066
|
}
|
|
30339
31067
|
|
|
30340
|
-
// src/cli/modelPricing.ts
|
|
30341
|
-
var PRICES_PER_MILLION = {
|
|
30342
|
-
// xAI Grok
|
|
30343
|
-
"grok-4": { input: 3, output: 15 },
|
|
30344
|
-
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
30345
|
-
"grok-3": { input: 3, output: 15 },
|
|
30346
|
-
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
30347
|
-
"grok-2-vision": { input: 2, output: 10 },
|
|
30348
|
-
// GLM / Z.AI
|
|
30349
|
-
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
30350
|
-
"glm-4.5": { input: 0.5, output: 2 },
|
|
30351
|
-
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
30352
|
-
"glm-z1": { input: 0.5, output: 2 },
|
|
30353
|
-
// MiniMax
|
|
30354
|
-
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
30355
|
-
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
30356
|
-
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
30357
|
-
// DeepSeek (global platform) — estimated list prices; override via
|
|
30358
|
-
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
30359
|
-
"deepseek-v4-flash": { input: 0.14, output: 0.28 },
|
|
30360
|
-
"deepseek-v4-pro": { input: 0.55, output: 2.19 },
|
|
30361
|
-
// OpenAI (for openai-compatible fallback)
|
|
30362
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
30363
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
30364
|
-
"gpt-4-turbo": { input: 10, output: 30 },
|
|
30365
|
-
"o1-preview": { input: 15, output: 60 },
|
|
30366
|
-
"o1-mini": { input: 3, output: 12 }
|
|
30367
|
-
};
|
|
30368
|
-
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
30369
|
-
function envOverride(model) {
|
|
30370
|
-
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
30371
|
-
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
30372
|
-
return parseRateOverride(raw);
|
|
30373
|
-
}
|
|
30374
|
-
function parseRateOverride(raw) {
|
|
30375
|
-
if (!raw) return null;
|
|
30376
|
-
const [inStr, outStr] = raw.split("/");
|
|
30377
|
-
const input = Number.parseFloat(inStr);
|
|
30378
|
-
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
30379
|
-
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
30380
|
-
return { input, output };
|
|
30381
|
-
}
|
|
30382
|
-
function getModelRate(model) {
|
|
30383
|
-
if (!model) return DEFAULT_RATE;
|
|
30384
|
-
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
30385
|
-
}
|
|
30386
|
-
function calculateCost(model, promptTokens, completionTokens) {
|
|
30387
|
-
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
30388
|
-
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
30389
|
-
const rate = getModelRate(model);
|
|
30390
|
-
const inputCost = promptTokens / 1e6 * rate.input;
|
|
30391
|
-
const outputCost = completionTokens / 1e6 * rate.output;
|
|
30392
|
-
return Number((inputCost + outputCost).toFixed(6));
|
|
30393
|
-
}
|
|
30394
|
-
|
|
30395
31068
|
// src/cli/hooks/chatStats.ts
|
|
30396
31069
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
|
|
30397
31070
|
const promptTokens = realUsage ? realUsage.promptTokens : Math.ceil(userText.length / 4);
|
|
30398
31071
|
const completionTokens = realUsage ? realUsage.completionTokens : Math.ceil(assistantContent.length / 4);
|
|
30399
|
-
const
|
|
31072
|
+
const cachedPromptTokens = realUsage?.cachedPromptTokens ?? 0;
|
|
31073
|
+
const turnCost = calculateCost(model, promptTokens, completionTokens, cachedPromptTokens);
|
|
30400
31074
|
return {
|
|
30401
31075
|
totalTokens: prev2.totalTokens + promptTokens + completionTokens,
|
|
30402
|
-
totalCostUsd: prev2.totalCostUsd + turnCost
|
|
31076
|
+
totalCostUsd: prev2.totalCostUsd + turnCost,
|
|
31077
|
+
cachedTokens: (prev2.cachedTokens ?? 0) + cachedPromptTokens
|
|
30403
31078
|
};
|
|
30404
31079
|
}
|
|
30405
31080
|
|
|
@@ -31344,6 +32019,8 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
31344
32019
|
/new \u2014 start a fresh session
|
|
31345
32020
|
/diff [--staged] \u2014 show uncommitted changes (or staged with --staged)
|
|
31346
32021
|
/undo [--yes] \u2014 revert working-tree changes (destructive! requires --yes)
|
|
32022
|
+
/checkpoint [label] \u2014 snapshot the working tree as a restore point
|
|
32023
|
+
/rollback [id|latest] \u2014 restore the working tree to a checkpoint (no arg: list)
|
|
31347
32024
|
/help \u2014 show this help
|
|
31348
32025
|
/exit \u2014 exit the CLI
|
|
31349
32026
|
|
|
@@ -31681,6 +32358,25 @@ ${formatSkillList(availableSkills)}`
|
|
|
31681
32358
|
message: "\u26A0 /undo is DESTRUCTIVE \u2014 it reverts all unstaged modifications and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm."
|
|
31682
32359
|
};
|
|
31683
32360
|
}
|
|
32361
|
+
case "checkpoint": {
|
|
32362
|
+
const label = args.join(" ").trim();
|
|
32363
|
+
return {
|
|
32364
|
+
handled: true,
|
|
32365
|
+
kind: "checkpoint_create",
|
|
32366
|
+
...label ? { checkpointLabel: label } : {}
|
|
32367
|
+
};
|
|
32368
|
+
}
|
|
32369
|
+
case "rollback": {
|
|
32370
|
+
const target = args[0]?.trim();
|
|
32371
|
+
if (!target) {
|
|
32372
|
+
return { handled: true, kind: "rollback_list" };
|
|
32373
|
+
}
|
|
32374
|
+
return {
|
|
32375
|
+
handled: true,
|
|
32376
|
+
kind: "rollback",
|
|
32377
|
+
...target === "latest" ? {} : { rollbackId: target }
|
|
32378
|
+
};
|
|
32379
|
+
}
|
|
31684
32380
|
case "workspace": {
|
|
31685
32381
|
const sub = args[0];
|
|
31686
32382
|
if (!sub || sub === "--help" || sub === "help") {
|
|
@@ -31744,13 +32440,13 @@ ${formatSkillList(availableSkills)}`
|
|
|
31744
32440
|
}
|
|
31745
32441
|
|
|
31746
32442
|
// src/cli/gitOps.ts
|
|
31747
|
-
import { execFile as
|
|
31748
|
-
import { promisify } from "node:util";
|
|
31749
|
-
import
|
|
31750
|
-
var
|
|
31751
|
-
async function
|
|
32443
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
32444
|
+
import { promisify as promisify2 } from "node:util";
|
|
32445
|
+
import path21 from "node:path";
|
|
32446
|
+
var execFileAsync2 = promisify2(execFile3);
|
|
32447
|
+
async function git2(cwd, args) {
|
|
31752
32448
|
try {
|
|
31753
|
-
const { stdout } = await
|
|
32449
|
+
const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
|
|
31754
32450
|
maxBuffer: 16 * 1024 * 1024
|
|
31755
32451
|
});
|
|
31756
32452
|
return stdout;
|
|
@@ -31758,15 +32454,15 @@ async function git(cwd, args) {
|
|
|
31758
32454
|
return null;
|
|
31759
32455
|
}
|
|
31760
32456
|
}
|
|
31761
|
-
async function
|
|
31762
|
-
const out = await
|
|
32457
|
+
async function isGitRepo2(cwd = process.cwd()) {
|
|
32458
|
+
const out = await git2(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
31763
32459
|
return out?.trim() === "true";
|
|
31764
32460
|
}
|
|
31765
32461
|
async function getWorkingDiff(opts = {}) {
|
|
31766
32462
|
const cwd = opts.cwd ?? process.cwd();
|
|
31767
32463
|
const maxChars = opts.maxChars ?? 5e4;
|
|
31768
|
-
const unstaged = await
|
|
31769
|
-
const staged = opts.staged ? await
|
|
32464
|
+
const unstaged = await git2(cwd, ["diff"]) ?? "";
|
|
32465
|
+
const staged = opts.staged ? await git2(cwd, ["diff", "--cached"]) ?? "" : "";
|
|
31770
32466
|
const combined = [staged, unstaged].filter((s) => s.length > 0).join("\n");
|
|
31771
32467
|
if (combined.length === 0) {
|
|
31772
32468
|
return { diff: "", truncated: false, empty: true };
|
|
@@ -31779,11 +32475,11 @@ async function getWorkingDiff(opts = {}) {
|
|
|
31779
32475
|
async function undoWorkingChanges(opts = {}) {
|
|
31780
32476
|
const cwd = opts.cwd ?? process.cwd();
|
|
31781
32477
|
const unstage = opts.unstage !== false;
|
|
31782
|
-
const statusBefore = await
|
|
32478
|
+
const statusBefore = await git2(cwd, ["status", "--porcelain"]) ?? "";
|
|
31783
32479
|
const modified = statusBefore.split("\n").filter((line) => line.length > 0).filter((line) => !line.startsWith("??") && !line.startsWith("!!")).map((line) => line.slice(3).trim());
|
|
31784
|
-
await
|
|
32480
|
+
await git2(cwd, ["checkout", "--", "."]);
|
|
31785
32481
|
if (unstage) {
|
|
31786
|
-
await
|
|
32482
|
+
await git2(cwd, ["reset", "HEAD", "--", "."]);
|
|
31787
32483
|
}
|
|
31788
32484
|
return {
|
|
31789
32485
|
reverted: modified,
|
|
@@ -31792,14 +32488,14 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
31792
32488
|
};
|
|
31793
32489
|
}
|
|
31794
32490
|
function defaultProjectRoot() {
|
|
31795
|
-
return
|
|
32491
|
+
return path21.resolve(__dirname, "..", "..", "..");
|
|
31796
32492
|
}
|
|
31797
32493
|
|
|
31798
32494
|
// src/cli/slashHandlers/git.ts
|
|
31799
32495
|
async function handleDiff(ctx, diffStaged) {
|
|
31800
32496
|
try {
|
|
31801
32497
|
const repoRoot = defaultProjectRoot();
|
|
31802
|
-
if (!await
|
|
32498
|
+
if (!await isGitRepo2(repoRoot)) {
|
|
31803
32499
|
appendSystem(ctx.setMessages, "[diff] not a git repository \u2014 nothing to show");
|
|
31804
32500
|
return;
|
|
31805
32501
|
}
|
|
@@ -31820,7 +32516,7 @@ async function handleUndo(ctx, warningMessage, doConfirm) {
|
|
|
31820
32516
|
if (!doConfirm) return;
|
|
31821
32517
|
try {
|
|
31822
32518
|
const repoRoot = defaultProjectRoot();
|
|
31823
|
-
if (!await
|
|
32519
|
+
if (!await isGitRepo2(repoRoot)) {
|
|
31824
32520
|
appendSystem(ctx.setMessages, "[undo] not a git repository \u2014 nothing to revert");
|
|
31825
32521
|
return;
|
|
31826
32522
|
}
|
|
@@ -31836,6 +32532,57 @@ async function handleUndo(ctx, warningMessage, doConfirm) {
|
|
|
31836
32532
|
}
|
|
31837
32533
|
}
|
|
31838
32534
|
|
|
32535
|
+
// src/cli/slashHandlers/checkpoint.ts
|
|
32536
|
+
init_checkpointManager();
|
|
32537
|
+
function ago(ms) {
|
|
32538
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1e3));
|
|
32539
|
+
if (s < 60) return `${s}s ago`;
|
|
32540
|
+
if (s < 3600) return `${Math.round(s / 60)}m ago`;
|
|
32541
|
+
return `${Math.round(s / 3600)}h ago`;
|
|
32542
|
+
}
|
|
32543
|
+
async function handleCheckpointCreate(ctx, label) {
|
|
32544
|
+
const res = await createCheckpoint(ctx.cwd, label ?? "manual checkpoint");
|
|
32545
|
+
if (res.ok) {
|
|
32546
|
+
appendSystem(
|
|
32547
|
+
ctx.setMessages,
|
|
32548
|
+
`[checkpoint] \u2713 ${res.value.id} created${label ? ` (\u201C${label}\u201D)` : ""} \u2014 restore with \`/rollback ${res.value.id}\``
|
|
32549
|
+
);
|
|
32550
|
+
} else {
|
|
32551
|
+
appendSystem(ctx.setMessages, `[checkpoint] \u2717 ${res.error}`);
|
|
32552
|
+
}
|
|
32553
|
+
}
|
|
32554
|
+
async function handleRollbackList(ctx) {
|
|
32555
|
+
const list = await listCheckpoints(ctx.cwd);
|
|
32556
|
+
if (list.length === 0) {
|
|
32557
|
+
appendSystem(
|
|
32558
|
+
ctx.setMessages,
|
|
32559
|
+
"[rollback] no checkpoints yet. Create one with `/checkpoint [label]` (Zelari missions create one automatically)."
|
|
32560
|
+
);
|
|
32561
|
+
return;
|
|
32562
|
+
}
|
|
32563
|
+
const lines = list.map(
|
|
32564
|
+
(c, i) => ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago(c.createdAt)} ${c.label}`
|
|
32565
|
+
);
|
|
32566
|
+
appendSystem(
|
|
32567
|
+
ctx.setMessages,
|
|
32568
|
+
`[rollback] ${list.length} checkpoint${list.length === 1 ? "" : "s"} (newest first):
|
|
32569
|
+
${lines.join("\n")}
|
|
32570
|
+
Restore with \`/rollback <id>\` or \`/rollback latest\`.`
|
|
32571
|
+
);
|
|
32572
|
+
}
|
|
32573
|
+
async function handleRollback(ctx, id) {
|
|
32574
|
+
const res = await restoreCheckpoint(ctx.cwd, id);
|
|
32575
|
+
if (res.ok) {
|
|
32576
|
+
const removed = res.value.deleted.length > 0 ? ` (removed ${res.value.deleted.length} file${res.value.deleted.length === 1 ? "" : "s"} created after the checkpoint)` : "";
|
|
32577
|
+
appendSystem(
|
|
32578
|
+
ctx.setMessages,
|
|
32579
|
+
`[rollback] \u2713 working tree restored to checkpoint ${res.value.id}${removed}.`
|
|
32580
|
+
);
|
|
32581
|
+
} else {
|
|
32582
|
+
appendSystem(ctx.setMessages, `[rollback] \u2717 ${res.error}`);
|
|
32583
|
+
}
|
|
32584
|
+
}
|
|
32585
|
+
|
|
31839
32586
|
// src/cli/compaction.ts
|
|
31840
32587
|
function compactTranscript(messages, options = {}) {
|
|
31841
32588
|
const threshold = options.threshold ?? 50;
|
|
@@ -31960,6 +32707,9 @@ ${output}`.toLowerCase();
|
|
|
31960
32707
|
if (haystack.includes("eacces") || haystack.includes("eperm")) {
|
|
31961
32708
|
return "\u{1F4A1} hint: permission denied. On macOS/Linux, avoid sudo with npm:\n https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally\n On Windows, re-run the terminal as Administrator.";
|
|
31962
32709
|
}
|
|
32710
|
+
if (haystack.includes("shim target not found") || haystack.includes("is not recognized") || exitCode === 127) {
|
|
32711
|
+
return '\u{1F4A1} hint: `npm` could not be launched \u2014 its bin shim is broken.\n "Shim target not found: npm.cmd" / exit 127 means a Node version\n manager (Volta, nvm-windows, fnm) has a stale npm shim, so npm\n itself won\'t run until you repair it:\n \u2022 Volta: volta install node (reinstalls node + npm shims)\n \u2022 nvm-windows: nvm use <version> (re-links node + npm)\n \u2022 fnm: fnm use --install-if-missing\n Then verify with `npm --version`, and re-run `/update --yes`.\n (zelari-code retried automatically via the npm bundled with your\n Node; if you still see this, that bundled npm was unavailable too.)';
|
|
32712
|
+
}
|
|
31963
32713
|
if (haystack.includes("enoent") && haystack.includes("npm")) {
|
|
31964
32714
|
return '\u{1F4A1} hint: `npm` was not found on PATH for the spawned shell.\n Re-open the terminal so it picks up your current PATH, or set:\n export PATH="$(npm prefix -g)/bin:$PATH" (POSIX)\n $env:Path = "$(npm prefix -g);$env:Path" (PowerShell)';
|
|
31965
32715
|
}
|
|
@@ -31971,15 +32721,15 @@ ${output}`.toLowerCase();
|
|
|
31971
32721
|
|
|
31972
32722
|
// src/cli/slashHandlers/promoteMember.ts
|
|
31973
32723
|
import { promises as fs15 } from "node:fs";
|
|
31974
|
-
import
|
|
32724
|
+
import path22 from "node:path";
|
|
31975
32725
|
import os8 from "node:os";
|
|
31976
32726
|
async function handlePromoteMember(ctx, memberId) {
|
|
31977
32727
|
try {
|
|
31978
32728
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
31979
32729
|
const { skill, markdown } = promoteMember2(memberId);
|
|
31980
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
32730
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path22.join(os8.homedir(), ".tmp", "zelari-code", "skills");
|
|
31981
32731
|
await fs15.mkdir(skillDir, { recursive: true });
|
|
31982
|
-
const filePath =
|
|
32732
|
+
const filePath = path22.join(skillDir, `${skill.id}.md`);
|
|
31983
32733
|
await fs15.writeFile(filePath, markdown, "utf8");
|
|
31984
32734
|
appendSystem(
|
|
31985
32735
|
ctx.setMessages,
|
|
@@ -31996,29 +32746,29 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
31996
32746
|
}
|
|
31997
32747
|
|
|
31998
32748
|
// src/cli/branchManager.ts
|
|
31999
|
-
import { promises as fs16, existsSync as
|
|
32000
|
-
import
|
|
32749
|
+
import { promises as fs16, existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
32750
|
+
import path23 from "node:path";
|
|
32001
32751
|
import os9 from "node:os";
|
|
32002
32752
|
var META_FILENAME = "meta.json";
|
|
32003
32753
|
var SESSIONS_SUBDIR = "sessions";
|
|
32004
32754
|
function getBranchesBaseDir() {
|
|
32005
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
32755
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path23.join(os9.homedir(), ".tmp", "zelari-code", "branches");
|
|
32006
32756
|
}
|
|
32007
32757
|
function getSessionsBaseDir() {
|
|
32008
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
32758
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path23.join(os9.homedir(), ".tmp", "zelari-code", "sessions");
|
|
32009
32759
|
}
|
|
32010
32760
|
function branchPathFor(name, baseDir) {
|
|
32011
|
-
return
|
|
32761
|
+
return path23.join(baseDir, name);
|
|
32012
32762
|
}
|
|
32013
32763
|
function metaPathFor(name, baseDir) {
|
|
32014
|
-
return
|
|
32764
|
+
return path23.join(baseDir, name, META_FILENAME);
|
|
32015
32765
|
}
|
|
32016
32766
|
function sessionsPathFor(name, baseDir) {
|
|
32017
|
-
return
|
|
32767
|
+
return path23.join(baseDir, name, SESSIONS_SUBDIR);
|
|
32018
32768
|
}
|
|
32019
32769
|
function readBranchMeta(name, baseDir) {
|
|
32020
32770
|
const metaPath = metaPathFor(name, baseDir);
|
|
32021
|
-
if (!
|
|
32771
|
+
if (!existsSync24(metaPath)) {
|
|
32022
32772
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
32023
32773
|
}
|
|
32024
32774
|
try {
|
|
@@ -32039,7 +32789,7 @@ function readBranchMeta(name, baseDir) {
|
|
|
32039
32789
|
}
|
|
32040
32790
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
32041
32791
|
const metaPath = metaPathFor(name, baseDir);
|
|
32042
|
-
mkdirSync10(
|
|
32792
|
+
mkdirSync10(path23.dirname(metaPath), { recursive: true });
|
|
32043
32793
|
writeFileSync15(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
32044
32794
|
}
|
|
32045
32795
|
async function countSessions(name, baseDir) {
|
|
@@ -32078,7 +32828,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
32078
32828
|
};
|
|
32079
32829
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
32080
32830
|
const bp = branchPathFor(name, baseDir);
|
|
32081
|
-
return
|
|
32831
|
+
return existsSync24(bp) && existsSync24(metaPathFor(name, baseDir));
|
|
32082
32832
|
}
|
|
32083
32833
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
32084
32834
|
if (!name || name.trim().length === 0) {
|
|
@@ -32090,14 +32840,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
32090
32840
|
if (branchExists(name, baseDir)) {
|
|
32091
32841
|
throw new BranchAlreadyExistsError(name);
|
|
32092
32842
|
}
|
|
32093
|
-
const sourcePath =
|
|
32094
|
-
if (!
|
|
32843
|
+
const sourcePath = path23.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
32844
|
+
if (!existsSync24(sourcePath)) {
|
|
32095
32845
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
32096
32846
|
}
|
|
32097
32847
|
const branchPath = branchPathFor(name, baseDir);
|
|
32098
32848
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
32099
32849
|
mkdirSync10(branchSessionsPath, { recursive: true });
|
|
32100
|
-
const destPath =
|
|
32850
|
+
const destPath = path23.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
32101
32851
|
await fs16.copyFile(sourcePath, destPath);
|
|
32102
32852
|
const meta3 = {
|
|
32103
32853
|
name,
|
|
@@ -32124,7 +32874,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
32124
32874
|
const results = [];
|
|
32125
32875
|
for (const entry of entries) {
|
|
32126
32876
|
const metaPath = metaPathFor(entry, baseDir);
|
|
32127
|
-
if (!
|
|
32877
|
+
if (!existsSync24(metaPath)) continue;
|
|
32128
32878
|
try {
|
|
32129
32879
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
32130
32880
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -32198,14 +32948,14 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
32198
32948
|
|
|
32199
32949
|
// src/cli/slashHandlers/workspace.ts
|
|
32200
32950
|
import { promises as fs17 } from "node:fs";
|
|
32201
|
-
import
|
|
32951
|
+
import path24 from "node:path";
|
|
32202
32952
|
async function handleWorkspaceShow(ctx, what) {
|
|
32203
32953
|
try {
|
|
32204
|
-
const zelari =
|
|
32954
|
+
const zelari = path24.join(process.cwd(), ".zelari");
|
|
32205
32955
|
let content;
|
|
32206
32956
|
switch (what) {
|
|
32207
32957
|
case "plan": {
|
|
32208
|
-
const planPath =
|
|
32958
|
+
const planPath = path24.join(zelari, "plan.md");
|
|
32209
32959
|
try {
|
|
32210
32960
|
content = await fs17.readFile(planPath, "utf-8");
|
|
32211
32961
|
} catch {
|
|
@@ -32214,7 +32964,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32214
32964
|
break;
|
|
32215
32965
|
}
|
|
32216
32966
|
case "decisions": {
|
|
32217
|
-
const decisionsDir =
|
|
32967
|
+
const decisionsDir = path24.join(zelari, "decisions");
|
|
32218
32968
|
try {
|
|
32219
32969
|
const files = (await fs17.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
32220
32970
|
if (files.length === 0) {
|
|
@@ -32224,7 +32974,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32224
32974
|
`];
|
|
32225
32975
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
32226
32976
|
for (const f of files) {
|
|
32227
|
-
const raw = await fs17.readFile(
|
|
32977
|
+
const raw = await fs17.readFile(path24.join(decisionsDir, f), "utf-8");
|
|
32228
32978
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
32229
32979
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
32230
32980
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -32237,7 +32987,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32237
32987
|
break;
|
|
32238
32988
|
}
|
|
32239
32989
|
case "risks": {
|
|
32240
|
-
const risksPath =
|
|
32990
|
+
const risksPath = path24.join(zelari, "risks.md");
|
|
32241
32991
|
try {
|
|
32242
32992
|
content = await fs17.readFile(risksPath, "utf-8");
|
|
32243
32993
|
} catch {
|
|
@@ -32246,7 +32996,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32246
32996
|
break;
|
|
32247
32997
|
}
|
|
32248
32998
|
case "agents": {
|
|
32249
|
-
const agentsPath =
|
|
32999
|
+
const agentsPath = path24.join(process.cwd(), "AGENTS.MD");
|
|
32250
33000
|
try {
|
|
32251
33001
|
content = await fs17.readFile(agentsPath, "utf-8");
|
|
32252
33002
|
} catch {
|
|
@@ -32255,7 +33005,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32255
33005
|
break;
|
|
32256
33006
|
}
|
|
32257
33007
|
case "docs": {
|
|
32258
|
-
const docsDir =
|
|
33008
|
+
const docsDir = path24.join(zelari, "docs");
|
|
32259
33009
|
try {
|
|
32260
33010
|
const files = (await fs17.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
32261
33011
|
content = files.length ? `# Docs (${files.length})
|
|
@@ -32297,7 +33047,7 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
32297
33047
|
return;
|
|
32298
33048
|
}
|
|
32299
33049
|
try {
|
|
32300
|
-
const target =
|
|
33050
|
+
const target = path24.join(process.cwd(), ".zelari");
|
|
32301
33051
|
await fs17.rm(target, { recursive: true, force: true });
|
|
32302
33052
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
32303
33053
|
} catch (err) {
|
|
@@ -32656,11 +33406,11 @@ function handleModelsRefresh(ctx) {
|
|
|
32656
33406
|
}
|
|
32657
33407
|
|
|
32658
33408
|
// src/cli/slashHandlers/skills.ts
|
|
32659
|
-
import
|
|
33409
|
+
import path25 from "node:path";
|
|
32660
33410
|
import os10 from "node:os";
|
|
32661
33411
|
|
|
32662
33412
|
// src/cli/skillHistory.ts
|
|
32663
|
-
import { promises as fs18, existsSync as
|
|
33413
|
+
import { promises as fs18, existsSync as existsSync25, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync11 } from "node:fs";
|
|
32664
33414
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
32665
33415
|
async function readSkillHistory(file2) {
|
|
32666
33416
|
let raw = "";
|
|
@@ -32758,7 +33508,7 @@ async function applySteerInterrupt(options) {
|
|
|
32758
33508
|
|
|
32759
33509
|
// src/cli/slashHandlers/skills.ts
|
|
32760
33510
|
async function handleSkillStats(ctx, skillId) {
|
|
32761
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
33511
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path25.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
32762
33512
|
try {
|
|
32763
33513
|
const records = await readSkillHistory(historyFile);
|
|
32764
33514
|
const stats = getSkillStats(records, skillId);
|
|
@@ -32774,7 +33524,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
32774
33524
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
32775
33525
|
return;
|
|
32776
33526
|
}
|
|
32777
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
33527
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path25.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
32778
33528
|
try {
|
|
32779
33529
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
32780
33530
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -33075,6 +33825,21 @@ function useSlashDispatch(params) {
|
|
|
33075
33825
|
setInput("");
|
|
33076
33826
|
return;
|
|
33077
33827
|
}
|
|
33828
|
+
if (result.kind === "checkpoint_create") {
|
|
33829
|
+
await handleCheckpointCreate({ ...baseCtx, cwd: process.cwd() }, result.checkpointLabel);
|
|
33830
|
+
setInput("");
|
|
33831
|
+
return;
|
|
33832
|
+
}
|
|
33833
|
+
if (result.kind === "rollback_list") {
|
|
33834
|
+
await handleRollbackList({ ...baseCtx, cwd: process.cwd() });
|
|
33835
|
+
setInput("");
|
|
33836
|
+
return;
|
|
33837
|
+
}
|
|
33838
|
+
if (result.kind === "rollback") {
|
|
33839
|
+
await handleRollback({ ...baseCtx, cwd: process.cwd() }, result.rollbackId);
|
|
33840
|
+
setInput("");
|
|
33841
|
+
return;
|
|
33842
|
+
}
|
|
33078
33843
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
33079
33844
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
33080
33845
|
setInput("");
|
|
@@ -33254,7 +34019,7 @@ function App() {
|
|
|
33254
34019
|
const [input, setInput] = useState8("");
|
|
33255
34020
|
const [busy, setBusy] = useState8(false);
|
|
33256
34021
|
const [providerConfig, setProviderConfig] = useState8(() => getProviderConfig());
|
|
33257
|
-
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0 });
|
|
34022
|
+
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0, cachedTokens: 0 });
|
|
33258
34023
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
33259
34024
|
const [mode, setMode] = useState8("agent");
|
|
33260
34025
|
const [picker, setPicker] = useState8(null);
|
|
@@ -33383,7 +34148,9 @@ cwd: ${cwd}
|
|
|
33383
34148
|
mode,
|
|
33384
34149
|
cwd,
|
|
33385
34150
|
elapsedMs: timer.elapsedMs,
|
|
33386
|
-
lastMs: timer.lastMs
|
|
34151
|
+
lastMs: timer.lastMs,
|
|
34152
|
+
costUsd: sessionStats.totalCostUsd,
|
|
34153
|
+
cachedTokens: sessionStats.cachedTokens
|
|
33387
34154
|
}
|
|
33388
34155
|
)), showSidebar && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
33389
34156
|
}
|
|
@@ -33563,9 +34330,9 @@ function SplashGate({
|
|
|
33563
34330
|
init_providerConfig();
|
|
33564
34331
|
|
|
33565
34332
|
// src/cli/wizard/firstRun.ts
|
|
33566
|
-
import { existsSync as
|
|
34333
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
33567
34334
|
function shouldRunWizard(input) {
|
|
33568
|
-
const exists = input.exists ??
|
|
34335
|
+
const exists = input.exists ?? existsSync26;
|
|
33569
34336
|
if (input.hasResetConfigFlag) {
|
|
33570
34337
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
33571
34338
|
}
|
|
@@ -33920,6 +34687,7 @@ function emitEvent(event) {
|
|
|
33920
34687
|
}
|
|
33921
34688
|
|
|
33922
34689
|
// src/cli/runHeadless.ts
|
|
34690
|
+
init_harness();
|
|
33923
34691
|
async function runHeadless(opts) {
|
|
33924
34692
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
33925
34693
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -34054,7 +34822,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
34054
34822
|
|
|
34055
34823
|
// src/cli/skillsMd.ts
|
|
34056
34824
|
init_skills2();
|
|
34057
|
-
import { existsSync as
|
|
34825
|
+
import { existsSync as existsSync27, readdirSync as readdirSync4, readFileSync as readFileSync22 } from "node:fs";
|
|
34058
34826
|
import { join as join23 } from "node:path";
|
|
34059
34827
|
import { homedir as homedir5 } from "node:os";
|
|
34060
34828
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -34132,7 +34900,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34132
34900
|
const summary = { loaded: [], skipped: [] };
|
|
34133
34901
|
const seen = new Set(options.existingIds ?? []);
|
|
34134
34902
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
34135
|
-
if (!
|
|
34903
|
+
if (!existsSync27(dir)) continue;
|
|
34136
34904
|
let entries;
|
|
34137
34905
|
try {
|
|
34138
34906
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -34141,7 +34909,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34141
34909
|
}
|
|
34142
34910
|
for (const entry of entries) {
|
|
34143
34911
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
34144
|
-
if (!
|
|
34912
|
+
if (!existsSync27(skillPath)) continue;
|
|
34145
34913
|
try {
|
|
34146
34914
|
const parsed = parseSkillMd(readFileSync22(skillPath, "utf8"), skillPath);
|
|
34147
34915
|
if (!parsed) {
|