zelari-code 1.43.0 → 1.44.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.
@@ -38784,10 +38784,181 @@ var init_completeDesign = __esm({
38784
38784
  }
38785
38785
  });
38786
38786
 
38787
+ // src/cli/workspace/planDriftCheck.ts
38788
+ import { existsSync as existsSync34, readFileSync as readFileSync30, readdirSync as readdirSync9, statSync as statSync5, writeFileSync as writeFileSync20 } from "node:fs";
38789
+ import { join as join29 } from "node:path";
38790
+ function findCanonicalDoc(rootDir) {
38791
+ const docsDir = join29(rootDir, "docs");
38792
+ if (!existsSync34(docsDir)) return null;
38793
+ const candidates = readdirSync9(docsDir).filter((f) => /^plan-canonical.*\.md$/i.test(f)).map((f) => ({ f, mtime: statSync5(join29(docsDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
38794
+ return candidates.length > 0 ? candidates[0].f : null;
38795
+ }
38796
+ function parseCanonicalDoc(text) {
38797
+ const activePhases = /* @__PURE__ */ new Set();
38798
+ const blockedPrefixes = /* @__PURE__ */ new Set();
38799
+ const headingPhase = /^#{2,6}[^\n]*?`([a-z0-9][a-z0-9-]*)`/gm;
38800
+ for (const m of text.matchAll(headingPhase)) activePhases.add(m[1]);
38801
+ const blocked = /`([a-z0-9][a-z0-9-]*)-\*`/g;
38802
+ for (const m of text.matchAll(blocked)) blockedPrefixes.add(`${m[1]}-`);
38803
+ return { activePhases, blockedPrefixes };
38804
+ }
38805
+ function normalizeTitle(value) {
38806
+ if (typeof value !== "string") return "";
38807
+ return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
38808
+ }
38809
+ function versionKey(value) {
38810
+ if (typeof value !== "string") return "";
38811
+ const m = value.toLowerCase().match(/v?\d+(?:\.\d+)*/);
38812
+ return m ? m[0].replace(/^v/, "") : value.trim().toLowerCase();
38813
+ }
38814
+ function firstString2(v) {
38815
+ return typeof v === "string" && v.trim().length > 0 ? v : null;
38816
+ }
38817
+ function readFileSyncSafe(path53) {
38818
+ try {
38819
+ return readFileSync30(path53, "utf8");
38820
+ } catch {
38821
+ return null;
38822
+ }
38823
+ }
38824
+ async function runPlanDriftCheck(rootDir) {
38825
+ if (process.env["ZELARI_DRIFT_CHECK"] === "0") {
38826
+ return { ran: false, reason: "ZELARI_DRIFT_CHECK=0 (disabled)" };
38827
+ }
38828
+ const planPath = join29(rootDir, "plan.json");
38829
+ if (!existsSync34(planPath)) {
38830
+ return { ran: false, reason: ".zelari/plan.json missing (not design-phase)" };
38831
+ }
38832
+ let plan;
38833
+ try {
38834
+ plan = JSON.parse(readFileSync30(planPath, "utf8"));
38835
+ } catch {
38836
+ return { ran: false, reason: ".zelari/plan.json corrupt" };
38837
+ }
38838
+ const findings = [];
38839
+ const phases = Array.isArray(plan.phases) ? plan.phases : [];
38840
+ const tasks = Array.isArray(plan.tasks) ? plan.tasks : [];
38841
+ const milestones = Array.isArray(plan.milestones) ? plan.milestones : [];
38842
+ const phaseIds = new Set(phases.map((p3) => typeof p3.id === "string" ? p3.id : "").filter(Boolean));
38843
+ const canonicalName = findCanonicalDoc(rootDir);
38844
+ const canonicalText = canonicalName ? readFileSyncSafe(join29(rootDir, "docs", canonicalName)) : null;
38845
+ if (canonicalText !== null) {
38846
+ const { activePhases, blockedPrefixes } = parseCanonicalDoc(canonicalText);
38847
+ for (const id of activePhases) {
38848
+ if (!phaseIds.has(id)) {
38849
+ findings.push({
38850
+ code: "CANONICAL_PHASE_MISSING",
38851
+ severity: "error",
38852
+ message: `canonical phase \`${id}\` (${canonicalName}) is missing from plan.json`
38853
+ });
38854
+ }
38855
+ }
38856
+ for (const id of phaseIds) {
38857
+ const hit = [...blockedPrefixes].find((p3) => id.startsWith(p3));
38858
+ if (hit) {
38859
+ findings.push({
38860
+ code: "PHASE_IN_CANONICAL_BLOCKLIST",
38861
+ severity: "error",
38862
+ message: `phase \`${id}\` matches canonical duplicate/descope prefix \`${hit}*\``
38863
+ });
38864
+ }
38865
+ }
38866
+ for (const t of tasks) {
38867
+ const id = typeof t.id === "string" ? t.id : "";
38868
+ const hit = id ? [...blockedPrefixes].find((p3) => id.startsWith(p3)) : void 0;
38869
+ if (hit) {
38870
+ findings.push({
38871
+ code: "TASK_IN_CANONICAL_BLOCKLIST",
38872
+ severity: "error",
38873
+ message: `task \`${id}\` matches canonical duplicate/descope prefix \`${hit}*\``
38874
+ });
38875
+ }
38876
+ }
38877
+ }
38878
+ const byVersion = /* @__PURE__ */ new Map();
38879
+ for (const m of milestones) {
38880
+ const key = versionKey(m.targetVersion);
38881
+ if (!key) continue;
38882
+ const id = typeof m.id === "string" ? m.id : "(no id)";
38883
+ byVersion.set(key, [...byVersion.get(key) ?? [], id]);
38884
+ }
38885
+ for (const [version2, ids] of byVersion) {
38886
+ if (ids.length > 1) {
38887
+ findings.push({
38888
+ code: "DUPLICATE_MILESTONE",
38889
+ severity: "error",
38890
+ message: `${ids.length} milestones target ${version2}: ${ids.join(", ")} \u2014 keep exactly one canonical`
38891
+ });
38892
+ }
38893
+ }
38894
+ const byTitle = /* @__PURE__ */ new Map();
38895
+ for (const t of tasks) {
38896
+ const title = normalizeTitle(
38897
+ firstString2(t.title) ?? firstString2(t.name) ?? firstString2(t.description)
38898
+ );
38899
+ const id = typeof t.id === "string" ? t.id : "(no id)";
38900
+ if (title) byTitle.set(title, [...byTitle.get(title) ?? [], id]);
38901
+ const phaseId = firstString2(t.phaseId);
38902
+ if (phaseId && !phaseIds.has(phaseId)) {
38903
+ findings.push({
38904
+ code: "TASK_IN_UNKNOWN_PHASE",
38905
+ severity: "warning",
38906
+ message: `task \`${id}\` references unknown phaseId \`${phaseId}\``
38907
+ });
38908
+ }
38909
+ }
38910
+ for (const [title, ids] of byTitle) {
38911
+ if (ids.length > 1) {
38912
+ findings.push({
38913
+ code: "DUPLICATE_TASK_TITLE",
38914
+ severity: "warning",
38915
+ message: `duplicate task title "${title.slice(0, 60)}": ${ids.join(", ")}`
38916
+ });
38917
+ }
38918
+ }
38919
+ const ok = findings.every((f) => f.severity !== "error");
38920
+ const canonicalParsed = canonicalName !== null && canonicalText !== null;
38921
+ let reportPath;
38922
+ try {
38923
+ reportPath = join29(rootDir, "drift-report.json");
38924
+ writeFileSync20(
38925
+ reportPath,
38926
+ JSON.stringify(
38927
+ {
38928
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
38929
+ ok,
38930
+ canonicalDoc: canonicalName ?? null,
38931
+ counts: { phases: phases.length, tasks: tasks.length, milestones: milestones.length },
38932
+ findings
38933
+ },
38934
+ null,
38935
+ 2
38936
+ ) + "\n",
38937
+ "utf8"
38938
+ );
38939
+ } catch {
38940
+ reportPath = void 0;
38941
+ }
38942
+ return {
38943
+ ran: true,
38944
+ ok,
38945
+ findings,
38946
+ ...canonicalParsed ? { canonicalDoc: canonicalName } : {
38947
+ reason: canonicalName ? `canonical doc ${canonicalName} unreadable (structural checks only)` : "no canonical doc (structural checks only)"
38948
+ },
38949
+ ...reportPath ? { reportPath } : {}
38950
+ };
38951
+ }
38952
+ var init_planDriftCheck = __esm({
38953
+ "src/cli/workspace/planDriftCheck.ts"() {
38954
+ "use strict";
38955
+ }
38956
+ });
38957
+
38787
38958
  // src/cli/workspace/projectSmoke.ts
38788
38959
  import { spawn as spawn10 } from "node:child_process";
38789
- import { existsSync as existsSync34, readFileSync as readFileSync30 } from "node:fs";
38790
- import { join as join29 } from "node:path";
38960
+ import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
38961
+ import { join as join30 } from "node:path";
38791
38962
  function pickSmokeScript(scripts) {
38792
38963
  if (!scripts) return null;
38793
38964
  for (const name of SMOKE_SCRIPT_PRIORITY) {
@@ -38799,13 +38970,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS2) {
38799
38970
  if (process.env["ZELARI_SMOKE"] === "0") {
38800
38971
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
38801
38972
  }
38802
- const pkgPath = join29(projectRoot, "package.json");
38803
- if (!existsSync34(pkgPath)) {
38973
+ const pkgPath = join30(projectRoot, "package.json");
38974
+ if (!existsSync35(pkgPath)) {
38804
38975
  return { ran: false, reason: "no package.json (skipped)" };
38805
38976
  }
38806
38977
  let scripts = {};
38807
38978
  try {
38808
- const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
38979
+ const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
38809
38980
  scripts = pkg.scripts ?? {};
38810
38981
  } catch {
38811
38982
  return { ran: false, reason: "package.json unreadable (skipped)" };
@@ -38893,8 +39064,8 @@ __export(postCouncilHook_exports, {
38893
39064
  runPostCouncilHook: () => runPostCouncilHook
38894
39065
  });
38895
39066
  import { spawn as spawn11 } from "node:child_process";
38896
- import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
38897
- import { join as join30 } from "node:path";
39067
+ import { existsSync as existsSync36, readFileSync as readFileSync32 } from "node:fs";
39068
+ import { join as join31 } from "node:path";
38898
39069
  async function runCompleteDesignPostProcessor(ctx, options) {
38899
39070
  if (options?.runMode === "implementation") {
38900
39071
  return {
@@ -38905,9 +39076,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
38905
39076
  if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
38906
39077
  return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
38907
39078
  }
38908
- const planJsonPath2 = join30(ctx.rootDir, "plan.json");
38909
- const scriptPath = join30(ctx.projectRoot, "complete-design.mjs");
38910
- if (!existsSync35(planJsonPath2)) {
39079
+ const planJsonPath2 = join31(ctx.rootDir, "plan.json");
39080
+ const scriptPath = join31(ctx.projectRoot, "complete-design.mjs");
39081
+ if (!existsSync36(planJsonPath2)) {
38911
39082
  return {
38912
39083
  ran: false,
38913
39084
  reason: ".zelari/plan.json missing (not design-phase)"
@@ -38915,7 +39086,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
38915
39086
  }
38916
39087
  let phaseCount = 0;
38917
39088
  try {
38918
- const parsed = JSON.parse(readFileSync31(planJsonPath2, "utf8"));
39089
+ const parsed = JSON.parse(readFileSync32(planJsonPath2, "utf8"));
38919
39090
  phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
38920
39091
  } catch {
38921
39092
  return { ran: false, reason: ".zelari/plan.json corrupt" };
@@ -38923,7 +39094,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
38923
39094
  if (phaseCount === 0) {
38924
39095
  return { ran: false, reason: ".zelari/plan.json has no phases" };
38925
39096
  }
38926
- if (!existsSync35(scriptPath)) {
39097
+ if (!existsSync36(scriptPath)) {
38927
39098
  try {
38928
39099
  const builtin = await runBuiltinCompleteDesign(ctx);
38929
39100
  return {
@@ -39026,6 +39197,7 @@ async function runPostCouncilHook(ctx, options) {
39026
39197
  }
39027
39198
  }
39028
39199
  const completeDesign = await runCompleteDesignPostProcessor(ctx, options);
39200
+ const driftCheck = await runPlanDriftCheck(ctx.rootDir);
39029
39201
  let verification = await runImplementationVerificationHook(ctx, options);
39030
39202
  let autofix = { ran: false };
39031
39203
  if (process.env["ZELARI_VERIFY_AUTOFIX"] !== "0" && verification.ran && verification.ok === false && verification.report) {
@@ -39117,7 +39289,7 @@ async function runPostCouncilHook(ctx, options) {
39117
39289
  }
39118
39290
  }
39119
39291
  return {
39120
- ran: agentsMdResult.ran || completeDesign.ran || verification.ran || lessons.ran || smoke.ran || completionHook.ran,
39292
+ ran: agentsMdResult.ran || completeDesign.ran || driftCheck.ran || verification.ran || lessons.ran || smoke.ran || completionHook.ran,
39121
39293
  changed: agentsMdResult.changed,
39122
39294
  sections: agentsMdResult.sections,
39123
39295
  ...agentsMdResult.reason ? { reason: agentsMdResult.reason } : {},
@@ -39126,7 +39298,8 @@ async function runPostCouncilHook(ctx, options) {
39126
39298
  autofix,
39127
39299
  lessons,
39128
39300
  smoke,
39129
- completion: completionHook
39301
+ completion: completionHook,
39302
+ driftCheck
39130
39303
  };
39131
39304
  }
39132
39305
  var init_postCouncilHook = __esm({
@@ -39135,6 +39308,7 @@ var init_postCouncilHook = __esm({
39135
39308
  init_council();
39136
39309
  init_agentsMd();
39137
39310
  init_completeDesign();
39311
+ init_planDriftCheck();
39138
39312
  init_projectSmoke();
39139
39313
  }
39140
39314
  });
@@ -39146,9 +39320,9 @@ __export(councilFeedback_exports, {
39146
39320
  });
39147
39321
  import {
39148
39322
  promises as fs16,
39149
- existsSync as existsSync36,
39150
- readFileSync as readFileSync32,
39151
- writeFileSync as writeFileSync20,
39323
+ existsSync as existsSync37,
39324
+ readFileSync as readFileSync33,
39325
+ writeFileSync as writeFileSync21,
39152
39326
  mkdirSync as mkdirSync17
39153
39327
  } from "node:fs";
39154
39328
  import path32 from "node:path";
@@ -39255,9 +39429,9 @@ var init_councilFeedback = __esm({
39255
39429
  }
39256
39430
  // --- persistence ---------------------------------------------------------
39257
39431
  load() {
39258
- if (!existsSync36(this.file)) return;
39432
+ if (!existsSync37(this.file)) return;
39259
39433
  try {
39260
- const raw = readFileSync32(this.file, "utf-8");
39434
+ const raw = readFileSync33(this.file, "utf-8");
39261
39435
  const parsed = JSON.parse(raw);
39262
39436
  if (parsed && Array.isArray(parsed.entries)) {
39263
39437
  this.entries = parsed.entries.filter(
@@ -39269,7 +39443,7 @@ var init_councilFeedback = __esm({
39269
39443
  }
39270
39444
  save() {
39271
39445
  mkdirSync17(path32.dirname(this.file), { recursive: true });
39272
- writeFileSync20(
39446
+ writeFileSync21(
39273
39447
  this.file,
39274
39448
  JSON.stringify({ entries: this.entries }, null, 2),
39275
39449
  { encoding: "utf-8", mode: 384 }
@@ -41695,7 +41869,7 @@ __export(executor_exports, {
41695
41869
  resolveNodeTimeoutMs: () => resolveNodeTimeoutMs,
41696
41870
  thoroughnessForKind: () => thoroughnessForKind
41697
41871
  });
41698
- import { existsSync as existsSync37 } from "node:fs";
41872
+ import { existsSync as existsSync38 } from "node:fs";
41699
41873
  import path40 from "node:path";
41700
41874
  function resolveMaxParallel(env = process.env) {
41701
41875
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
@@ -41754,7 +41928,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
41754
41928
  }
41755
41929
  function defaultChecksExists(cwd) {
41756
41930
  try {
41757
- return existsSync37(path40.join(cwd, ".zelari", "world", "checks.json"));
41931
+ return existsSync38(path40.join(cwd, ".zelari", "world", "checks.json"));
41758
41932
  } catch {
41759
41933
  return false;
41760
41934
  }
@@ -42721,7 +42895,7 @@ __export(prereqChecks_exports, {
42721
42895
  runPrereqChecks: () => runPrereqChecks
42722
42896
  });
42723
42897
  import { execSync, spawnSync as spawnSync2 } from "node:child_process";
42724
- import { existsSync as existsSync38 } from "node:fs";
42898
+ import { existsSync as existsSync39 } from "node:fs";
42725
42899
  import { dirname as dirname8 } from "node:path";
42726
42900
  function isWslBashPath2(p3) {
42727
42901
  if (!p3 || typeof p3 !== "string") return false;
@@ -42845,7 +43019,7 @@ function agentProbeEnv() {
42845
43019
  }
42846
43020
  function existsSyncSafe2(p3) {
42847
43021
  try {
42848
- return existsSync38(p3);
43022
+ return existsSync39(p3);
42849
43023
  } catch {
42850
43024
  return false;
42851
43025
  }
@@ -43092,7 +43266,7 @@ var init_prereqChecks = __esm({
43092
43266
  });
43093
43267
 
43094
43268
  // src/cli/plugins/prefs.ts
43095
- import { existsSync as existsSync39, readFileSync as readFileSync33, writeFileSync as writeFileSync21, mkdirSync as mkdirSync18 } from "node:fs";
43269
+ import { existsSync as existsSync40, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
43096
43270
  import path43 from "node:path";
43097
43271
  import os10 from "node:os";
43098
43272
  function getPluginPrefsPath() {
@@ -43101,8 +43275,8 @@ function getPluginPrefsPath() {
43101
43275
  function getPluginPrefs() {
43102
43276
  const file2 = getPluginPrefsPath();
43103
43277
  try {
43104
- if (!existsSync39(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
43105
- const raw = readFileSync33(file2, "utf-8");
43278
+ if (!existsSync40(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
43279
+ const raw = readFileSync34(file2, "utf-8");
43106
43280
  const parsed = JSON.parse(raw);
43107
43281
  if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
43108
43282
  const clean = {};
@@ -43118,7 +43292,7 @@ function getPluginPrefs() {
43118
43292
  function writePluginPrefs(prefs) {
43119
43293
  const file2 = getPluginPrefsPath();
43120
43294
  mkdirSync18(path43.dirname(file2), { recursive: true });
43121
- writeFileSync21(file2, JSON.stringify(prefs, null, 2), {
43295
+ writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
43122
43296
  encoding: "utf-8",
43123
43297
  mode: 384
43124
43298
  });
@@ -43153,7 +43327,7 @@ __export(registry_exports, {
43153
43327
  findPlugin: () => findPlugin,
43154
43328
  isBinaryOnPath: () => isBinaryOnPath
43155
43329
  });
43156
- import { existsSync as existsSync40 } from "node:fs";
43330
+ import { existsSync as existsSync41 } from "node:fs";
43157
43331
  import path44 from "node:path";
43158
43332
  function detectLocalBin(bin) {
43159
43333
  return (cwd) => {
@@ -43170,7 +43344,7 @@ function isBinaryOnPath(bin, opts = {}) {
43170
43344
  return false;
43171
43345
  }
43172
43346
  const platform = opts.platform ?? process.platform;
43173
- const exists = opts.exists ?? existsSync40;
43347
+ const exists = opts.exists ?? existsSync41;
43174
43348
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
43175
43349
  const pathMod = platform === "win32" ? path44.win32 : path44.posix;
43176
43350
  const sep2 = platform === "win32" ? ";" : ":";
@@ -43904,7 +44078,7 @@ __export(atMentions_exports, {
43904
44078
  extractAtMentions: () => extractAtMentions,
43905
44079
  hasAtMentions: () => hasAtMentions
43906
44080
  });
43907
- import { existsSync as existsSync43, readFileSync as readFileSync35, statSync as statSync7 } from "node:fs";
44081
+ import { existsSync as existsSync44, readFileSync as readFileSync36, statSync as statSync8 } from "node:fs";
43908
44082
  import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
43909
44083
  function isImagePath(abs) {
43910
44084
  const ext = abs.split(".").pop()?.toLowerCase() ?? "";
@@ -43958,7 +44132,7 @@ function resolveMention(token, cwd) {
43958
44132
  note: "outside project root \u2014 skipped"
43959
44133
  };
43960
44134
  }
43961
- if (!existsSync43(abs)) {
44135
+ if (!existsSync44(abs)) {
43962
44136
  return {
43963
44137
  raw: token,
43964
44138
  path: token,
@@ -43969,7 +44143,7 @@ function resolveMention(token, cwd) {
43969
44143
  }
43970
44144
  let st;
43971
44145
  try {
43972
- st = statSync7(abs);
44146
+ st = statSync8(abs);
43973
44147
  } catch {
43974
44148
  return {
43975
44149
  raw: token,
@@ -44010,7 +44184,7 @@ function resolveMention(token, cwd) {
44010
44184
  note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
44011
44185
  };
44012
44186
  }
44013
- const dataBase64 = readFileSync35(abs).toString("base64");
44187
+ const dataBase64 = readFileSync36(abs).toString("base64");
44014
44188
  return {
44015
44189
  raw: token,
44016
44190
  path: rel2,
@@ -44021,7 +44195,7 @@ function resolveMention(token, cwd) {
44021
44195
  };
44022
44196
  }
44023
44197
  try {
44024
- const buf = readFileSync35(abs);
44198
+ const buf = readFileSync36(abs);
44025
44199
  const head = buf.subarray(0, 800).toString("utf8");
44026
44200
  if (!isProbablyText(abs, head)) {
44027
44201
  return {
@@ -44518,14 +44692,14 @@ var init_skillCategories = __esm({
44518
44692
 
44519
44693
  // src/cli/skillConfigIo.ts
44520
44694
  import {
44521
- existsSync as existsSync45,
44695
+ existsSync as existsSync46,
44522
44696
  mkdirSync as mkdirSync21,
44523
- readdirSync as readdirSync9,
44524
- readFileSync as readFileSync37,
44697
+ readdirSync as readdirSync10,
44698
+ readFileSync as readFileSync38,
44525
44699
  rmSync as rmSync4,
44526
- writeFileSync as writeFileSync23
44700
+ writeFileSync as writeFileSync24
44527
44701
  } from "node:fs";
44528
- import { dirname as dirname10, join as join36 } from "node:path";
44702
+ import { dirname as dirname10, join as join37 } from "node:path";
44529
44703
  import { homedir as homedir12 } from "node:os";
44530
44704
  function ensureBuiltinSkillsLoadedSync() {
44531
44705
  if (builtinsLoaded) return;
@@ -44537,13 +44711,13 @@ function ensureBuiltinSkillsLoadedSync() {
44537
44711
  }
44538
44712
  }
44539
44713
  function getUserSkillsDir() {
44540
- return join36(homedir12(), ".zelari-code", "skills");
44714
+ return join37(homedir12(), ".zelari-code", "skills");
44541
44715
  }
44542
44716
  function getProjectSkillsDir(projectRoot) {
44543
- return join36(projectRoot, ".zelari", "skills");
44717
+ return join37(projectRoot, ".zelari", "skills");
44544
44718
  }
44545
44719
  function skillFilePath(dir, name) {
44546
- return join36(dir, name, "SKILL.md");
44720
+ return join37(dir, name, "SKILL.md");
44547
44721
  }
44548
44722
  function classifyScope(skillPath, projectRoot) {
44549
44723
  const userDir = getUserSkillsDir().replace(/\\/g, "/");
@@ -44592,18 +44766,18 @@ function entryFromBuiltin(skill) {
44592
44766
  };
44593
44767
  }
44594
44768
  function scanSkillsDir(dir, projectRoot, seen, out) {
44595
- if (!existsSync45(dir)) return;
44769
+ if (!existsSync46(dir)) return;
44596
44770
  let entries;
44597
44771
  try {
44598
- entries = readdirSync9(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
44772
+ entries = readdirSync10(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
44599
44773
  } catch {
44600
44774
  return;
44601
44775
  }
44602
44776
  for (const entry of entries) {
44603
44777
  const skillPath = skillFilePath(dir, entry);
44604
- if (!existsSync45(skillPath)) continue;
44778
+ if (!existsSync46(skillPath)) continue;
44605
44779
  try {
44606
- const parsed = parseSkillMd(readFileSync37(skillPath, "utf8"), skillPath);
44780
+ const parsed = parseSkillMd(readFileSync38(skillPath, "utf8"), skillPath);
44607
44781
  if (!parsed) continue;
44608
44782
  if (seen.has(parsed.name)) continue;
44609
44783
  seen.add(parsed.name);
@@ -44619,9 +44793,9 @@ function listSkillsSnapshot(projectRoot) {
44619
44793
  const skills = [];
44620
44794
  const seen = /* @__PURE__ */ new Set();
44621
44795
  if (root) {
44622
- scanSkillsDir(join36(root, ".zelari", "skills"), root, seen, skills);
44623
- scanSkillsDir(join36(root, ".claude", "skills"), root, seen, skills);
44624
- scanSkillsDir(join36(root, ".opencode", "skills"), root, seen, skills);
44796
+ scanSkillsDir(join37(root, ".zelari", "skills"), root, seen, skills);
44797
+ scanSkillsDir(join37(root, ".claude", "skills"), root, seen, skills);
44798
+ scanSkillsDir(join37(root, ".opencode", "skills"), root, seen, skills);
44625
44799
  }
44626
44800
  scanSkillsDir(userSkillsDir, root, seen, skills);
44627
44801
  for (const s of listCodingSkills()) {
@@ -44701,7 +44875,7 @@ function upsertSkill(opts) {
44701
44875
  return { ok: false, error: "Generated SKILL.md failed validation" };
44702
44876
  }
44703
44877
  mkdirSync21(dirname10(path53), { recursive: true });
44704
- writeFileSync23(path53, content, "utf8");
44878
+ writeFileSync24(path53, content, "utf8");
44705
44879
  return { ok: true, path: path53 };
44706
44880
  }
44707
44881
  function removeSkill(opts) {
@@ -44719,9 +44893,9 @@ function removeSkill(opts) {
44719
44893
  }
44720
44894
  dir = getProjectSkillsDir(root);
44721
44895
  }
44722
- const skillDir = join36(dir, name);
44896
+ const skillDir = join37(dir, name);
44723
44897
  const path53 = skillFilePath(dir, name);
44724
- if (!existsSync45(path53) && !existsSync45(skillDir)) {
44898
+ if (!existsSync46(path53) && !existsSync46(skillDir)) {
44725
44899
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
44726
44900
  }
44727
44901
  try {
@@ -45026,36 +45200,36 @@ var init_permissionCli = __esm({
45026
45200
 
45027
45201
  // src/cli/companion/config.ts
45028
45202
  import {
45029
- existsSync as existsSync46,
45203
+ existsSync as existsSync47,
45030
45204
  mkdirSync as mkdirSync22,
45031
- readFileSync as readFileSync38,
45032
- writeFileSync as writeFileSync24
45205
+ readFileSync as readFileSync39,
45206
+ writeFileSync as writeFileSync25
45033
45207
  } from "node:fs";
45034
- import { join as join37 } from "node:path";
45208
+ import { join as join38 } from "node:path";
45035
45209
  import { homedir as homedir13 } from "node:os";
45036
45210
  import { createHash as createHash9, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
45037
45211
  function getZelariHome() {
45038
- return join37(homedir13(), ".zelari-code");
45212
+ return join38(homedir13(), ".zelari-code");
45039
45213
  }
45040
45214
  function getCompanionConfigPath() {
45041
- return join37(getZelariHome(), "companion.json");
45215
+ return join38(getZelariHome(), "companion.json");
45042
45216
  }
45043
45217
  function getCompanionTokenPath() {
45044
- return join37(getZelariHome(), "companion.token");
45218
+ return join38(getZelariHome(), "companion.token");
45045
45219
  }
45046
45220
  function ensureHome() {
45047
45221
  const home = getZelariHome();
45048
- if (!existsSync46(home)) {
45222
+ if (!existsSync47(home)) {
45049
45223
  mkdirSync22(home, { recursive: true });
45050
45224
  }
45051
45225
  }
45052
45226
  function loadCompanionConfig() {
45053
45227
  const path53 = getCompanionConfigPath();
45054
- if (!existsSync46(path53)) {
45228
+ if (!existsSync47(path53)) {
45055
45229
  return { projects: [] };
45056
45230
  }
45057
45231
  try {
45058
- const raw = JSON.parse(readFileSync38(path53, "utf8"));
45232
+ const raw = JSON.parse(readFileSync39(path53, "utf8"));
45059
45233
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
45060
45234
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
45061
45235
  ).map((p3) => ({
@@ -45074,7 +45248,7 @@ function loadCompanionConfig() {
45074
45248
  }
45075
45249
  function saveCompanionConfig(cfg) {
45076
45250
  ensureHome();
45077
- writeFileSync24(
45251
+ writeFileSync25(
45078
45252
  getCompanionConfigPath(),
45079
45253
  JSON.stringify(
45080
45254
  {
@@ -45094,12 +45268,12 @@ function loadOrCreateToken(explicit) {
45094
45268
  }
45095
45269
  ensureHome();
45096
45270
  const path53 = getCompanionTokenPath();
45097
- if (existsSync46(path53)) {
45098
- const t = readFileSync38(path53, "utf8").trim();
45271
+ if (existsSync47(path53)) {
45272
+ const t = readFileSync39(path53, "utf8").trim();
45099
45273
  if (t) return { token: t, created: false };
45100
45274
  }
45101
45275
  const token = randomBytes5(24).toString("base64url");
45102
- writeFileSync24(path53, token + "\n", "utf8");
45276
+ writeFileSync25(path53, token + "\n", "utf8");
45103
45277
  try {
45104
45278
  const fs31 = __require("node:fs");
45105
45279
  fs31.chmodSync?.(path53, 384);
@@ -45188,8 +45362,8 @@ var init_config = __esm({
45188
45362
  import { spawn as spawn13 } from "node:child_process";
45189
45363
  import { createInterface as createInterface2 } from "node:readline";
45190
45364
  import { randomUUID as randomUUID8 } from "node:crypto";
45191
- import { writeFileSync as writeFileSync25, unlinkSync as unlinkSync3 } from "node:fs";
45192
- import { join as join38 } from "node:path";
45365
+ import { writeFileSync as writeFileSync26, unlinkSync as unlinkSync3 } from "node:fs";
45366
+ import { join as join39 } from "node:path";
45193
45367
  import { tmpdir as tmpdir3 } from "node:os";
45194
45368
  var RunManager;
45195
45369
  var init_runManager = __esm({
@@ -45293,9 +45467,9 @@ var init_runManager = __esm({
45293
45467
  }
45294
45468
  let historyFile;
45295
45469
  if (args.history && Array.isArray(args.history) && args.history.length > 0) {
45296
- historyFile = join38(tmpdir3(), `zelari-companion-hist-${id}.json`);
45470
+ historyFile = join39(tmpdir3(), `zelari-companion-hist-${id}.json`);
45297
45471
  try {
45298
- writeFileSync25(historyFile, JSON.stringify(args.history), "utf8");
45472
+ writeFileSync26(historyFile, JSON.stringify(args.history), "utf8");
45299
45473
  argv.push("--history-file", historyFile);
45300
45474
  } catch {
45301
45475
  historyFile = void 0;
@@ -45420,7 +45594,7 @@ __export(serve_exports, {
45420
45594
  runCompanionServe: () => runCompanionServe
45421
45595
  });
45422
45596
  import { createServer as createServer3 } from "node:http";
45423
- import { existsSync as existsSync47 } from "node:fs";
45597
+ import { existsSync as existsSync48 } from "node:fs";
45424
45598
  import { resolve as resolve2 } from "node:path";
45425
45599
  function readBody(req, max = 2e6) {
45426
45600
  return new Promise((resolveBody, reject) => {
@@ -45468,7 +45642,7 @@ async function runCompanionServe(opts = {}) {
45468
45642
  let projects = mergeProjects(fileCfg, opts.projects ?? []);
45469
45643
  projects = projects.filter((p3) => {
45470
45644
  const abs = resolve2(p3.path);
45471
- if (!existsSync47(abs)) {
45645
+ if (!existsSync48(abs)) {
45472
45646
  process.stderr.write(
45473
45647
  `[zelari-code serve] skip missing project path: ${p3.path}
45474
45648
  `
@@ -45790,7 +45964,7 @@ __export(doctor_exports, {
45790
45964
  runDoctor: () => runDoctor
45791
45965
  });
45792
45966
  import { execSync as execSync2 } from "node:child_process";
45793
- import { existsSync as existsSync48, readFileSync as readFileSync39, readlinkSync, statSync as statSync8 } from "node:fs";
45967
+ import { existsSync as existsSync49, readFileSync as readFileSync40, readlinkSync, statSync as statSync9 } from "node:fs";
45794
45968
  import { createRequire as createRequire3 } from "node:module";
45795
45969
  import { fileURLToPath as fileURLToPath2 } from "node:url";
45796
45970
  import path51 from "node:path";
@@ -45798,9 +45972,9 @@ function findPackageRoot(start) {
45798
45972
  let dir = start;
45799
45973
  for (let i = 0; i < 6; i += 1) {
45800
45974
  const candidate = path51.join(dir, "package.json");
45801
- if (existsSync48(candidate)) {
45975
+ if (existsSync49(candidate)) {
45802
45976
  try {
45803
- const pkg = JSON.parse(readFileSync39(candidate, "utf8"));
45977
+ const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
45804
45978
  if (pkg.name === "zelari-code") return dir;
45805
45979
  } catch {
45806
45980
  }
@@ -45824,7 +45998,7 @@ function tryExec(cmd) {
45824
45998
  function readPackageJson3() {
45825
45999
  try {
45826
46000
  const pkgPath = path51.join(packageRoot, "package.json");
45827
- return JSON.parse(readFileSync39(pkgPath, "utf8"));
46001
+ return JSON.parse(readFileSync40(pkgPath, "utf8"));
45828
46002
  } catch {
45829
46003
  return null;
45830
46004
  }
@@ -45840,16 +46014,16 @@ function checkShim(pkgName) {
45840
46014
  const isWin = process.platform === "win32";
45841
46015
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
45842
46016
  const shimPath = path51.join(prefix, shimName);
45843
- if (!existsSync48(shimPath)) {
46017
+ if (!existsSync49(shimPath)) {
45844
46018
  return FAIL(
45845
46019
  `shim not found at ${shimPath}
45846
46020
  fix: npm install -g ${pkgName}@latest --force`
45847
46021
  );
45848
46022
  }
45849
46023
  try {
45850
- const st = statSync8(shimPath);
46024
+ const st = statSync9(shimPath);
45851
46025
  if (isWin) {
45852
- const content = readFileSync39(shimPath, "utf8");
46026
+ const content = readFileSync40(shimPath, "utf8");
45853
46027
  if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
45854
46028
  return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
45855
46029
  }
@@ -45908,14 +46082,14 @@ function checkNode(pkg) {
45908
46082
  }
45909
46083
  function checkBundle() {
45910
46084
  const bundle = path51.join(packageRoot, "dist", "cli", "main.bundled.js");
45911
- if (!existsSync48(bundle)) {
46085
+ if (!existsSync49(bundle)) {
45912
46086
  return FAIL(
45913
46087
  `dist/cli/main.bundled.js missing at ${bundle}
45914
46088
  fix: npm run build:cli (then reinstall or run via tsx)`
45915
46089
  );
45916
46090
  }
45917
46091
  try {
45918
- const st = statSync8(bundle);
46092
+ const st = statSync9(bundle);
45919
46093
  return OK(`bundle OK (${(st.size / 1024 / 1024).toFixed(2)} MB)`);
45920
46094
  } catch (err) {
45921
46095
  return FAIL(
@@ -46289,7 +46463,7 @@ __export(inspect_exports, {
46289
46463
  runInspect: () => runInspect
46290
46464
  });
46291
46465
  import path52 from "node:path";
46292
- import { existsSync as existsSync49, readFileSync as readFileSync40, readdirSync as readdirSync10 } from "node:fs";
46466
+ import { existsSync as existsSync50, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
46293
46467
  import { homedir as homedir14 } from "node:os";
46294
46468
  async function collectInspectReport(cwd = process.cwd()) {
46295
46469
  ensureBuiltinSkillsLoadedSync();
@@ -46322,11 +46496,11 @@ async function collectInspectReport(cwd = process.cwd()) {
46322
46496
  folders: listTrustedFolders()
46323
46497
  },
46324
46498
  configSources: [
46325
- { path: userMcpPath, exists: existsSync49(userMcpPath) },
46326
- { path: projectMcpPath, exists: existsSync49(projectMcpPath) },
46327
- { path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync49(path52.join(homedir14(), ".zelari-code", "provider.json")) },
46328
- { path: path52.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync49(path52.join(cwd, ".zelari", "AGENTS.md")) },
46329
- { path: path52.join(cwd, "AGENTS.md"), exists: existsSync49(path52.join(cwd, "AGENTS.md")) }
46499
+ { path: userMcpPath, exists: existsSync50(userMcpPath) },
46500
+ { path: projectMcpPath, exists: existsSync50(projectMcpPath) },
46501
+ { path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync50(path52.join(homedir14(), ".zelari-code", "provider.json")) },
46502
+ { path: path52.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync50(path52.join(cwd, ".zelari", "AGENTS.md")) },
46503
+ { path: path52.join(cwd, "AGENTS.md"), exists: existsSync50(path52.join(cwd, "AGENTS.md")) }
46330
46504
  ],
46331
46505
  skills: {
46332
46506
  total: snap.skills.length,
@@ -46339,7 +46513,7 @@ async function collectInspectReport(cwd = process.cwd()) {
46339
46513
  user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
46340
46514
  project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
46341
46515
  projectTrusted,
46342
- projectConfigExists: existsSync49(projectMcpPath)
46516
+ projectConfigExists: existsSync50(projectMcpPath)
46343
46517
  },
46344
46518
  hooks: {
46345
46519
  global: {
@@ -46359,7 +46533,7 @@ async function collectInspectReport(cwd = process.cwd()) {
46359
46533
  }
46360
46534
  function listJsonFiles(dir) {
46361
46535
  try {
46362
- return readdirSync10(dir).filter((f) => f.endsWith(".json")).sort();
46536
+ return readdirSync11(dir).filter((f) => f.endsWith(".json")).sort();
46363
46537
  } catch {
46364
46538
  return [];
46365
46539
  }
@@ -46371,9 +46545,9 @@ function findAgentsMd(cwd) {
46371
46545
  ];
46372
46546
  const found = [];
46373
46547
  for (const c of candidates) {
46374
- if (existsSync49(c)) {
46548
+ if (existsSync50(c)) {
46375
46549
  try {
46376
- const text = readFileSync40(c, "utf8");
46550
+ const text = readFileSync41(c, "utf8");
46377
46551
  found.push(`${c} (${text.length} bytes)`);
46378
46552
  } catch {
46379
46553
  found.push(`${c} (unreadable)`);
@@ -53567,7 +53741,7 @@ async function handlePromoteMember(ctx, memberId) {
53567
53741
  }
53568
53742
 
53569
53743
  // src/cli/branchManager.ts
53570
- import { promises as fs26, existsSync as existsSync41, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync19, statSync as statSync5, rmSync as rmSync3 } from "node:fs";
53744
+ import { promises as fs26, existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync6, rmSync as rmSync3 } from "node:fs";
53571
53745
  import path46 from "node:path";
53572
53746
  import os12 from "node:os";
53573
53747
  var META_FILENAME = "meta.json";
@@ -53589,11 +53763,11 @@ function sessionsPathFor(name, baseDir) {
53589
53763
  }
53590
53764
  function readBranchMeta(name, baseDir) {
53591
53765
  const metaPath = metaPathFor(name, baseDir);
53592
- if (!existsSync41(metaPath)) {
53766
+ if (!existsSync42(metaPath)) {
53593
53767
  throw new BranchNotFoundError(`Branch "${name}" not found`);
53594
53768
  }
53595
53769
  try {
53596
- const raw = readFileSync34(metaPath, "utf-8");
53770
+ const raw = readFileSync35(metaPath, "utf-8");
53597
53771
  const parsed = JSON.parse(raw);
53598
53772
  if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
53599
53773
  throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
@@ -53611,7 +53785,7 @@ function readBranchMeta(name, baseDir) {
53611
53785
  function writeBranchMeta(name, baseDir, meta3) {
53612
53786
  const metaPath = metaPathFor(name, baseDir);
53613
53787
  mkdirSync19(path46.dirname(metaPath), { recursive: true });
53614
- writeFileSync22(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
53788
+ writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
53615
53789
  }
53616
53790
  async function countSessions(name, baseDir) {
53617
53791
  const sessionsPath = sessionsPathFor(name, baseDir);
@@ -53649,7 +53823,7 @@ var SessionNotFoundError = class extends Error {
53649
53823
  };
53650
53824
  function branchExists(name, baseDir = getBranchesBaseDir()) {
53651
53825
  const bp = branchPathFor(name, baseDir);
53652
- return existsSync41(bp) && existsSync41(metaPathFor(name, baseDir));
53826
+ return existsSync42(bp) && existsSync42(metaPathFor(name, baseDir));
53653
53827
  }
53654
53828
  async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
53655
53829
  if (!name || name.trim().length === 0) {
@@ -53662,7 +53836,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
53662
53836
  throw new BranchAlreadyExistsError(name);
53663
53837
  }
53664
53838
  const sourcePath = path46.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
53665
- if (!existsSync41(sourcePath)) {
53839
+ if (!existsSync42(sourcePath)) {
53666
53840
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
53667
53841
  }
53668
53842
  const branchPath = branchPathFor(name, baseDir);
@@ -53695,7 +53869,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
53695
53869
  const results = [];
53696
53870
  for (const entry of entries) {
53697
53871
  const metaPath = metaPathFor(entry, baseDir);
53698
- if (!existsSync41(metaPath)) continue;
53872
+ if (!existsSync42(metaPath)) continue;
53699
53873
  try {
53700
53874
  const meta3 = readBranchMeta(entry, baseDir);
53701
53875
  const sessionCount = await countSessions(entry, baseDir);
@@ -53886,7 +54060,7 @@ import path48 from "node:path";
53886
54060
  import os13 from "node:os";
53887
54061
 
53888
54062
  // src/cli/skillHistory.ts
53889
- import { promises as fs28, existsSync as existsSync42, statSync as statSync6, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
54063
+ import { promises as fs28, existsSync as existsSync43, statSync as statSync7, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
53890
54064
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
53891
54065
  async function readSkillHistory(file2) {
53892
54066
  let raw = "";
@@ -55214,9 +55388,9 @@ function ContinueKey({ onContinue }) {
55214
55388
  init_providerConfig();
55215
55389
 
55216
55390
  // src/cli/wizard/firstRun.ts
55217
- import { existsSync as existsSync44 } from "node:fs";
55391
+ import { existsSync as existsSync45 } from "node:fs";
55218
55392
  function shouldRunWizard(input) {
55219
- const exists = input.exists ?? existsSync44;
55393
+ const exists = input.exists ?? existsSync45;
55220
55394
  if (input.hasResetConfigFlag) {
55221
55395
  return { shouldRun: true, reason: "--reset-config flag forced wizard" };
55222
55396
  }
@@ -55501,7 +55675,7 @@ init_keyStore();
55501
55675
  init_providerConfig();
55502
55676
  init_openai_compatible();
55503
55677
  init_phase();
55504
- import { readFileSync as readFileSync36 } from "node:fs";
55678
+ import { readFileSync as readFileSync37 } from "node:fs";
55505
55679
  function parseHeadlessFlags(argv) {
55506
55680
  if (!argv.includes("--headless")) {
55507
55681
  return { options: null };
@@ -55574,7 +55748,7 @@ function parseHeadlessFlags(argv) {
55574
55748
  let raw = null;
55575
55749
  if (arg === "--history-file") {
55576
55750
  try {
55577
- raw = readFileSync36(next, "utf-8");
55751
+ raw = readFileSync37(next, "utf-8");
55578
55752
  } catch {
55579
55753
  raw = null;
55580
55754
  }