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.
Files changed (38) hide show
  1. package/dist/cli/ast/tools.js +1 -1
  2. package/dist/cli/ast/tools.js.map +1 -1
  3. package/dist/cli/budget/budgetRuntime.js +4 -1
  4. package/dist/cli/budget/budgetRuntime.js.map +1 -1
  5. package/dist/cli/gauntlet/policy.js +2 -0
  6. package/dist/cli/gauntlet/policy.js.map +1 -1
  7. package/dist/cli/headless/runOneTurn.js +2 -2
  8. package/dist/cli/headless/runOneTurn.js.map +1 -1
  9. package/dist/cli/hooks/conversationContext.js +1 -1
  10. package/dist/cli/hooks/conversationContext.js.map +1 -1
  11. package/dist/cli/hooks/useChatTurn.js +4 -4
  12. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  13. package/dist/cli/kraken/delegationPolicy.js +1 -1
  14. package/dist/cli/kraken/turnRuntime.js +3 -0
  15. package/dist/cli/kraken/turnRuntime.js.map +1 -1
  16. package/dist/cli/lsp/tools.js +1 -1
  17. package/dist/cli/lsp/tools.js.map +1 -1
  18. package/dist/cli/main.bundled.js +844 -495
  19. package/dist/cli/main.bundled.js.map +4 -4
  20. package/dist/cli/missionSlice.js +4 -4
  21. package/dist/cli/missionSlice.js.map +1 -1
  22. package/dist/cli/phase.js +4 -1
  23. package/dist/cli/phase.js.map +1 -1
  24. package/dist/cli/runHeadless.js +1 -1
  25. package/dist/cli/runHeadless.js.map +1 -1
  26. package/dist/cli/safety/astGate.js +190 -0
  27. package/dist/cli/safety/astGate.js.map +1 -0
  28. package/dist/cli/safety/resourceClaims.js +3 -1
  29. package/dist/cli/safety/resourceClaims.js.map +1 -1
  30. package/dist/cli/sessionSpine.js +45 -0
  31. package/dist/cli/sessionSpine.js.map +1 -1
  32. package/dist/cli/spineFileEvents.js +124 -0
  33. package/dist/cli/spineFileEvents.js.map +1 -0
  34. package/dist/cli/toolRegistry.js +24 -14
  35. package/dist/cli/toolRegistry.js.map +1 -1
  36. package/dist/cli/zelariMission.js +1 -1
  37. package/dist/cli/zelariMission.js.map +1 -1
  38. package/package.json +2 -2
@@ -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, path93) {
3292
- if (!path93)
3291
+ function getElementAtPath(obj, path94) {
3292
+ if (!path94)
3293
3293
  return obj;
3294
- return path93.reduce((acc, key) => acc?.[key], obj);
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(path93, issues) {
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(path93);
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, path93 = []) => {
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 }, [...path93, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path94, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path93, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path93, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path93, ...issue2.path];
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, path93 = []) => {
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 }, [...path93, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path94, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path93, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path93, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path94, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path93, ...issue2.path];
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 path93 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path93) {
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 path93 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path93.length === 0) {
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 (path93[0] === defsKey) {
17432
- const key = path93[1];
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 path93 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
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: path93,
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 "${path93}".`;
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 path93 = join4(zelariRoot, "nfr-spec.json");
26424
- if (!existsSync7(path93))
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(path93, "utf8"));
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 path93 = join10(zelariRoot, LESSONS_FILE);
28812
+ const path94 = join10(zelariRoot, LESSONS_FILE);
28734
28813
  try {
28735
- const raw = readFileSync12(path93, "utf8");
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 path93 = join11(zelariRoot, LESSONS_FILE);
28837
- appendFileSync(path93, `${JSON.stringify(lesson)}
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 path93 = [];
30880
+ const path94 = [];
30802
30881
  color.set(start, GRAY);
30803
- path93.push(start);
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 = path93.indexOf(dep);
30817
- return [...path93.slice(at), dep];
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
- path93.push(dep);
30900
+ path94.push(dep);
30822
30901
  stack.push([dep, 0]);
30823
30902
  }
30824
30903
  } else {
30825
30904
  color.set(id3, BLACK);
30826
- path93.pop();
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 path93 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31747
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path93}`);
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 fs47 = options.fs ?? new NodeFsProvider(workspace);
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: fs47,
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(path93) {
34365
+ async function dirSize(path94) {
34280
34366
  let total = 0;
34281
34367
  let entries;
34282
34368
  try {
34283
- entries = await readdir(path93, { withFileTypes: true });
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(path93, entry.name);
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 path93 = join13(runsDir, entry.name);
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(path93, "manifest.json"), "utf8"));
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: path93, startedAt, endedAt, completed, bytes: await dirSize(path93) });
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, path93, sha256, content, extra = {}) {
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: path93, ...extra, ...digest ? { digest } : {} });
35187
+ const seq = await this.emitEvidence({ observation, path: path94, ...extra, ...digest ? { digest } : {} });
35102
35188
  return {
35103
35189
  tier: "fs-observation",
35104
- ref: path93,
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 fs47 = this.services.fs;
35113
- if (!fs47)
35198
+ const fs48 = this.services.fs;
35199
+ if (!fs48)
35114
35200
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
35115
- const exists = await fs47.exists(check2.path);
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 fs47.readFile(check2.path).catch(() => "");
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 fs47 = this.services.fs;
35128
- if (!fs47)
35213
+ const fs48 = this.services.fs;
35214
+ if (!fs48)
35129
35215
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
35130
- const exists = await fs47.exists(check2.path);
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 fs47 = this.services.fs;
35143
- if (!fs47)
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 fs47.readFile(check2.path);
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
- "edit_file",
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 path93 = join14(dir, file2);
38288
- writeFileSync11(path93, fullText, "utf8");
38289
- return path93;
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 path93 = spillToolOutput(text, { toolName: opts.toolName });
38336
- if (path93) {
38539
+ const path94 = spillToolOutput(text, { toolName: opts.toolName });
38540
+ if (path94) {
38337
38541
  const spillNote = `
