zelari-code 1.44.0 → 1.46.1

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.
@@ -665,7 +665,7 @@ async function refreshGrokToken(options) {
665
665
  return parseTokenResponseBody(obj, accessToken);
666
666
  }
667
667
  async function openBrowser(url2) {
668
- const { spawn: spawn14 } = await import("node:child_process");
668
+ const { spawn: spawn16 } = await import("node:child_process");
669
669
  const cmd = (() => {
670
670
  switch (process.platform) {
671
671
  case "darwin":
@@ -678,7 +678,7 @@ async function openBrowser(url2) {
678
678
  })();
679
679
  return new Promise((resolve3, reject) => {
680
680
  try {
681
- const child = spawn14(cmd.bin, cmd.args, {
681
+ const child = spawn16(cmd.bin, cmd.args, {
682
682
  stdio: "ignore",
683
683
  detached: true
684
684
  });
@@ -3181,10 +3181,10 @@ function mergeDefs(...defs) {
3181
3181
  function cloneDef(schema) {
3182
3182
  return mergeDefs(schema._zod.def);
3183
3183
  }
3184
- function getElementAtPath(obj, path53) {
3185
- if (!path53)
3184
+ function getElementAtPath(obj, path55) {
3185
+ if (!path55)
3186
3186
  return obj;
3187
- return path53.reduce((acc, key) => acc?.[key], obj);
3187
+ return path55.reduce((acc, key) => acc?.[key], obj);
3188
3188
  }
3189
3189
  function promiseAllObject(promisesObj) {
3190
3190
  const keys = Object.keys(promisesObj);
@@ -3512,11 +3512,11 @@ function explicitlyAborted(x, startIndex = 0) {
3512
3512
  }
3513
3513
  return false;
3514
3514
  }
3515
- function prefixIssues(path53, issues) {
3515
+ function prefixIssues(path55, issues) {
3516
3516
  return issues.map((iss) => {
3517
3517
  var _a3;
3518
3518
  (_a3 = iss).path ?? (_a3.path = []);
3519
- iss.path.unshift(path53);
3519
+ iss.path.unshift(path55);
3520
3520
  return iss;
3521
3521
  });
3522
3522
  }
@@ -3734,16 +3734,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3734
3734
  }
3735
3735
  function formatError(error51, mapper = (issue2) => issue2.message) {
3736
3736
  const fieldErrors = { _errors: [] };
3737
- const processError = (error52, path53 = []) => {
3737
+ const processError = (error52, path55 = []) => {
3738
3738
  for (const issue2 of error52.issues) {
3739
3739
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3740
- issue2.errors.map((issues) => processError({ issues }, [...path53, ...issue2.path]));
3740
+ issue2.errors.map((issues) => processError({ issues }, [...path55, ...issue2.path]));
3741
3741
  } else if (issue2.code === "invalid_key") {
3742
- processError({ issues: issue2.issues }, [...path53, ...issue2.path]);
3742
+ processError({ issues: issue2.issues }, [...path55, ...issue2.path]);
3743
3743
  } else if (issue2.code === "invalid_element") {
3744
- processError({ issues: issue2.issues }, [...path53, ...issue2.path]);
3744
+ processError({ issues: issue2.issues }, [...path55, ...issue2.path]);
3745
3745
  } else {
3746
- const fullpath = [...path53, ...issue2.path];
3746
+ const fullpath = [...path55, ...issue2.path];
3747
3747
  if (fullpath.length === 0) {
3748
3748
  fieldErrors._errors.push(mapper(issue2));
3749
3749
  } else {
@@ -3770,17 +3770,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3770
3770
  }
3771
3771
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3772
3772
  const result = { errors: [] };
3773
- const processError = (error52, path53 = []) => {
3773
+ const processError = (error52, path55 = []) => {
3774
3774
  var _a3, _b;
3775
3775
  for (const issue2 of error52.issues) {
3776
3776
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3777
- issue2.errors.map((issues) => processError({ issues }, [...path53, ...issue2.path]));
3777
+ issue2.errors.map((issues) => processError({ issues }, [...path55, ...issue2.path]));
3778
3778
  } else if (issue2.code === "invalid_key") {
3779
- processError({ issues: issue2.issues }, [...path53, ...issue2.path]);
3779
+ processError({ issues: issue2.issues }, [...path55, ...issue2.path]);
3780
3780
  } else if (issue2.code === "invalid_element") {
3781
- processError({ issues: issue2.issues }, [...path53, ...issue2.path]);
3781
+ processError({ issues: issue2.issues }, [...path55, ...issue2.path]);
3782
3782
  } else {
3783
- const fullpath = [...path53, ...issue2.path];
3783
+ const fullpath = [...path55, ...issue2.path];
3784
3784
  if (fullpath.length === 0) {
3785
3785
  result.errors.push(mapper(issue2));
3786
3786
  continue;
@@ -3812,8 +3812,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3812
3812
  }
3813
3813
  function toDotPath(_path) {
3814
3814
  const segs = [];
3815
- const path53 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3816
- for (const seg of path53) {
3815
+ const path55 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3816
+ for (const seg of path55) {
3817
3817
  if (typeof seg === "number")
3818
3818
  segs.push(`[${seg}]`);
3819
3819
  else if (typeof seg === "symbol")
@@ -17316,13 +17316,13 @@ function resolveRef(ref, ctx) {
17316
17316
  if (!ref.startsWith("#")) {
17317
17317
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17318
17318
  }
17319
- const path53 = ref.slice(1).split("/").filter(Boolean);
17320
- if (path53.length === 0) {
17319
+ const path55 = ref.slice(1).split("/").filter(Boolean);
17320
+ if (path55.length === 0) {
17321
17321
  return ctx.rootSchema;
17322
17322
  }
17323
17323
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17324
- if (path53[0] === defsKey) {
17325
- const key = path53[1];
17324
+ if (path55[0] === defsKey) {
17325
+ const key = path55[1];
17326
17326
  if (!key || !ctx.defs[key]) {
17327
17327
  throw new Error(`Reference not found: ${ref}`);
17328
17328
  }
@@ -18625,6 +18625,27 @@ function coerceStringList(value, fallback) {
18625
18625
  }
18626
18626
  return fallback;
18627
18627
  }
18628
+ function hasRecursiveGlob(include) {
18629
+ return include.some((g) => g.includes("**"));
18630
+ }
18631
+ function scopeWarnings(allEntries, include, matched) {
18632
+ const warnings = [];
18633
+ const filesWalked = allEntries.filter((e) => e.type === "file").length;
18634
+ if (filesWalked === 0)
18635
+ return warnings;
18636
+ if (matched === 0) {
18637
+ warnings.push(`SEARCH_EMPTY_SCOPE: include globs matched 0 of ${filesWalked} files walked \u2014 '*' matches only one path segment; use '**/<glob>' for recursive matching. Do not interpret this result as "pattern not found".`);
18638
+ return warnings;
18639
+ }
18640
+ if (hasRecursiveGlob(include) || matched >= filesWalked)
18641
+ return warnings;
18642
+ const recursiveRegexes = compileGlobs(include.map((g) => `**/${g}`));
18643
+ const wouldMatch = allEntries.filter((e) => e.type === "file" && matchesAnyCompiled(e.name, recursiveRegexes)).length;
18644
+ if (wouldMatch > matched) {
18645
+ warnings.push(`include globs matched ${matched} of ${filesWalked} files walked \u2014 '*' matches only one path segment; a '**/*.ts'-style recursive glob would have matched ${wouldMatch - matched} more file(s) in subdirectories`);
18646
+ }
18647
+ return warnings;
18648
+ }
18628
18649
  async function searchFile(absPath, relPath, regex, contextLines, remainingSlots) {
18629
18650
  let buf;
18630
18651
  try {
@@ -18701,7 +18722,7 @@ var init_search = __esm({
18701
18722
  });
18702
18723
  grepContentTool = {
18703
18724
  name: "grep_content",
18704
- description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and surrounding context.',
18725
+ description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics: "*" matches ONE path segment only ("*.ts" does NOT match "sub/file.ts"); use "**" for recursive matching ("**/*.ts" matches at any depth). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.',
18705
18726
  permissions: ["read"],
18706
18727
  timeoutMs: 3e4,
18707
18728
  inputSchema: GrepContentArgsSchema,
@@ -18709,8 +18730,15 @@ var init_search = __esm({
18709
18730
  try {
18710
18731
  const absRoot = path8.isAbsolute(args.path) ? args.path : path8.join(ctx.cwd, args.path);
18711
18732
  const regex = new RegExp(args.pattern, "gm");
18712
- const include = coerceStringList(args.include, ["*"]);
18733
+ const rawInclude = args.include;
18734
+ const include = coerceStringList(rawInclude, ["*"]);
18713
18735
  const exclude = coerceStringList(args.exclude, DEFAULT_EXCLUDES);
18736
+ const warnings = [];
18737
+ if (Array.isArray(rawInclude) && rawInclude.length === 0) {
18738
+ warnings.push('DEPRECATED_INPUT: empty include array \u2014 omit the field instead; this will become INVALID_ARGUMENT (planned v1.47). Fell back to ["*"]');
18739
+ } else if (typeof rawInclude === "string") {
18740
+ warnings.push(`include coerced from bare string to ${JSON.stringify(include)}`);
18741
+ }
18714
18742
  if (!await isDirectory(absRoot)) {
18715
18743
  const single = await searchFile(absRoot, args.path, regex, args.contextLines, args.maxMatches);
18716
18744
  return typedOk({
@@ -18718,12 +18746,18 @@ var init_search = __esm({
18718
18746
  totalMatches: single.total,
18719
18747
  truncated: single.truncated,
18720
18748
  filesSearched: 1,
18721
- filesInTree: 1
18749
+ filesInTree: 1,
18750
+ filesWalked: 1,
18751
+ effectiveInclude: include,
18752
+ effectiveExclude: exclude,
18753
+ ...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
18722
18754
  });
18723
18755
  }
18724
18756
  const allEntries = [];
18725
18757
  await walk(absRoot, "", 0, args.maxDepth, exclude, allEntries, ctx.signal);
18726
18758
  const matchedFiles = filterByInclude(allEntries, include);
18759
+ const filesWalked = allEntries.filter((e) => e.type === "file").length;
18760
+ warnings.push(...scopeWarnings(allEntries, include, matchedFiles.length));
18727
18761
  const allMatches = [];
18728
18762
  let totalMatches = 0;
18729
18763
  let truncated = false;
@@ -18748,7 +18782,11 @@ var init_search = __esm({
18748
18782
  totalMatches,
18749
18783
  truncated,
18750
18784
  filesSearched,
18751
- filesInTree: matchedFiles.length
18785
+ filesInTree: matchedFiles.length,
18786
+ filesWalked,
18787
+ effectiveInclude: include,
18788
+ effectiveExclude: exclude,
18789
+ ...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
18752
18790
  });
18753
18791
  } catch (err) {
18754
18792
  return typedErr(err instanceof Error ? err.message : String(err));
@@ -19535,11 +19573,11 @@ var init_tools = __esm({
19535
19573
  if (!ctx.addDocument)
19536
19574
  return "Knowledge vault tool not available.";
19537
19575
  const title = args["title"] || "New Document";
19538
- const path53 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19576
+ const path55 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19539
19577
  const content = args["content"] || "";
19540
19578
  const tags = args["tags"] || [];
19541
19579
  ctx.addDocument({
19542
- path: path53,
19580
+ path: path55,
19543
19581
  title,
19544
19582
  content,
19545
19583
  format: "markdown",
@@ -19548,7 +19586,7 @@ var init_tools = __esm({
19548
19586
  workspaceId: ctx.workspaceId
19549
19587
  });
19550
19588
  ctx.addActivity("vault", "created document", title);
19551
- return `Document "${title}" created at "${path53}".`;
19589
+ return `Document "${title}" created at "${path55}".`;
19552
19590
  }
19553
19591
  }
19554
19592
  ];
@@ -23877,11 +23915,11 @@ var init_synthesisAudit = __esm({
23877
23915
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
23878
23916
  import { join as join2 } from "node:path";
23879
23917
  function loadNfrSpec(zelariRoot) {
23880
- const path53 = join2(zelariRoot, "nfr-spec.json");
23881
- if (!existsSync7(path53))
23918
+ const path55 = join2(zelariRoot, "nfr-spec.json");
23919
+ if (!existsSync7(path55))
23882
23920
  return null;
23883
23921
  try {
23884
- const raw = JSON.parse(readFileSync7(path53, "utf8"));
23922
+ const raw = JSON.parse(readFileSync7(path55, "utf8"));
23885
23923
  if (raw.version !== 1 || !Array.isArray(raw.targets))
23886
23924
  return null;
23887
23925
  return raw;
@@ -26187,9 +26225,9 @@ var init_types4 = __esm({
26187
26225
  import { readFileSync as readFileSync12 } from "node:fs";
26188
26226
  import { join as join8 } from "node:path";
26189
26227
  function readLessonsDeduped(zelariRoot) {
26190
- const path53 = join8(zelariRoot, LESSONS_FILE);
26228
+ const path55 = join8(zelariRoot, LESSONS_FILE);
26191
26229
  try {
26192
- const raw = readFileSync12(path53, "utf8");
26230
+ const raw = readFileSync12(path55, "utf8");
26193
26231
  const byId = /* @__PURE__ */ new Map();
26194
26232
  for (const line of raw.split(/\r?\n/)) {
26195
26233
  if (!line.trim())
@@ -26290,8 +26328,8 @@ function keywordsFrom(check2, signature) {
26290
26328
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26291
26329
  }
26292
26330
  function writeLesson(zelariRoot, lesson) {
26293
- const path53 = join9(zelariRoot, LESSONS_FILE);
26294
- appendFileSync(path53, `${JSON.stringify(lesson)}
26331
+ const path55 = join9(zelariRoot, LESSONS_FILE);
26332
+ appendFileSync(path55, `${JSON.stringify(lesson)}
26295
26333
  `, "utf8");
26296
26334
  }
26297
26335
  function findSimilar(lessons, signature) {
@@ -26904,9 +26942,9 @@ function findCycle(nodes) {
26904
26942
  if (color.get(start) !== WHITE)
26905
26943
  continue;
26906
26944
  const stack = [[start, 0]];
26907
- const path53 = [];
26945
+ const path55 = [];
26908
26946
  color.set(start, GRAY);
26909
- path53.push(start);
26947
+ path55.push(start);
26910
26948
  while (stack.length > 0) {
26911
26949
  const top = stack[stack.length - 1];
26912
26950
  const [id, idx] = top;
@@ -26919,17 +26957,17 @@ function findCycle(nodes) {
26919
26957
  continue;
26920
26958
  const c = color.get(dep);
26921
26959
  if (c === GRAY) {
26922
- const at = path53.indexOf(dep);
26923
- return [...path53.slice(at), dep];
26960
+ const at = path55.indexOf(dep);
26961
+ return [...path55.slice(at), dep];
26924
26962
  }
26925
26963
  if (c === WHITE) {
26926
26964
  color.set(dep, GRAY);
26927
- path53.push(dep);
26965
+ path55.push(dep);
26928
26966
  stack.push([dep, 0]);
26929
26967
  }
26930
26968
  } else {
26931
26969
  color.set(id, BLACK);
26932
- path53.pop();
26970
+ path55.pop();
26933
26971
  stack.pop();
26934
26972
  }
26935
26973
  }
@@ -27849,8 +27887,8 @@ var init_runner = __esm({
27849
27887
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
27850
27888
  pending: []
27851
27889
  };
27852
- const path53 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
27853
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path53}`);
27890
+ const path55 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
27891
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path55}`);
27854
27892
  return snapshot;
27855
27893
  }
27856
27894
  callLog(msg, data) {
@@ -29463,9 +29501,9 @@ function spillToolOutput(fullText, meta3) {
29463
29501
  const rnd = randomBytes2(3).toString("hex");
29464
29502
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
29465
29503
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
29466
- const path53 = join11(dir, file2);
29467
- writeFileSync11(path53, fullText, "utf8");
29468
- return path53;
29504
+ const path55 = join11(dir, file2);
29505
+ writeFileSync11(path55, fullText, "utf8");
29506
+ return path55;
29469
29507
  } catch {
29470
29508
  return null;
29471
29509
  }
@@ -29511,10 +29549,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
29511
29549
  ${tail}`;
29512
29550
  }
29513
29551
  if (doSpill) {
29514
- const path53 = spillToolOutput(text, { toolName: opts.toolName });
29515
- if (path53) {
29552
+ const path55 = spillToolOutput(text, { toolName: opts.toolName });
29553
+ if (path55) {
29516
29554
  const spillNote = `
29517
- \u2026 [full output spilled to: ${path53} \u2014 re-read with read_file if you need the complete text] \u2026`;
29555
+ \u2026 [full output spilled to: ${path55} \u2014 re-read with read_file if you need the complete text] \u2026`;
29518
29556
  if (preview.includes("] \u2026\n")) {
29519
29557
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
29520
29558
  `);
@@ -30689,6 +30727,10 @@ var init_taskTool = __esm({
30689
30727
  EXPLORE_PROMPT = [
30690
30728
  "You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
30691
30729
  "READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
30730
+ "OBSERVATION INTEGRITY: negative evidence is valid only from a completed",
30731
+ "observation. Never conclude that code/symbols/files do not exist from",
30732
+ "degraded results, zero files examined, or unavailable backends - report",
30733
+ "the degraded status instead and widen the observation.",
30692
30734
  "Gather only what you need, then STOP with a concise conclusion:",
30693
30735
  "file paths, symbols, line refs, and how things connect. No large dumps.",
30694
30736
  "Respect any Scope / Acceptance sections in the user prompt.",
@@ -31375,28 +31417,28 @@ var init_storage = __esm({
31375
31417
  VALID_SCALARS = /^(true|false|null|~)$/i;
31376
31418
  Storage = class {
31377
31419
  /** Read a Markdown file with frontmatter. Throws if not found. */
31378
- read(path53) {
31379
- if (!existsSync18(path53)) {
31380
- throw new Error(`File not found: ${path53}`);
31420
+ read(path55) {
31421
+ if (!existsSync18(path55)) {
31422
+ throw new Error(`File not found: ${path55}`);
31381
31423
  }
31382
- const md = readFileSync16(path53, "utf8");
31424
+ const md = readFileSync16(path55, "utf8");
31383
31425
  return parseFrontmatter(md);
31384
31426
  }
31385
31427
  /** Read a Markdown file; returns null if not found. */
31386
- readIfExists(path53) {
31387
- if (!existsSync18(path53)) return null;
31388
- return this.read(path53);
31428
+ readIfExists(path55) {
31429
+ if (!existsSync18(path55)) return null;
31430
+ return this.read(path55);
31389
31431
  }
31390
31432
  /**
31391
31433
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
31392
31434
  * The meta object is serialized as YAML frontmatter; body as Markdown.
31393
31435
  */
31394
- write(path53, meta3, body) {
31395
- mkdirSync11(dirname2(path53), { recursive: true });
31396
- const tmp = path53 + ".tmp-" + process.pid;
31436
+ write(path55, meta3, body) {
31437
+ mkdirSync11(dirname2(path55), { recursive: true });
31438
+ const tmp = path55 + ".tmp-" + process.pid;
31397
31439
  const md = serializeFrontmatter(meta3, body);
31398
31440
  writeFileSync13(tmp, md, "utf8");
31399
- renameSync2(tmp, path53);
31441
+ renameSync2(tmp, path55);
31400
31442
  }
31401
31443
  /** List all .md files in a directory (non-recursive). */
31402
31444
  listMarkdown(dir) {
@@ -31458,8 +31500,8 @@ function nextPlanTaskId(store4) {
31458
31500
  return `t${store4.counter}`;
31459
31501
  }
31460
31502
  function writePlanTaskArtifact(rootDir, task) {
31461
- const path53 = join15(rootDir, "plan-tasks", `${task.id}.md`);
31462
- mkdirSync12(dirname3(path53), { recursive: true });
31503
+ const path55 = join15(rootDir, "plan-tasks", `${task.id}.md`);
31504
+ mkdirSync12(dirname3(path55), { recursive: true });
31463
31505
  const meta3 = {
31464
31506
  kind: "task",
31465
31507
  id: task.id,
@@ -31480,7 +31522,7 @@ function writePlanTaskArtifact(rootDir, task) {
31480
31522
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
31481
31523
  ""
31482
31524
  ].filter((l) => l !== null).join("\n");
31483
- new Storage().write(path53, meta3, body);
31525
+ new Storage().write(path55, meta3, body);
31484
31526
  }
31485
31527
  function loadHandle(rootDir) {
31486
31528
  const jsonPath = join15(rootDir, "plan.json");
@@ -31799,6 +31841,369 @@ var init_planTaskTools = __esm({
31799
31841
  }
31800
31842
  });
31801
31843
 
31844
+ // src/cli/tools/inspectTypecheckSafety.ts
31845
+ import { promises as fs12 } from "node:fs";
31846
+ import path21 from "node:path";
31847
+ import { spawn as spawn4 } from "node:child_process";
31848
+ async function scanTsbuildinfo(root) {
31849
+ const found = [];
31850
+ const stack = [root];
31851
+ while (stack.length > 0) {
31852
+ const dir = stack.pop();
31853
+ let entries;
31854
+ try {
31855
+ entries = await fs12.readdir(dir, { withFileTypes: true });
31856
+ } catch {
31857
+ continue;
31858
+ }
31859
+ for (const entry of entries) {
31860
+ const p3 = path21.join(dir, entry.name);
31861
+ if (entry.isDirectory()) {
31862
+ if (!SCAN_SKIP.has(entry.name)) stack.push(p3);
31863
+ } else if (entry.name.endsWith(".tsbuildinfo")) {
31864
+ found.push(path21.relative(root, p3).split(path21.sep).join("/"));
31865
+ }
31866
+ }
31867
+ }
31868
+ found.sort();
31869
+ return found;
31870
+ }
31871
+ async function gitStatusPorcelain(root) {
31872
+ return new Promise((resolve3) => {
31873
+ const child = spawn4("git", ["status", "--porcelain"], { cwd: root, shell: false });
31874
+ let out = "";
31875
+ child.stdout.on("data", (d) => out += d.toString());
31876
+ child.stderr.on("data", (d) => out += d.toString());
31877
+ child.on("error", () => resolve3("<git-unavailable>"));
31878
+ child.on("close", (code) => resolve3(code === 0 ? out : `<git-exit-${code ?? "none"}>`));
31879
+ });
31880
+ }
31881
+ async function fingerprintWorkspace(root) {
31882
+ const [gitStatus, tsbuildinfoFiles] = await Promise.all([
31883
+ gitStatusPorcelain(root),
31884
+ scanTsbuildinfo(root)
31885
+ ]);
31886
+ return { gitStatus, tsbuildinfoFiles };
31887
+ }
31888
+ function diffFingerprints(pre, post) {
31889
+ const preSet = new Set(pre.tsbuildinfoFiles);
31890
+ return {
31891
+ newTsbuildinfo: post.tsbuildinfoFiles.filter((f) => !preSet.has(f)),
31892
+ gitStatusChanged: post.gitStatus !== pre.gitStatus
31893
+ };
31894
+ }
31895
+ async function cleanupArtifacts(root, relPaths) {
31896
+ const cleaned = [];
31897
+ const failed = [];
31898
+ for (const rel2 of relPaths) {
31899
+ try {
31900
+ await fs12.unlink(path21.join(root, rel2));
31901
+ cleaned.push(rel2);
31902
+ } catch {
31903
+ failed.push(rel2);
31904
+ }
31905
+ }
31906
+ return { cleaned, failed };
31907
+ }
31908
+ function classifyTypecheckRefusal(output) {
31909
+ if (/composite/i.test(output) && /(may not|cannot|disallow|disable)/i.test(output)) {
31910
+ return "the TypeScript compiler refused to run on this composite/incremental project shape (tsc-level error about the build setup, not a type error) \u2014 inspect_command will not fake an empty result; run the project typecheck script directly if you need it";
31911
+ }
31912
+ return null;
31913
+ }
31914
+ var SCAN_SKIP;
31915
+ var init_inspectTypecheckSafety = __esm({
31916
+ "src/cli/tools/inspectTypecheckSafety.ts"() {
31917
+ "use strict";
31918
+ SCAN_SKIP = /* @__PURE__ */ new Set(["node_modules", ".git"]);
31919
+ }
31920
+ });
31921
+
31922
+ // src/cli/tools/inspectCommand.ts
31923
+ import { spawn as spawn5 } from "node:child_process";
31924
+ import { createHash as createHash5 } from "node:crypto";
31925
+ import { existsSync as existsSync20, promises as fs13 } from "node:fs";
31926
+ import os8 from "node:os";
31927
+ import path22 from "node:path";
31928
+ function resolveNodeModuleBin(start, rel2) {
31929
+ let dir = path22.resolve(start);
31930
+ for (; ; ) {
31931
+ const candidate = path22.join(dir, "node_modules", rel2);
31932
+ if (existsSync20(candidate)) return candidate;
31933
+ const parent = path22.dirname(dir);
31934
+ if (parent === dir) return void 0;
31935
+ dir = parent;
31936
+ }
31937
+ }
31938
+ function rejectFlagLike(kind, value) {
31939
+ if (value.startsWith("-")) {
31940
+ return `${kind} must not start with "-" (got ${JSON.stringify(value)}) \u2014 pass a value, not a flag`;
31941
+ }
31942
+ return null;
31943
+ }
31944
+ function buildInspectCommand(op, ctx) {
31945
+ switch (op.operation) {
31946
+ case "git_status":
31947
+ return {
31948
+ ok: true,
31949
+ command: "git",
31950
+ argv: ["status", ...op.short ? ["--short"] : []],
31951
+ inspectionClass: "git-inspection"
31952
+ };
31953
+ case "git_log":
31954
+ return {
31955
+ ok: true,
31956
+ command: "git",
31957
+ argv: [
31958
+ "log",
31959
+ ...op.oneline ? ["--oneline"] : [],
31960
+ ...op.limit !== void 0 ? ["-n", String(op.limit)] : []
31961
+ ],
31962
+ inspectionClass: "git-inspection"
31963
+ };
31964
+ case "git_diff": {
31965
+ if (op.path !== void 0) {
31966
+ const err = rejectFlagLike("path", op.path);
31967
+ if (err) return { ok: false, reason: err };
31968
+ }
31969
+ return {
31970
+ ok: true,
31971
+ command: "git",
31972
+ argv: [
31973
+ "diff",
31974
+ "--no-ext-diff",
31975
+ "--no-textconv",
31976
+ ...op.staged ? ["--staged"] : [],
31977
+ ...op.path !== void 0 ? ["--", op.path] : []
31978
+ ],
31979
+ inspectionClass: "git-inspection"
31980
+ };
31981
+ }
31982
+ case "git_show": {
31983
+ const err = rejectFlagLike("ref", op.ref);
31984
+ if (err) return { ok: false, reason: err };
31985
+ return {
31986
+ ok: true,
31987
+ command: "git",
31988
+ argv: ["show", "--no-ext-diff", "--no-textconv", op.ref],
31989
+ inspectionClass: "git-inspection"
31990
+ };
31991
+ }
31992
+ case "git_branch_current":
31993
+ return { ok: true, command: "git", argv: ["branch", "--show-current"], inspectionClass: "git-inspection" };
31994
+ case "git_ls_files":
31995
+ return { ok: true, command: "git", argv: ["ls-files"], inspectionClass: "git-inspection" };
31996
+ case "node_version":
31997
+ return { ok: true, command: process.execPath, argv: ["--version"], inspectionClass: "env-info" };
31998
+ case "npm_ls":
31999
+ case "npm_outdated":
32000
+ case "npm_view": {
32001
+ const npmCli = ctx.npmCliPath ?? resolveNodeModuleBin(ctx.root, path22.join("npm", "bin", "npm-cli.js")) ?? path22.join(ctx.root, "node_modules", "npm", "bin", "npm-cli.js");
32002
+ if (op.operation === "npm_view") {
32003
+ const err = rejectFlagLike("package", op.package);
32004
+ if (err) return { ok: false, reason: err };
32005
+ }
32006
+ const sub = op.operation === "npm_ls" ? ["ls", "--depth=0"] : op.operation === "npm_outdated" ? ["outdated"] : ["view", op.package];
32007
+ return { ok: true, command: process.execPath, argv: [npmCli, ...sub], inspectionClass: "env-info" };
32008
+ }
32009
+ case "typecheck": {
32010
+ const project = path22.resolve(ctx.cwd, op.project ?? "tsconfig.json");
32011
+ const hash3 = createHash5("sha256").update(project).digest("hex").slice(0, 16);
32012
+ const tsBuildInfoFile = path22.join(os8.tmpdir(), "zelari-inspect", `${hash3}.tsbuildinfo`);
32013
+ return {
32014
+ ok: true,
32015
+ command: process.execPath,
32016
+ argv: [
32017
+ ctx.tscPath ?? resolveNodeModuleBin(ctx.root, path22.join("typescript", "bin", "tsc")) ?? resolveNodeModuleBin(ctx.cwd, path22.join("typescript", "bin", "tsc")) ?? path22.join(ctx.root, "node_modules", "typescript", "bin", "tsc"),
32018
+ "--noEmit",
32019
+ // S3.5 primary mechanism: redirect, never disable — composite forces
32020
+ // incremental (TS#30661), so --incremental false would break on the
32021
+ // very fixture it must support. The CLI override wins over any
32022
+ // tsBuildInfoFile written into the tsconfig.
32023
+ "--incremental",
32024
+ "--tsBuildInfoFile",
32025
+ tsBuildInfoFile,
32026
+ "-p",
32027
+ project
32028
+ ],
32029
+ inspectionClass: "project-code-execution",
32030
+ tsBuildInfoFile
32031
+ };
32032
+ }
32033
+ }
32034
+ }
32035
+ function runSpawn(command, argv, opts) {
32036
+ return new Promise((resolve3) => {
32037
+ let child;
32038
+ try {
32039
+ child = spawn5(command, argv, { cwd: opts.cwd, shell: false });
32040
+ } catch (err) {
32041
+ resolve3({ code: null, stdout: "", stderr: String(err), timedOut: false, spawnError: String(err) });
32042
+ return;
32043
+ }
32044
+ let stdout = "";
32045
+ let stderr = "";
32046
+ let timedOut = false;
32047
+ let settled = false;
32048
+ const timer = setTimeout(() => {
32049
+ timedOut = true;
32050
+ child.kill();
32051
+ }, opts.timeoutMs);
32052
+ const onAbort = () => {
32053
+ timedOut = true;
32054
+ child.kill();
32055
+ };
32056
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
32057
+ child.stdout?.on("data", (d) => stdout += d.toString());
32058
+ child.stderr?.on("data", (d) => stderr += d.toString());
32059
+ child.on("error", (err) => {
32060
+ if (settled) return;
32061
+ settled = true;
32062
+ clearTimeout(timer);
32063
+ resolve3({ code: null, stdout, stderr, timedOut, spawnError: err.message });
32064
+ });
32065
+ child.on("close", (code) => {
32066
+ if (settled) return;
32067
+ settled = true;
32068
+ clearTimeout(timer);
32069
+ opts.signal?.removeEventListener("abort", onAbort);
32070
+ resolve3({ code, stdout, stderr, timedOut });
32071
+ });
32072
+ });
32073
+ }
32074
+ function capOutput(stdout, stderr) {
32075
+ const combined = stderr ? `${stdout}
32076
+ [stderr]
32077
+ ${stderr}` : stdout;
32078
+ if (combined.length <= MAX_OUTPUT_CHARS) return { output: combined, truncated: false };
32079
+ return {
32080
+ output: `${combined.slice(0, MAX_OUTPUT_CHARS)}
32081
+ \u2026 (truncated, ${combined.length} chars total)`,
32082
+ truncated: true
32083
+ };
32084
+ }
32085
+ async function typecheckGuarded(built, root, signal) {
32086
+ const pre = await fingerprintWorkspace(root);
32087
+ const r = await runSpawn(built.command, built.argv, { cwd: root, timeoutMs: TYPECHECK_TIMEOUT_MS, signal });
32088
+ if (r.spawnError) {
32089
+ return typedErr(`SPAWN_ERROR: typecheck could not launch: ${r.spawnError}`);
32090
+ }
32091
+ const { output, truncated } = capOutput(r.stdout, r.stderr);
32092
+ const refusal = r.code !== 0 ? classifyTypecheckRefusal(`${r.stdout}
32093
+ ${r.stderr}`) : null;
32094
+ if (refusal) {
32095
+ return typedOk({
32096
+ status: "unsupported_project_shape",
32097
+ operation: "typecheck",
32098
+ inspectionClass: built.inspectionClass,
32099
+ reason: refusal,
32100
+ exitCode: r.code,
32101
+ output,
32102
+ truncated
32103
+ });
32104
+ }
32105
+ const post = await fingerprintWorkspace(root);
32106
+ const delta = diffFingerprints(pre, post);
32107
+ if (delta.newTsbuildinfo.length > 0 || delta.gitStatusChanged) {
32108
+ const cleanup = await cleanupArtifacts(root, delta.newTsbuildinfo);
32109
+ return typedOk({
32110
+ status: "degraded",
32111
+ operation: "typecheck",
32112
+ inspectionClass: built.inspectionClass,
32113
+ exitCode: r.code,
32114
+ output,
32115
+ truncated,
32116
+ artifactsWritten: delta.newTsbuildinfo,
32117
+ gitStatusChanged: delta.gitStatusChanged,
32118
+ cleanedUp: cleanup.cleaned,
32119
+ cleanupFailed: cleanup.failed,
32120
+ note: `UNEXPECTED WORKSPACE ARTIFACTS: the typecheck wrote build artifacts into the workspace despite the tsbuildinfo redirect. This result is NOT a clean observation \u2014 treat the typecheck verdict as unreliable until the artifact leak is understood. ${cleanup.cleaned.length} artifact(s) removed, ${cleanup.failed.length} could not be removed.`
32121
+ });
32122
+ }
32123
+ return typedOk({
32124
+ status: "ok",
32125
+ operation: "typecheck",
32126
+ inspectionClass: built.inspectionClass,
32127
+ exitCode: r.code,
32128
+ output,
32129
+ truncated,
32130
+ note: r.code === 0 ? "compiler completed with no diagnostics; tsbuildinfo was redirected to the OS temp dir \u2014 the workspace is untouched (verified by pre/post fingerprint)" : `compiler exited ${r.code} \u2014 diagnostics above are real type errors (a successful, scoped observation: the run itself worked and wrote nothing to the workspace)`
32131
+ });
32132
+ }
32133
+ function createInspectCommandTool(rootOrDeps) {
32134
+ const deps = typeof rootOrDeps === "string" ? { root: rootOrDeps } : rootOrDeps;
32135
+ const tool = {
32136
+ name: "inspect_command",
32137
+ description: "Allowlisted read-only command inspector for plan/read-only sessions (no shell, no mutations). Pick an operation instead of writing a command: git_status, git_log, git_diff, git_show, git_branch_current, git_ls_files, typecheck, node_version, npm_ls, npm_outdated, npm_view. typecheck runs the project TypeScript compiler with --noEmit and a temp-dir tsbuildinfo redirect, verified by a pre/post workspace fingerprint (inspectionClass: 'project-code-execution' \u2014 you are executing the project's own toolchain, not reading git).",
32138
+ permissions: ["read"],
32139
+ timeoutMs: 9e4,
32140
+ inputSchema: inspectInputSchema,
32141
+ execute: async (input, ctx) => {
32142
+ const op = input;
32143
+ const cwd = ctx?.cwd ?? deps.root;
32144
+ const built = buildInspectCommand(op, { root: deps.root, cwd, tscPath: deps.tscPath, npmCliPath: deps.npmCliPath });
32145
+ if (!built.ok) return typedErr(`INVALID_ARGUMENT: ${built.reason}`);
32146
+ if (op.operation === "typecheck") {
32147
+ const tsc = built.argv[0];
32148
+ try {
32149
+ await fs13.access(tsc);
32150
+ } catch {
32151
+ return typedErr(
32152
+ `TYPESCRIPT_UNAVAILABLE: no TypeScript compiler at ${tsc} \u2014 inspect_command typecheck uses the project toolchain (walk-up to node_modules/typescript/bin/tsc) and refuses to guess. Install dependencies or fall back to read_file/grep_content.`
32153
+ );
32154
+ }
32155
+ return typecheckGuarded(built, deps.root, ctx?.signal);
32156
+ }
32157
+ const r = await runSpawn(built.command, built.argv, { cwd, timeoutMs: SPAWN_TIMEOUT_MS, signal: ctx?.signal });
32158
+ if (r.spawnError) return typedErr(`SPAWN_ERROR: ${op.operation} could not launch: ${r.spawnError}`);
32159
+ const { output, truncated } = capOutput(r.stdout, r.stderr);
32160
+ return typedOk({
32161
+ status: r.timedOut ? "timeout" : "ok",
32162
+ operation: op.operation,
32163
+ inspectionClass: built.inspectionClass,
32164
+ exitCode: r.code,
32165
+ output,
32166
+ truncated,
32167
+ ...r.timedOut ? { note: `command timed out after ${SPAWN_TIMEOUT_MS} ms` } : {}
32168
+ });
32169
+ }
32170
+ };
32171
+ return tool;
32172
+ }
32173
+ var MAX_OUTPUT_CHARS, SPAWN_TIMEOUT_MS, TYPECHECK_TIMEOUT_MS, inspectInputSchema;
32174
+ var init_inspectCommand = __esm({
32175
+ "src/cli/tools/inspectCommand.ts"() {
32176
+ "use strict";
32177
+ init_zod();
32178
+ init_toolTypes();
32179
+ init_inspectTypecheckSafety();
32180
+ MAX_OUTPUT_CHARS = 8 * 1024;
32181
+ SPAWN_TIMEOUT_MS = 85e3;
32182
+ TYPECHECK_TIMEOUT_MS = 85e3;
32183
+ inspectInputSchema = external_exports.discriminatedUnion("operation", [
32184
+ external_exports.object({ operation: external_exports.literal("git_status"), short: external_exports.boolean().optional() }),
32185
+ external_exports.object({
32186
+ operation: external_exports.literal("git_log"),
32187
+ limit: external_exports.number().int().min(1).max(200).optional(),
32188
+ oneline: external_exports.boolean().optional()
32189
+ }),
32190
+ external_exports.object({
32191
+ operation: external_exports.literal("git_diff"),
32192
+ staged: external_exports.boolean().optional(),
32193
+ path: external_exports.string().optional()
32194
+ }),
32195
+ external_exports.object({ operation: external_exports.literal("git_show"), ref: external_exports.string().min(1) }),
32196
+ external_exports.object({ operation: external_exports.literal("git_branch_current") }),
32197
+ external_exports.object({ operation: external_exports.literal("git_ls_files") }),
32198
+ external_exports.object({ operation: external_exports.literal("typecheck"), project: external_exports.string().optional() }),
32199
+ external_exports.object({ operation: external_exports.literal("node_version") }),
32200
+ external_exports.object({ operation: external_exports.literal("npm_ls") }),
32201
+ external_exports.object({ operation: external_exports.literal("npm_outdated") }),
32202
+ external_exports.object({ operation: external_exports.literal("npm_view"), package: external_exports.string().min(1) })
32203
+ ]);
32204
+ }
32205
+ });
32206
+
31802
32207
  // src/cli/lsp/protocol.ts
31803
32208
  function encodeMessage(message) {
31804
32209
  const json2 = JSON.stringify(message);
@@ -31862,6 +32267,10 @@ function fmtLocation(loc, relativeTo) {
31862
32267
  const col = (loc.range?.start?.character ?? 0) + 1;
31863
32268
  return `${rel2}:${line}:${col}`;
31864
32269
  }
32270
+ function degradedNote(provider, file2) {
32271
+ if (provider.serverStatusFor?.(file2) !== "unavailable") return void 0;
32272
+ return "LSP_PROVIDER_DEGRADED: no language server is available for this file (server not installed or failed to start) \u2014 an empty result here is NOT evidence about the symbol; fall back to ast_outline/find_symbol, grep_content or read_file.";
32273
+ }
31865
32274
  function createLspTools(provider, root = process.cwd()) {
31866
32275
  const goToDefinition = {
31867
32276
  name: "go_to_definition",
@@ -31871,9 +32280,11 @@ function createLspTools(provider, root = process.cwd()) {
31871
32280
  execute: async (args) => {
31872
32281
  const a = args;
31873
32282
  const locs = await provider.definition(a.path, a.line - 1, a.column - 1);
32283
+ const degraded = degradedNote(provider, a.path);
31874
32284
  return typedOk({
31875
32285
  definitions: locs.map((l) => fmtLocation(l, root)),
31876
- count: locs.length
32286
+ count: locs.length,
32287
+ ...degraded ? { degraded } : {}
31877
32288
  });
31878
32289
  }
31879
32290
  };
@@ -31885,9 +32296,11 @@ function createLspTools(provider, root = process.cwd()) {
31885
32296
  execute: async (args) => {
31886
32297
  const a = args;
31887
32298
  const locs = await provider.references(a.path, a.line - 1, a.column - 1);
32299
+ const degraded = degradedNote(provider, a.path);
31888
32300
  return typedOk({
31889
32301
  references: locs.map((l) => fmtLocation(l, root)),
31890
- count: locs.length
32302
+ count: locs.length,
32303
+ ...degraded ? { degraded } : {}
31891
32304
  });
31892
32305
  }
31893
32306
  };
@@ -31899,7 +32312,13 @@ function createLspTools(provider, root = process.cwd()) {
31899
32312
  execute: async (args) => {
31900
32313
  const a = args;
31901
32314
  const text = await provider.hover(a.path, a.line - 1, a.column - 1);
31902
- return typedOk({ hover: text ?? "(no hover information)" });
32315
+ const degraded = degradedNote(provider, a.path);
32316
+ return typedOk({
32317
+ // Degraded + no text → the note replaces the neutral message:
32318
+ // "(no hover information)" would be a fake-empty under a dead server.
32319
+ hover: text ?? degraded ?? "(no hover information)",
32320
+ ...degraded ? { degraded } : {}
32321
+ });
31903
32322
  }
31904
32323
  };
31905
32324
  const documentSymbols = {
@@ -31912,9 +32331,11 @@ function createLspTools(provider, root = process.cwd()) {
31912
32331
  execute: async (args) => {
31913
32332
  const a = args;
31914
32333
  const symbols = await provider.documentSymbols(a.path);
32334
+ const degraded = degradedNote(provider, a.path);
31915
32335
  return typedOk({
31916
32336
  symbols: symbols.map((s) => `${s.kind} ${s.name} (line ${s.line})`),
31917
- count: symbols.length
32337
+ count: symbols.length,
32338
+ ...degraded ? { degraded } : {}
31918
32339
  });
31919
32340
  }
31920
32341
  };
@@ -31928,12 +32349,16 @@ function createLspTools(provider, root = process.cwd()) {
31928
32349
  execute: async (args) => {
31929
32350
  const a = args;
31930
32351
  const result = await provider.rename(a.path, a.line - 1, a.column - 1, a.newName);
32352
+ const degraded = degradedNote(provider, a.path);
31931
32353
  if (!result) {
31932
- return typedOk({ preview: "no rename available at this position (symbol not found or not renameable)" });
32354
+ return typedOk({
32355
+ preview: degraded ?? "no rename available at this position (symbol not found or not renameable)"
32356
+ });
31933
32357
  }
31934
32358
  return typedOk({
31935
32359
  totalEdits: result.totalEdits,
31936
- files: result.files.map((f) => `${relativePosix(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
32360
+ files: result.files.map((f) => `${relativePosix(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`),
32361
+ ...degraded ? { degraded } : {}
31937
32362
  });
31938
32363
  }
31939
32364
  };
@@ -32035,9 +32460,9 @@ var init_client = __esm({
32035
32460
  });
32036
32461
 
32037
32462
  // src/cli/lsp/servers.ts
32038
- import path21 from "node:path";
32463
+ import path23 from "node:path";
32039
32464
  function languageIdForFile(file2) {
32040
- const ext = path21.extname(file2).toLowerCase();
32465
+ const ext = path23.extname(file2).toLowerCase();
32041
32466
  const map2 = {
32042
32467
  ".ts": "typescript",
32043
32468
  ".tsx": "typescriptreact",
@@ -32052,7 +32477,7 @@ function languageIdForFile(file2) {
32052
32477
  return map2[ext] ?? "plaintext";
32053
32478
  }
32054
32479
  function serverForFile(file2, servers = LSP_SERVERS) {
32055
- const ext = path21.extname(file2).toLowerCase();
32480
+ const ext = path23.extname(file2).toLowerCase();
32056
32481
  return servers.find((s) => s.extensions.includes(ext)) ?? null;
32057
32482
  }
32058
32483
  function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
@@ -32101,7 +32526,7 @@ var init_servers = __esm({
32101
32526
  });
32102
32527
 
32103
32528
  // src/cli/lsp/manager.ts
32104
- import { spawn as spawn4 } from "node:child_process";
32529
+ import { spawn as spawn6 } from "node:child_process";
32105
32530
  import { readFileSync as readFileSync18 } from "node:fs";
32106
32531
  function processTransport(child) {
32107
32532
  return {
@@ -32237,7 +32662,7 @@ var init_manager = __esm({
32237
32662
  // languages already flagged as unavailable
32238
32663
  constructor(options = {}) {
32239
32664
  this.cwd = options.cwd ?? process.cwd();
32240
- this.spawnImpl = options.spawnImpl ?? spawn4;
32665
+ this.spawnImpl = options.spawnImpl ?? spawn6;
32241
32666
  this.timeoutMs = options.timeoutMs ?? 15e3;
32242
32667
  this.onWarn = options.onWarn ?? ((m) => console.error(m));
32243
32668
  }
@@ -32401,6 +32826,14 @@ var init_manager = __esm({
32401
32826
  null
32402
32827
  );
32403
32828
  }
32829
+ /** See LspProvider.serverStatusFor (WS4). Call AFTER a provider method:
32830
+ * the server entry is lazily created on first use, so the status is only
32831
+ * meaningful once the manager has served that language once. */
32832
+ serverStatusFor(file2) {
32833
+ const cmd = resolveServerCommand(file2, this.cwd);
32834
+ if (!cmd) return "unavailable";
32835
+ return this.servers.get(cmd.language) === null ? "unavailable" : "available";
32836
+ }
32404
32837
  dispose() {
32405
32838
  for (const entry of this.servers.values()) entry?.dispose();
32406
32839
  this.servers.clear();
@@ -32420,31 +32853,69 @@ var init_manager = __esm({
32420
32853
 
32421
32854
  // src/cli/ast/engine.ts
32422
32855
  import { readFile } from "node:fs/promises";
32423
- import path22 from "node:path";
32424
- function isAstSupported(file2) {
32425
- return TS_EXTENSIONS.has(path22.extname(file2).toLowerCase());
32426
- }
32856
+ import path24 from "node:path";
32427
32857
  function loadTs() {
32428
32858
  if (!tsPromise) {
32429
32859
  tsPromise = import("typescript").then((m) => m.default ?? m).catch(() => null);
32430
32860
  }
32431
32861
  return tsPromise;
32432
32862
  }
32433
- async function parseFileSymbols(file2) {
32434
- if (!isAstSupported(file2)) return [];
32863
+ function errMessage(err) {
32864
+ return err instanceof Error ? err.message : String(err);
32865
+ }
32866
+ async function parseFileSymbolsDiag(file2, cwd) {
32867
+ const resolvedPath = path24.isAbsolute(file2) ? file2 : path24.join(cwd ?? process.cwd(), file2);
32868
+ const extension = path24.extname(resolvedPath).toLowerCase();
32869
+ if (!TS_EXTENSIONS.has(extension)) {
32870
+ return {
32871
+ status: "unsupported-extension",
32872
+ extension: extension || "(none)",
32873
+ resolvedPath,
32874
+ recoverable: false,
32875
+ recommendedFallback: "read_file"
32876
+ };
32877
+ }
32435
32878
  const ts = await loadTs();
32436
- if (!ts) return [];
32879
+ if (!ts) {
32880
+ return {
32881
+ status: "typescript-unavailable",
32882
+ resolvedPath,
32883
+ recoverable: true,
32884
+ recommendedFallback: "read_file"
32885
+ };
32886
+ }
32437
32887
  let text;
32438
32888
  try {
32439
- text = await readFile(file2, "utf8");
32440
- } catch {
32441
- return [];
32889
+ text = await readFile(resolvedPath, "utf8");
32890
+ } catch (err) {
32891
+ const code = err?.code;
32892
+ if (code === "ENOENT") {
32893
+ return {
32894
+ status: "file-not-found",
32895
+ resolvedPath,
32896
+ recoverable: false,
32897
+ recommendedFallback: "grep_content"
32898
+ };
32899
+ }
32900
+ return {
32901
+ status: "read-error",
32902
+ resolvedPath,
32903
+ message: errMessage(err),
32904
+ recoverable: true,
32905
+ recommendedFallback: "read_file"
32906
+ };
32442
32907
  }
32443
32908
  let source;
32444
32909
  try {
32445
- source = ts.createSourceFile(path22.basename(file2), text, ts.ScriptTarget.Latest, true);
32446
- } catch {
32447
- return [];
32910
+ source = ts.createSourceFile(path24.basename(resolvedPath), text, ts.ScriptTarget.Latest, true);
32911
+ } catch (err) {
32912
+ return {
32913
+ status: "parse-error",
32914
+ resolvedPath,
32915
+ message: errMessage(err),
32916
+ recoverable: false,
32917
+ recommendedFallback: "read_file"
32918
+ };
32448
32919
  }
32449
32920
  const out = [];
32450
32921
  const lineOf = (pos) => source.getLineAndCharacterOfPosition(pos).line + 1;
@@ -32490,15 +32961,7 @@ async function parseFileSymbols(file2) {
32490
32961
  ts.forEachChild(node, visit);
32491
32962
  };
32492
32963
  visit(source);
32493
- return out;
32494
- }
32495
- async function astOutline(file2) {
32496
- const symbols = await parseFileSymbols(file2);
32497
- return symbols.map(({ text: _text, ...rest }) => rest);
32498
- }
32499
- async function findSymbol(file2, name) {
32500
- const symbols = await parseFileSymbols(file2);
32501
- return symbols.find((s) => s.name === name) ?? null;
32964
+ return { status: "ok", symbols: out };
32502
32965
  }
32503
32966
  var TS_EXTENSIONS, tsPromise;
32504
32967
  var init_engine2 = __esm({
@@ -32509,23 +32972,54 @@ var init_engine2 = __esm({
32509
32972
  });
32510
32973
 
32511
32974
  // src/cli/ast/tools.ts
32512
- function createAstTools() {
32975
+ function degradedPayload(r) {
32976
+ return {
32977
+ status: r.status,
32978
+ resolvedPath: r.resolvedPath,
32979
+ recoverable: r.recoverable,
32980
+ recommendedFallback: r.recommendedFallback
32981
+ };
32982
+ }
32983
+ function degradedNote2(r) {
32984
+ switch (r.status) {
32985
+ case "file-not-found":
32986
+ return `file not found \u2014 looked at ${r.resolvedPath} (relative paths resolve against the workspace root)`;
32987
+ case "typescript-unavailable":
32988
+ return 'TypeScript compiler API unavailable \u2014 the "typescript" package could not be loaded. AST tools need it; disable them with ZELARI_AST=0 or fall back to read_file/grep_content.';
32989
+ case "unsupported-extension":
32990
+ return `unsupported file extension "${r.extension}" \u2014 ast_outline/find_symbol only parse TS/JS files (.ts/.tsx/.js/.jsx/.mjs/.cjs)`;
32991
+ case "read-error":
32992
+ return `could not read ${r.resolvedPath}: ${r.message}`;
32993
+ case "parse-error":
32994
+ return `TypeScript failed to parse ${r.resolvedPath}: ${r.message}`;
32995
+ }
32996
+ }
32997
+ function createAstTools(root) {
32513
32998
  const outline = {
32514
32999
  name: "ast_outline",
32515
- description: "Structural outline of a TS/JS file: every declaration (function, class, method, interface, type, enum, variable) with its line range and whether it's exported. Faster and more precise than reading the whole file to find where things are. TS/JS only.",
33000
+ description: "Structural outline of a TS/JS file: every declaration (function, class, method, interface, type, enum, variable) with its line range and whether it's exported. Faster and more precise than reading the whole file to find where things are. Relative paths resolve against the working directory. TS/JS only.",
32516
33001
  permissions: ["read"],
32517
33002
  inputSchema: external_exports.object({
32518
33003
  path: external_exports.string().min(1).describe("Path to the TS/JS file to outline.")
32519
33004
  }),
32520
- execute: async (args) => {
33005
+ execute: async (args, ctx) => {
32521
33006
  const { path: file2 } = args;
32522
- const symbols = await astOutline(file2);
32523
- if (symbols.length === 0) {
32524
- return typedOk({ symbols: [], note: "no declarations found (or not a TS/JS file / TypeScript unavailable)" });
33007
+ const r = await parseFileSymbolsDiag(file2, ctx?.cwd ?? root);
33008
+ if (r.status !== "ok") {
33009
+ return typedOk({ symbols: [], note: degradedNote2(r), ...degradedPayload(r) });
33010
+ }
33011
+ if (r.symbols.length === 0) {
33012
+ return typedOk({
33013
+ status: "ok",
33014
+ count: 0,
33015
+ symbols: [],
33016
+ note: "file parsed successfully but contains no declarations"
33017
+ });
32525
33018
  }
32526
33019
  return typedOk({
32527
- count: symbols.length,
32528
- symbols: symbols.map(
33020
+ status: "ok",
33021
+ count: r.symbols.length,
33022
+ symbols: r.symbols.map(
32529
33023
  (s) => `${s.exported ? "export " : ""}${s.kind} ${s.name} (lines ${s.line}-${s.endLine})`
32530
33024
  )
32531
33025
  });
@@ -32533,21 +33027,30 @@ function createAstTools() {
32533
33027
  };
32534
33028
  const findSymbolTool = {
32535
33029
  name: "find_symbol",
32536
- description: "Locate a named declaration in a TS/JS file and return its EXACT source text and line range. Use this to grab a function/class/method verbatim so you can edit_file it reliably (node-accurate) instead of guessing the surrounding text. TS/JS only.",
33030
+ 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.",
32537
33031
  permissions: ["read"],
32538
33032
  inputSchema: external_exports.object({
32539
33033
  path: external_exports.string().min(1).describe("Path to the TS/JS file."),
32540
33034
  name: external_exports.string().min(1).describe("The declaration name to find (function/class/method/etc).")
32541
33035
  }),
32542
- execute: async (args) => {
33036
+ execute: async (args, ctx) => {
32543
33037
  const { path: file2, name } = args;
32544
- const sym = await findSymbol(file2, name);
33038
+ const r = await parseFileSymbolsDiag(file2, ctx?.cwd ?? root);
33039
+ if (r.status !== "ok") {
33040
+ return typedOk({ found: false, note: degradedNote2(r), ...degradedPayload(r) });
33041
+ }
33042
+ const sym = r.symbols.find((s) => s.name === name);
32545
33043
  if (!sym) {
32546
- return typedOk({ found: false, note: `no declaration named "${name}" found in ${file2}` });
33044
+ return typedOk({
33045
+ found: false,
33046
+ status: "ok",
33047
+ note: `no declaration named "${name}" found in ${r.symbols.length > 0 ? `${file2} (declarations present: ${[...new Set(r.symbols.map((s) => s.name))].slice(0, 12).join(", ")})` : file2}`
33048
+ });
32547
33049
  }
32548
33050
  const truncated = sym.text.length > MAX_TEXT_CHARS;
32549
33051
  return typedOk({
32550
33052
  found: true,
33053
+ status: "ok",
32551
33054
  kind: sym.kind,
32552
33055
  exported: sym.exported,
32553
33056
  line: sym.line,
@@ -32627,13 +33130,13 @@ var init_store = __esm({
32627
33130
  });
32628
33131
 
32629
33132
  // src/cli/semantic/index.ts
32630
- import { promises as fs12, existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
33133
+ import { promises as fs14, existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
32631
33134
  import { homedir as homedir6 } from "node:os";
32632
- import path23 from "node:path";
32633
- import { createHash as createHash5 } from "node:crypto";
33135
+ import path25 from "node:path";
33136
+ import { createHash as createHash6 } from "node:crypto";
32634
33137
  function getIndexPath(root) {
32635
- const hash3 = createHash5("sha1").update(path23.resolve(root)).digest("hex").slice(0, 16);
32636
- return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
33138
+ const hash3 = createHash6("sha1").update(path25.resolve(root)).digest("hex").slice(0, 16);
33139
+ return process.env.ZELARI_SEMANTIC_FILE ?? path25.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
32637
33140
  }
32638
33141
  async function collectSourceFiles(root, maxFiles = 1500) {
32639
33142
  const out = [];
@@ -32641,7 +33144,7 @@ async function collectSourceFiles(root, maxFiles = 1500) {
32641
33144
  if (out.length >= maxFiles) return;
32642
33145
  let entries;
32643
33146
  try {
32644
- entries = await fs12.readdir(dir, { withFileTypes: true });
33147
+ entries = await fs14.readdir(dir, { withFileTypes: true });
32645
33148
  } catch {
32646
33149
  return;
32647
33150
  }
@@ -32651,11 +33154,11 @@ async function collectSourceFiles(root, maxFiles = 1500) {
32651
33154
  if (entry.isDirectory() && IGNORE_DIRS.has(entry.name)) continue;
32652
33155
  if (entry.isDirectory()) continue;
32653
33156
  }
32654
- const full = path23.join(dir, entry.name);
33157
+ const full = path25.join(dir, entry.name);
32655
33158
  if (entry.isDirectory()) {
32656
33159
  if (IGNORE_DIRS.has(entry.name)) continue;
32657
33160
  await walk2(full);
32658
- } else if (SOURCE_EXTENSIONS.has(path23.extname(entry.name).toLowerCase())) {
33161
+ } else if (SOURCE_EXTENSIONS.has(path25.extname(entry.name).toLowerCase())) {
32659
33162
  out.push(full);
32660
33163
  }
32661
33164
  }
@@ -32671,7 +33174,7 @@ async function buildIndex(files, embed, options) {
32671
33174
  for (const file2 of files) {
32672
33175
  let text;
32673
33176
  try {
32674
- text = await fs12.readFile(file2, "utf8");
33177
+ text = await fs14.readFile(file2, "utf8");
32675
33178
  } catch {
32676
33179
  continue;
32677
33180
  }
@@ -32704,14 +33207,14 @@ async function buildIndex(files, embed, options) {
32704
33207
  }
32705
33208
  async function saveIndex(root, data) {
32706
33209
  const file2 = getIndexPath(root);
32707
- await fs12.mkdir(path23.dirname(file2), { recursive: true });
33210
+ await fs14.mkdir(path25.dirname(file2), { recursive: true });
32708
33211
  const tmp = `${file2}.tmp-${process.pid}`;
32709
- await fs12.writeFile(tmp, JSON.stringify(data), "utf8");
32710
- await fs12.rename(tmp, file2);
33212
+ await fs14.writeFile(tmp, JSON.stringify(data), "utf8");
33213
+ await fs14.rename(tmp, file2);
32711
33214
  }
32712
33215
  function loadIndex(root) {
32713
33216
  const file2 = getIndexPath(root);
32714
- if (!existsSync20(file2)) return null;
33217
+ if (!existsSync21(file2)) return null;
32715
33218
  try {
32716
33219
  const parsed = JSON.parse(readFileSync19(file2, "utf8"));
32717
33220
  if (parsed && Array.isArray(parsed.chunks)) return parsed;
@@ -32856,7 +33359,7 @@ var init_provider = __esm({
32856
33359
  });
32857
33360
 
32858
33361
  // src/cli/semantic/tools.ts
32859
- import path24 from "node:path";
33362
+ import path26 from "node:path";
32860
33363
  function createSemanticTool(deps) {
32861
33364
  const buildEmbedFn = deps.buildEmbedFn ?? buildProviderEmbedFn;
32862
33365
  return {
@@ -32883,7 +33386,7 @@ function createSemanticTool(deps) {
32883
33386
  return typedOk({
32884
33387
  count: res.hits.length,
32885
33388
  results: res.hits.map((h) => ({
32886
- location: `${path24.relative(deps.root, h.file) || h.file}:${h.startLine}-${h.endLine}`,
33389
+ location: `${path26.relative(deps.root, h.file) || h.file}:${h.startLine}-${h.endLine}`,
32887
33390
  score: Number(h.score.toFixed(3)),
32888
33391
  preview: h.text.length > 400 ? `${h.text.slice(0, 400)}\u2026` : h.text
32889
33392
  }))
@@ -32903,7 +33406,7 @@ var init_tools4 = __esm({
32903
33406
 
32904
33407
  // src/cli/browser/driver.ts
32905
33408
  import { createRequire } from "node:module";
32906
- import path25 from "node:path";
33409
+ import path27 from "node:path";
32907
33410
  import { pathToFileURL } from "node:url";
32908
33411
  function asPlaywright(mod) {
32909
33412
  if (!mod || typeof mod !== "object") return null;
@@ -32914,10 +33417,10 @@ function asPlaywright(mod) {
32914
33417
  return null;
32915
33418
  }
32916
33419
  async function loadPlaywright(cwd) {
32917
- const base = cwd && cwd.length > 0 ? path25.resolve(cwd) : void 0;
33420
+ const base = cwd && cwd.length > 0 ? path27.resolve(cwd) : void 0;
32918
33421
  if (base) {
32919
33422
  try {
32920
- const req = createRequire(path25.join(base, "package.json"));
33423
+ const req = createRequire(path27.join(base, "package.json"));
32921
33424
  const resolved = req.resolve("playwright");
32922
33425
  const mod = await import(pathToFileURL(resolved).href);
32923
33426
  const pw = asPlaywright(mod);
@@ -33132,8 +33635,8 @@ var init_driver = __esm({
33132
33635
  });
33133
33636
 
33134
33637
  // src/cli/browser/tools.ts
33135
- import path26 from "node:path";
33136
- import os8 from "node:os";
33638
+ import path28 from "node:path";
33639
+ import os9 from "node:os";
33137
33640
  function createBrowserTool(deps = {}) {
33138
33641
  return {
33139
33642
  name: "browser_check",
@@ -33150,8 +33653,8 @@ function createBrowserTool(deps = {}) {
33150
33653
  }),
33151
33654
  execute: async (args, ctx) => {
33152
33655
  const a = args;
33153
- const dir = deps.screenshotDir ?? os8.tmpdir();
33154
- const screenshotPath = a.screenshot === false ? void 0 : path26.join(dir, `zelari-browser-${Date.now()}.png`);
33656
+ const dir = deps.screenshotDir ?? os9.tmpdir();
33657
+ const screenshotPath = a.screenshot === false ? void 0 : path28.join(dir, `zelari-browser-${Date.now()}.png`);
33155
33658
  const result = await runBrowserCheck(
33156
33659
  {
33157
33660
  url: a.url,
@@ -33243,14 +33746,14 @@ __export(targets_exports, {
33243
33746
  });
33244
33747
  import {
33245
33748
  chmodSync,
33246
- existsSync as existsSync21,
33749
+ existsSync as existsSync22,
33247
33750
  mkdirSync as mkdirSync13,
33248
33751
  readFileSync as readFileSync20,
33249
33752
  writeFileSync as writeFileSync15
33250
33753
  } from "node:fs";
33251
33754
  import { dirname as dirname4, join as join16 } from "node:path";
33252
33755
  import { homedir as homedir7 } from "node:os";
33253
- import { spawn as spawn5 } from "node:child_process";
33756
+ import { spawn as spawn7 } from "node:child_process";
33254
33757
  function getSshTargetsPath() {
33255
33758
  return join16(homedir7(), ".zelari-code", "ssh-targets.json");
33256
33759
  }
@@ -33263,21 +33766,21 @@ function normalizeAuth(auth) {
33263
33766
  return "agent";
33264
33767
  }
33265
33768
  function readSecrets() {
33266
- const path53 = getSshSecretsPath();
33267
- if (!existsSync21(path53)) return {};
33769
+ const path55 = getSshSecretsPath();
33770
+ if (!existsSync22(path55)) return {};
33268
33771
  try {
33269
- return JSON.parse(readFileSync20(path53, "utf8"));
33772
+ return JSON.parse(readFileSync20(path55, "utf8"));
33270
33773
  } catch {
33271
33774
  return {};
33272
33775
  }
33273
33776
  }
33274
33777
  function writeSecrets(data) {
33275
- const path53 = getSshSecretsPath();
33276
- mkdirSync13(dirname4(path53), { recursive: true });
33277
- writeFileSync15(path53, `${JSON.stringify(data, null, 2)}
33778
+ const path55 = getSshSecretsPath();
33779
+ mkdirSync13(dirname4(path55), { recursive: true });
33780
+ writeFileSync15(path55, `${JSON.stringify(data, null, 2)}
33278
33781
  `, "utf8");
33279
33782
  try {
33280
- chmodSync(path53, 384);
33783
+ chmodSync(path55, 384);
33281
33784
  } catch {
33282
33785
  }
33283
33786
  }
@@ -33306,10 +33809,10 @@ function deleteSshPassword(id) {
33306
33809
  writeSecrets({ passwords });
33307
33810
  }
33308
33811
  function readStore2() {
33309
- const path53 = getSshTargetsPath();
33310
- if (!existsSync21(path53)) return [];
33812
+ const path55 = getSshTargetsPath();
33813
+ if (!existsSync22(path55)) return [];
33311
33814
  try {
33312
- const parsed = JSON.parse(readFileSync20(path53, "utf8"));
33815
+ const parsed = JSON.parse(readFileSync20(path55, "utf8"));
33313
33816
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
33314
33817
  return list.filter(
33315
33818
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -33324,11 +33827,11 @@ function readStore2() {
33324
33827
  }
33325
33828
  }
33326
33829
  function writeStore2(targets) {
33327
- const path53 = getSshTargetsPath();
33328
- mkdirSync13(dirname4(path53), { recursive: true });
33830
+ const path55 = getSshTargetsPath();
33831
+ mkdirSync13(dirname4(path55), { recursive: true });
33329
33832
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
33330
33833
  writeFileSync15(
33331
- path53,
33834
+ path55,
33332
33835
  `${JSON.stringify({ targets: clean }, null, 2)}
33333
33836
  `,
33334
33837
  "utf8"
@@ -33480,7 +33983,7 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
33480
33983
  if (!env.DISPLAY) env.DISPLAY = "1";
33481
33984
  env.ZELARI_SSH_ASKPASS_PASS = pass;
33482
33985
  }
33483
- const child = spawn5("ssh", args, {
33986
+ const child = spawn7("ssh", args, {
33484
33987
  windowsHide: true,
33485
33988
  env,
33486
33989
  stdio: ["ignore", "pipe", "pipe"]
@@ -33521,7 +34024,7 @@ function readSshPublicKey(keyOrPubPath) {
33521
34024
  if (!raw) return { ok: false, error: "Empty path" };
33522
34025
  const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
33523
34026
  for (const p3 of candidates) {
33524
- if (!existsSync21(p3)) continue;
34027
+ if (!existsSync22(p3)) continue;
33525
34028
  try {
33526
34029
  const content = readFileSync20(p3, "utf8").trim();
33527
34030
  if (!content) continue;
@@ -33574,11 +34077,11 @@ function formatSshTargetsForPrompt() {
33574
34077
  ];
33575
34078
  for (const t of targets) {
33576
34079
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
33577
- const path53 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
34080
+ const path55 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
33578
34081
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
33579
34082
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
33580
34083
  lines.push(
33581
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path53}${tags}${allow}`
34084
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path55}${tags}${allow}`
33582
34085
  );
33583
34086
  }
33584
34087
  return lines.join("\n");
@@ -33704,26 +34207,26 @@ var init_tools6 = __esm({
33704
34207
  });
33705
34208
 
33706
34209
  // src/cli/workspace/worldModel.ts
33707
- import { promises as fs13 } from "node:fs";
33708
- import path27 from "node:path";
33709
- import { spawn as spawn6 } from "node:child_process";
34210
+ import { promises as fs15 } from "node:fs";
34211
+ import path29 from "node:path";
34212
+ import { spawn as spawn8 } from "node:child_process";
33710
34213
  function worldDir(cwd) {
33711
- return path27.join(cwd, WORLD_DIR_NAME);
34214
+ return path29.join(cwd, WORLD_DIR_NAME);
33712
34215
  }
33713
34216
  async function ensureWorldDir(cwd) {
33714
34217
  const dir = worldDir(cwd);
33715
- await fs13.mkdir(dir, { recursive: true });
34218
+ await fs15.mkdir(dir, { recursive: true });
33716
34219
  return dir;
33717
34220
  }
33718
34221
  async function appendTimeline(cwd, entry) {
33719
34222
  const dir = await ensureWorldDir(cwd);
33720
34223
  const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n";
33721
- await fs13.appendFile(path27.join(dir, TIMELINE_FILE), line, "utf8");
34224
+ await fs15.appendFile(path29.join(dir, TIMELINE_FILE), line, "utf8");
33722
34225
  }
33723
34226
  async function readChecks(cwd) {
33724
- const p3 = path27.join(worldDir(cwd), CHECKS_FILE);
34227
+ const p3 = path29.join(worldDir(cwd), CHECKS_FILE);
33725
34228
  try {
33726
- const raw = await fs13.readFile(p3, "utf8");
34229
+ const raw = await fs15.readFile(p3, "utf8");
33727
34230
  const parsed = JSON.parse(raw);
33728
34231
  return Array.isArray(parsed.checks) ? parsed.checks : [];
33729
34232
  } catch {
@@ -33733,7 +34236,7 @@ async function readChecks(cwd) {
33733
34236
  function runShell(command, cwd, timeoutMs, signal) {
33734
34237
  return new Promise((resolve3) => {
33735
34238
  const isWin = process.platform === "win32";
33736
- const child = spawn6(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
34239
+ const child = spawn8(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
33737
34240
  cwd,
33738
34241
  env: process.env,
33739
34242
  windowsHide: true,
@@ -33796,8 +34299,8 @@ function runShell(command, cwd, timeoutMs, signal) {
33796
34299
  });
33797
34300
  }
33798
34301
  async function runBacktest(cwd, signal) {
33799
- const checksPath = path27.join(worldDir(cwd), CHECKS_FILE);
33800
- const hypothesisPath = path27.join(worldDir(cwd), HYPOTHESIS_FILE);
34302
+ const checksPath = path29.join(worldDir(cwd), CHECKS_FILE);
34303
+ const hypothesisPath = path29.join(worldDir(cwd), HYPOTHESIS_FILE);
33801
34304
  const checks = await readChecks(cwd);
33802
34305
  if (checks.length === 0) {
33803
34306
  return {
@@ -33866,7 +34369,7 @@ var init_worldModel = __esm({
33866
34369
  "use strict";
33867
34370
  init_zod();
33868
34371
  init_toolTypes();
33869
- WORLD_DIR_NAME = path27.join(".zelari", "world");
34372
+ WORLD_DIR_NAME = path29.join(".zelari", "world");
33870
34373
  HYPOTHESIS_FILE = "hypothesis.md";
33871
34374
  CHECKS_FILE = "checks.json";
33872
34375
  TIMELINE_FILE = "timeline.jsonl";
@@ -33883,7 +34386,7 @@ var init_worldModel = __esm({
33883
34386
  execute: async (args, ctx) => {
33884
34387
  try {
33885
34388
  const dir = await ensureWorldDir(ctx.cwd);
33886
- const file2 = path27.join(dir, HYPOTHESIS_FILE);
34389
+ const file2 = path29.join(dir, HYPOTHESIS_FILE);
33887
34390
  if (args.append) {
33888
34391
  const block = `
33889
34392
 
@@ -33891,11 +34394,11 @@ var init_worldModel = __esm({
33891
34394
 
33892
34395
  ${args.content}
33893
34396
  `;
33894
- await fs13.appendFile(file2, block, "utf8");
34397
+ await fs15.appendFile(file2, block, "utf8");
33895
34398
  } else {
33896
- await fs13.writeFile(file2, args.content, "utf8");
34399
+ await fs15.writeFile(file2, args.content, "utf8");
33897
34400
  }
33898
- const st = await fs13.stat(file2);
34401
+ const st = await fs15.stat(file2);
33899
34402
  await appendTimeline(ctx.cwd, { kind: "hypothesis_update", bytes: st.size, append: !!args.append });
33900
34403
  return typedOk({ path: file2, bytes: st.size });
33901
34404
  } catch (err) {
@@ -33922,9 +34425,9 @@ ${args.content}
33922
34425
  execute: async (args, ctx) => {
33923
34426
  try {
33924
34427
  const dir = await ensureWorldDir(ctx.cwd);
33925
- const file2 = path27.join(dir, CHECKS_FILE);
34428
+ const file2 = path29.join(dir, CHECKS_FILE);
33926
34429
  const body = { checks: args.checks };
33927
- await fs13.writeFile(file2, JSON.stringify(body, null, 2) + "\n", "utf8");
34430
+ await fs15.writeFile(file2, JSON.stringify(body, null, 2) + "\n", "utf8");
33928
34431
  await appendTimeline(ctx.cwd, { kind: "checks_set", count: args.checks.length });
33929
34432
  return typedOk({ path: file2, count: args.checks.length });
33930
34433
  } catch (err) {
@@ -33961,8 +34464,8 @@ ${args.content}
33961
34464
  stdoutPreview: "(dryRun)",
33962
34465
  mismatch: "dryRun"
33963
34466
  })),
33964
- hypothesisPath: path27.join(worldDir(ctx.cwd), HYPOTHESIS_FILE),
33965
- checksPath: path27.join(worldDir(ctx.cwd), CHECKS_FILE)
34467
+ hypothesisPath: path29.join(worldDir(ctx.cwd), HYPOTHESIS_FILE),
34468
+ checksPath: path29.join(worldDir(ctx.cwd), CHECKS_FILE)
33966
34469
  });
33967
34470
  }
33968
34471
  const result = await runBacktest(ctx.cwd, ctx.signal);
@@ -33986,7 +34489,7 @@ ${args.content}
33986
34489
  execute: async (args, ctx) => {
33987
34490
  try {
33988
34491
  const dir = await ensureWorldDir(ctx.cwd);
33989
- const file2 = path27.join(dir, TIMELINE_FILE);
34492
+ const file2 = path29.join(dir, TIMELINE_FILE);
33990
34493
  await appendTimeline(ctx.cwd, {
33991
34494
  kind: args.kind,
33992
34495
  summary: args.summary,
@@ -34094,13 +34597,13 @@ __export(folderTrust_exports, {
34094
34597
  untrustFolder: () => untrustFolder
34095
34598
  });
34096
34599
  import { homedir as homedir8 } from "node:os";
34097
- import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
34098
- import path28 from "node:path";
34600
+ import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
34601
+ import path30 from "node:path";
34099
34602
  function trustStorePath() {
34100
- return _overrideStorePath ?? path28.join(homedir8(), ".zelari-code", "trust.json");
34603
+ return _overrideStorePath ?? path30.join(homedir8(), ".zelari-code", "trust.json");
34101
34604
  }
34102
34605
  function normalize(p3) {
34103
- const resolved = path28.resolve(p3);
34606
+ const resolved = path30.resolve(p3);
34104
34607
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
34105
34608
  }
34106
34609
  function readStore3() {
@@ -34116,7 +34619,7 @@ function readStore3() {
34116
34619
  function writeStore3(store4) {
34117
34620
  const p3 = trustStorePath();
34118
34621
  try {
34119
- mkdirSync14(path28.dirname(p3), { recursive: true });
34622
+ mkdirSync14(path30.dirname(p3), { recursive: true });
34120
34623
  writeFileSync16(p3, JSON.stringify(store4, null, 2), "utf8");
34121
34624
  } catch (err) {
34122
34625
  throw new Error(
@@ -34142,7 +34645,7 @@ function isFolderTrusted(folderPath) {
34142
34645
  }
34143
34646
  function trustFolder(folderPath) {
34144
34647
  const store4 = readStore3();
34145
- const normalized = path28.resolve(folderPath);
34648
+ const normalized = path30.resolve(folderPath);
34146
34649
  if (!store4.folders.some((f) => normalize(f.path) === normalize(normalized))) {
34147
34650
  store4.folders.push({ path: normalized, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
34148
34651
  writeStore3(store4);
@@ -34172,7 +34675,7 @@ function getTrustStorePath() {
34172
34675
  return trustStorePath();
34173
34676
  }
34174
34677
  function hasTrustStore() {
34175
- return existsSync22(trustStorePath());
34678
+ return existsSync23(trustStorePath());
34176
34679
  }
34177
34680
  function _setTrustStorePathForTests(p3) {
34178
34681
  _overrideStorePath = p3;
@@ -34256,9 +34759,9 @@ var init_lifecycleHooks = __esm({
34256
34759
  });
34257
34760
 
34258
34761
  // src/cli/toolResultCache.ts
34259
- import { createHash as createHash6 } from "node:crypto";
34260
- import { promises as fs14 } from "node:fs";
34261
- import path29 from "node:path";
34762
+ import { createHash as createHash7 } from "node:crypto";
34763
+ import { promises as fs16 } from "node:fs";
34764
+ import path31 from "node:path";
34262
34765
  function isToolCacheEnabled() {
34263
34766
  const raw = process.env.ZELARI_TOOL_CACHE;
34264
34767
  return raw !== "0" && raw !== "false" && raw !== "off";
@@ -34269,7 +34772,7 @@ function resolveToolCacheTtlMs() {
34269
34772
  return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
34270
34773
  }
34271
34774
  function hashKey(parts) {
34272
- return createHash6("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
34775
+ return createHash7("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
34273
34776
  }
34274
34777
  function resultBytes(result) {
34275
34778
  try {
@@ -34343,9 +34846,9 @@ async function statKey(toolName, input, ctx) {
34343
34846
  if (!input || typeof input !== "object") return null;
34344
34847
  const rawPath = input.path;
34345
34848
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
34346
- const abs = path29.isAbsolute(rawPath) ? rawPath : path29.join(ctx.cwd, rawPath);
34849
+ const abs = path31.isAbsolute(rawPath) ? rawPath : path31.join(ctx.cwd, rawPath);
34347
34850
  try {
34348
- const st = await fs14.stat(abs);
34851
+ const st = await fs16.stat(abs);
34349
34852
  return hashKey({
34350
34853
  tool: toolName,
34351
34854
  args: input,
@@ -34584,8 +35087,17 @@ function createBuiltinToolRegistry(options = {}) {
34584
35087
  description: t.description,
34585
35088
  permissions: t.permissions ?? []
34586
35089
  }));
35090
+ if (readOnly && process.env.ZELARI_INSPECT_COMMAND !== "0") {
35091
+ const inspectTool = createInspectCommandTool(root);
35092
+ registry4.register(withPerm(inspectTool));
35093
+ tools.push({
35094
+ name: inspectTool.name,
35095
+ description: inspectTool.description,
35096
+ permissions: inspectTool.permissions ?? []
35097
+ });
35098
+ }
34587
35099
  if (process.env.ZELARI_AST !== "0") {
34588
- for (const t of createAstTools()) {
35100
+ for (const t of createAstTools(root)) {
34589
35101
  registry4.register(t);
34590
35102
  tools.push({ name: t.name, description: t.description, permissions: t.permissions ?? [] });
34591
35103
  }
@@ -34641,7 +35153,7 @@ function createBuiltinToolRegistry(options = {}) {
34641
35153
  permissions: taskTool.permissions ?? []
34642
35154
  });
34643
35155
  }
34644
- if (!readOnly && process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
35156
+ if (process.env.ZELARI_LSP !== "0" && options.lspProvider !== null) {
34645
35157
  const lspTools = options.lspProvider ? createLspTools(options.lspProvider, root) : createLspTools(getSharedLspManager(root), root);
34646
35158
  for (const t of lspTools) {
34647
35159
  registry4.register(t);
@@ -34927,6 +35439,7 @@ var init_toolRegistry = __esm({
34927
35439
  init_skillTool();
34928
35440
  init_todoTools();
34929
35441
  init_planTaskTools();
35442
+ init_inspectCommand();
34930
35443
  init_tools2();
34931
35444
  init_manager();
34932
35445
  init_tools3();
@@ -34957,21 +35470,21 @@ var init_toolRegistry = __esm({
34957
35470
  });
34958
35471
 
34959
35472
  // src/cli/state/fileStateStore.ts
34960
- import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
34961
- import { promises as fs15 } from "node:fs";
34962
- import * as path30 from "node:path";
35473
+ import { createHash as createHash8, randomUUID as randomUUID2 } from "node:crypto";
35474
+ import { promises as fs17 } from "node:fs";
35475
+ import * as path32 from "node:path";
34963
35476
  function shortId() {
34964
35477
  return randomUUID2().replace(/-/g, "").slice(0, 12);
34965
35478
  }
34966
35479
  async function writeJsonAtomic(filePath, data) {
34967
- await fs15.mkdir(path30.dirname(filePath), { recursive: true });
35480
+ await fs17.mkdir(path32.dirname(filePath), { recursive: true });
34968
35481
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
34969
- await fs15.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
34970
- await fs15.rename(tmp, filePath);
35482
+ await fs17.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
35483
+ await fs17.rename(tmp, filePath);
34971
35484
  }
34972
35485
  async function readJsonFile(filePath) {
34973
35486
  try {
34974
- const raw = await fs15.readFile(filePath, "utf8");
35487
+ const raw = await fs17.readFile(filePath, "utf8");
34975
35488
  return JSON.parse(raw);
34976
35489
  } catch {
34977
35490
  return null;
@@ -35009,7 +35522,7 @@ async function getStateStore(projectRoot, env = process.env) {
35009
35522
  }
35010
35523
  }
35011
35524
  function hashStablePrompt(stable) {
35012
- return createHash7("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
35525
+ return createHash8("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
35013
35526
  }
35014
35527
  var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
35015
35528
  var init_fileStateStore = __esm({
@@ -35025,13 +35538,13 @@ var init_fileStateStore = __esm({
35025
35538
  indexPath = "";
35026
35539
  async init(projectRoot) {
35027
35540
  this.root = projectRoot;
35028
- this.stateDir = path30.join(projectRoot, ".zelari", "state");
35029
- this.commitsDir = path30.join(this.stateDir, "commits");
35030
- this.artifactsDir = path30.join(this.stateDir, "artifacts");
35031
- this.headPath = path30.join(this.stateDir, "HEAD.json");
35032
- this.indexPath = path30.join(this.stateDir, "index.jsonl");
35033
- await fs15.mkdir(this.commitsDir, { recursive: true });
35034
- await fs15.mkdir(this.artifactsDir, { recursive: true });
35541
+ this.stateDir = path32.join(projectRoot, ".zelari", "state");
35542
+ this.commitsDir = path32.join(this.stateDir, "commits");
35543
+ this.artifactsDir = path32.join(this.stateDir, "artifacts");
35544
+ this.headPath = path32.join(this.stateDir, "HEAD.json");
35545
+ this.indexPath = path32.join(this.stateDir, "index.jsonl");
35546
+ await fs17.mkdir(this.commitsDir, { recursive: true });
35547
+ await fs17.mkdir(this.artifactsDir, { recursive: true });
35035
35548
  }
35036
35549
  async commit(input) {
35037
35550
  if (!input.force && input.verification.ran && !input.verification.ok) {
@@ -35042,13 +35555,13 @@ var init_fileStateStore = __esm({
35042
35555
  const discoveries = input.discoveries ?? [];
35043
35556
  const parent = await this.head();
35044
35557
  const id = shortId();
35045
- const artifactRel = path30.join("artifacts", id);
35046
- const artifactAbs = path30.join(this.artifactsDir, id);
35047
- await fs15.mkdir(artifactAbs, { recursive: true });
35558
+ const artifactRel = path32.join("artifacts", id);
35559
+ const artifactAbs = path32.join(this.artifactsDir, id);
35560
+ await fs17.mkdir(artifactAbs, { recursive: true });
35048
35561
  const summary = defaultSummary(input, discoveries);
35049
- await fs15.writeFile(path30.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
35050
- await writeJsonAtomic(path30.join(artifactAbs, "discoveries.json"), discoveries);
35051
- await writeJsonAtomic(path30.join(artifactAbs, "verification.json"), input.verification);
35562
+ await fs17.writeFile(path32.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
35563
+ await writeJsonAtomic(path32.join(artifactAbs, "discoveries.json"), discoveries);
35564
+ await writeJsonAtomic(path32.join(artifactAbs, "verification.json"), input.verification);
35052
35565
  const meta3 = {
35053
35566
  id,
35054
35567
  parentId: parent?.id ?? null,
@@ -35060,16 +35573,16 @@ var init_fileStateStore = __esm({
35060
35573
  workspaceCheckpointId: input.workspaceCheckpointId,
35061
35574
  verification: {
35062
35575
  ...input.verification,
35063
- reportPath: input.verification.reportPath ?? path30.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
35576
+ reportPath: input.verification.reportPath ?? path32.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
35064
35577
  },
35065
35578
  changedPaths: input.changedPaths ?? [],
35066
35579
  stablePromptHash: input.stablePromptHash,
35067
35580
  discoveryCount: discoveries.length,
35068
35581
  artifactDir: artifactRel.replace(/\\/g, "/")
35069
35582
  };
35070
- await writeJsonAtomic(path30.join(this.commitsDir, `${id}.json`), meta3);
35583
+ await writeJsonAtomic(path32.join(this.commitsDir, `${id}.json`), meta3);
35071
35584
  await writeJsonAtomic(this.headPath, { id, updatedAt: meta3.createdAt });
35072
- await fs15.appendFile(this.indexPath, JSON.stringify({ id, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
35585
+ await fs17.appendFile(this.indexPath, JSON.stringify({ id, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
35073
35586
  return stripStored(meta3);
35074
35587
  }
35075
35588
  async head() {
@@ -35078,13 +35591,13 @@ var init_fileStateStore = __esm({
35078
35591
  return this.get(head.id);
35079
35592
  }
35080
35593
  async get(id) {
35081
- const stored = await readJsonFile(path30.join(this.commitsDir, `${id}.json`));
35594
+ const stored = await readJsonFile(path32.join(this.commitsDir, `${id}.json`));
35082
35595
  return stored ? stripStored(stored) : null;
35083
35596
  }
35084
35597
  async list(limit = 20) {
35085
35598
  let raw;
35086
35599
  try {
35087
- raw = await fs15.readFile(this.indexPath, "utf8");
35600
+ raw = await fs17.readFile(this.indexPath, "utf8");
35088
35601
  } catch {
35089
35602
  return [];
35090
35603
  }
@@ -35117,9 +35630,9 @@ var init_fileStateStore = __esm({
35117
35630
  async loadDiscoveries(id) {
35118
35631
  const meta3 = id ? await this.get(id) : await this.head();
35119
35632
  if (!meta3) return [];
35120
- const stored = await readJsonFile(path30.join(this.commitsDir, `${meta3.id}.json`));
35633
+ const stored = await readJsonFile(path32.join(this.commitsDir, `${meta3.id}.json`));
35121
35634
  if (!stored?.artifactDir) return [];
35122
- const discPath = path30.join(this.stateDir, stored.artifactDir, "discoveries.json");
35635
+ const discPath = path32.join(this.stateDir, stored.artifactDir, "discoveries.json");
35123
35636
  return await readJsonFile(discPath) ?? [];
35124
35637
  }
35125
35638
  async materializeContext(id, maxChars = DEFAULT_MATERIALIZE_CHARS) {
@@ -35722,7 +36235,7 @@ __export(conversationContext_exports, {
35722
36235
  setHistory: () => setHistory,
35723
36236
  setLastClarification: () => setLastClarification
35724
36237
  });
35725
- import { existsSync as existsSync23 } from "node:fs";
36238
+ import { existsSync as existsSync24 } from "node:fs";
35726
36239
  import { join as join19 } from "node:path";
35727
36240
  function getHistory() {
35728
36241
  return history;
@@ -35731,7 +36244,7 @@ function setHistory(messages) {
35731
36244
  history = [...messages];
35732
36245
  }
35733
36246
  function compactInPlace(cwd = process.cwd()) {
35734
- const durableStatePresent = existsSync23(join19(cwd, ".zelari", "state", "HEAD.json"));
36247
+ const durableStatePresent = existsSync24(join19(cwd, ".zelari", "state", "HEAD.json"));
35735
36248
  history = compactHistory(history, { durableStatePresent });
35736
36249
  }
35737
36250
  function appendMessages(msgs) {
@@ -36122,7 +36635,7 @@ var claudeProvider_exports = {};
36122
36635
  __export(claudeProvider_exports, {
36123
36636
  createLocalCliProvider: () => createLocalCliProvider
36124
36637
  });
36125
- import { spawn as spawn7 } from "node:child_process";
36638
+ import { spawn as spawn9 } from "node:child_process";
36126
36639
  function waitForExit(child, timeoutMs = 2e3) {
36127
36640
  return new Promise((resolve3) => {
36128
36641
  if (child.exitCode != null) return resolve3(child.exitCode);
@@ -36151,7 +36664,7 @@ function createLocalCliProvider(opts = {}) {
36151
36664
  );
36152
36665
  }
36153
36666
  }
36154
- const spawnFn = opts.spawnFn ?? spawn7;
36667
+ const spawnFn = opts.spawnFn ?? spawn9;
36155
36668
  let child;
36156
36669
  try {
36157
36670
  child = spawnFn(cli, args, {
@@ -36246,12 +36759,12 @@ var init_claudeProvider = __esm({
36246
36759
  });
36247
36760
 
36248
36761
  // src/cli/workspace/projectInstructions.ts
36249
- import { existsSync as existsSync24, readFileSync as readFileSync22 } from "node:fs";
36762
+ import { existsSync as existsSync25, readFileSync as readFileSync22 } from "node:fs";
36250
36763
  import { join as join20 } from "node:path";
36251
36764
  function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
36252
36765
  for (const name of CANDIDATES) {
36253
36766
  const full = join20(projectRoot, name);
36254
- if (!existsSync24(full)) continue;
36767
+ if (!existsSync25(full)) continue;
36255
36768
  try {
36256
36769
  let raw = readFileSync22(full, "utf8");
36257
36770
  raw = raw.replace(/\r\n/g, "\n").trim();
@@ -36297,7 +36810,7 @@ __export(workspaceSummary_exports, {
36297
36810
  buildWorkspaceSummary: () => buildWorkspaceSummary,
36298
36811
  buildZelariReadHint: () => buildZelariReadHint
36299
36812
  });
36300
- import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
36813
+ import { existsSync as existsSync26, readFileSync as readFileSync23, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
36301
36814
  import { join as join21, relative } from "node:path";
36302
36815
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
36303
36816
  const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
@@ -36332,7 +36845,7 @@ function formatTaskLine(t) {
36332
36845
  function buildPlanSummary(projectRoot = process.cwd(), options) {
36333
36846
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
36334
36847
  const planPath = join21(zelariRoot, "plan.json");
36335
- if (!existsSync25(planPath)) return null;
36848
+ if (!existsSync26(planPath)) return null;
36336
36849
  let plan;
36337
36850
  try {
36338
36851
  plan = JSON.parse(readFileSync23(planPath, "utf8"));
@@ -36475,7 +36988,7 @@ function pickNextTask(open) {
36475
36988
  }
36476
36989
  function buildZelariReadHint(projectRoot = process.cwd()) {
36477
36990
  const planPath = join21(resolveWorkspaceRoot(projectRoot), "plan.json");
36478
- if (!existsSync25(planPath)) return "";
36991
+ if (!existsSync26(planPath)) return "";
36479
36992
  return [
36480
36993
  "# Council workspace detected (.zelari/) \u2014 DRAFT vault",
36481
36994
  "`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
@@ -36491,7 +37004,7 @@ function safeProjectName(root) {
36491
37004
  }
36492
37005
  function readPackageJson(projectRoot) {
36493
37006
  const p3 = join21(projectRoot, "package.json");
36494
- if (!existsSync25(p3)) return null;
37007
+ if (!existsSync26(p3)) return null;
36495
37008
  try {
36496
37009
  return JSON.parse(readFileSync23(p3, "utf8"));
36497
37010
  } catch {
@@ -36595,12 +37108,12 @@ var init_workspaceSummary = __esm({
36595
37108
  });
36596
37109
 
36597
37110
  // src/cli/workspace/buildLessonsSummary.ts
36598
- import { existsSync as existsSync26 } from "node:fs";
37111
+ import { existsSync as existsSync27 } from "node:fs";
36599
37112
  import { join as join22 } from "node:path";
36600
37113
  function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
36601
37114
  if (process.env["ZELARI_LESSONS"] === "0") return null;
36602
37115
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
36603
- if (!existsSync26(join22(zelariRoot, "lessons.jsonl"))) return null;
37116
+ if (!existsSync27(join22(zelariRoot, "lessons.jsonl"))) return null;
36604
37117
  const lessons = recallLessons(zelariRoot, {
36605
37118
  maxLessons: 5,
36606
37119
  maxBytes: 2048,
@@ -36621,7 +37134,7 @@ var composeContext_exports = {};
36621
37134
  __export(composeContext_exports, {
36622
37135
  composeProjectContext: () => composeProjectContext
36623
37136
  });
36624
- import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
37137
+ import { existsSync as existsSync28, readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
36625
37138
  import { join as join23 } from "node:path";
36626
37139
  function cap2(text, max, label) {
36627
37140
  if (!text || text.length <= max) return { text: text || "", truncated: false };
@@ -36634,13 +37147,13 @@ function cap2(text, max, label) {
36634
37147
  }
36635
37148
  function buildDesignIndex(projectRoot, maxChars) {
36636
37149
  const root = resolveWorkspaceRoot(projectRoot);
36637
- if (!existsSync27(root)) return "";
37150
+ if (!existsSync28(root)) return "";
36638
37151
  const lines = [
36639
37152
  "# Design vault index (.zelari/) \u2014 HYPOTHESES only",
36640
37153
  "Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
36641
37154
  ];
36642
37155
  const docsDir = join23(root, "docs");
36643
- if (existsSync27(docsDir)) {
37156
+ if (existsSync28(docsDir)) {
36644
37157
  try {
36645
37158
  const docs = readdirSync7(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
36646
37159
  if (docs.length > 0) {
@@ -36654,12 +37167,12 @@ function buildDesignIndex(projectRoot, maxChars) {
36654
37167
  }
36655
37168
  }
36656
37169
  for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
36657
- if (existsSync27(join23(root, name))) {
37170
+ if (existsSync28(join23(root, name))) {
36658
37171
  lines.push(`- .zelari/${name} present`);
36659
37172
  }
36660
37173
  }
36661
37174
  const decisionsDir = join23(root, "decisions");
36662
- if (existsSync27(decisionsDir)) {
37175
+ if (existsSync28(decisionsDir)) {
36663
37176
  try {
36664
37177
  const n = readdirSync7(decisionsDir).filter((f) => f.endsWith(".md")).length;
36665
37178
  if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
@@ -36761,15 +37274,15 @@ function composeProjectContext(input) {
36761
37274
  function readDurableHeadSync(projectRoot) {
36762
37275
  try {
36763
37276
  const headPath = join23(projectRoot, ".zelari", "state", "HEAD.json");
36764
- if (!existsSync27(headPath)) return "";
37277
+ if (!existsSync28(headPath)) return "";
36765
37278
  const head = JSON.parse(readFileSync24(headPath, "utf8"));
36766
37279
  if (!head?.id) return "";
36767
37280
  const metaPath = join23(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
36768
- if (!existsSync27(metaPath)) return "";
37281
+ if (!existsSync28(metaPath)) return "";
36769
37282
  const meta3 = JSON.parse(readFileSync24(metaPath, "utf8"));
36770
37283
  const discPath = meta3.artifactDir ? join23(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join23(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
36771
37284
  let discoveries = [];
36772
- if (existsSync27(discPath)) {
37285
+ if (existsSync28(discPath)) {
36773
37286
  discoveries = JSON.parse(readFileSync24(discPath, "utf8"));
36774
37287
  }
36775
37288
  const reusable = discoveries.filter((d) => d.reusable !== false);
@@ -36803,11 +37316,11 @@ var planDetect_exports = {};
36803
37316
  __export(planDetect_exports, {
36804
37317
  hasWorkspacePlan: () => hasWorkspacePlan
36805
37318
  });
36806
- import { existsSync as existsSync28, readFileSync as readFileSync25 } from "node:fs";
37319
+ import { existsSync as existsSync29, readFileSync as readFileSync25 } from "node:fs";
36807
37320
  import { join as join24 } from "node:path";
36808
37321
  function hasWorkspacePlan(projectRoot = process.cwd()) {
36809
37322
  const planPath = join24(resolveWorkspaceRoot(projectRoot), "plan.json");
36810
- if (!existsSync28(planPath)) return false;
37323
+ if (!existsSync29(planPath)) return false;
36811
37324
  try {
36812
37325
  const parsed = JSON.parse(readFileSync25(planPath, "utf8"));
36813
37326
  return Array.isArray(parsed.phases) && parsed.phases.length > 0;
@@ -36868,7 +37381,7 @@ __export(stubs_exports, {
36868
37381
  resolveWorkspaceRoot: () => resolveWorkspaceRoot
36869
37382
  });
36870
37383
  import {
36871
- existsSync as existsSync29,
37384
+ existsSync as existsSync30,
36872
37385
  readdirSync as readdirSync8,
36873
37386
  writeFileSync as writeFileSync17,
36874
37387
  readFileSync as readFileSync26,
@@ -36889,7 +37402,7 @@ function planJsonPath(ctx) {
36889
37402
  }
36890
37403
  function readPlan(ctx) {
36891
37404
  const jsonPath = planJsonPath(ctx);
36892
- if (existsSync29(jsonPath)) {
37405
+ if (existsSync30(jsonPath)) {
36893
37406
  try {
36894
37407
  const parsed = JSON.parse(
36895
37408
  readFileSync26(jsonPath, "utf8")
@@ -36904,8 +37417,8 @@ function readPlan(ctx) {
36904
37417
  } catch {
36905
37418
  }
36906
37419
  }
36907
- const path53 = workspaceFile(ctx.rootDir, "plan");
36908
- const doc = ctx.storage.readIfExists(path53);
37420
+ const path55 = workspaceFile(ctx.rootDir, "plan");
37421
+ const doc = ctx.storage.readIfExists(path55);
36909
37422
  if (!doc) return { phases: [], tasks: [], milestones: [] };
36910
37423
  const meta3 = doc.meta;
36911
37424
  return {
@@ -36999,7 +37512,7 @@ function renderPlanBody(summary) {
36999
37512
  }
37000
37513
  function nextAdrId(ctx) {
37001
37514
  const decisionsDir = join25(ctx.rootDir, "decisions");
37002
- if (!existsSync29(decisionsDir)) return "001";
37515
+ if (!existsSync30(decisionsDir)) return "001";
37003
37516
  const existing = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
37004
37517
  const max = existing.length === 0 ? 0 : Math.max(...existing);
37005
37518
  return String(max + 1).padStart(3, "0");
@@ -37083,7 +37596,7 @@ function addMilestoneRecord(ctx, summary, input) {
37083
37596
  dueDate: input.dueDate,
37084
37597
  targetVersion: version2
37085
37598
  });
37086
- const path53 = join25(ctx.rootDir, "milestones", `${id}.md`);
37599
+ const path55 = join25(ctx.rootDir, "milestones", `${id}.md`);
37087
37600
  const meta3 = {
37088
37601
  kind: "milestone",
37089
37602
  id,
@@ -37100,7 +37613,7 @@ function addMilestoneRecord(ctx, summary, input) {
37100
37613
  `Target version: ${version2}`,
37101
37614
  ""
37102
37615
  ].join("\n");
37103
- ctx.storage.write(path53, meta3, body);
37616
+ ctx.storage.write(path55, meta3, body);
37104
37617
  return { id, created: true };
37105
37618
  }
37106
37619
  function readPlanSummary(ctx) {
@@ -37304,7 +37817,7 @@ function addIdeaStub(ctx) {
37304
37817
  const tags = args["tags"] ?? [];
37305
37818
  const category = args["category"] ?? "General";
37306
37819
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
37307
- const path53 = workspaceArtifact(ctx.rootDir, "decisions", id);
37820
+ const path55 = workspaceArtifact(ctx.rootDir, "decisions", id);
37308
37821
  const meta3 = {
37309
37822
  kind: "adr",
37310
37823
  status: "proposed",
@@ -37330,7 +37843,7 @@ function addIdeaStub(ctx) {
37330
37843
  ...consequences.map((c) => `- ${c}`),
37331
37844
  ""
37332
37845
  ].join("\n");
37333
- ctx.storage.write(path53, meta3, body);
37846
+ ctx.storage.write(path55, meta3, body);
37334
37847
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
37335
37848
  });
37336
37849
  }
@@ -37412,14 +37925,14 @@ function createDocumentStub(ctx) {
37412
37925
  ctx.storage.write(risksPath, riskMeta, content);
37413
37926
  return `Document "${title}" created at risks.md (workspace root).`;
37414
37927
  }
37415
- const path53 = workspaceArtifact(ctx.rootDir, "docs", slug);
37928
+ const path55 = workspaceArtifact(ctx.rootDir, "docs", slug);
37416
37929
  const meta3 = {
37417
37930
  kind: "doc",
37418
37931
  id: slug,
37419
37932
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
37420
37933
  tags
37421
37934
  };
37422
- ctx.storage.write(path53, meta3, content);
37935
+ ctx.storage.write(path55, meta3, content);
37423
37936
  return `Document "${title}" created at docs/${slug}.md.`;
37424
37937
  });
37425
37938
  }
@@ -37455,7 +37968,7 @@ function searchDocumentsStub(ctx) {
37455
37968
  ];
37456
37969
  const results = [];
37457
37970
  for (const file2 of files) {
37458
- if (!existsSync29(file2)) continue;
37971
+ if (!existsSync30(file2)) continue;
37459
37972
  const raw = readFileSync26(file2, "utf8");
37460
37973
  const content = raw.toLowerCase();
37461
37974
  let idx = -1;
@@ -37636,21 +38149,21 @@ __export(updater_exports, {
37636
38149
  resolveBundledNpmCli: () => resolveBundledNpmCli
37637
38150
  });
37638
38151
  import { createRequire as createRequire2 } from "node:module";
37639
- import { spawn as spawn8 } from "node:child_process";
37640
- import { existsSync as existsSync30 } from "node:fs";
37641
- import path31 from "node:path";
38152
+ import { spawn as spawn10 } from "node:child_process";
38153
+ import { existsSync as existsSync31 } from "node:fs";
38154
+ import path33 from "node:path";
37642
38155
  import { fileURLToPath } from "node:url";
37643
38156
  function resolveBundledNpmCli(execPath = process.execPath) {
37644
- const dir = path31.dirname(execPath);
38157
+ const dir = path33.dirname(execPath);
37645
38158
  const candidates = [
37646
38159
  // Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
37647
- path31.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
38160
+ path33.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
37648
38161
  // POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
37649
- path31.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
38162
+ path33.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
37650
38163
  ];
37651
38164
  for (const candidate of candidates) {
37652
38165
  try {
37653
- if (existsSync30(candidate)) return candidate;
38166
+ if (existsSync31(candidate)) return candidate;
37654
38167
  } catch {
37655
38168
  }
37656
38169
  }
@@ -37663,7 +38176,7 @@ function looksLikeBrokenShim(exitCode, output) {
37663
38176
  }
37664
38177
  function getCurrentVersion() {
37665
38178
  try {
37666
- const pkgPath = path31.resolve(__dirname2, "..", "..", "package.json");
38179
+ const pkgPath = path33.resolve(__dirname2, "..", "..", "package.json");
37667
38180
  const pkg = require2(pkgPath);
37668
38181
  return pkg.version;
37669
38182
  } catch {
@@ -37723,7 +38236,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
37723
38236
  updateAvailable: cmp < 0
37724
38237
  };
37725
38238
  }
37726
- async function performUpdate(packageName = "zelari-code", executor = spawn8, resolveNpmCli = resolveBundledNpmCli) {
38239
+ async function performUpdate(packageName = "zelari-code", executor = spawn10, resolveNpmCli = resolveBundledNpmCli) {
37727
38240
  const args = ["install", "-g", `${packageName}@latest`];
37728
38241
  const primary = await runNpm(executor, args, "shim");
37729
38242
  if (primary.ok) return primary;
@@ -37775,13 +38288,13 @@ var init_updater = __esm({
37775
38288
  "use strict";
37776
38289
  init_cmdline();
37777
38290
  require2 = createRequire2(import.meta.url);
37778
- __dirname2 = path31.dirname(fileURLToPath(import.meta.url));
38291
+ __dirname2 = path33.dirname(fileURLToPath(import.meta.url));
37779
38292
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
37780
38293
  }
37781
38294
  });
37782
38295
 
37783
38296
  // src/cli/mcp/mcpClient.ts
37784
- import { spawn as spawn9 } from "node:child_process";
38297
+ import { spawn as spawn11 } from "node:child_process";
37785
38298
  var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
37786
38299
  var init_mcpClient = __esm({
37787
38300
  "src/cli/mcp/mcpClient.ts"() {
@@ -37809,10 +38322,10 @@ var init_mcpClient = __esm({
37809
38322
  env: { ...process.env, ...this.config.env ?? {} },
37810
38323
  windowsHide: true
37811
38324
  };
37812
- const child = process.platform === "win32" ? spawn9(buildCmdLine(this.config.command, this.config.args ?? []), {
38325
+ const child = process.platform === "win32" ? spawn11(buildCmdLine(this.config.command, this.config.args ?? []), {
37813
38326
  ...spawnOpts,
37814
38327
  shell: true
37815
- }) : spawn9(this.config.command, this.config.args ?? [], spawnOpts);
38328
+ }) : spawn11(this.config.command, this.config.args ?? [], spawnOpts);
37816
38329
  this.child = child;
37817
38330
  child.stdout.setEncoding("utf8");
37818
38331
  child.stdout.on("data", (chunk) => this.onStdout(chunk));
@@ -37950,7 +38463,7 @@ var init_mcpClient = __esm({
37950
38463
 
37951
38464
  // src/cli/mcp/mcpConfigIo.ts
37952
38465
  import {
37953
- existsSync as existsSync31,
38466
+ existsSync as existsSync32,
37954
38467
  mkdirSync as mkdirSync16,
37955
38468
  readFileSync as readFileSync27,
37956
38469
  writeFileSync as writeFileSync18
@@ -37963,10 +38476,10 @@ function getUserMcpPath() {
37963
38476
  function getProjectMcpPath(projectRoot) {
37964
38477
  return join26(projectRoot, ".zelari", "mcp.json");
37965
38478
  }
37966
- function readFile2(path53) {
37967
- if (!existsSync31(path53)) return {};
38479
+ function readFile2(path55) {
38480
+ if (!existsSync32(path55)) return {};
37968
38481
  try {
37969
- const parsed = JSON.parse(readFileSync27(path53, "utf8"));
38482
+ const parsed = JSON.parse(readFileSync27(path55, "utf8"));
37970
38483
  const out = {};
37971
38484
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
37972
38485
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -37982,10 +38495,10 @@ function readFile2(path53) {
37982
38495
  return {};
37983
38496
  }
37984
38497
  }
37985
- function writeFile(path53, servers) {
37986
- mkdirSync16(dirname7(path53), { recursive: true });
38498
+ function writeFile(path55, servers) {
38499
+ mkdirSync16(dirname7(path55), { recursive: true });
37987
38500
  const body = { mcpServers: servers };
37988
- writeFileSync18(path53, `${JSON.stringify(body, null, 2)}
38501
+ writeFileSync18(path55, `${JSON.stringify(body, null, 2)}
37989
38502
  `, "utf8");
37990
38503
  }
37991
38504
  function listMcpServers(projectRoot) {
@@ -38018,9 +38531,9 @@ function upsertMcpServer(opts) {
38018
38531
  if (!opts.config.command?.trim()) {
38019
38532
  return { ok: false, error: "command is required" };
38020
38533
  }
38021
- let path53;
38534
+ let path55;
38022
38535
  if (opts.scope === "user") {
38023
- path53 = getUserMcpPath();
38536
+ path55 = getUserMcpPath();
38024
38537
  } else {
38025
38538
  const root = opts.projectRoot?.trim();
38026
38539
  if (!root) {
@@ -38029,30 +38542,30 @@ function upsertMcpServer(opts) {
38029
38542
  error: "projectRoot required for project scope (Open Folder first)"
38030
38543
  };
38031
38544
  }
38032
- path53 = getProjectMcpPath(root);
38545
+ path55 = getProjectMcpPath(root);
38033
38546
  }
38034
- const current = readFile2(path53);
38547
+ const current = readFile2(path55);
38035
38548
  current[name] = {
38036
38549
  command: opts.config.command.trim(),
38037
38550
  args: opts.config.args,
38038
38551
  env: opts.config.env,
38039
38552
  enabled: opts.config.enabled !== false
38040
38553
  };
38041
- writeFile(path53, current);
38042
- return { ok: true, path: path53 };
38554
+ writeFile(path55, current);
38555
+ return { ok: true, path: path55 };
38043
38556
  }
38044
38557
  function removeMcpServer(opts) {
38045
- const path53 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
38046
- if (!path53) {
38558
+ const path55 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
38559
+ if (!path55) {
38047
38560
  return { ok: false, error: "projectRoot required for project scope" };
38048
38561
  }
38049
- const current = readFile2(path53);
38562
+ const current = readFile2(path55);
38050
38563
  if (!(opts.name in current)) {
38051
- return { ok: false, error: `Server "${opts.name}" not found in ${path53}` };
38564
+ return { ok: false, error: `Server "${opts.name}" not found in ${path55}` };
38052
38565
  }
38053
38566
  delete current[opts.name];
38054
- writeFile(path53, current);
38055
- return { ok: true, path: path53 };
38567
+ writeFile(path55, current);
38568
+ return { ok: true, path: path55 };
38056
38569
  }
38057
38570
  var init_mcpConfigIo = __esm({
38058
38571
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -38194,7 +38707,7 @@ __export(mcpManager_exports, {
38194
38707
  readMcpConfig: () => readMcpConfig,
38195
38708
  registerMcpTools: () => registerMcpTools
38196
38709
  });
38197
- import { existsSync as existsSync32, readFileSync as readFileSync28 } from "node:fs";
38710
+ import { existsSync as existsSync33, readFileSync as readFileSync28 } from "node:fs";
38198
38711
  import { join as join27 } from "node:path";
38199
38712
  import { homedir as homedir11 } from "node:os";
38200
38713
  function readMcpConfig(projectRoot = process.cwd(), opts) {
@@ -38207,7 +38720,7 @@ function readMcpConfig(projectRoot = process.cwd(), opts) {
38207
38720
  paths.push(join27(projectRoot, ".zelari", "mcp.json"));
38208
38721
  }
38209
38722
  for (const p3 of paths) {
38210
- if (!existsSync32(p3)) continue;
38723
+ if (!existsSync33(p3)) continue;
38211
38724
  try {
38212
38725
  const parsed = JSON.parse(readFileSync28(p3, "utf8"));
38213
38726
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
@@ -38223,7 +38736,7 @@ async function ensureLoaded(projectRoot) {
38223
38736
  if (state2.loaded) return;
38224
38737
  state2.loaded = true;
38225
38738
  const trusted = isFolderTrusted(projectRoot);
38226
- if (!trusted && existsSync32(join27(projectRoot, ".zelari", "mcp.json"))) {
38739
+ if (!trusted && existsSync33(join27(projectRoot, ".zelari", "mcp.json"))) {
38227
38740
  state2.warnings.push(
38228
38741
  "[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
38229
38742
  );
@@ -38448,15 +38961,15 @@ __export(agentsMd_exports, {
38448
38961
  serializeAgentsMd: () => serializeAgentsMd,
38449
38962
  updateAgentsMd: () => updateAgentsMd
38450
38963
  });
38451
- import { existsSync as existsSync33, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "node:fs";
38452
- import { createHash as createHash8 } from "node:crypto";
38964
+ import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "node:fs";
38965
+ import { createHash as createHash9 } from "node:crypto";
38453
38966
  import { join as join28 } from "node:path";
38454
38967
  import { readFile as readFile3 } from "node:fs/promises";
38455
38968
  async function readPackageJson2(projectRoot) {
38456
- const path53 = join28(projectRoot, "package.json");
38457
- if (!existsSync33(path53)) return null;
38969
+ const path55 = join28(projectRoot, "package.json");
38970
+ if (!existsSync34(path55)) return null;
38458
38971
  try {
38459
- return JSON.parse(await readFile3(path53, "utf8"));
38972
+ return JSON.parse(await readFile3(path55, "utf8"));
38460
38973
  } catch {
38461
38974
  return null;
38462
38975
  }
@@ -38479,7 +38992,7 @@ async function genTechStack(ctx) {
38479
38992
  }
38480
38993
  async function genDecisions(ctx) {
38481
38994
  const decisionsDir = join28(ctx.rootDir, "decisions");
38482
- if (!existsSync33(decisionsDir)) return "_No ADRs yet._";
38995
+ if (!existsSync34(decisionsDir)) return "_No ADRs yet._";
38483
38996
  const files = ctx.storage.listMarkdown(decisionsDir).sort();
38484
38997
  const accepted = [];
38485
38998
  const proposed = [];
@@ -38505,7 +39018,7 @@ async function genDecisions(ctx) {
38505
39018
  async function genConventions(ctx) {
38506
39019
  const lines = [];
38507
39020
  const claudeMd = join28(ctx.projectRoot, "CLAUDE.MD");
38508
- if (existsSync33(claudeMd)) {
39021
+ if (existsSync34(claudeMd)) {
38509
39022
  const content = readFileSync29(claudeMd, "utf8");
38510
39023
  const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
38511
39024
  if (match) {
@@ -38538,9 +39051,9 @@ async function genBuild(ctx) {
38538
39051
  ].join("\n");
38539
39052
  }
38540
39053
  async function genOpenQuestions(ctx) {
38541
- const path53 = join28(ctx.rootDir, "risks.md");
38542
- if (!existsSync33(path53)) return "_No open questions._";
38543
- const content = readFileSync29(path53, "utf8");
39054
+ const path55 = join28(ctx.rootDir, "risks.md");
39055
+ if (!existsSync34(path55)) return "_No open questions._";
39056
+ const content = readFileSync29(path55, "utf8");
38544
39057
  const lines = content.split("\n");
38545
39058
  const questions = [];
38546
39059
  let currentTitle = "";
@@ -38615,7 +39128,7 @@ function titleCase(id) {
38615
39128
  }
38616
39129
  async function updateAgentsMd(ctx, projectRoot) {
38617
39130
  const agentsPath = join28(projectRoot, "AGENTS.MD");
38618
- if (existsSync33(agentsPath)) {
39131
+ if (existsSync34(agentsPath)) {
38619
39132
  const content = readFileSync29(agentsPath, "utf8");
38620
39133
  const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
38621
39134
  if (!hasAnyMarker) {
@@ -38631,7 +39144,7 @@ async function updateAgentsMd(ctx, projectRoot) {
38631
39144
  newSections.set(id, await GENERATORS[id](ctx));
38632
39145
  }
38633
39146
  let manualContent = "";
38634
- if (existsSync33(agentsPath)) {
39147
+ if (existsSync34(agentsPath)) {
38635
39148
  const { manualBlocks } = parseAgentsMd(readFileSync29(agentsPath, "utf8"));
38636
39149
  manualContent = manualBlocks.after;
38637
39150
  } else {
@@ -38648,7 +39161,7 @@ async function updateAgentsMd(ctx, projectRoot) {
38648
39161
  ""
38649
39162
  ].join("\n");
38650
39163
  }
38651
- const oldContent = existsSync33(agentsPath) ? readFileSync29(agentsPath, "utf8") : "";
39164
+ const oldContent = existsSync34(agentsPath) ? readFileSync29(agentsPath, "utf8") : "";
38652
39165
  const { sections: oldSections } = parseAgentsMd(oldContent);
38653
39166
  const changedSections = [];
38654
39167
  for (const id of AUTO_SECTIONS) {
@@ -38664,7 +39177,7 @@ async function updateAgentsMd(ctx, projectRoot) {
38664
39177
  return { changed: true, sections: changedSections };
38665
39178
  }
38666
39179
  function hash2(s) {
38667
- return createHash8("sha256").update(s).digest("hex").slice(0, 16);
39180
+ return createHash9("sha256").update(s).digest("hex").slice(0, 16);
38668
39181
  }
38669
39182
  var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
38670
39183
  var init_agentsMd = __esm({
@@ -38785,11 +39298,11 @@ var init_completeDesign = __esm({
38785
39298
  });
38786
39299
 
38787
39300
  // src/cli/workspace/planDriftCheck.ts
38788
- import { existsSync as existsSync34, readFileSync as readFileSync30, readdirSync as readdirSync9, statSync as statSync5, writeFileSync as writeFileSync20 } from "node:fs";
39301
+ import { existsSync as existsSync35, readFileSync as readFileSync30, readdirSync as readdirSync9, statSync as statSync5, writeFileSync as writeFileSync20 } from "node:fs";
38789
39302
  import { join as join29 } from "node:path";
38790
39303
  function findCanonicalDoc(rootDir) {
38791
39304
  const docsDir = join29(rootDir, "docs");
38792
- if (!existsSync34(docsDir)) return null;
39305
+ if (!existsSync35(docsDir)) return null;
38793
39306
  const candidates = readdirSync9(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync5(join29(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
38794
39307
  return candidates.length > 0 ? candidates[0].f : null;
38795
39308
  }
@@ -38814,9 +39327,9 @@ function versionKey(value) {
38814
39327
  function firstString2(v) {
38815
39328
  return typeof v === "string" && v.trim().length > 0 ? v : null;
38816
39329
  }
38817
- function readFileSyncSafe(path53) {
39330
+ function readFileSyncSafe(path55) {
38818
39331
  try {
38819
- return readFileSync30(path53, "utf8");
39332
+ return readFileSync30(path55, "utf8");
38820
39333
  } catch {
38821
39334
  return null;
38822
39335
  }
@@ -38826,7 +39339,7 @@ async function runPlanDriftCheck(rootDir) {
38826
39339
  return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
38827
39340
  }
38828
39341
  const planPath = join29(rootDir, "plan.json");
38829
- if (!existsSync34(planPath)) {
39342
+ if (!existsSync35(planPath)) {
38830
39343
  return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
38831
39344
  }
38832
39345
  let plan;
@@ -38956,8 +39469,8 @@ var init_planDriftCheck = __esm({
38956
39469
  });
38957
39470
 
38958
39471
  // src/cli/workspace/projectSmoke.ts
38959
- import { spawn as spawn10 } from "node:child_process";
38960
- import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
39472
+ import { spawn as spawn12 } from "node:child_process";
39473
+ import { existsSync as existsSync36, readFileSync as readFileSync31 } from "node:fs";
38961
39474
  import { join as join30 } from "node:path";
38962
39475
  function pickSmokeScript(scripts) {
38963
39476
  if (!scripts) return null;
@@ -38971,7 +39484,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS2) {
38971
39484
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
38972
39485
  }
38973
39486
  const pkgPath = join30(projectRoot, "package.json");
38974
- if (!existsSync35(pkgPath)) {
39487
+ if (!existsSync36(pkgPath)) {
38975
39488
  return { ran: false, reason: "no package.json (skipped)" };
38976
39489
  }
38977
39490
  let scripts = {};
@@ -38986,12 +39499,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS2) {
38986
39499
  return { ran: false, reason: "no typecheck/test/build script (skipped)" };
38987
39500
  }
38988
39501
  return await new Promise((resolveRun) => {
38989
- const child = process.platform === "win32" ? spawn10(buildCmdLine("npm.cmd", ["run", script]), {
39502
+ const child = process.platform === "win32" ? spawn12(buildCmdLine("npm.cmd", ["run", script]), {
38990
39503
  cwd: projectRoot,
38991
39504
  stdio: ["ignore", "pipe", "pipe"],
38992
39505
  env: process.env,
38993
39506
  shell: true
38994
- }) : spawn10("npm", ["run", script], {
39507
+ }) : spawn12("npm", ["run", script], {
38995
39508
  cwd: projectRoot,
38996
39509
  stdio: ["ignore", "pipe", "pipe"],
38997
39510
  env: process.env
@@ -39063,8 +39576,8 @@ __export(postCouncilHook_exports, {
39063
39576
  runImplementationVerificationHook: () => runImplementationVerificationHook,
39064
39577
  runPostCouncilHook: () => runPostCouncilHook
39065
39578
  });
39066
- import { spawn as spawn11 } from "node:child_process";
39067
- import { existsSync as existsSync36, readFileSync as readFileSync32 } from "node:fs";
39579
+ import { spawn as spawn13 } from "node:child_process";
39580
+ import { existsSync as existsSync37, readFileSync as readFileSync32 } from "node:fs";
39068
39581
  import { join as join31 } from "node:path";
39069
39582
  async function runCompleteDesignPostProcessor(ctx, options) {
39070
39583
  if (options?.runMode === "implementation") {
@@ -39078,7 +39591,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
39078
39591
  }
39079
39592
  const planJsonPath2 = join31(ctx.rootDir, "plan.json");
39080
39593
  const scriptPath = join31(ctx.projectRoot, "complete-design.mjs");
39081
- if (!existsSync36(planJsonPath2)) {
39594
+ if (!existsSync37(planJsonPath2)) {
39082
39595
  return {
39083
39596
  ran: false,
39084
39597
  reason: ".zelari/plan.json missing (not design-phase)"
@@ -39094,7 +39607,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
39094
39607
  if (phaseCount === 0) {
39095
39608
  return { ran: false, reason: ".zelari/plan.json has no phases" };
39096
39609
  }
39097
- if (!existsSync36(scriptPath)) {
39610
+ if (!existsSync37(scriptPath)) {
39098
39611
  try {
39099
39612
  const builtin = await runBuiltinCompleteDesign(ctx);
39100
39613
  return {
@@ -39112,7 +39625,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
39112
39625
  }
39113
39626
  }
39114
39627
  return await new Promise((resolveRun) => {
39115
- const child = spawn11(process.execPath, [scriptPath], {
39628
+ const child = spawn13(process.execPath, [scriptPath], {
39116
39629
  cwd: ctx.projectRoot,
39117
39630
  stdio: ["ignore", "pipe", "pipe"],
39118
39631
  env: process.env
@@ -39279,8 +39792,8 @@ async function runPostCouncilHook(ctx, options) {
39279
39792
  sources: scope.sources
39280
39793
  } : void 0
39281
39794
  });
39282
- const path53 = writeCouncilCompletion(ctx.rootDir, completion);
39283
- completionHook = { ran: true, path: path53, completion };
39795
+ const path55 = writeCouncilCompletion(ctx.rootDir, completion);
39796
+ completionHook = { ran: true, path: path55, completion };
39284
39797
  } catch (err) {
39285
39798
  completionHook = {
39286
39799
  ran: true,
@@ -39319,14 +39832,14 @@ __export(councilFeedback_exports, {
39319
39832
  FeedbackStore: () => FeedbackStore
39320
39833
  });
39321
39834
  import {
39322
- promises as fs16,
39323
- existsSync as existsSync37,
39835
+ promises as fs18,
39836
+ existsSync as existsSync38,
39324
39837
  readFileSync as readFileSync33,
39325
39838
  writeFileSync as writeFileSync21,
39326
39839
  mkdirSync as mkdirSync17
39327
39840
  } from "node:fs";
39328
- import path32 from "node:path";
39329
- import os9 from "node:os";
39841
+ import path34 from "node:path";
39842
+ import os10 from "node:os";
39330
39843
  var FeedbackStore;
39331
39844
  var init_councilFeedback = __esm({
39332
39845
  "src/cli/councilFeedback.ts"() {
@@ -39336,7 +39849,7 @@ var init_councilFeedback = __esm({
39336
39849
  now;
39337
39850
  entries = [];
39338
39851
  constructor(options = {}) {
39339
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path32.join(os9.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
39852
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path34.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
39340
39853
  this.now = options.now ?? Date.now;
39341
39854
  this.load();
39342
39855
  }
@@ -39429,7 +39942,7 @@ var init_councilFeedback = __esm({
39429
39942
  }
39430
39943
  // --- persistence ---------------------------------------------------------
39431
39944
  load() {
39432
- if (!existsSync37(this.file)) return;
39945
+ if (!existsSync38(this.file)) return;
39433
39946
  try {
39434
39947
  const raw = readFileSync33(this.file, "utf-8");
39435
39948
  const parsed = JSON.parse(raw);
@@ -39442,7 +39955,7 @@ var init_councilFeedback = __esm({
39442
39955
  }
39443
39956
  }
39444
39957
  save() {
39445
- mkdirSync17(path32.dirname(this.file), { recursive: true });
39958
+ mkdirSync17(path34.dirname(this.file), { recursive: true });
39446
39959
  writeFileSync21(
39447
39960
  this.file,
39448
39961
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -39452,7 +39965,7 @@ var init_councilFeedback = __esm({
39452
39965
  /** Async variant of load for callers that prefer async IO. */
39453
39966
  async loadAsync() {
39454
39967
  try {
39455
- const raw = await fs16.readFile(this.file, "utf-8");
39968
+ const raw = await fs18.readFile(this.file, "utf-8");
39456
39969
  const parsed = JSON.parse(raw);
39457
39970
  if (parsed && Array.isArray(parsed.entries)) {
39458
39971
  this.entries = parsed.entries.filter(
@@ -39510,7 +40023,7 @@ import { execFile as execFile3 } from "node:child_process";
39510
40023
  import { promisify as promisify2 } from "node:util";
39511
40024
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
39512
40025
  import { tmpdir as tmpdir2 } from "node:os";
39513
- import path33 from "node:path";
40026
+ import path35 from "node:path";
39514
40027
  import { randomUUID as randomUUID3 } from "node:crypto";
39515
40028
  async function git2(cwd, args, env) {
39516
40029
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
@@ -39530,8 +40043,8 @@ async function isGitRepo(cwd) {
39530
40043
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
39531
40044
  }
39532
40045
  async function withTempIndex(fn) {
39533
- const dir = mkdtempSync(path33.join(tmpdir2(), "zelari-ckpt-"));
39534
- const indexFile = path33.join(dir, "index");
40046
+ const dir = mkdtempSync(path35.join(tmpdir2(), "zelari-ckpt-"));
40047
+ const indexFile = path35.join(dir, "index");
39535
40048
  try {
39536
40049
  return await fn(indexFile);
39537
40050
  } finally {
@@ -39622,7 +40135,7 @@ async function restoreCheckpoint(cwd, id) {
39622
40135
  const deleted = [];
39623
40136
  for (const rel2 of added) {
39624
40137
  try {
39625
- rmSync2(path33.join(cwd, rel2), { force: true });
40138
+ rmSync2(path35.join(cwd, rel2), { force: true });
39626
40139
  deleted.push(rel2);
39627
40140
  } catch {
39628
40141
  }
@@ -39722,8 +40235,8 @@ __export(fileBackend_exports, {
39722
40235
  isMemoryEnabled: () => isMemoryEnabled
39723
40236
  });
39724
40237
  import { randomUUID as randomUUID4 } from "node:crypto";
39725
- import { promises as fs17 } from "node:fs";
39726
- import * as path34 from "node:path";
40238
+ import { promises as fs19 } from "node:fs";
40239
+ import * as path36 from "node:path";
39727
40240
  function tokenize(text) {
39728
40241
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
39729
40242
  }
@@ -39767,9 +40280,9 @@ var init_fileBackend = __esm({
39767
40280
  logPath = "";
39768
40281
  memoryDir = "";
39769
40282
  async init(projectRoot) {
39770
- this.memoryDir = path34.join(projectRoot, ".zelari", "memory");
39771
- this.logPath = path34.join(this.memoryDir, "log.jsonl");
39772
- await fs17.mkdir(this.memoryDir, { recursive: true });
40283
+ this.memoryDir = path36.join(projectRoot, ".zelari", "memory");
40284
+ this.logPath = path36.join(this.memoryDir, "log.jsonl");
40285
+ await fs19.mkdir(this.memoryDir, { recursive: true });
39773
40286
  }
39774
40287
  async add(content, metadata = {}, graph) {
39775
40288
  const fact = {
@@ -39779,7 +40292,7 @@ var init_fileBackend = __esm({
39779
40292
  ...graph ? { graph } : {},
39780
40293
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
39781
40294
  };
39782
- await fs17.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
40295
+ await fs19.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
39783
40296
  return fact.id;
39784
40297
  }
39785
40298
  async search(query, options = {}) {
@@ -39807,7 +40320,7 @@ var init_fileBackend = __esm({
39807
40320
  async readAll() {
39808
40321
  let raw;
39809
40322
  try {
39810
- raw = await fs17.readFile(this.logPath, "utf8");
40323
+ raw = await fs19.readFile(this.logPath, "utf8");
39811
40324
  } catch {
39812
40325
  return [];
39813
40326
  }
@@ -39840,23 +40353,23 @@ var init_fileBackend = __esm({
39840
40353
  });
39841
40354
 
39842
40355
  // src/cli/traceStore.ts
39843
- import { promises as fs18 } from "node:fs";
39844
- import * as path35 from "node:path";
40356
+ import { promises as fs20 } from "node:fs";
40357
+ import * as path37 from "node:path";
39845
40358
  function traceDir(projectRoot) {
39846
- return path35.join(projectRoot, ".zelari", "trace");
40359
+ return path37.join(projectRoot, ".zelari", "trace");
39847
40360
  }
39848
40361
  function tracePath(projectRoot, missionId) {
39849
- return path35.join(traceDir(projectRoot), `${missionId}.json`);
40362
+ return path37.join(traceDir(projectRoot), `${missionId}.json`);
39850
40363
  }
39851
40364
  async function saveTrace(projectRoot, missionId, entries) {
39852
40365
  const dir = traceDir(projectRoot);
39853
- await fs18.mkdir(dir, { recursive: true });
40366
+ await fs20.mkdir(dir, { recursive: true });
39854
40367
  const payload = {
39855
40368
  missionId,
39856
40369
  ts: Date.now(),
39857
40370
  entries
39858
40371
  };
39859
- await fs18.writeFile(
40372
+ await fs20.writeFile(
39860
40373
  tracePath(projectRoot, missionId),
39861
40374
  JSON.stringify(payload, null, 2) + "\n",
39862
40375
  "utf8"
@@ -39880,8 +40393,8 @@ __export(zelariMission_exports, {
39880
40393
  runZelariMission: () => runZelariMission
39881
40394
  });
39882
40395
  import { randomUUID as randomUUID5 } from "node:crypto";
39883
- import { promises as fs19 } from "node:fs";
39884
- import * as path36 from "node:path";
40396
+ import { promises as fs21 } from "node:fs";
40397
+ import * as path38 from "node:path";
39885
40398
  function resolveMaxIterations(env = process.env) {
39886
40399
  const raw = env.ZELARI_MISSION_MAX_ITER;
39887
40400
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -39909,10 +40422,10 @@ function isMissionAutoStart(env = process.env) {
39909
40422
  return env.ZELARI_MISSION_AUTO === "1";
39910
40423
  }
39911
40424
  async function writeMissionState(projectRoot, state3) {
39912
- const dir = path36.join(projectRoot, ".zelari");
39913
- await fs19.mkdir(dir, { recursive: true });
39914
- await fs19.writeFile(
39915
- path36.join(dir, "mission-state.json"),
40425
+ const dir = path38.join(projectRoot, ".zelari");
40426
+ await fs21.mkdir(dir, { recursive: true });
40427
+ await fs21.writeFile(
40428
+ path38.join(dir, "mission-state.json"),
39916
40429
  JSON.stringify(state3, null, 2) + "\n",
39917
40430
  "utf8"
39918
40431
  );
@@ -40512,7 +41025,7 @@ function safeSocketPath(socketPath) {
40512
41025
  return socketPath.trim();
40513
41026
  }
40514
41027
  function startPermissionBroker(socketPath, handlers, opts) {
40515
- const path53 = safeSocketPath(socketPath);
41028
+ const path55 = safeSocketPath(socketPath);
40516
41029
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
40517
41030
  const sockets = /* @__PURE__ */ new Set();
40518
41031
  const server = createServer2((socket) => {
@@ -40612,10 +41125,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
40612
41125
  return new Promise((resolve3, reject) => {
40613
41126
  const onError = (err) => reject(err);
40614
41127
  server.once("error", onError);
40615
- server.listen(path53, () => {
41128
+ server.listen(path55, () => {
40616
41129
  server.removeListener("error", onError);
40617
41130
  resolve3({
40618
- socketPath: path53,
41131
+ socketPath: path55,
40619
41132
  stop: () => new Promise((res) => {
40620
41133
  for (const s of sockets) s.destroy();
40621
41134
  sockets.clear();
@@ -40626,7 +41139,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
40626
41139
  if (done) return;
40627
41140
  done = true;
40628
41141
  if (process.platform !== "win32") {
40629
- unlink(path53, () => res());
41142
+ unlink(path55, () => res());
40630
41143
  } else {
40631
41144
  res();
40632
41145
  }
@@ -40639,11 +41152,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
40639
41152
  });
40640
41153
  }
40641
41154
  function requestBrokerAsk(socketPath, ask, opts) {
40642
- const path53 = safeSocketPath(socketPath);
41155
+ const path55 = safeSocketPath(socketPath);
40643
41156
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
40644
41157
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
40645
41158
  return new Promise((resolve3, reject) => {
40646
- const socket = connect(path53);
41159
+ const socket = connect(path55);
40647
41160
  let buffer = "";
40648
41161
  let settled = false;
40649
41162
  const settle = (fn) => {
@@ -40658,7 +41171,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
40658
41171
  settle(
40659
41172
  () => reject(
40660
41173
  new Error(
40661
- `permission broker unavailable at "${path53}" (connect timed out after ${connectTimeoutMs}ms)`
41174
+ `permission broker unavailable at "${path55}" (connect timed out after ${connectTimeoutMs}ms)`
40662
41175
  )
40663
41176
  )
40664
41177
  );
@@ -41390,10 +41903,10 @@ __export(graphMemory_exports, {
41390
41903
  saveGraphSnapshot: () => saveGraphSnapshot,
41391
41904
  toGraphSnapshot: () => toGraphSnapshot
41392
41905
  });
41393
- import { promises as fs20 } from "node:fs";
41394
- import path38 from "node:path";
41906
+ import { promises as fs22 } from "node:fs";
41907
+ import path40 from "node:path";
41395
41908
  function snapshotPath(cwd) {
41396
- return path38.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
41909
+ return path40.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
41397
41910
  }
41398
41911
  function toGraphSnapshot(graph, opts) {
41399
41912
  const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
@@ -41418,16 +41931,16 @@ function toGraphSnapshot(graph, opts) {
41418
41931
  }
41419
41932
  async function saveGraphSnapshot(cwd, snapshot) {
41420
41933
  try {
41421
- await fs20.access(cwd);
41934
+ await fs22.access(cwd);
41422
41935
  const file2 = snapshotPath(cwd);
41423
- await fs20.mkdir(path38.dirname(file2), { recursive: true });
41424
- await fs20.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
41936
+ await fs22.mkdir(path40.dirname(file2), { recursive: true });
41937
+ await fs22.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
41425
41938
  } catch {
41426
41939
  }
41427
41940
  }
41428
41941
  async function loadGraphSnapshot(cwd) {
41429
41942
  try {
41430
- const raw = await fs20.readFile(snapshotPath(cwd), "utf8");
41943
+ const raw = await fs22.readFile(snapshotPath(cwd), "utf8");
41431
41944
  const parsed = JSON.parse(raw);
41432
41945
  if (!parsed || !Array.isArray(parsed.nodes)) return null;
41433
41946
  return parsed;
@@ -41487,7 +42000,7 @@ var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
41487
42000
  var init_graphMemory = __esm({
41488
42001
  "src/cli/kraken/graphMemory.ts"() {
41489
42002
  "use strict";
41490
- SNAPSHOT_DIR = path38.join(".zelari", "kraken");
42003
+ SNAPSHOT_DIR = path40.join(".zelari", "kraken");
41491
42004
  SNAPSHOT_FILE = "last-graph.json";
41492
42005
  MAX_SNAPSHOT_FINDINGS_CHARS = 400;
41493
42006
  }
@@ -41502,15 +42015,15 @@ var init_tentacle = __esm({
41502
42015
  });
41503
42016
 
41504
42017
  // src/cli/kraken/workbench.ts
41505
- import { promises as fs21 } from "node:fs";
41506
- import path39 from "node:path";
42018
+ import { promises as fs23 } from "node:fs";
42019
+ import path41 from "node:path";
41507
42020
  function isWorkbenchEnabled(env = process.env) {
41508
42021
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
41509
42022
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
41510
42023
  return true;
41511
42024
  }
41512
42025
  function workbenchPath(cwd, graphId) {
41513
- return path39.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
42026
+ return path41.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
41514
42027
  }
41515
42028
  function countByStatus2(nodes) {
41516
42029
  const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
@@ -41673,11 +42186,11 @@ var init_workbench = __esm({
41673
42186
  if (!this.enabled) return null;
41674
42187
  if (!this.dirty && this.lastWrite) return this.lastWrite;
41675
42188
  const out = workbenchPath(this.cwd, this.graphId);
41676
- await fs21.mkdir(path39.dirname(out), { recursive: true });
42189
+ await fs23.mkdir(path41.dirname(out), { recursive: true });
41677
42190
  const body = this.render();
41678
42191
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
41679
- await fs21.writeFile(tmp, body, "utf8");
41680
- await fs21.rename(tmp, out);
42192
+ await fs23.writeFile(tmp, body, "utf8");
42193
+ await fs23.rename(tmp, out);
41681
42194
  this.dirty = false;
41682
42195
  this.lastWrite = Promise.resolve(out);
41683
42196
  return out;
@@ -41869,8 +42382,8 @@ __export(executor_exports, {
41869
42382
  resolveNodeTimeoutMs: () => resolveNodeTimeoutMs,
41870
42383
  thoroughnessForKind: () => thoroughnessForKind
41871
42384
  });
41872
- import { existsSync as existsSync38 } from "node:fs";
41873
- import path40 from "node:path";
42385
+ import { existsSync as existsSync39 } from "node:fs";
42386
+ import path42 from "node:path";
41874
42387
  function resolveMaxParallel(env = process.env) {
41875
42388
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
41876
42389
  if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
@@ -41928,7 +42441,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
41928
42441
  }
41929
42442
  function defaultChecksExists(cwd) {
41930
42443
  try {
41931
- return existsSync38(path40.join(cwd, ".zelari", "world", "checks.json"));
42444
+ return existsSync39(path42.join(cwd, ".zelari", "world", "checks.json"));
41932
42445
  } catch {
41933
42446
  return false;
41934
42447
  }
@@ -42895,7 +43408,7 @@ __export(prereqChecks_exports, {
42895
43408
  runPrereqChecks: () => runPrereqChecks
42896
43409
  });
42897
43410
  import { execSync, spawnSync as spawnSync2 } from "node:child_process";
42898
- import { existsSync as existsSync39 } from "node:fs";
43411
+ import { existsSync as existsSync40 } from "node:fs";
42899
43412
  import { dirname as dirname8 } from "node:path";
42900
43413
  function isWslBashPath2(p3) {
42901
43414
  if (!p3 || typeof p3 !== "string") return false;
@@ -43019,7 +43532,7 @@ function agentProbeEnv() {
43019
43532
  }
43020
43533
  function existsSyncSafe2(p3) {
43021
43534
  try {
43022
- return existsSync39(p3);
43535
+ return existsSync40(p3);
43023
43536
  } catch {
43024
43537
  return false;
43025
43538
  }
@@ -43266,16 +43779,16 @@ var init_prereqChecks = __esm({
43266
43779
  });
43267
43780
 
43268
43781
  // src/cli/plugins/prefs.ts
43269
- import { existsSync as existsSync40, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
43270
- import path43 from "node:path";
43271
- import os10 from "node:os";
43782
+ import { existsSync as existsSync41, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
43783
+ import path45 from "node:path";
43784
+ import os11 from "node:os";
43272
43785
  function getPluginPrefsPath() {
43273
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path43.join(os10.homedir(), ".tmp", "zelari-code", "plugins.json");
43786
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path45.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
43274
43787
  }
43275
43788
  function getPluginPrefs() {
43276
43789
  const file2 = getPluginPrefsPath();
43277
43790
  try {
43278
- if (!existsSync40(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
43791
+ if (!existsSync41(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
43279
43792
  const raw = readFileSync34(file2, "utf-8");
43280
43793
  const parsed = JSON.parse(raw);
43281
43794
  if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
@@ -43291,7 +43804,7 @@ function getPluginPrefs() {
43291
43804
  }
43292
43805
  function writePluginPrefs(prefs) {
43293
43806
  const file2 = getPluginPrefsPath();
43294
- mkdirSync18(path43.dirname(file2), { recursive: true });
43807
+ mkdirSync18(path45.dirname(file2), { recursive: true });
43295
43808
  writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
43296
43809
  encoding: "utf-8",
43297
43810
  mode: 384
@@ -43327,8 +43840,8 @@ __export(registry_exports, {
43327
43840
  findPlugin: () => findPlugin,
43328
43841
  isBinaryOnPath: () => isBinaryOnPath
43329
43842
  });
43330
- import { existsSync as existsSync41 } from "node:fs";
43331
- import path44 from "node:path";
43843
+ import { existsSync as existsSync42 } from "node:fs";
43844
+ import path46 from "node:path";
43332
43845
  function detectLocalBin(bin) {
43333
43846
  return (cwd) => {
43334
43847
  try {
@@ -43344,9 +43857,9 @@ function isBinaryOnPath(bin, opts = {}) {
43344
43857
  return false;
43345
43858
  }
43346
43859
  const platform = opts.platform ?? process.platform;
43347
- const exists = opts.exists ?? existsSync41;
43860
+ const exists = opts.exists ?? existsSync42;
43348
43861
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
43349
- const pathMod = platform === "win32" ? path44.win32 : path44.posix;
43862
+ const pathMod = platform === "win32" ? path46.win32 : path46.posix;
43350
43863
  const sep2 = platform === "win32" ? ";" : ":";
43351
43864
  const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
43352
43865
  const candidates = [bin];
@@ -44078,7 +44591,7 @@ __export(atMentions_exports, {
44078
44591
  extractAtMentions: () => extractAtMentions,
44079
44592
  hasAtMentions: () => hasAtMentions
44080
44593
  });
44081
- import { existsSync as existsSync44, readFileSync as readFileSync36, statSync as statSync8 } from "node:fs";
44594
+ import { existsSync as existsSync45, readFileSync as readFileSync36, statSync as statSync8 } from "node:fs";
44082
44595
  import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
44083
44596
  function isImagePath(abs) {
44084
44597
  const ext = abs.split(".").pop()?.toLowerCase() ?? "";
@@ -44132,7 +44645,7 @@ function resolveMention(token, cwd) {
44132
44645
  note: "outside project root \u2014 skipped"
44133
44646
  };
44134
44647
  }
44135
- if (!existsSync44(abs)) {
44648
+ if (!existsSync45(abs)) {
44136
44649
  return {
44137
44650
  raw: token,
44138
44651
  path: token,
@@ -44292,10 +44805,10 @@ __export(triggerLock_exports, {
44292
44805
  lockPath: () => lockPath,
44293
44806
  releaseLock: () => releaseLock
44294
44807
  });
44295
- import { promises as fs29 } from "node:fs";
44296
- import * as path49 from "node:path";
44808
+ import { promises as fs31 } from "node:fs";
44809
+ import * as path51 from "node:path";
44297
44810
  function lockPath(projectRoot) {
44298
- return path49.join(projectRoot, ".zelari", "trigger.lock");
44811
+ return path51.join(projectRoot, ".zelari", "trigger.lock");
44299
44812
  }
44300
44813
  function isPidAlive(pid) {
44301
44814
  try {
@@ -44308,10 +44821,10 @@ function isPidAlive(pid) {
44308
44821
  }
44309
44822
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
44310
44823
  const lp = lockPath(projectRoot);
44311
- const dir = path49.dirname(lp);
44312
- await fs29.mkdir(dir, { recursive: true });
44824
+ const dir = path51.dirname(lp);
44825
+ await fs31.mkdir(dir, { recursive: true });
44313
44826
  try {
44314
- const raw = await fs29.readFile(lp, "utf8");
44827
+ const raw = await fs31.readFile(lp, "utf8");
44315
44828
  const existing = JSON.parse(raw);
44316
44829
  if (existing.pid && isPidAlive(existing.pid)) {
44317
44830
  return { acquired: false, heldBy: existing.pid, lockPath: lp };
@@ -44322,13 +44835,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
44322
44835
  pid: process.pid,
44323
44836
  acquiredAt: now().toISOString()
44324
44837
  };
44325
- await fs29.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
44838
+ await fs31.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
44326
44839
  return { acquired: true, lockPath: lp };
44327
44840
  }
44328
44841
  async function releaseLock(projectRoot) {
44329
44842
  const lp = lockPath(projectRoot);
44330
44843
  try {
44331
- await fs29.unlink(lp);
44844
+ await fs31.unlink(lp);
44332
44845
  } catch {
44333
44846
  }
44334
44847
  }
@@ -44692,7 +45205,7 @@ var init_skillCategories = __esm({
44692
45205
 
44693
45206
  // src/cli/skillConfigIo.ts
44694
45207
  import {
44695
- existsSync as existsSync46,
45208
+ existsSync as existsSync47,
44696
45209
  mkdirSync as mkdirSync21,
44697
45210
  readdirSync as readdirSync10,
44698
45211
  readFileSync as readFileSync38,
@@ -44766,7 +45279,7 @@ function entryFromBuiltin(skill) {
44766
45279
  };
44767
45280
  }
44768
45281
  function scanSkillsDir(dir, projectRoot, seen, out) {
44769
- if (!existsSync46(dir)) return;
45282
+ if (!existsSync47(dir)) return;
44770
45283
  let entries;
44771
45284
  try {
44772
45285
  entries = readdirSync10(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -44775,7 +45288,7 @@ function scanSkillsDir(dir, projectRoot, seen, out) {
44775
45288
  }
44776
45289
  for (const entry of entries) {
44777
45290
  const skillPath = skillFilePath(dir, entry);
44778
- if (!existsSync46(skillPath)) continue;
45291
+ if (!existsSync47(skillPath)) continue;
44779
45292
  try {
44780
45293
  const parsed = parseSkillMd(readFileSync38(skillPath, "utf8"), skillPath);
44781
45294
  if (!parsed) continue;
@@ -44861,7 +45374,7 @@ function upsertSkill(opts) {
44861
45374
  }
44862
45375
  dir = getProjectSkillsDir(root);
44863
45376
  }
44864
- const path53 = skillFilePath(dir, name);
45377
+ const path55 = skillFilePath(dir, name);
44865
45378
  const content = serializeSkillMd({
44866
45379
  name,
44867
45380
  description,
@@ -44870,13 +45383,13 @@ function upsertSkill(opts) {
44870
45383
  tools: opts.tools,
44871
45384
  cost: opts.cost
44872
45385
  });
44873
- const parsed = parseSkillMd(content, path53);
45386
+ const parsed = parseSkillMd(content, path55);
44874
45387
  if (!parsed) {
44875
45388
  return { ok: false, error: "Generated SKILL.md failed validation" };
44876
45389
  }
44877
- mkdirSync21(dirname10(path53), { recursive: true });
44878
- writeFileSync24(path53, content, "utf8");
44879
- return { ok: true, path: path53 };
45390
+ mkdirSync21(dirname10(path55), { recursive: true });
45391
+ writeFileSync24(path55, content, "utf8");
45392
+ return { ok: true, path: path55 };
44880
45393
  }
44881
45394
  function removeSkill(opts) {
44882
45395
  const name = opts.name.trim().toLowerCase();
@@ -44894,8 +45407,8 @@ function removeSkill(opts) {
44894
45407
  dir = getProjectSkillsDir(root);
44895
45408
  }
44896
45409
  const skillDir = join37(dir, name);
44897
- const path53 = skillFilePath(dir, name);
44898
- if (!existsSync46(path53) && !existsSync46(skillDir)) {
45410
+ const path55 = skillFilePath(dir, name);
45411
+ if (!existsSync47(path55) && !existsSync47(skillDir)) {
44899
45412
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
44900
45413
  }
44901
45414
  try {
@@ -44906,7 +45419,7 @@ function removeSkill(opts) {
44906
45419
  error: err instanceof Error ? err.message : String(err)
44907
45420
  };
44908
45421
  }
44909
- return { ok: true, path: path53 };
45422
+ return { ok: true, path: path55 };
44910
45423
  }
44911
45424
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
44912
45425
  var init_skillConfigIo = __esm({
@@ -45200,14 +45713,14 @@ var init_permissionCli = __esm({
45200
45713
 
45201
45714
  // src/cli/companion/config.ts
45202
45715
  import {
45203
- existsSync as existsSync47,
45716
+ existsSync as existsSync48,
45204
45717
  mkdirSync as mkdirSync22,
45205
45718
  readFileSync as readFileSync39,
45206
45719
  writeFileSync as writeFileSync25
45207
45720
  } from "node:fs";
45208
45721
  import { join as join38 } from "node:path";
45209
45722
  import { homedir as homedir13 } from "node:os";
45210
- import { createHash as createHash9, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
45723
+ import { createHash as createHash10, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
45211
45724
  function getZelariHome() {
45212
45725
  return join38(homedir13(), ".zelari-code");
45213
45726
  }
@@ -45219,17 +45732,17 @@ function getCompanionTokenPath() {
45219
45732
  }
45220
45733
  function ensureHome() {
45221
45734
  const home = getZelariHome();
45222
- if (!existsSync47(home)) {
45735
+ if (!existsSync48(home)) {
45223
45736
  mkdirSync22(home, { recursive: true });
45224
45737
  }
45225
45738
  }
45226
45739
  function loadCompanionConfig() {
45227
- const path53 = getCompanionConfigPath();
45228
- if (!existsSync47(path53)) {
45740
+ const path55 = getCompanionConfigPath();
45741
+ if (!existsSync48(path55)) {
45229
45742
  return { projects: [] };
45230
45743
  }
45231
45744
  try {
45232
- const raw = JSON.parse(readFileSync39(path53, "utf8"));
45745
+ const raw = JSON.parse(readFileSync39(path55, "utf8"));
45233
45746
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
45234
45747
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
45235
45748
  ).map((p3) => ({
@@ -45267,24 +45780,24 @@ function loadOrCreateToken(explicit) {
45267
45780
  return { token: explicit.trim(), created: false };
45268
45781
  }
45269
45782
  ensureHome();
45270
- const path53 = getCompanionTokenPath();
45271
- if (existsSync47(path53)) {
45272
- const t = readFileSync39(path53, "utf8").trim();
45783
+ const path55 = getCompanionTokenPath();
45784
+ if (existsSync48(path55)) {
45785
+ const t = readFileSync39(path55, "utf8").trim();
45273
45786
  if (t) return { token: t, created: false };
45274
45787
  }
45275
45788
  const token = randomBytes5(24).toString("base64url");
45276
- writeFileSync25(path53, token + "\n", "utf8");
45789
+ writeFileSync25(path55, token + "\n", "utf8");
45277
45790
  try {
45278
- const fs31 = __require("node:fs");
45279
- fs31.chmodSync?.(path53, 384);
45791
+ const fs33 = __require("node:fs");
45792
+ fs33.chmodSync?.(path55, 384);
45280
45793
  } catch {
45281
45794
  }
45282
45795
  return { token, created: true };
45283
45796
  }
45284
45797
  function tokenMatches(expected, provided) {
45285
45798
  if (!provided) return false;
45286
- const a = createHash9("sha256").update(expected).digest();
45287
- const b = createHash9("sha256").update(provided).digest();
45799
+ const a = createHash10("sha256").update(expected).digest();
45800
+ const b = createHash10("sha256").update(provided).digest();
45288
45801
  try {
45289
45802
  return timingSafeEqual(a, b);
45290
45803
  } catch {
@@ -45301,17 +45814,17 @@ function mergeProjects(cfg, extraPaths) {
45301
45814
  byId.set(p3.id, p3);
45302
45815
  }
45303
45816
  for (const raw of extraPaths) {
45304
- const path53 = raw.trim();
45305
- if (!path53) continue;
45306
- let id = slugFromPath(path53);
45817
+ const path55 = raw.trim();
45818
+ if (!path55) continue;
45819
+ let id = slugFromPath(path55);
45307
45820
  let n = 2;
45308
- while (byId.has(id) && byId.get(id).path !== path53) {
45309
- id = `${slugFromPath(path53)}-${n++}`;
45821
+ while (byId.has(id) && byId.get(id).path !== path55) {
45822
+ id = `${slugFromPath(path55)}-${n++}`;
45310
45823
  }
45311
45824
  byId.set(id, {
45312
45825
  id,
45313
- name: slugFromPath(path53),
45314
- path: path53
45826
+ name: slugFromPath(path55),
45827
+ path: path55
45315
45828
  });
45316
45829
  }
45317
45830
  return [...byId.values()];
@@ -45359,7 +45872,7 @@ var init_config = __esm({
45359
45872
  });
45360
45873
 
45361
45874
  // src/cli/companion/runManager.ts
45362
- import { spawn as spawn13 } from "node:child_process";
45875
+ import { spawn as spawn15 } from "node:child_process";
45363
45876
  import { createInterface as createInterface2 } from "node:readline";
45364
45877
  import { randomUUID as randomUUID8 } from "node:crypto";
45365
45878
  import { writeFileSync as writeFileSync26, unlinkSync as unlinkSync3 } from "node:fs";
@@ -45475,7 +45988,7 @@ var init_runManager = __esm({
45475
45988
  historyFile = void 0;
45476
45989
  }
45477
45990
  }
45478
- const child = spawn13(process.execPath, argv, {
45991
+ const child = spawn15(process.execPath, argv, {
45479
45992
  cwd: args.cwd,
45480
45993
  env: {
45481
45994
  ...process.env,
@@ -45594,7 +46107,7 @@ __export(serve_exports, {
45594
46107
  runCompanionServe: () => runCompanionServe
45595
46108
  });
45596
46109
  import { createServer as createServer3 } from "node:http";
45597
- import { existsSync as existsSync48 } from "node:fs";
46110
+ import { existsSync as existsSync49 } from "node:fs";
45598
46111
  import { resolve as resolve2 } from "node:path";
45599
46112
  function readBody(req, max = 2e6) {
45600
46113
  return new Promise((resolveBody, reject) => {
@@ -45642,7 +46155,7 @@ async function runCompanionServe(opts = {}) {
45642
46155
  let projects = mergeProjects(fileCfg, opts.projects ?? []);
45643
46156
  projects = projects.filter((p3) => {
45644
46157
  const abs = resolve2(p3.path);
45645
- if (!existsSync48(abs)) {
46158
+ if (!existsSync49(abs)) {
45646
46159
  process.stderr.write(
45647
46160
  `[zelari-code serve] skip missing project path: ${p3.path}
45648
46161
  `
@@ -45688,9 +46201,9 @@ async function runCompanionServe(opts = {}) {
45688
46201
  return;
45689
46202
  }
45690
46203
  const url2 = parseUrl(req);
45691
- const path53 = url2.pathname.replace(/\/+$/, "") || "/";
46204
+ const path55 = url2.pathname.replace(/\/+$/, "") || "/";
45692
46205
  try {
45693
- if (req.method === "GET" && (path53 === "/health" || path53 === "/v1/health")) {
46206
+ if (req.method === "GET" && (path55 === "/health" || path55 === "/v1/health")) {
45694
46207
  sendJson2(res, 200, {
45695
46208
  ok: true,
45696
46209
  service: "zelari-companion",
@@ -45702,18 +46215,18 @@ async function runCompanionServe(opts = {}) {
45702
46215
  });
45703
46216
  return;
45704
46217
  }
45705
- if (path53.startsWith("/v1")) {
46218
+ if (path55.startsWith("/v1")) {
45706
46219
  if (!tokenMatches(token, getBearer(req))) {
45707
46220
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
45708
46221
  return;
45709
46222
  }
45710
46223
  }
45711
- if (req.method === "GET" && path53 === "/v1/config") {
46224
+ if (req.method === "GET" && path55 === "/v1/config") {
45712
46225
  const snap = buildDesktopConfigSnapshot();
45713
46226
  sendJson2(res, 200, { ok: true, ...snap });
45714
46227
  return;
45715
46228
  }
45716
- if (req.method === "GET" && path53 === "/v1/projects") {
46229
+ if (req.method === "GET" && path55 === "/v1/projects") {
45717
46230
  sendJson2(res, 200, {
45718
46231
  ok: true,
45719
46232
  projects: projects.map((p3) => ({
@@ -45724,7 +46237,7 @@ async function runCompanionServe(opts = {}) {
45724
46237
  });
45725
46238
  return;
45726
46239
  }
45727
- if (req.method === "GET" && path53 === "/v1/runs") {
46240
+ if (req.method === "GET" && path55 === "/v1/runs") {
45728
46241
  sendJson2(res, 200, {
45729
46242
  ok: true,
45730
46243
  active: runs.getActive(),
@@ -45742,7 +46255,7 @@ async function runCompanionServe(opts = {}) {
45742
46255
  });
45743
46256
  return;
45744
46257
  }
45745
- if (req.method === "POST" && path53 === "/v1/runs") {
46258
+ if (req.method === "POST" && path55 === "/v1/runs") {
45746
46259
  const raw = await readBody(req);
45747
46260
  let body = {};
45748
46261
  try {
@@ -45789,7 +46302,7 @@ async function runCompanionServe(opts = {}) {
45789
46302
  });
45790
46303
  return;
45791
46304
  }
45792
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path53);
46305
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path55);
45793
46306
  if (req.method === "GET" && eventsMatch) {
45794
46307
  const runId = eventsMatch[1];
45795
46308
  const run = runs.getRun(runId);
@@ -45854,7 +46367,7 @@ async function runCompanionServe(opts = {}) {
45854
46367
  }, 500);
45855
46368
  return;
45856
46369
  }
45857
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path53);
46370
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path55);
45858
46371
  if (req.method === "POST" && cancelMatch) {
45859
46372
  const runId = cancelMatch[1];
45860
46373
  const result = runs.cancel(runId);
@@ -45964,26 +46477,26 @@ __export(doctor_exports, {
45964
46477
  runDoctor: () => runDoctor
45965
46478
  });
45966
46479
  import { execSync as execSync2 } from "node:child_process";
45967
- import { existsSync as existsSync49, readFileSync as readFileSync40, readlinkSync, statSync as statSync9 } from "node:fs";
46480
+ import { existsSync as existsSync50, readFileSync as readFileSync40, readlinkSync, statSync as statSync9 } from "node:fs";
45968
46481
  import { createRequire as createRequire3 } from "node:module";
45969
46482
  import { fileURLToPath as fileURLToPath2 } from "node:url";
45970
- import path51 from "node:path";
46483
+ import path53 from "node:path";
45971
46484
  function findPackageRoot(start) {
45972
46485
  let dir = start;
45973
46486
  for (let i = 0; i < 6; i += 1) {
45974
- const candidate = path51.join(dir, "package.json");
45975
- if (existsSync49(candidate)) {
46487
+ const candidate = path53.join(dir, "package.json");
46488
+ if (existsSync50(candidate)) {
45976
46489
  try {
45977
46490
  const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
45978
46491
  if (pkg.name === "zelari-code") return dir;
45979
46492
  } catch {
45980
46493
  }
45981
46494
  }
45982
- const parent = path51.dirname(dir);
46495
+ const parent = path53.dirname(dir);
45983
46496
  if (parent === dir) break;
45984
46497
  dir = parent;
45985
46498
  }
45986
- return path51.resolve(__dirname3, "..", "..", "..");
46499
+ return path53.resolve(__dirname3, "..", "..", "..");
45987
46500
  }
45988
46501
  function tryExec(cmd) {
45989
46502
  try {
@@ -45997,7 +46510,7 @@ function tryExec(cmd) {
45997
46510
  }
45998
46511
  function readPackageJson3() {
45999
46512
  try {
46000
- const pkgPath = path51.join(packageRoot, "package.json");
46513
+ const pkgPath = path53.join(packageRoot, "package.json");
46001
46514
  return JSON.parse(readFileSync40(pkgPath, "utf8"));
46002
46515
  } catch {
46003
46516
  return null;
@@ -46013,8 +46526,8 @@ function checkShim(pkgName) {
46013
46526
  }
46014
46527
  const isWin = process.platform === "win32";
46015
46528
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
46016
- const shimPath = path51.join(prefix, shimName);
46017
- if (!existsSync49(shimPath)) {
46529
+ const shimPath = path53.join(prefix, shimName);
46530
+ if (!existsSync50(shimPath)) {
46018
46531
  return FAIL(
46019
46532
  `shim not found at ${shimPath}
46020
46533
  fix: npm install -g ${pkgName}@latest --force`
@@ -46041,8 +46554,8 @@ function checkShim(pkgName) {
46041
46554
  fix: npm install -g ${pkgName}@latest --force`
46042
46555
  );
46043
46556
  }
46044
- const resolved = path51.resolve(path51.dirname(shimPath), target);
46045
- const expected = path51.join(
46557
+ const resolved = path53.resolve(path53.dirname(shimPath), target);
46558
+ const expected = path53.join(
46046
46559
  prefix,
46047
46560
  "node_modules",
46048
46561
  pkgName,
@@ -46081,8 +46594,8 @@ function checkNode(pkg) {
46081
46594
  return OK(`node ${raw}`);
46082
46595
  }
46083
46596
  function checkBundle() {
46084
- const bundle = path51.join(packageRoot, "dist", "cli", "main.bundled.js");
46085
- if (!existsSync49(bundle)) {
46597
+ const bundle = path53.join(packageRoot, "dist", "cli", "main.bundled.js");
46598
+ if (!existsSync50(bundle)) {
46086
46599
  return FAIL(
46087
46600
  `dist/cli/main.bundled.js missing at ${bundle}
46088
46601
  fix: npm run build:cli (then reinstall or run via tsx)`
@@ -46102,7 +46615,7 @@ function checkRuntimeDeps() {
46102
46615
  const missing = [];
46103
46616
  for (const dep of required2) {
46104
46617
  try {
46105
- const localReq = createRequire3(path51.join(packageRoot, "package.json"));
46618
+ const localReq = createRequire3(path53.join(packageRoot, "package.json"));
46106
46619
  localReq.resolve(dep);
46107
46620
  } catch {
46108
46621
  missing.push(dep);
@@ -46278,7 +46791,7 @@ var init_doctor = __esm({
46278
46791
  "use strict";
46279
46792
  init_prereqChecks();
46280
46793
  require3 = createRequire3(import.meta.url);
46281
- __dirname3 = path51.dirname(fileURLToPath2(import.meta.url));
46794
+ __dirname3 = path53.dirname(fileURLToPath2(import.meta.url));
46282
46795
  packageRoot = findPackageRoot(__dirname3);
46283
46796
  OK = (message) => ({
46284
46797
  ok: true,
@@ -46462,15 +46975,15 @@ __export(inspect_exports, {
46462
46975
  collectInspectReport: () => collectInspectReport,
46463
46976
  runInspect: () => runInspect
46464
46977
  });
46465
- import path52 from "node:path";
46466
- import { existsSync as existsSync50, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
46978
+ import path54 from "node:path";
46979
+ import { existsSync as existsSync51, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
46467
46980
  import { homedir as homedir14 } from "node:os";
46468
46981
  async function collectInspectReport(cwd = process.cwd()) {
46469
46982
  ensureBuiltinSkillsLoadedSync();
46470
46983
  const snap = listSkillsSnapshot(cwd);
46471
46984
  const mcp = listMcpServers(cwd);
46472
- const userMcpPath = path52.join(homedir14(), ".zelari-code", "mcp.json");
46473
- const projectMcpPath = path52.join(cwd, ".zelari", "mcp.json");
46985
+ const userMcpPath = path54.join(homedir14(), ".zelari-code", "mcp.json");
46986
+ const projectMcpPath = path54.join(cwd, ".zelari", "mcp.json");
46474
46987
  const globalHooks = globalHooksDir();
46475
46988
  const projectHooks = projectHooksDir(cwd);
46476
46989
  const projectTrusted = isFolderTrusted(cwd);
@@ -46496,11 +47009,11 @@ async function collectInspectReport(cwd = process.cwd()) {
46496
47009
  folders: listTrustedFolders()
46497
47010
  },
46498
47011
  configSources: [
46499
- { path: userMcpPath, exists: existsSync50(userMcpPath) },
46500
- { path: projectMcpPath, exists: existsSync50(projectMcpPath) },
46501
- { path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync50(path52.join(homedir14(), ".zelari-code", "provider.json")) },
46502
- { path: path52.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync50(path52.join(cwd, ".zelari", "AGENTS.md")) },
46503
- { path: path52.join(cwd, "AGENTS.md"), exists: existsSync50(path52.join(cwd, "AGENTS.md")) }
47012
+ { path: userMcpPath, exists: existsSync51(userMcpPath) },
47013
+ { path: projectMcpPath, exists: existsSync51(projectMcpPath) },
47014
+ { path: path54.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync51(path54.join(homedir14(), ".zelari-code", "provider.json")) },
47015
+ { path: path54.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync51(path54.join(cwd, ".zelari", "AGENTS.md")) },
47016
+ { path: path54.join(cwd, "AGENTS.md"), exists: existsSync51(path54.join(cwd, "AGENTS.md")) }
46504
47017
  ],
46505
47018
  skills: {
46506
47019
  total: snap.skills.length,
@@ -46513,7 +47026,7 @@ async function collectInspectReport(cwd = process.cwd()) {
46513
47026
  user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
46514
47027
  project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
46515
47028
  projectTrusted,
46516
- projectConfigExists: existsSync50(projectMcpPath)
47029
+ projectConfigExists: existsSync51(projectMcpPath)
46517
47030
  },
46518
47031
  hooks: {
46519
47032
  global: {
@@ -46540,12 +47053,12 @@ function listJsonFiles(dir) {
46540
47053
  }
46541
47054
  function findAgentsMd(cwd) {
46542
47055
  const candidates = [
46543
- path52.join(cwd, "AGENTS.md"),
46544
- path52.join(cwd, ".zelari", "AGENTS.md")
47056
+ path54.join(cwd, "AGENTS.md"),
47057
+ path54.join(cwd, ".zelari", "AGENTS.md")
46545
47058
  ];
46546
47059
  const found = [];
46547
47060
  for (const c of candidates) {
46548
- if (existsSync50(c)) {
47061
+ if (existsSync51(c)) {
46549
47062
  try {
46550
47063
  const text = readFileSync41(c, "utf8");
46551
47064
  found.push(`${c} (${text.length} bytes)`);
@@ -50321,6 +50834,8 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
50321
50834
  "You are in PLAN mode. Explore and design only.",
50322
50835
  "- Do NOT implement production code or run destructive shell commands.",
50323
50836
  "- write_file / edit_file / bash / apply_diff are unavailable.",
50837
+ "- 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.",
50838
+ "- 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).",
50324
50839
  "- Produce a clear plan, ask clarifying questions (---QUESTION---), use workspace plan tools when relevant.",
50325
50840
  "- When the plan is ready, tell the user to run /build to implement."
50326
50841
  ].join("\n") : workPhase === "build" ? [
@@ -52423,7 +52938,7 @@ init_messageHelpers();
52423
52938
  // src/cli/gitOps.ts
52424
52939
  import { execFile as execFile4 } from "node:child_process";
52425
52940
  import { promisify as promisify3 } from "node:util";
52426
- import path37 from "node:path";
52941
+ import path39 from "node:path";
52427
52942
  var execFileAsync3 = promisify3(execFile4);
52428
52943
  async function git3(cwd, args) {
52429
52944
  try {
@@ -52469,7 +52984,7 @@ async function undoWorkingChanges(opts = {}) {
52469
52984
  };
52470
52985
  }
52471
52986
  function defaultProjectRoot() {
52472
- return path37.resolve(__dirname, "..", "..", "..");
52987
+ return path39.resolve(__dirname, "..", "..", "..");
52473
52988
  }
52474
52989
 
52475
52990
  // src/cli/slashHandlers/git.ts
@@ -52914,13 +53429,13 @@ ${digest}
52914
53429
  init_auditLogger();
52915
53430
  init_toolRegistry();
52916
53431
  init_messageHelpers();
52917
- import { promises as fs23 } from "node:fs";
53432
+ import { promises as fs25 } from "node:fs";
52918
53433
 
52919
53434
  // src/cli/tools/krakenCsvFanout.ts
52920
53435
  init_zod();
52921
53436
  init_taskTool();
52922
- import { promises as fs22 } from "node:fs";
52923
- import path41 from "node:path";
53437
+ import { promises as fs24 } from "node:fs";
53438
+ import path43 from "node:path";
52924
53439
  import { randomBytes as randomBytes4 } from "node:crypto";
52925
53440
  var CsvFanoutArgsSchema = external_exports.object({
52926
53441
  csv_path: external_exports.string().min(1),
@@ -52941,7 +53456,7 @@ var CsvFanoutArgsSchema = external_exports.object({
52941
53456
  max_runtime_seconds: external_exports.number().int().positive().optional()
52942
53457
  });
52943
53458
  async function readCsv(filePath) {
52944
- const text = await fs22.readFile(filePath, "utf8");
53459
+ const text = await fs24.readFile(filePath, "utf8");
52945
53460
  return parseCsv(text);
52946
53461
  }
52947
53462
  function parseCsv(text) {
@@ -53014,8 +53529,8 @@ function resolveMaxConcurrency(env = process.env) {
53014
53529
  }
53015
53530
  async function runCsvFanout(args, deps, opts) {
53016
53531
  const start = Date.now();
53017
- const absCsv = path41.isAbsolute(args.csv_path) ? args.csv_path : path41.join(opts.parentCwd, args.csv_path);
53018
- const absOut = path41.isAbsolute(args.output_csv_path) ? args.output_csv_path : path41.join(opts.parentCwd, args.output_csv_path);
53532
+ const absCsv = path43.isAbsolute(args.csv_path) ? args.csv_path : path43.join(opts.parentCwd, args.csv_path);
53533
+ const absOut = path43.isAbsolute(args.output_csv_path) ? args.output_csv_path : path43.join(opts.parentCwd, args.output_csv_path);
53019
53534
  const { headers: headers2, rows } = await readCsv(absCsv);
53020
53535
  if (headers2.length === 0) {
53021
53536
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
@@ -53071,7 +53586,7 @@ async function runCsvFanout(args, deps, opts) {
53071
53586
  errored += 1;
53072
53587
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
53073
53588
  }
53074
- await fs22.mkdir(path41.dirname(absOut), { recursive: true });
53589
+ await fs24.mkdir(path43.dirname(absOut), { recursive: true });
53075
53590
  await queueWrite(serializeCsv(outHeaders, outputRecords));
53076
53591
  }
53077
53592
  }
@@ -53093,8 +53608,8 @@ async function runCsvFanout(args, deps, opts) {
53093
53608
  }
53094
53609
  async function atomicWrite(file2, contents) {
53095
53610
  const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes4(6).toString("hex")}.tmp`;
53096
- await fs22.writeFile(tmp, contents, "utf8");
53097
- await fs22.rename(tmp, file2);
53611
+ await fs24.writeFile(tmp, contents, "utf8");
53612
+ await fs24.rename(tmp, file2);
53098
53613
  }
53099
53614
 
53100
53615
  // src/cli/slashHandlers/krakenFanout.ts
@@ -53192,7 +53707,7 @@ async function handleKrakenFanout(ctx, raw) {
53192
53707
  }
53193
53708
  const absCsv = isAbsolute(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
53194
53709
  try {
53195
- await fs23.access(absCsv);
53710
+ await fs25.access(absCsv);
53196
53711
  } catch {
53197
53712
  appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
53198
53713
  return;
@@ -53265,8 +53780,8 @@ function splitArgs(s) {
53265
53780
 
53266
53781
  // src/cli/slashHandlers/krakenWorkbench.ts
53267
53782
  init_messageHelpers();
53268
- import { promises as fs24 } from "node:fs";
53269
- import path42 from "node:path";
53783
+ import { promises as fs26 } from "node:fs";
53784
+ import path44 from "node:path";
53270
53785
 
53271
53786
  // src/cli/kraken/workbenchView.ts
53272
53787
  var EMPTY = {
@@ -53383,15 +53898,15 @@ function formatWorkbenchForTerminal(p3) {
53383
53898
 
53384
53899
  // src/cli/slashHandlers/krakenWorkbench.ts
53385
53900
  async function handleKrakenWorkbench(ctx) {
53386
- const dir = path42.join(ctx.cwd, ".zelari", "radio");
53901
+ const dir = path44.join(ctx.cwd, ".zelari", "radio");
53387
53902
  let latest = null;
53388
53903
  let latestMtime = 0;
53389
53904
  try {
53390
- const files = await fs24.readdir(dir);
53905
+ const files = await fs26.readdir(dir);
53391
53906
  for (const f of files) {
53392
53907
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
53393
- const full = path42.join(dir, f);
53394
- const stat = await fs24.stat(full);
53908
+ const full = path44.join(dir, f);
53909
+ const stat = await fs26.stat(full);
53395
53910
  if (stat.mtimeMs > latestMtime) {
53396
53911
  latestMtime = stat.mtimeMs;
53397
53912
  latest = full;
@@ -53403,14 +53918,14 @@ async function handleKrakenWorkbench(ctx) {
53403
53918
  appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
53404
53919
  return;
53405
53920
  }
53406
- const content = await fs24.readFile(latest, "utf8");
53921
+ const content = await fs26.readFile(latest, "utf8");
53407
53922
  const parsed = parseWorkbench(content);
53408
53923
  const rendered = formatWorkbenchForTerminal(parsed);
53409
53924
  if (!rendered.trim()) {
53410
- appendSystem(ctx.setMessages, `[kraken workbench] ${path42.basename(latest)}: (no nodes / no events yet)`);
53925
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path44.basename(latest)}: (no nodes / no events yet)`);
53411
53926
  return;
53412
53927
  }
53413
- appendSystem(ctx.setMessages, `[kraken workbench] ${path42.basename(latest)}:
53928
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path44.basename(latest)}:
53414
53929
  ${rendered}`);
53415
53930
  }
53416
53931
 
@@ -53564,8 +54079,8 @@ init_registry3();
53564
54079
  // src/cli/plugins/installer.ts
53565
54080
  init_cmdline();
53566
54081
  init_updater();
53567
- import { spawn as spawn12 } from "node:child_process";
53568
- async function installPlugin(spec, cwd, executor = spawn12) {
54082
+ import { spawn as spawn14 } from "node:child_process";
54083
+ async function installPlugin(spec, cwd, executor = spawn14) {
53569
54084
  const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
53570
54085
  const args = ["install", scopeFlag, spec.npmPackage];
53571
54086
  let result = await runNpm2(executor, args, cwd, "shim");
@@ -53715,17 +54230,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
53715
54230
 
53716
54231
  // src/cli/slashHandlers/promoteMember.ts
53717
54232
  init_messageHelpers();
53718
- import { promises as fs25 } from "node:fs";
53719
- import path45 from "node:path";
53720
- import os11 from "node:os";
54233
+ import { promises as fs27 } from "node:fs";
54234
+ import path47 from "node:path";
54235
+ import os12 from "node:os";
53721
54236
  async function handlePromoteMember(ctx, memberId) {
53722
54237
  try {
53723
54238
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
53724
54239
  const { skill, markdown } = promoteMember2(memberId);
53725
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path45.join(os11.homedir(), ".tmp", "zelari-code", "skills");
53726
- await fs25.mkdir(skillDir, { recursive: true });
53727
- const filePath = path45.join(skillDir, `${skill.id}.md`);
53728
- await fs25.writeFile(filePath, markdown, "utf8");
54240
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path47.join(os12.homedir(), ".tmp", "zelari-code", "skills");
54241
+ await fs27.mkdir(skillDir, { recursive: true });
54242
+ const filePath = path47.join(skillDir, `${skill.id}.md`);
54243
+ await fs27.writeFile(filePath, markdown, "utf8");
53729
54244
  appendSystem(
53730
54245
  ctx.setMessages,
53731
54246
  `[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
@@ -53741,29 +54256,29 @@ async function handlePromoteMember(ctx, memberId) {
53741
54256
  }
53742
54257
 
53743
54258
  // src/cli/branchManager.ts
53744
- import { promises as fs26, existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync6, rmSync as rmSync3 } from "node:fs";
53745
- import path46 from "node:path";
53746
- import os12 from "node:os";
54259
+ import { promises as fs28, existsSync as existsSync43, readFileSync as readFileSync35, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync6, rmSync as rmSync3 } from "node:fs";
54260
+ import path48 from "node:path";
54261
+ import os13 from "node:os";
53747
54262
  var META_FILENAME = "meta.json";
53748
54263
  var SESSIONS_SUBDIR = "sessions";
53749
54264
  function getBranchesBaseDir() {
53750
- return process.env.ANATHEMA_BRANCHES_DIR ?? path46.join(os12.homedir(), ".tmp", "zelari-code", "branches");
54265
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path48.join(os13.homedir(), ".tmp", "zelari-code", "branches");
53751
54266
  }
53752
54267
  function getSessionsBaseDir() {
53753
- return process.env.ANATHEMA_SESSIONS_DIR ?? path46.join(os12.homedir(), ".tmp", "zelari-code", "sessions");
54268
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path48.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
53754
54269
  }
53755
54270
  function branchPathFor(name, baseDir) {
53756
- return path46.join(baseDir, name);
54271
+ return path48.join(baseDir, name);
53757
54272
  }
53758
54273
  function metaPathFor(name, baseDir) {
53759
- return path46.join(baseDir, name, META_FILENAME);
54274
+ return path48.join(baseDir, name, META_FILENAME);
53760
54275
  }
53761
54276
  function sessionsPathFor(name, baseDir) {
53762
- return path46.join(baseDir, name, SESSIONS_SUBDIR);
54277
+ return path48.join(baseDir, name, SESSIONS_SUBDIR);
53763
54278
  }
53764
54279
  function readBranchMeta(name, baseDir) {
53765
54280
  const metaPath = metaPathFor(name, baseDir);
53766
- if (!existsSync42(metaPath)) {
54281
+ if (!existsSync43(metaPath)) {
53767
54282
  throw new BranchNotFoundError(`Branch "${name}" not found`);
53768
54283
  }
53769
54284
  try {
@@ -53784,13 +54299,13 @@ function readBranchMeta(name, baseDir) {
53784
54299
  }
53785
54300
  function writeBranchMeta(name, baseDir, meta3) {
53786
54301
  const metaPath = metaPathFor(name, baseDir);
53787
- mkdirSync19(path46.dirname(metaPath), { recursive: true });
54302
+ mkdirSync19(path48.dirname(metaPath), { recursive: true });
53788
54303
  writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
53789
54304
  }
53790
54305
  async function countSessions(name, baseDir) {
53791
54306
  const sessionsPath = sessionsPathFor(name, baseDir);
53792
54307
  try {
53793
- const entries = await fs26.readdir(sessionsPath);
54308
+ const entries = await fs28.readdir(sessionsPath);
53794
54309
  return entries.filter((e) => e.endsWith(".jsonl")).length;
53795
54310
  } catch (err) {
53796
54311
  if (err.code === "ENOENT") return 0;
@@ -53823,7 +54338,7 @@ var SessionNotFoundError = class extends Error {
53823
54338
  };
53824
54339
  function branchExists(name, baseDir = getBranchesBaseDir()) {
53825
54340
  const bp = branchPathFor(name, baseDir);
53826
- return existsSync42(bp) && existsSync42(metaPathFor(name, baseDir));
54341
+ return existsSync43(bp) && existsSync43(metaPathFor(name, baseDir));
53827
54342
  }
53828
54343
  async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
53829
54344
  if (!name || name.trim().length === 0) {
@@ -53835,15 +54350,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
53835
54350
  if (branchExists(name, baseDir)) {
53836
54351
  throw new BranchAlreadyExistsError(name);
53837
54352
  }
53838
- const sourcePath = path46.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
53839
- if (!existsSync42(sourcePath)) {
54353
+ const sourcePath = path48.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
54354
+ if (!existsSync43(sourcePath)) {
53840
54355
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
53841
54356
  }
53842
54357
  const branchPath = branchPathFor(name, baseDir);
53843
54358
  const branchSessionsPath = sessionsPathFor(name, baseDir);
53844
54359
  mkdirSync19(branchSessionsPath, { recursive: true });
53845
- const destPath = path46.join(branchSessionsPath, `${fromSessionId}.jsonl`);
53846
- await fs26.copyFile(sourcePath, destPath);
54360
+ const destPath = path48.join(branchSessionsPath, `${fromSessionId}.jsonl`);
54361
+ await fs28.copyFile(sourcePath, destPath);
53847
54362
  const meta3 = {
53848
54363
  name,
53849
54364
  createdAt: Date.now(),
@@ -53861,7 +54376,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
53861
54376
  async function listBranches(baseDir = getBranchesBaseDir()) {
53862
54377
  let entries;
53863
54378
  try {
53864
- entries = await fs26.readdir(baseDir);
54379
+ entries = await fs28.readdir(baseDir);
53865
54380
  } catch (err) {
53866
54381
  if (err.code === "ENOENT") return [];
53867
54382
  throw err;
@@ -53869,7 +54384,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
53869
54384
  const results = [];
53870
54385
  for (const entry of entries) {
53871
54386
  const metaPath = metaPathFor(entry, baseDir);
53872
- if (!existsSync42(metaPath)) continue;
54387
+ if (!existsSync43(metaPath)) continue;
53873
54388
  try {
53874
54389
  const meta3 = readBranchMeta(entry, baseDir);
53875
54390
  const sessionCount = await countSessions(entry, baseDir);
@@ -53944,26 +54459,26 @@ async function handleBranchCheckout(ctx, branchName) {
53944
54459
 
53945
54460
  // src/cli/slashHandlers/workspace.ts
53946
54461
  init_messageHelpers();
53947
- import { promises as fs27 } from "node:fs";
53948
- import path47 from "node:path";
54462
+ import { promises as fs29 } from "node:fs";
54463
+ import path49 from "node:path";
53949
54464
  async function handleWorkspaceShow(ctx, what) {
53950
54465
  try {
53951
- const zelari = path47.join(process.cwd(), ".zelari");
54466
+ const zelari = path49.join(process.cwd(), ".zelari");
53952
54467
  let content;
53953
54468
  switch (what) {
53954
54469
  case "plan": {
53955
- const planPath = path47.join(zelari, "plan.md");
54470
+ const planPath = path49.join(zelari, "plan.md");
53956
54471
  try {
53957
- content = await fs27.readFile(planPath, "utf-8");
54472
+ content = await fs29.readFile(planPath, "utf-8");
53958
54473
  } catch {
53959
54474
  content = "(no plan.md yet \u2014 run a council session first)";
53960
54475
  }
53961
54476
  break;
53962
54477
  }
53963
54478
  case "decisions": {
53964
- const decisionsDir = path47.join(zelari, "decisions");
54479
+ const decisionsDir = path49.join(zelari, "decisions");
53965
54480
  try {
53966
- const files = (await fs27.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
54481
+ const files = (await fs29.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
53967
54482
  if (files.length === 0) {
53968
54483
  content = "(no ADRs yet \u2014 invoke /council to generate some)";
53969
54484
  } else {
@@ -53971,7 +54486,7 @@ async function handleWorkspaceShow(ctx, what) {
53971
54486
  `];
53972
54487
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
53973
54488
  for (const f of files) {
53974
- const raw = await fs27.readFile(path47.join(decisionsDir, f), "utf-8");
54489
+ const raw = await fs29.readFile(path49.join(decisionsDir, f), "utf-8");
53975
54490
  const { meta: meta3, body } = parseFrontmatter2(raw);
53976
54491
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
53977
54492
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -53984,27 +54499,27 @@ async function handleWorkspaceShow(ctx, what) {
53984
54499
  break;
53985
54500
  }
53986
54501
  case "risks": {
53987
- const risksPath = path47.join(zelari, "risks.md");
54502
+ const risksPath = path49.join(zelari, "risks.md");
53988
54503
  try {
53989
- content = await fs27.readFile(risksPath, "utf-8");
54504
+ content = await fs29.readFile(risksPath, "utf-8");
53990
54505
  } catch {
53991
54506
  content = "(no risks.md yet)";
53992
54507
  }
53993
54508
  break;
53994
54509
  }
53995
54510
  case "agents": {
53996
- const agentsPath = path47.join(process.cwd(), "AGENTS.MD");
54511
+ const agentsPath = path49.join(process.cwd(), "AGENTS.MD");
53997
54512
  try {
53998
- content = await fs27.readFile(agentsPath, "utf-8");
54513
+ content = await fs29.readFile(agentsPath, "utf-8");
53999
54514
  } catch {
54000
54515
  content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
54001
54516
  }
54002
54517
  break;
54003
54518
  }
54004
54519
  case "docs": {
54005
- const docsDir = path47.join(zelari, "docs");
54520
+ const docsDir = path49.join(zelari, "docs");
54006
54521
  try {
54007
- const files = (await fs27.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
54522
+ const files = (await fs29.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
54008
54523
  content = files.length ? `# Docs (${files.length})
54009
54524
 
54010
54525
  ` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
@@ -54044,8 +54559,8 @@ async function handleWorkspaceReset(ctx, force) {
54044
54559
  return;
54045
54560
  }
54046
54561
  try {
54047
- const target = path47.join(process.cwd(), ".zelari");
54048
- await fs27.rm(target, { recursive: true, force: true });
54562
+ const target = path49.join(process.cwd(), ".zelari");
54563
+ await fs29.rm(target, { recursive: true, force: true });
54049
54564
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
54050
54565
  } catch (err) {
54051
54566
  appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
@@ -54056,16 +54571,16 @@ async function handleWorkspaceReset(ctx, force) {
54056
54571
  init_provider2();
54057
54572
 
54058
54573
  // src/cli/slashHandlers/skills.ts
54059
- import path48 from "node:path";
54060
- import os13 from "node:os";
54574
+ import path50 from "node:path";
54575
+ import os14 from "node:os";
54061
54576
 
54062
54577
  // src/cli/skillHistory.ts
54063
- import { promises as fs28, existsSync as existsSync43, statSync as statSync7, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
54578
+ import { promises as fs30, existsSync as existsSync44, statSync as statSync7, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
54064
54579
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
54065
54580
  async function readSkillHistory(file2) {
54066
54581
  let raw = "";
54067
54582
  try {
54068
- raw = await fs28.readFile(file2, "utf-8");
54583
+ raw = await fs30.readFile(file2, "utf-8");
54069
54584
  } catch {
54070
54585
  return [];
54071
54586
  }
@@ -54185,7 +54700,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
54185
54700
  });
54186
54701
  }
54187
54702
  async function handleSkillStats(ctx, skillId) {
54188
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path48.join(os13.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
54703
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path50.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
54189
54704
  try {
54190
54705
  const records = await readSkillHistory(historyFile);
54191
54706
  const stats = getSkillStats(records, skillId);
@@ -54201,7 +54716,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
54201
54716
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
54202
54717
  return;
54203
54718
  }
54204
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path48.join(os13.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
54719
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path50.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
54205
54720
  try {
54206
54721
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
54207
54722
  appendSystem(ctx.setMessages, formatted);
@@ -55388,9 +55903,9 @@ function ContinueKey({ onContinue }) {
55388
55903
  init_providerConfig();
55389
55904
 
55390
55905
  // src/cli/wizard/firstRun.ts
55391
- import { existsSync as existsSync45 } from "node:fs";
55906
+ import { existsSync as existsSync46 } from "node:fs";
55392
55907
  function shouldRunWizard(input) {
55393
- const exists = input.exists ?? existsSync45;
55908
+ const exists = input.exists ?? existsSync46;
55394
55909
  if (input.hasResetConfigFlag) {
55395
55910
  return { shouldRun: true, reason: "--reset-config flag forced wizard" };
55396
55911
  }
@@ -55711,6 +56226,16 @@ function parseHeadlessFlags(argv) {
55711
56226
  } else if (arg === "--task") {
55712
56227
  task = argv[i + 1];
55713
56228
  i++;
56229
+ } else if (arg === "--task-file") {
56230
+ const next = argv[i + 1];
56231
+ if (next) {
56232
+ try {
56233
+ const fromFile = readFileSync37(next, "utf-8");
56234
+ if (fromFile.trim()) task = fromFile;
56235
+ } catch {
56236
+ }
56237
+ }
56238
+ i++;
55714
56239
  } else if (arg === "--council") {
55715
56240
  councilFlag = true;
55716
56241
  } else if (arg === "--mode") {
@@ -55805,6 +56330,16 @@ function parseHeadlessFlags(argv) {
55805
56330
  } else if (arg === "--kraken-graph") {
55806
56331
  krakenGraph = argv[i + 1];
55807
56332
  i++;
56333
+ } else if (arg === "--kraken-graph-file") {
56334
+ const next = argv[i + 1];
56335
+ if (next) {
56336
+ try {
56337
+ const fromFile = readFileSync37(next, "utf-8");
56338
+ if (fromFile.trim()) krakenGraph = fromFile;
56339
+ } catch {
56340
+ }
56341
+ }
56342
+ i++;
55808
56343
  } else if (arg === "--plan-only") {
55809
56344
  planOnly = true;
55810
56345
  } else if (arg === "--run-plan") {
@@ -55913,8 +56448,8 @@ function createStreamScrubber2() {
55913
56448
  // src/cli/runHeadless.ts
55914
56449
  init_taskTool();
55915
56450
  init_sessionTodos();
55916
- import { promises as fs30 } from "node:fs";
55917
- import path50 from "node:path";
56451
+ import { promises as fs32 } from "node:fs";
56452
+ import path52 from "node:path";
55918
56453
  import { randomUUID as randomUUID6 } from "node:crypto";
55919
56454
  async function runHeadless(opts) {
55920
56455
  resetTaskSpawnCount();
@@ -56066,11 +56601,11 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
56066
56601
  try {
56067
56602
  let preflightGraph;
56068
56603
  if (opts.runPlan && opts.runPlan.trim() !== "") {
56069
- const planPath = path50.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
56604
+ const planPath = path52.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
56070
56605
  log(`loading pre-flight plan: ${planPath}`);
56071
56606
  let raw;
56072
56607
  try {
56073
- raw = await fs30.readFile(planPath, "utf8");
56608
+ raw = await fs32.readFile(planPath, "utf8");
56074
56609
  } catch (e) {
56075
56610
  log(`plan file not found: ${planPath} (${e.message})`);
56076
56611
  return 1;
@@ -56106,10 +56641,10 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
56106
56641
  log(formatKrakenGraphAscii2(graph));
56107
56642
  if (opts.planOnly) {
56108
56643
  const planId = randomUUID6();
56109
- const planDir = path50.join(cwd, ".zelari", "radio");
56110
- const planPath = path50.join(planDir, `plan-${planId}.json`);
56111
- await fs30.mkdir(planDir, { recursive: true });
56112
- await fs30.writeFile(
56644
+ const planDir = path52.join(cwd, ".zelari", "radio");
56645
+ const planPath = path52.join(planDir, `plan-${planId}.json`);
56646
+ await fs32.mkdir(planDir, { recursive: true });
56647
+ await fs32.writeFile(
56113
56648
  planPath,
56114
56649
  JSON.stringify(
56115
56650
  { id: graph.id, nodes: [...graph.nodes.values()] },
@@ -57214,8 +57749,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
57214
57749
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
57215
57750
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
57216
57751
  try {
57217
- const path53 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
57218
- name = path53 && /^[a-z0-9]/.test(path53) ? path53 : "imported-skill";
57752
+ const path55 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
57753
+ name = path55 && /^[a-z0-9]/.test(path55) ? path55 : "imported-skill";
57219
57754
  } catch {
57220
57755
  name = "imported-skill";
57221
57756
  }