zelari-code 1.14.3 → 1.15.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.
@@ -308,7 +308,7 @@ async function refreshGrokToken(options) {
308
308
  return parseTokenResponseBody(obj, accessToken);
309
309
  }
310
310
  async function openBrowser(url2) {
311
- const { spawn: spawn10 } = await import("node:child_process");
311
+ const { spawn: spawn11 } = await import("node:child_process");
312
312
  const cmd = (() => {
313
313
  switch (process.platform) {
314
314
  case "darwin":
@@ -321,7 +321,7 @@ async function openBrowser(url2) {
321
321
  })();
322
322
  return new Promise((resolve, reject) => {
323
323
  try {
324
- const child = spawn10(cmd.bin, cmd.args, {
324
+ const child = spawn11(cmd.bin, cmd.args, {
325
325
  stdio: "ignore",
326
326
  detached: true
327
327
  });
@@ -1543,10 +1543,10 @@ function mergeDefs(...defs) {
1543
1543
  function cloneDef(schema) {
1544
1544
  return mergeDefs(schema._zod.def);
1545
1545
  }
1546
- function getElementAtPath(obj, path36) {
1547
- if (!path36)
1546
+ function getElementAtPath(obj, path37) {
1547
+ if (!path37)
1548
1548
  return obj;
1549
- return path36.reduce((acc, key) => acc?.[key], obj);
1549
+ return path37.reduce((acc, key) => acc?.[key], obj);
1550
1550
  }
1551
1551
  function promiseAllObject(promisesObj) {
1552
1552
  const keys = Object.keys(promisesObj);
@@ -1874,11 +1874,11 @@ function explicitlyAborted(x, startIndex = 0) {
1874
1874
  }
1875
1875
  return false;
1876
1876
  }
1877
- function prefixIssues(path36, issues) {
1877
+ function prefixIssues(path37, issues) {
1878
1878
  return issues.map((iss) => {
1879
1879
  var _a3;
1880
1880
  (_a3 = iss).path ?? (_a3.path = []);
1881
- iss.path.unshift(path36);
1881
+ iss.path.unshift(path37);
1882
1882
  return iss;
1883
1883
  });
1884
1884
  }
@@ -2096,16 +2096,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2096
2096
  }
2097
2097
  function formatError(error51, mapper = (issue2) => issue2.message) {
2098
2098
  const fieldErrors = { _errors: [] };
2099
- const processError = (error52, path36 = []) => {
2099
+ const processError = (error52, path37 = []) => {
2100
2100
  for (const issue2 of error52.issues) {
2101
2101
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2102
- issue2.errors.map((issues) => processError({ issues }, [...path36, ...issue2.path]));
2102
+ issue2.errors.map((issues) => processError({ issues }, [...path37, ...issue2.path]));
2103
2103
  } else if (issue2.code === "invalid_key") {
2104
- processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
2104
+ processError({ issues: issue2.issues }, [...path37, ...issue2.path]);
2105
2105
  } else if (issue2.code === "invalid_element") {
2106
- processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
2106
+ processError({ issues: issue2.issues }, [...path37, ...issue2.path]);
2107
2107
  } else {
2108
- const fullpath = [...path36, ...issue2.path];
2108
+ const fullpath = [...path37, ...issue2.path];
2109
2109
  if (fullpath.length === 0) {
2110
2110
  fieldErrors._errors.push(mapper(issue2));
2111
2111
  } else {
@@ -2132,17 +2132,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2132
2132
  }
2133
2133
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2134
2134
  const result = { errors: [] };
2135
- const processError = (error52, path36 = []) => {
2135
+ const processError = (error52, path37 = []) => {
2136
2136
  var _a3, _b;
2137
2137
  for (const issue2 of error52.issues) {
2138
2138
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2139
- issue2.errors.map((issues) => processError({ issues }, [...path36, ...issue2.path]));
2139
+ issue2.errors.map((issues) => processError({ issues }, [...path37, ...issue2.path]));
2140
2140
  } else if (issue2.code === "invalid_key") {
2141
- processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
2141
+ processError({ issues: issue2.issues }, [...path37, ...issue2.path]);
2142
2142
  } else if (issue2.code === "invalid_element") {
2143
- processError({ issues: issue2.issues }, [...path36, ...issue2.path]);
2143
+ processError({ issues: issue2.issues }, [...path37, ...issue2.path]);
2144
2144
  } else {
2145
- const fullpath = [...path36, ...issue2.path];
2145
+ const fullpath = [...path37, ...issue2.path];
2146
2146
  if (fullpath.length === 0) {
2147
2147
  result.errors.push(mapper(issue2));
2148
2148
  continue;
@@ -2174,8 +2174,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2174
2174
  }
2175
2175
  function toDotPath(_path) {
2176
2176
  const segs = [];
2177
- const path36 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2178
- for (const seg of path36) {
2177
+ const path37 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2178
+ for (const seg of path37) {
2179
2179
  if (typeof seg === "number")
2180
2180
  segs.push(`[${seg}]`);
2181
2181
  else if (typeof seg === "symbol")
@@ -15678,13 +15678,13 @@ function resolveRef(ref, ctx) {
15678
15678
  if (!ref.startsWith("#")) {
15679
15679
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15680
15680
  }
15681
- const path36 = ref.slice(1).split("/").filter(Boolean);
15682
- if (path36.length === 0) {
15681
+ const path37 = ref.slice(1).split("/").filter(Boolean);
15682
+ if (path37.length === 0) {
15683
15683
  return ctx.rootSchema;
15684
15684
  }
15685
15685
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15686
- if (path36[0] === defsKey) {
15687
- const key = path36[1];
15686
+ if (path37[0] === defsKey) {
15687
+ const key = path37[1];
15688
15688
  if (!key || !ctx.defs[key]) {
15689
15689
  throw new Error(`Reference not found: ${ref}`);
15690
15690
  }
@@ -17784,11 +17784,11 @@ var init_tools = __esm({
17784
17784
  if (!ctx.addDocument)
17785
17785
  return "Knowledge vault tool not available.";
17786
17786
  const title = args["title"] || "New Document";
17787
- const path36 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17787
+ const path37 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17788
17788
  const content = args["content"] || "";
17789
17789
  const tags = args["tags"] || [];
17790
17790
  ctx.addDocument({
17791
- path: path36,
17791
+ path: path37,
17792
17792
  title,
17793
17793
  content,
17794
17794
  format: "markdown",
@@ -17797,7 +17797,7 @@ var init_tools = __esm({
17797
17797
  workspaceId: ctx.workspaceId
17798
17798
  });
17799
17799
  ctx.addActivity("vault", "created document", title);
17800
- return `Document "${title}" created at "${path36}".`;
17800
+ return `Document "${title}" created at "${path37}".`;
17801
17801
  }
17802
17802
  }
17803
17803
  ];
@@ -22509,21 +22509,21 @@ function normalizeAuth(auth) {
22509
22509
  return "agent";
22510
22510
  }
22511
22511
  function readSecrets() {
22512
- const path36 = getSshSecretsPath();
22513
- if (!existsSync9(path36)) return {};
22512
+ const path37 = getSshSecretsPath();
22513
+ if (!existsSync9(path37)) return {};
22514
22514
  try {
22515
- return JSON.parse(readFileSync7(path36, "utf8"));
22515
+ return JSON.parse(readFileSync7(path37, "utf8"));
22516
22516
  } catch {
22517
22517
  return {};
22518
22518
  }
22519
22519
  }
22520
22520
  function writeSecrets(data) {
22521
- const path36 = getSshSecretsPath();
22522
- mkdirSync6(dirname2(path36), { recursive: true });
22523
- writeFileSync5(path36, `${JSON.stringify(data, null, 2)}
22521
+ const path37 = getSshSecretsPath();
22522
+ mkdirSync6(dirname2(path37), { recursive: true });
22523
+ writeFileSync5(path37, `${JSON.stringify(data, null, 2)}
22524
22524
  `, "utf8");
22525
22525
  try {
22526
- chmodSync(path36, 384);
22526
+ chmodSync(path37, 384);
22527
22527
  } catch {
22528
22528
  }
22529
22529
  }
@@ -22552,10 +22552,10 @@ function deleteSshPassword(id) {
22552
22552
  writeSecrets({ passwords });
22553
22553
  }
22554
22554
  function readStore2() {
22555
- const path36 = getSshTargetsPath();
22556
- if (!existsSync9(path36)) return [];
22555
+ const path37 = getSshTargetsPath();
22556
+ if (!existsSync9(path37)) return [];
22557
22557
  try {
22558
- const parsed = JSON.parse(readFileSync7(path36, "utf8"));
22558
+ const parsed = JSON.parse(readFileSync7(path37, "utf8"));
22559
22559
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
22560
22560
  return list.filter(
22561
22561
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -22570,11 +22570,11 @@ function readStore2() {
22570
22570
  }
22571
22571
  }
22572
22572
  function writeStore2(targets) {
22573
- const path36 = getSshTargetsPath();
22574
- mkdirSync6(dirname2(path36), { recursive: true });
22573
+ const path37 = getSshTargetsPath();
22574
+ mkdirSync6(dirname2(path37), { recursive: true });
22575
22575
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
22576
22576
  writeFileSync5(
22577
- path36,
22577
+ path37,
22578
22578
  `${JSON.stringify({ targets: clean }, null, 2)}
22579
22579
  `,
22580
22580
  "utf8"
@@ -22820,11 +22820,11 @@ function formatSshTargetsForPrompt() {
22820
22820
  ];
22821
22821
  for (const t of targets) {
22822
22822
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
22823
- const path36 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
22823
+ const path37 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
22824
22824
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
22825
22825
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
22826
22826
  lines.push(
22827
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path36}${tags}${allow}`
22827
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path37}${tags}${allow}`
22828
22828
  );
22829
22829
  }
22830
22830
  return lines.join("\n");
@@ -22949,6 +22949,304 @@ var init_tools6 = __esm({
22949
22949
  }
22950
22950
  });
22951
22951
 
22952
+ // src/cli/workspace/worldModel.ts
22953
+ import { promises as fs13 } from "node:fs";
22954
+ import path23 from "node:path";
22955
+ import { spawn as spawn5 } from "node:child_process";
22956
+ function worldDir(cwd) {
22957
+ return path23.join(cwd, WORLD_DIR_NAME);
22958
+ }
22959
+ async function ensureWorldDir(cwd) {
22960
+ const dir = worldDir(cwd);
22961
+ await fs13.mkdir(dir, { recursive: true });
22962
+ return dir;
22963
+ }
22964
+ async function appendTimeline(cwd, entry) {
22965
+ const dir = await ensureWorldDir(cwd);
22966
+ const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n";
22967
+ await fs13.appendFile(path23.join(dir, TIMELINE_FILE), line, "utf8");
22968
+ }
22969
+ async function readChecks(cwd) {
22970
+ const p3 = path23.join(worldDir(cwd), CHECKS_FILE);
22971
+ try {
22972
+ const raw = await fs13.readFile(p3, "utf8");
22973
+ const parsed = JSON.parse(raw);
22974
+ return Array.isArray(parsed.checks) ? parsed.checks : [];
22975
+ } catch {
22976
+ return [];
22977
+ }
22978
+ }
22979
+ function runShell(command, cwd, timeoutMs, signal) {
22980
+ return new Promise((resolve) => {
22981
+ const isWin = process.platform === "win32";
22982
+ const child = spawn5(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
22983
+ cwd,
22984
+ env: process.env,
22985
+ windowsHide: true,
22986
+ stdio: ["ignore", "pipe", "pipe"]
22987
+ });
22988
+ let stdout = "";
22989
+ let stderr = "";
22990
+ let settled = false;
22991
+ const finish = (exitCode) => {
22992
+ if (settled) return;
22993
+ settled = true;
22994
+ resolve({ exitCode, stdout, stderr });
22995
+ };
22996
+ const timer = setTimeout(() => {
22997
+ try {
22998
+ child.kill("SIGTERM");
22999
+ } catch {
23000
+ }
23001
+ finish(124);
23002
+ }, timeoutMs);
23003
+ if (signal) {
23004
+ if (signal.aborted) {
23005
+ try {
23006
+ child.kill("SIGTERM");
23007
+ } catch {
23008
+ }
23009
+ clearTimeout(timer);
23010
+ finish(130);
23011
+ return;
23012
+ }
23013
+ signal.addEventListener(
23014
+ "abort",
23015
+ () => {
23016
+ try {
23017
+ child.kill("SIGTERM");
23018
+ } catch {
23019
+ }
23020
+ clearTimeout(timer);
23021
+ finish(130);
23022
+ },
23023
+ { once: true }
23024
+ );
23025
+ }
23026
+ child.stdout?.on("data", (d) => {
23027
+ stdout += d.toString("utf8");
23028
+ if (stdout.length > 32e3) stdout = stdout.slice(0, 32e3);
23029
+ });
23030
+ child.stderr?.on("data", (d) => {
23031
+ stderr += d.toString("utf8");
23032
+ if (stderr.length > 32e3) stderr = stderr.slice(0, 32e3);
23033
+ });
23034
+ child.on("error", () => {
23035
+ clearTimeout(timer);
23036
+ finish(1);
23037
+ });
23038
+ child.on("close", (code) => {
23039
+ clearTimeout(timer);
23040
+ finish(code ?? 1);
23041
+ });
23042
+ });
23043
+ }
23044
+ async function runBacktest(cwd, signal) {
23045
+ const checksPath = path23.join(worldDir(cwd), CHECKS_FILE);
23046
+ const hypothesisPath = path23.join(worldDir(cwd), HYPOTHESIS_FILE);
23047
+ const checks = await readChecks(cwd);
23048
+ if (checks.length === 0) {
23049
+ return {
23050
+ ok: false,
23051
+ passed: 0,
23052
+ failed: 0,
23053
+ total: 0,
23054
+ results: [],
23055
+ hypothesisPath,
23056
+ checksPath
23057
+ };
23058
+ }
23059
+ const results = [];
23060
+ for (const c of checks) {
23061
+ const expectExit = c.expectExit ?? 0;
23062
+ const timeoutMs = c.timeoutMs ?? 12e4;
23063
+ const start = Date.now();
23064
+ const { exitCode, stdout, stderr } = await runShell(c.command, cwd, timeoutMs, signal);
23065
+ const combined = `${stdout}${stderr}`;
23066
+ const preview = combined.slice(0, 400);
23067
+ let ok = exitCode === expectExit;
23068
+ let mismatch;
23069
+ if (!ok) {
23070
+ mismatch = `exit ${exitCode} (expected ${expectExit})`;
23071
+ } else if (c.expectStdoutIncludes && !combined.includes(c.expectStdoutIncludes)) {
23072
+ ok = false;
23073
+ mismatch = `stdout missing substring: ${c.expectStdoutIncludes}`;
23074
+ }
23075
+ results.push({
23076
+ id: c.id,
23077
+ command: c.command,
23078
+ ok,
23079
+ exitCode,
23080
+ expectExit,
23081
+ durationMs: Date.now() - start,
23082
+ stdoutPreview: preview,
23083
+ ...mismatch ? { mismatch } : {}
23084
+ });
23085
+ }
23086
+ const passed = results.filter((r) => r.ok).length;
23087
+ const failed = results.length - passed;
23088
+ const summary = {
23089
+ ok: failed === 0 && results.length > 0,
23090
+ passed,
23091
+ failed,
23092
+ total: results.length,
23093
+ results,
23094
+ hypothesisPath,
23095
+ checksPath
23096
+ };
23097
+ await appendTimeline(cwd, { kind: "backtest", ok: summary.ok, passed, failed, total: results.length });
23098
+ return summary;
23099
+ }
23100
+ function createWorldModelTools() {
23101
+ if (process.env["ZELARI_SCHEMA_LOOP"] === "0") return [];
23102
+ return [
23103
+ updateWorldHypothesisTool,
23104
+ setWorldChecksTool,
23105
+ runBacktestTool,
23106
+ recordWorldObservationTool
23107
+ ];
23108
+ }
23109
+ var WORLD_DIR_NAME, HYPOTHESIS_FILE, CHECKS_FILE, TIMELINE_FILE, UpdateHypothesisSchema, updateWorldHypothesisTool, WorldCheckSchema, SetChecksSchema, setWorldChecksTool, RunBacktestSchema, runBacktestTool, RecordObsSchema, recordWorldObservationTool;
23110
+ var init_worldModel = __esm({
23111
+ "src/cli/workspace/worldModel.ts"() {
23112
+ "use strict";
23113
+ init_zod();
23114
+ init_toolTypes();
23115
+ WORLD_DIR_NAME = path23.join(".zelari", "world");
23116
+ HYPOTHESIS_FILE = "hypothesis.md";
23117
+ CHECKS_FILE = "checks.json";
23118
+ TIMELINE_FILE = "timeline.jsonl";
23119
+ UpdateHypothesisSchema = external_exports.object({
23120
+ content: external_exports.string().min(1).describe("Full markdown hypothesis / working notes (replaces file)."),
23121
+ append: external_exports.boolean().optional().describe("If true, append content under a timestamp instead of replacing.")
23122
+ });
23123
+ updateWorldHypothesisTool = {
23124
+ name: "update_world_hypothesis",
23125
+ description: "Write or append the task world-model hypothesis (Schema-style notes.md) to .zelari/world/hypothesis.md. Use this to record what you believe is true about the bug/system, competing hypotheses, and what experiment would discriminate them. Do NOT claim work is done until run_backtest is green.",
23126
+ permissions: ["write"],
23127
+ timeoutMs: 1e4,
23128
+ inputSchema: UpdateHypothesisSchema,
23129
+ execute: async (args, ctx) => {
23130
+ try {
23131
+ const dir = await ensureWorldDir(ctx.cwd);
23132
+ const file2 = path23.join(dir, HYPOTHESIS_FILE);
23133
+ if (args.append) {
23134
+ const block = `
23135
+
23136
+ ## ${(/* @__PURE__ */ new Date()).toISOString()}
23137
+
23138
+ ${args.content}
23139
+ `;
23140
+ await fs13.appendFile(file2, block, "utf8");
23141
+ } else {
23142
+ await fs13.writeFile(file2, args.content, "utf8");
23143
+ }
23144
+ const st = await fs13.stat(file2);
23145
+ await appendTimeline(ctx.cwd, { kind: "hypothesis_update", bytes: st.size, append: !!args.append });
23146
+ return typedOk({ path: file2, bytes: st.size });
23147
+ } catch (err) {
23148
+ return typedErr(err instanceof Error ? err.message : String(err));
23149
+ }
23150
+ }
23151
+ };
23152
+ WorldCheckSchema = external_exports.object({
23153
+ id: external_exports.string().min(1),
23154
+ command: external_exports.string().min(1),
23155
+ expectExit: external_exports.number().int().optional(),
23156
+ expectStdoutIncludes: external_exports.string().optional(),
23157
+ timeoutMs: external_exports.number().int().positive().max(6e5).optional()
23158
+ });
23159
+ SetChecksSchema = external_exports.object({
23160
+ checks: external_exports.array(WorldCheckSchema).min(1).describe("List of certifiable shell checks (typecheck, unit tests, smoke scripts).")
23161
+ });
23162
+ setWorldChecksTool = {
23163
+ name: "set_world_checks",
23164
+ description: "Replace .zelari/world/checks.json with certifiable checks used by run_backtest. Each check is a shell command + expected exit code (default 0). Prefer fast, deterministic commands (npm run typecheck, npx vitest run path/to/file).",
23165
+ permissions: ["write"],
23166
+ timeoutMs: 1e4,
23167
+ inputSchema: SetChecksSchema,
23168
+ execute: async (args, ctx) => {
23169
+ try {
23170
+ const dir = await ensureWorldDir(ctx.cwd);
23171
+ const file2 = path23.join(dir, CHECKS_FILE);
23172
+ const body = { checks: args.checks };
23173
+ await fs13.writeFile(file2, JSON.stringify(body, null, 2) + "\n", "utf8");
23174
+ await appendTimeline(ctx.cwd, { kind: "checks_set", count: args.checks.length });
23175
+ return typedOk({ path: file2, count: args.checks.length });
23176
+ } catch (err) {
23177
+ return typedErr(err instanceof Error ? err.message : String(err));
23178
+ }
23179
+ }
23180
+ };
23181
+ RunBacktestSchema = external_exports.object({
23182
+ /** Reserved for future selective runs. */
23183
+ dryRun: external_exports.boolean().optional()
23184
+ });
23185
+ runBacktestTool = {
23186
+ name: "run_backtest",
23187
+ description: "Schema-style certify: run every check in .zelari/world/checks.json and report exact pass/fail (exit code + optional stdout substring). Appends a timeline entry. If total=0, call set_world_checks first. NEVER claim the task is done when ok=false. On mismatch, revise hypothesis (update_world_hypothesis) and the implementation, then re-run.",
23188
+ permissions: ["execute"],
23189
+ timeoutMs: 6e5,
23190
+ inputSchema: RunBacktestSchema,
23191
+ execute: async (args, ctx) => {
23192
+ try {
23193
+ if (args.dryRun) {
23194
+ const checks = await readChecks(ctx.cwd);
23195
+ return typedOk({
23196
+ ok: false,
23197
+ passed: 0,
23198
+ failed: 0,
23199
+ total: checks.length,
23200
+ results: checks.map((c) => ({
23201
+ id: c.id,
23202
+ command: c.command,
23203
+ ok: false,
23204
+ exitCode: -1,
23205
+ expectExit: c.expectExit ?? 0,
23206
+ durationMs: 0,
23207
+ stdoutPreview: "(dryRun)",
23208
+ mismatch: "dryRun"
23209
+ })),
23210
+ hypothesisPath: path23.join(worldDir(ctx.cwd), HYPOTHESIS_FILE),
23211
+ checksPath: path23.join(worldDir(ctx.cwd), CHECKS_FILE)
23212
+ });
23213
+ }
23214
+ const result = await runBacktest(ctx.cwd, ctx.signal);
23215
+ return typedOk(result);
23216
+ } catch (err) {
23217
+ return typedErr(err instanceof Error ? err.message : String(err));
23218
+ }
23219
+ }
23220
+ };
23221
+ RecordObsSchema = external_exports.object({
23222
+ kind: external_exports.string().min(1).describe("Observation kind, e.g. surprise, probe, prediction_mismatch."),
23223
+ summary: external_exports.string().min(1),
23224
+ data: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
23225
+ });
23226
+ recordWorldObservationTool = {
23227
+ name: "record_world_observation",
23228
+ description: "Append one ground-truth observation to .zelari/world/timeline.jsonl (Schema timeline). Use after a surprising tool result (prediction mismatch, unexpected exit code, wrong path) so later deliberation cannot rewrite history.",
23229
+ permissions: ["write"],
23230
+ timeoutMs: 5e3,
23231
+ inputSchema: RecordObsSchema,
23232
+ execute: async (args, ctx) => {
23233
+ try {
23234
+ const dir = await ensureWorldDir(ctx.cwd);
23235
+ const file2 = path23.join(dir, TIMELINE_FILE);
23236
+ await appendTimeline(ctx.cwd, {
23237
+ kind: args.kind,
23238
+ summary: args.summary,
23239
+ ...args.data ? { data: args.data } : {}
23240
+ });
23241
+ return typedOk({ path: file2 });
23242
+ } catch (err) {
23243
+ return typedErr(err instanceof Error ? err.message : String(err));
23244
+ }
23245
+ }
23246
+ };
23247
+ }
23248
+ });
23249
+
22952
23250
  // src/cli/toolRegistry.ts
22953
23251
  var toolRegistry_exports = {};
22954
23252
  __export(toolRegistry_exports, {
@@ -23037,6 +23335,17 @@ function createBuiltinToolRegistry(options = {}) {
23037
23335
  });
23038
23336
  }
23039
23337
  }
23338
+ if (!readOnly) {
23339
+ for (const t of createWorldModelTools()) {
23340
+ const safe = wrapWithAudit(t, audit, sessionId);
23341
+ registry3.register(safe);
23342
+ tools.push({
23343
+ name: t.name,
23344
+ description: t.description,
23345
+ permissions: t.permissions ?? []
23346
+ });
23347
+ }
23348
+ }
23040
23349
  if (!readOnly && options.enableTask !== false) {
23041
23350
  const taskTool = createTaskTool({
23042
23351
  createSubAgentContext: async () => {
@@ -23276,6 +23585,7 @@ var init_toolRegistry = __esm({
23276
23585
  init_tools4();
23277
23586
  init_tools5();
23278
23587
  init_tools6();
23588
+ init_worldModel();
23279
23589
  init_openai_compatible();
23280
23590
  init_skills2();
23281
23591
  HARNESS_BUILTIN_NAMES = /* @__PURE__ */ new Set([
@@ -24069,11 +24379,11 @@ var init_synthesisAudit = __esm({
24069
24379
  import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
24070
24380
  import { join as join3 } from "node:path";
24071
24381
  function loadNfrSpec(zelariRoot) {
24072
- const path36 = join3(zelariRoot, "nfr-spec.json");
24073
- if (!existsSync11(path36))
24382
+ const path37 = join3(zelariRoot, "nfr-spec.json");
24383
+ if (!existsSync11(path37))
24074
24384
  return null;
24075
24385
  try {
24076
- const raw = JSON.parse(readFileSync9(path36, "utf8"));
24386
+ const raw = JSON.parse(readFileSync9(path37, "utf8"));
24077
24387
  if (raw.version !== 1 || !Array.isArray(raw.targets))
24078
24388
  return null;
24079
24389
  return raw;
@@ -26321,9 +26631,9 @@ var init_types2 = __esm({
26321
26631
  import { readFileSync as readFileSync14 } from "node:fs";
26322
26632
  import { join as join9 } from "node:path";
26323
26633
  function readLessonsDeduped(zelariRoot) {
26324
- const path36 = join9(zelariRoot, LESSONS_FILE);
26634
+ const path37 = join9(zelariRoot, LESSONS_FILE);
26325
26635
  try {
26326
- const raw = readFileSync14(path36, "utf8");
26636
+ const raw = readFileSync14(path37, "utf8");
26327
26637
  const byId = /* @__PURE__ */ new Map();
26328
26638
  for (const line of raw.split(/\r?\n/)) {
26329
26639
  if (!line.trim())
@@ -26424,8 +26734,8 @@ function keywordsFrom(check2, signature) {
26424
26734
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26425
26735
  }
26426
26736
  function writeLesson(zelariRoot, lesson) {
26427
- const path36 = join10(zelariRoot, LESSONS_FILE);
26428
- appendFileSync2(path36, `${JSON.stringify(lesson)}
26737
+ const path37 = join10(zelariRoot, LESSONS_FILE);
26738
+ appendFileSync2(path37, `${JSON.stringify(lesson)}
26429
26739
  `, "utf8");
26430
26740
  }
26431
26741
  function findSimilar(lessons, signature) {
@@ -27933,28 +28243,28 @@ var init_storage = __esm({
27933
28243
  VALID_SCALARS = /^(true|false|null|~)$/i;
27934
28244
  Storage = class {
27935
28245
  /** Read a Markdown file with frontmatter. Throws if not found. */
27936
- read(path36) {
27937
- if (!existsSync16(path36)) {
27938
- throw new Error(`File not found: ${path36}`);
28246
+ read(path37) {
28247
+ if (!existsSync16(path37)) {
28248
+ throw new Error(`File not found: ${path37}`);
27939
28249
  }
27940
- const md = readFileSync16(path36, "utf8");
28250
+ const md = readFileSync16(path37, "utf8");
27941
28251
  return parseFrontmatter(md);
27942
28252
  }
27943
28253
  /** Read a Markdown file; returns null if not found. */
27944
- readIfExists(path36) {
27945
- if (!existsSync16(path36)) return null;
27946
- return this.read(path36);
28254
+ readIfExists(path37) {
28255
+ if (!existsSync16(path37)) return null;
28256
+ return this.read(path37);
27947
28257
  }
27948
28258
  /**
27949
28259
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
27950
28260
  * The meta object is serialized as YAML frontmatter; body as Markdown.
27951
28261
  */
27952
- write(path36, meta3, body) {
27953
- mkdirSync8(dirname3(path36), { recursive: true });
27954
- const tmp = path36 + ".tmp-" + process.pid;
28262
+ write(path37, meta3, body) {
28263
+ mkdirSync8(dirname3(path37), { recursive: true });
28264
+ const tmp = path37 + ".tmp-" + process.pid;
27955
28265
  const md = serializeFrontmatter(meta3, body);
27956
28266
  writeFileSync12(tmp, md, "utf8");
27957
- renameSync2(tmp, path36);
28267
+ renameSync2(tmp, path37);
27958
28268
  }
27959
28269
  /** List all .md files in a directory (non-recursive). */
27960
28270
  listMarkdown(dir) {
@@ -28032,8 +28342,8 @@ function readPlan(ctx) {
28032
28342
  } catch {
28033
28343
  }
28034
28344
  }
28035
- const path36 = workspaceFile(ctx.rootDir, "plan");
28036
- const doc = ctx.storage.readIfExists(path36);
28345
+ const path37 = workspaceFile(ctx.rootDir, "plan");
28346
+ const doc = ctx.storage.readIfExists(path37);
28037
28347
  if (!doc) return { phases: [], tasks: [], milestones: [] };
28038
28348
  const meta3 = doc.meta;
28039
28349
  return {
@@ -28198,7 +28508,7 @@ function addMilestoneRecord(ctx, summary, input) {
28198
28508
  dueDate: input.dueDate,
28199
28509
  targetVersion: version2
28200
28510
  });
28201
- const path36 = join15(ctx.rootDir, "milestones", `${id}.md`);
28511
+ const path37 = join15(ctx.rootDir, "milestones", `${id}.md`);
28202
28512
  const meta3 = {
28203
28513
  kind: "milestone",
28204
28514
  id,
@@ -28215,7 +28525,7 @@ function addMilestoneRecord(ctx, summary, input) {
28215
28525
  `Target version: ${version2}`,
28216
28526
  ""
28217
28527
  ].join("\n");
28218
- ctx.storage.write(path36, meta3, body);
28528
+ ctx.storage.write(path37, meta3, body);
28219
28529
  return { id, created: true };
28220
28530
  }
28221
28531
  function readPlanSummary(ctx) {
@@ -28419,7 +28729,7 @@ function addIdeaStub(ctx) {
28419
28729
  const tags = args["tags"] ?? [];
28420
28730
  const category = args["category"] ?? "General";
28421
28731
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
28422
- const path36 = workspaceArtifact(ctx.rootDir, "decisions", id);
28732
+ const path37 = workspaceArtifact(ctx.rootDir, "decisions", id);
28423
28733
  const meta3 = {
28424
28734
  kind: "adr",
28425
28735
  status: "proposed",
@@ -28445,7 +28755,7 @@ function addIdeaStub(ctx) {
28445
28755
  ...consequences.map((c) => `- ${c}`),
28446
28756
  ""
28447
28757
  ].join("\n");
28448
- ctx.storage.write(path36, meta3, body);
28758
+ ctx.storage.write(path37, meta3, body);
28449
28759
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
28450
28760
  });
28451
28761
  }
@@ -28527,14 +28837,14 @@ function createDocumentStub(ctx) {
28527
28837
  ctx.storage.write(risksPath, riskMeta, content);
28528
28838
  return `Document "${title}" created at risks.md (workspace root).`;
28529
28839
  }
28530
- const path36 = workspaceArtifact(ctx.rootDir, "docs", slug);
28840
+ const path37 = workspaceArtifact(ctx.rootDir, "docs", slug);
28531
28841
  const meta3 = {
28532
28842
  kind: "doc",
28533
28843
  id: slug,
28534
28844
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
28535
28845
  tags
28536
28846
  };
28537
- ctx.storage.write(path36, meta3, content);
28847
+ ctx.storage.write(path37, meta3, content);
28538
28848
  return `Document "${title}" created at docs/${slug}.md.`;
28539
28849
  });
28540
28850
  }
@@ -28751,17 +29061,17 @@ __export(updater_exports, {
28751
29061
  resolveBundledNpmCli: () => resolveBundledNpmCli
28752
29062
  });
28753
29063
  import { createRequire as createRequire2 } from "node:module";
28754
- import { spawn as spawn5 } from "node:child_process";
29064
+ import { spawn as spawn6 } from "node:child_process";
28755
29065
  import { existsSync as existsSync18 } from "node:fs";
28756
- import path23 from "node:path";
29066
+ import path24 from "node:path";
28757
29067
  import { fileURLToPath } from "node:url";
28758
29068
  function resolveBundledNpmCli(execPath = process.execPath) {
28759
- const dir = path23.dirname(execPath);
29069
+ const dir = path24.dirname(execPath);
28760
29070
  const candidates = [
28761
29071
  // Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
28762
- path23.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
29072
+ path24.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
28763
29073
  // POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
28764
- path23.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
29074
+ path24.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
28765
29075
  ];
28766
29076
  for (const candidate of candidates) {
28767
29077
  try {
@@ -28778,7 +29088,7 @@ function looksLikeBrokenShim(exitCode, output) {
28778
29088
  }
28779
29089
  function getCurrentVersion() {
28780
29090
  try {
28781
- const pkgPath = path23.resolve(__dirname2, "..", "..", "package.json");
29091
+ const pkgPath = path24.resolve(__dirname2, "..", "..", "package.json");
28782
29092
  const pkg = require2(pkgPath);
28783
29093
  return pkg.version;
28784
29094
  } catch {
@@ -28838,7 +29148,7 @@ async function checkForUpdate(fetcher = fetch, registryUrl) {
28838
29148
  updateAvailable: cmp < 0
28839
29149
  };
28840
29150
  }
28841
- async function performUpdate(packageName = "zelari-code", executor = spawn5, resolveNpmCli = resolveBundledNpmCli) {
29151
+ async function performUpdate(packageName = "zelari-code", executor = spawn6, resolveNpmCli = resolveBundledNpmCli) {
28842
29152
  const args = ["install", "-g", `${packageName}@latest`];
28843
29153
  const primary = await runNpm(executor, args, "shim");
28844
29154
  if (primary.ok) return primary;
@@ -28890,13 +29200,13 @@ var init_updater = __esm({
28890
29200
  "use strict";
28891
29201
  init_cmdline();
28892
29202
  require2 = createRequire2(import.meta.url);
28893
- __dirname2 = path23.dirname(fileURLToPath(import.meta.url));
29203
+ __dirname2 = path24.dirname(fileURLToPath(import.meta.url));
28894
29204
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
28895
29205
  }
28896
29206
  });
28897
29207
 
28898
29208
  // src/cli/mcp/mcpClient.ts
28899
- import { spawn as spawn6 } from "node:child_process";
29209
+ import { spawn as spawn7 } from "node:child_process";
28900
29210
  var DEFAULT_REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, MCP_PROTOCOL_VERSION, McpClient;
28901
29211
  var init_mcpClient = __esm({
28902
29212
  "src/cli/mcp/mcpClient.ts"() {
@@ -28924,10 +29234,10 @@ var init_mcpClient = __esm({
28924
29234
  env: { ...process.env, ...this.config.env ?? {} },
28925
29235
  windowsHide: true
28926
29236
  };
28927
- const child = process.platform === "win32" ? spawn6(buildCmdLine(this.config.command, this.config.args ?? []), {
29237
+ const child = process.platform === "win32" ? spawn7(buildCmdLine(this.config.command, this.config.args ?? []), {
28928
29238
  ...spawnOpts,
28929
29239
  shell: true
28930
- }) : spawn6(this.config.command, this.config.args ?? [], spawnOpts);
29240
+ }) : spawn7(this.config.command, this.config.args ?? [], spawnOpts);
28931
29241
  this.child = child;
28932
29242
  child.stdout.setEncoding("utf8");
28933
29243
  child.stdout.on("data", (chunk) => this.onStdout(chunk));
@@ -29076,11 +29386,11 @@ import { join as join16 } from "node:path";
29076
29386
  import { homedir as homedir6 } from "node:os";
29077
29387
  function readMcpConfig(projectRoot = process.cwd()) {
29078
29388
  const merged = {};
29079
- const paths = [
29080
- join16(homedir6(), ".zelari-code", "mcp.json"),
29081
- join16(projectRoot, ".zelari", "mcp.json")
29082
- // later = higher precedence
29083
- ];
29389
+ const paths = [];
29390
+ if (process.env["ZELARI_MCP_USER"] !== "0") {
29391
+ paths.push(join16(homedir6(), ".zelari-code", "mcp.json"));
29392
+ }
29393
+ paths.push(join16(projectRoot, ".zelari", "mcp.json"));
29084
29394
  for (const p3 of paths) {
29085
29395
  if (!existsSync19(p3)) continue;
29086
29396
  try {
@@ -29378,10 +29688,10 @@ import { createHash as createHash3 } from "node:crypto";
29378
29688
  import { join as join19 } from "node:path";
29379
29689
  import { readFile as readFile2 } from "node:fs/promises";
29380
29690
  async function readPackageJson2(projectRoot) {
29381
- const path36 = join19(projectRoot, "package.json");
29382
- if (!existsSync22(path36)) return null;
29691
+ const path37 = join19(projectRoot, "package.json");
29692
+ if (!existsSync22(path37)) return null;
29383
29693
  try {
29384
- return JSON.parse(await readFile2(path36, "utf8"));
29694
+ return JSON.parse(await readFile2(path37, "utf8"));
29385
29695
  } catch {
29386
29696
  return null;
29387
29697
  }
@@ -29463,9 +29773,9 @@ async function genBuild(ctx) {
29463
29773
  ].join("\n");
29464
29774
  }
29465
29775
  async function genOpenQuestions(ctx) {
29466
- const path36 = join19(ctx.rootDir, "risks.md");
29467
- if (!existsSync22(path36)) return "_No open questions._";
29468
- const content = readFileSync21(path36, "utf8");
29776
+ const path37 = join19(ctx.rootDir, "risks.md");
29777
+ if (!existsSync22(path37)) return "_No open questions._";
29778
+ const content = readFileSync21(path37, "utf8");
29469
29779
  const lines = content.split("\n");
29470
29780
  const questions = [];
29471
29781
  let currentTitle = "";
@@ -29710,7 +30020,7 @@ var init_completeDesign = __esm({
29710
30020
  });
29711
30021
 
29712
30022
  // src/cli/workspace/projectSmoke.ts
29713
- import { spawn as spawn7 } from "node:child_process";
30023
+ import { spawn as spawn8 } from "node:child_process";
29714
30024
  import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
29715
30025
  import { join as join20 } from "node:path";
29716
30026
  function pickSmokeScript(scripts) {
@@ -29740,12 +30050,12 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
29740
30050
  return { ran: false, reason: "no typecheck/test/build script (skipped)" };
29741
30051
  }
29742
30052
  return await new Promise((resolveRun) => {
29743
- const child = process.platform === "win32" ? spawn7(buildCmdLine("npm.cmd", ["run", script]), {
30053
+ const child = process.platform === "win32" ? spawn8(buildCmdLine("npm.cmd", ["run", script]), {
29744
30054
  cwd: projectRoot,
29745
30055
  stdio: ["ignore", "pipe", "pipe"],
29746
30056
  env: process.env,
29747
30057
  shell: true
29748
- }) : spawn7("npm", ["run", script], {
30058
+ }) : spawn8("npm", ["run", script], {
29749
30059
  cwd: projectRoot,
29750
30060
  stdio: ["ignore", "pipe", "pipe"],
29751
30061
  env: process.env
@@ -29817,7 +30127,7 @@ __export(postCouncilHook_exports, {
29817
30127
  runImplementationVerificationHook: () => runImplementationVerificationHook,
29818
30128
  runPostCouncilHook: () => runPostCouncilHook
29819
30129
  });
29820
- import { spawn as spawn8 } from "node:child_process";
30130
+ import { spawn as spawn9 } from "node:child_process";
29821
30131
  import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
29822
30132
  import { join as join21 } from "node:path";
29823
30133
  async function runCompleteDesignPostProcessor(ctx, options) {
@@ -29866,7 +30176,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
29866
30176
  }
29867
30177
  }
29868
30178
  return await new Promise((resolveRun) => {
29869
- const child = spawn8(process.execPath, [scriptPath], {
30179
+ const child = spawn9(process.execPath, [scriptPath], {
29870
30180
  cwd: ctx.projectRoot,
29871
30181
  stdio: ["ignore", "pipe", "pipe"],
29872
30182
  env: process.env
@@ -30032,8 +30342,8 @@ async function runPostCouncilHook(ctx, options) {
30032
30342
  sources: scope.sources
30033
30343
  } : void 0
30034
30344
  });
30035
- const path36 = writeCouncilCompletion(ctx.rootDir, completion);
30036
- completionHook = { ran: true, path: path36, completion };
30345
+ const path37 = writeCouncilCompletion(ctx.rootDir, completion);
30346
+ completionHook = { ran: true, path: path37, completion };
30037
30347
  } catch (err) {
30038
30348
  completionHook = {
30039
30349
  ran: true,
@@ -30096,13 +30406,13 @@ __export(councilFeedback_exports, {
30096
30406
  FeedbackStore: () => FeedbackStore
30097
30407
  });
30098
30408
  import {
30099
- promises as fs13,
30409
+ promises as fs14,
30100
30410
  existsSync as existsSync26,
30101
30411
  readFileSync as readFileSync24,
30102
30412
  writeFileSync as writeFileSync15,
30103
30413
  mkdirSync as mkdirSync10
30104
30414
  } from "node:fs";
30105
- import path24 from "node:path";
30415
+ import path25 from "node:path";
30106
30416
  import os8 from "node:os";
30107
30417
  var FeedbackStore;
30108
30418
  var init_councilFeedback = __esm({
@@ -30113,7 +30423,7 @@ var init_councilFeedback = __esm({
30113
30423
  now;
30114
30424
  entries = [];
30115
30425
  constructor(options = {}) {
30116
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path24.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
30426
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path25.join(os8.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
30117
30427
  this.now = options.now ?? Date.now;
30118
30428
  this.load();
30119
30429
  }
@@ -30219,7 +30529,7 @@ var init_councilFeedback = __esm({
30219
30529
  }
30220
30530
  }
30221
30531
  save() {
30222
- mkdirSync10(path24.dirname(this.file), { recursive: true });
30532
+ mkdirSync10(path25.dirname(this.file), { recursive: true });
30223
30533
  writeFileSync15(
30224
30534
  this.file,
30225
30535
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -30229,7 +30539,7 @@ var init_councilFeedback = __esm({
30229
30539
  /** Async variant of load for callers that prefer async IO. */
30230
30540
  async loadAsync() {
30231
30541
  try {
30232
- const raw = await fs13.readFile(this.file, "utf-8");
30542
+ const raw = await fs14.readFile(this.file, "utf-8");
30233
30543
  const parsed = JSON.parse(raw);
30234
30544
  if (parsed && Array.isArray(parsed.entries)) {
30235
30545
  this.entries = parsed.entries.filter(
@@ -30253,8 +30563,8 @@ __export(fileBackend_exports, {
30253
30563
  isMemoryEnabled: () => isMemoryEnabled
30254
30564
  });
30255
30565
  import { randomUUID as randomUUID2 } from "node:crypto";
30256
- import { promises as fs14 } from "node:fs";
30257
- import * as path25 from "node:path";
30566
+ import { promises as fs15 } from "node:fs";
30567
+ import * as path26 from "node:path";
30258
30568
  function tokenize(text) {
30259
30569
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
30260
30570
  }
@@ -30292,9 +30602,9 @@ var init_fileBackend = __esm({
30292
30602
  logPath = "";
30293
30603
  memoryDir = "";
30294
30604
  async init(projectRoot) {
30295
- this.memoryDir = path25.join(projectRoot, ".zelari", "memory");
30296
- this.logPath = path25.join(this.memoryDir, "log.jsonl");
30297
- await fs14.mkdir(this.memoryDir, { recursive: true });
30605
+ this.memoryDir = path26.join(projectRoot, ".zelari", "memory");
30606
+ this.logPath = path26.join(this.memoryDir, "log.jsonl");
30607
+ await fs15.mkdir(this.memoryDir, { recursive: true });
30298
30608
  }
30299
30609
  async add(content, metadata = {}, graph) {
30300
30610
  const fact = {
@@ -30304,7 +30614,7 @@ var init_fileBackend = __esm({
30304
30614
  ...graph ? { graph } : {},
30305
30615
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
30306
30616
  };
30307
- await fs14.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
30617
+ await fs15.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
30308
30618
  return fact.id;
30309
30619
  }
30310
30620
  async search(query, options = {}) {
@@ -30332,7 +30642,7 @@ var init_fileBackend = __esm({
30332
30642
  async readAll() {
30333
30643
  let raw;
30334
30644
  try {
30335
- raw = await fs14.readFile(this.logPath, "utf8");
30645
+ raw = await fs15.readFile(this.logPath, "utf8");
30336
30646
  } catch {
30337
30647
  return [];
30338
30648
  }
@@ -30368,7 +30678,7 @@ import { execFile as execFile2 } from "node:child_process";
30368
30678
  import { promisify } from "node:util";
30369
30679
  import { mkdtempSync, rmSync } from "node:fs";
30370
30680
  import { tmpdir } from "node:os";
30371
- import path26 from "node:path";
30681
+ import path27 from "node:path";
30372
30682
  import { randomUUID as randomUUID3 } from "node:crypto";
30373
30683
  async function git(cwd, args, env) {
30374
30684
  const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
@@ -30388,8 +30698,8 @@ async function isGitRepo(cwd) {
30388
30698
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
30389
30699
  }
30390
30700
  async function withTempIndex(fn) {
30391
- const dir = mkdtempSync(path26.join(tmpdir(), "zelari-ckpt-"));
30392
- const indexFile = path26.join(dir, "index");
30701
+ const dir = mkdtempSync(path27.join(tmpdir(), "zelari-ckpt-"));
30702
+ const indexFile = path27.join(dir, "index");
30393
30703
  try {
30394
30704
  return await fn(indexFile);
30395
30705
  } finally {
@@ -30480,7 +30790,7 @@ async function restoreCheckpoint(cwd, id) {
30480
30790
  const deleted = [];
30481
30791
  for (const rel2 of added) {
30482
30792
  try {
30483
- rmSync(path26.join(cwd, rel2), { force: true });
30793
+ rmSync(path27.join(cwd, rel2), { force: true });
30484
30794
  deleted.push(rel2);
30485
30795
  } catch {
30486
30796
  }
@@ -30509,8 +30819,8 @@ __export(zelariMission_exports, {
30509
30819
  runZelariMission: () => runZelariMission
30510
30820
  });
30511
30821
  import { randomUUID as randomUUID4 } from "node:crypto";
30512
- import { promises as fs15 } from "node:fs";
30513
- import * as path27 from "node:path";
30822
+ import { promises as fs16 } from "node:fs";
30823
+ import * as path28 from "node:path";
30514
30824
  function resolveMaxIterations(env = process.env) {
30515
30825
  const raw = env.ZELARI_MISSION_MAX_ITER;
30516
30826
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -30526,10 +30836,10 @@ function isMissionAutoStart(env = process.env) {
30526
30836
  return env.ZELARI_MISSION_AUTO === "1";
30527
30837
  }
30528
30838
  async function writeMissionState(projectRoot, state2) {
30529
- const dir = path27.join(projectRoot, ".zelari");
30530
- await fs15.mkdir(dir, { recursive: true });
30531
- await fs15.writeFile(
30532
- path27.join(dir, "mission-state.json"),
30839
+ const dir = path28.join(projectRoot, ".zelari");
30840
+ await fs16.mkdir(dir, { recursive: true });
30841
+ await fs16.writeFile(
30842
+ path28.join(dir, "mission-state.json"),
30533
30843
  JSON.stringify(state2, null, 2) + "\n",
30534
30844
  "utf8"
30535
30845
  );
@@ -30977,10 +31287,10 @@ var init_prereqChecks = __esm({
30977
31287
 
30978
31288
  // src/cli/plugins/prefs.ts
30979
31289
  import { existsSync as existsSync28, readFileSync as readFileSync25, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11 } from "node:fs";
30980
- import path29 from "node:path";
31290
+ import path30 from "node:path";
30981
31291
  import os9 from "node:os";
30982
31292
  function getPluginPrefsPath() {
30983
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path29.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
31293
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path30.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
30984
31294
  }
30985
31295
  function getPluginPrefs() {
30986
31296
  const file2 = getPluginPrefsPath();
@@ -31001,7 +31311,7 @@ function getPluginPrefs() {
31001
31311
  }
31002
31312
  function writePluginPrefs(prefs) {
31003
31313
  const file2 = getPluginPrefsPath();
31004
- mkdirSync11(path29.dirname(file2), { recursive: true });
31314
+ mkdirSync11(path30.dirname(file2), { recursive: true });
31005
31315
  writeFileSync16(file2, JSON.stringify(prefs, null, 2), {
31006
31316
  encoding: "utf-8",
31007
31317
  mode: 384
@@ -31038,7 +31348,7 @@ __export(registry_exports, {
31038
31348
  isBinaryOnPath: () => isBinaryOnPath
31039
31349
  });
31040
31350
  import { existsSync as existsSync29 } from "node:fs";
31041
- import path30 from "node:path";
31351
+ import path31 from "node:path";
31042
31352
  function detectLocalBin(bin) {
31043
31353
  return (cwd) => {
31044
31354
  try {
@@ -31056,7 +31366,7 @@ function isBinaryOnPath(bin, opts = {}) {
31056
31366
  const platform = opts.platform ?? process.platform;
31057
31367
  const exists = opts.exists ?? existsSync29;
31058
31368
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
31059
- const pathMod = platform === "win32" ? path30.win32 : path30.posix;
31369
+ const pathMod = platform === "win32" ? path31.win32 : path31.posix;
31060
31370
  const sep = platform === "win32" ? ";" : ":";
31061
31371
  const dirs = pathEnv.split(sep).filter((d) => d.length > 0);
31062
31372
  const candidates = [bin];
@@ -31208,11 +31518,11 @@ import { execSync as execSync2 } from "node:child_process";
31208
31518
  import { existsSync as existsSync35, readFileSync as readFileSync30, readlinkSync, statSync as statSync6 } from "node:fs";
31209
31519
  import { createRequire as createRequire3 } from "node:module";
31210
31520
  import { fileURLToPath as fileURLToPath2 } from "node:url";
31211
- import path35 from "node:path";
31521
+ import path36 from "node:path";
31212
31522
  function findPackageRoot(start) {
31213
31523
  let dir = start;
31214
31524
  for (let i = 0; i < 6; i += 1) {
31215
- const candidate = path35.join(dir, "package.json");
31525
+ const candidate = path36.join(dir, "package.json");
31216
31526
  if (existsSync35(candidate)) {
31217
31527
  try {
31218
31528
  const pkg = JSON.parse(readFileSync30(candidate, "utf8"));
@@ -31220,11 +31530,11 @@ function findPackageRoot(start) {
31220
31530
  } catch {
31221
31531
  }
31222
31532
  }
31223
- const parent = path35.dirname(dir);
31533
+ const parent = path36.dirname(dir);
31224
31534
  if (parent === dir) break;
31225
31535
  dir = parent;
31226
31536
  }
31227
- return path35.resolve(__dirname3, "..", "..", "..");
31537
+ return path36.resolve(__dirname3, "..", "..", "..");
31228
31538
  }
31229
31539
  function tryExec(cmd) {
31230
31540
  try {
@@ -31238,7 +31548,7 @@ function tryExec(cmd) {
31238
31548
  }
31239
31549
  function readPackageJson3() {
31240
31550
  try {
31241
- const pkgPath = path35.join(packageRoot, "package.json");
31551
+ const pkgPath = path36.join(packageRoot, "package.json");
31242
31552
  return JSON.parse(readFileSync30(pkgPath, "utf8"));
31243
31553
  } catch {
31244
31554
  return null;
@@ -31254,7 +31564,7 @@ function checkShim(pkgName) {
31254
31564
  }
31255
31565
  const isWin = process.platform === "win32";
31256
31566
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
31257
- const shimPath = path35.join(prefix, shimName);
31567
+ const shimPath = path36.join(prefix, shimName);
31258
31568
  if (!existsSync35(shimPath)) {
31259
31569
  return FAIL(
31260
31570
  `shim not found at ${shimPath}
@@ -31282,8 +31592,8 @@ function checkShim(pkgName) {
31282
31592
  fix: npm install -g ${pkgName}@latest --force`
31283
31593
  );
31284
31594
  }
31285
- const resolved = path35.resolve(path35.dirname(shimPath), target);
31286
- const expected = path35.join(
31595
+ const resolved = path36.resolve(path36.dirname(shimPath), target);
31596
+ const expected = path36.join(
31287
31597
  prefix,
31288
31598
  "node_modules",
31289
31599
  pkgName,
@@ -31322,7 +31632,7 @@ function checkNode(pkg) {
31322
31632
  return OK(`node ${raw}`);
31323
31633
  }
31324
31634
  function checkBundle() {
31325
- const bundle = path35.join(packageRoot, "dist", "cli", "main.bundled.js");
31635
+ const bundle = path36.join(packageRoot, "dist", "cli", "main.bundled.js");
31326
31636
  if (!existsSync35(bundle)) {
31327
31637
  return FAIL(
31328
31638
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -31343,7 +31653,7 @@ function checkRuntimeDeps() {
31343
31653
  const missing = [];
31344
31654
  for (const dep of required2) {
31345
31655
  try {
31346
- const localReq = createRequire3(path35.join(packageRoot, "package.json"));
31656
+ const localReq = createRequire3(path36.join(packageRoot, "package.json"));
31347
31657
  localReq.resolve(dep);
31348
31658
  } catch {
31349
31659
  missing.push(dep);
@@ -31464,7 +31774,7 @@ var init_doctor = __esm({
31464
31774
  "use strict";
31465
31775
  init_prereqChecks();
31466
31776
  require3 = createRequire3(import.meta.url);
31467
- __dirname3 = path35.dirname(fileURLToPath2(import.meta.url));
31777
+ __dirname3 = path36.dirname(fileURLToPath2(import.meta.url));
31468
31778
  packageRoot = findPackageRoot(__dirname3);
31469
31779
  OK = (message) => ({
31470
31780
  ok: true,
@@ -34512,6 +34822,91 @@ registerCodingSkill(writeUnitTests);
34512
34822
  registerCodingSkill(writeIntegrationTests);
34513
34823
  registerCodingSkill(regressionTest);
34514
34824
 
34825
+ // packages/core/dist/agents/skills/builtin/schemaLoop.js
34826
+ init_skills();
34827
+ var schemaLoop = {
34828
+ id: "schema-loop",
34829
+ version: "1.0.0",
34830
+ name: "Schema Loop (certify before done)",
34831
+ description: "Physicist-style coding harness: keep an explicit hypothesis, define certifiable checks, run_backtest before claiming done, and treat prediction mismatches as plan-voiding surprises. When \u22652 hypotheses remain, run one discriminating experiment first.",
34832
+ category: "debug",
34833
+ requiredRoles: [],
34834
+ requiredTools: [
34835
+ "update_world_hypothesis",
34836
+ "set_world_checks",
34837
+ "run_backtest",
34838
+ "record_world_observation",
34839
+ "bash",
34840
+ "read_file"
34841
+ ],
34842
+ estimatedCost: "medium",
34843
+ enabledByDefault: true,
34844
+ builtin: true,
34845
+ triggers: [
34846
+ "Non-trivial bug or multi-step implementation",
34847
+ "You are about to claim the task is done",
34848
+ "Tests or typecheck failed after edits",
34849
+ "Two or more competing root-cause hypotheses",
34850
+ "Surprising tool result (wrong exit code, missing path, shell mismatch)"
34851
+ ],
34852
+ antiPatterns: [
34853
+ "Trivial one-line typo fix with no uncertainty",
34854
+ "Pure documentation rewrite with no behavior change",
34855
+ "User explicitly asked for a draft-only answer without running tools"
34856
+ ],
34857
+ requires: [],
34858
+ relatedSkills: ["debug-with-rag", "root-cause-five-whys", "reproduce-bug"],
34859
+ tags: ["schema", "backtest", "hypothesis", "verify", "world-model", "debug"],
34860
+ examples: [
34861
+ {
34862
+ input: "Fix the flaky headless test timeout",
34863
+ output: {
34864
+ hypothesis: "Test hangs because registerMcpTools spawns real MCP servers; default 5s timeout is too low.",
34865
+ checks: [
34866
+ { id: "unit", command: "npx vitest run tests/unit/cli-useChatTurn.test.ts" }
34867
+ ],
34868
+ next: "set_world_checks \u2192 implement isolation \u2192 run_backtest \u2192 only then claim done"
34869
+ }
34870
+ }
34871
+ ],
34872
+ outputSchema: "{ hypothesis: string; checks: Array<{ id: string; command: string }>; backtestOk: boolean; surprises?: string[] }",
34873
+ systemPromptFragment: `You are running the **Schema Loop** adapted for software engineering
34874
+ (inspired by Schema harness / ARC world-models \u2014 but for repos, not grids).
34875
+
34876
+ ## Core idea
34877
+ The latent model of the task is **explicit files under .zelari/world/**, not only chat context:
34878
+ - hypothesis.md \u2014 what you believe (objects = modules, mechanism = bug cause / design)
34879
+ - checks.json \u2014 certifiable shell checks (the "reality" oracle)
34880
+ - timeline.jsonl \u2014 append-only observations (do not rewrite history)
34881
+
34882
+ ## Outer loop
34883
+ 1. **observe** \u2014 read errors, failing tests, tool results
34884
+ 2. **deliberate** \u2014 update_world_hypothesis; set_world_checks
34885
+ 3. **execute** \u2014 edit/run tools to change the repo
34886
+ 4. **record** \u2014 record_world_observation on surprises; run_backtest to certify
34887
+
34888
+ ## Hard rules (reality outranks the model)
34889
+ 1. **Never claim done** while the latest run_backtest has ok=false or total=0 without checks.
34890
+ 2. After any batch of write/edit, prefer run_backtest (or set checks then backtest) before asserting success.
34891
+ 3. **Surprise = void plan**: if a command exits unexpectedly, shell rejects POSIX on Windows, path missing, or tests fail differently than predicted \u2014 call record_world_observation (kind=surprise), revise hypothesis, do not keep patching blindly.
34892
+ 4. **Discovery actions**: when \u22652 hypotheses remain consistent with evidence, design ONE discriminating experiment (read/grep/test) whose outcomes diverge; do not spray random edits.
34893
+ 5. Prefer fast checks (single-file vitest, typecheck) over full suite until the model is stable.
34894
+
34895
+ ## Predict-then-act (soft)
34896
+ Before a risky bash/edit, state a short prediction in hypothesis or observation
34897
+ (expect exit 0, files touched). After the tool, if reality mismatches, log surprise.
34898
+
34899
+ ## Tools
34900
+ - update_world_hypothesis \u2014 write/append theory
34901
+ - set_world_checks \u2014 define certifiable commands
34902
+ - run_backtest \u2014 replay all checks; report exact pass/fail
34903
+ - record_world_observation \u2014 timeline ground truth
34904
+
34905
+ Kill switch for tools: ZELARI_SCHEMA_LOOP=0.
34906
+ ${""}`
34907
+ };
34908
+ registerCodingSkill(schemaLoop);
34909
+
34515
34910
  // src/cli/app.tsx
34516
34911
  init_providerConfig();
34517
34912
  init_harness();
@@ -36914,7 +37309,7 @@ ${formatSkillList(availableSkills)}`
36914
37309
  // src/cli/gitOps.ts
36915
37310
  import { execFile as execFile3 } from "node:child_process";
36916
37311
  import { promisify as promisify2 } from "node:util";
36917
- import path28 from "node:path";
37312
+ import path29 from "node:path";
36918
37313
  var execFileAsync2 = promisify2(execFile3);
36919
37314
  async function git2(cwd, args) {
36920
37315
  try {
@@ -36960,7 +37355,7 @@ async function undoWorkingChanges(opts = {}) {
36960
37355
  };
36961
37356
  }
36962
37357
  function defaultProjectRoot() {
36963
- return path28.resolve(__dirname, "..", "..", "..");
37358
+ return path29.resolve(__dirname, "..", "..", "..");
36964
37359
  }
36965
37360
 
36966
37361
  // src/cli/slashHandlers/git.ts
@@ -37269,8 +37664,8 @@ init_registry2();
37269
37664
  // src/cli/plugins/installer.ts
37270
37665
  init_cmdline();
37271
37666
  init_updater();
37272
- import { spawn as spawn9 } from "node:child_process";
37273
- async function installPlugin(spec, cwd, executor = spawn9) {
37667
+ import { spawn as spawn10 } from "node:child_process";
37668
+ async function installPlugin(spec, cwd, executor = spawn10) {
37274
37669
  const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
37275
37670
  const args = ["install", scopeFlag, spec.npmPackage];
37276
37671
  const primary = await runNpm2(executor, args, cwd, "shim");
@@ -37371,17 +37766,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
37371
37766
  }
37372
37767
 
37373
37768
  // src/cli/slashHandlers/promoteMember.ts
37374
- import { promises as fs16 } from "node:fs";
37375
- import path31 from "node:path";
37769
+ import { promises as fs17 } from "node:fs";
37770
+ import path32 from "node:path";
37376
37771
  import os10 from "node:os";
37377
37772
  async function handlePromoteMember(ctx, memberId) {
37378
37773
  try {
37379
37774
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
37380
37775
  const { skill, markdown } = promoteMember2(memberId);
37381
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path31.join(os10.homedir(), ".tmp", "zelari-code", "skills");
37382
- await fs16.mkdir(skillDir, { recursive: true });
37383
- const filePath = path31.join(skillDir, `${skill.id}.md`);
37384
- await fs16.writeFile(filePath, markdown, "utf8");
37776
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path32.join(os10.homedir(), ".tmp", "zelari-code", "skills");
37777
+ await fs17.mkdir(skillDir, { recursive: true });
37778
+ const filePath = path32.join(skillDir, `${skill.id}.md`);
37779
+ await fs17.writeFile(filePath, markdown, "utf8");
37385
37780
  appendSystem(
37386
37781
  ctx.setMessages,
37387
37782
  `[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
@@ -37397,25 +37792,25 @@ async function handlePromoteMember(ctx, memberId) {
37397
37792
  }
37398
37793
 
37399
37794
  // src/cli/branchManager.ts
37400
- import { promises as fs17, existsSync as existsSync30, readFileSync as readFileSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync12, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
37401
- import path32 from "node:path";
37795
+ import { promises as fs18, existsSync as existsSync30, readFileSync as readFileSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync12, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
37796
+ import path33 from "node:path";
37402
37797
  import os11 from "node:os";
37403
37798
  var META_FILENAME = "meta.json";
37404
37799
  var SESSIONS_SUBDIR = "sessions";
37405
37800
  function getBranchesBaseDir() {
37406
- return process.env.ANATHEMA_BRANCHES_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "branches");
37801
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path33.join(os11.homedir(), ".tmp", "zelari-code", "branches");
37407
37802
  }
37408
37803
  function getSessionsBaseDir() {
37409
- return process.env.ANATHEMA_SESSIONS_DIR ?? path32.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
37804
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path33.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
37410
37805
  }
37411
37806
  function branchPathFor(name, baseDir) {
37412
- return path32.join(baseDir, name);
37807
+ return path33.join(baseDir, name);
37413
37808
  }
37414
37809
  function metaPathFor(name, baseDir) {
37415
- return path32.join(baseDir, name, META_FILENAME);
37810
+ return path33.join(baseDir, name, META_FILENAME);
37416
37811
  }
37417
37812
  function sessionsPathFor(name, baseDir) {
37418
- return path32.join(baseDir, name, SESSIONS_SUBDIR);
37813
+ return path33.join(baseDir, name, SESSIONS_SUBDIR);
37419
37814
  }
37420
37815
  function readBranchMeta(name, baseDir) {
37421
37816
  const metaPath = metaPathFor(name, baseDir);
@@ -37440,13 +37835,13 @@ function readBranchMeta(name, baseDir) {
37440
37835
  }
37441
37836
  function writeBranchMeta(name, baseDir, meta3) {
37442
37837
  const metaPath = metaPathFor(name, baseDir);
37443
- mkdirSync12(path32.dirname(metaPath), { recursive: true });
37838
+ mkdirSync12(path33.dirname(metaPath), { recursive: true });
37444
37839
  writeFileSync17(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
37445
37840
  }
37446
37841
  async function countSessions(name, baseDir) {
37447
37842
  const sessionsPath = sessionsPathFor(name, baseDir);
37448
37843
  try {
37449
- const entries = await fs17.readdir(sessionsPath);
37844
+ const entries = await fs18.readdir(sessionsPath);
37450
37845
  return entries.filter((e) => e.endsWith(".jsonl")).length;
37451
37846
  } catch (err) {
37452
37847
  if (err.code === "ENOENT") return 0;
@@ -37491,15 +37886,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
37491
37886
  if (branchExists(name, baseDir)) {
37492
37887
  throw new BranchAlreadyExistsError(name);
37493
37888
  }
37494
- const sourcePath = path32.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
37889
+ const sourcePath = path33.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
37495
37890
  if (!existsSync30(sourcePath)) {
37496
37891
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
37497
37892
  }
37498
37893
  const branchPath = branchPathFor(name, baseDir);
37499
37894
  const branchSessionsPath = sessionsPathFor(name, baseDir);
37500
37895
  mkdirSync12(branchSessionsPath, { recursive: true });
37501
- const destPath = path32.join(branchSessionsPath, `${fromSessionId}.jsonl`);
37502
- await fs17.copyFile(sourcePath, destPath);
37896
+ const destPath = path33.join(branchSessionsPath, `${fromSessionId}.jsonl`);
37897
+ await fs18.copyFile(sourcePath, destPath);
37503
37898
  const meta3 = {
37504
37899
  name,
37505
37900
  createdAt: Date.now(),
@@ -37517,7 +37912,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
37517
37912
  async function listBranches(baseDir = getBranchesBaseDir()) {
37518
37913
  let entries;
37519
37914
  try {
37520
- entries = await fs17.readdir(baseDir);
37915
+ entries = await fs18.readdir(baseDir);
37521
37916
  } catch (err) {
37522
37917
  if (err.code === "ENOENT") return [];
37523
37918
  throw err;
@@ -37598,26 +37993,26 @@ async function handleBranchCheckout(ctx, branchName) {
37598
37993
  }
37599
37994
 
37600
37995
  // src/cli/slashHandlers/workspace.ts
37601
- import { promises as fs18 } from "node:fs";
37602
- import path33 from "node:path";
37996
+ import { promises as fs19 } from "node:fs";
37997
+ import path34 from "node:path";
37603
37998
  async function handleWorkspaceShow(ctx, what) {
37604
37999
  try {
37605
- const zelari = path33.join(process.cwd(), ".zelari");
38000
+ const zelari = path34.join(process.cwd(), ".zelari");
37606
38001
  let content;
37607
38002
  switch (what) {
37608
38003
  case "plan": {
37609
- const planPath = path33.join(zelari, "plan.md");
38004
+ const planPath = path34.join(zelari, "plan.md");
37610
38005
  try {
37611
- content = await fs18.readFile(planPath, "utf-8");
38006
+ content = await fs19.readFile(planPath, "utf-8");
37612
38007
  } catch {
37613
38008
  content = "(no plan.md yet \u2014 run a council session first)";
37614
38009
  }
37615
38010
  break;
37616
38011
  }
37617
38012
  case "decisions": {
37618
- const decisionsDir = path33.join(zelari, "decisions");
38013
+ const decisionsDir = path34.join(zelari, "decisions");
37619
38014
  try {
37620
- const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
38015
+ const files = (await fs19.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
37621
38016
  if (files.length === 0) {
37622
38017
  content = "(no ADRs yet \u2014 invoke /council to generate some)";
37623
38018
  } else {
@@ -37625,7 +38020,7 @@ async function handleWorkspaceShow(ctx, what) {
37625
38020
  `];
37626
38021
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
37627
38022
  for (const f of files) {
37628
- const raw = await fs18.readFile(path33.join(decisionsDir, f), "utf-8");
38023
+ const raw = await fs19.readFile(path34.join(decisionsDir, f), "utf-8");
37629
38024
  const { meta: meta3, body } = parseFrontmatter2(raw);
37630
38025
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
37631
38026
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -37638,27 +38033,27 @@ async function handleWorkspaceShow(ctx, what) {
37638
38033
  break;
37639
38034
  }
37640
38035
  case "risks": {
37641
- const risksPath = path33.join(zelari, "risks.md");
38036
+ const risksPath = path34.join(zelari, "risks.md");
37642
38037
  try {
37643
- content = await fs18.readFile(risksPath, "utf-8");
38038
+ content = await fs19.readFile(risksPath, "utf-8");
37644
38039
  } catch {
37645
38040
  content = "(no risks.md yet)";
37646
38041
  }
37647
38042
  break;
37648
38043
  }
37649
38044
  case "agents": {
37650
- const agentsPath = path33.join(process.cwd(), "AGENTS.MD");
38045
+ const agentsPath = path34.join(process.cwd(), "AGENTS.MD");
37651
38046
  try {
37652
- content = await fs18.readFile(agentsPath, "utf-8");
38047
+ content = await fs19.readFile(agentsPath, "utf-8");
37653
38048
  } catch {
37654
38049
  content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
37655
38050
  }
37656
38051
  break;
37657
38052
  }
37658
38053
  case "docs": {
37659
- const docsDir = path33.join(zelari, "docs");
38054
+ const docsDir = path34.join(zelari, "docs");
37660
38055
  try {
37661
- const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
38056
+ const files = (await fs19.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
37662
38057
  content = files.length ? `# Docs (${files.length})
37663
38058
 
37664
38059
  ` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
@@ -37698,8 +38093,8 @@ async function handleWorkspaceReset(ctx, force) {
37698
38093
  return;
37699
38094
  }
37700
38095
  try {
37701
- const target = path33.join(process.cwd(), ".zelari");
37702
- await fs18.rm(target, { recursive: true, force: true });
38096
+ const target = path34.join(process.cwd(), ".zelari");
38097
+ await fs19.rm(target, { recursive: true, force: true });
37703
38098
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
37704
38099
  } catch (err) {
37705
38100
  appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
@@ -38057,16 +38452,16 @@ function handleModelsRefresh(ctx) {
38057
38452
  }
38058
38453
 
38059
38454
  // src/cli/slashHandlers/skills.ts
38060
- import path34 from "node:path";
38455
+ import path35 from "node:path";
38061
38456
  import os12 from "node:os";
38062
38457
 
38063
38458
  // src/cli/skillHistory.ts
38064
- import { promises as fs19, existsSync as existsSync31, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "node:fs";
38459
+ import { promises as fs20, existsSync as existsSync31, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync13 } from "node:fs";
38065
38460
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
38066
38461
  async function readSkillHistory(file2) {
38067
38462
  let raw = "";
38068
38463
  try {
38069
- raw = await fs19.readFile(file2, "utf-8");
38464
+ raw = await fs20.readFile(file2, "utf-8");
38070
38465
  } catch {
38071
38466
  return [];
38072
38467
  }
@@ -38159,7 +38554,7 @@ async function applySteerInterrupt(options) {
38159
38554
 
38160
38555
  // src/cli/slashHandlers/skills.ts
38161
38556
  async function handleSkillStats(ctx, skillId) {
38162
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
38557
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path35.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
38163
38558
  try {
38164
38559
  const records = await readSkillHistory(historyFile);
38165
38560
  const stats = getSkillStats(records, skillId);
@@ -38175,7 +38570,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
38175
38570
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
38176
38571
  return;
38177
38572
  }
38178
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path34.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
38573
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path35.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
38179
38574
  try {
38180
38575
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
38181
38576
  appendSystem(ctx.setMessages, formatted);
@@ -40757,10 +41152,10 @@ function getUserMcpPath() {
40757
41152
  function getProjectMcpPath(projectRoot) {
40758
41153
  return join26(projectRoot, ".zelari", "mcp.json");
40759
41154
  }
40760
- function readFile3(path36) {
40761
- if (!existsSync34(path36)) return {};
41155
+ function readFile3(path37) {
41156
+ if (!existsSync34(path37)) return {};
40762
41157
  try {
40763
- const parsed = JSON.parse(readFileSync29(path36, "utf8"));
41158
+ const parsed = JSON.parse(readFileSync29(path37, "utf8"));
40764
41159
  const out = {};
40765
41160
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
40766
41161
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -40776,10 +41171,10 @@ function readFile3(path36) {
40776
41171
  return {};
40777
41172
  }
40778
41173
  }
40779
- function writeFile(path36, servers) {
40780
- mkdirSync14(dirname6(path36), { recursive: true });
41174
+ function writeFile(path37, servers) {
41175
+ mkdirSync14(dirname6(path37), { recursive: true });
40781
41176
  const body = { mcpServers: servers };
40782
- writeFileSync18(path36, `${JSON.stringify(body, null, 2)}
41177
+ writeFileSync18(path37, `${JSON.stringify(body, null, 2)}
40783
41178
  `, "utf8");
40784
41179
  }
40785
41180
  function listMcpServers(projectRoot) {
@@ -40812,9 +41207,9 @@ function upsertMcpServer(opts) {
40812
41207
  if (!opts.config.command?.trim()) {
40813
41208
  return { ok: false, error: "command is required" };
40814
41209
  }
40815
- let path36;
41210
+ let path37;
40816
41211
  if (opts.scope === "user") {
40817
- path36 = getUserMcpPath();
41212
+ path37 = getUserMcpPath();
40818
41213
  } else {
40819
41214
  const root = opts.projectRoot?.trim();
40820
41215
  if (!root) {
@@ -40823,30 +41218,30 @@ function upsertMcpServer(opts) {
40823
41218
  error: "projectRoot required for project scope (Open Folder first)"
40824
41219
  };
40825
41220
  }
40826
- path36 = getProjectMcpPath(root);
41221
+ path37 = getProjectMcpPath(root);
40827
41222
  }
40828
- const current = readFile3(path36);
41223
+ const current = readFile3(path37);
40829
41224
  current[name] = {
40830
41225
  command: opts.config.command.trim(),
40831
41226
  args: opts.config.args,
40832
41227
  env: opts.config.env,
40833
41228
  enabled: opts.config.enabled !== false
40834
41229
  };
40835
- writeFile(path36, current);
40836
- return { ok: true, path: path36 };
41230
+ writeFile(path37, current);
41231
+ return { ok: true, path: path37 };
40837
41232
  }
40838
41233
  function removeMcpServer(opts) {
40839
- const path36 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
40840
- if (!path36) {
41234
+ const path37 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
41235
+ if (!path37) {
40841
41236
  return { ok: false, error: "projectRoot required for project scope" };
40842
41237
  }
40843
- const current = readFile3(path36);
41238
+ const current = readFile3(path37);
40844
41239
  if (!(opts.name in current)) {
40845
- return { ok: false, error: `Server "${opts.name}" not found in ${path36}` };
41240
+ return { ok: false, error: `Server "${opts.name}" not found in ${path37}` };
40846
41241
  }
40847
41242
  delete current[opts.name];
40848
- writeFile(path36, current);
40849
- return { ok: true, path: path36 };
41243
+ writeFile(path37, current);
41244
+ return { ok: true, path: path37 };
40850
41245
  }
40851
41246
 
40852
41247
  // src/cli/main.ts