zelari-code 2.24.0 → 2.25.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/ast/tools.js +1 -1
- package/dist/cli/ast/tools.js.map +1 -1
- package/dist/cli/budget/budgetRuntime.js +4 -1
- package/dist/cli/budget/budgetRuntime.js.map +1 -1
- package/dist/cli/gauntlet/policy.js +2 -0
- package/dist/cli/gauntlet/policy.js.map +1 -1
- package/dist/cli/headless/runOneTurn.js +2 -2
- package/dist/cli/headless/runOneTurn.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +1 -1
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +4 -4
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/kraken/delegationPolicy.js +1 -1
- package/dist/cli/kraken/turnRuntime.js +3 -0
- package/dist/cli/kraken/turnRuntime.js.map +1 -1
- package/dist/cli/lsp/tools.js +1 -1
- package/dist/cli/lsp/tools.js.map +1 -1
- package/dist/cli/main.bundled.js +844 -495
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/missionSlice.js +4 -4
- package/dist/cli/missionSlice.js.map +1 -1
- package/dist/cli/phase.js +4 -1
- package/dist/cli/phase.js.map +1 -1
- package/dist/cli/runHeadless.js +1 -1
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/safety/astGate.js +190 -0
- package/dist/cli/safety/astGate.js.map +1 -0
- package/dist/cli/safety/resourceClaims.js +3 -1
- package/dist/cli/safety/resourceClaims.js.map +1 -1
- package/dist/cli/sessionSpine.js +45 -0
- package/dist/cli/sessionSpine.js.map +1 -1
- package/dist/cli/spineFileEvents.js +124 -0
- package/dist/cli/spineFileEvents.js.map +1 -0
- package/dist/cli/toolRegistry.js +24 -14
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/zelariMission.js +1 -1
- package/dist/cli/zelariMission.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
|
|
|
3288
3288
|
function cloneDef(schema) {
|
|
3289
3289
|
return mergeDefs(schema._zod.def);
|
|
3290
3290
|
}
|
|
3291
|
-
function getElementAtPath(obj,
|
|
3292
|
-
if (!
|
|
3291
|
+
function getElementAtPath(obj, path94) {
|
|
3292
|
+
if (!path94)
|
|
3293
3293
|
return obj;
|
|
3294
|
-
return
|
|
3294
|
+
return path94.reduce((acc, key) => acc?.[key], obj);
|
|
3295
3295
|
}
|
|
3296
3296
|
function promiseAllObject(promisesObj) {
|
|
3297
3297
|
const keys = Object.keys(promisesObj);
|
|
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
3619
3619
|
}
|
|
3620
3620
|
return false;
|
|
3621
3621
|
}
|
|
3622
|
-
function prefixIssues(
|
|
3622
|
+
function prefixIssues(path94, issues) {
|
|
3623
3623
|
return issues.map((iss) => {
|
|
3624
3624
|
var _a3;
|
|
3625
3625
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
3626
|
-
iss.path.unshift(
|
|
3626
|
+
iss.path.unshift(path94);
|
|
3627
3627
|
return iss;
|
|
3628
3628
|
});
|
|
3629
3629
|
}
|
|
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3841
3841
|
}
|
|
3842
3842
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
3843
3843
|
const fieldErrors = { _errors: [] };
|
|
3844
|
-
const processError = (error52,
|
|
3844
|
+
const processError = (error52, path94 = []) => {
|
|
3845
3845
|
for (const issue2 of error52.issues) {
|
|
3846
3846
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3847
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
3847
|
+
issue2.errors.map((issues) => processError({ issues }, [...path94, ...issue2.path]));
|
|
3848
3848
|
} else if (issue2.code === "invalid_key") {
|
|
3849
|
-
processError({ issues: issue2.issues }, [...
|
|
3849
|
+
processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
|
|
3850
3850
|
} else if (issue2.code === "invalid_element") {
|
|
3851
|
-
processError({ issues: issue2.issues }, [...
|
|
3851
|
+
processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
|
|
3852
3852
|
} else {
|
|
3853
|
-
const fullpath = [...
|
|
3853
|
+
const fullpath = [...path94, ...issue2.path];
|
|
3854
3854
|
if (fullpath.length === 0) {
|
|
3855
3855
|
fieldErrors._errors.push(mapper(issue2));
|
|
3856
3856
|
} else {
|
|
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3877
3877
|
}
|
|
3878
3878
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
3879
3879
|
const result = { errors: [] };
|
|
3880
|
-
const processError = (error52,
|
|
3880
|
+
const processError = (error52, path94 = []) => {
|
|
3881
3881
|
var _a3, _b;
|
|
3882
3882
|
for (const issue2 of error52.issues) {
|
|
3883
3883
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3884
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
3884
|
+
issue2.errors.map((issues) => processError({ issues }, [...path94, ...issue2.path]));
|
|
3885
3885
|
} else if (issue2.code === "invalid_key") {
|
|
3886
|
-
processError({ issues: issue2.issues }, [...
|
|
3886
|
+
processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
|
|
3887
3887
|
} else if (issue2.code === "invalid_element") {
|
|
3888
|
-
processError({ issues: issue2.issues }, [...
|
|
3888
|
+
processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
|
|
3889
3889
|
} else {
|
|
3890
|
-
const fullpath = [...
|
|
3890
|
+
const fullpath = [...path94, ...issue2.path];
|
|
3891
3891
|
if (fullpath.length === 0) {
|
|
3892
3892
|
result.errors.push(mapper(issue2));
|
|
3893
3893
|
continue;
|
|
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3919
3919
|
}
|
|
3920
3920
|
function toDotPath(_path) {
|
|
3921
3921
|
const segs = [];
|
|
3922
|
-
const
|
|
3923
|
-
for (const seg of
|
|
3922
|
+
const path94 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
3923
|
+
for (const seg of path94) {
|
|
3924
3924
|
if (typeof seg === "number")
|
|
3925
3925
|
segs.push(`[${seg}]`);
|
|
3926
3926
|
else if (typeof seg === "symbol")
|
|
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
|
|
|
17423
17423
|
if (!ref.startsWith("#")) {
|
|
17424
17424
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17425
17425
|
}
|
|
17426
|
-
const
|
|
17427
|
-
if (
|
|
17426
|
+
const path94 = ref.slice(1).split("/").filter(Boolean);
|
|
17427
|
+
if (path94.length === 0) {
|
|
17428
17428
|
return ctx.rootSchema;
|
|
17429
17429
|
}
|
|
17430
17430
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17431
|
-
if (
|
|
17432
|
-
const key =
|
|
17431
|
+
if (path94[0] === defsKey) {
|
|
17432
|
+
const key = path94[1];
|
|
17433
17433
|
if (!key || !ctx.defs[key]) {
|
|
17434
17434
|
throw new Error(`Reference not found: ${ref}`);
|
|
17435
17435
|
}
|
|
@@ -18260,6 +18260,37 @@ var init_newlines = __esm({
|
|
|
18260
18260
|
}
|
|
18261
18261
|
});
|
|
18262
18262
|
|
|
18263
|
+
// packages/core/dist/core/tools/builtin/fileEvents.js
|
|
18264
|
+
function fileReadEvent(path94, snapshotId) {
|
|
18265
|
+
return { kind: "file.read", actor: { type: "tool" }, data: { path: path94, snapshotId } };
|
|
18266
|
+
}
|
|
18267
|
+
function fileAppliedEvent(path94, snapshotId, bytes) {
|
|
18268
|
+
return { kind: "file.applied", actor: { type: "tool" }, data: { path: path94, snapshotId, bytes } };
|
|
18269
|
+
}
|
|
18270
|
+
function fileRejectedEvent(path94, reason, hint) {
|
|
18271
|
+
return {
|
|
18272
|
+
kind: "file.rejected",
|
|
18273
|
+
actor: { type: "tool" },
|
|
18274
|
+
data: hint === void 0 ? { path: path94, reason } : { path: path94, reason, hint }
|
|
18275
|
+
};
|
|
18276
|
+
}
|
|
18277
|
+
function reReadHint(reject) {
|
|
18278
|
+
return `re-read ${reject.next.path}, then retry with the fresh snapshotId`;
|
|
18279
|
+
}
|
|
18280
|
+
async function emitFileEvent(emit, input) {
|
|
18281
|
+
if (!emit)
|
|
18282
|
+
return;
|
|
18283
|
+
try {
|
|
18284
|
+
await emit(input);
|
|
18285
|
+
} catch {
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
var init_fileEvents = __esm({
|
|
18289
|
+
"packages/core/dist/core/tools/builtin/fileEvents.js"() {
|
|
18290
|
+
"use strict";
|
|
18291
|
+
}
|
|
18292
|
+
});
|
|
18293
|
+
|
|
18263
18294
|
// packages/core/dist/core/tools/builtin/filesystem.js
|
|
18264
18295
|
import { createHash as createHash2 } from "node:crypto";
|
|
18265
18296
|
import { promises as fs4 } from "node:fs";
|
|
@@ -18267,6 +18298,19 @@ import path6 from "node:path";
|
|
|
18267
18298
|
function snapshotIdOf(content) {
|
|
18268
18299
|
return createHash2("sha256").update(content, "utf8").digest("hex").slice(0, 16);
|
|
18269
18300
|
}
|
|
18301
|
+
function fileExistsMinimalDiff(existing, incoming, pathLabel) {
|
|
18302
|
+
const EXISTING_HEAD_LINES = 10;
|
|
18303
|
+
const MAX_BODY_LINES = 40;
|
|
18304
|
+
const oldHead = splitLinesLF(existing).slice(0, EXISTING_HEAD_LINES);
|
|
18305
|
+
const newHead = splitLinesLF(incoming).slice(0, MAX_BODY_LINES - oldHead.length);
|
|
18306
|
+
return [
|
|
18307
|
+
`--- ${pathLabel}`,
|
|
18308
|
+
`+++ ${pathLabel}`,
|
|
18309
|
+
`@@ -1,${oldHead.length} +1,${newHead.length} @@`,
|
|
18310
|
+
...oldHead.map((l) => `-${l}`),
|
|
18311
|
+
...newHead.map((l) => `+${l}`)
|
|
18312
|
+
].join("\n");
|
|
18313
|
+
}
|
|
18270
18314
|
function replaceFileString(text, oldString, newString, replaceAll) {
|
|
18271
18315
|
const exact = replaceOnceOrAll(text, oldString, newString, replaceAll);
|
|
18272
18316
|
if (exact.occurrences > 0)
|
|
@@ -18299,6 +18343,7 @@ var init_filesystem = __esm({
|
|
|
18299
18343
|
init_zod();
|
|
18300
18344
|
init_toolTypes();
|
|
18301
18345
|
init_newlines();
|
|
18346
|
+
init_fileEvents();
|
|
18302
18347
|
ReadFileArgsSchema = external_exports.object({
|
|
18303
18348
|
path: external_exports.string().min(1),
|
|
18304
18349
|
startLine: external_exports.number().int().nonnegative().optional().describe("0-based first line to include. Range is applied to the full file before maxBytes."),
|
|
@@ -18318,6 +18363,7 @@ var init_filesystem = __esm({
|
|
|
18318
18363
|
const buf = await fs4.readFile(absPath, { encoding: "utf-8", signal: ctx.signal });
|
|
18319
18364
|
const content = typeof buf === "string" ? buf : buf.toString("utf-8");
|
|
18320
18365
|
const snapshotId = snapshotIdOf(content);
|
|
18366
|
+
await emitFileEvent(ctx.emitSessionEvent, fileReadEvent(absPath, snapshotId));
|
|
18321
18367
|
const allLines = content.split("\n");
|
|
18322
18368
|
const totalLines = allLines.length;
|
|
18323
18369
|
const start = args.startLine ?? 0;
|
|
@@ -18365,11 +18411,18 @@ var init_filesystem = __esm({
|
|
|
18365
18411
|
WriteFileArgsSchema = external_exports.object({
|
|
18366
18412
|
path: external_exports.string().min(1),
|
|
18367
18413
|
content: external_exports.string(),
|
|
18368
|
-
createDirs: external_exports.boolean().default(false)
|
|
18414
|
+
createDirs: external_exports.boolean().default(false),
|
|
18415
|
+
/**
|
|
18416
|
+
* ADR-0033 file_exists guard. Absent/false: an existing target is NEVER
|
|
18417
|
+
* clobbered — the tool rejects with a structured `file_exists` WriteReject
|
|
18418
|
+
* and writes nothing. Explicit `true` restores the legacy overwrite,
|
|
18419
|
+
* on purpose and on record.
|
|
18420
|
+
*/
|
|
18421
|
+
overwrite: external_exports.boolean().optional()
|
|
18369
18422
|
});
|
|
18370
18423
|
writeFileTool = {
|
|
18371
18424
|
name: "write_file",
|
|
18372
|
-
description: "Write or create a file. Use createDirs=true to auto-create parent directories.",
|
|
18425
|
+
description: "Write or create a file. Use createDirs=true to auto-create parent directories. If the target already exists and overwrite is not true, rejects with a structured file_exists WriteReject (meta.reject) and writes nothing \u2014 read it first with read_file, then use edit with its snapshotId, or pass overwrite: true to replace it.",
|
|
18373
18426
|
permissions: ["write"],
|
|
18374
18427
|
sideEffect: "local",
|
|
18375
18428
|
timeoutMs: 1e4,
|
|
@@ -18377,6 +18430,29 @@ var init_filesystem = __esm({
|
|
|
18377
18430
|
execute: async (args, ctx) => {
|
|
18378
18431
|
try {
|
|
18379
18432
|
const absPath = path6.isAbsolute(args.path) ? args.path : path6.join(ctx.cwd, args.path);
|
|
18433
|
+
if (!args.overwrite) {
|
|
18434
|
+
let existing = null;
|
|
18435
|
+
try {
|
|
18436
|
+
const buf = await fs4.readFile(absPath, { encoding: "utf-8", signal: ctx.signal });
|
|
18437
|
+
existing = typeof buf === "string" ? buf : buf.toString("utf-8");
|
|
18438
|
+
} catch (err) {
|
|
18439
|
+
if (err?.code !== "ENOENT")
|
|
18440
|
+
throw err;
|
|
18441
|
+
}
|
|
18442
|
+
if (existing !== null) {
|
|
18443
|
+
return typedErr(`write_file: ${args.path} already exists (FILE_EXISTS). Read it with read_file and use edit with its snapshotId, or pass overwrite: true.`, {
|
|
18444
|
+
status: "failed",
|
|
18445
|
+
warnings: ["FILE_EXISTS"],
|
|
18446
|
+
reject: {
|
|
18447
|
+
ok: false,
|
|
18448
|
+
status: "file_exists",
|
|
18449
|
+
path: absPath,
|
|
18450
|
+
minimalDiff: fileExistsMinimalDiff(existing, args.content, absPath),
|
|
18451
|
+
next: { action: "re-read", path: absPath }
|
|
18452
|
+
}
|
|
18453
|
+
});
|
|
18454
|
+
}
|
|
18455
|
+
}
|
|
18380
18456
|
if (args.createDirs) {
|
|
18381
18457
|
await fs4.mkdir(path6.dirname(absPath), { recursive: true });
|
|
18382
18458
|
}
|
|
@@ -18549,6 +18625,7 @@ var init_edit = __esm({
|
|
|
18549
18625
|
init_toolTypes();
|
|
18550
18626
|
init_filesystem();
|
|
18551
18627
|
init_newlines();
|
|
18628
|
+
init_fileEvents();
|
|
18552
18629
|
WriteRejectSchema = external_exports.object({
|
|
18553
18630
|
ok: external_exports.literal(false),
|
|
18554
18631
|
status: external_exports.enum(["stale_snapshot", "hunk_mismatch", "parse_error", "file_exists"]),
|
|
@@ -18600,6 +18677,7 @@ var init_edit = __esm({
|
|
|
18600
18677
|
});
|
|
18601
18678
|
if (!result.ok) {
|
|
18602
18679
|
const detail = result.reject.status === "stale_snapshot" ? `(expected ${result.reject.expectedHash}, actual ${result.reject.actualHash})` : "(oldString not found \u2014 no relocation attempted)";
|
|
18680
|
+
await emitFileEvent(ctx.emitSessionEvent, fileRejectedEvent(absPath, result.reject.status, reReadHint(result.reject)));
|
|
18603
18681
|
return typedErr(`edit: ${result.reject.status}: ${args.path} ${detail}`, {
|
|
18604
18682
|
status: "failed",
|
|
18605
18683
|
warnings: [result.reject.status === "stale_snapshot" ? "STALE_SNAPSHOT" : "HUNK_MISMATCH"],
|
|
@@ -18610,6 +18688,7 @@ var init_edit = __esm({
|
|
|
18610
18688
|
encoding: "utf-8",
|
|
18611
18689
|
signal: ctx.signal
|
|
18612
18690
|
});
|
|
18691
|
+
await emitFileEvent(ctx.emitSessionEvent, fileAppliedEvent(absPath, result.snapshotId, result.newContent.length));
|
|
18613
18692
|
return typedOk({
|
|
18614
18693
|
path: absPath,
|
|
18615
18694
|
applied: true,
|
|
@@ -20061,11 +20140,11 @@ var init_tools = __esm({
|
|
|
20061
20140
|
if (!ctx.addDocument)
|
|
20062
20141
|
return "Knowledge vault tool not available.";
|
|
20063
20142
|
const title = args["title"] || "New Document";
|
|
20064
|
-
const
|
|
20143
|
+
const path94 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
20065
20144
|
const content = args["content"] || "";
|
|
20066
20145
|
const tags = args["tags"] || [];
|
|
20067
20146
|
ctx.addDocument({
|
|
20068
|
-
path:
|
|
20147
|
+
path: path94,
|
|
20069
20148
|
title,
|
|
20070
20149
|
content,
|
|
20071
20150
|
format: "markdown",
|
|
@@ -20074,7 +20153,7 @@ var init_tools = __esm({
|
|
|
20074
20153
|
workspaceId: ctx.workspaceId
|
|
20075
20154
|
});
|
|
20076
20155
|
ctx.addActivity("vault", "created document", title);
|
|
20077
|
-
return `Document "${title}" created at "${
|
|
20156
|
+
return `Document "${title}" created at "${path94}".`;
|
|
20078
20157
|
}
|
|
20079
20158
|
}
|
|
20080
20159
|
];
|
|
@@ -26420,11 +26499,11 @@ var init_synthesisAudit = __esm({
|
|
|
26420
26499
|
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
26421
26500
|
import { join as join4 } from "node:path";
|
|
26422
26501
|
function loadNfrSpec(zelariRoot) {
|
|
26423
|
-
const
|
|
26424
|
-
if (!existsSync7(
|
|
26502
|
+
const path94 = join4(zelariRoot, "nfr-spec.json");
|
|
26503
|
+
if (!existsSync7(path94))
|
|
26425
26504
|
return null;
|
|
26426
26505
|
try {
|
|
26427
|
-
const raw = JSON.parse(readFileSync7(
|
|
26506
|
+
const raw = JSON.parse(readFileSync7(path94, "utf8"));
|
|
26428
26507
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
26429
26508
|
return null;
|
|
26430
26509
|
return raw;
|
|
@@ -28730,9 +28809,9 @@ var init_types5 = __esm({
|
|
|
28730
28809
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
28731
28810
|
import { join as join10 } from "node:path";
|
|
28732
28811
|
function readLessonsDeduped(zelariRoot) {
|
|
28733
|
-
const
|
|
28812
|
+
const path94 = join10(zelariRoot, LESSONS_FILE);
|
|
28734
28813
|
try {
|
|
28735
|
-
const raw = readFileSync12(
|
|
28814
|
+
const raw = readFileSync12(path94, "utf8");
|
|
28736
28815
|
const byId = /* @__PURE__ */ new Map();
|
|
28737
28816
|
for (const line of raw.split(/\r?\n/)) {
|
|
28738
28817
|
if (!line.trim())
|
|
@@ -28833,8 +28912,8 @@ function keywordsFrom(check2, signature) {
|
|
|
28833
28912
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
28834
28913
|
}
|
|
28835
28914
|
function writeLesson(zelariRoot, lesson) {
|
|
28836
|
-
const
|
|
28837
|
-
appendFileSync(
|
|
28915
|
+
const path94 = join11(zelariRoot, LESSONS_FILE);
|
|
28916
|
+
appendFileSync(path94, `${JSON.stringify(lesson)}
|
|
28838
28917
|
`, "utf8");
|
|
28839
28918
|
}
|
|
28840
28919
|
function findSimilar(lessons, signature) {
|
|
@@ -30798,9 +30877,9 @@ function findCycle(nodes) {
|
|
|
30798
30877
|
if (color.get(start) !== WHITE)
|
|
30799
30878
|
continue;
|
|
30800
30879
|
const stack = [[start, 0]];
|
|
30801
|
-
const
|
|
30880
|
+
const path94 = [];
|
|
30802
30881
|
color.set(start, GRAY);
|
|
30803
|
-
|
|
30882
|
+
path94.push(start);
|
|
30804
30883
|
while (stack.length > 0) {
|
|
30805
30884
|
const top = stack[stack.length - 1];
|
|
30806
30885
|
const [id3, idx] = top;
|
|
@@ -30813,17 +30892,17 @@ function findCycle(nodes) {
|
|
|
30813
30892
|
continue;
|
|
30814
30893
|
const c = color.get(dep);
|
|
30815
30894
|
if (c === GRAY) {
|
|
30816
|
-
const at =
|
|
30817
|
-
return [...
|
|
30895
|
+
const at = path94.indexOf(dep);
|
|
30896
|
+
return [...path94.slice(at), dep];
|
|
30818
30897
|
}
|
|
30819
30898
|
if (c === WHITE) {
|
|
30820
30899
|
color.set(dep, GRAY);
|
|
30821
|
-
|
|
30900
|
+
path94.push(dep);
|
|
30822
30901
|
stack.push([dep, 0]);
|
|
30823
30902
|
}
|
|
30824
30903
|
} else {
|
|
30825
30904
|
color.set(id3, BLACK);
|
|
30826
|
-
|
|
30905
|
+
path94.pop();
|
|
30827
30906
|
stack.pop();
|
|
30828
30907
|
}
|
|
30829
30908
|
}
|
|
@@ -31743,8 +31822,8 @@ var init_runner = __esm({
|
|
|
31743
31822
|
failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
|
|
31744
31823
|
pending: []
|
|
31745
31824
|
};
|
|
31746
|
-
const
|
|
31747
|
-
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${
|
|
31825
|
+
const path94 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
|
|
31826
|
+
this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path94}`);
|
|
31748
31827
|
return snapshot;
|
|
31749
31828
|
}
|
|
31750
31829
|
callLog(msg, data) {
|
|
@@ -31879,6 +31958,13 @@ var init_types9 = __esm({
|
|
|
31879
31958
|
// older readers skip them via the tolerant replay (schema-mismatch issues).
|
|
31880
31959
|
"graph.node_started",
|
|
31881
31960
|
"graph.node_ended",
|
|
31961
|
+
// ADR-0033 (t75): file write-path lifecycle telemetry. State-only (never
|
|
31962
|
+
// model-surface): data = {path, snapshotId?, status?}. Additive kinds —
|
|
31963
|
+
// no SCHEMA_VERSION bump (ADR-0021): older readers report schema-mismatch
|
|
31964
|
+
// and skip them via the tolerant replay; deriveMessages ignores them.
|
|
31965
|
+
"file.read",
|
|
31966
|
+
"file.applied",
|
|
31967
|
+
"file.rejected",
|
|
31882
31968
|
"note"
|
|
31883
31969
|
];
|
|
31884
31970
|
SessionEventEnvelopeSchema = external_exports.object({
|
|
@@ -34017,7 +34103,7 @@ async function createExecutionContext(options = {}) {
|
|
|
34017
34103
|
workspaceKind: workspace.kind
|
|
34018
34104
|
}
|
|
34019
34105
|
});
|
|
34020
|
-
const
|
|
34106
|
+
const fs48 = options.fs ?? new NodeFsProvider(workspace);
|
|
34021
34107
|
const shell = options.shell ?? new NodeShellProvider(workspace);
|
|
34022
34108
|
const subagent = options.subagent ?? NOOP_SUBAGENT_PROVIDER;
|
|
34023
34109
|
const env = options.env ?? process.env;
|
|
@@ -34025,7 +34111,7 @@ async function createExecutionContext(options = {}) {
|
|
|
34025
34111
|
sessionId: sessionId2,
|
|
34026
34112
|
profile,
|
|
34027
34113
|
workspace,
|
|
34028
|
-
fs:
|
|
34114
|
+
fs: fs48,
|
|
34029
34115
|
shell,
|
|
34030
34116
|
subagent,
|
|
34031
34117
|
appendSessionEvent: (input) => writer.append(input),
|
|
@@ -34276,16 +34362,16 @@ function runRetentionFromEnv() {
|
|
|
34276
34362
|
maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
|
|
34277
34363
|
};
|
|
34278
34364
|
}
|
|
34279
|
-
async function dirSize(
|
|
34365
|
+
async function dirSize(path94) {
|
|
34280
34366
|
let total = 0;
|
|
34281
34367
|
let entries;
|
|
34282
34368
|
try {
|
|
34283
|
-
entries = await readdir(
|
|
34369
|
+
entries = await readdir(path94, { withFileTypes: true });
|
|
34284
34370
|
} catch {
|
|
34285
34371
|
return 0;
|
|
34286
34372
|
}
|
|
34287
34373
|
for (const entry of entries) {
|
|
34288
|
-
const child = join13(
|
|
34374
|
+
const child = join13(path94, entry.name);
|
|
34289
34375
|
if (entry.isDirectory())
|
|
34290
34376
|
total += await dirSize(child);
|
|
34291
34377
|
else {
|
|
@@ -34312,19 +34398,19 @@ async function enforceRunRetention(runsDir, options = {}) {
|
|
|
34312
34398
|
for (const entry of entries) {
|
|
34313
34399
|
if (!entry.isDirectory())
|
|
34314
34400
|
continue;
|
|
34315
|
-
const
|
|
34401
|
+
const path94 = join13(runsDir, entry.name);
|
|
34316
34402
|
let startedAt = 0;
|
|
34317
34403
|
let endedAt;
|
|
34318
34404
|
let completed = false;
|
|
34319
34405
|
try {
|
|
34320
|
-
const manifest = JSON.parse(await readFile(join13(
|
|
34406
|
+
const manifest = JSON.parse(await readFile(join13(path94, "manifest.json"), "utf8"));
|
|
34321
34407
|
startedAt = manifest.startedAt ?? 0;
|
|
34322
34408
|
endedAt = manifest.endedAt;
|
|
34323
34409
|
completed = Boolean(endedAt) && manifest.status !== "running";
|
|
34324
34410
|
} catch {
|
|
34325
34411
|
completed = false;
|
|
34326
34412
|
}
|
|
34327
|
-
infos.push({ name: entry.name, path:
|
|
34413
|
+
infos.push({ name: entry.name, path: path94, startedAt, endedAt, completed, bytes: await dirSize(path94) });
|
|
34328
34414
|
}
|
|
34329
34415
|
const remove = async (info) => {
|
|
34330
34416
|
await rm(info.path, { recursive: true, force: true });
|
|
@@ -35096,12 +35182,12 @@ var init_engine = __esm({
|
|
|
35096
35182
|
* content digest) and the returned ref carries the event seq when the
|
|
35097
35183
|
* emitter resolved one.
|
|
35098
35184
|
*/
|
|
35099
|
-
async fsEvidence(observation,
|
|
35185
|
+
async fsEvidence(observation, path94, sha256, content, extra = {}) {
|
|
35100
35186
|
const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
|
|
35101
|
-
const seq = await this.emitEvidence({ observation, path:
|
|
35187
|
+
const seq = await this.emitEvidence({ observation, path: path94, ...extra, ...digest ? { digest } : {} });
|
|
35102
35188
|
return {
|
|
35103
35189
|
tier: "fs-observation",
|
|
35104
|
-
ref:
|
|
35190
|
+
ref: path94,
|
|
35105
35191
|
capturedAt: Date.now(),
|
|
35106
35192
|
...digest ? { digest } : {},
|
|
35107
35193
|
...seq !== void 0 ? { seq } : {}
|
|
@@ -35109,25 +35195,25 @@ var init_engine = __esm({
|
|
|
35109
35195
|
}
|
|
35110
35196
|
async evalFileExists(check2, done) {
|
|
35111
35197
|
const sha256 = this.options.sha256 ?? defaultSha256;
|
|
35112
|
-
const
|
|
35113
|
-
if (!
|
|
35198
|
+
const fs48 = this.services.fs;
|
|
35199
|
+
if (!fs48)
|
|
35114
35200
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
35115
|
-
const exists = await
|
|
35201
|
+
const exists = await fs48.exists(check2.path);
|
|
35116
35202
|
if (!exists) {
|
|
35117
35203
|
await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
|
|
35118
35204
|
return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
|
|
35119
35205
|
}
|
|
35120
|
-
const content = await
|
|
35206
|
+
const content = await fs48.readFile(check2.path).catch(() => "");
|
|
35121
35207
|
return done({
|
|
35122
35208
|
status: "pass",
|
|
35123
35209
|
evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
|
|
35124
35210
|
});
|
|
35125
35211
|
}
|
|
35126
35212
|
async evalFileAbsent(check2, done) {
|
|
35127
|
-
const
|
|
35128
|
-
if (!
|
|
35213
|
+
const fs48 = this.services.fs;
|
|
35214
|
+
if (!fs48)
|
|
35129
35215
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
35130
|
-
const exists = await
|
|
35216
|
+
const exists = await fs48.exists(check2.path);
|
|
35131
35217
|
if (exists) {
|
|
35132
35218
|
await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
|
|
35133
35219
|
return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
|
|
@@ -35139,12 +35225,12 @@ var init_engine = __esm({
|
|
|
35139
35225
|
}
|
|
35140
35226
|
async evalFileContains(check2, done) {
|
|
35141
35227
|
const sha256 = this.options.sha256 ?? defaultSha256;
|
|
35142
|
-
const
|
|
35143
|
-
if (!
|
|
35228
|
+
const fs48 = this.services.fs;
|
|
35229
|
+
if (!fs48)
|
|
35144
35230
|
return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
|
|
35145
35231
|
let content;
|
|
35146
35232
|
try {
|
|
35147
|
-
content = await
|
|
35233
|
+
content = await fs48.readFile(check2.path);
|
|
35148
35234
|
} catch {
|
|
35149
35235
|
await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
|
|
35150
35236
|
return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
|
|
@@ -36903,8 +36989,11 @@ var init_budgetRuntime = __esm({
|
|
|
36903
36989
|
init_resourceSnapshot();
|
|
36904
36990
|
DEFAULT_ESSENTIAL_TOOLS = [
|
|
36905
36991
|
"read_file",
|
|
36906
|
-
"
|
|
36992
|
+
"edit",
|
|
36907
36993
|
"write_file",
|
|
36994
|
+
// ADR-0033 t77: legacy names kept for verification-essential classification
|
|
36995
|
+
// of past-session replays (edit_file/apply_diff left the default catalog).
|
|
36996
|
+
"edit_file",
|
|
36908
36997
|
"apply_diff",
|
|
36909
36998
|
"grep_content",
|
|
36910
36999
|
"list_files",
|
|
@@ -37378,6 +37467,88 @@ var init_taskContract2 = __esm({
|
|
|
37378
37467
|
}
|
|
37379
37468
|
});
|
|
37380
37469
|
|
|
37470
|
+
// src/cli/spineFileEvents.ts
|
|
37471
|
+
function asRecord(v) {
|
|
37472
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
37473
|
+
}
|
|
37474
|
+
function asString3(v) {
|
|
37475
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
37476
|
+
}
|
|
37477
|
+
function extractReject(output) {
|
|
37478
|
+
const rec = asRecord(output);
|
|
37479
|
+
if (!rec) return null;
|
|
37480
|
+
if (rec.ok !== false) return null;
|
|
37481
|
+
const meta3 = asRecord(rec.meta);
|
|
37482
|
+
const reject = asRecord(meta3?.reject) ?? asRecord(rec.reject);
|
|
37483
|
+
if (reject) return reject;
|
|
37484
|
+
return asString3(rec.status) && asString3(rec.path) ? rec : null;
|
|
37485
|
+
}
|
|
37486
|
+
function decodeValue(output) {
|
|
37487
|
+
let rec;
|
|
37488
|
+
if (typeof output !== "string") {
|
|
37489
|
+
rec = asRecord(output);
|
|
37490
|
+
} else {
|
|
37491
|
+
try {
|
|
37492
|
+
rec = asRecord(JSON.parse(output));
|
|
37493
|
+
} catch {
|
|
37494
|
+
return null;
|
|
37495
|
+
}
|
|
37496
|
+
}
|
|
37497
|
+
if (rec && rec.ok === true) return asRecord(rec.value);
|
|
37498
|
+
return rec;
|
|
37499
|
+
}
|
|
37500
|
+
function deriveFileEvents(toolName, output) {
|
|
37501
|
+
if (typeof toolName !== "string" || !FILE_TOOLS.has(toolName)) return [];
|
|
37502
|
+
const reject = toolName === "edit" ? extractReject(output) : null;
|
|
37503
|
+
if (reject) {
|
|
37504
|
+
const status = asString3(reject.status);
|
|
37505
|
+
const rejectPath = asString3(reject.path);
|
|
37506
|
+
return status && rejectPath ? [{ kind: "file.rejected", data: { path: rejectPath, status } }] : [];
|
|
37507
|
+
}
|
|
37508
|
+
const rec = decodeValue(output);
|
|
37509
|
+
if (toolName === "edit") {
|
|
37510
|
+
if (!rec) {
|
|
37511
|
+
const m = typeof output === "string" ? EDIT_REJECT_RE.exec(output) : null;
|
|
37512
|
+
return m ? [{ kind: "file.rejected", data: { path: m[2], status: m[1] } }] : [];
|
|
37513
|
+
}
|
|
37514
|
+
if (rec.applied !== true) return [];
|
|
37515
|
+
const p3 = asString3(rec.path);
|
|
37516
|
+
const snapshotId = asString3(rec.snapshotId);
|
|
37517
|
+
if (!p3 || !snapshotId) return [];
|
|
37518
|
+
const occurrences = typeof rec.occurrencesReplaced === "number" && Number.isFinite(rec.occurrencesReplaced) ? rec.occurrencesReplaced : null;
|
|
37519
|
+
return [
|
|
37520
|
+
{
|
|
37521
|
+
kind: "file.applied",
|
|
37522
|
+
data: { path: p3, snapshotId, ...occurrences !== null ? { occurrencesReplaced: occurrences } : {} }
|
|
37523
|
+
}
|
|
37524
|
+
];
|
|
37525
|
+
}
|
|
37526
|
+
if (!rec) return [];
|
|
37527
|
+
switch (toolName) {
|
|
37528
|
+
case "read_file": {
|
|
37529
|
+
const p3 = asString3(rec.path);
|
|
37530
|
+
const snapshotId = asString3(rec.snapshotId);
|
|
37531
|
+
return p3 && snapshotId ? [{ kind: "file.read", data: { path: p3, snapshotId } }] : [];
|
|
37532
|
+
}
|
|
37533
|
+
case "write_file": {
|
|
37534
|
+
const p3 = asString3(rec.path);
|
|
37535
|
+
if (!p3 || typeof rec.bytesWritten !== "number") return [];
|
|
37536
|
+
const created = typeof rec.created === "boolean" ? rec.created : null;
|
|
37537
|
+
return [{ kind: "file.applied", data: { path: p3, ...created !== null ? { created } : {} } }];
|
|
37538
|
+
}
|
|
37539
|
+
default:
|
|
37540
|
+
return [];
|
|
37541
|
+
}
|
|
37542
|
+
}
|
|
37543
|
+
var FILE_TOOLS, EDIT_REJECT_RE;
|
|
37544
|
+
var init_spineFileEvents = __esm({
|
|
37545
|
+
"src/cli/spineFileEvents.ts"() {
|
|
37546
|
+
"use strict";
|
|
37547
|
+
FILE_TOOLS = /* @__PURE__ */ new Set(["read_file", "write_file", "edit"]);
|
|
37548
|
+
EDIT_REJECT_RE = /^edit: (stale_snapshot|hunk_mismatch|parse_error|file_exists): (.+?)(?: \(|\n|$)/;
|
|
37549
|
+
}
|
|
37550
|
+
});
|
|
37551
|
+
|
|
37381
37552
|
// src/cli/sessionSpine.ts
|
|
37382
37553
|
import path24 from "node:path";
|
|
37383
37554
|
function spineEnabled() {
|
|
@@ -37528,7 +37699,7 @@ async function noteHarnessLifecycle(spine, sessionId2, profileId, budget, baseDi
|
|
|
37528
37699
|
} catch {
|
|
37529
37700
|
}
|
|
37530
37701
|
}
|
|
37531
|
-
var MAX_STREAM_BUFFERS, SessionSpineMirror, SpineMirroringWriter;
|
|
37702
|
+
var MAX_PENDING_TOOL_NAMES, MAX_STREAM_BUFFERS, SessionSpineMirror, SpineMirroringWriter;
|
|
37532
37703
|
var init_sessionSpine = __esm({
|
|
37533
37704
|
"src/cli/sessionSpine.ts"() {
|
|
37534
37705
|
"use strict";
|
|
@@ -37541,6 +37712,8 @@ var init_sessionSpine = __esm({
|
|
|
37541
37712
|
init_runtime2();
|
|
37542
37713
|
init_harnessManifest2();
|
|
37543
37714
|
init_taskContract2();
|
|
37715
|
+
init_spineFileEvents();
|
|
37716
|
+
MAX_PENDING_TOOL_NAMES = 256;
|
|
37544
37717
|
MAX_STREAM_BUFFERS = 32;
|
|
37545
37718
|
SessionSpineMirror = class _SessionSpineMirror {
|
|
37546
37719
|
constructor(sessionId2, options) {
|
|
@@ -37561,6 +37734,8 @@ var init_sessionSpine = __esm({
|
|
|
37561
37734
|
resourceTurnPrepared = false;
|
|
37562
37735
|
/** 2.6 Track A: set once a task.contract has been seeded (or the log had one). */
|
|
37563
37736
|
contractSeeded = false;
|
|
37737
|
+
/** ADR-0033 (t75): bounded callId→tool-name map for result derivation. */
|
|
37738
|
+
pendingToolNames = /* @__PURE__ */ new Map();
|
|
37564
37739
|
status = "disabled";
|
|
37565
37740
|
/** Seq the log continued from when adopting an existing session. */
|
|
37566
37741
|
resumedFromSeq;
|
|
@@ -37845,6 +38020,30 @@ var init_sessionSpine = __esm({
|
|
|
37845
38020
|
}
|
|
37846
38021
|
append(input) {
|
|
37847
38022
|
if (!this.writer || this.status === "closed") return Promise.resolve(null);
|
|
38023
|
+
if (input.kind === "tool.call") {
|
|
38024
|
+
const callId = input.data?.callId;
|
|
38025
|
+
const tool = input.data?.tool;
|
|
38026
|
+
if (typeof callId === "string" && typeof tool === "string") {
|
|
38027
|
+
if (this.pendingToolNames.size >= MAX_PENDING_TOOL_NAMES) {
|
|
38028
|
+
const oldest = this.pendingToolNames.keys().next().value;
|
|
38029
|
+
if (oldest !== void 0) this.pendingToolNames.delete(oldest);
|
|
38030
|
+
}
|
|
38031
|
+
this.pendingToolNames.set(callId, tool);
|
|
38032
|
+
}
|
|
38033
|
+
}
|
|
38034
|
+
let derivedFileEvents = [];
|
|
38035
|
+
if (input.kind === "tool.result") {
|
|
38036
|
+
const callId = input.data?.callId;
|
|
38037
|
+
const toolName = typeof callId === "string" ? this.pendingToolNames.get(callId) : void 0;
|
|
38038
|
+
if (typeof callId === "string") this.pendingToolNames.delete(callId);
|
|
38039
|
+
if (toolName !== void 0) {
|
|
38040
|
+
try {
|
|
38041
|
+
derivedFileEvents = deriveFileEvents(toolName, input.data?.output);
|
|
38042
|
+
} catch {
|
|
38043
|
+
derivedFileEvents = [];
|
|
38044
|
+
}
|
|
38045
|
+
}
|
|
38046
|
+
}
|
|
37848
38047
|
const budgetEffect = input.kind === "tool.call" ? this.onToolCallBudget() : null;
|
|
37849
38048
|
const dueContract = input.kind === "user.message" && taskContractsEnabled() && !this.contractSeeded ? (this.contractSeeded = true, input.data?.text) : null;
|
|
37850
38049
|
const steerText = input.kind === "user.message" && taskContractsEnabled() && this.contractSeeded && !dueContract ? input.data?.text ?? null : null;
|
|
@@ -37861,6 +38060,11 @@ var init_sessionSpine = __esm({
|
|
|
37861
38060
|
(s) => this.writer.append({ kind: "resource.snapshot", actor: ACTOR_SYSTEM, data: { ...dueSnapshot } }).then(() => s)
|
|
37862
38061
|
);
|
|
37863
38062
|
}
|
|
38063
|
+
for (const fileEvent of derivedFileEvents) {
|
|
38064
|
+
seq = seq.then(
|
|
38065
|
+
(s) => this.writer.append({ kind: fileEvent.kind, actor: ACTOR_AGENT, data: { ...fileEvent.data } }).then(() => s)
|
|
38066
|
+
);
|
|
38067
|
+
}
|
|
37864
38068
|
if (dueContract) {
|
|
37865
38069
|
seq = seq.then(async (s) => {
|
|
37866
38070
|
try {
|
|
@@ -38284,9 +38488,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
38284
38488
|
const rnd = randomBytes3(3).toString("hex");
|
|
38285
38489
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
38286
38490
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
38287
|
-
const
|
|
38288
|
-
writeFileSync11(
|
|
38289
|
-
return
|
|
38491
|
+
const path94 = join14(dir, file2);
|
|
38492
|
+
writeFileSync11(path94, fullText, "utf8");
|
|
38493
|
+
return path94;
|
|
38290
38494
|
} catch {
|
|
38291
38495
|
return null;
|
|
38292
38496
|
}
|
|
@@ -38332,10 +38536,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
38332
38536
|
${tail2}`;
|
|
38333
38537
|
}
|
|
38334
38538
|
if (doSpill) {
|
|
38335
|
-
const
|
|
38336
|
-
if (
|
|
38539
|
+
const path94 = spillToolOutput(text, { toolName: opts.toolName });
|
|
38540
|
+
if (path94) {
|
|
38337
38541
|
const spillNote = `
|
|
38338
|
-
\u2026 [full output spilled to: ${
|
|
38542
|
+
\u2026 [full output spilled to: ${path94} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
38339
38543
|
if (preview.includes("] \u2026\n")) {
|
|
38340
38544
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
38341
38545
|
`);
|
|
@@ -46130,28 +46334,28 @@ var init_storage = __esm({
|
|
|
46130
46334
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
46131
46335
|
Storage = class {
|
|
46132
46336
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
46133
|
-
read(
|
|
46134
|
-
if (!existsSync21(
|
|
46135
|
-
throw new Error(`File not found: ${
|
|
46337
|
+
read(path94) {
|
|
46338
|
+
if (!existsSync21(path94)) {
|
|
46339
|
+
throw new Error(`File not found: ${path94}`);
|
|
46136
46340
|
}
|
|
46137
|
-
const md = readFileSync17(
|
|
46341
|
+
const md = readFileSync17(path94, "utf8");
|
|
46138
46342
|
return parseFrontmatter(md);
|
|
46139
46343
|
}
|
|
46140
46344
|
/** Read a Markdown file; returns null if not found. */
|
|
46141
|
-
readIfExists(
|
|
46142
|
-
if (!existsSync21(
|
|
46143
|
-
return this.read(
|
|
46345
|
+
readIfExists(path94) {
|
|
46346
|
+
if (!existsSync21(path94)) return null;
|
|
46347
|
+
return this.read(path94);
|
|
46144
46348
|
}
|
|
46145
46349
|
/**
|
|
46146
46350
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
46147
46351
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
46148
46352
|
*/
|
|
46149
|
-
write(
|
|
46150
|
-
mkdirSync10(dirname2(
|
|
46151
|
-
const tmp =
|
|
46353
|
+
write(path94, meta3, body) {
|
|
46354
|
+
mkdirSync10(dirname2(path94), { recursive: true });
|
|
46355
|
+
const tmp = path94 + ".tmp-" + process.pid;
|
|
46152
46356
|
const md = serializeFrontmatter(meta3, body);
|
|
46153
46357
|
writeFileSync13(tmp, md, "utf8");
|
|
46154
|
-
renameSync(tmp,
|
|
46358
|
+
renameSync(tmp, path94);
|
|
46155
46359
|
}
|
|
46156
46360
|
/** List all .md files in a directory (non-recursive). */
|
|
46157
46361
|
listMarkdown(dir) {
|
|
@@ -46213,8 +46417,8 @@ function nextPlanTaskId(store6) {
|
|
|
46213
46417
|
return `t${store6.counter}`;
|
|
46214
46418
|
}
|
|
46215
46419
|
function writePlanTaskArtifact(rootDir, task) {
|
|
46216
|
-
const
|
|
46217
|
-
mkdirSync11(dirname3(
|
|
46420
|
+
const path94 = join18(rootDir, "plan-tasks", `${task.id}.md`);
|
|
46421
|
+
mkdirSync11(dirname3(path94), { recursive: true });
|
|
46218
46422
|
const meta3 = {
|
|
46219
46423
|
kind: "task",
|
|
46220
46424
|
id: task.id,
|
|
@@ -46235,7 +46439,7 @@ function writePlanTaskArtifact(rootDir, task) {
|
|
|
46235
46439
|
task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
|
|
46236
46440
|
""
|
|
46237
46441
|
].filter((l) => l !== null).join("\n");
|
|
46238
|
-
new Storage().write(
|
|
46442
|
+
new Storage().write(path94, meta3, body);
|
|
46239
46443
|
}
|
|
46240
46444
|
function loadHandle(rootDir) {
|
|
46241
46445
|
const jsonPath = join18(rootDir, "plan.json");
|
|
@@ -47949,7 +48153,7 @@ function createLspTools(provider, root = process.cwd()) {
|
|
|
47949
48153
|
};
|
|
47950
48154
|
const renameSymbol = {
|
|
47951
48155
|
name: "rename_symbol",
|
|
47952
|
-
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
|
|
48156
|
+
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 once the scope looks right.",
|
|
47953
48157
|
permissions: ["read"],
|
|
47954
48158
|
inputSchema: PosArgs.extend({
|
|
47955
48159
|
newName: external_exports.string().min(1).describe("The new symbol name.")
|
|
@@ -48526,7 +48730,7 @@ function createAstTools(root) {
|
|
|
48526
48730
|
};
|
|
48527
48731
|
const findSymbolTool = {
|
|
48528
48732
|
name: "find_symbol",
|
|
48529
|
-
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
|
|
48733
|
+
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 it reliably (node-accurate) instead of guessing the surrounding text. Relative paths resolve against the working directory. TS/JS only.",
|
|
48530
48734
|
permissions: ["read"],
|
|
48531
48735
|
inputSchema: external_exports.object({
|
|
48532
48736
|
path: external_exports.string().min(1).describe("Path to the TS/JS file."),
|
|
@@ -49888,21 +50092,21 @@ function normalizeAuth(auth) {
|
|
|
49888
50092
|
return "agent";
|
|
49889
50093
|
}
|
|
49890
50094
|
function readSecrets() {
|
|
49891
|
-
const
|
|
49892
|
-
if (!existsSync27(
|
|
50095
|
+
const path94 = getSshSecretsPath();
|
|
50096
|
+
if (!existsSync27(path94)) return {};
|
|
49893
50097
|
try {
|
|
49894
|
-
return JSON.parse(readFileSync21(
|
|
50098
|
+
return JSON.parse(readFileSync21(path94, "utf8"));
|
|
49895
50099
|
} catch {
|
|
49896
50100
|
return {};
|
|
49897
50101
|
}
|
|
49898
50102
|
}
|
|
49899
50103
|
function writeSecrets(data) {
|
|
49900
|
-
const
|
|
49901
|
-
mkdirSync12(dirname4(
|
|
49902
|
-
writeFileSync15(
|
|
50104
|
+
const path94 = getSshSecretsPath();
|
|
50105
|
+
mkdirSync12(dirname4(path94), { recursive: true });
|
|
50106
|
+
writeFileSync15(path94, `${JSON.stringify(data, null, 2)}
|
|
49903
50107
|
`, "utf8");
|
|
49904
50108
|
try {
|
|
49905
|
-
chmodSync(
|
|
50109
|
+
chmodSync(path94, 384);
|
|
49906
50110
|
} catch {
|
|
49907
50111
|
}
|
|
49908
50112
|
}
|
|
@@ -49931,10 +50135,10 @@ function deleteSshPassword(id3) {
|
|
|
49931
50135
|
writeSecrets({ passwords });
|
|
49932
50136
|
}
|
|
49933
50137
|
function readStore2() {
|
|
49934
|
-
const
|
|
49935
|
-
if (!existsSync27(
|
|
50138
|
+
const path94 = getSshTargetsPath();
|
|
50139
|
+
if (!existsSync27(path94)) return [];
|
|
49936
50140
|
try {
|
|
49937
|
-
const parsed = JSON.parse(readFileSync21(
|
|
50141
|
+
const parsed = JSON.parse(readFileSync21(path94, "utf8"));
|
|
49938
50142
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
49939
50143
|
return list.filter(
|
|
49940
50144
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -49949,11 +50153,11 @@ function readStore2() {
|
|
|
49949
50153
|
}
|
|
49950
50154
|
}
|
|
49951
50155
|
function writeStore2(targets) {
|
|
49952
|
-
const
|
|
49953
|
-
mkdirSync12(dirname4(
|
|
50156
|
+
const path94 = getSshTargetsPath();
|
|
50157
|
+
mkdirSync12(dirname4(path94), { recursive: true });
|
|
49954
50158
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
49955
50159
|
writeFileSync15(
|
|
49956
|
-
|
|
50160
|
+
path94,
|
|
49957
50161
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
49958
50162
|
`,
|
|
49959
50163
|
"utf8"
|
|
@@ -50199,11 +50403,11 @@ function formatSshTargetsForPrompt() {
|
|
|
50199
50403
|
];
|
|
50200
50404
|
for (const t of targets) {
|
|
50201
50405
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
50202
|
-
const
|
|
50406
|
+
const path94 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
50203
50407
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
50204
50408
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
50205
50409
|
lines.push(
|
|
50206
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
50410
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path94}${tags}${allow}`
|
|
50207
50411
|
);
|
|
50208
50412
|
}
|
|
50209
50413
|
return lines.join("\n");
|
|
@@ -51192,6 +51396,133 @@ var init_lifecycleHooks = __esm({
|
|
|
51192
51396
|
}
|
|
51193
51397
|
});
|
|
51194
51398
|
|
|
51399
|
+
// src/cli/safety/astGate.ts
|
|
51400
|
+
import { promises as fs23 } from "node:fs";
|
|
51401
|
+
import path49 from "node:path";
|
|
51402
|
+
function astGateEnabled() {
|
|
51403
|
+
return process.env.ZELARI_AST_GATE !== "0";
|
|
51404
|
+
}
|
|
51405
|
+
function loadTs2() {
|
|
51406
|
+
if (!tsPromise2) {
|
|
51407
|
+
tsPromise2 = import("typescript").then((m) => m.default ?? m).catch(() => null);
|
|
51408
|
+
}
|
|
51409
|
+
return tsPromise2;
|
|
51410
|
+
}
|
|
51411
|
+
function firstSyntaxError(ts, fileName, content) {
|
|
51412
|
+
const source2 = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
|
51413
|
+
const diags = source2.parseDiagnostics;
|
|
51414
|
+
const first = diags?.[0];
|
|
51415
|
+
if (!first) return null;
|
|
51416
|
+
const { line, character } = source2.getLineAndCharacterOfPosition(first.start ?? 0);
|
|
51417
|
+
const flattened = typeof ts.flattenDiagnosticMessageText === "function" ? ts.flattenDiagnosticMessageText(first.messageText, " ") : String(first.messageText);
|
|
51418
|
+
const code = typeof first.code === "number" ? `TS${first.code}: ` : "";
|
|
51419
|
+
return { message: `${code}${flattened}`, line: line + 1, character: character + 1 };
|
|
51420
|
+
}
|
|
51421
|
+
function minimalDiffOf(pathLabel, brokenContent, line) {
|
|
51422
|
+
const brokenLine = (brokenContent.split(/\r?\n/)[line - 1] ?? "").slice(0, 160);
|
|
51423
|
+
return [
|
|
51424
|
+
`--- a/${pathLabel}`,
|
|
51425
|
+
`+++ b/${pathLabel}`,
|
|
51426
|
+
`@@ -${line},1 +${line},1 @@`,
|
|
51427
|
+
`- ${brokenLine}`,
|
|
51428
|
+
"+ (write REVERTED \u2014 pre-write content restored)"
|
|
51429
|
+
].join("\n");
|
|
51430
|
+
}
|
|
51431
|
+
function wrapWithAstGate(original, opts) {
|
|
51432
|
+
if (!AST_GATE_TOOLS.has(original.name)) return original;
|
|
51433
|
+
const containedPathOf = (args) => {
|
|
51434
|
+
const raw = args["path"];
|
|
51435
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
51436
|
+
const absPath = path49.isAbsolute(raw) ? raw : path49.join(opts.root, raw);
|
|
51437
|
+
const rel2 = path49.relative(opts.root, absPath);
|
|
51438
|
+
if (rel2.startsWith("..") || path49.isAbsolute(rel2)) return null;
|
|
51439
|
+
return absPath;
|
|
51440
|
+
};
|
|
51441
|
+
return {
|
|
51442
|
+
...original,
|
|
51443
|
+
execute: async (args, ctx) => {
|
|
51444
|
+
let preContent;
|
|
51445
|
+
let gateTarget = null;
|
|
51446
|
+
try {
|
|
51447
|
+
if (astGateEnabled()) gateTarget = containedPathOf(args);
|
|
51448
|
+
if (gateTarget !== null && isAstSupported(gateTarget)) {
|
|
51449
|
+
try {
|
|
51450
|
+
preContent = await fs23.readFile(gateTarget, "utf8");
|
|
51451
|
+
} catch (err) {
|
|
51452
|
+
if (err?.code === "ENOENT") preContent = null;
|
|
51453
|
+
}
|
|
51454
|
+
}
|
|
51455
|
+
} catch {
|
|
51456
|
+
gateTarget = null;
|
|
51457
|
+
preContent = void 0;
|
|
51458
|
+
}
|
|
51459
|
+
const result = await original.execute(args, ctx);
|
|
51460
|
+
if (!result.ok) return result;
|
|
51461
|
+
if (gateTarget !== null && !isAstSupported(gateTarget)) {
|
|
51462
|
+
process.stderr.write(
|
|
51463
|
+
`[ast_gate] LOUD SKIP (unsupported-extension): ${original.name} ${gateTarget} \u2014 ${path49.extname(gateTarget) || "(no extension)"} is outside the AST surface; write KEPT, NOT syntax-gated.
|
|
51464
|
+
`
|
|
51465
|
+
);
|
|
51466
|
+
return result;
|
|
51467
|
+
}
|
|
51468
|
+
if (preContent === void 0) return result;
|
|
51469
|
+
try {
|
|
51470
|
+
const absPath = gateTarget;
|
|
51471
|
+
const ts = await loadTs2();
|
|
51472
|
+
if (!ts) {
|
|
51473
|
+
process.stderr.write(
|
|
51474
|
+
`[ast_gate] LOUD SKIP (typescript-unavailable): ${original.name} ${absPath} \u2014 TypeScript is not loadable in this runtime; write KEPT, NOT syntax-gated.
|
|
51475
|
+
`
|
|
51476
|
+
);
|
|
51477
|
+
return result;
|
|
51478
|
+
}
|
|
51479
|
+
const post = await fs23.readFile(absPath, "utf8");
|
|
51480
|
+
const syntaxError = firstSyntaxError(ts, path49.basename(absPath), post);
|
|
51481
|
+
if (!syntaxError) return result;
|
|
51482
|
+
const parseError = `${syntaxError.message} (line ${syntaxError.line}, col ${syntaxError.character})`;
|
|
51483
|
+
let revertedTo;
|
|
51484
|
+
if (preContent === null) {
|
|
51485
|
+
await fs23.unlink(absPath).catch(() => void 0);
|
|
51486
|
+
revertedTo = "absent";
|
|
51487
|
+
} else {
|
|
51488
|
+
await fs23.writeFile(absPath, preContent, "utf8");
|
|
51489
|
+
revertedTo = snapshotIdOf(preContent);
|
|
51490
|
+
}
|
|
51491
|
+
const relLabel = path49.relative(opts.root, absPath) || path49.basename(absPath);
|
|
51492
|
+
return typedErr(
|
|
51493
|
+
`[ast_gate_reverted] ${original.name}: ${absPath} written but the file no longer parses \u2014 write REVERTED (revertedTo=${revertedTo}). ${parseError}. Fix the syntax and re-apply (read_file first for a fresh snapshotId).`,
|
|
51494
|
+
{
|
|
51495
|
+
status: "failed",
|
|
51496
|
+
warnings: ["AST_GATE_REVERTED"],
|
|
51497
|
+
// WriteReject (ADR-0033 schema): conforming payload — the parse
|
|
51498
|
+
// detail lives in the error string + minimalDiff/span.
|
|
51499
|
+
reject: {
|
|
51500
|
+
ok: false,
|
|
51501
|
+
status: "parse_error",
|
|
51502
|
+
path: absPath,
|
|
51503
|
+
minimalDiff: minimalDiffOf(relLabel, post, syntaxError.line),
|
|
51504
|
+
next: { action: "re-read", path: absPath },
|
|
51505
|
+
span: { startLine: syntaxError.line, endLine: syntaxError.line }
|
|
51506
|
+
}
|
|
51507
|
+
}
|
|
51508
|
+
);
|
|
51509
|
+
} catch {
|
|
51510
|
+
return result;
|
|
51511
|
+
}
|
|
51512
|
+
}
|
|
51513
|
+
};
|
|
51514
|
+
}
|
|
51515
|
+
var AST_GATE_TOOLS, tsPromise2;
|
|
51516
|
+
var init_astGate = __esm({
|
|
51517
|
+
"src/cli/safety/astGate.ts"() {
|
|
51518
|
+
"use strict";
|
|
51519
|
+
init_toolTypes();
|
|
51520
|
+
init_filesystem();
|
|
51521
|
+
init_engine3();
|
|
51522
|
+
AST_GATE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit"]);
|
|
51523
|
+
}
|
|
51524
|
+
});
|
|
51525
|
+
|
|
51195
51526
|
// src/cli/extensions/extensionToolWiring.ts
|
|
51196
51527
|
function extensionToolToDefinition(entry) {
|
|
51197
51528
|
const { spec, extensionId } = entry;
|
|
@@ -51386,7 +51717,9 @@ function resourceClaimsFor(toolName, args) {
|
|
|
51386
51717
|
const a = asArgs(args);
|
|
51387
51718
|
switch (toolName) {
|
|
51388
51719
|
case "write_file":
|
|
51389
|
-
case "edit_file":
|
|
51720
|
+
case "edit_file":
|
|
51721
|
+
// ADR-0033 t77: the anchored `edit` carries the same single-path args.
|
|
51722
|
+
case "edit": {
|
|
51390
51723
|
const p3 = primaryPath(a);
|
|
51391
51724
|
return p3 ? [{ kind: "path", operation: "write", path: p3 }] : [];
|
|
51392
51725
|
}
|
|
@@ -51579,8 +51912,8 @@ var init_resourceClaims = __esm({
|
|
|
51579
51912
|
|
|
51580
51913
|
// src/cli/toolResultCache.ts
|
|
51581
51914
|
import { createHash as createHash14 } from "node:crypto";
|
|
51582
|
-
import { promises as
|
|
51583
|
-
import
|
|
51915
|
+
import { promises as fs24 } from "node:fs";
|
|
51916
|
+
import path50 from "node:path";
|
|
51584
51917
|
function isToolCacheEnabled() {
|
|
51585
51918
|
const raw = process.env.ZELARI_TOOL_CACHE;
|
|
51586
51919
|
return raw !== "0" && raw !== "false" && raw !== "off";
|
|
@@ -51665,9 +51998,9 @@ async function statKey(toolName, input, ctx) {
|
|
|
51665
51998
|
if (!input || typeof input !== "object") return null;
|
|
51666
51999
|
const rawPath = input.path;
|
|
51667
52000
|
if (typeof rawPath !== "string" || rawPath.length === 0) return null;
|
|
51668
|
-
const abs =
|
|
52001
|
+
const abs = path50.isAbsolute(rawPath) ? rawPath : path50.join(ctx.cwd, rawPath);
|
|
51669
52002
|
try {
|
|
51670
|
-
const st = await
|
|
52003
|
+
const st = await fs24.stat(abs);
|
|
51671
52004
|
return hashKey({
|
|
51672
52005
|
tool: toolName,
|
|
51673
52006
|
args: input,
|
|
@@ -51721,7 +52054,7 @@ __export(toolRegistry_exports, {
|
|
|
51721
52054
|
wrapWithSandbox: () => wrapWithSandbox
|
|
51722
52055
|
});
|
|
51723
52056
|
import { existsSync as existsSync29 } from "node:fs";
|
|
51724
|
-
import
|
|
52057
|
+
import path51 from "node:path";
|
|
51725
52058
|
function createBuiltinToolRegistry(options = {}) {
|
|
51726
52059
|
const root = options.root ?? process.cwd();
|
|
51727
52060
|
const audit = options.audit ?? new AuditLogger();
|
|
@@ -51737,10 +52070,16 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
51737
52070
|
sessionId2
|
|
51738
52071
|
);
|
|
51739
52072
|
const safeWriteFile = withDiag(
|
|
51740
|
-
|
|
52073
|
+
wrapWithAstGate(
|
|
52074
|
+
wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId2, { verifyWrite: true }),
|
|
52075
|
+
{ root }
|
|
52076
|
+
)
|
|
51741
52077
|
);
|
|
51742
|
-
const
|
|
51743
|
-
|
|
52078
|
+
const safeEdit = withDiag(
|
|
52079
|
+
wrapWithAstGate(
|
|
52080
|
+
wrapWithSandbox(editTool, ["path"], root, audit, sessionId2, { verifyWrite: true }),
|
|
52081
|
+
{ root }
|
|
52082
|
+
)
|
|
51744
52083
|
);
|
|
51745
52084
|
const safeGrepContent = wrapWithSandbox(
|
|
51746
52085
|
withResultCache(grepContentTool, { kind: "ttl" }),
|
|
@@ -51757,9 +52096,6 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
51757
52096
|
sessionId2
|
|
51758
52097
|
);
|
|
51759
52098
|
const safeShowDiff = wrapWithSandbox(showDiffTool, ["path"], root, audit, sessionId2);
|
|
51760
|
-
const safeApplyDiff = withDiag(
|
|
51761
|
-
wrapWithSandbox(applyDiffTool, ["path"], root, audit, sessionId2, { verifyWrite: true })
|
|
51762
|
-
);
|
|
51763
52099
|
const jailSpec = buildJailSpec({ root });
|
|
51764
52100
|
const resolveJailNetwork = (toolName, args) => {
|
|
51765
52101
|
const allowedHosts = [];
|
|
@@ -51855,8 +52191,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
51855
52191
|
}
|
|
51856
52192
|
if (allowMutators) {
|
|
51857
52193
|
registry4.register(withPerm(safeWriteFile));
|
|
51858
|
-
registry4.register(withPerm(
|
|
51859
|
-
registry4.register(withPerm(safeApplyDiff));
|
|
52194
|
+
registry4.register(withPerm(safeEdit));
|
|
51860
52195
|
}
|
|
51861
52196
|
if (allowBash) {
|
|
51862
52197
|
registry4.register(withPerm(safeBash));
|
|
@@ -51890,7 +52225,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
51890
52225
|
safeWebSearch,
|
|
51891
52226
|
...observeBatchTool ? [observeBatchTool] : [],
|
|
51892
52227
|
...retrieveObservationTool ? [retrieveObservationTool] : [],
|
|
51893
|
-
...allowMutators ? [safeWriteFile,
|
|
52228
|
+
...allowMutators ? [safeWriteFile, safeEdit] : [],
|
|
51894
52229
|
...allowBash ? [safeBash, safeExecProcess] : [],
|
|
51895
52230
|
...askUserTool ? [askUserTool] : [],
|
|
51896
52231
|
...skillTool ? [skillTool] : [],
|
|
@@ -52285,14 +52620,14 @@ function wrapWithDiagnostics(original, root, runner) {
|
|
|
52285
52620
|
function claimedSourcePath(token, args, root) {
|
|
52286
52621
|
const cleaned = token.replace(/^["']|["']$/g, "");
|
|
52287
52622
|
if (!cleaned || cleaned.startsWith("-")) return null;
|
|
52288
|
-
if (!DIAG_SOURCE_EXTENSIONS.has(
|
|
52623
|
+
if (!DIAG_SOURCE_EXTENSIONS.has(path51.extname(cleaned).toLowerCase())) return null;
|
|
52289
52624
|
const bases = [root];
|
|
52290
52625
|
const cwd = args["cwd"];
|
|
52291
52626
|
if (typeof cwd === "string" && cwd.length > 0) {
|
|
52292
|
-
bases.unshift(
|
|
52627
|
+
bases.unshift(path51.isAbsolute(cwd) ? cwd : path51.resolve(root, cwd));
|
|
52293
52628
|
}
|
|
52294
52629
|
for (const base2 of bases) {
|
|
52295
|
-
const candidate =
|
|
52630
|
+
const candidate = path51.isAbsolute(cleaned) ? path51.normalize(cleaned) : path51.resolve(base2, cleaned);
|
|
52296
52631
|
try {
|
|
52297
52632
|
const contained = resolveSandboxedPath(candidate, { root });
|
|
52298
52633
|
if (existsSync29(contained)) return contained;
|
|
@@ -52535,6 +52870,7 @@ var init_toolRegistry = __esm({
|
|
|
52535
52870
|
"use strict";
|
|
52536
52871
|
init_registry2();
|
|
52537
52872
|
init_filesystem();
|
|
52873
|
+
init_edit();
|
|
52538
52874
|
init_shell();
|
|
52539
52875
|
init_search();
|
|
52540
52876
|
init_listFiles();
|
|
@@ -52566,6 +52902,7 @@ var init_toolRegistry = __esm({
|
|
|
52566
52902
|
init_providerConfig();
|
|
52567
52903
|
init_toolPermissions();
|
|
52568
52904
|
init_lifecycleHooks();
|
|
52905
|
+
init_astGate();
|
|
52569
52906
|
init_extensionToolWiring();
|
|
52570
52907
|
init_policyEngine();
|
|
52571
52908
|
init_policyLoadMode();
|
|
@@ -52580,6 +52917,10 @@ var init_toolRegistry = __esm({
|
|
|
52580
52917
|
HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
52581
52918
|
"read_file",
|
|
52582
52919
|
"write_file",
|
|
52920
|
+
"edit",
|
|
52921
|
+
// ADR-0033 t77: edit_file/apply_diff left the default catalog, but the
|
|
52922
|
+
// harness bridge still exposes them for the deprecation cycle — keep
|
|
52923
|
+
// skipping them here so catalog re-bridging never duplicates.
|
|
52583
52924
|
"edit_file",
|
|
52584
52925
|
"bash",
|
|
52585
52926
|
"grep_content",
|
|
@@ -52597,13 +52938,13 @@ var init_toolRegistry = __esm({
|
|
|
52597
52938
|
});
|
|
52598
52939
|
|
|
52599
52940
|
// src/cli/metrics.ts
|
|
52600
|
-
import { promises as
|
|
52601
|
-
import
|
|
52941
|
+
import { promises as fs25, existsSync as existsSync30, statSync as statSync4, renameSync as renameSync3, appendFileSync as appendFileSync3, mkdirSync as mkdirSync14 } from "node:fs";
|
|
52942
|
+
import path52 from "node:path";
|
|
52602
52943
|
import os9 from "node:os";
|
|
52603
52944
|
async function readMetrics(file2) {
|
|
52604
52945
|
let raw = "";
|
|
52605
52946
|
try {
|
|
52606
|
-
raw = await
|
|
52947
|
+
raw = await fs25.readFile(file2, "utf-8");
|
|
52607
52948
|
} catch {
|
|
52608
52949
|
return [];
|
|
52609
52950
|
}
|
|
@@ -52647,8 +52988,8 @@ var init_metrics3 = __esm({
|
|
|
52647
52988
|
file;
|
|
52648
52989
|
writeQueue = Promise.resolve();
|
|
52649
52990
|
constructor(file2) {
|
|
52650
|
-
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ??
|
|
52651
|
-
mkdirSync14(
|
|
52991
|
+
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path52.join(os9.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
|
|
52992
|
+
mkdirSync14(path52.dirname(this.file), { recursive: true });
|
|
52652
52993
|
}
|
|
52653
52994
|
/** Metrics file path — doctor/summary readers use this. */
|
|
52654
52995
|
get filePath() {
|
|
@@ -52706,6 +53047,9 @@ var init_turnRuntime = __esm({
|
|
|
52706
53047
|
init_events2();
|
|
52707
53048
|
WRITE_TOOLS3 = /* @__PURE__ */ new Set([
|
|
52708
53049
|
"write_file",
|
|
53050
|
+
"edit",
|
|
53051
|
+
// ADR-0033 t77: legacy names kept so replays of past sessions still
|
|
53052
|
+
// classify as writes (edit_file/apply_diff left the default catalog).
|
|
52709
53053
|
"edit_file",
|
|
52710
53054
|
"apply_diff"
|
|
52711
53055
|
]);
|
|
@@ -52958,7 +53302,7 @@ You are an orchestrator, not an implementer. DEFAULT to spawning tentacles.
|
|
|
52958
53302
|
Current policy: **lead only**.
|
|
52959
53303
|
|
|
52960
53304
|
- Do **not** spawn \`task\` tentacles (explore / general / verify) unless the user explicitly asks.
|
|
52961
|
-
- Do the work yourself with \`read_file\` / \`grep_content\` / \`bash\` / \`
|
|
53305
|
+
- Do the work yourself with \`read_file\` / \`grep_content\` / \`bash\` / \`edit\`.
|
|
52962
53306
|
- The \`task\` tool remains available for an explicit user request only.
|
|
52963
53307
|
`;
|
|
52964
53308
|
FRAGMENTS = {
|
|
@@ -52971,7 +53315,7 @@ Current policy: **lead only**.
|
|
|
52971
53315
|
|
|
52972
53316
|
// src/cli/kraken/verificationAdapters/node.ts
|
|
52973
53317
|
import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
|
|
52974
|
-
import
|
|
53318
|
+
import path53 from "node:path";
|
|
52975
53319
|
async function fileExists(candidate) {
|
|
52976
53320
|
try {
|
|
52977
53321
|
return (await stat2(candidate)).isFile();
|
|
@@ -52981,7 +53325,7 @@ async function fileExists(candidate) {
|
|
|
52981
53325
|
}
|
|
52982
53326
|
async function readPackageJson(root) {
|
|
52983
53327
|
try {
|
|
52984
|
-
return JSON.parse(await readFile4(
|
|
53328
|
+
return JSON.parse(await readFile4(path53.join(root, "package.json"), "utf-8"));
|
|
52985
53329
|
} catch {
|
|
52986
53330
|
return null;
|
|
52987
53331
|
}
|
|
@@ -52995,7 +53339,7 @@ async function resolvePackageManager(root) {
|
|
|
52995
53339
|
const fromField = packageManagerFromField(await readPackageJson(root));
|
|
52996
53340
|
if (fromField) return { pm: fromField, declaredToolchain: true };
|
|
52997
53341
|
for (const [marker, pm] of PM_LOCKFILES) {
|
|
52998
|
-
if (await fileExists(
|
|
53342
|
+
if (await fileExists(path53.join(root, marker))) return { pm, declaredToolchain: true };
|
|
52999
53343
|
}
|
|
53000
53344
|
return { pm: "npm", declaredToolchain: false };
|
|
53001
53345
|
}
|
|
@@ -53018,7 +53362,7 @@ var init_node = __esm({
|
|
|
53018
53362
|
KNOWN_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
|
|
53019
53363
|
nodeAdapter = {
|
|
53020
53364
|
async detect(root) {
|
|
53021
|
-
if (!await fileExists(
|
|
53365
|
+
if (!await fileExists(path53.join(root, "package.json"))) return 0;
|
|
53022
53366
|
const { declaredToolchain } = await resolvePackageManager(root);
|
|
53023
53367
|
return declaredToolchain ? 20 : 10;
|
|
53024
53368
|
},
|
|
@@ -53037,7 +53381,7 @@ var init_node = __esm({
|
|
|
53037
53381
|
|
|
53038
53382
|
// src/cli/kraken/verificationAdapters/python.ts
|
|
53039
53383
|
import { readFile as readFile5, stat as stat3 } from "node:fs/promises";
|
|
53040
|
-
import
|
|
53384
|
+
import path54 from "node:path";
|
|
53041
53385
|
async function fileExists2(candidate) {
|
|
53042
53386
|
try {
|
|
53043
53387
|
return (await stat3(candidate)).isFile();
|
|
@@ -53073,7 +53417,7 @@ var init_python = __esm({
|
|
|
53073
53417
|
async detect(root) {
|
|
53074
53418
|
let best = 0;
|
|
53075
53419
|
for (const [marker, score] of DETECT_MARKERS) {
|
|
53076
|
-
if (score > best && await fileExists2(
|
|
53420
|
+
if (score > best && await fileExists2(path54.join(root, marker))) best = score;
|
|
53077
53421
|
}
|
|
53078
53422
|
return best;
|
|
53079
53423
|
},
|
|
@@ -53081,12 +53425,12 @@ var init_python = __esm({
|
|
|
53081
53425
|
const present = /* @__PURE__ */ new Map();
|
|
53082
53426
|
for (const name of SCAN_FILES) {
|
|
53083
53427
|
try {
|
|
53084
|
-
present.set(name, await readFile5(
|
|
53428
|
+
present.set(name, await readFile5(path54.join(root, name), "utf-8"));
|
|
53085
53429
|
} catch {
|
|
53086
53430
|
}
|
|
53087
53431
|
}
|
|
53088
53432
|
const hasToken = (token) => [...present.values()].some((text) => text.includes(token));
|
|
53089
|
-
const pytestEvidenced = hasToken("pytest") || await dirExists(
|
|
53433
|
+
const pytestEvidenced = hasToken("pytest") || await dirExists(path54.join(root, "tests"));
|
|
53090
53434
|
const mypyReferenced = hasToken("mypy") || present.has("mypy.ini") || present.has(".mypy.ini");
|
|
53091
53435
|
const pyrightReferenced = hasToken("pyright") || present.has("pyrightconfig.json");
|
|
53092
53436
|
return {
|
|
@@ -53102,7 +53446,7 @@ var init_python = __esm({
|
|
|
53102
53446
|
|
|
53103
53447
|
// src/cli/kraken/verificationAdapters/rust.ts
|
|
53104
53448
|
import { stat as stat4 } from "node:fs/promises";
|
|
53105
|
-
import
|
|
53449
|
+
import path55 from "node:path";
|
|
53106
53450
|
async function fileExists3(candidate) {
|
|
53107
53451
|
try {
|
|
53108
53452
|
return (await stat4(candidate)).isFile();
|
|
@@ -53116,7 +53460,7 @@ var init_rust = __esm({
|
|
|
53116
53460
|
"use strict";
|
|
53117
53461
|
rustAdapter = {
|
|
53118
53462
|
async detect(root) {
|
|
53119
|
-
return await fileExists3(
|
|
53463
|
+
return await fileExists3(path55.join(root, "Cargo.toml")) ? 10 : 0;
|
|
53120
53464
|
},
|
|
53121
53465
|
async buildPlan(_root) {
|
|
53122
53466
|
void _root;
|
|
@@ -53132,7 +53476,7 @@ var init_rust = __esm({
|
|
|
53132
53476
|
|
|
53133
53477
|
// src/cli/kraken/verificationAdapters/go.ts
|
|
53134
53478
|
import { stat as stat5 } from "node:fs/promises";
|
|
53135
|
-
import
|
|
53479
|
+
import path56 from "node:path";
|
|
53136
53480
|
async function fileExists4(candidate) {
|
|
53137
53481
|
try {
|
|
53138
53482
|
return (await stat5(candidate)).isFile();
|
|
@@ -53146,7 +53490,7 @@ var init_go = __esm({
|
|
|
53146
53490
|
"use strict";
|
|
53147
53491
|
goAdapter = {
|
|
53148
53492
|
async detect(root) {
|
|
53149
|
-
return await fileExists4(
|
|
53493
|
+
return await fileExists4(path56.join(root, "go.mod")) ? 10 : 0;
|
|
53150
53494
|
},
|
|
53151
53495
|
async buildPlan(_root) {
|
|
53152
53496
|
return {
|
|
@@ -53162,7 +53506,7 @@ var init_go = __esm({
|
|
|
53162
53506
|
|
|
53163
53507
|
// src/cli/kraken/verificationAdapters/java.ts
|
|
53164
53508
|
import { stat as stat6 } from "node:fs/promises";
|
|
53165
|
-
import
|
|
53509
|
+
import path57 from "node:path";
|
|
53166
53510
|
async function fileExists5(candidate) {
|
|
53167
53511
|
try {
|
|
53168
53512
|
return (await stat6(candidate)).isFile();
|
|
@@ -53172,13 +53516,13 @@ async function fileExists5(candidate) {
|
|
|
53172
53516
|
}
|
|
53173
53517
|
async function hasGradleMarker(root) {
|
|
53174
53518
|
for (const [marker] of GRADLE_MARKERS) {
|
|
53175
|
-
if (await fileExists5(
|
|
53519
|
+
if (await fileExists5(path57.join(root, marker))) return true;
|
|
53176
53520
|
}
|
|
53177
53521
|
return false;
|
|
53178
53522
|
}
|
|
53179
53523
|
async function gradleCommand(root, verb, platform = process.platform) {
|
|
53180
53524
|
const wrapper = platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
53181
|
-
if (await fileExists5(
|
|
53525
|
+
if (await fileExists5(path57.join(root, wrapper))) {
|
|
53182
53526
|
return platform === "win32" ? `gradlew.bat ${verb}` : `./gradlew ${verb}`;
|
|
53183
53527
|
}
|
|
53184
53528
|
return `gradle ${verb}`;
|
|
@@ -53203,7 +53547,7 @@ var init_java = __esm({
|
|
|
53203
53547
|
async detect(root) {
|
|
53204
53548
|
let best = 0;
|
|
53205
53549
|
for (const [marker, score] of DETECT_MARKERS2) {
|
|
53206
|
-
if (score > best && await fileExists5(
|
|
53550
|
+
if (score > best && await fileExists5(path57.join(root, marker))) best = score;
|
|
53207
53551
|
}
|
|
53208
53552
|
return best;
|
|
53209
53553
|
},
|
|
@@ -53216,7 +53560,7 @@ var init_java = __esm({
|
|
|
53216
53560
|
buildCommand: await gradleCommand(root, "build")
|
|
53217
53561
|
};
|
|
53218
53562
|
}
|
|
53219
|
-
if (await fileExists5(
|
|
53563
|
+
if (await fileExists5(path57.join(root, "pom.xml"))) {
|
|
53220
53564
|
return {
|
|
53221
53565
|
typecheckCommand: null,
|
|
53222
53566
|
// compilation rides the test/package lifecycle
|
|
@@ -53325,7 +53669,7 @@ __export(nativeVerification_exports, {
|
|
|
53325
53669
|
resolvePackCommandsForRoot: () => resolvePackCommandsForRoot
|
|
53326
53670
|
});
|
|
53327
53671
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
53328
|
-
import
|
|
53672
|
+
import path58 from "node:path";
|
|
53329
53673
|
function nativePackEnabled(env = process.env) {
|
|
53330
53674
|
const v = env.ZELARI_VERIFY_PACK?.toLowerCase();
|
|
53331
53675
|
if (v === "0" || v === "off" || v === "false") return false;
|
|
@@ -53368,7 +53712,7 @@ function packTimeoutMs(env = process.env) {
|
|
|
53368
53712
|
}
|
|
53369
53713
|
async function readPackageScripts(cwd = process.cwd()) {
|
|
53370
53714
|
try {
|
|
53371
|
-
const raw = await readFile6(
|
|
53715
|
+
const raw = await readFile6(path58.join(cwd, "package.json"), "utf-8");
|
|
53372
53716
|
const parsed = JSON.parse(raw);
|
|
53373
53717
|
if (parsed && typeof parsed === "object" && typeof parsed.scripts === "object") {
|
|
53374
53718
|
return parsed.scripts;
|
|
@@ -53901,7 +54245,7 @@ var init_completionProofAttestation = __esm({
|
|
|
53901
54245
|
// src/cli/kraken/completionProofPersist.ts
|
|
53902
54246
|
import { open, rename as rename2, rm as rm2 } from "node:fs/promises";
|
|
53903
54247
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
53904
|
-
import
|
|
54248
|
+
import path59 from "node:path";
|
|
53905
54249
|
function isTruthyFlag2(v) {
|
|
53906
54250
|
const n = v?.trim().toLowerCase();
|
|
53907
54251
|
return n === "1" || n === "true" || n === "yes" || n === "on";
|
|
@@ -53943,8 +54287,8 @@ function isWindowsRenameBlock(err) {
|
|
|
53943
54287
|
return code === "EPERM" || code === "ENOTEMPTY" || code === "EEXIST";
|
|
53944
54288
|
}
|
|
53945
54289
|
async function writeFileAtomic(target, data) {
|
|
53946
|
-
const dir =
|
|
53947
|
-
const tmp =
|
|
54290
|
+
const dir = path59.dirname(target);
|
|
54291
|
+
const tmp = path59.join(dir, `.${path59.basename(target)}.${randomBytes5(6).toString("hex")}.tmp`);
|
|
53948
54292
|
let fh = null;
|
|
53949
54293
|
try {
|
|
53950
54294
|
fh = await open(tmp, "w");
|
|
@@ -53990,7 +54334,7 @@ var init_completionProofPersist = __esm({
|
|
|
53990
54334
|
|
|
53991
54335
|
// src/cli/kraken/completionProof.ts
|
|
53992
54336
|
import { mkdir as mkdir3 } from "node:fs/promises";
|
|
53993
|
-
import
|
|
54337
|
+
import path60 from "node:path";
|
|
53994
54338
|
function verdictOf(evaluation) {
|
|
53995
54339
|
return evaluation.evaluation?.verdict ?? (evaluation.blocked ? "BLOCKED" : "PASS");
|
|
53996
54340
|
}
|
|
@@ -54135,7 +54479,7 @@ async function writeCompletionProofDetailed(evaluation, options = {}) {
|
|
|
54135
54479
|
const mode = options.persistenceMode ?? activeProofPersistenceMode();
|
|
54136
54480
|
try {
|
|
54137
54481
|
const baseDir = options.baseDir ?? process.cwd();
|
|
54138
|
-
const dir =
|
|
54482
|
+
const dir = path60.join(baseDir, ".zelari");
|
|
54139
54483
|
await mkdir3(dir, { recursive: true });
|
|
54140
54484
|
const requested = options.attestation ?? {};
|
|
54141
54485
|
const plan = requested.skipProbes || requested.verificationPlan !== void 0 ? void 0 : await defaultVerificationPlanSnapshot(baseDir);
|
|
@@ -54155,8 +54499,8 @@ async function writeCompletionProofDetailed(evaluation, options = {}) {
|
|
|
54155
54499
|
baseDir
|
|
54156
54500
|
);
|
|
54157
54501
|
const rendered = renderCompletionProof(evaluation, options.meta ?? {}, wrapper.attestation);
|
|
54158
|
-
const markdownPath =
|
|
54159
|
-
const jsonPath =
|
|
54502
|
+
const markdownPath = path60.join(dir, "completion-proof.md");
|
|
54503
|
+
const jsonPath = path60.join(dir, "completion-proof.json");
|
|
54160
54504
|
await writeFileAtomic(markdownPath, rendered.markdown);
|
|
54161
54505
|
await writeFileAtomic(jsonPath, rendered.json);
|
|
54162
54506
|
return { paths: { markdownPath, jsonPath }, mode, requiredBlockReason: null };
|
|
@@ -54246,20 +54590,20 @@ var init_spineTelemetry = __esm({
|
|
|
54246
54590
|
|
|
54247
54591
|
// src/cli/state/fileStateStore.ts
|
|
54248
54592
|
import { createHash as createHash17, randomUUID as randomUUID5 } from "node:crypto";
|
|
54249
|
-
import { promises as
|
|
54250
|
-
import * as
|
|
54593
|
+
import { promises as fs26 } from "node:fs";
|
|
54594
|
+
import * as path61 from "node:path";
|
|
54251
54595
|
function shortId() {
|
|
54252
54596
|
return randomUUID5().replace(/-/g, "").slice(0, 12);
|
|
54253
54597
|
}
|
|
54254
54598
|
async function writeJsonAtomic(filePath, data) {
|
|
54255
|
-
await
|
|
54599
|
+
await fs26.mkdir(path61.dirname(filePath), { recursive: true });
|
|
54256
54600
|
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
54257
|
-
await
|
|
54258
|
-
await
|
|
54601
|
+
await fs26.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
54602
|
+
await fs26.rename(tmp, filePath);
|
|
54259
54603
|
}
|
|
54260
54604
|
async function readJsonFile(filePath) {
|
|
54261
54605
|
try {
|
|
54262
|
-
const raw = await
|
|
54606
|
+
const raw = await fs26.readFile(filePath, "utf8");
|
|
54263
54607
|
return JSON.parse(raw);
|
|
54264
54608
|
} catch {
|
|
54265
54609
|
return null;
|
|
@@ -54313,13 +54657,13 @@ var init_fileStateStore = __esm({
|
|
|
54313
54657
|
indexPath = "";
|
|
54314
54658
|
async init(projectRoot) {
|
|
54315
54659
|
this.root = projectRoot;
|
|
54316
|
-
this.stateDir =
|
|
54317
|
-
this.commitsDir =
|
|
54318
|
-
this.artifactsDir =
|
|
54319
|
-
this.headPath =
|
|
54320
|
-
this.indexPath =
|
|
54321
|
-
await
|
|
54322
|
-
await
|
|
54660
|
+
this.stateDir = path61.join(projectRoot, ".zelari", "state");
|
|
54661
|
+
this.commitsDir = path61.join(this.stateDir, "commits");
|
|
54662
|
+
this.artifactsDir = path61.join(this.stateDir, "artifacts");
|
|
54663
|
+
this.headPath = path61.join(this.stateDir, "HEAD.json");
|
|
54664
|
+
this.indexPath = path61.join(this.stateDir, "index.jsonl");
|
|
54665
|
+
await fs26.mkdir(this.commitsDir, { recursive: true });
|
|
54666
|
+
await fs26.mkdir(this.artifactsDir, { recursive: true });
|
|
54323
54667
|
}
|
|
54324
54668
|
async commit(input) {
|
|
54325
54669
|
if (!input.force && input.verification.ran && !input.verification.ok) {
|
|
@@ -54330,13 +54674,13 @@ var init_fileStateStore = __esm({
|
|
|
54330
54674
|
const discoveries = input.discoveries ?? [];
|
|
54331
54675
|
const parent = await this.head();
|
|
54332
54676
|
const id3 = shortId();
|
|
54333
|
-
const artifactRel =
|
|
54334
|
-
const artifactAbs =
|
|
54335
|
-
await
|
|
54677
|
+
const artifactRel = path61.join("artifacts", id3);
|
|
54678
|
+
const artifactAbs = path61.join(this.artifactsDir, id3);
|
|
54679
|
+
await fs26.mkdir(artifactAbs, { recursive: true });
|
|
54336
54680
|
const summary = defaultSummary(input, discoveries);
|
|
54337
|
-
await
|
|
54338
|
-
await writeJsonAtomic(
|
|
54339
|
-
await writeJsonAtomic(
|
|
54681
|
+
await fs26.writeFile(path61.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
|
|
54682
|
+
await writeJsonAtomic(path61.join(artifactAbs, "discoveries.json"), discoveries);
|
|
54683
|
+
await writeJsonAtomic(path61.join(artifactAbs, "verification.json"), input.verification);
|
|
54340
54684
|
const meta3 = {
|
|
54341
54685
|
id: id3,
|
|
54342
54686
|
parentId: parent?.id ?? null,
|
|
@@ -54348,16 +54692,16 @@ var init_fileStateStore = __esm({
|
|
|
54348
54692
|
workspaceCheckpointId: input.workspaceCheckpointId,
|
|
54349
54693
|
verification: {
|
|
54350
54694
|
...input.verification,
|
|
54351
|
-
reportPath: input.verification.reportPath ??
|
|
54695
|
+
reportPath: input.verification.reportPath ?? path61.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
|
|
54352
54696
|
},
|
|
54353
54697
|
changedPaths: input.changedPaths ?? [],
|
|
54354
54698
|
stablePromptHash: input.stablePromptHash,
|
|
54355
54699
|
discoveryCount: discoveries.length,
|
|
54356
54700
|
artifactDir: artifactRel.replace(/\\/g, "/")
|
|
54357
54701
|
};
|
|
54358
|
-
await writeJsonAtomic(
|
|
54702
|
+
await writeJsonAtomic(path61.join(this.commitsDir, `${id3}.json`), meta3);
|
|
54359
54703
|
await writeJsonAtomic(this.headPath, { id: id3, updatedAt: meta3.createdAt });
|
|
54360
|
-
await
|
|
54704
|
+
await fs26.appendFile(this.indexPath, JSON.stringify({ id: id3, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
|
|
54361
54705
|
return stripStored(meta3);
|
|
54362
54706
|
}
|
|
54363
54707
|
async head() {
|
|
@@ -54366,13 +54710,13 @@ var init_fileStateStore = __esm({
|
|
|
54366
54710
|
return this.get(head.id);
|
|
54367
54711
|
}
|
|
54368
54712
|
async get(id3) {
|
|
54369
|
-
const stored = await readJsonFile(
|
|
54713
|
+
const stored = await readJsonFile(path61.join(this.commitsDir, `${id3}.json`));
|
|
54370
54714
|
return stored ? stripStored(stored) : null;
|
|
54371
54715
|
}
|
|
54372
54716
|
async list(limit = 20) {
|
|
54373
54717
|
let raw;
|
|
54374
54718
|
try {
|
|
54375
|
-
raw = await
|
|
54719
|
+
raw = await fs26.readFile(this.indexPath, "utf8");
|
|
54376
54720
|
} catch {
|
|
54377
54721
|
return [];
|
|
54378
54722
|
}
|
|
@@ -54405,9 +54749,9 @@ var init_fileStateStore = __esm({
|
|
|
54405
54749
|
async loadDiscoveries(id3) {
|
|
54406
54750
|
const meta3 = id3 ? await this.get(id3) : await this.head();
|
|
54407
54751
|
if (!meta3) return [];
|
|
54408
|
-
const stored = await readJsonFile(
|
|
54752
|
+
const stored = await readJsonFile(path61.join(this.commitsDir, `${meta3.id}.json`));
|
|
54409
54753
|
if (!stored?.artifactDir) return [];
|
|
54410
|
-
const discPath =
|
|
54754
|
+
const discPath = path61.join(this.stateDir, stored.artifactDir, "discoveries.json");
|
|
54411
54755
|
return await readJsonFile(discPath) ?? [];
|
|
54412
54756
|
}
|
|
54413
54757
|
async materializeContext(id3, maxChars = DEFAULT_MATERIALIZE_CHARS) {
|
|
@@ -54722,7 +55066,7 @@ function buildContinueUserMessage(task, prior, opts) {
|
|
|
54722
55066
|
## CRITICAL \u2014 plan text \u2260 done on disk
|
|
54723
55067
|
The prior assistant output is a PLAN / SPEC / PROPOSAL (or analysis). It is NOT proof that project files already contain those changes.
|
|
54724
55068
|
The user CONFIRMED the plan and wants you to IMPLEMENT it ON DISK NOW.
|
|
54725
|
-
- You MUST use write_file and/or
|
|
55069
|
+
- You MUST use write_file and/or edit (and bash when needed) to apply every planned change.
|
|
54726
55070
|
- Reading files alone is incomplete. Do not stop after read_file/list_files/grep.
|
|
54727
55071
|
- Do NOT claim "already implemented" / "tutto fatto" unless you verified the changes exist on disk in THIS turn (read after your own successful writes).
|
|
54728
55072
|
- Do NOT restart from zero or re-ask for the overall goal.
|
|
@@ -54870,10 +55214,13 @@ var init_phase = __esm({
|
|
|
54870
55214
|
"createDecision",
|
|
54871
55215
|
"linkDocuments"
|
|
54872
55216
|
// Soft writes that only touch .zelari / plan paths are still gated in
|
|
54873
|
-
// toolRegistry by path when needed; write_file/
|
|
55217
|
+
// toolRegistry by path when needed; write_file/edit stay DENIED.
|
|
54874
55218
|
]);
|
|
54875
55219
|
PLAN_BLOCKED_TOOLS = /* @__PURE__ */ new Set([
|
|
54876
55220
|
"write_file",
|
|
55221
|
+
"edit",
|
|
55222
|
+
// ADR-0033 t77: edit_file/apply_diff left the default catalog but past
|
|
55223
|
+
// sessions' replays still classify by name — keep them blocked.
|
|
54877
55224
|
"edit_file",
|
|
54878
55225
|
"apply_diff",
|
|
54879
55226
|
"bash"
|
|
@@ -55007,10 +55354,10 @@ var init_mode = __esm({
|
|
|
55007
55354
|
|
|
55008
55355
|
// src/cli/headless.ts
|
|
55009
55356
|
import { readFileSync as readFileSync23 } from "node:fs";
|
|
55010
|
-
import
|
|
55357
|
+
import path62 from "node:path";
|
|
55011
55358
|
function resolveHeadlessCwd(opts) {
|
|
55012
55359
|
const raw = typeof opts.cwd === "string" ? opts.cwd.trim() : "";
|
|
55013
|
-
return
|
|
55360
|
+
return path62.resolve(raw.length > 0 ? raw : process.cwd());
|
|
55014
55361
|
}
|
|
55015
55362
|
function defaultProfileForMode(mode) {
|
|
55016
55363
|
switch (mode) {
|
|
@@ -56004,8 +56351,8 @@ var init_claudeProvider = __esm({
|
|
|
56004
56351
|
|
|
56005
56352
|
// src/cli/memory/legacyImport.ts
|
|
56006
56353
|
import { createHash as createHash18 } from "node:crypto";
|
|
56007
|
-
import { promises as
|
|
56008
|
-
import * as
|
|
56354
|
+
import { promises as fs27 } from "node:fs";
|
|
56355
|
+
import * as path63 from "node:path";
|
|
56009
56356
|
function sourceId(fact, line) {
|
|
56010
56357
|
return `jsonl:${fact.id ?? createHash18("sha256").update(line).digest("hex")}`;
|
|
56011
56358
|
}
|
|
@@ -56023,10 +56370,10 @@ function timestamp(value) {
|
|
|
56023
56370
|
}
|
|
56024
56371
|
async function importLegacyMemoryLog(backend, service) {
|
|
56025
56372
|
const result = { found: 0, imported: 0, skipped: 0, corrupt: 0 };
|
|
56026
|
-
const logPath =
|
|
56373
|
+
const logPath = path63.join(path63.dirname(backend.databasePath), "log.jsonl");
|
|
56027
56374
|
let raw;
|
|
56028
56375
|
try {
|
|
56029
|
-
raw = await
|
|
56376
|
+
raw = await fs27.readFile(logPath, "utf8");
|
|
56030
56377
|
} catch {
|
|
56031
56378
|
return result;
|
|
56032
56379
|
}
|
|
@@ -56206,7 +56553,7 @@ var init_sqliteCodec = __esm({
|
|
|
56206
56553
|
// src/cli/memory/sqliteRpc.ts
|
|
56207
56554
|
import { existsSync as existsSync32 } from "node:fs";
|
|
56208
56555
|
import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
56209
|
-
import * as
|
|
56556
|
+
import * as path64 from "node:path";
|
|
56210
56557
|
import { Worker } from "node:worker_threads";
|
|
56211
56558
|
function isBusy(error51) {
|
|
56212
56559
|
const candidate = error51;
|
|
@@ -56215,10 +56562,10 @@ function isBusy(error51) {
|
|
|
56215
56562
|
);
|
|
56216
56563
|
}
|
|
56217
56564
|
function resolveWorkerUrl() {
|
|
56218
|
-
const here =
|
|
56219
|
-
const direct =
|
|
56565
|
+
const here = path64.dirname(fileURLToPath2(import.meta.url));
|
|
56566
|
+
const direct = path64.join(here, "sqliteWorker.mjs");
|
|
56220
56567
|
if (existsSync32(direct)) return pathToFileURL2(direct);
|
|
56221
|
-
return pathToFileURL2(
|
|
56568
|
+
return pathToFileURL2(path64.join(here, "memory", "sqliteWorker.mjs"));
|
|
56222
56569
|
}
|
|
56223
56570
|
var SqliteWorkerRpc;
|
|
56224
56571
|
var init_sqliteRpc = __esm({
|
|
@@ -56506,8 +56853,8 @@ WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.node_id = n.id);
|
|
|
56506
56853
|
|
|
56507
56854
|
// src/cli/memory/sqliteBackend.ts
|
|
56508
56855
|
import { createHash as createHash19, randomUUID as randomUUID6 } from "node:crypto";
|
|
56509
|
-
import { promises as
|
|
56510
|
-
import * as
|
|
56856
|
+
import { promises as fs28 } from "node:fs";
|
|
56857
|
+
import * as path65 from "node:path";
|
|
56511
56858
|
function boundedLimit(value, fallback = 50) {
|
|
56512
56859
|
return Math.max(1, Math.min(Math.floor(value ?? fallback), 1e5));
|
|
56513
56860
|
}
|
|
@@ -56567,42 +56914,42 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
|
|
|
56567
56914
|
async init(projectRoot) {
|
|
56568
56915
|
let resolved;
|
|
56569
56916
|
try {
|
|
56570
|
-
resolved = await
|
|
56917
|
+
resolved = await fs28.realpath(projectRoot);
|
|
56571
56918
|
} catch {
|
|
56572
|
-
resolved =
|
|
56919
|
+
resolved = path65.resolve(projectRoot);
|
|
56573
56920
|
}
|
|
56574
56921
|
if (this.initialized && resolved === this.projectRoot) return;
|
|
56575
56922
|
if (this.initialized) await this.close();
|
|
56576
56923
|
const filename = this.options.filename ?? "memory.db";
|
|
56577
|
-
if (
|
|
56924
|
+
if (path65.basename(filename) !== filename || filename === "." || filename === "..") {
|
|
56578
56925
|
throw new Error("SQLite memory filename must not contain a path.");
|
|
56579
56926
|
}
|
|
56580
|
-
const zelariDirectory =
|
|
56581
|
-
const directory =
|
|
56927
|
+
const zelariDirectory = path65.join(resolved, ".zelari");
|
|
56928
|
+
const directory = path65.join(zelariDirectory, "memory");
|
|
56582
56929
|
for (const candidate of [zelariDirectory, directory]) {
|
|
56583
56930
|
let stat7;
|
|
56584
56931
|
try {
|
|
56585
|
-
stat7 = await
|
|
56932
|
+
stat7 = await fs28.lstat(candidate);
|
|
56586
56933
|
} catch (error51) {
|
|
56587
56934
|
if (error51.code !== "ENOENT") throw error51;
|
|
56588
56935
|
try {
|
|
56589
|
-
await
|
|
56936
|
+
await fs28.mkdir(candidate);
|
|
56590
56937
|
} catch (mkdirError) {
|
|
56591
56938
|
if (mkdirError.code !== "EEXIST") throw mkdirError;
|
|
56592
56939
|
}
|
|
56593
|
-
stat7 = await
|
|
56940
|
+
stat7 = await fs28.lstat(candidate);
|
|
56594
56941
|
}
|
|
56595
56942
|
if (stat7.isSymbolicLink() || !stat7.isDirectory()) {
|
|
56596
56943
|
throw new Error(`Memory directory is not a real directory: ${candidate}`);
|
|
56597
56944
|
}
|
|
56598
56945
|
}
|
|
56599
|
-
const canonicalDirectory = await
|
|
56600
|
-
const relativeDirectory =
|
|
56601
|
-
if (relativeDirectory.startsWith("..") ||
|
|
56946
|
+
const canonicalDirectory = await fs28.realpath(directory);
|
|
56947
|
+
const relativeDirectory = path65.relative(resolved, canonicalDirectory);
|
|
56948
|
+
if (relativeDirectory.startsWith("..") || path65.isAbsolute(relativeDirectory)) {
|
|
56602
56949
|
throw new Error("SQLite memory directory resolves outside the active project.");
|
|
56603
56950
|
}
|
|
56604
56951
|
this.projectRoot = resolved;
|
|
56605
|
-
this.databasePath =
|
|
56952
|
+
this.databasePath = path65.join(canonicalDirectory, filename);
|
|
56606
56953
|
const opened = await this.rpc.open({
|
|
56607
56954
|
dbPath: this.databasePath,
|
|
56608
56955
|
schemaSql: SQLITE_MEMORY_BASE_SCHEMA,
|
|
@@ -57134,8 +57481,8 @@ __export(serviceFactory_exports, {
|
|
|
57134
57481
|
isMemoryV2Enabled: () => isMemoryV2Enabled
|
|
57135
57482
|
});
|
|
57136
57483
|
import { createHash as createHash20 } from "node:crypto";
|
|
57137
|
-
import { promises as
|
|
57138
|
-
import * as
|
|
57484
|
+
import { promises as fs29 } from "node:fs";
|
|
57485
|
+
import * as path66 from "node:path";
|
|
57139
57486
|
function isMemoryV2Enabled(env = process.env) {
|
|
57140
57487
|
if (env.ZELARI_MEMORY === "0") return false;
|
|
57141
57488
|
if (env.ZELARI_MEMORY_BACKEND === "file" || env.ZELARI_MEMORY_BACKEND === "jsonl") return false;
|
|
@@ -57154,9 +57501,9 @@ function semanticMinScore(env) {
|
|
|
57154
57501
|
async function canonicalProjectId(projectRoot) {
|
|
57155
57502
|
let canonical;
|
|
57156
57503
|
try {
|
|
57157
|
-
canonical = await
|
|
57504
|
+
canonical = await fs29.realpath(projectRoot);
|
|
57158
57505
|
} catch {
|
|
57159
|
-
canonical =
|
|
57506
|
+
canonical = path66.resolve(projectRoot);
|
|
57160
57507
|
}
|
|
57161
57508
|
canonical = canonical.replace(/\\/g, "/").replace(/\/$/, "");
|
|
57162
57509
|
if (process.platform === "win32") canonical = canonical.toLocaleLowerCase("en-US");
|
|
@@ -57882,8 +58229,8 @@ function readPlan(ctx) {
|
|
|
57882
58229
|
} catch {
|
|
57883
58230
|
}
|
|
57884
58231
|
}
|
|
57885
|
-
const
|
|
57886
|
-
const doc = ctx.storage.readIfExists(
|
|
58232
|
+
const path94 = workspaceFile(ctx.rootDir, "plan");
|
|
58233
|
+
const doc = ctx.storage.readIfExists(path94);
|
|
57887
58234
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
57888
58235
|
const meta3 = doc.meta;
|
|
57889
58236
|
return {
|
|
@@ -58061,7 +58408,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
58061
58408
|
dueDate: input.dueDate,
|
|
58062
58409
|
targetVersion: version2
|
|
58063
58410
|
});
|
|
58064
|
-
const
|
|
58411
|
+
const path94 = join31(ctx.rootDir, "milestones", `${id3}.md`);
|
|
58065
58412
|
const meta3 = {
|
|
58066
58413
|
kind: "milestone",
|
|
58067
58414
|
id: id3,
|
|
@@ -58078,7 +58425,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
58078
58425
|
`Target version: ${version2}`,
|
|
58079
58426
|
""
|
|
58080
58427
|
].join("\n");
|
|
58081
|
-
ctx.storage.write(
|
|
58428
|
+
ctx.storage.write(path94, meta3, body);
|
|
58082
58429
|
return { id: id3, created: true };
|
|
58083
58430
|
}
|
|
58084
58431
|
function readPlanSummary(ctx) {
|
|
@@ -58282,7 +58629,7 @@ function addIdeaStub(ctx) {
|
|
|
58282
58629
|
const tags = args["tags"] ?? [];
|
|
58283
58630
|
const category = args["category"] ?? "General";
|
|
58284
58631
|
const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
58285
|
-
const
|
|
58632
|
+
const path94 = workspaceArtifact(ctx.rootDir, "decisions", id3);
|
|
58286
58633
|
const meta3 = {
|
|
58287
58634
|
kind: "adr",
|
|
58288
58635
|
status: "proposed",
|
|
@@ -58308,7 +58655,7 @@ function addIdeaStub(ctx) {
|
|
|
58308
58655
|
...consequences.map((c) => `- ${c}`),
|
|
58309
58656
|
""
|
|
58310
58657
|
].join("\n");
|
|
58311
|
-
ctx.storage.write(
|
|
58658
|
+
ctx.storage.write(path94, meta3, body);
|
|
58312
58659
|
return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
58313
58660
|
});
|
|
58314
58661
|
}
|
|
@@ -58390,14 +58737,14 @@ function createDocumentStub(ctx) {
|
|
|
58390
58737
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
58391
58738
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
58392
58739
|
}
|
|
58393
|
-
const
|
|
58740
|
+
const path94 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
58394
58741
|
const meta3 = {
|
|
58395
58742
|
kind: "doc",
|
|
58396
58743
|
id: slug,
|
|
58397
58744
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
58398
58745
|
tags
|
|
58399
58746
|
};
|
|
58400
|
-
ctx.storage.write(
|
|
58747
|
+
ctx.storage.write(path94, meta3, content);
|
|
58401
58748
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
58402
58749
|
});
|
|
58403
58750
|
}
|
|
@@ -59023,10 +59370,10 @@ function getUserMcpPath() {
|
|
|
59023
59370
|
function getProjectMcpPath(projectRoot) {
|
|
59024
59371
|
return join32(projectRoot, ".zelari", "mcp.json");
|
|
59025
59372
|
}
|
|
59026
|
-
function readFile7(
|
|
59027
|
-
if (!existsSync39(
|
|
59373
|
+
function readFile7(path94) {
|
|
59374
|
+
if (!existsSync39(path94)) return {};
|
|
59028
59375
|
try {
|
|
59029
|
-
const parsed = JSON.parse(readFileSync29(
|
|
59376
|
+
const parsed = JSON.parse(readFileSync29(path94, "utf8"));
|
|
59030
59377
|
const out = {};
|
|
59031
59378
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
59032
59379
|
const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
|
|
@@ -59048,10 +59395,10 @@ function readFile7(path93) {
|
|
|
59048
59395
|
return {};
|
|
59049
59396
|
}
|
|
59050
59397
|
}
|
|
59051
|
-
function writeFile3(
|
|
59052
|
-
mkdirSync16(dirname9(
|
|
59398
|
+
function writeFile3(path94, servers) {
|
|
59399
|
+
mkdirSync16(dirname9(path94), { recursive: true });
|
|
59053
59400
|
const body = { mcpServers: servers };
|
|
59054
|
-
writeFileSync18(
|
|
59401
|
+
writeFileSync18(path94, `${JSON.stringify(body, null, 2)}
|
|
59055
59402
|
`, "utf8");
|
|
59056
59403
|
}
|
|
59057
59404
|
function listMcpServers(projectRoot) {
|
|
@@ -59089,9 +59436,9 @@ function upsertMcpServer(opts) {
|
|
|
59089
59436
|
error: "either command (stdio) or url (http) is required"
|
|
59090
59437
|
};
|
|
59091
59438
|
}
|
|
59092
|
-
let
|
|
59439
|
+
let path94;
|
|
59093
59440
|
if (opts.scope === "user") {
|
|
59094
|
-
|
|
59441
|
+
path94 = getUserMcpPath();
|
|
59095
59442
|
} else {
|
|
59096
59443
|
const root = opts.projectRoot?.trim();
|
|
59097
59444
|
if (!root) {
|
|
@@ -59100,9 +59447,9 @@ function upsertMcpServer(opts) {
|
|
|
59100
59447
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
59101
59448
|
};
|
|
59102
59449
|
}
|
|
59103
|
-
|
|
59450
|
+
path94 = getProjectMcpPath(root);
|
|
59104
59451
|
}
|
|
59105
|
-
const current = readFile7(
|
|
59452
|
+
const current = readFile7(path94);
|
|
59106
59453
|
current[name] = {
|
|
59107
59454
|
command: hasCommand ? opts.config.command.trim() : void 0,
|
|
59108
59455
|
args: opts.config.args,
|
|
@@ -59113,21 +59460,21 @@ function upsertMcpServer(opts) {
|
|
|
59113
59460
|
serial: opts.config.serial,
|
|
59114
59461
|
enabled: opts.config.enabled !== false
|
|
59115
59462
|
};
|
|
59116
|
-
writeFile3(
|
|
59117
|
-
return { ok: true, path:
|
|
59463
|
+
writeFile3(path94, current);
|
|
59464
|
+
return { ok: true, path: path94 };
|
|
59118
59465
|
}
|
|
59119
59466
|
function removeMcpServer(opts) {
|
|
59120
|
-
const
|
|
59121
|
-
if (!
|
|
59467
|
+
const path94 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
59468
|
+
if (!path94) {
|
|
59122
59469
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
59123
59470
|
}
|
|
59124
|
-
const current = readFile7(
|
|
59471
|
+
const current = readFile7(path94);
|
|
59125
59472
|
if (!(opts.name in current)) {
|
|
59126
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
59473
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path94}` };
|
|
59127
59474
|
}
|
|
59128
59475
|
delete current[opts.name];
|
|
59129
|
-
writeFile3(
|
|
59130
|
-
return { ok: true, path:
|
|
59476
|
+
writeFile3(path94, current);
|
|
59477
|
+
return { ok: true, path: path94 };
|
|
59131
59478
|
}
|
|
59132
59479
|
var init_mcpConfigIo = __esm({
|
|
59133
59480
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -59596,10 +59943,10 @@ import { createHash as createHash21 } from "node:crypto";
|
|
|
59596
59943
|
import { join as join34 } from "node:path";
|
|
59597
59944
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
59598
59945
|
async function readPackageJson3(projectRoot) {
|
|
59599
|
-
const
|
|
59600
|
-
if (!existsSync41(
|
|
59946
|
+
const path94 = join34(projectRoot, "package.json");
|
|
59947
|
+
if (!existsSync41(path94)) return null;
|
|
59601
59948
|
try {
|
|
59602
|
-
return JSON.parse(await readFile8(
|
|
59949
|
+
return JSON.parse(await readFile8(path94, "utf8"));
|
|
59603
59950
|
} catch {
|
|
59604
59951
|
return null;
|
|
59605
59952
|
}
|
|
@@ -59681,9 +60028,9 @@ async function genBuild(ctx) {
|
|
|
59681
60028
|
].join("\n");
|
|
59682
60029
|
}
|
|
59683
60030
|
async function genOpenQuestions(ctx) {
|
|
59684
|
-
const
|
|
59685
|
-
if (!existsSync41(
|
|
59686
|
-
const content = readFileSync31(
|
|
60031
|
+
const path94 = join34(ctx.rootDir, "risks.md");
|
|
60032
|
+
if (!existsSync41(path94)) return "_No open questions._";
|
|
60033
|
+
const content = readFileSync31(path94, "utf8");
|
|
59687
60034
|
const lines = content.split("\n");
|
|
59688
60035
|
const questions = [];
|
|
59689
60036
|
let currentTitle = "";
|
|
@@ -59957,9 +60304,9 @@ function versionKey(value) {
|
|
|
59957
60304
|
function firstString2(v) {
|
|
59958
60305
|
return typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
59959
60306
|
}
|
|
59960
|
-
function readFileSyncSafe(
|
|
60307
|
+
function readFileSyncSafe(path94) {
|
|
59961
60308
|
try {
|
|
59962
|
-
return readFileSync32(
|
|
60309
|
+
return readFileSync32(path94, "utf8");
|
|
59963
60310
|
} catch {
|
|
59964
60311
|
return null;
|
|
59965
60312
|
}
|
|
@@ -60422,8 +60769,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
60422
60769
|
sources: scope.sources
|
|
60423
60770
|
} : void 0
|
|
60424
60771
|
});
|
|
60425
|
-
const
|
|
60426
|
-
completionHook = { ran: true, path:
|
|
60772
|
+
const path94 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
60773
|
+
completionHook = { ran: true, path: path94, completion };
|
|
60427
60774
|
} catch (err) {
|
|
60428
60775
|
completionHook = {
|
|
60429
60776
|
ran: true,
|
|
@@ -60462,13 +60809,13 @@ __export(councilFeedback_exports, {
|
|
|
60462
60809
|
FeedbackStore: () => FeedbackStore
|
|
60463
60810
|
});
|
|
60464
60811
|
import {
|
|
60465
|
-
promises as
|
|
60812
|
+
promises as fs30,
|
|
60466
60813
|
existsSync as existsSync45,
|
|
60467
60814
|
readFileSync as readFileSync35,
|
|
60468
60815
|
writeFileSync as writeFileSync21,
|
|
60469
60816
|
mkdirSync as mkdirSync17
|
|
60470
60817
|
} from "node:fs";
|
|
60471
|
-
import
|
|
60818
|
+
import path67 from "node:path";
|
|
60472
60819
|
import os10 from "node:os";
|
|
60473
60820
|
var FeedbackStore;
|
|
60474
60821
|
var init_councilFeedback = __esm({
|
|
@@ -60479,7 +60826,7 @@ var init_councilFeedback = __esm({
|
|
|
60479
60826
|
now;
|
|
60480
60827
|
entries = [];
|
|
60481
60828
|
constructor(options = {}) {
|
|
60482
|
-
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ??
|
|
60829
|
+
this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path67.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
|
|
60483
60830
|
this.now = options.now ?? Date.now;
|
|
60484
60831
|
this.load();
|
|
60485
60832
|
}
|
|
@@ -60585,7 +60932,7 @@ var init_councilFeedback = __esm({
|
|
|
60585
60932
|
}
|
|
60586
60933
|
}
|
|
60587
60934
|
save() {
|
|
60588
|
-
mkdirSync17(
|
|
60935
|
+
mkdirSync17(path67.dirname(this.file), { recursive: true });
|
|
60589
60936
|
writeFileSync21(
|
|
60590
60937
|
this.file,
|
|
60591
60938
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
@@ -60595,7 +60942,7 @@ var init_councilFeedback = __esm({
|
|
|
60595
60942
|
/** Async variant of load for callers that prefer async IO. */
|
|
60596
60943
|
async loadAsync() {
|
|
60597
60944
|
try {
|
|
60598
|
-
const raw = await
|
|
60945
|
+
const raw = await fs30.readFile(this.file, "utf-8");
|
|
60599
60946
|
const parsed = JSON.parse(raw);
|
|
60600
60947
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
60601
60948
|
this.entries = parsed.entries.filter(
|
|
@@ -60729,8 +61076,8 @@ __export(fileBackend_exports, {
|
|
|
60729
61076
|
isMemoryEnabled: () => isMemoryEnabled
|
|
60730
61077
|
});
|
|
60731
61078
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
60732
|
-
import { promises as
|
|
60733
|
-
import * as
|
|
61079
|
+
import { promises as fs31 } from "node:fs";
|
|
61080
|
+
import * as path68 from "node:path";
|
|
60734
61081
|
function tokenize2(text) {
|
|
60735
61082
|
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
|
|
60736
61083
|
}
|
|
@@ -60783,9 +61130,9 @@ var init_fileBackend = __esm({
|
|
|
60783
61130
|
logPath = "";
|
|
60784
61131
|
memoryDir = "";
|
|
60785
61132
|
async init(projectRoot) {
|
|
60786
|
-
this.memoryDir =
|
|
60787
|
-
this.logPath =
|
|
60788
|
-
await
|
|
61133
|
+
this.memoryDir = path68.join(projectRoot, ".zelari", "memory");
|
|
61134
|
+
this.logPath = path68.join(this.memoryDir, "log.jsonl");
|
|
61135
|
+
await fs31.mkdir(this.memoryDir, { recursive: true });
|
|
60789
61136
|
}
|
|
60790
61137
|
async add(content, metadata2 = {}, graph) {
|
|
60791
61138
|
const fact = {
|
|
@@ -60795,7 +61142,7 @@ var init_fileBackend = __esm({
|
|
|
60795
61142
|
...graph ? { graph } : {},
|
|
60796
61143
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
60797
61144
|
};
|
|
60798
|
-
await
|
|
61145
|
+
await fs31.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
|
|
60799
61146
|
return fact.id;
|
|
60800
61147
|
}
|
|
60801
61148
|
async search(query, options = {}) {
|
|
@@ -60823,7 +61170,7 @@ var init_fileBackend = __esm({
|
|
|
60823
61170
|
async readAll() {
|
|
60824
61171
|
let raw;
|
|
60825
61172
|
try {
|
|
60826
|
-
raw = await
|
|
61173
|
+
raw = await fs31.readFile(this.logPath, "utf8");
|
|
60827
61174
|
} catch {
|
|
60828
61175
|
return [];
|
|
60829
61176
|
}
|
|
@@ -60856,23 +61203,23 @@ var init_fileBackend = __esm({
|
|
|
60856
61203
|
});
|
|
60857
61204
|
|
|
60858
61205
|
// src/cli/traceStore.ts
|
|
60859
|
-
import { promises as
|
|
60860
|
-
import * as
|
|
61206
|
+
import { promises as fs32 } from "node:fs";
|
|
61207
|
+
import * as path69 from "node:path";
|
|
60861
61208
|
function traceDir(projectRoot) {
|
|
60862
|
-
return
|
|
61209
|
+
return path69.join(projectRoot, ".zelari", "trace");
|
|
60863
61210
|
}
|
|
60864
61211
|
function tracePath(projectRoot, missionId) {
|
|
60865
|
-
return
|
|
61212
|
+
return path69.join(traceDir(projectRoot), `${missionId}.json`);
|
|
60866
61213
|
}
|
|
60867
61214
|
async function saveTrace(projectRoot, missionId, entries) {
|
|
60868
61215
|
const dir = traceDir(projectRoot);
|
|
60869
|
-
await
|
|
61216
|
+
await fs32.mkdir(dir, { recursive: true });
|
|
60870
61217
|
const payload = {
|
|
60871
61218
|
missionId,
|
|
60872
61219
|
ts: Date.now(),
|
|
60873
61220
|
entries
|
|
60874
61221
|
};
|
|
60875
|
-
await
|
|
61222
|
+
await fs32.writeFile(
|
|
60876
61223
|
tracePath(projectRoot, missionId),
|
|
60877
61224
|
JSON.stringify(payload, null, 2) + "\n",
|
|
60878
61225
|
"utf8"
|
|
@@ -60898,8 +61245,8 @@ __export(zelariMission_exports, {
|
|
|
60898
61245
|
runZelariMission: () => runZelariMission
|
|
60899
61246
|
});
|
|
60900
61247
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
60901
|
-
import { promises as
|
|
60902
|
-
import * as
|
|
61248
|
+
import { promises as fs33 } from "node:fs";
|
|
61249
|
+
import * as path70 from "node:path";
|
|
60903
61250
|
function resolveMaxIterations(env = process.env) {
|
|
60904
61251
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
60905
61252
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -60942,10 +61289,10 @@ function isMissionAutoStart(env = process.env) {
|
|
|
60942
61289
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
60943
61290
|
}
|
|
60944
61291
|
async function writeMissionState(projectRoot, state3) {
|
|
60945
|
-
const dir =
|
|
60946
|
-
await
|
|
60947
|
-
await
|
|
60948
|
-
|
|
61292
|
+
const dir = path70.join(projectRoot, ".zelari");
|
|
61293
|
+
await fs33.mkdir(dir, { recursive: true });
|
|
61294
|
+
await fs33.writeFile(
|
|
61295
|
+
path70.join(dir, "mission-state.json"),
|
|
60949
61296
|
JSON.stringify(state3, null, 2) + "\n",
|
|
60950
61297
|
"utf8"
|
|
60951
61298
|
);
|
|
@@ -60965,7 +61312,7 @@ function buildSlicePrompt(brief, userMessage, runMode, iteration) {
|
|
|
60965
61312
|
const fix = iteration > 1 ? " Address any remaining verification failures recorded in .zelari/completion.json." : "";
|
|
60966
61313
|
return `${userMessage}
|
|
60967
61314
|
|
|
60968
|
-
[Zelari mission] Implement the MVP slice: ${brief.deliverableThisMission}.${fix} You MUST create or modify the real project files with write_file /
|
|
61315
|
+
[Zelari mission] Implement the MVP slice: ${brief.deliverableThisMission}.${fix} You MUST create or modify the real project files with write_file / edit \u2014 not just describe them in prose. A run that claims completion without writing any file is a failed run and will not be accepted.`;
|
|
60969
61316
|
}
|
|
60970
61317
|
function formatBriefForChat(brief) {
|
|
60971
61318
|
const lines = [
|
|
@@ -61323,7 +61670,7 @@ function createWriteCounter() {
|
|
|
61323
61670
|
if (id3) pending.delete(id3);
|
|
61324
61671
|
if (!MUTATING.has(name) || event.isError) return;
|
|
61325
61672
|
const result = String(event.result ?? "");
|
|
61326
|
-
const zeroEdit = name === "edit_file" && /occurrencesReplaced["']?\s*[:=]\s*0\b|0 occurrence|no changes/i.test(
|
|
61673
|
+
const zeroEdit = (name === "edit_file" || name === "edit") && /occurrencesReplaced["']?\s*[:=]\s*0\b|0 occurrence|no changes/i.test(
|
|
61327
61674
|
result
|
|
61328
61675
|
);
|
|
61329
61676
|
if (!zeroEdit) state3.successfulWrites += 1;
|
|
@@ -61381,7 +61728,7 @@ async function runAgentMissionSlice(deps) {
|
|
|
61381
61728
|
"",
|
|
61382
61729
|
"# Work phase: BUILD (Zelari mission implementation slice)",
|
|
61383
61730
|
"IMPLEMENT ON DISK. Prior design under .zelari/ is a SPEC, not proof of delivery.",
|
|
61384
|
-
"You MUST call write_file and/or
|
|
61731
|
+
"You MUST call write_file and/or edit before claiming done."
|
|
61385
61732
|
].join("\n");
|
|
61386
61733
|
const headlessRole = {
|
|
61387
61734
|
id: "mission-agent",
|
|
@@ -61597,8 +61944,8 @@ var init_missionSlice = __esm({
|
|
|
61597
61944
|
init_envNumber();
|
|
61598
61945
|
init_modelPricing();
|
|
61599
61946
|
init_delegationPolicy();
|
|
61600
|
-
MUTATING = /* @__PURE__ */ new Set(["write_file", "edit_file", "apply_diff"]);
|
|
61601
|
-
AGENT_MISSION_IMPLEMENTER_PREAMBLE = "You are the sole implementer for this Zelari mission slice. A multi-agent council may already have produced a plan under `.zelari/` \u2014 treat it as a SPEC to apply on disk. You MUST create or modify real project files with write_file /
|
|
61947
|
+
MUTATING = /* @__PURE__ */ new Set(["write_file", "edit", "edit_file", "apply_diff"]);
|
|
61948
|
+
AGENT_MISSION_IMPLEMENTER_PREAMBLE = "You are the sole implementer for this Zelari mission slice. A multi-agent council may already have produced a plan under `.zelari/` \u2014 treat it as a SPEC to apply on disk. You MUST create or modify real project files with write_file / edit. Prose without successful writes is a failed slice.";
|
|
61602
61949
|
}
|
|
61603
61950
|
});
|
|
61604
61951
|
|
|
@@ -61620,7 +61967,7 @@ function safeSocketPath(socketPath) {
|
|
|
61620
61967
|
return socketPath.trim();
|
|
61621
61968
|
}
|
|
61622
61969
|
function startPermissionBroker(socketPath, handlers, opts) {
|
|
61623
|
-
const
|
|
61970
|
+
const path94 = safeSocketPath(socketPath);
|
|
61624
61971
|
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
61625
61972
|
const sockets = /* @__PURE__ */ new Set();
|
|
61626
61973
|
const server = createServer2((socket) => {
|
|
@@ -61720,10 +62067,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
61720
62067
|
return new Promise((resolve9, reject) => {
|
|
61721
62068
|
const onError = (err) => reject(err);
|
|
61722
62069
|
server.once("error", onError);
|
|
61723
|
-
server.listen(
|
|
62070
|
+
server.listen(path94, () => {
|
|
61724
62071
|
server.removeListener("error", onError);
|
|
61725
62072
|
resolve9({
|
|
61726
|
-
socketPath:
|
|
62073
|
+
socketPath: path94,
|
|
61727
62074
|
stop: () => new Promise((res) => {
|
|
61728
62075
|
for (const s of sockets) s.destroy();
|
|
61729
62076
|
sockets.clear();
|
|
@@ -61734,7 +62081,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
61734
62081
|
if (done) return;
|
|
61735
62082
|
done = true;
|
|
61736
62083
|
if (process.platform !== "win32") {
|
|
61737
|
-
unlink(
|
|
62084
|
+
unlink(path94, () => res());
|
|
61738
62085
|
} else {
|
|
61739
62086
|
res();
|
|
61740
62087
|
}
|
|
@@ -61747,11 +62094,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
|
|
|
61747
62094
|
});
|
|
61748
62095
|
}
|
|
61749
62096
|
function requestBrokerAsk(socketPath, ask, opts) {
|
|
61750
|
-
const
|
|
62097
|
+
const path94 = safeSocketPath(socketPath);
|
|
61751
62098
|
const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
|
|
61752
62099
|
const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
|
|
61753
62100
|
return new Promise((resolve9, reject) => {
|
|
61754
|
-
const socket = connect(
|
|
62101
|
+
const socket = connect(path94);
|
|
61755
62102
|
let buffer = "";
|
|
61756
62103
|
let settled = false;
|
|
61757
62104
|
const settle = (fn) => {
|
|
@@ -61766,7 +62113,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
|
|
|
61766
62113
|
settle(
|
|
61767
62114
|
() => reject(
|
|
61768
62115
|
new Error(
|
|
61769
|
-
`permission broker unavailable at "${
|
|
62116
|
+
`permission broker unavailable at "${path94}" (connect timed out after ${connectTimeoutMs}ms)`
|
|
61770
62117
|
)
|
|
61771
62118
|
)
|
|
61772
62119
|
);
|
|
@@ -61906,7 +62253,7 @@ var init_brokerHandlers = __esm({
|
|
|
61906
62253
|
// src/cli/gitOps.ts
|
|
61907
62254
|
import { execFile as execFile5 } from "node:child_process";
|
|
61908
62255
|
import { promisify as promisify4 } from "node:util";
|
|
61909
|
-
import
|
|
62256
|
+
import path71 from "node:path";
|
|
61910
62257
|
async function git5(cwd, args) {
|
|
61911
62258
|
try {
|
|
61912
62259
|
const { stdout } = await execFileAsync4("git", ["-C", cwd, ...args], {
|
|
@@ -61951,7 +62298,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
61951
62298
|
};
|
|
61952
62299
|
}
|
|
61953
62300
|
function defaultProjectRoot() {
|
|
61954
|
-
return
|
|
62301
|
+
return path71.resolve(__dirname, "..", "..", "..");
|
|
61955
62302
|
}
|
|
61956
62303
|
var execFileAsync4;
|
|
61957
62304
|
var init_gitOps = __esm({
|
|
@@ -62944,10 +63291,10 @@ var init_prereqChecks = __esm({
|
|
|
62944
63291
|
|
|
62945
63292
|
// src/cli/plugins/prefs.ts
|
|
62946
63293
|
import { existsSync as existsSync47, readFileSync as readFileSync36, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
|
|
62947
|
-
import
|
|
63294
|
+
import path76 from "node:path";
|
|
62948
63295
|
import os11 from "node:os";
|
|
62949
63296
|
function getPluginPrefsPath() {
|
|
62950
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
63297
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path76.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
62951
63298
|
}
|
|
62952
63299
|
function getPluginPrefs() {
|
|
62953
63300
|
const file2 = getPluginPrefsPath();
|
|
@@ -62968,7 +63315,7 @@ function getPluginPrefs() {
|
|
|
62968
63315
|
}
|
|
62969
63316
|
function writePluginPrefs(prefs) {
|
|
62970
63317
|
const file2 = getPluginPrefsPath();
|
|
62971
|
-
mkdirSync18(
|
|
63318
|
+
mkdirSync18(path76.dirname(file2), { recursive: true });
|
|
62972
63319
|
writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
|
|
62973
63320
|
encoding: "utf-8",
|
|
62974
63321
|
mode: 384
|
|
@@ -63005,7 +63352,7 @@ __export(registry_exports, {
|
|
|
63005
63352
|
isBinaryOnPath: () => isBinaryOnPath
|
|
63006
63353
|
});
|
|
63007
63354
|
import { existsSync as existsSync48 } from "node:fs";
|
|
63008
|
-
import
|
|
63355
|
+
import path77 from "node:path";
|
|
63009
63356
|
function detectLocalBin(bin) {
|
|
63010
63357
|
return (cwd) => {
|
|
63011
63358
|
try {
|
|
@@ -63023,7 +63370,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
63023
63370
|
const platform = opts.platform ?? process.platform;
|
|
63024
63371
|
const exists = opts.exists ?? existsSync48;
|
|
63025
63372
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
63026
|
-
const pathMod = platform === "win32" ?
|
|
63373
|
+
const pathMod = platform === "win32" ? path77.win32 : path77.posix;
|
|
63027
63374
|
const sep4 = platform === "win32" ? ";" : ":";
|
|
63028
63375
|
const dirs = pathEnv.split(sep4).filter((d) => d.length > 0);
|
|
63029
63376
|
const candidates = [bin];
|
|
@@ -64117,11 +64464,11 @@ var init_policy = __esm({
|
|
|
64117
64464
|
});
|
|
64118
64465
|
|
|
64119
64466
|
// src/cli/orchestration/facts.ts
|
|
64120
|
-
import { promises as
|
|
64121
|
-
import
|
|
64467
|
+
import { promises as fs43 } from "node:fs";
|
|
64468
|
+
import path82 from "node:path";
|
|
64122
64469
|
async function collectRepoFileCount(root = process.cwd()) {
|
|
64123
64470
|
try {
|
|
64124
|
-
await
|
|
64471
|
+
await fs43.readdir(root);
|
|
64125
64472
|
} catch {
|
|
64126
64473
|
return void 0;
|
|
64127
64474
|
}
|
|
@@ -64132,13 +64479,13 @@ async function collectRepoFileCount(root = process.cwd()) {
|
|
|
64132
64479
|
const dir = queue.pop();
|
|
64133
64480
|
let entries;
|
|
64134
64481
|
try {
|
|
64135
|
-
entries = await
|
|
64482
|
+
entries = await fs43.readdir(dir, { withFileTypes: true });
|
|
64136
64483
|
} catch {
|
|
64137
64484
|
continue;
|
|
64138
64485
|
}
|
|
64139
64486
|
for (const e of entries) {
|
|
64140
64487
|
if (e.isDirectory()) {
|
|
64141
|
-
if (!SKIP_DIRS.has(e.name)) queue.push(
|
|
64488
|
+
if (!SKIP_DIRS.has(e.name)) queue.push(path82.join(dir, e.name));
|
|
64142
64489
|
} else if (e.isFile()) {
|
|
64143
64490
|
count++;
|
|
64144
64491
|
if (count > MAX_WALK_FILES) return count;
|
|
@@ -64235,8 +64582,8 @@ var init_streamScrub = __esm({
|
|
|
64235
64582
|
});
|
|
64236
64583
|
|
|
64237
64584
|
// src/cli/harnessState.ts
|
|
64238
|
-
import
|
|
64239
|
-
function
|
|
64585
|
+
import path83 from "node:path";
|
|
64586
|
+
function asString4(v) {
|
|
64240
64587
|
return typeof v === "string" ? v : "";
|
|
64241
64588
|
}
|
|
64242
64589
|
function asNumber2(v) {
|
|
@@ -64246,7 +64593,7 @@ function asCallId(v, seq) {
|
|
|
64246
64593
|
return typeof v === "string" && v.length > 0 ? v : `seq:${seq}`;
|
|
64247
64594
|
}
|
|
64248
64595
|
function parseProjection(data) {
|
|
64249
|
-
const policy =
|
|
64596
|
+
const policy = asString4(data.policy);
|
|
64250
64597
|
return {
|
|
64251
64598
|
contextChars: asNumber2(data.contextChars) ?? 0,
|
|
64252
64599
|
returnedCount: asNumber2(data.returnedCount) ?? 0,
|
|
@@ -64255,7 +64602,7 @@ function parseProjection(data) {
|
|
|
64255
64602
|
contextLimit: asNumber2(data.contextLimit),
|
|
64256
64603
|
contextPressureTokens: asNumber2(data.contextPressureTokens),
|
|
64257
64604
|
durationMs: asNumber2(data.durationMs),
|
|
64258
|
-
backend:
|
|
64605
|
+
backend: asString4(data.backend) || void 0,
|
|
64259
64606
|
policy: policy === "ok" || policy === "warn" || policy === "compact" || policy === "hard" ? policy : void 0
|
|
64260
64607
|
};
|
|
64261
64608
|
}
|
|
@@ -64290,7 +64637,7 @@ function deriveHarnessState(events) {
|
|
|
64290
64637
|
break;
|
|
64291
64638
|
case "session.ended": {
|
|
64292
64639
|
session.endedAt = e.ts;
|
|
64293
|
-
const reason =
|
|
64640
|
+
const reason = asString4(e.data.reason);
|
|
64294
64641
|
session.status = reason.length > 0 ? reason : "ended";
|
|
64295
64642
|
if (current) {
|
|
64296
64643
|
current.closedBy = "session-ended";
|
|
@@ -64301,14 +64648,14 @@ function deriveHarnessState(events) {
|
|
|
64301
64648
|
}
|
|
64302
64649
|
case "user.message": {
|
|
64303
64650
|
if (current) current.closedBy = "next-turn";
|
|
64304
|
-
current = newTurn(acc.length + 1,
|
|
64651
|
+
current = newTurn(acc.length + 1, asString4(e.data.text));
|
|
64305
64652
|
acc.push(current);
|
|
64306
64653
|
break;
|
|
64307
64654
|
}
|
|
64308
64655
|
case "assistant.message": {
|
|
64309
64656
|
if (!current) break;
|
|
64310
64657
|
current.assistantMessages += 1;
|
|
64311
|
-
const text =
|
|
64658
|
+
const text = asString4(e.data.text);
|
|
64312
64659
|
current.assistantChars = (current.assistantChars ?? 0) + text.length;
|
|
64313
64660
|
current.assistantText = text;
|
|
64314
64661
|
break;
|
|
@@ -64316,7 +64663,7 @@ function deriveHarnessState(events) {
|
|
|
64316
64663
|
case "tool.call": {
|
|
64317
64664
|
if (!current) break;
|
|
64318
64665
|
current.toolCalls += 1;
|
|
64319
|
-
const tool =
|
|
64666
|
+
const tool = asString4(e.data.tool);
|
|
64320
64667
|
if (tool.length > 0 && !current.toolKinds.includes(tool)) current.toolKinds.push(tool);
|
|
64321
64668
|
current.unsettledCallIds.add(asCallId(e.data.callId, e.seq));
|
|
64322
64669
|
break;
|
|
@@ -64334,7 +64681,7 @@ function deriveHarnessState(events) {
|
|
|
64334
64681
|
if (!current) break;
|
|
64335
64682
|
current.verification = {
|
|
64336
64683
|
strict: e.data.strict === true,
|
|
64337
|
-
verdict:
|
|
64684
|
+
verdict: asString4(e.data.verdict) || "unknown"
|
|
64338
64685
|
};
|
|
64339
64686
|
break;
|
|
64340
64687
|
}
|
|
@@ -64345,7 +64692,7 @@ function deriveHarnessState(events) {
|
|
|
64345
64692
|
break;
|
|
64346
64693
|
}
|
|
64347
64694
|
case "note": {
|
|
64348
|
-
const subject =
|
|
64695
|
+
const subject = asString4(e.data.subject);
|
|
64349
64696
|
if (subject === "context.projection") {
|
|
64350
64697
|
support.contextProjections.push(parseProjection(e.data));
|
|
64351
64698
|
} else if (subject === "memory_event") {
|
|
@@ -64410,7 +64757,7 @@ function contractFor(t) {
|
|
|
64410
64757
|
};
|
|
64411
64758
|
}
|
|
64412
64759
|
async function readHarnessState(sessionDir) {
|
|
64413
|
-
const report = await readSessionLog(
|
|
64760
|
+
const report = await readSessionLog(path83.join(sessionDir, "events.jsonl"));
|
|
64414
64761
|
return deriveHarnessState(report.events);
|
|
64415
64762
|
}
|
|
64416
64763
|
var init_harnessState = __esm({
|
|
@@ -64421,12 +64768,12 @@ var init_harnessState = __esm({
|
|
|
64421
64768
|
});
|
|
64422
64769
|
|
|
64423
64770
|
// src/cli/headless/harnessStateEmit.ts
|
|
64424
|
-
import
|
|
64771
|
+
import path84 from "node:path";
|
|
64425
64772
|
async function emitHarnessStateEvent(opts) {
|
|
64426
64773
|
if (opts.output !== "json") return;
|
|
64427
64774
|
try {
|
|
64428
64775
|
const sessionsDir = resolveSessionsDir({ workspaceRoot: opts.workspaceRoot });
|
|
64429
|
-
const state3 = await readHarnessState(
|
|
64776
|
+
const state3 = await readHarnessState(path84.join(sessionsDir, opts.spine.sessionId));
|
|
64430
64777
|
opts.emitEvent({ type: "harness_state", ...state3 });
|
|
64431
64778
|
} catch (err) {
|
|
64432
64779
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -64921,13 +65268,13 @@ var init_sessionControl = __esm({
|
|
|
64921
65268
|
|
|
64922
65269
|
// src/cli/extensions/sandboxedFs.ts
|
|
64923
65270
|
import { promises as fsp } from "node:fs";
|
|
64924
|
-
import
|
|
65271
|
+
import path85 from "node:path";
|
|
64925
65272
|
function errText(prefix, p3, err) {
|
|
64926
65273
|
const msg = err instanceof Error ? err.message : String(err);
|
|
64927
65274
|
return `[extension-fs] ${prefix} "${p3}": ${msg}`;
|
|
64928
65275
|
}
|
|
64929
65276
|
function bindSandboxedFs(root) {
|
|
64930
|
-
const resolvedRoot =
|
|
65277
|
+
const resolvedRoot = path85.resolve(root);
|
|
64931
65278
|
return {
|
|
64932
65279
|
root: resolvedRoot,
|
|
64933
65280
|
async readFile(relativePath) {
|
|
@@ -64943,7 +65290,7 @@ function bindSandboxedFs(root) {
|
|
|
64943
65290
|
try {
|
|
64944
65291
|
const target = resolveSandboxedPath(relativePath, { root: resolvedRoot });
|
|
64945
65292
|
verifyContainment(target, { root: resolvedRoot });
|
|
64946
|
-
await fsp.mkdir(
|
|
65293
|
+
await fsp.mkdir(path85.dirname(target), { recursive: true });
|
|
64947
65294
|
await fsp.writeFile(target, data, "utf8");
|
|
64948
65295
|
return typedOk({ path: target });
|
|
64949
65296
|
} catch (err) {
|
|
@@ -65013,7 +65360,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
|
|
|
65013
65360
|
skipped
|
|
65014
65361
|
};
|
|
65015
65362
|
const fsRoot = options.fsRoot ?? process.cwd();
|
|
65016
|
-
let
|
|
65363
|
+
let fs48 = null;
|
|
65017
65364
|
const pending = [];
|
|
65018
65365
|
for (const dir of dirs) {
|
|
65019
65366
|
const files = candidateFiles(dir.path);
|
|
@@ -65068,9 +65415,9 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
|
|
|
65068
65415
|
skipped.push(`${file2}: missing ZelariExtension export`);
|
|
65069
65416
|
continue;
|
|
65070
65417
|
}
|
|
65071
|
-
if (!
|
|
65418
|
+
if (!fs48) fs48 = bindSandboxedFs(fsRoot);
|
|
65072
65419
|
try {
|
|
65073
|
-
await runtime.registry.registerExtension(ext, { fs:
|
|
65420
|
+
await runtime.registry.registerExtension(ext, { fs: fs48 });
|
|
65074
65421
|
runtime.loaded.push({ id: ext.id, file: file2, scope });
|
|
65075
65422
|
} catch (err) {
|
|
65076
65423
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -65127,8 +65474,8 @@ var init_loader = __esm({
|
|
|
65127
65474
|
});
|
|
65128
65475
|
|
|
65129
65476
|
// src/cli/headless/runOneTurn.ts
|
|
65130
|
-
import { promises as
|
|
65131
|
-
import
|
|
65477
|
+
import { promises as fs44 } from "node:fs";
|
|
65478
|
+
import path86 from "node:path";
|
|
65132
65479
|
function planModeFromOpts(opts) {
|
|
65133
65480
|
return (opts.phase ?? "build") === "plan";
|
|
65134
65481
|
}
|
|
@@ -65341,13 +65688,13 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
|
|
|
65341
65688
|
`# Work phase: ${opts.phase ?? "build"}`,
|
|
65342
65689
|
(opts.phase ?? "build") === "plan" ? [
|
|
65343
65690
|
"PLAN phase: explore and design only.",
|
|
65344
|
-
"Do not write project source files (write_file/
|
|
65691
|
+
"Do not write project source files (write_file/edit/bash blocked).",
|
|
65345
65692
|
"Plan artifacts under .zelari are allowed.",
|
|
65346
65693
|
"When the plan is ready, tell the user to switch to BUILD to implement on disk."
|
|
65347
65694
|
].join(" ") : [
|
|
65348
65695
|
"BUILD phase \u2014 IMPLEMENT ON DISK (mandatory when the user wants code/file changes).",
|
|
65349
65696
|
"Prior chat may contain a plan or synthesis: that text is a SPEC to apply, NOT proof that files already changed.",
|
|
65350
|
-
"You MUST call write_file and/or
|
|
65697
|
+
"You MUST call write_file and/or edit for every file you change before saying you are done.",
|
|
65351
65698
|
"After read_file: if the planned change is missing, WRITE it \u2014 do not stop at analysis.",
|
|
65352
65699
|
'Never claim "already implemented" / "tutto fatto" based only on reading a plan or skimming code.',
|
|
65353
65700
|
"Only claim done after successful mutating tool calls in THIS turn (or after proving the exact planned diff already exists on disk via read_file of the real files)."
|
|
@@ -65696,8 +66043,8 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
|
|
|
65696
66043
|
if (json3) {
|
|
65697
66044
|
if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
|
|
65698
66045
|
else {
|
|
65699
|
-
await
|
|
65700
|
-
await
|
|
66046
|
+
await fs44.mkdir(path86.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
|
|
66047
|
+
await fs44.writeFile(opts.exportSessionPath, json3, "utf8");
|
|
65701
66048
|
}
|
|
65702
66049
|
}
|
|
65703
66050
|
} catch {
|
|
@@ -65841,6 +66188,8 @@ var init_policy2 = __esm({
|
|
|
65841
66188
|
DEFAULT_WALL_MS = 45 * 60 * 1e3;
|
|
65842
66189
|
GAUNTLET_PARENT_BLOCKED_TOOLS = [
|
|
65843
66190
|
"write_file",
|
|
66191
|
+
"edit",
|
|
66192
|
+
// ADR-0033 t77: legacy names stay blocked (past sessions may replay them).
|
|
65844
66193
|
"edit_file",
|
|
65845
66194
|
"apply_diff",
|
|
65846
66195
|
"bash"
|
|
@@ -66672,10 +67021,10 @@ __export(triggerLock_exports, {
|
|
|
66672
67021
|
lockPath: () => lockPath,
|
|
66673
67022
|
releaseLock: () => releaseLock
|
|
66674
67023
|
});
|
|
66675
|
-
import { promises as
|
|
66676
|
-
import * as
|
|
67024
|
+
import { promises as fs45 } from "node:fs";
|
|
67025
|
+
import * as path87 from "node:path";
|
|
66677
67026
|
function lockPath(projectRoot) {
|
|
66678
|
-
return
|
|
67027
|
+
return path87.join(projectRoot, ".zelari", "trigger.lock");
|
|
66679
67028
|
}
|
|
66680
67029
|
function isPidAlive(pid) {
|
|
66681
67030
|
try {
|
|
@@ -66688,10 +67037,10 @@ function isPidAlive(pid) {
|
|
|
66688
67037
|
}
|
|
66689
67038
|
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
66690
67039
|
const lp = lockPath(projectRoot);
|
|
66691
|
-
const dir =
|
|
66692
|
-
await
|
|
67040
|
+
const dir = path87.dirname(lp);
|
|
67041
|
+
await fs45.mkdir(dir, { recursive: true });
|
|
66693
67042
|
try {
|
|
66694
|
-
const raw = await
|
|
67043
|
+
const raw = await fs45.readFile(lp, "utf8");
|
|
66695
67044
|
const existing = JSON.parse(raw);
|
|
66696
67045
|
if (existing.pid && isPidAlive(existing.pid)) {
|
|
66697
67046
|
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
@@ -66702,13 +67051,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
|
|
|
66702
67051
|
pid: process.pid,
|
|
66703
67052
|
acquiredAt: now().toISOString()
|
|
66704
67053
|
};
|
|
66705
|
-
await
|
|
67054
|
+
await fs45.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
66706
67055
|
return { acquired: true, lockPath: lp };
|
|
66707
67056
|
}
|
|
66708
67057
|
async function releaseLock(projectRoot) {
|
|
66709
67058
|
const lp = lockPath(projectRoot);
|
|
66710
67059
|
try {
|
|
66711
|
-
await
|
|
67060
|
+
await fs45.unlink(lp);
|
|
66712
67061
|
} catch {
|
|
66713
67062
|
}
|
|
66714
67063
|
}
|
|
@@ -66719,8 +67068,8 @@ var init_triggerLock = __esm({
|
|
|
66719
67068
|
});
|
|
66720
67069
|
|
|
66721
67070
|
// src/cli/runHeadless.ts
|
|
66722
|
-
import { promises as
|
|
66723
|
-
import
|
|
67071
|
+
import { promises as fs46 } from "node:fs";
|
|
67072
|
+
import path88 from "node:path";
|
|
66724
67073
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
66725
67074
|
async function runHeadless(opts) {
|
|
66726
67075
|
resetTaskSpawnCount();
|
|
@@ -66964,11 +67313,11 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
66964
67313
|
try {
|
|
66965
67314
|
let preflightGraph;
|
|
66966
67315
|
if (opts.runPlan && opts.runPlan.trim() !== "") {
|
|
66967
|
-
const planPath =
|
|
67316
|
+
const planPath = path88.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
|
|
66968
67317
|
log(`loading pre-flight plan: ${planPath}`);
|
|
66969
67318
|
let raw;
|
|
66970
67319
|
try {
|
|
66971
|
-
raw = await
|
|
67320
|
+
raw = await fs46.readFile(planPath, "utf8");
|
|
66972
67321
|
} catch (e) {
|
|
66973
67322
|
log(`plan file not found: ${planPath} (${e.message})`);
|
|
66974
67323
|
exitCode = 1;
|
|
@@ -67007,10 +67356,10 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
|
|
|
67007
67356
|
log(formatKrakenGraphAscii2(graph));
|
|
67008
67357
|
if (opts.planOnly) {
|
|
67009
67358
|
const planId = randomUUID10();
|
|
67010
|
-
const planDir =
|
|
67011
|
-
const planPath =
|
|
67012
|
-
await
|
|
67013
|
-
await
|
|
67359
|
+
const planDir = path88.join(cwd, ".zelari", "radio");
|
|
67360
|
+
const planPath = path88.join(planDir, `plan-${planId}.json`);
|
|
67361
|
+
await fs46.mkdir(planDir, { recursive: true });
|
|
67362
|
+
await fs46.writeFile(
|
|
67014
67363
|
planPath,
|
|
67015
67364
|
JSON.stringify(
|
|
67016
67365
|
{ id: graph.id, nodes: [...graph.nodes.values()] },
|
|
@@ -67355,8 +67704,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
|
|
|
67355
67704
|
if (json3) {
|
|
67356
67705
|
if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
|
|
67357
67706
|
else {
|
|
67358
|
-
await
|
|
67359
|
-
await
|
|
67707
|
+
await fs46.mkdir(path88.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
|
|
67708
|
+
await fs46.writeFile(opts.exportSessionPath, json3, "utf8");
|
|
67360
67709
|
}
|
|
67361
67710
|
}
|
|
67362
67711
|
} catch {
|
|
@@ -67579,7 +67928,7 @@ ${ragContext}` : slicePrompt;
|
|
|
67579
67928
|
}
|
|
67580
67929
|
if (event.type === "tool_execution_end") {
|
|
67581
67930
|
const name = event.toolName ?? event.name ?? "";
|
|
67582
|
-
if (name === "write_file" || name === "edit_file" || name === "apply_diff") {
|
|
67931
|
+
if (name === "write_file" || name === "edit" || name === "edit_file" || name === "apply_diff") {
|
|
67583
67932
|
writeCount += 1;
|
|
67584
67933
|
}
|
|
67585
67934
|
}
|
|
@@ -67782,8 +68131,8 @@ ${ragContext}` : slicePrompt;
|
|
|
67782
68131
|
if (json3) {
|
|
67783
68132
|
if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
|
|
67784
68133
|
else {
|
|
67785
|
-
await
|
|
67786
|
-
await
|
|
68134
|
+
await fs46.mkdir(path88.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
|
|
68135
|
+
await fs46.writeFile(opts.exportSessionPath, json3, "utf8");
|
|
67787
68136
|
}
|
|
67788
68137
|
}
|
|
67789
68138
|
} catch {
|
|
@@ -68395,7 +68744,7 @@ function upsertSkill(opts) {
|
|
|
68395
68744
|
}
|
|
68396
68745
|
dir = getProjectSkillsDir(root);
|
|
68397
68746
|
}
|
|
68398
|
-
const
|
|
68747
|
+
const path94 = skillFilePath(dir, name);
|
|
68399
68748
|
const content = serializeSkillMd({
|
|
68400
68749
|
name,
|
|
68401
68750
|
description,
|
|
@@ -68404,13 +68753,13 @@ function upsertSkill(opts) {
|
|
|
68404
68753
|
tools: opts.tools,
|
|
68405
68754
|
cost: opts.cost
|
|
68406
68755
|
});
|
|
68407
|
-
const parsed = parseSkillMd(content,
|
|
68756
|
+
const parsed = parseSkillMd(content, path94);
|
|
68408
68757
|
if (!parsed) {
|
|
68409
68758
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
68410
68759
|
}
|
|
68411
|
-
mkdirSync21(dirname13(
|
|
68412
|
-
writeFileSync24(
|
|
68413
|
-
return { ok: true, path:
|
|
68760
|
+
mkdirSync21(dirname13(path94), { recursive: true });
|
|
68761
|
+
writeFileSync24(path94, content, "utf8");
|
|
68762
|
+
return { ok: true, path: path94 };
|
|
68414
68763
|
}
|
|
68415
68764
|
function removeSkill(opts) {
|
|
68416
68765
|
const name = opts.name.trim().toLowerCase();
|
|
@@ -68428,8 +68777,8 @@ function removeSkill(opts) {
|
|
|
68428
68777
|
dir = getProjectSkillsDir(root);
|
|
68429
68778
|
}
|
|
68430
68779
|
const skillDir = join46(dir, name);
|
|
68431
|
-
const
|
|
68432
|
-
if (!existsSync53(
|
|
68780
|
+
const path94 = skillFilePath(dir, name);
|
|
68781
|
+
if (!existsSync53(path94) && !existsSync53(skillDir)) {
|
|
68433
68782
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
68434
68783
|
}
|
|
68435
68784
|
try {
|
|
@@ -68440,7 +68789,7 @@ function removeSkill(opts) {
|
|
|
68440
68789
|
error: err instanceof Error ? err.message : String(err)
|
|
68441
68790
|
};
|
|
68442
68791
|
}
|
|
68443
|
-
return { ok: true, path:
|
|
68792
|
+
return { ok: true, path: path94 };
|
|
68444
68793
|
}
|
|
68445
68794
|
var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
|
|
68446
68795
|
var init_skillConfigIo = __esm({
|
|
@@ -68560,7 +68909,7 @@ var init_jsonApi = __esm({
|
|
|
68560
68909
|
});
|
|
68561
68910
|
|
|
68562
68911
|
// src/cli/memory/mcpAdapter.ts
|
|
68563
|
-
import * as
|
|
68912
|
+
import * as path89 from "node:path";
|
|
68564
68913
|
var id2, projectId, source, SearchSchema, AddSchema, LinkSchema, RetractSchema, MEMORY_MCP_TOOLS, MemoryMcpAdapter;
|
|
68565
68914
|
var init_mcpAdapter = __esm({
|
|
68566
68915
|
"src/cli/memory/mcpAdapter.ts"() {
|
|
@@ -68730,8 +69079,8 @@ var init_mcpAdapter = __esm({
|
|
|
68730
69079
|
this.takeWrite();
|
|
68731
69080
|
const externalFile = args.source?.file;
|
|
68732
69081
|
if (externalFile) {
|
|
68733
|
-
const normalized =
|
|
68734
|
-
if (
|
|
69082
|
+
const normalized = path89.normalize(externalFile);
|
|
69083
|
+
if (path89.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path89.sep}`)) {
|
|
68735
69084
|
throw new Error("source.file must be project-relative and cannot escape the project");
|
|
68736
69085
|
}
|
|
68737
69086
|
}
|
|
@@ -69259,12 +69608,12 @@ function ensureHome() {
|
|
|
69259
69608
|
}
|
|
69260
69609
|
}
|
|
69261
69610
|
function loadCompanionConfig() {
|
|
69262
|
-
const
|
|
69263
|
-
if (!existsSync54(
|
|
69611
|
+
const path94 = getCompanionConfigPath();
|
|
69612
|
+
if (!existsSync54(path94)) {
|
|
69264
69613
|
return { projects: [] };
|
|
69265
69614
|
}
|
|
69266
69615
|
try {
|
|
69267
|
-
const raw = JSON.parse(readFileSync41(
|
|
69616
|
+
const raw = JSON.parse(readFileSync41(path94, "utf8"));
|
|
69268
69617
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
69269
69618
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
69270
69619
|
).map((p3) => ({
|
|
@@ -69302,16 +69651,16 @@ function loadOrCreateToken(explicit) {
|
|
|
69302
69651
|
return { token: explicit.trim(), created: false };
|
|
69303
69652
|
}
|
|
69304
69653
|
ensureHome();
|
|
69305
|
-
const
|
|
69306
|
-
if (existsSync54(
|
|
69307
|
-
const t = readFileSync41(
|
|
69654
|
+
const path94 = getCompanionTokenPath();
|
|
69655
|
+
if (existsSync54(path94)) {
|
|
69656
|
+
const t = readFileSync41(path94, "utf8").trim();
|
|
69308
69657
|
if (t) return { token: t, created: false };
|
|
69309
69658
|
}
|
|
69310
69659
|
const token = randomBytes7(24).toString("base64url");
|
|
69311
|
-
writeFileSync25(
|
|
69660
|
+
writeFileSync25(path94, token + "\n", "utf8");
|
|
69312
69661
|
try {
|
|
69313
|
-
const
|
|
69314
|
-
|
|
69662
|
+
const fs48 = __require("node:fs");
|
|
69663
|
+
fs48.chmodSync?.(path94, 384);
|
|
69315
69664
|
} catch {
|
|
69316
69665
|
}
|
|
69317
69666
|
return { token, created: true };
|
|
@@ -69336,17 +69685,17 @@ function mergeProjects(cfg, extraPaths) {
|
|
|
69336
69685
|
byId.set(p3.id, p3);
|
|
69337
69686
|
}
|
|
69338
69687
|
for (const raw of extraPaths) {
|
|
69339
|
-
const
|
|
69340
|
-
if (!
|
|
69341
|
-
let id3 = slugFromPath(
|
|
69688
|
+
const path94 = raw.trim();
|
|
69689
|
+
if (!path94) continue;
|
|
69690
|
+
let id3 = slugFromPath(path94);
|
|
69342
69691
|
let n = 2;
|
|
69343
|
-
while (byId.has(id3) && byId.get(id3).path !==
|
|
69344
|
-
id3 = `${slugFromPath(
|
|
69692
|
+
while (byId.has(id3) && byId.get(id3).path !== path94) {
|
|
69693
|
+
id3 = `${slugFromPath(path94)}-${n++}`;
|
|
69345
69694
|
}
|
|
69346
69695
|
byId.set(id3, {
|
|
69347
69696
|
id: id3,
|
|
69348
|
-
name: slugFromPath(
|
|
69349
|
-
path:
|
|
69697
|
+
name: slugFromPath(path94),
|
|
69698
|
+
path: path94
|
|
69350
69699
|
});
|
|
69351
69700
|
}
|
|
69352
69701
|
return [...byId.values()];
|
|
@@ -69394,8 +69743,8 @@ var init_config = __esm({
|
|
|
69394
69743
|
});
|
|
69395
69744
|
|
|
69396
69745
|
// src/cli/serve/spineLockSweep.ts
|
|
69397
|
-
import { promises as
|
|
69398
|
-
import
|
|
69746
|
+
import { promises as fs47 } from "node:fs";
|
|
69747
|
+
import path90 from "node:path";
|
|
69399
69748
|
async function sweepOrphanSpineLocks(sessionsDir, options = {}) {
|
|
69400
69749
|
const dir = sessionsDir ?? resolveSessionsDir();
|
|
69401
69750
|
const onSwept = options.onSwept ?? ((sessionId2, reason) => {
|
|
@@ -69408,14 +69757,14 @@ async function sweepOrphanSpineLocks(sessionsDir, options = {}) {
|
|
|
69408
69757
|
const result = { swept: 0, kept: 0, errors: 0 };
|
|
69409
69758
|
let entries;
|
|
69410
69759
|
try {
|
|
69411
|
-
entries = await
|
|
69760
|
+
entries = await fs47.readdir(dir);
|
|
69412
69761
|
} catch {
|
|
69413
69762
|
return result;
|
|
69414
69763
|
}
|
|
69415
69764
|
for (const sessionId2 of entries) {
|
|
69416
|
-
const lockPath2 =
|
|
69765
|
+
const lockPath2 = path90.join(dir, sessionId2, "writer.lock");
|
|
69417
69766
|
try {
|
|
69418
|
-
const raw = await
|
|
69767
|
+
const raw = await fs47.readFile(lockPath2, "utf-8");
|
|
69419
69768
|
let lockInfo = {};
|
|
69420
69769
|
try {
|
|
69421
69770
|
lockInfo = JSON.parse(raw);
|
|
@@ -69432,7 +69781,7 @@ async function sweepOrphanSpineLocks(sessionsDir, options = {}) {
|
|
|
69432
69781
|
result.kept += 1;
|
|
69433
69782
|
continue;
|
|
69434
69783
|
}
|
|
69435
|
-
await
|
|
69784
|
+
await fs47.rm(lockPath2, { force: true });
|
|
69436
69785
|
result.swept += 1;
|
|
69437
69786
|
onSwept(sessionId2, verdict.reason);
|
|
69438
69787
|
} catch (err) {
|
|
@@ -70383,9 +70732,9 @@ async function runCompanionServe(opts = {}) {
|
|
|
70383
70732
|
return;
|
|
70384
70733
|
}
|
|
70385
70734
|
const url2 = parseUrl(req);
|
|
70386
|
-
const
|
|
70735
|
+
const path94 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
70387
70736
|
try {
|
|
70388
|
-
if (req.method === "GET" && (
|
|
70737
|
+
if (req.method === "GET" && (path94 === "/health" || path94 === "/v1/health")) {
|
|
70389
70738
|
sendJson2(res, 200, {
|
|
70390
70739
|
ok: true,
|
|
70391
70740
|
service: "zelari-companion",
|
|
@@ -70397,18 +70746,18 @@ async function runCompanionServe(opts = {}) {
|
|
|
70397
70746
|
});
|
|
70398
70747
|
return;
|
|
70399
70748
|
}
|
|
70400
|
-
if (
|
|
70749
|
+
if (path94.startsWith("/v1")) {
|
|
70401
70750
|
if (!tokenMatches(token, getBearer(req))) {
|
|
70402
70751
|
sendJson2(res, 401, { ok: false, error: "unauthorized" });
|
|
70403
70752
|
return;
|
|
70404
70753
|
}
|
|
70405
70754
|
}
|
|
70406
|
-
if (req.method === "GET" &&
|
|
70755
|
+
if (req.method === "GET" && path94 === "/v1/config") {
|
|
70407
70756
|
const snap = buildDesktopConfigSnapshot();
|
|
70408
70757
|
sendJson2(res, 200, { ok: true, ...snap });
|
|
70409
70758
|
return;
|
|
70410
70759
|
}
|
|
70411
|
-
if (req.method === "GET" &&
|
|
70760
|
+
if (req.method === "GET" && path94 === "/v1/projects") {
|
|
70412
70761
|
sendJson2(res, 200, {
|
|
70413
70762
|
ok: true,
|
|
70414
70763
|
projects: projects.map((p3) => ({
|
|
@@ -70419,7 +70768,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
70419
70768
|
});
|
|
70420
70769
|
return;
|
|
70421
70770
|
}
|
|
70422
|
-
if (req.method === "GET" &&
|
|
70771
|
+
if (req.method === "GET" && path94 === "/v1/runs") {
|
|
70423
70772
|
sendJson2(res, 200, {
|
|
70424
70773
|
ok: true,
|
|
70425
70774
|
active: runs.getActive(),
|
|
@@ -70437,7 +70786,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
70437
70786
|
});
|
|
70438
70787
|
return;
|
|
70439
70788
|
}
|
|
70440
|
-
if (req.method === "POST" &&
|
|
70789
|
+
if (req.method === "POST" && path94 === "/v1/runs") {
|
|
70441
70790
|
const raw = await readBody(req);
|
|
70442
70791
|
let body = {};
|
|
70443
70792
|
try {
|
|
@@ -70485,7 +70834,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
70485
70834
|
});
|
|
70486
70835
|
return;
|
|
70487
70836
|
}
|
|
70488
|
-
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(
|
|
70837
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path94);
|
|
70489
70838
|
if (req.method === "GET" && eventsMatch) {
|
|
70490
70839
|
const runId = eventsMatch[1];
|
|
70491
70840
|
const run = runs.getRun(runId);
|
|
@@ -70550,7 +70899,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
70550
70899
|
}, 500);
|
|
70551
70900
|
return;
|
|
70552
70901
|
}
|
|
70553
|
-
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(
|
|
70902
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path94);
|
|
70554
70903
|
if (req.method === "POST" && cancelMatch) {
|
|
70555
70904
|
const runId = cancelMatch[1];
|
|
70556
70905
|
const result = runs.cancel(runId);
|
|
@@ -70561,7 +70910,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
70561
70910
|
sendJson2(res, 200, { ok: true, cancelled: runId });
|
|
70562
70911
|
return;
|
|
70563
70912
|
}
|
|
70564
|
-
const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(
|
|
70913
|
+
const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path94);
|
|
70565
70914
|
if (req.method === "POST" && steerMatch) {
|
|
70566
70915
|
const runId = steerMatch[1];
|
|
70567
70916
|
const raw = await readBody(req);
|
|
@@ -70728,11 +71077,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
70728
71077
|
import { existsSync as existsSync56, readFileSync as readFileSync42, readlinkSync, statSync as statSync10 } from "node:fs";
|
|
70729
71078
|
import { createRequire as createRequire3 } from "node:module";
|
|
70730
71079
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
70731
|
-
import
|
|
71080
|
+
import path91 from "node:path";
|
|
70732
71081
|
function findPackageRoot(start) {
|
|
70733
71082
|
let dir = start;
|
|
70734
71083
|
for (let i = 0; i < 6; i += 1) {
|
|
70735
|
-
const candidate =
|
|
71084
|
+
const candidate = path91.join(dir, "package.json");
|
|
70736
71085
|
if (existsSync56(candidate)) {
|
|
70737
71086
|
try {
|
|
70738
71087
|
const pkg = JSON.parse(readFileSync42(candidate, "utf8"));
|
|
@@ -70740,11 +71089,11 @@ function findPackageRoot(start) {
|
|
|
70740
71089
|
} catch {
|
|
70741
71090
|
}
|
|
70742
71091
|
}
|
|
70743
|
-
const parent =
|
|
71092
|
+
const parent = path91.dirname(dir);
|
|
70744
71093
|
if (parent === dir) break;
|
|
70745
71094
|
dir = parent;
|
|
70746
71095
|
}
|
|
70747
|
-
return
|
|
71096
|
+
return path91.resolve(__dirname3, "..", "..", "..");
|
|
70748
71097
|
}
|
|
70749
71098
|
function tryExec(cmd) {
|
|
70750
71099
|
try {
|
|
@@ -70758,7 +71107,7 @@ function tryExec(cmd) {
|
|
|
70758
71107
|
}
|
|
70759
71108
|
function readPackageJson4() {
|
|
70760
71109
|
try {
|
|
70761
|
-
const pkgPath =
|
|
71110
|
+
const pkgPath = path91.join(packageRoot, "package.json");
|
|
70762
71111
|
return JSON.parse(readFileSync42(pkgPath, "utf8"));
|
|
70763
71112
|
} catch {
|
|
70764
71113
|
return null;
|
|
@@ -70774,7 +71123,7 @@ function checkShim(pkgName) {
|
|
|
70774
71123
|
}
|
|
70775
71124
|
const isWin = process.platform === "win32";
|
|
70776
71125
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
70777
|
-
const shimPath =
|
|
71126
|
+
const shimPath = path91.join(prefix, shimName);
|
|
70778
71127
|
if (!existsSync56(shimPath)) {
|
|
70779
71128
|
return FAIL(
|
|
70780
71129
|
`shim not found at ${shimPath}
|
|
@@ -70802,8 +71151,8 @@ function checkShim(pkgName) {
|
|
|
70802
71151
|
fix: npm install -g ${pkgName}@latest --force`
|
|
70803
71152
|
);
|
|
70804
71153
|
}
|
|
70805
|
-
const resolved =
|
|
70806
|
-
const expected =
|
|
71154
|
+
const resolved = path91.resolve(path91.dirname(shimPath), target);
|
|
71155
|
+
const expected = path91.join(
|
|
70807
71156
|
prefix,
|
|
70808
71157
|
"node_modules",
|
|
70809
71158
|
pkgName,
|
|
@@ -70845,7 +71194,7 @@ function checkNode(pkg) {
|
|
|
70845
71194
|
return OK(`node ${raw} (engines.node ${enginesNode ?? ">= 20.0.0"})`);
|
|
70846
71195
|
}
|
|
70847
71196
|
function checkBundle() {
|
|
70848
|
-
const bundle =
|
|
71197
|
+
const bundle = path91.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
70849
71198
|
if (!existsSync56(bundle)) {
|
|
70850
71199
|
return FAIL(
|
|
70851
71200
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -70866,7 +71215,7 @@ function checkRuntimeDeps() {
|
|
|
70866
71215
|
const missing = [];
|
|
70867
71216
|
for (const dep of required2) {
|
|
70868
71217
|
try {
|
|
70869
|
-
const localReq = createRequire3(
|
|
71218
|
+
const localReq = createRequire3(path91.join(packageRoot, "package.json"));
|
|
70870
71219
|
localReq.resolve(dep);
|
|
70871
71220
|
} catch {
|
|
70872
71221
|
missing.push(dep);
|
|
@@ -71066,7 +71415,7 @@ var init_doctor = __esm({
|
|
|
71066
71415
|
init_metrics3();
|
|
71067
71416
|
init_contextGrowthSummary();
|
|
71068
71417
|
require3 = createRequire3(import.meta.url);
|
|
71069
|
-
__dirname3 =
|
|
71418
|
+
__dirname3 = path91.dirname(fileURLToPath3(import.meta.url));
|
|
71070
71419
|
packageRoot = findPackageRoot(__dirname3);
|
|
71071
71420
|
OK = (message) => ({
|
|
71072
71421
|
ok: true,
|
|
@@ -71250,7 +71599,7 @@ __export(inspectSession_exports, {
|
|
|
71250
71599
|
renderInspectReport: () => renderInspectReport,
|
|
71251
71600
|
runInspectSession: () => runInspectSession
|
|
71252
71601
|
});
|
|
71253
|
-
import
|
|
71602
|
+
import path92 from "node:path";
|
|
71254
71603
|
import { existsSync as existsSync57 } from "node:fs";
|
|
71255
71604
|
function formatLimit(limit) {
|
|
71256
71605
|
return `${Math.round(limit / 1e3)}k`;
|
|
@@ -71284,8 +71633,8 @@ function renderInspectReport(state3) {
|
|
|
71284
71633
|
}
|
|
71285
71634
|
async function runInspectSession(opts) {
|
|
71286
71635
|
const sessionsDir = resolveSessionsDir({ workspaceRoot: opts.cwd ?? process.cwd() });
|
|
71287
|
-
const sessionDir =
|
|
71288
|
-
const eventsPath =
|
|
71636
|
+
const sessionDir = path92.join(sessionsDir, opts.sessionId);
|
|
71637
|
+
const eventsPath = path92.join(sessionDir, "events.jsonl");
|
|
71289
71638
|
if (!existsSync57(sessionDir)) {
|
|
71290
71639
|
console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
|
|
71291
71640
|
return 1;
|
|
@@ -71319,15 +71668,15 @@ __export(inspect_exports, {
|
|
|
71319
71668
|
collectInspectReport: () => collectInspectReport,
|
|
71320
71669
|
runInspect: () => runInspect
|
|
71321
71670
|
});
|
|
71322
|
-
import
|
|
71671
|
+
import path93 from "node:path";
|
|
71323
71672
|
import { existsSync as existsSync58, readFileSync as readFileSync43, readdirSync as readdirSync12 } from "node:fs";
|
|
71324
71673
|
import { homedir as homedir17 } from "node:os";
|
|
71325
71674
|
async function collectInspectReport(cwd = process.cwd()) {
|
|
71326
71675
|
ensureBuiltinSkillsLoadedSync();
|
|
71327
71676
|
const snap = listSkillsSnapshot(cwd);
|
|
71328
71677
|
const mcp = listMcpServers(cwd);
|
|
71329
|
-
const userMcpPath =
|
|
71330
|
-
const projectMcpPath =
|
|
71678
|
+
const userMcpPath = path93.join(homedir17(), ".zelari-code", "mcp.json");
|
|
71679
|
+
const projectMcpPath = path93.join(cwd, ".zelari", "mcp.json");
|
|
71331
71680
|
const globalHooks = globalHooksDir();
|
|
71332
71681
|
const projectHooks = projectHooksDir(cwd);
|
|
71333
71682
|
const projectTrusted = isFolderTrusted(cwd);
|
|
@@ -71355,9 +71704,9 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
71355
71704
|
configSources: [
|
|
71356
71705
|
{ path: userMcpPath, exists: existsSync58(userMcpPath) },
|
|
71357
71706
|
{ path: projectMcpPath, exists: existsSync58(projectMcpPath) },
|
|
71358
|
-
{ path:
|
|
71359
|
-
{ path:
|
|
71360
|
-
{ path:
|
|
71707
|
+
{ path: path93.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync58(path93.join(homedir17(), ".zelari-code", "provider.json")) },
|
|
71708
|
+
{ path: path93.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync58(path93.join(cwd, ".zelari", "AGENTS.md")) },
|
|
71709
|
+
{ path: path93.join(cwd, "AGENTS.md"), exists: existsSync58(path93.join(cwd, "AGENTS.md")) }
|
|
71361
71710
|
],
|
|
71362
71711
|
skills: {
|
|
71363
71712
|
total: snap.skills.length,
|
|
@@ -71397,8 +71746,8 @@ function listJsonFiles(dir) {
|
|
|
71397
71746
|
}
|
|
71398
71747
|
function findAgentsMd(cwd) {
|
|
71399
71748
|
const candidates = [
|
|
71400
|
-
|
|
71401
|
-
|
|
71749
|
+
path93.join(cwd, "AGENTS.md"),
|
|
71750
|
+
path93.join(cwd, ".zelari", "AGENTS.md")
|
|
71402
71751
|
];
|
|
71403
71752
|
const found = [];
|
|
71404
71753
|
for (const c of candidates) {
|
|
@@ -74970,7 +75319,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
74970
75319
|
"# Work Phase: PLAN",
|
|
74971
75320
|
"You are in PLAN mode. Explore and design only.",
|
|
74972
75321
|
"- Do NOT implement production code or run destructive shell commands.",
|
|
74973
|
-
"- write_file /
|
|
75322
|
+
"- write_file / edit / bash are unavailable.",
|
|
74974
75323
|
"- inspect_command IS available: allowlisted read-only inspector (no shell). Use it for git_status/git_log/git_diff/git_show/git_branch_current/git_ls_files/typecheck/node_version/npm_ls/npm_outdated/npm_view - it turns claims into execution-verified observations.",
|
|
74975
75324
|
"- OBSERVATION INTEGRITY: negative evidence is valid only from a completed observation. Never conclude that code/symbols/files do not exist from degraded results, zero files examined, or unavailable backends (grep_content SEARCH_EMPTY_SCOPE, ast/LSP degraded status, inspect_command unsupported shapes).",
|
|
74976
75325
|
"- Produce a clear plan, ask clarifying questions (---QUESTION---), use workspace plan tools when relevant.",
|
|
@@ -74979,7 +75328,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
74979
75328
|
"# Work Phase: BUILD",
|
|
74980
75329
|
"Implement on disk. Prefer acting over describing.",
|
|
74981
75330
|
"- Prior plan/synthesis text is a SPEC to apply \u2014 not proof files already changed.",
|
|
74982
|
-
"- You MUST use write_file/
|
|
75331
|
+
"- You MUST use write_file/edit for every file you change before claiming done.",
|
|
74983
75332
|
"- After read_file: if the planned change is missing, WRITE it \u2014 do not stop at analysis.",
|
|
74984
75333
|
"- Never claim already-implemented based only on reading a plan or skimming code.",
|
|
74985
75334
|
hasPlan ? "- A draft plan exists under .zelari/ \u2014 implement grounded tasks and update statuses; do not treat design docs as shipped code." : ""
|
|
@@ -75965,7 +76314,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
75965
76314
|
streamMemberId = null;
|
|
75966
76315
|
membersCompleted++;
|
|
75967
76316
|
} else if (event.type === "tool_execution_start") {
|
|
75968
|
-
if (event.toolName === "write_file" || event.toolName === "edit_file") {
|
|
76317
|
+
if (event.toolName === "write_file" || event.toolName === "edit_file" || event.toolName === "edit") {
|
|
75969
76318
|
luciferWriteCount++;
|
|
75970
76319
|
}
|
|
75971
76320
|
if (streamContent) {
|
|
@@ -77590,12 +77939,12 @@ function handleCacheStats(ctx) {
|
|
|
77590
77939
|
// src/cli/slashHandlers/memory.ts
|
|
77591
77940
|
init_messageHelpers();
|
|
77592
77941
|
init_serviceFactory();
|
|
77593
|
-
import { promises as
|
|
77594
|
-
import * as
|
|
77942
|
+
import { promises as fs35 } from "node:fs";
|
|
77943
|
+
import * as path73 from "node:path";
|
|
77595
77944
|
|
|
77596
77945
|
// src/cli/memory/promotion.ts
|
|
77597
|
-
import { promises as
|
|
77598
|
-
import * as
|
|
77946
|
+
import { promises as fs34 } from "node:fs";
|
|
77947
|
+
import * as path72 from "node:path";
|
|
77599
77948
|
var START = "<!-- zelari:memory-promotions:start -->";
|
|
77600
77949
|
var END = "<!-- zelari:memory-promotions:end -->";
|
|
77601
77950
|
var DURABLE_KINDS = /* @__PURE__ */ new Set(["fact", "decision", "constraint", "preference", "procedure"]);
|
|
@@ -77606,22 +77955,22 @@ function lineFor(node) {
|
|
|
77606
77955
|
}
|
|
77607
77956
|
async function promoteMemoryToAgentsMd(projectRoot, node) {
|
|
77608
77957
|
if (node.status !== "active") {
|
|
77609
|
-
return { added: false, path:
|
|
77958
|
+
return { added: false, path: path72.join(projectRoot, "AGENTS.md"), reason: `memory is ${node.status}` };
|
|
77610
77959
|
}
|
|
77611
77960
|
if (!DURABLE_KINDS.has(node.kind)) {
|
|
77612
|
-
return { added: false, path:
|
|
77961
|
+
return { added: false, path: path72.join(projectRoot, "AGENTS.md"), reason: `${node.kind} is not a durable instruction kind` };
|
|
77613
77962
|
}
|
|
77614
|
-
const root = await
|
|
77615
|
-
const target =
|
|
77963
|
+
const root = await fs34.realpath(projectRoot).catch(() => path72.resolve(projectRoot));
|
|
77964
|
+
const target = path72.join(root, "AGENTS.md");
|
|
77616
77965
|
try {
|
|
77617
|
-
const stat7 = await
|
|
77966
|
+
const stat7 = await fs34.lstat(target);
|
|
77618
77967
|
if (stat7.isSymbolicLink() || !stat7.isFile()) throw new Error("AGENTS.md must be a regular project file.");
|
|
77619
77968
|
} catch (error51) {
|
|
77620
77969
|
if (error51.code !== "ENOENT") throw error51;
|
|
77621
77970
|
}
|
|
77622
77971
|
let current = "";
|
|
77623
77972
|
try {
|
|
77624
|
-
current = await
|
|
77973
|
+
current = await fs34.readFile(target, "utf8");
|
|
77625
77974
|
} catch (error51) {
|
|
77626
77975
|
if (error51.code !== "ENOENT") throw error51;
|
|
77627
77976
|
}
|
|
@@ -77644,11 +77993,11 @@ ${END}
|
|
|
77644
77993
|
`;
|
|
77645
77994
|
}
|
|
77646
77995
|
const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
77647
|
-
await
|
|
77996
|
+
await fs34.writeFile(temporary, updated, { encoding: "utf8", flag: "wx" });
|
|
77648
77997
|
try {
|
|
77649
|
-
await
|
|
77998
|
+
await fs34.rename(temporary, target);
|
|
77650
77999
|
} catch (error51) {
|
|
77651
|
-
await
|
|
78000
|
+
await fs34.unlink(temporary).catch(() => void 0);
|
|
77652
78001
|
throw error51;
|
|
77653
78002
|
}
|
|
77654
78003
|
return { added: true, path: target };
|
|
@@ -77677,24 +78026,24 @@ function sourceLine(source2) {
|
|
|
77677
78026
|
return entries.length ? entries.map(([key, value]) => `${key}=${value}`).join(" \xB7 ") : "unknown";
|
|
77678
78027
|
}
|
|
77679
78028
|
function isInside(root, target) {
|
|
77680
|
-
const relative6 =
|
|
77681
|
-
return relative6 === "" || !relative6.startsWith("..") && !
|
|
78029
|
+
const relative6 = path73.relative(root, target);
|
|
78030
|
+
return relative6 === "" || !relative6.startsWith("..") && !path73.isAbsolute(relative6);
|
|
77682
78031
|
}
|
|
77683
78032
|
async function safeExportPath(cwd, requested) {
|
|
77684
|
-
const lexicalRoot =
|
|
77685
|
-
const root = await
|
|
77686
|
-
const fallback =
|
|
77687
|
-
const target = requested?.trim() ?
|
|
78033
|
+
const lexicalRoot = path73.resolve(cwd);
|
|
78034
|
+
const root = await fs35.realpath(lexicalRoot).catch(() => lexicalRoot);
|
|
78035
|
+
const fallback = path73.join(root, ".zelari", "memory", `export-${Date.now()}.json`);
|
|
78036
|
+
const target = requested?.trim() ? path73.resolve(root, requested.trim()) : fallback;
|
|
77688
78037
|
if (!isInside(root, target)) {
|
|
77689
78038
|
throw new Error("Export path must stay inside the active project.");
|
|
77690
78039
|
}
|
|
77691
|
-
const parent =
|
|
77692
|
-
const relativeParent =
|
|
78040
|
+
const parent = path73.dirname(target);
|
|
78041
|
+
const relativeParent = path73.relative(root, parent);
|
|
77693
78042
|
let cursor = root;
|
|
77694
|
-
for (const segment of relativeParent.split(
|
|
77695
|
-
cursor =
|
|
78043
|
+
for (const segment of relativeParent.split(path73.sep).filter(Boolean)) {
|
|
78044
|
+
cursor = path73.join(cursor, segment);
|
|
77696
78045
|
try {
|
|
77697
|
-
const stat7 = await
|
|
78046
|
+
const stat7 = await fs35.lstat(cursor);
|
|
77698
78047
|
if (stat7.isSymbolicLink()) {
|
|
77699
78048
|
throw new Error("Export path must not traverse a symbolic link.");
|
|
77700
78049
|
}
|
|
@@ -77704,7 +78053,7 @@ async function safeExportPath(cwd, requested) {
|
|
|
77704
78053
|
}
|
|
77705
78054
|
}
|
|
77706
78055
|
try {
|
|
77707
|
-
if ((await
|
|
78056
|
+
if ((await fs35.lstat(target)).isSymbolicLink()) {
|
|
77708
78057
|
throw new Error("Export target must not be a symbolic link.");
|
|
77709
78058
|
}
|
|
77710
78059
|
} catch (error51) {
|
|
@@ -77864,13 +78213,13 @@ ${message}` : message
|
|
|
77864
78213
|
}
|
|
77865
78214
|
case "export": {
|
|
77866
78215
|
const target = await safeExportPath(ctx.cwd, args.join(" ").trim() || void 0);
|
|
77867
|
-
await
|
|
77868
|
-
const root = await
|
|
77869
|
-
const realParent = await
|
|
78216
|
+
await fs35.mkdir(path73.dirname(target), { recursive: true });
|
|
78217
|
+
const root = await fs35.realpath(ctx.cwd).catch(() => path73.resolve(ctx.cwd));
|
|
78218
|
+
const realParent = await fs35.realpath(path73.dirname(target));
|
|
77870
78219
|
if (!isInside(root, realParent)) {
|
|
77871
78220
|
throw new Error("Export path resolves outside the active project.");
|
|
77872
78221
|
}
|
|
77873
|
-
await
|
|
78222
|
+
await fs35.writeFile(target, JSON.stringify(await memory.export(), null, 2) + "\n", "utf8");
|
|
77874
78223
|
emit(`[memory] export written to ${target}`);
|
|
77875
78224
|
return;
|
|
77876
78225
|
}
|
|
@@ -78064,13 +78413,13 @@ ${digest}
|
|
|
78064
78413
|
init_auditLogger();
|
|
78065
78414
|
init_toolRegistry();
|
|
78066
78415
|
init_messageHelpers();
|
|
78067
|
-
import { promises as
|
|
78416
|
+
import { promises as fs37 } from "node:fs";
|
|
78068
78417
|
|
|
78069
78418
|
// src/cli/tools/krakenCsvFanout.ts
|
|
78070
78419
|
init_zod();
|
|
78071
78420
|
init_taskTool();
|
|
78072
|
-
import { promises as
|
|
78073
|
-
import
|
|
78421
|
+
import { promises as fs36 } from "node:fs";
|
|
78422
|
+
import path74 from "node:path";
|
|
78074
78423
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
78075
78424
|
var CsvFanoutArgsSchema = external_exports.object({
|
|
78076
78425
|
csv_path: external_exports.string().min(1),
|
|
@@ -78091,7 +78440,7 @@ var CsvFanoutArgsSchema = external_exports.object({
|
|
|
78091
78440
|
max_runtime_seconds: external_exports.number().int().positive().optional()
|
|
78092
78441
|
});
|
|
78093
78442
|
async function readCsv(filePath) {
|
|
78094
|
-
const text = await
|
|
78443
|
+
const text = await fs36.readFile(filePath, "utf8");
|
|
78095
78444
|
return parseCsv(text);
|
|
78096
78445
|
}
|
|
78097
78446
|
function parseCsv(text) {
|
|
@@ -78164,8 +78513,8 @@ function resolveMaxConcurrency(env = process.env) {
|
|
|
78164
78513
|
}
|
|
78165
78514
|
async function runCsvFanout(args, deps, opts) {
|
|
78166
78515
|
const start = Date.now();
|
|
78167
|
-
const absCsv =
|
|
78168
|
-
const absOut =
|
|
78516
|
+
const absCsv = path74.isAbsolute(args.csv_path) ? args.csv_path : path74.join(opts.parentCwd, args.csv_path);
|
|
78517
|
+
const absOut = path74.isAbsolute(args.output_csv_path) ? args.output_csv_path : path74.join(opts.parentCwd, args.output_csv_path);
|
|
78169
78518
|
const { headers: headers2, rows } = await readCsv(absCsv);
|
|
78170
78519
|
if (headers2.length === 0) {
|
|
78171
78520
|
throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
|
|
@@ -78221,7 +78570,7 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
78221
78570
|
errored += 1;
|
|
78222
78571
|
errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
|
|
78223
78572
|
}
|
|
78224
|
-
await
|
|
78573
|
+
await fs36.mkdir(path74.dirname(absOut), { recursive: true });
|
|
78225
78574
|
await queueWrite(serializeCsv(outHeaders, outputRecords));
|
|
78226
78575
|
}
|
|
78227
78576
|
}
|
|
@@ -78243,8 +78592,8 @@ async function runCsvFanout(args, deps, opts) {
|
|
|
78243
78592
|
}
|
|
78244
78593
|
async function atomicWrite(file2, contents) {
|
|
78245
78594
|
const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes6(6).toString("hex")}.tmp`;
|
|
78246
|
-
await
|
|
78247
|
-
await
|
|
78595
|
+
await fs36.writeFile(tmp, contents, "utf8");
|
|
78596
|
+
await fs36.rename(tmp, file2);
|
|
78248
78597
|
}
|
|
78249
78598
|
|
|
78250
78599
|
// src/cli/slashHandlers/krakenFanout.ts
|
|
@@ -78342,7 +78691,7 @@ async function handleKrakenFanout(ctx, raw) {
|
|
|
78342
78691
|
}
|
|
78343
78692
|
const absCsv = isAbsolute3(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
|
|
78344
78693
|
try {
|
|
78345
|
-
await
|
|
78694
|
+
await fs37.access(absCsv);
|
|
78346
78695
|
} catch {
|
|
78347
78696
|
appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
|
|
78348
78697
|
return;
|
|
@@ -78415,8 +78764,8 @@ function splitArgs(s) {
|
|
|
78415
78764
|
|
|
78416
78765
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
78417
78766
|
init_messageHelpers();
|
|
78418
|
-
import { promises as
|
|
78419
|
-
import
|
|
78767
|
+
import { promises as fs38 } from "node:fs";
|
|
78768
|
+
import path75 from "node:path";
|
|
78420
78769
|
|
|
78421
78770
|
// src/cli/kraken/workbenchView.ts
|
|
78422
78771
|
var EMPTY = {
|
|
@@ -78533,15 +78882,15 @@ function formatWorkbenchForTerminal(p3) {
|
|
|
78533
78882
|
|
|
78534
78883
|
// src/cli/slashHandlers/krakenWorkbench.ts
|
|
78535
78884
|
async function handleKrakenWorkbench(ctx) {
|
|
78536
|
-
const dir =
|
|
78885
|
+
const dir = path75.join(ctx.cwd, ".zelari", "radio");
|
|
78537
78886
|
let latest = null;
|
|
78538
78887
|
let latestMtime = 0;
|
|
78539
78888
|
try {
|
|
78540
|
-
const files = await
|
|
78889
|
+
const files = await fs38.readdir(dir);
|
|
78541
78890
|
for (const f of files) {
|
|
78542
78891
|
if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
|
|
78543
|
-
const full =
|
|
78544
|
-
const stat7 = await
|
|
78892
|
+
const full = path75.join(dir, f);
|
|
78893
|
+
const stat7 = await fs38.stat(full);
|
|
78545
78894
|
if (stat7.mtimeMs > latestMtime) {
|
|
78546
78895
|
latestMtime = stat7.mtimeMs;
|
|
78547
78896
|
latest = full;
|
|
@@ -78553,14 +78902,14 @@ async function handleKrakenWorkbench(ctx) {
|
|
|
78553
78902
|
appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
|
|
78554
78903
|
return;
|
|
78555
78904
|
}
|
|
78556
|
-
const content = await
|
|
78905
|
+
const content = await fs38.readFile(latest, "utf8");
|
|
78557
78906
|
const parsed = parseWorkbench(content);
|
|
78558
78907
|
const rendered = formatWorkbenchForTerminal(parsed);
|
|
78559
78908
|
if (!rendered.trim()) {
|
|
78560
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
78909
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path75.basename(latest)}: (no nodes / no events yet)`);
|
|
78561
78910
|
return;
|
|
78562
78911
|
}
|
|
78563
|
-
appendSystem(ctx.setMessages, `[kraken workbench] ${
|
|
78912
|
+
appendSystem(ctx.setMessages, `[kraken workbench] ${path75.basename(latest)}:
|
|
78564
78913
|
${rendered}`);
|
|
78565
78914
|
}
|
|
78566
78915
|
|
|
@@ -78866,17 +79215,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
78866
79215
|
|
|
78867
79216
|
// src/cli/slashHandlers/promoteMember.ts
|
|
78868
79217
|
init_messageHelpers();
|
|
78869
|
-
import { promises as
|
|
78870
|
-
import
|
|
79218
|
+
import { promises as fs39 } from "node:fs";
|
|
79219
|
+
import path78 from "node:path";
|
|
78871
79220
|
import os12 from "node:os";
|
|
78872
79221
|
async function handlePromoteMember(ctx, memberId) {
|
|
78873
79222
|
try {
|
|
78874
79223
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
78875
79224
|
const { skill, markdown } = promoteMember2(memberId);
|
|
78876
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
78877
|
-
await
|
|
78878
|
-
const filePath =
|
|
78879
|
-
await
|
|
79225
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path78.join(os12.homedir(), ".tmp", "zelari-code", "skills");
|
|
79226
|
+
await fs39.mkdir(skillDir, { recursive: true });
|
|
79227
|
+
const filePath = path78.join(skillDir, `${skill.id}.md`);
|
|
79228
|
+
await fs39.writeFile(filePath, markdown, "utf8");
|
|
78880
79229
|
appendSystem(
|
|
78881
79230
|
ctx.setMessages,
|
|
78882
79231
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -78892,25 +79241,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
78892
79241
|
}
|
|
78893
79242
|
|
|
78894
79243
|
// src/cli/branchManager.ts
|
|
78895
|
-
import { promises as
|
|
78896
|
-
import
|
|
79244
|
+
import { promises as fs40, existsSync as existsSync49, readFileSync as readFileSync37, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
|
|
79245
|
+
import path79 from "node:path";
|
|
78897
79246
|
import os13 from "node:os";
|
|
78898
79247
|
var META_FILENAME = "meta.json";
|
|
78899
79248
|
var SESSIONS_SUBDIR = "sessions";
|
|
78900
79249
|
function getBranchesBaseDir() {
|
|
78901
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
79250
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path79.join(os13.homedir(), ".tmp", "zelari-code", "branches");
|
|
78902
79251
|
}
|
|
78903
79252
|
function getSessionsBaseDir() {
|
|
78904
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
79253
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path79.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
|
|
78905
79254
|
}
|
|
78906
79255
|
function branchPathFor(name, baseDir) {
|
|
78907
|
-
return
|
|
79256
|
+
return path79.join(baseDir, name);
|
|
78908
79257
|
}
|
|
78909
79258
|
function metaPathFor(name, baseDir) {
|
|
78910
|
-
return
|
|
79259
|
+
return path79.join(baseDir, name, META_FILENAME);
|
|
78911
79260
|
}
|
|
78912
79261
|
function sessionsPathFor(name, baseDir) {
|
|
78913
|
-
return
|
|
79262
|
+
return path79.join(baseDir, name, SESSIONS_SUBDIR);
|
|
78914
79263
|
}
|
|
78915
79264
|
function readBranchMeta(name, baseDir) {
|
|
78916
79265
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -78935,13 +79284,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
78935
79284
|
}
|
|
78936
79285
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
78937
79286
|
const metaPath = metaPathFor(name, baseDir);
|
|
78938
|
-
mkdirSync19(
|
|
79287
|
+
mkdirSync19(path79.dirname(metaPath), { recursive: true });
|
|
78939
79288
|
writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
78940
79289
|
}
|
|
78941
79290
|
async function countSessions(name, baseDir) {
|
|
78942
79291
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
78943
79292
|
try {
|
|
78944
|
-
const entries = await
|
|
79293
|
+
const entries = await fs40.readdir(sessionsPath);
|
|
78945
79294
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
78946
79295
|
} catch (err) {
|
|
78947
79296
|
if (err.code === "ENOENT") return 0;
|
|
@@ -78986,15 +79335,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
78986
79335
|
if (branchExists(name, baseDir)) {
|
|
78987
79336
|
throw new BranchAlreadyExistsError(name);
|
|
78988
79337
|
}
|
|
78989
|
-
const sourcePath =
|
|
79338
|
+
const sourcePath = path79.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
78990
79339
|
if (!existsSync49(sourcePath)) {
|
|
78991
79340
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
78992
79341
|
}
|
|
78993
79342
|
const branchPath = branchPathFor(name, baseDir);
|
|
78994
79343
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
78995
79344
|
mkdirSync19(branchSessionsPath, { recursive: true });
|
|
78996
|
-
const destPath =
|
|
78997
|
-
await
|
|
79345
|
+
const destPath = path79.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
79346
|
+
await fs40.copyFile(sourcePath, destPath);
|
|
78998
79347
|
const meta3 = {
|
|
78999
79348
|
name,
|
|
79000
79349
|
createdAt: Date.now(),
|
|
@@ -79012,7 +79361,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
79012
79361
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
79013
79362
|
let entries;
|
|
79014
79363
|
try {
|
|
79015
|
-
entries = await
|
|
79364
|
+
entries = await fs40.readdir(baseDir);
|
|
79016
79365
|
} catch (err) {
|
|
79017
79366
|
if (err.code === "ENOENT") return [];
|
|
79018
79367
|
throw err;
|
|
@@ -79096,26 +79445,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
79096
79445
|
|
|
79097
79446
|
// src/cli/slashHandlers/workspace.ts
|
|
79098
79447
|
init_messageHelpers();
|
|
79099
|
-
import { promises as
|
|
79100
|
-
import
|
|
79448
|
+
import { promises as fs41 } from "node:fs";
|
|
79449
|
+
import path80 from "node:path";
|
|
79101
79450
|
async function handleWorkspaceShow(ctx, what) {
|
|
79102
79451
|
try {
|
|
79103
|
-
const zelari =
|
|
79452
|
+
const zelari = path80.join(process.cwd(), ".zelari");
|
|
79104
79453
|
let content;
|
|
79105
79454
|
switch (what) {
|
|
79106
79455
|
case "plan": {
|
|
79107
|
-
const planPath =
|
|
79456
|
+
const planPath = path80.join(zelari, "plan.md");
|
|
79108
79457
|
try {
|
|
79109
|
-
content = await
|
|
79458
|
+
content = await fs41.readFile(planPath, "utf-8");
|
|
79110
79459
|
} catch {
|
|
79111
79460
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
79112
79461
|
}
|
|
79113
79462
|
break;
|
|
79114
79463
|
}
|
|
79115
79464
|
case "decisions": {
|
|
79116
|
-
const decisionsDir =
|
|
79465
|
+
const decisionsDir = path80.join(zelari, "decisions");
|
|
79117
79466
|
try {
|
|
79118
|
-
const files = (await
|
|
79467
|
+
const files = (await fs41.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
79119
79468
|
if (files.length === 0) {
|
|
79120
79469
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
79121
79470
|
} else {
|
|
@@ -79123,7 +79472,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
79123
79472
|
`];
|
|
79124
79473
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
79125
79474
|
for (const f of files) {
|
|
79126
|
-
const raw = await
|
|
79475
|
+
const raw = await fs41.readFile(path80.join(decisionsDir, f), "utf-8");
|
|
79127
79476
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
79128
79477
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
79129
79478
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -79136,27 +79485,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
79136
79485
|
break;
|
|
79137
79486
|
}
|
|
79138
79487
|
case "risks": {
|
|
79139
|
-
const risksPath =
|
|
79488
|
+
const risksPath = path80.join(zelari, "risks.md");
|
|
79140
79489
|
try {
|
|
79141
|
-
content = await
|
|
79490
|
+
content = await fs41.readFile(risksPath, "utf-8");
|
|
79142
79491
|
} catch {
|
|
79143
79492
|
content = "(no risks.md yet)";
|
|
79144
79493
|
}
|
|
79145
79494
|
break;
|
|
79146
79495
|
}
|
|
79147
79496
|
case "agents": {
|
|
79148
|
-
const agentsPath =
|
|
79497
|
+
const agentsPath = path80.join(process.cwd(), "AGENTS.MD");
|
|
79149
79498
|
try {
|
|
79150
|
-
content = await
|
|
79499
|
+
content = await fs41.readFile(agentsPath, "utf-8");
|
|
79151
79500
|
} catch {
|
|
79152
79501
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
79153
79502
|
}
|
|
79154
79503
|
break;
|
|
79155
79504
|
}
|
|
79156
79505
|
case "docs": {
|
|
79157
|
-
const docsDir =
|
|
79506
|
+
const docsDir = path80.join(zelari, "docs");
|
|
79158
79507
|
try {
|
|
79159
|
-
const files = (await
|
|
79508
|
+
const files = (await fs41.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
79160
79509
|
content = files.length ? `# Docs (${files.length})
|
|
79161
79510
|
|
|
79162
79511
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -79196,8 +79545,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
79196
79545
|
return;
|
|
79197
79546
|
}
|
|
79198
79547
|
try {
|
|
79199
|
-
const target =
|
|
79200
|
-
await
|
|
79548
|
+
const target = path80.join(process.cwd(), ".zelari");
|
|
79549
|
+
await fs41.rm(target, { recursive: true, force: true });
|
|
79201
79550
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
79202
79551
|
} catch (err) {
|
|
79203
79552
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -79208,16 +79557,16 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
79208
79557
|
init_provider2();
|
|
79209
79558
|
|
|
79210
79559
|
// src/cli/slashHandlers/skills.ts
|
|
79211
|
-
import
|
|
79560
|
+
import path81 from "node:path";
|
|
79212
79561
|
import os14 from "node:os";
|
|
79213
79562
|
|
|
79214
79563
|
// src/cli/skillHistory.ts
|
|
79215
|
-
import { promises as
|
|
79564
|
+
import { promises as fs42, existsSync as existsSync50, statSync as statSync8, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
|
|
79216
79565
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
79217
79566
|
async function readSkillHistory(file2) {
|
|
79218
79567
|
let raw = "";
|
|
79219
79568
|
try {
|
|
79220
|
-
raw = await
|
|
79569
|
+
raw = await fs42.readFile(file2, "utf-8");
|
|
79221
79570
|
} catch {
|
|
79222
79571
|
return [];
|
|
79223
79572
|
}
|
|
@@ -79337,7 +79686,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
|
79337
79686
|
});
|
|
79338
79687
|
}
|
|
79339
79688
|
async function handleSkillStats(ctx, skillId) {
|
|
79340
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
79689
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path81.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
79341
79690
|
try {
|
|
79342
79691
|
const records = await readSkillHistory(historyFile);
|
|
79343
79692
|
const stats = getSkillStats(records, skillId);
|
|
@@ -79353,7 +79702,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
79353
79702
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
79354
79703
|
return;
|
|
79355
79704
|
}
|
|
79356
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
79705
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path81.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
79357
79706
|
try {
|
|
79358
79707
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
79359
79708
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -81054,8 +81403,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
|
81054
81403
|
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
81055
81404
|
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
81056
81405
|
try {
|
|
81057
|
-
const
|
|
81058
|
-
name =
|
|
81406
|
+
const path94 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
81407
|
+
name = path94 && /^[a-z0-9]/.test(path94) ? path94 : "imported-skill";
|
|
81059
81408
|
} catch {
|
|
81060
81409
|
name = "imported-skill";
|
|
81061
81410
|
}
|