38338
- \u2026 [full output spilled to: ${path93} \u2014 re-read with read_file if you need the complete text] \u2026`;
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(path93) {
46134
- if (!existsSync21(path93)) {
46135
- throw new Error(`File not found: ${path93}`);
46337
+ read(path94) {
46338
+ if (!existsSync21(path94)) {
46339
+ throw new Error(`File not found: ${path94}`);
46136
46340
  }
46137
- const md = readFileSync17(path93, "utf8");
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(path93) {
46142
- if (!existsSync21(path93)) return null;
46143
- return this.read(path93);
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(path93, meta3, body) {
46150
- mkdirSync10(dirname2(path93), { recursive: true });
46151
- const tmp = path93 + ".tmp-" + process.pid;
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, path93);
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 path93 = join18(rootDir, "plan-tasks", `${task.id}.md`);
46217
- mkdirSync11(dirname3(path93), { recursive: true });
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(path93, meta3, body);
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 edit_file once the scope looks right.",
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 edit_file it reliably (node-accurate) instead of guessing the surrounding text. Relative paths resolve against the working directory. TS/JS only.",
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 path93 = getSshSecretsPath();
49892
- if (!existsSync27(path93)) return {};
50095
+ const path94 = getSshSecretsPath();
50096
+ if (!existsSync27(path94)) return {};
49893
50097
  try {
49894
- return JSON.parse(readFileSync21(path93, "utf8"));
50098
+ return JSON.parse(readFileSync21(path94, "utf8"));
49895
50099
  } catch {
49896
50100
  return {};
49897
50101
  }
49898
50102
  }
49899
50103
  function writeSecrets(data) {
49900
- const path93 = getSshSecretsPath();
49901
- mkdirSync12(dirname4(path93), { recursive: true });
49902
- writeFileSync15(path93, `${JSON.stringify(data, null, 2)}
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(path93, 384);
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 path93 = getSshTargetsPath();
49935
- if (!existsSync27(path93)) return [];
50138
+ const path94 = getSshTargetsPath();
50139
+ if (!existsSync27(path94)) return [];
49936
50140
  try {
49937
- const parsed = JSON.parse(readFileSync21(path93, "utf8"));
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 path93 = getSshTargetsPath();
49953
- mkdirSync12(dirname4(path93), { recursive: true });
50156
+ const path94 = getSshTargetsPath();
50157
+ mkdirSync12(dirname4(path94), { recursive: true });
49954
50158
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
49955
50159
  writeFileSync15(
49956
- path93,
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 path93 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
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}${path93}${tags}${allow}`
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 fs23 } from "node:fs";
51583
- import path49 from "node:path";
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 = path49.isAbsolute(rawPath) ? rawPath : path49.join(ctx.cwd, rawPath);
52001
+ const abs = path50.isAbsolute(rawPath) ? rawPath : path50.join(ctx.cwd, rawPath);
51669
52002
  try {
51670
- const st = await fs23.stat(abs);
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 path50 from "node:path";
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
- wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId2, { verifyWrite: true })
52073
+ wrapWithAstGate(
52074
+ wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId2, { verifyWrite: true }),
52075
+ { root }
52076
+ )
51741
52077
  );
51742
- const safeEditFile = withDiag(
51743
- wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId2, { verifyWrite: true })
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(safeEditFile));
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, safeEditFile, safeApplyDiff] : [],
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(path50.extname(cleaned).toLowerCase())) return null;
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(path50.isAbsolute(cwd) ? cwd : path50.resolve(root, cwd));
52627
+ bases.unshift(path51.isAbsolute(cwd) ? cwd : path51.resolve(root, cwd));
52293
52628
  }
52294
52629
  for (const base2 of bases) {
52295
- const candidate = path50.isAbsolute(cleaned) ? path50.normalize(cleaned) : path50.resolve(base2, cleaned);
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 fs24, existsSync as existsSync30, statSync as statSync4, renameSync as renameSync3, appendFileSync as appendFileSync3, mkdirSync as mkdirSync14 } from "node:fs";
52601
- import path51 from "node:path";
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 fs24.readFile(file2, "utf-8");
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 ?? path51.join(os9.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
52651
- mkdirSync14(path51.dirname(this.file), { recursive: true });
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\` / \`edit_file\`.
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 path52 from "node:path";
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(path52.join(root, "package.json"), "utf-8"));
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(path52.join(root, marker))) return { pm, declaredToolchain: true };
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(path52.join(root, "package.json"))) return 0;
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 path53 from "node:path";
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(path53.join(root, marker))) best = score;
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(path53.join(root, name), "utf-8"));
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(path53.join(root, "tests"));
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 path54 from "node:path";
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(path54.join(root, "Cargo.toml")) ? 10 : 0;
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 path55 from "node:path";
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(path55.join(root, "go.mod")) ? 10 : 0;
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 path56 from "node:path";
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(path56.join(root, marker))) return true;
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(path56.join(root, wrapper))) {
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(path56.join(root, marker))) best = score;
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(path56.join(root, "pom.xml"))) {
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 path57 from "node:path";
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(path57.join(cwd, "package.json"), "utf-8");
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 path58 from "node:path";
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 = path58.dirname(target);
53947
- const tmp = path58.join(dir, `.${path58.basename(target)}.${randomBytes5(6).toString("hex")}.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 path59 from "node:path";
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 = path59.join(baseDir, ".zelari");
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 = path59.join(dir, "completion-proof.md");
54159
- const jsonPath = path59.join(dir, "completion-proof.json");
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 fs25 } from "node:fs";
54250
- import * as path60 from "node:path";
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 fs25.mkdir(path60.dirname(filePath), { recursive: true });
54599
+ await fs26.mkdir(path61.dirname(filePath), { recursive: true });
54256
54600
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
54257
- await fs25.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
54258
- await fs25.rename(tmp, filePath);
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 fs25.readFile(filePath, "utf8");
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 = path60.join(projectRoot, ".zelari", "state");
54317
- this.commitsDir = path60.join(this.stateDir, "commits");
54318
- this.artifactsDir = path60.join(this.stateDir, "artifacts");
54319
- this.headPath = path60.join(this.stateDir, "HEAD.json");
54320
- this.indexPath = path60.join(this.stateDir, "index.jsonl");
54321
- await fs25.mkdir(this.commitsDir, { recursive: true });
54322
- await fs25.mkdir(this.artifactsDir, { recursive: true });
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 = path60.join("artifacts", id3);
54334
- const artifactAbs = path60.join(this.artifactsDir, id3);
54335
- await fs25.mkdir(artifactAbs, { recursive: true });
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 fs25.writeFile(path60.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
54338
- await writeJsonAtomic(path60.join(artifactAbs, "discoveries.json"), discoveries);
54339
- await writeJsonAtomic(path60.join(artifactAbs, "verification.json"), input.verification);
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 ?? path60.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
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(path60.join(this.commitsDir, `${id3}.json`), meta3);
54702
+ await writeJsonAtomic(path61.join(this.commitsDir, `${id3}.json`), meta3);
54359
54703
  await writeJsonAtomic(this.headPath, { id: id3, updatedAt: meta3.createdAt });
54360
- await fs25.appendFile(this.indexPath, JSON.stringify({ id: id3, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
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(path60.join(this.commitsDir, `${id3}.json`));
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 fs25.readFile(this.indexPath, "utf8");
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(path60.join(this.commitsDir, `${meta3.id}.json`));
54752
+ const stored = await readJsonFile(path61.join(this.commitsDir, `${meta3.id}.json`));
54409
54753
  if (!stored?.artifactDir) return [];
54410
- const discPath = path60.join(this.stateDir, stored.artifactDir, "discoveries.json");
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 edit_file (and bash when needed) to apply every planned change.
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/edit_file stay DENIED.
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 path61 from "node:path";
55357
+ import path62 from "node:path";
55011
55358
  function resolveHeadlessCwd(opts) {
55012
55359
  const raw = typeof opts.cwd === "string" ? opts.cwd.trim() : "";
55013
- return path61.resolve(raw.length > 0 ? raw : process.cwd());
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 fs26 } from "node:fs";
56008
- import * as path62 from "node:path";
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 = path62.join(path62.dirname(backend.databasePath), "log.jsonl");
56373
+ const logPath = path63.join(path63.dirname(backend.databasePath), "log.jsonl");
56027
56374
  let raw;
56028
56375
  try {
56029
- raw = await fs26.readFile(logPath, "utf8");
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 path63 from "node:path";
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 = path63.dirname(fileURLToPath2(import.meta.url));
56219
- const direct = path63.join(here, "sqliteWorker.mjs");
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(path63.join(here, "memory", "sqliteWorker.mjs"));
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 fs27 } from "node:fs";
56510
- import * as path64 from "node:path";
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 fs27.realpath(projectRoot);
56917
+ resolved = await fs28.realpath(projectRoot);
56571
56918
  } catch {
56572
- resolved = path64.resolve(projectRoot);
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 (path64.basename(filename) !== filename || filename === "." || filename === "..") {
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 = path64.join(resolved, ".zelari");
56581
- const directory = path64.join(zelariDirectory, "memory");
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 fs27.lstat(candidate);
56932
+ stat7 = await fs28.lstat(candidate);
56586
56933
  } catch (error51) {
56587
56934
  if (error51.code !== "ENOENT") throw error51;
56588
56935
  try {
56589
- await fs27.mkdir(candidate);
56936
+ await fs28.mkdir(candidate);
56590
56937
  } catch (mkdirError) {
56591
56938
  if (mkdirError.code !== "EEXIST") throw mkdirError;
56592
56939
  }
56593
- stat7 = await fs27.lstat(candidate);
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 fs27.realpath(directory);
56600
- const relativeDirectory = path64.relative(resolved, canonicalDirectory);
56601
- if (relativeDirectory.startsWith("..") || path64.isAbsolute(relativeDirectory)) {
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 = path64.join(canonicalDirectory, filename);
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 fs28 } from "node:fs";
57138
- import * as path65 from "node:path";
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 fs28.realpath(projectRoot);
57504
+ canonical = await fs29.realpath(projectRoot);
57158
57505
  } catch {
57159
- canonical = path65.resolve(projectRoot);
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 path93 = workspaceFile(ctx.rootDir, "plan");
57886
- const doc = ctx.storage.readIfExists(path93);
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 path93 = join31(ctx.rootDir, "milestones", `${id3}.md`);
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(path93, meta3, body);
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 path93 = workspaceArtifact(ctx.rootDir, "decisions", id3);
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(path93, meta3, body);
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 path93 = workspaceArtifact(ctx.rootDir, "docs", slug);
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(path93, meta3, content);
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(path93) {
59027
- if (!existsSync39(path93)) return {};
59373
+ function readFile7(path94) {
59374
+ if (!existsSync39(path94)) return {};
59028
59375
  try {
59029
- const parsed = JSON.parse(readFileSync29(path93, "utf8"));
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(path93, servers) {
59052
- mkdirSync16(dirname9(path93), { recursive: true });
59398
+ function writeFile3(path94, servers) {
59399
+ mkdirSync16(dirname9(path94), { recursive: true });
59053
59400
  const body = { mcpServers: servers };
59054
- writeFileSync18(path93, `${JSON.stringify(body, null, 2)}
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 path93;
59439
+ let path94;
59093
59440
  if (opts.scope === "user") {
59094
- path93 = getUserMcpPath();
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
- path93 = getProjectMcpPath(root);
59450
+ path94 = getProjectMcpPath(root);
59104
59451
  }
59105
- const current = readFile7(path93);
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(path93, current);
59117
- return { ok: true, path: path93 };
59463
+ writeFile3(path94, current);
59464
+ return { ok: true, path: path94 };
59118
59465
  }
59119
59466
  function removeMcpServer(opts) {
59120
- const path93 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
59121
- if (!path93) {
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(path93);
59471
+ const current = readFile7(path94);
59125
59472
  if (!(opts.name in current)) {
59126
- return { ok: false, error: `Server "${opts.name}" not found in ${path93}` };
59473
+ return { ok: false, error: `Server "${opts.name}" not found in ${path94}` };
59127
59474
  }
59128
59475
  delete current[opts.name];
59129
- writeFile3(path93, current);
59130
- return { ok: true, path: path93 };
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 path93 = join34(projectRoot, "package.json");
59600
- if (!existsSync41(path93)) return null;
59946
+ const path94 = join34(projectRoot, "package.json");
59947
+ if (!existsSync41(path94)) return null;
59601
59948
  try {
59602
- return JSON.parse(await readFile8(path93, "utf8"));
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 path93 = join34(ctx.rootDir, "risks.md");
59685
- if (!existsSync41(path93)) return "_No open questions._";
59686
- const content = readFileSync31(path93, "utf8");
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(path93) {
60307
+ function readFileSyncSafe(path94) {
59961
60308
  try {
59962
- return readFileSync32(path93, "utf8");
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 path93 = writeCouncilCompletion(ctx.rootDir, completion);
60426
- completionHook = { ran: true, path: path93, completion };
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 fs29,
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 path66 from "node:path";
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 ?? path66.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
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(path66.dirname(this.file), { recursive: true });
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 fs29.readFile(this.file, "utf-8");
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 fs30 } from "node:fs";
60733
- import * as path67 from "node:path";
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 = path67.join(projectRoot, ".zelari", "memory");
60787
- this.logPath = path67.join(this.memoryDir, "log.jsonl");
60788
- await fs30.mkdir(this.memoryDir, { recursive: true });
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 fs30.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
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 fs30.readFile(this.logPath, "utf8");
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 fs31 } from "node:fs";
60860
- import * as path68 from "node:path";
61206
+ import { promises as fs32 } from "node:fs";
61207
+ import * as path69 from "node:path";
60861
61208
  function traceDir(projectRoot) {
60862
- return path68.join(projectRoot, ".zelari", "trace");
61209
+ return path69.join(projectRoot, ".zelari", "trace");
60863
61210
  }
60864
61211
  function tracePath(projectRoot, missionId) {
60865
- return path68.join(traceDir(projectRoot), `${missionId}.json`);
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 fs31.mkdir(dir, { recursive: true });
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 fs31.writeFile(
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 fs32 } from "node:fs";
60902
- import * as path69 from "node:path";
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 = path69.join(projectRoot, ".zelari");
60946
- await fs32.mkdir(dir, { recursive: true });
60947
- await fs32.writeFile(
60948
- path69.join(dir, "mission-state.json"),
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 / edit_file \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.`;
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 edit_file before claiming done."
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 / edit_file. Prose without successful writes is a failed slice.";
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 path93 = safeSocketPath(socketPath);
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(path93, () => {
62070
+ server.listen(path94, () => {
61724
62071
  server.removeListener("error", onError);
61725
62072
  resolve9({
61726
- socketPath: path93,
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(path93, () => res());
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 path93 = safeSocketPath(socketPath);
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(path93);
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 "${path93}" (connect timed out after ${connectTimeoutMs}ms)`
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 path70 from "node:path";
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 path70.resolve(__dirname, "..", "..", "..");
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 path75 from "node:path";
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 ?? path75.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
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(path75.dirname(file2), { recursive: true });
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 path76 from "node:path";
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" ? path76.win32 : path76.posix;
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 fs42 } from "node:fs";
64121
- import path81 from "node:path";
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 fs42.readdir(root);
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 fs42.readdir(dir, { withFileTypes: true });
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(path81.join(dir, e.name));
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 path82 from "node:path";
64239
- function asString3(v) {
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 = asString3(data.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: asString3(data.backend) || void 0,
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 = asString3(e.data.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, asString3(e.data.text));
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 = asString3(e.data.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 = asString3(e.data.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: asString3(e.data.verdict) || "unknown"
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 = asString3(e.data.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(path82.join(sessionDir, "events.jsonl"));
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 path83 from "node:path";
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(path83.join(sessionsDir, opts.spine.sessionId));
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 path84 from "node:path";
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 = path84.resolve(root);
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(path84.dirname(target), { recursive: true });
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 fs47 = null;
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 (!fs47) fs47 = bindSandboxedFs(fsRoot);
65418
+ if (!fs48) fs48 = bindSandboxedFs(fsRoot);
65072
65419
  try {
65073
- await runtime.registry.registerExtension(ext, { fs: fs47 });
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 fs43 } from "node:fs";
65131
- import path85 from "node:path";
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/edit_file/bash blocked).",
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 edit_file for every file you change before saying you are done.",
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 fs43.mkdir(path85.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
65700
- await fs43.writeFile(opts.exportSessionPath, json3, "utf8");
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 fs44 } from "node:fs";
66676
- import * as path86 from "node:path";
67024
+ import { promises as fs45 } from "node:fs";
67025
+ import * as path87 from "node:path";
66677
67026
  function lockPath(projectRoot) {
66678
- return path86.join(projectRoot, ".zelari", "trigger.lock");
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 = path86.dirname(lp);
66692
- await fs44.mkdir(dir, { recursive: true });
67040
+ const dir = path87.dirname(lp);
67041
+ await fs45.mkdir(dir, { recursive: true });
66693
67042
  try {
66694
- const raw = await fs44.readFile(lp, "utf8");
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 fs44.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
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 fs44.unlink(lp);
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 fs45 } from "node:fs";
66723
- import path87 from "node:path";
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 = path87.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
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 fs45.readFile(planPath, "utf8");
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 = path87.join(cwd, ".zelari", "radio");
67011
- const planPath = path87.join(planDir, `plan-${planId}.json`);
67012
- await fs45.mkdir(planDir, { recursive: true });
67013
- await fs45.writeFile(
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 fs45.mkdir(path87.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
67359
- await fs45.writeFile(opts.exportSessionPath, json3, "utf8");
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 fs45.mkdir(path87.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
67786
- await fs45.writeFile(opts.exportSessionPath, json3, "utf8");
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 path93 = skillFilePath(dir, name);
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, path93);
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(path93), { recursive: true });
68412
- writeFileSync24(path93, content, "utf8");
68413
- return { ok: true, path: path93 };
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 path93 = skillFilePath(dir, name);
68432
- if (!existsSync53(path93) && !existsSync53(skillDir)) {
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: path93 };
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 path88 from "node:path";
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 = path88.normalize(externalFile);
68734
- if (path88.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path88.sep}`)) {
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 path93 = getCompanionConfigPath();
69263
- if (!existsSync54(path93)) {
69611
+ const path94 = getCompanionConfigPath();
69612
+ if (!existsSync54(path94)) {
69264
69613
  return { projects: [] };
69265
69614
  }
69266
69615
  try {
69267
- const raw = JSON.parse(readFileSync41(path93, "utf8"));
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 path93 = getCompanionTokenPath();
69306
- if (existsSync54(path93)) {
69307
- const t = readFileSync41(path93, "utf8").trim();
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(path93, token + "\n", "utf8");
69660
+ writeFileSync25(path94, token + "\n", "utf8");
69312
69661
  try {
69313
- const fs47 = __require("node:fs");
69314
- fs47.chmodSync?.(path93, 384);
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 path93 = raw.trim();
69340
- if (!path93) continue;
69341
- let id3 = slugFromPath(path93);
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 !== path93) {
69344
- id3 = `${slugFromPath(path93)}-${n++}`;
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(path93),
69349
- path: path93
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 fs46 } from "node:fs";
69398
- import path89 from "node:path";
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 fs46.readdir(dir);
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 = path89.join(dir, sessionId2, "writer.lock");
69765
+ const lockPath2 = path90.join(dir, sessionId2, "writer.lock");
69417
69766
  try {
69418
- const raw = await fs46.readFile(lockPath2, "utf-8");
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 fs46.rm(lockPath2, { force: true });
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 path93 = url2.pathname.replace(/\/+$/, "") || "/";
70735
+ const path94 = url2.pathname.replace(/\/+$/, "") || "/";
70387
70736
  try {
70388
- if (req.method === "GET" && (path93 === "/health" || path93 === "/v1/health")) {
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 (path93.startsWith("/v1")) {
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" && path93 === "/v1/config") {
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" && path93 === "/v1/projects") {
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" && path93 === "/v1/runs") {
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" && path93 === "/v1/runs") {
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(path93);
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(path93);
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(path93);
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 path90 from "node:path";
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 = path90.join(dir, "package.json");
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 = path90.dirname(dir);
71092
+ const parent = path91.dirname(dir);
70744
71093
  if (parent === dir) break;
70745
71094
  dir = parent;
70746
71095
  }
70747
- return path90.resolve(__dirname3, "..", "..", "..");
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 = path90.join(packageRoot, "package.json");
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 = path90.join(prefix, shimName);
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 = path90.resolve(path90.dirname(shimPath), target);
70806
- const expected = path90.join(
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 = path90.join(packageRoot, "dist", "cli", "main.bundled.js");
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(path90.join(packageRoot, "package.json"));
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 = path90.dirname(fileURLToPath3(import.meta.url));
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 path91 from "node:path";
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 = path91.join(sessionsDir, opts.sessionId);
71288
- const eventsPath = path91.join(sessionDir, "events.jsonl");
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 path92 from "node:path";
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 = path92.join(homedir17(), ".zelari-code", "mcp.json");
71330
- const projectMcpPath = path92.join(cwd, ".zelari", "mcp.json");
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: path92.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync58(path92.join(homedir17(), ".zelari-code", "provider.json")) },
71359
- { path: path92.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync58(path92.join(cwd, ".zelari", "AGENTS.md")) },
71360
- { path: path92.join(cwd, "AGENTS.md"), exists: existsSync58(path92.join(cwd, "AGENTS.md")) }
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
- path92.join(cwd, "AGENTS.md"),
71401
- path92.join(cwd, ".zelari", "AGENTS.md")
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 / edit_file / bash / apply_diff are unavailable.",
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/edit_file for every file you change before claiming done.",
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 fs34 } from "node:fs";
77594
- import * as path72 from "node:path";
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 fs33 } from "node:fs";
77598
- import * as path71 from "node:path";
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: path71.join(projectRoot, "AGENTS.md"), reason: `memory is ${node.status}` };
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: path71.join(projectRoot, "AGENTS.md"), reason: `${node.kind} is not a durable instruction kind` };
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 fs33.realpath(projectRoot).catch(() => path71.resolve(projectRoot));
77615
- const target = path71.join(root, "AGENTS.md");
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 fs33.lstat(target);
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 fs33.readFile(target, "utf8");
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 fs33.writeFile(temporary, updated, { encoding: "utf8", flag: "wx" });
77996
+ await fs34.writeFile(temporary, updated, { encoding: "utf8", flag: "wx" });
77648
77997
  try {
77649
- await fs33.rename(temporary, target);
77998
+ await fs34.rename(temporary, target);
77650
77999
  } catch (error51) {
77651
- await fs33.unlink(temporary).catch(() => void 0);
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 = path72.relative(root, target);
77681
- return relative6 === "" || !relative6.startsWith("..") && !path72.isAbsolute(relative6);
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 = path72.resolve(cwd);
77685
- const root = await fs34.realpath(lexicalRoot).catch(() => lexicalRoot);
77686
- const fallback = path72.join(root, ".zelari", "memory", `export-${Date.now()}.json`);
77687
- const target = requested?.trim() ? path72.resolve(root, requested.trim()) : fallback;
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 = path72.dirname(target);
77692
- const relativeParent = path72.relative(root, parent);
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(path72.sep).filter(Boolean)) {
77695
- cursor = path72.join(cursor, segment);
78043
+ for (const segment of relativeParent.split(path73.sep).filter(Boolean)) {
78044
+ cursor = path73.join(cursor, segment);
77696
78045
  try {
77697
- const stat7 = await fs34.lstat(cursor);
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 fs34.lstat(target)).isSymbolicLink()) {
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 fs34.mkdir(path72.dirname(target), { recursive: true });
77868
- const root = await fs34.realpath(ctx.cwd).catch(() => path72.resolve(ctx.cwd));
77869
- const realParent = await fs34.realpath(path72.dirname(target));
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 fs34.writeFile(target, JSON.stringify(await memory.export(), null, 2) + "\n", "utf8");
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 fs36 } from "node:fs";
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 fs35 } from "node:fs";
78073
- import path73 from "node:path";
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 fs35.readFile(filePath, "utf8");
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 = path73.isAbsolute(args.csv_path) ? args.csv_path : path73.join(opts.parentCwd, args.csv_path);
78168
- const absOut = path73.isAbsolute(args.output_csv_path) ? args.output_csv_path : path73.join(opts.parentCwd, args.output_csv_path);
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 fs35.mkdir(path73.dirname(absOut), { recursive: true });
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 fs35.writeFile(tmp, contents, "utf8");
78247
- await fs35.rename(tmp, file2);
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 fs36.access(absCsv);
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 fs37 } from "node:fs";
78419
- import path74 from "node:path";
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 = path74.join(ctx.cwd, ".zelari", "radio");
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 fs37.readdir(dir);
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 = path74.join(dir, f);
78544
- const stat7 = await fs37.stat(full);
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 fs37.readFile(latest, "utf8");
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] ${path74.basename(latest)}: (no nodes / no events yet)`);
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] ${path74.basename(latest)}:
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 fs38 } from "node:fs";
78870
- import path77 from "node:path";
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 ?? path77.join(os12.homedir(), ".tmp", "zelari-code", "skills");
78877
- await fs38.mkdir(skillDir, { recursive: true });
78878
- const filePath = path77.join(skillDir, `${skill.id}.md`);
78879
- await fs38.writeFile(filePath, markdown, "utf8");
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 fs39, existsSync as existsSync49, readFileSync as readFileSync37, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
78896
- import path78 from "node:path";
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 ?? path78.join(os13.homedir(), ".tmp", "zelari-code", "branches");
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 ?? path78.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
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 path78.join(baseDir, name);
79256
+ return path79.join(baseDir, name);
78908
79257
  }
78909
79258
  function metaPathFor(name, baseDir) {
78910
- return path78.join(baseDir, name, META_FILENAME);
79259
+ return path79.join(baseDir, name, META_FILENAME);
78911
79260
  }
78912
79261
  function sessionsPathFor(name, baseDir) {
78913
- return path78.join(baseDir, name, SESSIONS_SUBDIR);
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(path78.dirname(metaPath), { recursive: true });
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 fs39.readdir(sessionsPath);
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 = path78.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
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 = path78.join(branchSessionsPath, `${fromSessionId}.jsonl`);
78997
- await fs39.copyFile(sourcePath, destPath);
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 fs39.readdir(baseDir);
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 fs40 } from "node:fs";
79100
- import path79 from "node:path";
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 = path79.join(process.cwd(), ".zelari");
79452
+ const zelari = path80.join(process.cwd(), ".zelari");
79104
79453
  let content;
79105
79454
  switch (what) {
79106
79455
  case "plan": {
79107
- const planPath = path79.join(zelari, "plan.md");
79456
+ const planPath = path80.join(zelari, "plan.md");
79108
79457
  try {
79109
- content = await fs40.readFile(planPath, "utf-8");
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 = path79.join(zelari, "decisions");
79465
+ const decisionsDir = path80.join(zelari, "decisions");
79117
79466
  try {
79118
- const files = (await fs40.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
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 fs40.readFile(path79.join(decisionsDir, f), "utf-8");
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 = path79.join(zelari, "risks.md");
79488
+ const risksPath = path80.join(zelari, "risks.md");
79140
79489
  try {
79141
- content = await fs40.readFile(risksPath, "utf-8");
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 = path79.join(process.cwd(), "AGENTS.MD");
79497
+ const agentsPath = path80.join(process.cwd(), "AGENTS.MD");
79149
79498
  try {
79150
- content = await fs40.readFile(agentsPath, "utf-8");
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 = path79.join(zelari, "docs");
79506
+ const docsDir = path80.join(zelari, "docs");
79158
79507
  try {
79159
- const files = (await fs40.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
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 = path79.join(process.cwd(), ".zelari");
79200
- await fs40.rm(target, { recursive: true, force: true });
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 path80 from "node:path";
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 fs41, existsSync as existsSync50, statSync as statSync8, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
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 fs41.readFile(file2, "utf-8");
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 ?? path80.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
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 ?? path80.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
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 path93 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
81058
- name = path93 && /^[a-z0-9]/.test(path93) ? path93 : "imported-skill";
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
  }