zelari-code 1.27.1 → 1.28.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/kraken/executor.js +128 -20
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/graphMemory.js +111 -0
- package/dist/cli/kraken/graphMemory.js.map +1 -0
- package/dist/cli/kraken/planner.js +160 -21
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/main.bundled.js +537 -246
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/runHeadless.js +15 -1
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashHandlers/krakenGraph.js +12 -1
- package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
- package/dist/cli/tools/taskTool.js +47 -2
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -1829,10 +1829,10 @@ function mergeDefs(...defs) {
|
|
|
1829
1829
|
function cloneDef(schema) {
|
|
1830
1830
|
return mergeDefs(schema._zod.def);
|
|
1831
1831
|
}
|
|
1832
|
-
function getElementAtPath(obj,
|
|
1833
|
-
if (!
|
|
1832
|
+
function getElementAtPath(obj, path44) {
|
|
1833
|
+
if (!path44)
|
|
1834
1834
|
return obj;
|
|
1835
|
-
return
|
|
1835
|
+
return path44.reduce((acc, key) => acc?.[key], obj);
|
|
1836
1836
|
}
|
|
1837
1837
|
function promiseAllObject(promisesObj) {
|
|
1838
1838
|
const keys = Object.keys(promisesObj);
|
|
@@ -2160,11 +2160,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
2160
2160
|
}
|
|
2161
2161
|
return false;
|
|
2162
2162
|
}
|
|
2163
|
-
function prefixIssues(
|
|
2163
|
+
function prefixIssues(path44, issues) {
|
|
2164
2164
|
return issues.map((iss) => {
|
|
2165
2165
|
var _a3;
|
|
2166
2166
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
2167
|
-
iss.path.unshift(
|
|
2167
|
+
iss.path.unshift(path44);
|
|
2168
2168
|
return iss;
|
|
2169
2169
|
});
|
|
2170
2170
|
}
|
|
@@ -2382,16 +2382,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2382
2382
|
}
|
|
2383
2383
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2384
2384
|
const fieldErrors = { _errors: [] };
|
|
2385
|
-
const processError = (error52,
|
|
2385
|
+
const processError = (error52, path44 = []) => {
|
|
2386
2386
|
for (const issue2 of error52.issues) {
|
|
2387
2387
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2388
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2388
|
+
issue2.errors.map((issues) => processError({ issues }, [...path44, ...issue2.path]));
|
|
2389
2389
|
} else if (issue2.code === "invalid_key") {
|
|
2390
|
-
processError({ issues: issue2.issues }, [...
|
|
2390
|
+
processError({ issues: issue2.issues }, [...path44, ...issue2.path]);
|
|
2391
2391
|
} else if (issue2.code === "invalid_element") {
|
|
2392
|
-
processError({ issues: issue2.issues }, [...
|
|
2392
|
+
processError({ issues: issue2.issues }, [...path44, ...issue2.path]);
|
|
2393
2393
|
} else {
|
|
2394
|
-
const fullpath = [...
|
|
2394
|
+
const fullpath = [...path44, ...issue2.path];
|
|
2395
2395
|
if (fullpath.length === 0) {
|
|
2396
2396
|
fieldErrors._errors.push(mapper(issue2));
|
|
2397
2397
|
} else {
|
|
@@ -2418,17 +2418,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2418
2418
|
}
|
|
2419
2419
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2420
2420
|
const result = { errors: [] };
|
|
2421
|
-
const processError = (error52,
|
|
2421
|
+
const processError = (error52, path44 = []) => {
|
|
2422
2422
|
var _a3, _b;
|
|
2423
2423
|
for (const issue2 of error52.issues) {
|
|
2424
2424
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2425
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2425
|
+
issue2.errors.map((issues) => processError({ issues }, [...path44, ...issue2.path]));
|
|
2426
2426
|
} else if (issue2.code === "invalid_key") {
|
|
2427
|
-
processError({ issues: issue2.issues }, [...
|
|
2427
|
+
processError({ issues: issue2.issues }, [...path44, ...issue2.path]);
|
|
2428
2428
|
} else if (issue2.code === "invalid_element") {
|
|
2429
|
-
processError({ issues: issue2.issues }, [...
|
|
2429
|
+
processError({ issues: issue2.issues }, [...path44, ...issue2.path]);
|
|
2430
2430
|
} else {
|
|
2431
|
-
const fullpath = [...
|
|
2431
|
+
const fullpath = [...path44, ...issue2.path];
|
|
2432
2432
|
if (fullpath.length === 0) {
|
|
2433
2433
|
result.errors.push(mapper(issue2));
|
|
2434
2434
|
continue;
|
|
@@ -2460,8 +2460,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2460
2460
|
}
|
|
2461
2461
|
function toDotPath(_path) {
|
|
2462
2462
|
const segs = [];
|
|
2463
|
-
const
|
|
2464
|
-
for (const seg of
|
|
2463
|
+
const path44 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2464
|
+
for (const seg of path44) {
|
|
2465
2465
|
if (typeof seg === "number")
|
|
2466
2466
|
segs.push(`[${seg}]`);
|
|
2467
2467
|
else if (typeof seg === "symbol")
|
|
@@ -15964,13 +15964,13 @@ function resolveRef(ref, ctx) {
|
|
|
15964
15964
|
if (!ref.startsWith("#")) {
|
|
15965
15965
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15966
15966
|
}
|
|
15967
|
-
const
|
|
15968
|
-
if (
|
|
15967
|
+
const path44 = ref.slice(1).split("/").filter(Boolean);
|
|
15968
|
+
if (path44.length === 0) {
|
|
15969
15969
|
return ctx.rootSchema;
|
|
15970
15970
|
}
|
|
15971
15971
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15972
|
-
if (
|
|
15973
|
-
const key =
|
|
15972
|
+
if (path44[0] === defsKey) {
|
|
15973
|
+
const key = path44[1];
|
|
15974
15974
|
if (!key || !ctx.defs[key]) {
|
|
15975
15975
|
throw new Error(`Reference not found: ${ref}`);
|
|
15976
15976
|
}
|
|
@@ -18134,11 +18134,11 @@ var init_tools = __esm({
|
|
|
18134
18134
|
if (!ctx.addDocument)
|
|
18135
18135
|
return "Knowledge vault tool not available.";
|
|
18136
18136
|
const title = args["title"] || "New Document";
|
|
18137
|
-
const
|
|
18137
|
+
const path44 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
18138
18138
|
const content = args["content"] || "";
|
|
18139
18139
|
const tags = args["tags"] || [];
|
|
18140
18140
|
ctx.addDocument({
|
|
18141
|
-
path:
|
|
18141
|
+
path: path44,
|
|
18142
18142
|
title,
|
|
18143
18143
|
content,
|
|
18144
18144
|
format: "markdown",
|
|
@@ -18147,7 +18147,7 @@ var init_tools = __esm({
|
|
|
18147
18147
|
workspaceId: ctx.workspaceId
|
|
18148
18148
|
});
|
|
18149
18149
|
ctx.addActivity("vault", "created document", title);
|
|
18150
|
-
return `Document "${title}" created at "${
|
|
18150
|
+
return `Document "${title}" created at "${path44}".`;
|
|
18151
18151
|
}
|
|
18152
18152
|
}
|
|
18153
18153
|
];
|
|
@@ -21839,11 +21839,11 @@ var init_synthesisAudit = __esm({
|
|
|
21839
21839
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21840
21840
|
import { join as join2 } from "node:path";
|
|
21841
21841
|
function loadNfrSpec(zelariRoot) {
|
|
21842
|
-
const
|
|
21843
|
-
if (!existsSync6(
|
|
21842
|
+
const path44 = join2(zelariRoot, "nfr-spec.json");
|
|
21843
|
+
if (!existsSync6(path44))
|
|
21844
21844
|
return null;
|
|
21845
21845
|
try {
|
|
21846
|
-
const raw = JSON.parse(readFileSync5(
|
|
21846
|
+
const raw = JSON.parse(readFileSync5(path44, "utf8"));
|
|
21847
21847
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
21848
21848
|
return null;
|
|
21849
21849
|
return raw;
|
|
@@ -24149,9 +24149,9 @@ var init_types3 = __esm({
|
|
|
24149
24149
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
24150
24150
|
import { join as join8 } from "node:path";
|
|
24151
24151
|
function readLessonsDeduped(zelariRoot) {
|
|
24152
|
-
const
|
|
24152
|
+
const path44 = join8(zelariRoot, LESSONS_FILE);
|
|
24153
24153
|
try {
|
|
24154
|
-
const raw = readFileSync10(
|
|
24154
|
+
const raw = readFileSync10(path44, "utf8");
|
|
24155
24155
|
const byId = /* @__PURE__ */ new Map();
|
|
24156
24156
|
for (const line of raw.split(/\r?\n/)) {
|
|
24157
24157
|
if (!line.trim())
|
|
@@ -24252,8 +24252,8 @@ function keywordsFrom(check2, signature) {
|
|
|
24252
24252
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
24253
24253
|
}
|
|
24254
24254
|
function writeLesson(zelariRoot, lesson) {
|
|
24255
|
-
const
|
|
24256
|
-
appendFileSync(
|
|
24255
|
+
const path44 = join9(zelariRoot, LESSONS_FILE);
|
|
24256
|
+
appendFileSync(path44, `${JSON.stringify(lesson)}
|
|
24257
24257
|
`, "utf8");
|
|
24258
24258
|
}
|
|
24259
24259
|
function findSimilar(lessons, signature) {
|
|
@@ -24866,9 +24866,9 @@ function findCycle(nodes) {
|
|
|
24866
24866
|
if (color.get(start) !== WHITE)
|
|
24867
24867
|
continue;
|
|
24868
24868
|
const stack = [[start, 0]];
|
|
24869
|
-
const
|
|
24869
|
+
const path44 = [];
|
|
24870
24870
|
color.set(start, GRAY);
|
|
24871
|
-
|
|
24871
|
+
path44.push(start);
|
|
24872
24872
|
while (stack.length > 0) {
|
|
24873
24873
|
const top = stack[stack.length - 1];
|
|
24874
24874
|
const [id, idx] = top;
|
|
@@ -24881,17 +24881,17 @@ function findCycle(nodes) {
|
|
|
24881
24881
|
continue;
|
|
24882
24882
|
const c = color.get(dep);
|
|
24883
24883
|
if (c === GRAY) {
|
|
24884
|
-
const at =
|
|
24885
|
-
return [...
|
|
24884
|
+
const at = path44.indexOf(dep);
|
|
24885
|
+
return [...path44.slice(at), dep];
|
|
24886
24886
|
}
|
|
24887
24887
|
if (c === WHITE) {
|
|
24888
24888
|
color.set(dep, GRAY);
|
|
24889
|
-
|
|
24889
|
+
path44.push(dep);
|
|
24890
24890
|
stack.push([dep, 0]);
|
|
24891
24891
|
}
|
|
24892
24892
|
} else {
|
|
24893
24893
|
color.set(id, BLACK);
|
|
24894
|
-
|
|
24894
|
+
path44.pop();
|
|
24895
24895
|
stack.pop();
|
|
24896
24896
|
}
|
|
24897
24897
|
}
|
|
@@ -25700,9 +25700,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
25700
25700
|
const rnd = randomBytes(3).toString("hex");
|
|
25701
25701
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
25702
25702
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
25703
|
-
const
|
|
25704
|
-
writeFileSync10(
|
|
25705
|
-
return
|
|
25703
|
+
const path44 = join11(dir, file2);
|
|
25704
|
+
writeFileSync10(path44, fullText, "utf8");
|
|
25705
|
+
return path44;
|
|
25706
25706
|
} catch {
|
|
25707
25707
|
return null;
|
|
25708
25708
|
}
|
|
@@ -25748,10 +25748,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
25748
25748
|
${tail}`;
|
|
25749
25749
|
}
|
|
25750
25750
|
if (doSpill) {
|
|
25751
|
-
const
|
|
25752
|
-
if (
|
|
25751
|
+
const path44 = spillToolOutput(text, { toolName: opts.toolName });
|
|
25752
|
+
if (path44) {
|
|
25753
25753
|
const spillNote = `
|
|
25754
|
-
\u2026 [full output spilled to: ${
|
|
25754
|
+
\u2026 [full output spilled to: ${path44} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
25755
25755
|
if (preview.includes("] \u2026\n")) {
|
|
25756
25756
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
25757
25757
|
`);
|
|
@@ -26581,11 +26581,20 @@ function maxToolCallsForThoroughness(thoroughness, agent) {
|
|
|
26581
26581
|
if (thoroughness === "deep") return 12;
|
|
26582
26582
|
return 6;
|
|
26583
26583
|
}
|
|
26584
|
-
async function runSubAgent(harness) {
|
|
26584
|
+
async function runSubAgent(harness, opts = {}) {
|
|
26585
|
+
const { signal } = opts;
|
|
26585
26586
|
let current = "";
|
|
26586
26587
|
let lastCompleted = "";
|
|
26587
26588
|
let error51;
|
|
26589
|
+
if (signal?.aborted) return { result: "", aborted: true };
|
|
26588
26590
|
for await (const ev of harness.run()) {
|
|
26591
|
+
if (signal?.aborted) {
|
|
26592
|
+
return {
|
|
26593
|
+
result: (lastCompleted || current).trim(),
|
|
26594
|
+
...error51 ? { error: error51 } : {},
|
|
26595
|
+
aborted: true
|
|
26596
|
+
};
|
|
26597
|
+
}
|
|
26589
26598
|
switch (ev.type) {
|
|
26590
26599
|
case "message_start":
|
|
26591
26600
|
current = "";
|
|
@@ -26715,8 +26724,31 @@ async function runTentacle(opts) {
|
|
|
26715
26724
|
error: `task: failed to start sub-agent \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
26716
26725
|
};
|
|
26717
26726
|
}
|
|
26718
|
-
const { result, error: error51 } = await runSubAgent(harness
|
|
26727
|
+
const { result, error: error51, aborted: aborted2 } = await runSubAgent(harness, {
|
|
26728
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
26729
|
+
});
|
|
26719
26730
|
const durationMs = Date.now() - started;
|
|
26731
|
+
if (aborted2) {
|
|
26732
|
+
if (worktree && !shouldKeepWorktree()) await cleanupKrakenWorktree(worktree);
|
|
26733
|
+
appendKrakenRadio(parentCwd, sessionId, {
|
|
26734
|
+
kind: "error",
|
|
26735
|
+
agent,
|
|
26736
|
+
thoroughness,
|
|
26737
|
+
description: args.description,
|
|
26738
|
+
detail: "cancelled: node timeout",
|
|
26739
|
+
model: sub.model,
|
|
26740
|
+
worktree: worktree?.path ?? null,
|
|
26741
|
+
durationMs,
|
|
26742
|
+
ok: false
|
|
26743
|
+
});
|
|
26744
|
+
krakenTentacleEnd(liveId, {
|
|
26745
|
+
ok: false,
|
|
26746
|
+
model: sub.model,
|
|
26747
|
+
detail: "cancelled",
|
|
26748
|
+
durationMs
|
|
26749
|
+
});
|
|
26750
|
+
return { ok: false, agent, error: "task: sub-agent cancelled (node timeout)", cancelled: true };
|
|
26751
|
+
}
|
|
26720
26752
|
if (!result) {
|
|
26721
26753
|
if (worktree) await cleanupKrakenWorktree(worktree);
|
|
26722
26754
|
appendKrakenRadio(parentCwd, sessionId, {
|
|
@@ -28660,21 +28692,21 @@ function normalizeAuth(auth) {
|
|
|
28660
28692
|
return "agent";
|
|
28661
28693
|
}
|
|
28662
28694
|
function readSecrets() {
|
|
28663
|
-
const
|
|
28664
|
-
if (!existsSync17(
|
|
28695
|
+
const path44 = getSshSecretsPath();
|
|
28696
|
+
if (!existsSync17(path44)) return {};
|
|
28665
28697
|
try {
|
|
28666
|
-
return JSON.parse(readFileSync16(
|
|
28698
|
+
return JSON.parse(readFileSync16(path44, "utf8"));
|
|
28667
28699
|
} catch {
|
|
28668
28700
|
return {};
|
|
28669
28701
|
}
|
|
28670
28702
|
}
|
|
28671
28703
|
function writeSecrets(data) {
|
|
28672
|
-
const
|
|
28673
|
-
mkdirSync9(dirname2(
|
|
28674
|
-
writeFileSync11(
|
|
28704
|
+
const path44 = getSshSecretsPath();
|
|
28705
|
+
mkdirSync9(dirname2(path44), { recursive: true });
|
|
28706
|
+
writeFileSync11(path44, `${JSON.stringify(data, null, 2)}
|
|
28675
28707
|
`, "utf8");
|
|
28676
28708
|
try {
|
|
28677
|
-
chmodSync(
|
|
28709
|
+
chmodSync(path44, 384);
|
|
28678
28710
|
} catch {
|
|
28679
28711
|
}
|
|
28680
28712
|
}
|
|
@@ -28703,10 +28735,10 @@ function deleteSshPassword(id) {
|
|
|
28703
28735
|
writeSecrets({ passwords });
|
|
28704
28736
|
}
|
|
28705
28737
|
function readStore2() {
|
|
28706
|
-
const
|
|
28707
|
-
if (!existsSync17(
|
|
28738
|
+
const path44 = getSshTargetsPath();
|
|
28739
|
+
if (!existsSync17(path44)) return [];
|
|
28708
28740
|
try {
|
|
28709
|
-
const parsed = JSON.parse(readFileSync16(
|
|
28741
|
+
const parsed = JSON.parse(readFileSync16(path44, "utf8"));
|
|
28710
28742
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
28711
28743
|
return list.filter(
|
|
28712
28744
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -28721,11 +28753,11 @@ function readStore2() {
|
|
|
28721
28753
|
}
|
|
28722
28754
|
}
|
|
28723
28755
|
function writeStore2(targets) {
|
|
28724
|
-
const
|
|
28725
|
-
mkdirSync9(dirname2(
|
|
28756
|
+
const path44 = getSshTargetsPath();
|
|
28757
|
+
mkdirSync9(dirname2(path44), { recursive: true });
|
|
28726
28758
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
28727
28759
|
writeFileSync11(
|
|
28728
|
-
|
|
28760
|
+
path44,
|
|
28729
28761
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
28730
28762
|
`,
|
|
28731
28763
|
"utf8"
|
|
@@ -28971,11 +29003,11 @@ function formatSshTargetsForPrompt() {
|
|
|
28971
29003
|
];
|
|
28972
29004
|
for (const t of targets) {
|
|
28973
29005
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
28974
|
-
const
|
|
29006
|
+
const path44 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
28975
29007
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
28976
29008
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
28977
29009
|
lines.push(
|
|
28978
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
29010
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path44}${tags}${allow}`
|
|
28979
29011
|
);
|
|
28980
29012
|
}
|
|
28981
29013
|
return lines.join("\n");
|
|
@@ -31827,28 +31859,28 @@ var init_storage = __esm({
|
|
|
31827
31859
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
31828
31860
|
Storage = class {
|
|
31829
31861
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
31830
|
-
read(
|
|
31831
|
-
if (!existsSync25(
|
|
31832
|
-
throw new Error(`File not found: ${
|
|
31862
|
+
read(path44) {
|
|
31863
|
+
if (!existsSync25(path44)) {
|
|
31864
|
+
throw new Error(`File not found: ${path44}`);
|
|
31833
31865
|
}
|
|
31834
|
-
const md = readFileSync21(
|
|
31866
|
+
const md = readFileSync21(path44, "utf8");
|
|
31835
31867
|
return parseFrontmatter(md);
|
|
31836
31868
|
}
|
|
31837
31869
|
/** Read a Markdown file; returns null if not found. */
|
|
31838
|
-
readIfExists(
|
|
31839
|
-
if (!existsSync25(
|
|
31840
|
-
return this.read(
|
|
31870
|
+
readIfExists(path44) {
|
|
31871
|
+
if (!existsSync25(path44)) return null;
|
|
31872
|
+
return this.read(path44);
|
|
31841
31873
|
}
|
|
31842
31874
|
/**
|
|
31843
31875
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
31844
31876
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
31845
31877
|
*/
|
|
31846
|
-
write(
|
|
31847
|
-
mkdirSync11(dirname4(
|
|
31848
|
-
const tmp =
|
|
31878
|
+
write(path44, meta3, body) {
|
|
31879
|
+
mkdirSync11(dirname4(path44), { recursive: true });
|
|
31880
|
+
const tmp = path44 + ".tmp-" + process.pid;
|
|
31849
31881
|
const md = serializeFrontmatter(meta3, body);
|
|
31850
31882
|
writeFileSync13(tmp, md, "utf8");
|
|
31851
|
-
renameSync2(tmp,
|
|
31883
|
+
renameSync2(tmp, path44);
|
|
31852
31884
|
}
|
|
31853
31885
|
/** List all .md files in a directory (non-recursive). */
|
|
31854
31886
|
listMarkdown(dir) {
|
|
@@ -31926,8 +31958,8 @@ function readPlan(ctx) {
|
|
|
31926
31958
|
} catch {
|
|
31927
31959
|
}
|
|
31928
31960
|
}
|
|
31929
|
-
const
|
|
31930
|
-
const doc = ctx.storage.readIfExists(
|
|
31961
|
+
const path44 = workspaceFile(ctx.rootDir, "plan");
|
|
31962
|
+
const doc = ctx.storage.readIfExists(path44);
|
|
31931
31963
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
31932
31964
|
const meta3 = doc.meta;
|
|
31933
31965
|
return {
|
|
@@ -32092,7 +32124,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
32092
32124
|
dueDate: input.dueDate,
|
|
32093
32125
|
targetVersion: version2
|
|
32094
32126
|
});
|
|
32095
|
-
const
|
|
32127
|
+
const path44 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
32096
32128
|
const meta3 = {
|
|
32097
32129
|
kind: "milestone",
|
|
32098
32130
|
id,
|
|
@@ -32109,7 +32141,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
32109
32141
|
`Target version: ${version2}`,
|
|
32110
32142
|
""
|
|
32111
32143
|
].join("\n");
|
|
32112
|
-
ctx.storage.write(
|
|
32144
|
+
ctx.storage.write(path44, meta3, body);
|
|
32113
32145
|
return { id, created: true };
|
|
32114
32146
|
}
|
|
32115
32147
|
function readPlanSummary(ctx) {
|
|
@@ -32313,7 +32345,7 @@ function addIdeaStub(ctx) {
|
|
|
32313
32345
|
const tags = args["tags"] ?? [];
|
|
32314
32346
|
const category = args["category"] ?? "General";
|
|
32315
32347
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
32316
|
-
const
|
|
32348
|
+
const path44 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
32317
32349
|
const meta3 = {
|
|
32318
32350
|
kind: "adr",
|
|
32319
32351
|
status: "proposed",
|
|
@@ -32339,7 +32371,7 @@ function addIdeaStub(ctx) {
|
|
|
32339
32371
|
...consequences.map((c) => `- ${c}`),
|
|
32340
32372
|
""
|
|
32341
32373
|
].join("\n");
|
|
32342
|
-
ctx.storage.write(
|
|
32374
|
+
ctx.storage.write(path44, meta3, body);
|
|
32343
32375
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
32344
32376
|
});
|
|
32345
32377
|
}
|
|
@@ -32421,14 +32453,14 @@ function createDocumentStub(ctx) {
|
|
|
32421
32453
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
32422
32454
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
32423
32455
|
}
|
|
32424
|
-
const
|
|
32456
|
+
const path44 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
32425
32457
|
const meta3 = {
|
|
32426
32458
|
kind: "doc",
|
|
32427
32459
|
id: slug,
|
|
32428
32460
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
32429
32461
|
tags
|
|
32430
32462
|
};
|
|
32431
|
-
ctx.storage.write(
|
|
32463
|
+
ctx.storage.write(path44, meta3, content);
|
|
32432
32464
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
32433
32465
|
});
|
|
32434
32466
|
}
|
|
@@ -32972,10 +33004,10 @@ function getUserMcpPath() {
|
|
|
32972
33004
|
function getProjectMcpPath(projectRoot) {
|
|
32973
33005
|
return join24(projectRoot, ".zelari", "mcp.json");
|
|
32974
33006
|
}
|
|
32975
|
-
function readFile2(
|
|
32976
|
-
if (!existsSync28(
|
|
33007
|
+
function readFile2(path44) {
|
|
33008
|
+
if (!existsSync28(path44)) return {};
|
|
32977
33009
|
try {
|
|
32978
|
-
const parsed = JSON.parse(readFileSync23(
|
|
33010
|
+
const parsed = JSON.parse(readFileSync23(path44, "utf8"));
|
|
32979
33011
|
const out = {};
|
|
32980
33012
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
32981
33013
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -32991,10 +33023,10 @@ function readFile2(path43) {
|
|
|
32991
33023
|
return {};
|
|
32992
33024
|
}
|
|
32993
33025
|
}
|
|
32994
|
-
function writeFile(
|
|
32995
|
-
mkdirSync13(dirname6(
|
|
33026
|
+
function writeFile(path44, servers) {
|
|
33027
|
+
mkdirSync13(dirname6(path44), { recursive: true });
|
|
32996
33028
|
const body = { mcpServers: servers };
|
|
32997
|
-
writeFileSync15(
|
|
33029
|
+
writeFileSync15(path44, `${JSON.stringify(body, null, 2)}
|
|
32998
33030
|
`, "utf8");
|
|
32999
33031
|
}
|
|
33000
33032
|
function listMcpServers(projectRoot) {
|
|
@@ -33027,9 +33059,9 @@ function upsertMcpServer(opts) {
|
|
|
33027
33059
|
if (!opts.config.command?.trim()) {
|
|
33028
33060
|
return { ok: false, error: "command is required" };
|
|
33029
33061
|
}
|
|
33030
|
-
let
|
|
33062
|
+
let path44;
|
|
33031
33063
|
if (opts.scope === "user") {
|
|
33032
|
-
|
|
33064
|
+
path44 = getUserMcpPath();
|
|
33033
33065
|
} else {
|
|
33034
33066
|
const root = opts.projectRoot?.trim();
|
|
33035
33067
|
if (!root) {
|
|
@@ -33038,30 +33070,30 @@ function upsertMcpServer(opts) {
|
|
|
33038
33070
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
33039
33071
|
};
|
|
33040
33072
|
}
|
|
33041
|
-
|
|
33073
|
+
path44 = getProjectMcpPath(root);
|
|
33042
33074
|
}
|
|
33043
|
-
const current = readFile2(
|
|
33075
|
+
const current = readFile2(path44);
|
|
33044
33076
|
current[name] = {
|
|
33045
33077
|
command: opts.config.command.trim(),
|
|
33046
33078
|
args: opts.config.args,
|
|
33047
33079
|
env: opts.config.env,
|
|
33048
33080
|
enabled: opts.config.enabled !== false
|
|
33049
33081
|
};
|
|
33050
|
-
writeFile(
|
|
33051
|
-
return { ok: true, path:
|
|
33082
|
+
writeFile(path44, current);
|
|
33083
|
+
return { ok: true, path: path44 };
|
|
33052
33084
|
}
|
|
33053
33085
|
function removeMcpServer(opts) {
|
|
33054
|
-
const
|
|
33055
|
-
if (!
|
|
33086
|
+
const path44 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
33087
|
+
if (!path44) {
|
|
33056
33088
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
33057
33089
|
}
|
|
33058
|
-
const current = readFile2(
|
|
33090
|
+
const current = readFile2(path44);
|
|
33059
33091
|
if (!(opts.name in current)) {
|
|
33060
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
33092
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path44}` };
|
|
33061
33093
|
}
|
|
33062
33094
|
delete current[opts.name];
|
|
33063
|
-
writeFile(
|
|
33064
|
-
return { ok: true, path:
|
|
33095
|
+
writeFile(path44, current);
|
|
33096
|
+
return { ok: true, path: path44 };
|
|
33065
33097
|
}
|
|
33066
33098
|
var init_mcpConfigIo = __esm({
|
|
33067
33099
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -33398,10 +33430,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
33398
33430
|
import { join as join26 } from "node:path";
|
|
33399
33431
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
33400
33432
|
async function readPackageJson2(projectRoot) {
|
|
33401
|
-
const
|
|
33402
|
-
if (!existsSync30(
|
|
33433
|
+
const path44 = join26(projectRoot, "package.json");
|
|
33434
|
+
if (!existsSync30(path44)) return null;
|
|
33403
33435
|
try {
|
|
33404
|
-
return JSON.parse(await readFile3(
|
|
33436
|
+
return JSON.parse(await readFile3(path44, "utf8"));
|
|
33405
33437
|
} catch {
|
|
33406
33438
|
return null;
|
|
33407
33439
|
}
|
|
@@ -33483,9 +33515,9 @@ async function genBuild(ctx) {
|
|
|
33483
33515
|
].join("\n");
|
|
33484
33516
|
}
|
|
33485
33517
|
async function genOpenQuestions(ctx) {
|
|
33486
|
-
const
|
|
33487
|
-
if (!existsSync30(
|
|
33488
|
-
const content = readFileSync25(
|
|
33518
|
+
const path44 = join26(ctx.rootDir, "risks.md");
|
|
33519
|
+
if (!existsSync30(path44)) return "_No open questions._";
|
|
33520
|
+
const content = readFileSync25(path44, "utf8");
|
|
33489
33521
|
const lines = content.split("\n");
|
|
33490
33522
|
const questions = [];
|
|
33491
33523
|
let currentTitle = "";
|
|
@@ -34052,8 +34084,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
34052
34084
|
sources: scope.sources
|
|
34053
34085
|
} : void 0
|
|
34054
34086
|
});
|
|
34055
|
-
const
|
|
34056
|
-
completionHook = { ran: true, path:
|
|
34087
|
+
const path44 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
34088
|
+
completionHook = { ran: true, path: path44, completion };
|
|
34057
34089
|
} catch (err) {
|
|
34058
34090
|
completionHook = {
|
|
34059
34091
|
ran: true,
|
|
@@ -35272,7 +35304,8 @@ __export(planner_exports, {
|
|
|
35272
35304
|
PlannerTransportError: () => PlannerTransportError,
|
|
35273
35305
|
buildGraphFromPlan: () => buildGraphFromPlan,
|
|
35274
35306
|
extractJsonObject: () => extractJsonObject,
|
|
35275
|
-
planTaskGraph: () => planTaskGraph
|
|
35307
|
+
planTaskGraph: () => planTaskGraph,
|
|
35308
|
+
stripReasoningBlocks: () => stripReasoningBlocks
|
|
35276
35309
|
});
|
|
35277
35310
|
function resolvePlannerTimeoutMs(env = process.env) {
|
|
35278
35311
|
const raw = env.ZELARI_KRAKEN_PLANNER_TIMEOUT_MS;
|
|
@@ -35286,22 +35319,77 @@ function resolvePlannerMaxTokens(env = process.env) {
|
|
|
35286
35319
|
const n = Number.parseInt(raw, 10);
|
|
35287
35320
|
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LLM_MAX_TOKENS;
|
|
35288
35321
|
}
|
|
35289
|
-
function extractJsonObject(text) {
|
|
35290
|
-
const
|
|
35291
|
-
const
|
|
35292
|
-
|
|
35293
|
-
const
|
|
35294
|
-
|
|
35322
|
+
function extractJsonObject(text, opts = {}) {
|
|
35323
|
+
const stripped = stripReasoningBlocks(text);
|
|
35324
|
+
const usable = findJsonIn(stripped, opts);
|
|
35325
|
+
if (usable) return usable.value;
|
|
35326
|
+
const raw = findJsonIn(text, opts);
|
|
35327
|
+
if (raw) return raw.value;
|
|
35328
|
+
const body = bodyOf(stripped) || bodyOf(text);
|
|
35295
35329
|
try {
|
|
35296
|
-
return JSON.parse(
|
|
35330
|
+
return JSON.parse(body);
|
|
35297
35331
|
} catch (err) {
|
|
35298
35332
|
try {
|
|
35299
|
-
return JSON.parse(repairLooseJson(
|
|
35333
|
+
return JSON.parse(repairLooseJson(body));
|
|
35300
35334
|
} catch {
|
|
35301
35335
|
throw err instanceof Error ? err : new Error(String(err));
|
|
35302
35336
|
}
|
|
35303
35337
|
}
|
|
35304
35338
|
}
|
|
35339
|
+
function bodyOf(text) {
|
|
35340
|
+
const trimmed = text.trim();
|
|
35341
|
+
const fenced = /^```(?:json)?\s*([\s\S]*?)```$/i.exec(trimmed);
|
|
35342
|
+
return fenced ? fenced[1].trim() : trimmed;
|
|
35343
|
+
}
|
|
35344
|
+
function findJsonIn(text, opts) {
|
|
35345
|
+
let firstParsed = null;
|
|
35346
|
+
for (const candidate of balancedJsonObjects(bodyOf(text))) {
|
|
35347
|
+
let value;
|
|
35348
|
+
try {
|
|
35349
|
+
value = JSON.parse(candidate);
|
|
35350
|
+
} catch {
|
|
35351
|
+
try {
|
|
35352
|
+
value = JSON.parse(repairLooseJson(candidate));
|
|
35353
|
+
} catch {
|
|
35354
|
+
continue;
|
|
35355
|
+
}
|
|
35356
|
+
}
|
|
35357
|
+
if (!opts.requireKey) return { value };
|
|
35358
|
+
if (value && typeof value === "object" && opts.requireKey in value) {
|
|
35359
|
+
return { value };
|
|
35360
|
+
}
|
|
35361
|
+
const unwrapped = unwrapKey(value, opts.requireKey);
|
|
35362
|
+
if (unwrapped) return { value: unwrapped };
|
|
35363
|
+
if (!firstParsed) firstParsed = { value };
|
|
35364
|
+
}
|
|
35365
|
+
return firstParsed;
|
|
35366
|
+
}
|
|
35367
|
+
function unwrapKey(value, key) {
|
|
35368
|
+
if (!value || typeof value !== "object") return null;
|
|
35369
|
+
for (const child of Object.values(value)) {
|
|
35370
|
+
if (child && typeof child === "object" && key in child) {
|
|
35371
|
+
return child;
|
|
35372
|
+
}
|
|
35373
|
+
}
|
|
35374
|
+
return null;
|
|
35375
|
+
}
|
|
35376
|
+
function stripReasoningBlocks(text) {
|
|
35377
|
+
let out = text.replace(/<(think|thinking|reasoning)>[\s\S]*?<\/\1>/gi, "");
|
|
35378
|
+
const openIdx = out.search(/<(think|thinking|reasoning)>/i);
|
|
35379
|
+
if (openIdx >= 0) out = out.slice(0, openIdx);
|
|
35380
|
+
return out;
|
|
35381
|
+
}
|
|
35382
|
+
function* balancedJsonObjects(s) {
|
|
35383
|
+
let from = 0;
|
|
35384
|
+
while (from < s.length) {
|
|
35385
|
+
const next = extractBalancedJsonObject2(s.slice(from));
|
|
35386
|
+
if (!next) return;
|
|
35387
|
+
yield next;
|
|
35388
|
+
const idx = s.indexOf(next, from);
|
|
35389
|
+
if (idx < 0) return;
|
|
35390
|
+
from = idx + next.length;
|
|
35391
|
+
}
|
|
35392
|
+
}
|
|
35305
35393
|
function extractBalancedJsonObject2(s) {
|
|
35306
35394
|
const start = s.indexOf("{");
|
|
35307
35395
|
if (start < 0) return null;
|
|
@@ -35328,6 +35416,23 @@ function extractBalancedJsonObject2(s) {
|
|
|
35328
35416
|
}
|
|
35329
35417
|
return null;
|
|
35330
35418
|
}
|
|
35419
|
+
function escapeJsonControlChar(c) {
|
|
35420
|
+
if (c >= " ") return c;
|
|
35421
|
+
switch (c) {
|
|
35422
|
+
case "\n":
|
|
35423
|
+
return "\\n";
|
|
35424
|
+
case "\r":
|
|
35425
|
+
return "\\r";
|
|
35426
|
+
case " ":
|
|
35427
|
+
return "\\t";
|
|
35428
|
+
case "\b":
|
|
35429
|
+
return "\\b";
|
|
35430
|
+
case "\f":
|
|
35431
|
+
return "\\f";
|
|
35432
|
+
default:
|
|
35433
|
+
return `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
35434
|
+
}
|
|
35435
|
+
}
|
|
35331
35436
|
function repairLooseJson(s) {
|
|
35332
35437
|
let out = "";
|
|
35333
35438
|
let i = 0;
|
|
@@ -35339,15 +35444,16 @@ function repairLooseJson(s) {
|
|
|
35339
35444
|
i += 1;
|
|
35340
35445
|
while (i < n) {
|
|
35341
35446
|
const c = s[i];
|
|
35342
|
-
out += c;
|
|
35343
35447
|
i += 1;
|
|
35344
35448
|
if (c === "\\") {
|
|
35449
|
+
out += c;
|
|
35345
35450
|
if (i < n) {
|
|
35346
35451
|
out += s[i];
|
|
35347
35452
|
i += 1;
|
|
35348
35453
|
}
|
|
35349
35454
|
continue;
|
|
35350
35455
|
}
|
|
35456
|
+
out += escapeJsonControlChar(c);
|
|
35351
35457
|
if (c === '"') break;
|
|
35352
35458
|
}
|
|
35353
35459
|
continue;
|
|
@@ -35366,7 +35472,7 @@ function repairLooseJson(s) {
|
|
|
35366
35472
|
i += 1;
|
|
35367
35473
|
break;
|
|
35368
35474
|
}
|
|
35369
|
-
value += c;
|
|
35475
|
+
value += escapeJsonControlChar(c);
|
|
35370
35476
|
i += 1;
|
|
35371
35477
|
}
|
|
35372
35478
|
out += `"${value.replace(/"/g, '\\"')}"`;
|
|
@@ -35509,10 +35615,13 @@ async function createDefaultLlmClient(opts) {
|
|
|
35509
35615
|
}
|
|
35510
35616
|
};
|
|
35511
35617
|
}
|
|
35512
|
-
function buildPlannerUserPrompt(prompt) {
|
|
35618
|
+
function buildPlannerUserPrompt(prompt, previousAttempt) {
|
|
35619
|
+
const prev2 = previousAttempt?.trim() ? `
|
|
35620
|
+
${previousAttempt.trim()}
|
|
35621
|
+
` : "";
|
|
35513
35622
|
return `Goal:
|
|
35514
35623
|
${prompt.trim()}
|
|
35515
|
-
|
|
35624
|
+
${prev2}
|
|
35516
35625
|
Return ONLY the JSON object described in the system prompt.`;
|
|
35517
35626
|
}
|
|
35518
35627
|
function uniqueId(base, existing) {
|
|
@@ -35579,14 +35688,19 @@ async function planTaskGraph(opts) {
|
|
|
35579
35688
|
const client = opts.llmClient ?? await createDefaultLlmClient({ provider: opts.provider, model: opts.model });
|
|
35580
35689
|
const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
|
|
35581
35690
|
const graphId = opts.graphId ?? `kraken-${Date.now().toString(36)}`;
|
|
35582
|
-
const userBase = buildPlannerUserPrompt(opts.prompt);
|
|
35691
|
+
const userBase = buildPlannerUserPrompt(opts.prompt, opts.previousAttempt);
|
|
35583
35692
|
let lastError;
|
|
35584
35693
|
let userMessage = userBase;
|
|
35585
35694
|
for (let attempt = 1; attempt <= MAX_PLAN_ATTEMPTS; attempt++) {
|
|
35586
35695
|
try {
|
|
35587
35696
|
const text = await client.complete({ system: KRAKEN_PLANNER_SYSTEM_PROMPT, user: userMessage });
|
|
35588
|
-
const parsedJson = extractJsonObject(text);
|
|
35697
|
+
const parsedJson = extractJsonObject(text, { requireKey: "nodes" });
|
|
35589
35698
|
const validated = PlannedGraphSchema.parse(parsedJson);
|
|
35699
|
+
if (!validated.nodes.some((n) => n.kind === "general")) {
|
|
35700
|
+
throw new Error(
|
|
35701
|
+
'the plan contains no "general" node, so it cannot change anything \u2014 every node is read-only "explore". Include at least one "general" node that performs the actual work.'
|
|
35702
|
+
);
|
|
35703
|
+
}
|
|
35590
35704
|
const graph = buildGraphFromPlan(graphId, validated.nodes);
|
|
35591
35705
|
const check2 = validateGraph(graph, { maxNodes });
|
|
35592
35706
|
if (!check2.ok) {
|
|
@@ -35664,6 +35778,93 @@ var init_planner = __esm({
|
|
|
35664
35778
|
}
|
|
35665
35779
|
});
|
|
35666
35780
|
|
|
35781
|
+
// src/cli/kraken/graphMemory.ts
|
|
35782
|
+
var graphMemory_exports = {};
|
|
35783
|
+
__export(graphMemory_exports, {
|
|
35784
|
+
formatSnapshotForPlanner: () => formatSnapshotForPlanner,
|
|
35785
|
+
loadGraphSnapshot: () => loadGraphSnapshot,
|
|
35786
|
+
saveGraphSnapshot: () => saveGraphSnapshot,
|
|
35787
|
+
toGraphSnapshot: () => toGraphSnapshot
|
|
35788
|
+
});
|
|
35789
|
+
import { promises as fs19 } from "node:fs";
|
|
35790
|
+
import path34 from "node:path";
|
|
35791
|
+
function snapshotPath(cwd) {
|
|
35792
|
+
return path34.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
|
|
35793
|
+
}
|
|
35794
|
+
function toGraphSnapshot(graph, opts) {
|
|
35795
|
+
return {
|
|
35796
|
+
graphId: graph.id,
|
|
35797
|
+
goal: opts.goal,
|
|
35798
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35799
|
+
converged: opts.converged,
|
|
35800
|
+
nodes: [...graph.nodes.values()].map((n) => ({
|
|
35801
|
+
id: n.id,
|
|
35802
|
+
kind: n.kind,
|
|
35803
|
+
label: n.label,
|
|
35804
|
+
status: n.status,
|
|
35805
|
+
...n.scope && n.scope.length > 0 ? { scope: n.scope } : {},
|
|
35806
|
+
...n.error ? { error: n.error.slice(0, 300) } : {}
|
|
35807
|
+
}))
|
|
35808
|
+
};
|
|
35809
|
+
}
|
|
35810
|
+
async function saveGraphSnapshot(cwd, snapshot) {
|
|
35811
|
+
try {
|
|
35812
|
+
await fs19.access(cwd);
|
|
35813
|
+
const file2 = snapshotPath(cwd);
|
|
35814
|
+
await fs19.mkdir(path34.dirname(file2), { recursive: true });
|
|
35815
|
+
await fs19.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
35816
|
+
} catch {
|
|
35817
|
+
}
|
|
35818
|
+
}
|
|
35819
|
+
async function loadGraphSnapshot(cwd) {
|
|
35820
|
+
try {
|
|
35821
|
+
const raw = await fs19.readFile(snapshotPath(cwd), "utf8");
|
|
35822
|
+
const parsed = JSON.parse(raw);
|
|
35823
|
+
if (!parsed || !Array.isArray(parsed.nodes)) return null;
|
|
35824
|
+
return parsed;
|
|
35825
|
+
} catch {
|
|
35826
|
+
return null;
|
|
35827
|
+
}
|
|
35828
|
+
}
|
|
35829
|
+
function formatSnapshotForPlanner(snapshot) {
|
|
35830
|
+
if (!snapshot) return "";
|
|
35831
|
+
const done = snapshot.nodes.filter((n) => n.status === "done");
|
|
35832
|
+
const failed = snapshot.nodes.filter((n) => n.status === "error");
|
|
35833
|
+
const skipped = snapshot.nodes.filter((n) => n.status === "skipped");
|
|
35834
|
+
if (failed.length === 0 && skipped.length === 0) return "";
|
|
35835
|
+
const line = (n) => `- ${n.label}${n.scope ? ` [${n.scope.join(", ")}]` : ""}${n.error ? ` \u2014 ${n.error}` : ""}`;
|
|
35836
|
+
const parts = [
|
|
35837
|
+
"",
|
|
35838
|
+
"## Previous unfinished task graph in this project",
|
|
35839
|
+
`Goal it was working on: "${snapshot.goal}"`,
|
|
35840
|
+
`It did NOT finish (${done.length} done, ${failed.length} failed, ${skipped.length} never ran).`,
|
|
35841
|
+
"",
|
|
35842
|
+
"If that goal is unrelated to the one above, ignore this section entirely.",
|
|
35843
|
+
""
|
|
35844
|
+
];
|
|
35845
|
+
if (done.length > 0) {
|
|
35846
|
+
parts.push("Already completed \u2014 do NOT redo this work:", ...done.map(line), "");
|
|
35847
|
+
}
|
|
35848
|
+
if (failed.length > 0) {
|
|
35849
|
+
parts.push("Failed \u2014 needs to be finished or repaired:", ...failed.map(line), "");
|
|
35850
|
+
}
|
|
35851
|
+
if (skipped.length > 0) {
|
|
35852
|
+
parts.push("Never ran (blocked by the failures above):", ...skipped.map(line), "");
|
|
35853
|
+
}
|
|
35854
|
+
parts.push(
|
|
35855
|
+
"Plan only the remaining work. Inspect what is already on disk before rewriting it, and prefer wiring/finishing existing modules over recreating them."
|
|
35856
|
+
);
|
|
35857
|
+
return parts.join("\n");
|
|
35858
|
+
}
|
|
35859
|
+
var SNAPSHOT_DIR, SNAPSHOT_FILE;
|
|
35860
|
+
var init_graphMemory = __esm({
|
|
35861
|
+
"src/cli/kraken/graphMemory.ts"() {
|
|
35862
|
+
"use strict";
|
|
35863
|
+
SNAPSHOT_DIR = path34.join(".zelari", "kraken");
|
|
35864
|
+
SNAPSHOT_FILE = "last-graph.json";
|
|
35865
|
+
}
|
|
35866
|
+
});
|
|
35867
|
+
|
|
35667
35868
|
// src/cli/kraken/tentacle.ts
|
|
35668
35869
|
var init_tentacle = __esm({
|
|
35669
35870
|
"src/cli/kraken/tentacle.ts"() {
|
|
@@ -35675,18 +35876,21 @@ var init_tentacle = __esm({
|
|
|
35675
35876
|
// src/cli/kraken/executor.ts
|
|
35676
35877
|
var executor_exports = {};
|
|
35677
35878
|
__export(executor_exports, {
|
|
35879
|
+
DEFAULT_CANCEL_GRACE_MS: () => DEFAULT_CANCEL_GRACE_MS,
|
|
35678
35880
|
DEFAULT_FIX_BUDGET: () => DEFAULT_FIX_BUDGET,
|
|
35679
35881
|
DEFAULT_MAX_PARALLEL: () => DEFAULT_MAX_PARALLEL,
|
|
35680
35882
|
DEFAULT_NODE_TIMEOUT_MS: () => DEFAULT_NODE_TIMEOUT_MS,
|
|
35883
|
+
DEFAULT_WRITER_NODE_TIMEOUT_MS: () => DEFAULT_WRITER_NODE_TIMEOUT_MS,
|
|
35681
35884
|
KrakenGraphExecutor: () => KrakenGraphExecutor,
|
|
35682
35885
|
isKrakenGraphEnabled: () => isKrakenGraphEnabled,
|
|
35683
35886
|
isWorldModelGateEnabled: () => isWorldModelGateEnabled,
|
|
35887
|
+
resolveCancelGraceMs: () => resolveCancelGraceMs,
|
|
35684
35888
|
resolveFixBudget: () => resolveFixBudget,
|
|
35685
35889
|
resolveMaxParallel: () => resolveMaxParallel,
|
|
35686
35890
|
resolveNodeTimeoutMs: () => resolveNodeTimeoutMs
|
|
35687
35891
|
});
|
|
35688
35892
|
import { existsSync as existsSync34 } from "node:fs";
|
|
35689
|
-
import
|
|
35893
|
+
import path35 from "node:path";
|
|
35690
35894
|
function resolveMaxParallel(env = process.env) {
|
|
35691
35895
|
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
35692
35896
|
if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
|
|
@@ -35699,11 +35903,27 @@ function resolveFixBudget(env = process.env) {
|
|
|
35699
35903
|
const n = Number.parseInt(raw, 10);
|
|
35700
35904
|
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_FIX_BUDGET;
|
|
35701
35905
|
}
|
|
35702
|
-
function
|
|
35703
|
-
const raw = env.
|
|
35704
|
-
if (raw === void 0 || raw === "") return
|
|
35906
|
+
function resolveCancelGraceMs(env = process.env) {
|
|
35907
|
+
const raw = env.ZELARI_KRAKEN_CANCEL_GRACE_MS;
|
|
35908
|
+
if (raw === void 0 || raw === "") return DEFAULT_CANCEL_GRACE_MS;
|
|
35705
35909
|
const n = Number.parseInt(raw, 10);
|
|
35706
|
-
return Number.isFinite(n) && n >= 0 ? n :
|
|
35910
|
+
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_CANCEL_GRACE_MS;
|
|
35911
|
+
}
|
|
35912
|
+
function resolveNodeTimeoutMs(env = process.env, agent) {
|
|
35913
|
+
const raw = env.ZELARI_KRAKEN_NODE_TIMEOUT_MS;
|
|
35914
|
+
if (raw !== void 0 && raw !== "") {
|
|
35915
|
+
const n = Number.parseInt(raw, 10);
|
|
35916
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
35917
|
+
}
|
|
35918
|
+
if (agent === "general") {
|
|
35919
|
+
const rawWriter = env.ZELARI_KRAKEN_WRITER_NODE_TIMEOUT_MS;
|
|
35920
|
+
if (rawWriter !== void 0 && rawWriter !== "") {
|
|
35921
|
+
const n = Number.parseInt(rawWriter, 10);
|
|
35922
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
35923
|
+
}
|
|
35924
|
+
return DEFAULT_WRITER_NODE_TIMEOUT_MS;
|
|
35925
|
+
}
|
|
35926
|
+
return DEFAULT_NODE_TIMEOUT_MS;
|
|
35707
35927
|
}
|
|
35708
35928
|
function isKrakenGraphEnabled(env = process.env) {
|
|
35709
35929
|
return env.ZELARI_KRAKEN_GRAPH !== "0";
|
|
@@ -35714,12 +35934,12 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
|
|
|
35714
35934
|
}
|
|
35715
35935
|
function defaultChecksExists(cwd) {
|
|
35716
35936
|
try {
|
|
35717
|
-
return existsSync34(
|
|
35937
|
+
return existsSync34(path35.join(cwd, ".zelari", "world", "checks.json"));
|
|
35718
35938
|
} catch {
|
|
35719
35939
|
return false;
|
|
35720
35940
|
}
|
|
35721
35941
|
}
|
|
35722
|
-
var DEFAULT_MAX_PARALLEL, DEFAULT_FIX_BUDGET, DEFAULT_NODE_TIMEOUT_MS, KrakenGraphExecutor;
|
|
35942
|
+
var DEFAULT_MAX_PARALLEL, DEFAULT_FIX_BUDGET, DEFAULT_NODE_TIMEOUT_MS, DEFAULT_WRITER_NODE_TIMEOUT_MS, DEFAULT_CANCEL_GRACE_MS, KrakenGraphExecutor;
|
|
35723
35943
|
var init_executor = __esm({
|
|
35724
35944
|
"src/cli/kraken/executor.ts"() {
|
|
35725
35945
|
"use strict";
|
|
@@ -35729,15 +35949,21 @@ var init_executor = __esm({
|
|
|
35729
35949
|
init_krakenRadio();
|
|
35730
35950
|
init_worldModel();
|
|
35731
35951
|
init_graphStatus();
|
|
35952
|
+
init_graphMemory();
|
|
35732
35953
|
DEFAULT_MAX_PARALLEL = 12;
|
|
35733
35954
|
DEFAULT_FIX_BUDGET = 3;
|
|
35734
35955
|
DEFAULT_NODE_TIMEOUT_MS = 3e5;
|
|
35956
|
+
DEFAULT_WRITER_NODE_TIMEOUT_MS = 9e5;
|
|
35957
|
+
DEFAULT_CANCEL_GRACE_MS = 3e4;
|
|
35735
35958
|
KrakenGraphExecutor = class {
|
|
35736
35959
|
deps;
|
|
35737
35960
|
parentCwd;
|
|
35738
35961
|
sessionId;
|
|
35962
|
+
goal;
|
|
35739
35963
|
maxParallel;
|
|
35964
|
+
/** Explicit all-kinds override; when undefined the budget is per-kind. */
|
|
35740
35965
|
nodeTimeoutMs;
|
|
35966
|
+
cancelGraceMs;
|
|
35741
35967
|
fixBudgetRemaining;
|
|
35742
35968
|
worldModelGateOverride;
|
|
35743
35969
|
runTentacleFn;
|
|
@@ -35749,8 +35975,10 @@ var init_executor = __esm({
|
|
|
35749
35975
|
this.deps = opts.taskToolDeps;
|
|
35750
35976
|
this.parentCwd = opts.parentCwd;
|
|
35751
35977
|
this.sessionId = opts.sessionId;
|
|
35978
|
+
this.goal = opts.goal;
|
|
35752
35979
|
this.maxParallel = opts.maxParallel ?? resolveMaxParallel();
|
|
35753
|
-
this.nodeTimeoutMs = opts.nodeTimeoutMs
|
|
35980
|
+
this.nodeTimeoutMs = opts.nodeTimeoutMs;
|
|
35981
|
+
this.cancelGraceMs = opts.cancelGraceMs;
|
|
35754
35982
|
this.fixBudgetRemaining = opts.fixBudget ?? resolveFixBudget();
|
|
35755
35983
|
this.worldModelGateOverride = opts.worldModelGate;
|
|
35756
35984
|
this.runTentacleFn = opts.runTentacleFn ?? runTentacle;
|
|
@@ -35808,6 +36036,10 @@ var init_executor = __esm({
|
|
|
35808
36036
|
});
|
|
35809
36037
|
}
|
|
35810
36038
|
endKrakenGraphLive(graph, converged);
|
|
36039
|
+
await saveGraphSnapshot(
|
|
36040
|
+
this.parentCwd,
|
|
36041
|
+
toGraphSnapshot(graph, { goal: this.goal ?? graph.id, converged })
|
|
36042
|
+
);
|
|
35811
36043
|
return {
|
|
35812
36044
|
graph,
|
|
35813
36045
|
converged,
|
|
@@ -35824,6 +36056,7 @@ var init_executor = __esm({
|
|
|
35824
36056
|
}
|
|
35825
36057
|
const usesWorktree = node.kind === "general" || node.kind === "fix";
|
|
35826
36058
|
const agent = node.kind === "fix" ? "general" : node.kind;
|
|
36059
|
+
const controller = new AbortController();
|
|
35827
36060
|
const res = await this.withNodeTimeout(
|
|
35828
36061
|
this.runTentacleFn({
|
|
35829
36062
|
deps: this.deps,
|
|
@@ -35841,9 +36074,11 @@ var init_executor = __esm({
|
|
|
35841
36074
|
// (Correction 4); explore/verify never create a worktree.
|
|
35842
36075
|
deferMerge: usesWorktree,
|
|
35843
36076
|
graphId: graph.id,
|
|
35844
|
-
nodeId: node.id
|
|
36077
|
+
nodeId: node.id,
|
|
36078
|
+
signal: controller.signal
|
|
35845
36079
|
}),
|
|
35846
|
-
agent
|
|
36080
|
+
agent,
|
|
36081
|
+
controller
|
|
35847
36082
|
);
|
|
35848
36083
|
if (res.ok && usesWorktree) {
|
|
35849
36084
|
this.nodeRunState.set(node.id, { worktreeHandle: res.worktreeHandle });
|
|
@@ -35851,25 +36086,50 @@ var init_executor = __esm({
|
|
|
35851
36086
|
return res;
|
|
35852
36087
|
}
|
|
35853
36088
|
/**
|
|
35854
|
-
* Bound a tentacle run to
|
|
35855
|
-
*
|
|
35856
|
-
*
|
|
35857
|
-
*
|
|
35858
|
-
*
|
|
35859
|
-
*
|
|
36089
|
+
* Bound a tentacle run to its wall-clock budget (explicit `nodeTimeoutMs`
|
|
36090
|
+
* option, else per-kind — writers get more than readers). On timeout the
|
|
36091
|
+
* run is CANCELLED via its AbortSignal, then given `cancelGraceMs` to
|
|
36092
|
+
* unwind. Cancellation lands at the sub-agent's next event boundary (an
|
|
36093
|
+
* async generator only observes `.return()` when it next yields), so a run
|
|
36094
|
+
* blocked on a slow provider call can outlive the grace period — that case
|
|
36095
|
+
* resolves with `cancelled: false` and `applyResult` refuses to re-spawn
|
|
36096
|
+
* the node, since a tentacle that may still be writing must not be joined
|
|
36097
|
+
* by a second one on the same scope.
|
|
36098
|
+
*
|
|
36099
|
+
* Either way `execute()` always settles, so the caller's process can exit
|
|
36100
|
+
* instead of hanging forever on one stuck node.
|
|
35860
36101
|
*/
|
|
35861
|
-
withNodeTimeout(promise2, agent) {
|
|
35862
|
-
|
|
35863
|
-
|
|
36102
|
+
async withNodeTimeout(promise2, agent, controller) {
|
|
36103
|
+
const ms = this.nodeTimeoutMs ?? resolveNodeTimeoutMs(process.env, agent);
|
|
36104
|
+
if (ms <= 0) return promise2;
|
|
35864
36105
|
let timer;
|
|
36106
|
+
const TIMED_OUT = Symbol("timeout");
|
|
35865
36107
|
const timeout = new Promise((resolve3) => {
|
|
35866
|
-
timer = setTimeout(() =>
|
|
35867
|
-
resolve3({ ok: false, agent, error: `tentacle timed out after ${ms}ms` });
|
|
35868
|
-
}, ms);
|
|
36108
|
+
timer = setTimeout(() => resolve3(TIMED_OUT), ms);
|
|
35869
36109
|
});
|
|
35870
|
-
|
|
36110
|
+
const raced = await Promise.race([promise2, timeout]).finally(() => {
|
|
35871
36111
|
if (timer) clearTimeout(timer);
|
|
35872
36112
|
});
|
|
36113
|
+
if (raced !== TIMED_OUT) return raced;
|
|
36114
|
+
controller?.abort();
|
|
36115
|
+
const graceMs = this.cancelGraceMs ?? resolveCancelGraceMs();
|
|
36116
|
+
let graceTimer;
|
|
36117
|
+
const grace = new Promise((resolve3) => {
|
|
36118
|
+
graceTimer = setTimeout(() => resolve3(TIMED_OUT), graceMs);
|
|
36119
|
+
});
|
|
36120
|
+
const settled = await Promise.race([promise2, grace]).finally(() => {
|
|
36121
|
+
if (graceTimer) clearTimeout(graceTimer);
|
|
36122
|
+
});
|
|
36123
|
+
if (settled !== TIMED_OUT) {
|
|
36124
|
+
if (settled.ok) return settled;
|
|
36125
|
+
return { ...settled, cancelled: true };
|
|
36126
|
+
}
|
|
36127
|
+
return {
|
|
36128
|
+
ok: false,
|
|
36129
|
+
agent,
|
|
36130
|
+
error: `tentacle timed out after ${ms}ms and did not stop within ${graceMs}ms of being cancelled; not retrying to avoid two tentacles writing the same scope`,
|
|
36131
|
+
cancelled: false
|
|
36132
|
+
};
|
|
35873
36133
|
}
|
|
35874
36134
|
/**
|
|
35875
36135
|
* Sequentially merge every dep node's deferred worktree (in dep order) into
|
|
@@ -35933,6 +36193,16 @@ var init_executor = __esm({
|
|
|
35933
36193
|
return;
|
|
35934
36194
|
}
|
|
35935
36195
|
node.error = res.error;
|
|
36196
|
+
if (res.cancelled === false) {
|
|
36197
|
+
node.status = "error";
|
|
36198
|
+
this.radio("node_end", {
|
|
36199
|
+
description: node.label,
|
|
36200
|
+
agent: node.kind,
|
|
36201
|
+
detail: res.error,
|
|
36202
|
+
ok: false
|
|
36203
|
+
});
|
|
36204
|
+
return;
|
|
36205
|
+
}
|
|
35936
36206
|
if (node.retryCount < node.maxRetries) {
|
|
35937
36207
|
node.retryCount += 1;
|
|
35938
36208
|
node.status = "pending";
|
|
@@ -36421,10 +36691,10 @@ var init_prereqChecks = __esm({
|
|
|
36421
36691
|
|
|
36422
36692
|
// src/cli/plugins/prefs.ts
|
|
36423
36693
|
import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync18, mkdirSync as mkdirSync15 } from "node:fs";
|
|
36424
|
-
import
|
|
36694
|
+
import path36 from "node:path";
|
|
36425
36695
|
import os9 from "node:os";
|
|
36426
36696
|
function getPluginPrefsPath() {
|
|
36427
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
36697
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path36.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
36428
36698
|
}
|
|
36429
36699
|
function getPluginPrefs() {
|
|
36430
36700
|
const file2 = getPluginPrefsPath();
|
|
@@ -36445,7 +36715,7 @@ function getPluginPrefs() {
|
|
|
36445
36715
|
}
|
|
36446
36716
|
function writePluginPrefs(prefs) {
|
|
36447
36717
|
const file2 = getPluginPrefsPath();
|
|
36448
|
-
mkdirSync15(
|
|
36718
|
+
mkdirSync15(path36.dirname(file2), { recursive: true });
|
|
36449
36719
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
36450
36720
|
encoding: "utf-8",
|
|
36451
36721
|
mode: 384
|
|
@@ -36482,7 +36752,7 @@ __export(registry_exports, {
|
|
|
36482
36752
|
isBinaryOnPath: () => isBinaryOnPath
|
|
36483
36753
|
});
|
|
36484
36754
|
import { existsSync as existsSync37 } from "node:fs";
|
|
36485
|
-
import
|
|
36755
|
+
import path37 from "node:path";
|
|
36486
36756
|
function detectLocalBin(bin) {
|
|
36487
36757
|
return (cwd) => {
|
|
36488
36758
|
try {
|
|
@@ -36500,7 +36770,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
36500
36770
|
const platform = opts.platform ?? process.platform;
|
|
36501
36771
|
const exists = opts.exists ?? existsSync37;
|
|
36502
36772
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
36503
|
-
const pathMod = platform === "win32" ?
|
|
36773
|
+
const pathMod = platform === "win32" ? path37.win32 : path37.posix;
|
|
36504
36774
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
36505
36775
|
const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
|
|
36506
36776
|
const candidates = [bin];
|
|
@@ -36809,10 +37079,10 @@ __export(triggerLock_exports, {
|
|
|
36809
37079
|
lockPath: () => lockPath,
|
|
36810
37080
|
releaseLock: () => releaseLock
|
|
36811
37081
|
});
|
|
36812
|
-
import { promises as
|
|
36813
|
-
import * as
|
|
37082
|
+
import { promises as fs24 } from "node:fs";
|
|
37083
|
+
import * as path42 from "node:path";
|
|
36814
37084
|
function lockPath(projectRoot) {
|
|
36815
|
-
return
|
|
37085
|
+
return path42.join(projectRoot, ".zelari", "trigger.lock");
|
|
36816
37086
|
}
|
|
36817
37087
|
function isPidAlive(pid) {
|
|
36818
37088
|
try {
|
|
@@ -36825,10 +37095,10 @@ function isPidAlive(pid) {
|
|
|
36825
37095
|
}
|
|
36826
37096
|
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
36827
37097
|
const lp = lockPath(projectRoot);
|
|
36828
|
-
const dir =
|
|
36829
|
-
await
|
|
37098
|
+
const dir = path42.dirname(lp);
|
|
37099
|
+
await fs24.mkdir(dir, { recursive: true });
|
|
36830
37100
|
try {
|
|
36831
|
-
const raw = await
|
|
37101
|
+
const raw = await fs24.readFile(lp, "utf8");
|
|
36832
37102
|
const existing = JSON.parse(raw);
|
|
36833
37103
|
if (existing.pid && isPidAlive(existing.pid)) {
|
|
36834
37104
|
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
@@ -36839,13 +37109,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
|
|
|
36839
37109
|
pid: process.pid,
|
|
36840
37110
|
acquiredAt: now().toISOString()
|
|
36841
37111
|
};
|
|
36842
|
-
await
|
|
37112
|
+
await fs24.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
36843
37113
|
return { acquired: true, lockPath: lp };
|
|
36844
37114
|
}
|
|
36845
37115
|
async function releaseLock(projectRoot) {
|
|
36846
37116
|
const lp = lockPath(projectRoot);
|
|
36847
37117
|
try {
|
|
36848
|
-
await
|
|
37118
|
+
await fs24.unlink(lp);
|
|
36849
37119
|
} catch {
|
|
36850
37120
|
}
|
|
36851
37121
|
}
|
|
@@ -37133,12 +37403,12 @@ function ensureHome() {
|
|
|
37133
37403
|
}
|
|
37134
37404
|
}
|
|
37135
37405
|
function loadCompanionConfig() {
|
|
37136
|
-
const
|
|
37137
|
-
if (!existsSync43(
|
|
37406
|
+
const path44 = getCompanionConfigPath();
|
|
37407
|
+
if (!existsSync43(path44)) {
|
|
37138
37408
|
return { projects: [] };
|
|
37139
37409
|
}
|
|
37140
37410
|
try {
|
|
37141
|
-
const raw = JSON.parse(readFileSync34(
|
|
37411
|
+
const raw = JSON.parse(readFileSync34(path44, "utf8"));
|
|
37142
37412
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
37143
37413
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
37144
37414
|
).map((p3) => ({
|
|
@@ -37176,16 +37446,16 @@ function loadOrCreateToken(explicit) {
|
|
|
37176
37446
|
return { token: explicit.trim(), created: false };
|
|
37177
37447
|
}
|
|
37178
37448
|
ensureHome();
|
|
37179
|
-
const
|
|
37180
|
-
if (existsSync43(
|
|
37181
|
-
const t = readFileSync34(
|
|
37449
|
+
const path44 = getCompanionTokenPath();
|
|
37450
|
+
if (existsSync43(path44)) {
|
|
37451
|
+
const t = readFileSync34(path44, "utf8").trim();
|
|
37182
37452
|
if (t) return { token: t, created: false };
|
|
37183
37453
|
}
|
|
37184
37454
|
const token = randomBytes3(24).toString("base64url");
|
|
37185
|
-
writeFileSync21(
|
|
37455
|
+
writeFileSync21(path44, token + "\n", "utf8");
|
|
37186
37456
|
try {
|
|
37187
|
-
const
|
|
37188
|
-
|
|
37457
|
+
const fs25 = __require("node:fs");
|
|
37458
|
+
fs25.chmodSync?.(path44, 384);
|
|
37189
37459
|
} catch {
|
|
37190
37460
|
}
|
|
37191
37461
|
return { token, created: true };
|
|
@@ -37210,17 +37480,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
37210
37480
|
byId.set(p3.id, p3);
|
|
37211
37481
|
}
|
|
37212
37482
|
for (const raw of extraPaths) {
|
|
37213
|
-
const
|
|
37214
|
-
if (!
|
|
37215
|
-
let id = slugFromPath(
|
|
37483
|
+
const path44 = raw.trim();
|
|
37484
|
+
if (!path44) continue;
|
|
37485
|
+
let id = slugFromPath(path44);
|
|
37216
37486
|
let n = 2;
|
|
37217
|
-
while (byId.has(id) && byId.get(id).path !==
|
|
37218
|
-
id = `${slugFromPath(
|
|
37487
|
+
while (byId.has(id) && byId.get(id).path !== path44) {
|
|
37488
|
+
id = `${slugFromPath(path44)}-${n++}`;
|
|
37219
37489
|
}
|
|
37220
37490
|
byId.set(id, {
|
|
37221
37491
|
id,
|
|
37222
|
-
name: slugFromPath(
|
|
37223
|
-
path:
|
|
37492
|
+
name: slugFromPath(path44),
|
|
37493
|
+
path: path44
|
|
37224
37494
|
});
|
|
37225
37495
|
}
|
|
37226
37496
|
return [...byId.values()];
|
|
@@ -37597,9 +37867,9 @@ async function runCompanionServe(opts = {}) {
|
|
|
37597
37867
|
return;
|
|
37598
37868
|
}
|
|
37599
37869
|
const url2 = parseUrl(req);
|
|
37600
|
-
const
|
|
37870
|
+
const path44 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
37601
37871
|
try {
|
|
37602
|
-
if (req.method === "GET" && (
|
|
37872
|
+
if (req.method === "GET" && (path44 === "/health" || path44 === "/v1/health")) {
|
|
37603
37873
|
sendJson(res, 200, {
|
|
37604
37874
|
ok: true,
|
|
37605
37875
|
service: "zelari-companion",
|
|
@@ -37611,18 +37881,18 @@ async function runCompanionServe(opts = {}) {
|
|
|
37611
37881
|
});
|
|
37612
37882
|
return;
|
|
37613
37883
|
}
|
|
37614
|
-
if (
|
|
37884
|
+
if (path44.startsWith("/v1")) {
|
|
37615
37885
|
if (!tokenMatches(token, getBearer(req))) {
|
|
37616
37886
|
sendJson(res, 401, { ok: false, error: "unauthorized" });
|
|
37617
37887
|
return;
|
|
37618
37888
|
}
|
|
37619
37889
|
}
|
|
37620
|
-
if (req.method === "GET" &&
|
|
37890
|
+
if (req.method === "GET" && path44 === "/v1/config") {
|
|
37621
37891
|
const snap = buildDesktopConfigSnapshot();
|
|
37622
37892
|
sendJson(res, 200, { ok: true, ...snap });
|
|
37623
37893
|
return;
|
|
37624
37894
|
}
|
|
37625
|
-
if (req.method === "GET" &&
|
|
37895
|
+
if (req.method === "GET" && path44 === "/v1/projects") {
|
|
37626
37896
|
sendJson(res, 200, {
|
|
37627
37897
|
ok: true,
|
|
37628
37898
|
projects: projects.map((p3) => ({
|
|
@@ -37633,7 +37903,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37633
37903
|
});
|
|
37634
37904
|
return;
|
|
37635
37905
|
}
|
|
37636
|
-
if (req.method === "GET" &&
|
|
37906
|
+
if (req.method === "GET" && path44 === "/v1/runs") {
|
|
37637
37907
|
sendJson(res, 200, {
|
|
37638
37908
|
ok: true,
|
|
37639
37909
|
active: runs.getActive(),
|
|
@@ -37651,7 +37921,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37651
37921
|
});
|
|
37652
37922
|
return;
|
|
37653
37923
|
}
|
|
37654
|
-
if (req.method === "POST" &&
|
|
37924
|
+
if (req.method === "POST" && path44 === "/v1/runs") {
|
|
37655
37925
|
const raw = await readBody(req);
|
|
37656
37926
|
let body = {};
|
|
37657
37927
|
try {
|
|
@@ -37698,7 +37968,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37698
37968
|
});
|
|
37699
37969
|
return;
|
|
37700
37970
|
}
|
|
37701
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
37971
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path44);
|
|
37702
37972
|
if (req.method === "GET" && eventsMatch) {
|
|
37703
37973
|
const runId = eventsMatch[1];
|
|
37704
37974
|
const run = runs.getRun(runId);
|
|
@@ -37763,7 +38033,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
37763
38033
|
}, 500);
|
|
37764
38034
|
return;
|
|
37765
38035
|
}
|
|
37766
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
38036
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path44);
|
|
37767
38037
|
if (req.method === "POST" && cancelMatch) {
|
|
37768
38038
|
const runId = cancelMatch[1];
|
|
37769
38039
|
const result = runs.cancel(runId);
|
|
@@ -37875,11 +38145,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
37875
38145
|
import { existsSync as existsSync45, readFileSync as readFileSync35, readlinkSync, statSync as statSync7 } from "node:fs";
|
|
37876
38146
|
import { createRequire as createRequire3 } from "node:module";
|
|
37877
38147
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
37878
|
-
import
|
|
38148
|
+
import path43 from "node:path";
|
|
37879
38149
|
function findPackageRoot(start) {
|
|
37880
38150
|
let dir = start;
|
|
37881
38151
|
for (let i = 0; i < 6; i += 1) {
|
|
37882
|
-
const candidate =
|
|
38152
|
+
const candidate = path43.join(dir, "package.json");
|
|
37883
38153
|
if (existsSync45(candidate)) {
|
|
37884
38154
|
try {
|
|
37885
38155
|
const pkg = JSON.parse(readFileSync35(candidate, "utf8"));
|
|
@@ -37887,11 +38157,11 @@ function findPackageRoot(start) {
|
|
|
37887
38157
|
} catch {
|
|
37888
38158
|
}
|
|
37889
38159
|
}
|
|
37890
|
-
const parent =
|
|
38160
|
+
const parent = path43.dirname(dir);
|
|
37891
38161
|
if (parent === dir) break;
|
|
37892
38162
|
dir = parent;
|
|
37893
38163
|
}
|
|
37894
|
-
return
|
|
38164
|
+
return path43.resolve(__dirname3, "..", "..", "..");
|
|
37895
38165
|
}
|
|
37896
38166
|
function tryExec(cmd) {
|
|
37897
38167
|
try {
|
|
@@ -37905,7 +38175,7 @@ function tryExec(cmd) {
|
|
|
37905
38175
|
}
|
|
37906
38176
|
function readPackageJson3() {
|
|
37907
38177
|
try {
|
|
37908
|
-
const pkgPath =
|
|
38178
|
+
const pkgPath = path43.join(packageRoot, "package.json");
|
|
37909
38179
|
return JSON.parse(readFileSync35(pkgPath, "utf8"));
|
|
37910
38180
|
} catch {
|
|
37911
38181
|
return null;
|
|
@@ -37921,7 +38191,7 @@ function checkShim(pkgName) {
|
|
|
37921
38191
|
}
|
|
37922
38192
|
const isWin = process.platform === "win32";
|
|
37923
38193
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
37924
|
-
const shimPath =
|
|
38194
|
+
const shimPath = path43.join(prefix, shimName);
|
|
37925
38195
|
if (!existsSync45(shimPath)) {
|
|
37926
38196
|
return FAIL(
|
|
37927
38197
|
`shim not found at ${shimPath}
|
|
@@ -37949,8 +38219,8 @@ function checkShim(pkgName) {
|
|
|
37949
38219
|
fix: npm install -g ${pkgName}@latest --force`
|
|
37950
38220
|
);
|
|
37951
38221
|
}
|
|
37952
|
-
const resolved =
|
|
37953
|
-
const expected =
|
|
38222
|
+
const resolved = path43.resolve(path43.dirname(shimPath), target);
|
|
38223
|
+
const expected = path43.join(
|
|
37954
38224
|
prefix,
|
|
37955
38225
|
"node_modules",
|
|
37956
38226
|
pkgName,
|
|
@@ -37989,7 +38259,7 @@ function checkNode(pkg) {
|
|
|
37989
38259
|
return OK(`node ${raw}`);
|
|
37990
38260
|
}
|
|
37991
38261
|
function checkBundle() {
|
|
37992
|
-
const bundle =
|
|
38262
|
+
const bundle = path43.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
37993
38263
|
if (!existsSync45(bundle)) {
|
|
37994
38264
|
return FAIL(
|
|
37995
38265
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -38010,7 +38280,7 @@ function checkRuntimeDeps() {
|
|
|
38010
38280
|
const missing = [];
|
|
38011
38281
|
for (const dep of required2) {
|
|
38012
38282
|
try {
|
|
38013
|
-
const localReq = createRequire3(
|
|
38283
|
+
const localReq = createRequire3(path43.join(packageRoot, "package.json"));
|
|
38014
38284
|
localReq.resolve(dep);
|
|
38015
38285
|
} catch {
|
|
38016
38286
|
missing.push(dep);
|
|
@@ -38186,7 +38456,7 @@ var init_doctor = __esm({
|
|
|
38186
38456
|
"use strict";
|
|
38187
38457
|
init_prereqChecks();
|
|
38188
38458
|
require3 = createRequire3(import.meta.url);
|
|
38189
|
-
__dirname3 =
|
|
38459
|
+
__dirname3 = path43.dirname(fileURLToPath2(import.meta.url));
|
|
38190
38460
|
packageRoot = findPackageRoot(__dirname3);
|
|
38191
38461
|
OK = (message) => ({
|
|
38192
38462
|
ok: true,
|
|
@@ -44601,6 +44871,7 @@ init_krakenRadio();
|
|
|
44601
44871
|
init_auditLogger();
|
|
44602
44872
|
init_toolRegistry();
|
|
44603
44873
|
init_planner();
|
|
44874
|
+
init_graphMemory();
|
|
44604
44875
|
init_executor();
|
|
44605
44876
|
init_graphStatus();
|
|
44606
44877
|
async function handleKrakenGraph(ctx, prompt) {
|
|
@@ -44625,12 +44896,22 @@ async function handleKrakenGraph(ctx, prompt) {
|
|
|
44625
44896
|
})
|
|
44626
44897
|
};
|
|
44627
44898
|
try {
|
|
44628
|
-
const
|
|
44899
|
+
const previous = await loadGraphSnapshot(ctx.cwd);
|
|
44900
|
+
const previousAttempt = formatSnapshotForPlanner(previous);
|
|
44901
|
+
if (previousAttempt) {
|
|
44902
|
+
appendSystem(ctx.setMessages, "[kraken] resuming from the previous unfinished graph");
|
|
44903
|
+
}
|
|
44904
|
+
const graph = await planTaskGraph({
|
|
44905
|
+
prompt,
|
|
44906
|
+
graphId: `kraken-${Date.now().toString(36)}`,
|
|
44907
|
+
...previousAttempt ? { previousAttempt } : {}
|
|
44908
|
+
});
|
|
44629
44909
|
appendSystem(ctx.setMessages, formatKrakenGraphAscii(graph));
|
|
44630
44910
|
const executor = new KrakenGraphExecutor({
|
|
44631
44911
|
taskToolDeps,
|
|
44632
44912
|
parentCwd: ctx.cwd,
|
|
44633
|
-
sessionId: ctx.sessionId
|
|
44913
|
+
sessionId: ctx.sessionId,
|
|
44914
|
+
goal: prompt
|
|
44634
44915
|
});
|
|
44635
44916
|
const summary = await executor.execute(graph);
|
|
44636
44917
|
appendSystem(
|
|
@@ -44944,17 +45225,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
44944
45225
|
}
|
|
44945
45226
|
|
|
44946
45227
|
// src/cli/slashHandlers/promoteMember.ts
|
|
44947
|
-
import { promises as
|
|
44948
|
-
import
|
|
45228
|
+
import { promises as fs20 } from "node:fs";
|
|
45229
|
+
import path38 from "node:path";
|
|
44949
45230
|
import os10 from "node:os";
|
|
44950
45231
|
async function handlePromoteMember(ctx, memberId) {
|
|
44951
45232
|
try {
|
|
44952
45233
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
44953
45234
|
const { skill, markdown } = promoteMember2(memberId);
|
|
44954
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
44955
|
-
await
|
|
44956
|
-
const filePath =
|
|
44957
|
-
await
|
|
45235
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path38.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
45236
|
+
await fs20.mkdir(skillDir, { recursive: true });
|
|
45237
|
+
const filePath = path38.join(skillDir, `${skill.id}.md`);
|
|
45238
|
+
await fs20.writeFile(filePath, markdown, "utf8");
|
|
44958
45239
|
appendSystem(
|
|
44959
45240
|
ctx.setMessages,
|
|
44960
45241
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -44970,25 +45251,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
44970
45251
|
}
|
|
44971
45252
|
|
|
44972
45253
|
// src/cli/branchManager.ts
|
|
44973
|
-
import { promises as
|
|
44974
|
-
import
|
|
45254
|
+
import { promises as fs21, existsSync as existsSync38, readFileSync as readFileSync30, writeFileSync as writeFileSync19, mkdirSync as mkdirSync16, statSync as statSync4, rmSync as rmSync3 } from "node:fs";
|
|
45255
|
+
import path39 from "node:path";
|
|
44975
45256
|
import os11 from "node:os";
|
|
44976
45257
|
var META_FILENAME = "meta.json";
|
|
44977
45258
|
var SESSIONS_SUBDIR = "sessions";
|
|
44978
45259
|
function getBranchesBaseDir() {
|
|
44979
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
45260
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path39.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
44980
45261
|
}
|
|
44981
45262
|
function getSessionsBaseDir() {
|
|
44982
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
45263
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path39.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
44983
45264
|
}
|
|
44984
45265
|
function branchPathFor(name, baseDir) {
|
|
44985
|
-
return
|
|
45266
|
+
return path39.join(baseDir, name);
|
|
44986
45267
|
}
|
|
44987
45268
|
function metaPathFor(name, baseDir) {
|
|
44988
|
-
return
|
|
45269
|
+
return path39.join(baseDir, name, META_FILENAME);
|
|
44989
45270
|
}
|
|
44990
45271
|
function sessionsPathFor(name, baseDir) {
|
|
44991
|
-
return
|
|
45272
|
+
return path39.join(baseDir, name, SESSIONS_SUBDIR);
|
|
44992
45273
|
}
|
|
44993
45274
|
function readBranchMeta(name, baseDir) {
|
|
44994
45275
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -45013,13 +45294,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
45013
45294
|
}
|
|
45014
45295
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
45015
45296
|
const metaPath = metaPathFor(name, baseDir);
|
|
45016
|
-
mkdirSync16(
|
|
45297
|
+
mkdirSync16(path39.dirname(metaPath), { recursive: true });
|
|
45017
45298
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
45018
45299
|
}
|
|
45019
45300
|
async function countSessions(name, baseDir) {
|
|
45020
45301
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
45021
45302
|
try {
|
|
45022
|
-
const entries = await
|
|
45303
|
+
const entries = await fs21.readdir(sessionsPath);
|
|
45023
45304
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
45024
45305
|
} catch (err) {
|
|
45025
45306
|
if (err.code === "ENOENT") return 0;
|
|
@@ -45064,15 +45345,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
45064
45345
|
if (branchExists(name, baseDir)) {
|
|
45065
45346
|
throw new BranchAlreadyExistsError(name);
|
|
45066
45347
|
}
|
|
45067
|
-
const sourcePath =
|
|
45348
|
+
const sourcePath = path39.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
45068
45349
|
if (!existsSync38(sourcePath)) {
|
|
45069
45350
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
45070
45351
|
}
|
|
45071
45352
|
const branchPath = branchPathFor(name, baseDir);
|
|
45072
45353
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
45073
45354
|
mkdirSync16(branchSessionsPath, { recursive: true });
|
|
45074
|
-
const destPath =
|
|
45075
|
-
await
|
|
45355
|
+
const destPath = path39.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
45356
|
+
await fs21.copyFile(sourcePath, destPath);
|
|
45076
45357
|
const meta3 = {
|
|
45077
45358
|
name,
|
|
45078
45359
|
createdAt: Date.now(),
|
|
@@ -45090,7 +45371,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
45090
45371
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
45091
45372
|
let entries;
|
|
45092
45373
|
try {
|
|
45093
|
-
entries = await
|
|
45374
|
+
entries = await fs21.readdir(baseDir);
|
|
45094
45375
|
} catch (err) {
|
|
45095
45376
|
if (err.code === "ENOENT") return [];
|
|
45096
45377
|
throw err;
|
|
@@ -45171,26 +45452,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
45171
45452
|
}
|
|
45172
45453
|
|
|
45173
45454
|
// src/cli/slashHandlers/workspace.ts
|
|
45174
|
-
import { promises as
|
|
45175
|
-
import
|
|
45455
|
+
import { promises as fs22 } from "node:fs";
|
|
45456
|
+
import path40 from "node:path";
|
|
45176
45457
|
async function handleWorkspaceShow(ctx, what) {
|
|
45177
45458
|
try {
|
|
45178
|
-
const zelari =
|
|
45459
|
+
const zelari = path40.join(process.cwd(), ".zelari");
|
|
45179
45460
|
let content;
|
|
45180
45461
|
switch (what) {
|
|
45181
45462
|
case "plan": {
|
|
45182
|
-
const planPath =
|
|
45463
|
+
const planPath = path40.join(zelari, "plan.md");
|
|
45183
45464
|
try {
|
|
45184
|
-
content = await
|
|
45465
|
+
content = await fs22.readFile(planPath, "utf-8");
|
|
45185
45466
|
} catch {
|
|
45186
45467
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
45187
45468
|
}
|
|
45188
45469
|
break;
|
|
45189
45470
|
}
|
|
45190
45471
|
case "decisions": {
|
|
45191
|
-
const decisionsDir =
|
|
45472
|
+
const decisionsDir = path40.join(zelari, "decisions");
|
|
45192
45473
|
try {
|
|
45193
|
-
const files = (await
|
|
45474
|
+
const files = (await fs22.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
45194
45475
|
if (files.length === 0) {
|
|
45195
45476
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
45196
45477
|
} else {
|
|
@@ -45198,7 +45479,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
45198
45479
|
`];
|
|
45199
45480
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
45200
45481
|
for (const f of files) {
|
|
45201
|
-
const raw = await
|
|
45482
|
+
const raw = await fs22.readFile(path40.join(decisionsDir, f), "utf-8");
|
|
45202
45483
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
45203
45484
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
45204
45485
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -45211,27 +45492,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
45211
45492
|
break;
|
|
45212
45493
|
}
|
|
45213
45494
|
case "risks": {
|
|
45214
|
-
const risksPath =
|
|
45495
|
+
const risksPath = path40.join(zelari, "risks.md");
|
|
45215
45496
|
try {
|
|
45216
|
-
content = await
|
|
45497
|
+
content = await fs22.readFile(risksPath, "utf-8");
|
|
45217
45498
|
} catch {
|
|
45218
45499
|
content = "(no risks.md yet)";
|
|
45219
45500
|
}
|
|
45220
45501
|
break;
|
|
45221
45502
|
}
|
|
45222
45503
|
case "agents": {
|
|
45223
|
-
const agentsPath =
|
|
45504
|
+
const agentsPath = path40.join(process.cwd(), "AGENTS.MD");
|
|
45224
45505
|
try {
|
|
45225
|
-
content = await
|
|
45506
|
+
content = await fs22.readFile(agentsPath, "utf-8");
|
|
45226
45507
|
} catch {
|
|
45227
45508
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
45228
45509
|
}
|
|
45229
45510
|
break;
|
|
45230
45511
|
}
|
|
45231
45512
|
case "docs": {
|
|
45232
|
-
const docsDir =
|
|
45513
|
+
const docsDir = path40.join(zelari, "docs");
|
|
45233
45514
|
try {
|
|
45234
|
-
const files = (await
|
|
45515
|
+
const files = (await fs22.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
45235
45516
|
content = files.length ? `# Docs (${files.length})
|
|
45236
45517
|
|
|
45237
45518
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -45271,8 +45552,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
45271
45552
|
return;
|
|
45272
45553
|
}
|
|
45273
45554
|
try {
|
|
45274
|
-
const target =
|
|
45275
|
-
await
|
|
45555
|
+
const target = path40.join(process.cwd(), ".zelari");
|
|
45556
|
+
await fs22.rm(target, { recursive: true, force: true });
|
|
45276
45557
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
45277
45558
|
} catch (err) {
|
|
45278
45559
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -45631,16 +45912,16 @@ function handleModelsRefresh(ctx) {
|
|
|
45631
45912
|
}
|
|
45632
45913
|
|
|
45633
45914
|
// src/cli/slashHandlers/skills.ts
|
|
45634
|
-
import
|
|
45915
|
+
import path41 from "node:path";
|
|
45635
45916
|
import os12 from "node:os";
|
|
45636
45917
|
|
|
45637
45918
|
// src/cli/skillHistory.ts
|
|
45638
|
-
import { promises as
|
|
45919
|
+
import { promises as fs23, existsSync as existsSync39, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync4, mkdirSync as mkdirSync17 } from "node:fs";
|
|
45639
45920
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
45640
45921
|
async function readSkillHistory(file2) {
|
|
45641
45922
|
let raw = "";
|
|
45642
45923
|
try {
|
|
45643
|
-
raw = await
|
|
45924
|
+
raw = await fs23.readFile(file2, "utf-8");
|
|
45644
45925
|
} catch {
|
|
45645
45926
|
return [];
|
|
45646
45927
|
}
|
|
@@ -45757,7 +46038,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
|
45757
46038
|
});
|
|
45758
46039
|
}
|
|
45759
46040
|
async function handleSkillStats(ctx, skillId) {
|
|
45760
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
46041
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path41.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
45761
46042
|
try {
|
|
45762
46043
|
const records = await readSkillHistory(historyFile);
|
|
45763
46044
|
const stats = getSkillStats(records, skillId);
|
|
@@ -45773,7 +46054,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
45773
46054
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
45774
46055
|
return;
|
|
45775
46056
|
}
|
|
45776
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
46057
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path41.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
45777
46058
|
try {
|
|
45778
46059
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
45779
46060
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -47498,6 +47779,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47498
47779
|
return 1;
|
|
47499
47780
|
}
|
|
47500
47781
|
const { planTaskGraph: planTaskGraph2 } = await Promise.resolve().then(() => (init_planner(), planner_exports));
|
|
47782
|
+
const { loadGraphSnapshot: loadGraphSnapshot2, formatSnapshotForPlanner: formatSnapshotForPlanner2 } = await Promise.resolve().then(() => (init_graphMemory(), graphMemory_exports));
|
|
47501
47783
|
const { formatKrakenGraphAscii: formatKrakenGraphAscii2 } = await Promise.resolve().then(() => (init_graphStatus(), graphStatus_exports));
|
|
47502
47784
|
const { AuditLogger: AuditLogger2 } = await Promise.resolve().then(() => (init_auditLogger(), auditLogger_exports));
|
|
47503
47785
|
const { createKrakenSubAgentContextFactory: createKrakenSubAgentContextFactory2 } = await Promise.resolve().then(() => (init_toolRegistry(), toolRegistry_exports));
|
|
@@ -47513,7 +47795,15 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47513
47795
|
};
|
|
47514
47796
|
try {
|
|
47515
47797
|
log(`planning kraken graph: ${prompt}`);
|
|
47516
|
-
const
|
|
47798
|
+
const previous = await loadGraphSnapshot2(cwd);
|
|
47799
|
+
const previousAttempt = formatSnapshotForPlanner2(previous);
|
|
47800
|
+
if (previousAttempt) log("resuming from the previous unfinished graph");
|
|
47801
|
+
const graph = await planTaskGraph2({
|
|
47802
|
+
prompt,
|
|
47803
|
+
provider,
|
|
47804
|
+
model,
|
|
47805
|
+
...previousAttempt ? { previousAttempt } : {}
|
|
47806
|
+
});
|
|
47517
47807
|
log(formatKrakenGraphAscii2(graph));
|
|
47518
47808
|
const audit = new AuditLogger2();
|
|
47519
47809
|
const executor = new KrakenGraphExecutor2({
|
|
@@ -47532,7 +47822,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
47532
47822
|
})
|
|
47533
47823
|
},
|
|
47534
47824
|
parentCwd: cwd,
|
|
47535
|
-
sessionId
|
|
47825
|
+
sessionId,
|
|
47826
|
+
goal: prompt
|
|
47536
47827
|
});
|
|
47537
47828
|
const summary = await executor.execute(graph);
|
|
47538
47829
|
const finalAscii = formatKrakenGraphAscii2(summary.graph);
|
|
@@ -48677,7 +48968,7 @@ function upsertSkill(opts) {
|
|
|
48677
48968
|
}
|
|
48678
48969
|
dir = getProjectSkillsDir(root);
|
|
48679
48970
|
}
|
|
48680
|
-
const
|
|
48971
|
+
const path44 = skillFilePath(dir, name);
|
|
48681
48972
|
const content = serializeSkillMd({
|
|
48682
48973
|
name,
|
|
48683
48974
|
description,
|
|
@@ -48686,13 +48977,13 @@ function upsertSkill(opts) {
|
|
|
48686
48977
|
tools: opts.tools,
|
|
48687
48978
|
cost: opts.cost
|
|
48688
48979
|
});
|
|
48689
|
-
const parsed = parseSkillMd(content,
|
|
48980
|
+
const parsed = parseSkillMd(content, path44);
|
|
48690
48981
|
if (!parsed) {
|
|
48691
48982
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
48692
48983
|
}
|
|
48693
|
-
mkdirSync18(dirname9(
|
|
48694
|
-
writeFileSync20(
|
|
48695
|
-
return { ok: true, path:
|
|
48984
|
+
mkdirSync18(dirname9(path44), { recursive: true });
|
|
48985
|
+
writeFileSync20(path44, content, "utf8");
|
|
48986
|
+
return { ok: true, path: path44 };
|
|
48696
48987
|
}
|
|
48697
48988
|
function removeSkill(opts) {
|
|
48698
48989
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -48710,8 +49001,8 @@ function removeSkill(opts) {
|
|
|
48710
49001
|
dir = getProjectSkillsDir(root);
|
|
48711
49002
|
}
|
|
48712
49003
|
const skillDir = join34(dir, name);
|
|
48713
|
-
const
|
|
48714
|
-
if (!existsSync42(
|
|
49004
|
+
const path44 = skillFilePath(dir, name);
|
|
49005
|
+
if (!existsSync42(path44) && !existsSync42(skillDir)) {
|
|
48715
49006
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
48716
49007
|
}
|
|
48717
49008
|
try {
|
|
@@ -48722,7 +49013,7 @@ function removeSkill(opts) {
|
|
|
48722
49013
|
error: err instanceof Error ? err.message : String(err)
|
|
48723
49014
|
};
|
|
48724
49015
|
}
|
|
48725
|
-
return { ok: true, path:
|
|
49016
|
+
return { ok: true, path: path44 };
|
|
48726
49017
|
}
|
|
48727
49018
|
|
|
48728
49019
|
// src/cli/generateSkillFromUrl.ts
|
|
@@ -48812,8 +49103,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
48812
49103
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
48813
49104
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
48814
49105
|
try {
|
|
48815
|
-
const
|
|
48816
|
-
name =
|
|
49106
|
+
const path44 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
49107
|
+
name = path44 && /^[a-z0-9]/.test(path44) ? path44 : "imported-skill";
|
|
48817
49108
|
} catch {
|
|
48818
49109
|
name = "imported-skill";
|
|
48819
49110
|
}
|