zelari-code 2.41.0 → 2.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -807,7 +807,7 @@ async function openBrowser(url2) {
807
807
  return { bin: "xdg-open", args: [url2] };
808
808
  }
809
809
  })();
810
- return new Promise((resolve9, reject) => {
810
+ return new Promise((resolve10, reject) => {
811
811
  try {
812
812
  const child = spawn19(cmd.bin, cmd.args, {
813
813
  stdio: "ignore",
@@ -816,9 +816,9 @@ async function openBrowser(url2) {
816
816
  child.on("error", (err) => reject(err));
817
817
  child.on("spawn", () => {
818
818
  child.unref();
819
- resolve9();
819
+ resolve10();
820
820
  });
821
- setTimeout(() => resolve9(), 100);
821
+ setTimeout(() => resolve10(), 100);
822
822
  } catch (err) {
823
823
  reject(err);
824
824
  }
@@ -867,7 +867,7 @@ async function waitForLoopbackCallback(options) {
867
867
  const host = options.host ?? "127.0.0.1";
868
868
  const timeoutMs2 = options.timeoutMs ?? 3e5;
869
869
  const expectedPath = options.path.startsWith("/") ? options.path : `/${options.path}`;
870
- return new Promise((resolve9, reject) => {
870
+ return new Promise((resolve10, reject) => {
871
871
  let settled = false;
872
872
  const server = createServer((req, res) => {
873
873
  const url2 = new URL(req.url ?? "/", `http://${host}`);
@@ -896,7 +896,7 @@ async function waitForLoopbackCallback(options) {
896
896
  clearTimeout(timer);
897
897
  server.close(() => {
898
898
  if (err) reject(err);
899
- else resolve9(value ?? {});
899
+ else resolve10(value ?? {});
900
900
  });
901
901
  };
902
902
  server.on("error", (err) => finish2(void 0, err));
@@ -3482,10 +3482,10 @@ function mergeDefs(...defs) {
3482
3482
  function cloneDef(schema) {
3483
3483
  return mergeDefs(schema._zod.def);
3484
3484
  }
3485
- function getElementAtPath(obj, path103) {
3486
- if (!path103)
3485
+ function getElementAtPath(obj, path104) {
3486
+ if (!path104)
3487
3487
  return obj;
3488
- return path103.reduce((acc, key) => acc?.[key], obj);
3488
+ return path104.reduce((acc, key) => acc?.[key], obj);
3489
3489
  }
3490
3490
  function promiseAllObject(promisesObj) {
3491
3491
  const keys = Object.keys(promisesObj);
@@ -3813,11 +3813,11 @@ function explicitlyAborted(x, startIndex = 0) {
3813
3813
  }
3814
3814
  return false;
3815
3815
  }
3816
- function prefixIssues(path103, issues) {
3816
+ function prefixIssues(path104, issues) {
3817
3817
  return issues.map((iss) => {
3818
3818
  var _a3;
3819
3819
  (_a3 = iss).path ?? (_a3.path = []);
3820
- iss.path.unshift(path103);
3820
+ iss.path.unshift(path104);
3821
3821
  return iss;
3822
3822
  });
3823
3823
  }
@@ -4035,16 +4035,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
4035
4035
  }
4036
4036
  function formatError(error51, mapper = (issue2) => issue2.message) {
4037
4037
  const fieldErrors = { _errors: [] };
4038
- const processError = (error52, path103 = []) => {
4038
+ const processError = (error52, path104 = []) => {
4039
4039
  for (const issue2 of error52.issues) {
4040
4040
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4041
- issue2.errors.map((issues) => processError({ issues }, [...path103, ...issue2.path]));
4041
+ issue2.errors.map((issues) => processError({ issues }, [...path104, ...issue2.path]));
4042
4042
  } else if (issue2.code === "invalid_key") {
4043
- processError({ issues: issue2.issues }, [...path103, ...issue2.path]);
4043
+ processError({ issues: issue2.issues }, [...path104, ...issue2.path]);
4044
4044
  } else if (issue2.code === "invalid_element") {
4045
- processError({ issues: issue2.issues }, [...path103, ...issue2.path]);
4045
+ processError({ issues: issue2.issues }, [...path104, ...issue2.path]);
4046
4046
  } else {
4047
- const fullpath = [...path103, ...issue2.path];
4047
+ const fullpath = [...path104, ...issue2.path];
4048
4048
  if (fullpath.length === 0) {
4049
4049
  fieldErrors._errors.push(mapper(issue2));
4050
4050
  } else {
@@ -4071,17 +4071,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
4071
4071
  }
4072
4072
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
4073
4073
  const result = { errors: [] };
4074
- const processError = (error52, path103 = []) => {
4074
+ const processError = (error52, path104 = []) => {
4075
4075
  var _a3, _b;
4076
4076
  for (const issue2 of error52.issues) {
4077
4077
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4078
- issue2.errors.map((issues) => processError({ issues }, [...path103, ...issue2.path]));
4078
+ issue2.errors.map((issues) => processError({ issues }, [...path104, ...issue2.path]));
4079
4079
  } else if (issue2.code === "invalid_key") {
4080
- processError({ issues: issue2.issues }, [...path103, ...issue2.path]);
4080
+ processError({ issues: issue2.issues }, [...path104, ...issue2.path]);
4081
4081
  } else if (issue2.code === "invalid_element") {
4082
- processError({ issues: issue2.issues }, [...path103, ...issue2.path]);
4082
+ processError({ issues: issue2.issues }, [...path104, ...issue2.path]);
4083
4083
  } else {
4084
- const fullpath = [...path103, ...issue2.path];
4084
+ const fullpath = [...path104, ...issue2.path];
4085
4085
  if (fullpath.length === 0) {
4086
4086
  result.errors.push(mapper(issue2));
4087
4087
  continue;
@@ -4113,8 +4113,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
4113
4113
  }
4114
4114
  function toDotPath(_path) {
4115
4115
  const segs = [];
4116
- const path103 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4117
- for (const seg of path103) {
4116
+ const path104 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4117
+ for (const seg of path104) {
4118
4118
  if (typeof seg === "number")
4119
4119
  segs.push(`[${seg}]`);
4120
4120
  else if (typeof seg === "symbol")
@@ -17617,13 +17617,13 @@ function resolveRef(ref, ctx) {
17617
17617
  if (!ref.startsWith("#")) {
17618
17618
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17619
17619
  }
17620
- const path103 = ref.slice(1).split("/").filter(Boolean);
17621
- if (path103.length === 0) {
17620
+ const path104 = ref.slice(1).split("/").filter(Boolean);
17621
+ if (path104.length === 0) {
17622
17622
  return ctx.rootSchema;
17623
17623
  }
17624
17624
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17625
- if (path103[0] === defsKey) {
17626
- const key = path103[1];
17625
+ if (path104[0] === defsKey) {
17626
+ const key = path104[1];
17627
17627
  if (!key || !ctx.defs[key]) {
17628
17628
  throw new Error(`Reference not found: ${ref}`);
17629
17629
  }
@@ -18457,17 +18457,17 @@ var init_newlines = __esm({
18457
18457
  });
18458
18458
 
18459
18459
  // packages/core/dist/core/tools/builtin/fileEvents.js
18460
- function fileReadEvent(path103, snapshotId) {
18461
- return { kind: "file.read", actor: { type: "tool" }, data: { path: path103, snapshotId } };
18460
+ function fileReadEvent(path104, snapshotId) {
18461
+ return { kind: "file.read", actor: { type: "tool" }, data: { path: path104, snapshotId } };
18462
18462
  }
18463
- function fileAppliedEvent(path103, snapshotId, bytes) {
18464
- return { kind: "file.applied", actor: { type: "tool" }, data: { path: path103, snapshotId, bytes } };
18463
+ function fileAppliedEvent(path104, snapshotId, bytes) {
18464
+ return { kind: "file.applied", actor: { type: "tool" }, data: { path: path104, snapshotId, bytes } };
18465
18465
  }
18466
- function fileRejectedEvent(path103, reason, hint) {
18466
+ function fileRejectedEvent(path104, reason, hint) {
18467
18467
  return {
18468
18468
  kind: "file.rejected",
18469
18469
  actor: { type: "tool" },
18470
- data: hint === void 0 ? { path: path103, reason } : { path: path103, reason, hint }
18470
+ data: hint === void 0 ? { path: path104, reason } : { path: path104, reason, hint }
18471
18471
  };
18472
18472
  }
18473
18473
  function reReadHint(reject) {
@@ -19066,12 +19066,12 @@ function withNodeDirOnPath(base2) {
19066
19066
  const nodeDir = dirname(process.execPath);
19067
19067
  if (!nodeDir)
19068
19068
  return env;
19069
- const sep4 = process.platform === "win32" ? ";" : ":";
19069
+ const sep5 = process.platform === "win32" ? ";" : ":";
19070
19070
  const current = env.PATH ?? env.Path ?? "";
19071
- const parts = current.split(sep4).filter((p3) => p3.length > 0);
19071
+ const parts = current.split(sep5).filter((p3) => p3.length > 0);
19072
19072
  const has = parts.some((p3) => p3.toLowerCase() === nodeDir.toLowerCase());
19073
19073
  if (!has) {
19074
- env.PATH = `${nodeDir}${sep4}${current}`;
19074
+ env.PATH = `${nodeDir}${sep5}${current}`;
19075
19075
  }
19076
19076
  } catch {
19077
19077
  }
@@ -19086,7 +19086,7 @@ function createBashTool(spawnSeam) {
19086
19086
  timeoutMs: 6e4,
19087
19087
  inputSchema: BashArgsSchema,
19088
19088
  execute: async (args, ctx) => {
19089
- return new Promise((resolve9) => {
19089
+ return new Promise((resolve10) => {
19090
19090
  const start = Date.now();
19091
19091
  const cwd = args.cwd ?? ctx.cwd;
19092
19092
  const resolved = resolveShell();
@@ -19113,7 +19113,7 @@ function createBashTool(spawnSeam) {
19113
19113
  if (spawnSeam) {
19114
19114
  const seamResult = spawnSeam({ program, argv, cwd, env, signal: ctx.signal });
19115
19115
  if (seamResult.outcome !== "spawned") {
19116
- resolve9(typedErr(seamResult.reason));
19116
+ resolve10(typedErr(seamResult.reason));
19117
19117
  return;
19118
19118
  }
19119
19119
  child = seamResult.child;
@@ -19140,14 +19140,14 @@ function createBashTool(spawnSeam) {
19140
19140
  });
19141
19141
  const timer = setTimeout(() => {
19142
19142
  child.kill("SIGTERM");
19143
- resolve9(typedErr(`Bash command timed out after ${args.timeoutMs}ms. If it was waiting for interactive input: stdin is closed \u2014 pass non-interactive flags or create the files directly instead of retrying.`));
19143
+ resolve10(typedErr(`Bash command timed out after ${args.timeoutMs}ms. If it was waiting for interactive input: stdin is closed \u2014 pass non-interactive flags or create the files directly instead of retrying.`));
19144
19144
  }, args.timeoutMs);
19145
19145
  child.on("close", (code) => {
19146
19146
  clearTimeout(timer);
19147
19147
  const cappedStdout = stdout.slice(0, maxBuffer);
19148
19148
  const cappedStderr = stderr.slice(0, maxBuffer);
19149
19149
  const interactive = INTERACTIVE_CANCEL_RE.test(cappedStdout) || INTERACTIVE_CANCEL_RE.test(cappedStderr);
19150
- resolve9(typedOk({
19150
+ resolve10(typedOk({
19151
19151
  stdout: cappedStdout,
19152
19152
  stderr: cappedStderr,
19153
19153
  exitCode: code ?? -1,
@@ -19159,7 +19159,7 @@ function createBashTool(spawnSeam) {
19159
19159
  });
19160
19160
  child.on("error", (err) => {
19161
19161
  clearTimeout(timer);
19162
- resolve9(typedErr(err.message));
19162
+ resolve10(typedErr(err.message));
19163
19163
  });
19164
19164
  });
19165
19165
  }
@@ -19304,22 +19304,54 @@ var init_walk = __esm({
19304
19304
  // packages/core/dist/core/tools/builtin/search.js
19305
19305
  import { promises as fs7 } from "node:fs";
19306
19306
  import path10 from "node:path";
19307
+ function cleanGlobFragment(g) {
19308
+ let out = g.trim();
19309
+ if (out.startsWith("[") && out.charAt(1) === '"')
19310
+ out = out.slice(1);
19311
+ if (out.endsWith("]") && out.charAt(out.length - 2) === '"')
19312
+ out = out.slice(0, -1);
19313
+ if (out.length >= 2 && (out.charAt(0) === '"' && out.charAt(out.length - 1) === '"' || out.charAt(0) === "'" && out.charAt(out.length - 1) === "'")) {
19314
+ out = out.slice(1, -1).trim();
19315
+ }
19316
+ return out;
19317
+ }
19318
+ function isJsonArrayString(value) {
19319
+ if (typeof value !== "string")
19320
+ return false;
19321
+ const s = value.trim();
19322
+ if (!s.startsWith("[") || !s.endsWith("]"))
19323
+ return false;
19324
+ try {
19325
+ return Array.isArray(JSON.parse(s));
19326
+ } catch {
19327
+ return false;
19328
+ }
19329
+ }
19307
19330
  function coerceStringList(value, fallback) {
19308
19331
  if (value === void 0 || value === null)
19309
19332
  return fallback;
19310
19333
  if (Array.isArray(value)) {
19311
- const cleaned = value.filter((x) => typeof x === "string" && x.trim().length > 0);
19334
+ const cleaned = value.map((x) => typeof x === "string" ? cleanGlobFragment(x) : x).filter((x) => typeof x === "string" && x.trim().length > 0);
19312
19335
  return cleaned.length > 0 ? cleaned : fallback;
19313
19336
  }
19314
19337
  if (typeof value === "string") {
19315
19338
  const s = value.trim();
19316
19339
  if (!s)
19317
19340
  return fallback;
19341
+ if (s.startsWith("[") && s.endsWith("]")) {
19342
+ try {
19343
+ const parsed = JSON.parse(s);
19344
+ if (Array.isArray(parsed))
19345
+ return coerceStringList(parsed, fallback);
19346
+ } catch {
19347
+ }
19348
+ }
19318
19349
  if (s.includes(",") && !s.includes("{")) {
19319
- const parts = s.split(",").map((x) => x.trim()).filter(Boolean);
19350
+ const parts = s.split(",").map((x) => cleanGlobFragment(x)).filter(Boolean);
19320
19351
  return parts.length > 0 ? parts : fallback;
19321
19352
  }
19322
- return [s];
19353
+ const single = cleanGlobFragment(s);
19354
+ return single ? [single] : fallback;
19323
19355
  }
19324
19356
  return fallback;
19325
19357
  }
@@ -19425,7 +19457,7 @@ var init_search = __esm({
19425
19457
  });
19426
19458
  grepContentTool = {
19427
19459
  name: "grep_content",
19428
- 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 (grep --include style): a glob without '/' (e.g. "*.md") matches the file basename at ANY depth; a glob with '/' (e.g. "src/*.ts") matches the relative path at exactly that level; "**" is explicit recursion ("**/*.ts" matches at any depth, same as the bare form). 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.`,
19460
+ 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 (grep --include style): a glob without '/' (e.g. "*.md") matches the file basename at ANY depth; a glob with '/' (e.g. "src/*.ts") matches the relative path at exactly that level; "**" is explicit recursion ("**/*.ts" matches at any depth, same as the bare form). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Stringified-array ('["*.ts"]') or quote-wrapped forms are auto-repaired, with a warning. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.`,
19429
19461
  permissions: ["read"],
19430
19462
  sideEffect: "none",
19431
19463
  timeoutMs: 3e4,
@@ -19440,9 +19472,14 @@ var init_search = __esm({
19440
19472
  const warnings = [];
19441
19473
  if (Array.isArray(rawInclude) && rawInclude.length === 0) {
19442
19474
  warnings.push('DEPRECATED_INPUT: empty include array \u2014 omit the field instead; this will become INVALID_ARGUMENT (planned v1.47). Fell back to ["*"]');
19475
+ } else if (isJsonArrayString(rawInclude)) {
19476
+ warnings.push(`include repaired from stringified-array form to ${JSON.stringify(include)}`);
19443
19477
  } else if (typeof rawInclude === "string") {
19444
19478
  warnings.push(`include coerced from bare string to ${JSON.stringify(include)}`);
19445
19479
  }
19480
+ if (isJsonArrayString(args.exclude)) {
19481
+ warnings.push(`exclude repaired from stringified-array form to ${JSON.stringify(exclude)}`);
19482
+ }
19446
19483
  if (!await isDirectory(absRoot)) {
19447
19484
  let exists = true;
19448
19485
  try {
@@ -20336,11 +20373,11 @@ var init_tools = __esm({
20336
20373
  if (!ctx.addDocument)
20337
20374
  return "Knowledge vault tool not available.";
20338
20375
  const title = args["title"] || "New Document";
20339
- const path103 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
20376
+ const path104 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
20340
20377
  const content = args["content"] || "";
20341
20378
  const tags = args["tags"] || [];
20342
20379
  ctx.addDocument({
20343
- path: path103,
20380
+ path: path104,
20344
20381
  title,
20345
20382
  content,
20346
20383
  format: "markdown",
@@ -20349,7 +20386,7 @@ var init_tools = __esm({
20349
20386
  workspaceId: ctx.workspaceId
20350
20387
  });
20351
20388
  ctx.addActivity("vault", "created document", title);
20352
- return `Document "${title}" created at "${path103}".`;
20389
+ return `Document "${title}" created at "${path104}".`;
20353
20390
  }
20354
20391
  }
20355
20392
  ];
@@ -22259,7 +22296,7 @@ var init_nodeProviders = __esm({
22259
22296
  const isWindows = process.platform === "win32";
22260
22297
  const file2 = isWindows ? "cmd.exe" : "/bin/sh";
22261
22298
  const args = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
22262
- return await new Promise((resolve9) => {
22299
+ return await new Promise((resolve10) => {
22263
22300
  const child = spawn2(file2, args, {
22264
22301
  cwd,
22265
22302
  shell: false,
@@ -22292,7 +22329,7 @@ var init_nodeProviders = __esm({
22292
22329
  });
22293
22330
  child.on("error", (err) => {
22294
22331
  clearTimeout(timer);
22295
- resolve9({
22332
+ resolve10({
22296
22333
  exitCode: null,
22297
22334
  stdout,
22298
22335
  stderr: `${stderr}${err.message}`,
@@ -22302,7 +22339,7 @@ var init_nodeProviders = __esm({
22302
22339
  });
22303
22340
  child.on("close", (code) => {
22304
22341
  clearTimeout(timer);
22305
- resolve9({ exitCode: code, stdout, stderr, durationMs: Date.now() - started, timedOut });
22342
+ resolve10({ exitCode: code, stdout, stderr, durationMs: Date.now() - started, timedOut });
22306
22343
  });
22307
22344
  });
22308
22345
  }
@@ -22380,7 +22417,7 @@ import path16 from "node:path";
22380
22417
  import crypto2 from "node:crypto";
22381
22418
  import { spawn as spawn3 } from "node:child_process";
22382
22419
  async function git(cwd, args) {
22383
- return await new Promise((resolve9, reject) => {
22420
+ return await new Promise((resolve10, reject) => {
22384
22421
  const child = spawn3("git", args, { cwd, shell: false });
22385
22422
  let stdout = "";
22386
22423
  let stderr = "";
@@ -22389,7 +22426,7 @@ async function git(cwd, args) {
22389
22426
  child.on("error", (err) => reject(err));
22390
22427
  child.on("close", (code) => {
22391
22428
  if (code === 0)
22392
- resolve9(stdout);
22429
+ resolve10(stdout);
22393
22430
  else
22394
22431
  reject(new Error(`git ${args.join(" ")} failed (${code}): ${stderr.trim()}`));
22395
22432
  });
@@ -25287,16 +25324,16 @@ function runRetentionFromEnv() {
25287
25324
  maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
25288
25325
  };
25289
25326
  }
25290
- async function dirSize(path103) {
25327
+ async function dirSize(path104) {
25291
25328
  let total = 0;
25292
25329
  let entries;
25293
25330
  try {
25294
- entries = await readdir(path103, { withFileTypes: true });
25331
+ entries = await readdir(path104, { withFileTypes: true });
25295
25332
  } catch {
25296
25333
  return 0;
25297
25334
  }
25298
25335
  for (const entry of entries) {
25299
- const child = join3(path103, entry.name);
25336
+ const child = join3(path104, entry.name);
25300
25337
  if (entry.isDirectory())
25301
25338
  total += await dirSize(child);
25302
25339
  else {
@@ -25323,19 +25360,19 @@ async function enforceRunRetention(runsDir, options = {}) {
25323
25360
  for (const entry of entries) {
25324
25361
  if (!entry.isDirectory())
25325
25362
  continue;
25326
- const path103 = join3(runsDir, entry.name);
25363
+ const path104 = join3(runsDir, entry.name);
25327
25364
  let startedAt = 0;
25328
25365
  let endedAt;
25329
25366
  let completed = false;
25330
25367
  try {
25331
- const manifest = JSON.parse(await readFile(join3(path103, "manifest.json"), "utf8"));
25368
+ const manifest = JSON.parse(await readFile(join3(path104, "manifest.json"), "utf8"));
25332
25369
  startedAt = manifest.startedAt ?? 0;
25333
25370
  endedAt = manifest.endedAt;
25334
25371
  completed = Boolean(endedAt) && manifest.status !== "running";
25335
25372
  } catch {
25336
25373
  completed = false;
25337
25374
  }
25338
- infos.push({ name: entry.name, path: path103, startedAt, endedAt, completed, bytes: await dirSize(path103) });
25375
+ infos.push({ name: entry.name, path: path104, startedAt, endedAt, completed, bytes: await dirSize(path104) });
25339
25376
  }
25340
25377
  const remove = async (info) => {
25341
25378
  await rm(info.path, { recursive: true, force: true });
@@ -25893,12 +25930,12 @@ var init_engine = __esm({
25893
25930
  * content digest) and the returned ref carries the event seq when the
25894
25931
  * emitter resolved one.
25895
25932
  */
25896
- async fsEvidence(observation, path103, sha2562, content, extra = {}) {
25933
+ async fsEvidence(observation, path104, sha2562, content, extra = {}) {
25897
25934
  const digest = sha2562 && content !== void 0 ? sha2562(content) : void 0;
25898
- const seq = await this.emitEvidence({ observation, path: path103, ...extra, ...digest ? { digest } : {} });
25935
+ const seq = await this.emitEvidence({ observation, path: path104, ...extra, ...digest ? { digest } : {} });
25899
25936
  return {
25900
25937
  tier: "fs-observation",
25901
- ref: path103,
25938
+ ref: path104,
25902
25939
  capturedAt: Date.now(),
25903
25940
  ...digest ? { digest } : {},
25904
25941
  ...seq !== void 0 ? { seq } : {}
@@ -29044,22 +29081,32 @@ function parseMinimaxStyleToolCalls(text) {
29044
29081
  }
29045
29082
  function parseLooseArgs(body) {
29046
29083
  const args = {};
29047
- const paramTag = /(?:parameter\s+name=|<\s*)["']?([a-zA-Z_][\w]*)["']?\s*(?:>|=\s*["']?)([^<\]\n]+)/gi;
29084
+ const paramFull = /<parameter\s+name=["']([a-zA-Z_][\w]*)["']\s*>([\s\S]*?)<\/parameter>/gi;
29048
29085
  let m;
29049
- while ((m = paramTag.exec(body)) !== null) {
29086
+ while ((m = paramFull.exec(body)) !== null) {
29087
+ const key = m[1];
29088
+ if (["invoke", "minimax", "tool_call", "parameter"].includes(key))
29089
+ continue;
29090
+ args[key] = coerceJsonishValue(m[2]);
29091
+ }
29092
+ const rest = body.replace(/<parameter\s+name=["'][a-zA-Z_][\w]*["']\s*>[\s\S]*?<\/parameter>/gi, " ");
29093
+ const paramTag = /(?:parameter\s+name=|<\s*)["']?([a-zA-Z_][\w]*)["']?\s*(?:>|=\s*["']?)([^<\]\n]+)/gi;
29094
+ while ((m = paramTag.exec(rest)) !== null) {
29050
29095
  const key = m[1];
29051
29096
  if (key === "invoke" || key === "minimax" || key === "tool_call")
29052
29097
  continue;
29053
- args[key] = m[2].replace(/^["']|["']$/g, "").trim();
29098
+ if (args[key] !== void 0)
29099
+ continue;
29100
+ args[key] = coerceJsonishValue(m[2]);
29054
29101
  }
29055
29102
  const lineRe = /(?:^|[\s\[>])([a-zA-Z_][\w]*)\s*[>:=]\s*([^\n<\]]+)/g;
29056
- while ((m = lineRe.exec(body)) !== null) {
29103
+ while ((m = lineRe.exec(rest)) !== null) {
29057
29104
  const key = m[1];
29058
29105
  if (args[key] !== void 0)
29059
29106
  continue;
29060
29107
  if (["invoke", "name", "minimax", "tool_call", "parameter"].includes(key))
29061
29108
  continue;
29062
- args[key] = m[2].replace(/^["']|["']$/g, "").trim();
29109
+ args[key] = coerceJsonishValue(m[2]);
29063
29110
  }
29064
29111
  if (Object.keys(args).length === 0) {
29065
29112
  const brace = body.indexOf("{");
@@ -29075,6 +29122,20 @@ function parseLooseArgs(body) {
29075
29122
  }
29076
29123
  return args;
29077
29124
  }
29125
+ function coerceJsonishValue(raw) {
29126
+ const t = raw.trim();
29127
+ if (t.startsWith("[") || t.startsWith("{")) {
29128
+ try {
29129
+ return JSON.parse(t);
29130
+ } catch {
29131
+ }
29132
+ try {
29133
+ return JSON.parse(t + (t.startsWith("[") ? "]" : "}"));
29134
+ } catch {
29135
+ }
29136
+ }
29137
+ return t.replace(/^["']|["']$/g, "").trim();
29138
+ }
29078
29139
  function tryParseToolArray(raw) {
29079
29140
  let parsed;
29080
29141
  try {
@@ -30504,8 +30565,8 @@ function wrapLegacyStream(legacyStream, params, _messages, _tools) {
30504
30565
  }
30505
30566
  if (done)
30506
30567
  break;
30507
- await new Promise((resolve9) => {
30508
- resolveNext = resolve9;
30568
+ await new Promise((resolve10) => {
30569
+ resolveNext = resolve10;
30509
30570
  });
30510
30571
  }
30511
30572
  await promise2;
@@ -30638,11 +30699,11 @@ var init_sessionJsonl = __esm({
30638
30699
  */
30639
30700
  scheduleFlush() {
30640
30701
  if (this.pending.length > 0 && !this.flushDeferred) {
30641
- let resolve9;
30702
+ let resolve10;
30642
30703
  const promise2 = new Promise((r) => {
30643
- resolve9 = r;
30704
+ resolve10 = r;
30644
30705
  });
30645
- this.flushDeferred = { promise: promise2, resolve: resolve9 };
30706
+ this.flushDeferred = { promise: promise2, resolve: resolve10 };
30646
30707
  }
30647
30708
  if (this.flushTimer === null && this.pending.length > 0) {
30648
30709
  this.flushTimer = setTimeout(() => {
@@ -30949,7 +31010,7 @@ var init_lifecycleHookRunner = __esm({
30949
31010
  }
30950
31011
  /** Spawn `command`, write JSON payload to stdin, read stdout until close. */
30951
31012
  execCommand(command, payload, timeoutMs2, cwd) {
30952
- return new Promise((resolve9, reject) => {
31013
+ return new Promise((resolve10, reject) => {
30953
31014
  const argv = splitHookCommandLine(command);
30954
31015
  if (argv.length === 0) {
30955
31016
  reject(new Error("empty hook command"));
@@ -30983,7 +31044,7 @@ var init_lifecycleHookRunner = __esm({
30983
31044
  reject(new Error(`exited ${code}: ${stderr.trim().slice(0, 200)}`));
30984
31045
  return;
30985
31046
  }
30986
- resolve9(stdout);
31047
+ resolve10(stdout);
30987
31048
  });
30988
31049
  child.stdin?.on("error", () => {
30989
31050
  });
@@ -31032,7 +31093,17 @@ var init_appServerTypes = __esm({
31032
31093
 
31033
31094
  // packages/core/dist/harness/appServer.js
31034
31095
  import { randomUUID } from "node:crypto";
31035
- import { resolve } from "node:path";
31096
+ import { isAbsolute, resolve } from "node:path";
31097
+ function resolveSessionWorkspaceRoot(workspaceRoot) {
31098
+ if (typeof workspaceRoot !== "string" || workspaceRoot.trim() === "") {
31099
+ throw new Error("HarnessAppServer: createSession requires a non-empty `workspaceRoot` (it never falls back to process.cwd(): sessions on different workspaces must stay isolated)");
31100
+ }
31101
+ const raw = workspaceRoot.trim();
31102
+ if (!isAbsolute(raw)) {
31103
+ throw new Error(`HarnessAppServer: createSession workspaceRoot must be an absolute path (got '${raw}') \u2014 a relative root would resolve against the server process.cwd() and mix sessions (ADR-0016)`);
31104
+ }
31105
+ return resolve(raw);
31106
+ }
31036
31107
  var HarnessAppServer;
31037
31108
  var init_appServer = __esm({
31038
31109
  "packages/core/dist/harness/appServer.js"() {
@@ -31053,7 +31124,7 @@ var init_appServer = __esm({
31053
31124
  this.defaultServicesFactory = options.createWorkspaceServices;
31054
31125
  }
31055
31126
  createSession(options) {
31056
- const root = resolve(options.workspaceRoot);
31127
+ const root = resolveSessionWorkspaceRoot(options.workspaceRoot);
31057
31128
  const factory = options.createWorkspaceServices ?? this.defaultServicesFactory;
31058
31129
  if (!factory) {
31059
31130
  throw new Error("HarnessAppServer: createWorkspaceServices factory is required");
@@ -31289,6 +31360,7 @@ __export(harness_exports, {
31289
31360
  recordToolResult: () => recordToolResult,
31290
31361
  recordUsage: () => recordUsage,
31291
31362
  resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
31363
+ resolveSessionWorkspaceRoot: () => resolveSessionWorkspaceRoot,
31292
31364
  runExtensionPreToolUse: () => runExtensionPreToolUse,
31293
31365
  sha256Hex: () => sha256Hex,
31294
31366
  splitHookCommandLine: () => splitHookCommandLine,
@@ -32107,11 +32179,11 @@ var init_synthesisAudit = __esm({
32107
32179
  import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
32108
32180
  import { join as join5 } from "node:path";
32109
32181
  function loadNfrSpec(zelariRoot) {
32110
- const path103 = join5(zelariRoot, "nfr-spec.json");
32111
- if (!existsSync10(path103))
32182
+ const path104 = join5(zelariRoot, "nfr-spec.json");
32183
+ if (!existsSync10(path104))
32112
32184
  return null;
32113
32185
  try {
32114
- const raw = JSON.parse(readFileSync9(path103, "utf8"));
32186
+ const raw = JSON.parse(readFileSync9(path104, "utf8"));
32115
32187
  if (raw.version !== 1 || !Array.isArray(raw.targets))
32116
32188
  return null;
32117
32189
  return raw;
@@ -34512,9 +34584,9 @@ var init_types9 = __esm({
34512
34584
  import { readFileSync as readFileSync14 } from "node:fs";
34513
34585
  import { join as join11 } from "node:path";
34514
34586
  function readLessonsDeduped(zelariRoot) {
34515
- const path103 = join11(zelariRoot, LESSONS_FILE);
34587
+ const path104 = join11(zelariRoot, LESSONS_FILE);
34516
34588
  try {
34517
- const raw = readFileSync14(path103, "utf8");
34589
+ const raw = readFileSync14(path104, "utf8");
34518
34590
  const byId = /* @__PURE__ */ new Map();
34519
34591
  for (const line of raw.split(/\r?\n/)) {
34520
34592
  if (!line.trim())
@@ -34615,8 +34687,8 @@ function keywordsFrom(check2, signature) {
34615
34687
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
34616
34688
  }
34617
34689
  function writeLesson(zelariRoot, lesson) {
34618
- const path103 = join12(zelariRoot, LESSONS_FILE);
34619
- appendFileSync(path103, `${JSON.stringify(lesson)}
34690
+ const path104 = join12(zelariRoot, LESSONS_FILE);
34691
+ appendFileSync(path104, `${JSON.stringify(lesson)}
34620
34692
  `, "utf8");
34621
34693
  }
34622
34694
  function findSimilar(lessons, signature) {
@@ -36584,9 +36656,9 @@ function findCycle(nodes) {
36584
36656
  if (color.get(start) !== WHITE)
36585
36657
  continue;
36586
36658
  const stack = [[start, 0]];
36587
- const path103 = [];
36659
+ const path104 = [];
36588
36660
  color.set(start, GRAY);
36589
- path103.push(start);
36661
+ path104.push(start);
36590
36662
  while (stack.length > 0) {
36591
36663
  const top = stack[stack.length - 1];
36592
36664
  const [id3, idx] = top;
@@ -36599,17 +36671,17 @@ function findCycle(nodes) {
36599
36671
  continue;
36600
36672
  const c = color.get(dep);
36601
36673
  if (c === GRAY) {
36602
- const at = path103.indexOf(dep);
36603
- return [...path103.slice(at), dep];
36674
+ const at = path104.indexOf(dep);
36675
+ return [...path104.slice(at), dep];
36604
36676
  }
36605
36677
  if (c === WHITE) {
36606
36678
  color.set(dep, GRAY);
36607
- path103.push(dep);
36679
+ path104.push(dep);
36608
36680
  stack.push([dep, 0]);
36609
36681
  }
36610
36682
  } else {
36611
36683
  color.set(id3, BLACK);
36612
- path103.pop();
36684
+ path104.pop();
36613
36685
  stack.pop();
36614
36686
  }
36615
36687
  }
@@ -37529,8 +37601,8 @@ var init_runner = __esm({
37529
37601
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
37530
37602
  pending: []
37531
37603
  };
37532
- const path103 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
37533
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path103}`);
37604
+ const path104 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
37605
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path104}`);
37534
37606
  return snapshot;
37535
37607
  }
37536
37608
  callLog(msg, data) {
@@ -38826,7 +38898,7 @@ var CORE_VERSION;
38826
38898
  var init_version = __esm({
38827
38899
  "packages/core/dist/version.js"() {
38828
38900
  "use strict";
38829
- CORE_VERSION = "2.41.0";
38901
+ CORE_VERSION = "2.43.0";
38830
38902
  }
38831
38903
  });
38832
38904
 
@@ -39309,6 +39381,7 @@ __export(dist_exports, {
39309
39381
  resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
39310
39382
  resolveResponseLanguage: () => resolveResponseLanguage,
39311
39383
  resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
39384
+ resolveSessionWorkspaceRoot: () => resolveSessionWorkspaceRoot,
39312
39385
  resolveSessionsDir: () => resolveSessionsDir,
39313
39386
  resolveSkillDependencies: () => resolveSkillDependencies,
39314
39387
  resolveVerifyRetryTool: () => resolveVerifyRetryTool,
@@ -40162,7 +40235,7 @@ ${fallback.output}`
40162
40235
  return primary;
40163
40236
  }
40164
40237
  function runNpm(executor, args, mode, npmCliPath) {
40165
- return new Promise((resolve9) => {
40238
+ return new Promise((resolve10) => {
40166
40239
  let stdout = "";
40167
40240
  let stderr = "";
40168
40241
  const stdio = ["ignore", "pipe", "pipe"];
@@ -40174,7 +40247,7 @@ function runNpm(executor, args, mode, npmCliPath) {
40174
40247
  stderr += chunk.toString();
40175
40248
  });
40176
40249
  child.on("error", (err) => {
40177
- resolve9({
40250
+ resolve10({
40178
40251
  ok: false,
40179
40252
  output: stdout + stderr,
40180
40253
  error: err.message,
@@ -40183,7 +40256,7 @@ function runNpm(executor, args, mode, npmCliPath) {
40183
40256
  });
40184
40257
  child.on("close", (code) => {
40185
40258
  const ok = code === 0;
40186
- resolve9({
40259
+ resolve10({
40187
40260
  ok,
40188
40261
  output: stdout + stderr,
40189
40262
  error: ok ? void 0 : `npm exited with code ${code}`,
@@ -41308,9 +41381,9 @@ function spillToolOutput(fullText, meta3) {
41308
41381
  const rnd = randomBytes3(3).toString("hex");
41309
41382
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
41310
41383
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
41311
- const path103 = join14(dir, file2);
41312
- writeFileSync12(path103, fullText, "utf8");
41313
- return path103;
41384
+ const path104 = join14(dir, file2);
41385
+ writeFileSync12(path104, fullText, "utf8");
41386
+ return path104;
41314
41387
  } catch {
41315
41388
  return null;
41316
41389
  }
@@ -41356,10 +41429,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
41356
41429
  ${tail2}`;
41357
41430
  }
41358
41431
  if (doSpill) {
41359
- const path103 = spillToolOutput(text, { toolName: opts.toolName });
41360
- if (path103) {
41432
+ const path104 = spillToolOutput(text, { toolName: opts.toolName });
41433
+ if (path104) {
41361
41434
  const spillNote = `
41362
- \u2026 [full output spilled to: ${path103} \u2014 re-read with read_file if you need the complete text] \u2026`;
41435
+ \u2026 [full output spilled to: ${path104} \u2014 re-read with read_file if you need the complete text] \u2026`;
41363
41436
  if (preview.includes("] \u2026\n")) {
41364
41437
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
41365
41438
  `);
@@ -41716,8 +41789,15 @@ import path36 from "node:path";
41716
41789
  function radioDir(cwd) {
41717
41790
  return path36.join(cwd, ".zelari", "radio");
41718
41791
  }
41792
+ function processFallbackRadioId() {
41793
+ if (fallbackRadioId === null) {
41794
+ fallbackRadioId = `default-${process.pid}-${Date.now().toString(36)}`;
41795
+ }
41796
+ return fallbackRadioId;
41797
+ }
41719
41798
  function radioPath(cwd, sessionId2) {
41720
- const safe = (sessionId2 || "default").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
41799
+ const id3 = sessionId2.trim() ? sessionId2 : processFallbackRadioId();
41800
+ const safe = id3.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
41721
41801
  return path36.join(radioDir(cwd), `${safe}.jsonl`);
41722
41802
  }
41723
41803
  function appendRadioLine(file2, line) {
@@ -41812,10 +41892,11 @@ function formatKrakenRadioStatus(cwd, sessionId2, limit = 12) {
41812
41892
  });
41813
41893
  return [`Kraken radio (last ${events.length}) session=${sessionId2}:`, ...lines].join("\n");
41814
41894
  }
41815
- var MAX_CACHED_RADIO_FDS, radioFds;
41895
+ var fallbackRadioId, MAX_CACHED_RADIO_FDS, radioFds;
41816
41896
  var init_krakenRadio = __esm({
41817
41897
  "src/cli/tools/krakenRadio.ts"() {
41818
41898
  "use strict";
41899
+ fallbackRadioId = null;
41819
41900
  MAX_CACHED_RADIO_FDS = 32;
41820
41901
  radioFds = /* @__PURE__ */ new Map();
41821
41902
  }
@@ -42162,28 +42243,28 @@ var init_storage = __esm({
42162
42243
  VALID_SCALARS = /^(true|false|null|~)$/i;
42163
42244
  Storage = class {
42164
42245
  /** Read a Markdown file with frontmatter. Throws if not found. */
42165
- read(path103) {
42166
- if (!existsSync19(path103)) {
42167
- throw new Error(`File not found: ${path103}`);
42246
+ read(path104) {
42247
+ if (!existsSync19(path104)) {
42248
+ throw new Error(`File not found: ${path104}`);
42168
42249
  }
42169
- const md = readFileSync17(path103, "utf8");
42250
+ const md = readFileSync17(path104, "utf8");
42170
42251
  return parseFrontmatter(md);
42171
42252
  }
42172
42253
  /** Read a Markdown file; returns null if not found. */
42173
- readIfExists(path103) {
42174
- if (!existsSync19(path103)) return null;
42175
- return this.read(path103);
42254
+ readIfExists(path104) {
42255
+ if (!existsSync19(path104)) return null;
42256
+ return this.read(path104);
42176
42257
  }
42177
42258
  /**
42178
42259
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
42179
42260
  * The meta object is serialized as YAML frontmatter; body as Markdown.
42180
42261
  */
42181
- write(path103, meta3, body) {
42182
- mkdirSync10(dirname2(path103), { recursive: true });
42183
- const tmp = path103 + ".tmp-" + process.pid;
42262
+ write(path104, meta3, body) {
42263
+ mkdirSync10(dirname2(path104), { recursive: true });
42264
+ const tmp = path104 + ".tmp-" + process.pid;
42184
42265
  const md = serializeFrontmatter(meta3, body);
42185
42266
  writeFileSync14(tmp, md, "utf8");
42186
- renameSync2(tmp, path103);
42267
+ renameSync2(tmp, path104);
42187
42268
  }
42188
42269
  /** List all .md files in a directory (non-recursive). */
42189
42270
  listMarkdown(dir) {
@@ -42197,8 +42278,8 @@ var init_storage = __esm({
42197
42278
  const prev2 = this.chains.get(key) ?? Promise.resolve();
42198
42279
  let release = () => {
42199
42280
  };
42200
- const next = new Promise((resolve9) => {
42201
- release = resolve9;
42281
+ const next = new Promise((resolve10) => {
42282
+ release = resolve10;
42202
42283
  });
42203
42284
  const chained = prev2.then(() => next);
42204
42285
  this.chains.set(key, chained);
@@ -42294,8 +42375,8 @@ function nextPlanTaskId(store6) {
42294
42375
  return `t${store6.counter}`;
42295
42376
  }
42296
42377
  function writePlanTaskArtifact(rootDir, task) {
42297
- const path103 = join17(rootDir, "plan-tasks", `${task.id}.md`);
42298
- mkdirSync11(dirname3(path103), { recursive: true });
42378
+ const path104 = join17(rootDir, "plan-tasks", `${task.id}.md`);
42379
+ mkdirSync11(dirname3(path104), { recursive: true });
42299
42380
  const meta3 = {
42300
42381
  kind: "task",
42301
42382
  id: task.id,
@@ -42316,7 +42397,7 @@ function writePlanTaskArtifact(rootDir, task) {
42316
42397
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
42317
42398
  ""
42318
42399
  ].filter((l) => l !== null).join("\n");
42319
- new Storage().write(path103, meta3, body);
42400
+ new Storage().write(path104, meta3, body);
42320
42401
  }
42321
42402
  function loadHandle(rootDir) {
42322
42403
  const jsonPath = join17(rootDir, "plan.json");
@@ -42994,14 +43075,14 @@ var init_engine2 = __esm({
42994
43075
  parse: parseRuffJson
42995
43076
  }
42996
43077
  ];
42997
- defaultRunner = (cmd, args, opts) => new Promise((resolve9) => {
43078
+ defaultRunner = (cmd, args, opts) => new Promise((resolve10) => {
42998
43079
  let stdout = "";
42999
43080
  let stderr = "";
43000
43081
  let settled = false;
43001
43082
  const done = (r) => {
43002
43083
  if (settled) return;
43003
43084
  settled = true;
43004
- resolve9(r);
43085
+ resolve10(r);
43005
43086
  };
43006
43087
  let child;
43007
43088
  try {
@@ -44056,7 +44137,7 @@ async function readChecks(cwd) {
44056
44137
  }
44057
44138
  }
44058
44139
  function runShell(command, cwd, timeoutMs2, signal) {
44059
- return new Promise((resolve9) => {
44140
+ return new Promise((resolve10) => {
44060
44141
  const isWin = process.platform === "win32";
44061
44142
  const child = spawn8(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
44062
44143
  cwd,
@@ -44070,7 +44151,7 @@ function runShell(command, cwd, timeoutMs2, signal) {
44070
44151
  const finish2 = (exitCode) => {
44071
44152
  if (settled) return;
44072
44153
  settled = true;
44073
- resolve9({ exitCode, stdout, stderr });
44154
+ resolve10({ exitCode, stdout, stderr });
44074
44155
  };
44075
44156
  const timer = setTimeout(() => {
44076
44157
  try {
@@ -46781,8 +46862,8 @@ ${upstream}` : node.prompt,
46781
46862
  if (ms <= 0) return promise2;
46782
46863
  let timer;
46783
46864
  const TIMED_OUT = Symbol("timeout");
46784
- const timeout = new Promise((resolve9) => {
46785
- timer = setTimeout(() => resolve9(TIMED_OUT), ms);
46865
+ const timeout = new Promise((resolve10) => {
46866
+ timer = setTimeout(() => resolve10(TIMED_OUT), ms);
46786
46867
  });
46787
46868
  const raced = await Promise.race([promise2, timeout]).finally(() => {
46788
46869
  if (timer) clearTimeout(timer);
@@ -46791,8 +46872,8 @@ ${upstream}` : node.prompt,
46791
46872
  controller?.abort();
46792
46873
  const graceMs = this.cancelGraceMs ?? resolveCancelGraceMs();
46793
46874
  let graceTimer;
46794
- const grace = new Promise((resolve9) => {
46795
- graceTimer = setTimeout(() => resolve9(TIMED_OUT), graceMs);
46875
+ const grace = new Promise((resolve10) => {
46876
+ graceTimer = setTimeout(() => resolve10(TIMED_OUT), graceMs);
46796
46877
  });
46797
46878
  const settled = await Promise.race([promise2, grace]).finally(() => {
46798
46879
  if (graceTimer) clearTimeout(graceTimer);
@@ -47850,7 +47931,10 @@ async function runTentacle(opts) {
47850
47931
  emitActivity({
47851
47932
  type: "agent_ended",
47852
47933
  agentId: id3,
47853
- reason: info.detail ?? (info.ok === false ? "failed" : "completed"),
47934
+ // t102: `reason` feeds a failure-heuristic on the Desktop; free text in
47935
+ // it (result excerpt) could flip a SUCCESSFUL tentacle to "failed".
47936
+ // Only genuine failures carry detail; successes are enum-only.
47937
+ reason: info.ok === false ? info.detail ?? "failed" : "completed",
47854
47938
  ok: info.ok !== false,
47855
47939
  durationMs: info.durationMs ?? 0,
47856
47940
  ts: Date.now()
@@ -47861,7 +47945,10 @@ async function runTentacle(opts) {
47861
47945
  sub = await deps.createSubAgentContext({
47862
47946
  agent,
47863
47947
  thoroughness,
47864
- cwd: effectiveCwd
47948
+ cwd: effectiveCwd,
47949
+ // Per-spawn thinking effort (ADR-0017): the factory applies arg > per-kind
47950
+ // env > inherited provider default and reports the winner back on `sub`.
47951
+ ...args.thinkingEffort ? { thinkingEffort: args.thinkingEffort } : {}
47865
47952
  });
47866
47953
  } catch (err) {
47867
47954
  if (worktree) await cleanupKrakenWorktree(worktree);
@@ -47897,6 +47984,8 @@ async function runTentacle(opts) {
47897
47984
  error: "task: no provider configured for the sub-agent (set an API key / run /login)."
47898
47985
  };
47899
47986
  }
47987
+ const requestedThinking = args.thinkingEffort && args.thinkingEffort.toLowerCase() !== "inherit" ? args.thinkingEffort : void 0;
47988
+ const spawnThinking = sub.thinking ?? requestedThinking;
47900
47989
  emitActivity({
47901
47990
  type: "agent_spawned",
47902
47991
  agentId: liveId,
@@ -47904,6 +47993,7 @@ async function runTentacle(opts) {
47904
47993
  title: args.description,
47905
47994
  ...sub.model ? { model: sub.model } : {},
47906
47995
  ...sub.provider ? { provider: sub.provider } : {},
47996
+ ...spawnThinking ? { thinking: spawnThinking } : {},
47907
47997
  ...args.scope && args.scope.length > 0 ? { scope: args.scope } : {},
47908
47998
  ...worktree ? { worktree: worktree.path } : {},
47909
47999
  ts: Date.now()
@@ -48238,7 +48328,8 @@ RESTRICTED in this mode: only agent=${allowedAgents.join("|")} is allowed.` : ""
48238
48328
  description: args.description,
48239
48329
  prompt: args.prompt,
48240
48330
  scope: args.scope,
48241
- acceptance: args.acceptance
48331
+ acceptance: args.acceptance,
48332
+ ...args.thinkingEffort ? { thinkingEffort: args.thinkingEffort } : {}
48242
48333
  },
48243
48334
  agent,
48244
48335
  thoroughness,
@@ -48394,6 +48485,9 @@ var init_taskTool = __esm({
48394
48485
  ),
48395
48486
  acceptance: external_exports.array(external_exports.string().min(1)).max(16).optional().describe(
48396
48487
  "Optional acceptance checklist (contract). Appended to the prompt as Acceptance criteria."
48488
+ ),
48489
+ thinkingEffort: external_exports.enum(["inherit", "auto", "off", "low", "medium", "high", "xhigh", "max"]).optional().describe(
48490
+ "Thinking effort for THIS tentacle only (ADR-0017), overriding the provider default used by the lead. 'inherit' (and omitting the field) defers to the per-kind default: ZELARI_KRAKEN_<EXPLORE|GENERAL|VERIFY>_THINKING, else the provider thinkingByProvider value. Token budgets are not accepted here \u2014 set ZELARI_KRAKEN_<KIND>_THINKING='budget:<n>' instead. Invalid values are ignored."
48397
48491
  )
48398
48492
  });
48399
48493
  TaskPurposeSchema = external_exports.enum(["candidate"]).optional().describe(
@@ -49910,6 +50004,383 @@ var init_skillTool = __esm({
49910
50004
  }
49911
50005
  });
49912
50006
 
50007
+ // src/cli/skillCategories.ts
50008
+ var CODING_CATEGORIES_LIST, CODING_CATEGORIES2;
50009
+ var init_skillCategories = __esm({
50010
+ "src/cli/skillCategories.ts"() {
50011
+ "use strict";
50012
+ CODING_CATEGORIES_LIST = [
50013
+ "plan",
50014
+ "refactor",
50015
+ "debug",
50016
+ "review",
50017
+ "test",
50018
+ "docs",
50019
+ "ops",
50020
+ "git",
50021
+ "db",
50022
+ "maint"
50023
+ ];
50024
+ CODING_CATEGORIES2 = new Set(CODING_CATEGORIES_LIST);
50025
+ }
50026
+ });
50027
+
50028
+ // src/cli/skillConfigIo.ts
50029
+ import {
50030
+ existsSync as existsSync28,
50031
+ mkdirSync as mkdirSync13,
50032
+ readdirSync as readdirSync6,
50033
+ readFileSync as readFileSync20,
50034
+ rmSync as rmSync3,
50035
+ writeFileSync as writeFileSync16
50036
+ } from "node:fs";
50037
+ import { dirname as dirname4, join as join20 } from "node:path";
50038
+ function ensureBuiltinSkillsLoadedSync() {
50039
+ if (builtinsLoaded) return;
50040
+ for (const spec of BUILTIN_SKILL_MODULES) {
50041
+ try {
50042
+ require?.(spec);
50043
+ } catch {
50044
+ }
50045
+ }
50046
+ }
50047
+ function getUserSkillsDir() {
50048
+ return join20(zelariHome(), "skills");
50049
+ }
50050
+ function getProjectSkillsDir(projectRoot) {
50051
+ return join20(projectRoot, ".zelari", "skills");
50052
+ }
50053
+ function skillFilePath(dir, name) {
50054
+ return join20(dir, name, "SKILL.md");
50055
+ }
50056
+ function classifyScope(skillPath, projectRoot) {
50057
+ const userDir = getUserSkillsDir().replace(/\\/g, "/");
50058
+ const norm = skillPath.replace(/\\/g, "/");
50059
+ if (norm.startsWith(userDir + "/") || norm === userDir) {
50060
+ return { scope: "user", writable: true };
50061
+ }
50062
+ if (projectRoot) {
50063
+ const projDir = getProjectSkillsDir(projectRoot).replace(/\\/g, "/");
50064
+ if (norm.startsWith(projDir + "/") || norm === projDir) {
50065
+ return { scope: "project", writable: true };
50066
+ }
50067
+ }
50068
+ return { scope: "compat", writable: false };
50069
+ }
50070
+ function entryFromParsed(parsed, projectRoot) {
50071
+ const { scope, writable } = classifyScope(parsed.sourcePath, projectRoot);
50072
+ return {
50073
+ id: parsed.name,
50074
+ name: parsed.name,
50075
+ description: parsed.description,
50076
+ category: parsed.category,
50077
+ estimatedCost: parsed.estimatedCost,
50078
+ requiredTools: parsed.requiredTools,
50079
+ scope,
50080
+ path: parsed.sourcePath,
50081
+ body: parsed.body,
50082
+ builtin: false,
50083
+ writable
50084
+ };
50085
+ }
50086
+ function entryFromBuiltin(skill) {
50087
+ return {
50088
+ id: skill.id,
50089
+ name: skill.name || skill.id,
50090
+ description: skill.description || "",
50091
+ category: skill.category,
50092
+ estimatedCost: skill.estimatedCost,
50093
+ requiredTools: skill.requiredTools,
50094
+ scope: "builtin",
50095
+ path: null,
50096
+ // Expose body so Desktop skill picker can expand like `/skill <id>`.
50097
+ body: skill.systemPromptFragment || void 0,
50098
+ builtin: true,
50099
+ writable: false
50100
+ };
50101
+ }
50102
+ function scanSkillsDir(dir, projectRoot, seen, out) {
50103
+ if (!existsSync28(dir)) return;
50104
+ let entries;
50105
+ try {
50106
+ entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
50107
+ } catch {
50108
+ return;
50109
+ }
50110
+ for (const entry of entries) {
50111
+ const skillPath = skillFilePath(dir, entry);
50112
+ if (!existsSync28(skillPath)) continue;
50113
+ try {
50114
+ const parsed = parseSkillMd(readFileSync20(skillPath, "utf8"), skillPath);
50115
+ if (!parsed) continue;
50116
+ if (seen.has(parsed.name)) continue;
50117
+ seen.add(parsed.name);
50118
+ out.push(entryFromParsed(parsed, projectRoot));
50119
+ } catch {
50120
+ }
50121
+ }
50122
+ }
50123
+ function listSkillsSnapshot(projectRoot) {
50124
+ const root = projectRoot && projectRoot.trim() ? projectRoot.trim() : null;
50125
+ const userSkillsDir = getUserSkillsDir();
50126
+ const projectSkillsDir = root ? getProjectSkillsDir(root) : null;
50127
+ const skills = [];
50128
+ const seen = /* @__PURE__ */ new Set();
50129
+ if (root) {
50130
+ scanSkillsDir(join20(root, ".zelari", "skills"), root, seen, skills);
50131
+ scanSkillsDir(join20(root, ".claude", "skills"), root, seen, skills);
50132
+ scanSkillsDir(join20(root, ".opencode", "skills"), root, seen, skills);
50133
+ }
50134
+ scanSkillsDir(userSkillsDir, root, seen, skills);
50135
+ for (const s of listCodingSkills()) {
50136
+ if (seen.has(s.id)) continue;
50137
+ seen.add(s.id);
50138
+ if (s.builtin) {
50139
+ skills.push(entryFromBuiltin(s));
50140
+ }
50141
+ }
50142
+ skills.sort((a, b) => {
50143
+ const order = (s) => s === "project" ? 0 : s === "user" ? 1 : s === "compat" ? 2 : 3;
50144
+ const d = order(a.scope) - order(b.scope);
50145
+ if (d !== 0) return d;
50146
+ return a.id.localeCompare(b.id);
50147
+ });
50148
+ return { userSkillsDir, projectSkillsDir, skills };
50149
+ }
50150
+ function serializeSkillMd(opts) {
50151
+ const name = opts.name.trim().toLowerCase();
50152
+ const description = opts.description.trim();
50153
+ const body = opts.body.trim();
50154
+ const lines = ["---", `name: ${name}`, `description: ${description}`];
50155
+ const cat = (opts.category ?? "").trim().toLowerCase();
50156
+ if (cat && CODING_CATEGORIES2.has(cat)) {
50157
+ lines.push(`category: ${cat}`);
50158
+ }
50159
+ if (opts.tools && opts.tools.length > 0) {
50160
+ lines.push(`tools: [${opts.tools.map((t) => t.trim()).filter(Boolean).join(", ")}]`);
50161
+ }
50162
+ const cost = (opts.cost ?? "").trim().toLowerCase();
50163
+ if (cost === "low" || cost === "medium" || cost === "high") {
50164
+ lines.push(`cost: ${cost}`);
50165
+ }
50166
+ lines.push("---", "", body, "");
50167
+ return lines.join("\n");
50168
+ }
50169
+ function upsertSkill(opts) {
50170
+ const name = opts.name.trim().toLowerCase();
50171
+ if (!name || !NAME_RE.test(name)) {
50172
+ return {
50173
+ ok: false,
50174
+ error: "Invalid skill name (use lowercase letters, digits, hyphens; max 64 chars)"
50175
+ };
50176
+ }
50177
+ const description = opts.description.trim();
50178
+ if (!description) {
50179
+ return { ok: false, error: "description is required" };
50180
+ }
50181
+ const body = opts.body.trim();
50182
+ if (!body) {
50183
+ return { ok: false, error: "body is required" };
50184
+ }
50185
+ let dir;
50186
+ if (opts.scope === "user") {
50187
+ dir = getUserSkillsDir();
50188
+ } else {
50189
+ const root = opts.projectRoot?.trim();
50190
+ if (!root) {
50191
+ return {
50192
+ ok: false,
50193
+ error: "projectRoot required for project scope (Open Folder first)"
50194
+ };
50195
+ }
50196
+ dir = getProjectSkillsDir(root);
50197
+ }
50198
+ const path104 = skillFilePath(dir, name);
50199
+ const content = serializeSkillMd({
50200
+ name,
50201
+ description,
50202
+ body,
50203
+ category: opts.category,
50204
+ tools: opts.tools,
50205
+ cost: opts.cost
50206
+ });
50207
+ const parsed = parseSkillMd(content, path104);
50208
+ if (!parsed) {
50209
+ return { ok: false, error: "Generated SKILL.md failed validation" };
50210
+ }
50211
+ mkdirSync13(dirname4(path104), { recursive: true });
50212
+ writeFileSync16(path104, content, "utf8");
50213
+ return { ok: true, path: path104 };
50214
+ }
50215
+ function removeSkill(opts) {
50216
+ const name = opts.name.trim().toLowerCase();
50217
+ if (!name) {
50218
+ return { ok: false, error: "name is required" };
50219
+ }
50220
+ let dir;
50221
+ if (opts.scope === "user") {
50222
+ dir = getUserSkillsDir();
50223
+ } else {
50224
+ const root = opts.projectRoot?.trim();
50225
+ if (!root) {
50226
+ return { ok: false, error: "projectRoot required for project scope" };
50227
+ }
50228
+ dir = getProjectSkillsDir(root);
50229
+ }
50230
+ const skillDir = join20(dir, name);
50231
+ const path104 = skillFilePath(dir, name);
50232
+ if (!existsSync28(path104) && !existsSync28(skillDir)) {
50233
+ return { ok: false, error: `Skill "${name}" not found in ${dir}` };
50234
+ }
50235
+ try {
50236
+ rmSync3(skillDir, { recursive: true, force: true });
50237
+ } catch (err) {
50238
+ return {
50239
+ ok: false,
50240
+ error: err instanceof Error ? err.message : String(err)
50241
+ };
50242
+ }
50243
+ return { ok: true, path: path104 };
50244
+ }
50245
+ var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
50246
+ var init_skillConfigIo = __esm({
50247
+ "src/cli/skillConfigIo.ts"() {
50248
+ "use strict";
50249
+ init_skills2();
50250
+ init_skillsMd();
50251
+ init_skillCategories();
50252
+ init_paths();
50253
+ NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
50254
+ BUILTIN_SKILL_MODULES = [
50255
+ "@zelari/core/skills/builtin/debugging",
50256
+ "@zelari/core/skills/builtin/docs",
50257
+ "@zelari/core/skills/builtin/git-ops",
50258
+ "@zelari/core/skills/builtin/planning",
50259
+ "@zelari/core/skills/builtin/refactoring",
50260
+ "@zelari/core/skills/builtin/review",
50261
+ "@zelari/core/skills/builtin/unrealEditor",
50262
+ "@zelari/core/skills/builtin/testing",
50263
+ "@zelari/core/skills/builtin/schema-loop",
50264
+ "@zelari/core/skills/builtin/computer-use-cua",
50265
+ "@zelari/core/skills/builtin/qwen-mm-plugins"
50266
+ ];
50267
+ builtinsLoaded = false;
50268
+ }
50269
+ });
50270
+
50271
+ // src/cli/tools/createSkillTool.ts
50272
+ import { existsSync as existsSync29 } from "node:fs";
50273
+ import { resolve as resolve3, sep } from "node:path";
50274
+ function createCreateSkillTool(opts) {
50275
+ const root = opts?.cwd ?? process.cwd();
50276
+ return {
50277
+ name: "create_skill",
50278
+ description: 'Create a NEW reusable skill (a SKILL.md procedure) that you can invoke in later turns with the `skill` tool or `/skill <name>`. Use it when the user asks to save a procedure as a skill ("salva questa procedura come skill", "crea una skill per X", "make this reusable"), or when you just worked out a repeatable workflow worth persisting. Write `instructions` as a COMPLETE, self-contained markdown procedure (goal, ordered steps, exact commands, verification, pitfalls): it becomes the skill body verbatim. scope=project (default) saves to <cwd>/.zelari/skills and is shared with the repo; scope=user saves to ~/.zelari-code/skills for every project. Creating an existing name fails unless overwrite=true. Does not edit built-in skills.',
50279
+ permissions: ["write"],
50280
+ timeoutMs: 1e4,
50281
+ inputSchema: CreateSkillArgsSchema,
50282
+ execute: async (args) => {
50283
+ const name = typeof args?.name === "string" ? args.name.trim() : "";
50284
+ if (!NAME_RE2.test(name)) {
50285
+ return typedErr(
50286
+ `Invalid skill name ${JSON.stringify(name)}. Use a slug of 2-40 chars: lowercase letters, digits and hyphens, starting with a letter or digit (e.g. "release-checklist").`
50287
+ );
50288
+ }
50289
+ const description = (args?.description ?? "").replace(/\s+/g, " ").trim();
50290
+ if (!description) {
50291
+ return typedErr("description is required: one line explaining when to use this skill.");
50292
+ }
50293
+ const instructions = (args?.instructions ?? "").trim();
50294
+ if (!instructions) {
50295
+ return typedErr(
50296
+ "instructions is required: the markdown body of the skill (goal, steps, commands, pitfalls)."
50297
+ );
50298
+ }
50299
+ const scope = args?.scope === "user" ? "user" : "project";
50300
+ const overwrite = args?.overwrite === true;
50301
+ const dir = scope === "user" ? getUserSkillsDir() : getProjectSkillsDir(root);
50302
+ const skillDir = resolve3(dir, name);
50303
+ const path104 = resolve3(skillDir, "SKILL.md");
50304
+ if (!path104.startsWith(resolve3(dir) + sep)) {
50305
+ return typedErr(
50306
+ `Refused: "${name}" would resolve outside the ${scope} skills directory (${resolve3(dir)}).`
50307
+ );
50308
+ }
50309
+ const existed = existsSync29(path104);
50310
+ if (existed && !overwrite) {
50311
+ return typedErr(
50312
+ `Skill "${name}" already exists at ${path104} \u2014 nothing was written. Call create_skill again with overwrite: true to replace it, or pick another name.`
50313
+ );
50314
+ }
50315
+ const written = upsertSkill({
50316
+ scope,
50317
+ name,
50318
+ description,
50319
+ body: instructions,
50320
+ projectRoot: root
50321
+ });
50322
+ if (!written.ok) {
50323
+ return typedErr(`Could not write SKILL.md for "${name}": ${written.error}`);
50324
+ }
50325
+ let loadable = false;
50326
+ try {
50327
+ loadSkillMdSkills(root);
50328
+ const registered = getCodingSkillById(name);
50329
+ loadable = (registered?.systemPromptFragment ?? "").trim() === instructions;
50330
+ } catch {
50331
+ }
50332
+ const verb = existed ? "Replaced existing skill" : "Created skill";
50333
+ const lines = [
50334
+ `${verb} "${name}" (${scope} scope).`,
50335
+ `Saved to: ${written.path}`
50336
+ ];
50337
+ if (!loadable) {
50338
+ lines.push(
50339
+ `Note: \`${name}\` currently resolves to a DIFFERENT skill already registered from another root (a built-in or an earlier search dir wins), so \`/skill ${name}\` may load that one instead. The file on disk is unchanged.`
50340
+ );
50341
+ }
50342
+ lines.push(
50343
+ `Use it now: \`/skill ${name}\` \u2014 or call the \`skill\` tool with name "${name}". Skills are discovered on demand, so no restart is needed.`
50344
+ );
50345
+ return typedOk({
50346
+ name,
50347
+ scope,
50348
+ path: written.path,
50349
+ overwritten: existed,
50350
+ loadable,
50351
+ message: lines.join("\n")
50352
+ });
50353
+ }
50354
+ };
50355
+ }
50356
+ var NAME_RE2, CreateSkillArgsSchema;
50357
+ var init_createSkillTool = __esm({
50358
+ "src/cli/tools/createSkillTool.ts"() {
50359
+ "use strict";
50360
+ init_zod();
50361
+ init_skills2();
50362
+ init_toolTypes();
50363
+ init_skillConfigIo();
50364
+ init_skillsMd();
50365
+ NAME_RE2 = /^[a-z0-9][a-z0-9-]{1,39}$/;
50366
+ CreateSkillArgsSchema = external_exports.object({
50367
+ name: external_exports.string().regex(NAME_RE2).describe(
50368
+ "Skill id/slug: 2-40 chars, lowercase letters/digits/hyphens, must start with a letter or digit."
50369
+ ),
50370
+ description: external_exports.string().min(1).describe(
50371
+ "One-line description shown in the skill catalog (when to use it). Collapsed to a single line."
50372
+ ),
50373
+ instructions: external_exports.string().min(1).describe(
50374
+ "The skill body, in markdown: goal, step-by-step procedure, exact commands, pitfalls. Becomes the loaded instructions verbatim."
50375
+ ),
50376
+ scope: external_exports.enum(["project", "user"]).optional().describe(
50377
+ "Where to save: 'project' (default) = <cwd>/.zelari/skills (repo-shared); 'user' = ~/.zelari-code/skills (every project)."
50378
+ ),
50379
+ overwrite: external_exports.boolean().optional().describe("Default false: fail if a SKILL.md for that name already exists. Pass true to replace it.")
50380
+ });
50381
+ }
50382
+ });
50383
+
49913
50384
  // src/cli/tools/todoTools.ts
49914
50385
  function createTodoWriteTool() {
49915
50386
  return {
@@ -50301,13 +50772,13 @@ async function scanTsbuildinfo(root) {
50301
50772
  return found;
50302
50773
  }
50303
50774
  async function gitStatusPorcelain(root) {
50304
- return new Promise((resolve9) => {
50775
+ return new Promise((resolve10) => {
50305
50776
  const child = spawn9("git", ["status", "--porcelain"], { cwd: root, shell: false });
50306
50777
  let out = "";
50307
50778
  child.stdout.on("data", (d) => out += d.toString());
50308
50779
  child.stderr.on("data", (d) => out += d.toString());
50309
- child.on("error", () => resolve9("<git-unavailable>"));
50310
- child.on("close", (code) => resolve9(code === 0 ? out : `<git-exit-${code ?? "none"}>`));
50780
+ child.on("error", () => resolve10("<git-unavailable>"));
50781
+ child.on("close", (code) => resolve10(code === 0 ? out : `<git-exit-${code ?? "none"}>`));
50311
50782
  });
50312
50783
  }
50313
50784
  async function fingerprintWorkspace(root) {
@@ -50354,14 +50825,14 @@ var init_inspectTypecheckSafety = __esm({
50354
50825
  // src/cli/tools/inspectCommand.ts
50355
50826
  import { spawn as spawn10 } from "node:child_process";
50356
50827
  import { createHash as createHash14 } from "node:crypto";
50357
- import { existsSync as existsSync28, promises as fs23 } from "node:fs";
50828
+ import { existsSync as existsSync30, promises as fs23 } from "node:fs";
50358
50829
  import os2 from "node:os";
50359
50830
  import path52 from "node:path";
50360
50831
  function resolveNodeModuleBin(start, rel2) {
50361
50832
  let dir = path52.resolve(start);
50362
50833
  for (; ; ) {
50363
50834
  const candidate = path52.join(dir, "node_modules", rel2);
50364
- if (existsSync28(candidate)) return candidate;
50835
+ if (existsSync30(candidate)) return candidate;
50365
50836
  const parent = path52.dirname(dir);
50366
50837
  if (parent === dir) return void 0;
50367
50838
  dir = parent;
@@ -50465,12 +50936,12 @@ function buildInspectCommand(op, ctx) {
50465
50936
  }
50466
50937
  }
50467
50938
  function runSpawn(command, argv, opts) {
50468
- return new Promise((resolve9) => {
50939
+ return new Promise((resolve10) => {
50469
50940
  let child;
50470
50941
  try {
50471
50942
  child = spawn10(command, argv, { cwd: opts.cwd, shell: false });
50472
50943
  } catch (err) {
50473
- resolve9({ code: null, stdout: "", stderr: String(err), timedOut: false, spawnError: String(err) });
50944
+ resolve10({ code: null, stdout: "", stderr: String(err), timedOut: false, spawnError: String(err) });
50474
50945
  return;
50475
50946
  }
50476
50947
  let stdout = "";
@@ -50492,14 +50963,14 @@ function runSpawn(command, argv, opts) {
50492
50963
  if (settled) return;
50493
50964
  settled = true;
50494
50965
  clearTimeout(timer);
50495
- resolve9({ code: null, stdout, stderr, timedOut, spawnError: err.message });
50966
+ resolve10({ code: null, stdout, stderr, timedOut, spawnError: err.message });
50496
50967
  });
50497
50968
  child.on("close", (code) => {
50498
50969
  if (settled) return;
50499
50970
  settled = true;
50500
50971
  clearTimeout(timer);
50501
50972
  opts.signal?.removeEventListener("abort", onAbort);
50502
- resolve9({ code, stdout, stderr, timedOut });
50973
+ resolve10({ code, stdout, stderr, timedOut });
50503
50974
  });
50504
50975
  });
50505
50976
  }
@@ -50784,7 +51255,7 @@ function createExecProcessTool(root, opts = {}) {
50784
51255
  const msg = err instanceof SandboxViolationError ? err.message : String(err);
50785
51256
  return typedErr(`[sandbox] ${msg}`);
50786
51257
  }
50787
- return new Promise((resolve9) => {
51258
+ return new Promise((resolve10) => {
50788
51259
  const start = Date.now();
50789
51260
  const spec = { ...baseSpec, network: opts.resolveNetwork?.(input) ?? baseSpec.network };
50790
51261
  const res = spawnJailed(spec, {
@@ -50796,11 +51267,11 @@ function createExecProcessTool(root, opts = {}) {
50796
51267
  envExtras: { CI: process.env.CI ?? "1" }
50797
51268
  });
50798
51269
  if (res.outcome === "denied") {
50799
- resolve9(typedErr(res.reason));
51270
+ resolve10(typedErr(res.reason));
50800
51271
  return;
50801
51272
  }
50802
51273
  if (res.outcome === "failed") {
50803
- resolve9(typedErr(`exec_process could not launch ${input.program}: ${res.reason}`));
51274
+ resolve10(typedErr(`exec_process could not launch ${input.program}: ${res.reason}`));
50804
51275
  return;
50805
51276
  }
50806
51277
  const meta3 = res.notice ? { status: "complete", warnings: [`os-jail: ${res.notice}`] } : void 0;
@@ -50816,7 +51287,7 @@ function createExecProcessTool(root, opts = {}) {
50816
51287
  const timeoutMs2 = typeof input.timeoutMs === "number" && input.timeoutMs > 0 ? input.timeoutMs : 3e4;
50817
51288
  const timer = setTimeout(() => {
50818
51289
  child.kill("SIGTERM");
50819
- resolve9(
51290
+ resolve10(
50820
51291
  typedErr(
50821
51292
  `exec_process timed out after ${timeoutMs2}ms running: ${[input.program, ...argv].join(" ")}`
50822
51293
  )
@@ -50824,11 +51295,11 @@ function createExecProcessTool(root, opts = {}) {
50824
51295
  }, timeoutMs2);
50825
51296
  child.on("error", (err) => {
50826
51297
  clearTimeout(timer);
50827
- resolve9(typedErr(`exec_process could not launch ${input.program}: ${err.message}`));
51298
+ resolve10(typedErr(`exec_process could not launch ${input.program}: ${err.message}`));
50828
51299
  });
50829
51300
  child.on("close", (code) => {
50830
51301
  clearTimeout(timer);
50831
- resolve9(
51302
+ resolve10(
50832
51303
  typedOk(
50833
51304
  {
50834
51305
  exitCode: code ?? -1,
@@ -51357,12 +51828,12 @@ var init_client = __esm({
51357
51828
  if (this.closed) return Promise.reject(new Error("LSP transport is closed"));
51358
51829
  const id3 = this.nextId++;
51359
51830
  const msg = { jsonrpc: "2.0", id: id3, method, ...params !== void 0 ? { params } : {} };
51360
- return new Promise((resolve9, reject) => {
51831
+ return new Promise((resolve10, reject) => {
51361
51832
  const timer = setTimeout(() => {
51362
51833
  this.pending.delete(id3);
51363
51834
  reject(new Error(`LSP request "${method}" timed out after ${this.timeoutMs}ms`));
51364
51835
  }, this.timeoutMs);
51365
- this.pending.set(id3, { resolve: resolve9, reject, timer });
51836
+ this.pending.set(id3, { resolve: resolve10, reject, timer });
51366
51837
  this.transport.send(encodeMessage(msg));
51367
51838
  });
51368
51839
  }
@@ -51481,7 +51952,7 @@ var init_servers = __esm({
51481
51952
 
51482
51953
  // src/cli/lsp/manager.ts
51483
51954
  import { spawn as spawn11 } from "node:child_process";
51484
- import { readFileSync as readFileSync20 } from "node:fs";
51955
+ import { readFileSync as readFileSync21 } from "node:fs";
51485
51956
  import path54 from "node:path";
51486
51957
  function processTransport(child) {
51487
51958
  return {
@@ -51639,8 +52110,8 @@ var init_manager = __esm({
51639
52110
  if (cached2 !== void 0) return cached2;
51640
52111
  let resolveInit;
51641
52112
  let rejectInit;
51642
- const initialized = new Promise((resolve9, reject) => {
51643
- resolveInit = resolve9;
52113
+ const initialized = new Promise((resolve10, reject) => {
52114
+ resolveInit = resolve10;
51644
52115
  rejectInit = reject;
51645
52116
  });
51646
52117
  let entry = null;
@@ -51695,7 +52166,7 @@ var init_manager = __esm({
51695
52166
  const uri = pathToUri(file2);
51696
52167
  let text;
51697
52168
  try {
51698
- text = readFileSync20(file2, "utf8");
52169
+ text = readFileSync21(file2, "utf8");
51699
52170
  } catch {
51700
52171
  text = "";
51701
52172
  }
@@ -51975,7 +52446,7 @@ var init_store2 = __esm({
51975
52446
  });
51976
52447
 
51977
52448
  // src/cli/semantic/index.ts
51978
- import { promises as fs24, existsSync as existsSync29, readFileSync as readFileSync21 } from "node:fs";
52449
+ import { promises as fs24, existsSync as existsSync31, readFileSync as readFileSync22 } from "node:fs";
51979
52450
  import path55 from "node:path";
51980
52451
  import { createHash as createHash15 } from "node:crypto";
51981
52452
  function getIndexPath(root) {
@@ -52058,9 +52529,9 @@ async function saveIndex(root, data) {
52058
52529
  }
52059
52530
  function loadIndex(root) {
52060
52531
  const file2 = getIndexPath(root);
52061
- if (!existsSync29(file2)) return null;
52532
+ if (!existsSync31(file2)) return null;
52062
52533
  try {
52063
- const parsed = JSON.parse(readFileSync21(file2, "utf8"));
52534
+ const parsed = JSON.parse(readFileSync22(file2, "utf8"));
52064
52535
  if (parsed && Array.isArray(parsed.chunks)) return parsed;
52065
52536
  } catch {
52066
52537
  }
@@ -52165,14 +52636,14 @@ function resolveStreamTimeouts(capabilities) {
52165
52636
  };
52166
52637
  }
52167
52638
  function abortableSleep(ms, signal) {
52168
- return new Promise((resolve9) => {
52169
- if (signal?.aborted) return resolve9();
52170
- const t = setTimeout(resolve9, ms);
52639
+ return new Promise((resolve10) => {
52640
+ if (signal?.aborted) return resolve10();
52641
+ const t = setTimeout(resolve10, ms);
52171
52642
  signal?.addEventListener(
52172
52643
  "abort",
52173
52644
  () => {
52174
52645
  clearTimeout(t);
52175
- resolve9();
52646
+ resolve10();
52176
52647
  },
52177
52648
  { once: true }
52178
52649
  );
@@ -53046,8 +53517,8 @@ async function runBrowserCheck(options, loader) {
53046
53517
  const failedRequests = [];
53047
53518
  const evaluateResults = [];
53048
53519
  const base2 = { ok: false, consoleErrors, pageErrors, failedRequests };
53049
- const resolve9 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
53050
- const pw = await resolve9();
53520
+ const resolve10 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
53521
+ const pw = await resolve10();
53051
53522
  if (!pw) {
53052
53523
  return {
53053
53524
  ...base2,
@@ -53422,18 +53893,18 @@ __export(targets_exports, {
53422
53893
  });
53423
53894
  import {
53424
53895
  chmodSync,
53425
- existsSync as existsSync30,
53426
- mkdirSync as mkdirSync13,
53427
- readFileSync as readFileSync22,
53428
- writeFileSync as writeFileSync16
53896
+ existsSync as existsSync32,
53897
+ mkdirSync as mkdirSync14,
53898
+ readFileSync as readFileSync23,
53899
+ writeFileSync as writeFileSync17
53429
53900
  } from "node:fs";
53430
- import { dirname as dirname4, join as join20 } from "node:path";
53901
+ import { dirname as dirname5, join as join21 } from "node:path";
53431
53902
  import { spawn as spawn12 } from "node:child_process";
53432
53903
  function getSshTargetsPath() {
53433
- return join20(zelariHome(), "ssh-targets.json");
53904
+ return join21(zelariHome(), "ssh-targets.json");
53434
53905
  }
53435
53906
  function getSshSecretsPath() {
53436
- return join20(zelariHome(), "ssh-secrets.json");
53907
+ return join21(zelariHome(), "ssh-secrets.json");
53437
53908
  }
53438
53909
  function normalizeAuth(auth) {
53439
53910
  if (auth === "keyPath") return "keyPath";
@@ -53441,21 +53912,21 @@ function normalizeAuth(auth) {
53441
53912
  return "agent";
53442
53913
  }
53443
53914
  function readSecrets() {
53444
- const path103 = getSshSecretsPath();
53445
- if (!existsSync30(path103)) return {};
53915
+ const path104 = getSshSecretsPath();
53916
+ if (!existsSync32(path104)) return {};
53446
53917
  try {
53447
- return JSON.parse(readFileSync22(path103, "utf8"));
53918
+ return JSON.parse(readFileSync23(path104, "utf8"));
53448
53919
  } catch {
53449
53920
  return {};
53450
53921
  }
53451
53922
  }
53452
53923
  function writeSecrets(data) {
53453
- const path103 = getSshSecretsPath();
53454
- mkdirSync13(dirname4(path103), { recursive: true });
53455
- writeFileSync16(path103, `${JSON.stringify(data, null, 2)}
53924
+ const path104 = getSshSecretsPath();
53925
+ mkdirSync14(dirname5(path104), { recursive: true });
53926
+ writeFileSync17(path104, `${JSON.stringify(data, null, 2)}
53456
53927
  `, "utf8");
53457
53928
  try {
53458
- chmodSync(path103, 384);
53929
+ chmodSync(path104, 384);
53459
53930
  } catch {
53460
53931
  }
53461
53932
  }
@@ -53484,10 +53955,10 @@ function deleteSshPassword(id3) {
53484
53955
  writeSecrets({ passwords });
53485
53956
  }
53486
53957
  function readStore2() {
53487
- const path103 = getSshTargetsPath();
53488
- if (!existsSync30(path103)) return [];
53958
+ const path104 = getSshTargetsPath();
53959
+ if (!existsSync32(path104)) return [];
53489
53960
  try {
53490
- const parsed = JSON.parse(readFileSync22(path103, "utf8"));
53961
+ const parsed = JSON.parse(readFileSync23(path104, "utf8"));
53491
53962
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
53492
53963
  return list.filter(
53493
53964
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -53502,11 +53973,11 @@ function readStore2() {
53502
53973
  }
53503
53974
  }
53504
53975
  function writeStore2(targets) {
53505
- const path103 = getSshTargetsPath();
53506
- mkdirSync13(dirname4(path103), { recursive: true });
53976
+ const path104 = getSshTargetsPath();
53977
+ mkdirSync14(dirname5(path104), { recursive: true });
53507
53978
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
53508
- writeFileSync16(
53509
- path103,
53979
+ writeFileSync17(
53980
+ path104,
53510
53981
  `${JSON.stringify({ targets: clean }, null, 2)}
53511
53982
  `,
53512
53983
  "utf8"
@@ -53606,17 +54077,17 @@ function buildSshBaseArgs(target) {
53606
54077
  return args;
53607
54078
  }
53608
54079
  function ensureAskpassHelper() {
53609
- const dir = join20(zelariHome(), "ssh-helpers");
53610
- mkdirSync13(dir, { recursive: true });
53611
- const cjs = join20(dir, "askpass.cjs");
53612
- writeFileSync16(
54080
+ const dir = join21(zelariHome(), "ssh-helpers");
54081
+ mkdirSync14(dir, { recursive: true });
54082
+ const cjs = join21(dir, "askpass.cjs");
54083
+ writeFileSync17(
53613
54084
  cjs,
53614
54085
  "process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
53615
54086
  "utf8"
53616
54087
  );
53617
54088
  if (process.platform === "win32") {
53618
- const cmd = join20(dir, "askpass.cmd");
53619
- writeFileSync16(
54089
+ const cmd = join21(dir, "askpass.cmd");
54090
+ writeFileSync17(
53620
54091
  cmd,
53621
54092
  `@echo off\r
53622
54093
  node "%~dp0askpass.cjs"\r
@@ -53625,8 +54096,8 @@ node "%~dp0askpass.cjs"\r
53625
54096
  );
53626
54097
  return cmd;
53627
54098
  }
53628
- const sh = join20(dir, "askpass.sh");
53629
- writeFileSync16(
54099
+ const sh = join21(dir, "askpass.sh");
54100
+ writeFileSync17(
53630
54101
  sh,
53631
54102
  `#!/bin/sh
53632
54103
  exec node "$(dirname "$0")/askpass.cjs"
@@ -53640,9 +54111,9 @@ exec node "$(dirname "$0")/askpass.cjs"
53640
54111
  return sh;
53641
54112
  }
53642
54113
  function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53643
- return new Promise((resolve9) => {
54114
+ return new Promise((resolve10) => {
53644
54115
  if (target.auth === "password" && !getSshPassword(target.id)) {
53645
- resolve9({
54116
+ resolve10({
53646
54117
  code: 1,
53647
54118
  stdout: "",
53648
54119
  stderr: "No password stored for this target \u2014 edit target and set password"
@@ -53674,7 +54145,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53674
54145
  });
53675
54146
  const timer = setTimeout(() => {
53676
54147
  child.kill("SIGTERM");
53677
- resolve9({
54148
+ resolve10({
53678
54149
  code: 124,
53679
54150
  stdout,
53680
54151
  stderr: stderr + "\n[ssh] timeout"
@@ -53682,7 +54153,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53682
54153
  }, timeoutMs2);
53683
54154
  child.on("error", (err) => {
53684
54155
  clearTimeout(timer);
53685
- resolve9({
54156
+ resolve10({
53686
54157
  code: 127,
53687
54158
  stdout,
53688
54159
  stderr: err.message.includes("ENOENT") ? "ssh not found on PATH \u2014 install OpenSSH client" : err.message
@@ -53690,7 +54161,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53690
54161
  });
53691
54162
  child.on("close", (code) => {
53692
54163
  clearTimeout(timer);
53693
- resolve9({ code: code ?? 1, stdout, stderr });
54164
+ resolve10({ code: code ?? 1, stdout, stderr });
53694
54165
  });
53695
54166
  });
53696
54167
  }
@@ -53699,9 +54170,9 @@ function readSshPublicKey(keyOrPubPath) {
53699
54170
  if (!raw) return { ok: false, error: "Empty path" };
53700
54171
  const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
53701
54172
  for (const p3 of candidates) {
53702
- if (!existsSync30(p3)) continue;
54173
+ if (!existsSync32(p3)) continue;
53703
54174
  try {
53704
- const content = readFileSync22(p3, "utf8").trim();
54175
+ const content = readFileSync23(p3, "utf8").trim();
53705
54176
  if (!content) continue;
53706
54177
  if (/BEGIN .*PRIVATE KEY/i.test(content)) {
53707
54178
  return {
@@ -53752,11 +54223,11 @@ function formatSshTargetsForPrompt() {
53752
54223
  ];
53753
54224
  for (const t of targets) {
53754
54225
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
53755
- const path103 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
54226
+ const path104 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
53756
54227
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
53757
54228
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
53758
54229
  lines.push(
53759
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path103}${tags}${allow}`
54230
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path104}${tags}${allow}`
53760
54231
  );
53761
54232
  }
53762
54233
  return lines.join("\n");
@@ -54467,14 +54938,14 @@ function backoffDelay2(attempt, retryAfterHeader) {
54467
54938
  return Math.min(BACKOFF_BASE_MS2 * 2 ** attempt, BACKOFF_CAP_MS2);
54468
54939
  }
54469
54940
  function abortableSleep2(ms, signal) {
54470
- return new Promise((resolve9) => {
54471
- if (signal?.aborted) return resolve9();
54472
- const t = setTimeout(resolve9, ms);
54941
+ return new Promise((resolve10) => {
54942
+ if (signal?.aborted) return resolve10();
54943
+ const t = setTimeout(resolve10, ms);
54473
54944
  signal?.addEventListener(
54474
54945
  "abort",
54475
54946
  () => {
54476
54947
  clearTimeout(t);
54477
- resolve9();
54948
+ resolve10();
54478
54949
  },
54479
54950
  { once: true }
54480
54951
  );
@@ -54930,14 +55401,18 @@ function activePermissionPreset() {
54930
55401
  return parsePermissionPreset(process.env.ZELARI_PERMISSION_PRESET) ?? "standard";
54931
55402
  }
54932
55403
  function defaultPermissionPolicy(overrides) {
54933
- const preset = PERMISSION_PRESETS[activePermissionPreset()];
55404
+ const presetName = activePermissionPreset();
55405
+ const preset = PERMISSION_PRESETS[presetName];
54934
55406
  return {
54935
55407
  read: parseAction(process.env.ZELARI_PERMISSION_READ, preset.read),
54936
55408
  write: parseAction(process.env.ZELARI_PERMISSION_WRITE, preset.write),
54937
55409
  execute: parseAction(process.env.ZELARI_PERMISSION_EXECUTE, preset.execute),
54938
55410
  network: parseAction(process.env.ZELARI_PERMISSION_NETWORK, preset.network),
54939
55411
  ui: "allow",
54940
- auto: isAutoPermissions(),
55412
+ // yolo means "go alone": residual asks auto-approve without a UI handler
55413
+ // (no more "denied timed out" piles on unattended builds). Explicit
55414
+ // ZELARI_AUTO=1 keeps working for every preset; deny is never promoted.
55415
+ auto: isAutoPermissions() || presetName === "yolo",
54941
55416
  ...overrides
54942
55417
  };
54943
55418
  }
@@ -55141,7 +55616,7 @@ __export(folderTrust_exports, {
55141
55616
  trustFolder: () => trustFolder,
55142
55617
  untrustFolder: () => untrustFolder
55143
55618
  });
55144
- import { existsSync as existsSync31, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "node:fs";
55619
+ import { existsSync as existsSync33, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "node:fs";
55145
55620
  import path60 from "node:path";
55146
55621
  function trustStorePath() {
55147
55622
  return _overrideStorePath ?? trustConfigPath();
@@ -55152,7 +55627,7 @@ function normalize6(p3) {
55152
55627
  }
55153
55628
  function readStore3() {
55154
55629
  try {
55155
- const raw = readFileSync23(trustStorePath(), "utf8");
55630
+ const raw = readFileSync24(trustStorePath(), "utf8");
55156
55631
  const parsed = JSON.parse(raw);
55157
55632
  if (parsed && Array.isArray(parsed.folders)) return parsed;
55158
55633
  return DEFAULT_STORE;
@@ -55163,8 +55638,8 @@ function readStore3() {
55163
55638
  function writeStore3(store6) {
55164
55639
  const p3 = trustStorePath();
55165
55640
  try {
55166
- mkdirSync14(path60.dirname(p3), { recursive: true });
55167
- writeFileSync17(p3, JSON.stringify(store6, null, 2), "utf8");
55641
+ mkdirSync15(path60.dirname(p3), { recursive: true });
55642
+ writeFileSync18(p3, JSON.stringify(store6, null, 2), "utf8");
55168
55643
  } catch (err) {
55169
55644
  throw new Error(
55170
55645
  `failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
@@ -55219,7 +55694,7 @@ function getTrustStorePath() {
55219
55694
  return trustStorePath();
55220
55695
  }
55221
55696
  function hasTrustStore() {
55222
- return existsSync31(trustStorePath());
55697
+ return existsSync33(trustStorePath());
55223
55698
  }
55224
55699
  function _setTrustStorePathForTests(p3) {
55225
55700
  _overrideStorePath = p3;
@@ -55235,13 +55710,13 @@ var init_folderTrust = __esm({
55235
55710
  });
55236
55711
 
55237
55712
  // src/cli/safety/lifecycleHooks.ts
55238
- import { join as join21 } from "node:path";
55239
- import { readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
55713
+ import { join as join22 } from "node:path";
55714
+ import { readdirSync as readdirSync7, statSync as statSync3 } from "node:fs";
55240
55715
  function globalHooksDir() {
55241
- return join21(zelariHome(), "hooks");
55716
+ return join22(zelariHome(), "hooks");
55242
55717
  }
55243
55718
  function projectHooksDir(projectRoot) {
55244
- return join21(projectRoot, ".zelari", "hooks");
55719
+ return join22(projectRoot, ".zelari", "hooks");
55245
55720
  }
55246
55721
  function resolveHookFailureMode(override, env = process.env) {
55247
55722
  const v = override?.trim().toLowerCase();
@@ -55254,7 +55729,7 @@ function fingerprintHookDirs(dirs) {
55254
55729
  for (const dir of dirs) {
55255
55730
  let names;
55256
55731
  try {
55257
- names = readdirSync6(dir).filter((f) => f.endsWith(".json")).sort();
55732
+ names = readdirSync7(dir).filter((f) => f.endsWith(".json")).sort();
55258
55733
  } catch {
55259
55734
  parts.push(`${dir}:missing`);
55260
55735
  continue;
@@ -55268,7 +55743,7 @@ function fingerprintHookDirs(dirs) {
55268
55743
  continue;
55269
55744
  }
55270
55745
  for (const name of names) {
55271
- const full = join21(dir, name);
55746
+ const full = join22(dir, name);
55272
55747
  try {
55273
55748
  const st = statSync3(full);
55274
55749
  parts.push(`${full}:${st.mtimeMs}:${st.size}`);
@@ -55553,8 +56028,8 @@ function networkClaimsForUrls(urls) {
55553
56028
  }
55554
56029
  function mcpClaimFromName(registryName) {
55555
56030
  const rest = registryName.startsWith("mcp_") ? registryName.slice("mcp_".length) : "";
55556
- const sep4 = rest.indexOf("_");
55557
- return sep4 <= 0 || sep4 === rest.length - 1 ? null : { kind: "mcp", server: rest.slice(0, sep4), tool: rest.slice(sep4 + 1) };
56031
+ const sep5 = rest.indexOf("_");
56032
+ return sep5 <= 0 || sep5 === rest.length - 1 ? null : { kind: "mcp", server: rest.slice(0, sep5), tool: rest.slice(sep5 + 1) };
55558
56033
  }
55559
56034
  function browserActionUrls(a) {
55560
56035
  const actions = Array.isArray(a["actions"]) ? a["actions"] : [];
@@ -55968,9 +56443,11 @@ __export(toolRegistry_exports, {
55968
56443
  createKrakenSubAgentContextFactory: () => createKrakenSubAgentContextFactory,
55969
56444
  getCliToolCatalogEntries: () => getCliToolCatalogEntries,
55970
56445
  registerCliToolsIntoCouncilCatalog: () => registerCliToolsIntoCouncilCatalog,
56446
+ resolveTentacleThinkingInput: () => resolveTentacleThinkingInput,
56447
+ tentacleThinkingEnvKey: () => tentacleThinkingEnvKey,
55971
56448
  wrapWithSandbox: () => wrapWithSandbox
55972
56449
  });
55973
- import { existsSync as existsSync32 } from "node:fs";
56450
+ import { existsSync as existsSync34 } from "node:fs";
55974
56451
  import path63 from "node:path";
55975
56452
  function createBuiltinToolRegistry(options = {}) {
55976
56453
  const root = options.root ?? process.cwd();
@@ -56059,9 +56536,11 @@ function createBuiltinToolRegistry(options = {}) {
56059
56536
  const gauntletParent = options.gauntletParent === true;
56060
56537
  const allowMutators = !readOnly && !verifyMode && !gauntletParent;
56061
56538
  const allowBash = (allowMutators || verifyMode) && !gauntletParent;
56062
- if (isParent && allowMutators && sessionId2) {
56063
- registry4.setToolResultListener(createTaskTouchGuard({ projectRoot: root, sessionId: sessionId2 }));
56064
- void runTaskStalenessCheck({ projectRoot: root, sessionId: sessionId2 });
56539
+ if (isParent && allowMutators && options.sessionId) {
56540
+ registry4.setToolResultListener(
56541
+ createTaskTouchGuard({ projectRoot: root, sessionId: options.sessionId })
56542
+ );
56543
+ void runTaskStalenessCheck({ projectRoot: root, sessionId: options.sessionId });
56065
56544
  }
56066
56545
  const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
56067
56546
  const agentPolicySet = (() => {
@@ -56127,6 +56606,10 @@ function createBuiltinToolRegistry(options = {}) {
56127
56606
  if (skillTool) {
56128
56607
  registry4.register(skillTool);
56129
56608
  }
56609
+ const skillCreateTool = enableSkill ? withPerm(createCreateSkillTool({ cwd: root })) : null;
56610
+ if (skillCreateTool) {
56611
+ registry4.register(skillCreateTool);
56612
+ }
56130
56613
  const enableTodos = options.enableTodos !== false && options.readOnly !== true && !gauntletParent && profile === "full";
56131
56614
  const todoWrite = enableTodos ? withPerm(createTodoWriteTool()) : null;
56132
56615
  const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
@@ -56154,6 +56637,7 @@ function createBuiltinToolRegistry(options = {}) {
56154
56637
  ...allowBash ? [safeBash, safeExecProcess] : [],
56155
56638
  ...askUserTool ? [askUserTool] : [],
56156
56639
  ...skillTool ? [skillTool] : [],
56640
+ ...skillCreateTool ? [skillCreateTool] : [],
56157
56641
  ...todoWrite ? [todoWrite] : [],
56158
56642
  ...todoRead ? [todoRead] : [],
56159
56643
  ...planTaskToolsWrapped.length > 0 ? planTaskToolsWrapped : []
@@ -56337,9 +56821,29 @@ function taskAgentToProfile(agent) {
56337
56821
  if (agent === "verify") return "verify";
56338
56822
  return "explore";
56339
56823
  }
56824
+ function tentacleThinkingEnvKey(agent) {
56825
+ if (agent === "explore") return "ZELARI_KRAKEN_EXPLORE_THINKING";
56826
+ if (agent === "verify") return "ZELARI_KRAKEN_VERIFY_THINKING";
56827
+ return "ZELARI_KRAKEN_GENERAL_THINKING";
56828
+ }
56829
+ function resolveTentacleThinkingInput(agent, taskArg, env = process.env) {
56830
+ const fromArg = typeof taskArg === "string" ? taskArg.trim() : "";
56831
+ const argWins = fromArg.length > 0 && fromArg.toLowerCase() !== "inherit";
56832
+ const key = tentacleThinkingEnvKey(agent);
56833
+ const raw = (argWins ? fromArg : (env[key] ?? "").trim()).trim();
56834
+ if (!raw) return void 0;
56835
+ if (isValidThinkingInput(raw)) return raw.toLowerCase();
56836
+ if (!warnedThinkingInputs.has(raw)) {
56837
+ warnedThinkingInputs.add(raw);
56838
+ console.warn(
56839
+ `[kraken] ignoring invalid thinking effort "${raw}" for the ${agent} tentacle (${key}); using the provider default.`
56840
+ );
56841
+ }
56842
+ return void 0;
56843
+ }
56340
56844
  function createKrakenSubAgentContextFactory(opts) {
56341
56845
  const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride, parentPolicy, onPermissionAsk } = opts;
56342
- return async ({ agent, cwd: subCwd }) => {
56846
+ return async ({ agent, cwd: subCwd, thinkingEffort }) => {
56343
56847
  const cfg = providerOverride ? await providerConfigFor(providerOverride) : await providerFromEnv();
56344
56848
  if (!cfg) return null;
56345
56849
  const { resolveKrakenSubModelAsync: resolveKrakenSubModelAsync2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
@@ -56358,6 +56862,8 @@ function createKrakenSubAgentContextFactory(opts) {
56358
56862
  }
56359
56863
  }
56360
56864
  const subCfg = { ...effCfg, model };
56865
+ const thinkingInput = resolveTentacleThinkingInput(agent, thinkingEffort, process.env);
56866
+ if (thinkingInput) subCfg.thinking = parseThinkingSpec(thinkingInput);
56361
56867
  const subProfile = taskAgentToProfile(agent);
56362
56868
  const subRoot = subCwd || root;
56363
56869
  const agentPolicyForSubProfile = defaultPermissionPolicy({ auto: true });
@@ -56394,6 +56900,9 @@ function createKrakenSubAgentContextFactory(opts) {
56394
56900
  providerStream,
56395
56901
  model,
56396
56902
  provider: subCfg.providerId,
56903
+ // E (thinking): report the spec actually applied so the parent's
56904
+ // `agent_spawned` event can surface it (arg > kind env > inherited).
56905
+ ...subCfg.thinking !== void 0 ? { thinking: stringifyThinkingSpec(subCfg.thinking) } : {},
56397
56906
  ...model !== parentModel ? {
56398
56907
  fallback: {
56399
56908
  model: parentModel,
@@ -56617,7 +57126,7 @@ function claimedSourcePath(token, args, root) {
56617
57126
  const candidate = path63.isAbsolute(cleaned) ? path63.normalize(cleaned) : path63.resolve(base2, cleaned);
56618
57127
  try {
56619
57128
  const contained = resolveSandboxedPath(candidate, { root });
56620
- if (existsSync32(contained)) return contained;
57129
+ if (existsSync34(contained)) return contained;
56621
57130
  } catch {
56622
57131
  }
56623
57132
  }
@@ -56851,7 +57360,7 @@ function redactForAudit(args) {
56851
57360
  }
56852
57361
  return out;
56853
57362
  }
56854
- var HARNESS_BUILTIN_NAMES, DIAG_SOURCE_EXTENSIONS, EXEC_DIAGNOSTICS_MAX_FILES;
57363
+ var HARNESS_BUILTIN_NAMES, warnedThinkingInputs, DIAG_SOURCE_EXTENSIONS, EXEC_DIAGNOSTICS_MAX_FILES;
56855
57364
  var init_toolRegistry = __esm({
56856
57365
  "src/cli/toolRegistry.ts"() {
56857
57366
  "use strict";
@@ -56873,6 +57382,7 @@ var init_toolRegistry = __esm({
56873
57382
  init_krakenSelectTool();
56874
57383
  init_askUser();
56875
57384
  init_skillTool();
57385
+ init_createSkillTool();
56876
57386
  init_todoTools();
56877
57387
  init_planTaskTools();
56878
57388
  init_inspectCommand();
@@ -56890,6 +57400,7 @@ var init_toolRegistry = __esm({
56890
57400
  init_openai_compatible();
56891
57401
  init_resolveStream();
56892
57402
  init_providerConfig();
57403
+ init_thinking();
56893
57404
  init_crossProviderFailover();
56894
57405
  init_keyStore();
56895
57406
  init_toolPermissions();
@@ -56924,6 +57435,7 @@ var init_toolRegistry = __esm({
56924
57435
  "fetch_url",
56925
57436
  "web_search"
56926
57437
  ]);
57438
+ warnedThinkingInputs = /* @__PURE__ */ new Set();
56927
57439
  DIAG_SOURCE_EXTENSIONS = new Set(
56928
57440
  DEFAULT_PROVIDERS.flatMap((p3) => p3.extensions)
56929
57441
  );
@@ -56932,7 +57444,7 @@ var init_toolRegistry = __esm({
56932
57444
  });
56933
57445
 
56934
57446
  // src/cli/metrics.ts
56935
- import { promises as fs27, existsSync as existsSync33, statSync as statSync4, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
57447
+ import { promises as fs27, existsSync as existsSync35, statSync as statSync4, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync16 } from "node:fs";
56936
57448
  import path64 from "node:path";
56937
57449
  async function readMetrics(file2) {
56938
57450
  let raw = "";
@@ -56983,7 +57495,7 @@ var init_metrics3 = __esm({
56983
57495
  writeQueue = Promise.resolve();
56984
57496
  constructor(file2) {
56985
57497
  this.file = file2 ?? metricsPath();
56986
- mkdirSync15(path64.dirname(this.file), { recursive: true });
57498
+ mkdirSync16(path64.dirname(this.file), { recursive: true });
56987
57499
  }
56988
57500
  /** Metrics file path — doctor/summary readers use this. */
56989
57501
  get filePath() {
@@ -56998,12 +57510,12 @@ var init_metrics3 = __esm({
56998
57510
  }
56999
57511
  /** Append a single record synchronously (file I/O). */
57000
57512
  append(rec) {
57001
- return new Promise((resolve9, reject) => {
57513
+ return new Promise((resolve10, reject) => {
57002
57514
  try {
57003
57515
  this.maybeRotate();
57004
57516
  const line = JSON.stringify(rec) + "\n";
57005
57517
  appendFileSync3(this.file, line, { encoding: "utf-8" });
57006
- resolve9();
57518
+ resolve10();
57007
57519
  } catch (err) {
57008
57520
  reject(err);
57009
57521
  }
@@ -57018,7 +57530,7 @@ var init_metrics3 = __esm({
57018
57530
  }
57019
57531
  /** If the file is over the rotation threshold, rotate it. */
57020
57532
  maybeRotate() {
57021
- if (!existsSync33(this.file)) return;
57533
+ if (!existsSync35(this.file)) return;
57022
57534
  try {
57023
57535
  const stat8 = statSync4(this.file);
57024
57536
  if (stat8.size >= METRICS_ROTATE_BYTES) {
@@ -58300,7 +58812,7 @@ var init_sqliteCodec = __esm({
58300
58812
  });
58301
58813
 
58302
58814
  // src/cli/memory/sqliteRpc.ts
58303
- import { existsSync as existsSync34 } from "node:fs";
58815
+ import { existsSync as existsSync36 } from "node:fs";
58304
58816
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
58305
58817
  import * as path69 from "node:path";
58306
58818
  import { Worker } from "node:worker_threads";
@@ -58313,7 +58825,7 @@ function isBusy(error51) {
58313
58825
  function resolveWorkerUrl() {
58314
58826
  const here = path69.dirname(fileURLToPath2(import.meta.url));
58315
58827
  const direct = path69.join(here, "sqliteWorker.mjs");
58316
- if (existsSync34(direct)) return pathToFileURL2(direct);
58828
+ if (existsSync36(direct)) return pathToFileURL2(direct);
58317
58829
  return pathToFileURL2(path69.join(here, "memory", "sqliteWorker.mjs"));
58318
58830
  }
58319
58831
  var SqliteWorkerRpc;
@@ -58388,8 +58900,8 @@ var init_sqliteRpc = __esm({
58388
58900
  const worker = this.worker;
58389
58901
  if (!worker) return Promise.reject(new Error("SQLite worker is not open."));
58390
58902
  const id3 = this.nextId++;
58391
- return new Promise((resolve9, reject) => {
58392
- this.pending.set(id3, { resolve: resolve9, reject });
58903
+ return new Promise((resolve10, reject) => {
58904
+ this.pending.set(id3, { resolve: resolve10, reject });
58393
58905
  worker.postMessage({ id: id3, operation, args });
58394
58906
  });
58395
58907
  }
@@ -58400,7 +58912,7 @@ var init_sqliteRpc = __esm({
58400
58912
  return await this.request(operation, args);
58401
58913
  } catch (error51) {
58402
58914
  if (!isBusy(error51) || attempt >= 3) throw error51;
58403
- await new Promise((resolve9) => setTimeout(resolve9, 25 * 2 ** attempt));
58915
+ await new Promise((resolve10) => setTimeout(resolve10, 25 * 2 ** attempt));
58404
58916
  attempt += 1;
58405
58917
  }
58406
58918
  }
@@ -60024,8 +60536,8 @@ __export(conversationContext_exports, {
60024
60536
  setHistory: () => setHistory,
60025
60537
  setLastClarification: () => setLastClarification
60026
60538
  });
60027
- import { existsSync as existsSync35 } from "node:fs";
60028
- import { join as join27 } from "node:path";
60539
+ import { existsSync as existsSync37 } from "node:fs";
60540
+ import { join as join28 } from "node:path";
60029
60541
  function getHistory() {
60030
60542
  return history;
60031
60543
  }
@@ -60034,7 +60546,7 @@ function setHistory(messages) {
60034
60546
  history = projected === messages ? [...messages] : projected;
60035
60547
  }
60036
60548
  function compactInPlace(cwd = process.cwd()) {
60037
- const durableStatePresent = existsSync35(join27(cwd, ".zelari", "state", "HEAD.json"));
60549
+ const durableStatePresent = existsSync37(join28(cwd, ".zelari", "state", "HEAD.json"));
60038
60550
  history = applySessionSurface(compactHistory(history, { durableStatePresent }));
60039
60551
  }
60040
60552
  function appendMessages(msgs) {
@@ -60416,7 +60928,7 @@ var init_mode = __esm({
60416
60928
  });
60417
60929
 
60418
60930
  // src/cli/headless.ts
60419
- import { readFileSync as readFileSync24 } from "node:fs";
60931
+ import { readFileSync as readFileSync25 } from "node:fs";
60420
60932
  import path73 from "node:path";
60421
60933
  function resolveHeadlessCwd(opts) {
60422
60934
  const raw = typeof opts.cwd === "string" ? opts.cwd.trim() : "";
@@ -60479,7 +60991,7 @@ function parseHeadlessFlags(argv) {
60479
60991
  const next = argv[i + 1];
60480
60992
  if (next) {
60481
60993
  try {
60482
- const fromFile = readFileSync24(next, "utf-8");
60994
+ const fromFile = readFileSync25(next, "utf-8");
60483
60995
  if (fromFile.trim()) task = fromFile;
60484
60996
  } catch {
60485
60997
  }
@@ -60528,7 +61040,7 @@ function parseHeadlessFlags(argv) {
60528
61040
  let raw = null;
60529
61041
  if (arg === "--history-file") {
60530
61042
  try {
60531
- raw = readFileSync24(next, "utf-8");
61043
+ raw = readFileSync25(next, "utf-8");
60532
61044
  } catch {
60533
61045
  raw = null;
60534
61046
  }
@@ -60630,7 +61142,7 @@ function parseHeadlessFlags(argv) {
60630
61142
  const next = argv[i + 1];
60631
61143
  if (next) {
60632
61144
  try {
60633
- const fromFile = readFileSync24(next, "utf-8");
61145
+ const fromFile = readFileSync25(next, "utf-8");
60634
61146
  if (fromFile.trim()) krakenGraph = fromFile;
60635
61147
  } catch {
60636
61148
  }
@@ -61319,13 +61831,13 @@ __export(claudeProvider_exports, {
61319
61831
  });
61320
61832
  import { spawn as spawn13 } from "node:child_process";
61321
61833
  function waitForExit(child, timeoutMs2 = 2e3) {
61322
- return new Promise((resolve9) => {
61323
- if (child.exitCode != null) return resolve9(child.exitCode);
61324
- const timer = setTimeout(() => resolve9(child.exitCode ?? null), timeoutMs2);
61834
+ return new Promise((resolve10) => {
61835
+ if (child.exitCode != null) return resolve10(child.exitCode);
61836
+ const timer = setTimeout(() => resolve10(child.exitCode ?? null), timeoutMs2);
61325
61837
  timer.unref?.();
61326
61838
  child.once("exit", (code) => {
61327
61839
  clearTimeout(timer);
61328
- resolve9(code ?? null);
61840
+ resolve10(code ?? null);
61329
61841
  });
61330
61842
  });
61331
61843
  }
@@ -61528,13 +62040,13 @@ var planDetect_exports = {};
61528
62040
  __export(planDetect_exports, {
61529
62041
  hasWorkspacePlan: () => hasWorkspacePlan
61530
62042
  });
61531
- import { existsSync as existsSync36, readFileSync as readFileSync25 } from "node:fs";
61532
- import { join as join28 } from "node:path";
62043
+ import { existsSync as existsSync38, readFileSync as readFileSync26 } from "node:fs";
62044
+ import { join as join29 } from "node:path";
61533
62045
  function hasWorkspacePlan(projectRoot = process.cwd()) {
61534
- const planPath = join28(resolveWorkspaceRoot(projectRoot), "plan.json");
61535
- if (!existsSync36(planPath)) return false;
62046
+ const planPath = join29(resolveWorkspaceRoot(projectRoot), "plan.json");
62047
+ if (!existsSync38(planPath)) return false;
61536
62048
  try {
61537
- const parsed = JSON.parse(readFileSync25(planPath, "utf8"));
62049
+ const parsed = JSON.parse(readFileSync26(planPath, "utf8"));
61538
62050
  return Array.isArray(parsed.phases) && parsed.phases.length > 0;
61539
62051
  } catch {
61540
62052
  return false;
@@ -61548,14 +62060,14 @@ var init_planDetect = __esm({
61548
62060
  });
61549
62061
 
61550
62062
  // src/cli/workspace/projectInstructions.ts
61551
- import { existsSync as existsSync37, readFileSync as readFileSync26 } from "node:fs";
61552
- import { join as join29 } from "node:path";
62063
+ import { existsSync as existsSync39, readFileSync as readFileSync27 } from "node:fs";
62064
+ import { join as join30 } from "node:path";
61553
62065
  function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
61554
62066
  for (const name of CANDIDATES) {
61555
- const full = join29(projectRoot, name);
61556
- if (!existsSync37(full)) continue;
62067
+ const full = join30(projectRoot, name);
62068
+ if (!existsSync39(full)) continue;
61557
62069
  try {
61558
- let raw = readFileSync26(full, "utf8");
62070
+ let raw = readFileSync27(full, "utf8");
61559
62071
  raw = raw.replace(/\r\n/g, "\n").trim();
61560
62072
  if (!raw) continue;
61561
62073
  if (raw.length <= maxChars) {
@@ -61599,8 +62111,8 @@ __export(workspaceSummary_exports, {
61599
62111
  buildWorkspaceSummary: () => buildWorkspaceSummary,
61600
62112
  buildZelariReadHint: () => buildZelariReadHint
61601
62113
  });
61602
- import { existsSync as existsSync38, readFileSync as readFileSync27, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
61603
- import { join as join30, relative as relative2 } from "node:path";
62114
+ import { existsSync as existsSync40, readFileSync as readFileSync28, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
62115
+ import { join as join31, relative as relative2 } from "node:path";
61604
62116
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
61605
62117
  const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
61606
62118
  const name = safeProjectName(projectRoot);
@@ -61633,11 +62145,11 @@ function formatTaskLine(t) {
61633
62145
  }
61634
62146
  function buildPlanSummary(projectRoot = process.cwd(), options) {
61635
62147
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
61636
- const planPath = join30(zelariRoot, "plan.json");
61637
- if (!existsSync38(planPath)) return null;
62148
+ const planPath = join31(zelariRoot, "plan.json");
62149
+ if (!existsSync40(planPath)) return null;
61638
62150
  let plan;
61639
62151
  try {
61640
- plan = JSON.parse(readFileSync27(planPath, "utf8"));
62152
+ plan = JSON.parse(readFileSync28(planPath, "utf8"));
61641
62153
  } catch {
61642
62154
  return null;
61643
62155
  }
@@ -61776,8 +62288,8 @@ function pickNextTask(open2) {
61776
62288
  )[0];
61777
62289
  }
61778
62290
  function buildZelariReadHint(projectRoot = process.cwd()) {
61779
- const planPath = join30(resolveWorkspaceRoot(projectRoot), "plan.json");
61780
- if (!existsSync38(planPath)) return "";
62291
+ const planPath = join31(resolveWorkspaceRoot(projectRoot), "plan.json");
62292
+ if (!existsSync40(planPath)) return "";
61781
62293
  return [
61782
62294
  "# Council workspace detected (.zelari/) \u2014 DRAFT vault",
61783
62295
  "`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
@@ -61792,10 +62304,10 @@ function safeProjectName(root) {
61792
62304
  }
61793
62305
  }
61794
62306
  function readPackageJson2(projectRoot) {
61795
- const p3 = join30(projectRoot, "package.json");
61796
- if (!existsSync38(p3)) return null;
62307
+ const p3 = join31(projectRoot, "package.json");
62308
+ if (!existsSync40(p3)) return null;
61797
62309
  try {
61798
- return JSON.parse(readFileSync27(p3, "utf8"));
62310
+ return JSON.parse(readFileSync28(p3, "utf8"));
61799
62311
  } catch {
61800
62312
  return null;
61801
62313
  }
@@ -61836,7 +62348,7 @@ function readBuildScripts(projectRoot, maxScripts = 16) {
61836
62348
  function listShallow(projectRoot, maxEntries) {
61837
62349
  const out = [];
61838
62350
  try {
61839
- const top = readdirSync7(projectRoot, { withFileTypes: true }).filter(
62351
+ const top = readdirSync8(projectRoot, { withFileTypes: true }).filter(
61840
62352
  (e) => !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist"
61841
62353
  ).sort((a, b) => a.name.localeCompare(b.name));
61842
62354
  let count = 0;
@@ -61845,11 +62357,11 @@ function listShallow(projectRoot, maxEntries) {
61845
62357
  out.push(`\u2026 (+${top.length - count} more)`);
61846
62358
  break;
61847
62359
  }
61848
- const rel2 = relative2(projectRoot, join30(projectRoot, entry.name));
62360
+ const rel2 = relative2(projectRoot, join31(projectRoot, entry.name));
61849
62361
  if (entry.isDirectory()) {
61850
62362
  let inner = "";
61851
62363
  try {
61852
- const sub = readdirSync7(join30(projectRoot, entry.name), {
62364
+ const sub = readdirSync8(join31(projectRoot, entry.name), {
61853
62365
  withFileTypes: true
61854
62366
  }).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
61855
62367
  if (sub.length > 0)
@@ -61897,12 +62409,12 @@ var init_workspaceSummary = __esm({
61897
62409
  });
61898
62410
 
61899
62411
  // src/cli/workspace/buildLessonsSummary.ts
61900
- import { existsSync as existsSync39 } from "node:fs";
61901
- import { join as join31 } from "node:path";
62412
+ import { existsSync as existsSync41 } from "node:fs";
62413
+ import { join as join32 } from "node:path";
61902
62414
  function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
61903
62415
  if (process.env["ZELARI_LESSONS"] === "0") return null;
61904
62416
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
61905
- if (!existsSync39(join31(zelariRoot, "lessons.jsonl"))) return null;
62417
+ if (!existsSync41(join32(zelariRoot, "lessons.jsonl"))) return null;
61906
62418
  const lessons = recallLessons(zelariRoot, {
61907
62419
  maxLessons: 5,
61908
62420
  maxBytes: 2048,
@@ -61923,8 +62435,8 @@ var composeContext_exports = {};
61923
62435
  __export(composeContext_exports, {
61924
62436
  composeProjectContext: () => composeProjectContext
61925
62437
  });
61926
- import { existsSync as existsSync40, readdirSync as readdirSync8, readFileSync as readFileSync28 } from "node:fs";
61927
- import { join as join32 } from "node:path";
62438
+ import { existsSync as existsSync42, readdirSync as readdirSync9, readFileSync as readFileSync29 } from "node:fs";
62439
+ import { join as join33 } from "node:path";
61928
62440
  function cap2(text, max, label) {
61929
62441
  if (!text || text.length <= max) return { text: text || "", truncated: false };
61930
62442
  return {
@@ -61936,19 +62448,19 @@ function cap2(text, max, label) {
61936
62448
  }
61937
62449
  function buildDesignIndex(projectRoot, maxChars) {
61938
62450
  const root = resolveWorkspaceRoot(projectRoot);
61939
- if (!existsSync40(root)) return "";
62451
+ if (!existsSync42(root)) return "";
61940
62452
  const lines = [
61941
62453
  "# Design vault index (.zelari/) \u2014 HYPOTHESES only",
61942
62454
  "Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
61943
62455
  ];
61944
- const docsDir = join32(root, "docs");
61945
- if (existsSync40(docsDir)) {
62456
+ const docsDir = join33(root, "docs");
62457
+ if (existsSync42(docsDir)) {
61946
62458
  try {
61947
- const docs = readdirSync8(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
62459
+ const docs = readdirSync9(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
61948
62460
  if (docs.length > 0) {
61949
62461
  lines.push("", "## docs/ (titles only)");
61950
62462
  for (const d of docs) lines.push(`- .zelari/docs/${d}`);
61951
- if (readdirSync8(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
62463
+ if (readdirSync9(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
61952
62464
  lines.push("- \u2026 (more under .zelari/docs/)");
61953
62465
  }
61954
62466
  }
@@ -61956,14 +62468,14 @@ function buildDesignIndex(projectRoot, maxChars) {
61956
62468
  }
61957
62469
  }
61958
62470
  for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
61959
- if (existsSync40(join32(root, name))) {
62471
+ if (existsSync42(join33(root, name))) {
61960
62472
  lines.push(`- .zelari/${name} present`);
61961
62473
  }
61962
62474
  }
61963
- const decisionsDir = join32(root, "decisions");
61964
- if (existsSync40(decisionsDir)) {
62475
+ const decisionsDir = join33(root, "decisions");
62476
+ if (existsSync42(decisionsDir)) {
61965
62477
  try {
61966
- const n = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).length;
62478
+ const n = readdirSync9(decisionsDir).filter((f) => f.endsWith(".md")).length;
61967
62479
  if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
61968
62480
  } catch {
61969
62481
  }
@@ -62062,17 +62574,17 @@ function composeProjectContext(input) {
62062
62574
  }
62063
62575
  function readDurableHeadSync(projectRoot) {
62064
62576
  try {
62065
- const headPath = join32(projectRoot, ".zelari", "state", "HEAD.json");
62066
- if (!existsSync40(headPath)) return "";
62067
- const head = JSON.parse(readFileSync28(headPath, "utf8"));
62577
+ const headPath = join33(projectRoot, ".zelari", "state", "HEAD.json");
62578
+ if (!existsSync42(headPath)) return "";
62579
+ const head = JSON.parse(readFileSync29(headPath, "utf8"));
62068
62580
  if (!head?.id) return "";
62069
- const metaPath = join32(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
62070
- if (!existsSync40(metaPath)) return "";
62071
- const meta3 = JSON.parse(readFileSync28(metaPath, "utf8"));
62072
- const discPath = meta3.artifactDir ? join32(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join32(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
62581
+ const metaPath = join33(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
62582
+ if (!existsSync42(metaPath)) return "";
62583
+ const meta3 = JSON.parse(readFileSync29(metaPath, "utf8"));
62584
+ const discPath = meta3.artifactDir ? join33(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join33(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
62073
62585
  let discoveries = [];
62074
- if (existsSync40(discPath)) {
62075
- discoveries = JSON.parse(readFileSync28(discPath, "utf8"));
62586
+ if (existsSync42(discPath)) {
62587
+ discoveries = JSON.parse(readFileSync29(discPath, "utf8"));
62076
62588
  }
62077
62589
  const reusable = discoveries.filter((d) => d.reusable !== false);
62078
62590
  const lines = [
@@ -62146,14 +62658,14 @@ __export(stubs_exports, {
62146
62658
  resolveWorkspaceRoot: () => resolveWorkspaceRoot
62147
62659
  });
62148
62660
  import {
62149
- existsSync as existsSync41,
62150
- readdirSync as readdirSync9,
62151
- writeFileSync as writeFileSync18,
62152
- readFileSync as readFileSync29,
62153
- mkdirSync as mkdirSync16,
62661
+ existsSync as existsSync43,
62662
+ readdirSync as readdirSync10,
62663
+ writeFileSync as writeFileSync19,
62664
+ readFileSync as readFileSync30,
62665
+ mkdirSync as mkdirSync17,
62154
62666
  renameSync as renameSync5
62155
62667
  } from "node:fs";
62156
- import { join as join33, basename as basename4, dirname as dirname9, relative as relative3 } from "node:path";
62668
+ import { join as join34, basename as basename4, dirname as dirname10, relative as relative3 } from "node:path";
62157
62669
  function createWorkspaceContext(projectRoot = process.cwd()) {
62158
62670
  const rootDir = resolveWorkspaceRoot(projectRoot);
62159
62671
  return {
@@ -62163,14 +62675,14 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
62163
62675
  };
62164
62676
  }
62165
62677
  function planJsonPath(ctx) {
62166
- return join33(ctx.rootDir, "plan.json");
62678
+ return join34(ctx.rootDir, "plan.json");
62167
62679
  }
62168
62680
  function readPlan(ctx) {
62169
62681
  const jsonPath = planJsonPath(ctx);
62170
- if (existsSync41(jsonPath)) {
62682
+ if (existsSync43(jsonPath)) {
62171
62683
  try {
62172
62684
  const parsed = JSON.parse(
62173
- readFileSync29(jsonPath, "utf8")
62685
+ readFileSync30(jsonPath, "utf8")
62174
62686
  );
62175
62687
  const { phases, tasks, milestones, ...root } = parsed;
62176
62688
  return {
@@ -62182,8 +62694,8 @@ function readPlan(ctx) {
62182
62694
  } catch {
62183
62695
  }
62184
62696
  }
62185
- const path103 = workspaceFile(ctx.rootDir, "plan");
62186
- const doc = ctx.storage.readIfExists(path103);
62697
+ const path104 = workspaceFile(ctx.rootDir, "plan");
62698
+ const doc = ctx.storage.readIfExists(path104);
62187
62699
  if (!doc) return { phases: [], tasks: [], milestones: [] };
62188
62700
  const meta3 = doc.meta;
62189
62701
  return {
@@ -62194,9 +62706,9 @@ function readPlan(ctx) {
62194
62706
  }
62195
62707
  function writePlan(ctx, summary) {
62196
62708
  const jsonPath = planJsonPath(ctx);
62197
- mkdirSync16(dirname9(jsonPath), { recursive: true });
62709
+ mkdirSync17(dirname10(jsonPath), { recursive: true });
62198
62710
  const tmp = jsonPath + ".tmp-" + process.pid;
62199
- writeFileSync18(
62711
+ writeFileSync19(
62200
62712
  tmp,
62201
62713
  JSON.stringify(
62202
62714
  {
@@ -62276,9 +62788,9 @@ function renderPlanBody(summary) {
62276
62788
  return lines.join("\n");
62277
62789
  }
62278
62790
  function nextAdrId(ctx) {
62279
- const decisionsDir = join33(ctx.rootDir, "decisions");
62280
- if (!existsSync41(decisionsDir)) return "001";
62281
- const existing = readdirSync9(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
62791
+ const decisionsDir = join34(ctx.rootDir, "decisions");
62792
+ if (!existsSync43(decisionsDir)) return "001";
62793
+ const existing = readdirSync10(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
62282
62794
  const max = existing.length === 0 ? 0 : Math.max(...existing);
62283
62795
  return String(max + 1).padStart(3, "0");
62284
62796
  }
@@ -62322,7 +62834,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
62322
62834
  // plan.json record (they already live in tags + the artifact body md).
62323
62835
  files: t.fileRefs
62324
62836
  });
62325
- const taskPath = join33(ctx.rootDir, "plan-tasks", `${id3}.md`);
62837
+ const taskPath = join34(ctx.rootDir, "plan-tasks", `${id3}.md`);
62326
62838
  const meta3 = {
62327
62839
  kind: "task",
62328
62840
  id: id3,
@@ -62364,7 +62876,7 @@ function addMilestoneRecord(ctx, summary, input) {
62364
62876
  dueDate: input.dueDate,
62365
62877
  targetVersion: version2
62366
62878
  });
62367
- const path103 = join33(ctx.rootDir, "milestones", `${id3}.md`);
62879
+ const path104 = join34(ctx.rootDir, "milestones", `${id3}.md`);
62368
62880
  const meta3 = {
62369
62881
  kind: "milestone",
62370
62882
  id: id3,
@@ -62381,7 +62893,7 @@ function addMilestoneRecord(ctx, summary, input) {
62381
62893
  `Target version: ${version2}`,
62382
62894
  ""
62383
62895
  ].join("\n");
62384
- ctx.storage.write(path103, meta3, body);
62896
+ ctx.storage.write(path104, meta3, body);
62385
62897
  return { id: id3, created: true };
62386
62898
  }
62387
62899
  function readPlanSummary(ctx) {
@@ -62585,7 +63097,7 @@ function addIdeaStub(ctx) {
62585
63097
  const tags = args["tags"] ?? [];
62586
63098
  const category = args["category"] ?? "General";
62587
63099
  const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
62588
- const path103 = workspaceArtifact(ctx.rootDir, "decisions", id3);
63100
+ const path104 = workspaceArtifact(ctx.rootDir, "decisions", id3);
62589
63101
  const meta3 = {
62590
63102
  kind: "adr",
62591
63103
  status: "proposed",
@@ -62611,7 +63123,7 @@ function addIdeaStub(ctx) {
62611
63123
  ...consequences.map((c) => `- ${c}`),
62612
63124
  ""
62613
63125
  ].join("\n");
62614
- ctx.storage.write(path103, meta3, body);
63126
+ ctx.storage.write(path104, meta3, body);
62615
63127
  return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
62616
63128
  });
62617
63129
  }
@@ -62662,8 +63174,8 @@ function createNfrSpecStub(ctx) {
62662
63174
  },
62663
63175
  planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
62664
63176
  };
62665
- const outPath = join33(ctx.rootDir, "nfr-spec.json");
62666
- writeFileSync18(outPath, JSON.stringify(spec, null, 2), "utf8");
63177
+ const outPath = join34(ctx.rootDir, "nfr-spec.json");
63178
+ writeFileSync19(outPath, JSON.stringify(spec, null, 2), "utf8");
62667
63179
  return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
62668
63180
  });
62669
63181
  }
@@ -62693,14 +63205,14 @@ function createDocumentStub(ctx) {
62693
63205
  ctx.storage.write(risksPath, riskMeta, content);
62694
63206
  return `Document "${title}" created at risks.md (workspace root).`;
62695
63207
  }
62696
- const path103 = workspaceArtifact(ctx.rootDir, "docs", slug);
63208
+ const path104 = workspaceArtifact(ctx.rootDir, "docs", slug);
62697
63209
  const meta3 = {
62698
63210
  kind: "doc",
62699
63211
  id: slug,
62700
63212
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
62701
63213
  tags
62702
63214
  };
62703
- ctx.storage.write(path103, meta3, content);
63215
+ ctx.storage.write(path104, meta3, content);
62704
63216
  return `Document "${title}" created at docs/${slug}.md.`;
62705
63217
  });
62706
63218
  }
@@ -62726,18 +63238,18 @@ function searchDocumentsStub(ctx) {
62726
63238
  (w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
62727
63239
  );
62728
63240
  const files = [
62729
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "decisions")),
62730
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "docs")),
62731
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "reviews")),
62732
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "plan-tasks")),
62733
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "milestones")),
63241
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63242
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63243
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews")),
63244
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "plan-tasks")),
63245
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "milestones")),
62734
63246
  workspaceFile(ctx.rootDir, "plan"),
62735
63247
  workspaceFile(ctx.rootDir, "risks")
62736
63248
  ];
62737
63249
  const results = [];
62738
63250
  for (const file2 of files) {
62739
- if (!existsSync41(file2)) continue;
62740
- const raw = readFileSync29(file2, "utf8");
63251
+ if (!existsSync43(file2)) continue;
63252
+ const raw = readFileSync30(file2, "utf8");
62741
63253
  const content = raw.toLowerCase();
62742
63254
  let idx = -1;
62743
63255
  let matchLen = 0;
@@ -62788,9 +63300,9 @@ function linkDocumentsStub(ctx) {
62788
63300
  const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
62789
63301
  if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
62790
63302
  const allFiles = [
62791
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "decisions")),
62792
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "docs")),
62793
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "reviews"))
63303
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63304
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63305
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews"))
62794
63306
  ];
62795
63307
  const source2 = allFiles.find((f) => {
62796
63308
  try {
@@ -62821,9 +63333,9 @@ function getDocumentBacklinksStub(ctx) {
62821
63333
  const targetId = args["targetId"] ?? args["id"];
62822
63334
  if (!targetId) return "getDocumentBacklinks requires targetId.";
62823
63335
  const allFiles = [
62824
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "decisions")),
62825
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "docs")),
62826
- ...ctx.storage.listMarkdown(join33(ctx.rootDir, "reviews"))
63336
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63337
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63338
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews"))
62827
63339
  ];
62828
63340
  const backlinks = [];
62829
63341
  for (const file2 of allFiles) {
@@ -63067,9 +63579,10 @@ var init_mcpClient = __esm({
63067
63579
  MAX_LIST_PAGES = 50;
63068
63580
  MCP_PROTOCOL_VERSION = "2025-03-26";
63069
63581
  McpClient = class {
63070
- constructor(serverName, config2) {
63582
+ constructor(serverName, config2, spawnCwd) {
63071
63583
  this.serverName = serverName;
63072
63584
  this.config = config2;
63585
+ this.spawnCwd = spawnCwd;
63073
63586
  this.defaultTimeoutMs = config2.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
63074
63587
  this.serial = config2.serial ?? this.transportKind() === "http";
63075
63588
  }
@@ -63116,7 +63629,11 @@ var init_mcpClient = __esm({
63116
63629
  const spawnOpts = {
63117
63630
  stdio: ["pipe", "pipe", "pipe"],
63118
63631
  env: { ...process.env, ...this.config.env ?? {} },
63119
- windowsHide: true
63632
+ windowsHide: true,
63633
+ // MCP servers are project-scoped: their cwd is the session project
63634
+ // root, not the CLI process cwd (the Desktop launches the CLI from its
63635
+ // install dir, so "." entries would jail themselves to the app folder).
63636
+ cwd: this.spawnCwd
63120
63637
  };
63121
63638
  const child = process.platform === "win32" ? spawn14(buildCmdLine(command, this.config.args ?? []), {
63122
63639
  ...spawnOpts,
@@ -63235,7 +63752,7 @@ var init_mcpClient = __esm({
63235
63752
  if (!transport && !child)
63236
63753
  return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
63237
63754
  const id3 = this.nextId++;
63238
- return new Promise((resolve9, reject) => {
63755
+ return new Promise((resolve10, reject) => {
63239
63756
  const timer = setTimeout(() => {
63240
63757
  this.pending.delete(id3);
63241
63758
  reject(
@@ -63244,7 +63761,7 @@ var init_mcpClient = __esm({
63244
63761
  )
63245
63762
  );
63246
63763
  }, timeoutMs2);
63247
- this.pending.set(id3, { resolve: resolve9, reject, timer });
63764
+ this.pending.set(id3, { resolve: resolve10, reject, timer });
63248
63765
  if (transport) {
63249
63766
  void transport.send({ id: id3, method, params }, timeoutMs2);
63250
63767
  } else {
@@ -63313,22 +63830,22 @@ var init_mcpClient = __esm({
63313
63830
 
63314
63831
  // src/cli/mcp/mcpConfigIo.ts
63315
63832
  import {
63316
- existsSync as existsSync42,
63317
- mkdirSync as mkdirSync17,
63318
- readFileSync as readFileSync30,
63319
- writeFileSync as writeFileSync19
63833
+ existsSync as existsSync44,
63834
+ mkdirSync as mkdirSync18,
63835
+ readFileSync as readFileSync31,
63836
+ writeFileSync as writeFileSync20
63320
63837
  } from "node:fs";
63321
- import { dirname as dirname10, join as join34 } from "node:path";
63838
+ import { dirname as dirname11, join as join35 } from "node:path";
63322
63839
  function getUserMcpPath() {
63323
- return join34(zelariHome(), "mcp.json");
63840
+ return join35(zelariHome(), "mcp.json");
63324
63841
  }
63325
63842
  function getProjectMcpPath(projectRoot) {
63326
- return join34(projectRoot, ".zelari", "mcp.json");
63843
+ return join35(projectRoot, ".zelari", "mcp.json");
63327
63844
  }
63328
- function readFile10(path103) {
63329
- if (!existsSync42(path103)) return {};
63845
+ function readFile10(path104) {
63846
+ if (!existsSync44(path104)) return {};
63330
63847
  try {
63331
- const parsed = JSON.parse(readFileSync30(path103, "utf8"));
63848
+ const parsed = JSON.parse(readFileSync31(path104, "utf8"));
63332
63849
  const out = {};
63333
63850
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
63334
63851
  const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
@@ -63350,10 +63867,10 @@ function readFile10(path103) {
63350
63867
  return {};
63351
63868
  }
63352
63869
  }
63353
- function writeFile4(path103, servers) {
63354
- mkdirSync17(dirname10(path103), { recursive: true });
63870
+ function writeFile4(path104, servers) {
63871
+ mkdirSync18(dirname11(path104), { recursive: true });
63355
63872
  const body = { mcpServers: servers };
63356
- writeFileSync19(path103, `${JSON.stringify(body, null, 2)}
63873
+ writeFileSync20(path104, `${JSON.stringify(body, null, 2)}
63357
63874
  `, "utf8");
63358
63875
  }
63359
63876
  function listMcpServers(projectRoot) {
@@ -63375,6 +63892,46 @@ function listMcpServers(projectRoot) {
63375
63892
  };
63376
63893
  return { userPath, projectPath, servers, merged };
63377
63894
  }
63895
+ function requireEnvKey(key) {
63896
+ if (!ENV_KEY_RE.test(key)) {
63897
+ throw new Error(
63898
+ `Invalid env key "${key}" \u2014 use letters, digits, _ (no leading digit)`
63899
+ );
63900
+ }
63901
+ return key;
63902
+ }
63903
+ function parseMcpEnvFlag(raw) {
63904
+ if (raw.length === 0) return void 0;
63905
+ const env = {};
63906
+ for (const value of raw) {
63907
+ const text = value.trim();
63908
+ if (text.startsWith("{") || text.startsWith("[")) {
63909
+ let parsed;
63910
+ try {
63911
+ parsed = JSON.parse(text);
63912
+ } catch {
63913
+ throw new Error(`--env is not valid JSON: ${text}`);
63914
+ }
63915
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
63916
+ throw new Error('--env JSON must be an object of "KEY": "VALUE" pairs');
63917
+ }
63918
+ for (const [key2, val] of Object.entries(parsed)) {
63919
+ if (typeof val !== "string") {
63920
+ throw new Error(`--env "${key2}" must map to a string value`);
63921
+ }
63922
+ env[requireEnvKey(key2)] = val;
63923
+ }
63924
+ continue;
63925
+ }
63926
+ const eq = text.indexOf("=");
63927
+ const key = eq > 0 ? text.slice(0, eq).trim() : "";
63928
+ if (!key) {
63929
+ throw new Error(`Invalid --env "${text}" \u2014 use KEY=VALUE or a JSON object`);
63930
+ }
63931
+ env[requireEnvKey(key)] = text.slice(eq + 1);
63932
+ }
63933
+ return env;
63934
+ }
63378
63935
  function upsertMcpServer(opts) {
63379
63936
  const name = opts.name.trim();
63380
63937
  if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
@@ -63391,9 +63948,9 @@ function upsertMcpServer(opts) {
63391
63948
  error: "either command (stdio) or url (http) is required"
63392
63949
  };
63393
63950
  }
63394
- let path103;
63951
+ let path104;
63395
63952
  if (opts.scope === "user") {
63396
- path103 = getUserMcpPath();
63953
+ path104 = getUserMcpPath();
63397
63954
  } else {
63398
63955
  const root = opts.projectRoot?.trim();
63399
63956
  if (!root) {
@@ -63402,39 +63959,46 @@ function upsertMcpServer(opts) {
63402
63959
  error: "projectRoot required for project scope (Open Folder first)"
63403
63960
  };
63404
63961
  }
63405
- path103 = getProjectMcpPath(root);
63962
+ path104 = getProjectMcpPath(root);
63406
63963
  }
63407
- const current = readFile10(path103);
63964
+ const current = readFile10(path104);
63965
+ const previous = current[name];
63408
63966
  current[name] = {
63409
63967
  command: hasCommand ? opts.config.command.trim() : void 0,
63410
63968
  args: opts.config.args,
63411
- env: opts.config.env,
63969
+ // Back-compat: callers that only flip `enabled` (Desktop toggle) or that
63970
+ // have no env channel at all (`--set-mcp` without `--env`) must not wipe
63971
+ // env written by hand or by another tool. An explicit env always wins —
63972
+ // including `{}`, which clears it.
63973
+ env: opts.config.env ?? previous?.env,
63412
63974
  type: hasUrl ? "http" : opts.config.type === "http" ? "http" : "stdio",
63413
63975
  url: hasUrl ? opts.config.url.trim() : void 0,
63414
63976
  timeoutMs: opts.config.timeoutMs,
63415
63977
  serial: opts.config.serial,
63416
63978
  enabled: opts.config.enabled !== false
63417
63979
  };
63418
- writeFile4(path103, current);
63419
- return { ok: true, path: path103 };
63980
+ writeFile4(path104, current);
63981
+ return { ok: true, path: path104 };
63420
63982
  }
63421
63983
  function removeMcpServer(opts) {
63422
- const path103 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
63423
- if (!path103) {
63984
+ const path104 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
63985
+ if (!path104) {
63424
63986
  return { ok: false, error: "projectRoot required for project scope" };
63425
63987
  }
63426
- const current = readFile10(path103);
63988
+ const current = readFile10(path104);
63427
63989
  if (!(opts.name in current)) {
63428
- return { ok: false, error: `Server "${opts.name}" not found in ${path103}` };
63990
+ return { ok: false, error: `Server "${opts.name}" not found in ${path104}` };
63429
63991
  }
63430
63992
  delete current[opts.name];
63431
- writeFile4(path103, current);
63432
- return { ok: true, path: path103 };
63993
+ writeFile4(path104, current);
63994
+ return { ok: true, path: path104 };
63433
63995
  }
63996
+ var ENV_KEY_RE;
63434
63997
  var init_mcpConfigIo = __esm({
63435
63998
  "src/cli/mcp/mcpConfigIo.ts"() {
63436
63999
  "use strict";
63437
64000
  init_paths();
64001
+ ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
63438
64002
  }
63439
64003
  });
63440
64004
 
@@ -63601,21 +64165,21 @@ __export(mcpManager_exports, {
63601
64165
  readMcpConfig: () => readMcpConfig,
63602
64166
  registerMcpTools: () => registerMcpTools
63603
64167
  });
63604
- import { existsSync as existsSync43, readFileSync as readFileSync31 } from "node:fs";
63605
- import { join as join35 } from "node:path";
64168
+ import { existsSync as existsSync45, readFileSync as readFileSync32 } from "node:fs";
64169
+ import { join as join36 } from "node:path";
63606
64170
  function readMcpConfig(projectRoot = process.cwd(), opts) {
63607
64171
  const merged = {};
63608
64172
  const paths = [];
63609
64173
  if (process.env["ZELARI_MCP_USER"] !== "0") {
63610
- paths.push(join35(zelariHome(), "mcp.json"));
64174
+ paths.push(join36(zelariHome(), "mcp.json"));
63611
64175
  }
63612
64176
  if (!opts?.skipProjectMcp) {
63613
- paths.push(join35(projectRoot, ".zelari", "mcp.json"));
64177
+ paths.push(join36(projectRoot, ".zelari", "mcp.json"));
63614
64178
  }
63615
64179
  for (const p3 of paths) {
63616
- if (!existsSync43(p3)) continue;
64180
+ if (!existsSync45(p3)) continue;
63617
64181
  try {
63618
- const parsed = JSON.parse(readFileSync31(p3, "utf8"));
64182
+ const parsed = JSON.parse(readFileSync32(p3, "utf8"));
63619
64183
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
63620
64184
  const hasCommand = !!cfg && typeof cfg.command === "string" && cfg.command.length > 0;
63621
64185
  const hasUrl = !!cfg && typeof cfg.url === "string" && /^https?:\/\//i.test(cfg.url);
@@ -63631,7 +64195,7 @@ async function ensureLoaded(projectRoot) {
63631
64195
  if (state2.loaded) return;
63632
64196
  state2.loaded = true;
63633
64197
  const trusted = isFolderTrusted(projectRoot);
63634
- if (!trusted && existsSync43(join35(projectRoot, ".zelari", "mcp.json"))) {
64198
+ if (!trusted && existsSync45(join36(projectRoot, ".zelari", "mcp.json"))) {
63635
64199
  state2.warnings.push(
63636
64200
  "[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
63637
64201
  );
@@ -63645,7 +64209,7 @@ async function ensureLoaded(projectRoot) {
63645
64209
  );
63646
64210
  continue;
63647
64211
  }
63648
- const client = new McpClient(name, cfg);
64212
+ const client = new McpClient(name, cfg, projectRoot);
63649
64213
  try {
63650
64214
  await client.start();
63651
64215
  const tools = await client.listTools();
@@ -63895,15 +64459,15 @@ __export(agentsMd_exports, {
63895
64459
  serializeAgentsMd: () => serializeAgentsMd,
63896
64460
  updateAgentsMd: () => updateAgentsMd
63897
64461
  });
63898
- import { existsSync as existsSync44, readFileSync as readFileSync32, writeFileSync as writeFileSync20 } from "node:fs";
64462
+ import { existsSync as existsSync46, readFileSync as readFileSync33, writeFileSync as writeFileSync21 } from "node:fs";
63899
64463
  import { createHash as createHash24 } from "node:crypto";
63900
- import { join as join36 } from "node:path";
64464
+ import { join as join37 } from "node:path";
63901
64465
  import { readFile as readFile11 } from "node:fs/promises";
63902
64466
  async function readPackageJson3(projectRoot) {
63903
- const path103 = join36(projectRoot, "package.json");
63904
- if (!existsSync44(path103)) return null;
64467
+ const path104 = join37(projectRoot, "package.json");
64468
+ if (!existsSync46(path104)) return null;
63905
64469
  try {
63906
- return JSON.parse(await readFile11(path103, "utf8"));
64470
+ return JSON.parse(await readFile11(path104, "utf8"));
63907
64471
  } catch {
63908
64472
  return null;
63909
64473
  }
@@ -63925,8 +64489,8 @@ async function genTechStack(ctx) {
63925
64489
  ].join("\n");
63926
64490
  }
63927
64491
  async function genDecisions(ctx) {
63928
- const decisionsDir = join36(ctx.rootDir, "decisions");
63929
- if (!existsSync44(decisionsDir)) return "_No ADRs yet._";
64492
+ const decisionsDir = join37(ctx.rootDir, "decisions");
64493
+ if (!existsSync46(decisionsDir)) return "_No ADRs yet._";
63930
64494
  const files = ctx.storage.listMarkdown(decisionsDir).sort();
63931
64495
  const accepted = [];
63932
64496
  const proposed = [];
@@ -63951,9 +64515,9 @@ async function genDecisions(ctx) {
63951
64515
  }
63952
64516
  async function genConventions(ctx) {
63953
64517
  const lines = [];
63954
- const claudeMd = join36(ctx.projectRoot, "CLAUDE.MD");
63955
- if (existsSync44(claudeMd)) {
63956
- const content = readFileSync32(claudeMd, "utf8");
64518
+ const claudeMd = join37(ctx.projectRoot, "CLAUDE.MD");
64519
+ if (existsSync46(claudeMd)) {
64520
+ const content = readFileSync33(claudeMd, "utf8");
63957
64521
  const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
63958
64522
  if (match) {
63959
64523
  lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
@@ -63985,9 +64549,9 @@ async function genBuild(ctx) {
63985
64549
  ].join("\n");
63986
64550
  }
63987
64551
  async function genOpenQuestions(ctx) {
63988
- const path103 = join36(ctx.rootDir, "risks.md");
63989
- if (!existsSync44(path103)) return "_No open questions._";
63990
- const content = readFileSync32(path103, "utf8");
64552
+ const path104 = join37(ctx.rootDir, "risks.md");
64553
+ if (!existsSync46(path104)) return "_No open questions._";
64554
+ const content = readFileSync33(path104, "utf8");
63991
64555
  const lines = content.split("\n");
63992
64556
  const questions = [];
63993
64557
  let currentTitle = "";
@@ -64061,9 +64625,9 @@ function titleCase(id3) {
64061
64625
  return id3.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
64062
64626
  }
64063
64627
  async function updateAgentsMd(ctx, projectRoot) {
64064
- const agentsPath = join36(projectRoot, "AGENTS.MD");
64065
- if (existsSync44(agentsPath)) {
64066
- const content = readFileSync32(agentsPath, "utf8");
64628
+ const agentsPath = join37(projectRoot, "AGENTS.MD");
64629
+ if (existsSync46(agentsPath)) {
64630
+ const content = readFileSync33(agentsPath, "utf8");
64067
64631
  const hasAnyMarker = AUTO_SECTIONS.some((id3) => content.includes(MARKER_OPEN(id3)));
64068
64632
  if (!hasAnyMarker) {
64069
64633
  return {
@@ -64078,8 +64642,8 @@ async function updateAgentsMd(ctx, projectRoot) {
64078
64642
  newSections.set(id3, await GENERATORS[id3](ctx));
64079
64643
  }
64080
64644
  let manualContent = "";
64081
- if (existsSync44(agentsPath)) {
64082
- const { manualBlocks } = parseAgentsMd(readFileSync32(agentsPath, "utf8"));
64645
+ if (existsSync46(agentsPath)) {
64646
+ const { manualBlocks } = parseAgentsMd(readFileSync33(agentsPath, "utf8"));
64083
64647
  manualContent = manualBlocks.after;
64084
64648
  } else {
64085
64649
  const projectName2 = projectName(projectRoot);
@@ -64095,7 +64659,7 @@ async function updateAgentsMd(ctx, projectRoot) {
64095
64659
  ""
64096
64660
  ].join("\n");
64097
64661
  }
64098
- const oldContent = existsSync44(agentsPath) ? readFileSync32(agentsPath, "utf8") : "";
64662
+ const oldContent = existsSync46(agentsPath) ? readFileSync33(agentsPath, "utf8") : "";
64099
64663
  const { sections: oldSections } = parseAgentsMd(oldContent);
64100
64664
  const changedSections = [];
64101
64665
  for (const id3 of AUTO_SECTIONS) {
@@ -64107,7 +64671,7 @@ async function updateAgentsMd(ctx, projectRoot) {
64107
64671
  return { changed: false, sections: [] };
64108
64672
  }
64109
64673
  const newContent = serializeAgentsMd(manualContent, newSections);
64110
- writeFileSync20(agentsPath, newContent, "utf8");
64674
+ writeFileSync21(agentsPath, newContent, "utf8");
64111
64675
  return { changed: true, sections: changedSections };
64112
64676
  }
64113
64677
  function hash2(s) {
@@ -64232,12 +64796,12 @@ var init_completeDesign = __esm({
64232
64796
  });
64233
64797
 
64234
64798
  // src/cli/workspace/planDriftCheck.ts
64235
- import { existsSync as existsSync45, readFileSync as readFileSync33, readdirSync as readdirSync10, statSync as statSync6, writeFileSync as writeFileSync21 } from "node:fs";
64236
- import { join as join37 } from "node:path";
64799
+ import { existsSync as existsSync47, readFileSync as readFileSync34, readdirSync as readdirSync11, statSync as statSync6, writeFileSync as writeFileSync22 } from "node:fs";
64800
+ import { join as join38 } from "node:path";
64237
64801
  function findCanonicalDoc(rootDir) {
64238
- const docsDir = join37(rootDir, "docs");
64239
- if (!existsSync45(docsDir)) return null;
64240
- const candidates = readdirSync10(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync6(join37(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
64802
+ const docsDir = join38(rootDir, "docs");
64803
+ if (!existsSync47(docsDir)) return null;
64804
+ const candidates = readdirSync11(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync6(join38(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
64241
64805
  return candidates.length > 0 ? candidates[0].f : null;
64242
64806
  }
64243
64807
  function parseCanonicalDoc(text) {
@@ -64261,9 +64825,9 @@ function versionKey(value) {
64261
64825
  function firstString2(v) {
64262
64826
  return typeof v === "string" && v.trim().length > 0 ? v : null;
64263
64827
  }
64264
- function readFileSyncSafe(path103) {
64828
+ function readFileSyncSafe(path104) {
64265
64829
  try {
64266
- return readFileSync33(path103, "utf8");
64830
+ return readFileSync34(path104, "utf8");
64267
64831
  } catch {
64268
64832
  return null;
64269
64833
  }
@@ -64272,13 +64836,13 @@ async function runPlanDriftCheck(rootDir) {
64272
64836
  if (process.env["ZELARI_DRIFT_CHECK"] === "0") {
64273
64837
  return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
64274
64838
  }
64275
- const planPath = join37(rootDir, "plan.json");
64276
- if (!existsSync45(planPath)) {
64839
+ const planPath = join38(rootDir, "plan.json");
64840
+ if (!existsSync47(planPath)) {
64277
64841
  return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
64278
64842
  }
64279
64843
  let plan;
64280
64844
  try {
64281
- plan = JSON.parse(readFileSync33(planPath, "utf8"));
64845
+ plan = JSON.parse(readFileSync34(planPath, "utf8"));
64282
64846
  } catch {
64283
64847
  return { ran: false, reason: ".zelari/plan.json corrupt" };
64284
64848
  }
@@ -64288,7 +64852,7 @@ async function runPlanDriftCheck(rootDir) {
64288
64852
  const milestones = Array.isArray(plan.milestones) ? plan.milestones : [];
64289
64853
  const phaseIds = new Set(phases.map((p3) => typeof p3.id === "string" ? p3.id : "").filter(Boolean));
64290
64854
  const canonicalName = findCanonicalDoc(rootDir);
64291
- const canonicalText = canonicalName ? readFileSyncSafe(join37(rootDir, "docs", canonicalName)) : null;
64855
+ const canonicalText = canonicalName ? readFileSyncSafe(join38(rootDir, "docs", canonicalName)) : null;
64292
64856
  if (canonicalText !== null) {
64293
64857
  const { activePhases, blockedPrefixes } = parseCanonicalDoc(canonicalText);
64294
64858
  for (const id3 of activePhases) {
@@ -64367,8 +64931,8 @@ async function runPlanDriftCheck(rootDir) {
64367
64931
  const canonicalParsed = canonicalName !== null && canonicalText !== null;
64368
64932
  let reportPath;
64369
64933
  try {
64370
- reportPath = join37(rootDir, "drift-report.json");
64371
- writeFileSync21(
64934
+ reportPath = join38(rootDir, "drift-report.json");
64935
+ writeFileSync22(
64372
64936
  reportPath,
64373
64937
  JSON.stringify(
64374
64938
  {
@@ -64404,8 +64968,8 @@ var init_planDriftCheck = __esm({
64404
64968
 
64405
64969
  // src/cli/workspace/projectSmoke.ts
64406
64970
  import { spawn as spawn15 } from "node:child_process";
64407
- import { existsSync as existsSync46, readFileSync as readFileSync34 } from "node:fs";
64408
- import { join as join38 } from "node:path";
64971
+ import { existsSync as existsSync48, readFileSync as readFileSync35 } from "node:fs";
64972
+ import { join as join39 } from "node:path";
64409
64973
  function pickSmokeScript(scripts) {
64410
64974
  if (!scripts) return null;
64411
64975
  for (const name of SMOKE_SCRIPT_PRIORITY) {
@@ -64417,13 +64981,13 @@ async function runProjectSmoke(projectRoot, timeoutMs2 = DEFAULT_TIMEOUT_MS3) {
64417
64981
  if (process.env["ZELARI_SMOKE"] === "0") {
64418
64982
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
64419
64983
  }
64420
- const pkgPath = join38(projectRoot, "package.json");
64421
- if (!existsSync46(pkgPath)) {
64984
+ const pkgPath = join39(projectRoot, "package.json");
64985
+ if (!existsSync48(pkgPath)) {
64422
64986
  return { ran: false, reason: "no package.json (skipped)" };
64423
64987
  }
64424
64988
  let scripts = {};
64425
64989
  try {
64426
- const pkg = JSON.parse(readFileSync34(pkgPath, "utf8"));
64990
+ const pkg = JSON.parse(readFileSync35(pkgPath, "utf8"));
64427
64991
  scripts = pkg.scripts ?? {};
64428
64992
  } catch {
64429
64993
  return { ran: false, reason: "package.json unreadable (skipped)" };
@@ -64510,7 +65074,7 @@ __export(evidenceFromSpine_exports, {
64510
65074
  collectSessionEvidenceRefs: () => collectSessionEvidenceRefs,
64511
65075
  evidenceRefsFromEventLines: () => evidenceRefsFromEventLines
64512
65076
  });
64513
- import { existsSync as existsSync47, readFileSync as readFileSync35 } from "node:fs";
65077
+ import { existsSync as existsSync49, readFileSync as readFileSync36 } from "node:fs";
64514
65078
  import path75 from "node:path";
64515
65079
  function asRecord2(v) {
64516
65080
  return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
@@ -64550,8 +65114,8 @@ function evidenceRefsFromEventLines(lines) {
64550
65114
  function collectSessionEvidenceRefs(sessionId2) {
64551
65115
  try {
64552
65116
  const file2 = path75.join(sessionsDir(), sessionId2, "events.jsonl");
64553
- if (!existsSync47(file2)) return [];
64554
- return evidenceRefsFromEventLines(readFileSync35(file2, "utf8").split("\n"));
65117
+ if (!existsSync49(file2)) return [];
65118
+ return evidenceRefsFromEventLines(readFileSync36(file2, "utf8").split("\n"));
64555
65119
  } catch {
64556
65120
  return [];
64557
65121
  }
@@ -64574,8 +65138,8 @@ __export(postCouncilHook_exports, {
64574
65138
  runPostCouncilHook: () => runPostCouncilHook
64575
65139
  });
64576
65140
  import { spawn as spawn16 } from "node:child_process";
64577
- import { existsSync as existsSync48, readFileSync as readFileSync36 } from "node:fs";
64578
- import { join as join39 } from "node:path";
65141
+ import { existsSync as existsSync50, readFileSync as readFileSync37 } from "node:fs";
65142
+ import { join as join40 } from "node:path";
64579
65143
  async function runCompleteDesignPostProcessor(ctx, options) {
64580
65144
  if (options?.runMode === "implementation") {
64581
65145
  return {
@@ -64586,9 +65150,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64586
65150
  if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
64587
65151
  return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
64588
65152
  }
64589
- const planJsonPath2 = join39(ctx.rootDir, "plan.json");
64590
- const scriptPath = join39(ctx.projectRoot, "complete-design.mjs");
64591
- if (!existsSync48(planJsonPath2)) {
65153
+ const planJsonPath2 = join40(ctx.rootDir, "plan.json");
65154
+ const scriptPath = join40(ctx.projectRoot, "complete-design.mjs");
65155
+ if (!existsSync50(planJsonPath2)) {
64592
65156
  return {
64593
65157
  ran: false,
64594
65158
  reason: ".zelari/plan.json missing (not design-phase)"
@@ -64596,7 +65160,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64596
65160
  }
64597
65161
  let phaseCount = 0;
64598
65162
  try {
64599
- const parsed = JSON.parse(readFileSync36(planJsonPath2, "utf8"));
65163
+ const parsed = JSON.parse(readFileSync37(planJsonPath2, "utf8"));
64600
65164
  phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
64601
65165
  } catch {
64602
65166
  return { ran: false, reason: ".zelari/plan.json corrupt" };
@@ -64604,7 +65168,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64604
65168
  if (phaseCount === 0) {
64605
65169
  return { ran: false, reason: ".zelari/plan.json has no phases" };
64606
65170
  }
64607
- if (!existsSync48(scriptPath)) {
65171
+ if (!existsSync50(scriptPath)) {
64608
65172
  try {
64609
65173
  const builtin = await runBuiltinCompleteDesign(ctx);
64610
65174
  return {
@@ -64799,8 +65363,8 @@ async function runPostCouncilHook(ctx, options) {
64799
65363
  sources: scope.sources
64800
65364
  } : void 0
64801
65365
  });
64802
- const path103 = writeCouncilCompletion(ctx.rootDir, completion);
64803
- completionHook = { ran: true, path: path103, completion };
65366
+ const path104 = writeCouncilCompletion(ctx.rootDir, completion);
65367
+ completionHook = { ran: true, path: path104, completion };
64804
65368
  } catch (err) {
64805
65369
  completionHook = {
64806
65370
  ran: true,
@@ -64840,10 +65404,10 @@ __export(councilFeedback_exports, {
64840
65404
  });
64841
65405
  import {
64842
65406
  promises as fs33,
64843
- existsSync as existsSync49,
64844
- readFileSync as readFileSync37,
64845
- writeFileSync as writeFileSync22,
64846
- mkdirSync as mkdirSync18
65407
+ existsSync as existsSync51,
65408
+ readFileSync as readFileSync38,
65409
+ writeFileSync as writeFileSync23,
65410
+ mkdirSync as mkdirSync19
64847
65411
  } from "node:fs";
64848
65412
  import path76 from "node:path";
64849
65413
  var FeedbackStore;
@@ -64949,9 +65513,9 @@ var init_councilFeedback = __esm({
64949
65513
  }
64950
65514
  // --- persistence ---------------------------------------------------------
64951
65515
  load() {
64952
- if (!existsSync49(this.file)) return;
65516
+ if (!existsSync51(this.file)) return;
64953
65517
  try {
64954
- const raw = readFileSync37(this.file, "utf-8");
65518
+ const raw = readFileSync38(this.file, "utf-8");
64955
65519
  const parsed = JSON.parse(raw);
64956
65520
  if (parsed && Array.isArray(parsed.entries)) {
64957
65521
  this.entries = parsed.entries.filter(
@@ -64962,8 +65526,8 @@ var init_councilFeedback = __esm({
64962
65526
  }
64963
65527
  }
64964
65528
  save() {
64965
- mkdirSync18(path76.dirname(this.file), { recursive: true });
64966
- writeFileSync22(
65529
+ mkdirSync19(path76.dirname(this.file), { recursive: true });
65530
+ writeFileSync23(
64967
65531
  this.file,
64968
65532
  JSON.stringify({ entries: this.entries }, null, 2),
64969
65533
  { encoding: "utf-8", mode: 384 }
@@ -65036,7 +65600,7 @@ __export(ledger_exports, {
65036
65600
  ledgerStats: () => ledgerStats,
65037
65601
  readLedger: () => readLedger
65038
65602
  });
65039
- import { appendFileSync as appendFileSync4, existsSync as existsSync50, mkdirSync as mkdirSync19, readFileSync as readFileSync38 } from "node:fs";
65603
+ import { appendFileSync as appendFileSync4, existsSync as existsSync52, mkdirSync as mkdirSync20, readFileSync as readFileSync39 } from "node:fs";
65040
65604
  import path77 from "node:path";
65041
65605
  function evolutionMode(env = process.env) {
65042
65606
  return env[EVOLUTION_ENV] === "shadow" ? "shadow" : "0";
@@ -65050,7 +65614,7 @@ function appendLedgerEntry(cwd, entry) {
65050
65614
  }
65051
65615
  try {
65052
65616
  const file2 = ledgerPath(cwd);
65053
- mkdirSync19(path77.dirname(file2), { recursive: true });
65617
+ mkdirSync20(path77.dirname(file2), { recursive: true });
65054
65618
  appendFileSync4(file2, `${JSON.stringify(entry)}
65055
65619
  `, "utf8");
65056
65620
  return { written: true, path: file2 };
@@ -65063,10 +65627,10 @@ function appendLedgerEntry(cwd, entry) {
65063
65627
  }
65064
65628
  function readLedger(cwd) {
65065
65629
  const file2 = ledgerPath(cwd);
65066
- if (!existsSync50(file2)) return [];
65630
+ if (!existsSync52(file2)) return [];
65067
65631
  let raw;
65068
65632
  try {
65069
- raw = readFileSync38(file2, "utf8");
65633
+ raw = readFileSync39(file2, "utf8");
65070
65634
  } catch {
65071
65635
  return [];
65072
65636
  }
@@ -66286,7 +66850,7 @@ function safeSocketPath(socketPath) {
66286
66850
  return socketPath.trim();
66287
66851
  }
66288
66852
  function startPermissionBroker(socketPath, handlers, opts) {
66289
- const path103 = safeSocketPath(socketPath);
66853
+ const path104 = safeSocketPath(socketPath);
66290
66854
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
66291
66855
  const sockets = /* @__PURE__ */ new Set();
66292
66856
  const server = createServer2((socket) => {
@@ -66383,13 +66947,13 @@ function startPermissionBroker(socketPath, handlers, opts) {
66383
66947
  });
66384
66948
  }
66385
66949
  }
66386
- return new Promise((resolve9, reject) => {
66950
+ return new Promise((resolve10, reject) => {
66387
66951
  const onError = (err) => reject(err);
66388
66952
  server.once("error", onError);
66389
- server.listen(path103, () => {
66953
+ server.listen(path104, () => {
66390
66954
  server.removeListener("error", onError);
66391
- resolve9({
66392
- socketPath: path103,
66955
+ resolve10({
66956
+ socketPath: path104,
66393
66957
  stop: () => new Promise((res) => {
66394
66958
  for (const s of sockets) s.destroy();
66395
66959
  sockets.clear();
@@ -66400,7 +66964,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
66400
66964
  if (done) return;
66401
66965
  done = true;
66402
66966
  if (process.platform !== "win32") {
66403
- unlink(path103, () => res());
66967
+ unlink(path104, () => res());
66404
66968
  } else {
66405
66969
  res();
66406
66970
  }
@@ -66413,11 +66977,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
66413
66977
  });
66414
66978
  }
66415
66979
  function requestBrokerAsk(socketPath, ask, opts) {
66416
- const path103 = safeSocketPath(socketPath);
66980
+ const path104 = safeSocketPath(socketPath);
66417
66981
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
66418
66982
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
66419
- return new Promise((resolve9, reject) => {
66420
- const socket = connect(path103);
66983
+ return new Promise((resolve10, reject) => {
66984
+ const socket = connect(path104);
66421
66985
  let buffer = "";
66422
66986
  let settled = false;
66423
66987
  const settle = (fn) => {
@@ -66432,7 +66996,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
66432
66996
  settle(
66433
66997
  () => reject(
66434
66998
  new Error(
66435
- `permission broker unavailable at "${path103}" (connect timed out after ${connectTimeoutMs}ms)`
66999
+ `permission broker unavailable at "${path104}" (connect timed out after ${connectTimeoutMs}ms)`
66436
67000
  )
66437
67001
  )
66438
67002
  );
@@ -66469,7 +67033,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
66469
67033
  }
66470
67034
  if (msg?.t === "answer" && msg.id === ask.id) {
66471
67035
  const { t: _t, id: _id, ...answer } = msg;
66472
- settle(() => resolve9(answer));
67036
+ settle(() => resolve10(answer));
66473
67037
  return;
66474
67038
  }
66475
67039
  }
@@ -67249,8 +67813,8 @@ __export(prereqChecks_exports, {
67249
67813
  runPrereqChecks: () => runPrereqChecks
67250
67814
  });
67251
67815
  import { execSync, spawnSync as spawnSync2 } from "node:child_process";
67252
- import { existsSync as existsSync52 } from "node:fs";
67253
- import { dirname as dirname12 } from "node:path";
67816
+ import { existsSync as existsSync54 } from "node:fs";
67817
+ import { dirname as dirname13 } from "node:path";
67254
67818
  function isWslBashPath2(p3) {
67255
67819
  if (!p3 || typeof p3 !== "string") return false;
67256
67820
  const n = p3.replace(/\//g, "\\").toLowerCase();
@@ -67356,16 +67920,16 @@ function resolveAgentShellSync() {
67356
67920
  function agentProbeEnv() {
67357
67921
  const env = { ...process.env };
67358
67922
  try {
67359
- const nodeDir = dirname12(process.execPath);
67923
+ const nodeDir = dirname13(process.execPath);
67360
67924
  if (!nodeDir) return env;
67361
- const sep4 = process.platform === "win32" ? ";" : ":";
67925
+ const sep5 = process.platform === "win32" ? ";" : ":";
67362
67926
  const current = env.PATH ?? env.Path ?? "";
67363
- const parts = current.split(sep4).filter((p3) => p3.length > 0);
67927
+ const parts = current.split(sep5).filter((p3) => p3.length > 0);
67364
67928
  const has = parts.some(
67365
67929
  (p3) => p3.toLowerCase() === nodeDir.toLowerCase()
67366
67930
  );
67367
67931
  if (!has) {
67368
- env.PATH = `${nodeDir}${sep4}${current}`;
67932
+ env.PATH = `${nodeDir}${sep5}${current}`;
67369
67933
  }
67370
67934
  } catch {
67371
67935
  }
@@ -67373,7 +67937,7 @@ function agentProbeEnv() {
67373
67937
  }
67374
67938
  function existsSyncSafe2(p3) {
67375
67939
  try {
67376
- return existsSync52(p3);
67940
+ return existsSync54(p3);
67377
67941
  } catch {
67378
67942
  return false;
67379
67943
  }
@@ -67620,7 +68184,7 @@ var init_prereqChecks = __esm({
67620
68184
  });
67621
68185
 
67622
68186
  // src/cli/plugins/prefs.ts
67623
- import { existsSync as existsSync53, readFileSync as readFileSync40, writeFileSync as writeFileSync23, mkdirSync as mkdirSync20 } from "node:fs";
68187
+ import { existsSync as existsSync55, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync21 } from "node:fs";
67624
68188
  import path85 from "node:path";
67625
68189
  function getPluginPrefsPath() {
67626
68190
  return pluginsPrefsPath();
@@ -67628,8 +68192,8 @@ function getPluginPrefsPath() {
67628
68192
  function getPluginPrefs() {
67629
68193
  const file2 = getPluginPrefsPath();
67630
68194
  try {
67631
- if (!existsSync53(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
67632
- const raw = readFileSync40(file2, "utf-8");
68195
+ if (!existsSync55(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
68196
+ const raw = readFileSync41(file2, "utf-8");
67633
68197
  const parsed = JSON.parse(raw);
67634
68198
  if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
67635
68199
  const clean = {};
@@ -67644,8 +68208,8 @@ function getPluginPrefs() {
67644
68208
  }
67645
68209
  function writePluginPrefs(prefs) {
67646
68210
  const file2 = getPluginPrefsPath();
67647
- mkdirSync20(path85.dirname(file2), { recursive: true });
67648
- writeFileSync23(file2, JSON.stringify(prefs, null, 2), {
68211
+ mkdirSync21(path85.dirname(file2), { recursive: true });
68212
+ writeFileSync24(file2, JSON.stringify(prefs, null, 2), {
67649
68213
  encoding: "utf-8",
67650
68214
  mode: 384
67651
68215
  });
@@ -67681,7 +68245,7 @@ __export(registry_exports, {
67681
68245
  findPlugin: () => findPlugin,
67682
68246
  isBinaryOnPath: () => isBinaryOnPath
67683
68247
  });
67684
- import { existsSync as existsSync54 } from "node:fs";
68248
+ import { existsSync as existsSync56 } from "node:fs";
67685
68249
  import path86 from "node:path";
67686
68250
  function detectLocalBin(bin) {
67687
68251
  return (cwd) => {
@@ -67698,11 +68262,11 @@ function isBinaryOnPath(bin, opts = {}) {
67698
68262
  return false;
67699
68263
  }
67700
68264
  const platform = opts.platform ?? process.platform;
67701
- const exists = opts.exists ?? existsSync54;
68265
+ const exists = opts.exists ?? existsSync56;
67702
68266
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
67703
68267
  const pathMod = platform === "win32" ? path86.win32 : path86.posix;
67704
- const sep4 = platform === "win32" ? ";" : ":";
67705
- const dirs = pathEnv.split(sep4).filter((d) => d.length > 0);
68268
+ const sep5 = platform === "win32" ? ";" : ":";
68269
+ const dirs = pathEnv.split(sep5).filter((d) => d.length > 0);
67706
68270
  const candidates = [bin];
67707
68271
  if (platform === "win32") {
67708
68272
  const pathExt = opts.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
@@ -68442,8 +69006,8 @@ __export(atMentions_exports, {
68442
69006
  extractAtMentions: () => extractAtMentions,
68443
69007
  hasAtMentions: () => hasAtMentions
68444
69008
  });
68445
- import { existsSync as existsSync57, readFileSync as readFileSync42, statSync as statSync9 } from "node:fs";
68446
- import { basename as basename5, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve6, sep as sep2 } from "node:path";
69009
+ import { existsSync as existsSync59, readFileSync as readFileSync43, statSync as statSync9 } from "node:fs";
69010
+ import { basename as basename5, isAbsolute as isAbsolute5, relative as relative5, resolve as resolve7, sep as sep3 } from "node:path";
68447
69011
  function isImagePath(abs) {
68448
69012
  const ext = abs.split(".").pop()?.toLowerCase() ?? "";
68449
69013
  return ext in IMAGE_MIME_BY_EXT;
@@ -68457,9 +69021,9 @@ function isProbablyText(name, head) {
68457
69021
  return !head.includes("\0") && /[\x09\x0a\x0d\x20-\x7e]/.test(head.slice(0, 200));
68458
69022
  }
68459
69023
  function underRoot(abs, root) {
68460
- const a = resolve6(abs);
68461
- const r = resolve6(root);
68462
- const prefix = r.endsWith(sep2) ? r : r + sep2;
69024
+ const a = resolve7(abs);
69025
+ const r = resolve7(root);
69026
+ const prefix = r.endsWith(sep3) ? r : r + sep3;
68463
69027
  return a === r || a.startsWith(prefix);
68464
69028
  }
68465
69029
  function extractAtMentions(text) {
@@ -68486,7 +69050,7 @@ function extractAtMentions(text) {
68486
69050
  return out;
68487
69051
  }
68488
69052
  function resolveMention(token, cwd) {
68489
- const abs = isAbsolute4(token) ? resolve6(token) : resolve6(cwd, token);
69053
+ const abs = isAbsolute5(token) ? resolve7(token) : resolve7(cwd, token);
68490
69054
  if (!underRoot(abs, cwd) && !isImagePath(abs)) {
68491
69055
  return {
68492
69056
  raw: token,
@@ -68496,7 +69060,7 @@ function resolveMention(token, cwd) {
68496
69060
  note: "outside project root \u2014 skipped"
68497
69061
  };
68498
69062
  }
68499
- if (!existsSync57(abs)) {
69063
+ if (!existsSync59(abs)) {
68500
69064
  return {
68501
69065
  raw: token,
68502
69066
  path: token,
@@ -68548,7 +69112,7 @@ function resolveMention(token, cwd) {
68548
69112
  note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
68549
69113
  };
68550
69114
  }
68551
- const dataBase64 = readFileSync42(abs).toString("base64");
69115
+ const dataBase64 = readFileSync43(abs).toString("base64");
68552
69116
  return {
68553
69117
  raw: token,
68554
69118
  path: rel2,
@@ -68559,7 +69123,7 @@ function resolveMention(token, cwd) {
68559
69123
  };
68560
69124
  }
68561
69125
  try {
68562
- const buf = readFileSync42(abs);
69126
+ const buf = readFileSync43(abs);
68563
69127
  const head = buf.subarray(0, 800).toString("utf8");
68564
69128
  if (!isProbablyText(abs, head)) {
68565
69129
  return {
@@ -69458,7 +70022,7 @@ var init_liveTurnAbort = __esm({
69458
70022
  });
69459
70023
 
69460
70024
  // src/cli/headless/policyGate.ts
69461
- import { isAbsolute as isAbsolute5, resolve as resolve7 } from "node:path";
70025
+ import { isAbsolute as isAbsolute6, resolve as resolve8 } from "node:path";
69462
70026
  import { randomUUID as randomUUID9 } from "node:crypto";
69463
70027
  function checkStrictPolicyLoad(root, opts) {
69464
70028
  if (isPolicyEngineDisabled()) return { blocked: false, warnings: [] };
@@ -69467,7 +70031,7 @@ function checkStrictPolicyLoad(root, opts) {
69467
70031
  return { blocked: false, warnings: set2.warnings };
69468
70032
  } catch (err) {
69469
70033
  if (!(err instanceof PolicyLoadError)) throw err;
69470
- const file2 = isAbsolute5(err.file) ? err.file : resolve7(root, err.file);
70034
+ const file2 = isAbsolute6(err.file) ? err.file : resolve8(root, err.file);
69471
70035
  return {
69472
70036
  blocked: true,
69473
70037
  warnings: [],
@@ -69720,27 +70284,27 @@ var init_sandboxedFs = __esm({
69720
70284
  });
69721
70285
 
69722
70286
  // src/cli/extensions/loader.ts
69723
- import { join as join45 } from "node:path";
69724
- import { readdirSync as readdirSync11, readFileSync as readFileSync43 } from "node:fs";
70287
+ import { join as join46 } from "node:path";
70288
+ import { readdirSync as readdirSync12, readFileSync as readFileSync44 } from "node:fs";
69725
70289
  import { createHash as createHash25 } from "node:crypto";
69726
70290
  import { pathToFileURL as pathToFileURL3 } from "node:url";
69727
70291
  function globalExtensionsDir() {
69728
- return join45(zelariHome(), "extensions");
70292
+ return join46(zelariHome(), "extensions");
69729
70293
  }
69730
70294
  function projectExtensionsDir(projectRoot) {
69731
- return join45(projectRoot, ".zelari", "extensions");
70295
+ return join46(projectRoot, ".zelari", "extensions");
69732
70296
  }
69733
70297
  function sha256File(file2) {
69734
- return createHash25("sha256").update(readFileSync43(file2)).digest("hex");
70298
+ return createHash25("sha256").update(readFileSync44(file2)).digest("hex");
69735
70299
  }
69736
70300
  function candidateFiles(dir) {
69737
70301
  let names;
69738
70302
  try {
69739
- names = readdirSync11(dir);
70303
+ names = readdirSync12(dir);
69740
70304
  } catch {
69741
70305
  return [];
69742
70306
  }
69743
- return names.filter((n) => EXTENSION_FILE_EXTS.has(n.slice(n.lastIndexOf(".")).toLowerCase())).sort().map((n) => join45(dir, n));
70307
+ return names.filter((n) => EXTENSION_FILE_EXTS.has(n.slice(n.lastIndexOf(".")).toLowerCase())).sort().map((n) => join46(dir, n));
69744
70308
  }
69745
70309
  function extractExtension(mod) {
69746
70310
  const m = mod;
@@ -69769,7 +70333,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
69769
70333
  if (files.length === 0) continue;
69770
70334
  let lock = null;
69771
70335
  try {
69772
- lock = JSON.parse(readFileSync43(join45(dir.path, EXTENSIONS_LOCK_FILE), "utf8"));
70336
+ lock = JSON.parse(readFileSync44(join46(dir.path, EXTENSIONS_LOCK_FILE), "utf8"));
69773
70337
  } catch {
69774
70338
  lock = null;
69775
70339
  if (dirHasLockFile(dir.path)) {
@@ -69836,7 +70400,7 @@ function basenameOf(file2) {
69836
70400
  }
69837
70401
  function dirHasLockFile(dir) {
69838
70402
  try {
69839
- readFileSync43(join45(dir, EXTENSIONS_LOCK_FILE));
70403
+ readFileSync44(join46(dir, EXTENSIONS_LOCK_FILE));
69840
70404
  return true;
69841
70405
  } catch {
69842
70406
  return false;
@@ -72692,7 +73256,10 @@ ${ragContext}` : slicePrompt;
72692
73256
  if (opts.output === "json") {
72693
73257
  emitEvent({ type: "verification_run", ...missionVerificationPayload });
72694
73258
  }
72695
- await writeProofSafe(missionGate, { surface: "mission", sessionId: spine.sessionId }, projectRoot);
73259
+ surfaceOpsKnowledgeNotices(
73260
+ await writeProofSafe(missionGate, { surface: "mission", sessionId: spine.sessionId }, projectRoot),
73261
+ opts
73262
+ );
72696
73263
  if (missionGate.blocked) {
72697
73264
  exitCode = strictGateExitCode(missionGate);
72698
73265
  spine.missionPhase("verification", "mission-strict-blocked");
@@ -72789,6 +73356,11 @@ var init_runHeadless = __esm({
72789
73356
  ["krakenExploreModel", "ZELARI_KRAKEN_EXPLORE_MODEL"],
72790
73357
  ["krakenGeneralModel", "ZELARI_KRAKEN_GENERAL_MODEL"],
72791
73358
  ["krakenVerifyModel", "ZELARI_KRAKEN_VERIFY_MODEL"],
73359
+ // Per-tentacle thinking effort (ADR-0017) — same per-kind mapping as the
73360
+ // models above; the spawn factory reads them to override thinkingByProvider.
73361
+ ["krakenExploreThinking", "ZELARI_KRAKEN_EXPLORE_THINKING"],
73362
+ ["krakenGeneralThinking", "ZELARI_KRAKEN_GENERAL_THINKING"],
73363
+ ["krakenVerifyThinking", "ZELARI_KRAKEN_VERIFY_THINKING"],
72792
73364
  ["krakenPlannerModel", "ZELARI_KRAKEN_PLANNER_MODEL"],
72793
73365
  ["krakenDelegation", "ZELARI_KRAKEN_DELEGATION"]
72794
73366
  ];
@@ -73179,270 +73751,6 @@ var init_desktopConfig = __esm({
73179
73751
  }
73180
73752
  });
73181
73753
 
73182
- // src/cli/skillCategories.ts
73183
- var CODING_CATEGORIES_LIST, CODING_CATEGORIES2;
73184
- var init_skillCategories = __esm({
73185
- "src/cli/skillCategories.ts"() {
73186
- "use strict";
73187
- CODING_CATEGORIES_LIST = [
73188
- "plan",
73189
- "refactor",
73190
- "debug",
73191
- "review",
73192
- "test",
73193
- "docs",
73194
- "ops",
73195
- "git",
73196
- "db",
73197
- "maint"
73198
- ];
73199
- CODING_CATEGORIES2 = new Set(CODING_CATEGORIES_LIST);
73200
- }
73201
- });
73202
-
73203
- // src/cli/skillConfigIo.ts
73204
- import {
73205
- existsSync as existsSync59,
73206
- mkdirSync as mkdirSync23,
73207
- readdirSync as readdirSync12,
73208
- readFileSync as readFileSync44,
73209
- rmSync as rmSync4,
73210
- writeFileSync as writeFileSync25
73211
- } from "node:fs";
73212
- import { dirname as dirname14, join as join47 } from "node:path";
73213
- function ensureBuiltinSkillsLoadedSync() {
73214
- if (builtinsLoaded) return;
73215
- for (const spec of BUILTIN_SKILL_MODULES) {
73216
- try {
73217
- require?.(spec);
73218
- } catch {
73219
- }
73220
- }
73221
- }
73222
- function getUserSkillsDir() {
73223
- return join47(zelariHome(), "skills");
73224
- }
73225
- function getProjectSkillsDir(projectRoot) {
73226
- return join47(projectRoot, ".zelari", "skills");
73227
- }
73228
- function skillFilePath(dir, name) {
73229
- return join47(dir, name, "SKILL.md");
73230
- }
73231
- function classifyScope(skillPath, projectRoot) {
73232
- const userDir = getUserSkillsDir().replace(/\\/g, "/");
73233
- const norm = skillPath.replace(/\\/g, "/");
73234
- if (norm.startsWith(userDir + "/") || norm === userDir) {
73235
- return { scope: "user", writable: true };
73236
- }
73237
- if (projectRoot) {
73238
- const projDir = getProjectSkillsDir(projectRoot).replace(/\\/g, "/");
73239
- if (norm.startsWith(projDir + "/") || norm === projDir) {
73240
- return { scope: "project", writable: true };
73241
- }
73242
- }
73243
- return { scope: "compat", writable: false };
73244
- }
73245
- function entryFromParsed(parsed, projectRoot) {
73246
- const { scope, writable } = classifyScope(parsed.sourcePath, projectRoot);
73247
- return {
73248
- id: parsed.name,
73249
- name: parsed.name,
73250
- description: parsed.description,
73251
- category: parsed.category,
73252
- estimatedCost: parsed.estimatedCost,
73253
- requiredTools: parsed.requiredTools,
73254
- scope,
73255
- path: parsed.sourcePath,
73256
- body: parsed.body,
73257
- builtin: false,
73258
- writable
73259
- };
73260
- }
73261
- function entryFromBuiltin(skill) {
73262
- return {
73263
- id: skill.id,
73264
- name: skill.name || skill.id,
73265
- description: skill.description || "",
73266
- category: skill.category,
73267
- estimatedCost: skill.estimatedCost,
73268
- requiredTools: skill.requiredTools,
73269
- scope: "builtin",
73270
- path: null,
73271
- // Expose body so Desktop skill picker can expand like `/skill <id>`.
73272
- body: skill.systemPromptFragment || void 0,
73273
- builtin: true,
73274
- writable: false
73275
- };
73276
- }
73277
- function scanSkillsDir(dir, projectRoot, seen, out) {
73278
- if (!existsSync59(dir)) return;
73279
- let entries;
73280
- try {
73281
- entries = readdirSync12(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
73282
- } catch {
73283
- return;
73284
- }
73285
- for (const entry of entries) {
73286
- const skillPath = skillFilePath(dir, entry);
73287
- if (!existsSync59(skillPath)) continue;
73288
- try {
73289
- const parsed = parseSkillMd(readFileSync44(skillPath, "utf8"), skillPath);
73290
- if (!parsed) continue;
73291
- if (seen.has(parsed.name)) continue;
73292
- seen.add(parsed.name);
73293
- out.push(entryFromParsed(parsed, projectRoot));
73294
- } catch {
73295
- }
73296
- }
73297
- }
73298
- function listSkillsSnapshot(projectRoot) {
73299
- const root = projectRoot && projectRoot.trim() ? projectRoot.trim() : null;
73300
- const userSkillsDir = getUserSkillsDir();
73301
- const projectSkillsDir = root ? getProjectSkillsDir(root) : null;
73302
- const skills = [];
73303
- const seen = /* @__PURE__ */ new Set();
73304
- if (root) {
73305
- scanSkillsDir(join47(root, ".zelari", "skills"), root, seen, skills);
73306
- scanSkillsDir(join47(root, ".claude", "skills"), root, seen, skills);
73307
- scanSkillsDir(join47(root, ".opencode", "skills"), root, seen, skills);
73308
- }
73309
- scanSkillsDir(userSkillsDir, root, seen, skills);
73310
- for (const s of listCodingSkills()) {
73311
- if (seen.has(s.id)) continue;
73312
- seen.add(s.id);
73313
- if (s.builtin) {
73314
- skills.push(entryFromBuiltin(s));
73315
- }
73316
- }
73317
- skills.sort((a, b) => {
73318
- const order = (s) => s === "project" ? 0 : s === "user" ? 1 : s === "compat" ? 2 : 3;
73319
- const d = order(a.scope) - order(b.scope);
73320
- if (d !== 0) return d;
73321
- return a.id.localeCompare(b.id);
73322
- });
73323
- return { userSkillsDir, projectSkillsDir, skills };
73324
- }
73325
- function serializeSkillMd(opts) {
73326
- const name = opts.name.trim().toLowerCase();
73327
- const description = opts.description.trim();
73328
- const body = opts.body.trim();
73329
- const lines = ["---", `name: ${name}`, `description: ${description}`];
73330
- const cat = (opts.category ?? "").trim().toLowerCase();
73331
- if (cat && CODING_CATEGORIES2.has(cat)) {
73332
- lines.push(`category: ${cat}`);
73333
- }
73334
- if (opts.tools && opts.tools.length > 0) {
73335
- lines.push(`tools: [${opts.tools.map((t) => t.trim()).filter(Boolean).join(", ")}]`);
73336
- }
73337
- const cost = (opts.cost ?? "").trim().toLowerCase();
73338
- if (cost === "low" || cost === "medium" || cost === "high") {
73339
- lines.push(`cost: ${cost}`);
73340
- }
73341
- lines.push("---", "", body, "");
73342
- return lines.join("\n");
73343
- }
73344
- function upsertSkill(opts) {
73345
- const name = opts.name.trim().toLowerCase();
73346
- if (!name || !NAME_RE.test(name)) {
73347
- return {
73348
- ok: false,
73349
- error: "Invalid skill name (use lowercase letters, digits, hyphens; max 64 chars)"
73350
- };
73351
- }
73352
- const description = opts.description.trim();
73353
- if (!description) {
73354
- return { ok: false, error: "description is required" };
73355
- }
73356
- const body = opts.body.trim();
73357
- if (!body) {
73358
- return { ok: false, error: "body is required" };
73359
- }
73360
- let dir;
73361
- if (opts.scope === "user") {
73362
- dir = getUserSkillsDir();
73363
- } else {
73364
- const root = opts.projectRoot?.trim();
73365
- if (!root) {
73366
- return {
73367
- ok: false,
73368
- error: "projectRoot required for project scope (Open Folder first)"
73369
- };
73370
- }
73371
- dir = getProjectSkillsDir(root);
73372
- }
73373
- const path103 = skillFilePath(dir, name);
73374
- const content = serializeSkillMd({
73375
- name,
73376
- description,
73377
- body,
73378
- category: opts.category,
73379
- tools: opts.tools,
73380
- cost: opts.cost
73381
- });
73382
- const parsed = parseSkillMd(content, path103);
73383
- if (!parsed) {
73384
- return { ok: false, error: "Generated SKILL.md failed validation" };
73385
- }
73386
- mkdirSync23(dirname14(path103), { recursive: true });
73387
- writeFileSync25(path103, content, "utf8");
73388
- return { ok: true, path: path103 };
73389
- }
73390
- function removeSkill(opts) {
73391
- const name = opts.name.trim().toLowerCase();
73392
- if (!name) {
73393
- return { ok: false, error: "name is required" };
73394
- }
73395
- let dir;
73396
- if (opts.scope === "user") {
73397
- dir = getUserSkillsDir();
73398
- } else {
73399
- const root = opts.projectRoot?.trim();
73400
- if (!root) {
73401
- return { ok: false, error: "projectRoot required for project scope" };
73402
- }
73403
- dir = getProjectSkillsDir(root);
73404
- }
73405
- const skillDir = join47(dir, name);
73406
- const path103 = skillFilePath(dir, name);
73407
- if (!existsSync59(path103) && !existsSync59(skillDir)) {
73408
- return { ok: false, error: `Skill "${name}" not found in ${dir}` };
73409
- }
73410
- try {
73411
- rmSync4(skillDir, { recursive: true, force: true });
73412
- } catch (err) {
73413
- return {
73414
- ok: false,
73415
- error: err instanceof Error ? err.message : String(err)
73416
- };
73417
- }
73418
- return { ok: true, path: path103 };
73419
- }
73420
- var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
73421
- var init_skillConfigIo = __esm({
73422
- "src/cli/skillConfigIo.ts"() {
73423
- "use strict";
73424
- init_skills2();
73425
- init_skillsMd();
73426
- init_skillCategories();
73427
- init_paths();
73428
- NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
73429
- BUILTIN_SKILL_MODULES = [
73430
- "@zelari/core/skills/builtin/debugging",
73431
- "@zelari/core/skills/builtin/docs",
73432
- "@zelari/core/skills/builtin/git-ops",
73433
- "@zelari/core/skills/builtin/planning",
73434
- "@zelari/core/skills/builtin/refactoring",
73435
- "@zelari/core/skills/builtin/review",
73436
- "@zelari/core/skills/builtin/unrealEditor",
73437
- "@zelari/core/skills/builtin/testing",
73438
- "@zelari/core/skills/builtin/schema-loop",
73439
- "@zelari/core/skills/builtin/computer-use-cua",
73440
- "@zelari/core/skills/builtin/qwen-mm-plugins"
73441
- ];
73442
- builtinsLoaded = false;
73443
- }
73444
- });
73445
-
73446
73754
  // src/cli/memory/jsonApi.ts
73447
73755
  var jsonApi_exports = {};
73448
73756
  __export(jsonApi_exports, {
@@ -73809,8 +74117,8 @@ async function startMemoryMcpServer(options) {
73809
74117
  let stopped = false;
73810
74118
  let finalizePromise;
73811
74119
  let resolveClosed = () => void 0;
73812
- const closed = new Promise((resolve9) => {
73813
- resolveClosed = resolve9;
74120
+ const closed = new Promise((resolve10) => {
74121
+ resolveClosed = resolve10;
73814
74122
  });
73815
74123
  const adapter = new MemoryMcpAdapter({
73816
74124
  service,
@@ -73957,8 +74265,8 @@ function startPermissionMcpServer(opts) {
73957
74265
  });
73958
74266
  let closedResolve = () => {
73959
74267
  };
73960
- const closed = new Promise((resolve9) => {
73961
- closedResolve = resolve9;
74268
+ const closed = new Promise((resolve10) => {
74269
+ closedResolve = resolve10;
73962
74270
  });
73963
74271
  let stopped = false;
73964
74272
  const send = (obj) => {
@@ -74211,7 +74519,7 @@ var init_permissionCli = __esm({
74211
74519
 
74212
74520
  // src/cli/companion/config.ts
74213
74521
  import {
74214
- existsSync as existsSync60,
74522
+ existsSync as existsSync61,
74215
74523
  mkdirSync as mkdirSync24,
74216
74524
  readFileSync as readFileSync45,
74217
74525
  writeFileSync as writeFileSync26
@@ -74229,17 +74537,17 @@ function getCompanionTokenPath() {
74229
74537
  }
74230
74538
  function ensureHome() {
74231
74539
  const home = getZelariHome();
74232
- if (!existsSync60(home)) {
74540
+ if (!existsSync61(home)) {
74233
74541
  mkdirSync24(home, { recursive: true });
74234
74542
  }
74235
74543
  }
74236
74544
  function loadCompanionConfig() {
74237
- const path103 = getCompanionConfigPath();
74238
- if (!existsSync60(path103)) {
74545
+ const path104 = getCompanionConfigPath();
74546
+ if (!existsSync61(path104)) {
74239
74547
  return { projects: [] };
74240
74548
  }
74241
74549
  try {
74242
- const raw = JSON.parse(readFileSync45(path103, "utf8"));
74550
+ const raw = JSON.parse(readFileSync45(path104, "utf8"));
74243
74551
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
74244
74552
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
74245
74553
  ).map((p3) => ({
@@ -74277,16 +74585,16 @@ function loadOrCreateToken(explicit) {
74277
74585
  return { token: explicit.trim(), created: false };
74278
74586
  }
74279
74587
  ensureHome();
74280
- const path103 = getCompanionTokenPath();
74281
- if (existsSync60(path103)) {
74282
- const t = readFileSync45(path103, "utf8").trim();
74588
+ const path104 = getCompanionTokenPath();
74589
+ if (existsSync61(path104)) {
74590
+ const t = readFileSync45(path104, "utf8").trim();
74283
74591
  if (t) return { token: t, created: false };
74284
74592
  }
74285
74593
  const token = randomBytes7(24).toString("base64url");
74286
- writeFileSync26(path103, token + "\n", "utf8");
74594
+ writeFileSync26(path104, token + "\n", "utf8");
74287
74595
  try {
74288
74596
  const fs50 = __require("node:fs");
74289
- fs50.chmodSync?.(path103, 384);
74597
+ fs50.chmodSync?.(path104, 384);
74290
74598
  } catch {
74291
74599
  }
74292
74600
  return { token, created: true };
@@ -74311,17 +74619,17 @@ function mergeProjects(cfg, extraPaths) {
74311
74619
  byId.set(p3.id, p3);
74312
74620
  }
74313
74621
  for (const raw of extraPaths) {
74314
- const path103 = raw.trim();
74315
- if (!path103) continue;
74316
- let id3 = slugFromPath(path103);
74622
+ const path104 = raw.trim();
74623
+ if (!path104) continue;
74624
+ let id3 = slugFromPath(path104);
74317
74625
  let n = 2;
74318
- while (byId.has(id3) && byId.get(id3).path !== path103) {
74319
- id3 = `${slugFromPath(path103)}-${n++}`;
74626
+ while (byId.has(id3) && byId.get(id3).path !== path104) {
74627
+ id3 = `${slugFromPath(path104)}-${n++}`;
74320
74628
  }
74321
74629
  byId.set(id3, {
74322
74630
  id: id3,
74323
- name: slugFromPath(path103),
74324
- path: path103
74631
+ name: slugFromPath(path104),
74632
+ path: path104
74325
74633
  });
74326
74634
  }
74327
74635
  return [...byId.values()];
@@ -74407,11 +74715,11 @@ function createServePermissionBridge(write, timeoutMs2 = 12e4) {
74407
74715
  onPermissionAsk(payload) {
74408
74716
  const requestId = `perm-${Date.now()}-${++seq}`;
74409
74717
  const categories = payload.categories && payload.categories.length > 0 ? payload.categories : payload.category ? payload.category.split(",").map((c) => c.trim()).filter(Boolean) : [];
74410
- return new Promise((resolve9) => {
74718
+ return new Promise((resolve10) => {
74411
74719
  const timer = setTimeout(() => {
74412
74720
  settle(requestId, "deny", true);
74413
74721
  }, timeoutMs2);
74414
- pending.set(requestId, { resolve: resolve9, timer, payload });
74722
+ pending.set(requestId, { resolve: resolve10, timer, payload });
74415
74723
  write(
74416
74724
  JSON.stringify({
74417
74725
  type: "permission.request",
@@ -74521,11 +74829,11 @@ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
74521
74829
  const choices = req.choices.map((c) => c.trim()).filter(Boolean);
74522
74830
  if (choices.length < 2) return Promise.resolve(null);
74523
74831
  const requestId = `ask-${Date.now()}-${++seq}`;
74524
- return new Promise((resolve9) => {
74832
+ return new Promise((resolve10) => {
74525
74833
  const timer = setTimeout(() => {
74526
74834
  settle(requestId, null, true);
74527
74835
  }, timeoutMs2);
74528
- pending.set(requestId, { resolve: resolve9, timer });
74836
+ pending.set(requestId, { resolve: resolve10, timer });
74529
74837
  write(
74530
74838
  JSON.stringify({
74531
74839
  type: "ask_user.request",
@@ -74625,11 +74933,13 @@ __export(harnessServer_exports, {
74625
74933
  bindHarnessTurnOptions: () => bindHarnessTurnOptions,
74626
74934
  createCliRunTurn: () => createCliRunTurn,
74627
74935
  createCliWorkspaceServices: () => createCliWorkspaceServices,
74936
+ resolveSessionCreateRoot: () => resolveSessionCreateRoot,
74628
74937
  resolveTurnLspProvider: () => resolveTurnLspProvider,
74629
74938
  runHarnessServer: () => runHarnessServer,
74630
74939
  startHarnessServer: () => startHarnessServer
74631
74940
  });
74632
74941
  import { createInterface as createInterface3 } from "node:readline";
74942
+ import path99 from "node:path";
74633
74943
  import { randomUUID as randomUUID12 } from "node:crypto";
74634
74944
  function createCliWorkspaceServices(workspaceRoot) {
74635
74945
  const mode = activePolicyLoadMode();
@@ -74655,6 +74965,23 @@ function createCliWorkspaceServices(workspaceRoot) {
74655
74965
  }
74656
74966
  };
74657
74967
  }
74968
+ function resolveSessionCreateRoot(params, cwd = process.cwd()) {
74969
+ const raw = params.workspaceRoot;
74970
+ if (typeof raw !== "string" || raw.trim() === "") {
74971
+ return {
74972
+ ok: false,
74973
+ message: "session.create requires a non-empty string `workspaceRoot` (the sidecar never substitutes its own cwd: sessions on different workspaces must stay isolated)"
74974
+ };
74975
+ }
74976
+ const root = raw.trim();
74977
+ if (!path99.isAbsolute(root)) {
74978
+ return {
74979
+ ok: false,
74980
+ message: `session.create workspaceRoot must be an absolute path (got '${root}') \u2014 a relative root would resolve against the sidecar cwd (${cwd}) and mix conversations (ADR-0016)`
74981
+ };
74982
+ }
74983
+ return { ok: true, root };
74984
+ }
74658
74985
  function bindHarnessTurnOptions(input, workspaceRoot) {
74659
74986
  const turnInput = input;
74660
74987
  if (typeof turnInput.task !== "string" || turnInput.task.length === 0) {
@@ -74760,9 +75087,12 @@ function startHarnessServer(options = {}) {
74760
75087
  };
74761
75088
  }
74762
75089
  case "session.create": {
74763
- const root = typeof params.workspaceRoot === "string" ? params.workspaceRoot : process.cwd();
75090
+ const resolved = resolveSessionCreateRoot(params);
75091
+ if (!resolved.ok) {
75092
+ return { id: req.id ?? null, ok: false, error: { code: "bad_request", message: resolved.message } };
75093
+ }
74764
75094
  const session = server.createSession({
74765
- workspaceRoot: root,
75095
+ workspaceRoot: resolved.root,
74766
75096
  runTurn: options.runTurn ?? createCliRunTurn(
74767
75097
  asRegistryAskHandler(permissionBridge),
74768
75098
  askUserBridge.onAskUser
@@ -74984,8 +75314,8 @@ var init_harnessClient = __esm({
74984
75314
  );
74985
75315
  }
74986
75316
  const id3 = this.nextId++;
74987
- return new Promise((resolve9, reject) => {
74988
- this.pending.set(id3, { resolve: resolve9, reject });
75317
+ return new Promise((resolve10, reject) => {
75318
+ this.pending.set(id3, { resolve: resolve10, reject });
74989
75319
  this.transport.write(JSON.stringify({ id: id3, method, params }));
74990
75320
  });
74991
75321
  }
@@ -75480,8 +75810,8 @@ __export(serve_exports, {
75480
75810
  runCompanionServe: () => runCompanionServe
75481
75811
  });
75482
75812
  import { createServer as createServer3 } from "node:http";
75483
- import { existsSync as existsSync61 } from "node:fs";
75484
- import { resolve as resolve8 } from "node:path";
75813
+ import { existsSync as existsSync62 } from "node:fs";
75814
+ import { resolve as resolve9 } from "node:path";
75485
75815
  function readBody(req, max = 2e6) {
75486
75816
  return new Promise((resolveBody, reject) => {
75487
75817
  const chunks = [];
@@ -75528,8 +75858,8 @@ async function runCompanionServe(opts = {}) {
75528
75858
  const { token, created } = loadOrCreateToken(opts.token);
75529
75859
  let projects = mergeProjects(fileCfg, opts.projects ?? []);
75530
75860
  projects = projects.filter((p3) => {
75531
- const abs = resolve8(p3.path);
75532
- if (!existsSync61(abs)) {
75861
+ const abs = resolve9(p3.path);
75862
+ if (!existsSync62(abs)) {
75533
75863
  process.stderr.write(
75534
75864
  `[zelari-code serve] skip missing project path: ${p3.path}
75535
75865
  `
@@ -75548,7 +75878,7 @@ async function runCompanionServe(opts = {}) {
75548
75878
  });
75549
75879
  }
75550
75880
  if (projects.length === 0) {
75551
- const cwd = resolve8(process.cwd());
75881
+ const cwd = resolve9(process.cwd());
75552
75882
  projects = [
75553
75883
  {
75554
75884
  id: "default",
@@ -75580,9 +75910,9 @@ async function runCompanionServe(opts = {}) {
75580
75910
  return;
75581
75911
  }
75582
75912
  const url2 = parseUrl(req);
75583
- const path103 = url2.pathname.replace(/\/+$/, "") || "/";
75913
+ const path104 = url2.pathname.replace(/\/+$/, "") || "/";
75584
75914
  try {
75585
- if (req.method === "GET" && (path103 === "/health" || path103 === "/v1/health")) {
75915
+ if (req.method === "GET" && (path104 === "/health" || path104 === "/v1/health")) {
75586
75916
  sendJson2(res, 200, {
75587
75917
  ok: true,
75588
75918
  service: "zelari-companion",
@@ -75594,18 +75924,18 @@ async function runCompanionServe(opts = {}) {
75594
75924
  });
75595
75925
  return;
75596
75926
  }
75597
- if (path103.startsWith("/v1")) {
75927
+ if (path104.startsWith("/v1")) {
75598
75928
  if (!tokenMatches(token, getBearer(req))) {
75599
75929
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
75600
75930
  return;
75601
75931
  }
75602
75932
  }
75603
- if (req.method === "GET" && path103 === "/v1/config") {
75933
+ if (req.method === "GET" && path104 === "/v1/config") {
75604
75934
  const snap = buildDesktopConfigSnapshot();
75605
75935
  sendJson2(res, 200, { ok: true, ...snap });
75606
75936
  return;
75607
75937
  }
75608
- if (req.method === "GET" && path103 === "/v1/projects") {
75938
+ if (req.method === "GET" && path104 === "/v1/projects") {
75609
75939
  sendJson2(res, 200, {
75610
75940
  ok: true,
75611
75941
  projects: projects.map((p3) => ({
@@ -75616,7 +75946,7 @@ async function runCompanionServe(opts = {}) {
75616
75946
  });
75617
75947
  return;
75618
75948
  }
75619
- if (req.method === "GET" && path103 === "/v1/runs") {
75949
+ if (req.method === "GET" && path104 === "/v1/runs") {
75620
75950
  sendJson2(res, 200, {
75621
75951
  ok: true,
75622
75952
  active: runs.getActive(),
@@ -75634,7 +75964,7 @@ async function runCompanionServe(opts = {}) {
75634
75964
  });
75635
75965
  return;
75636
75966
  }
75637
- if (req.method === "POST" && path103 === "/v1/runs") {
75967
+ if (req.method === "POST" && path104 === "/v1/runs") {
75638
75968
  const raw = await readBody(req);
75639
75969
  let body = {};
75640
75970
  try {
@@ -75682,7 +76012,7 @@ async function runCompanionServe(opts = {}) {
75682
76012
  });
75683
76013
  return;
75684
76014
  }
75685
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path103);
76015
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path104);
75686
76016
  if (req.method === "GET" && eventsMatch) {
75687
76017
  const runId = eventsMatch[1];
75688
76018
  const run = runs.getRun(runId);
@@ -75747,7 +76077,7 @@ async function runCompanionServe(opts = {}) {
75747
76077
  }, 500);
75748
76078
  return;
75749
76079
  }
75750
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path103);
76080
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path104);
75751
76081
  if (req.method === "POST" && cancelMatch) {
75752
76082
  const runId = cancelMatch[1];
75753
76083
  const result = runs.cancel(runId);
@@ -75758,7 +76088,7 @@ async function runCompanionServe(opts = {}) {
75758
76088
  sendJson2(res, 200, { ok: true, cancelled: runId });
75759
76089
  return;
75760
76090
  }
75761
- const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path103);
76091
+ const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path104);
75762
76092
  if (req.method === "POST" && steerMatch) {
75763
76093
  const runId = steerMatch[1];
75764
76094
  const raw = await readBody(req);
@@ -75924,26 +76254,26 @@ __export(doctor_exports, {
75924
76254
  runDoctor: () => runDoctor
75925
76255
  });
75926
76256
  import { execSync as execSync2 } from "node:child_process";
75927
- import { existsSync as existsSync62, readFileSync as readFileSync46, readlinkSync, statSync as statSync10 } from "node:fs";
76257
+ import { existsSync as existsSync63, readFileSync as readFileSync46, readlinkSync, statSync as statSync10 } from "node:fs";
75928
76258
  import { createRequire as createRequire3 } from "node:module";
75929
76259
  import { fileURLToPath as fileURLToPath3 } from "node:url";
75930
- import path99 from "node:path";
76260
+ import path100 from "node:path";
75931
76261
  function findPackageRoot(start) {
75932
76262
  let dir = start;
75933
76263
  for (let i = 0; i < 6; i += 1) {
75934
- const candidate = path99.join(dir, "package.json");
75935
- if (existsSync62(candidate)) {
76264
+ const candidate = path100.join(dir, "package.json");
76265
+ if (existsSync63(candidate)) {
75936
76266
  try {
75937
76267
  const pkg = JSON.parse(readFileSync46(candidate, "utf8"));
75938
76268
  if (pkg.name === "zelari-code") return dir;
75939
76269
  } catch {
75940
76270
  }
75941
76271
  }
75942
- const parent = path99.dirname(dir);
76272
+ const parent = path100.dirname(dir);
75943
76273
  if (parent === dir) break;
75944
76274
  dir = parent;
75945
76275
  }
75946
- return path99.resolve(__dirname3, "..", "..", "..");
76276
+ return path100.resolve(__dirname3, "..", "..", "..");
75947
76277
  }
75948
76278
  function tryExec(cmd) {
75949
76279
  try {
@@ -75957,7 +76287,7 @@ function tryExec(cmd) {
75957
76287
  }
75958
76288
  function readPackageJson4() {
75959
76289
  try {
75960
- const pkgPath = path99.join(packageRoot, "package.json");
76290
+ const pkgPath = path100.join(packageRoot, "package.json");
75961
76291
  return JSON.parse(readFileSync46(pkgPath, "utf8"));
75962
76292
  } catch {
75963
76293
  return null;
@@ -75969,7 +76299,7 @@ function getGlobalPrefix() {
75969
76299
  return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim();
75970
76300
  }
75971
76301
  function isSourceCheckout() {
75972
- return existsSync62(path99.join(packageRoot, "src", "cli", "main.ts")) && existsSync62(path99.join(packageRoot, "apps", "desktop", "package.json"));
76302
+ return existsSync63(path100.join(packageRoot, "src", "cli", "main.ts")) && existsSync63(path100.join(packageRoot, "apps", "desktop", "package.json"));
75973
76303
  }
75974
76304
  function checkShim(pkgName) {
75975
76305
  const prefix = getGlobalPrefix();
@@ -75978,10 +76308,10 @@ function checkShim(pkgName) {
75978
76308
  }
75979
76309
  const isWin = process.platform === "win32";
75980
76310
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
75981
- const shimPath = path99.join(prefix, shimName);
75982
- if (!existsSync62(shimPath)) {
75983
- const localBin = path99.join(packageRoot, "bin", "zelari-code.js");
75984
- if (isSourceCheckout() && existsSync62(localBin)) {
76311
+ const shimPath = path100.join(prefix, shimName);
76312
+ if (!existsSync63(shimPath)) {
76313
+ const localBin = path100.join(packageRoot, "bin", "zelari-code.js");
76314
+ if (isSourceCheckout() && existsSync63(localBin)) {
75985
76315
  return WARN(
75986
76316
  `global shim not found at ${shimPath}
75987
76317
  source checkout \u2014 using ${localBin}
@@ -76014,8 +76344,8 @@ function checkShim(pkgName) {
76014
76344
  fix: npm install -g ${pkgName}@latest --force`
76015
76345
  );
76016
76346
  }
76017
- const resolved = path99.resolve(path99.dirname(shimPath), target);
76018
- const expected = path99.join(
76347
+ const resolved = path100.resolve(path100.dirname(shimPath), target);
76348
+ const expected = path100.join(
76019
76349
  prefix,
76020
76350
  "node_modules",
76021
76351
  pkgName,
@@ -76057,8 +76387,8 @@ function checkNode(pkg) {
76057
76387
  return OK(`node ${raw} (engines.node ${enginesNode ?? ">= 20.0.0"})`);
76058
76388
  }
76059
76389
  function checkBundle() {
76060
- const bundle = path99.join(packageRoot, "dist", "cli", "main.bundled.js");
76061
- if (!existsSync62(bundle)) {
76390
+ const bundle = path100.join(packageRoot, "dist", "cli", "main.bundled.js");
76391
+ if (!existsSync63(bundle)) {
76062
76392
  return FAIL(
76063
76393
  `dist/cli/main.bundled.js missing at ${bundle}
76064
76394
  fix: npm run build:cli (then reinstall or run via tsx)`
@@ -76078,7 +76408,7 @@ function checkRuntimeDeps() {
76078
76408
  const missing = [];
76079
76409
  for (const dep of required2) {
76080
76410
  try {
76081
- const localReq = createRequire3(path99.join(packageRoot, "package.json"));
76411
+ const localReq = createRequire3(path100.join(packageRoot, "package.json"));
76082
76412
  localReq.resolve(dep);
76083
76413
  } catch {
76084
76414
  missing.push(dep);
@@ -76355,7 +76685,7 @@ var init_doctor = __esm({
76355
76685
  init_metrics3();
76356
76686
  init_contextGrowthSummary();
76357
76687
  require3 = createRequire3(import.meta.url);
76358
- __dirname3 = path99.dirname(fileURLToPath3(import.meta.url));
76688
+ __dirname3 = path100.dirname(fileURLToPath3(import.meta.url));
76359
76689
  packageRoot = findPackageRoot(__dirname3);
76360
76690
  OK = (message) => ({
76361
76691
  ok: true,
@@ -76463,8 +76793,8 @@ __export(userSettings_exports, {
76463
76793
  resolveUserSettings: () => resolveUserSettings,
76464
76794
  settingsOverrides: () => settingsOverrides
76465
76795
  });
76466
- import { existsSync as existsSync63, readFileSync as readFileSync47 } from "node:fs";
76467
- import path100 from "node:path";
76796
+ import { existsSync as existsSync64, readFileSync as readFileSync47 } from "node:fs";
76797
+ import path101 from "node:path";
76468
76798
  function parseBool(raw) {
76469
76799
  const v = raw.trim().toLowerCase();
76470
76800
  if (["1", "true", "yes", "on"].includes(v)) return true;
@@ -76479,7 +76809,7 @@ function parseEnum(values, raw) {
76479
76809
  return values.includes(v) ? v : void 0;
76480
76810
  }
76481
76811
  function loadFileLayer(file2, origin, warnings) {
76482
- if (!existsSync63(file2)) return {};
76812
+ if (!existsSync64(file2)) return {};
76483
76813
  let raw;
76484
76814
  try {
76485
76815
  raw = readFileSync47(file2, "utf8");
@@ -76531,8 +76861,8 @@ function envValueFor(key, env) {
76531
76861
  function resolveUserSettings(opts = {}) {
76532
76862
  const cwd = opts.cwd ?? process.cwd();
76533
76863
  const env = opts.env ?? process.env;
76534
- const userPath = path100.join(zelariHome(), SETTINGS_FILE_NAME);
76535
- const projectPath = path100.join(cwd, ".zelari", SETTINGS_FILE_NAME);
76864
+ const userPath = path101.join(zelariHome(), SETTINGS_FILE_NAME);
76865
+ const projectPath = path101.join(cwd, ".zelari", SETTINGS_FILE_NAME);
76536
76866
  const warnings = [];
76537
76867
  const userLayer = loadFileLayer(userPath, "user", warnings);
76538
76868
  const projectLayer = loadFileLayer(projectPath, "project", warnings);
@@ -76590,8 +76920,8 @@ function printSettingsReport(opts = {}) {
76590
76920
  }
76591
76921
  lines.push("");
76592
76922
  lines.push("layers:");
76593
- lines.push(` user: ${r.userPath} (${existsSync63(r.userPath) ? "loaded" : "not found"})`);
76594
- lines.push(` project: ${r.projectPath} (${existsSync63(r.projectPath) ? "loaded" : "not found"})`);
76923
+ lines.push(` user: ${r.userPath} (${existsSync64(r.userPath) ? "loaded" : "not found"})`);
76924
+ lines.push(` project: ${r.projectPath} (${existsSync64(r.projectPath) ? "loaded" : "not found"})`);
76595
76925
  if (r.warnings.length > 0) {
76596
76926
  lines.push("");
76597
76927
  lines.push("warnings:");
@@ -76746,8 +77076,8 @@ __export(inspectSession_exports, {
76746
77076
  renderInspectReport: () => renderInspectReport,
76747
77077
  runInspectSession: () => runInspectSession
76748
77078
  });
76749
- import path101 from "node:path";
76750
- import { existsSync as existsSync64 } from "node:fs";
77079
+ import path102 from "node:path";
77080
+ import { existsSync as existsSync65 } from "node:fs";
76751
77081
  function formatLimit(limit) {
76752
77082
  return `${Math.round(limit / 1e3)}k`;
76753
77083
  }
@@ -76780,13 +77110,13 @@ function renderInspectReport(state3) {
76780
77110
  }
76781
77111
  async function runInspectSession(opts) {
76782
77112
  const sessionsDir2 = resolveSessionsDir({ workspaceRoot: opts.cwd ?? process.cwd() });
76783
- const sessionDir = path101.join(sessionsDir2, opts.sessionId);
76784
- const eventsPath = path101.join(sessionDir, "events.jsonl");
76785
- if (!existsSync64(sessionDir)) {
77113
+ const sessionDir = path102.join(sessionsDir2, opts.sessionId);
77114
+ const eventsPath = path102.join(sessionDir, "events.jsonl");
77115
+ if (!existsSync65(sessionDir)) {
76786
77116
  console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
76787
77117
  return 1;
76788
77118
  }
76789
- if (!existsSync64(eventsPath)) {
77119
+ if (!existsSync65(eventsPath)) {
76790
77120
  console.error(`zelari-code inspect: session directory has no events.jsonl at ${eventsPath}`);
76791
77121
  return 1;
76792
77122
  }
@@ -76815,14 +77145,14 @@ __export(inspect_exports, {
76815
77145
  collectInspectReport: () => collectInspectReport,
76816
77146
  runInspect: () => runInspect
76817
77147
  });
76818
- import path102 from "node:path";
76819
- import { existsSync as existsSync65, readFileSync as readFileSync48, readdirSync as readdirSync13 } from "node:fs";
77148
+ import path103 from "node:path";
77149
+ import { existsSync as existsSync66, readFileSync as readFileSync48, readdirSync as readdirSync13 } from "node:fs";
76820
77150
  async function collectInspectReport(cwd = process.cwd()) {
76821
77151
  ensureBuiltinSkillsLoadedSync();
76822
77152
  const snap = listSkillsSnapshot(cwd);
76823
77153
  const mcp = listMcpServers(cwd);
76824
- const userMcpPath = path102.join(zelariHome(), "mcp.json");
76825
- const projectMcpPath = path102.join(cwd, ".zelari", "mcp.json");
77154
+ const userMcpPath = path103.join(zelariHome(), "mcp.json");
77155
+ const projectMcpPath = path103.join(cwd, ".zelari", "mcp.json");
76826
77156
  const globalHooks = globalHooksDir();
76827
77157
  const projectHooks = projectHooksDir(cwd);
76828
77158
  const projectTrusted = isFolderTrusted(cwd);
@@ -76848,11 +77178,11 @@ async function collectInspectReport(cwd = process.cwd()) {
76848
77178
  folders: listTrustedFolders()
76849
77179
  },
76850
77180
  configSources: [
76851
- { path: userMcpPath, exists: existsSync65(userMcpPath) },
76852
- { path: projectMcpPath, exists: existsSync65(projectMcpPath) },
76853
- { path: path102.join(zelariHome(), "provider.json"), exists: existsSync65(path102.join(zelariHome(), "provider.json")) },
76854
- { path: path102.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync65(path102.join(cwd, ".zelari", "AGENTS.md")) },
76855
- { path: path102.join(cwd, "AGENTS.md"), exists: existsSync65(path102.join(cwd, "AGENTS.md")) }
77181
+ { path: userMcpPath, exists: existsSync66(userMcpPath) },
77182
+ { path: projectMcpPath, exists: existsSync66(projectMcpPath) },
77183
+ { path: path103.join(zelariHome(), "provider.json"), exists: existsSync66(path103.join(zelariHome(), "provider.json")) },
77184
+ { path: path103.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync66(path103.join(cwd, ".zelari", "AGENTS.md")) },
77185
+ { path: path103.join(cwd, "AGENTS.md"), exists: existsSync66(path103.join(cwd, "AGENTS.md")) }
76856
77186
  ],
76857
77187
  skills: {
76858
77188
  total: snap.skills.length,
@@ -76865,7 +77195,7 @@ async function collectInspectReport(cwd = process.cwd()) {
76865
77195
  user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
76866
77196
  project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
76867
77197
  projectTrusted,
76868
- projectConfigExists: existsSync65(projectMcpPath)
77198
+ projectConfigExists: existsSync66(projectMcpPath)
76869
77199
  },
76870
77200
  hooks: {
76871
77201
  global: {
@@ -76892,12 +77222,12 @@ function listJsonFiles(dir) {
76892
77222
  }
76893
77223
  function findAgentsMd(cwd) {
76894
77224
  const candidates = [
76895
- path102.join(cwd, "AGENTS.md"),
76896
- path102.join(cwd, ".zelari", "AGENTS.md")
77225
+ path103.join(cwd, "AGENTS.md"),
77226
+ path103.join(cwd, ".zelari", "AGENTS.md")
76897
77227
  ];
76898
77228
  const found = [];
76899
77229
  for (const c of candidates) {
76900
- if (existsSync65(c)) {
77230
+ if (existsSync66(c)) {
76901
77231
  try {
76902
77232
  const text = readFileSync48(c, "utf8");
76903
77233
  found.push(`${c} (${text.length} bytes)`);
@@ -77352,12 +77682,12 @@ function mergeChanges(unstaged, staged, untrackedPaths) {
77352
77682
  return [...byPath.values()].sort((a, b) => churn(b) - churn(a));
77353
77683
  }
77354
77684
  function runGit(args, cwd) {
77355
- return new Promise((resolve9, reject) => {
77685
+ return new Promise((resolve10, reject) => {
77356
77686
  execFile(
77357
77687
  "git",
77358
77688
  args,
77359
77689
  { cwd, timeout: 5e3, windowsHide: true, maxBuffer: 4 * 1024 * 1024 },
77360
- (err, stdout) => err ? reject(err) : resolve9(stdout)
77690
+ (err, stdout) => err ? reject(err) : resolve10(stdout)
77361
77691
  );
77362
77692
  });
77363
77693
  }
@@ -79936,7 +80266,7 @@ init_toolPermissions();
79936
80266
  init_askUserTimeout();
79937
80267
  function createPermissionAskHandler(opts) {
79938
80268
  const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
79939
- return (req) => new Promise((resolve9) => {
80269
+ return (req) => new Promise((resolve10) => {
79940
80270
  const cats = req.categories;
79941
80271
  const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
79942
80272
  const title = `Allow tool "${req.toolName}"?`;
@@ -79962,7 +80292,7 @@ ${detail}${note}${claimsBlock}
79962
80292
  cancelAskTimeout();
79963
80293
  setPicker2(null);
79964
80294
  if (note2) appendSystem2?.(note2, Date.now());
79965
- resolve9(ok);
80295
+ resolve10(ok);
79966
80296
  };
79967
80297
  cancelAskTimeout = armPickerTimeout(
79968
80298
  () => finish2(
@@ -80161,10 +80491,10 @@ function useChatTurn(params) {
80161
80491
  }
80162
80492
  } catch {
80163
80493
  }
80164
- const onAskUser = setPicker2 ? (req) => new Promise((resolve9) => {
80494
+ const onAskUser = setPicker2 ? (req) => new Promise((resolve10) => {
80165
80495
  const choices = req.choices ?? [];
80166
80496
  if (choices.length < 2) {
80167
- resolve9(null);
80497
+ resolve10(null);
80168
80498
  return;
80169
80499
  }
80170
80500
  setLastClarification({
@@ -80188,7 +80518,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
80188
80518
  settled = true;
80189
80519
  cancelAskTimeout();
80190
80520
  setPicker2(null);
80191
- resolve9(value);
80521
+ resolve10(value);
80192
80522
  };
80193
80523
  cancelAskTimeout = armPickerTimeout(
80194
80524
  () => {
@@ -81134,10 +81464,10 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
81134
81464
  const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
81135
81465
  const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
81136
81466
  const workPhase = getPhase();
81137
- const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve9) => {
81467
+ const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve10) => {
81138
81468
  const choices = req.choices ?? [];
81139
81469
  if (choices.length < 2) {
81140
- resolve9(null);
81470
+ resolve10(null);
81141
81471
  return;
81142
81472
  }
81143
81473
  setLastClarification({
@@ -81161,7 +81491,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81161
81491
  settled = true;
81162
81492
  cancelAskTimeout();
81163
81493
  setPicker2(null);
81164
- resolve9(value);
81494
+ resolve10(value);
81165
81495
  };
81166
81496
  cancelAskTimeout = armPickerTimeout(
81167
81497
  () => {
@@ -81343,10 +81673,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81343
81673
  appendSystem(setMessages, message, Date.now());
81344
81674
  },
81345
81675
  // v1.8.0: pause council when a member asks a structured question.
81346
- onClarification: setPicker2 ? (req) => new Promise((resolve9) => {
81676
+ onClarification: setPicker2 ? (req) => new Promise((resolve10) => {
81347
81677
  const choices = req.choices ?? [];
81348
81678
  if (choices.length < 2) {
81349
- resolve9(null);
81679
+ resolve10(null);
81350
81680
  return;
81351
81681
  }
81352
81682
  setLastClarification({
@@ -81367,7 +81697,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81367
81697
  if (settled) return;
81368
81698
  settled = true;
81369
81699
  setPicker2(null);
81370
- resolve9(value);
81700
+ resolve10(value);
81371
81701
  };
81372
81702
  setPicker2({
81373
81703
  kind: "clarification",
@@ -82025,10 +82355,10 @@ function usePermissionBroker(opts) {
82025
82355
  setPicker: setPicker2,
82026
82356
  appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
82027
82357
  });
82028
- const onQuestion = (req) => new Promise((resolve9) => {
82358
+ const onQuestion = (req) => new Promise((resolve10) => {
82029
82359
  const choices = (req.choices ?? []).map((c) => String(c).trim()).filter((c) => c.length > 0);
82030
82360
  if (choices.length < 2) {
82031
- resolve9(null);
82361
+ resolve10(null);
82032
82362
  return;
82033
82363
  }
82034
82364
  let settled = false;
@@ -82038,7 +82368,7 @@ function usePermissionBroker(opts) {
82038
82368
  settled = true;
82039
82369
  cancelWaitTimeout?.();
82040
82370
  setPicker2(null);
82041
- resolve9(value);
82371
+ resolve10(value);
82042
82372
  };
82043
82373
  const waitTimeoutMs = askUserTimeoutMs();
82044
82374
  cancelWaitTimeout = armPickerTimeout(() => {
@@ -82107,7 +82437,7 @@ init_mode();
82107
82437
  init_ledger();
82108
82438
 
82109
82439
  // src/cli/evolution/proposals.ts
82110
- import { existsSync as existsSync51, readFileSync as readFileSync39 } from "node:fs";
82440
+ import { existsSync as existsSync53, readFileSync as readFileSync40 } from "node:fs";
82111
82441
  import path81 from "node:path";
82112
82442
  var PROPOSALS_REL = path81.join(".zelari", "evolution", "proposals.jsonl");
82113
82443
  function proposalsPath(cwd) {
@@ -82115,10 +82445,10 @@ function proposalsPath(cwd) {
82115
82445
  }
82116
82446
  function readProposalStore(cwd) {
82117
82447
  const file2 = proposalsPath(cwd);
82118
- if (!existsSync51(file2)) return [];
82448
+ if (!existsSync53(file2)) return [];
82119
82449
  let raw;
82120
82450
  try {
82121
- raw = readFileSync39(file2, "utf8");
82451
+ raw = readFileSync40(file2, "utf8");
82122
82452
  } catch {
82123
82453
  return [];
82124
82454
  }
@@ -84008,7 +84338,7 @@ async function handleKrakenFanout(ctx, raw) {
84008
84338
  }
84009
84339
  return;
84010
84340
  }
84011
- const absCsv = isAbsolute3(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
84341
+ const absCsv = isAbsolute4(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
84012
84342
  try {
84013
84343
  await fs39.access(absCsv);
84014
84344
  } catch {
@@ -84044,7 +84374,7 @@ async function handleKrakenFanout(ctx, raw) {
84044
84374
  );
84045
84375
  }
84046
84376
  }
84047
- function isAbsolute3(p3) {
84377
+ function isAbsolute4(p3) {
84048
84378
  return /^([A-Za-z]:[\\/]|[\\/])/.test(p3);
84049
84379
  }
84050
84380
  function joinPath(a, b) {
@@ -84416,7 +84746,7 @@ ${browsers.output}` : "") + (browsers.ok ? "" : `
84416
84746
  return result;
84417
84747
  }
84418
84748
  function runPlaywrightInstallChromium(cwd, executor) {
84419
- return new Promise((resolve9) => {
84749
+ return new Promise((resolve10) => {
84420
84750
  let stdout = "";
84421
84751
  let stderr = "";
84422
84752
  const stdio = ["ignore", "pipe", "pipe"];
@@ -84429,7 +84759,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84429
84759
  stderr += chunk.toString();
84430
84760
  });
84431
84761
  child.on("error", (err) => {
84432
- resolve9({
84762
+ resolve10({
84433
84763
  ok: false,
84434
84764
  output: stdout + stderr,
84435
84765
  error: err.message,
@@ -84438,7 +84768,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84438
84768
  });
84439
84769
  child.on("close", (code) => {
84440
84770
  const ok = code === 0;
84441
- resolve9({
84771
+ resolve10({
84442
84772
  ok,
84443
84773
  output: stdout + stderr,
84444
84774
  exitCode: code,
@@ -84448,7 +84778,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84448
84778
  });
84449
84779
  }
84450
84780
  function runNpm2(executor, args, cwd, mode, npmCliPath) {
84451
- return new Promise((resolve9) => {
84781
+ return new Promise((resolve10) => {
84452
84782
  let stdout = "";
84453
84783
  let stderr = "";
84454
84784
  const stdio = ["ignore", "pipe", "pipe"];
@@ -84460,7 +84790,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
84460
84790
  stderr += chunk.toString();
84461
84791
  });
84462
84792
  child.on("error", (err) => {
84463
- resolve9({
84793
+ resolve10({
84464
84794
  ok: false,
84465
84795
  output: stdout + stderr,
84466
84796
  error: err.message,
@@ -84469,7 +84799,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
84469
84799
  });
84470
84800
  child.on("close", (code) => {
84471
84801
  const ok = code === 0;
84472
- resolve9({
84802
+ resolve10({
84473
84803
  ok,
84474
84804
  output: stdout + stderr,
84475
84805
  exitCode: code,
@@ -84568,7 +84898,7 @@ ${lineage}
84568
84898
 
84569
84899
  // src/cli/branchManager.ts
84570
84900
  init_paths();
84571
- import { promises as fs42, existsSync as existsSync55, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync21, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
84901
+ import { promises as fs42, existsSync as existsSync57, readFileSync as readFileSync42, writeFileSync as writeFileSync25, mkdirSync as mkdirSync22, statSync as statSync7, rmSync as rmSync4 } from "node:fs";
84572
84902
  import path88 from "node:path";
84573
84903
  var META_FILENAME = "meta.json";
84574
84904
  var SESSIONS_SUBDIR = "sessions";
@@ -84589,11 +84919,11 @@ function sessionsPathFor(name, baseDir) {
84589
84919
  }
84590
84920
  function readBranchMeta(name, baseDir) {
84591
84921
  const metaPath = metaPathFor(name, baseDir);
84592
- if (!existsSync55(metaPath)) {
84922
+ if (!existsSync57(metaPath)) {
84593
84923
  throw new BranchNotFoundError(`Branch "${name}" not found`);
84594
84924
  }
84595
84925
  try {
84596
- const raw = readFileSync41(metaPath, "utf-8");
84926
+ const raw = readFileSync42(metaPath, "utf-8");
84597
84927
  const parsed = JSON.parse(raw);
84598
84928
  if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
84599
84929
  throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
@@ -84610,8 +84940,8 @@ function readBranchMeta(name, baseDir) {
84610
84940
  }
84611
84941
  function writeBranchMeta(name, baseDir, meta3) {
84612
84942
  const metaPath = metaPathFor(name, baseDir);
84613
- mkdirSync21(path88.dirname(metaPath), { recursive: true });
84614
- writeFileSync24(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
84943
+ mkdirSync22(path88.dirname(metaPath), { recursive: true });
84944
+ writeFileSync25(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
84615
84945
  }
84616
84946
  async function countSessions(name, baseDir) {
84617
84947
  const sessionsPath = sessionsPathFor(name, baseDir);
@@ -84649,7 +84979,7 @@ var SessionNotFoundError = class extends Error {
84649
84979
  };
84650
84980
  function branchExists(name, baseDir = getBranchesBaseDir()) {
84651
84981
  const bp = branchPathFor(name, baseDir);
84652
- return existsSync55(bp) && existsSync55(metaPathFor(name, baseDir));
84982
+ return existsSync57(bp) && existsSync57(metaPathFor(name, baseDir));
84653
84983
  }
84654
84984
  async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
84655
84985
  if (!name || name.trim().length === 0) {
@@ -84662,12 +84992,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
84662
84992
  throw new BranchAlreadyExistsError(name);
84663
84993
  }
84664
84994
  const sourcePath = path88.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
84665
- if (!existsSync55(sourcePath)) {
84995
+ if (!existsSync57(sourcePath)) {
84666
84996
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
84667
84997
  }
84668
84998
  const branchPath = branchPathFor(name, baseDir);
84669
84999
  const branchSessionsPath = sessionsPathFor(name, baseDir);
84670
- mkdirSync21(branchSessionsPath, { recursive: true });
85000
+ mkdirSync22(branchSessionsPath, { recursive: true });
84671
85001
  const destPath = path88.join(branchSessionsPath, `${fromSessionId}.jsonl`);
84672
85002
  await fs42.copyFile(sourcePath, destPath);
84673
85003
  const meta3 = {
@@ -84695,7 +85025,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
84695
85025
  const results = [];
84696
85026
  for (const entry of entries) {
84697
85027
  const metaPath = metaPathFor(entry, baseDir);
84698
- if (!existsSync55(metaPath)) continue;
85028
+ if (!existsSync57(metaPath)) continue;
84699
85029
  try {
84700
85030
  const meta3 = readBranchMeta(entry, baseDir);
84701
85031
  const sessionCount = await countSessions(entry, baseDir);
@@ -84883,7 +85213,7 @@ async function handleWorkspaceReset(ctx, force) {
84883
85213
  init_provider2();
84884
85214
 
84885
85215
  // src/cli/skillHistory.ts
84886
- import { promises as fs44, existsSync as existsSync56, statSync as statSync8, renameSync as renameSync6, appendFileSync as appendFileSync5, mkdirSync as mkdirSync22 } from "node:fs";
85216
+ import { promises as fs44, existsSync as existsSync58, statSync as statSync8, renameSync as renameSync6, appendFileSync as appendFileSync5, mkdirSync as mkdirSync23 } from "node:fs";
84887
85217
  init_paths();
84888
85218
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
84889
85219
  async function readSkillHistory(file2) {
@@ -86294,9 +86624,9 @@ init_metrics3();
86294
86624
  init_providerConfig();
86295
86625
 
86296
86626
  // src/cli/wizard/firstRun.ts
86297
- import { existsSync as existsSync58 } from "node:fs";
86627
+ import { existsSync as existsSync60 } from "node:fs";
86298
86628
  function shouldRunWizard(input) {
86299
- const exists = input.exists ?? existsSync58;
86629
+ const exists = input.exists ?? existsSync60;
86300
86630
  if (input.hasResetConfigFlag) {
86301
86631
  return { shouldRun: true, reason: "--reset-config flag forced wizard" };
86302
86632
  }
@@ -86769,8 +87099,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
86769
87099
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
86770
87100
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
86771
87101
  try {
86772
- const path103 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
86773
- name = path103 && /^[a-z0-9]/.test(path103) ? path103 : "imported-skill";
87102
+ const path104 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
87103
+ name = path104 && /^[a-z0-9]/.test(path104) ? path104 : "imported-skill";
86774
87104
  } catch {
86775
87105
  name = "imported-skill";
86776
87106
  }
@@ -86933,7 +87263,7 @@ function runPreflight() {
86933
87263
  }
86934
87264
  async function backgroundUpdateCheck() {
86935
87265
  if (process.env.ANATHEMA_DEV === "1") return;
86936
- await new Promise((resolve9) => setTimeout(resolve9, 3e3));
87266
+ await new Promise((resolve10) => setTimeout(resolve10, 3e3));
86937
87267
  try {
86938
87268
  const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_updater(), updater_exports));
86939
87269
  const info = await checkForUpdate2();
@@ -87183,7 +87513,7 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87183
87513
  }
87184
87514
  if (argv.includes("--help") || argv.includes("-h")) {
87185
87515
  console.log(
87186
- "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor [--json] Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n zelari/mission mode auto-scopes slices from open tasks in .zelari/plan.json\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --resume-mission Resume .zelari/mission-state.json (not the spine)\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --allow-unverified Exit 0 when strict is ON but nothing could be verified (M1.2)\n --task-file <path> Read the task prompt from a file (Windows argv cap)\n --once Single-cycle run (cron/git-hook triggers, ADR-0014)\n --kraken-graph <goal> Plan + execute a Kraken task graph\n (variant: --kraken-graph-file <path>; kill-switch\n ZELARI_KRAKEN_GRAPH=0)\n --plan-only Serialize the graph plan to .zelari/radio/ and exit 0\n --run-plan <id> Execute a pre-built .zelari/radio/plan-<id>.json\n --gauntlet Host-driven gauntlet loop (builder/critic tentacles)\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --serve-harness Long-lived harness kernel for hosts (NDJSON JSON-RPC\n on stdin/stdout; Desktop/companion transport)\n --print-config Print provider/model config as JSON (no secrets)\n --print-settings Print zelari.config.json values + the origin of\n each (default < user < project < env)\n --permissions <p> Permission preset: strict | standard | yolo \u2014 changes\n category DEFAULTS only (env vars and policy files win)\n --evolve-status Evolution ledger stats (read-only; ADR-0036; the\n ledger is written only when ZELARI_EVOLUTION=shadow)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable for stdio servers\n --url <endpoint> Streamable HTTP endpoint (e.g. UE 5.8 editor)\n --args <json> JSON array of args (stdio, optional)\n --timeout <ms> Per-server request timeout (http, optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --preset unreal-mcp Unreal Engine 5.8+ editor (MCP over HTTP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ZELARI_MEMORY_MCP=1 Enable the optional external memory MCP server\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
87516
+ "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor [--json] Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --inspect [--json] Unified project inspection (config, skills, MCP,\n hooks, plugins, AGENTS.md, trust status)\n --trust [path] Trust the cwd (or path) so project MCP + hooks load\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --permission-mcp <socket> MCP stdio server for external agent permission prompts\n (spawned by claude --permission-prompt-tool)\n --memory-mcp Optional MCP stdio server for project memory\n --cwd <path> Trusted project root (requires ZELARI_MEMORY_MCP=1)\n --client-id <id> Stable local owner identity for private memories\n --memory-json <json> Read-only project-memory bridge for Zelari Desktop\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode kraken|council|zelari Dispatch mode (default: kraken; agent=alias)\n zelari/mission mode auto-scopes slices from open tasks in .zelari/plan.json\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --profile <id> Capability profile (minimal/v1|kraken/v1|council/v1|mission/v1)\n --resume <id> Continue a 2.0 spine session\n --resume-mission Resume .zelari/mission-state.json (not the spine)\n --export-session <path> Write zelari-session-export/1 after the run\n --strict-done Evidence-based BUILD completion gate\n --allow-unverified Exit 0 when strict is ON but nothing could be verified (M1.2)\n --task-file <path> Read the task prompt from a file (Windows argv cap)\n --once Single-cycle run (cron/git-hook triggers, ADR-0014)\n --kraken-graph <goal> Plan + execute a Kraken task graph\n (variant: --kraken-graph-file <path>; kill-switch\n ZELARI_KRAKEN_GRAPH=0)\n --plan-only Serialize the graph plan to .zelari/radio/ and exit 0\n --run-plan <id> Execute a pre-built .zelari/radio/plan-<id>.json\n --gauntlet Host-driven gauntlet loop (builder/critic tentacles)\n --session-export <id> Print a portable 2.0 session export (no LLM)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --serve-harness Long-lived harness kernel for hosts (NDJSON JSON-RPC\n on stdin/stdout; Desktop/companion transport)\n --print-config Print provider/model config as JSON (no secrets)\n --print-settings Print zelari.config.json values + the origin of\n each (default < user < project < env)\n --permissions <p> Permission preset: strict | standard | yolo \u2014 changes\n category DEFAULTS only (env vars and policy files win);\n yolo also auto-approves residual asks (unattended runs)\n --evolve-status Evolution ledger stats (read-only; ADR-0036; the\n ledger is written only when ZELARI_EVOLUTION=shadow)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --thinking <spec> Thinking effort (auto|off|low|medium|high|budget:N)\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --login-oauth Start subscription OAuth (grok, chatgpt, anthropic)\n --provider <id> grok | chatgpt | anthropic\n --code <paste> Anthropic magic-link code (CODE#STATE)\n --no-browser Do not open the system browser\n --refresh-oauth Force-refresh an OAuth access token\n --provider <id> grok | chatgpt | anthropic\n --logout-oauth Clear stored OAuth credentials\n --provider <id> grok | chatgpt | anthropic\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable for stdio servers\n --url <endpoint> Streamable HTTP endpoint (e.g. UE 5.8 editor)\n --args <json> JSON array of args (stdio, optional)\n --env <json|K=V> Env for the server: JSON object or KEY=VALUE,\n repeatable (omit to keep the stored env)\n --timeout <ms> Per-server request timeout (http, optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --preset unreal-mcp Unreal Engine 5.8+ editor (MCP over HTTP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ZELARI_MEMORY_MCP=1 Enable the optional external memory MCP server\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
87187
87517
  );
87188
87518
  process.exit(0);
87189
87519
  }
@@ -87223,6 +87553,11 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87223
87553
  if (!Array.isArray(parsed)) throw new Error("--args must be a JSON array");
87224
87554
  args = parsed.map(String);
87225
87555
  }
87556
+ const envValues = [];
87557
+ for (let i = 0; i < argv.length - 1; i++) {
87558
+ if (argv[i] === "--env") envValues.push(argv[i + 1]);
87559
+ }
87560
+ const env = parseMcpEnvFlag(envValues);
87226
87561
  const timeoutMs2 = timeoutRaw !== void 0 ? Number(timeoutRaw) : void 0;
87227
87562
  if (timeoutMs2 !== void 0 && (!Number.isFinite(timeoutMs2) || timeoutMs2 <= 0)) {
87228
87563
  throw new Error("--timeout must be a positive number of milliseconds");
@@ -87238,7 +87573,7 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87238
87573
  scope,
87239
87574
  name,
87240
87575
  projectRoot: cwd,
87241
- config: url2 ? { type: "http", url: url2, timeoutMs: timeoutMs2, serial: true, enabled } : { command, args, enabled }
87576
+ config: url2 ? { type: "http", url: url2, timeoutMs: timeoutMs2, serial: true, enabled, env } : { command, args, env, enabled }
87242
87577
  });
87243
87578
  if (!result.ok) throw new Error(result.error);
87244
87579
  console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
@@ -87729,7 +88064,7 @@ function main() {
87729
88064
  closeMcpClients2();
87730
88065
  } catch {
87731
88066
  }
87732
- await new Promise((resolve9) => setImmediate(resolve9));
88067
+ await new Promise((resolve10) => setImmediate(resolve10));
87733
88068
  process.exit(code);
87734
88069
  }).catch(async (err) => {
87735
88070
  console.error(