zelari-code 2.41.0 → 2.42.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
  }
@@ -20336,11 +20336,11 @@ var init_tools = __esm({
20336
20336
  if (!ctx.addDocument)
20337
20337
  return "Knowledge vault tool not available.";
20338
20338
  const title = args["title"] || "New Document";
20339
- const path103 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
20339
+ const path104 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
20340
20340
  const content = args["content"] || "";
20341
20341
  const tags = args["tags"] || [];
20342
20342
  ctx.addDocument({
20343
- path: path103,
20343
+ path: path104,
20344
20344
  title,
20345
20345
  content,
20346
20346
  format: "markdown",
@@ -20349,7 +20349,7 @@ var init_tools = __esm({
20349
20349
  workspaceId: ctx.workspaceId
20350
20350
  });
20351
20351
  ctx.addActivity("vault", "created document", title);
20352
- return `Document "${title}" created at "${path103}".`;
20352
+ return `Document "${title}" created at "${path104}".`;
20353
20353
  }
20354
20354
  }
20355
20355
  ];
@@ -22259,7 +22259,7 @@ var init_nodeProviders = __esm({
22259
22259
  const isWindows = process.platform === "win32";
22260
22260
  const file2 = isWindows ? "cmd.exe" : "/bin/sh";
22261
22261
  const args = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
22262
- return await new Promise((resolve9) => {
22262
+ return await new Promise((resolve10) => {
22263
22263
  const child = spawn2(file2, args, {
22264
22264
  cwd,
22265
22265
  shell: false,
@@ -22292,7 +22292,7 @@ var init_nodeProviders = __esm({
22292
22292
  });
22293
22293
  child.on("error", (err) => {
22294
22294
  clearTimeout(timer);
22295
- resolve9({
22295
+ resolve10({
22296
22296
  exitCode: null,
22297
22297
  stdout,
22298
22298
  stderr: `${stderr}${err.message}`,
@@ -22302,7 +22302,7 @@ var init_nodeProviders = __esm({
22302
22302
  });
22303
22303
  child.on("close", (code) => {
22304
22304
  clearTimeout(timer);
22305
- resolve9({ exitCode: code, stdout, stderr, durationMs: Date.now() - started, timedOut });
22305
+ resolve10({ exitCode: code, stdout, stderr, durationMs: Date.now() - started, timedOut });
22306
22306
  });
22307
22307
  });
22308
22308
  }
@@ -22380,7 +22380,7 @@ import path16 from "node:path";
22380
22380
  import crypto2 from "node:crypto";
22381
22381
  import { spawn as spawn3 } from "node:child_process";
22382
22382
  async function git(cwd, args) {
22383
- return await new Promise((resolve9, reject) => {
22383
+ return await new Promise((resolve10, reject) => {
22384
22384
  const child = spawn3("git", args, { cwd, shell: false });
22385
22385
  let stdout = "";
22386
22386
  let stderr = "";
@@ -22389,7 +22389,7 @@ async function git(cwd, args) {
22389
22389
  child.on("error", (err) => reject(err));
22390
22390
  child.on("close", (code) => {
22391
22391
  if (code === 0)
22392
- resolve9(stdout);
22392
+ resolve10(stdout);
22393
22393
  else
22394
22394
  reject(new Error(`git ${args.join(" ")} failed (${code}): ${stderr.trim()}`));
22395
22395
  });
@@ -25287,16 +25287,16 @@ function runRetentionFromEnv() {
25287
25287
  maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
25288
25288
  };
25289
25289
  }
25290
- async function dirSize(path103) {
25290
+ async function dirSize(path104) {
25291
25291
  let total = 0;
25292
25292
  let entries;
25293
25293
  try {
25294
- entries = await readdir(path103, { withFileTypes: true });
25294
+ entries = await readdir(path104, { withFileTypes: true });
25295
25295
  } catch {
25296
25296
  return 0;
25297
25297
  }
25298
25298
  for (const entry of entries) {
25299
- const child = join3(path103, entry.name);
25299
+ const child = join3(path104, entry.name);
25300
25300
  if (entry.isDirectory())
25301
25301
  total += await dirSize(child);
25302
25302
  else {
@@ -25323,19 +25323,19 @@ async function enforceRunRetention(runsDir, options = {}) {
25323
25323
  for (const entry of entries) {
25324
25324
  if (!entry.isDirectory())
25325
25325
  continue;
25326
- const path103 = join3(runsDir, entry.name);
25326
+ const path104 = join3(runsDir, entry.name);
25327
25327
  let startedAt = 0;
25328
25328
  let endedAt;
25329
25329
  let completed = false;
25330
25330
  try {
25331
- const manifest = JSON.parse(await readFile(join3(path103, "manifest.json"), "utf8"));
25331
+ const manifest = JSON.parse(await readFile(join3(path104, "manifest.json"), "utf8"));
25332
25332
  startedAt = manifest.startedAt ?? 0;
25333
25333
  endedAt = manifest.endedAt;
25334
25334
  completed = Boolean(endedAt) && manifest.status !== "running";
25335
25335
  } catch {
25336
25336
  completed = false;
25337
25337
  }
25338
- infos.push({ name: entry.name, path: path103, startedAt, endedAt, completed, bytes: await dirSize(path103) });
25338
+ infos.push({ name: entry.name, path: path104, startedAt, endedAt, completed, bytes: await dirSize(path104) });
25339
25339
  }
25340
25340
  const remove = async (info) => {
25341
25341
  await rm(info.path, { recursive: true, force: true });
@@ -25893,12 +25893,12 @@ var init_engine = __esm({
25893
25893
  * content digest) and the returned ref carries the event seq when the
25894
25894
  * emitter resolved one.
25895
25895
  */
25896
- async fsEvidence(observation, path103, sha2562, content, extra = {}) {
25896
+ async fsEvidence(observation, path104, sha2562, content, extra = {}) {
25897
25897
  const digest = sha2562 && content !== void 0 ? sha2562(content) : void 0;
25898
- const seq = await this.emitEvidence({ observation, path: path103, ...extra, ...digest ? { digest } : {} });
25898
+ const seq = await this.emitEvidence({ observation, path: path104, ...extra, ...digest ? { digest } : {} });
25899
25899
  return {
25900
25900
  tier: "fs-observation",
25901
- ref: path103,
25901
+ ref: path104,
25902
25902
  capturedAt: Date.now(),
25903
25903
  ...digest ? { digest } : {},
25904
25904
  ...seq !== void 0 ? { seq } : {}
@@ -30504,8 +30504,8 @@ function wrapLegacyStream(legacyStream, params, _messages, _tools) {
30504
30504
  }
30505
30505
  if (done)
30506
30506
  break;
30507
- await new Promise((resolve9) => {
30508
- resolveNext = resolve9;
30507
+ await new Promise((resolve10) => {
30508
+ resolveNext = resolve10;
30509
30509
  });
30510
30510
  }
30511
30511
  await promise2;
@@ -30638,11 +30638,11 @@ var init_sessionJsonl = __esm({
30638
30638
  */
30639
30639
  scheduleFlush() {
30640
30640
  if (this.pending.length > 0 && !this.flushDeferred) {
30641
- let resolve9;
30641
+ let resolve10;
30642
30642
  const promise2 = new Promise((r) => {
30643
- resolve9 = r;
30643
+ resolve10 = r;
30644
30644
  });
30645
- this.flushDeferred = { promise: promise2, resolve: resolve9 };
30645
+ this.flushDeferred = { promise: promise2, resolve: resolve10 };
30646
30646
  }
30647
30647
  if (this.flushTimer === null && this.pending.length > 0) {
30648
30648
  this.flushTimer = setTimeout(() => {
@@ -30949,7 +30949,7 @@ var init_lifecycleHookRunner = __esm({
30949
30949
  }
30950
30950
  /** Spawn `command`, write JSON payload to stdin, read stdout until close. */
30951
30951
  execCommand(command, payload, timeoutMs2, cwd) {
30952
- return new Promise((resolve9, reject) => {
30952
+ return new Promise((resolve10, reject) => {
30953
30953
  const argv = splitHookCommandLine(command);
30954
30954
  if (argv.length === 0) {
30955
30955
  reject(new Error("empty hook command"));
@@ -30983,7 +30983,7 @@ var init_lifecycleHookRunner = __esm({
30983
30983
  reject(new Error(`exited ${code}: ${stderr.trim().slice(0, 200)}`));
30984
30984
  return;
30985
30985
  }
30986
- resolve9(stdout);
30986
+ resolve10(stdout);
30987
30987
  });
30988
30988
  child.stdin?.on("error", () => {
30989
30989
  });
@@ -31032,7 +31032,17 @@ var init_appServerTypes = __esm({
31032
31032
 
31033
31033
  // packages/core/dist/harness/appServer.js
31034
31034
  import { randomUUID } from "node:crypto";
31035
- import { resolve } from "node:path";
31035
+ import { isAbsolute, resolve } from "node:path";
31036
+ function resolveSessionWorkspaceRoot(workspaceRoot) {
31037
+ if (typeof workspaceRoot !== "string" || workspaceRoot.trim() === "") {
31038
+ throw new Error("HarnessAppServer: createSession requires a non-empty `workspaceRoot` (it never falls back to process.cwd(): sessions on different workspaces must stay isolated)");
31039
+ }
31040
+ const raw = workspaceRoot.trim();
31041
+ if (!isAbsolute(raw)) {
31042
+ 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)`);
31043
+ }
31044
+ return resolve(raw);
31045
+ }
31036
31046
  var HarnessAppServer;
31037
31047
  var init_appServer = __esm({
31038
31048
  "packages/core/dist/harness/appServer.js"() {
@@ -31053,7 +31063,7 @@ var init_appServer = __esm({
31053
31063
  this.defaultServicesFactory = options.createWorkspaceServices;
31054
31064
  }
31055
31065
  createSession(options) {
31056
- const root = resolve(options.workspaceRoot);
31066
+ const root = resolveSessionWorkspaceRoot(options.workspaceRoot);
31057
31067
  const factory = options.createWorkspaceServices ?? this.defaultServicesFactory;
31058
31068
  if (!factory) {
31059
31069
  throw new Error("HarnessAppServer: createWorkspaceServices factory is required");
@@ -31289,6 +31299,7 @@ __export(harness_exports, {
31289
31299
  recordToolResult: () => recordToolResult,
31290
31300
  recordUsage: () => recordUsage,
31291
31301
  resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
31302
+ resolveSessionWorkspaceRoot: () => resolveSessionWorkspaceRoot,
31292
31303
  runExtensionPreToolUse: () => runExtensionPreToolUse,
31293
31304
  sha256Hex: () => sha256Hex,
31294
31305
  splitHookCommandLine: () => splitHookCommandLine,
@@ -32107,11 +32118,11 @@ var init_synthesisAudit = __esm({
32107
32118
  import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
32108
32119
  import { join as join5 } from "node:path";
32109
32120
  function loadNfrSpec(zelariRoot) {
32110
- const path103 = join5(zelariRoot, "nfr-spec.json");
32111
- if (!existsSync10(path103))
32121
+ const path104 = join5(zelariRoot, "nfr-spec.json");
32122
+ if (!existsSync10(path104))
32112
32123
  return null;
32113
32124
  try {
32114
- const raw = JSON.parse(readFileSync9(path103, "utf8"));
32125
+ const raw = JSON.parse(readFileSync9(path104, "utf8"));
32115
32126
  if (raw.version !== 1 || !Array.isArray(raw.targets))
32116
32127
  return null;
32117
32128
  return raw;
@@ -34512,9 +34523,9 @@ var init_types9 = __esm({
34512
34523
  import { readFileSync as readFileSync14 } from "node:fs";
34513
34524
  import { join as join11 } from "node:path";
34514
34525
  function readLessonsDeduped(zelariRoot) {
34515
- const path103 = join11(zelariRoot, LESSONS_FILE);
34526
+ const path104 = join11(zelariRoot, LESSONS_FILE);
34516
34527
  try {
34517
- const raw = readFileSync14(path103, "utf8");
34528
+ const raw = readFileSync14(path104, "utf8");
34518
34529
  const byId = /* @__PURE__ */ new Map();
34519
34530
  for (const line of raw.split(/\r?\n/)) {
34520
34531
  if (!line.trim())
@@ -34615,8 +34626,8 @@ function keywordsFrom(check2, signature) {
34615
34626
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
34616
34627
  }
34617
34628
  function writeLesson(zelariRoot, lesson) {
34618
- const path103 = join12(zelariRoot, LESSONS_FILE);
34619
- appendFileSync(path103, `${JSON.stringify(lesson)}
34629
+ const path104 = join12(zelariRoot, LESSONS_FILE);
34630
+ appendFileSync(path104, `${JSON.stringify(lesson)}
34620
34631
  `, "utf8");
34621
34632
  }
34622
34633
  function findSimilar(lessons, signature) {
@@ -36584,9 +36595,9 @@ function findCycle(nodes) {
36584
36595
  if (color.get(start) !== WHITE)
36585
36596
  continue;
36586
36597
  const stack = [[start, 0]];
36587
- const path103 = [];
36598
+ const path104 = [];
36588
36599
  color.set(start, GRAY);
36589
- path103.push(start);
36600
+ path104.push(start);
36590
36601
  while (stack.length > 0) {
36591
36602
  const top = stack[stack.length - 1];
36592
36603
  const [id3, idx] = top;
@@ -36599,17 +36610,17 @@ function findCycle(nodes) {
36599
36610
  continue;
36600
36611
  const c = color.get(dep);
36601
36612
  if (c === GRAY) {
36602
- const at = path103.indexOf(dep);
36603
- return [...path103.slice(at), dep];
36613
+ const at = path104.indexOf(dep);
36614
+ return [...path104.slice(at), dep];
36604
36615
  }
36605
36616
  if (c === WHITE) {
36606
36617
  color.set(dep, GRAY);
36607
- path103.push(dep);
36618
+ path104.push(dep);
36608
36619
  stack.push([dep, 0]);
36609
36620
  }
36610
36621
  } else {
36611
36622
  color.set(id3, BLACK);
36612
- path103.pop();
36623
+ path104.pop();
36613
36624
  stack.pop();
36614
36625
  }
36615
36626
  }
@@ -37529,8 +37540,8 @@ var init_runner = __esm({
37529
37540
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
37530
37541
  pending: []
37531
37542
  };
37532
- const path103 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
37533
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path103}`);
37543
+ const path104 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
37544
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path104}`);
37534
37545
  return snapshot;
37535
37546
  }
37536
37547
  callLog(msg, data) {
@@ -38826,7 +38837,7 @@ var CORE_VERSION;
38826
38837
  var init_version = __esm({
38827
38838
  "packages/core/dist/version.js"() {
38828
38839
  "use strict";
38829
- CORE_VERSION = "2.41.0";
38840
+ CORE_VERSION = "2.42.0";
38830
38841
  }
38831
38842
  });
38832
38843
 
@@ -39309,6 +39320,7 @@ __export(dist_exports, {
39309
39320
  resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
39310
39321
  resolveResponseLanguage: () => resolveResponseLanguage,
39311
39322
  resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
39323
+ resolveSessionWorkspaceRoot: () => resolveSessionWorkspaceRoot,
39312
39324
  resolveSessionsDir: () => resolveSessionsDir,
39313
39325
  resolveSkillDependencies: () => resolveSkillDependencies,
39314
39326
  resolveVerifyRetryTool: () => resolveVerifyRetryTool,
@@ -40162,7 +40174,7 @@ ${fallback.output}`
40162
40174
  return primary;
40163
40175
  }
40164
40176
  function runNpm(executor, args, mode, npmCliPath) {
40165
- return new Promise((resolve9) => {
40177
+ return new Promise((resolve10) => {
40166
40178
  let stdout = "";
40167
40179
  let stderr = "";
40168
40180
  const stdio = ["ignore", "pipe", "pipe"];
@@ -40174,7 +40186,7 @@ function runNpm(executor, args, mode, npmCliPath) {
40174
40186
  stderr += chunk.toString();
40175
40187
  });
40176
40188
  child.on("error", (err) => {
40177
- resolve9({
40189
+ resolve10({
40178
40190
  ok: false,
40179
40191
  output: stdout + stderr,
40180
40192
  error: err.message,
@@ -40183,7 +40195,7 @@ function runNpm(executor, args, mode, npmCliPath) {
40183
40195
  });
40184
40196
  child.on("close", (code) => {
40185
40197
  const ok = code === 0;
40186
- resolve9({
40198
+ resolve10({
40187
40199
  ok,
40188
40200
  output: stdout + stderr,
40189
40201
  error: ok ? void 0 : `npm exited with code ${code}`,
@@ -41308,9 +41320,9 @@ function spillToolOutput(fullText, meta3) {
41308
41320
  const rnd = randomBytes3(3).toString("hex");
41309
41321
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
41310
41322
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
41311
- const path103 = join14(dir, file2);
41312
- writeFileSync12(path103, fullText, "utf8");
41313
- return path103;
41323
+ const path104 = join14(dir, file2);
41324
+ writeFileSync12(path104, fullText, "utf8");
41325
+ return path104;
41314
41326
  } catch {
41315
41327
  return null;
41316
41328
  }
@@ -41356,10 +41368,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
41356
41368
  ${tail2}`;
41357
41369
  }
41358
41370
  if (doSpill) {
41359
- const path103 = spillToolOutput(text, { toolName: opts.toolName });
41360
- if (path103) {
41371
+ const path104 = spillToolOutput(text, { toolName: opts.toolName });
41372
+ if (path104) {
41361
41373
  const spillNote = `
41362
- \u2026 [full output spilled to: ${path103} \u2014 re-read with read_file if you need the complete text] \u2026`;
41374
+ \u2026 [full output spilled to: ${path104} \u2014 re-read with read_file if you need the complete text] \u2026`;
41363
41375
  if (preview.includes("] \u2026\n")) {
41364
41376
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
41365
41377
  `);
@@ -42162,28 +42174,28 @@ var init_storage = __esm({
42162
42174
  VALID_SCALARS = /^(true|false|null|~)$/i;
42163
42175
  Storage = class {
42164
42176
  /** 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}`);
42177
+ read(path104) {
42178
+ if (!existsSync19(path104)) {
42179
+ throw new Error(`File not found: ${path104}`);
42168
42180
  }
42169
- const md = readFileSync17(path103, "utf8");
42181
+ const md = readFileSync17(path104, "utf8");
42170
42182
  return parseFrontmatter(md);
42171
42183
  }
42172
42184
  /** Read a Markdown file; returns null if not found. */
42173
- readIfExists(path103) {
42174
- if (!existsSync19(path103)) return null;
42175
- return this.read(path103);
42185
+ readIfExists(path104) {
42186
+ if (!existsSync19(path104)) return null;
42187
+ return this.read(path104);
42176
42188
  }
42177
42189
  /**
42178
42190
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
42179
42191
  * The meta object is serialized as YAML frontmatter; body as Markdown.
42180
42192
  */
42181
- write(path103, meta3, body) {
42182
- mkdirSync10(dirname2(path103), { recursive: true });
42183
- const tmp = path103 + ".tmp-" + process.pid;
42193
+ write(path104, meta3, body) {
42194
+ mkdirSync10(dirname2(path104), { recursive: true });
42195
+ const tmp = path104 + ".tmp-" + process.pid;
42184
42196
  const md = serializeFrontmatter(meta3, body);
42185
42197
  writeFileSync14(tmp, md, "utf8");
42186
- renameSync2(tmp, path103);
42198
+ renameSync2(tmp, path104);
42187
42199
  }
42188
42200
  /** List all .md files in a directory (non-recursive). */
42189
42201
  listMarkdown(dir) {
@@ -42197,8 +42209,8 @@ var init_storage = __esm({
42197
42209
  const prev2 = this.chains.get(key) ?? Promise.resolve();
42198
42210
  let release = () => {
42199
42211
  };
42200
- const next = new Promise((resolve9) => {
42201
- release = resolve9;
42212
+ const next = new Promise((resolve10) => {
42213
+ release = resolve10;
42202
42214
  });
42203
42215
  const chained = prev2.then(() => next);
42204
42216
  this.chains.set(key, chained);
@@ -42294,8 +42306,8 @@ function nextPlanTaskId(store6) {
42294
42306
  return `t${store6.counter}`;
42295
42307
  }
42296
42308
  function writePlanTaskArtifact(rootDir, task) {
42297
- const path103 = join17(rootDir, "plan-tasks", `${task.id}.md`);
42298
- mkdirSync11(dirname3(path103), { recursive: true });
42309
+ const path104 = join17(rootDir, "plan-tasks", `${task.id}.md`);
42310
+ mkdirSync11(dirname3(path104), { recursive: true });
42299
42311
  const meta3 = {
42300
42312
  kind: "task",
42301
42313
  id: task.id,
@@ -42316,7 +42328,7 @@ function writePlanTaskArtifact(rootDir, task) {
42316
42328
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
42317
42329
  ""
42318
42330
  ].filter((l) => l !== null).join("\n");
42319
- new Storage().write(path103, meta3, body);
42331
+ new Storage().write(path104, meta3, body);
42320
42332
  }
42321
42333
  function loadHandle(rootDir) {
42322
42334
  const jsonPath = join17(rootDir, "plan.json");
@@ -42994,14 +43006,14 @@ var init_engine2 = __esm({
42994
43006
  parse: parseRuffJson
42995
43007
  }
42996
43008
  ];
42997
- defaultRunner = (cmd, args, opts) => new Promise((resolve9) => {
43009
+ defaultRunner = (cmd, args, opts) => new Promise((resolve10) => {
42998
43010
  let stdout = "";
42999
43011
  let stderr = "";
43000
43012
  let settled = false;
43001
43013
  const done = (r) => {
43002
43014
  if (settled) return;
43003
43015
  settled = true;
43004
- resolve9(r);
43016
+ resolve10(r);
43005
43017
  };
43006
43018
  let child;
43007
43019
  try {
@@ -44056,7 +44068,7 @@ async function readChecks(cwd) {
44056
44068
  }
44057
44069
  }
44058
44070
  function runShell(command, cwd, timeoutMs2, signal) {
44059
- return new Promise((resolve9) => {
44071
+ return new Promise((resolve10) => {
44060
44072
  const isWin = process.platform === "win32";
44061
44073
  const child = spawn8(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
44062
44074
  cwd,
@@ -44070,7 +44082,7 @@ function runShell(command, cwd, timeoutMs2, signal) {
44070
44082
  const finish2 = (exitCode) => {
44071
44083
  if (settled) return;
44072
44084
  settled = true;
44073
- resolve9({ exitCode, stdout, stderr });
44085
+ resolve10({ exitCode, stdout, stderr });
44074
44086
  };
44075
44087
  const timer = setTimeout(() => {
44076
44088
  try {
@@ -46781,8 +46793,8 @@ ${upstream}` : node.prompt,
46781
46793
  if (ms <= 0) return promise2;
46782
46794
  let timer;
46783
46795
  const TIMED_OUT = Symbol("timeout");
46784
- const timeout = new Promise((resolve9) => {
46785
- timer = setTimeout(() => resolve9(TIMED_OUT), ms);
46796
+ const timeout = new Promise((resolve10) => {
46797
+ timer = setTimeout(() => resolve10(TIMED_OUT), ms);
46786
46798
  });
46787
46799
  const raced = await Promise.race([promise2, timeout]).finally(() => {
46788
46800
  if (timer) clearTimeout(timer);
@@ -46791,8 +46803,8 @@ ${upstream}` : node.prompt,
46791
46803
  controller?.abort();
46792
46804
  const graceMs = this.cancelGraceMs ?? resolveCancelGraceMs();
46793
46805
  let graceTimer;
46794
- const grace = new Promise((resolve9) => {
46795
- graceTimer = setTimeout(() => resolve9(TIMED_OUT), graceMs);
46806
+ const grace = new Promise((resolve10) => {
46807
+ graceTimer = setTimeout(() => resolve10(TIMED_OUT), graceMs);
46796
46808
  });
46797
46809
  const settled = await Promise.race([promise2, grace]).finally(() => {
46798
46810
  if (graceTimer) clearTimeout(graceTimer);
@@ -47861,7 +47873,10 @@ async function runTentacle(opts) {
47861
47873
  sub = await deps.createSubAgentContext({
47862
47874
  agent,
47863
47875
  thoroughness,
47864
- cwd: effectiveCwd
47876
+ cwd: effectiveCwd,
47877
+ // Per-spawn thinking effort (ADR-0017): the factory applies arg > per-kind
47878
+ // env > inherited provider default and reports the winner back on `sub`.
47879
+ ...args.thinkingEffort ? { thinkingEffort: args.thinkingEffort } : {}
47865
47880
  });
47866
47881
  } catch (err) {
47867
47882
  if (worktree) await cleanupKrakenWorktree(worktree);
@@ -47897,6 +47912,8 @@ async function runTentacle(opts) {
47897
47912
  error: "task: no provider configured for the sub-agent (set an API key / run /login)."
47898
47913
  };
47899
47914
  }
47915
+ const requestedThinking = args.thinkingEffort && args.thinkingEffort.toLowerCase() !== "inherit" ? args.thinkingEffort : void 0;
47916
+ const spawnThinking = sub.thinking ?? requestedThinking;
47900
47917
  emitActivity({
47901
47918
  type: "agent_spawned",
47902
47919
  agentId: liveId,
@@ -47904,6 +47921,7 @@ async function runTentacle(opts) {
47904
47921
  title: args.description,
47905
47922
  ...sub.model ? { model: sub.model } : {},
47906
47923
  ...sub.provider ? { provider: sub.provider } : {},
47924
+ ...spawnThinking ? { thinking: spawnThinking } : {},
47907
47925
  ...args.scope && args.scope.length > 0 ? { scope: args.scope } : {},
47908
47926
  ...worktree ? { worktree: worktree.path } : {},
47909
47927
  ts: Date.now()
@@ -48238,7 +48256,8 @@ RESTRICTED in this mode: only agent=${allowedAgents.join("|")} is allowed.` : ""
48238
48256
  description: args.description,
48239
48257
  prompt: args.prompt,
48240
48258
  scope: args.scope,
48241
- acceptance: args.acceptance
48259
+ acceptance: args.acceptance,
48260
+ ...args.thinkingEffort ? { thinkingEffort: args.thinkingEffort } : {}
48242
48261
  },
48243
48262
  agent,
48244
48263
  thoroughness,
@@ -48394,6 +48413,9 @@ var init_taskTool = __esm({
48394
48413
  ),
48395
48414
  acceptance: external_exports.array(external_exports.string().min(1)).max(16).optional().describe(
48396
48415
  "Optional acceptance checklist (contract). Appended to the prompt as Acceptance criteria."
48416
+ ),
48417
+ thinkingEffort: external_exports.enum(["inherit", "auto", "off", "low", "medium", "high", "xhigh", "max"]).optional().describe(
48418
+ "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
48419
  )
48398
48420
  });
48399
48421
  TaskPurposeSchema = external_exports.enum(["candidate"]).optional().describe(
@@ -49910,6 +49932,383 @@ var init_skillTool = __esm({
49910
49932
  }
49911
49933
  });
49912
49934
 
49935
+ // src/cli/skillCategories.ts
49936
+ var CODING_CATEGORIES_LIST, CODING_CATEGORIES2;
49937
+ var init_skillCategories = __esm({
49938
+ "src/cli/skillCategories.ts"() {
49939
+ "use strict";
49940
+ CODING_CATEGORIES_LIST = [
49941
+ "plan",
49942
+ "refactor",
49943
+ "debug",
49944
+ "review",
49945
+ "test",
49946
+ "docs",
49947
+ "ops",
49948
+ "git",
49949
+ "db",
49950
+ "maint"
49951
+ ];
49952
+ CODING_CATEGORIES2 = new Set(CODING_CATEGORIES_LIST);
49953
+ }
49954
+ });
49955
+
49956
+ // src/cli/skillConfigIo.ts
49957
+ import {
49958
+ existsSync as existsSync28,
49959
+ mkdirSync as mkdirSync13,
49960
+ readdirSync as readdirSync6,
49961
+ readFileSync as readFileSync20,
49962
+ rmSync as rmSync3,
49963
+ writeFileSync as writeFileSync16
49964
+ } from "node:fs";
49965
+ import { dirname as dirname4, join as join20 } from "node:path";
49966
+ function ensureBuiltinSkillsLoadedSync() {
49967
+ if (builtinsLoaded) return;
49968
+ for (const spec of BUILTIN_SKILL_MODULES) {
49969
+ try {
49970
+ require?.(spec);
49971
+ } catch {
49972
+ }
49973
+ }
49974
+ }
49975
+ function getUserSkillsDir() {
49976
+ return join20(zelariHome(), "skills");
49977
+ }
49978
+ function getProjectSkillsDir(projectRoot) {
49979
+ return join20(projectRoot, ".zelari", "skills");
49980
+ }
49981
+ function skillFilePath(dir, name) {
49982
+ return join20(dir, name, "SKILL.md");
49983
+ }
49984
+ function classifyScope(skillPath, projectRoot) {
49985
+ const userDir = getUserSkillsDir().replace(/\\/g, "/");
49986
+ const norm = skillPath.replace(/\\/g, "/");
49987
+ if (norm.startsWith(userDir + "/") || norm === userDir) {
49988
+ return { scope: "user", writable: true };
49989
+ }
49990
+ if (projectRoot) {
49991
+ const projDir = getProjectSkillsDir(projectRoot).replace(/\\/g, "/");
49992
+ if (norm.startsWith(projDir + "/") || norm === projDir) {
49993
+ return { scope: "project", writable: true };
49994
+ }
49995
+ }
49996
+ return { scope: "compat", writable: false };
49997
+ }
49998
+ function entryFromParsed(parsed, projectRoot) {
49999
+ const { scope, writable } = classifyScope(parsed.sourcePath, projectRoot);
50000
+ return {
50001
+ id: parsed.name,
50002
+ name: parsed.name,
50003
+ description: parsed.description,
50004
+ category: parsed.category,
50005
+ estimatedCost: parsed.estimatedCost,
50006
+ requiredTools: parsed.requiredTools,
50007
+ scope,
50008
+ path: parsed.sourcePath,
50009
+ body: parsed.body,
50010
+ builtin: false,
50011
+ writable
50012
+ };
50013
+ }
50014
+ function entryFromBuiltin(skill) {
50015
+ return {
50016
+ id: skill.id,
50017
+ name: skill.name || skill.id,
50018
+ description: skill.description || "",
50019
+ category: skill.category,
50020
+ estimatedCost: skill.estimatedCost,
50021
+ requiredTools: skill.requiredTools,
50022
+ scope: "builtin",
50023
+ path: null,
50024
+ // Expose body so Desktop skill picker can expand like `/skill <id>`.
50025
+ body: skill.systemPromptFragment || void 0,
50026
+ builtin: true,
50027
+ writable: false
50028
+ };
50029
+ }
50030
+ function scanSkillsDir(dir, projectRoot, seen, out) {
50031
+ if (!existsSync28(dir)) return;
50032
+ let entries;
50033
+ try {
50034
+ entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
50035
+ } catch {
50036
+ return;
50037
+ }
50038
+ for (const entry of entries) {
50039
+ const skillPath = skillFilePath(dir, entry);
50040
+ if (!existsSync28(skillPath)) continue;
50041
+ try {
50042
+ const parsed = parseSkillMd(readFileSync20(skillPath, "utf8"), skillPath);
50043
+ if (!parsed) continue;
50044
+ if (seen.has(parsed.name)) continue;
50045
+ seen.add(parsed.name);
50046
+ out.push(entryFromParsed(parsed, projectRoot));
50047
+ } catch {
50048
+ }
50049
+ }
50050
+ }
50051
+ function listSkillsSnapshot(projectRoot) {
50052
+ const root = projectRoot && projectRoot.trim() ? projectRoot.trim() : null;
50053
+ const userSkillsDir = getUserSkillsDir();
50054
+ const projectSkillsDir = root ? getProjectSkillsDir(root) : null;
50055
+ const skills = [];
50056
+ const seen = /* @__PURE__ */ new Set();
50057
+ if (root) {
50058
+ scanSkillsDir(join20(root, ".zelari", "skills"), root, seen, skills);
50059
+ scanSkillsDir(join20(root, ".claude", "skills"), root, seen, skills);
50060
+ scanSkillsDir(join20(root, ".opencode", "skills"), root, seen, skills);
50061
+ }
50062
+ scanSkillsDir(userSkillsDir, root, seen, skills);
50063
+ for (const s of listCodingSkills()) {
50064
+ if (seen.has(s.id)) continue;
50065
+ seen.add(s.id);
50066
+ if (s.builtin) {
50067
+ skills.push(entryFromBuiltin(s));
50068
+ }
50069
+ }
50070
+ skills.sort((a, b) => {
50071
+ const order = (s) => s === "project" ? 0 : s === "user" ? 1 : s === "compat" ? 2 : 3;
50072
+ const d = order(a.scope) - order(b.scope);
50073
+ if (d !== 0) return d;
50074
+ return a.id.localeCompare(b.id);
50075
+ });
50076
+ return { userSkillsDir, projectSkillsDir, skills };
50077
+ }
50078
+ function serializeSkillMd(opts) {
50079
+ const name = opts.name.trim().toLowerCase();
50080
+ const description = opts.description.trim();
50081
+ const body = opts.body.trim();
50082
+ const lines = ["---", `name: ${name}`, `description: ${description}`];
50083
+ const cat = (opts.category ?? "").trim().toLowerCase();
50084
+ if (cat && CODING_CATEGORIES2.has(cat)) {
50085
+ lines.push(`category: ${cat}`);
50086
+ }
50087
+ if (opts.tools && opts.tools.length > 0) {
50088
+ lines.push(`tools: [${opts.tools.map((t) => t.trim()).filter(Boolean).join(", ")}]`);
50089
+ }
50090
+ const cost = (opts.cost ?? "").trim().toLowerCase();
50091
+ if (cost === "low" || cost === "medium" || cost === "high") {
50092
+ lines.push(`cost: ${cost}`);
50093
+ }
50094
+ lines.push("---", "", body, "");
50095
+ return lines.join("\n");
50096
+ }
50097
+ function upsertSkill(opts) {
50098
+ const name = opts.name.trim().toLowerCase();
50099
+ if (!name || !NAME_RE.test(name)) {
50100
+ return {
50101
+ ok: false,
50102
+ error: "Invalid skill name (use lowercase letters, digits, hyphens; max 64 chars)"
50103
+ };
50104
+ }
50105
+ const description = opts.description.trim();
50106
+ if (!description) {
50107
+ return { ok: false, error: "description is required" };
50108
+ }
50109
+ const body = opts.body.trim();
50110
+ if (!body) {
50111
+ return { ok: false, error: "body is required" };
50112
+ }
50113
+ let dir;
50114
+ if (opts.scope === "user") {
50115
+ dir = getUserSkillsDir();
50116
+ } else {
50117
+ const root = opts.projectRoot?.trim();
50118
+ if (!root) {
50119
+ return {
50120
+ ok: false,
50121
+ error: "projectRoot required for project scope (Open Folder first)"
50122
+ };
50123
+ }
50124
+ dir = getProjectSkillsDir(root);
50125
+ }
50126
+ const path104 = skillFilePath(dir, name);
50127
+ const content = serializeSkillMd({
50128
+ name,
50129
+ description,
50130
+ body,
50131
+ category: opts.category,
50132
+ tools: opts.tools,
50133
+ cost: opts.cost
50134
+ });
50135
+ const parsed = parseSkillMd(content, path104);
50136
+ if (!parsed) {
50137
+ return { ok: false, error: "Generated SKILL.md failed validation" };
50138
+ }
50139
+ mkdirSync13(dirname4(path104), { recursive: true });
50140
+ writeFileSync16(path104, content, "utf8");
50141
+ return { ok: true, path: path104 };
50142
+ }
50143
+ function removeSkill(opts) {
50144
+ const name = opts.name.trim().toLowerCase();
50145
+ if (!name) {
50146
+ return { ok: false, error: "name is required" };
50147
+ }
50148
+ let dir;
50149
+ if (opts.scope === "user") {
50150
+ dir = getUserSkillsDir();
50151
+ } else {
50152
+ const root = opts.projectRoot?.trim();
50153
+ if (!root) {
50154
+ return { ok: false, error: "projectRoot required for project scope" };
50155
+ }
50156
+ dir = getProjectSkillsDir(root);
50157
+ }
50158
+ const skillDir = join20(dir, name);
50159
+ const path104 = skillFilePath(dir, name);
50160
+ if (!existsSync28(path104) && !existsSync28(skillDir)) {
50161
+ return { ok: false, error: `Skill "${name}" not found in ${dir}` };
50162
+ }
50163
+ try {
50164
+ rmSync3(skillDir, { recursive: true, force: true });
50165
+ } catch (err) {
50166
+ return {
50167
+ ok: false,
50168
+ error: err instanceof Error ? err.message : String(err)
50169
+ };
50170
+ }
50171
+ return { ok: true, path: path104 };
50172
+ }
50173
+ var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
50174
+ var init_skillConfigIo = __esm({
50175
+ "src/cli/skillConfigIo.ts"() {
50176
+ "use strict";
50177
+ init_skills2();
50178
+ init_skillsMd();
50179
+ init_skillCategories();
50180
+ init_paths();
50181
+ NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
50182
+ BUILTIN_SKILL_MODULES = [
50183
+ "@zelari/core/skills/builtin/debugging",
50184
+ "@zelari/core/skills/builtin/docs",
50185
+ "@zelari/core/skills/builtin/git-ops",
50186
+ "@zelari/core/skills/builtin/planning",
50187
+ "@zelari/core/skills/builtin/refactoring",
50188
+ "@zelari/core/skills/builtin/review",
50189
+ "@zelari/core/skills/builtin/unrealEditor",
50190
+ "@zelari/core/skills/builtin/testing",
50191
+ "@zelari/core/skills/builtin/schema-loop",
50192
+ "@zelari/core/skills/builtin/computer-use-cua",
50193
+ "@zelari/core/skills/builtin/qwen-mm-plugins"
50194
+ ];
50195
+ builtinsLoaded = false;
50196
+ }
50197
+ });
50198
+
50199
+ // src/cli/tools/createSkillTool.ts
50200
+ import { existsSync as existsSync29 } from "node:fs";
50201
+ import { resolve as resolve3, sep } from "node:path";
50202
+ function createCreateSkillTool(opts) {
50203
+ const root = opts?.cwd ?? process.cwd();
50204
+ return {
50205
+ name: "create_skill",
50206
+ 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.',
50207
+ permissions: ["write"],
50208
+ timeoutMs: 1e4,
50209
+ inputSchema: CreateSkillArgsSchema,
50210
+ execute: async (args) => {
50211
+ const name = typeof args?.name === "string" ? args.name.trim() : "";
50212
+ if (!NAME_RE2.test(name)) {
50213
+ return typedErr(
50214
+ `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").`
50215
+ );
50216
+ }
50217
+ const description = (args?.description ?? "").replace(/\s+/g, " ").trim();
50218
+ if (!description) {
50219
+ return typedErr("description is required: one line explaining when to use this skill.");
50220
+ }
50221
+ const instructions = (args?.instructions ?? "").trim();
50222
+ if (!instructions) {
50223
+ return typedErr(
50224
+ "instructions is required: the markdown body of the skill (goal, steps, commands, pitfalls)."
50225
+ );
50226
+ }
50227
+ const scope = args?.scope === "user" ? "user" : "project";
50228
+ const overwrite = args?.overwrite === true;
50229
+ const dir = scope === "user" ? getUserSkillsDir() : getProjectSkillsDir(root);
50230
+ const skillDir = resolve3(dir, name);
50231
+ const path104 = resolve3(skillDir, "SKILL.md");
50232
+ if (!path104.startsWith(resolve3(dir) + sep)) {
50233
+ return typedErr(
50234
+ `Refused: "${name}" would resolve outside the ${scope} skills directory (${resolve3(dir)}).`
50235
+ );
50236
+ }
50237
+ const existed = existsSync29(path104);
50238
+ if (existed && !overwrite) {
50239
+ return typedErr(
50240
+ `Skill "${name}" already exists at ${path104} \u2014 nothing was written. Call create_skill again with overwrite: true to replace it, or pick another name.`
50241
+ );
50242
+ }
50243
+ const written = upsertSkill({
50244
+ scope,
50245
+ name,
50246
+ description,
50247
+ body: instructions,
50248
+ projectRoot: root
50249
+ });
50250
+ if (!written.ok) {
50251
+ return typedErr(`Could not write SKILL.md for "${name}": ${written.error}`);
50252
+ }
50253
+ let loadable = false;
50254
+ try {
50255
+ loadSkillMdSkills(root);
50256
+ const registered = getCodingSkillById(name);
50257
+ loadable = (registered?.systemPromptFragment ?? "").trim() === instructions;
50258
+ } catch {
50259
+ }
50260
+ const verb = existed ? "Replaced existing skill" : "Created skill";
50261
+ const lines = [
50262
+ `${verb} "${name}" (${scope} scope).`,
50263
+ `Saved to: ${written.path}`
50264
+ ];
50265
+ if (!loadable) {
50266
+ lines.push(
50267
+ `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.`
50268
+ );
50269
+ }
50270
+ lines.push(
50271
+ `Use it now: \`/skill ${name}\` \u2014 or call the \`skill\` tool with name "${name}". Skills are discovered on demand, so no restart is needed.`
50272
+ );
50273
+ return typedOk({
50274
+ name,
50275
+ scope,
50276
+ path: written.path,
50277
+ overwritten: existed,
50278
+ loadable,
50279
+ message: lines.join("\n")
50280
+ });
50281
+ }
50282
+ };
50283
+ }
50284
+ var NAME_RE2, CreateSkillArgsSchema;
50285
+ var init_createSkillTool = __esm({
50286
+ "src/cli/tools/createSkillTool.ts"() {
50287
+ "use strict";
50288
+ init_zod();
50289
+ init_skills2();
50290
+ init_toolTypes();
50291
+ init_skillConfigIo();
50292
+ init_skillsMd();
50293
+ NAME_RE2 = /^[a-z0-9][a-z0-9-]{1,39}$/;
50294
+ CreateSkillArgsSchema = external_exports.object({
50295
+ name: external_exports.string().regex(NAME_RE2).describe(
50296
+ "Skill id/slug: 2-40 chars, lowercase letters/digits/hyphens, must start with a letter or digit."
50297
+ ),
50298
+ description: external_exports.string().min(1).describe(
50299
+ "One-line description shown in the skill catalog (when to use it). Collapsed to a single line."
50300
+ ),
50301
+ instructions: external_exports.string().min(1).describe(
50302
+ "The skill body, in markdown: goal, step-by-step procedure, exact commands, pitfalls. Becomes the loaded instructions verbatim."
50303
+ ),
50304
+ scope: external_exports.enum(["project", "user"]).optional().describe(
50305
+ "Where to save: 'project' (default) = <cwd>/.zelari/skills (repo-shared); 'user' = ~/.zelari-code/skills (every project)."
50306
+ ),
50307
+ overwrite: external_exports.boolean().optional().describe("Default false: fail if a SKILL.md for that name already exists. Pass true to replace it.")
50308
+ });
50309
+ }
50310
+ });
50311
+
49913
50312
  // src/cli/tools/todoTools.ts
49914
50313
  function createTodoWriteTool() {
49915
50314
  return {
@@ -50301,13 +50700,13 @@ async function scanTsbuildinfo(root) {
50301
50700
  return found;
50302
50701
  }
50303
50702
  async function gitStatusPorcelain(root) {
50304
- return new Promise((resolve9) => {
50703
+ return new Promise((resolve10) => {
50305
50704
  const child = spawn9("git", ["status", "--porcelain"], { cwd: root, shell: false });
50306
50705
  let out = "";
50307
50706
  child.stdout.on("data", (d) => out += d.toString());
50308
50707
  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"}>`));
50708
+ child.on("error", () => resolve10("<git-unavailable>"));
50709
+ child.on("close", (code) => resolve10(code === 0 ? out : `<git-exit-${code ?? "none"}>`));
50311
50710
  });
50312
50711
  }
50313
50712
  async function fingerprintWorkspace(root) {
@@ -50354,14 +50753,14 @@ var init_inspectTypecheckSafety = __esm({
50354
50753
  // src/cli/tools/inspectCommand.ts
50355
50754
  import { spawn as spawn10 } from "node:child_process";
50356
50755
  import { createHash as createHash14 } from "node:crypto";
50357
- import { existsSync as existsSync28, promises as fs23 } from "node:fs";
50756
+ import { existsSync as existsSync30, promises as fs23 } from "node:fs";
50358
50757
  import os2 from "node:os";
50359
50758
  import path52 from "node:path";
50360
50759
  function resolveNodeModuleBin(start, rel2) {
50361
50760
  let dir = path52.resolve(start);
50362
50761
  for (; ; ) {
50363
50762
  const candidate = path52.join(dir, "node_modules", rel2);
50364
- if (existsSync28(candidate)) return candidate;
50763
+ if (existsSync30(candidate)) return candidate;
50365
50764
  const parent = path52.dirname(dir);
50366
50765
  if (parent === dir) return void 0;
50367
50766
  dir = parent;
@@ -50465,12 +50864,12 @@ function buildInspectCommand(op, ctx) {
50465
50864
  }
50466
50865
  }
50467
50866
  function runSpawn(command, argv, opts) {
50468
- return new Promise((resolve9) => {
50867
+ return new Promise((resolve10) => {
50469
50868
  let child;
50470
50869
  try {
50471
50870
  child = spawn10(command, argv, { cwd: opts.cwd, shell: false });
50472
50871
  } catch (err) {
50473
- resolve9({ code: null, stdout: "", stderr: String(err), timedOut: false, spawnError: String(err) });
50872
+ resolve10({ code: null, stdout: "", stderr: String(err), timedOut: false, spawnError: String(err) });
50474
50873
  return;
50475
50874
  }
50476
50875
  let stdout = "";
@@ -50492,14 +50891,14 @@ function runSpawn(command, argv, opts) {
50492
50891
  if (settled) return;
50493
50892
  settled = true;
50494
50893
  clearTimeout(timer);
50495
- resolve9({ code: null, stdout, stderr, timedOut, spawnError: err.message });
50894
+ resolve10({ code: null, stdout, stderr, timedOut, spawnError: err.message });
50496
50895
  });
50497
50896
  child.on("close", (code) => {
50498
50897
  if (settled) return;
50499
50898
  settled = true;
50500
50899
  clearTimeout(timer);
50501
50900
  opts.signal?.removeEventListener("abort", onAbort);
50502
- resolve9({ code, stdout, stderr, timedOut });
50901
+ resolve10({ code, stdout, stderr, timedOut });
50503
50902
  });
50504
50903
  });
50505
50904
  }
@@ -50784,7 +51183,7 @@ function createExecProcessTool(root, opts = {}) {
50784
51183
  const msg = err instanceof SandboxViolationError ? err.message : String(err);
50785
51184
  return typedErr(`[sandbox] ${msg}`);
50786
51185
  }
50787
- return new Promise((resolve9) => {
51186
+ return new Promise((resolve10) => {
50788
51187
  const start = Date.now();
50789
51188
  const spec = { ...baseSpec, network: opts.resolveNetwork?.(input) ?? baseSpec.network };
50790
51189
  const res = spawnJailed(spec, {
@@ -50796,11 +51195,11 @@ function createExecProcessTool(root, opts = {}) {
50796
51195
  envExtras: { CI: process.env.CI ?? "1" }
50797
51196
  });
50798
51197
  if (res.outcome === "denied") {
50799
- resolve9(typedErr(res.reason));
51198
+ resolve10(typedErr(res.reason));
50800
51199
  return;
50801
51200
  }
50802
51201
  if (res.outcome === "failed") {
50803
- resolve9(typedErr(`exec_process could not launch ${input.program}: ${res.reason}`));
51202
+ resolve10(typedErr(`exec_process could not launch ${input.program}: ${res.reason}`));
50804
51203
  return;
50805
51204
  }
50806
51205
  const meta3 = res.notice ? { status: "complete", warnings: [`os-jail: ${res.notice}`] } : void 0;
@@ -50816,7 +51215,7 @@ function createExecProcessTool(root, opts = {}) {
50816
51215
  const timeoutMs2 = typeof input.timeoutMs === "number" && input.timeoutMs > 0 ? input.timeoutMs : 3e4;
50817
51216
  const timer = setTimeout(() => {
50818
51217
  child.kill("SIGTERM");
50819
- resolve9(
51218
+ resolve10(
50820
51219
  typedErr(
50821
51220
  `exec_process timed out after ${timeoutMs2}ms running: ${[input.program, ...argv].join(" ")}`
50822
51221
  )
@@ -50824,11 +51223,11 @@ function createExecProcessTool(root, opts = {}) {
50824
51223
  }, timeoutMs2);
50825
51224
  child.on("error", (err) => {
50826
51225
  clearTimeout(timer);
50827
- resolve9(typedErr(`exec_process could not launch ${input.program}: ${err.message}`));
51226
+ resolve10(typedErr(`exec_process could not launch ${input.program}: ${err.message}`));
50828
51227
  });
50829
51228
  child.on("close", (code) => {
50830
51229
  clearTimeout(timer);
50831
- resolve9(
51230
+ resolve10(
50832
51231
  typedOk(
50833
51232
  {
50834
51233
  exitCode: code ?? -1,
@@ -51357,12 +51756,12 @@ var init_client = __esm({
51357
51756
  if (this.closed) return Promise.reject(new Error("LSP transport is closed"));
51358
51757
  const id3 = this.nextId++;
51359
51758
  const msg = { jsonrpc: "2.0", id: id3, method, ...params !== void 0 ? { params } : {} };
51360
- return new Promise((resolve9, reject) => {
51759
+ return new Promise((resolve10, reject) => {
51361
51760
  const timer = setTimeout(() => {
51362
51761
  this.pending.delete(id3);
51363
51762
  reject(new Error(`LSP request "${method}" timed out after ${this.timeoutMs}ms`));
51364
51763
  }, this.timeoutMs);
51365
- this.pending.set(id3, { resolve: resolve9, reject, timer });
51764
+ this.pending.set(id3, { resolve: resolve10, reject, timer });
51366
51765
  this.transport.send(encodeMessage(msg));
51367
51766
  });
51368
51767
  }
@@ -51481,7 +51880,7 @@ var init_servers = __esm({
51481
51880
 
51482
51881
  // src/cli/lsp/manager.ts
51483
51882
  import { spawn as spawn11 } from "node:child_process";
51484
- import { readFileSync as readFileSync20 } from "node:fs";
51883
+ import { readFileSync as readFileSync21 } from "node:fs";
51485
51884
  import path54 from "node:path";
51486
51885
  function processTransport(child) {
51487
51886
  return {
@@ -51639,8 +52038,8 @@ var init_manager = __esm({
51639
52038
  if (cached2 !== void 0) return cached2;
51640
52039
  let resolveInit;
51641
52040
  let rejectInit;
51642
- const initialized = new Promise((resolve9, reject) => {
51643
- resolveInit = resolve9;
52041
+ const initialized = new Promise((resolve10, reject) => {
52042
+ resolveInit = resolve10;
51644
52043
  rejectInit = reject;
51645
52044
  });
51646
52045
  let entry = null;
@@ -51695,7 +52094,7 @@ var init_manager = __esm({
51695
52094
  const uri = pathToUri(file2);
51696
52095
  let text;
51697
52096
  try {
51698
- text = readFileSync20(file2, "utf8");
52097
+ text = readFileSync21(file2, "utf8");
51699
52098
  } catch {
51700
52099
  text = "";
51701
52100
  }
@@ -51975,7 +52374,7 @@ var init_store2 = __esm({
51975
52374
  });
51976
52375
 
51977
52376
  // src/cli/semantic/index.ts
51978
- import { promises as fs24, existsSync as existsSync29, readFileSync as readFileSync21 } from "node:fs";
52377
+ import { promises as fs24, existsSync as existsSync31, readFileSync as readFileSync22 } from "node:fs";
51979
52378
  import path55 from "node:path";
51980
52379
  import { createHash as createHash15 } from "node:crypto";
51981
52380
  function getIndexPath(root) {
@@ -52058,9 +52457,9 @@ async function saveIndex(root, data) {
52058
52457
  }
52059
52458
  function loadIndex(root) {
52060
52459
  const file2 = getIndexPath(root);
52061
- if (!existsSync29(file2)) return null;
52460
+ if (!existsSync31(file2)) return null;
52062
52461
  try {
52063
- const parsed = JSON.parse(readFileSync21(file2, "utf8"));
52462
+ const parsed = JSON.parse(readFileSync22(file2, "utf8"));
52064
52463
  if (parsed && Array.isArray(parsed.chunks)) return parsed;
52065
52464
  } catch {
52066
52465
  }
@@ -52165,14 +52564,14 @@ function resolveStreamTimeouts(capabilities) {
52165
52564
  };
52166
52565
  }
52167
52566
  function abortableSleep(ms, signal) {
52168
- return new Promise((resolve9) => {
52169
- if (signal?.aborted) return resolve9();
52170
- const t = setTimeout(resolve9, ms);
52567
+ return new Promise((resolve10) => {
52568
+ if (signal?.aborted) return resolve10();
52569
+ const t = setTimeout(resolve10, ms);
52171
52570
  signal?.addEventListener(
52172
52571
  "abort",
52173
52572
  () => {
52174
52573
  clearTimeout(t);
52175
- resolve9();
52574
+ resolve10();
52176
52575
  },
52177
52576
  { once: true }
52178
52577
  );
@@ -53046,8 +53445,8 @@ async function runBrowserCheck(options, loader) {
53046
53445
  const failedRequests = [];
53047
53446
  const evaluateResults = [];
53048
53447
  const base2 = { ok: false, consoleErrors, pageErrors, failedRequests };
53049
- const resolve9 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
53050
- const pw = await resolve9();
53448
+ const resolve10 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
53449
+ const pw = await resolve10();
53051
53450
  if (!pw) {
53052
53451
  return {
53053
53452
  ...base2,
@@ -53422,18 +53821,18 @@ __export(targets_exports, {
53422
53821
  });
53423
53822
  import {
53424
53823
  chmodSync,
53425
- existsSync as existsSync30,
53426
- mkdirSync as mkdirSync13,
53427
- readFileSync as readFileSync22,
53428
- writeFileSync as writeFileSync16
53824
+ existsSync as existsSync32,
53825
+ mkdirSync as mkdirSync14,
53826
+ readFileSync as readFileSync23,
53827
+ writeFileSync as writeFileSync17
53429
53828
  } from "node:fs";
53430
- import { dirname as dirname4, join as join20 } from "node:path";
53829
+ import { dirname as dirname5, join as join21 } from "node:path";
53431
53830
  import { spawn as spawn12 } from "node:child_process";
53432
53831
  function getSshTargetsPath() {
53433
- return join20(zelariHome(), "ssh-targets.json");
53832
+ return join21(zelariHome(), "ssh-targets.json");
53434
53833
  }
53435
53834
  function getSshSecretsPath() {
53436
- return join20(zelariHome(), "ssh-secrets.json");
53835
+ return join21(zelariHome(), "ssh-secrets.json");
53437
53836
  }
53438
53837
  function normalizeAuth(auth) {
53439
53838
  if (auth === "keyPath") return "keyPath";
@@ -53441,21 +53840,21 @@ function normalizeAuth(auth) {
53441
53840
  return "agent";
53442
53841
  }
53443
53842
  function readSecrets() {
53444
- const path103 = getSshSecretsPath();
53445
- if (!existsSync30(path103)) return {};
53843
+ const path104 = getSshSecretsPath();
53844
+ if (!existsSync32(path104)) return {};
53446
53845
  try {
53447
- return JSON.parse(readFileSync22(path103, "utf8"));
53846
+ return JSON.parse(readFileSync23(path104, "utf8"));
53448
53847
  } catch {
53449
53848
  return {};
53450
53849
  }
53451
53850
  }
53452
53851
  function writeSecrets(data) {
53453
- const path103 = getSshSecretsPath();
53454
- mkdirSync13(dirname4(path103), { recursive: true });
53455
- writeFileSync16(path103, `${JSON.stringify(data, null, 2)}
53852
+ const path104 = getSshSecretsPath();
53853
+ mkdirSync14(dirname5(path104), { recursive: true });
53854
+ writeFileSync17(path104, `${JSON.stringify(data, null, 2)}
53456
53855
  `, "utf8");
53457
53856
  try {
53458
- chmodSync(path103, 384);
53857
+ chmodSync(path104, 384);
53459
53858
  } catch {
53460
53859
  }
53461
53860
  }
@@ -53484,10 +53883,10 @@ function deleteSshPassword(id3) {
53484
53883
  writeSecrets({ passwords });
53485
53884
  }
53486
53885
  function readStore2() {
53487
- const path103 = getSshTargetsPath();
53488
- if (!existsSync30(path103)) return [];
53886
+ const path104 = getSshTargetsPath();
53887
+ if (!existsSync32(path104)) return [];
53489
53888
  try {
53490
- const parsed = JSON.parse(readFileSync22(path103, "utf8"));
53889
+ const parsed = JSON.parse(readFileSync23(path104, "utf8"));
53491
53890
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
53492
53891
  return list.filter(
53493
53892
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -53502,11 +53901,11 @@ function readStore2() {
53502
53901
  }
53503
53902
  }
53504
53903
  function writeStore2(targets) {
53505
- const path103 = getSshTargetsPath();
53506
- mkdirSync13(dirname4(path103), { recursive: true });
53904
+ const path104 = getSshTargetsPath();
53905
+ mkdirSync14(dirname5(path104), { recursive: true });
53507
53906
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
53508
- writeFileSync16(
53509
- path103,
53907
+ writeFileSync17(
53908
+ path104,
53510
53909
  `${JSON.stringify({ targets: clean }, null, 2)}
53511
53910
  `,
53512
53911
  "utf8"
@@ -53606,17 +54005,17 @@ function buildSshBaseArgs(target) {
53606
54005
  return args;
53607
54006
  }
53608
54007
  function ensureAskpassHelper() {
53609
- const dir = join20(zelariHome(), "ssh-helpers");
53610
- mkdirSync13(dir, { recursive: true });
53611
- const cjs = join20(dir, "askpass.cjs");
53612
- writeFileSync16(
54008
+ const dir = join21(zelariHome(), "ssh-helpers");
54009
+ mkdirSync14(dir, { recursive: true });
54010
+ const cjs = join21(dir, "askpass.cjs");
54011
+ writeFileSync17(
53613
54012
  cjs,
53614
54013
  "process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
53615
54014
  "utf8"
53616
54015
  );
53617
54016
  if (process.platform === "win32") {
53618
- const cmd = join20(dir, "askpass.cmd");
53619
- writeFileSync16(
54017
+ const cmd = join21(dir, "askpass.cmd");
54018
+ writeFileSync17(
53620
54019
  cmd,
53621
54020
  `@echo off\r
53622
54021
  node "%~dp0askpass.cjs"\r
@@ -53625,8 +54024,8 @@ node "%~dp0askpass.cjs"\r
53625
54024
  );
53626
54025
  return cmd;
53627
54026
  }
53628
- const sh = join20(dir, "askpass.sh");
53629
- writeFileSync16(
54027
+ const sh = join21(dir, "askpass.sh");
54028
+ writeFileSync17(
53630
54029
  sh,
53631
54030
  `#!/bin/sh
53632
54031
  exec node "$(dirname "$0")/askpass.cjs"
@@ -53640,9 +54039,9 @@ exec node "$(dirname "$0")/askpass.cjs"
53640
54039
  return sh;
53641
54040
  }
53642
54041
  function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53643
- return new Promise((resolve9) => {
54042
+ return new Promise((resolve10) => {
53644
54043
  if (target.auth === "password" && !getSshPassword(target.id)) {
53645
- resolve9({
54044
+ resolve10({
53646
54045
  code: 1,
53647
54046
  stdout: "",
53648
54047
  stderr: "No password stored for this target \u2014 edit target and set password"
@@ -53674,7 +54073,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53674
54073
  });
53675
54074
  const timer = setTimeout(() => {
53676
54075
  child.kill("SIGTERM");
53677
- resolve9({
54076
+ resolve10({
53678
54077
  code: 124,
53679
54078
  stdout,
53680
54079
  stderr: stderr + "\n[ssh] timeout"
@@ -53682,7 +54081,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53682
54081
  }, timeoutMs2);
53683
54082
  child.on("error", (err) => {
53684
54083
  clearTimeout(timer);
53685
- resolve9({
54084
+ resolve10({
53686
54085
  code: 127,
53687
54086
  stdout,
53688
54087
  stderr: err.message.includes("ENOENT") ? "ssh not found on PATH \u2014 install OpenSSH client" : err.message
@@ -53690,7 +54089,7 @@ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
53690
54089
  });
53691
54090
  child.on("close", (code) => {
53692
54091
  clearTimeout(timer);
53693
- resolve9({ code: code ?? 1, stdout, stderr });
54092
+ resolve10({ code: code ?? 1, stdout, stderr });
53694
54093
  });
53695
54094
  });
53696
54095
  }
@@ -53699,9 +54098,9 @@ function readSshPublicKey(keyOrPubPath) {
53699
54098
  if (!raw) return { ok: false, error: "Empty path" };
53700
54099
  const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
53701
54100
  for (const p3 of candidates) {
53702
- if (!existsSync30(p3)) continue;
54101
+ if (!existsSync32(p3)) continue;
53703
54102
  try {
53704
- const content = readFileSync22(p3, "utf8").trim();
54103
+ const content = readFileSync23(p3, "utf8").trim();
53705
54104
  if (!content) continue;
53706
54105
  if (/BEGIN .*PRIVATE KEY/i.test(content)) {
53707
54106
  return {
@@ -53752,11 +54151,11 @@ function formatSshTargetsForPrompt() {
53752
54151
  ];
53753
54152
  for (const t of targets) {
53754
54153
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
53755
- const path103 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
54154
+ const path104 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
53756
54155
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
53757
54156
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
53758
54157
  lines.push(
53759
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path103}${tags}${allow}`
54158
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path104}${tags}${allow}`
53760
54159
  );
53761
54160
  }
53762
54161
  return lines.join("\n");
@@ -54467,14 +54866,14 @@ function backoffDelay2(attempt, retryAfterHeader) {
54467
54866
  return Math.min(BACKOFF_BASE_MS2 * 2 ** attempt, BACKOFF_CAP_MS2);
54468
54867
  }
54469
54868
  function abortableSleep2(ms, signal) {
54470
- return new Promise((resolve9) => {
54471
- if (signal?.aborted) return resolve9();
54472
- const t = setTimeout(resolve9, ms);
54869
+ return new Promise((resolve10) => {
54870
+ if (signal?.aborted) return resolve10();
54871
+ const t = setTimeout(resolve10, ms);
54473
54872
  signal?.addEventListener(
54474
54873
  "abort",
54475
54874
  () => {
54476
54875
  clearTimeout(t);
54477
- resolve9();
54876
+ resolve10();
54478
54877
  },
54479
54878
  { once: true }
54480
54879
  );
@@ -55141,7 +55540,7 @@ __export(folderTrust_exports, {
55141
55540
  trustFolder: () => trustFolder,
55142
55541
  untrustFolder: () => untrustFolder
55143
55542
  });
55144
- import { existsSync as existsSync31, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "node:fs";
55543
+ import { existsSync as existsSync33, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "node:fs";
55145
55544
  import path60 from "node:path";
55146
55545
  function trustStorePath() {
55147
55546
  return _overrideStorePath ?? trustConfigPath();
@@ -55152,7 +55551,7 @@ function normalize6(p3) {
55152
55551
  }
55153
55552
  function readStore3() {
55154
55553
  try {
55155
- const raw = readFileSync23(trustStorePath(), "utf8");
55554
+ const raw = readFileSync24(trustStorePath(), "utf8");
55156
55555
  const parsed = JSON.parse(raw);
55157
55556
  if (parsed && Array.isArray(parsed.folders)) return parsed;
55158
55557
  return DEFAULT_STORE;
@@ -55163,8 +55562,8 @@ function readStore3() {
55163
55562
  function writeStore3(store6) {
55164
55563
  const p3 = trustStorePath();
55165
55564
  try {
55166
- mkdirSync14(path60.dirname(p3), { recursive: true });
55167
- writeFileSync17(p3, JSON.stringify(store6, null, 2), "utf8");
55565
+ mkdirSync15(path60.dirname(p3), { recursive: true });
55566
+ writeFileSync18(p3, JSON.stringify(store6, null, 2), "utf8");
55168
55567
  } catch (err) {
55169
55568
  throw new Error(
55170
55569
  `failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
@@ -55219,7 +55618,7 @@ function getTrustStorePath() {
55219
55618
  return trustStorePath();
55220
55619
  }
55221
55620
  function hasTrustStore() {
55222
- return existsSync31(trustStorePath());
55621
+ return existsSync33(trustStorePath());
55223
55622
  }
55224
55623
  function _setTrustStorePathForTests(p3) {
55225
55624
  _overrideStorePath = p3;
@@ -55235,13 +55634,13 @@ var init_folderTrust = __esm({
55235
55634
  });
55236
55635
 
55237
55636
  // src/cli/safety/lifecycleHooks.ts
55238
- import { join as join21 } from "node:path";
55239
- import { readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
55637
+ import { join as join22 } from "node:path";
55638
+ import { readdirSync as readdirSync7, statSync as statSync3 } from "node:fs";
55240
55639
  function globalHooksDir() {
55241
- return join21(zelariHome(), "hooks");
55640
+ return join22(zelariHome(), "hooks");
55242
55641
  }
55243
55642
  function projectHooksDir(projectRoot) {
55244
- return join21(projectRoot, ".zelari", "hooks");
55643
+ return join22(projectRoot, ".zelari", "hooks");
55245
55644
  }
55246
55645
  function resolveHookFailureMode(override, env = process.env) {
55247
55646
  const v = override?.trim().toLowerCase();
@@ -55254,7 +55653,7 @@ function fingerprintHookDirs(dirs) {
55254
55653
  for (const dir of dirs) {
55255
55654
  let names;
55256
55655
  try {
55257
- names = readdirSync6(dir).filter((f) => f.endsWith(".json")).sort();
55656
+ names = readdirSync7(dir).filter((f) => f.endsWith(".json")).sort();
55258
55657
  } catch {
55259
55658
  parts.push(`${dir}:missing`);
55260
55659
  continue;
@@ -55268,7 +55667,7 @@ function fingerprintHookDirs(dirs) {
55268
55667
  continue;
55269
55668
  }
55270
55669
  for (const name of names) {
55271
- const full = join21(dir, name);
55670
+ const full = join22(dir, name);
55272
55671
  try {
55273
55672
  const st = statSync3(full);
55274
55673
  parts.push(`${full}:${st.mtimeMs}:${st.size}`);
@@ -55553,8 +55952,8 @@ function networkClaimsForUrls(urls) {
55553
55952
  }
55554
55953
  function mcpClaimFromName(registryName) {
55555
55954
  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) };
55955
+ const sep5 = rest.indexOf("_");
55956
+ return sep5 <= 0 || sep5 === rest.length - 1 ? null : { kind: "mcp", server: rest.slice(0, sep5), tool: rest.slice(sep5 + 1) };
55558
55957
  }
55559
55958
  function browserActionUrls(a) {
55560
55959
  const actions = Array.isArray(a["actions"]) ? a["actions"] : [];
@@ -55968,9 +56367,11 @@ __export(toolRegistry_exports, {
55968
56367
  createKrakenSubAgentContextFactory: () => createKrakenSubAgentContextFactory,
55969
56368
  getCliToolCatalogEntries: () => getCliToolCatalogEntries,
55970
56369
  registerCliToolsIntoCouncilCatalog: () => registerCliToolsIntoCouncilCatalog,
56370
+ resolveTentacleThinkingInput: () => resolveTentacleThinkingInput,
56371
+ tentacleThinkingEnvKey: () => tentacleThinkingEnvKey,
55971
56372
  wrapWithSandbox: () => wrapWithSandbox
55972
56373
  });
55973
- import { existsSync as existsSync32 } from "node:fs";
56374
+ import { existsSync as existsSync34 } from "node:fs";
55974
56375
  import path63 from "node:path";
55975
56376
  function createBuiltinToolRegistry(options = {}) {
55976
56377
  const root = options.root ?? process.cwd();
@@ -56127,6 +56528,10 @@ function createBuiltinToolRegistry(options = {}) {
56127
56528
  if (skillTool) {
56128
56529
  registry4.register(skillTool);
56129
56530
  }
56531
+ const skillCreateTool = enableSkill ? withPerm(createCreateSkillTool({ cwd: root })) : null;
56532
+ if (skillCreateTool) {
56533
+ registry4.register(skillCreateTool);
56534
+ }
56130
56535
  const enableTodos = options.enableTodos !== false && options.readOnly !== true && !gauntletParent && profile === "full";
56131
56536
  const todoWrite = enableTodos ? withPerm(createTodoWriteTool()) : null;
56132
56537
  const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
@@ -56154,6 +56559,7 @@ function createBuiltinToolRegistry(options = {}) {
56154
56559
  ...allowBash ? [safeBash, safeExecProcess] : [],
56155
56560
  ...askUserTool ? [askUserTool] : [],
56156
56561
  ...skillTool ? [skillTool] : [],
56562
+ ...skillCreateTool ? [skillCreateTool] : [],
56157
56563
  ...todoWrite ? [todoWrite] : [],
56158
56564
  ...todoRead ? [todoRead] : [],
56159
56565
  ...planTaskToolsWrapped.length > 0 ? planTaskToolsWrapped : []
@@ -56337,9 +56743,29 @@ function taskAgentToProfile(agent) {
56337
56743
  if (agent === "verify") return "verify";
56338
56744
  return "explore";
56339
56745
  }
56746
+ function tentacleThinkingEnvKey(agent) {
56747
+ if (agent === "explore") return "ZELARI_KRAKEN_EXPLORE_THINKING";
56748
+ if (agent === "verify") return "ZELARI_KRAKEN_VERIFY_THINKING";
56749
+ return "ZELARI_KRAKEN_GENERAL_THINKING";
56750
+ }
56751
+ function resolveTentacleThinkingInput(agent, taskArg, env = process.env) {
56752
+ const fromArg = typeof taskArg === "string" ? taskArg.trim() : "";
56753
+ const argWins = fromArg.length > 0 && fromArg.toLowerCase() !== "inherit";
56754
+ const key = tentacleThinkingEnvKey(agent);
56755
+ const raw = (argWins ? fromArg : (env[key] ?? "").trim()).trim();
56756
+ if (!raw) return void 0;
56757
+ if (isValidThinkingInput(raw)) return raw.toLowerCase();
56758
+ if (!warnedThinkingInputs.has(raw)) {
56759
+ warnedThinkingInputs.add(raw);
56760
+ console.warn(
56761
+ `[kraken] ignoring invalid thinking effort "${raw}" for the ${agent} tentacle (${key}); using the provider default.`
56762
+ );
56763
+ }
56764
+ return void 0;
56765
+ }
56340
56766
  function createKrakenSubAgentContextFactory(opts) {
56341
56767
  const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride, parentPolicy, onPermissionAsk } = opts;
56342
- return async ({ agent, cwd: subCwd }) => {
56768
+ return async ({ agent, cwd: subCwd, thinkingEffort }) => {
56343
56769
  const cfg = providerOverride ? await providerConfigFor(providerOverride) : await providerFromEnv();
56344
56770
  if (!cfg) return null;
56345
56771
  const { resolveKrakenSubModelAsync: resolveKrakenSubModelAsync2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
@@ -56358,6 +56784,8 @@ function createKrakenSubAgentContextFactory(opts) {
56358
56784
  }
56359
56785
  }
56360
56786
  const subCfg = { ...effCfg, model };
56787
+ const thinkingInput = resolveTentacleThinkingInput(agent, thinkingEffort, process.env);
56788
+ if (thinkingInput) subCfg.thinking = parseThinkingSpec(thinkingInput);
56361
56789
  const subProfile = taskAgentToProfile(agent);
56362
56790
  const subRoot = subCwd || root;
56363
56791
  const agentPolicyForSubProfile = defaultPermissionPolicy({ auto: true });
@@ -56394,6 +56822,9 @@ function createKrakenSubAgentContextFactory(opts) {
56394
56822
  providerStream,
56395
56823
  model,
56396
56824
  provider: subCfg.providerId,
56825
+ // E (thinking): report the spec actually applied so the parent's
56826
+ // `agent_spawned` event can surface it (arg > kind env > inherited).
56827
+ ...subCfg.thinking !== void 0 ? { thinking: stringifyThinkingSpec(subCfg.thinking) } : {},
56397
56828
  ...model !== parentModel ? {
56398
56829
  fallback: {
56399
56830
  model: parentModel,
@@ -56617,7 +57048,7 @@ function claimedSourcePath(token, args, root) {
56617
57048
  const candidate = path63.isAbsolute(cleaned) ? path63.normalize(cleaned) : path63.resolve(base2, cleaned);
56618
57049
  try {
56619
57050
  const contained = resolveSandboxedPath(candidate, { root });
56620
- if (existsSync32(contained)) return contained;
57051
+ if (existsSync34(contained)) return contained;
56621
57052
  } catch {
56622
57053
  }
56623
57054
  }
@@ -56851,7 +57282,7 @@ function redactForAudit(args) {
56851
57282
  }
56852
57283
  return out;
56853
57284
  }
56854
- var HARNESS_BUILTIN_NAMES, DIAG_SOURCE_EXTENSIONS, EXEC_DIAGNOSTICS_MAX_FILES;
57285
+ var HARNESS_BUILTIN_NAMES, warnedThinkingInputs, DIAG_SOURCE_EXTENSIONS, EXEC_DIAGNOSTICS_MAX_FILES;
56855
57286
  var init_toolRegistry = __esm({
56856
57287
  "src/cli/toolRegistry.ts"() {
56857
57288
  "use strict";
@@ -56873,6 +57304,7 @@ var init_toolRegistry = __esm({
56873
57304
  init_krakenSelectTool();
56874
57305
  init_askUser();
56875
57306
  init_skillTool();
57307
+ init_createSkillTool();
56876
57308
  init_todoTools();
56877
57309
  init_planTaskTools();
56878
57310
  init_inspectCommand();
@@ -56890,6 +57322,7 @@ var init_toolRegistry = __esm({
56890
57322
  init_openai_compatible();
56891
57323
  init_resolveStream();
56892
57324
  init_providerConfig();
57325
+ init_thinking();
56893
57326
  init_crossProviderFailover();
56894
57327
  init_keyStore();
56895
57328
  init_toolPermissions();
@@ -56924,6 +57357,7 @@ var init_toolRegistry = __esm({
56924
57357
  "fetch_url",
56925
57358
  "web_search"
56926
57359
  ]);
57360
+ warnedThinkingInputs = /* @__PURE__ */ new Set();
56927
57361
  DIAG_SOURCE_EXTENSIONS = new Set(
56928
57362
  DEFAULT_PROVIDERS.flatMap((p3) => p3.extensions)
56929
57363
  );
@@ -56932,7 +57366,7 @@ var init_toolRegistry = __esm({
56932
57366
  });
56933
57367
 
56934
57368
  // 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";
57369
+ import { promises as fs27, existsSync as existsSync35, statSync as statSync4, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync16 } from "node:fs";
56936
57370
  import path64 from "node:path";
56937
57371
  async function readMetrics(file2) {
56938
57372
  let raw = "";
@@ -56983,7 +57417,7 @@ var init_metrics3 = __esm({
56983
57417
  writeQueue = Promise.resolve();
56984
57418
  constructor(file2) {
56985
57419
  this.file = file2 ?? metricsPath();
56986
- mkdirSync15(path64.dirname(this.file), { recursive: true });
57420
+ mkdirSync16(path64.dirname(this.file), { recursive: true });
56987
57421
  }
56988
57422
  /** Metrics file path — doctor/summary readers use this. */
56989
57423
  get filePath() {
@@ -56998,12 +57432,12 @@ var init_metrics3 = __esm({
56998
57432
  }
56999
57433
  /** Append a single record synchronously (file I/O). */
57000
57434
  append(rec) {
57001
- return new Promise((resolve9, reject) => {
57435
+ return new Promise((resolve10, reject) => {
57002
57436
  try {
57003
57437
  this.maybeRotate();
57004
57438
  const line = JSON.stringify(rec) + "\n";
57005
57439
  appendFileSync3(this.file, line, { encoding: "utf-8" });
57006
- resolve9();
57440
+ resolve10();
57007
57441
  } catch (err) {
57008
57442
  reject(err);
57009
57443
  }
@@ -57018,7 +57452,7 @@ var init_metrics3 = __esm({
57018
57452
  }
57019
57453
  /** If the file is over the rotation threshold, rotate it. */
57020
57454
  maybeRotate() {
57021
- if (!existsSync33(this.file)) return;
57455
+ if (!existsSync35(this.file)) return;
57022
57456
  try {
57023
57457
  const stat8 = statSync4(this.file);
57024
57458
  if (stat8.size >= METRICS_ROTATE_BYTES) {
@@ -58300,7 +58734,7 @@ var init_sqliteCodec = __esm({
58300
58734
  });
58301
58735
 
58302
58736
  // src/cli/memory/sqliteRpc.ts
58303
- import { existsSync as existsSync34 } from "node:fs";
58737
+ import { existsSync as existsSync36 } from "node:fs";
58304
58738
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
58305
58739
  import * as path69 from "node:path";
58306
58740
  import { Worker } from "node:worker_threads";
@@ -58313,7 +58747,7 @@ function isBusy(error51) {
58313
58747
  function resolveWorkerUrl() {
58314
58748
  const here = path69.dirname(fileURLToPath2(import.meta.url));
58315
58749
  const direct = path69.join(here, "sqliteWorker.mjs");
58316
- if (existsSync34(direct)) return pathToFileURL2(direct);
58750
+ if (existsSync36(direct)) return pathToFileURL2(direct);
58317
58751
  return pathToFileURL2(path69.join(here, "memory", "sqliteWorker.mjs"));
58318
58752
  }
58319
58753
  var SqliteWorkerRpc;
@@ -58388,8 +58822,8 @@ var init_sqliteRpc = __esm({
58388
58822
  const worker = this.worker;
58389
58823
  if (!worker) return Promise.reject(new Error("SQLite worker is not open."));
58390
58824
  const id3 = this.nextId++;
58391
- return new Promise((resolve9, reject) => {
58392
- this.pending.set(id3, { resolve: resolve9, reject });
58825
+ return new Promise((resolve10, reject) => {
58826
+ this.pending.set(id3, { resolve: resolve10, reject });
58393
58827
  worker.postMessage({ id: id3, operation, args });
58394
58828
  });
58395
58829
  }
@@ -58400,7 +58834,7 @@ var init_sqliteRpc = __esm({
58400
58834
  return await this.request(operation, args);
58401
58835
  } catch (error51) {
58402
58836
  if (!isBusy(error51) || attempt >= 3) throw error51;
58403
- await new Promise((resolve9) => setTimeout(resolve9, 25 * 2 ** attempt));
58837
+ await new Promise((resolve10) => setTimeout(resolve10, 25 * 2 ** attempt));
58404
58838
  attempt += 1;
58405
58839
  }
58406
58840
  }
@@ -60024,8 +60458,8 @@ __export(conversationContext_exports, {
60024
60458
  setHistory: () => setHistory,
60025
60459
  setLastClarification: () => setLastClarification
60026
60460
  });
60027
- import { existsSync as existsSync35 } from "node:fs";
60028
- import { join as join27 } from "node:path";
60461
+ import { existsSync as existsSync37 } from "node:fs";
60462
+ import { join as join28 } from "node:path";
60029
60463
  function getHistory() {
60030
60464
  return history;
60031
60465
  }
@@ -60034,7 +60468,7 @@ function setHistory(messages) {
60034
60468
  history = projected === messages ? [...messages] : projected;
60035
60469
  }
60036
60470
  function compactInPlace(cwd = process.cwd()) {
60037
- const durableStatePresent = existsSync35(join27(cwd, ".zelari", "state", "HEAD.json"));
60471
+ const durableStatePresent = existsSync37(join28(cwd, ".zelari", "state", "HEAD.json"));
60038
60472
  history = applySessionSurface(compactHistory(history, { durableStatePresent }));
60039
60473
  }
60040
60474
  function appendMessages(msgs) {
@@ -60416,7 +60850,7 @@ var init_mode = __esm({
60416
60850
  });
60417
60851
 
60418
60852
  // src/cli/headless.ts
60419
- import { readFileSync as readFileSync24 } from "node:fs";
60853
+ import { readFileSync as readFileSync25 } from "node:fs";
60420
60854
  import path73 from "node:path";
60421
60855
  function resolveHeadlessCwd(opts) {
60422
60856
  const raw = typeof opts.cwd === "string" ? opts.cwd.trim() : "";
@@ -60479,7 +60913,7 @@ function parseHeadlessFlags(argv) {
60479
60913
  const next = argv[i + 1];
60480
60914
  if (next) {
60481
60915
  try {
60482
- const fromFile = readFileSync24(next, "utf-8");
60916
+ const fromFile = readFileSync25(next, "utf-8");
60483
60917
  if (fromFile.trim()) task = fromFile;
60484
60918
  } catch {
60485
60919
  }
@@ -60528,7 +60962,7 @@ function parseHeadlessFlags(argv) {
60528
60962
  let raw = null;
60529
60963
  if (arg === "--history-file") {
60530
60964
  try {
60531
- raw = readFileSync24(next, "utf-8");
60965
+ raw = readFileSync25(next, "utf-8");
60532
60966
  } catch {
60533
60967
  raw = null;
60534
60968
  }
@@ -60630,7 +61064,7 @@ function parseHeadlessFlags(argv) {
60630
61064
  const next = argv[i + 1];
60631
61065
  if (next) {
60632
61066
  try {
60633
- const fromFile = readFileSync24(next, "utf-8");
61067
+ const fromFile = readFileSync25(next, "utf-8");
60634
61068
  if (fromFile.trim()) krakenGraph = fromFile;
60635
61069
  } catch {
60636
61070
  }
@@ -61319,13 +61753,13 @@ __export(claudeProvider_exports, {
61319
61753
  });
61320
61754
  import { spawn as spawn13 } from "node:child_process";
61321
61755
  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);
61756
+ return new Promise((resolve10) => {
61757
+ if (child.exitCode != null) return resolve10(child.exitCode);
61758
+ const timer = setTimeout(() => resolve10(child.exitCode ?? null), timeoutMs2);
61325
61759
  timer.unref?.();
61326
61760
  child.once("exit", (code) => {
61327
61761
  clearTimeout(timer);
61328
- resolve9(code ?? null);
61762
+ resolve10(code ?? null);
61329
61763
  });
61330
61764
  });
61331
61765
  }
@@ -61528,13 +61962,13 @@ var planDetect_exports = {};
61528
61962
  __export(planDetect_exports, {
61529
61963
  hasWorkspacePlan: () => hasWorkspacePlan
61530
61964
  });
61531
- import { existsSync as existsSync36, readFileSync as readFileSync25 } from "node:fs";
61532
- import { join as join28 } from "node:path";
61965
+ import { existsSync as existsSync38, readFileSync as readFileSync26 } from "node:fs";
61966
+ import { join as join29 } from "node:path";
61533
61967
  function hasWorkspacePlan(projectRoot = process.cwd()) {
61534
- const planPath = join28(resolveWorkspaceRoot(projectRoot), "plan.json");
61535
- if (!existsSync36(planPath)) return false;
61968
+ const planPath = join29(resolveWorkspaceRoot(projectRoot), "plan.json");
61969
+ if (!existsSync38(planPath)) return false;
61536
61970
  try {
61537
- const parsed = JSON.parse(readFileSync25(planPath, "utf8"));
61971
+ const parsed = JSON.parse(readFileSync26(planPath, "utf8"));
61538
61972
  return Array.isArray(parsed.phases) && parsed.phases.length > 0;
61539
61973
  } catch {
61540
61974
  return false;
@@ -61548,14 +61982,14 @@ var init_planDetect = __esm({
61548
61982
  });
61549
61983
 
61550
61984
  // src/cli/workspace/projectInstructions.ts
61551
- import { existsSync as existsSync37, readFileSync as readFileSync26 } from "node:fs";
61552
- import { join as join29 } from "node:path";
61985
+ import { existsSync as existsSync39, readFileSync as readFileSync27 } from "node:fs";
61986
+ import { join as join30 } from "node:path";
61553
61987
  function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
61554
61988
  for (const name of CANDIDATES) {
61555
- const full = join29(projectRoot, name);
61556
- if (!existsSync37(full)) continue;
61989
+ const full = join30(projectRoot, name);
61990
+ if (!existsSync39(full)) continue;
61557
61991
  try {
61558
- let raw = readFileSync26(full, "utf8");
61992
+ let raw = readFileSync27(full, "utf8");
61559
61993
  raw = raw.replace(/\r\n/g, "\n").trim();
61560
61994
  if (!raw) continue;
61561
61995
  if (raw.length <= maxChars) {
@@ -61599,8 +62033,8 @@ __export(workspaceSummary_exports, {
61599
62033
  buildWorkspaceSummary: () => buildWorkspaceSummary,
61600
62034
  buildZelariReadHint: () => buildZelariReadHint
61601
62035
  });
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";
62036
+ import { existsSync as existsSync40, readFileSync as readFileSync28, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
62037
+ import { join as join31, relative as relative2 } from "node:path";
61604
62038
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
61605
62039
  const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
61606
62040
  const name = safeProjectName(projectRoot);
@@ -61633,11 +62067,11 @@ function formatTaskLine(t) {
61633
62067
  }
61634
62068
  function buildPlanSummary(projectRoot = process.cwd(), options) {
61635
62069
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
61636
- const planPath = join30(zelariRoot, "plan.json");
61637
- if (!existsSync38(planPath)) return null;
62070
+ const planPath = join31(zelariRoot, "plan.json");
62071
+ if (!existsSync40(planPath)) return null;
61638
62072
  let plan;
61639
62073
  try {
61640
- plan = JSON.parse(readFileSync27(planPath, "utf8"));
62074
+ plan = JSON.parse(readFileSync28(planPath, "utf8"));
61641
62075
  } catch {
61642
62076
  return null;
61643
62077
  }
@@ -61776,8 +62210,8 @@ function pickNextTask(open2) {
61776
62210
  )[0];
61777
62211
  }
61778
62212
  function buildZelariReadHint(projectRoot = process.cwd()) {
61779
- const planPath = join30(resolveWorkspaceRoot(projectRoot), "plan.json");
61780
- if (!existsSync38(planPath)) return "";
62213
+ const planPath = join31(resolveWorkspaceRoot(projectRoot), "plan.json");
62214
+ if (!existsSync40(planPath)) return "";
61781
62215
  return [
61782
62216
  "# Council workspace detected (.zelari/) \u2014 DRAFT vault",
61783
62217
  "`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
@@ -61792,10 +62226,10 @@ function safeProjectName(root) {
61792
62226
  }
61793
62227
  }
61794
62228
  function readPackageJson2(projectRoot) {
61795
- const p3 = join30(projectRoot, "package.json");
61796
- if (!existsSync38(p3)) return null;
62229
+ const p3 = join31(projectRoot, "package.json");
62230
+ if (!existsSync40(p3)) return null;
61797
62231
  try {
61798
- return JSON.parse(readFileSync27(p3, "utf8"));
62232
+ return JSON.parse(readFileSync28(p3, "utf8"));
61799
62233
  } catch {
61800
62234
  return null;
61801
62235
  }
@@ -61836,7 +62270,7 @@ function readBuildScripts(projectRoot, maxScripts = 16) {
61836
62270
  function listShallow(projectRoot, maxEntries) {
61837
62271
  const out = [];
61838
62272
  try {
61839
- const top = readdirSync7(projectRoot, { withFileTypes: true }).filter(
62273
+ const top = readdirSync8(projectRoot, { withFileTypes: true }).filter(
61840
62274
  (e) => !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist"
61841
62275
  ).sort((a, b) => a.name.localeCompare(b.name));
61842
62276
  let count = 0;
@@ -61845,11 +62279,11 @@ function listShallow(projectRoot, maxEntries) {
61845
62279
  out.push(`\u2026 (+${top.length - count} more)`);
61846
62280
  break;
61847
62281
  }
61848
- const rel2 = relative2(projectRoot, join30(projectRoot, entry.name));
62282
+ const rel2 = relative2(projectRoot, join31(projectRoot, entry.name));
61849
62283
  if (entry.isDirectory()) {
61850
62284
  let inner = "";
61851
62285
  try {
61852
- const sub = readdirSync7(join30(projectRoot, entry.name), {
62286
+ const sub = readdirSync8(join31(projectRoot, entry.name), {
61853
62287
  withFileTypes: true
61854
62288
  }).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
61855
62289
  if (sub.length > 0)
@@ -61897,12 +62331,12 @@ var init_workspaceSummary = __esm({
61897
62331
  });
61898
62332
 
61899
62333
  // src/cli/workspace/buildLessonsSummary.ts
61900
- import { existsSync as existsSync39 } from "node:fs";
61901
- import { join as join31 } from "node:path";
62334
+ import { existsSync as existsSync41 } from "node:fs";
62335
+ import { join as join32 } from "node:path";
61902
62336
  function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
61903
62337
  if (process.env["ZELARI_LESSONS"] === "0") return null;
61904
62338
  const zelariRoot = resolveWorkspaceRoot(projectRoot);
61905
- if (!existsSync39(join31(zelariRoot, "lessons.jsonl"))) return null;
62339
+ if (!existsSync41(join32(zelariRoot, "lessons.jsonl"))) return null;
61906
62340
  const lessons = recallLessons(zelariRoot, {
61907
62341
  maxLessons: 5,
61908
62342
  maxBytes: 2048,
@@ -61923,8 +62357,8 @@ var composeContext_exports = {};
61923
62357
  __export(composeContext_exports, {
61924
62358
  composeProjectContext: () => composeProjectContext
61925
62359
  });
61926
- import { existsSync as existsSync40, readdirSync as readdirSync8, readFileSync as readFileSync28 } from "node:fs";
61927
- import { join as join32 } from "node:path";
62360
+ import { existsSync as existsSync42, readdirSync as readdirSync9, readFileSync as readFileSync29 } from "node:fs";
62361
+ import { join as join33 } from "node:path";
61928
62362
  function cap2(text, max, label) {
61929
62363
  if (!text || text.length <= max) return { text: text || "", truncated: false };
61930
62364
  return {
@@ -61936,19 +62370,19 @@ function cap2(text, max, label) {
61936
62370
  }
61937
62371
  function buildDesignIndex(projectRoot, maxChars) {
61938
62372
  const root = resolveWorkspaceRoot(projectRoot);
61939
- if (!existsSync40(root)) return "";
62373
+ if (!existsSync42(root)) return "";
61940
62374
  const lines = [
61941
62375
  "# Design vault index (.zelari/) \u2014 HYPOTHESES only",
61942
62376
  "Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
61943
62377
  ];
61944
- const docsDir = join32(root, "docs");
61945
- if (existsSync40(docsDir)) {
62378
+ const docsDir = join33(root, "docs");
62379
+ if (existsSync42(docsDir)) {
61946
62380
  try {
61947
- const docs = readdirSync8(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
62381
+ const docs = readdirSync9(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
61948
62382
  if (docs.length > 0) {
61949
62383
  lines.push("", "## docs/ (titles only)");
61950
62384
  for (const d of docs) lines.push(`- .zelari/docs/${d}`);
61951
- if (readdirSync8(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
62385
+ if (readdirSync9(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
61952
62386
  lines.push("- \u2026 (more under .zelari/docs/)");
61953
62387
  }
61954
62388
  }
@@ -61956,14 +62390,14 @@ function buildDesignIndex(projectRoot, maxChars) {
61956
62390
  }
61957
62391
  }
61958
62392
  for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
61959
- if (existsSync40(join32(root, name))) {
62393
+ if (existsSync42(join33(root, name))) {
61960
62394
  lines.push(`- .zelari/${name} present`);
61961
62395
  }
61962
62396
  }
61963
- const decisionsDir = join32(root, "decisions");
61964
- if (existsSync40(decisionsDir)) {
62397
+ const decisionsDir = join33(root, "decisions");
62398
+ if (existsSync42(decisionsDir)) {
61965
62399
  try {
61966
- const n = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).length;
62400
+ const n = readdirSync9(decisionsDir).filter((f) => f.endsWith(".md")).length;
61967
62401
  if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
61968
62402
  } catch {
61969
62403
  }
@@ -62062,17 +62496,17 @@ function composeProjectContext(input) {
62062
62496
  }
62063
62497
  function readDurableHeadSync(projectRoot) {
62064
62498
  try {
62065
- const headPath = join32(projectRoot, ".zelari", "state", "HEAD.json");
62066
- if (!existsSync40(headPath)) return "";
62067
- const head = JSON.parse(readFileSync28(headPath, "utf8"));
62499
+ const headPath = join33(projectRoot, ".zelari", "state", "HEAD.json");
62500
+ if (!existsSync42(headPath)) return "";
62501
+ const head = JSON.parse(readFileSync29(headPath, "utf8"));
62068
62502
  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");
62503
+ const metaPath = join33(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
62504
+ if (!existsSync42(metaPath)) return "";
62505
+ const meta3 = JSON.parse(readFileSync29(metaPath, "utf8"));
62506
+ const discPath = meta3.artifactDir ? join33(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join33(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
62073
62507
  let discoveries = [];
62074
- if (existsSync40(discPath)) {
62075
- discoveries = JSON.parse(readFileSync28(discPath, "utf8"));
62508
+ if (existsSync42(discPath)) {
62509
+ discoveries = JSON.parse(readFileSync29(discPath, "utf8"));
62076
62510
  }
62077
62511
  const reusable = discoveries.filter((d) => d.reusable !== false);
62078
62512
  const lines = [
@@ -62146,14 +62580,14 @@ __export(stubs_exports, {
62146
62580
  resolveWorkspaceRoot: () => resolveWorkspaceRoot
62147
62581
  });
62148
62582
  import {
62149
- existsSync as existsSync41,
62150
- readdirSync as readdirSync9,
62151
- writeFileSync as writeFileSync18,
62152
- readFileSync as readFileSync29,
62153
- mkdirSync as mkdirSync16,
62583
+ existsSync as existsSync43,
62584
+ readdirSync as readdirSync10,
62585
+ writeFileSync as writeFileSync19,
62586
+ readFileSync as readFileSync30,
62587
+ mkdirSync as mkdirSync17,
62154
62588
  renameSync as renameSync5
62155
62589
  } from "node:fs";
62156
- import { join as join33, basename as basename4, dirname as dirname9, relative as relative3 } from "node:path";
62590
+ import { join as join34, basename as basename4, dirname as dirname10, relative as relative3 } from "node:path";
62157
62591
  function createWorkspaceContext(projectRoot = process.cwd()) {
62158
62592
  const rootDir = resolveWorkspaceRoot(projectRoot);
62159
62593
  return {
@@ -62163,14 +62597,14 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
62163
62597
  };
62164
62598
  }
62165
62599
  function planJsonPath(ctx) {
62166
- return join33(ctx.rootDir, "plan.json");
62600
+ return join34(ctx.rootDir, "plan.json");
62167
62601
  }
62168
62602
  function readPlan(ctx) {
62169
62603
  const jsonPath = planJsonPath(ctx);
62170
- if (existsSync41(jsonPath)) {
62604
+ if (existsSync43(jsonPath)) {
62171
62605
  try {
62172
62606
  const parsed = JSON.parse(
62173
- readFileSync29(jsonPath, "utf8")
62607
+ readFileSync30(jsonPath, "utf8")
62174
62608
  );
62175
62609
  const { phases, tasks, milestones, ...root } = parsed;
62176
62610
  return {
@@ -62182,8 +62616,8 @@ function readPlan(ctx) {
62182
62616
  } catch {
62183
62617
  }
62184
62618
  }
62185
- const path103 = workspaceFile(ctx.rootDir, "plan");
62186
- const doc = ctx.storage.readIfExists(path103);
62619
+ const path104 = workspaceFile(ctx.rootDir, "plan");
62620
+ const doc = ctx.storage.readIfExists(path104);
62187
62621
  if (!doc) return { phases: [], tasks: [], milestones: [] };
62188
62622
  const meta3 = doc.meta;
62189
62623
  return {
@@ -62194,9 +62628,9 @@ function readPlan(ctx) {
62194
62628
  }
62195
62629
  function writePlan(ctx, summary) {
62196
62630
  const jsonPath = planJsonPath(ctx);
62197
- mkdirSync16(dirname9(jsonPath), { recursive: true });
62631
+ mkdirSync17(dirname10(jsonPath), { recursive: true });
62198
62632
  const tmp = jsonPath + ".tmp-" + process.pid;
62199
- writeFileSync18(
62633
+ writeFileSync19(
62200
62634
  tmp,
62201
62635
  JSON.stringify(
62202
62636
  {
@@ -62276,9 +62710,9 @@ function renderPlanBody(summary) {
62276
62710
  return lines.join("\n");
62277
62711
  }
62278
62712
  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));
62713
+ const decisionsDir = join34(ctx.rootDir, "decisions");
62714
+ if (!existsSync43(decisionsDir)) return "001";
62715
+ const existing = readdirSync10(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
62282
62716
  const max = existing.length === 0 ? 0 : Math.max(...existing);
62283
62717
  return String(max + 1).padStart(3, "0");
62284
62718
  }
@@ -62322,7 +62756,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
62322
62756
  // plan.json record (they already live in tags + the artifact body md).
62323
62757
  files: t.fileRefs
62324
62758
  });
62325
- const taskPath = join33(ctx.rootDir, "plan-tasks", `${id3}.md`);
62759
+ const taskPath = join34(ctx.rootDir, "plan-tasks", `${id3}.md`);
62326
62760
  const meta3 = {
62327
62761
  kind: "task",
62328
62762
  id: id3,
@@ -62364,7 +62798,7 @@ function addMilestoneRecord(ctx, summary, input) {
62364
62798
  dueDate: input.dueDate,
62365
62799
  targetVersion: version2
62366
62800
  });
62367
- const path103 = join33(ctx.rootDir, "milestones", `${id3}.md`);
62801
+ const path104 = join34(ctx.rootDir, "milestones", `${id3}.md`);
62368
62802
  const meta3 = {
62369
62803
  kind: "milestone",
62370
62804
  id: id3,
@@ -62381,7 +62815,7 @@ function addMilestoneRecord(ctx, summary, input) {
62381
62815
  `Target version: ${version2}`,
62382
62816
  ""
62383
62817
  ].join("\n");
62384
- ctx.storage.write(path103, meta3, body);
62818
+ ctx.storage.write(path104, meta3, body);
62385
62819
  return { id: id3, created: true };
62386
62820
  }
62387
62821
  function readPlanSummary(ctx) {
@@ -62585,7 +63019,7 @@ function addIdeaStub(ctx) {
62585
63019
  const tags = args["tags"] ?? [];
62586
63020
  const category = args["category"] ?? "General";
62587
63021
  const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
62588
- const path103 = workspaceArtifact(ctx.rootDir, "decisions", id3);
63022
+ const path104 = workspaceArtifact(ctx.rootDir, "decisions", id3);
62589
63023
  const meta3 = {
62590
63024
  kind: "adr",
62591
63025
  status: "proposed",
@@ -62611,7 +63045,7 @@ function addIdeaStub(ctx) {
62611
63045
  ...consequences.map((c) => `- ${c}`),
62612
63046
  ""
62613
63047
  ].join("\n");
62614
- ctx.storage.write(path103, meta3, body);
63048
+ ctx.storage.write(path104, meta3, body);
62615
63049
  return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
62616
63050
  });
62617
63051
  }
@@ -62662,8 +63096,8 @@ function createNfrSpecStub(ctx) {
62662
63096
  },
62663
63097
  planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
62664
63098
  };
62665
- const outPath = join33(ctx.rootDir, "nfr-spec.json");
62666
- writeFileSync18(outPath, JSON.stringify(spec, null, 2), "utf8");
63099
+ const outPath = join34(ctx.rootDir, "nfr-spec.json");
63100
+ writeFileSync19(outPath, JSON.stringify(spec, null, 2), "utf8");
62667
63101
  return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
62668
63102
  });
62669
63103
  }
@@ -62693,14 +63127,14 @@ function createDocumentStub(ctx) {
62693
63127
  ctx.storage.write(risksPath, riskMeta, content);
62694
63128
  return `Document "${title}" created at risks.md (workspace root).`;
62695
63129
  }
62696
- const path103 = workspaceArtifact(ctx.rootDir, "docs", slug);
63130
+ const path104 = workspaceArtifact(ctx.rootDir, "docs", slug);
62697
63131
  const meta3 = {
62698
63132
  kind: "doc",
62699
63133
  id: slug,
62700
63134
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
62701
63135
  tags
62702
63136
  };
62703
- ctx.storage.write(path103, meta3, content);
63137
+ ctx.storage.write(path104, meta3, content);
62704
63138
  return `Document "${title}" created at docs/${slug}.md.`;
62705
63139
  });
62706
63140
  }
@@ -62726,18 +63160,18 @@ function searchDocumentsStub(ctx) {
62726
63160
  (w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
62727
63161
  );
62728
63162
  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")),
63163
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63164
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63165
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews")),
63166
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "plan-tasks")),
63167
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "milestones")),
62734
63168
  workspaceFile(ctx.rootDir, "plan"),
62735
63169
  workspaceFile(ctx.rootDir, "risks")
62736
63170
  ];
62737
63171
  const results = [];
62738
63172
  for (const file2 of files) {
62739
- if (!existsSync41(file2)) continue;
62740
- const raw = readFileSync29(file2, "utf8");
63173
+ if (!existsSync43(file2)) continue;
63174
+ const raw = readFileSync30(file2, "utf8");
62741
63175
  const content = raw.toLowerCase();
62742
63176
  let idx = -1;
62743
63177
  let matchLen = 0;
@@ -62788,9 +63222,9 @@ function linkDocumentsStub(ctx) {
62788
63222
  const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
62789
63223
  if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
62790
63224
  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"))
63225
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63226
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63227
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews"))
62794
63228
  ];
62795
63229
  const source2 = allFiles.find((f) => {
62796
63230
  try {
@@ -62821,9 +63255,9 @@ function getDocumentBacklinksStub(ctx) {
62821
63255
  const targetId = args["targetId"] ?? args["id"];
62822
63256
  if (!targetId) return "getDocumentBacklinks requires targetId.";
62823
63257
  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"))
63258
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "decisions")),
63259
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "docs")),
63260
+ ...ctx.storage.listMarkdown(join34(ctx.rootDir, "reviews"))
62827
63261
  ];
62828
63262
  const backlinks = [];
62829
63263
  for (const file2 of allFiles) {
@@ -63067,9 +63501,10 @@ var init_mcpClient = __esm({
63067
63501
  MAX_LIST_PAGES = 50;
63068
63502
  MCP_PROTOCOL_VERSION = "2025-03-26";
63069
63503
  McpClient = class {
63070
- constructor(serverName, config2) {
63504
+ constructor(serverName, config2, spawnCwd) {
63071
63505
  this.serverName = serverName;
63072
63506
  this.config = config2;
63507
+ this.spawnCwd = spawnCwd;
63073
63508
  this.defaultTimeoutMs = config2.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
63074
63509
  this.serial = config2.serial ?? this.transportKind() === "http";
63075
63510
  }
@@ -63116,7 +63551,11 @@ var init_mcpClient = __esm({
63116
63551
  const spawnOpts = {
63117
63552
  stdio: ["pipe", "pipe", "pipe"],
63118
63553
  env: { ...process.env, ...this.config.env ?? {} },
63119
- windowsHide: true
63554
+ windowsHide: true,
63555
+ // MCP servers are project-scoped: their cwd is the session project
63556
+ // root, not the CLI process cwd (the Desktop launches the CLI from its
63557
+ // install dir, so "." entries would jail themselves to the app folder).
63558
+ cwd: this.spawnCwd
63120
63559
  };
63121
63560
  const child = process.platform === "win32" ? spawn14(buildCmdLine(command, this.config.args ?? []), {
63122
63561
  ...spawnOpts,
@@ -63235,7 +63674,7 @@ var init_mcpClient = __esm({
63235
63674
  if (!transport && !child)
63236
63675
  return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
63237
63676
  const id3 = this.nextId++;
63238
- return new Promise((resolve9, reject) => {
63677
+ return new Promise((resolve10, reject) => {
63239
63678
  const timer = setTimeout(() => {
63240
63679
  this.pending.delete(id3);
63241
63680
  reject(
@@ -63244,7 +63683,7 @@ var init_mcpClient = __esm({
63244
63683
  )
63245
63684
  );
63246
63685
  }, timeoutMs2);
63247
- this.pending.set(id3, { resolve: resolve9, reject, timer });
63686
+ this.pending.set(id3, { resolve: resolve10, reject, timer });
63248
63687
  if (transport) {
63249
63688
  void transport.send({ id: id3, method, params }, timeoutMs2);
63250
63689
  } else {
@@ -63313,22 +63752,22 @@ var init_mcpClient = __esm({
63313
63752
 
63314
63753
  // src/cli/mcp/mcpConfigIo.ts
63315
63754
  import {
63316
- existsSync as existsSync42,
63317
- mkdirSync as mkdirSync17,
63318
- readFileSync as readFileSync30,
63319
- writeFileSync as writeFileSync19
63755
+ existsSync as existsSync44,
63756
+ mkdirSync as mkdirSync18,
63757
+ readFileSync as readFileSync31,
63758
+ writeFileSync as writeFileSync20
63320
63759
  } from "node:fs";
63321
- import { dirname as dirname10, join as join34 } from "node:path";
63760
+ import { dirname as dirname11, join as join35 } from "node:path";
63322
63761
  function getUserMcpPath() {
63323
- return join34(zelariHome(), "mcp.json");
63762
+ return join35(zelariHome(), "mcp.json");
63324
63763
  }
63325
63764
  function getProjectMcpPath(projectRoot) {
63326
- return join34(projectRoot, ".zelari", "mcp.json");
63765
+ return join35(projectRoot, ".zelari", "mcp.json");
63327
63766
  }
63328
- function readFile10(path103) {
63329
- if (!existsSync42(path103)) return {};
63767
+ function readFile10(path104) {
63768
+ if (!existsSync44(path104)) return {};
63330
63769
  try {
63331
- const parsed = JSON.parse(readFileSync30(path103, "utf8"));
63770
+ const parsed = JSON.parse(readFileSync31(path104, "utf8"));
63332
63771
  const out = {};
63333
63772
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
63334
63773
  const hasCommand = !!cfg && typeof cfg.command === "string" && !!cfg.command.trim();
@@ -63350,10 +63789,10 @@ function readFile10(path103) {
63350
63789
  return {};
63351
63790
  }
63352
63791
  }
63353
- function writeFile4(path103, servers) {
63354
- mkdirSync17(dirname10(path103), { recursive: true });
63792
+ function writeFile4(path104, servers) {
63793
+ mkdirSync18(dirname11(path104), { recursive: true });
63355
63794
  const body = { mcpServers: servers };
63356
- writeFileSync19(path103, `${JSON.stringify(body, null, 2)}
63795
+ writeFileSync20(path104, `${JSON.stringify(body, null, 2)}
63357
63796
  `, "utf8");
63358
63797
  }
63359
63798
  function listMcpServers(projectRoot) {
@@ -63375,6 +63814,46 @@ function listMcpServers(projectRoot) {
63375
63814
  };
63376
63815
  return { userPath, projectPath, servers, merged };
63377
63816
  }
63817
+ function requireEnvKey(key) {
63818
+ if (!ENV_KEY_RE.test(key)) {
63819
+ throw new Error(
63820
+ `Invalid env key "${key}" \u2014 use letters, digits, _ (no leading digit)`
63821
+ );
63822
+ }
63823
+ return key;
63824
+ }
63825
+ function parseMcpEnvFlag(raw) {
63826
+ if (raw.length === 0) return void 0;
63827
+ const env = {};
63828
+ for (const value of raw) {
63829
+ const text = value.trim();
63830
+ if (text.startsWith("{") || text.startsWith("[")) {
63831
+ let parsed;
63832
+ try {
63833
+ parsed = JSON.parse(text);
63834
+ } catch {
63835
+ throw new Error(`--env is not valid JSON: ${text}`);
63836
+ }
63837
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
63838
+ throw new Error('--env JSON must be an object of "KEY": "VALUE" pairs');
63839
+ }
63840
+ for (const [key2, val] of Object.entries(parsed)) {
63841
+ if (typeof val !== "string") {
63842
+ throw new Error(`--env "${key2}" must map to a string value`);
63843
+ }
63844
+ env[requireEnvKey(key2)] = val;
63845
+ }
63846
+ continue;
63847
+ }
63848
+ const eq = text.indexOf("=");
63849
+ const key = eq > 0 ? text.slice(0, eq).trim() : "";
63850
+ if (!key) {
63851
+ throw new Error(`Invalid --env "${text}" \u2014 use KEY=VALUE or a JSON object`);
63852
+ }
63853
+ env[requireEnvKey(key)] = text.slice(eq + 1);
63854
+ }
63855
+ return env;
63856
+ }
63378
63857
  function upsertMcpServer(opts) {
63379
63858
  const name = opts.name.trim();
63380
63859
  if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
@@ -63391,9 +63870,9 @@ function upsertMcpServer(opts) {
63391
63870
  error: "either command (stdio) or url (http) is required"
63392
63871
  };
63393
63872
  }
63394
- let path103;
63873
+ let path104;
63395
63874
  if (opts.scope === "user") {
63396
- path103 = getUserMcpPath();
63875
+ path104 = getUserMcpPath();
63397
63876
  } else {
63398
63877
  const root = opts.projectRoot?.trim();
63399
63878
  if (!root) {
@@ -63402,39 +63881,46 @@ function upsertMcpServer(opts) {
63402
63881
  error: "projectRoot required for project scope (Open Folder first)"
63403
63882
  };
63404
63883
  }
63405
- path103 = getProjectMcpPath(root);
63884
+ path104 = getProjectMcpPath(root);
63406
63885
  }
63407
- const current = readFile10(path103);
63886
+ const current = readFile10(path104);
63887
+ const previous = current[name];
63408
63888
  current[name] = {
63409
63889
  command: hasCommand ? opts.config.command.trim() : void 0,
63410
63890
  args: opts.config.args,
63411
- env: opts.config.env,
63891
+ // Back-compat: callers that only flip `enabled` (Desktop toggle) or that
63892
+ // have no env channel at all (`--set-mcp` without `--env`) must not wipe
63893
+ // env written by hand or by another tool. An explicit env always wins —
63894
+ // including `{}`, which clears it.
63895
+ env: opts.config.env ?? previous?.env,
63412
63896
  type: hasUrl ? "http" : opts.config.type === "http" ? "http" : "stdio",
63413
63897
  url: hasUrl ? opts.config.url.trim() : void 0,
63414
63898
  timeoutMs: opts.config.timeoutMs,
63415
63899
  serial: opts.config.serial,
63416
63900
  enabled: opts.config.enabled !== false
63417
63901
  };
63418
- writeFile4(path103, current);
63419
- return { ok: true, path: path103 };
63902
+ writeFile4(path104, current);
63903
+ return { ok: true, path: path104 };
63420
63904
  }
63421
63905
  function removeMcpServer(opts) {
63422
- const path103 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
63423
- if (!path103) {
63906
+ const path104 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
63907
+ if (!path104) {
63424
63908
  return { ok: false, error: "projectRoot required for project scope" };
63425
63909
  }
63426
- const current = readFile10(path103);
63910
+ const current = readFile10(path104);
63427
63911
  if (!(opts.name in current)) {
63428
- return { ok: false, error: `Server "${opts.name}" not found in ${path103}` };
63912
+ return { ok: false, error: `Server "${opts.name}" not found in ${path104}` };
63429
63913
  }
63430
63914
  delete current[opts.name];
63431
- writeFile4(path103, current);
63432
- return { ok: true, path: path103 };
63915
+ writeFile4(path104, current);
63916
+ return { ok: true, path: path104 };
63433
63917
  }
63918
+ var ENV_KEY_RE;
63434
63919
  var init_mcpConfigIo = __esm({
63435
63920
  "src/cli/mcp/mcpConfigIo.ts"() {
63436
63921
  "use strict";
63437
63922
  init_paths();
63923
+ ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
63438
63924
  }
63439
63925
  });
63440
63926
 
@@ -63601,21 +64087,21 @@ __export(mcpManager_exports, {
63601
64087
  readMcpConfig: () => readMcpConfig,
63602
64088
  registerMcpTools: () => registerMcpTools
63603
64089
  });
63604
- import { existsSync as existsSync43, readFileSync as readFileSync31 } from "node:fs";
63605
- import { join as join35 } from "node:path";
64090
+ import { existsSync as existsSync45, readFileSync as readFileSync32 } from "node:fs";
64091
+ import { join as join36 } from "node:path";
63606
64092
  function readMcpConfig(projectRoot = process.cwd(), opts) {
63607
64093
  const merged = {};
63608
64094
  const paths = [];
63609
64095
  if (process.env["ZELARI_MCP_USER"] !== "0") {
63610
- paths.push(join35(zelariHome(), "mcp.json"));
64096
+ paths.push(join36(zelariHome(), "mcp.json"));
63611
64097
  }
63612
64098
  if (!opts?.skipProjectMcp) {
63613
- paths.push(join35(projectRoot, ".zelari", "mcp.json"));
64099
+ paths.push(join36(projectRoot, ".zelari", "mcp.json"));
63614
64100
  }
63615
64101
  for (const p3 of paths) {
63616
- if (!existsSync43(p3)) continue;
64102
+ if (!existsSync45(p3)) continue;
63617
64103
  try {
63618
- const parsed = JSON.parse(readFileSync31(p3, "utf8"));
64104
+ const parsed = JSON.parse(readFileSync32(p3, "utf8"));
63619
64105
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
63620
64106
  const hasCommand = !!cfg && typeof cfg.command === "string" && cfg.command.length > 0;
63621
64107
  const hasUrl = !!cfg && typeof cfg.url === "string" && /^https?:\/\//i.test(cfg.url);
@@ -63631,7 +64117,7 @@ async function ensureLoaded(projectRoot) {
63631
64117
  if (state2.loaded) return;
63632
64118
  state2.loaded = true;
63633
64119
  const trusted = isFolderTrusted(projectRoot);
63634
- if (!trusted && existsSync43(join35(projectRoot, ".zelari", "mcp.json"))) {
64120
+ if (!trusted && existsSync45(join36(projectRoot, ".zelari", "mcp.json"))) {
63635
64121
  state2.warnings.push(
63636
64122
  "[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
63637
64123
  );
@@ -63645,7 +64131,7 @@ async function ensureLoaded(projectRoot) {
63645
64131
  );
63646
64132
  continue;
63647
64133
  }
63648
- const client = new McpClient(name, cfg);
64134
+ const client = new McpClient(name, cfg, projectRoot);
63649
64135
  try {
63650
64136
  await client.start();
63651
64137
  const tools = await client.listTools();
@@ -63895,15 +64381,15 @@ __export(agentsMd_exports, {
63895
64381
  serializeAgentsMd: () => serializeAgentsMd,
63896
64382
  updateAgentsMd: () => updateAgentsMd
63897
64383
  });
63898
- import { existsSync as existsSync44, readFileSync as readFileSync32, writeFileSync as writeFileSync20 } from "node:fs";
64384
+ import { existsSync as existsSync46, readFileSync as readFileSync33, writeFileSync as writeFileSync21 } from "node:fs";
63899
64385
  import { createHash as createHash24 } from "node:crypto";
63900
- import { join as join36 } from "node:path";
64386
+ import { join as join37 } from "node:path";
63901
64387
  import { readFile as readFile11 } from "node:fs/promises";
63902
64388
  async function readPackageJson3(projectRoot) {
63903
- const path103 = join36(projectRoot, "package.json");
63904
- if (!existsSync44(path103)) return null;
64389
+ const path104 = join37(projectRoot, "package.json");
64390
+ if (!existsSync46(path104)) return null;
63905
64391
  try {
63906
- return JSON.parse(await readFile11(path103, "utf8"));
64392
+ return JSON.parse(await readFile11(path104, "utf8"));
63907
64393
  } catch {
63908
64394
  return null;
63909
64395
  }
@@ -63925,8 +64411,8 @@ async function genTechStack(ctx) {
63925
64411
  ].join("\n");
63926
64412
  }
63927
64413
  async function genDecisions(ctx) {
63928
- const decisionsDir = join36(ctx.rootDir, "decisions");
63929
- if (!existsSync44(decisionsDir)) return "_No ADRs yet._";
64414
+ const decisionsDir = join37(ctx.rootDir, "decisions");
64415
+ if (!existsSync46(decisionsDir)) return "_No ADRs yet._";
63930
64416
  const files = ctx.storage.listMarkdown(decisionsDir).sort();
63931
64417
  const accepted = [];
63932
64418
  const proposed = [];
@@ -63951,9 +64437,9 @@ async function genDecisions(ctx) {
63951
64437
  }
63952
64438
  async function genConventions(ctx) {
63953
64439
  const lines = [];
63954
- const claudeMd = join36(ctx.projectRoot, "CLAUDE.MD");
63955
- if (existsSync44(claudeMd)) {
63956
- const content = readFileSync32(claudeMd, "utf8");
64440
+ const claudeMd = join37(ctx.projectRoot, "CLAUDE.MD");
64441
+ if (existsSync46(claudeMd)) {
64442
+ const content = readFileSync33(claudeMd, "utf8");
63957
64443
  const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
63958
64444
  if (match) {
63959
64445
  lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
@@ -63985,9 +64471,9 @@ async function genBuild(ctx) {
63985
64471
  ].join("\n");
63986
64472
  }
63987
64473
  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");
64474
+ const path104 = join37(ctx.rootDir, "risks.md");
64475
+ if (!existsSync46(path104)) return "_No open questions._";
64476
+ const content = readFileSync33(path104, "utf8");
63991
64477
  const lines = content.split("\n");
63992
64478
  const questions = [];
63993
64479
  let currentTitle = "";
@@ -64061,9 +64547,9 @@ function titleCase(id3) {
64061
64547
  return id3.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
64062
64548
  }
64063
64549
  async function updateAgentsMd(ctx, projectRoot) {
64064
- const agentsPath = join36(projectRoot, "AGENTS.MD");
64065
- if (existsSync44(agentsPath)) {
64066
- const content = readFileSync32(agentsPath, "utf8");
64550
+ const agentsPath = join37(projectRoot, "AGENTS.MD");
64551
+ if (existsSync46(agentsPath)) {
64552
+ const content = readFileSync33(agentsPath, "utf8");
64067
64553
  const hasAnyMarker = AUTO_SECTIONS.some((id3) => content.includes(MARKER_OPEN(id3)));
64068
64554
  if (!hasAnyMarker) {
64069
64555
  return {
@@ -64078,8 +64564,8 @@ async function updateAgentsMd(ctx, projectRoot) {
64078
64564
  newSections.set(id3, await GENERATORS[id3](ctx));
64079
64565
  }
64080
64566
  let manualContent = "";
64081
- if (existsSync44(agentsPath)) {
64082
- const { manualBlocks } = parseAgentsMd(readFileSync32(agentsPath, "utf8"));
64567
+ if (existsSync46(agentsPath)) {
64568
+ const { manualBlocks } = parseAgentsMd(readFileSync33(agentsPath, "utf8"));
64083
64569
  manualContent = manualBlocks.after;
64084
64570
  } else {
64085
64571
  const projectName2 = projectName(projectRoot);
@@ -64095,7 +64581,7 @@ async function updateAgentsMd(ctx, projectRoot) {
64095
64581
  ""
64096
64582
  ].join("\n");
64097
64583
  }
64098
- const oldContent = existsSync44(agentsPath) ? readFileSync32(agentsPath, "utf8") : "";
64584
+ const oldContent = existsSync46(agentsPath) ? readFileSync33(agentsPath, "utf8") : "";
64099
64585
  const { sections: oldSections } = parseAgentsMd(oldContent);
64100
64586
  const changedSections = [];
64101
64587
  for (const id3 of AUTO_SECTIONS) {
@@ -64107,7 +64593,7 @@ async function updateAgentsMd(ctx, projectRoot) {
64107
64593
  return { changed: false, sections: [] };
64108
64594
  }
64109
64595
  const newContent = serializeAgentsMd(manualContent, newSections);
64110
- writeFileSync20(agentsPath, newContent, "utf8");
64596
+ writeFileSync21(agentsPath, newContent, "utf8");
64111
64597
  return { changed: true, sections: changedSections };
64112
64598
  }
64113
64599
  function hash2(s) {
@@ -64232,12 +64718,12 @@ var init_completeDesign = __esm({
64232
64718
  });
64233
64719
 
64234
64720
  // 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";
64721
+ import { existsSync as existsSync47, readFileSync as readFileSync34, readdirSync as readdirSync11, statSync as statSync6, writeFileSync as writeFileSync22 } from "node:fs";
64722
+ import { join as join38 } from "node:path";
64237
64723
  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);
64724
+ const docsDir = join38(rootDir, "docs");
64725
+ if (!existsSync47(docsDir)) return null;
64726
+ 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
64727
  return candidates.length > 0 ? candidates[0].f : null;
64242
64728
  }
64243
64729
  function parseCanonicalDoc(text) {
@@ -64261,9 +64747,9 @@ function versionKey(value) {
64261
64747
  function firstString2(v) {
64262
64748
  return typeof v === "string" && v.trim().length > 0 ? v : null;
64263
64749
  }
64264
- function readFileSyncSafe(path103) {
64750
+ function readFileSyncSafe(path104) {
64265
64751
  try {
64266
- return readFileSync33(path103, "utf8");
64752
+ return readFileSync34(path104, "utf8");
64267
64753
  } catch {
64268
64754
  return null;
64269
64755
  }
@@ -64272,13 +64758,13 @@ async function runPlanDriftCheck(rootDir) {
64272
64758
  if (process.env["ZELARI_DRIFT_CHECK"] === "0") {
64273
64759
  return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
64274
64760
  }
64275
- const planPath = join37(rootDir, "plan.json");
64276
- if (!existsSync45(planPath)) {
64761
+ const planPath = join38(rootDir, "plan.json");
64762
+ if (!existsSync47(planPath)) {
64277
64763
  return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
64278
64764
  }
64279
64765
  let plan;
64280
64766
  try {
64281
- plan = JSON.parse(readFileSync33(planPath, "utf8"));
64767
+ plan = JSON.parse(readFileSync34(planPath, "utf8"));
64282
64768
  } catch {
64283
64769
  return { ran: false, reason: ".zelari/plan.json corrupt" };
64284
64770
  }
@@ -64288,7 +64774,7 @@ async function runPlanDriftCheck(rootDir) {
64288
64774
  const milestones = Array.isArray(plan.milestones) ? plan.milestones : [];
64289
64775
  const phaseIds = new Set(phases.map((p3) => typeof p3.id === "string" ? p3.id : "").filter(Boolean));
64290
64776
  const canonicalName = findCanonicalDoc(rootDir);
64291
- const canonicalText = canonicalName ? readFileSyncSafe(join37(rootDir, "docs", canonicalName)) : null;
64777
+ const canonicalText = canonicalName ? readFileSyncSafe(join38(rootDir, "docs", canonicalName)) : null;
64292
64778
  if (canonicalText !== null) {
64293
64779
  const { activePhases, blockedPrefixes } = parseCanonicalDoc(canonicalText);
64294
64780
  for (const id3 of activePhases) {
@@ -64367,8 +64853,8 @@ async function runPlanDriftCheck(rootDir) {
64367
64853
  const canonicalParsed = canonicalName !== null && canonicalText !== null;
64368
64854
  let reportPath;
64369
64855
  try {
64370
- reportPath = join37(rootDir, "drift-report.json");
64371
- writeFileSync21(
64856
+ reportPath = join38(rootDir, "drift-report.json");
64857
+ writeFileSync22(
64372
64858
  reportPath,
64373
64859
  JSON.stringify(
64374
64860
  {
@@ -64404,8 +64890,8 @@ var init_planDriftCheck = __esm({
64404
64890
 
64405
64891
  // src/cli/workspace/projectSmoke.ts
64406
64892
  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";
64893
+ import { existsSync as existsSync48, readFileSync as readFileSync35 } from "node:fs";
64894
+ import { join as join39 } from "node:path";
64409
64895
  function pickSmokeScript(scripts) {
64410
64896
  if (!scripts) return null;
64411
64897
  for (const name of SMOKE_SCRIPT_PRIORITY) {
@@ -64417,13 +64903,13 @@ async function runProjectSmoke(projectRoot, timeoutMs2 = DEFAULT_TIMEOUT_MS3) {
64417
64903
  if (process.env["ZELARI_SMOKE"] === "0") {
64418
64904
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
64419
64905
  }
64420
- const pkgPath = join38(projectRoot, "package.json");
64421
- if (!existsSync46(pkgPath)) {
64906
+ const pkgPath = join39(projectRoot, "package.json");
64907
+ if (!existsSync48(pkgPath)) {
64422
64908
  return { ran: false, reason: "no package.json (skipped)" };
64423
64909
  }
64424
64910
  let scripts = {};
64425
64911
  try {
64426
- const pkg = JSON.parse(readFileSync34(pkgPath, "utf8"));
64912
+ const pkg = JSON.parse(readFileSync35(pkgPath, "utf8"));
64427
64913
  scripts = pkg.scripts ?? {};
64428
64914
  } catch {
64429
64915
  return { ran: false, reason: "package.json unreadable (skipped)" };
@@ -64510,7 +64996,7 @@ __export(evidenceFromSpine_exports, {
64510
64996
  collectSessionEvidenceRefs: () => collectSessionEvidenceRefs,
64511
64997
  evidenceRefsFromEventLines: () => evidenceRefsFromEventLines
64512
64998
  });
64513
- import { existsSync as existsSync47, readFileSync as readFileSync35 } from "node:fs";
64999
+ import { existsSync as existsSync49, readFileSync as readFileSync36 } from "node:fs";
64514
65000
  import path75 from "node:path";
64515
65001
  function asRecord2(v) {
64516
65002
  return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
@@ -64550,8 +65036,8 @@ function evidenceRefsFromEventLines(lines) {
64550
65036
  function collectSessionEvidenceRefs(sessionId2) {
64551
65037
  try {
64552
65038
  const file2 = path75.join(sessionsDir(), sessionId2, "events.jsonl");
64553
- if (!existsSync47(file2)) return [];
64554
- return evidenceRefsFromEventLines(readFileSync35(file2, "utf8").split("\n"));
65039
+ if (!existsSync49(file2)) return [];
65040
+ return evidenceRefsFromEventLines(readFileSync36(file2, "utf8").split("\n"));
64555
65041
  } catch {
64556
65042
  return [];
64557
65043
  }
@@ -64574,8 +65060,8 @@ __export(postCouncilHook_exports, {
64574
65060
  runPostCouncilHook: () => runPostCouncilHook
64575
65061
  });
64576
65062
  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";
65063
+ import { existsSync as existsSync50, readFileSync as readFileSync37 } from "node:fs";
65064
+ import { join as join40 } from "node:path";
64579
65065
  async function runCompleteDesignPostProcessor(ctx, options) {
64580
65066
  if (options?.runMode === "implementation") {
64581
65067
  return {
@@ -64586,9 +65072,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64586
65072
  if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
64587
65073
  return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
64588
65074
  }
64589
- const planJsonPath2 = join39(ctx.rootDir, "plan.json");
64590
- const scriptPath = join39(ctx.projectRoot, "complete-design.mjs");
64591
- if (!existsSync48(planJsonPath2)) {
65075
+ const planJsonPath2 = join40(ctx.rootDir, "plan.json");
65076
+ const scriptPath = join40(ctx.projectRoot, "complete-design.mjs");
65077
+ if (!existsSync50(planJsonPath2)) {
64592
65078
  return {
64593
65079
  ran: false,
64594
65080
  reason: ".zelari/plan.json missing (not design-phase)"
@@ -64596,7 +65082,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64596
65082
  }
64597
65083
  let phaseCount = 0;
64598
65084
  try {
64599
- const parsed = JSON.parse(readFileSync36(planJsonPath2, "utf8"));
65085
+ const parsed = JSON.parse(readFileSync37(planJsonPath2, "utf8"));
64600
65086
  phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
64601
65087
  } catch {
64602
65088
  return { ran: false, reason: ".zelari/plan.json corrupt" };
@@ -64604,7 +65090,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
64604
65090
  if (phaseCount === 0) {
64605
65091
  return { ran: false, reason: ".zelari/plan.json has no phases" };
64606
65092
  }
64607
- if (!existsSync48(scriptPath)) {
65093
+ if (!existsSync50(scriptPath)) {
64608
65094
  try {
64609
65095
  const builtin = await runBuiltinCompleteDesign(ctx);
64610
65096
  return {
@@ -64799,8 +65285,8 @@ async function runPostCouncilHook(ctx, options) {
64799
65285
  sources: scope.sources
64800
65286
  } : void 0
64801
65287
  });
64802
- const path103 = writeCouncilCompletion(ctx.rootDir, completion);
64803
- completionHook = { ran: true, path: path103, completion };
65288
+ const path104 = writeCouncilCompletion(ctx.rootDir, completion);
65289
+ completionHook = { ran: true, path: path104, completion };
64804
65290
  } catch (err) {
64805
65291
  completionHook = {
64806
65292
  ran: true,
@@ -64840,10 +65326,10 @@ __export(councilFeedback_exports, {
64840
65326
  });
64841
65327
  import {
64842
65328
  promises as fs33,
64843
- existsSync as existsSync49,
64844
- readFileSync as readFileSync37,
64845
- writeFileSync as writeFileSync22,
64846
- mkdirSync as mkdirSync18
65329
+ existsSync as existsSync51,
65330
+ readFileSync as readFileSync38,
65331
+ writeFileSync as writeFileSync23,
65332
+ mkdirSync as mkdirSync19
64847
65333
  } from "node:fs";
64848
65334
  import path76 from "node:path";
64849
65335
  var FeedbackStore;
@@ -64949,9 +65435,9 @@ var init_councilFeedback = __esm({
64949
65435
  }
64950
65436
  // --- persistence ---------------------------------------------------------
64951
65437
  load() {
64952
- if (!existsSync49(this.file)) return;
65438
+ if (!existsSync51(this.file)) return;
64953
65439
  try {
64954
- const raw = readFileSync37(this.file, "utf-8");
65440
+ const raw = readFileSync38(this.file, "utf-8");
64955
65441
  const parsed = JSON.parse(raw);
64956
65442
  if (parsed && Array.isArray(parsed.entries)) {
64957
65443
  this.entries = parsed.entries.filter(
@@ -64962,8 +65448,8 @@ var init_councilFeedback = __esm({
64962
65448
  }
64963
65449
  }
64964
65450
  save() {
64965
- mkdirSync18(path76.dirname(this.file), { recursive: true });
64966
- writeFileSync22(
65451
+ mkdirSync19(path76.dirname(this.file), { recursive: true });
65452
+ writeFileSync23(
64967
65453
  this.file,
64968
65454
  JSON.stringify({ entries: this.entries }, null, 2),
64969
65455
  { encoding: "utf-8", mode: 384 }
@@ -65036,7 +65522,7 @@ __export(ledger_exports, {
65036
65522
  ledgerStats: () => ledgerStats,
65037
65523
  readLedger: () => readLedger
65038
65524
  });
65039
- import { appendFileSync as appendFileSync4, existsSync as existsSync50, mkdirSync as mkdirSync19, readFileSync as readFileSync38 } from "node:fs";
65525
+ import { appendFileSync as appendFileSync4, existsSync as existsSync52, mkdirSync as mkdirSync20, readFileSync as readFileSync39 } from "node:fs";
65040
65526
  import path77 from "node:path";
65041
65527
  function evolutionMode(env = process.env) {
65042
65528
  return env[EVOLUTION_ENV] === "shadow" ? "shadow" : "0";
@@ -65050,7 +65536,7 @@ function appendLedgerEntry(cwd, entry) {
65050
65536
  }
65051
65537
  try {
65052
65538
  const file2 = ledgerPath(cwd);
65053
- mkdirSync19(path77.dirname(file2), { recursive: true });
65539
+ mkdirSync20(path77.dirname(file2), { recursive: true });
65054
65540
  appendFileSync4(file2, `${JSON.stringify(entry)}
65055
65541
  `, "utf8");
65056
65542
  return { written: true, path: file2 };
@@ -65063,10 +65549,10 @@ function appendLedgerEntry(cwd, entry) {
65063
65549
  }
65064
65550
  function readLedger(cwd) {
65065
65551
  const file2 = ledgerPath(cwd);
65066
- if (!existsSync50(file2)) return [];
65552
+ if (!existsSync52(file2)) return [];
65067
65553
  let raw;
65068
65554
  try {
65069
- raw = readFileSync38(file2, "utf8");
65555
+ raw = readFileSync39(file2, "utf8");
65070
65556
  } catch {
65071
65557
  return [];
65072
65558
  }
@@ -66286,7 +66772,7 @@ function safeSocketPath(socketPath) {
66286
66772
  return socketPath.trim();
66287
66773
  }
66288
66774
  function startPermissionBroker(socketPath, handlers, opts) {
66289
- const path103 = safeSocketPath(socketPath);
66775
+ const path104 = safeSocketPath(socketPath);
66290
66776
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
66291
66777
  const sockets = /* @__PURE__ */ new Set();
66292
66778
  const server = createServer2((socket) => {
@@ -66383,13 +66869,13 @@ function startPermissionBroker(socketPath, handlers, opts) {
66383
66869
  });
66384
66870
  }
66385
66871
  }
66386
- return new Promise((resolve9, reject) => {
66872
+ return new Promise((resolve10, reject) => {
66387
66873
  const onError = (err) => reject(err);
66388
66874
  server.once("error", onError);
66389
- server.listen(path103, () => {
66875
+ server.listen(path104, () => {
66390
66876
  server.removeListener("error", onError);
66391
- resolve9({
66392
- socketPath: path103,
66877
+ resolve10({
66878
+ socketPath: path104,
66393
66879
  stop: () => new Promise((res) => {
66394
66880
  for (const s of sockets) s.destroy();
66395
66881
  sockets.clear();
@@ -66400,7 +66886,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
66400
66886
  if (done) return;
66401
66887
  done = true;
66402
66888
  if (process.platform !== "win32") {
66403
- unlink(path103, () => res());
66889
+ unlink(path104, () => res());
66404
66890
  } else {
66405
66891
  res();
66406
66892
  }
@@ -66413,11 +66899,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
66413
66899
  });
66414
66900
  }
66415
66901
  function requestBrokerAsk(socketPath, ask, opts) {
66416
- const path103 = safeSocketPath(socketPath);
66902
+ const path104 = safeSocketPath(socketPath);
66417
66903
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
66418
66904
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
66419
- return new Promise((resolve9, reject) => {
66420
- const socket = connect(path103);
66905
+ return new Promise((resolve10, reject) => {
66906
+ const socket = connect(path104);
66421
66907
  let buffer = "";
66422
66908
  let settled = false;
66423
66909
  const settle = (fn) => {
@@ -66432,7 +66918,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
66432
66918
  settle(
66433
66919
  () => reject(
66434
66920
  new Error(
66435
- `permission broker unavailable at "${path103}" (connect timed out after ${connectTimeoutMs}ms)`
66921
+ `permission broker unavailable at "${path104}" (connect timed out after ${connectTimeoutMs}ms)`
66436
66922
  )
66437
66923
  )
66438
66924
  );
@@ -66469,7 +66955,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
66469
66955
  }
66470
66956
  if (msg?.t === "answer" && msg.id === ask.id) {
66471
66957
  const { t: _t, id: _id, ...answer } = msg;
66472
- settle(() => resolve9(answer));
66958
+ settle(() => resolve10(answer));
66473
66959
  return;
66474
66960
  }
66475
66961
  }
@@ -67249,8 +67735,8 @@ __export(prereqChecks_exports, {
67249
67735
  runPrereqChecks: () => runPrereqChecks
67250
67736
  });
67251
67737
  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";
67738
+ import { existsSync as existsSync54 } from "node:fs";
67739
+ import { dirname as dirname13 } from "node:path";
67254
67740
  function isWslBashPath2(p3) {
67255
67741
  if (!p3 || typeof p3 !== "string") return false;
67256
67742
  const n = p3.replace(/\//g, "\\").toLowerCase();
@@ -67356,16 +67842,16 @@ function resolveAgentShellSync() {
67356
67842
  function agentProbeEnv() {
67357
67843
  const env = { ...process.env };
67358
67844
  try {
67359
- const nodeDir = dirname12(process.execPath);
67845
+ const nodeDir = dirname13(process.execPath);
67360
67846
  if (!nodeDir) return env;
67361
- const sep4 = process.platform === "win32" ? ";" : ":";
67847
+ const sep5 = process.platform === "win32" ? ";" : ":";
67362
67848
  const current = env.PATH ?? env.Path ?? "";
67363
- const parts = current.split(sep4).filter((p3) => p3.length > 0);
67849
+ const parts = current.split(sep5).filter((p3) => p3.length > 0);
67364
67850
  const has = parts.some(
67365
67851
  (p3) => p3.toLowerCase() === nodeDir.toLowerCase()
67366
67852
  );
67367
67853
  if (!has) {
67368
- env.PATH = `${nodeDir}${sep4}${current}`;
67854
+ env.PATH = `${nodeDir}${sep5}${current}`;
67369
67855
  }
67370
67856
  } catch {
67371
67857
  }
@@ -67373,7 +67859,7 @@ function agentProbeEnv() {
67373
67859
  }
67374
67860
  function existsSyncSafe2(p3) {
67375
67861
  try {
67376
- return existsSync52(p3);
67862
+ return existsSync54(p3);
67377
67863
  } catch {
67378
67864
  return false;
67379
67865
  }
@@ -67620,7 +68106,7 @@ var init_prereqChecks = __esm({
67620
68106
  });
67621
68107
 
67622
68108
  // src/cli/plugins/prefs.ts
67623
- import { existsSync as existsSync53, readFileSync as readFileSync40, writeFileSync as writeFileSync23, mkdirSync as mkdirSync20 } from "node:fs";
68109
+ import { existsSync as existsSync55, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync21 } from "node:fs";
67624
68110
  import path85 from "node:path";
67625
68111
  function getPluginPrefsPath() {
67626
68112
  return pluginsPrefsPath();
@@ -67628,8 +68114,8 @@ function getPluginPrefsPath() {
67628
68114
  function getPluginPrefs() {
67629
68115
  const file2 = getPluginPrefsPath();
67630
68116
  try {
67631
- if (!existsSync53(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
67632
- const raw = readFileSync40(file2, "utf-8");
68117
+ if (!existsSync55(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
68118
+ const raw = readFileSync41(file2, "utf-8");
67633
68119
  const parsed = JSON.parse(raw);
67634
68120
  if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
67635
68121
  const clean = {};
@@ -67644,8 +68130,8 @@ function getPluginPrefs() {
67644
68130
  }
67645
68131
  function writePluginPrefs(prefs) {
67646
68132
  const file2 = getPluginPrefsPath();
67647
- mkdirSync20(path85.dirname(file2), { recursive: true });
67648
- writeFileSync23(file2, JSON.stringify(prefs, null, 2), {
68133
+ mkdirSync21(path85.dirname(file2), { recursive: true });
68134
+ writeFileSync24(file2, JSON.stringify(prefs, null, 2), {
67649
68135
  encoding: "utf-8",
67650
68136
  mode: 384
67651
68137
  });
@@ -67681,7 +68167,7 @@ __export(registry_exports, {
67681
68167
  findPlugin: () => findPlugin,
67682
68168
  isBinaryOnPath: () => isBinaryOnPath
67683
68169
  });
67684
- import { existsSync as existsSync54 } from "node:fs";
68170
+ import { existsSync as existsSync56 } from "node:fs";
67685
68171
  import path86 from "node:path";
67686
68172
  function detectLocalBin(bin) {
67687
68173
  return (cwd) => {
@@ -67698,11 +68184,11 @@ function isBinaryOnPath(bin, opts = {}) {
67698
68184
  return false;
67699
68185
  }
67700
68186
  const platform = opts.platform ?? process.platform;
67701
- const exists = opts.exists ?? existsSync54;
68187
+ const exists = opts.exists ?? existsSync56;
67702
68188
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
67703
68189
  const pathMod = platform === "win32" ? path86.win32 : path86.posix;
67704
- const sep4 = platform === "win32" ? ";" : ":";
67705
- const dirs = pathEnv.split(sep4).filter((d) => d.length > 0);
68190
+ const sep5 = platform === "win32" ? ";" : ":";
68191
+ const dirs = pathEnv.split(sep5).filter((d) => d.length > 0);
67706
68192
  const candidates = [bin];
67707
68193
  if (platform === "win32") {
67708
68194
  const pathExt = opts.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
@@ -68442,8 +68928,8 @@ __export(atMentions_exports, {
68442
68928
  extractAtMentions: () => extractAtMentions,
68443
68929
  hasAtMentions: () => hasAtMentions
68444
68930
  });
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";
68931
+ import { existsSync as existsSync59, readFileSync as readFileSync43, statSync as statSync9 } from "node:fs";
68932
+ import { basename as basename5, isAbsolute as isAbsolute5, relative as relative5, resolve as resolve7, sep as sep3 } from "node:path";
68447
68933
  function isImagePath(abs) {
68448
68934
  const ext = abs.split(".").pop()?.toLowerCase() ?? "";
68449
68935
  return ext in IMAGE_MIME_BY_EXT;
@@ -68457,9 +68943,9 @@ function isProbablyText(name, head) {
68457
68943
  return !head.includes("\0") && /[\x09\x0a\x0d\x20-\x7e]/.test(head.slice(0, 200));
68458
68944
  }
68459
68945
  function underRoot(abs, root) {
68460
- const a = resolve6(abs);
68461
- const r = resolve6(root);
68462
- const prefix = r.endsWith(sep2) ? r : r + sep2;
68946
+ const a = resolve7(abs);
68947
+ const r = resolve7(root);
68948
+ const prefix = r.endsWith(sep3) ? r : r + sep3;
68463
68949
  return a === r || a.startsWith(prefix);
68464
68950
  }
68465
68951
  function extractAtMentions(text) {
@@ -68486,7 +68972,7 @@ function extractAtMentions(text) {
68486
68972
  return out;
68487
68973
  }
68488
68974
  function resolveMention(token, cwd) {
68489
- const abs = isAbsolute4(token) ? resolve6(token) : resolve6(cwd, token);
68975
+ const abs = isAbsolute5(token) ? resolve7(token) : resolve7(cwd, token);
68490
68976
  if (!underRoot(abs, cwd) && !isImagePath(abs)) {
68491
68977
  return {
68492
68978
  raw: token,
@@ -68496,7 +68982,7 @@ function resolveMention(token, cwd) {
68496
68982
  note: "outside project root \u2014 skipped"
68497
68983
  };
68498
68984
  }
68499
- if (!existsSync57(abs)) {
68985
+ if (!existsSync59(abs)) {
68500
68986
  return {
68501
68987
  raw: token,
68502
68988
  path: token,
@@ -68548,7 +69034,7 @@ function resolveMention(token, cwd) {
68548
69034
  note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
68549
69035
  };
68550
69036
  }
68551
- const dataBase64 = readFileSync42(abs).toString("base64");
69037
+ const dataBase64 = readFileSync43(abs).toString("base64");
68552
69038
  return {
68553
69039
  raw: token,
68554
69040
  path: rel2,
@@ -68559,7 +69045,7 @@ function resolveMention(token, cwd) {
68559
69045
  };
68560
69046
  }
68561
69047
  try {
68562
- const buf = readFileSync42(abs);
69048
+ const buf = readFileSync43(abs);
68563
69049
  const head = buf.subarray(0, 800).toString("utf8");
68564
69050
  if (!isProbablyText(abs, head)) {
68565
69051
  return {
@@ -69458,7 +69944,7 @@ var init_liveTurnAbort = __esm({
69458
69944
  });
69459
69945
 
69460
69946
  // src/cli/headless/policyGate.ts
69461
- import { isAbsolute as isAbsolute5, resolve as resolve7 } from "node:path";
69947
+ import { isAbsolute as isAbsolute6, resolve as resolve8 } from "node:path";
69462
69948
  import { randomUUID as randomUUID9 } from "node:crypto";
69463
69949
  function checkStrictPolicyLoad(root, opts) {
69464
69950
  if (isPolicyEngineDisabled()) return { blocked: false, warnings: [] };
@@ -69467,7 +69953,7 @@ function checkStrictPolicyLoad(root, opts) {
69467
69953
  return { blocked: false, warnings: set2.warnings };
69468
69954
  } catch (err) {
69469
69955
  if (!(err instanceof PolicyLoadError)) throw err;
69470
- const file2 = isAbsolute5(err.file) ? err.file : resolve7(root, err.file);
69956
+ const file2 = isAbsolute6(err.file) ? err.file : resolve8(root, err.file);
69471
69957
  return {
69472
69958
  blocked: true,
69473
69959
  warnings: [],
@@ -69720,27 +70206,27 @@ var init_sandboxedFs = __esm({
69720
70206
  });
69721
70207
 
69722
70208
  // src/cli/extensions/loader.ts
69723
- import { join as join45 } from "node:path";
69724
- import { readdirSync as readdirSync11, readFileSync as readFileSync43 } from "node:fs";
70209
+ import { join as join46 } from "node:path";
70210
+ import { readdirSync as readdirSync12, readFileSync as readFileSync44 } from "node:fs";
69725
70211
  import { createHash as createHash25 } from "node:crypto";
69726
70212
  import { pathToFileURL as pathToFileURL3 } from "node:url";
69727
70213
  function globalExtensionsDir() {
69728
- return join45(zelariHome(), "extensions");
70214
+ return join46(zelariHome(), "extensions");
69729
70215
  }
69730
70216
  function projectExtensionsDir(projectRoot) {
69731
- return join45(projectRoot, ".zelari", "extensions");
70217
+ return join46(projectRoot, ".zelari", "extensions");
69732
70218
  }
69733
70219
  function sha256File(file2) {
69734
- return createHash25("sha256").update(readFileSync43(file2)).digest("hex");
70220
+ return createHash25("sha256").update(readFileSync44(file2)).digest("hex");
69735
70221
  }
69736
70222
  function candidateFiles(dir) {
69737
70223
  let names;
69738
70224
  try {
69739
- names = readdirSync11(dir);
70225
+ names = readdirSync12(dir);
69740
70226
  } catch {
69741
70227
  return [];
69742
70228
  }
69743
- return names.filter((n) => EXTENSION_FILE_EXTS.has(n.slice(n.lastIndexOf(".")).toLowerCase())).sort().map((n) => join45(dir, n));
70229
+ return names.filter((n) => EXTENSION_FILE_EXTS.has(n.slice(n.lastIndexOf(".")).toLowerCase())).sort().map((n) => join46(dir, n));
69744
70230
  }
69745
70231
  function extractExtension(mod) {
69746
70232
  const m = mod;
@@ -69769,7 +70255,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
69769
70255
  if (files.length === 0) continue;
69770
70256
  let lock = null;
69771
70257
  try {
69772
- lock = JSON.parse(readFileSync43(join45(dir.path, EXTENSIONS_LOCK_FILE), "utf8"));
70258
+ lock = JSON.parse(readFileSync44(join46(dir.path, EXTENSIONS_LOCK_FILE), "utf8"));
69773
70259
  } catch {
69774
70260
  lock = null;
69775
70261
  if (dirHasLockFile(dir.path)) {
@@ -69836,7 +70322,7 @@ function basenameOf(file2) {
69836
70322
  }
69837
70323
  function dirHasLockFile(dir) {
69838
70324
  try {
69839
- readFileSync43(join45(dir, EXTENSIONS_LOCK_FILE));
70325
+ readFileSync44(join46(dir, EXTENSIONS_LOCK_FILE));
69840
70326
  return true;
69841
70327
  } catch {
69842
70328
  return false;
@@ -72692,7 +73178,10 @@ ${ragContext}` : slicePrompt;
72692
73178
  if (opts.output === "json") {
72693
73179
  emitEvent({ type: "verification_run", ...missionVerificationPayload });
72694
73180
  }
72695
- await writeProofSafe(missionGate, { surface: "mission", sessionId: spine.sessionId }, projectRoot);
73181
+ surfaceOpsKnowledgeNotices(
73182
+ await writeProofSafe(missionGate, { surface: "mission", sessionId: spine.sessionId }, projectRoot),
73183
+ opts
73184
+ );
72696
73185
  if (missionGate.blocked) {
72697
73186
  exitCode = strictGateExitCode(missionGate);
72698
73187
  spine.missionPhase("verification", "mission-strict-blocked");
@@ -72789,6 +73278,11 @@ var init_runHeadless = __esm({
72789
73278
  ["krakenExploreModel", "ZELARI_KRAKEN_EXPLORE_MODEL"],
72790
73279
  ["krakenGeneralModel", "ZELARI_KRAKEN_GENERAL_MODEL"],
72791
73280
  ["krakenVerifyModel", "ZELARI_KRAKEN_VERIFY_MODEL"],
73281
+ // Per-tentacle thinking effort (ADR-0017) — same per-kind mapping as the
73282
+ // models above; the spawn factory reads them to override thinkingByProvider.
73283
+ ["krakenExploreThinking", "ZELARI_KRAKEN_EXPLORE_THINKING"],
73284
+ ["krakenGeneralThinking", "ZELARI_KRAKEN_GENERAL_THINKING"],
73285
+ ["krakenVerifyThinking", "ZELARI_KRAKEN_VERIFY_THINKING"],
72792
73286
  ["krakenPlannerModel", "ZELARI_KRAKEN_PLANNER_MODEL"],
72793
73287
  ["krakenDelegation", "ZELARI_KRAKEN_DELEGATION"]
72794
73288
  ];
@@ -73179,270 +73673,6 @@ var init_desktopConfig = __esm({
73179
73673
  }
73180
73674
  });
73181
73675
 
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
73676
  // src/cli/memory/jsonApi.ts
73447
73677
  var jsonApi_exports = {};
73448
73678
  __export(jsonApi_exports, {
@@ -73809,8 +74039,8 @@ async function startMemoryMcpServer(options) {
73809
74039
  let stopped = false;
73810
74040
  let finalizePromise;
73811
74041
  let resolveClosed = () => void 0;
73812
- const closed = new Promise((resolve9) => {
73813
- resolveClosed = resolve9;
74042
+ const closed = new Promise((resolve10) => {
74043
+ resolveClosed = resolve10;
73814
74044
  });
73815
74045
  const adapter = new MemoryMcpAdapter({
73816
74046
  service,
@@ -73957,8 +74187,8 @@ function startPermissionMcpServer(opts) {
73957
74187
  });
73958
74188
  let closedResolve = () => {
73959
74189
  };
73960
- const closed = new Promise((resolve9) => {
73961
- closedResolve = resolve9;
74190
+ const closed = new Promise((resolve10) => {
74191
+ closedResolve = resolve10;
73962
74192
  });
73963
74193
  let stopped = false;
73964
74194
  const send = (obj) => {
@@ -74211,7 +74441,7 @@ var init_permissionCli = __esm({
74211
74441
 
74212
74442
  // src/cli/companion/config.ts
74213
74443
  import {
74214
- existsSync as existsSync60,
74444
+ existsSync as existsSync61,
74215
74445
  mkdirSync as mkdirSync24,
74216
74446
  readFileSync as readFileSync45,
74217
74447
  writeFileSync as writeFileSync26
@@ -74229,17 +74459,17 @@ function getCompanionTokenPath() {
74229
74459
  }
74230
74460
  function ensureHome() {
74231
74461
  const home = getZelariHome();
74232
- if (!existsSync60(home)) {
74462
+ if (!existsSync61(home)) {
74233
74463
  mkdirSync24(home, { recursive: true });
74234
74464
  }
74235
74465
  }
74236
74466
  function loadCompanionConfig() {
74237
- const path103 = getCompanionConfigPath();
74238
- if (!existsSync60(path103)) {
74467
+ const path104 = getCompanionConfigPath();
74468
+ if (!existsSync61(path104)) {
74239
74469
  return { projects: [] };
74240
74470
  }
74241
74471
  try {
74242
- const raw = JSON.parse(readFileSync45(path103, "utf8"));
74472
+ const raw = JSON.parse(readFileSync45(path104, "utf8"));
74243
74473
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
74244
74474
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
74245
74475
  ).map((p3) => ({
@@ -74277,16 +74507,16 @@ function loadOrCreateToken(explicit) {
74277
74507
  return { token: explicit.trim(), created: false };
74278
74508
  }
74279
74509
  ensureHome();
74280
- const path103 = getCompanionTokenPath();
74281
- if (existsSync60(path103)) {
74282
- const t = readFileSync45(path103, "utf8").trim();
74510
+ const path104 = getCompanionTokenPath();
74511
+ if (existsSync61(path104)) {
74512
+ const t = readFileSync45(path104, "utf8").trim();
74283
74513
  if (t) return { token: t, created: false };
74284
74514
  }
74285
74515
  const token = randomBytes7(24).toString("base64url");
74286
- writeFileSync26(path103, token + "\n", "utf8");
74516
+ writeFileSync26(path104, token + "\n", "utf8");
74287
74517
  try {
74288
74518
  const fs50 = __require("node:fs");
74289
- fs50.chmodSync?.(path103, 384);
74519
+ fs50.chmodSync?.(path104, 384);
74290
74520
  } catch {
74291
74521
  }
74292
74522
  return { token, created: true };
@@ -74311,17 +74541,17 @@ function mergeProjects(cfg, extraPaths) {
74311
74541
  byId.set(p3.id, p3);
74312
74542
  }
74313
74543
  for (const raw of extraPaths) {
74314
- const path103 = raw.trim();
74315
- if (!path103) continue;
74316
- let id3 = slugFromPath(path103);
74544
+ const path104 = raw.trim();
74545
+ if (!path104) continue;
74546
+ let id3 = slugFromPath(path104);
74317
74547
  let n = 2;
74318
- while (byId.has(id3) && byId.get(id3).path !== path103) {
74319
- id3 = `${slugFromPath(path103)}-${n++}`;
74548
+ while (byId.has(id3) && byId.get(id3).path !== path104) {
74549
+ id3 = `${slugFromPath(path104)}-${n++}`;
74320
74550
  }
74321
74551
  byId.set(id3, {
74322
74552
  id: id3,
74323
- name: slugFromPath(path103),
74324
- path: path103
74553
+ name: slugFromPath(path104),
74554
+ path: path104
74325
74555
  });
74326
74556
  }
74327
74557
  return [...byId.values()];
@@ -74407,11 +74637,11 @@ function createServePermissionBridge(write, timeoutMs2 = 12e4) {
74407
74637
  onPermissionAsk(payload) {
74408
74638
  const requestId = `perm-${Date.now()}-${++seq}`;
74409
74639
  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) => {
74640
+ return new Promise((resolve10) => {
74411
74641
  const timer = setTimeout(() => {
74412
74642
  settle(requestId, "deny", true);
74413
74643
  }, timeoutMs2);
74414
- pending.set(requestId, { resolve: resolve9, timer, payload });
74644
+ pending.set(requestId, { resolve: resolve10, timer, payload });
74415
74645
  write(
74416
74646
  JSON.stringify({
74417
74647
  type: "permission.request",
@@ -74521,11 +74751,11 @@ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
74521
74751
  const choices = req.choices.map((c) => c.trim()).filter(Boolean);
74522
74752
  if (choices.length < 2) return Promise.resolve(null);
74523
74753
  const requestId = `ask-${Date.now()}-${++seq}`;
74524
- return new Promise((resolve9) => {
74754
+ return new Promise((resolve10) => {
74525
74755
  const timer = setTimeout(() => {
74526
74756
  settle(requestId, null, true);
74527
74757
  }, timeoutMs2);
74528
- pending.set(requestId, { resolve: resolve9, timer });
74758
+ pending.set(requestId, { resolve: resolve10, timer });
74529
74759
  write(
74530
74760
  JSON.stringify({
74531
74761
  type: "ask_user.request",
@@ -74625,11 +74855,13 @@ __export(harnessServer_exports, {
74625
74855
  bindHarnessTurnOptions: () => bindHarnessTurnOptions,
74626
74856
  createCliRunTurn: () => createCliRunTurn,
74627
74857
  createCliWorkspaceServices: () => createCliWorkspaceServices,
74858
+ resolveSessionCreateRoot: () => resolveSessionCreateRoot,
74628
74859
  resolveTurnLspProvider: () => resolveTurnLspProvider,
74629
74860
  runHarnessServer: () => runHarnessServer,
74630
74861
  startHarnessServer: () => startHarnessServer
74631
74862
  });
74632
74863
  import { createInterface as createInterface3 } from "node:readline";
74864
+ import path99 from "node:path";
74633
74865
  import { randomUUID as randomUUID12 } from "node:crypto";
74634
74866
  function createCliWorkspaceServices(workspaceRoot) {
74635
74867
  const mode = activePolicyLoadMode();
@@ -74655,6 +74887,23 @@ function createCliWorkspaceServices(workspaceRoot) {
74655
74887
  }
74656
74888
  };
74657
74889
  }
74890
+ function resolveSessionCreateRoot(params, cwd = process.cwd()) {
74891
+ const raw = params.workspaceRoot;
74892
+ if (typeof raw !== "string" || raw.trim() === "") {
74893
+ return {
74894
+ ok: false,
74895
+ message: "session.create requires a non-empty string `workspaceRoot` (the sidecar never substitutes its own cwd: sessions on different workspaces must stay isolated)"
74896
+ };
74897
+ }
74898
+ const root = raw.trim();
74899
+ if (!path99.isAbsolute(root)) {
74900
+ return {
74901
+ ok: false,
74902
+ 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)`
74903
+ };
74904
+ }
74905
+ return { ok: true, root };
74906
+ }
74658
74907
  function bindHarnessTurnOptions(input, workspaceRoot) {
74659
74908
  const turnInput = input;
74660
74909
  if (typeof turnInput.task !== "string" || turnInput.task.length === 0) {
@@ -74760,9 +75009,12 @@ function startHarnessServer(options = {}) {
74760
75009
  };
74761
75010
  }
74762
75011
  case "session.create": {
74763
- const root = typeof params.workspaceRoot === "string" ? params.workspaceRoot : process.cwd();
75012
+ const resolved = resolveSessionCreateRoot(params);
75013
+ if (!resolved.ok) {
75014
+ return { id: req.id ?? null, ok: false, error: { code: "bad_request", message: resolved.message } };
75015
+ }
74764
75016
  const session = server.createSession({
74765
- workspaceRoot: root,
75017
+ workspaceRoot: resolved.root,
74766
75018
  runTurn: options.runTurn ?? createCliRunTurn(
74767
75019
  asRegistryAskHandler(permissionBridge),
74768
75020
  askUserBridge.onAskUser
@@ -74984,8 +75236,8 @@ var init_harnessClient = __esm({
74984
75236
  );
74985
75237
  }
74986
75238
  const id3 = this.nextId++;
74987
- return new Promise((resolve9, reject) => {
74988
- this.pending.set(id3, { resolve: resolve9, reject });
75239
+ return new Promise((resolve10, reject) => {
75240
+ this.pending.set(id3, { resolve: resolve10, reject });
74989
75241
  this.transport.write(JSON.stringify({ id: id3, method, params }));
74990
75242
  });
74991
75243
  }
@@ -75480,8 +75732,8 @@ __export(serve_exports, {
75480
75732
  runCompanionServe: () => runCompanionServe
75481
75733
  });
75482
75734
  import { createServer as createServer3 } from "node:http";
75483
- import { existsSync as existsSync61 } from "node:fs";
75484
- import { resolve as resolve8 } from "node:path";
75735
+ import { existsSync as existsSync62 } from "node:fs";
75736
+ import { resolve as resolve9 } from "node:path";
75485
75737
  function readBody(req, max = 2e6) {
75486
75738
  return new Promise((resolveBody, reject) => {
75487
75739
  const chunks = [];
@@ -75528,8 +75780,8 @@ async function runCompanionServe(opts = {}) {
75528
75780
  const { token, created } = loadOrCreateToken(opts.token);
75529
75781
  let projects = mergeProjects(fileCfg, opts.projects ?? []);
75530
75782
  projects = projects.filter((p3) => {
75531
- const abs = resolve8(p3.path);
75532
- if (!existsSync61(abs)) {
75783
+ const abs = resolve9(p3.path);
75784
+ if (!existsSync62(abs)) {
75533
75785
  process.stderr.write(
75534
75786
  `[zelari-code serve] skip missing project path: ${p3.path}
75535
75787
  `
@@ -75548,7 +75800,7 @@ async function runCompanionServe(opts = {}) {
75548
75800
  });
75549
75801
  }
75550
75802
  if (projects.length === 0) {
75551
- const cwd = resolve8(process.cwd());
75803
+ const cwd = resolve9(process.cwd());
75552
75804
  projects = [
75553
75805
  {
75554
75806
  id: "default",
@@ -75580,9 +75832,9 @@ async function runCompanionServe(opts = {}) {
75580
75832
  return;
75581
75833
  }
75582
75834
  const url2 = parseUrl(req);
75583
- const path103 = url2.pathname.replace(/\/+$/, "") || "/";
75835
+ const path104 = url2.pathname.replace(/\/+$/, "") || "/";
75584
75836
  try {
75585
- if (req.method === "GET" && (path103 === "/health" || path103 === "/v1/health")) {
75837
+ if (req.method === "GET" && (path104 === "/health" || path104 === "/v1/health")) {
75586
75838
  sendJson2(res, 200, {
75587
75839
  ok: true,
75588
75840
  service: "zelari-companion",
@@ -75594,18 +75846,18 @@ async function runCompanionServe(opts = {}) {
75594
75846
  });
75595
75847
  return;
75596
75848
  }
75597
- if (path103.startsWith("/v1")) {
75849
+ if (path104.startsWith("/v1")) {
75598
75850
  if (!tokenMatches(token, getBearer(req))) {
75599
75851
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
75600
75852
  return;
75601
75853
  }
75602
75854
  }
75603
- if (req.method === "GET" && path103 === "/v1/config") {
75855
+ if (req.method === "GET" && path104 === "/v1/config") {
75604
75856
  const snap = buildDesktopConfigSnapshot();
75605
75857
  sendJson2(res, 200, { ok: true, ...snap });
75606
75858
  return;
75607
75859
  }
75608
- if (req.method === "GET" && path103 === "/v1/projects") {
75860
+ if (req.method === "GET" && path104 === "/v1/projects") {
75609
75861
  sendJson2(res, 200, {
75610
75862
  ok: true,
75611
75863
  projects: projects.map((p3) => ({
@@ -75616,7 +75868,7 @@ async function runCompanionServe(opts = {}) {
75616
75868
  });
75617
75869
  return;
75618
75870
  }
75619
- if (req.method === "GET" && path103 === "/v1/runs") {
75871
+ if (req.method === "GET" && path104 === "/v1/runs") {
75620
75872
  sendJson2(res, 200, {
75621
75873
  ok: true,
75622
75874
  active: runs.getActive(),
@@ -75634,7 +75886,7 @@ async function runCompanionServe(opts = {}) {
75634
75886
  });
75635
75887
  return;
75636
75888
  }
75637
- if (req.method === "POST" && path103 === "/v1/runs") {
75889
+ if (req.method === "POST" && path104 === "/v1/runs") {
75638
75890
  const raw = await readBody(req);
75639
75891
  let body = {};
75640
75892
  try {
@@ -75682,7 +75934,7 @@ async function runCompanionServe(opts = {}) {
75682
75934
  });
75683
75935
  return;
75684
75936
  }
75685
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path103);
75937
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path104);
75686
75938
  if (req.method === "GET" && eventsMatch) {
75687
75939
  const runId = eventsMatch[1];
75688
75940
  const run = runs.getRun(runId);
@@ -75747,7 +75999,7 @@ async function runCompanionServe(opts = {}) {
75747
75999
  }, 500);
75748
76000
  return;
75749
76001
  }
75750
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path103);
76002
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path104);
75751
76003
  if (req.method === "POST" && cancelMatch) {
75752
76004
  const runId = cancelMatch[1];
75753
76005
  const result = runs.cancel(runId);
@@ -75758,7 +76010,7 @@ async function runCompanionServe(opts = {}) {
75758
76010
  sendJson2(res, 200, { ok: true, cancelled: runId });
75759
76011
  return;
75760
76012
  }
75761
- const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path103);
76013
+ const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path104);
75762
76014
  if (req.method === "POST" && steerMatch) {
75763
76015
  const runId = steerMatch[1];
75764
76016
  const raw = await readBody(req);
@@ -75924,26 +76176,26 @@ __export(doctor_exports, {
75924
76176
  runDoctor: () => runDoctor
75925
76177
  });
75926
76178
  import { execSync as execSync2 } from "node:child_process";
75927
- import { existsSync as existsSync62, readFileSync as readFileSync46, readlinkSync, statSync as statSync10 } from "node:fs";
76179
+ import { existsSync as existsSync63, readFileSync as readFileSync46, readlinkSync, statSync as statSync10 } from "node:fs";
75928
76180
  import { createRequire as createRequire3 } from "node:module";
75929
76181
  import { fileURLToPath as fileURLToPath3 } from "node:url";
75930
- import path99 from "node:path";
76182
+ import path100 from "node:path";
75931
76183
  function findPackageRoot(start) {
75932
76184
  let dir = start;
75933
76185
  for (let i = 0; i < 6; i += 1) {
75934
- const candidate = path99.join(dir, "package.json");
75935
- if (existsSync62(candidate)) {
76186
+ const candidate = path100.join(dir, "package.json");
76187
+ if (existsSync63(candidate)) {
75936
76188
  try {
75937
76189
  const pkg = JSON.parse(readFileSync46(candidate, "utf8"));
75938
76190
  if (pkg.name === "zelari-code") return dir;
75939
76191
  } catch {
75940
76192
  }
75941
76193
  }
75942
- const parent = path99.dirname(dir);
76194
+ const parent = path100.dirname(dir);
75943
76195
  if (parent === dir) break;
75944
76196
  dir = parent;
75945
76197
  }
75946
- return path99.resolve(__dirname3, "..", "..", "..");
76198
+ return path100.resolve(__dirname3, "..", "..", "..");
75947
76199
  }
75948
76200
  function tryExec(cmd) {
75949
76201
  try {
@@ -75957,7 +76209,7 @@ function tryExec(cmd) {
75957
76209
  }
75958
76210
  function readPackageJson4() {
75959
76211
  try {
75960
- const pkgPath = path99.join(packageRoot, "package.json");
76212
+ const pkgPath = path100.join(packageRoot, "package.json");
75961
76213
  return JSON.parse(readFileSync46(pkgPath, "utf8"));
75962
76214
  } catch {
75963
76215
  return null;
@@ -75969,7 +76221,7 @@ function getGlobalPrefix() {
75969
76221
  return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim();
75970
76222
  }
75971
76223
  function isSourceCheckout() {
75972
- return existsSync62(path99.join(packageRoot, "src", "cli", "main.ts")) && existsSync62(path99.join(packageRoot, "apps", "desktop", "package.json"));
76224
+ return existsSync63(path100.join(packageRoot, "src", "cli", "main.ts")) && existsSync63(path100.join(packageRoot, "apps", "desktop", "package.json"));
75973
76225
  }
75974
76226
  function checkShim(pkgName) {
75975
76227
  const prefix = getGlobalPrefix();
@@ -75978,10 +76230,10 @@ function checkShim(pkgName) {
75978
76230
  }
75979
76231
  const isWin = process.platform === "win32";
75980
76232
  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)) {
76233
+ const shimPath = path100.join(prefix, shimName);
76234
+ if (!existsSync63(shimPath)) {
76235
+ const localBin = path100.join(packageRoot, "bin", "zelari-code.js");
76236
+ if (isSourceCheckout() && existsSync63(localBin)) {
75985
76237
  return WARN(
75986
76238
  `global shim not found at ${shimPath}
75987
76239
  source checkout \u2014 using ${localBin}
@@ -76014,8 +76266,8 @@ function checkShim(pkgName) {
76014
76266
  fix: npm install -g ${pkgName}@latest --force`
76015
76267
  );
76016
76268
  }
76017
- const resolved = path99.resolve(path99.dirname(shimPath), target);
76018
- const expected = path99.join(
76269
+ const resolved = path100.resolve(path100.dirname(shimPath), target);
76270
+ const expected = path100.join(
76019
76271
  prefix,
76020
76272
  "node_modules",
76021
76273
  pkgName,
@@ -76057,8 +76309,8 @@ function checkNode(pkg) {
76057
76309
  return OK(`node ${raw} (engines.node ${enginesNode ?? ">= 20.0.0"})`);
76058
76310
  }
76059
76311
  function checkBundle() {
76060
- const bundle = path99.join(packageRoot, "dist", "cli", "main.bundled.js");
76061
- if (!existsSync62(bundle)) {
76312
+ const bundle = path100.join(packageRoot, "dist", "cli", "main.bundled.js");
76313
+ if (!existsSync63(bundle)) {
76062
76314
  return FAIL(
76063
76315
  `dist/cli/main.bundled.js missing at ${bundle}
76064
76316
  fix: npm run build:cli (then reinstall or run via tsx)`
@@ -76078,7 +76330,7 @@ function checkRuntimeDeps() {
76078
76330
  const missing = [];
76079
76331
  for (const dep of required2) {
76080
76332
  try {
76081
- const localReq = createRequire3(path99.join(packageRoot, "package.json"));
76333
+ const localReq = createRequire3(path100.join(packageRoot, "package.json"));
76082
76334
  localReq.resolve(dep);
76083
76335
  } catch {
76084
76336
  missing.push(dep);
@@ -76355,7 +76607,7 @@ var init_doctor = __esm({
76355
76607
  init_metrics3();
76356
76608
  init_contextGrowthSummary();
76357
76609
  require3 = createRequire3(import.meta.url);
76358
- __dirname3 = path99.dirname(fileURLToPath3(import.meta.url));
76610
+ __dirname3 = path100.dirname(fileURLToPath3(import.meta.url));
76359
76611
  packageRoot = findPackageRoot(__dirname3);
76360
76612
  OK = (message) => ({
76361
76613
  ok: true,
@@ -76463,8 +76715,8 @@ __export(userSettings_exports, {
76463
76715
  resolveUserSettings: () => resolveUserSettings,
76464
76716
  settingsOverrides: () => settingsOverrides
76465
76717
  });
76466
- import { existsSync as existsSync63, readFileSync as readFileSync47 } from "node:fs";
76467
- import path100 from "node:path";
76718
+ import { existsSync as existsSync64, readFileSync as readFileSync47 } from "node:fs";
76719
+ import path101 from "node:path";
76468
76720
  function parseBool(raw) {
76469
76721
  const v = raw.trim().toLowerCase();
76470
76722
  if (["1", "true", "yes", "on"].includes(v)) return true;
@@ -76479,7 +76731,7 @@ function parseEnum(values, raw) {
76479
76731
  return values.includes(v) ? v : void 0;
76480
76732
  }
76481
76733
  function loadFileLayer(file2, origin, warnings) {
76482
- if (!existsSync63(file2)) return {};
76734
+ if (!existsSync64(file2)) return {};
76483
76735
  let raw;
76484
76736
  try {
76485
76737
  raw = readFileSync47(file2, "utf8");
@@ -76531,8 +76783,8 @@ function envValueFor(key, env) {
76531
76783
  function resolveUserSettings(opts = {}) {
76532
76784
  const cwd = opts.cwd ?? process.cwd();
76533
76785
  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);
76786
+ const userPath = path101.join(zelariHome(), SETTINGS_FILE_NAME);
76787
+ const projectPath = path101.join(cwd, ".zelari", SETTINGS_FILE_NAME);
76536
76788
  const warnings = [];
76537
76789
  const userLayer = loadFileLayer(userPath, "user", warnings);
76538
76790
  const projectLayer = loadFileLayer(projectPath, "project", warnings);
@@ -76590,8 +76842,8 @@ function printSettingsReport(opts = {}) {
76590
76842
  }
76591
76843
  lines.push("");
76592
76844
  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"})`);
76845
+ lines.push(` user: ${r.userPath} (${existsSync64(r.userPath) ? "loaded" : "not found"})`);
76846
+ lines.push(` project: ${r.projectPath} (${existsSync64(r.projectPath) ? "loaded" : "not found"})`);
76595
76847
  if (r.warnings.length > 0) {
76596
76848
  lines.push("");
76597
76849
  lines.push("warnings:");
@@ -76746,8 +76998,8 @@ __export(inspectSession_exports, {
76746
76998
  renderInspectReport: () => renderInspectReport,
76747
76999
  runInspectSession: () => runInspectSession
76748
77000
  });
76749
- import path101 from "node:path";
76750
- import { existsSync as existsSync64 } from "node:fs";
77001
+ import path102 from "node:path";
77002
+ import { existsSync as existsSync65 } from "node:fs";
76751
77003
  function formatLimit(limit) {
76752
77004
  return `${Math.round(limit / 1e3)}k`;
76753
77005
  }
@@ -76780,13 +77032,13 @@ function renderInspectReport(state3) {
76780
77032
  }
76781
77033
  async function runInspectSession(opts) {
76782
77034
  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)) {
77035
+ const sessionDir = path102.join(sessionsDir2, opts.sessionId);
77036
+ const eventsPath = path102.join(sessionDir, "events.jsonl");
77037
+ if (!existsSync65(sessionDir)) {
76786
77038
  console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
76787
77039
  return 1;
76788
77040
  }
76789
- if (!existsSync64(eventsPath)) {
77041
+ if (!existsSync65(eventsPath)) {
76790
77042
  console.error(`zelari-code inspect: session directory has no events.jsonl at ${eventsPath}`);
76791
77043
  return 1;
76792
77044
  }
@@ -76815,14 +77067,14 @@ __export(inspect_exports, {
76815
77067
  collectInspectReport: () => collectInspectReport,
76816
77068
  runInspect: () => runInspect
76817
77069
  });
76818
- import path102 from "node:path";
76819
- import { existsSync as existsSync65, readFileSync as readFileSync48, readdirSync as readdirSync13 } from "node:fs";
77070
+ import path103 from "node:path";
77071
+ import { existsSync as existsSync66, readFileSync as readFileSync48, readdirSync as readdirSync13 } from "node:fs";
76820
77072
  async function collectInspectReport(cwd = process.cwd()) {
76821
77073
  ensureBuiltinSkillsLoadedSync();
76822
77074
  const snap = listSkillsSnapshot(cwd);
76823
77075
  const mcp = listMcpServers(cwd);
76824
- const userMcpPath = path102.join(zelariHome(), "mcp.json");
76825
- const projectMcpPath = path102.join(cwd, ".zelari", "mcp.json");
77076
+ const userMcpPath = path103.join(zelariHome(), "mcp.json");
77077
+ const projectMcpPath = path103.join(cwd, ".zelari", "mcp.json");
76826
77078
  const globalHooks = globalHooksDir();
76827
77079
  const projectHooks = projectHooksDir(cwd);
76828
77080
  const projectTrusted = isFolderTrusted(cwd);
@@ -76848,11 +77100,11 @@ async function collectInspectReport(cwd = process.cwd()) {
76848
77100
  folders: listTrustedFolders()
76849
77101
  },
76850
77102
  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")) }
77103
+ { path: userMcpPath, exists: existsSync66(userMcpPath) },
77104
+ { path: projectMcpPath, exists: existsSync66(projectMcpPath) },
77105
+ { path: path103.join(zelariHome(), "provider.json"), exists: existsSync66(path103.join(zelariHome(), "provider.json")) },
77106
+ { path: path103.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync66(path103.join(cwd, ".zelari", "AGENTS.md")) },
77107
+ { path: path103.join(cwd, "AGENTS.md"), exists: existsSync66(path103.join(cwd, "AGENTS.md")) }
76856
77108
  ],
76857
77109
  skills: {
76858
77110
  total: snap.skills.length,
@@ -76865,7 +77117,7 @@ async function collectInspectReport(cwd = process.cwd()) {
76865
77117
  user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
76866
77118
  project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
76867
77119
  projectTrusted,
76868
- projectConfigExists: existsSync65(projectMcpPath)
77120
+ projectConfigExists: existsSync66(projectMcpPath)
76869
77121
  },
76870
77122
  hooks: {
76871
77123
  global: {
@@ -76892,12 +77144,12 @@ function listJsonFiles(dir) {
76892
77144
  }
76893
77145
  function findAgentsMd(cwd) {
76894
77146
  const candidates = [
76895
- path102.join(cwd, "AGENTS.md"),
76896
- path102.join(cwd, ".zelari", "AGENTS.md")
77147
+ path103.join(cwd, "AGENTS.md"),
77148
+ path103.join(cwd, ".zelari", "AGENTS.md")
76897
77149
  ];
76898
77150
  const found = [];
76899
77151
  for (const c of candidates) {
76900
- if (existsSync65(c)) {
77152
+ if (existsSync66(c)) {
76901
77153
  try {
76902
77154
  const text = readFileSync48(c, "utf8");
76903
77155
  found.push(`${c} (${text.length} bytes)`);
@@ -77352,12 +77604,12 @@ function mergeChanges(unstaged, staged, untrackedPaths) {
77352
77604
  return [...byPath.values()].sort((a, b) => churn(b) - churn(a));
77353
77605
  }
77354
77606
  function runGit(args, cwd) {
77355
- return new Promise((resolve9, reject) => {
77607
+ return new Promise((resolve10, reject) => {
77356
77608
  execFile(
77357
77609
  "git",
77358
77610
  args,
77359
77611
  { cwd, timeout: 5e3, windowsHide: true, maxBuffer: 4 * 1024 * 1024 },
77360
- (err, stdout) => err ? reject(err) : resolve9(stdout)
77612
+ (err, stdout) => err ? reject(err) : resolve10(stdout)
77361
77613
  );
77362
77614
  });
77363
77615
  }
@@ -79936,7 +80188,7 @@ init_toolPermissions();
79936
80188
  init_askUserTimeout();
79937
80189
  function createPermissionAskHandler(opts) {
79938
80190
  const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
79939
- return (req) => new Promise((resolve9) => {
80191
+ return (req) => new Promise((resolve10) => {
79940
80192
  const cats = req.categories;
79941
80193
  const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
79942
80194
  const title = `Allow tool "${req.toolName}"?`;
@@ -79962,7 +80214,7 @@ ${detail}${note}${claimsBlock}
79962
80214
  cancelAskTimeout();
79963
80215
  setPicker2(null);
79964
80216
  if (note2) appendSystem2?.(note2, Date.now());
79965
- resolve9(ok);
80217
+ resolve10(ok);
79966
80218
  };
79967
80219
  cancelAskTimeout = armPickerTimeout(
79968
80220
  () => finish2(
@@ -80161,10 +80413,10 @@ function useChatTurn(params) {
80161
80413
  }
80162
80414
  } catch {
80163
80415
  }
80164
- const onAskUser = setPicker2 ? (req) => new Promise((resolve9) => {
80416
+ const onAskUser = setPicker2 ? (req) => new Promise((resolve10) => {
80165
80417
  const choices = req.choices ?? [];
80166
80418
  if (choices.length < 2) {
80167
- resolve9(null);
80419
+ resolve10(null);
80168
80420
  return;
80169
80421
  }
80170
80422
  setLastClarification({
@@ -80188,7 +80440,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
80188
80440
  settled = true;
80189
80441
  cancelAskTimeout();
80190
80442
  setPicker2(null);
80191
- resolve9(value);
80443
+ resolve10(value);
80192
80444
  };
80193
80445
  cancelAskTimeout = armPickerTimeout(
80194
80446
  () => {
@@ -81134,10 +81386,10 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
81134
81386
  const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
81135
81387
  const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
81136
81388
  const workPhase = getPhase();
81137
- const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve9) => {
81389
+ const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve10) => {
81138
81390
  const choices = req.choices ?? [];
81139
81391
  if (choices.length < 2) {
81140
- resolve9(null);
81392
+ resolve10(null);
81141
81393
  return;
81142
81394
  }
81143
81395
  setLastClarification({
@@ -81161,7 +81413,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81161
81413
  settled = true;
81162
81414
  cancelAskTimeout();
81163
81415
  setPicker2(null);
81164
- resolve9(value);
81416
+ resolve10(value);
81165
81417
  };
81166
81418
  cancelAskTimeout = armPickerTimeout(
81167
81419
  () => {
@@ -81343,10 +81595,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81343
81595
  appendSystem(setMessages, message, Date.now());
81344
81596
  },
81345
81597
  // v1.8.0: pause council when a member asks a structured question.
81346
- onClarification: setPicker2 ? (req) => new Promise((resolve9) => {
81598
+ onClarification: setPicker2 ? (req) => new Promise((resolve10) => {
81347
81599
  const choices = req.choices ?? [];
81348
81600
  if (choices.length < 2) {
81349
- resolve9(null);
81601
+ resolve10(null);
81350
81602
  return;
81351
81603
  }
81352
81604
  setLastClarification({
@@ -81367,7 +81619,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
81367
81619
  if (settled) return;
81368
81620
  settled = true;
81369
81621
  setPicker2(null);
81370
- resolve9(value);
81622
+ resolve10(value);
81371
81623
  };
81372
81624
  setPicker2({
81373
81625
  kind: "clarification",
@@ -82025,10 +82277,10 @@ function usePermissionBroker(opts) {
82025
82277
  setPicker: setPicker2,
82026
82278
  appendSystem: (msg, at) => appendSystem(setMessages, msg, at ?? Date.now())
82027
82279
  });
82028
- const onQuestion = (req) => new Promise((resolve9) => {
82280
+ const onQuestion = (req) => new Promise((resolve10) => {
82029
82281
  const choices = (req.choices ?? []).map((c) => String(c).trim()).filter((c) => c.length > 0);
82030
82282
  if (choices.length < 2) {
82031
- resolve9(null);
82283
+ resolve10(null);
82032
82284
  return;
82033
82285
  }
82034
82286
  let settled = false;
@@ -82038,7 +82290,7 @@ function usePermissionBroker(opts) {
82038
82290
  settled = true;
82039
82291
  cancelWaitTimeout?.();
82040
82292
  setPicker2(null);
82041
- resolve9(value);
82293
+ resolve10(value);
82042
82294
  };
82043
82295
  const waitTimeoutMs = askUserTimeoutMs();
82044
82296
  cancelWaitTimeout = armPickerTimeout(() => {
@@ -82107,7 +82359,7 @@ init_mode();
82107
82359
  init_ledger();
82108
82360
 
82109
82361
  // src/cli/evolution/proposals.ts
82110
- import { existsSync as existsSync51, readFileSync as readFileSync39 } from "node:fs";
82362
+ import { existsSync as existsSync53, readFileSync as readFileSync40 } from "node:fs";
82111
82363
  import path81 from "node:path";
82112
82364
  var PROPOSALS_REL = path81.join(".zelari", "evolution", "proposals.jsonl");
82113
82365
  function proposalsPath(cwd) {
@@ -82115,10 +82367,10 @@ function proposalsPath(cwd) {
82115
82367
  }
82116
82368
  function readProposalStore(cwd) {
82117
82369
  const file2 = proposalsPath(cwd);
82118
- if (!existsSync51(file2)) return [];
82370
+ if (!existsSync53(file2)) return [];
82119
82371
  let raw;
82120
82372
  try {
82121
- raw = readFileSync39(file2, "utf8");
82373
+ raw = readFileSync40(file2, "utf8");
82122
82374
  } catch {
82123
82375
  return [];
82124
82376
  }
@@ -84008,7 +84260,7 @@ async function handleKrakenFanout(ctx, raw) {
84008
84260
  }
84009
84261
  return;
84010
84262
  }
84011
- const absCsv = isAbsolute3(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
84263
+ const absCsv = isAbsolute4(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
84012
84264
  try {
84013
84265
  await fs39.access(absCsv);
84014
84266
  } catch {
@@ -84044,7 +84296,7 @@ async function handleKrakenFanout(ctx, raw) {
84044
84296
  );
84045
84297
  }
84046
84298
  }
84047
- function isAbsolute3(p3) {
84299
+ function isAbsolute4(p3) {
84048
84300
  return /^([A-Za-z]:[\\/]|[\\/])/.test(p3);
84049
84301
  }
84050
84302
  function joinPath(a, b) {
@@ -84416,7 +84668,7 @@ ${browsers.output}` : "") + (browsers.ok ? "" : `
84416
84668
  return result;
84417
84669
  }
84418
84670
  function runPlaywrightInstallChromium(cwd, executor) {
84419
- return new Promise((resolve9) => {
84671
+ return new Promise((resolve10) => {
84420
84672
  let stdout = "";
84421
84673
  let stderr = "";
84422
84674
  const stdio = ["ignore", "pipe", "pipe"];
@@ -84429,7 +84681,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84429
84681
  stderr += chunk.toString();
84430
84682
  });
84431
84683
  child.on("error", (err) => {
84432
- resolve9({
84684
+ resolve10({
84433
84685
  ok: false,
84434
84686
  output: stdout + stderr,
84435
84687
  error: err.message,
@@ -84438,7 +84690,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84438
84690
  });
84439
84691
  child.on("close", (code) => {
84440
84692
  const ok = code === 0;
84441
- resolve9({
84693
+ resolve10({
84442
84694
  ok,
84443
84695
  output: stdout + stderr,
84444
84696
  exitCode: code,
@@ -84448,7 +84700,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
84448
84700
  });
84449
84701
  }
84450
84702
  function runNpm2(executor, args, cwd, mode, npmCliPath) {
84451
- return new Promise((resolve9) => {
84703
+ return new Promise((resolve10) => {
84452
84704
  let stdout = "";
84453
84705
  let stderr = "";
84454
84706
  const stdio = ["ignore", "pipe", "pipe"];
@@ -84460,7 +84712,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
84460
84712
  stderr += chunk.toString();
84461
84713
  });
84462
84714
  child.on("error", (err) => {
84463
- resolve9({
84715
+ resolve10({
84464
84716
  ok: false,
84465
84717
  output: stdout + stderr,
84466
84718
  error: err.message,
@@ -84469,7 +84721,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
84469
84721
  });
84470
84722
  child.on("close", (code) => {
84471
84723
  const ok = code === 0;
84472
- resolve9({
84724
+ resolve10({
84473
84725
  ok,
84474
84726
  output: stdout + stderr,
84475
84727
  exitCode: code,
@@ -84568,7 +84820,7 @@ ${lineage}
84568
84820
 
84569
84821
  // src/cli/branchManager.ts
84570
84822
  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";
84823
+ 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
84824
  import path88 from "node:path";
84573
84825
  var META_FILENAME = "meta.json";
84574
84826
  var SESSIONS_SUBDIR = "sessions";
@@ -84589,11 +84841,11 @@ function sessionsPathFor(name, baseDir) {
84589
84841
  }
84590
84842
  function readBranchMeta(name, baseDir) {
84591
84843
  const metaPath = metaPathFor(name, baseDir);
84592
- if (!existsSync55(metaPath)) {
84844
+ if (!existsSync57(metaPath)) {
84593
84845
  throw new BranchNotFoundError(`Branch "${name}" not found`);
84594
84846
  }
84595
84847
  try {
84596
- const raw = readFileSync41(metaPath, "utf-8");
84848
+ const raw = readFileSync42(metaPath, "utf-8");
84597
84849
  const parsed = JSON.parse(raw);
84598
84850
  if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
84599
84851
  throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
@@ -84610,8 +84862,8 @@ function readBranchMeta(name, baseDir) {
84610
84862
  }
84611
84863
  function writeBranchMeta(name, baseDir, meta3) {
84612
84864
  const metaPath = metaPathFor(name, baseDir);
84613
- mkdirSync21(path88.dirname(metaPath), { recursive: true });
84614
- writeFileSync24(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
84865
+ mkdirSync22(path88.dirname(metaPath), { recursive: true });
84866
+ writeFileSync25(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
84615
84867
  }
84616
84868
  async function countSessions(name, baseDir) {
84617
84869
  const sessionsPath = sessionsPathFor(name, baseDir);
@@ -84649,7 +84901,7 @@ var SessionNotFoundError = class extends Error {
84649
84901
  };
84650
84902
  function branchExists(name, baseDir = getBranchesBaseDir()) {
84651
84903
  const bp = branchPathFor(name, baseDir);
84652
- return existsSync55(bp) && existsSync55(metaPathFor(name, baseDir));
84904
+ return existsSync57(bp) && existsSync57(metaPathFor(name, baseDir));
84653
84905
  }
84654
84906
  async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
84655
84907
  if (!name || name.trim().length === 0) {
@@ -84662,12 +84914,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
84662
84914
  throw new BranchAlreadyExistsError(name);
84663
84915
  }
84664
84916
  const sourcePath = path88.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
84665
- if (!existsSync55(sourcePath)) {
84917
+ if (!existsSync57(sourcePath)) {
84666
84918
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
84667
84919
  }
84668
84920
  const branchPath = branchPathFor(name, baseDir);
84669
84921
  const branchSessionsPath = sessionsPathFor(name, baseDir);
84670
- mkdirSync21(branchSessionsPath, { recursive: true });
84922
+ mkdirSync22(branchSessionsPath, { recursive: true });
84671
84923
  const destPath = path88.join(branchSessionsPath, `${fromSessionId}.jsonl`);
84672
84924
  await fs42.copyFile(sourcePath, destPath);
84673
84925
  const meta3 = {
@@ -84695,7 +84947,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
84695
84947
  const results = [];
84696
84948
  for (const entry of entries) {
84697
84949
  const metaPath = metaPathFor(entry, baseDir);
84698
- if (!existsSync55(metaPath)) continue;
84950
+ if (!existsSync57(metaPath)) continue;
84699
84951
  try {
84700
84952
  const meta3 = readBranchMeta(entry, baseDir);
84701
84953
  const sessionCount = await countSessions(entry, baseDir);
@@ -84883,7 +85135,7 @@ async function handleWorkspaceReset(ctx, force) {
84883
85135
  init_provider2();
84884
85136
 
84885
85137
  // 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";
85138
+ import { promises as fs44, existsSync as existsSync58, statSync as statSync8, renameSync as renameSync6, appendFileSync as appendFileSync5, mkdirSync as mkdirSync23 } from "node:fs";
84887
85139
  init_paths();
84888
85140
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
84889
85141
  async function readSkillHistory(file2) {
@@ -86294,9 +86546,9 @@ init_metrics3();
86294
86546
  init_providerConfig();
86295
86547
 
86296
86548
  // src/cli/wizard/firstRun.ts
86297
- import { existsSync as existsSync58 } from "node:fs";
86549
+ import { existsSync as existsSync60 } from "node:fs";
86298
86550
  function shouldRunWizard(input) {
86299
- const exists = input.exists ?? existsSync58;
86551
+ const exists = input.exists ?? existsSync60;
86300
86552
  if (input.hasResetConfigFlag) {
86301
86553
  return { shouldRun: true, reason: "--reset-config flag forced wizard" };
86302
86554
  }
@@ -86769,8 +87021,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
86769
87021
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
86770
87022
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
86771
87023
  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";
87024
+ 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);
87025
+ name = path104 && /^[a-z0-9]/.test(path104) ? path104 : "imported-skill";
86774
87026
  } catch {
86775
87027
  name = "imported-skill";
86776
87028
  }
@@ -86933,7 +87185,7 @@ function runPreflight() {
86933
87185
  }
86934
87186
  async function backgroundUpdateCheck() {
86935
87187
  if (process.env.ANATHEMA_DEV === "1") return;
86936
- await new Promise((resolve9) => setTimeout(resolve9, 3e3));
87188
+ await new Promise((resolve10) => setTimeout(resolve10, 3e3));
86937
87189
  try {
86938
87190
  const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_updater(), updater_exports));
86939
87191
  const info = await checkForUpdate2();
@@ -87183,7 +87435,7 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87183
87435
  }
87184
87436
  if (argv.includes("--help") || argv.includes("-h")) {
87185
87437
  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"
87438
+ "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 --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
87439
  );
87188
87440
  process.exit(0);
87189
87441
  }
@@ -87223,6 +87475,11 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87223
87475
  if (!Array.isArray(parsed)) throw new Error("--args must be a JSON array");
87224
87476
  args = parsed.map(String);
87225
87477
  }
87478
+ const envValues = [];
87479
+ for (let i = 0; i < argv.length - 1; i++) {
87480
+ if (argv[i] === "--env") envValues.push(argv[i + 1]);
87481
+ }
87482
+ const env = parseMcpEnvFlag(envValues);
87226
87483
  const timeoutMs2 = timeoutRaw !== void 0 ? Number(timeoutRaw) : void 0;
87227
87484
  if (timeoutMs2 !== void 0 && (!Number.isFinite(timeoutMs2) || timeoutMs2 <= 0)) {
87228
87485
  throw new Error("--timeout must be a positive number of milliseconds");
@@ -87238,7 +87495,7 @@ proposals: npm run evolve:propose \u2014 decisions in npm run evolve:decide (P1:
87238
87495
  scope,
87239
87496
  name,
87240
87497
  projectRoot: cwd,
87241
- config: url2 ? { type: "http", url: url2, timeoutMs: timeoutMs2, serial: true, enabled } : { command, args, enabled }
87498
+ config: url2 ? { type: "http", url: url2, timeoutMs: timeoutMs2, serial: true, enabled, env } : { command, args, env, enabled }
87242
87499
  });
87243
87500
  if (!result.ok) throw new Error(result.error);
87244
87501
  console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
@@ -87729,7 +87986,7 @@ function main() {
87729
87986
  closeMcpClients2();
87730
87987
  } catch {
87731
87988
  }
87732
- await new Promise((resolve9) => setImmediate(resolve9));
87989
+ await new Promise((resolve10) => setImmediate(resolve10));
87733
87990
  process.exit(code);
87734
87991
  }).catch(async (err) => {
87735
87992
  console.error(