zelari-code 1.4.1 → 1.5.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.
Files changed (37) hide show
  1. package/dist/cli/components/PluginGate.js +195 -0
  2. package/dist/cli/components/PluginGate.js.map +1 -0
  3. package/dist/cli/diagnostics/engine.js +2 -10
  4. package/dist/cli/diagnostics/engine.js.map +1 -1
  5. package/dist/cli/hooks/useSlashDispatch.js +17 -0
  6. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  7. package/dist/cli/lsp/tools.js +3 -12
  8. package/dist/cli/lsp/tools.js.map +1 -1
  9. package/dist/cli/main.bundled.js +1141 -545
  10. package/dist/cli/main.bundled.js.map +4 -4
  11. package/dist/cli/main.js +49 -3
  12. package/dist/cli/main.js.map +1 -1
  13. package/dist/cli/plugins/installer.js +103 -0
  14. package/dist/cli/plugins/installer.js.map +1 -0
  15. package/dist/cli/plugins/prefs.js +96 -0
  16. package/dist/cli/plugins/prefs.js.map +1 -0
  17. package/dist/cli/plugins/registry.js +209 -0
  18. package/dist/cli/plugins/registry.js.map +1 -0
  19. package/dist/cli/slashCommands.js +18 -1
  20. package/dist/cli/slashCommands.js.map +1 -1
  21. package/dist/cli/slashHandlers/plugins.js +75 -0
  22. package/dist/cli/slashHandlers/plugins.js.map +1 -0
  23. package/dist/cli/updater.js +1 -1
  24. package/dist/cli/updater.js.map +1 -1
  25. package/dist/cli/utils/doctor.js +43 -4
  26. package/dist/cli/utils/doctor.js.map +1 -1
  27. package/dist/cli/utils/fixPath.js +119 -0
  28. package/dist/cli/utils/fixPath.js.map +1 -0
  29. package/dist/cli/utils/paths.js +47 -0
  30. package/dist/cli/utils/paths.js.map +1 -1
  31. package/package.json +4 -2
  32. package/scripts/diagnose-path.ps1 +22 -0
  33. package/scripts/dump-path.ps1 +13 -0
  34. package/scripts/fix-path.ps1 +26 -0
  35. package/scripts/path-length.ps1 +15 -0
  36. package/scripts/postinstall.mjs +32 -0
  37. package/scripts/repair-path.mjs +104 -0
