zelari-code 1.1.1 → 1.3.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 +5 -3
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/ast/engine.js +126 -0
- package/dist/cli/ast/engine.js.map +1 -0
- package/dist/cli/ast/tools.js +71 -0
- package/dist/cli/ast/tools.js.map +1 -0
- package/dist/cli/browser/driver.js +125 -0
- package/dist/cli/browser/driver.js.map +1 -0
- package/dist/cli/browser/tools.js +71 -0
- package/dist/cli/browser/tools.js.map +1 -0
- 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 +50 -2
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/lsp/client.js +89 -0
- package/dist/cli/lsp/client.js.map +1 -0
- package/dist/cli/lsp/manager.js +287 -0
- package/dist/cli/lsp/manager.js.map +1 -0
- package/dist/cli/lsp/protocol.js +83 -0
- package/dist/cli/lsp/protocol.js.map +1 -0
- package/dist/cli/lsp/servers.js +80 -0
- package/dist/cli/lsp/servers.js.map +1 -0
- package/dist/cli/lsp/tools.js +125 -0
- package/dist/cli/lsp/tools.js.map +1 -0
- package/dist/cli/main.bundled.js +2442 -424
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/mode.js +32 -0
- package/dist/cli/mode.js.map +1 -0
- 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/semantic/embeddings.js +71 -0
- package/dist/cli/semantic/embeddings.js.map +1 -0
- package/dist/cli/semantic/index.js +147 -0
- package/dist/cli/semantic/index.js.map +1 -0
- package/dist/cli/semantic/provider.js +29 -0
- package/dist/cli/semantic/provider.js.map +1 -0
- package/dist/cli/semantic/store.js +71 -0
- package/dist/cli/semantic/store.js.map +1 -0
- package/dist/cli/semantic/tools.js +54 -0
- package/dist/cli/semantic/tools.js.map +1 -0
- package/dist/cli/slashCommands.js +45 -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/semantic.js +37 -0
- package/dist/cli/slashHandlers/semantic.js.map +1 -0
- package/dist/cli/toolRegistry.js +176 -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/zelariMission.js +10 -0
- package/dist/cli/zelariMission.js.map +1 -1
- package/package.json +3 -3
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: spawn8 } = 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 = spawn8(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, path33) {
|
|
1539
|
+
if (!path33)
|
|
1540
1540
|
return obj;
|
|
1541
|
-
return
|
|
1541
|
+
return path33.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(path33, 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(path33);
|
|
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, path33 = []) => {
|
|
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 }, [...path33, ...issue2.path]));
|
|
2095
2095
|
} else if (issue2.code === "invalid_key") {
|
|
2096
|
-
processError({ issues: issue2.issues }, [...
|
|
2096
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2097
2097
|
} else if (issue2.code === "invalid_element") {
|
|
2098
|
-
processError({ issues: issue2.issues }, [...
|
|
2098
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2099
2099
|
} else {
|
|
2100
|
-
const fullpath = [...
|
|
2100
|
+
const fullpath = [...path33, ...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, path33 = []) => {
|
|
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 }, [...path33, ...issue2.path]));
|
|
2132
2132
|
} else if (issue2.code === "invalid_key") {
|
|
2133
|
-
processError({ issues: issue2.issues }, [...
|
|
2133
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2134
2134
|
} else if (issue2.code === "invalid_element") {
|
|
2135
|
-
processError({ issues: issue2.issues }, [...
|
|
2135
|
+
processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
|
|
2136
2136
|
} else {
|
|
2137
|
-
const fullpath = [...
|
|
2137
|
+
const fullpath = [...path33, ...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 path33 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2170
|
+
for (const seg of path33) {
|
|
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 path33 = ref.slice(1).split("/").filter(Boolean);
|
|
15674
|
+
if (path33.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 (path33[0] === defsKey) {
|
|
15679
|
+
const key = path33[1];
|
|
15680
15680
|
if (!key || !ctx.defs[key]) {
|
|
15681
15681
|
throw new Error(`Reference not found: ${ref}`);
|
|
15682
15682
|
}
|
|
@@ -16836,7 +16836,7 @@ var init_walk = __esm({
|
|
|
16836
16836
|
// packages/core/dist/core/tools/builtin/search.js
|
|
16837
16837
|
import { promises as fs6 } from "node:fs";
|
|
16838
16838
|
import path7 from "node:path";
|
|
16839
|
-
async function searchFile(absPath,
|
|
16839
|
+
async function searchFile(absPath, relPath2, regex, contextLines, remainingSlots) {
|
|
16840
16840
|
let buf;
|
|
16841
16841
|
try {
|
|
16842
16842
|
buf = await fs6.readFile(absPath, "utf-8");
|
|
@@ -16856,7 +16856,7 @@ async function searchFile(absPath, relPath, regex, contextLines, remainingSlots)
|
|
|
16856
16856
|
const endAfter = Math.min(lines.length - 1, i + contextLines);
|
|
16857
16857
|
matches.push({
|
|
16858
16858
|
file: absPath,
|
|
16859
|
-
relPath,
|
|
16859
|
+
relPath: relPath2,
|
|
16860
16860
|
line: i + 1,
|
|
16861
16861
|
text: lines[i],
|
|
16862
16862
|
context: {
|
|
@@ -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 path33 = 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: path33,
|
|
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 "${path33}".`;
|
|
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 existsSync9, readFileSync as readFileSync7 } 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 (!existsSync9(abs)) {
|
|
19953
20133
|
results.push({
|
|
19954
20134
|
id: "synthesis.cite-invalid",
|
|
19955
20135
|
severity: "error",
|
|
@@ -19962,7 +20142,7 @@ function verifyCitations(projectRoot, synthesisText) {
|
|
|
19962
20142
|
});
|
|
19963
20143
|
continue;
|
|
19964
20144
|
}
|
|
19965
|
-
const lines =
|
|
20145
|
+
const lines = readFileSync7(abs, "utf8").split(/\r?\n/);
|
|
19966
20146
|
if (cite.line > lines.length) {
|
|
19967
20147
|
results.push({
|
|
19968
20148
|
id: "synthesis.cite-invalid",
|
|
@@ -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 existsSync10, readFileSync as readFileSync8, 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 path33 = join2(zelariRoot, "nfr-spec.json");
|
|
20412
|
+
if (!existsSync10(path33))
|
|
20233
20413
|
return null;
|
|
20234
20414
|
try {
|
|
20235
|
-
const raw = JSON.parse(
|
|
20415
|
+
const raw = JSON.parse(readFileSync8(path33, "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 (existsSync10(join2(projectRoot, rel2))) {
|
|
20247
20427
|
found.push(rel2);
|
|
20248
20428
|
}
|
|
20249
20429
|
}
|
|
20250
|
-
if (found.length === 0 &&
|
|
20430
|
+
if (found.length === 0 && existsSync10(join2(projectRoot, "index.html"))) {
|
|
20251
20431
|
return ["index.html"];
|
|
20252
20432
|
}
|
|
20253
20433
|
return found;
|
|
@@ -20303,17 +20483,17 @@ 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 (!existsSync10(planPath) || keywords.length === 0)
|
|
20307
20487
|
return [];
|
|
20308
20488
|
let plan;
|
|
20309
20489
|
try {
|
|
20310
|
-
plan = JSON.parse(
|
|
20490
|
+
plan = JSON.parse(readFileSync8(planPath, "utf8"));
|
|
20311
20491
|
} catch {
|
|
20312
20492
|
return [];
|
|
20313
20493
|
}
|
|
20314
20494
|
const milestoneText = (plan.milestones ?? []).map((m) => `${m.name ?? ""} ${m.description ?? ""}`).join(" ").toLowerCase();
|
|
20315
20495
|
const results = [];
|
|
20316
|
-
const targetContent = targets.map((t) =>
|
|
20496
|
+
const targetContent = targets.map((t) => readFileSync8(join2(projectRoot, t), "utf8").toLowerCase()).join("\n");
|
|
20317
20497
|
for (const kw of keywords) {
|
|
20318
20498
|
const low = kw.toLowerCase();
|
|
20319
20499
|
if (!milestoneText.includes(low))
|
|
@@ -20343,13 +20523,13 @@ 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 (!existsSync10(readmePath) || targets.length === 0)
|
|
20347
20527
|
return [];
|
|
20348
|
-
const readme =
|
|
20528
|
+
const readme = readFileSync8(readmePath, "utf8");
|
|
20349
20529
|
const htmlPath = join2(projectRoot, targets[0]);
|
|
20350
|
-
if (!
|
|
20530
|
+
if (!existsSync10(htmlPath))
|
|
20351
20531
|
return [];
|
|
20352
|
-
const html =
|
|
20532
|
+
const html = readFileSync8(htmlPath, "utf8");
|
|
20353
20533
|
const sectionCount = (html.match(/<section\s+id=/gi) ?? []).length;
|
|
20354
20534
|
const readmeSections = readme.match(/(\d+)\s+sezioni/i);
|
|
20355
20535
|
if (readmeSections) {
|
|
@@ -20387,7 +20567,7 @@ function runImplementationVerification(input) {
|
|
|
20387
20567
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
20388
20568
|
};
|
|
20389
20569
|
for (const rel2 of targets) {
|
|
20390
|
-
const html =
|
|
20570
|
+
const html = readFileSync8(join2(input.projectRoot, rel2), "utf8");
|
|
20391
20571
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
20392
20572
|
results.push({
|
|
20393
20573
|
id: "motion.keyframes",
|
|
@@ -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 existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
20477
20657
|
import { join as join3 } from "node:path";
|
|
20478
20658
|
function checkDeadHooksInHtml(html) {
|
|
20479
20659
|
const warnings = [];
|
|
@@ -20501,9 +20681,9 @@ function checkDeadHooksInHtml(html) {
|
|
|
20501
20681
|
}
|
|
20502
20682
|
return warnings;
|
|
20503
20683
|
}
|
|
20504
|
-
function runMicroVerificationOnFile(projectRoot,
|
|
20505
|
-
const abs = join3(projectRoot,
|
|
20506
|
-
if (!
|
|
20684
|
+
function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
|
|
20685
|
+
const abs = join3(projectRoot, relPath2);
|
|
20686
|
+
if (!existsSync11(abs) || !/\.html?$/i.test(relPath2))
|
|
20507
20687
|
return [];
|
|
20508
20688
|
const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
|
|
20509
20689
|
const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
|
|
@@ -20511,13 +20691,13 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
20511
20691
|
compositorOnly: anim.compositorOnly ?? true,
|
|
20512
20692
|
forbidLayoutProps: anim.forbidLayoutProps ?? true
|
|
20513
20693
|
};
|
|
20514
|
-
const html =
|
|
20694
|
+
const html = readFileSync9(abs, "utf8");
|
|
20515
20695
|
const warnings = [];
|
|
20516
20696
|
for (const v of scanKeyframesViolations(html, scanOpts)) {
|
|
20517
20697
|
warnings.push({
|
|
20518
20698
|
id: "motion.keyframes",
|
|
20519
20699
|
message: `@keyframes uses non-allowed property "${v.property}"`,
|
|
20520
|
-
file:
|
|
20700
|
+
file: relPath2,
|
|
20521
20701
|
line: v.line
|
|
20522
20702
|
});
|
|
20523
20703
|
}
|
|
@@ -20525,12 +20705,12 @@ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
|
|
|
20525
20705
|
warnings.push({
|
|
20526
20706
|
id: "motion.transitions",
|
|
20527
20707
|
message: `transition uses non-allowed property "${v.property}"`,
|
|
20528
|
-
file:
|
|
20708
|
+
file: relPath2,
|
|
20529
20709
|
line: v.line
|
|
20530
20710
|
});
|
|
20531
20711
|
}
|
|
20532
20712
|
for (const w of checkDeadHooksInHtml(html)) {
|
|
20533
|
-
warnings.push({ ...w, file:
|
|
20713
|
+
warnings.push({ ...w, file: relPath2 });
|
|
20534
20714
|
}
|
|
20535
20715
|
return warnings;
|
|
20536
20716
|
}
|
|
@@ -20867,7 +21047,7 @@ var init_implementationDelivery = __esm({
|
|
|
20867
21047
|
});
|
|
20868
21048
|
|
|
20869
21049
|
// packages/core/dist/council/verification/inlineJsAutofix.js
|
|
20870
|
-
import { readFileSync as
|
|
21050
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
20871
21051
|
import { join as join4 } from "node:path";
|
|
20872
21052
|
function minifyInlineJs(js) {
|
|
20873
21053
|
let out = js.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
@@ -20914,7 +21094,7 @@ function applyInlineJsAutofix(projectRoot, report) {
|
|
|
20914
21094
|
const abs = join4(projectRoot, rel2);
|
|
20915
21095
|
let html;
|
|
20916
21096
|
try {
|
|
20917
|
-
html =
|
|
21097
|
+
html = readFileSync10(abs, "utf8");
|
|
20918
21098
|
} catch {
|
|
20919
21099
|
continue;
|
|
20920
21100
|
}
|
|
@@ -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 existsSync12 } 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 (!existsSync12(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 (!existsSync12(join5(chairmanProjectRoot, rel2)))
|
|
21492
21672
|
continue;
|
|
21493
21673
|
changedTargetFiles.add(rel2);
|
|
21494
21674
|
for (const w of runChairmanMicroGate({
|
|
@@ -21859,8 +22039,8 @@ async function* runChairmanFixLoop(args) {
|
|
|
21859
22039
|
break;
|
|
21860
22040
|
}
|
|
21861
22041
|
const rescanned = /* @__PURE__ */ new Map();
|
|
21862
|
-
for (const
|
|
21863
|
-
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath, zelariRoot })) {
|
|
22042
|
+
for (const relPath2 of args.changedFiles) {
|
|
22043
|
+
for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath: relPath2, zelariRoot })) {
|
|
21864
22044
|
rescanned.set(`${w.id}|${w.file}|${w.line ?? ""}`, w);
|
|
21865
22045
|
}
|
|
21866
22046
|
}
|
|
@@ -22196,7 +22376,7 @@ var init_types = __esm({
|
|
|
22196
22376
|
});
|
|
22197
22377
|
|
|
22198
22378
|
// packages/core/dist/council/verification/motionAutofix.js
|
|
22199
|
-
import { readFileSync as
|
|
22379
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
|
|
22200
22380
|
import { join as join6 } from "node:path";
|
|
22201
22381
|
function sanitizeTransitionPart(part) {
|
|
22202
22382
|
const tokens = part.trim().split(/\s+/);
|
|
@@ -22275,7 +22455,7 @@ function applyMotionAutofix(projectRoot, report) {
|
|
|
22275
22455
|
const abs = join6(projectRoot, rel2);
|
|
22276
22456
|
let html;
|
|
22277
22457
|
try {
|
|
22278
|
-
html =
|
|
22458
|
+
html = readFileSync11(abs, "utf8");
|
|
22279
22459
|
} catch {
|
|
22280
22460
|
continue;
|
|
22281
22461
|
}
|
|
@@ -22347,7 +22527,7 @@ var init_motionAutofix = __esm({
|
|
|
22347
22527
|
});
|
|
22348
22528
|
|
|
22349
22529
|
// packages/core/dist/council/verification/autofix.js
|
|
22350
|
-
import { readFileSync as
|
|
22530
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
|
|
22351
22531
|
import { join as join7 } from "node:path";
|
|
22352
22532
|
function applyDeterministicAutofix(projectRoot, report) {
|
|
22353
22533
|
const motion = applyMotionAutofix(projectRoot, report);
|
|
@@ -22363,7 +22543,7 @@ function applyDeterministicAutofix(projectRoot, report) {
|
|
|
22363
22543
|
if (!m || m[1] === "rm")
|
|
22364
22544
|
continue;
|
|
22365
22545
|
const abs = join7(projectRoot, rel2);
|
|
22366
|
-
let html =
|
|
22546
|
+
let html = readFileSync12(abs, "utf8");
|
|
22367
22547
|
const snippet = m[0];
|
|
22368
22548
|
if (!html.includes(snippet))
|
|
22369
22549
|
continue;
|
|
@@ -22418,12 +22598,12 @@ var init_types2 = __esm({
|
|
|
22418
22598
|
});
|
|
22419
22599
|
|
|
22420
22600
|
// packages/core/dist/council/lessons/io.js
|
|
22421
|
-
import { readFileSync as
|
|
22601
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
22422
22602
|
import { join as join8 } from "node:path";
|
|
22423
22603
|
function readLessonsDeduped(zelariRoot) {
|
|
22424
|
-
const
|
|
22604
|
+
const path33 = join8(zelariRoot, LESSONS_FILE);
|
|
22425
22605
|
try {
|
|
22426
|
-
const raw =
|
|
22606
|
+
const raw = readFileSync13(path33, "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 path33 = join9(zelariRoot, LESSONS_FILE);
|
|
22708
|
+
appendFileSync2(path33, `${JSON.stringify(lesson)}
|
|
22529
22709
|
`, "utf8");
|
|
22530
22710
|
}
|
|
22531
22711
|
function findSimilar(lessons, signature) {
|
|
@@ -23062,18 +23242,18 @@ var init_council = __esm({
|
|
|
23062
23242
|
import {
|
|
23063
23243
|
mkdirSync as mkdirSync6,
|
|
23064
23244
|
writeFileSync as writeFileSync10,
|
|
23065
|
-
existsSync as
|
|
23245
|
+
existsSync as existsSync13,
|
|
23066
23246
|
accessSync,
|
|
23067
23247
|
constants,
|
|
23068
23248
|
realpathSync
|
|
23069
23249
|
} from "node:fs";
|
|
23070
23250
|
import { join as join11, basename } from "node:path";
|
|
23071
|
-
import { homedir as
|
|
23072
|
-
import { createHash } from "node:crypto";
|
|
23251
|
+
import { homedir as homedir4 } from "node:os";
|
|
23252
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
23073
23253
|
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
23074
23254
|
const candidates = [
|
|
23075
23255
|
join11(projectRoot, ".zelari"),
|
|
23076
|
-
join11(
|
|
23256
|
+
join11(homedir4(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
23077
23257
|
];
|
|
23078
23258
|
for (const candidate of candidates) {
|
|
23079
23259
|
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
@@ -23085,11 +23265,11 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
|
23085
23265
|
return candidates[0];
|
|
23086
23266
|
}
|
|
23087
23267
|
function hashProject(projectPath) {
|
|
23088
|
-
return
|
|
23268
|
+
return createHash2("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
23089
23269
|
}
|
|
23090
23270
|
function isWritableDir(dir) {
|
|
23091
23271
|
try {
|
|
23092
|
-
if (!
|
|
23272
|
+
if (!existsSync13(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") && existsSync13(join11(workspaceDir, "..", ".git"))) {
|
|
23102
23282
|
const gitignorePath = join11(workspaceDir, ".gitignore");
|
|
23103
|
-
if (!
|
|
23283
|
+
if (!existsSync13(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 existsSync14, readFileSync as readFileSync14, 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,10 +23344,10 @@ 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 (!existsSync14(planPath)) return null;
|
|
23168
23348
|
let plan;
|
|
23169
23349
|
try {
|
|
23170
|
-
plan = JSON.parse(
|
|
23350
|
+
plan = JSON.parse(readFileSync14(planPath, "utf8"));
|
|
23171
23351
|
} catch {
|
|
23172
23352
|
return null;
|
|
23173
23353
|
}
|
|
@@ -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 (!existsSync14(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,9 +23493,9 @@ function safeProjectName(root) {
|
|
|
23313
23493
|
}
|
|
23314
23494
|
function readPackageJson(projectRoot) {
|
|
23315
23495
|
const p3 = join12(projectRoot, "package.json");
|
|
23316
|
-
if (!
|
|
23496
|
+
if (!existsSync14(p3)) return null;
|
|
23317
23497
|
try {
|
|
23318
|
-
return JSON.parse(
|
|
23498
|
+
return JSON.parse(readFileSync14(p3, "utf8"));
|
|
23319
23499
|
} catch {
|
|
23320
23500
|
return null;
|
|
23321
23501
|
}
|
|
@@ -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 {
|
|
@@ -23404,9 +23584,9 @@ __export(storage_exports, {
|
|
|
23404
23584
|
workspaceMutex: () => workspaceMutex
|
|
23405
23585
|
});
|
|
23406
23586
|
import {
|
|
23407
|
-
readFileSync as
|
|
23587
|
+
readFileSync as readFileSync15,
|
|
23408
23588
|
writeFileSync as writeFileSync11,
|
|
23409
|
-
existsSync as
|
|
23589
|
+
existsSync as existsSync15,
|
|
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(path33) {
|
|
23849
|
+
if (!existsSync15(path33)) {
|
|
23850
|
+
throw new Error(`File not found: ${path33}`);
|
|
23671
23851
|
}
|
|
23672
|
-
const md =
|
|
23852
|
+
const md = readFileSync15(path33, "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(path33) {
|
|
23857
|
+
if (!existsSync15(path33)) return null;
|
|
23858
|
+
return this.read(path33);
|
|
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(path33, meta3, body) {
|
|
23865
|
+
mkdirSync7(dirname(path33), { recursive: true });
|
|
23866
|
+
const tmp = path33 + ".tmp-" + process.pid;
|
|
23687
23867
|
const md = serializeFrontmatter(meta3, body);
|
|
23688
23868
|
writeFileSync11(tmp, md, "utf8");
|
|
23689
|
-
renameSync2(tmp,
|
|
23869
|
+
renameSync2(tmp, path33);
|
|
23690
23870
|
}
|
|
23691
23871
|
/** List all .md files in a directory (non-recursive). */
|
|
23692
23872
|
listMarkdown(dir) {
|
|
23693
|
-
if (!
|
|
23873
|
+
if (!existsSync15(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 existsSync16,
|
|
23734
23914
|
readdirSync as readdirSync3,
|
|
23735
23915
|
writeFileSync as writeFileSync12,
|
|
23736
|
-
readFileSync as
|
|
23916
|
+
readFileSync as readFileSync16,
|
|
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,10 +23931,10 @@ function planJsonPath(ctx) {
|
|
|
23751
23931
|
}
|
|
23752
23932
|
function readPlan(ctx) {
|
|
23753
23933
|
const jsonPath = planJsonPath(ctx);
|
|
23754
|
-
if (
|
|
23934
|
+
if (existsSync16(jsonPath)) {
|
|
23755
23935
|
try {
|
|
23756
23936
|
const parsed = JSON.parse(
|
|
23757
|
-
|
|
23937
|
+
readFileSync16(jsonPath, "utf8")
|
|
23758
23938
|
);
|
|
23759
23939
|
return {
|
|
23760
23940
|
phases: Array.isArray(parsed.phases) ? parsed.phases : [],
|
|
@@ -23764,8 +23944,8 @@ function readPlan(ctx) {
|
|
|
23764
23944
|
} catch {
|
|
23765
23945
|
}
|
|
23766
23946
|
}
|
|
23767
|
-
const
|
|
23768
|
-
const doc = ctx.storage.readIfExists(
|
|
23947
|
+
const path33 = workspaceFile(ctx.rootDir, "plan");
|
|
23948
|
+
const doc = ctx.storage.readIfExists(path33);
|
|
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 (!existsSync16(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 path33 = 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(path33, 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 path33 = 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(path33, 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 path33 = 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(path33, meta3, content);
|
|
24270
24450
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
24271
24451
|
});
|
|
24272
24452
|
}
|
|
@@ -24302,8 +24482,8 @@ function searchDocumentsStub(ctx) {
|
|
|
24302
24482
|
];
|
|
24303
24483
|
const results = [];
|
|
24304
24484
|
for (const file2 of files) {
|
|
24305
|
-
if (!
|
|
24306
|
-
const raw =
|
|
24485
|
+
if (!existsSync16(file2)) continue;
|
|
24486
|
+
const raw = readFileSync16(file2, "utf8");
|
|
24307
24487
|
const content = raw.toLowerCase();
|
|
24308
24488
|
let idx = -1;
|
|
24309
24489
|
let matchLen = 0;
|
|
@@ -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;
|
|
@@ -24497,21 +24677,21 @@ __export(updater_exports, {
|
|
|
24497
24677
|
resolveBundledNpmCli: () => resolveBundledNpmCli
|
|
24498
24678
|
});
|
|
24499
24679
|
import { createRequire } from "node:module";
|
|
24500
|
-
import { spawn as
|
|
24501
|
-
import { existsSync as
|
|
24502
|
-
import
|
|
24680
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
24681
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
24682
|
+
import path22 from "node:path";
|
|
24503
24683
|
import { fileURLToPath } from "node:url";
|
|
24504
24684
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
24505
|
-
const dir =
|
|
24685
|
+
const dir = path22.dirname(execPath);
|
|
24506
24686
|
const candidates = [
|
|
24507
24687
|
// Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
|
|
24508
|
-
|
|
24688
|
+
path22.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
24509
24689
|
// POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
|
|
24510
|
-
|
|
24690
|
+
path22.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
24511
24691
|
];
|
|
24512
24692
|
for (const candidate of candidates) {
|
|
24513
24693
|
try {
|
|
24514
|
-
if (
|
|
24694
|
+
if (existsSync17(candidate)) return candidate;
|
|
24515
24695
|
} catch {
|
|
24516
24696
|
}
|
|
24517
24697
|
}
|
|
@@ -24524,7 +24704,7 @@ function looksLikeBrokenShim(exitCode, output) {
|
|
|
24524
24704
|
}
|
|
24525
24705
|
function getCurrentVersion() {
|
|
24526
24706
|
try {
|
|
24527
|
-
const pkgPath =
|
|
24707
|
+
const pkgPath = path22.resolve(__dirname2, "..", "..", "package.json");
|
|
24528
24708
|
const pkg = require2(pkgPath);
|
|
24529
24709
|
return pkg.version;
|
|
24530
24710
|
} catch {
|
|
@@ -24584,7 +24764,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
|
|
|
24584
24764
|
updateAvailable: cmp < 0
|
|
24585
24765
|
};
|
|
24586
24766
|
}
|
|
24587
|
-
async function performUpdate(packageName = "zelari-code", executor =
|
|
24767
|
+
async function performUpdate(packageName = "zelari-code", executor = spawn4, resolveNpmCli = resolveBundledNpmCli) {
|
|
24588
24768
|
const args = ["install", "-g", `${packageName}@latest`];
|
|
24589
24769
|
const primary = await runNpm(executor, args, "shim");
|
|
24590
24770
|
if (primary.ok) return primary;
|
|
@@ -24636,13 +24816,13 @@ var init_updater = __esm({
|
|
|
24636
24816
|
"use strict";
|
|
24637
24817
|
init_cmdline();
|
|
24638
24818
|
require2 = createRequire(import.meta.url);
|
|
24639
|
-
__dirname2 =
|
|
24819
|
+
__dirname2 = path22.dirname(fileURLToPath(import.meta.url));
|
|
24640
24820
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
24641
24821
|
}
|
|
24642
24822
|
});
|
|
24643
24823
|
|
|
24644
24824
|
// src/cli/mcp/mcpClient.ts
|
|
24645
|
-
import { spawn as
|
|
24825
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
24646
24826
|
var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
|
|
24647
24827
|
var init_mcpClient = __esm({
|
|
24648
24828
|
"src/cli/mcp/mcpClient.ts"() {
|
|
@@ -24670,10 +24850,10 @@ var init_mcpClient = __esm({
|
|
|
24670
24850
|
env: { ...process.env, ...this.config.env ?? {} },
|
|
24671
24851
|
windowsHide: true
|
|
24672
24852
|
};
|
|
24673
|
-
const child = process.platform === "win32" ?
|
|
24853
|
+
const child = process.platform === "win32" ? spawn5(buildCmdLine(this.config.command, this.config.args ?? []), {
|
|
24674
24854
|
...spawnOpts,
|
|
24675
24855
|
shell: true
|
|
24676
|
-
}) :
|
|
24856
|
+
}) : spawn5(this.config.command, this.config.args ?? [], spawnOpts);
|
|
24677
24857
|
this.child = child;
|
|
24678
24858
|
child.stdout.setEncoding("utf8");
|
|
24679
24859
|
child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
@@ -24817,20 +24997,20 @@ __export(mcpManager_exports, {
|
|
|
24817
24997
|
readMcpConfig: () => readMcpConfig,
|
|
24818
24998
|
registerMcpTools: () => registerMcpTools
|
|
24819
24999
|
});
|
|
24820
|
-
import { existsSync as
|
|
25000
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17 } from "node:fs";
|
|
24821
25001
|
import { join as join15 } from "node:path";
|
|
24822
|
-
import { homedir as
|
|
25002
|
+
import { homedir as homedir5 } from "node:os";
|
|
24823
25003
|
function readMcpConfig(projectRoot = process.cwd()) {
|
|
24824
25004
|
const merged = {};
|
|
24825
25005
|
const paths = [
|
|
24826
|
-
join15(
|
|
25006
|
+
join15(homedir5(), ".zelari-code", "mcp.json"),
|
|
24827
25007
|
join15(projectRoot, ".zelari", "mcp.json")
|
|
24828
25008
|
// later = higher precedence
|
|
24829
25009
|
];
|
|
24830
25010
|
for (const p3 of paths) {
|
|
24831
|
-
if (!
|
|
25011
|
+
if (!existsSync18(p3)) continue;
|
|
24832
25012
|
try {
|
|
24833
|
-
const parsed = JSON.parse(
|
|
25013
|
+
const parsed = JSON.parse(readFileSync17(p3, "utf8"));
|
|
24834
25014
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
24835
25015
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
24836
25016
|
merged[name] = cfg;
|
|
@@ -24962,13 +25142,13 @@ var planDetect_exports = {};
|
|
|
24962
25142
|
__export(planDetect_exports, {
|
|
24963
25143
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
24964
25144
|
});
|
|
24965
|
-
import { existsSync as
|
|
25145
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
|
|
24966
25146
|
import { join as join16 } from "node:path";
|
|
24967
25147
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
24968
25148
|
const planPath = join16(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
24969
|
-
if (!
|
|
25149
|
+
if (!existsSync19(planPath)) return false;
|
|
24970
25150
|
try {
|
|
24971
|
-
const parsed = JSON.parse(
|
|
25151
|
+
const parsed = JSON.parse(readFileSync18(planPath, "utf8"));
|
|
24972
25152
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
24973
25153
|
} catch {
|
|
24974
25154
|
return false;
|
|
@@ -25065,15 +25245,15 @@ __export(agentsMd_exports, {
|
|
|
25065
25245
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
25066
25246
|
updateAgentsMd: () => updateAgentsMd
|
|
25067
25247
|
});
|
|
25068
|
-
import { existsSync as
|
|
25069
|
-
import { createHash as
|
|
25248
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "node:fs";
|
|
25249
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
25070
25250
|
import { join as join17 } from "node:path";
|
|
25071
|
-
import { readFile } from "node:fs/promises";
|
|
25251
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
25072
25252
|
async function readPackageJson2(projectRoot) {
|
|
25073
|
-
const
|
|
25074
|
-
if (!
|
|
25253
|
+
const path33 = join17(projectRoot, "package.json");
|
|
25254
|
+
if (!existsSync20(path33)) return null;
|
|
25075
25255
|
try {
|
|
25076
|
-
return JSON.parse(await
|
|
25256
|
+
return JSON.parse(await readFile2(path33, "utf8"));
|
|
25077
25257
|
} catch {
|
|
25078
25258
|
return null;
|
|
25079
25259
|
}
|
|
@@ -25096,7 +25276,7 @@ async function genTechStack(ctx) {
|
|
|
25096
25276
|
}
|
|
25097
25277
|
async function genDecisions(ctx) {
|
|
25098
25278
|
const decisionsDir = join17(ctx.rootDir, "decisions");
|
|
25099
|
-
if (!
|
|
25279
|
+
if (!existsSync20(decisionsDir)) return "_No ADRs yet._";
|
|
25100
25280
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
25101
25281
|
const accepted = [];
|
|
25102
25282
|
const proposed = [];
|
|
@@ -25122,8 +25302,8 @@ async function genDecisions(ctx) {
|
|
|
25122
25302
|
async function genConventions(ctx) {
|
|
25123
25303
|
const lines = [];
|
|
25124
25304
|
const claudeMd = join17(ctx.projectRoot, "CLAUDE.MD");
|
|
25125
|
-
if (
|
|
25126
|
-
const content =
|
|
25305
|
+
if (existsSync20(claudeMd)) {
|
|
25306
|
+
const content = readFileSync19(claudeMd, "utf8");
|
|
25127
25307
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
25128
25308
|
if (match) {
|
|
25129
25309
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -25155,9 +25335,9 @@ async function genBuild(ctx) {
|
|
|
25155
25335
|
].join("\n");
|
|
25156
25336
|
}
|
|
25157
25337
|
async function genOpenQuestions(ctx) {
|
|
25158
|
-
const
|
|
25159
|
-
if (!
|
|
25160
|
-
const content =
|
|
25338
|
+
const path33 = join17(ctx.rootDir, "risks.md");
|
|
25339
|
+
if (!existsSync20(path33)) return "_No open questions._";
|
|
25340
|
+
const content = readFileSync19(path33, "utf8");
|
|
25161
25341
|
const lines = content.split("\n");
|
|
25162
25342
|
const questions = [];
|
|
25163
25343
|
let currentTitle = "";
|
|
@@ -25232,8 +25412,8 @@ function titleCase(id) {
|
|
|
25232
25412
|
}
|
|
25233
25413
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
25234
25414
|
const agentsPath = join17(projectRoot, "AGENTS.MD");
|
|
25235
|
-
if (
|
|
25236
|
-
const content =
|
|
25415
|
+
if (existsSync20(agentsPath)) {
|
|
25416
|
+
const content = readFileSync19(agentsPath, "utf8");
|
|
25237
25417
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
25238
25418
|
if (!hasAnyMarker) {
|
|
25239
25419
|
return {
|
|
@@ -25248,8 +25428,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25248
25428
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
25249
25429
|
}
|
|
25250
25430
|
let manualContent = "";
|
|
25251
|
-
if (
|
|
25252
|
-
const { manualBlocks } = parseAgentsMd(
|
|
25431
|
+
if (existsSync20(agentsPath)) {
|
|
25432
|
+
const { manualBlocks } = parseAgentsMd(readFileSync19(agentsPath, "utf8"));
|
|
25253
25433
|
manualContent = manualBlocks.after;
|
|
25254
25434
|
} else {
|
|
25255
25435
|
const projectName2 = projectName(projectRoot);
|
|
@@ -25265,7 +25445,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25265
25445
|
""
|
|
25266
25446
|
].join("\n");
|
|
25267
25447
|
}
|
|
25268
|
-
const oldContent =
|
|
25448
|
+
const oldContent = existsSync20(agentsPath) ? readFileSync19(agentsPath, "utf8") : "";
|
|
25269
25449
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
25270
25450
|
const changedSections = [];
|
|
25271
25451
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -25281,7 +25461,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
25281
25461
|
return { changed: true, sections: changedSections };
|
|
25282
25462
|
}
|
|
25283
25463
|
function hash2(s) {
|
|
25284
|
-
return
|
|
25464
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
25285
25465
|
}
|
|
25286
25466
|
var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
25287
25467
|
var init_agentsMd = __esm({
|
|
@@ -25402,8 +25582,8 @@ var init_completeDesign = __esm({
|
|
|
25402
25582
|
});
|
|
25403
25583
|
|
|
25404
25584
|
// src/cli/workspace/projectSmoke.ts
|
|
25405
|
-
import { spawn as
|
|
25406
|
-
import { existsSync as
|
|
25585
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
25586
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
|
|
25407
25587
|
import { join as join18 } from "node:path";
|
|
25408
25588
|
function pickSmokeScript(scripts) {
|
|
25409
25589
|
if (!scripts) return null;
|
|
@@ -25417,12 +25597,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25417
25597
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
25418
25598
|
}
|
|
25419
25599
|
const pkgPath = join18(projectRoot, "package.json");
|
|
25420
|
-
if (!
|
|
25600
|
+
if (!existsSync21(pkgPath)) {
|
|
25421
25601
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
25422
25602
|
}
|
|
25423
25603
|
let scripts = {};
|
|
25424
25604
|
try {
|
|
25425
|
-
const pkg = JSON.parse(
|
|
25605
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
25426
25606
|
scripts = pkg.scripts ?? {};
|
|
25427
25607
|
} catch {
|
|
25428
25608
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -25433,7 +25613,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
25433
25613
|
}
|
|
25434
25614
|
return await new Promise((resolveRun) => {
|
|
25435
25615
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
25436
|
-
const child =
|
|
25616
|
+
const child = spawn6(npmCmd, ["run", script], {
|
|
25437
25617
|
cwd: projectRoot,
|
|
25438
25618
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25439
25619
|
env: process.env,
|
|
@@ -25505,8 +25685,8 @@ __export(postCouncilHook_exports, {
|
|
|
25505
25685
|
runImplementationVerificationHook: () => runImplementationVerificationHook,
|
|
25506
25686
|
runPostCouncilHook: () => runPostCouncilHook
|
|
25507
25687
|
});
|
|
25508
|
-
import { spawn as
|
|
25509
|
-
import { existsSync as
|
|
25688
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
25689
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
|
|
25510
25690
|
import { join as join19 } from "node:path";
|
|
25511
25691
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
25512
25692
|
if (options?.runMode === "implementation") {
|
|
@@ -25520,7 +25700,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25520
25700
|
}
|
|
25521
25701
|
const planJsonPath2 = join19(ctx.rootDir, "plan.json");
|
|
25522
25702
|
const scriptPath = join19(ctx.projectRoot, "complete-design.mjs");
|
|
25523
|
-
if (!
|
|
25703
|
+
if (!existsSync22(planJsonPath2)) {
|
|
25524
25704
|
return {
|
|
25525
25705
|
ran: false,
|
|
25526
25706
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -25528,7 +25708,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25528
25708
|
}
|
|
25529
25709
|
let phaseCount = 0;
|
|
25530
25710
|
try {
|
|
25531
|
-
const parsed = JSON.parse(
|
|
25711
|
+
const parsed = JSON.parse(readFileSync21(planJsonPath2, "utf8"));
|
|
25532
25712
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
25533
25713
|
} catch {
|
|
25534
25714
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -25536,7 +25716,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25536
25716
|
if (phaseCount === 0) {
|
|
25537
25717
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
25538
25718
|
}
|
|
25539
|
-
if (!
|
|
25719
|
+
if (!existsSync22(scriptPath)) {
|
|
25540
25720
|
try {
|
|
25541
25721
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
25542
25722
|
return {
|
|
@@ -25554,7 +25734,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
25554
25734
|
}
|
|
25555
25735
|
}
|
|
25556
25736
|
return await new Promise((resolveRun) => {
|
|
25557
|
-
const child =
|
|
25737
|
+
const child = spawn7(process.execPath, [scriptPath], {
|
|
25558
25738
|
cwd: ctx.projectRoot,
|
|
25559
25739
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25560
25740
|
env: process.env
|
|
@@ -25720,8 +25900,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
25720
25900
|
sources: scope.sources
|
|
25721
25901
|
} : void 0
|
|
25722
25902
|
});
|
|
25723
|
-
const
|
|
25724
|
-
completionHook = { ran: true, path:
|
|
25903
|
+
const path33 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
25904
|
+
completionHook = { ran: true, path: path33, completion };
|
|
25725
25905
|
} catch (err) {
|
|
25726
25906
|
completionHook = {
|
|
25727
25907
|
ran: true,
|
|
@@ -25757,12 +25937,12 @@ var buildLessonsSummary_exports = {};
|
|
|
25757
25937
|
__export(buildLessonsSummary_exports, {
|
|
25758
25938
|
buildLessonsSummary: () => buildLessonsSummary
|
|
25759
25939
|
});
|
|
25760
|
-
import { existsSync as
|
|
25940
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
25761
25941
|
import { join as join20 } from "node:path";
|
|
25762
25942
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
25763
25943
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
25764
25944
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
25765
|
-
if (!
|
|
25945
|
+
if (!existsSync23(join20(zelariRoot, "lessons.jsonl"))) return null;
|
|
25766
25946
|
const lessons = recallLessons(zelariRoot, {
|
|
25767
25947
|
maxLessons: 5,
|
|
25768
25948
|
maxBytes: 2048,
|
|
@@ -25784,14 +25964,14 @@ __export(councilFeedback_exports, {
|
|
|
25784
25964
|
FeedbackStore: () => FeedbackStore
|
|
25785
25965
|
});
|
|
25786
25966
|
import {
|
|
25787
|
-
promises as
|
|
25788
|
-
existsSync as
|
|
25789
|
-
readFileSync as
|
|
25967
|
+
promises as fs13,
|
|
25968
|
+
existsSync as existsSync24,
|
|
25969
|
+
readFileSync as readFileSync22,
|
|
25790
25970
|
writeFileSync as writeFileSync14,
|
|
25791
25971
|
mkdirSync as mkdirSync9
|
|
25792
25972
|
} from "node:fs";
|
|
25793
|
-
import
|
|
25794
|
-
import
|
|
25973
|
+
import path23 from "node:path";
|
|
25974
|
+
import os8 from "node:os";
|
|
25795
25975
|
var FeedbackStore;
|
|
25796
25976
|
var init_councilFeedback = __esm({
|
|
25797
25977
|
"src/cli/councilFeedback.ts"() {
|
|
@@ -25801,7 +25981,7 @@ var init_councilFeedback = __esm({
|
|
|
25801
25981
|
now;
|
|
25802
25982
|
entries = [];
|
|
25803
25983
|
constructor(options = {}) {
|
|
25804
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
25984
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path23.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
25805
25985
|
this.now = options.now ?? Date.now;
|
|
25806
25986
|
this.load();
|
|
25807
25987
|
}
|
|
@@ -25894,9 +26074,9 @@ var init_councilFeedback = __esm({
|
|
|
25894
26074
|
}
|
|
25895
26075
|
// --- persistence ---------------------------------------------------------
|
|
25896
26076
|
load() {
|
|
25897
|
-
if (!
|
|
26077
|
+
if (!existsSync24(this.file)) return;
|
|
25898
26078
|
try {
|
|
25899
|
-
const raw =
|
|
26079
|
+
const raw = readFileSync22(this.file, "utf-8");
|
|
25900
26080
|
const parsed = JSON.parse(raw);
|
|
25901
26081
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
25902
26082
|
this.entries = parsed.entries.filter(
|
|
@@ -25907,7 +26087,7 @@ var init_councilFeedback = __esm({
|
|
|
25907
26087
|
}
|
|
25908
26088
|
}
|
|
25909
26089
|
save() {
|
|
25910
|
-
mkdirSync9(
|
|
26090
|
+
mkdirSync9(path23.dirname(this.file), { recursive: true });
|
|
25911
26091
|
writeFileSync14(
|
|
25912
26092
|
this.file,
|
|
25913
26093
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -25917,7 +26097,7 @@ var init_councilFeedback = __esm({
|
|
|
25917
26097
|
/** Async variant of load for callers that prefer async IO. */
|
|
25918
26098
|
async loadAsync() {
|
|
25919
26099
|
try {
|
|
25920
|
-
const raw = await
|
|
26100
|
+
const raw = await fs13.readFile(this.file, "utf-8");
|
|
25921
26101
|
const parsed = JSON.parse(raw);
|
|
25922
26102
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
25923
26103
|
this.entries = parsed.entries.filter(
|
|
@@ -25941,8 +26121,8 @@ __export(fileBackend_exports, {
|
|
|
25941
26121
|
isMemoryEnabled: () => isMemoryEnabled
|
|
25942
26122
|
});
|
|
25943
26123
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
25944
|
-
import { promises as
|
|
25945
|
-
import * as
|
|
26124
|
+
import { promises as fs14 } from "node:fs";
|
|
26125
|
+
import * as path24 from "node:path";
|
|
25946
26126
|
function tokenize(text) {
|
|
25947
26127
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
25948
26128
|
}
|
|
@@ -25980,9 +26160,9 @@ var init_fileBackend = __esm({
|
|
|
25980
26160
|
logPath = "";
|
|
25981
26161
|
memoryDir = "";
|
|
25982
26162
|
async init(projectRoot) {
|
|
25983
|
-
this.memoryDir =
|
|
25984
|
-
this.logPath =
|
|
25985
|
-
await
|
|
26163
|
+
this.memoryDir = path24.join(projectRoot, ".zelari", "memory");
|
|
26164
|
+
this.logPath = path24.join(this.memoryDir, "log.jsonl");
|
|
26165
|
+
await fs14.mkdir(this.memoryDir, { recursive: true });
|
|
25986
26166
|
}
|
|
25987
26167
|
async add(content, metadata = {}, graph) {
|
|
25988
26168
|
const fact = {
|
|
@@ -25992,7 +26172,7 @@ var init_fileBackend = __esm({
|
|
|
25992
26172
|
...graph ? { graph } : {},
|
|
25993
26173
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25994
26174
|
};
|
|
25995
|
-
await
|
|
26175
|
+
await fs14.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
|
|
25996
26176
|
return fact.id;
|
|
25997
26177
|
}
|
|
25998
26178
|
async search(query, options = {}) {
|
|
@@ -26020,7 +26200,7 @@ var init_fileBackend = __esm({
|
|
|
26020
26200
|
async readAll() {
|
|
26021
26201
|
let raw;
|
|
26022
26202
|
try {
|
|
26023
|
-
raw = await
|
|
26203
|
+
raw = await fs14.readFile(this.logPath, "utf8");
|
|
26024
26204
|
} catch {
|
|
26025
26205
|
return [];
|
|
26026
26206
|
}
|
|
@@ -26051,6 +26231,142 @@ var init_fileBackend = __esm({
|
|
|
26051
26231
|
}
|
|
26052
26232
|
});
|
|
26053
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 path25 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(path25.join(tmpdir(), "zelari-ckpt-"));
|
|
26260
|
+
const indexFile = path25.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(path25.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
|
+
|
|
26054
26370
|
// src/cli/zelariMission.ts
|
|
26055
26371
|
var zelariMission_exports = {};
|
|
26056
26372
|
__export(zelariMission_exports, {
|
|
@@ -26060,9 +26376,9 @@ __export(zelariMission_exports, {
|
|
|
26060
26376
|
resolveMaxStall: () => resolveMaxStall,
|
|
26061
26377
|
runZelariMission: () => runZelariMission
|
|
26062
26378
|
});
|
|
26063
|
-
import { randomUUID as
|
|
26064
|
-
import { promises as
|
|
26065
|
-
import * as
|
|
26379
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
26380
|
+
import { promises as fs15 } from "node:fs";
|
|
26381
|
+
import * as path26 from "node:path";
|
|
26066
26382
|
function resolveMaxIterations(env = process.env) {
|
|
26067
26383
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
26068
26384
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -26078,10 +26394,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
26078
26394
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
26079
26395
|
}
|
|
26080
26396
|
async function writeMissionState(projectRoot, state2) {
|
|
26081
|
-
const dir =
|
|
26082
|
-
await
|
|
26083
|
-
await
|
|
26084
|
-
|
|
26397
|
+
const dir = path26.join(projectRoot, ".zelari");
|
|
26398
|
+
await fs15.mkdir(dir, { recursive: true });
|
|
26399
|
+
await fs15.writeFile(
|
|
26400
|
+
path26.join(dir, "mission-state.json"),
|
|
26085
26401
|
JSON.stringify(state2, null, 2) + "\n",
|
|
26086
26402
|
"utf8"
|
|
26087
26403
|
);
|
|
@@ -26122,7 +26438,7 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
26122
26438
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
26123
26439
|
const maxIter = deps.maxIterations ?? resolveMaxIterations(deps.env);
|
|
26124
26440
|
const maxStall = resolveMaxStall(deps.env);
|
|
26125
|
-
const missionId = deps.missionId ?? `m_${
|
|
26441
|
+
const missionId = deps.missionId ?? `m_${randomUUID4().slice(0, 8)}`;
|
|
26126
26442
|
const startedAt = now().toISOString();
|
|
26127
26443
|
const state2 = {
|
|
26128
26444
|
missionId,
|
|
@@ -26137,6 +26453,14 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
26137
26453
|
};
|
|
26138
26454
|
await deps.memory.init(deps.projectRoot);
|
|
26139
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
|
+
}
|
|
26140
26464
|
const designFirst = brief.phases[0]?.mode === "design-phase";
|
|
26141
26465
|
let noWriteStreak = 0;
|
|
26142
26466
|
for (let i = 1; i <= maxIter; i++) {
|
|
@@ -26204,6 +26528,7 @@ var init_zelariMission = __esm({
|
|
|
26204
26528
|
"src/cli/zelariMission.ts"() {
|
|
26205
26529
|
"use strict";
|
|
26206
26530
|
init_fileBackend();
|
|
26531
|
+
init_checkpointManager();
|
|
26207
26532
|
DEFAULT_MAX_ITER = 10;
|
|
26208
26533
|
DEFAULT_MAX_STALL = 2;
|
|
26209
26534
|
}
|
|
@@ -26215,26 +26540,26 @@ __export(doctor_exports, {
|
|
|
26215
26540
|
runDoctor: () => runDoctor
|
|
26216
26541
|
});
|
|
26217
26542
|
import { execSync } from "node:child_process";
|
|
26218
|
-
import { existsSync as
|
|
26543
|
+
import { existsSync as existsSync29, readFileSync as readFileSync25, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
26219
26544
|
import { createRequire as createRequire2 } from "node:module";
|
|
26220
26545
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
26221
|
-
import
|
|
26546
|
+
import path32 from "node:path";
|
|
26222
26547
|
function findPackageRoot(start) {
|
|
26223
26548
|
let dir = start;
|
|
26224
26549
|
for (let i = 0; i < 6; i += 1) {
|
|
26225
|
-
const candidate =
|
|
26226
|
-
if (
|
|
26550
|
+
const candidate = path32.join(dir, "package.json");
|
|
26551
|
+
if (existsSync29(candidate)) {
|
|
26227
26552
|
try {
|
|
26228
|
-
const pkg = JSON.parse(
|
|
26553
|
+
const pkg = JSON.parse(readFileSync25(candidate, "utf8"));
|
|
26229
26554
|
if (pkg.name === "zelari-code") return dir;
|
|
26230
26555
|
} catch {
|
|
26231
26556
|
}
|
|
26232
26557
|
}
|
|
26233
|
-
const parent =
|
|
26558
|
+
const parent = path32.dirname(dir);
|
|
26234
26559
|
if (parent === dir) break;
|
|
26235
26560
|
dir = parent;
|
|
26236
26561
|
}
|
|
26237
|
-
return
|
|
26562
|
+
return path32.resolve(__dirname3, "..", "..", "..");
|
|
26238
26563
|
}
|
|
26239
26564
|
function tryExec(cmd) {
|
|
26240
26565
|
try {
|
|
@@ -26248,8 +26573,8 @@ function tryExec(cmd) {
|
|
|
26248
26573
|
}
|
|
26249
26574
|
function readPackageJson3() {
|
|
26250
26575
|
try {
|
|
26251
|
-
const pkgPath =
|
|
26252
|
-
return JSON.parse(
|
|
26576
|
+
const pkgPath = path32.join(packageRoot, "package.json");
|
|
26577
|
+
return JSON.parse(readFileSync25(pkgPath, "utf8"));
|
|
26253
26578
|
} catch {
|
|
26254
26579
|
return null;
|
|
26255
26580
|
}
|
|
@@ -26264,8 +26589,8 @@ function checkShim(pkgName) {
|
|
|
26264
26589
|
}
|
|
26265
26590
|
const isWin = process.platform === "win32";
|
|
26266
26591
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
26267
|
-
const shimPath =
|
|
26268
|
-
if (!
|
|
26592
|
+
const shimPath = path32.join(prefix, shimName);
|
|
26593
|
+
if (!existsSync29(shimPath)) {
|
|
26269
26594
|
return FAIL(
|
|
26270
26595
|
`shim not found at ${shimPath}
|
|
26271
26596
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -26274,7 +26599,7 @@ function checkShim(pkgName) {
|
|
|
26274
26599
|
try {
|
|
26275
26600
|
const st = statSync6(shimPath);
|
|
26276
26601
|
if (isWin) {
|
|
26277
|
-
const content =
|
|
26602
|
+
const content = readFileSync25(shimPath, "utf8");
|
|
26278
26603
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
26279
26604
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
26280
26605
|
}
|
|
@@ -26292,8 +26617,8 @@ function checkShim(pkgName) {
|
|
|
26292
26617
|
fix: npm install -g ${pkgName}@latest --force`
|
|
26293
26618
|
);
|
|
26294
26619
|
}
|
|
26295
|
-
const resolved =
|
|
26296
|
-
const expected =
|
|
26620
|
+
const resolved = path32.resolve(path32.dirname(shimPath), target);
|
|
26621
|
+
const expected = path32.join(
|
|
26297
26622
|
prefix,
|
|
26298
26623
|
"node_modules",
|
|
26299
26624
|
pkgName,
|
|
@@ -26332,8 +26657,8 @@ function checkNode(pkg) {
|
|
|
26332
26657
|
return OK(`node ${raw}`);
|
|
26333
26658
|
}
|
|
26334
26659
|
function checkBundle() {
|
|
26335
|
-
const bundle =
|
|
26336
|
-
if (!
|
|
26660
|
+
const bundle = path32.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
26661
|
+
if (!existsSync29(bundle)) {
|
|
26337
26662
|
return FAIL(
|
|
26338
26663
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
26339
26664
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -26353,7 +26678,7 @@ function checkRuntimeDeps() {
|
|
|
26353
26678
|
const missing = [];
|
|
26354
26679
|
for (const dep of required2) {
|
|
26355
26680
|
try {
|
|
26356
|
-
const localReq = createRequire2(
|
|
26681
|
+
const localReq = createRequire2(path32.join(packageRoot, "package.json"));
|
|
26357
26682
|
localReq.resolve(dep);
|
|
26358
26683
|
} catch {
|
|
26359
26684
|
missing.push(dep);
|
|
@@ -26435,7 +26760,7 @@ var init_doctor = __esm({
|
|
|
26435
26760
|
"src/cli/utils/doctor.ts"() {
|
|
26436
26761
|
"use strict";
|
|
26437
26762
|
require3 = createRequire2(import.meta.url);
|
|
26438
|
-
__dirname3 =
|
|
26763
|
+
__dirname3 = path32.dirname(fileURLToPath2(import.meta.url));
|
|
26439
26764
|
packageRoot = findPackageRoot(__dirname3);
|
|
26440
26765
|
OK = (message) => ({
|
|
26441
26766
|
ok: true,
|
|
@@ -26882,6 +27207,81 @@ function StreamingTail(m) {
|
|
|
26882
27207
|
// src/cli/components/StatusBar.tsx
|
|
26883
27208
|
import React6 from "react";
|
|
26884
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
|
|
26885
27285
|
function StatusBar({
|
|
26886
27286
|
model,
|
|
26887
27287
|
provider,
|
|
@@ -26892,7 +27292,9 @@ function StatusBar({
|
|
|
26892
27292
|
mode = "agent",
|
|
26893
27293
|
cwd,
|
|
26894
27294
|
elapsedMs = null,
|
|
26895
|
-
lastMs = null
|
|
27295
|
+
lastMs = null,
|
|
27296
|
+
costUsd = 0,
|
|
27297
|
+
cachedTokens = 0
|
|
26896
27298
|
}) {
|
|
26897
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(
|
|
26898
27300
|
Text6,
|
|
@@ -26901,7 +27303,7 @@ function StatusBar({
|
|
|
26901
27303
|
color: mode === "council" ? "magenta" : mode === "zelari" ? "green" : "cyan"
|
|
26902
27304
|
},
|
|
26903
27305
|
mode === "council" ? "\u26EC council" : mode === "zelari" ? "\u26A1 zelari" : "\u23F5 agent"
|
|
26904
|
-
), /* @__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))));
|
|
26905
27307
|
}
|
|
26906
27308
|
|
|
26907
27309
|
// src/cli/components/SelectList.tsx
|
|
@@ -29307,75 +29709,13 @@ registerCodingSkill(regressionTest);
|
|
|
29307
29709
|
|
|
29308
29710
|
// src/cli/app.tsx
|
|
29309
29711
|
init_providerConfig();
|
|
29310
|
-
|
|
29311
|
-
// packages/core/dist/harness/index.js
|
|
29312
|
-
init_AgentHarness();
|
|
29313
|
-
|
|
29314
|
-
// packages/core/dist/core/sessionJsonl.js
|
|
29315
|
-
import { promises as fs8 } from "node:fs";
|
|
29316
|
-
import path10 from "node:path";
|
|
29317
|
-
import os3 from "node:os";
|
|
29318
|
-
var SessionJsonlWriter = class {
|
|
29319
|
-
filePath;
|
|
29320
|
-
onError;
|
|
29321
|
-
constructor(sessionId, options = {}) {
|
|
29322
|
-
const baseDir = options.baseDir ?? defaultBaseDir();
|
|
29323
|
-
this.filePath = path10.join(baseDir, `${sessionId}.jsonl`);
|
|
29324
|
-
this.onError = options.onError ?? console.error;
|
|
29325
|
-
}
|
|
29326
|
-
/** Absolute path to the session JSONL file. */
|
|
29327
|
-
get path() {
|
|
29328
|
-
return this.filePath;
|
|
29329
|
-
}
|
|
29330
|
-
/** Append a BrainEvent as one JSON line. Creates the file + parent dirs if missing. */
|
|
29331
|
-
async append(event) {
|
|
29332
|
-
try {
|
|
29333
|
-
await fs8.mkdir(path10.dirname(this.filePath), { recursive: true });
|
|
29334
|
-
const line = JSON.stringify({
|
|
29335
|
-
ts: event.ts,
|
|
29336
|
-
sessionId: event.sessionId,
|
|
29337
|
-
event
|
|
29338
|
-
}) + "\n";
|
|
29339
|
-
await fs8.appendFile(this.filePath, line, { encoding: "utf-8", mode: 420 });
|
|
29340
|
-
} catch (err) {
|
|
29341
|
-
this.onError(`[sessionJsonl] failed to append event to ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
29342
|
-
}
|
|
29343
|
-
}
|
|
29344
|
-
/** Close the writer (no-op currently, but reserved for future buffered mode). */
|
|
29345
|
-
async close() {
|
|
29346
|
-
}
|
|
29347
|
-
};
|
|
29348
|
-
async function readSession(filePath) {
|
|
29349
|
-
try {
|
|
29350
|
-
const content = await fs8.readFile(filePath, "utf-8");
|
|
29351
|
-
const events = [];
|
|
29352
|
-
for (const line of content.split("\n")) {
|
|
29353
|
-
const trimmed = line.trim();
|
|
29354
|
-
if (!trimmed)
|
|
29355
|
-
continue;
|
|
29356
|
-
try {
|
|
29357
|
-
const parsed = JSON.parse(trimmed);
|
|
29358
|
-
if (parsed && typeof parsed === "object" && "event" in parsed) {
|
|
29359
|
-
events.push(parsed.event);
|
|
29360
|
-
}
|
|
29361
|
-
} catch {
|
|
29362
|
-
}
|
|
29363
|
-
}
|
|
29364
|
-
return events;
|
|
29365
|
-
} catch (err) {
|
|
29366
|
-
if (err.code === "ENOENT")
|
|
29367
|
-
return [];
|
|
29368
|
-
throw err;
|
|
29369
|
-
}
|
|
29370
|
-
}
|
|
29371
|
-
function defaultBaseDir() {
|
|
29372
|
-
return path10.join(os3.tmpdir(), "zelari-code", "sessions");
|
|
29373
|
-
}
|
|
29712
|
+
init_harness();
|
|
29374
29713
|
|
|
29375
29714
|
// src/cli/hooks/useSession.ts
|
|
29376
29715
|
import { useState as useState5, useRef as useRef3, useCallback, useEffect as useEffect4 } from "react";
|
|
29377
29716
|
|
|
29378
29717
|
// src/cli/sessionManager.ts
|
|
29718
|
+
init_harness();
|
|
29379
29719
|
import { promises as fs9, existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync, statSync } from "node:fs";
|
|
29380
29720
|
import path11 from "node:path";
|
|
29381
29721
|
import os4 from "node:os";
|
|
@@ -29497,6 +29837,9 @@ async function loadSessionEvents(id) {
|
|
|
29497
29837
|
return readSession(filePath);
|
|
29498
29838
|
}
|
|
29499
29839
|
|
|
29840
|
+
// src/cli/hooks/useSession.ts
|
|
29841
|
+
init_harness();
|
|
29842
|
+
|
|
29500
29843
|
// src/cli/hooks/eventsToMessages.ts
|
|
29501
29844
|
function eventsToMessages(events) {
|
|
29502
29845
|
const out = [];
|
|
@@ -29777,6 +30120,7 @@ ${lines.join("\n")}`;
|
|
|
29777
30120
|
}
|
|
29778
30121
|
|
|
29779
30122
|
// src/cli/hooks/useChatTurn.ts
|
|
30123
|
+
init_harness();
|
|
29780
30124
|
import { useState as useState6, useRef as useRef4, useCallback as useCallback2 } from "react";
|
|
29781
30125
|
|
|
29782
30126
|
// src/cli/metrics.ts
|
|
@@ -30119,33 +30463,1477 @@ function safeStringify(value) {
|
|
|
30119
30463
|
}
|
|
30120
30464
|
}
|
|
30121
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
|
+
|
|
30744
|
+
// src/cli/lsp/tools.ts
|
|
30745
|
+
init_zod();
|
|
30746
|
+
init_toolTypes();
|
|
30747
|
+
import path16 from "node:path";
|
|
30748
|
+
|
|
30749
|
+
// src/cli/lsp/protocol.ts
|
|
30750
|
+
function encodeMessage(message) {
|
|
30751
|
+
const json2 = JSON.stringify(message);
|
|
30752
|
+
const contentLength = Buffer.byteLength(json2, "utf8");
|
|
30753
|
+
return `Content-Length: ${contentLength}\r
|
|
30754
|
+
\r
|
|
30755
|
+
${json2}`;
|
|
30756
|
+
}
|
|
30757
|
+
function createMessageParser() {
|
|
30758
|
+
let buffer = "";
|
|
30759
|
+
return {
|
|
30760
|
+
push(chunk) {
|
|
30761
|
+
buffer += chunk;
|
|
30762
|
+
const out = [];
|
|
30763
|
+
for (; ; ) {
|
|
30764
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
30765
|
+
if (headerEnd === -1) break;
|
|
30766
|
+
const header = buffer.slice(0, headerEnd);
|
|
30767
|
+
const match = /Content-Length:\s*(\d+)/i.exec(header);
|
|
30768
|
+
if (!match) {
|
|
30769
|
+
buffer = buffer.slice(headerEnd + 4);
|
|
30770
|
+
continue;
|
|
30771
|
+
}
|
|
30772
|
+
const length = Number(match[1]);
|
|
30773
|
+
const bodyStart = headerEnd + 4;
|
|
30774
|
+
const rest = Buffer.from(buffer.slice(bodyStart), "utf8");
|
|
30775
|
+
if (rest.length < length) break;
|
|
30776
|
+
const body = rest.subarray(0, length).toString("utf8");
|
|
30777
|
+
buffer = rest.subarray(length).toString("utf8");
|
|
30778
|
+
try {
|
|
30779
|
+
out.push(JSON.parse(body));
|
|
30780
|
+
} catch {
|
|
30781
|
+
}
|
|
30782
|
+
}
|
|
30783
|
+
return out;
|
|
30784
|
+
}
|
|
30785
|
+
};
|
|
30786
|
+
}
|
|
30787
|
+
function pathToUri(filePath) {
|
|
30788
|
+
let p3 = filePath.replace(/\\/g, "/");
|
|
30789
|
+
if (!p3.startsWith("/")) p3 = `/${p3}`;
|
|
30790
|
+
const encoded = p3.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
30791
|
+
return `file://${encoded}`;
|
|
30792
|
+
}
|
|
30793
|
+
function uriToPath(uri) {
|
|
30794
|
+
if (!uri.startsWith("file://")) return uri;
|
|
30795
|
+
const withoutScheme = decodeURIComponent(uri.slice("file://".length));
|
|
30796
|
+
return /^\/[A-Za-z]:/.test(withoutScheme) ? withoutScheme.slice(1) : withoutScheme;
|
|
30797
|
+
}
|
|
30798
|
+
|
|
30799
|
+
// src/cli/lsp/tools.ts
|
|
30800
|
+
function fmtLocation(loc, relativeTo) {
|
|
30801
|
+
const file2 = uriToPath(loc.uri);
|
|
30802
|
+
const rel2 = relativeTo ? relPath(relativeTo, file2) : file2;
|
|
30803
|
+
const line = (loc.range?.start?.line ?? 0) + 1;
|
|
30804
|
+
const col = (loc.range?.start?.character ?? 0) + 1;
|
|
30805
|
+
return `${rel2}:${line}:${col}`;
|
|
30806
|
+
}
|
|
30807
|
+
function relPath(from, to) {
|
|
30808
|
+
try {
|
|
30809
|
+
const r = path16.relative(from, to);
|
|
30810
|
+
return r && !r.startsWith("..") ? r : to;
|
|
30811
|
+
} catch {
|
|
30812
|
+
return to;
|
|
30813
|
+
}
|
|
30814
|
+
}
|
|
30815
|
+
var PosArgs = external_exports.object({
|
|
30816
|
+
path: external_exports.string().min(1).describe("File path (relative to the project root or absolute)."),
|
|
30817
|
+
line: external_exports.number().int().positive().describe("1-based line number of the symbol."),
|
|
30818
|
+
column: external_exports.number().int().positive().describe("1-based column of the symbol.")
|
|
30819
|
+
});
|
|
30820
|
+
function createLspTools(provider, root = process.cwd()) {
|
|
30821
|
+
const goToDefinition = {
|
|
30822
|
+
name: "go_to_definition",
|
|
30823
|
+
description: "Jump to where the symbol at a position is defined (via the language server). Returns the defining file:line:col \u2014 use it instead of guessing with grep.",
|
|
30824
|
+
permissions: ["read"],
|
|
30825
|
+
inputSchema: PosArgs,
|
|
30826
|
+
execute: async (args) => {
|
|
30827
|
+
const a = args;
|
|
30828
|
+
const locs = await provider.definition(a.path, a.line - 1, a.column - 1);
|
|
30829
|
+
return typedOk({
|
|
30830
|
+
definitions: locs.map((l) => fmtLocation(l, root)),
|
|
30831
|
+
count: locs.length
|
|
30832
|
+
});
|
|
30833
|
+
}
|
|
30834
|
+
};
|
|
30835
|
+
const findReferences = {
|
|
30836
|
+
name: "find_references",
|
|
30837
|
+
description: "Find every reference to the symbol at a position across the workspace (via the language server). Returns a list of file:line:col \u2014 reliable where a text grep would miss shadowed names or match strings/comments.",
|
|
30838
|
+
permissions: ["read"],
|
|
30839
|
+
inputSchema: PosArgs,
|
|
30840
|
+
execute: async (args) => {
|
|
30841
|
+
const a = args;
|
|
30842
|
+
const locs = await provider.references(a.path, a.line - 1, a.column - 1);
|
|
30843
|
+
return typedOk({
|
|
30844
|
+
references: locs.map((l) => fmtLocation(l, root)),
|
|
30845
|
+
count: locs.length
|
|
30846
|
+
});
|
|
30847
|
+
}
|
|
30848
|
+
};
|
|
30849
|
+
const hoverType = {
|
|
30850
|
+
name: "hover_type",
|
|
30851
|
+
description: "Get the resolved type signature and documentation for the symbol at a position (via the language server) \u2014 the real type the compiler sees.",
|
|
30852
|
+
permissions: ["read"],
|
|
30853
|
+
inputSchema: PosArgs,
|
|
30854
|
+
execute: async (args) => {
|
|
30855
|
+
const a = args;
|
|
30856
|
+
const text = await provider.hover(a.path, a.line - 1, a.column - 1);
|
|
30857
|
+
return typedOk({ hover: text ?? "(no hover information)" });
|
|
30858
|
+
}
|
|
30859
|
+
};
|
|
30860
|
+
const documentSymbols = {
|
|
30861
|
+
name: "document_symbols",
|
|
30862
|
+
description: "List the symbols (functions, classes, methods, variables) declared in a file with their line numbers \u2014 a fast structural outline via the language server.",
|
|
30863
|
+
permissions: ["read"],
|
|
30864
|
+
inputSchema: external_exports.object({
|
|
30865
|
+
path: external_exports.string().min(1).describe("File path to outline.")
|
|
30866
|
+
}),
|
|
30867
|
+
execute: async (args) => {
|
|
30868
|
+
const a = args;
|
|
30869
|
+
const symbols = await provider.documentSymbols(a.path);
|
|
30870
|
+
return typedOk({
|
|
30871
|
+
symbols: symbols.map((s) => `${s.kind} ${s.name} (line ${s.line})`),
|
|
30872
|
+
count: symbols.length
|
|
30873
|
+
});
|
|
30874
|
+
}
|
|
30875
|
+
};
|
|
30876
|
+
const renameSymbol = {
|
|
30877
|
+
name: "rename_symbol",
|
|
30878
|
+
description: "PREVIEW a safe, workspace-wide rename of the symbol at a position (via the language server): returns which files change and how many edits each gets, so you know the blast radius before touching anything. It does NOT write files \u2014 apply the change yourself with edit_file once the scope looks right.",
|
|
30879
|
+
permissions: ["read"],
|
|
30880
|
+
inputSchema: PosArgs.extend({
|
|
30881
|
+
newName: external_exports.string().min(1).describe("The new symbol name.")
|
|
30882
|
+
}),
|
|
30883
|
+
execute: async (args) => {
|
|
30884
|
+
const a = args;
|
|
30885
|
+
const result = await provider.rename(a.path, a.line - 1, a.column - 1, a.newName);
|
|
30886
|
+
if (!result) {
|
|
30887
|
+
return typedOk({ preview: "no rename available at this position (symbol not found or not renameable)" });
|
|
30888
|
+
}
|
|
30889
|
+
return typedOk({
|
|
30890
|
+
totalEdits: result.totalEdits,
|
|
30891
|
+
files: result.files.map((f) => `${relPath(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
|
|
30892
|
+
});
|
|
30893
|
+
}
|
|
30894
|
+
};
|
|
30895
|
+
return [goToDefinition, findReferences, hoverType, documentSymbols, renameSymbol];
|
|
30896
|
+
}
|
|
30897
|
+
|
|
30898
|
+
// src/cli/lsp/manager.ts
|
|
30899
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
30900
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
30901
|
+
|
|
30902
|
+
// src/cli/lsp/client.ts
|
|
30903
|
+
var LspClient = class {
|
|
30904
|
+
constructor(transport, options = {}) {
|
|
30905
|
+
this.transport = transport;
|
|
30906
|
+
this.timeoutMs = options.timeoutMs ?? 15e3;
|
|
30907
|
+
transport.onData((chunk) => this.onData(chunk));
|
|
30908
|
+
transport.onClose(() => this.onClose());
|
|
30909
|
+
}
|
|
30910
|
+
nextId = 1;
|
|
30911
|
+
pending = /* @__PURE__ */ new Map();
|
|
30912
|
+
parser = createMessageParser();
|
|
30913
|
+
closed = false;
|
|
30914
|
+
timeoutMs;
|
|
30915
|
+
/** Send a request and await the matching response. */
|
|
30916
|
+
request(method, params) {
|
|
30917
|
+
if (this.closed) return Promise.reject(new Error("LSP transport is closed"));
|
|
30918
|
+
const id = this.nextId++;
|
|
30919
|
+
const msg = { jsonrpc: "2.0", id, method, ...params !== void 0 ? { params } : {} };
|
|
30920
|
+
return new Promise((resolve, reject) => {
|
|
30921
|
+
const timer = setTimeout(() => {
|
|
30922
|
+
this.pending.delete(id);
|
|
30923
|
+
reject(new Error(`LSP request "${method}" timed out after ${this.timeoutMs}ms`));
|
|
30924
|
+
}, this.timeoutMs);
|
|
30925
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
30926
|
+
this.transport.send(encodeMessage(msg));
|
|
30927
|
+
});
|
|
30928
|
+
}
|
|
30929
|
+
/** Fire a notification (no response expected). */
|
|
30930
|
+
notify(method, params) {
|
|
30931
|
+
if (this.closed) return;
|
|
30932
|
+
const msg = { jsonrpc: "2.0", method, ...params !== void 0 ? { params } : {} };
|
|
30933
|
+
this.transport.send(encodeMessage(msg));
|
|
30934
|
+
}
|
|
30935
|
+
onData(chunk) {
|
|
30936
|
+
for (const message of this.parser.push(chunk)) {
|
|
30937
|
+
this.handleMessage(message);
|
|
30938
|
+
}
|
|
30939
|
+
}
|
|
30940
|
+
handleMessage(message) {
|
|
30941
|
+
if (message.method === void 0 && message.id !== void 0) {
|
|
30942
|
+
const entry = this.pending.get(message.id);
|
|
30943
|
+
if (!entry) return;
|
|
30944
|
+
this.pending.delete(message.id);
|
|
30945
|
+
clearTimeout(entry.timer);
|
|
30946
|
+
if (message.error) {
|
|
30947
|
+
entry.reject(new Error(`LSP error ${message.error.code}: ${message.error.message}`));
|
|
30948
|
+
} else {
|
|
30949
|
+
entry.resolve(message.result);
|
|
30950
|
+
}
|
|
30951
|
+
return;
|
|
30952
|
+
}
|
|
30953
|
+
if (message.method !== void 0 && message.id !== void 0) {
|
|
30954
|
+
this.transport.send(
|
|
30955
|
+
encodeMessage({ jsonrpc: "2.0", id: message.id, result: null })
|
|
30956
|
+
);
|
|
30957
|
+
return;
|
|
30958
|
+
}
|
|
30959
|
+
}
|
|
30960
|
+
onClose() {
|
|
30961
|
+
this.closed = true;
|
|
30962
|
+
for (const [, entry] of this.pending) {
|
|
30963
|
+
clearTimeout(entry.timer);
|
|
30964
|
+
entry.reject(new Error("LSP transport closed before response"));
|
|
30965
|
+
}
|
|
30966
|
+
this.pending.clear();
|
|
30967
|
+
}
|
|
30968
|
+
dispose() {
|
|
30969
|
+
this.onClose();
|
|
30970
|
+
this.transport.dispose();
|
|
30971
|
+
}
|
|
30972
|
+
};
|
|
30973
|
+
|
|
30974
|
+
// src/cli/lsp/servers.ts
|
|
30975
|
+
import path17 from "node:path";
|
|
30976
|
+
var LSP_SERVERS = [
|
|
30977
|
+
{
|
|
30978
|
+
language: "typescript",
|
|
30979
|
+
bin: "typescript-language-server",
|
|
30980
|
+
args: ["--stdio"],
|
|
30981
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
30982
|
+
},
|
|
30983
|
+
{
|
|
30984
|
+
language: "python",
|
|
30985
|
+
bin: "pyright-langserver",
|
|
30986
|
+
args: ["--stdio"],
|
|
30987
|
+
extensions: [".py"]
|
|
30988
|
+
},
|
|
30989
|
+
{
|
|
30990
|
+
language: "go",
|
|
30991
|
+
bin: "gopls",
|
|
30992
|
+
args: [],
|
|
30993
|
+
extensions: [".go"]
|
|
30994
|
+
},
|
|
30995
|
+
{
|
|
30996
|
+
language: "rust",
|
|
30997
|
+
bin: "rust-analyzer",
|
|
30998
|
+
args: [],
|
|
30999
|
+
extensions: [".rs"]
|
|
31000
|
+
}
|
|
31001
|
+
];
|
|
31002
|
+
function languageIdForFile(file2) {
|
|
31003
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
31004
|
+
const map2 = {
|
|
31005
|
+
".ts": "typescript",
|
|
31006
|
+
".tsx": "typescriptreact",
|
|
31007
|
+
".js": "javascript",
|
|
31008
|
+
".jsx": "javascriptreact",
|
|
31009
|
+
".mjs": "javascript",
|
|
31010
|
+
".cjs": "javascript",
|
|
31011
|
+
".py": "python",
|
|
31012
|
+
".go": "go",
|
|
31013
|
+
".rs": "rust"
|
|
31014
|
+
};
|
|
31015
|
+
return map2[ext] ?? "plaintext";
|
|
31016
|
+
}
|
|
31017
|
+
function serverForFile(file2, servers = LSP_SERVERS) {
|
|
31018
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
31019
|
+
return servers.find((s) => s.extensions.includes(ext)) ?? null;
|
|
31020
|
+
}
|
|
31021
|
+
function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
|
|
31022
|
+
const spec = serverForFile(file2, servers);
|
|
31023
|
+
if (!spec) return null;
|
|
31024
|
+
const command = resolveBin(spec.bin, cwd);
|
|
31025
|
+
return {
|
|
31026
|
+
language: spec.language,
|
|
31027
|
+
command,
|
|
31028
|
+
args: spec.args,
|
|
31029
|
+
resolved: command !== spec.bin
|
|
31030
|
+
};
|
|
31031
|
+
}
|
|
31032
|
+
|
|
31033
|
+
// src/cli/lsp/manager.ts
|
|
31034
|
+
function processTransport(child) {
|
|
31035
|
+
return {
|
|
31036
|
+
send: (data) => {
|
|
31037
|
+
if (child.stdin.writable) child.stdin.write(data);
|
|
31038
|
+
},
|
|
31039
|
+
onData: (cb) => child.stdout.on("data", (b) => cb(b.toString("utf8"))),
|
|
31040
|
+
onClose: (cb) => child.on("exit", cb),
|
|
31041
|
+
dispose: () => {
|
|
31042
|
+
try {
|
|
31043
|
+
child.kill();
|
|
31044
|
+
} catch {
|
|
31045
|
+
}
|
|
31046
|
+
}
|
|
31047
|
+
};
|
|
31048
|
+
}
|
|
31049
|
+
var SYMBOL_KINDS = {
|
|
31050
|
+
1: "File",
|
|
31051
|
+
2: "Module",
|
|
31052
|
+
3: "Namespace",
|
|
31053
|
+
4: "Package",
|
|
31054
|
+
5: "Class",
|
|
31055
|
+
6: "Method",
|
|
31056
|
+
7: "Property",
|
|
31057
|
+
8: "Field",
|
|
31058
|
+
9: "Constructor",
|
|
31059
|
+
10: "Enum",
|
|
31060
|
+
11: "Interface",
|
|
31061
|
+
12: "Function",
|
|
31062
|
+
13: "Variable",
|
|
31063
|
+
14: "Constant",
|
|
31064
|
+
15: "String",
|
|
31065
|
+
16: "Number",
|
|
31066
|
+
17: "Boolean",
|
|
31067
|
+
18: "Array",
|
|
31068
|
+
19: "Object",
|
|
31069
|
+
20: "Key",
|
|
31070
|
+
21: "Null",
|
|
31071
|
+
22: "EnumMember",
|
|
31072
|
+
23: "Struct",
|
|
31073
|
+
24: "Event",
|
|
31074
|
+
25: "Operator",
|
|
31075
|
+
26: "TypeParameter"
|
|
31076
|
+
};
|
|
31077
|
+
var LspManager = class {
|
|
31078
|
+
cwd;
|
|
31079
|
+
spawnImpl;
|
|
31080
|
+
timeoutMs;
|
|
31081
|
+
servers = /* @__PURE__ */ new Map();
|
|
31082
|
+
// language → entry (null = unavailable)
|
|
31083
|
+
constructor(options = {}) {
|
|
31084
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
31085
|
+
this.spawnImpl = options.spawnImpl ?? spawn3;
|
|
31086
|
+
this.timeoutMs = options.timeoutMs ?? 15e3;
|
|
31087
|
+
}
|
|
31088
|
+
/** Lazily start (or reuse) the server for a file's language. Null if none. */
|
|
31089
|
+
getServer(file2) {
|
|
31090
|
+
const cmd = resolveServerCommand(file2, this.cwd);
|
|
31091
|
+
if (!cmd) return null;
|
|
31092
|
+
const cached2 = this.servers.get(cmd.language);
|
|
31093
|
+
if (cached2 !== void 0) return cached2;
|
|
31094
|
+
let entry = null;
|
|
31095
|
+
try {
|
|
31096
|
+
const child = this.spawnImpl(cmd.command, cmd.args, {
|
|
31097
|
+
cwd: this.cwd
|
|
31098
|
+
});
|
|
31099
|
+
const client = new LspClient(processTransport(child), { timeoutMs: this.timeoutMs });
|
|
31100
|
+
const initialized = client.request("initialize", {
|
|
31101
|
+
processId: process.pid,
|
|
31102
|
+
rootUri: pathToUri(this.cwd),
|
|
31103
|
+
capabilities: {},
|
|
31104
|
+
workspaceFolders: [{ uri: pathToUri(this.cwd), name: "root" }]
|
|
31105
|
+
}).then(() => {
|
|
31106
|
+
client.notify("initialized", {});
|
|
31107
|
+
});
|
|
31108
|
+
entry = { client, initialized, opened: /* @__PURE__ */ new Map(), dispose: () => client.dispose() };
|
|
31109
|
+
} catch {
|
|
31110
|
+
entry = null;
|
|
31111
|
+
}
|
|
31112
|
+
this.servers.set(cmd.language, entry);
|
|
31113
|
+
return entry;
|
|
31114
|
+
}
|
|
31115
|
+
/** Ensure a document is open (or synced) on the server. */
|
|
31116
|
+
async openDoc(entry, file2) {
|
|
31117
|
+
await entry.initialized;
|
|
31118
|
+
const uri = pathToUri(file2);
|
|
31119
|
+
let text;
|
|
31120
|
+
try {
|
|
31121
|
+
text = readFileSync5(file2, "utf8");
|
|
31122
|
+
} catch {
|
|
31123
|
+
text = "";
|
|
31124
|
+
}
|
|
31125
|
+
const prev2 = entry.opened.get(uri);
|
|
31126
|
+
if (prev2 === void 0) {
|
|
31127
|
+
entry.client.notify("textDocument/didOpen", {
|
|
31128
|
+
textDocument: { uri, languageId: languageIdForFile(file2), version: 1, text }
|
|
31129
|
+
});
|
|
31130
|
+
entry.opened.set(uri, 1);
|
|
31131
|
+
} else {
|
|
31132
|
+
const version2 = prev2 + 1;
|
|
31133
|
+
entry.client.notify("textDocument/didChange", {
|
|
31134
|
+
textDocument: { uri, version: version2 },
|
|
31135
|
+
contentChanges: [{ text }]
|
|
31136
|
+
});
|
|
31137
|
+
entry.opened.set(uri, version2);
|
|
31138
|
+
}
|
|
31139
|
+
return uri;
|
|
31140
|
+
}
|
|
31141
|
+
async withDoc(file2, fn, fallback) {
|
|
31142
|
+
const entry = this.getServer(file2);
|
|
31143
|
+
if (!entry) return fallback;
|
|
31144
|
+
try {
|
|
31145
|
+
const uri = await this.openDoc(entry, file2);
|
|
31146
|
+
return await fn(entry, uri);
|
|
31147
|
+
} catch {
|
|
31148
|
+
return fallback;
|
|
31149
|
+
}
|
|
31150
|
+
}
|
|
31151
|
+
async definition(file2, line, character) {
|
|
31152
|
+
return this.withDoc(
|
|
31153
|
+
file2,
|
|
31154
|
+
async (entry, uri) => {
|
|
31155
|
+
const res = await entry.client.request("textDocument/definition", {
|
|
31156
|
+
textDocument: { uri },
|
|
31157
|
+
position: { line, character }
|
|
31158
|
+
});
|
|
31159
|
+
return normalizeLocations(res);
|
|
31160
|
+
},
|
|
31161
|
+
[]
|
|
31162
|
+
);
|
|
31163
|
+
}
|
|
31164
|
+
async references(file2, line, character) {
|
|
31165
|
+
return this.withDoc(
|
|
31166
|
+
file2,
|
|
31167
|
+
async (entry, uri) => {
|
|
31168
|
+
const res = await entry.client.request("textDocument/references", {
|
|
31169
|
+
textDocument: { uri },
|
|
31170
|
+
position: { line, character },
|
|
31171
|
+
context: { includeDeclaration: true }
|
|
31172
|
+
});
|
|
31173
|
+
return normalizeLocations(res);
|
|
31174
|
+
},
|
|
31175
|
+
[]
|
|
31176
|
+
);
|
|
31177
|
+
}
|
|
31178
|
+
async hover(file2, line, character) {
|
|
31179
|
+
return this.withDoc(
|
|
31180
|
+
file2,
|
|
31181
|
+
async (entry, uri) => {
|
|
31182
|
+
const res = await entry.client.request("textDocument/hover", {
|
|
31183
|
+
textDocument: { uri },
|
|
31184
|
+
position: { line, character }
|
|
31185
|
+
});
|
|
31186
|
+
return extractHoverText(res);
|
|
31187
|
+
},
|
|
31188
|
+
null
|
|
31189
|
+
);
|
|
31190
|
+
}
|
|
31191
|
+
async documentSymbols(file2) {
|
|
31192
|
+
return this.withDoc(
|
|
31193
|
+
file2,
|
|
31194
|
+
async (entry, uri) => {
|
|
31195
|
+
const res = await entry.client.request("textDocument/documentSymbol", {
|
|
31196
|
+
textDocument: { uri }
|
|
31197
|
+
});
|
|
31198
|
+
return normalizeSymbols(res);
|
|
31199
|
+
},
|
|
31200
|
+
[]
|
|
31201
|
+
);
|
|
31202
|
+
}
|
|
31203
|
+
async rename(file2, line, character, newName) {
|
|
31204
|
+
return this.withDoc(
|
|
31205
|
+
file2,
|
|
31206
|
+
async (entry, uri) => {
|
|
31207
|
+
const res = await entry.client.request("textDocument/rename", {
|
|
31208
|
+
textDocument: { uri },
|
|
31209
|
+
position: { line, character },
|
|
31210
|
+
newName
|
|
31211
|
+
});
|
|
31212
|
+
return normalizeRename(res);
|
|
31213
|
+
},
|
|
31214
|
+
null
|
|
31215
|
+
);
|
|
31216
|
+
}
|
|
31217
|
+
dispose() {
|
|
31218
|
+
for (const entry of this.servers.values()) entry?.dispose();
|
|
31219
|
+
this.servers.clear();
|
|
31220
|
+
}
|
|
31221
|
+
};
|
|
31222
|
+
var shared = null;
|
|
31223
|
+
function getSharedLspManager(cwd = process.cwd()) {
|
|
31224
|
+
if (shared && shared.cwd === cwd) return shared.manager;
|
|
31225
|
+
shared?.manager.dispose();
|
|
31226
|
+
const manager = new LspManager({ cwd });
|
|
31227
|
+
shared = { cwd, manager };
|
|
31228
|
+
return manager;
|
|
31229
|
+
}
|
|
31230
|
+
if (typeof process !== "undefined" && typeof process.once === "function") {
|
|
31231
|
+
process.once("exit", () => {
|
|
31232
|
+
try {
|
|
31233
|
+
shared?.manager.dispose();
|
|
31234
|
+
} catch {
|
|
31235
|
+
}
|
|
31236
|
+
});
|
|
31237
|
+
}
|
|
31238
|
+
function normalizeLocations(res) {
|
|
31239
|
+
if (!res) return [];
|
|
31240
|
+
const arr = Array.isArray(res) ? res : [res];
|
|
31241
|
+
const out = [];
|
|
31242
|
+
for (const item of arr) {
|
|
31243
|
+
if (!item || typeof item !== "object") continue;
|
|
31244
|
+
const loc = item;
|
|
31245
|
+
const uri = loc.uri ?? loc.targetUri;
|
|
31246
|
+
const range = loc.range ?? loc.targetRange;
|
|
31247
|
+
if (typeof uri === "string" && range) out.push({ uri, range });
|
|
31248
|
+
}
|
|
31249
|
+
return out;
|
|
31250
|
+
}
|
|
31251
|
+
function extractHoverText(res) {
|
|
31252
|
+
if (!res || !res.contents) return null;
|
|
31253
|
+
const c = res.contents;
|
|
31254
|
+
if (typeof c === "string") return c.trim() || null;
|
|
31255
|
+
if (Array.isArray(c)) {
|
|
31256
|
+
return c.map((x) => typeof x === "string" ? x : x?.value ?? "").filter(Boolean).join("\n").trim() || null;
|
|
31257
|
+
}
|
|
31258
|
+
if (typeof c === "object" && "value" in c) {
|
|
31259
|
+
return (c.value ?? "").trim() || null;
|
|
31260
|
+
}
|
|
31261
|
+
return null;
|
|
31262
|
+
}
|
|
31263
|
+
function normalizeSymbols(res) {
|
|
31264
|
+
if (!Array.isArray(res)) return [];
|
|
31265
|
+
const out = [];
|
|
31266
|
+
const visit = (nodes) => {
|
|
31267
|
+
for (const n of nodes) {
|
|
31268
|
+
if (!n || typeof n !== "object") continue;
|
|
31269
|
+
const s = n;
|
|
31270
|
+
const line0 = s.range?.start?.line ?? s.location?.range?.start?.line;
|
|
31271
|
+
if (typeof s.name === "string" && typeof line0 === "number") {
|
|
31272
|
+
out.push({ name: s.name, kind: SYMBOL_KINDS[s.kind ?? 0] ?? "Symbol", line: line0 + 1 });
|
|
31273
|
+
}
|
|
31274
|
+
if (Array.isArray(s.children)) visit(s.children);
|
|
31275
|
+
}
|
|
31276
|
+
};
|
|
31277
|
+
visit(res);
|
|
31278
|
+
return out;
|
|
31279
|
+
}
|
|
31280
|
+
function normalizeRename(res) {
|
|
31281
|
+
if (!res) return null;
|
|
31282
|
+
const files = [];
|
|
31283
|
+
let total = 0;
|
|
31284
|
+
if (res.changes && typeof res.changes === "object") {
|
|
31285
|
+
for (const [uri, edits] of Object.entries(res.changes)) {
|
|
31286
|
+
const count = Array.isArray(edits) ? edits.length : 0;
|
|
31287
|
+
files.push({ file: uriToPath(uri), count });
|
|
31288
|
+
total += count;
|
|
31289
|
+
}
|
|
31290
|
+
}
|
|
31291
|
+
if (Array.isArray(res.documentChanges)) {
|
|
31292
|
+
for (const dc of res.documentChanges) {
|
|
31293
|
+
const d = dc;
|
|
31294
|
+
if (d?.textDocument?.uri) {
|
|
31295
|
+
const count = Array.isArray(d.edits) ? d.edits.length : 0;
|
|
31296
|
+
files.push({ file: uriToPath(d.textDocument.uri), count });
|
|
31297
|
+
total += count;
|
|
31298
|
+
}
|
|
31299
|
+
}
|
|
31300
|
+
}
|
|
31301
|
+
if (files.length === 0) return null;
|
|
31302
|
+
return { files, totalEdits: total };
|
|
31303
|
+
}
|
|
31304
|
+
|
|
31305
|
+
// src/cli/ast/tools.ts
|
|
31306
|
+
init_zod();
|
|
31307
|
+
init_toolTypes();
|
|
31308
|
+
|
|
31309
|
+
// src/cli/ast/engine.ts
|
|
31310
|
+
import { readFile } from "node:fs/promises";
|
|
31311
|
+
import path18 from "node:path";
|
|
31312
|
+
var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
31313
|
+
function isAstSupported(file2) {
|
|
31314
|
+
return TS_EXTENSIONS.has(path18.extname(file2).toLowerCase());
|
|
31315
|
+
}
|
|
31316
|
+
var tsPromise;
|
|
31317
|
+
function loadTs() {
|
|
31318
|
+
if (!tsPromise) {
|
|
31319
|
+
tsPromise = import("typescript").then((m) => m.default ?? m).catch(() => null);
|
|
31320
|
+
}
|
|
31321
|
+
return tsPromise;
|
|
31322
|
+
}
|
|
31323
|
+
async function parseFileSymbols(file2) {
|
|
31324
|
+
if (!isAstSupported(file2)) return [];
|
|
31325
|
+
const ts = await loadTs();
|
|
31326
|
+
if (!ts) return [];
|
|
31327
|
+
let text;
|
|
31328
|
+
try {
|
|
31329
|
+
text = await readFile(file2, "utf8");
|
|
31330
|
+
} catch {
|
|
31331
|
+
return [];
|
|
31332
|
+
}
|
|
31333
|
+
let source;
|
|
31334
|
+
try {
|
|
31335
|
+
source = ts.createSourceFile(path18.basename(file2), text, ts.ScriptTarget.Latest, true);
|
|
31336
|
+
} catch {
|
|
31337
|
+
return [];
|
|
31338
|
+
}
|
|
31339
|
+
const out = [];
|
|
31340
|
+
const lineOf = (pos) => source.getLineAndCharacterOfPosition(pos).line + 1;
|
|
31341
|
+
const hasExport = (node) => {
|
|
31342
|
+
const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0;
|
|
31343
|
+
return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
31344
|
+
};
|
|
31345
|
+
const record2 = (name, kind, node, exported) => {
|
|
31346
|
+
out.push({
|
|
31347
|
+
name,
|
|
31348
|
+
kind,
|
|
31349
|
+
line: lineOf(node.getStart(source)),
|
|
31350
|
+
endLine: lineOf(node.getEnd()),
|
|
31351
|
+
exported,
|
|
31352
|
+
text: node.getText(source)
|
|
31353
|
+
});
|
|
31354
|
+
};
|
|
31355
|
+
const visit = (node) => {
|
|
31356
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
31357
|
+
record2(node.name.text, "function", node, hasExport(node));
|
|
31358
|
+
} else if (ts.isClassDeclaration(node) && node.name) {
|
|
31359
|
+
record2(node.name.text, "class", node, hasExport(node));
|
|
31360
|
+
for (const member of node.members) {
|
|
31361
|
+
if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
|
|
31362
|
+
record2(member.name.text, "method", member, false);
|
|
31363
|
+
}
|
|
31364
|
+
}
|
|
31365
|
+
} else if (ts.isInterfaceDeclaration(node)) {
|
|
31366
|
+
record2(node.name.text, "interface", node, hasExport(node));
|
|
31367
|
+
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
31368
|
+
record2(node.name.text, "type", node, hasExport(node));
|
|
31369
|
+
} else if (ts.isEnumDeclaration(node)) {
|
|
31370
|
+
record2(node.name.text, "enum", node, hasExport(node));
|
|
31371
|
+
} else if (ts.isVariableStatement(node)) {
|
|
31372
|
+
const exported = hasExport(node);
|
|
31373
|
+
for (const decl of node.declarationList.declarations) {
|
|
31374
|
+
if (!ts.isIdentifier(decl.name)) continue;
|
|
31375
|
+
const init = decl.initializer;
|
|
31376
|
+
const isFn = !!init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init));
|
|
31377
|
+
record2(decl.name.text, isFn ? "function" : "variable", node, exported);
|
|
31378
|
+
}
|
|
31379
|
+
}
|
|
31380
|
+
ts.forEachChild(node, visit);
|
|
31381
|
+
};
|
|
31382
|
+
visit(source);
|
|
31383
|
+
return out;
|
|
31384
|
+
}
|
|
31385
|
+
async function astOutline(file2) {
|
|
31386
|
+
const symbols = await parseFileSymbols(file2);
|
|
31387
|
+
return symbols.map(({ text: _text, ...rest }) => rest);
|
|
31388
|
+
}
|
|
31389
|
+
async function findSymbol(file2, name) {
|
|
31390
|
+
const symbols = await parseFileSymbols(file2);
|
|
31391
|
+
return symbols.find((s) => s.name === name) ?? null;
|
|
31392
|
+
}
|
|
31393
|
+
|
|
31394
|
+
// src/cli/ast/tools.ts
|
|
31395
|
+
var MAX_TEXT_CHARS = 4e3;
|
|
31396
|
+
function createAstTools() {
|
|
31397
|
+
const outline = {
|
|
31398
|
+
name: "ast_outline",
|
|
31399
|
+
description: "Structural outline of a TS/JS file: every declaration (function, class, method, interface, type, enum, variable) with its line range and whether it's exported. Faster and more precise than reading the whole file to find where things are. TS/JS only.",
|
|
31400
|
+
permissions: ["read"],
|
|
31401
|
+
inputSchema: external_exports.object({
|
|
31402
|
+
path: external_exports.string().min(1).describe("Path to the TS/JS file to outline.")
|
|
31403
|
+
}),
|
|
31404
|
+
execute: async (args) => {
|
|
31405
|
+
const { path: file2 } = args;
|
|
31406
|
+
const symbols = await astOutline(file2);
|
|
31407
|
+
if (symbols.length === 0) {
|
|
31408
|
+
return typedOk({ symbols: [], note: "no declarations found (or not a TS/JS file / TypeScript unavailable)" });
|
|
31409
|
+
}
|
|
31410
|
+
return typedOk({
|
|
31411
|
+
count: symbols.length,
|
|
31412
|
+
symbols: symbols.map(
|
|
31413
|
+
(s) => `${s.exported ? "export " : ""}${s.kind} ${s.name} (lines ${s.line}-${s.endLine})`
|
|
31414
|
+
)
|
|
31415
|
+
});
|
|
31416
|
+
}
|
|
31417
|
+
};
|
|
31418
|
+
const findSymbolTool = {
|
|
31419
|
+
name: "find_symbol",
|
|
31420
|
+
description: "Locate a named declaration in a TS/JS file and return its EXACT source text and line range. Use this to grab a function/class/method verbatim so you can edit_file it reliably (node-accurate) instead of guessing the surrounding text. TS/JS only.",
|
|
31421
|
+
permissions: ["read"],
|
|
31422
|
+
inputSchema: external_exports.object({
|
|
31423
|
+
path: external_exports.string().min(1).describe("Path to the TS/JS file."),
|
|
31424
|
+
name: external_exports.string().min(1).describe("The declaration name to find (function/class/method/etc).")
|
|
31425
|
+
}),
|
|
31426
|
+
execute: async (args) => {
|
|
31427
|
+
const { path: file2, name } = args;
|
|
31428
|
+
const sym = await findSymbol(file2, name);
|
|
31429
|
+
if (!sym) {
|
|
31430
|
+
return typedOk({ found: false, note: `no declaration named "${name}" found in ${file2}` });
|
|
31431
|
+
}
|
|
31432
|
+
const truncated = sym.text.length > MAX_TEXT_CHARS;
|
|
31433
|
+
return typedOk({
|
|
31434
|
+
found: true,
|
|
31435
|
+
kind: sym.kind,
|
|
31436
|
+
exported: sym.exported,
|
|
31437
|
+
line: sym.line,
|
|
31438
|
+
endLine: sym.endLine,
|
|
31439
|
+
text: truncated ? `${sym.text.slice(0, MAX_TEXT_CHARS)}
|
|
31440
|
+
\u2026 (truncated, ${sym.text.length} chars total)` : sym.text
|
|
31441
|
+
});
|
|
31442
|
+
}
|
|
31443
|
+
};
|
|
31444
|
+
return [outline, findSymbolTool];
|
|
31445
|
+
}
|
|
31446
|
+
|
|
31447
|
+
// src/cli/semantic/tools.ts
|
|
31448
|
+
init_zod();
|
|
31449
|
+
init_toolTypes();
|
|
31450
|
+
import path20 from "node:path";
|
|
31451
|
+
|
|
31452
|
+
// src/cli/semantic/index.ts
|
|
31453
|
+
import { promises as fs12, existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
31454
|
+
import { homedir as homedir3 } from "node:os";
|
|
31455
|
+
import path19 from "node:path";
|
|
31456
|
+
import { createHash } from "node:crypto";
|
|
31457
|
+
|
|
31458
|
+
// src/cli/semantic/store.ts
|
|
31459
|
+
function chunkFile(file2, text, opts = {}) {
|
|
31460
|
+
const maxLines = Math.max(1, opts.maxLines ?? 40);
|
|
31461
|
+
const overlap = Math.max(0, Math.min(opts.overlap ?? 8, maxLines - 1));
|
|
31462
|
+
const lines = text.split("\n");
|
|
31463
|
+
const step = maxLines - overlap;
|
|
31464
|
+
const chunks = [];
|
|
31465
|
+
for (let start = 0; start < lines.length; start += step) {
|
|
31466
|
+
const end = Math.min(start + maxLines, lines.length);
|
|
31467
|
+
const slice = lines.slice(start, end);
|
|
31468
|
+
if (slice.join("").trim().length > 0) {
|
|
31469
|
+
chunks.push({
|
|
31470
|
+
file: file2,
|
|
31471
|
+
startLine: start + 1,
|
|
31472
|
+
endLine: end,
|
|
31473
|
+
text: slice.join("\n")
|
|
31474
|
+
});
|
|
31475
|
+
}
|
|
31476
|
+
if (end >= lines.length) break;
|
|
31477
|
+
}
|
|
31478
|
+
return chunks;
|
|
31479
|
+
}
|
|
31480
|
+
function cosineSimilarity(a, b) {
|
|
31481
|
+
if (a.length === 0 || a.length !== b.length) return 0;
|
|
31482
|
+
let dot = 0;
|
|
31483
|
+
let na = 0;
|
|
31484
|
+
let nb = 0;
|
|
31485
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
31486
|
+
dot += a[i] * b[i];
|
|
31487
|
+
na += a[i] * a[i];
|
|
31488
|
+
nb += b[i] * b[i];
|
|
31489
|
+
}
|
|
31490
|
+
if (na === 0 || nb === 0) return 0;
|
|
31491
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
31492
|
+
}
|
|
31493
|
+
function searchIndex(data, queryEmbedding, k = 8) {
|
|
31494
|
+
const scored = [];
|
|
31495
|
+
for (const chunk of data.chunks) {
|
|
31496
|
+
const score = cosineSimilarity(queryEmbedding, chunk.embedding);
|
|
31497
|
+
scored.push({
|
|
31498
|
+
file: chunk.file,
|
|
31499
|
+
startLine: chunk.startLine,
|
|
31500
|
+
endLine: chunk.endLine,
|
|
31501
|
+
text: chunk.text,
|
|
31502
|
+
score
|
|
31503
|
+
});
|
|
31504
|
+
}
|
|
31505
|
+
scored.sort((a, b) => b.score - a.score);
|
|
31506
|
+
return scored.slice(0, Math.max(0, k));
|
|
31507
|
+
}
|
|
31508
|
+
|
|
31509
|
+
// src/cli/semantic/index.ts
|
|
31510
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
31511
|
+
".ts",
|
|
31512
|
+
".tsx",
|
|
31513
|
+
".js",
|
|
31514
|
+
".jsx",
|
|
31515
|
+
".mjs",
|
|
31516
|
+
".cjs",
|
|
31517
|
+
".py",
|
|
31518
|
+
".go",
|
|
31519
|
+
".rs",
|
|
31520
|
+
".java",
|
|
31521
|
+
".rb",
|
|
31522
|
+
".php",
|
|
31523
|
+
".c",
|
|
31524
|
+
".h",
|
|
31525
|
+
".cc",
|
|
31526
|
+
".cpp",
|
|
31527
|
+
".hpp",
|
|
31528
|
+
".cs",
|
|
31529
|
+
".swift",
|
|
31530
|
+
".kt",
|
|
31531
|
+
".scala",
|
|
31532
|
+
".sh",
|
|
31533
|
+
".md"
|
|
31534
|
+
]);
|
|
31535
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
31536
|
+
"node_modules",
|
|
31537
|
+
".git",
|
|
31538
|
+
"dist",
|
|
31539
|
+
"build",
|
|
31540
|
+
"out",
|
|
31541
|
+
"coverage",
|
|
31542
|
+
".next",
|
|
31543
|
+
".turbo",
|
|
31544
|
+
".cache",
|
|
31545
|
+
"vendor",
|
|
31546
|
+
"__pycache__",
|
|
31547
|
+
".venv",
|
|
31548
|
+
"venv",
|
|
31549
|
+
".tmp"
|
|
31550
|
+
]);
|
|
31551
|
+
function getIndexPath(root) {
|
|
31552
|
+
const hash3 = createHash("sha1").update(path19.resolve(root)).digest("hex").slice(0, 16);
|
|
31553
|
+
return process.env.ZELARI_SEMANTIC_FILE ?? path19.join(homedir3(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
31554
|
+
}
|
|
31555
|
+
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
31556
|
+
const out = [];
|
|
31557
|
+
const walk2 = async (dir) => {
|
|
31558
|
+
if (out.length >= maxFiles) return;
|
|
31559
|
+
let entries;
|
|
31560
|
+
try {
|
|
31561
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
31562
|
+
} catch {
|
|
31563
|
+
return;
|
|
31564
|
+
}
|
|
31565
|
+
for (const entry of entries) {
|
|
31566
|
+
if (out.length >= maxFiles) return;
|
|
31567
|
+
if (entry.name.startsWith(".") && entry.name !== ".") {
|
|
31568
|
+
if (entry.isDirectory() && IGNORE_DIRS.has(entry.name)) continue;
|
|
31569
|
+
if (entry.isDirectory()) continue;
|
|
31570
|
+
}
|
|
31571
|
+
const full = path19.join(dir, entry.name);
|
|
31572
|
+
if (entry.isDirectory()) {
|
|
31573
|
+
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
31574
|
+
await walk2(full);
|
|
31575
|
+
} else if (SOURCE_EXTENSIONS.has(path19.extname(entry.name).toLowerCase())) {
|
|
31576
|
+
out.push(full);
|
|
31577
|
+
}
|
|
31578
|
+
}
|
|
31579
|
+
};
|
|
31580
|
+
await walk2(root);
|
|
31581
|
+
return out;
|
|
31582
|
+
}
|
|
31583
|
+
async function buildIndex(files, embed, options) {
|
|
31584
|
+
const batchSize = options.batchSize ?? 64;
|
|
31585
|
+
const maxChunkChars = options.maxChunkChars ?? 8e3;
|
|
31586
|
+
const chunks = [];
|
|
31587
|
+
let filesIndexed = 0;
|
|
31588
|
+
for (const file2 of files) {
|
|
31589
|
+
let text;
|
|
31590
|
+
try {
|
|
31591
|
+
text = await fs12.readFile(file2, "utf8");
|
|
31592
|
+
} catch {
|
|
31593
|
+
continue;
|
|
31594
|
+
}
|
|
31595
|
+
const fileChunks = chunkFile(file2, text, options).filter((c) => c.text.length <= maxChunkChars);
|
|
31596
|
+
if (fileChunks.length > 0) filesIndexed += 1;
|
|
31597
|
+
chunks.push(...fileChunks);
|
|
31598
|
+
}
|
|
31599
|
+
if (chunks.length === 0) {
|
|
31600
|
+
return { error: "no indexable source found", filesIndexed: 0, chunksIndexed: 0 };
|
|
31601
|
+
}
|
|
31602
|
+
const indexed = [];
|
|
31603
|
+
for (let i = 0; i < chunks.length; i += batchSize) {
|
|
31604
|
+
const batch = chunks.slice(i, i + batchSize);
|
|
31605
|
+
const res = await embed(batch.map((c) => c.text));
|
|
31606
|
+
if ("error" in res) {
|
|
31607
|
+
return { error: res.error, filesIndexed, chunksIndexed: 0 };
|
|
31608
|
+
}
|
|
31609
|
+
if (res.length !== batch.length) {
|
|
31610
|
+
return { error: "embedding count mismatch", filesIndexed, chunksIndexed: 0 };
|
|
31611
|
+
}
|
|
31612
|
+
batch.forEach((c, j) => indexed.push({ ...c, embedding: res[j] }));
|
|
31613
|
+
}
|
|
31614
|
+
const data = {
|
|
31615
|
+
model: options.model,
|
|
31616
|
+
dim: indexed[0]?.embedding.length ?? 0,
|
|
31617
|
+
chunks: indexed,
|
|
31618
|
+
builtAt: Date.now()
|
|
31619
|
+
};
|
|
31620
|
+
return { data, filesIndexed, chunksIndexed: indexed.length };
|
|
31621
|
+
}
|
|
31622
|
+
async function saveIndex(root, data) {
|
|
31623
|
+
const file2 = getIndexPath(root);
|
|
31624
|
+
await fs12.mkdir(path19.dirname(file2), { recursive: true });
|
|
31625
|
+
const tmp = `${file2}.tmp-${process.pid}`;
|
|
31626
|
+
await fs12.writeFile(tmp, JSON.stringify(data), "utf8");
|
|
31627
|
+
await fs12.rename(tmp, file2);
|
|
31628
|
+
}
|
|
31629
|
+
function loadIndex(root) {
|
|
31630
|
+
const file2 = getIndexPath(root);
|
|
31631
|
+
if (!existsSync8(file2)) return null;
|
|
31632
|
+
try {
|
|
31633
|
+
const parsed = JSON.parse(readFileSync6(file2, "utf8"));
|
|
31634
|
+
if (parsed && Array.isArray(parsed.chunks)) return parsed;
|
|
31635
|
+
} catch {
|
|
31636
|
+
}
|
|
31637
|
+
return null;
|
|
31638
|
+
}
|
|
31639
|
+
async function semanticSearch(root, query, embed, k = 8) {
|
|
31640
|
+
const data = loadIndex(root);
|
|
31641
|
+
if (!data) return { error: "no semantic index \u2014 run /index first" };
|
|
31642
|
+
const res = await embed([query]);
|
|
31643
|
+
if ("error" in res) return { error: res.error };
|
|
31644
|
+
const vector = res[0];
|
|
31645
|
+
if (!vector) return { error: "query embedding failed" };
|
|
31646
|
+
return { hits: searchIndex(data, vector, k) };
|
|
31647
|
+
}
|
|
31648
|
+
|
|
31649
|
+
// src/cli/semantic/provider.ts
|
|
31650
|
+
init_openai_compatible();
|
|
31651
|
+
|
|
31652
|
+
// src/cli/semantic/embeddings.ts
|
|
31653
|
+
function parseEmbeddingsResponse(json2, expected) {
|
|
31654
|
+
if (!json2 || typeof json2 !== "object") return null;
|
|
31655
|
+
const data = json2.data;
|
|
31656
|
+
if (!Array.isArray(data)) return null;
|
|
31657
|
+
const rows = [];
|
|
31658
|
+
for (const item of data) {
|
|
31659
|
+
if (!item || typeof item !== "object") continue;
|
|
31660
|
+
const it = item;
|
|
31661
|
+
if (!Array.isArray(it.embedding)) continue;
|
|
31662
|
+
const embedding = it.embedding.filter((n) => typeof n === "number");
|
|
31663
|
+
if (embedding.length === 0) continue;
|
|
31664
|
+
rows.push({ index: typeof it.index === "number" ? it.index : rows.length, embedding });
|
|
31665
|
+
}
|
|
31666
|
+
if (rows.length === 0) return null;
|
|
31667
|
+
rows.sort((a, b) => a.index - b.index);
|
|
31668
|
+
const out = rows.map((r) => r.embedding);
|
|
31669
|
+
return out.length === expected ? out : null;
|
|
31670
|
+
}
|
|
31671
|
+
async function embedTexts(texts, config2, fetchImpl = fetch) {
|
|
31672
|
+
if (texts.length === 0) return { embeddings: [] };
|
|
31673
|
+
const url2 = `${config2.baseUrl.replace(/\/$/, "")}/embeddings`;
|
|
31674
|
+
let response;
|
|
31675
|
+
try {
|
|
31676
|
+
response = await fetchImpl(url2, {
|
|
31677
|
+
method: "POST",
|
|
31678
|
+
headers: {
|
|
31679
|
+
"Content-Type": "application/json",
|
|
31680
|
+
Authorization: `Bearer ${config2.apiKey}`
|
|
31681
|
+
},
|
|
31682
|
+
body: JSON.stringify({ model: config2.model, input: texts })
|
|
31683
|
+
});
|
|
31684
|
+
} catch (err) {
|
|
31685
|
+
return { error: `network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}` };
|
|
31686
|
+
}
|
|
31687
|
+
if (!response.ok) {
|
|
31688
|
+
const body = await response.text().catch(() => "");
|
|
31689
|
+
return { error: `HTTP ${response.status} from ${url2}: ${body.slice(0, 160)}` };
|
|
31690
|
+
}
|
|
31691
|
+
let json2;
|
|
31692
|
+
try {
|
|
31693
|
+
json2 = await response.json();
|
|
31694
|
+
} catch (err) {
|
|
31695
|
+
return { error: `invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}` };
|
|
31696
|
+
}
|
|
31697
|
+
const embeddings = parseEmbeddingsResponse(json2, texts.length);
|
|
31698
|
+
if (!embeddings) return { error: `unexpected embeddings response shape from ${url2}` };
|
|
31699
|
+
return { embeddings };
|
|
31700
|
+
}
|
|
31701
|
+
|
|
31702
|
+
// src/cli/semantic/provider.ts
|
|
31703
|
+
var DEFAULT_EMBED_MODEL = "text-embedding-3-small";
|
|
31704
|
+
function embedModel() {
|
|
31705
|
+
return process.env.ZELARI_EMBED_MODEL ?? DEFAULT_EMBED_MODEL;
|
|
31706
|
+
}
|
|
31707
|
+
async function buildProviderEmbedFn() {
|
|
31708
|
+
const cfg = await providerFromEnv();
|
|
31709
|
+
if (!cfg) return null;
|
|
31710
|
+
const embedCfg = { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, model: embedModel() };
|
|
31711
|
+
return async (texts) => {
|
|
31712
|
+
const res = await embedTexts(texts, embedCfg);
|
|
31713
|
+
return "error" in res ? { error: res.error } : res.embeddings;
|
|
31714
|
+
};
|
|
31715
|
+
}
|
|
31716
|
+
|
|
31717
|
+
// src/cli/semantic/tools.ts
|
|
31718
|
+
function createSemanticTool(deps) {
|
|
31719
|
+
const buildEmbedFn = deps.buildEmbedFn ?? buildProviderEmbedFn;
|
|
31720
|
+
return {
|
|
31721
|
+
name: "semantic_search",
|
|
31722
|
+
description: 'Concept-level search over the indexed codebase: describe what you are looking for in plain language ("where is rate-limit backoff handled?") and get the most relevant code chunks (file:line + snippet), even when they share no exact keyword with your query. Requires an index \u2014 if none exists, ask the user to run /index. Complements grep_content (exact matches).',
|
|
31723
|
+
permissions: ["read"],
|
|
31724
|
+
inputSchema: external_exports.object({
|
|
31725
|
+
query: external_exports.string().min(1).describe("Natural-language description of the code you want."),
|
|
31726
|
+
k: external_exports.number().int().positive().max(25).optional().describe("Max results (default 8).")
|
|
31727
|
+
}),
|
|
31728
|
+
execute: async (args) => {
|
|
31729
|
+
const { query, k } = args;
|
|
31730
|
+
if (!loadIndex(deps.root)) {
|
|
31731
|
+
return typedOk({ results: [], note: "no semantic index yet \u2014 run /index to build one, then retry" });
|
|
31732
|
+
}
|
|
31733
|
+
const embed = await buildEmbedFn();
|
|
31734
|
+
if (!embed) {
|
|
31735
|
+
return typedOk({ results: [], note: "no provider/API key configured for embeddings" });
|
|
31736
|
+
}
|
|
31737
|
+
const res = await semanticSearch(deps.root, query, embed, k ?? 8);
|
|
31738
|
+
if ("error" in res) {
|
|
31739
|
+
return typedOk({ results: [], note: `semantic search unavailable: ${res.error}` });
|
|
31740
|
+
}
|
|
31741
|
+
return typedOk({
|
|
31742
|
+
count: res.hits.length,
|
|
31743
|
+
results: res.hits.map((h) => ({
|
|
31744
|
+
location: `${path20.relative(deps.root, h.file) || h.file}:${h.startLine}-${h.endLine}`,
|
|
31745
|
+
score: Number(h.score.toFixed(3)),
|
|
31746
|
+
preview: h.text.length > 400 ? `${h.text.slice(0, 400)}\u2026` : h.text
|
|
31747
|
+
}))
|
|
31748
|
+
});
|
|
31749
|
+
}
|
|
31750
|
+
};
|
|
31751
|
+
}
|
|
31752
|
+
|
|
31753
|
+
// src/cli/browser/tools.ts
|
|
31754
|
+
init_zod();
|
|
31755
|
+
init_toolTypes();
|
|
31756
|
+
import path21 from "node:path";
|
|
31757
|
+
import os7 from "node:os";
|
|
31758
|
+
|
|
31759
|
+
// src/cli/browser/driver.ts
|
|
31760
|
+
var defaultPlaywrightLoader = async () => {
|
|
31761
|
+
try {
|
|
31762
|
+
const pkg = "playwright";
|
|
31763
|
+
const mod = await import(pkg);
|
|
31764
|
+
if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
|
|
31765
|
+
return null;
|
|
31766
|
+
} catch {
|
|
31767
|
+
return null;
|
|
31768
|
+
}
|
|
31769
|
+
};
|
|
31770
|
+
async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
|
|
31771
|
+
const consoleErrors = [];
|
|
31772
|
+
const pageErrors = [];
|
|
31773
|
+
const failedRequests = [];
|
|
31774
|
+
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
31775
|
+
const pw = await loader();
|
|
31776
|
+
if (!pw) {
|
|
31777
|
+
return {
|
|
31778
|
+
...base,
|
|
31779
|
+
error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
|
|
31780
|
+
};
|
|
31781
|
+
}
|
|
31782
|
+
const timeout = options.timeoutMs ?? 15e3;
|
|
31783
|
+
let browser;
|
|
31784
|
+
try {
|
|
31785
|
+
browser = await pw.chromium.launch({ headless: true });
|
|
31786
|
+
const page = await browser.newPage();
|
|
31787
|
+
page.on("console", (msg) => {
|
|
31788
|
+
if (msg.type() === "error") consoleErrors.push(msg.text());
|
|
31789
|
+
});
|
|
31790
|
+
page.on("pageerror", (err) => pageErrors.push(err.message));
|
|
31791
|
+
page.on("requestfailed", (req) => {
|
|
31792
|
+
const f = req.failure();
|
|
31793
|
+
failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
|
|
31794
|
+
});
|
|
31795
|
+
await page.goto(options.url, { waitUntil: "load", timeout });
|
|
31796
|
+
for (const action of options.actions ?? []) {
|
|
31797
|
+
switch (action.type) {
|
|
31798
|
+
case "click":
|
|
31799
|
+
await page.click(action.selector, { timeout });
|
|
31800
|
+
break;
|
|
31801
|
+
case "fill":
|
|
31802
|
+
await page.fill(action.selector, action.value, { timeout });
|
|
31803
|
+
break;
|
|
31804
|
+
case "goto":
|
|
31805
|
+
await page.goto(action.url, { waitUntil: "load", timeout });
|
|
31806
|
+
break;
|
|
31807
|
+
case "wait":
|
|
31808
|
+
await page.waitForTimeout(action.ms);
|
|
31809
|
+
break;
|
|
31810
|
+
default:
|
|
31811
|
+
break;
|
|
31812
|
+
}
|
|
31813
|
+
}
|
|
31814
|
+
let selectorFound;
|
|
31815
|
+
if (options.waitForSelector) {
|
|
31816
|
+
try {
|
|
31817
|
+
await page.waitForSelector(options.waitForSelector, { timeout });
|
|
31818
|
+
selectorFound = true;
|
|
31819
|
+
} catch {
|
|
31820
|
+
selectorFound = false;
|
|
31821
|
+
}
|
|
31822
|
+
}
|
|
31823
|
+
let screenshotPath;
|
|
31824
|
+
if (options.screenshotPath) {
|
|
31825
|
+
try {
|
|
31826
|
+
await page.screenshot({ path: options.screenshotPath });
|
|
31827
|
+
screenshotPath = options.screenshotPath;
|
|
31828
|
+
} catch {
|
|
31829
|
+
}
|
|
31830
|
+
}
|
|
31831
|
+
const title = await page.title().catch(() => void 0);
|
|
31832
|
+
return {
|
|
31833
|
+
ok: true,
|
|
31834
|
+
consoleErrors,
|
|
31835
|
+
pageErrors,
|
|
31836
|
+
failedRequests,
|
|
31837
|
+
url: page.url(),
|
|
31838
|
+
...title !== void 0 ? { title } : {},
|
|
31839
|
+
...selectorFound !== void 0 ? { selectorFound } : {},
|
|
31840
|
+
...screenshotPath ? { screenshotPath } : {}
|
|
31841
|
+
};
|
|
31842
|
+
} catch (err) {
|
|
31843
|
+
return { ...base, error: err instanceof Error ? err.message : String(err) };
|
|
31844
|
+
} finally {
|
|
31845
|
+
try {
|
|
31846
|
+
await browser?.close();
|
|
31847
|
+
} catch {
|
|
31848
|
+
}
|
|
31849
|
+
}
|
|
31850
|
+
}
|
|
31851
|
+
|
|
31852
|
+
// src/cli/browser/tools.ts
|
|
31853
|
+
var ActionSchema = external_exports.discriminatedUnion("type", [
|
|
31854
|
+
external_exports.object({ type: external_exports.literal("click"), selector: external_exports.string().min(1) }),
|
|
31855
|
+
external_exports.object({ type: external_exports.literal("fill"), selector: external_exports.string().min(1), value: external_exports.string() }),
|
|
31856
|
+
external_exports.object({ type: external_exports.literal("wait"), ms: external_exports.number().int().positive().max(3e4) }),
|
|
31857
|
+
external_exports.object({ type: external_exports.literal("goto"), url: external_exports.string().min(1) })
|
|
31858
|
+
]);
|
|
31859
|
+
function createBrowserTool(deps = {}) {
|
|
31860
|
+
return {
|
|
31861
|
+
name: "browser_check",
|
|
31862
|
+
description: 'Open a URL in a headless browser to VERIFY a web change: optionally run click/fill/goto/wait actions, then report console errors, uncaught page exceptions, failed network requests, the final title/URL, whether an expected selector appeared, and a screenshot path. Use it to confirm UI edits actually render and run \u2014 stronger than "tests pass" for front-end. Requires Playwright (optional dependency).',
|
|
31863
|
+
permissions: ["network"],
|
|
31864
|
+
// A browser launch + navigation can take a while.
|
|
31865
|
+
timeoutMs: 6e4,
|
|
31866
|
+
inputSchema: external_exports.object({
|
|
31867
|
+
url: external_exports.string().min(1).describe("URL to open (e.g. http://localhost:3000)."),
|
|
31868
|
+
actions: external_exports.array(ActionSchema).optional().describe("Optional sequence of interactions before checking."),
|
|
31869
|
+
waitForSelector: external_exports.string().optional().describe("Assert this CSS selector is present after actions."),
|
|
31870
|
+
screenshot: external_exports.boolean().optional().describe("Save a screenshot (default true).")
|
|
31871
|
+
}),
|
|
31872
|
+
execute: async (args) => {
|
|
31873
|
+
const a = args;
|
|
31874
|
+
const dir = deps.screenshotDir ?? os7.tmpdir();
|
|
31875
|
+
const screenshotPath = a.screenshot === false ? void 0 : path21.join(dir, `zelari-browser-${Date.now()}.png`);
|
|
31876
|
+
const result = await runBrowserCheck(
|
|
31877
|
+
{
|
|
31878
|
+
url: a.url,
|
|
31879
|
+
...a.actions ? { actions: a.actions } : {},
|
|
31880
|
+
...a.waitForSelector ? { waitForSelector: a.waitForSelector } : {},
|
|
31881
|
+
...screenshotPath ? { screenshotPath } : {}
|
|
31882
|
+
},
|
|
31883
|
+
deps.loader
|
|
31884
|
+
);
|
|
31885
|
+
if (!result.ok) {
|
|
31886
|
+
return typedOk({ ok: false, note: result.error ?? "browser check failed" });
|
|
31887
|
+
}
|
|
31888
|
+
const clean = result.consoleErrors.length === 0 && result.pageErrors.length === 0 && result.failedRequests.length === 0 && result.selectorFound !== false;
|
|
31889
|
+
return typedOk({
|
|
31890
|
+
ok: true,
|
|
31891
|
+
clean,
|
|
31892
|
+
title: result.title,
|
|
31893
|
+
url: result.url,
|
|
31894
|
+
consoleErrors: result.consoleErrors,
|
|
31895
|
+
pageErrors: result.pageErrors,
|
|
31896
|
+
failedRequests: result.failedRequests,
|
|
31897
|
+
...result.selectorFound !== void 0 ? { selectorFound: result.selectorFound } : {},
|
|
31898
|
+
...result.screenshotPath ? { screenshotPath: result.screenshotPath } : {}
|
|
31899
|
+
});
|
|
31900
|
+
}
|
|
31901
|
+
};
|
|
31902
|
+
}
|
|
31903
|
+
|
|
30122
31904
|
// src/cli/toolRegistry.ts
|
|
31905
|
+
init_openai_compatible();
|
|
30123
31906
|
function createBuiltinToolRegistry(options = {}) {
|
|
30124
31907
|
const root = options.root ?? process.cwd();
|
|
30125
31908
|
const audit = options.audit ?? new AuditLogger();
|
|
30126
31909
|
const sessionId = options.sessionId ?? "cli";
|
|
31910
|
+
const diagnosticsOn = options.diagnostics ?? process.env.ZELARI_DIAGNOSTICS !== "0";
|
|
31911
|
+
const withDiag = (t) => diagnosticsOn ? wrapWithDiagnostics(t, root, options.diagnosticsRunner) : t;
|
|
30127
31912
|
const safeReadFile = wrapWithSandbox(readFileTool, ["path"], root, audit, sessionId);
|
|
30128
|
-
const safeWriteFile = wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId);
|
|
30129
|
-
const safeEditFile = wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId);
|
|
31913
|
+
const safeWriteFile = withDiag(wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId));
|
|
31914
|
+
const safeEditFile = withDiag(wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId));
|
|
30130
31915
|
const safeGrepContent = wrapWithSandbox(grepContentTool, ["path"], root, audit, sessionId);
|
|
30131
31916
|
const safeListFiles = wrapWithSandbox(listFilesTool, ["path"], root, audit, sessionId);
|
|
30132
31917
|
const safeShowDiff = wrapWithSandbox(showDiffTool, ["path"], root, audit, sessionId);
|
|
30133
|
-
const safeApplyDiff = wrapWithSandbox(applyDiffTool, ["path"], root, audit, sessionId);
|
|
31918
|
+
const safeApplyDiff = withDiag(wrapWithSandbox(applyDiffTool, ["path"], root, audit, sessionId));
|
|
30134
31919
|
const safeBash = wrapWithShellSafety(bashTool, audit, sessionId);
|
|
30135
31920
|
const safeFetchUrl = wrapWithAudit(fetchUrlTool, audit, sessionId);
|
|
30136
31921
|
const safeWebSearch = wrapWithAudit(webSearchTool, audit, sessionId);
|
|
30137
31922
|
const registry3 = new ToolRegistry();
|
|
31923
|
+
const readOnly = options.readOnly === true;
|
|
30138
31924
|
registry3.register(safeReadFile);
|
|
30139
|
-
registry3.register(safeWriteFile);
|
|
30140
|
-
registry3.register(safeEditFile);
|
|
30141
|
-
registry3.register(safeBash);
|
|
30142
31925
|
registry3.register(safeGrepContent);
|
|
30143
31926
|
registry3.register(safeListFiles);
|
|
30144
31927
|
registry3.register(safeShowDiff);
|
|
30145
|
-
registry3.register(safeApplyDiff);
|
|
30146
31928
|
registry3.register(safeFetchUrl);
|
|
30147
31929
|
registry3.register(safeWebSearch);
|
|
30148
|
-
|
|
31930
|
+
if (!readOnly) {
|
|
31931
|
+
registry3.register(safeWriteFile);
|
|
31932
|
+
registry3.register(safeEditFile);
|
|
31933
|
+
registry3.register(safeBash);
|
|
31934
|
+
registry3.register(safeApplyDiff);
|
|
31935
|
+
}
|
|
31936
|
+
const summary = readOnly ? [safeReadFile, safeGrepContent, safeListFiles, safeShowDiff, safeFetchUrl, safeWebSearch] : [
|
|
30149
31937
|
safeReadFile,
|
|
30150
31938
|
safeWriteFile,
|
|
30151
31939
|
safeEditFile,
|
|
@@ -30156,11 +31944,75 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
30156
31944
|
safeApplyDiff,
|
|
30157
31945
|
safeFetchUrl,
|
|
30158
31946
|
safeWebSearch
|
|
30159
|
-
]
|
|
31947
|
+
];
|
|
31948
|
+
const tools = summary.map((t) => ({
|
|
30160
31949
|
name: t.name,
|
|
30161
31950
|
description: t.description,
|
|
30162
31951
|
permissions: t.permissions ?? []
|
|
30163
31952
|
}));
|
|
31953
|
+
if (process.env.ZELARI_AST !== "0") {
|
|
31954
|
+
for (const t of createAstTools()) {
|
|
31955
|
+
registry3.register(t);
|
|
31956
|
+
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
31957
|
+
}
|
|
31958
|
+
}
|
|
31959
|
+
if (process.env.ZELARI_SEMANTIC !== "0") {
|
|
31960
|
+
const semanticTool = createSemanticTool({ root });
|
|
31961
|
+
registry3.register(semanticTool);
|
|
31962
|
+
tools.push({
|
|
31963
|
+
name: semanticTool.name,
|
|
31964
|
+
description: semanticTool.description,
|
|
31965
|
+
permissions: semanticTool.permissions ?? []
|
|
31966
|
+
});
|
|
31967
|
+
}
|
|
31968
|
+
if (!readOnly && process.env.ZELARI_BROWSER !== "0") {
|
|
31969
|
+
const browserTool = createBrowserTool();
|
|
31970
|
+
registry3.register(browserTool);
|
|
31971
|
+
tools.push({
|
|
31972
|
+
name: browserTool.name,
|
|
31973
|
+
description: browserTool.description,
|
|
31974
|
+
permissions: browserTool.permissions ?? []
|
|
31975
|
+
});
|
|
31976
|
+
}
|
|
31977
|
+
if (!readOnly && options.enableTask !== false) {
|
|
31978
|
+
const taskTool = createTaskTool({
|
|
31979
|
+
createSubAgentContext: async () => {
|
|
31980
|
+
const cfg = await providerFromEnv();
|
|
31981
|
+
if (!cfg) return null;
|
|
31982
|
+
const { registry: subRegistry } = createBuiltinToolRegistry({
|
|
31983
|
+
root,
|
|
31984
|
+
audit,
|
|
31985
|
+
sessionId,
|
|
31986
|
+
readOnly: true,
|
|
31987
|
+
diagnostics: false
|
|
31988
|
+
});
|
|
31989
|
+
return {
|
|
31990
|
+
providerStream: openaiCompatibleProvider(cfg),
|
|
31991
|
+
model: cfg.model,
|
|
31992
|
+
provider: "openai-compatible",
|
|
31993
|
+
registry: subRegistry,
|
|
31994
|
+
tools: subRegistry.toOpenAITools().map((t) => ({
|
|
31995
|
+
name: t.function.name,
|
|
31996
|
+
description: t.function.description,
|
|
31997
|
+
parameters: t.function.parameters
|
|
31998
|
+
}))
|
|
31999
|
+
};
|
|
32000
|
+
}
|
|
32001
|
+
});
|
|
32002
|
+
registry3.register(taskTool);
|
|
32003
|
+
tools.push({
|
|
32004
|
+
name: taskTool.name,
|
|
32005
|
+
description: taskTool.description,
|
|
32006
|
+
permissions: taskTool.permissions ?? []
|
|
32007
|
+
});
|
|
32008
|
+
}
|
|
32009
|
+
if (!readOnly && process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
|
|
32010
|
+
const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
|
|
32011
|
+
for (const t of lspTools) {
|
|
32012
|
+
registry3.register(t);
|
|
32013
|
+
tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
|
|
32014
|
+
}
|
|
32015
|
+
}
|
|
30164
32016
|
return { registry: registry3, tools };
|
|
30165
32017
|
}
|
|
30166
32018
|
function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
@@ -30210,6 +32062,36 @@ function wrapWithSandbox(original, pathArgs, root, audit, sessionId) {
|
|
|
30210
32062
|
}
|
|
30211
32063
|
};
|
|
30212
32064
|
}
|
|
32065
|
+
function wrapWithDiagnostics(original, root, runner) {
|
|
32066
|
+
return {
|
|
32067
|
+
...original,
|
|
32068
|
+
execute: async (rawArgs, ctx) => {
|
|
32069
|
+
const result = await original.execute(rawArgs, ctx);
|
|
32070
|
+
if (!result.ok) return result;
|
|
32071
|
+
if (rawArgs.dryRun === true) return result;
|
|
32072
|
+
const value = result.value;
|
|
32073
|
+
const filePath = value && typeof value === "object" && typeof value.path === "string" ? value.path : void 0;
|
|
32074
|
+
if (!filePath) return result;
|
|
32075
|
+
try {
|
|
32076
|
+
const timeoutMs = Number(process.env.ZELARI_DIAGNOSTICS_TIMEOUT_MS) || 5e3;
|
|
32077
|
+
const diags = await runDiagnosticsForFile(filePath, {
|
|
32078
|
+
cwd: root,
|
|
32079
|
+
timeoutMs,
|
|
32080
|
+
...runner ? { runner } : {}
|
|
32081
|
+
});
|
|
32082
|
+
const formatted = formatDiagnostics(diags, { relativeTo: root });
|
|
32083
|
+
if (formatted) {
|
|
32084
|
+
return {
|
|
32085
|
+
ok: true,
|
|
32086
|
+
value: { ...value, diagnostics: formatted }
|
|
32087
|
+
};
|
|
32088
|
+
}
|
|
32089
|
+
} catch {
|
|
32090
|
+
}
|
|
32091
|
+
return result;
|
|
32092
|
+
}
|
|
32093
|
+
};
|
|
32094
|
+
}
|
|
30213
32095
|
function wrapWithAudit(original, audit, sessionId) {
|
|
30214
32096
|
return {
|
|
30215
32097
|
...original,
|
|
@@ -30374,69 +32256,16 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
30374
32256
|
});
|
|
30375
32257
|
}
|
|
30376
32258
|
|
|
30377
|
-
// src/cli/modelPricing.ts
|
|
30378
|
-
var PRICES_PER_MILLION = {
|
|
30379
|
-
// xAI Grok
|
|
30380
|
-
"grok-4": { input: 3, output: 15 },
|
|
30381
|
-
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
30382
|
-
"grok-3": { input: 3, output: 15 },
|
|
30383
|
-
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
30384
|
-
"grok-2-vision": { input: 2, output: 10 },
|
|
30385
|
-
// GLM / Z.AI
|
|
30386
|
-
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
30387
|
-
"glm-4.5": { input: 0.5, output: 2 },
|
|
30388
|
-
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
30389
|
-
"glm-z1": { input: 0.5, output: 2 },
|
|
30390
|
-
// MiniMax
|
|
30391
|
-
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
30392
|
-
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
30393
|
-
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
30394
|
-
// DeepSeek (global platform) — estimated list prices; override via
|
|
30395
|
-
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
30396
|
-
"deepseek-v4-flash": { input: 0.14, output: 0.28 },
|
|
30397
|
-
"deepseek-v4-pro": { input: 0.55, output: 2.19 },
|
|
30398
|
-
// OpenAI (for openai-compatible fallback)
|
|
30399
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
30400
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
30401
|
-
"gpt-4-turbo": { input: 10, output: 30 },
|
|
30402
|
-
"o1-preview": { input: 15, output: 60 },
|
|
30403
|
-
"o1-mini": { input: 3, output: 12 }
|
|
30404
|
-
};
|
|
30405
|
-
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
30406
|
-
function envOverride(model) {
|
|
30407
|
-
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
30408
|
-
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
30409
|
-
return parseRateOverride(raw);
|
|
30410
|
-
}
|
|
30411
|
-
function parseRateOverride(raw) {
|
|
30412
|
-
if (!raw) return null;
|
|
30413
|
-
const [inStr, outStr] = raw.split("/");
|
|
30414
|
-
const input = Number.parseFloat(inStr);
|
|
30415
|
-
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
30416
|
-
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
30417
|
-
return { input, output };
|
|
30418
|
-
}
|
|
30419
|
-
function getModelRate(model) {
|
|
30420
|
-
if (!model) return DEFAULT_RATE;
|
|
30421
|
-
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
30422
|
-
}
|
|
30423
|
-
function calculateCost(model, promptTokens, completionTokens) {
|
|
30424
|
-
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
30425
|
-
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
30426
|
-
const rate = getModelRate(model);
|
|
30427
|
-
const inputCost = promptTokens / 1e6 * rate.input;
|
|
30428
|
-
const outputCost = completionTokens / 1e6 * rate.output;
|
|
30429
|
-
return Number((inputCost + outputCost).toFixed(6));
|
|
30430
|
-
}
|
|
30431
|
-
|
|
30432
32259
|
// src/cli/hooks/chatStats.ts
|
|
30433
32260
|
function computeSessionStatsDelta(realUsage, userText, assistantContent, model, prev2) {
|
|
30434
32261
|
const promptTokens = realUsage ? realUsage.promptTokens : Math.ceil(userText.length / 4);
|
|
30435
32262
|
const completionTokens = realUsage ? realUsage.completionTokens : Math.ceil(assistantContent.length / 4);
|
|
30436
|
-
const
|
|
32263
|
+
const cachedPromptTokens = realUsage?.cachedPromptTokens ?? 0;
|
|
32264
|
+
const turnCost = calculateCost(model, promptTokens, completionTokens, cachedPromptTokens);
|
|
30437
32265
|
return {
|
|
30438
32266
|
totalTokens: prev2.totalTokens + promptTokens + completionTokens,
|
|
30439
|
-
totalCostUsd: prev2.totalCostUsd + turnCost
|
|
32267
|
+
totalCostUsd: prev2.totalCostUsd + turnCost,
|
|
32268
|
+
cachedTokens: (prev2.cachedTokens ?? 0) + cachedPromptTokens
|
|
30440
32269
|
};
|
|
30441
32270
|
}
|
|
30442
32271
|
|
|
@@ -31381,6 +33210,10 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
31381
33210
|
/new \u2014 start a fresh session
|
|
31382
33211
|
/diff [--staged] \u2014 show uncommitted changes (or staged with --staged)
|
|
31383
33212
|
/undo [--yes] \u2014 revert working-tree changes (destructive! requires --yes)
|
|
33213
|
+
/checkpoint [label] \u2014 snapshot the working tree as a restore point
|
|
33214
|
+
/rollback [id|latest] \u2014 restore the working tree to a checkpoint (no arg: list)
|
|
33215
|
+
/index [status] \u2014 build the semantic code index for semantic_search
|
|
33216
|
+
/mode [agent|council|zelari] \u2014 switch dispatch mode (same as shift+tab; no arg cycles)
|
|
31384
33217
|
/help \u2014 show this help
|
|
31385
33218
|
/exit \u2014 exit the CLI
|
|
31386
33219
|
|
|
@@ -31718,6 +33551,38 @@ ${formatSkillList(availableSkills)}`
|
|
|
31718
33551
|
message: "\u26A0 /undo is DESTRUCTIVE \u2014 it reverts all unstaged modifications and unstages everything.\nUse `/undo --yes` (or `/undo -y`) to confirm."
|
|
31719
33552
|
};
|
|
31720
33553
|
}
|
|
33554
|
+
case "mode": {
|
|
33555
|
+
const target = args[0]?.trim().toLowerCase();
|
|
33556
|
+
if (target && ["agent", "council", "zelari"].includes(target)) {
|
|
33557
|
+
return { handled: true, kind: "mode_set", modeTarget: target };
|
|
33558
|
+
}
|
|
33559
|
+
if (target) {
|
|
33560
|
+
return { handled: true, kind: "mode_set", message: `[mode] unknown: ${target}. Use agent, council, or zelari (or /mode to cycle).` };
|
|
33561
|
+
}
|
|
33562
|
+
return { handled: true, kind: "mode_set" };
|
|
33563
|
+
}
|
|
33564
|
+
case "index": {
|
|
33565
|
+
return args[0] === "status" ? { handled: true, kind: "index_status" } : { handled: true, kind: "index_build" };
|
|
33566
|
+
}
|
|
33567
|
+
case "checkpoint": {
|
|
33568
|
+
const label = args.join(" ").trim();
|
|
33569
|
+
return {
|
|
33570
|
+
handled: true,
|
|
33571
|
+
kind: "checkpoint_create",
|
|
33572
|
+
...label ? { checkpointLabel: label } : {}
|
|
33573
|
+
};
|
|
33574
|
+
}
|
|
33575
|
+
case "rollback": {
|
|
33576
|
+
const target = args[0]?.trim();
|
|
33577
|
+
if (!target) {
|
|
33578
|
+
return { handled: true, kind: "rollback_list" };
|
|
33579
|
+
}
|
|
33580
|
+
return {
|
|
33581
|
+
handled: true,
|
|
33582
|
+
kind: "rollback",
|
|
33583
|
+
...target === "latest" ? {} : { rollbackId: target }
|
|
33584
|
+
};
|
|
33585
|
+
}
|
|
31721
33586
|
case "workspace": {
|
|
31722
33587
|
const sub = args[0];
|
|
31723
33588
|
if (!sub || sub === "--help" || sub === "help") {
|
|
@@ -31781,13 +33646,13 @@ ${formatSkillList(availableSkills)}`
|
|
|
31781
33646
|
}
|
|
31782
33647
|
|
|
31783
33648
|
// src/cli/gitOps.ts
|
|
31784
|
-
import { execFile as
|
|
31785
|
-
import { promisify } from "node:util";
|
|
31786
|
-
import
|
|
31787
|
-
var
|
|
31788
|
-
async function
|
|
33649
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
33650
|
+
import { promisify as promisify2 } from "node:util";
|
|
33651
|
+
import path27 from "node:path";
|
|
33652
|
+
var execFileAsync2 = promisify2(execFile3);
|
|
33653
|
+
async function git2(cwd, args) {
|
|
31789
33654
|
try {
|
|
31790
|
-
const { stdout } = await
|
|
33655
|
+
const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
|
|
31791
33656
|
maxBuffer: 16 * 1024 * 1024
|
|
31792
33657
|
});
|
|
31793
33658
|
return stdout;
|
|
@@ -31795,15 +33660,15 @@ async function git(cwd, args) {
|
|
|
31795
33660
|
return null;
|
|
31796
33661
|
}
|
|
31797
33662
|
}
|
|
31798
|
-
async function
|
|
31799
|
-
const out = await
|
|
33663
|
+
async function isGitRepo2(cwd = process.cwd()) {
|
|
33664
|
+
const out = await git2(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
31800
33665
|
return out?.trim() === "true";
|
|
31801
33666
|
}
|
|
31802
33667
|
async function getWorkingDiff(opts = {}) {
|
|
31803
33668
|
const cwd = opts.cwd ?? process.cwd();
|
|
31804
33669
|
const maxChars = opts.maxChars ?? 5e4;
|
|
31805
|
-
const unstaged = await
|
|
31806
|
-
const staged = opts.staged ? await
|
|
33670
|
+
const unstaged = await git2(cwd, ["diff"]) ?? "";
|
|
33671
|
+
const staged = opts.staged ? await git2(cwd, ["diff", "--cached"]) ?? "" : "";
|
|
31807
33672
|
const combined = [staged, unstaged].filter((s) => s.length > 0).join("\n");
|
|
31808
33673
|
if (combined.length === 0) {
|
|
31809
33674
|
return { diff: "", truncated: false, empty: true };
|
|
@@ -31816,11 +33681,11 @@ async function getWorkingDiff(opts = {}) {
|
|
|
31816
33681
|
async function undoWorkingChanges(opts = {}) {
|
|
31817
33682
|
const cwd = opts.cwd ?? process.cwd();
|
|
31818
33683
|
const unstage = opts.unstage !== false;
|
|
31819
|
-
const statusBefore = await
|
|
33684
|
+
const statusBefore = await git2(cwd, ["status", "--porcelain"]) ?? "";
|
|
31820
33685
|
const modified = statusBefore.split("\n").filter((line) => line.length > 0).filter((line) => !line.startsWith("??") && !line.startsWith("!!")).map((line) => line.slice(3).trim());
|
|
31821
|
-
await
|
|
33686
|
+
await git2(cwd, ["checkout", "--", "."]);
|
|
31822
33687
|
if (unstage) {
|
|
31823
|
-
await
|
|
33688
|
+
await git2(cwd, ["reset", "HEAD", "--", "."]);
|
|
31824
33689
|
}
|
|
31825
33690
|
return {
|
|
31826
33691
|
reverted: modified,
|
|
@@ -31829,14 +33694,14 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
31829
33694
|
};
|
|
31830
33695
|
}
|
|
31831
33696
|
function defaultProjectRoot() {
|
|
31832
|
-
return
|
|
33697
|
+
return path27.resolve(__dirname, "..", "..", "..");
|
|
31833
33698
|
}
|
|
31834
33699
|
|
|
31835
33700
|
// src/cli/slashHandlers/git.ts
|
|
31836
33701
|
async function handleDiff(ctx, diffStaged) {
|
|
31837
33702
|
try {
|
|
31838
33703
|
const repoRoot = defaultProjectRoot();
|
|
31839
|
-
if (!await
|
|
33704
|
+
if (!await isGitRepo2(repoRoot)) {
|
|
31840
33705
|
appendSystem(ctx.setMessages, "[diff] not a git repository \u2014 nothing to show");
|
|
31841
33706
|
return;
|
|
31842
33707
|
}
|
|
@@ -31857,7 +33722,7 @@ async function handleUndo(ctx, warningMessage, doConfirm) {
|
|
|
31857
33722
|
if (!doConfirm) return;
|
|
31858
33723
|
try {
|
|
31859
33724
|
const repoRoot = defaultProjectRoot();
|
|
31860
|
-
if (!await
|
|
33725
|
+
if (!await isGitRepo2(repoRoot)) {
|
|
31861
33726
|
appendSystem(ctx.setMessages, "[undo] not a git repository \u2014 nothing to revert");
|
|
31862
33727
|
return;
|
|
31863
33728
|
}
|
|
@@ -31873,6 +33738,117 @@ async function handleUndo(ctx, warningMessage, doConfirm) {
|
|
|
31873
33738
|
}
|
|
31874
33739
|
}
|
|
31875
33740
|
|
|
33741
|
+
// src/cli/slashHandlers/checkpoint.ts
|
|
33742
|
+
init_checkpointManager();
|
|
33743
|
+
function ago(ms) {
|
|
33744
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1e3));
|
|
33745
|
+
if (s < 60) return `${s}s ago`;
|
|
33746
|
+
if (s < 3600) return `${Math.round(s / 60)}m ago`;
|
|
33747
|
+
return `${Math.round(s / 3600)}h ago`;
|
|
33748
|
+
}
|
|
33749
|
+
async function handleCheckpointCreate(ctx, label) {
|
|
33750
|
+
const res = await createCheckpoint(ctx.cwd, label ?? "manual checkpoint");
|
|
33751
|
+
if (res.ok) {
|
|
33752
|
+
appendSystem(
|
|
33753
|
+
ctx.setMessages,
|
|
33754
|
+
`[checkpoint] \u2713 ${res.value.id} created${label ? ` (\u201C${label}\u201D)` : ""} \u2014 restore with \`/rollback ${res.value.id}\``
|
|
33755
|
+
);
|
|
33756
|
+
} else {
|
|
33757
|
+
appendSystem(ctx.setMessages, `[checkpoint] \u2717 ${res.error}`);
|
|
33758
|
+
}
|
|
33759
|
+
}
|
|
33760
|
+
async function handleRollbackList(ctx) {
|
|
33761
|
+
const list = await listCheckpoints(ctx.cwd);
|
|
33762
|
+
if (list.length === 0) {
|
|
33763
|
+
appendSystem(
|
|
33764
|
+
ctx.setMessages,
|
|
33765
|
+
"[rollback] no checkpoints yet. Create one with `/checkpoint [label]` (Zelari missions create one automatically)."
|
|
33766
|
+
);
|
|
33767
|
+
return;
|
|
33768
|
+
}
|
|
33769
|
+
const lines = list.map(
|
|
33770
|
+
(c, i) => ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago(c.createdAt)} ${c.label}`
|
|
33771
|
+
);
|
|
33772
|
+
appendSystem(
|
|
33773
|
+
ctx.setMessages,
|
|
33774
|
+
`[rollback] ${list.length} checkpoint${list.length === 1 ? "" : "s"} (newest first):
|
|
33775
|
+
${lines.join("\n")}
|
|
33776
|
+
Restore with \`/rollback <id>\` or \`/rollback latest\`.`
|
|
33777
|
+
);
|
|
33778
|
+
}
|
|
33779
|
+
async function handleRollback(ctx, id) {
|
|
33780
|
+
const res = await restoreCheckpoint(ctx.cwd, id);
|
|
33781
|
+
if (res.ok) {
|
|
33782
|
+
const removed = res.value.deleted.length > 0 ? ` (removed ${res.value.deleted.length} file${res.value.deleted.length === 1 ? "" : "s"} created after the checkpoint)` : "";
|
|
33783
|
+
appendSystem(
|
|
33784
|
+
ctx.setMessages,
|
|
33785
|
+
`[rollback] \u2713 working tree restored to checkpoint ${res.value.id}${removed}.`
|
|
33786
|
+
);
|
|
33787
|
+
} else {
|
|
33788
|
+
appendSystem(ctx.setMessages, `[rollback] \u2717 ${res.error}`);
|
|
33789
|
+
}
|
|
33790
|
+
}
|
|
33791
|
+
|
|
33792
|
+
// src/cli/slashHandlers/semantic.ts
|
|
33793
|
+
async function handleIndexBuild(ctx) {
|
|
33794
|
+
const embed = await buildProviderEmbedFn();
|
|
33795
|
+
if (!embed) {
|
|
33796
|
+
appendSystem(ctx.setMessages, "[index] no provider/API key configured \u2014 run /login first.");
|
|
33797
|
+
return;
|
|
33798
|
+
}
|
|
33799
|
+
appendSystem(
|
|
33800
|
+
ctx.setMessages,
|
|
33801
|
+
`[index] scanning source files and embedding with "${embedModel()}"\u2026 (this can take a moment)`
|
|
33802
|
+
);
|
|
33803
|
+
const files = await collectSourceFiles(ctx.cwd);
|
|
33804
|
+
if (files.length === 0) {
|
|
33805
|
+
appendSystem(ctx.setMessages, "[index] no source files found to index.");
|
|
33806
|
+
return;
|
|
33807
|
+
}
|
|
33808
|
+
const result = await buildIndex(files, embed, { model: embedModel() });
|
|
33809
|
+
if (result.error || !result.data) {
|
|
33810
|
+
appendSystem(
|
|
33811
|
+
ctx.setMessages,
|
|
33812
|
+
`[index] \u2717 ${result.error ?? "build failed"}` + (result.error?.includes("HTTP") || result.error?.includes("shape") ? "\n (does this provider expose /embeddings? set ZELARI_EMBED_MODEL or switch provider.)" : "")
|
|
33813
|
+
);
|
|
33814
|
+
return;
|
|
33815
|
+
}
|
|
33816
|
+
await saveIndex(ctx.cwd, result.data);
|
|
33817
|
+
appendSystem(
|
|
33818
|
+
ctx.setMessages,
|
|
33819
|
+
`[index] \u2713 indexed ${result.chunksIndexed} chunks from ${result.filesIndexed} files (dim ${result.data.dim}). Use semantic_search now. Saved to ${getIndexPath(ctx.cwd)}`
|
|
33820
|
+
);
|
|
33821
|
+
}
|
|
33822
|
+
function handleIndexStatus(ctx) {
|
|
33823
|
+
const data = loadIndex(ctx.cwd);
|
|
33824
|
+
if (!data) {
|
|
33825
|
+
appendSystem(ctx.setMessages, "[index] no semantic index yet \u2014 run /index to build one.");
|
|
33826
|
+
return;
|
|
33827
|
+
}
|
|
33828
|
+
const ageMin = Math.round((Date.now() - data.builtAt) / 6e4);
|
|
33829
|
+
appendSystem(
|
|
33830
|
+
ctx.setMessages,
|
|
33831
|
+
`[index] ${data.chunks.length} chunks, model "${data.model}", dim ${data.dim}, built ${ageMin}m ago.`
|
|
33832
|
+
);
|
|
33833
|
+
}
|
|
33834
|
+
|
|
33835
|
+
// src/cli/mode.ts
|
|
33836
|
+
var MODES = ["agent", "council", "zelari"];
|
|
33837
|
+
function nextMode(current) {
|
|
33838
|
+
const i = MODES.indexOf(current);
|
|
33839
|
+
return MODES[(i + 1) % MODES.length] ?? "agent";
|
|
33840
|
+
}
|
|
33841
|
+
function describeMode(mode) {
|
|
33842
|
+
switch (mode) {
|
|
33843
|
+
case "council":
|
|
33844
|
+
return "council \u2014 6-member pipeline (Caronte\u2026Lucifero)";
|
|
33845
|
+
case "zelari":
|
|
33846
|
+
return "zelari \u2014 autonomous multi-run mission";
|
|
33847
|
+
default:
|
|
33848
|
+
return "agent \u2014 single LLM turn";
|
|
33849
|
+
}
|
|
33850
|
+
}
|
|
33851
|
+
|
|
31876
33852
|
// src/cli/compaction.ts
|
|
31877
33853
|
function compactTranscript(messages, options = {}) {
|
|
31878
33854
|
const threshold = options.threshold ?? 50;
|
|
@@ -32010,17 +33986,17 @@ ${output}`.toLowerCase();
|
|
|
32010
33986
|
}
|
|
32011
33987
|
|
|
32012
33988
|
// src/cli/slashHandlers/promoteMember.ts
|
|
32013
|
-
import { promises as
|
|
32014
|
-
import
|
|
32015
|
-
import
|
|
33989
|
+
import { promises as fs16 } from "node:fs";
|
|
33990
|
+
import path28 from "node:path";
|
|
33991
|
+
import os9 from "node:os";
|
|
32016
33992
|
async function handlePromoteMember(ctx, memberId) {
|
|
32017
33993
|
try {
|
|
32018
33994
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
32019
33995
|
const { skill, markdown } = promoteMember2(memberId);
|
|
32020
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
32021
|
-
await
|
|
32022
|
-
const filePath =
|
|
32023
|
-
await
|
|
33996
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path28.join(os9.homedir(), ".tmp", "zelari-code", "skills");
|
|
33997
|
+
await fs16.mkdir(skillDir, { recursive: true });
|
|
33998
|
+
const filePath = path28.join(skillDir, `${skill.id}.md`);
|
|
33999
|
+
await fs16.writeFile(filePath, markdown, "utf8");
|
|
32024
34000
|
appendSystem(
|
|
32025
34001
|
ctx.setMessages,
|
|
32026
34002
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -32036,33 +34012,33 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
32036
34012
|
}
|
|
32037
34013
|
|
|
32038
34014
|
// src/cli/branchManager.ts
|
|
32039
|
-
import { promises as
|
|
32040
|
-
import
|
|
32041
|
-
import
|
|
34015
|
+
import { promises as fs17, existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
34016
|
+
import path29 from "node:path";
|
|
34017
|
+
import os10 from "node:os";
|
|
32042
34018
|
var META_FILENAME = "meta.json";
|
|
32043
34019
|
var SESSIONS_SUBDIR = "sessions";
|
|
32044
34020
|
function getBranchesBaseDir() {
|
|
32045
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
34021
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "branches");
|
|
32046
34022
|
}
|
|
32047
34023
|
function getSessionsBaseDir() {
|
|
32048
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
34024
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "sessions");
|
|
32049
34025
|
}
|
|
32050
34026
|
function branchPathFor(name, baseDir) {
|
|
32051
|
-
return
|
|
34027
|
+
return path29.join(baseDir, name);
|
|
32052
34028
|
}
|
|
32053
34029
|
function metaPathFor(name, baseDir) {
|
|
32054
|
-
return
|
|
34030
|
+
return path29.join(baseDir, name, META_FILENAME);
|
|
32055
34031
|
}
|
|
32056
34032
|
function sessionsPathFor(name, baseDir) {
|
|
32057
|
-
return
|
|
34033
|
+
return path29.join(baseDir, name, SESSIONS_SUBDIR);
|
|
32058
34034
|
}
|
|
32059
34035
|
function readBranchMeta(name, baseDir) {
|
|
32060
34036
|
const metaPath = metaPathFor(name, baseDir);
|
|
32061
|
-
if (!
|
|
34037
|
+
if (!existsSync25(metaPath)) {
|
|
32062
34038
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
32063
34039
|
}
|
|
32064
34040
|
try {
|
|
32065
|
-
const raw =
|
|
34041
|
+
const raw = readFileSync23(metaPath, "utf-8");
|
|
32066
34042
|
const parsed = JSON.parse(raw);
|
|
32067
34043
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
32068
34044
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -32079,13 +34055,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
32079
34055
|
}
|
|
32080
34056
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
32081
34057
|
const metaPath = metaPathFor(name, baseDir);
|
|
32082
|
-
mkdirSync10(
|
|
34058
|
+
mkdirSync10(path29.dirname(metaPath), { recursive: true });
|
|
32083
34059
|
writeFileSync15(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
32084
34060
|
}
|
|
32085
34061
|
async function countSessions(name, baseDir) {
|
|
32086
34062
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
32087
34063
|
try {
|
|
32088
|
-
const entries = await
|
|
34064
|
+
const entries = await fs17.readdir(sessionsPath);
|
|
32089
34065
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
32090
34066
|
} catch (err) {
|
|
32091
34067
|
if (err.code === "ENOENT") return 0;
|
|
@@ -32118,7 +34094,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
32118
34094
|
};
|
|
32119
34095
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
32120
34096
|
const bp = branchPathFor(name, baseDir);
|
|
32121
|
-
return
|
|
34097
|
+
return existsSync25(bp) && existsSync25(metaPathFor(name, baseDir));
|
|
32122
34098
|
}
|
|
32123
34099
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
32124
34100
|
if (!name || name.trim().length === 0) {
|
|
@@ -32130,15 +34106,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
32130
34106
|
if (branchExists(name, baseDir)) {
|
|
32131
34107
|
throw new BranchAlreadyExistsError(name);
|
|
32132
34108
|
}
|
|
32133
|
-
const sourcePath =
|
|
32134
|
-
if (!
|
|
34109
|
+
const sourcePath = path29.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
34110
|
+
if (!existsSync25(sourcePath)) {
|
|
32135
34111
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
32136
34112
|
}
|
|
32137
34113
|
const branchPath = branchPathFor(name, baseDir);
|
|
32138
34114
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
32139
34115
|
mkdirSync10(branchSessionsPath, { recursive: true });
|
|
32140
|
-
const destPath =
|
|
32141
|
-
await
|
|
34116
|
+
const destPath = path29.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
34117
|
+
await fs17.copyFile(sourcePath, destPath);
|
|
32142
34118
|
const meta3 = {
|
|
32143
34119
|
name,
|
|
32144
34120
|
createdAt: Date.now(),
|
|
@@ -32156,7 +34132,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
32156
34132
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
32157
34133
|
let entries;
|
|
32158
34134
|
try {
|
|
32159
|
-
entries = await
|
|
34135
|
+
entries = await fs17.readdir(baseDir);
|
|
32160
34136
|
} catch (err) {
|
|
32161
34137
|
if (err.code === "ENOENT") return [];
|
|
32162
34138
|
throw err;
|
|
@@ -32164,7 +34140,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
32164
34140
|
const results = [];
|
|
32165
34141
|
for (const entry of entries) {
|
|
32166
34142
|
const metaPath = metaPathFor(entry, baseDir);
|
|
32167
|
-
if (!
|
|
34143
|
+
if (!existsSync25(metaPath)) continue;
|
|
32168
34144
|
try {
|
|
32169
34145
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
32170
34146
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -32237,26 +34213,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
32237
34213
|
}
|
|
32238
34214
|
|
|
32239
34215
|
// src/cli/slashHandlers/workspace.ts
|
|
32240
|
-
import { promises as
|
|
32241
|
-
import
|
|
34216
|
+
import { promises as fs18 } from "node:fs";
|
|
34217
|
+
import path30 from "node:path";
|
|
32242
34218
|
async function handleWorkspaceShow(ctx, what) {
|
|
32243
34219
|
try {
|
|
32244
|
-
const zelari =
|
|
34220
|
+
const zelari = path30.join(process.cwd(), ".zelari");
|
|
32245
34221
|
let content;
|
|
32246
34222
|
switch (what) {
|
|
32247
34223
|
case "plan": {
|
|
32248
|
-
const planPath =
|
|
34224
|
+
const planPath = path30.join(zelari, "plan.md");
|
|
32249
34225
|
try {
|
|
32250
|
-
content = await
|
|
34226
|
+
content = await fs18.readFile(planPath, "utf-8");
|
|
32251
34227
|
} catch {
|
|
32252
34228
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
32253
34229
|
}
|
|
32254
34230
|
break;
|
|
32255
34231
|
}
|
|
32256
34232
|
case "decisions": {
|
|
32257
|
-
const decisionsDir =
|
|
34233
|
+
const decisionsDir = path30.join(zelari, "decisions");
|
|
32258
34234
|
try {
|
|
32259
|
-
const files = (await
|
|
34235
|
+
const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
32260
34236
|
if (files.length === 0) {
|
|
32261
34237
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
32262
34238
|
} else {
|
|
@@ -32264,7 +34240,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32264
34240
|
`];
|
|
32265
34241
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
32266
34242
|
for (const f of files) {
|
|
32267
|
-
const raw = await
|
|
34243
|
+
const raw = await fs18.readFile(path30.join(decisionsDir, f), "utf-8");
|
|
32268
34244
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
32269
34245
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
32270
34246
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -32277,27 +34253,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
32277
34253
|
break;
|
|
32278
34254
|
}
|
|
32279
34255
|
case "risks": {
|
|
32280
|
-
const risksPath =
|
|
34256
|
+
const risksPath = path30.join(zelari, "risks.md");
|
|
32281
34257
|
try {
|
|
32282
|
-
content = await
|
|
34258
|
+
content = await fs18.readFile(risksPath, "utf-8");
|
|
32283
34259
|
} catch {
|
|
32284
34260
|
content = "(no risks.md yet)";
|
|
32285
34261
|
}
|
|
32286
34262
|
break;
|
|
32287
34263
|
}
|
|
32288
34264
|
case "agents": {
|
|
32289
|
-
const agentsPath =
|
|
34265
|
+
const agentsPath = path30.join(process.cwd(), "AGENTS.MD");
|
|
32290
34266
|
try {
|
|
32291
|
-
content = await
|
|
34267
|
+
content = await fs18.readFile(agentsPath, "utf-8");
|
|
32292
34268
|
} catch {
|
|
32293
34269
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
32294
34270
|
}
|
|
32295
34271
|
break;
|
|
32296
34272
|
}
|
|
32297
34273
|
case "docs": {
|
|
32298
|
-
const docsDir =
|
|
34274
|
+
const docsDir = path30.join(zelari, "docs");
|
|
32299
34275
|
try {
|
|
32300
|
-
const files = (await
|
|
34276
|
+
const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
32301
34277
|
content = files.length ? `# Docs (${files.length})
|
|
32302
34278
|
|
|
32303
34279
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -32337,8 +34313,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
32337
34313
|
return;
|
|
32338
34314
|
}
|
|
32339
34315
|
try {
|
|
32340
|
-
const target =
|
|
32341
|
-
await
|
|
34316
|
+
const target = path30.join(process.cwd(), ".zelari");
|
|
34317
|
+
await fs18.rm(target, { recursive: true, force: true });
|
|
32342
34318
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
32343
34319
|
} catch (err) {
|
|
32344
34320
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -32696,16 +34672,16 @@ function handleModelsRefresh(ctx) {
|
|
|
32696
34672
|
}
|
|
32697
34673
|
|
|
32698
34674
|
// src/cli/slashHandlers/skills.ts
|
|
32699
|
-
import
|
|
32700
|
-
import
|
|
34675
|
+
import path31 from "node:path";
|
|
34676
|
+
import os11 from "node:os";
|
|
32701
34677
|
|
|
32702
34678
|
// src/cli/skillHistory.ts
|
|
32703
|
-
import { promises as
|
|
34679
|
+
import { promises as fs19, existsSync as existsSync26, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync11 } from "node:fs";
|
|
32704
34680
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
32705
34681
|
async function readSkillHistory(file2) {
|
|
32706
34682
|
let raw = "";
|
|
32707
34683
|
try {
|
|
32708
|
-
raw = await
|
|
34684
|
+
raw = await fs19.readFile(file2, "utf-8");
|
|
32709
34685
|
} catch {
|
|
32710
34686
|
return [];
|
|
32711
34687
|
}
|
|
@@ -32798,7 +34774,7 @@ async function applySteerInterrupt(options) {
|
|
|
32798
34774
|
|
|
32799
34775
|
// src/cli/slashHandlers/skills.ts
|
|
32800
34776
|
async function handleSkillStats(ctx, skillId) {
|
|
32801
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
34777
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
32802
34778
|
try {
|
|
32803
34779
|
const records = await readSkillHistory(historyFile);
|
|
32804
34780
|
const stats = getSkillStats(records, skillId);
|
|
@@ -32814,7 +34790,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
32814
34790
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
32815
34791
|
return;
|
|
32816
34792
|
}
|
|
32817
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
34793
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
32818
34794
|
try {
|
|
32819
34795
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
32820
34796
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -32889,7 +34865,8 @@ function useSlashDispatch(params) {
|
|
|
32889
34865
|
dispatchPrompt,
|
|
32890
34866
|
dispatchCouncilPrompt,
|
|
32891
34867
|
dispatchZelariPrompt,
|
|
32892
|
-
mode = "agent"
|
|
34868
|
+
mode = "agent",
|
|
34869
|
+
setMode
|
|
32893
34870
|
} = params;
|
|
32894
34871
|
return useCallback3(async (value) => {
|
|
32895
34872
|
if (!value.trim()) return;
|
|
@@ -33115,6 +35092,44 @@ function useSlashDispatch(params) {
|
|
|
33115
35092
|
setInput("");
|
|
33116
35093
|
return;
|
|
33117
35094
|
}
|
|
35095
|
+
if (result.kind === "checkpoint_create") {
|
|
35096
|
+
await handleCheckpointCreate({ ...baseCtx, cwd: process.cwd() }, result.checkpointLabel);
|
|
35097
|
+
setInput("");
|
|
35098
|
+
return;
|
|
35099
|
+
}
|
|
35100
|
+
if (result.kind === "rollback_list") {
|
|
35101
|
+
await handleRollbackList({ ...baseCtx, cwd: process.cwd() });
|
|
35102
|
+
setInput("");
|
|
35103
|
+
return;
|
|
35104
|
+
}
|
|
35105
|
+
if (result.kind === "rollback") {
|
|
35106
|
+
await handleRollback({ ...baseCtx, cwd: process.cwd() }, result.rollbackId);
|
|
35107
|
+
setInput("");
|
|
35108
|
+
return;
|
|
35109
|
+
}
|
|
35110
|
+
if (result.kind === "index_build") {
|
|
35111
|
+
await handleIndexBuild({ ...baseCtx, cwd: process.cwd() });
|
|
35112
|
+
setInput("");
|
|
35113
|
+
return;
|
|
35114
|
+
}
|
|
35115
|
+
if (result.kind === "index_status") {
|
|
35116
|
+
handleIndexStatus({ ...baseCtx, cwd: process.cwd() });
|
|
35117
|
+
setInput("");
|
|
35118
|
+
return;
|
|
35119
|
+
}
|
|
35120
|
+
if (result.kind === "mode_set") {
|
|
35121
|
+
if (result.message) {
|
|
35122
|
+
appendSystem(setMessages, result.message);
|
|
35123
|
+
} else if (setMode) {
|
|
35124
|
+
const target = result.modeTarget ?? nextMode(mode);
|
|
35125
|
+
setMode(target);
|
|
35126
|
+
appendSystem(setMessages, `[mode] ${describeMode(target)}`);
|
|
35127
|
+
} else {
|
|
35128
|
+
appendSystem(setMessages, "[mode] switching unavailable in this context");
|
|
35129
|
+
}
|
|
35130
|
+
setInput("");
|
|
35131
|
+
return;
|
|
35132
|
+
}
|
|
33118
35133
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
33119
35134
|
await handlePromoteMember(baseCtx, result.promoteMemberId);
|
|
33120
35135
|
setInput("");
|
|
@@ -33181,6 +35196,7 @@ function useSlashDispatch(params) {
|
|
|
33181
35196
|
dispatchCouncilPrompt,
|
|
33182
35197
|
dispatchZelariPrompt,
|
|
33183
35198
|
mode,
|
|
35199
|
+
setMode,
|
|
33184
35200
|
params
|
|
33185
35201
|
]);
|
|
33186
35202
|
}
|
|
@@ -33294,7 +35310,7 @@ function App() {
|
|
|
33294
35310
|
const [input, setInput] = useState8("");
|
|
33295
35311
|
const [busy, setBusy] = useState8(false);
|
|
33296
35312
|
const [providerConfig, setProviderConfig] = useState8(() => getProviderConfig());
|
|
33297
|
-
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0 });
|
|
35313
|
+
const [sessionStats, setSessionStats] = useState8({ totalTokens: 0, totalCostUsd: 0, cachedTokens: 0 });
|
|
33298
35314
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
33299
35315
|
const [mode, setMode] = useState8("agent");
|
|
33300
35316
|
const [picker, setPicker] = useState8(null);
|
|
@@ -33309,9 +35325,7 @@ function App() {
|
|
|
33309
35325
|
useInput2(
|
|
33310
35326
|
(_input, key) => {
|
|
33311
35327
|
if (key.tab && key.shift) {
|
|
33312
|
-
setMode(
|
|
33313
|
-
(m) => m === "agent" ? "council" : m === "council" ? "zelari" : "agent"
|
|
33314
|
-
);
|
|
35328
|
+
setMode(nextMode);
|
|
33315
35329
|
}
|
|
33316
35330
|
},
|
|
33317
35331
|
{ isActive: isRawModeSupported === true }
|
|
@@ -33369,6 +35383,7 @@ function App() {
|
|
|
33369
35383
|
dispatchCouncilPrompt: chatTurn.dispatchCouncilPrompt,
|
|
33370
35384
|
dispatchZelariPrompt: chatTurn.dispatchZelariPrompt,
|
|
33371
35385
|
mode,
|
|
35386
|
+
setMode,
|
|
33372
35387
|
openPicker: setPicker,
|
|
33373
35388
|
onNewSession,
|
|
33374
35389
|
onExit,
|
|
@@ -33423,7 +35438,9 @@ cwd: ${cwd}
|
|
|
33423
35438
|
mode,
|
|
33424
35439
|
cwd,
|
|
33425
35440
|
elapsedMs: timer.elapsedMs,
|
|
33426
|
-
lastMs: timer.lastMs
|
|
35441
|
+
lastMs: timer.lastMs,
|
|
35442
|
+
costUsd: sessionStats.totalCostUsd,
|
|
35443
|
+
cachedTokens: sessionStats.cachedTokens
|
|
33427
35444
|
}
|
|
33428
35445
|
)), showSidebar && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })));
|
|
33429
35446
|
}
|
|
@@ -33603,9 +35620,9 @@ function SplashGate({
|
|
|
33603
35620
|
init_providerConfig();
|
|
33604
35621
|
|
|
33605
35622
|
// src/cli/wizard/firstRun.ts
|
|
33606
|
-
import { existsSync as
|
|
35623
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
33607
35624
|
function shouldRunWizard(input) {
|
|
33608
|
-
const exists = input.exists ??
|
|
35625
|
+
const exists = input.exists ?? existsSync27;
|
|
33609
35626
|
if (input.hasResetConfigFlag) {
|
|
33610
35627
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
33611
35628
|
}
|
|
@@ -33960,6 +35977,7 @@ function emitEvent(event) {
|
|
|
33960
35977
|
}
|
|
33961
35978
|
|
|
33962
35979
|
// src/cli/runHeadless.ts
|
|
35980
|
+
init_harness();
|
|
33963
35981
|
async function runHeadless(opts) {
|
|
33964
35982
|
const { provider, model } = resolveHeadlessProvider(opts);
|
|
33965
35983
|
const key = await resolveHeadlessKey(provider);
|
|
@@ -34094,9 +36112,9 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
34094
36112
|
|
|
34095
36113
|
// src/cli/skillsMd.ts
|
|
34096
36114
|
init_skills2();
|
|
34097
|
-
import { existsSync as
|
|
36115
|
+
import { existsSync as existsSync28, readdirSync as readdirSync4, readFileSync as readFileSync24 } from "node:fs";
|
|
34098
36116
|
import { join as join23 } from "node:path";
|
|
34099
|
-
import { homedir as
|
|
36117
|
+
import { homedir as homedir6 } from "node:os";
|
|
34100
36118
|
var CODING_CATEGORIES = /* @__PURE__ */ new Set([
|
|
34101
36119
|
"plan",
|
|
34102
36120
|
"refactor",
|
|
@@ -34114,7 +36132,7 @@ function skillMdSearchDirs(projectRoot = process.cwd()) {
|
|
|
34114
36132
|
join23(projectRoot, ".zelari", "skills"),
|
|
34115
36133
|
join23(projectRoot, ".claude", "skills"),
|
|
34116
36134
|
join23(projectRoot, ".opencode", "skills"),
|
|
34117
|
-
join23(
|
|
36135
|
+
join23(homedir6(), ".zelari-code", "skills")
|
|
34118
36136
|
];
|
|
34119
36137
|
}
|
|
34120
36138
|
function parseSkillMd(content, sourcePath) {
|
|
@@ -34172,7 +36190,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34172
36190
|
const summary = { loaded: [], skipped: [] };
|
|
34173
36191
|
const seen = new Set(options.existingIds ?? []);
|
|
34174
36192
|
for (const dir of skillMdSearchDirs(projectRoot)) {
|
|
34175
|
-
if (!
|
|
36193
|
+
if (!existsSync28(dir)) continue;
|
|
34176
36194
|
let entries;
|
|
34177
36195
|
try {
|
|
34178
36196
|
entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -34181,9 +36199,9 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
|
|
|
34181
36199
|
}
|
|
34182
36200
|
for (const entry of entries) {
|
|
34183
36201
|
const skillPath = join23(dir, entry, "SKILL.md");
|
|
34184
|
-
if (!
|
|
36202
|
+
if (!existsSync28(skillPath)) continue;
|
|
34185
36203
|
try {
|
|
34186
|
-
const parsed = parseSkillMd(
|
|
36204
|
+
const parsed = parseSkillMd(readFileSync24(skillPath, "utf8"), skillPath);
|
|
34187
36205
|
if (!parsed) {
|
|
34188
36206
|
summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
|
|
34189
36207
|
continue;
|