@@ -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: spawn8 } = await import("node:child_process");
311
+ const { spawn: spawn9 } = 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 = spawn8(cmd.bin, cmd.args, {
324
+ const child = spawn9(cmd.bin, cmd.args, {
325
325
  stdio: "ignore",
326
326
  detached: true
327
327
  });
@@ -1535,10 +1535,10 @@ function mergeDefs(...defs) {
1535
1535
  function cloneDef(schema) {
1536
1536
  return mergeDefs(schema._zod.def);
1537
1537
  }
1538
- function getElementAtPath(obj, path33) {
1539
- if (!path33)
1538
+ function getElementAtPath(obj, path34) {
1539
+ if (!path34)
1540
1540
  return obj;
1541
- return path33.reduce((acc, key) => acc?.[key], obj);
1541
+ return path34.reduce((acc, key) => acc?.[key], obj);
1542
1542
  }
1543
1543
  function promiseAllObject(promisesObj) {
1544
1544
  const keys = Object.keys(promisesObj);
@@ -1866,11 +1866,11 @@ function explicitlyAborted(x, startIndex = 0) {
1866
1866
  }
1867
1867
  return false;
1868
1868
  }
1869
- function prefixIssues(path33, issues) {
1869
+ function prefixIssues(path34, issues) {
1870
1870
  return issues.map((iss) => {
1871
1871
  var _a3;
1872
1872
  (_a3 = iss).path ?? (_a3.path = []);
1873
- iss.path.unshift(path33);
1873
+ iss.path.unshift(path34);
1874
1874
  return iss;
1875
1875
  });
1876
1876
  }
@@ -2088,16 +2088,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2088
2088
  }
2089
2089
  function formatError(error51, mapper = (issue2) => issue2.message) {
2090
2090
  const fieldErrors = { _errors: [] };
2091
- const processError = (error52, path33 = []) => {
2091
+ const processError = (error52, path34 = []) => {
2092
2092
  for (const issue2 of error52.issues) {
2093
2093
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2094
- issue2.errors.map((issues) => processError({ issues }, [...path33, ...issue2.path]));
2094
+ issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
2095
2095
  } else if (issue2.code === "invalid_key") {
2096
- processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
2096
+ processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
2097
2097
  } else if (issue2.code === "invalid_element") {
2098
- processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
2098
+ processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
2099
2099
  } else {
2100
- const fullpath = [...path33, ...issue2.path];
2100
+ const fullpath = [...path34, ...issue2.path];
2101
2101
  if (fullpath.length === 0) {
2102
2102
  fieldErrors._errors.push(mapper(issue2));
2103
2103
  } else {
@@ -2124,17 +2124,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2124
2124
  }
2125
2125
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2126
2126
  const result = { errors: [] };
2127
- const processError = (error52, path33 = []) => {
2127
+ const processError = (error52, path34 = []) => {
2128
2128
  var _a3, _b;
2129
2129
  for (const issue2 of error52.issues) {
2130
2130
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2131
- issue2.errors.map((issues) => processError({ issues }, [...path33, ...issue2.path]));
2131
+ issue2.errors.map((issues) => processError({ issues }, [...path34, ...issue2.path]));
2132
2132
  } else if (issue2.code === "invalid_key") {
2133
- processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
2133
+ processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
2134
2134
  } else if (issue2.code === "invalid_element") {
2135
- processError({ issues: issue2.issues }, [...path33, ...issue2.path]);
2135
+ processError({ issues: issue2.issues }, [...path34, ...issue2.path]);
2136
2136
  } else {
2137
- const fullpath = [...path33, ...issue2.path];
2137
+ const fullpath = [...path34, ...issue2.path];
2138
2138
  if (fullpath.length === 0) {
2139
2139
  result.errors.push(mapper(issue2));
2140
2140
  continue;
@@ -2166,8 +2166,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2166
2166
  }
2167
2167
  function toDotPath(_path) {
2168
2168
  const segs = [];
2169
- const path33 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2170
- for (const seg of path33) {
2169
+ const path34 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2170
+ for (const seg of path34) {
2171
2171
  if (typeof seg === "number")
2172
2172
  segs.push(`[${seg}]`);
2173
2173
  else if (typeof seg === "symbol")
@@ -15670,13 +15670,13 @@ function resolveRef(ref, ctx) {
15670
15670
  if (!ref.startsWith("#")) {
15671
15671
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15672
15672
  }
15673
- const path33 = ref.slice(1).split("/").filter(Boolean);
15674
- if (path33.length === 0) {
15673
+ const path34 = ref.slice(1).split("/").filter(Boolean);
15674
+ if (path34.length === 0) {
15675
15675
  return ctx.rootSchema;
15676
15676
  }
15677
15677
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15678
- if (path33[0] === defsKey) {
15679
- const key = path33[1];
15678
+ if (path34[0] === defsKey) {
15679
+ const key = path34[1];
15680
15680
  if (!key || !ctx.defs[key]) {
15681
15681
  throw new Error(`Reference not found: ${ref}`);
15682
15682
  }
@@ -16836,7 +16836,7 @@ var init_walk = __esm({
16836
16836
  // packages/core/dist/core/tools/builtin/search.js
16837
16837
  import { promises as fs6 } from "node:fs";
16838
16838
  import path7 from "node:path";
16839
- async function searchFile(absPath, relPath2, regex, contextLines, remainingSlots) {
16839
+ async function searchFile(absPath, relPath, regex, contextLines, remainingSlots) {
16840
16840
  let buf;
16841
16841
  try {
16842
16842
  buf = await fs6.readFile(absPath, "utf-8");
@@ -16856,7 +16856,7 @@ async function searchFile(absPath, relPath2, regex, contextLines, remainingSlots
16856
16856
  const endAfter = Math.min(lines.length - 1, i + contextLines);
16857
16857
  matches.push({
16858
16858
  file: absPath,
16859
- relPath: relPath2,
16859
+ relPath,
16860
16860
  line: i + 1,
16861
16861
  text: lines[i],
16862
16862
  context: {
@@ -17706,11 +17706,11 @@ var init_tools = __esm({
17706
17706
  if (!ctx.addDocument)
17707
17707
  return "Knowledge vault tool not available.";
17708
17708
  const title = args["title"] || "New Document";
17709
- const path33 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17709
+ const path34 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17710
17710
  const content = args["content"] || "";
17711
17711
  const tags = args["tags"] || [];
17712
17712
  ctx.addDocument({
17713
- path: path33,
17713
+ path: path34,
17714
17714
  title,
17715
17715
  content,
17716
17716
  format: "markdown",
@@ -17719,7 +17719,7 @@ var init_tools = __esm({
17719
17719
  workspaceId: ctx.workspaceId
17720
17720
  });
17721
17721
  ctx.addActivity("vault", "created document", title);
17722
- return `Document "${title}" created at "${path33}".`;
17722
+ return `Document "${title}" created at "${path34}".`;
17723
17723
  }
17724
17724
  }
17725
17725
  ];
@@ -18096,6 +18096,33 @@ var init_skills2 = __esm({
18096
18096
  }
18097
18097
  });
18098
18098
 
18099
+ // src/cli/utils/paths.ts
18100
+ import { homedir as homedir2 } from "node:os";
18101
+ import path10 from "node:path";
18102
+ function shortenCwd(p3, maxLen = 40, home = homedir2()) {
18103
+ let out = p3;
18104
+ if (home && (out === home || out.startsWith(`${home}\\`) || out.startsWith(`${home}/`))) {
18105
+ out = `~${out.slice(home.length)}`;
18106
+ }
18107
+ if (out.length <= maxLen) return out;
18108
+ return `\u2026${out.slice(-(maxLen - 1))}`;
18109
+ }
18110
+ function relativePosix(from, to) {
18111
+ try {
18112
+ const p3 = process.platform === "win32" ? path10.win32 : path10.posix;
18113
+ const rel2 = p3.relative(from, to);
18114
+ if (!rel2 || rel2.startsWith("..")) return to;
18115
+ return rel2.split(/[\\/]/).join("/");
18116
+ } catch {
18117
+ return to;
18118
+ }
18119
+ }
18120
+ var init_paths = __esm({
18121
+ "src/cli/utils/paths.ts"() {
18122
+ "use strict";
18123
+ }
18124
+ });
18125
+
18099
18126
  // packages/core/dist/shared/events.js
18100
18127
  function createBrainEvent(type, sessionId, data) {
18101
18128
  return {
@@ -18802,7 +18829,7 @@ var init_providerStream = __esm({
18802
18829
 
18803
18830
  // packages/core/dist/core/sessionJsonl.js
18804
18831
  import { promises as fs8 } from "node:fs";
18805
- import path10 from "node:path";
18832
+ import path11 from "node:path";
18806
18833
  import os3 from "node:os";
18807
18834
  async function readSession(filePath) {
18808
18835
  try {
@@ -18828,7 +18855,7 @@ async function readSession(filePath) {
18828
18855
  }
18829
18856
  }
18830
18857
  function defaultBaseDir() {
18831
- return path10.join(os3.tmpdir(), "zelari-code", "sessions");
18858
+ return path11.join(os3.tmpdir(), "zelari-code", "sessions");
18832
18859
  }
18833
18860
  var SessionJsonlWriter;
18834
18861
  var init_sessionJsonl = __esm({
@@ -18839,7 +18866,7 @@ var init_sessionJsonl = __esm({
18839
18866
  onError;
18840
18867
  constructor(sessionId, options = {}) {
18841
18868
  const baseDir = options.baseDir ?? defaultBaseDir();
18842
- this.filePath = path10.join(baseDir, `${sessionId}.jsonl`);
18869
+ this.filePath = path11.join(baseDir, `${sessionId}.jsonl`);
18843
18870
  this.onError = options.onError ?? console.error;
18844
18871
  }
18845
18872
  /** Absolute path to the session JSONL file. */
@@ -18849,7 +18876,7 @@ var init_sessionJsonl = __esm({
18849
18876
  /** Append a BrainEvent as one JSON line. Creates the file + parent dirs if missing. */
18850
18877
  async append(event) {
18851
18878
  try {
18852
- await fs8.mkdir(path10.dirname(this.filePath), { recursive: true });
18879
+ await fs8.mkdir(path11.dirname(this.filePath), { recursive: true });
18853
18880
  const line = JSON.stringify({
18854
18881
  ts: event.ts,
18855
18882
  sessionId: event.sessionId,
@@ -19221,6 +19248,346 @@ var init_registry = __esm({
19221
19248
  }
19222
19249
  });
19223
19250
 
19251
+ // src/cli/diagnostics/engine.ts
19252
+ import { spawn as spawn2 } from "node:child_process";
19253
+ import { existsSync as existsSync7 } from "node:fs";
19254
+ import path16 from "node:path";
19255
+ function parseEslintJson(stdout, _file2) {
19256
+ const json2 = safeJson(stdout);
19257
+ if (!Array.isArray(json2)) return [];
19258
+ const out = [];
19259
+ for (const fileResult of json2) {
19260
+ if (!fileResult || typeof fileResult !== "object") continue;
19261
+ const fr = fileResult;
19262
+ const filePath = typeof fr.filePath === "string" ? fr.filePath : _file2;
19263
+ if (!Array.isArray(fr.messages)) continue;
19264
+ for (const m of fr.messages) {
19265
+ if (!m || typeof m !== "object") continue;
19266
+ const msg = m;
19267
+ out.push({
19268
+ file: filePath,
19269
+ line: typeof msg.line === "number" ? msg.line : 0,
19270
+ ...typeof msg.column === "number" ? { column: msg.column } : {},
19271
+ // ESLint severity: 2 = error, 1 = warning.
19272
+ severity: msg.severity === 2 ? "error" : "warning",
19273
+ message: typeof msg.message === "string" ? msg.message : "(no message)",
19274
+ ...typeof msg.ruleId === "string" && msg.ruleId ? { code: msg.ruleId } : {},
19275
+ source: "eslint"
19276
+ });
19277
+ }
19278
+ }
19279
+ return out;
19280
+ }
19281
+ function parseRuffJson(stdout, _file2) {
19282
+ const json2 = safeJson(stdout);
19283
+ if (!Array.isArray(json2)) return [];
19284
+ const out = [];
19285
+ for (const issue2 of json2) {
19286
+ if (!issue2 || typeof issue2 !== "object") continue;
19287
+ const it = issue2;
19288
+ const code = typeof it.code === "string" ? it.code : void 0;
19289
+ out.push({
19290
+ file: typeof it.filename === "string" ? it.filename : _file2,
19291
+ line: typeof it.location?.row === "number" ? it.location.row : 0,
19292
+ ...typeof it.location?.column === "number" ? { column: it.location.column } : {},
19293
+ // Ruff has no severity field; E999 is a syntax error, everything else
19294
+ // is a lint warning.
19295
+ severity: code === "E999" ? "error" : "warning",
19296
+ message: typeof it.message === "string" ? it.message : "(no message)",
19297
+ ...code ? { code } : {},
19298
+ source: "ruff"
19299
+ });
19300
+ }
19301
+ return out;
19302
+ }
19303
+ function safeJson(s) {
19304
+ const trimmed = s.trim();
19305
+ if (!trimmed) return null;
19306
+ try {
19307
+ return JSON.parse(trimmed);
19308
+ } catch {
19309
+ return null;
19310
+ }
19311
+ }
19312
+ function providerForFile(file2, providers = DEFAULT_PROVIDERS) {
19313
+ const ext = path16.extname(file2).toLowerCase();
19314
+ return providers.find((p3) => p3.extensions.includes(ext)) ?? null;
19315
+ }
19316
+ function resolveBin(bin, cwd) {
19317
+ const suffixes = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
19318
+ let dir = cwd;
19319
+ for (let i = 0; i < 6; i += 1) {
19320
+ for (const suffix of suffixes) {
19321
+ const candidate = path16.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
19322
+ if (existsSync7(candidate)) return candidate;
19323
+ }
19324
+ const parent = path16.dirname(dir);
19325
+ if (parent === dir) break;
19326
+ dir = parent;
19327
+ }
19328
+ return bin;
19329
+ }
19330
+ async function runDiagnosticsForFile(file2, options = {}) {
19331
+ const provider = providerForFile(file2, options.providers);
19332
+ if (!provider) return [];
19333
+ const cwd = options.cwd ?? process.cwd();
19334
+ const timeoutMs = options.timeoutMs ?? 5e3;
19335
+ const runner = options.runner ?? defaultRunner;
19336
+ try {
19337
+ const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
19338
+ const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
19339
+ return provider.parse(result.stdout, file2);
19340
+ } catch {
19341
+ return [];
19342
+ }
19343
+ }
19344
+ function formatDiagnostics(diagnostics, opts = {}) {
19345
+ if (diagnostics.length === 0) return "";
19346
+ const maxLines = opts.maxLines ?? 20;
19347
+ const rank = { error: 0, warning: 1, info: 2 };
19348
+ const sorted = [...diagnostics].sort(
19349
+ (a, b) => rank[a.severity] - rank[b.severity] || a.line - b.line
19350
+ );
19351
+ const errors = sorted.filter((d) => d.severity === "error").length;
19352
+ const warnings = sorted.filter((d) => d.severity === "warning").length;
19353
+ const header = `\u26A0 ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"} (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) \u2014 fix before continuing:`;
19354
+ const shown = sorted.slice(0, maxLines).map((d) => {
19355
+ const loc = opts.relativeTo ? relativePosix(opts.relativeTo, d.file) : d.file;
19356
+ const pos = d.column ? `${d.line}:${d.column}` : `${d.line}`;
19357
+ const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
19358
+ const code = d.code ? ` [${d.code}]` : "";
19359
+ return ` ${loc}:${pos} ${tag}${code}: ${d.message} (${d.source})`;
19360
+ });
19361
+ const overflow = sorted.length > maxLines ? [` \u2026 and ${sorted.length - maxLines} more`] : [];
19362
+ return [header, ...shown, ...overflow].join("\n");
19363
+ }
19364
+ var DEFAULT_PROVIDERS, defaultRunner;
19365
+ var init_engine = __esm({
19366
+ "src/cli/diagnostics/engine.ts"() {
19367
+ "use strict";
19368
+ init_paths();
19369
+ DEFAULT_PROVIDERS = [
19370
+ {
19371
+ name: "eslint",
19372
+ bin: "eslint",
19373
+ extensions: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"],
19374
+ args: (file2) => ["--format", "json", file2],
19375
+ parse: parseEslintJson
19376
+ },
19377
+ {
19378
+ name: "ruff",
19379
+ bin: "ruff",
19380
+ extensions: [".py"],
19381
+ args: (file2) => ["check", "--output-format", "json", file2],
19382
+ parse: parseRuffJson
19383
+ }
19384
+ ];
19385
+ defaultRunner = (cmd, args, opts) => new Promise((resolve) => {
19386
+ let stdout = "";
19387
+ let stderr = "";
19388
+ let settled = false;
19389
+ const done = (r) => {
19390
+ if (settled) return;
19391
+ settled = true;
19392
+ resolve(r);
19393
+ };
19394
+ let child;
19395
+ try {
19396
+ child = process.platform === "win32" ? spawn2(`${cmd} ${args.join(" ")}`, { cwd: opts.cwd, shell: true }) : spawn2(cmd, args, { cwd: opts.cwd });
19397
+ } catch {
19398
+ done({ code: null, stdout: "", stderr: "" });
19399
+ return;
19400
+ }
19401
+ const timer = setTimeout(() => {
19402
+ try {
19403
+ child.kill();
19404
+ } catch {
19405
+ }
19406
+ done({ code: null, stdout, stderr });
19407
+ }, opts.timeoutMs);
19408
+ child.stdout?.on("data", (c) => {
19409
+ stdout += c.toString();
19410
+ });
19411
+ child.stderr?.on("data", (c) => {
19412
+ stderr += c.toString();
19413
+ });
19414
+ child.on("error", () => {
19415
+ clearTimeout(timer);
19416
+ done({ code: null, stdout: "", stderr: "" });
19417
+ });
19418
+ child.on("close", (code) => {
19419
+ clearTimeout(timer);
19420
+ done({ code, stdout, stderr });
19421
+ });
19422
+ });
19423
+ }
19424
+ });
19425
+
19426
+ // src/cli/lsp/servers.ts
19427
+ import path17 from "node:path";
19428
+ function languageIdForFile(file2) {
19429
+ const ext = path17.extname(file2).toLowerCase();
19430
+ const map2 = {
19431
+ ".ts": "typescript",
19432
+ ".tsx": "typescriptreact",
19433
+ ".js": "javascript",
19434
+ ".jsx": "javascriptreact",
19435
+ ".mjs": "javascript",
19436
+ ".cjs": "javascript",
19437
+ ".py": "python",
19438
+ ".go": "go",
19439
+ ".rs": "rust"
19440
+ };
19441
+ return map2[ext] ?? "plaintext";
19442
+ }
19443
+ function serverForFile(file2, servers = LSP_SERVERS) {
19444
+ const ext = path17.extname(file2).toLowerCase();
19445
+ return servers.find((s) => s.extensions.includes(ext)) ?? null;
19446
+ }
19447
+ function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
19448
+ const spec = serverForFile(file2, servers);
19449
+ if (!spec) return null;
19450
+ const command = resolveBin(spec.bin, cwd);
19451
+ return {
19452
+ language: spec.language,
19453
+ command,
19454
+ args: spec.args,
19455
+ resolved: command !== spec.bin
19456
+ };
19457
+ }
19458
+ var LSP_SERVERS;
19459
+ var init_servers = __esm({
19460
+ "src/cli/lsp/servers.ts"() {
19461
+ "use strict";
19462
+ init_engine();
19463
+ LSP_SERVERS = [
19464
+ {
19465
+ language: "typescript",
19466
+ bin: "typescript-language-server",
19467
+ args: ["--stdio"],
19468
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
19469
+ },
19470
+ {
19471
+ language: "python",
19472
+ bin: "pyright-langserver",
19473
+ args: ["--stdio"],
19474
+ extensions: [".py"]
19475
+ },
19476
+ {
19477
+ language: "go",
19478
+ bin: "gopls",
19479
+ args: [],
19480
+ extensions: [".go"]
19481
+ },
19482
+ {
19483
+ language: "rust",
19484
+ bin: "rust-analyzer",
19485
+ args: [],
19486
+ extensions: [".rs"]
19487
+ }
19488
+ ];
19489
+ }
19490
+ });
19491
+
19492
+ // src/cli/browser/driver.ts
19493
+ async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
19494
+ const consoleErrors = [];
19495
+ const pageErrors = [];
19496
+ const failedRequests = [];
19497
+ const base = { ok: false, consoleErrors, pageErrors, failedRequests };
19498
+ const pw = await loader();
19499
+ if (!pw) {
19500
+ return {
19501
+ ...base,
19502
+ error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
19503
+ };
19504
+ }
19505
+ const timeout = options.timeoutMs ?? 15e3;
19506
+ let browser;
19507
+ try {
19508
+ browser = await pw.chromium.launch({ headless: true });
19509
+ const page = await browser.newPage();
19510
+ page.on("console", (msg) => {
19511
+ if (msg.type() === "error") consoleErrors.push(msg.text());
19512
+ });
19513
+ page.on("pageerror", (err) => pageErrors.push(err.message));
19514
+ page.on("requestfailed", (req) => {
19515
+ const f = req.failure();
19516
+ failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
19517
+ });
19518
+ await page.goto(options.url, { waitUntil: "load", timeout });
19519
+ for (const action of options.actions ?? []) {
19520
+ switch (action.type) {
19521
+ case "click":
19522
+ await page.click(action.selector, { timeout });
19523
+ break;
19524
+ case "fill":
19525
+ await page.fill(action.selector, action.value, { timeout });
19526
+ break;
19527
+ case "goto":
19528
+ await page.goto(action.url, { waitUntil: "load", timeout });
19529
+ break;
19530
+ case "wait":
19531
+ await page.waitForTimeout(action.ms);
19532
+ break;
19533
+ default:
19534
+ break;
19535
+ }
19536
+ }
19537
+ let selectorFound;
19538
+ if (options.waitForSelector) {
19539
+ try {
19540
+ await page.waitForSelector(options.waitForSelector, { timeout });
19541
+ selectorFound = true;
19542
+ } catch {
19543
+ selectorFound = false;
19544
+ }
19545
+ }
19546
+ let screenshotPath;
19547
+ if (options.screenshotPath) {
19548
+ try {
19549
+ await page.screenshot({ path: options.screenshotPath });
19550
+ screenshotPath = options.screenshotPath;
19551
+ } catch {
19552
+ }
19553
+ }
19554
+ const title = await page.title().catch(() => void 0);
19555
+ return {
19556
+ ok: true,
19557
+ consoleErrors,
19558
+ pageErrors,
19559
+ failedRequests,
19560
+ url: page.url(),
19561
+ ...title !== void 0 ? { title } : {},
19562
+ ...selectorFound !== void 0 ? { selectorFound } : {},
19563
+ ...screenshotPath ? { screenshotPath } : {}
19564
+ };
19565
+ } catch (err) {
19566
+ return { ...base, error: err instanceof Error ? err.message : String(err) };
19567
+ } finally {
19568
+ try {
19569
+ await browser?.close();
19570
+ } catch {
19571
+ }
19572
+ }
19573
+ }
19574
+ var defaultPlaywrightLoader;
19575
+ var init_driver = __esm({
19576
+ "src/cli/browser/driver.ts"() {
19577
+ "use strict";
19578
+ defaultPlaywrightLoader = async () => {
19579
+ try {
19580
+ const pkg = "playwright";
19581
+ const mod = await import(pkg);
19582
+ if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
19583
+ return null;
19584
+ } catch {
19585
+ return null;
19586
+ }
19587
+ };
19588
+ }
19589
+ });
19590
+
19224
19591
  // packages/core/dist/agents/roles.js
19225
19592
  function getAgent(id) {
19226
19593
  return AGENT_ROLES.find((a) => a.id === id);
@@ -20408,11 +20775,11 @@ var init_synthesisAudit = __esm({
20408
20775
  import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
20409
20776
  import { join as join2 } from "node:path";
20410
20777
  function loadNfrSpec(zelariRoot) {
20411
- const path33 = join2(zelariRoot, "nfr-spec.json");
20412
- if (!existsSync10(path33))
20778
+ const path34 = join2(zelariRoot, "nfr-spec.json");
20779
+ if (!existsSync10(path34))
20413
20780
  return null;
20414
20781
  try {
20415
- const raw = JSON.parse(readFileSync8(path33, "utf8"));
20782
+ const raw = JSON.parse(readFileSync8(path34, "utf8"));
20416
20783
  if (raw.version !== 1 || !Array.isArray(raw.targets))
20417
20784
  return null;
20418
20785
  return raw;
@@ -20681,9 +21048,9 @@ function checkDeadHooksInHtml(html) {
20681
21048
  }
20682
21049
  return warnings;
20683
21050
  }
20684
- function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
20685
- const abs = join3(projectRoot, relPath2);
20686
- if (!existsSync11(abs) || !/\.html?$/i.test(relPath2))
21051
+ function runMicroVerificationOnFile(projectRoot, relPath, zelariRoot) {
21052
+ const abs = join3(projectRoot, relPath);
21053
+ if (!existsSync11(abs) || !/\.html?$/i.test(relPath))
20687
21054
  return [];
20688
21055
  const spec = (zelariRoot ? loadNfrSpec(zelariRoot) : null) ?? DEFAULT_NFR_SPEC;
20689
21056
  const anim = spec.animation ?? { compositorOnly: true, forbidLayoutProps: true };
@@ -20697,7 +21064,7 @@ function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
20697
21064
  warnings.push({
20698
21065
  id: "motion.keyframes",
20699
21066
  message: `@keyframes uses non-allowed property "${v.property}"`,
20700
- file: relPath2,
21067
+ file: relPath,
20701
21068
  line: v.line
20702
21069
  });
20703
21070
  }
@@ -20705,12 +21072,12 @@ function runMicroVerificationOnFile(projectRoot, relPath2, zelariRoot) {
20705
21072
  warnings.push({
20706
21073
  id: "motion.transitions",
20707
21074
  message: `transition uses non-allowed property "${v.property}"`,
20708
- file: relPath2,
21075
+ file: relPath,
20709
21076
  line: v.line
20710
21077
  });
20711
21078
  }
20712
21079
  for (const w of checkDeadHooksInHtml(html)) {
20713
- warnings.push({ ...w, file: relPath2 });
21080
+ warnings.push({ ...w, file: relPath });
20714
21081
  }
20715
21082
  return warnings;
20716
21083
  }
@@ -22039,8 +22406,8 @@ async function* runChairmanFixLoop(args) {
22039
22406
  break;
22040
22407
  }
22041
22408
  const rescanned = /* @__PURE__ */ new Map();
22042
- for (const relPath2 of args.changedFiles) {
22043
- for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath: relPath2, zelariRoot })) {
22409
+ for (const relPath of args.changedFiles) {
22410
+ for (const w of runChairmanMicroGate({ projectRoot: args.projectRoot, relPath, zelariRoot })) {
22044
22411
  rescanned.set(`${w.id}|${w.file}|${w.line ?? ""}`, w);
22045
22412
  }
22046
22413
  }
@@ -22601,9 +22968,9 @@ var init_types2 = __esm({
22601
22968
  import { readFileSync as readFileSync13 } from "node:fs";
22602
22969
  import { join as join8 } from "node:path";
22603
22970
  function readLessonsDeduped(zelariRoot) {
22604
- const path33 = join8(zelariRoot, LESSONS_FILE);
22971
+ const path34 = join8(zelariRoot, LESSONS_FILE);
22605
22972
  try {
22606
- const raw = readFileSync13(path33, "utf8");
22973
+ const raw = readFileSync13(path34, "utf8");
22607
22974
  const byId = /* @__PURE__ */ new Map();
22608
22975
  for (const line of raw.split(/\r?\n/)) {
22609
22976
  if (!line.trim())
@@ -22704,8 +23071,8 @@ function keywordsFrom(check2, signature) {
22704
23071
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
22705
23072
  }
22706
23073
  function writeLesson(zelariRoot, lesson) {
22707
- const path33 = join9(zelariRoot, LESSONS_FILE);
22708
- appendFileSync2(path33, `${JSON.stringify(lesson)}
23074
+ const path34 = join9(zelariRoot, LESSONS_FILE);
23075
+ appendFileSync2(path34, `${JSON.stringify(lesson)}
22709
23076
  `, "utf8");
22710
23077
  }
22711
23078
  function findSimilar(lessons, signature) {
@@ -23301,7 +23668,7 @@ function workspaceArtifact(rootDir, subdir, slug) {
23301
23668
  function projectName(projectRoot = process.cwd()) {
23302
23669
  return basename(realpathSync(projectRoot));
23303
23670
  }
23304
- var init_paths = __esm({
23671
+ var init_paths2 = __esm({
23305
23672
  "src/cli/workspace/paths.ts"() {
23306
23673
  "use strict";
23307
23674
  }
@@ -23316,7 +23683,7 @@ __export(workspaceSummary_exports, {
23316
23683
  buildZelariReadHint: () => buildZelariReadHint
23317
23684
  });
23318
23685
  import { existsSync as existsSync14, readFileSync as readFileSync14, readdirSync, statSync as statSync3 } from "node:fs";
23319
- import { join as join12, relative as relative2 } from "node:path";
23686
+ import { join as join12, relative } from "node:path";
23320
23687
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
23321
23688
  const { maxEntries = 30 } = options;
23322
23689
  const name = safeProjectName(projectRoot);
@@ -23529,7 +23896,7 @@ function listShallow(projectRoot, maxEntries) {
23529
23896
  out.push(`\u2026 (+${top.length - count} more)`);
23530
23897
  break;
23531
23898
  }
23532
- const rel2 = relative2(projectRoot, join12(projectRoot, entry.name));
23899
+ const rel2 = relative(projectRoot, join12(projectRoot, entry.name));
23533
23900
  if (entry.isDirectory()) {
23534
23901
  let inner = "";
23535
23902
  try {
@@ -23562,7 +23929,7 @@ var init_workspaceSummary = __esm({
23562
23929
  "src/cli/workspace/workspaceSummary.ts"() {
23563
23930
  "use strict";
23564
23931
  init_council();
23565
- init_paths();
23932
+ init_paths2();
23566
23933
  PLAN_SUMMARY_MAX_TASKS = 15;
23567
23934
  PRIORITY_RANK = {
23568
23935
  critical: 3,
@@ -23845,28 +24212,28 @@ var init_storage = __esm({
23845
24212
  VALID_SCALARS = /^(true|false|null|~)$/i;
23846
24213
  Storage = class {
23847
24214
  /** Read a Markdown file with frontmatter. Throws if not found. */
23848
- read(path33) {
23849
- if (!existsSync15(path33)) {
23850
- throw new Error(`File not found: ${path33}`);
24215
+ read(path34) {
24216
+ if (!existsSync15(path34)) {
24217
+ throw new Error(`File not found: ${path34}`);
23851
24218
  }
23852
- const md = readFileSync15(path33, "utf8");
24219
+ const md = readFileSync15(path34, "utf8");
23853
24220
  return parseFrontmatter(md);
23854
24221
  }
23855
24222
  /** Read a Markdown file; returns null if not found. */
23856
- readIfExists(path33) {
23857
- if (!existsSync15(path33)) return null;
23858
- return this.read(path33);
24223
+ readIfExists(path34) {
24224
+ if (!existsSync15(path34)) return null;
24225
+ return this.read(path34);
23859
24226
  }
23860
24227
  /**
23861
24228
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
23862
24229
  * The meta object is serialized as YAML frontmatter; body as Markdown.
23863
24230
  */
23864
- write(path33, meta3, body) {
23865
- mkdirSync7(dirname(path33), { recursive: true });
23866
- const tmp = path33 + ".tmp-" + process.pid;
24231
+ write(path34, meta3, body) {
24232
+ mkdirSync7(dirname(path34), { recursive: true });
24233
+ const tmp = path34 + ".tmp-" + process.pid;
23867
24234
  const md = serializeFrontmatter(meta3, body);
23868
24235
  writeFileSync11(tmp, md, "utf8");
23869
- renameSync2(tmp, path33);
24236
+ renameSync2(tmp, path34);
23870
24237
  }
23871
24238
  /** List all .md files in a directory (non-recursive). */
23872
24239
  listMarkdown(dir) {
@@ -23917,7 +24284,7 @@ import {
23917
24284
  mkdirSync as mkdirSync8,
23918
24285
  renameSync as renameSync3
23919
24286
  } from "node:fs";
23920
- import { join as join14, basename as basename2, dirname as dirname2, relative as relative3 } from "node:path";
24287
+ import { join as join14, basename as basename2, dirname as dirname2, relative as relative2 } from "node:path";
23921
24288
  function createWorkspaceContext(projectRoot = process.cwd()) {
23922
24289
  const rootDir = resolveWorkspaceRoot(projectRoot);
23923
24290
  return {
@@ -23944,8 +24311,8 @@ function readPlan(ctx) {
23944
24311
  } catch {
23945
24312
  }
23946
24313
  }
23947
- const path33 = workspaceFile(ctx.rootDir, "plan");
23948
- const doc = ctx.storage.readIfExists(path33);
24314
+ const path34 = workspaceFile(ctx.rootDir, "plan");
24315
+ const doc = ctx.storage.readIfExists(path34);
23949
24316
  if (!doc) return { phases: [], tasks: [], milestones: [] };
23950
24317
  const meta3 = doc.meta;
23951
24318
  return {
@@ -24110,7 +24477,7 @@ function addMilestoneRecord(ctx, summary, input) {
24110
24477
  dueDate: input.dueDate,
24111
24478
  targetVersion: version2
24112
24479
  });
24113
- const path33 = join14(ctx.rootDir, "milestones", `${id}.md`);
24480
+ const path34 = join14(ctx.rootDir, "milestones", `${id}.md`);
24114
24481
  const meta3 = {
24115
24482
  kind: "milestone",
24116
24483
  id,
@@ -24127,7 +24494,7 @@ function addMilestoneRecord(ctx, summary, input) {
24127
24494
  `Target version: ${version2}`,
24128
24495
  ""
24129
24496
  ].join("\n");
24130
- ctx.storage.write(path33, meta3, body);
24497
+ ctx.storage.write(path34, meta3, body);
24131
24498
  return { id, created: true };
24132
24499
  }
24133
24500
  function readPlanSummary(ctx) {
@@ -24331,7 +24698,7 @@ function addIdeaStub(ctx) {
24331
24698
  const tags = args["tags"] ?? [];
24332
24699
  const category = args["category"] ?? "General";
24333
24700
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
24334
- const path33 = workspaceArtifact(ctx.rootDir, "decisions", id);
24701
+ const path34 = workspaceArtifact(ctx.rootDir, "decisions", id);
24335
24702
  const meta3 = {
24336
24703
  kind: "adr",
24337
24704
  status: "proposed",
@@ -24357,7 +24724,7 @@ function addIdeaStub(ctx) {
24357
24724
  ...consequences.map((c) => `- ${c}`),
24358
24725
  ""
24359
24726
  ].join("\n");
24360
- ctx.storage.write(path33, meta3, body);
24727
+ ctx.storage.write(path34, meta3, body);
24361
24728
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
24362
24729
  });
24363
24730
  }
@@ -24439,14 +24806,14 @@ function createDocumentStub(ctx) {
24439
24806
  ctx.storage.write(risksPath, riskMeta, content);
24440
24807
  return `Document "${title}" created at risks.md (workspace root).`;
24441
24808
  }
24442
- const path33 = workspaceArtifact(ctx.rootDir, "docs", slug);
24809
+ const path34 = workspaceArtifact(ctx.rootDir, "docs", slug);
24443
24810
  const meta3 = {
24444
24811
  kind: "doc",
24445
24812
  id: slug,
24446
24813
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
24447
24814
  tags
24448
24815
  };
24449
- ctx.storage.write(path33, meta3, content);
24816
+ ctx.storage.write(path34, meta3, content);
24450
24817
  return `Document "${title}" created at docs/${slug}.md.`;
24451
24818
  });
24452
24819
  }
@@ -24510,7 +24877,7 @@ function searchDocumentsStub(ctx) {
24510
24877
  const end = Math.min(raw.length, idx + matchLen + 40);
24511
24878
  const snippet = "\u2026" + raw.slice(start, end).replace(/\n/g, " ").trim() + "\u2026";
24512
24879
  results.push({
24513
- path: relative3(ctx.rootDir, file2) || basename2(file2),
24880
+ path: relative2(ctx.rootDir, file2) || basename2(file2),
24514
24881
  snippet
24515
24882
  });
24516
24883
  if (results.length >= limit) break;
@@ -24594,7 +24961,7 @@ var init_stubs = __esm({
24594
24961
  "src/cli/workspace/stubs.ts"() {
24595
24962
  "use strict";
24596
24963
  init_storage();
24597
- init_paths();
24964
+ init_paths2();
24598
24965
  init_storage();
24599
24966
  SEARCH_NUDGE_THRESHOLD = 5;
24600
24967
  }
@@ -24673,6 +25040,7 @@ __export(updater_exports, {
24673
25040
  compareSemver: () => compareSemver,
24674
25041
  fetchLatestVersion: () => fetchLatestVersion,
24675
25042
  getCurrentVersion: () => getCurrentVersion,
25043
+ looksLikeBrokenShim: () => looksLikeBrokenShim,
24676
25044
  performUpdate: () => performUpdate,
24677
25045
  resolveBundledNpmCli: () => resolveBundledNpmCli
24678
25046
  });
@@ -25157,7 +25525,7 @@ function hasWorkspacePlan(projectRoot = process.cwd()) {
25157
25525
  var init_planDetect = __esm({
25158
25526
  "src/cli/workspace/planDetect.ts"() {
25159
25527
  "use strict";
25160
- init_paths();
25528
+ init_paths2();
25161
25529
  }
25162
25530
  });
25163
25531
 
@@ -25250,10 +25618,10 @@ import { createHash as createHash3 } from "node:crypto";
25250
25618
  import { join as join17 } from "node:path";
25251
25619
  import { readFile as readFile2 } from "node:fs/promises";
25252
25620
  async function readPackageJson2(projectRoot) {
25253
- const path33 = join17(projectRoot, "package.json");
25254
- if (!existsSync20(path33)) return null;
25621
+ const path34 = join17(projectRoot, "package.json");
25622
+ if (!existsSync20(path34)) return null;
25255
25623
  try {
25256
- return JSON.parse(await readFile2(path33, "utf8"));
25624
+ return JSON.parse(await readFile2(path34, "utf8"));
25257
25625
  } catch {
25258
25626
  return null;
25259
25627
  }
@@ -25335,9 +25703,9 @@ async function genBuild(ctx) {
25335
25703
  ].join("\n");
25336
25704
  }
25337
25705
  async function genOpenQuestions(ctx) {
25338
- const path33 = join17(ctx.rootDir, "risks.md");
25339
- if (!existsSync20(path33)) return "_No open questions._";
25340
- const content = readFileSync19(path33, "utf8");
25706
+ const path34 = join17(ctx.rootDir, "risks.md");
25707
+ if (!existsSync20(path34)) return "_No open questions._";
25708
+ const content = readFileSync19(path34, "utf8");
25341
25709
  const lines = content.split("\n");
25342
25710
  const questions = [];
25343
25711
  let currentTitle = "";
@@ -25467,7 +25835,7 @@ var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
25467
25835
  var init_agentsMd = __esm({
25468
25836
  "src/cli/workspace/agentsMd.ts"() {
25469
25837
  "use strict";
25470
- init_paths();
25838
+ init_paths2();
25471
25839
  AUTO_SECTIONS = [
25472
25840
  "tech-stack",
25473
25841
  "decisions",
@@ -25900,8 +26268,8 @@ async function runPostCouncilHook(ctx, options) {
25900
26268
  sources: scope.sources
25901
26269
  } : void 0
25902
26270
  });
25903
- const path33 = writeCouncilCompletion(ctx.rootDir, completion);
25904
- completionHook = { ran: true, path: path33, completion };
26271
+ const path34 = writeCouncilCompletion(ctx.rootDir, completion);
26272
+ completionHook = { ran: true, path: path34, completion };
25905
26273
  } catch (err) {
25906
26274
  completionHook = {
25907
26275
  ran: true,
@@ -25954,7 +26322,7 @@ var init_buildLessonsSummary = __esm({
25954
26322
  "src/cli/workspace/buildLessonsSummary.ts"() {
25955
26323
  "use strict";
25956
26324
  init_council();
25957
- init_paths();
26325
+ init_paths2();
25958
26326
  }
25959
26327
  });
25960
26328
 
@@ -26794,32 +27162,214 @@ var init_prereqChecks = __esm({
26794
27162
  }
26795
27163
  });
26796
27164
 
27165
+ // src/cli/plugins/prefs.ts
27166
+ import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10 } from "node:fs";
27167
+ import path28 from "node:path";
27168
+ import os9 from "node:os";
27169
+ function getPluginPrefsPath() {
27170
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path28.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
27171
+ }
27172
+ function getPluginPrefs() {
27173
+ const file2 = getPluginPrefsPath();
27174
+ try {
27175
+ if (!existsSync26(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
27176
+ const raw = readFileSync23(file2, "utf-8");
27177
+ const parsed = JSON.parse(raw);
27178
+ if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
27179
+ const clean = {};
27180
+ for (const [k, v] of Object.entries(parsed.dontAskAgain)) {
27181
+ if (typeof k === "string" && k.length > 0 && v === true) clean[k] = true;
27182
+ }
27183
+ return { version: 1, dontAskAgain: clean };
27184
+ }
27185
+ } catch {
27186
+ }
27187
+ return { ...DEFAULTS2, dontAskAgain: {} };
27188
+ }
27189
+ function writePluginPrefs(prefs) {
27190
+ const file2 = getPluginPrefsPath();
27191
+ mkdirSync10(path28.dirname(file2), { recursive: true });
27192
+ writeFileSync15(file2, JSON.stringify(prefs, null, 2), {
27193
+ encoding: "utf-8",
27194
+ mode: 384
27195
+ });
27196
+ }
27197
+ function markDontAskAgain(pluginId) {
27198
+ try {
27199
+ const prefs = getPluginPrefs();
27200
+ prefs.dontAskAgain[pluginId] = true;
27201
+ writePluginPrefs(prefs);
27202
+ } catch {
27203
+ }
27204
+ }
27205
+ function isMuted(pluginId) {
27206
+ return getPluginPrefs().dontAskAgain[pluginId] === true;
27207
+ }
27208
+ var DEFAULTS2;
27209
+ var init_prefs = __esm({
27210
+ "src/cli/plugins/prefs.ts"() {
27211
+ "use strict";
27212
+ DEFAULTS2 = {
27213
+ version: 1,
27214
+ dontAskAgain: {}
27215
+ };
27216
+ }
27217
+ });
27218
+
27219
+ // src/cli/plugins/registry.ts
27220
+ var registry_exports = {};
27221
+ __export(registry_exports, {
27222
+ PLUGINS: () => PLUGINS,
27223
+ detectMissingPlugins: () => detectMissingPlugins,
27224
+ findPlugin: () => findPlugin
27225
+ });
27226
+ import { spawnSync as spawnSync3 } from "node:child_process";
27227
+ function detectLocalBin(bin) {
27228
+ return (cwd) => {
27229
+ try {
27230
+ const resolved = resolveBin(bin, cwd);
27231
+ return Promise.resolve(resolved !== bin);
27232
+ } catch {
27233
+ return Promise.resolve(false);
27234
+ }
27235
+ };
27236
+ }
27237
+ function detectGlobalBin(bin) {
27238
+ return () => {
27239
+ try {
27240
+ const res = spawnSync3(bin, ["--version"], {
27241
+ stdio: ["ignore", "pipe", "ignore"],
27242
+ shell: process.platform === "win32",
27243
+ timeout: 4e3
27244
+ });
27245
+ return Promise.resolve(res.status === 0 || res.stdout != null && res.stdout.toString().trim().length > 0 && res.error === void 0);
27246
+ } catch {
27247
+ return Promise.resolve(false);
27248
+ }
27249
+ };
27250
+ }
27251
+ async function detectPlaywright() {
27252
+ try {
27253
+ const mod = await defaultPlaywrightLoader();
27254
+ return mod !== null;
27255
+ } catch {
27256
+ return false;
27257
+ }
27258
+ }
27259
+ function binForProvider(name) {
27260
+ const p3 = DEFAULT_PROVIDERS.find((x) => x.name === name);
27261
+ if (!p3) throw new Error(`plugin registry: unknown diagnostic provider '${name}'`);
27262
+ return p3.bin;
27263
+ }
27264
+ function binForLspLanguage(language) {
27265
+ const s = LSP_SERVERS.find((x) => x.language === language);
27266
+ if (!s) throw new Error(`plugin registry: unknown LSP language '${language}'`);
27267
+ return s.bin;
27268
+ }
27269
+ async function detectMissingPlugins(cwd, opts = {}) {
27270
+ const missing = [];
27271
+ for (const spec of PLUGINS) {
27272
+ if (process.env[spec.featureGate] === "0") continue;
27273
+ if (!opts.includeMuted && isMuted(spec.id)) continue;
27274
+ let present = false;
27275
+ try {
27276
+ present = await spec.detect(cwd);
27277
+ } catch {
27278
+ present = false;
27279
+ }
27280
+ if (!present) missing.push(spec);
27281
+ }
27282
+ return missing;
27283
+ }
27284
+ function findPlugin(id) {
27285
+ return PLUGINS.find((p3) => p3.id === id);
27286
+ }
27287
+ var PLUGINS;
27288
+ var init_registry2 = __esm({
27289
+ "src/cli/plugins/registry.ts"() {
27290
+ "use strict";
27291
+ init_engine();
27292
+ init_driver();
27293
+ init_engine();
27294
+ init_servers();
27295
+ init_prefs();
27296
+ PLUGINS = [
27297
+ {
27298
+ id: "eslint",
27299
+ label: "ESLint (diagnostics for JS/TS)",
27300
+ npmPackage: "eslint",
27301
+ installScope: "dev",
27302
+ detect: detectLocalBin(binForProvider("eslint")),
27303
+ featureGate: "ZELARI_DIAGNOSTICS",
27304
+ description: "Enables the post-edit diagnostics loop for .js/.jsx/.ts/.tsx/.mjs/.cjs files."
27305
+ },
27306
+ {
27307
+ id: "ruff",
27308
+ label: "Ruff (diagnostics for Python)",
27309
+ npmPackage: "ruff",
27310
+ installScope: "dev",
27311
+ detect: detectLocalBin(binForProvider("ruff")),
27312
+ featureGate: "ZELARI_DIAGNOSTICS",
27313
+ description: "Enables the post-edit diagnostics loop for .py files."
27314
+ },
27315
+ {
27316
+ id: "playwright",
27317
+ label: "Playwright (browser_check tool)",
27318
+ npmPackage: "playwright",
27319
+ installScope: "dev",
27320
+ detect: () => detectPlaywright(),
27321
+ postInstallHint: "Then fetch the browser binary: `npx playwright install chromium`",
27322
+ featureGate: "ZELARI_BROWSER",
27323
+ description: "Powers the browser_check tool (URL probing, click/fill/wait, screenshots)."
27324
+ },
27325
+ {
27326
+ id: "typescript-language-server",
27327
+ label: "typescript-language-server (LSP for TS/JS)",
27328
+ npmPackage: "typescript-language-server",
27329
+ installScope: "global",
27330
+ detect: detectGlobalBin(binForLspLanguage("typescript")),
27331
+ featureGate: "ZELARI_LSP",
27332
+ description: "Powers go_to_definition / find_references / hover_type / rename_symbol for TS/JS."
27333
+ },
27334
+ {
27335
+ id: "pyright",
27336
+ label: "pyright (LSP for Python)",
27337
+ npmPackage: "pyright",
27338
+ installScope: "global",
27339
+ detect: detectGlobalBin(binForLspLanguage("python")),
27340
+ featureGate: "ZELARI_LSP",
27341
+ description: "Powers go_to_definition / find_references / hover_type / rename_symbol for Python."
27342
+ }
27343
+ ];
27344
+ }
27345
+ });
27346
+
26797
27347
  // src/cli/utils/doctor.ts
26798
27348
  var doctor_exports = {};
26799
27349
  __export(doctor_exports, {
26800
27350
  runDoctor: () => runDoctor
26801
27351
  });
26802
27352
  import { execSync as execSync2 } from "node:child_process";
26803
- import { existsSync as existsSync30, readFileSync as readFileSync25, readlinkSync, statSync as statSync6 } from "node:fs";
27353
+ import { existsSync as existsSync31, readFileSync as readFileSync26, readlinkSync, statSync as statSync6 } from "node:fs";
26804
27354
  import { createRequire as createRequire2 } from "node:module";
26805
27355
  import { fileURLToPath as fileURLToPath2 } from "node:url";
26806
- import path32 from "node:path";
27356
+ import path33 from "node:path";
26807
27357
  function findPackageRoot(start) {
26808
27358
  let dir = start;
26809
27359
  for (let i = 0; i < 6; i += 1) {
26810
- const candidate = path32.join(dir, "package.json");
26811
- if (existsSync30(candidate)) {
27360
+ const candidate = path33.join(dir, "package.json");
27361
+ if (existsSync31(candidate)) {
26812
27362
  try {
26813
- const pkg = JSON.parse(readFileSync25(candidate, "utf8"));
27363
+ const pkg = JSON.parse(readFileSync26(candidate, "utf8"));
26814
27364
  if (pkg.name === "zelari-code") return dir;
26815
27365
  } catch {
26816
27366
  }
26817
27367
  }
26818
- const parent = path32.dirname(dir);
27368
+ const parent = path33.dirname(dir);
26819
27369
  if (parent === dir) break;
26820
27370
  dir = parent;
26821
27371
  }
26822
- return path32.resolve(__dirname3, "..", "..", "..");
27372
+ return path33.resolve(__dirname3, "..", "..", "..");
26823
27373
  }
26824
27374
  function tryExec(cmd) {
26825
27375
  try {
@@ -26833,8 +27383,8 @@ function tryExec(cmd) {
26833
27383
  }
26834
27384
  function readPackageJson3() {
26835
27385
  try {
26836
- const pkgPath = path32.join(packageRoot, "package.json");
26837
- return JSON.parse(readFileSync25(pkgPath, "utf8"));
27386
+ const pkgPath = path33.join(packageRoot, "package.json");
27387
+ return JSON.parse(readFileSync26(pkgPath, "utf8"));
26838
27388
  } catch {
26839
27389
  return null;
26840
27390
  }
@@ -26849,8 +27399,8 @@ function checkShim(pkgName) {
26849
27399
  }
26850
27400
  const isWin = process.platform === "win32";
26851
27401
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
26852
- const shimPath = path32.join(prefix, shimName);
26853
- if (!existsSync30(shimPath)) {
27402
+ const shimPath = path33.join(prefix, shimName);
27403
+ if (!existsSync31(shimPath)) {
26854
27404
  return FAIL(
26855
27405
  `shim not found at ${shimPath}
26856
27406
  fix: npm install -g ${pkgName}@latest --force`
@@ -26859,7 +27409,7 @@ function checkShim(pkgName) {
26859
27409
  try {
26860
27410
  const st = statSync6(shimPath);
26861
27411
  if (isWin) {
26862
- const content = readFileSync25(shimPath, "utf8");
27412
+ const content = readFileSync26(shimPath, "utf8");
26863
27413
  if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
26864
27414
  return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
26865
27415
  }
@@ -26877,8 +27427,8 @@ function checkShim(pkgName) {
26877
27427
  fix: npm install -g ${pkgName}@latest --force`
26878
27428
  );
26879
27429
  }
26880
- const resolved = path32.resolve(path32.dirname(shimPath), target);
26881
- const expected = path32.join(
27430
+ const resolved = path33.resolve(path33.dirname(shimPath), target);
27431
+ const expected = path33.join(
26882
27432
  prefix,
26883
27433
  "node_modules",
26884
27434
  pkgName,
@@ -26917,8 +27467,8 @@ function checkNode(pkg) {
26917
27467
  return OK(`node ${raw}`);
26918
27468
  }
26919
27469
  function checkBundle() {
26920
- const bundle = path32.join(packageRoot, "dist", "cli", "main.bundled.js");
26921
- if (!existsSync30(bundle)) {
27470
+ const bundle = path33.join(packageRoot, "dist", "cli", "main.bundled.js");
27471
+ if (!existsSync31(bundle)) {
26922
27472
  return FAIL(
26923
27473
  `dist/cli/main.bundled.js missing at ${bundle}
26924
27474
  fix: npm run build:cli (then reinstall or run via tsx)`
@@ -26938,7 +27488,7 @@ function checkRuntimeDeps() {
26938
27488
  const missing = [];
26939
27489
  for (const dep of required2) {
26940
27490
  try {
26941
- const localReq = createRequire2(path32.join(packageRoot, "package.json"));
27491
+ const localReq = createRequire2(path33.join(packageRoot, "package.json"));
26942
27492
  localReq.resolve(dep);
26943
27493
  } catch {
26944
27494
  missing.push(dep);
@@ -26962,18 +27512,37 @@ function checkPath() {
26962
27512
  if (pathDirs.includes(prefix)) {
26963
27513
  return OK(`PATH includes npm prefix (${prefix})`);
26964
27514
  }
27515
+ const winHint = ` fix: zelari-code --fix-path (adds ${prefix} to the user PATH)
27516
+ then open a NEW terminal and run \`zelari-code --version\`
27517
+ if it persists: see scripts/diagnose-path.ps1 in the repo`;
27518
+ const posixHint = ` fix: export PATH="${prefix}/bin:$PATH"
27519
+ (add it to ~/.bashrc / ~/.zshrc to persist)`;
26965
27520
  return WARN(
26966
27521
  `PATH does not include npm prefix (${prefix})
26967
27522
  symptom: "zelari-code: command not found" after install
26968
- fix (POSIX): export PATH="$(npm prefix -g)/bin:$PATH"
26969
- fix (Windows): $env:Path = "$(npm prefix -g);$env:Path"`
27523
+ ` + (process.platform === "win32" ? winHint : posixHint)
26970
27524
  );
26971
27525
  }
26972
27526
  function prereqToCheckResult(r) {
26973
27527
  if (r.ok) return OK(r.message);
26974
27528
  return r.severity === "critical" ? FAIL(r.message, "critical") : WARN(r.message);
26975
27529
  }
26976
- function runDoctor() {
27530
+ async function checkOptionalPlugins() {
27531
+ const { detectMissingPlugins: detectMissingPlugins2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
27532
+ let missing;
27533
+ try {
27534
+ missing = await detectMissingPlugins2(packageRoot, { includeMuted: true });
27535
+ } catch {
27536
+ return WARN("could not detect optional plugins");
27537
+ }
27538
+ if (missing.length === 0) return OK("all optional plugins available");
27539
+ const lines = missing.map((p3) => `${p3.id} missing \u2014 /plugins install ${p3.id}`);
27540
+ return WARN(
27541
+ `${missing.length} optional plugin(s) missing (features degrade silently without them):
27542
+ ` + lines.map((l) => ` ${l}`).join("\n") + "\n install via: /plugins (or the boot prompt)"
27543
+ );
27544
+ }
27545
+ async function runDoctor() {
26977
27546
  const pkg = readPackageJson3();
26978
27547
  const pkgName = pkg?.name ?? "zelari-code";
26979
27548
  const checks = [
@@ -26990,7 +27559,13 @@ function runDoctor() {
26990
27559
  // invisible to Git Bash) that silently breaks council builds.
26991
27560
  { name: "node (agent shell)", run: () => prereqToCheckResult(checkAgentNode()) },
26992
27561
  { name: "git (agent shell)", run: () => prereqToCheckResult(checkAgentGit()) },
26993
- { name: "bash", run: () => prereqToCheckResult(checkAgentBash()) }
27562
+ { name: "bash", run: () => prereqToCheckResult(checkAgentBash()) },
27563
+ // --- optional tool plugins (v1.5.0) ---
27564
+ // Playwright / eslint / ruff / LSP servers. WARN-only (never critical):
27565
+ // these power edge features that degrade silently when absent. Surfaced
27566
+ // here so `zelari-code --doctor` tells the user what's missing + how to
27567
+ // install (`/plugins` or the boot PluginGate).
27568
+ { name: "plugins", run: () => checkOptionalPlugins() }
26994
27569
  ];
26995
27570
  console.log(`zelari-code doctor (v${pkg?.version ?? "unknown"})`);
26996
27571
  console.log("platform:", process.platform, process.arch);
@@ -27003,7 +27578,7 @@ function runDoctor() {
27003
27578
  for (const c of checks) {
27004
27579
  let result;
27005
27580
  try {
27006
- result = c.run();
27581
+ result = await c.run();
27007
27582
  } catch (err) {
27008
27583
  result = FAIL(
27009
27584
  `unexpected error: ${err instanceof Error ? err.message : String(err)}`
@@ -27034,7 +27609,7 @@ var init_doctor = __esm({
27034
27609
  "use strict";
27035
27610
  init_prereqChecks();
27036
27611
  require3 = createRequire2(import.meta.url);
27037
- __dirname3 = path32.dirname(fileURLToPath2(import.meta.url));
27612
+ __dirname3 = path33.dirname(fileURLToPath2(import.meta.url));
27038
27613
  packageRoot = findPackageRoot(__dirname3);
27039
27614
  OK = (message) => ({
27040
27615
  ok: true,
@@ -27054,8 +27629,87 @@ var init_doctor = __esm({
27054
27629
  }
27055
27630
  });
27056
27631
 
27632
+ // src/cli/utils/fixPath.ts
27633
+ var fixPath_exports = {};
27634
+ __export(fixPath_exports, {
27635
+ repairWindowsUserPath: () => repairWindowsUserPath
27636
+ });
27637
+ import { spawnSync as spawnSync4 } from "node:child_process";
27638
+ function getGlobalPrefix2() {
27639
+ return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
27640
+ try {
27641
+ return spawnSync4("npm", ["prefix", "-g"], {
27642
+ encoding: "utf8",
27643
+ stdio: ["ignore", "pipe", "ignore"]
27644
+ }).stdout?.trim() ?? "";
27645
+ } catch {
27646
+ return "";
27647
+ }
27648
+ })();
27649
+ }
27650
+ function powershell(script) {
27651
+ try {
27652
+ const res = spawnSync4(
27653
+ "powershell.exe",
27654
+ ["-NoProfile", "-NonInteractive", "-Command", script],
27655
+ { encoding: "utf8" }
27656
+ );
27657
+ if (res.status === 0) return (res.stdout || "").trim();
27658
+ return "";
27659
+ } catch {
27660
+ return "";
27661
+ }
27662
+ }
27663
+ function repairWindowsUserPath() {
27664
+ const prefix = getGlobalPrefix2();
27665
+ if (!prefix) {
27666
+ return {
27667
+ ok: false,
27668
+ prefix: "",
27669
+ error: "could not detect the npm global prefix (run inside an npm context, or set npm_config_prefix)"
27670
+ };
27671
+ }
27672
+ if (process.platform !== "win32") {
27673
+ return {
27674
+ ok: false,
27675
+ prefix,
27676
+ error: `--fix-path is Windows-only. On ${process.platform}, add to your shell profile:
27677
+ export PATH="${prefix}/bin:$PATH"`
27678
+ };
27679
+ }
27680
+ const userPath = powershell(
27681
+ "[Environment]::GetEnvironmentVariable('Path','User')"
27682
+ );
27683
+ const norm = (p3) => p3.toLowerCase().replace(/\/+/g, "\\").replace(/\\+$/, "");
27684
+ const entries = userPath.split(";").map((e) => e.trim()).filter(Boolean);
27685
+ if (entries.some((e) => norm(e) === norm(prefix))) {
27686
+ return { ok: true, alreadyOk: true, prefix };
27687
+ }
27688
+ const updated = userPath === "" || userPath.endsWith(";") ? `${userPath}${prefix}` : `${userPath};${prefix}`;
27689
+ const writeRes = powershell(
27690
+ `[Environment]::SetEnvironmentVariable('Path', ${JSON.stringify(updated)}, 'User')`
27691
+ );
27692
+ const reread = powershell(
27693
+ "[Environment]::GetEnvironmentVariable('Path','User')"
27694
+ );
27695
+ const rereadEntries = reread.split(";").map((e) => e.trim()).filter(Boolean);
27696
+ if (rereadEntries.some((e) => norm(e) === norm(prefix))) {
27697
+ return { ok: true, alreadyOk: false, prefix };
27698
+ }
27699
+ return {
27700
+ ok: false,
27701
+ prefix,
27702
+ error: writeRes === null && reread === userPath ? "PowerShell write did not take effect (permission denied? run as the same user that owns the install)" : "PATH write failed for an unknown reason"
27703
+ };
27704
+ }
27705
+ var init_fixPath = __esm({
27706
+ "src/cli/utils/fixPath.ts"() {
27707
+ "use strict";
27708
+ }
27709
+ });
27710
+
27057
27711
  // src/cli/main.ts
27058
- import React13 from "react";
27712
+ import React14 from "react";
27059
27713
  import { render } from "ink";
27060
27714
 
27061
27715
  // src/cli/app.tsx
@@ -27988,16 +28642,8 @@ function useExecutionTimer(busy, tickMs = 1e3) {
27988
28642
  return { elapsedMs, lastMs };
27989
28643
  }
27990
28644
 
27991
- // src/cli/utils/paths.ts
27992
- import { homedir as homedir2 } from "node:os";
27993
- function shortenCwd(p3, maxLen = 40, home = homedir2()) {
27994
- let out = p3;
27995
- if (home && (out === home || out.startsWith(`${home}\\`) || out.startsWith(`${home}/`))) {
27996
- out = `~${out.slice(home.length)}`;
27997
- }
27998
- if (out.length <= maxLen) return out;
27999
- return `\u2026${out.slice(-(maxLen - 1))}`;
28000
- }
28645
+ // src/cli/app.tsx
28646
+ init_paths();
28001
28647
 
28002
28648
  // packages/core/dist/agents/skills/builtin/debugging.js
28003
28649
  init_skills();
@@ -29991,14 +30637,14 @@ import { useState as useState5, useRef as useRef3, useCallback, useEffect as use
29991
30637
  // src/cli/sessionManager.ts
29992
30638
  init_harness();
29993
30639
  import { promises as fs9, existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync, statSync } from "node:fs";
29994
- import path11 from "node:path";
30640
+ import path12 from "node:path";
29995
30641
  import os4 from "node:os";
29996
30642
  import { randomUUID } from "node:crypto";
29997
30643
  function getSessionBaseDir() {
29998
- return process.env.ANATHEMA_SESSIONS_DIR ?? path11.join(os4.homedir(), ".tmp", "zelari-code", "sessions");
30644
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path12.join(os4.homedir(), ".tmp", "zelari-code", "sessions");
29999
30645
  }
30000
30646
  function getCurrentSessionFile() {
30001
- return process.env.ANATHEMA_CURRENT_SESSION_FILE ?? path11.join(os4.homedir(), ".tmp", "zelari-code", "current.txt");
30647
+ return process.env.ANATHEMA_CURRENT_SESSION_FILE ?? path12.join(os4.homedir(), ".tmp", "zelari-code", "current.txt");
30002
30648
  }
30003
30649
  async function ensureSessionDir() {
30004
30650
  await fs9.mkdir(getSessionBaseDir(), { recursive: true });
@@ -30015,7 +30661,7 @@ function getCurrentSessionId() {
30015
30661
  }
30016
30662
  function setCurrentSessionId(id) {
30017
30663
  const file2 = getCurrentSessionFile();
30018
- mkdirSync4(path11.dirname(file2), { recursive: true });
30664
+ mkdirSync4(path12.dirname(file2), { recursive: true });
30019
30665
  writeFileSync4(file2, id, "utf-8");
30020
30666
  }
30021
30667
  function clearCurrentSessionId() {
@@ -30027,7 +30673,7 @@ function clearCurrentSessionId() {
30027
30673
  }
30028
30674
  }
30029
30675
  function getCurrentBranchFile() {
30030
- return process.env.ANATHEMA_CURRENT_BRANCH_FILE ?? path11.join(path11.dirname(getCurrentSessionFile()), "currentBranch.txt");
30676
+ return process.env.ANATHEMA_CURRENT_BRANCH_FILE ?? path12.join(path12.dirname(getCurrentSessionFile()), "currentBranch.txt");
30031
30677
  }
30032
30678
  function getCurrentBranch() {
30033
30679
  const file2 = getCurrentBranchFile();
@@ -30041,7 +30687,7 @@ function getCurrentBranch() {
30041
30687
  }
30042
30688
  function setCurrentBranch(name) {
30043
30689
  const file2 = getCurrentBranchFile();
30044
- mkdirSync4(path11.dirname(file2), { recursive: true });
30690
+ mkdirSync4(path12.dirname(file2), { recursive: true });
30045
30691
  writeFileSync4(file2, name, "utf-8");
30046
30692
  }
30047
30693
  function newSessionId() {
@@ -30060,7 +30706,7 @@ async function listSessions() {
30060
30706
  for (const entry of entries) {
30061
30707
  if (!entry.endsWith(".jsonl")) continue;
30062
30708
  const id = entry.replace(/\.jsonl$/, "");
30063
- const filePath = path11.join(baseDir, entry);
30709
+ const filePath = path12.join(baseDir, entry);
30064
30710
  try {
30065
30711
  const events = await readSession(filePath);
30066
30712
  let firstTs = 0;
@@ -30107,7 +30753,7 @@ ${lines.join("\n")}`
30107
30753
  return { message: `[${kind}] handled` };
30108
30754
  }
30109
30755
  async function loadSessionEvents(id) {
30110
- const filePath = path11.join(getSessionBaseDir(), `${id}.jsonl`);
30756
+ const filePath = path12.join(getSessionBaseDir(), `${id}.jsonl`);
30111
30757
  return readSession(filePath);
30112
30758
  }
30113
30759
 
@@ -30399,15 +31045,15 @@ import { useState as useState6, useRef as useRef4, useCallback as useCallback2 }
30399
31045
 
30400
31046
  // src/cli/metrics.ts
30401
31047
  import { promises as fs10, existsSync as existsSync6, statSync as statSync2, renameSync, appendFileSync, mkdirSync as mkdirSync5 } from "node:fs";
30402
- import path12 from "node:path";
31048
+ import path13 from "node:path";
30403
31049
  import os5 from "node:os";
30404
31050
  var METRICS_ROTATE_BYTES = 10 * 1024 * 1024;
30405
31051
  var MetricsLogger = class {
30406
31052
  file;
30407
31053
  writeQueue = Promise.resolve();
30408
31054
  constructor(file2) {
30409
- this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path12.join(os5.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
30410
- mkdirSync5(path12.dirname(this.file), { recursive: true });
31055
+ this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path13.join(os5.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
31056
+ mkdirSync5(path13.dirname(this.file), { recursive: true });
30411
31057
  }
30412
31058
  /** Fire-and-forget record append. */
30413
31059
  record(rec) {
@@ -30572,7 +31218,7 @@ init_diff();
30572
31218
  init_web();
30573
31219
 
30574
31220
  // src/cli/safety/sandboxPath.ts
30575
- import path13 from "node:path";
31221
+ import path14 from "node:path";
30576
31222
  var SandboxViolationError = class extends Error {
30577
31223
  constructor(message, attemptedPath, resolvedPath) {
30578
31224
  super(message);
@@ -30585,9 +31231,9 @@ function resolveSandboxedPath(userPath, options = {}) {
30585
31231
  if (typeof userPath !== "string" || userPath.length === 0) {
30586
31232
  throw new SandboxViolationError("Empty path", userPath, "");
30587
31233
  }
30588
- const root = path13.resolve(options.root ?? process.cwd());
30589
- const resolved = path13.isAbsolute(userPath) ? path13.resolve(userPath) : path13.resolve(root, userPath);
30590
- const rootWithSep = root.endsWith(path13.sep) ? root : root + path13.sep;
31234
+ const root = path14.resolve(options.root ?? process.cwd());
31235
+ const resolved = path14.isAbsolute(userPath) ? path14.resolve(userPath) : path14.resolve(root, userPath);
31236
+ const rootWithSep = root.endsWith(path14.sep) ? root : root + path14.sep;
30591
31237
  if (resolved !== root && !resolved.startsWith(rootWithSep)) {
30592
31238
  throw new SandboxViolationError(
30593
31239
  `Path escapes sandbox root: ${userPath} \u2192 ${resolved} (root: ${root})`,
@@ -30651,7 +31297,7 @@ function assertShellAllowed(command) {
30651
31297
 
30652
31298
  // src/cli/safety/auditLogger.ts
30653
31299
  import { promises as fs11 } from "node:fs";
30654
- import path14 from "node:path";
31300
+ import path15 from "node:path";
30655
31301
  import os6 from "node:os";
30656
31302
  var AuditLogger = class {
30657
31303
  logPath;
@@ -30670,7 +31316,7 @@ var AuditLogger = class {
30670
31316
  async append(entry) {
30671
31317
  const line = JSON.stringify(entry) + "\n";
30672
31318
  this.writeQueue = this.writeQueue.then(async () => {
30673
- await fs11.mkdir(path14.dirname(this.logPath), { recursive: true });
31319
+ await fs11.mkdir(path15.dirname(this.logPath), { recursive: true });
30674
31320
  await fs11.appendFile(this.logPath, line, "utf-8");
30675
31321
  });
30676
31322
  return this.writeQueue;
@@ -30714,7 +31360,7 @@ var AuditLogger = class {
30714
31360
  function defaultAuditPath() {
30715
31361
  const override = process.env.ANATHEMA_AUDIT_LOG;
30716
31362
  if (override && override.trim().length > 0) return override;
30717
- return path14.join(os6.tmpdir(), "zelari-code", "audit.jsonl");
31363
+ return path15.join(os6.tmpdir(), "zelari-code", "audit.jsonl");
30718
31364
  }
30719
31365
  function redactArgs(args) {
30720
31366
  const redacted = {};
@@ -30737,181 +31383,8 @@ function safeStringify(value) {
30737
31383
  }
30738
31384
  }
30739
31385
 
30740
- // src/cli/diagnostics/engine.ts
30741
- import { spawn as spawn2 } from "node:child_process";
30742
- import { existsSync as existsSync7 } from "node:fs";
30743
- import path15 from "node:path";
30744
- function parseEslintJson(stdout, _file2) {
30745
- const json2 = safeJson(stdout);
30746
- if (!Array.isArray(json2)) return [];
30747
- const out = [];
30748
- for (const fileResult of json2) {
30749
- if (!fileResult || typeof fileResult !== "object") continue;
30750
- const fr = fileResult;
30751
- const filePath = typeof fr.filePath === "string" ? fr.filePath : _file2;
30752
- if (!Array.isArray(fr.messages)) continue;
30753
- for (const m of fr.messages) {
30754
- if (!m || typeof m !== "object") continue;
30755
- const msg = m;
30756
- out.push({
30757
- file: filePath,
30758
- line: typeof msg.line === "number" ? msg.line : 0,
30759
- ...typeof msg.column === "number" ? { column: msg.column } : {},
30760
- // ESLint severity: 2 = error, 1 = warning.
30761
- severity: msg.severity === 2 ? "error" : "warning",
30762
- message: typeof msg.message === "string" ? msg.message : "(no message)",
30763
- ...typeof msg.ruleId === "string" && msg.ruleId ? { code: msg.ruleId } : {},
30764
- source: "eslint"
30765
- });
30766
- }
30767
- }
30768
- return out;
30769
- }
30770
- function parseRuffJson(stdout, _file2) {
30771
- const json2 = safeJson(stdout);
30772
- if (!Array.isArray(json2)) return [];
30773
- const out = [];
30774
- for (const issue2 of json2) {
30775
- if (!issue2 || typeof issue2 !== "object") continue;
30776
- const it = issue2;
30777
- const code = typeof it.code === "string" ? it.code : void 0;
30778
- out.push({
30779
- file: typeof it.filename === "string" ? it.filename : _file2,
30780
- line: typeof it.location?.row === "number" ? it.location.row : 0,
30781
- ...typeof it.location?.column === "number" ? { column: it.location.column } : {},
30782
- // Ruff has no severity field; E999 is a syntax error, everything else
30783
- // is a lint warning.
30784
- severity: code === "E999" ? "error" : "warning",
30785
- message: typeof it.message === "string" ? it.message : "(no message)",
30786
- ...code ? { code } : {},
30787
- source: "ruff"
30788
- });
30789
- }
30790
- return out;
30791
- }
30792
- function safeJson(s) {
30793
- const trimmed = s.trim();
30794
- if (!trimmed) return null;
30795
- try {
30796
- return JSON.parse(trimmed);
30797
- } catch {
30798
- return null;
30799
- }
30800
- }
30801
- var DEFAULT_PROVIDERS = [
30802
- {
30803
- name: "eslint",
30804
- bin: "eslint",
30805
- extensions: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"],
30806
- args: (file2) => ["--format", "json", file2],
30807
- parse: parseEslintJson
30808
- },
30809
- {
30810
- name: "ruff",
30811
- bin: "ruff",
30812
- extensions: [".py"],
30813
- args: (file2) => ["check", "--output-format", "json", file2],
30814
- parse: parseRuffJson
30815
- }
30816
- ];
30817
- function providerForFile(file2, providers = DEFAULT_PROVIDERS) {
30818
- const ext = path15.extname(file2).toLowerCase();
30819
- return providers.find((p3) => p3.extensions.includes(ext)) ?? null;
30820
- }
30821
- function resolveBin(bin, cwd) {
30822
- const suffixes = process.platform === "win32" ? [".cmd", ".exe", ""] : [""];
30823
- let dir = cwd;
30824
- for (let i = 0; i < 6; i += 1) {
30825
- for (const suffix of suffixes) {
30826
- const candidate = path15.join(dir, "node_modules", ".bin", `${bin}${suffix}`);
30827
- if (existsSync7(candidate)) return candidate;
30828
- }
30829
- const parent = path15.dirname(dir);
30830
- if (parent === dir) break;
30831
- dir = parent;
30832
- }
30833
- return bin;
30834
- }
30835
- var defaultRunner = (cmd, args, opts) => new Promise((resolve) => {
30836
- let stdout = "";
30837
- let stderr = "";
30838
- let settled = false;
30839
- const done = (r) => {
30840
- if (settled) return;
30841
- settled = true;
30842
- resolve(r);
30843
- };
30844
- let child;
30845
- try {
30846
- child = process.platform === "win32" ? spawn2(`${cmd} ${args.join(" ")}`, { cwd: opts.cwd, shell: true }) : spawn2(cmd, args, { cwd: opts.cwd });
30847
- } catch {
30848
- done({ code: null, stdout: "", stderr: "" });
30849
- return;
30850
- }
30851
- const timer = setTimeout(() => {
30852
- try {
30853
- child.kill();
30854
- } catch {
30855
- }
30856
- done({ code: null, stdout, stderr });
30857
- }, opts.timeoutMs);
30858
- child.stdout?.on("data", (c) => {
30859
- stdout += c.toString();
30860
- });
30861
- child.stderr?.on("data", (c) => {
30862
- stderr += c.toString();
30863
- });
30864
- child.on("error", () => {
30865
- clearTimeout(timer);
30866
- done({ code: null, stdout: "", stderr: "" });
30867
- });
30868
- child.on("close", (code) => {
30869
- clearTimeout(timer);
30870
- done({ code, stdout, stderr });
30871
- });
30872
- });
30873
- async function runDiagnosticsForFile(file2, options = {}) {
30874
- const provider = providerForFile(file2, options.providers);
30875
- if (!provider) return [];
30876
- const cwd = options.cwd ?? process.cwd();
30877
- const timeoutMs = options.timeoutMs ?? 5e3;
30878
- const runner = options.runner ?? defaultRunner;
30879
- try {
30880
- const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
30881
- const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
30882
- return provider.parse(result.stdout, file2);
30883
- } catch {
30884
- return [];
30885
- }
30886
- }
30887
- function formatDiagnostics(diagnostics, opts = {}) {
30888
- if (diagnostics.length === 0) return "";
30889
- const maxLines = opts.maxLines ?? 20;
30890
- const rank = { error: 0, warning: 1, info: 2 };
30891
- const sorted = [...diagnostics].sort(
30892
- (a, b) => rank[a.severity] - rank[b.severity] || a.line - b.line
30893
- );
30894
- const errors = sorted.filter((d) => d.severity === "error").length;
30895
- const warnings = sorted.filter((d) => d.severity === "warning").length;
30896
- const header = `\u26A0 ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"} (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) \u2014 fix before continuing:`;
30897
- const shown = sorted.slice(0, maxLines).map((d) => {
30898
- const loc = opts.relativeTo ? relative(opts.relativeTo, d.file) : d.file;
30899
- const pos = d.column ? `${d.line}:${d.column}` : `${d.line}`;
30900
- const tag = d.severity === "error" ? "error" : d.severity === "warning" ? "warn" : "info";
30901
- const code = d.code ? ` [${d.code}]` : "";
30902
- return ` ${loc}:${pos} ${tag}${code}: ${d.message} (${d.source})`;
30903
- });
30904
- const overflow = sorted.length > maxLines ? [` \u2026 and ${sorted.length - maxLines} more`] : [];
30905
- return [header, ...shown, ...overflow].join("\n");
30906
- }
30907
- function relative(from, to) {
30908
- try {
30909
- const rel2 = path15.relative(from, to);
30910
- return rel2 && !rel2.startsWith("..") ? rel2 : to;
30911
- } catch {
30912
- return to;
30913
- }
30914
- }
31386
+ // src/cli/toolRegistry.ts
31387
+ init_engine();
30915
31388
 
30916
31389
  // src/cli/tools/taskTool.ts
30917
31390
  init_zod();
@@ -31018,7 +31491,6 @@ function createTaskTool(deps) {
31018
31491
  // src/cli/lsp/tools.ts
31019
31492
  init_zod();
31020
31493
  init_toolTypes();
31021
- import path16 from "node:path";
31022
31494
 
31023
31495
  // src/cli/lsp/protocol.ts
31024
31496
  function encodeMessage(message) {
@@ -31071,21 +31543,14 @@ function uriToPath(uri) {
31071
31543
  }
31072
31544
 
31073
31545
  // src/cli/lsp/tools.ts
31546
+ init_paths();
31074
31547
  function fmtLocation(loc, relativeTo) {
31075
31548
  const file2 = uriToPath(loc.uri);
31076
- const rel2 = relativeTo ? relPath(relativeTo, file2) : file2;
31549
+ const rel2 = relativeTo ? relativePosix(relativeTo, file2) : file2;
31077
31550
  const line = (loc.range?.start?.line ?? 0) + 1;
31078
31551
  const col = (loc.range?.start?.character ?? 0) + 1;
31079
31552
  return `${rel2}:${line}:${col}`;
31080
31553
  }
31081
- function relPath(from, to) {
31082
- try {
31083
- const r = path16.relative(from, to);
31084
- return r && !r.startsWith("..") ? r : to;
31085
- } catch {
31086
- return to;
31087
- }
31088
- }
31089
31554
  var PosArgs = external_exports.object({
31090
31555
  path: external_exports.string().min(1).describe("File path (relative to the project root or absolute)."),
31091
31556
  line: external_exports.number().int().positive().describe("1-based line number of the symbol."),
@@ -31162,7 +31627,7 @@ function createLspTools(provider, root = process.cwd()) {
31162
31627
  }
31163
31628
  return typedOk({
31164
31629
  totalEdits: result.totalEdits,
31165
- files: result.files.map((f) => `${relPath(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
31630
+ files: result.files.map((f) => `${relativePosix(root, f.file)} (${f.count} edit${f.count === 1 ? "" : "s"})`)
31166
31631
  });
31167
31632
  }
31168
31633
  };
@@ -31245,66 +31710,8 @@ var LspClient = class {
31245
31710
  }
31246
31711
  };
31247
31712
 
31248
- // src/cli/lsp/servers.ts
31249
- import path17 from "node:path";
31250
- var LSP_SERVERS = [
31251
- {
31252
- language: "typescript",
31253
- bin: "typescript-language-server",
31254
- args: ["--stdio"],
31255
- extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
31256
- },
31257
- {
31258
- language: "python",
31259
- bin: "pyright-langserver",
31260
- args: ["--stdio"],
31261
- extensions: [".py"]
31262
- },
31263
- {
31264
- language: "go",
31265
- bin: "gopls",
31266
- args: [],
31267
- extensions: [".go"]
31268
- },
31269
- {
31270
- language: "rust",
31271
- bin: "rust-analyzer",
31272
- args: [],
31273
- extensions: [".rs"]
31274
- }
31275
- ];
31276
- function languageIdForFile(file2) {
31277
- const ext = path17.extname(file2).toLowerCase();
31278
- const map2 = {
31279
- ".ts": "typescript",
31280
- ".tsx": "typescriptreact",
31281
- ".js": "javascript",
31282
- ".jsx": "javascriptreact",
31283
- ".mjs": "javascript",
31284
- ".cjs": "javascript",
31285
- ".py": "python",
31286
- ".go": "go",
31287
- ".rs": "rust"
31288
- };
31289
- return map2[ext] ?? "plaintext";
31290
- }
31291
- function serverForFile(file2, servers = LSP_SERVERS) {
31292
- const ext = path17.extname(file2).toLowerCase();
31293
- return servers.find((s) => s.extensions.includes(ext)) ?? null;
31294
- }
31295
- function resolveServerCommand(file2, cwd, servers = LSP_SERVERS) {
31296
- const spec = serverForFile(file2, servers);
31297
- if (!spec) return null;
31298
- const command = resolveBin(spec.bin, cwd);
31299
- return {
31300
- language: spec.language,
31301
- command,
31302
- args: spec.args,
31303
- resolved: command !== spec.bin
31304
- };
31305
- }
31306
-
31307
31713
  // src/cli/lsp/manager.ts
31714
+ init_servers();
31308
31715
  function processTransport(child) {
31309
31716
  return {
31310
31717
  send: (data) => {
@@ -32027,103 +32434,9 @@ function createSemanticTool(deps) {
32027
32434
  // src/cli/browser/tools.ts
32028
32435
  init_zod();
32029
32436
  init_toolTypes();
32437
+ init_driver();
32030
32438
  import path21 from "node:path";
32031
32439
  import os7 from "node:os";
32032
-
32033
- // src/cli/browser/driver.ts
32034
- var defaultPlaywrightLoader = async () => {
32035
- try {
32036
- const pkg = "playwright";
32037
- const mod = await import(pkg);
32038
- if (mod && mod.chromium && typeof mod.chromium.launch === "function") return mod;
32039
- return null;
32040
- } catch {
32041
- return null;
32042
- }
32043
- };
32044
- async function runBrowserCheck(options, loader = defaultPlaywrightLoader) {
32045
- const consoleErrors = [];
32046
- const pageErrors = [];
32047
- const failedRequests = [];
32048
- const base = { ok: false, consoleErrors, pageErrors, failedRequests };
32049
- const pw = await loader();
32050
- if (!pw) {
32051
- return {
32052
- ...base,
32053
- error: "browser automation unavailable \u2014 install Playwright (`npm i -D playwright && npx playwright install chromium`) to enable browser_check"
32054
- };
32055
- }
32056
- const timeout = options.timeoutMs ?? 15e3;
32057
- let browser;
32058
- try {
32059
- browser = await pw.chromium.launch({ headless: true });
32060
- const page = await browser.newPage();
32061
- page.on("console", (msg) => {
32062
- if (msg.type() === "error") consoleErrors.push(msg.text());
32063
- });
32064
- page.on("pageerror", (err) => pageErrors.push(err.message));
32065
- page.on("requestfailed", (req) => {
32066
- const f = req.failure();
32067
- failedRequests.push(`${req.url()}${f ? ` (${f.errorText})` : ""}`);
32068
- });
32069
- await page.goto(options.url, { waitUntil: "load", timeout });
32070
- for (const action of options.actions ?? []) {
32071
- switch (action.type) {
32072
- case "click":
32073
- await page.click(action.selector, { timeout });
32074
- break;
32075
- case "fill":
32076
- await page.fill(action.selector, action.value, { timeout });
32077
- break;
32078
- case "goto":
32079
- await page.goto(action.url, { waitUntil: "load", timeout });
32080
- break;
32081
- case "wait":
32082
- await page.waitForTimeout(action.ms);
32083
- break;
32084
- default:
32085
- break;
32086
- }
32087
- }
32088
- let selectorFound;
32089
- if (options.waitForSelector) {
32090
- try {
32091
- await page.waitForSelector(options.waitForSelector, { timeout });
32092
- selectorFound = true;
32093
- } catch {
32094
- selectorFound = false;
32095
- }
32096
- }
32097
- let screenshotPath;
32098
- if (options.screenshotPath) {
32099
- try {
32100
- await page.screenshot({ path: options.screenshotPath });
32101
- screenshotPath = options.screenshotPath;
32102
- } catch {
32103
- }
32104
- }
32105
- const title = await page.title().catch(() => void 0);
32106
- return {
32107
- ok: true,
32108
- consoleErrors,
32109
- pageErrors,
32110
- failedRequests,
32111
- url: page.url(),
32112
- ...title !== void 0 ? { title } : {},
32113
- ...selectorFound !== void 0 ? { selectorFound } : {},
32114
- ...screenshotPath ? { screenshotPath } : {}
32115
- };
32116
- } catch (err) {
32117
- return { ...base, error: err instanceof Error ? err.message : String(err) };
32118
- } finally {
32119
- try {
32120
- await browser?.close();
32121
- } catch {
32122
- }
32123
- }
32124
- }
32125
-
32126
- // src/cli/browser/tools.ts
32127
32440
  var ActionSchema = external_exports.discriminatedUnion("type", [
32128
32441
  external_exports.object({ type: external_exports.literal("click"), selector: external_exports.string().min(1) }),
32129
32442
  external_exports.object({ type: external_exports.literal("fill"), selector: external_exports.string().min(1), value: external_exports.string() }),
@@ -33472,6 +33785,8 @@ function handleSlashCommand(text, availableSkills) {
33472
33785
  /council-feedback <memberId> <1-5> [note] \u2014 rate a council member for future ranking (Task I.2)
33473
33786
  /promote-member <memberId> \u2014 promote a council member to a standalone skill (v3-K)
33474
33787
  /update [--yes|-y] \u2014 check for zelari-code updates; --yes performs the update (v3-N)
33788
+ /plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)
33789
+ /plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)
33475
33790
  /steer <text> \u2014 enqueue a follow-up prompt on the active run (Task 18.2)
33476
33791
  /steer --interrupt <text> \u2014 cancel current run + enqueue <text> for next dispatch (Task C.3.2)
33477
33792
  /compact \u2014 compact the session transcript
@@ -33814,6 +34129,19 @@ ${formatSkillList(availableSkills)}`
33814
34129
  updateForce: force
33815
34130
  };
33816
34131
  }
34132
+ case "plugins": {
34133
+ if (args.length === 0) {
34134
+ return { handled: true, kind: "plugins_list" };
34135
+ }
34136
+ if (args[0] === "install" && args[1]) {
34137
+ return { handled: true, kind: "plugins_install", pluginId: args[1] };
34138
+ }
34139
+ return {
34140
+ handled: true,
34141
+ kind: "plugins_usage",
34142
+ message: "Usage: /plugins \u2014 list optional tool plugins (Playwright, eslint, ruff, LSP servers)\n /plugins install <id> \u2014 install a plugin now (e.g. /plugins install eslint)"
34143
+ };
34144
+ }
33817
34145
  case "undo": {
33818
34146
  const confirmed = args.includes("--yes") || args.includes("-y");
33819
34147
  if (confirmed) {
@@ -34265,17 +34593,124 @@ ${output}`.toLowerCase();
34265
34593
  return "\u{1F4A1} hint: the install did not succeed. To retry with more detail:\n npm install -g zelari-code@latest --verbose 2>&1 | tail -40\n On Windows, also try: `npm install -g zelari-code@latest --force`\n" + (isWin ? " Run `zelari-code doctor` from a new terminal to diagnose shim / PATH issues.\n" : "");
34266
34594
  }
34267
34595
 
34596
+ // src/cli/slashHandlers/plugins.ts
34597
+ init_registry2();
34598
+
34599
+ // src/cli/plugins/installer.ts
34600
+ init_cmdline();
34601
+ init_updater();
34602
+ import { spawn as spawn8 } from "node:child_process";
34603
+ async function installPlugin(spec, cwd, executor = spawn8) {
34604
+ const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
34605
+ const args = ["install", scopeFlag, spec.npmPackage];
34606
+ const primary = await runNpm2(executor, args, cwd, "shim");
34607
+ if (primary.ok) return primary;
34608
+ const npmCli = resolveBundledNpmCli();
34609
+ if (npmCli && looksLikeBrokenShim(primary.exitCode, primary.output)) {
34610
+ const fallback = await runNpm2(executor, args, cwd, "bundled", npmCli);
34611
+ return {
34612
+ ...fallback,
34613
+ output: `[plugins] npm shim failed (${primary.error ?? "exit " + primary.exitCode}); retried via bundled npm (${npmCli}).
34614
+ ${fallback.output}`
34615
+ };
34616
+ }
34617
+ return primary;
34618
+ }
34619
+ function runNpm2(executor, args, cwd, mode, npmCliPath) {
34620
+ return new Promise((resolve) => {
34621
+ let stdout = "";
34622
+ let stderr = "";
34623
+ const stdio = ["ignore", "pipe", "pipe"];
34624
+ const child = mode === "bundled" && npmCliPath ? executor(process.execPath, [npmCliPath, ...args], { stdio, cwd }) : process.platform === "win32" ? executor(buildCmdLine("npm", args), { stdio, shell: true, cwd }) : executor("npm", args, { stdio, cwd });
34625
+ child.stdout?.on("data", (chunk) => {
34626
+ stdout += chunk.toString();
34627
+ });
34628
+ child.stderr?.on("data", (chunk) => {
34629
+ stderr += chunk.toString();
34630
+ });
34631
+ child.on("error", (err) => {
34632
+ resolve({
34633
+ ok: false,
34634
+ output: stdout + stderr,
34635
+ error: err.message,
34636
+ exitCode: null
34637
+ });
34638
+ });
34639
+ child.on("close", (code) => {
34640
+ const ok = code === 0;
34641
+ resolve({
34642
+ ok,
34643
+ output: stdout + stderr,
34644
+ exitCode: code,
34645
+ error: ok ? void 0 : `npm exited with code ${code}`
34646
+ });
34647
+ });
34648
+ });
34649
+ }
34650
+
34651
+ // src/cli/slashHandlers/plugins.ts
34652
+ async function handlePluginsList(ctx, cwd) {
34653
+ const lines = ["[plugins] optional tool plugins:"];
34654
+ for (const spec of PLUGINS) {
34655
+ let present = false;
34656
+ try {
34657
+ present = await spec.detect(cwd);
34658
+ } catch {
34659
+ present = false;
34660
+ }
34661
+ const mark = present ? "\u2713" : "\u2717";
34662
+ const state2 = present ? "installed" : "missing";
34663
+ const hint = present ? "" : ` \u2014 run \`/plugins install ${spec.id}\``;
34664
+ lines.push(` ${mark} ${spec.label.padEnd(38)} ${state2}${hint}`);
34665
+ }
34666
+ lines.push("Missing plugins are optional \u2014 features degrade silently without them.");
34667
+ appendSystem(ctx.setMessages, lines.join("\n"));
34668
+ }
34669
+ async function handlePluginsInstall(ctx, cwd, pluginId) {
34670
+ const spec = findPlugin(pluginId);
34671
+ if (!spec) {
34672
+ appendSystem(
34673
+ ctx.setMessages,
34674
+ `[plugins] unknown plugin id: '${pluginId}'. Available: ${PLUGINS.map((p3) => p3.id).join(", ")}`
34675
+ );
34676
+ return;
34677
+ }
34678
+ const scopeFlag = spec.installScope === "global" ? "-g" : "-D";
34679
+ appendSystem(
34680
+ ctx.setMessages,
34681
+ `[plugins] installing ${spec.label} (\`npm i ${scopeFlag} ${spec.npmPackage}\`)\u2026`
34682
+ );
34683
+ let result;
34684
+ try {
34685
+ result = await installPlugin(spec, cwd);
34686
+ } catch {
34687
+ result = { ok: false, output: "", exitCode: null, error: "unexpected error" };
34688
+ }
34689
+ if (result.ok) {
34690
+ const post = spec.postInstallHint ? `
34691
+ \u2192 ${spec.postInstallHint}` : "";
34692
+ appendSystem(ctx.setMessages, `[plugins] \u2713 installed ${spec.label}${post}`);
34693
+ } else {
34694
+ const tail = result.output ? `
34695
+ ${result.output.split("\n").slice(-8).join("\n")}` : "";
34696
+ appendSystem(
34697
+ ctx.setMessages,
34698
+ `[plugins] \u2717 install failed for ${spec.label}: ${result.error ?? "unknown error"}${tail}`
34699
+ );
34700
+ }
34701
+ }
34702
+
34268
34703
  // src/cli/slashHandlers/promoteMember.ts
34269
34704
  import { promises as fs16 } from "node:fs";
34270
- import path28 from "node:path";
34271
- import os9 from "node:os";
34705
+ import path29 from "node:path";
34706
+ import os10 from "node:os";
34272
34707
  async function handlePromoteMember(ctx, memberId) {
34273
34708
  try {
34274
34709
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
34275
34710
  const { skill, markdown } = promoteMember2(memberId);
34276
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path28.join(os9.homedir(), ".tmp", "zelari-code", "skills");
34711
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "skills");
34277
34712
  await fs16.mkdir(skillDir, { recursive: true });
34278
- const filePath = path28.join(skillDir, `${skill.id}.md`);
34713
+ const filePath = path29.join(skillDir, `${skill.id}.md`);
34279
34714
  await fs16.writeFile(filePath, markdown, "utf8");
34280
34715
  appendSystem(
34281
34716
  ctx.setMessages,
@@ -34292,33 +34727,33 @@ async function handlePromoteMember(ctx, memberId) {
34292
34727
  }
34293
34728
 
34294
34729
  // src/cli/branchManager.ts
34295
- import { promises as fs17, existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync10, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
34296
- import path29 from "node:path";
34297
- import os10 from "node:os";
34730
+ import { promises as fs17, existsSync as existsSync27, readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
34731
+ import path30 from "node:path";
34732
+ import os11 from "node:os";
34298
34733
  var META_FILENAME = "meta.json";
34299
34734
  var SESSIONS_SUBDIR = "sessions";
34300
34735
  function getBranchesBaseDir() {
34301
- return process.env.ANATHEMA_BRANCHES_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "branches");
34736
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path30.join(os11.homedir(), ".tmp", "zelari-code", "branches");
34302
34737
  }
34303
34738
  function getSessionsBaseDir() {
34304
- return process.env.ANATHEMA_SESSIONS_DIR ?? path29.join(os10.homedir(), ".tmp", "zelari-code", "sessions");
34739
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path30.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
34305
34740
  }
34306
34741
  function branchPathFor(name, baseDir) {
34307
- return path29.join(baseDir, name);
34742
+ return path30.join(baseDir, name);
34308
34743
  }
34309
34744
  function metaPathFor(name, baseDir) {
34310
- return path29.join(baseDir, name, META_FILENAME);
34745
+ return path30.join(baseDir, name, META_FILENAME);
34311
34746
  }
34312
34747
  function sessionsPathFor(name, baseDir) {
34313
- return path29.join(baseDir, name, SESSIONS_SUBDIR);
34748
+ return path30.join(baseDir, name, SESSIONS_SUBDIR);
34314
34749
  }
34315
34750
  function readBranchMeta(name, baseDir) {
34316
34751
  const metaPath = metaPathFor(name, baseDir);
34317
- if (!existsSync26(metaPath)) {
34752
+ if (!existsSync27(metaPath)) {
34318
34753
  throw new BranchNotFoundError(`Branch "${name}" not found`);
34319
34754
  }
34320
34755
  try {
34321
- const raw = readFileSync23(metaPath, "utf-8");
34756
+ const raw = readFileSync24(metaPath, "utf-8");
34322
34757
  const parsed = JSON.parse(raw);
34323
34758
  if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
34324
34759
  throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
@@ -34335,8 +34770,8 @@ function readBranchMeta(name, baseDir) {
34335
34770
  }
34336
34771
  function writeBranchMeta(name, baseDir, meta3) {
34337
34772
  const metaPath = metaPathFor(name, baseDir);
34338
- mkdirSync10(path29.dirname(metaPath), { recursive: true });
34339
- writeFileSync15(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
34773
+ mkdirSync11(path30.dirname(metaPath), { recursive: true });
34774
+ writeFileSync16(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
34340
34775
  }
34341
34776
  async function countSessions(name, baseDir) {
34342
34777
  const sessionsPath = sessionsPathFor(name, baseDir);
@@ -34374,7 +34809,7 @@ var SessionNotFoundError = class extends Error {
34374
34809
  };
34375
34810
  function branchExists(name, baseDir = getBranchesBaseDir()) {
34376
34811
  const bp = branchPathFor(name, baseDir);
34377
- return existsSync26(bp) && existsSync26(metaPathFor(name, baseDir));
34812
+ return existsSync27(bp) && existsSync27(metaPathFor(name, baseDir));
34378
34813
  }
34379
34814
  async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
34380
34815
  if (!name || name.trim().length === 0) {
@@ -34386,14 +34821,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
34386
34821
  if (branchExists(name, baseDir)) {
34387
34822
  throw new BranchAlreadyExistsError(name);
34388
34823
  }
34389
- const sourcePath = path29.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
34390
- if (!existsSync26(sourcePath)) {
34824
+ const sourcePath = path30.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
34825
+ if (!existsSync27(sourcePath)) {
34391
34826
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
34392
34827
  }
34393
34828
  const branchPath = branchPathFor(name, baseDir);
34394
34829
  const branchSessionsPath = sessionsPathFor(name, baseDir);
34395
- mkdirSync10(branchSessionsPath, { recursive: true });
34396
- const destPath = path29.join(branchSessionsPath, `${fromSessionId}.jsonl`);
34830
+ mkdirSync11(branchSessionsPath, { recursive: true });
34831
+ const destPath = path30.join(branchSessionsPath, `${fromSessionId}.jsonl`);
34397
34832
  await fs17.copyFile(sourcePath, destPath);
34398
34833
  const meta3 = {
34399
34834
  name,
@@ -34420,7 +34855,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
34420
34855
  const results = [];
34421
34856
  for (const entry of entries) {
34422
34857
  const metaPath = metaPathFor(entry, baseDir);
34423
- if (!existsSync26(metaPath)) continue;
34858
+ if (!existsSync27(metaPath)) continue;
34424
34859
  try {
34425
34860
  const meta3 = readBranchMeta(entry, baseDir);
34426
34861
  const sessionCount = await countSessions(entry, baseDir);
@@ -34494,14 +34929,14 @@ async function handleBranchCheckout(ctx, branchName) {
34494
34929
 
34495
34930
  // src/cli/slashHandlers/workspace.ts
34496
34931
  import { promises as fs18 } from "node:fs";
34497
- import path30 from "node:path";
34932
+ import path31 from "node:path";
34498
34933
  async function handleWorkspaceShow(ctx, what) {
34499
34934
  try {
34500
- const zelari = path30.join(process.cwd(), ".zelari");
34935
+ const zelari = path31.join(process.cwd(), ".zelari");
34501
34936
  let content;
34502
34937
  switch (what) {
34503
34938
  case "plan": {
34504
- const planPath = path30.join(zelari, "plan.md");
34939
+ const planPath = path31.join(zelari, "plan.md");
34505
34940
  try {
34506
34941
  content = await fs18.readFile(planPath, "utf-8");
34507
34942
  } catch {
@@ -34510,7 +34945,7 @@ async function handleWorkspaceShow(ctx, what) {
34510
34945
  break;
34511
34946
  }
34512
34947
  case "decisions": {
34513
- const decisionsDir = path30.join(zelari, "decisions");
34948
+ const decisionsDir = path31.join(zelari, "decisions");
34514
34949
  try {
34515
34950
  const files = (await fs18.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
34516
34951
  if (files.length === 0) {
@@ -34520,7 +34955,7 @@ async function handleWorkspaceShow(ctx, what) {
34520
34955
  `];
34521
34956
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
34522
34957
  for (const f of files) {
34523
- const raw = await fs18.readFile(path30.join(decisionsDir, f), "utf-8");
34958
+ const raw = await fs18.readFile(path31.join(decisionsDir, f), "utf-8");
34524
34959
  const { meta: meta3, body } = parseFrontmatter2(raw);
34525
34960
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
34526
34961
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -34533,7 +34968,7 @@ async function handleWorkspaceShow(ctx, what) {
34533
34968
  break;
34534
34969
  }
34535
34970
  case "risks": {
34536
- const risksPath = path30.join(zelari, "risks.md");
34971
+ const risksPath = path31.join(zelari, "risks.md");
34537
34972
  try {
34538
34973
  content = await fs18.readFile(risksPath, "utf-8");
34539
34974
  } catch {
@@ -34542,7 +34977,7 @@ async function handleWorkspaceShow(ctx, what) {
34542
34977
  break;
34543
34978
  }
34544
34979
  case "agents": {
34545
- const agentsPath = path30.join(process.cwd(), "AGENTS.MD");
34980
+ const agentsPath = path31.join(process.cwd(), "AGENTS.MD");
34546
34981
  try {
34547
34982
  content = await fs18.readFile(agentsPath, "utf-8");
34548
34983
  } catch {
@@ -34551,7 +34986,7 @@ async function handleWorkspaceShow(ctx, what) {
34551
34986
  break;
34552
34987
  }
34553
34988
  case "docs": {
34554
- const docsDir = path30.join(zelari, "docs");
34989
+ const docsDir = path31.join(zelari, "docs");
34555
34990
  try {
34556
34991
  const files = (await fs18.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
34557
34992
  content = files.length ? `# Docs (${files.length})
@@ -34593,7 +35028,7 @@ async function handleWorkspaceReset(ctx, force) {
34593
35028
  return;
34594
35029
  }
34595
35030
  try {
34596
- const target = path30.join(process.cwd(), ".zelari");
35031
+ const target = path31.join(process.cwd(), ".zelari");
34597
35032
  await fs18.rm(target, { recursive: true, force: true });
34598
35033
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
34599
35034
  } catch (err) {
@@ -34952,11 +35387,11 @@ function handleModelsRefresh(ctx) {
34952
35387
  }
34953
35388
 
34954
35389
  // src/cli/slashHandlers/skills.ts
34955
- import path31 from "node:path";
34956
- import os11 from "node:os";
35390
+ import path32 from "node:path";
35391
+ import os12 from "node:os";
34957
35392
 
34958
35393
  // src/cli/skillHistory.ts
34959
- import { promises as fs19, existsSync as existsSync27, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync11 } from "node:fs";
35394
+ import { promises as fs19, existsSync as existsSync28, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync12 } from "node:fs";
34960
35395
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
34961
35396
  async function readSkillHistory(file2) {
34962
35397
  let raw = "";
@@ -35054,7 +35489,7 @@ async function applySteerInterrupt(options) {
35054
35489
 
35055
35490
  // src/cli/slashHandlers/skills.ts
35056
35491
  async function handleSkillStats(ctx, skillId) {
35057
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
35492
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path32.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
35058
35493
  try {
35059
35494
  const records = await readSkillHistory(historyFile);
35060
35495
  const stats = getSkillStats(records, skillId);
@@ -35070,7 +35505,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
35070
35505
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
35071
35506
  return;
35072
35507
  }
35073
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path31.join(os11.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
35508
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path32.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
35074
35509
  try {
35075
35510
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
35076
35511
  appendSystem(ctx.setMessages, formatted);
@@ -35333,6 +35768,24 @@ function useSlashDispatch(params) {
35333
35768
  setInput("");
35334
35769
  return;
35335
35770
  }
35771
+ if (result.kind === "plugins_list") {
35772
+ await handlePluginsList({ setMessages }, process.cwd());
35773
+ setInput("");
35774
+ return;
35775
+ }
35776
+ if (result.kind === "plugins_install") {
35777
+ await handlePluginsInstall({ setMessages }, process.cwd(), result.pluginId ?? "");
35778
+ setInput("");
35779
+ return;
35780
+ }
35781
+ if (result.kind === "plugins_usage") {
35782
+ appendSystem(
35783
+ setMessages,
35784
+ result.message ?? "Usage: /plugins | /plugins install <id>"
35785
+ );
35786
+ setInput("");
35787
+ return;
35788
+ }
35336
35789
  if (result.kind === "workspace") {
35337
35790
  appendSystem(
35338
35791
  setMessages,
@@ -35896,13 +36349,131 @@ function SplashGate({
35896
36349
  return /* @__PURE__ */ React10.createElement(React10.Fragment, null, children);
35897
36350
  }
35898
36351
 
36352
+ // src/cli/components/PluginGate.tsx
36353
+ import React11, { useEffect as useEffect9, useState as useState10, useCallback as useCallback6 } from "react";
36354
+ import { Box as Box10, Text as Text11, useStdin as useStdin4 } from "ink";
36355
+ init_registry2();
36356
+ init_prefs();
36357
+ var CHOICE_INSTALL = "__install__";
36358
+ var CHOICE_LATER = "__later__";
36359
+ var CHOICE_NEVER = "__never__";
36360
+ function PluginGate({ cwd, children }) {
36361
+ const { isRawModeSupported } = useStdin4();
36362
+ const [phase, setPhase] = useState10("detecting");
36363
+ const [queue, setQueue] = useState10([]);
36364
+ const [current, setCurrent] = useState10(null);
36365
+ const [result, setResult] = useState10(null);
36366
+ const skipGate = process.env.ZELARI_NO_PLUGIN_PROMPT === "1" || isRawModeSupported !== true;
36367
+ useEffect9(() => {
36368
+ if (skipGate) {
36369
+ setPhase("done");
36370
+ return;
36371
+ }
36372
+ let cancelled = false;
36373
+ void detectMissingPlugins(cwd).then((missing) => {
36374
+ if (cancelled) return;
36375
+ if (missing.length === 0) {
36376
+ setPhase("done");
36377
+ return;
36378
+ }
36379
+ setQueue(missing);
36380
+ setCurrent(missing[0] ?? null);
36381
+ setPhase(missing[0] ? "prompting" : "done");
36382
+ }).catch(() => {
36383
+ if (!cancelled) setPhase("done");
36384
+ });
36385
+ return () => {
36386
+ cancelled = true;
36387
+ };
36388
+ }, [cwd, skipGate]);
36389
+ const advance = useCallback6(() => {
36390
+ setQueue((q) => {
36391
+ const rest = q.slice(1);
36392
+ if (rest.length === 0) {
36393
+ setPhase("done");
36394
+ setCurrent(null);
36395
+ return rest;
36396
+ }
36397
+ setCurrent(rest[0] ?? null);
36398
+ setPhase(rest[0] ? "prompting" : "done");
36399
+ setResult(null);
36400
+ return rest;
36401
+ });
36402
+ }, []);
36403
+ const onSelect = useCallback6(
36404
+ (value) => {
36405
+ if (!current) return;
36406
+ if (value === CHOICE_LATER) {
36407
+ advance();
36408
+ } else if (value === CHOICE_NEVER) {
36409
+ markDontAskAgain(current.id);
36410
+ advance();
36411
+ } else if (value === CHOICE_INSTALL) {
36412
+ setPhase("installing");
36413
+ void installPlugin(current, cwd).then((r) => {
36414
+ setResult(r);
36415
+ setPhase("result");
36416
+ }).catch(() => {
36417
+ setResult({ ok: false, output: "", exitCode: null, error: "unexpected error" });
36418
+ setPhase("result");
36419
+ });
36420
+ }
36421
+ },
36422
+ [current, cwd, advance]
36423
+ );
36424
+ if (phase === "done") {
36425
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
36426
+ }
36427
+ if (phase === "detecting") {
36428
+ return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Checking for optional tool plugins\u2026"));
36429
+ }
36430
+ if (!current) {
36431
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, children);
36432
+ }
36433
+ if (phase === "installing") {
36434
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, "\u23F3 Installing ", current.label, "\u2026"), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "npm install ", current.installScope === "global" ? "-g" : "-D", " ", current.npmPackage));
36435
+ }
36436
+ if (phase === "result") {
36437
+ const ok = result?.ok === true;
36438
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: ok ? "green" : "red" }, ok ? "\u2713" : "\u2717", " ", ok ? "Installed" : "Install failed", ": ", current.label), !ok && result?.error ? /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, result.error) : null, ok && current.postInstallHint ? /* @__PURE__ */ React11.createElement(Text11, { color: "yellow" }, " \u2192 ", current.postInstallHint) : null, /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Press any key to continue\u2026"), /* @__PURE__ */ React11.createElement(ContinueKey, { onContinue: advance }));
36439
+ }
36440
+ const items = [
36441
+ {
36442
+ value: CHOICE_INSTALL,
36443
+ label: `Install now`,
36444
+ hint: `npm i ${current.installScope === "global" ? "-g" : "-D"} ${current.npmPackage}`
36445
+ },
36446
+ { value: CHOICE_LATER, label: "Maybe later" },
36447
+ { value: CHOICE_NEVER, label: "Don't ask again" }
36448
+ ];
36449
+ return /* @__PURE__ */ React11.createElement(
36450
+ SelectList,
36451
+ {
36452
+ title: `Optional plugin missing: ${current.label}`,
36453
+ items,
36454
+ onSelect,
36455
+ onCancel: advance,
36456
+ maxVisible: 4
36457
+ }
36458
+ );
36459
+ }
36460
+ function ContinueKey({ onContinue }) {
36461
+ const { isRawModeSupported } = useStdin4();
36462
+ useEffect9(() => {
36463
+ const t = setTimeout(onContinue, 1500);
36464
+ return () => clearTimeout(t);
36465
+ }, [onContinue]);
36466
+ void isRawModeSupported;
36467
+ return null;
36468
+ }
36469
+
35899
36470
  // src/cli/main.ts
35900
36471
  init_providerConfig();
35901
36472
 
35902
36473
  // src/cli/wizard/firstRun.ts
35903
- import { existsSync as existsSync28 } from "node:fs";
36474
+ import { existsSync as existsSync29 } from "node:fs";
35904
36475
  function shouldRunWizard(input) {
35905
- const exists = input.exists ?? existsSync28;
36476
+ const exists = input.exists ?? existsSync29;
35906
36477
  if (input.hasResetConfigFlag) {
35907
36478
  return { shouldRun: true, reason: "--reset-config flag forced wizard" };
35908
36479
  }
@@ -35937,15 +36508,15 @@ function parseWizardFlags(argv) {
35937
36508
  }
35938
36509
 
35939
36510
  // src/cli/wizard/runWizard.tsx
35940
- import React12, { useEffect as useEffect9, useState as useState10 } from "react";
35941
- import { Box as Box11, Text as Text12 } from "ink";
36511
+ import React13, { useEffect as useEffect10, useState as useState11 } from "react";
36512
+ import { Box as Box12, Text as Text13 } from "ink";
35942
36513
  import { useInput as useInput4 } from "ink";
35943
36514
  init_keyStore();
35944
36515
  init_providerConfig();
35945
36516
 
35946
36517
  // src/cli/wizard/index.tsx
35947
- import React11 from "react";
35948
- import { Box as Box10, Text as Text11 } from "ink";
36518
+ import React12 from "react";
36519
+ import { Box as Box11, Text as Text12 } from "ink";
35949
36520
 
35950
36521
  // src/cli/wizard/useWizardState.ts
35951
36522
  init_providerConfig();
@@ -36061,16 +36632,16 @@ var API_KEY_OPTIONS = ["env", "keystore", "skip"];
36061
36632
 
36062
36633
  // src/cli/wizard/index.tsx
36063
36634
  function Frame({ children }) {
36064
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 }, children);
36635
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 }, children);
36065
36636
  }
36066
36637
  function Step(props) {
36067
- return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: props.active ? "cyan" : "gray", inverse: props.active }, " ", props.index, "/", props.total, " ", props.name, " "));
36638
+ return /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text12, { color: props.active ? "cyan" : "gray", inverse: props.active }, " ", props.index, "/", props.total, " ", props.name, " "));
36068
36639
  }
36069
36640
  function renderProviderList(providers, cursor) {
36070
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, providers.map((p3, i) => {
36641
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, providers.map((p3, i) => {
36071
36642
  const arrow = i === cursor ? "\u279C " : " ";
36072
36643
  const color = i === cursor ? "cyan" : void 0;
36073
- return /* @__PURE__ */ React11.createElement(Text11, { key: p3.id, color }, arrow, p3.displayName, " ", /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "(", p3.id, ", uses env ", p3.envVar, ")"));
36644
+ return /* @__PURE__ */ React12.createElement(Text12, { key: p3.id, color }, arrow, p3.displayName, " ", /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "(", p3.id, ", uses env ", p3.envVar, ")"));
36074
36645
  }));
36075
36646
  }
36076
36647
  function renderApiKeyOptions(cursor) {
@@ -36079,30 +36650,30 @@ function renderApiKeyOptions(cursor) {
36079
36650
  keystore: "Save to local keyStore (encrypted)",
36080
36651
  skip: "Skip for now (chat will fail until key is set)"
36081
36652
  };
36082
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, API_KEY_OPTIONS.map((choice, i) => {
36653
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, API_KEY_OPTIONS.map((choice, i) => {
36083
36654
  const arrow = i === cursor ? "\u279C " : " ";
36084
36655
  const color = i === cursor ? "cyan" : void 0;
36085
- return /* @__PURE__ */ React11.createElement(Text11, { key: choice, color }, arrow, labels[choice]);
36656
+ return /* @__PURE__ */ React12.createElement(Text12, { key: choice, color }, arrow, labels[choice]);
36086
36657
  }));
36087
36658
  }
36088
36659
  function Wizard({ state: state2, providers }) {
36089
36660
  const s = state2.state;
36090
36661
  if (s.committed) {
36091
- return /* @__PURE__ */ React11.createElement(Frame, null, /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "\u2713 Setup complete!"), /* @__PURE__ */ React11.createElement(Text11, null, "Provider: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.providerId), " | ", "Model: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.model), " | ", "API key: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.apiKeyChoice ?? "n/a")), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "Launching zelari-code\u2026 (press any key)")));
36662
+ return /* @__PURE__ */ React12.createElement(Frame, null, /* @__PURE__ */ React12.createElement(Text12, { color: "green" }, "\u2713 Setup complete!"), /* @__PURE__ */ React12.createElement(Text12, null, "Provider: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId), " | ", "Model: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.model), " | ", "API key: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.apiKeyChoice ?? "n/a")), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "Launching zelari-code\u2026 (press any key)")));
36092
36663
  }
36093
- return /* @__PURE__ */ React11.createElement(Frame, null, /* @__PURE__ */ React11.createElement(Text11, { color: "cyan", bold: true }, "zelari-code v", VERSION, " \u2014 first-time setup"), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, marginBottom: 1, flexDirection: "row" }, /* @__PURE__ */ React11.createElement(Step, { index: 1, total: 5, name: "welcome", active: s.step === "welcome" }), /* @__PURE__ */ React11.createElement(Step, { index: 2, total: 5, name: "provider", active: s.step === "provider" }), /* @__PURE__ */ React11.createElement(Step, { index: 3, total: 5, name: "model", active: s.step === "model" }), /* @__PURE__ */ React11.createElement(Step, { index: 4, total: 5, name: "apikey", active: s.step === "apikey" }), /* @__PURE__ */ React11.createElement(Step, { index: 5, total: 5, name: "confirm", active: s.step === "confirm" })), s.step === "welcome" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text11, null, "Welcome! Let's get you coding in under two minutes."), /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "We'll pick a provider, default model, and how to handle your API key."), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, null, "Press "), /* @__PURE__ */ React11.createElement(Text11, { color: "cyan", inverse: true }, " Enter "), /* @__PURE__ */ React11.createElement(Text11, null, " to continue, or "), /* @__PURE__ */ React11.createElement(Text11, { color: "red", inverse: true }, " Q "), /* @__PURE__ */ React11.createElement(Text11, null, " to quit (re-run with "), /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "--no-wizard"), /* @__PURE__ */ React11.createElement(Text11, null, " to skip later)."))), s.step === "provider" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text11, null, "Choose your LLM provider:"), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, renderProviderList(providers, s.providerCursor)), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "model" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text11, null, "Model for ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.providerId), ":"), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, null, s.model ?? "(empty)")), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, null, "Press ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan", inverse: true }, " Enter "), "to accept, or type a new name. ", /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "(default kept on empty input)")))), s.step === "apikey" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text11, null, "How should we handle the API key?"), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, renderApiKeyOptions(s.apiKeyCursor)), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "confirm" && /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text11, null, "Confirm your setup:"), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Provider: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.providerId)), /* @__PURE__ */ React11.createElement(Text11, null, "Model: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.model)), /* @__PURE__ */ React11.createElement(Text11, null, "API key: ", /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, s.apiKeyChoice ?? "(unset)"))), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text11, null, "Press ", /* @__PURE__ */ React11.createElement(Text11, { color: "green", inverse: true }, " Enter "), "to save and launch, or ", /* @__PURE__ */ React11.createElement(Text11, { color: "yellow", inverse: true }, " B "), "to go back."))));
36664
+ return /* @__PURE__ */ React12.createElement(Frame, null, /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", bold: true }, "zelari-code v", VERSION, " \u2014 first-time setup"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, marginBottom: 1, flexDirection: "row" }, /* @__PURE__ */ React12.createElement(Step, { index: 1, total: 5, name: "welcome", active: s.step === "welcome" }), /* @__PURE__ */ React12.createElement(Step, { index: 2, total: 5, name: "provider", active: s.step === "provider" }), /* @__PURE__ */ React12.createElement(Step, { index: 3, total: 5, name: "model", active: s.step === "model" }), /* @__PURE__ */ React12.createElement(Step, { index: 4, total: 5, name: "apikey", active: s.step === "apikey" }), /* @__PURE__ */ React12.createElement(Step, { index: 5, total: 5, name: "confirm", active: s.step === "confirm" })), s.step === "welcome" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Welcome! Let's get you coding in under two minutes."), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "We'll pick a provider, default model, and how to handle your API key."), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press "), /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", inverse: true }, " Enter "), /* @__PURE__ */ React12.createElement(Text12, null, " to continue, or "), /* @__PURE__ */ React12.createElement(Text12, { color: "red", inverse: true }, " Q "), /* @__PURE__ */ React12.createElement(Text12, null, " to quit (re-run with "), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "--no-wizard"), /* @__PURE__ */ React12.createElement(Text12, null, " to skip later)."))), s.step === "provider" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Choose your LLM provider:"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, renderProviderList(providers, s.providerCursor)), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "model" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Model for ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId), ":"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, s.model ?? "(empty)")), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan", inverse: true }, " Enter "), "to accept, or type a new name. ", /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "(default kept on empty input)")))), s.step === "apikey" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "How should we handle the API key?"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, renderApiKeyOptions(s.apiKeyCursor)), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "\u2191/\u2193 to move, Enter to confirm"))), s.step === "confirm" && /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text12, null, "Confirm your setup:"), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text12, null, "Provider: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.providerId)), /* @__PURE__ */ React12.createElement(Text12, null, "Model: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.model)), /* @__PURE__ */ React12.createElement(Text12, null, "API key: ", /* @__PURE__ */ React12.createElement(Text12, { color: "cyan" }, s.apiKeyChoice ?? "(unset)"))), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text12, null, "Press ", /* @__PURE__ */ React12.createElement(Text12, { color: "green", inverse: true }, " Enter "), "to save and launch, or ", /* @__PURE__ */ React12.createElement(Text12, { color: "yellow", inverse: true }, " B "), "to go back."))));
36094
36665
  }
36095
36666
 
36096
36667
  // src/cli/wizard/runWizard.tsx
36097
36668
  function RunWizard(_props) {
36098
- const [wiz] = useState10(
36669
+ const [wiz] = useState11(
36099
36670
  () => createWizardState({
36100
36671
  providers: PROVIDERS,
36101
36672
  defaultModelFor: (id) => getModelForProvider(id)
36102
36673
  })
36103
36674
  );
36104
- const [, force] = useState10(0);
36105
- useEffect9(() => {
36675
+ const [, force] = useState11(0);
36676
+ useEffect10(() => {
36106
36677
  if (typeof wiz.subscribe === "function") {
36107
36678
  const sub = wiz.subscribe(() => force((n) => n + 1));
36108
36679
  return () => sub();
@@ -36166,20 +36737,20 @@ function RunWizard(_props) {
36166
36737
  }
36167
36738
  });
36168
36739
  if (wiz.state.committed) {
36169
- return /* @__PURE__ */ React12.createElement(PostCommitBridge, null);
36740
+ return /* @__PURE__ */ React13.createElement(PostCommitBridge, null);
36170
36741
  }
36171
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React12.createElement(Wizard, { state: wiz, providers: PROVIDERS }));
36742
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React13.createElement(Wizard, { state: wiz, providers: PROVIDERS }));
36172
36743
  }
36173
36744
  function PostCommitBridge() {
36174
- const [showApp, setShowApp] = useState10(false);
36175
- useEffect9(() => {
36745
+ const [showApp, setShowApp] = useState11(false);
36746
+ useEffect10(() => {
36176
36747
  const t = setTimeout(() => setShowApp(true), 1200);
36177
36748
  return () => clearTimeout(t);
36178
36749
  }, []);
36179
36750
  if (showApp) {
36180
- return /* @__PURE__ */ React12.createElement(App, null);
36751
+ return /* @__PURE__ */ React13.createElement(App, null);
36181
36752
  }
36182
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React12.createElement(Box11, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text12, { color: "green", bold: true }, "\u2713 Setup complete! Launching zelari-code\u2026"), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "Press Ctrl+C any time to exit.")));
36753
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React13.createElement(Box12, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Text13, { color: "green", bold: true }, "\u2713 Setup complete! Launching zelari-code\u2026"), /* @__PURE__ */ React13.createElement(Text13, { color: "gray" }, "Press Ctrl+C any time to exit.")));
36183
36754
  }
36184
36755
 
36185
36756
  // src/cli/headless.ts
@@ -36392,7 +36963,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
36392
36963
 
36393
36964
  // src/cli/skillsMd.ts
36394
36965
  init_skills2();
36395
- import { existsSync as existsSync29, readdirSync as readdirSync4, readFileSync as readFileSync24 } from "node:fs";
36966
+ import { existsSync as existsSync30, readdirSync as readdirSync4, readFileSync as readFileSync25 } from "node:fs";
36396
36967
  import { join as join23 } from "node:path";
36397
36968
  import { homedir as homedir6 } from "node:os";
36398
36969
  var CODING_CATEGORIES = /* @__PURE__ */ new Set([
@@ -36470,7 +37041,7 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
36470
37041
  const summary = { loaded: [], skipped: [] };
36471
37042
  const seen = new Set(options.existingIds ?? []);
36472
37043
  for (const dir of skillMdSearchDirs(projectRoot)) {
36473
- if (!existsSync29(dir)) continue;
37044
+ if (!existsSync30(dir)) continue;
36474
37045
  let entries;
36475
37046
  try {
36476
37047
  entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -36479,9 +37050,9 @@ function loadSkillMdSkills(projectRoot = process.cwd(), options = {}) {
36479
37050
  }
36480
37051
  for (const entry of entries) {
36481
37052
  const skillPath = join23(dir, entry, "SKILL.md");
36482
- if (!existsSync29(skillPath)) continue;
37053
+ if (!existsSync30(skillPath)) continue;
36483
37054
  try {
36484
- const parsed = parseSkillMd(readFileSync24(skillPath, "utf8"), skillPath);
37055
+ const parsed = parseSkillMd(readFileSync25(skillPath, "utf8"), skillPath);
36485
37056
  if (!parsed) {
36486
37057
  summary.skipped.push({ path: skillPath, reason: "missing/invalid frontmatter (name, description) or empty body" });
36487
37058
  continue;
@@ -36571,12 +37142,34 @@ function pickRootComponent() {
36571
37142
  }
36572
37143
  if (argv.includes("--doctor") || argv.includes("doctor")) {
36573
37144
  const { runDoctor: runDoctor2 } = (init_doctor(), __toCommonJS(doctor_exports));
36574
- const healthy = runDoctor2();
36575
- process.exit(healthy ? 0 : 1);
37145
+ void runDoctor2().then((healthy) => process.exit(healthy ? 0 : 1));
37146
+ return { kind: "done" };
37147
+ }
37148
+ if (argv.includes("--fix-path") || argv.includes("fix-path")) {
37149
+ const { repairWindowsUserPath: repairWindowsUserPath2 } = (init_fixPath(), __toCommonJS(fixPath_exports));
37150
+ const result = repairWindowsUserPath2();
37151
+ const green = "\x1B[32m";
37152
+ const red = "\x1B[31m";
37153
+ const dim = "\x1B[2m";
37154
+ const reset = "\x1B[0m";
37155
+ if (result.ok) {
37156
+ if (result.alreadyOk) {
37157
+ console.log(`${green}\u2714${reset} npm prefix already on user PATH: ${result.prefix}`);
37158
+ } else {
37159
+ console.log(`${green}\u2714${reset} added npm prefix to user PATH: ${result.prefix}`);
37160
+ console.log(`${dim}open a NEW terminal for the change to take effect, then run: zelari-code --version${reset}`);
37161
+ }
37162
+ process.exit(0);
37163
+ }
37164
+ console.error(`${red}\u2717${reset} ${result.error}`);
37165
+ if (result.prefix) {
37166
+ console.error(`${dim}prefix: ${result.prefix}${reset}`);
37167
+ }
37168
+ process.exit(1);
36576
37169
  }
36577
37170
  if (argv.includes("--help") || argv.includes("-h")) {
36578
37171
  console.log(
36579
- "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --council Use the 6-member council pipeline\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
37172
+ "zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --council Use the 6-member council pipeline\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
36580
37173
  );
36581
37174
  process.exit(0);
36582
37175
  }
@@ -36597,14 +37190,17 @@ function pickRootComponent() {
36597
37190
  });
36598
37191
  if (decision.shouldRun) {
36599
37192
  console.error(`[zelari-code] starting wizard: ${decision.reason}`);
36600
- return { kind: "wizard", element: React13.createElement(RunWizard) };
37193
+ return { kind: "wizard", element: React14.createElement(RunWizard) };
36601
37194
  }
36602
37195
  return {
36603
37196
  kind: "app",
36604
- element: React13.createElement(
37197
+ element: React14.createElement(
36605
37198
  SplashGate,
36606
37199
  { version: VERSION },
36607
- React13.createElement(App)
37200
+ React14.createElement(PluginGate, {
37201
+ cwd: process.cwd(),
37202
+ children: React14.createElement(App)
37203
+ })
36608
37204
  )
36609
37205
  };
36610
37206
  }