zelari-code 1.0.2 → 1.1.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.
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __crq } from 'node:module'; const require = __crq(import.meta.url);
3
3
  var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __esm = (fn, res) => function __init() {
6
8
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
9
  };
@@ -9,6 +11,15 @@ var __export = (target, all) => {
9
11
  for (var name in all)
10
12
  __defProp(target, name, { get: all[name], enumerable: true });
11
13
  };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
12
23
 
13
24
  // src/cli/grokOAuth.ts
14
25
  var grokOAuth_exports = {};
@@ -549,7 +560,8 @@ var init_keyStore = __esm({
549
560
  { id: "openai-compatible", displayName: "OpenAI-compatible", envVar: "OPENAI_API_KEY" },
550
561
  { id: "minimax", displayName: "MiniMax", envVar: "MINIMAX_API_KEY", baseUrl: "https://api.minimax.io/v1" },
551
562
  { id: "glm", displayName: "GLM / Z.AI", envVar: "GLM_API_KEY", baseUrl: "https://api.z.ai/api/coding/paas/v4" },
552
- { id: "grok", displayName: "xAI Grok", envVar: "GROK_API_KEY", baseUrl: "https://api.x.ai/v1" }
563
+ { id: "grok", displayName: "xAI Grok", envVar: "GROK_API_KEY", baseUrl: "https://api.x.ai/v1" },
564
+ { id: "deepseek", displayName: "DeepSeek", envVar: "DEEPSEEK_API_KEY", baseUrl: "https://api.deepseek.com" }
553
565
  ];
554
566
  defaultRefreshImpl = async (providerId, refreshToken) => {
555
567
  const impl = getRefreshImpl(providerId);
@@ -733,6 +745,7 @@ var init_providerConfig = __esm({
733
745
  "minimax": "MiniMax-M2.5",
734
746
  "glm": "glm-4.6",
735
747
  "grok": "grok-4",
748
+ "deepseek": "deepseek-v4-pro",
736
749
  "custom": ""
737
750
  },
738
751
  customEndpoints: {}
@@ -1522,10 +1535,10 @@ function mergeDefs(...defs) {
1522
1535
  function cloneDef(schema) {
1523
1536
  return mergeDefs(schema._zod.def);
1524
1537
  }
1525
- function getElementAtPath(obj, path24) {
1526
- if (!path24)
1538
+ function getElementAtPath(obj, path25) {
1539
+ if (!path25)
1527
1540
  return obj;
1528
- return path24.reduce((acc, key) => acc?.[key], obj);
1541
+ return path25.reduce((acc, key) => acc?.[key], obj);
1529
1542
  }
1530
1543
  function promiseAllObject(promisesObj) {
1531
1544
  const keys = Object.keys(promisesObj);
@@ -1853,11 +1866,11 @@ function explicitlyAborted(x, startIndex = 0) {
1853
1866
  }
1854
1867
  return false;
1855
1868
  }
1856
- function prefixIssues(path24, issues) {
1869
+ function prefixIssues(path25, issues) {
1857
1870
  return issues.map((iss) => {
1858
1871
  var _a3;
1859
1872
  (_a3 = iss).path ?? (_a3.path = []);
1860
- iss.path.unshift(path24);
1873
+ iss.path.unshift(path25);
1861
1874
  return iss;
1862
1875
  });
1863
1876
  }
@@ -2075,16 +2088,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2075
2088
  }
2076
2089
  function formatError(error51, mapper = (issue2) => issue2.message) {
2077
2090
  const fieldErrors = { _errors: [] };
2078
- const processError = (error52, path24 = []) => {
2091
+ const processError = (error52, path25 = []) => {
2079
2092
  for (const issue2 of error52.issues) {
2080
2093
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2081
- issue2.errors.map((issues) => processError({ issues }, [...path24, ...issue2.path]));
2094
+ issue2.errors.map((issues) => processError({ issues }, [...path25, ...issue2.path]));
2082
2095
  } else if (issue2.code === "invalid_key") {
2083
- processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
2096
+ processError({ issues: issue2.issues }, [...path25, ...issue2.path]);
2084
2097
  } else if (issue2.code === "invalid_element") {
2085
- processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
2098
+ processError({ issues: issue2.issues }, [...path25, ...issue2.path]);
2086
2099
  } else {
2087
- const fullpath = [...path24, ...issue2.path];
2100
+ const fullpath = [...path25, ...issue2.path];
2088
2101
  if (fullpath.length === 0) {
2089
2102
  fieldErrors._errors.push(mapper(issue2));
2090
2103
  } else {
@@ -2111,17 +2124,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2111
2124
  }
2112
2125
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2113
2126
  const result = { errors: [] };
2114
- const processError = (error52, path24 = []) => {
2127
+ const processError = (error52, path25 = []) => {
2115
2128
  var _a3, _b;
2116
2129
  for (const issue2 of error52.issues) {
2117
2130
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2118
- issue2.errors.map((issues) => processError({ issues }, [...path24, ...issue2.path]));
2131
+ issue2.errors.map((issues) => processError({ issues }, [...path25, ...issue2.path]));
2119
2132
  } else if (issue2.code === "invalid_key") {
2120
- processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
2133
+ processError({ issues: issue2.issues }, [...path25, ...issue2.path]);
2121
2134
  } else if (issue2.code === "invalid_element") {
2122
- processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
2135
+ processError({ issues: issue2.issues }, [...path25, ...issue2.path]);
2123
2136
  } else {
2124
- const fullpath = [...path24, ...issue2.path];
2137
+ const fullpath = [...path25, ...issue2.path];
2125
2138
  if (fullpath.length === 0) {
2126
2139
  result.errors.push(mapper(issue2));
2127
2140
  continue;
@@ -2153,8 +2166,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2153
2166
  }
2154
2167
  function toDotPath(_path) {
2155
2168
  const segs = [];
2156
- const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2157
- for (const seg of path24) {
2169
+ const path25 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2170
+ for (const seg of path25) {
2158
2171
  if (typeof seg === "number")
2159
2172
  segs.push(`[${seg}]`);
2160
2173
  else if (typeof seg === "symbol")
@@ -15657,13 +15670,13 @@ function resolveRef(ref, ctx) {
15657
15670
  if (!ref.startsWith("#")) {
15658
15671
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15659
15672
  }
15660
- const path24 = ref.slice(1).split("/").filter(Boolean);
15661
- if (path24.length === 0) {
15673
+ const path25 = ref.slice(1).split("/").filter(Boolean);
15674
+ if (path25.length === 0) {
15662
15675
  return ctx.rootSchema;
15663
15676
  }
15664
15677
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15665
- if (path24[0] === defsKey) {
15666
- const key = path24[1];
15678
+ if (path25[0] === defsKey) {
15679
+ const key = path25[1];
15667
15680
  if (!key || !ctx.defs[key]) {
15668
15681
  throw new Error(`Reference not found: ${ref}`);
15669
15682
  }
@@ -17693,11 +17706,11 @@ var init_tools = __esm({
17693
17706
  if (!ctx.addDocument)
17694
17707
  return "Knowledge vault tool not available.";
17695
17708
  const title = args["title"] || "New Document";
17696
- const path24 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17709
+ const path25 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
17697
17710
  const content = args["content"] || "";
17698
17711
  const tags = args["tags"] || [];
17699
17712
  ctx.addDocument({
17700
- path: path24,
17713
+ path: path25,
17701
17714
  title,
17702
17715
  content,
17703
17716
  format: "markdown",
@@ -17706,7 +17719,7 @@ var init_tools = __esm({
17706
17719
  workspaceId: ctx.workspaceId
17707
17720
  });
17708
17721
  ctx.addActivity("vault", "created document", title);
17709
- return `Document "${title}" created at "${path24}".`;
17722
+ return `Document "${title}" created at "${path25}".`;
17710
17723
  }
17711
17724
  }
17712
17725
  ];
@@ -18917,6 +18930,9 @@ var init_openai_compatible = __esm({
18917
18930
  // Pay-per-token API users can override with `/provider custom https://api.z.ai/api/paas/v4`.
18918
18931
  "glm": "https://api.z.ai/api/coding/paas/v4",
18919
18932
  "grok": "https://api.x.ai/v1",
18933
+ // DeepSeek global platform (OpenAI-compatible). Chat → /chat/completions,
18934
+ // discovery → /models against this same host.
18935
+ "deepseek": "https://api.deepseek.com",
18920
18936
  "custom": ""
18921
18937
  };
18922
18938
  }
@@ -20212,11 +20228,11 @@ var init_synthesisAudit = __esm({
20212
20228
  import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
20213
20229
  import { join as join2 } from "node:path";
20214
20230
  function loadNfrSpec(zelariRoot) {
20215
- const path24 = join2(zelariRoot, "nfr-spec.json");
20216
- if (!existsSync8(path24))
20231
+ const path25 = join2(zelariRoot, "nfr-spec.json");
20232
+ if (!existsSync8(path25))
20217
20233
  return null;
20218
20234
  try {
20219
- const raw = JSON.parse(readFileSync6(path24, "utf8"));
20235
+ const raw = JSON.parse(readFileSync6(path25, "utf8"));
20220
20236
  if (raw.version !== 1 || !Array.isArray(raw.targets))
20221
20237
  return null;
20222
20238
  return raw;
@@ -22405,9 +22421,9 @@ var init_types2 = __esm({
22405
22421
  import { readFileSync as readFileSync11 } from "node:fs";
22406
22422
  import { join as join8 } from "node:path";
22407
22423
  function readLessonsDeduped(zelariRoot) {
22408
- const path24 = join8(zelariRoot, LESSONS_FILE);
22424
+ const path25 = join8(zelariRoot, LESSONS_FILE);
22409
22425
  try {
22410
- const raw = readFileSync11(path24, "utf8");
22426
+ const raw = readFileSync11(path25, "utf8");
22411
22427
  const byId = /* @__PURE__ */ new Map();
22412
22428
  for (const line of raw.split(/\r?\n/)) {
22413
22429
  if (!line.trim())
@@ -22508,8 +22524,8 @@ function keywordsFrom(check2, signature) {
22508
22524
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
22509
22525
  }
22510
22526
  function writeLesson(zelariRoot, lesson) {
22511
- const path24 = join9(zelariRoot, LESSONS_FILE);
22512
- appendFileSync2(path24, `${JSON.stringify(lesson)}
22527
+ const path25 = join9(zelariRoot, LESSONS_FILE);
22528
+ appendFileSync2(path25, `${JSON.stringify(lesson)}
22513
22529
  `, "utf8");
22514
22530
  }
22515
22531
  function findSimilar(lessons, signature) {
@@ -23649,28 +23665,28 @@ var init_storage = __esm({
23649
23665
  VALID_SCALARS = /^(true|false|null|~)$/i;
23650
23666
  Storage = class {
23651
23667
  /** Read a Markdown file with frontmatter. Throws if not found. */
23652
- read(path24) {
23653
- if (!existsSync13(path24)) {
23654
- throw new Error(`File not found: ${path24}`);
23668
+ read(path25) {
23669
+ if (!existsSync13(path25)) {
23670
+ throw new Error(`File not found: ${path25}`);
23655
23671
  }
23656
- const md = readFileSync13(path24, "utf8");
23672
+ const md = readFileSync13(path25, "utf8");
23657
23673
  return parseFrontmatter(md);
23658
23674
  }
23659
23675
  /** Read a Markdown file; returns null if not found. */
23660
- readIfExists(path24) {
23661
- if (!existsSync13(path24)) return null;
23662
- return this.read(path24);
23676
+ readIfExists(path25) {
23677
+ if (!existsSync13(path25)) return null;
23678
+ return this.read(path25);
23663
23679
  }
23664
23680
  /**
23665
23681
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
23666
23682
  * The meta object is serialized as YAML frontmatter; body as Markdown.
23667
23683
  */
23668
- write(path24, meta3, body) {
23669
- mkdirSync7(dirname(path24), { recursive: true });
23670
- const tmp = path24 + ".tmp-" + process.pid;
23684
+ write(path25, meta3, body) {
23685
+ mkdirSync7(dirname(path25), { recursive: true });
23686
+ const tmp = path25 + ".tmp-" + process.pid;
23671
23687
  const md = serializeFrontmatter(meta3, body);
23672
23688
  writeFileSync11(tmp, md, "utf8");
23673
- renameSync2(tmp, path24);
23689
+ renameSync2(tmp, path25);
23674
23690
  }
23675
23691
  /** List all .md files in a directory (non-recursive). */
23676
23692
  listMarkdown(dir) {
@@ -23748,8 +23764,8 @@ function readPlan(ctx) {
23748
23764
  } catch {
23749
23765
  }
23750
23766
  }
23751
- const path24 = workspaceFile(ctx.rootDir, "plan");
23752
- const doc = ctx.storage.readIfExists(path24);
23767
+ const path25 = workspaceFile(ctx.rootDir, "plan");
23768
+ const doc = ctx.storage.readIfExists(path25);
23753
23769
  if (!doc) return { phases: [], tasks: [], milestones: [] };
23754
23770
  const meta3 = doc.meta;
23755
23771
  return {
@@ -23914,7 +23930,7 @@ function addMilestoneRecord(ctx, summary, input) {
23914
23930
  dueDate: input.dueDate,
23915
23931
  targetVersion: version2
23916
23932
  });
23917
- const path24 = join14(ctx.rootDir, "milestones", `${id}.md`);
23933
+ const path25 = join14(ctx.rootDir, "milestones", `${id}.md`);
23918
23934
  const meta3 = {
23919
23935
  kind: "milestone",
23920
23936
  id,
@@ -23931,7 +23947,7 @@ function addMilestoneRecord(ctx, summary, input) {
23931
23947
  `Target version: ${version2}`,
23932
23948
  ""
23933
23949
  ].join("\n");
23934
- ctx.storage.write(path24, meta3, body);
23950
+ ctx.storage.write(path25, meta3, body);
23935
23951
  return { id, created: true };
23936
23952
  }
23937
23953
  function readPlanSummary(ctx) {
@@ -24135,7 +24151,7 @@ function addIdeaStub(ctx) {
24135
24151
  const tags = args["tags"] ?? [];
24136
24152
  const category = args["category"] ?? "General";
24137
24153
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
24138
- const path24 = workspaceArtifact(ctx.rootDir, "decisions", id);
24154
+ const path25 = workspaceArtifact(ctx.rootDir, "decisions", id);
24139
24155
  const meta3 = {
24140
24156
  kind: "adr",
24141
24157
  status: "proposed",
@@ -24161,7 +24177,7 @@ function addIdeaStub(ctx) {
24161
24177
  ...consequences.map((c) => `- ${c}`),
24162
24178
  ""
24163
24179
  ].join("\n");
24164
- ctx.storage.write(path24, meta3, body);
24180
+ ctx.storage.write(path25, meta3, body);
24165
24181
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
24166
24182
  });
24167
24183
  }
@@ -24243,14 +24259,14 @@ function createDocumentStub(ctx) {
24243
24259
  ctx.storage.write(risksPath, riskMeta, content);
24244
24260
  return `Document "${title}" created at risks.md (workspace root).`;
24245
24261
  }
24246
- const path24 = workspaceArtifact(ctx.rootDir, "docs", slug);
24262
+ const path25 = workspaceArtifact(ctx.rootDir, "docs", slug);
24247
24263
  const meta3 = {
24248
24264
  kind: "doc",
24249
24265
  id: slug,
24250
24266
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
24251
24267
  tags
24252
24268
  };
24253
- ctx.storage.write(path24, meta3, content);
24269
+ ctx.storage.write(path25, meta3, content);
24254
24270
  return `Document "${title}" created at docs/${slug}.md.`;
24255
24271
  });
24256
24272
  }
@@ -25017,10 +25033,10 @@ import { createHash as createHash2 } from "node:crypto";
25017
25033
  import { join as join17 } from "node:path";
25018
25034
  import { readFile } from "node:fs/promises";
25019
25035
  async function readPackageJson2(projectRoot) {
25020
- const path24 = join17(projectRoot, "package.json");
25021
- if (!existsSync17(path24)) return null;
25036
+ const path25 = join17(projectRoot, "package.json");
25037
+ if (!existsSync17(path25)) return null;
25022
25038
  try {
25023
- return JSON.parse(await readFile(path24, "utf8"));
25039
+ return JSON.parse(await readFile(path25, "utf8"));
25024
25040
  } catch {
25025
25041
  return null;
25026
25042
  }
@@ -25102,9 +25118,9 @@ async function genBuild(ctx) {
25102
25118
  ].join("\n");
25103
25119
  }
25104
25120
  async function genOpenQuestions(ctx) {
25105
- const path24 = join17(ctx.rootDir, "risks.md");
25106
- if (!existsSync17(path24)) return "_No open questions._";
25107
- const content = readFileSync17(path24, "utf8");
25121
+ const path25 = join17(ctx.rootDir, "risks.md");
25122
+ if (!existsSync17(path25)) return "_No open questions._";
25123
+ const content = readFileSync17(path25, "utf8");
25108
25124
  const lines = content.split("\n");
25109
25125
  const questions = [];
25110
25126
  let currentTitle = "";
@@ -25667,8 +25683,8 @@ async function runPostCouncilHook(ctx, options) {
25667
25683
  sources: scope.sources
25668
25684
  } : void 0
25669
25685
  });
25670
- const path24 = writeCouncilCompletion(ctx.rootDir, completion);
25671
- completionHook = { ran: true, path: path24, completion };
25686
+ const path25 = writeCouncilCompletion(ctx.rootDir, completion);
25687
+ completionHook = { ran: true, path: path25, completion };
25672
25688
  } catch (err) {
25673
25689
  completionHook = {
25674
25690
  ran: true,
@@ -26156,6 +26172,252 @@ var init_zelariMission = __esm({
26156
26172
  }
26157
26173
  });
26158
26174
 
26175
+ // src/cli/utils/doctor.ts
26176
+ var doctor_exports = {};
26177
+ __export(doctor_exports, {
26178
+ runDoctor: () => runDoctor
26179
+ });
26180
+ import { execSync } from "node:child_process";
26181
+ import { existsSync as existsSync26, readFileSync as readFileSync23, readlinkSync, statSync as statSync6 } from "node:fs";
26182
+ import { createRequire as createRequire2 } from "node:module";
26183
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
26184
+ import path24 from "node:path";
26185
+ function findPackageRoot(start) {
26186
+ let dir = start;
26187
+ for (let i = 0; i < 6; i += 1) {
26188
+ const candidate = path24.join(dir, "package.json");
26189
+ if (existsSync26(candidate)) {
26190
+ try {
26191
+ const pkg = JSON.parse(readFileSync23(candidate, "utf8"));
26192
+ if (pkg.name === "zelari-code") return dir;
26193
+ } catch {
26194
+ }
26195
+ }
26196
+ const parent = path24.dirname(dir);
26197
+ if (parent === dir) break;
26198
+ dir = parent;
26199
+ }
26200
+ return path24.resolve(__dirname3, "..", "..", "..");
26201
+ }
26202
+ function tryExec(cmd) {
26203
+ try {
26204
+ return execSync(cmd, {
26205
+ encoding: "utf8",
26206
+ stdio: ["ignore", "pipe", "ignore"]
26207
+ }).trim();
26208
+ } catch {
26209
+ return "";
26210
+ }
26211
+ }
26212
+ function readPackageJson3() {
26213
+ try {
26214
+ const pkgPath = path24.join(packageRoot, "package.json");
26215
+ return JSON.parse(readFileSync23(pkgPath, "utf8"));
26216
+ } catch {
26217
+ return null;
26218
+ }
26219
+ }
26220
+ function getGlobalPrefix() {
26221
+ return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || tryExec("npm prefix -g");
26222
+ }
26223
+ function checkShim(pkgName) {
26224
+ const prefix = getGlobalPrefix();
26225
+ if (!prefix) {
26226
+ return WARN("npm global prefix not detectable (run inside an npm context)");
26227
+ }
26228
+ const isWin = process.platform === "win32";
26229
+ const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
26230
+ const shimPath = path24.join(prefix, shimName);
26231
+ if (!existsSync26(shimPath)) {
26232
+ return FAIL(
26233
+ `shim not found at ${shimPath}
26234
+ fix: npm install -g ${pkgName}@latest --force`
26235
+ );
26236
+ }
26237
+ try {
26238
+ const st = statSync6(shimPath);
26239
+ if (isWin) {
26240
+ const content = readFileSync23(shimPath, "utf8");
26241
+ if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
26242
+ return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
26243
+ }
26244
+ return FAIL(
26245
+ `shim at ${shimPath} does not reference ${pkgName}/bin/
26246
+ fix: npm install -g ${pkgName}@latest --force`
26247
+ );
26248
+ }
26249
+ let target;
26250
+ try {
26251
+ target = readlinkSync(shimPath);
26252
+ } catch {
26253
+ return FAIL(
26254
+ `shim at ${shimPath} is not a symlink (POSIX global installs should be symlinks)
26255
+ fix: npm install -g ${pkgName}@latest --force`
26256
+ );
26257
+ }
26258
+ const resolved = path24.resolve(path24.dirname(shimPath), target);
26259
+ const expected = path24.join(
26260
+ prefix,
26261
+ "node_modules",
26262
+ pkgName,
26263
+ "bin",
26264
+ "zelari-code.js"
26265
+ );
26266
+ if (resolved === expected) {
26267
+ return OK(`shim OK at ${shimPath} \u2192 ${resolved}`);
26268
+ }
26269
+ return FAIL(
26270
+ `shim at ${shimPath} points to ${resolved}
26271
+ expected ${expected}
26272
+ fix: npm install -g ${pkgName}@latest --force`
26273
+ );
26274
+ } catch (err) {
26275
+ return FAIL(
26276
+ `could not inspect shim: ${err instanceof Error ? err.message : String(err)}`
26277
+ );
26278
+ }
26279
+ }
26280
+ function checkNode(pkg) {
26281
+ const raw = tryExec("node --version");
26282
+ if (!raw) {
26283
+ return FAIL("`node` not found on PATH");
26284
+ }
26285
+ const m = raw.match(/^v?(\d+)\.(\d+)\.(\d+)/);
26286
+ if (!m) {
26287
+ return WARN(`could not parse node version: ${raw}`);
26288
+ }
26289
+ const major = Number(m[1]);
26290
+ if (major < 20) {
26291
+ return FAIL(
26292
+ `node ${raw} is older than the required engines.node (>= 20.0.0)`
26293
+ );
26294
+ }
26295
+ return OK(`node ${raw}`);
26296
+ }
26297
+ function checkBundle() {
26298
+ const bundle = path24.join(packageRoot, "dist", "cli", "main.bundled.js");
26299
+ if (!existsSync26(bundle)) {
26300
+ return FAIL(
26301
+ `dist/cli/main.bundled.js missing at ${bundle}
26302
+ fix: npm run build:cli (then reinstall or run via tsx)`
26303
+ );
26304
+ }
26305
+ try {
26306
+ const st = statSync6(bundle);
26307
+ return OK(`bundle OK (${(st.size / 1024 / 1024).toFixed(2)} MB)`);
26308
+ } catch (err) {
26309
+ return FAIL(
26310
+ `could not stat bundle: ${err instanceof Error ? err.message : String(err)}`
26311
+ );
26312
+ }
26313
+ }
26314
+ function checkRuntimeDeps() {
26315
+ const required2 = ["react", "react-dom", "ink", "ink-text-input", "zod"];
26316
+ const missing = [];
26317
+ for (const dep of required2) {
26318
+ try {
26319
+ const localReq = createRequire2(path24.join(packageRoot, "package.json"));
26320
+ localReq.resolve(dep);
26321
+ } catch {
26322
+ missing.push(dep);
26323
+ }
26324
+ }
26325
+ if (missing.length === 0) {
26326
+ return OK(
26327
+ "runtime deps resolvable (react, react-dom, ink, ink-text-input, zod)"
26328
+ );
26329
+ }
26330
+ return FAIL(
26331
+ `missing runtime deps: ${missing.join(", ")}
26332
+ fix: npm install -g ${"<pkg>"} --force (then reopen the terminal)`
26333
+ );
26334
+ }
26335
+ function checkPath() {
26336
+ const prefix = getGlobalPrefix();
26337
+ if (!prefix) return WARN("npm global prefix not detectable");
26338
+ const pathSep = process.platform === "win32" ? ";" : ":";
26339
+ const pathDirs = (process.env.PATH || "").split(pathSep);
26340
+ if (pathDirs.includes(prefix)) {
26341
+ return OK(`PATH includes npm prefix (${prefix})`);
26342
+ }
26343
+ return WARN(
26344
+ `PATH does not include npm prefix (${prefix})
26345
+ symptom: "zelari-code: command not found" after install
26346
+ fix (POSIX): export PATH="$(npm prefix -g)/bin:$PATH"
26347
+ fix (Windows): $env:Path = "$(npm prefix -g);$env:Path"`
26348
+ );
26349
+ }
26350
+ function runDoctor() {
26351
+ const pkg = readPackageJson3();
26352
+ const pkgName = pkg?.name ?? "zelari-code";
26353
+ const checks = [
26354
+ { name: "node", run: () => checkNode(pkg) },
26355
+ { name: "bin shim", run: () => checkShim(pkgName) },
26356
+ { name: "cli bundle", run: () => checkBundle() },
26357
+ { name: "runtime deps", run: () => checkRuntimeDeps() },
26358
+ { name: "PATH", run: () => checkPath() }
26359
+ ];
26360
+ console.log(`zelari-code doctor (v${pkg?.version ?? "unknown"})`);
26361
+ console.log("platform:", process.platform, process.arch);
26362
+ console.log("node: ", process.version);
26363
+ console.log("prefix: ", getGlobalPrefix() || "(unknown)");
26364
+ console.log("root: ", packageRoot);
26365
+ console.log("");
26366
+ let criticalFails = 0;
26367
+ let warns = 0;
26368
+ for (const c of checks) {
26369
+ let result;
26370
+ try {
26371
+ result = c.run();
26372
+ } catch (err) {
26373
+ result = FAIL(
26374
+ `unexpected error: ${err instanceof Error ? err.message : String(err)}`
26375
+ );
26376
+ }
26377
+ const tag = result.ok ? "OK " : result.severity === "critical" ? "FAIL" : "WARN";
26378
+ const color = result.ok ? "\x1B[32m" : result.severity === "critical" ? "\x1B[31m" : "\x1B[33m";
26379
+ const reset = "\x1B[0m";
26380
+ console.log(
26381
+ ` ${color}${tag}${reset} ${c.name.padEnd(14)}${result.message.replace(/\n/g, "\n ")}`
26382
+ );
26383
+ if (!result.ok) {
26384
+ if (result.severity === "critical") criticalFails += 1;
26385
+ else warns += 1;
26386
+ }
26387
+ }
26388
+ console.log("");
26389
+ if (criticalFails === 0 && warns === 0) {
26390
+ console.log("\x1B[32m\u2714 all checks passed\x1B[0m");
26391
+ return true;
26392
+ }
26393
+ console.log(`\u2717 ${criticalFails} critical failure(s), ${warns} warning(s)`);
26394
+ return criticalFails === 0;
26395
+ }
26396
+ var require3, __dirname3, packageRoot, OK, FAIL, WARN;
26397
+ var init_doctor = __esm({
26398
+ "src/cli/utils/doctor.ts"() {
26399
+ "use strict";
26400
+ require3 = createRequire2(import.meta.url);
26401
+ __dirname3 = path24.dirname(fileURLToPath2(import.meta.url));
26402
+ packageRoot = findPackageRoot(__dirname3);
26403
+ OK = (message) => ({
26404
+ ok: true,
26405
+ message,
26406
+ severity: "critical"
26407
+ });
26408
+ FAIL = (message, severity = "critical") => ({
26409
+ ok: false,
26410
+ message,
26411
+ severity
26412
+ });
26413
+ WARN = (message) => ({
26414
+ ok: false,
26415
+ message,
26416
+ severity: "warn"
26417
+ });
26418
+ }
26419
+ });
26420
+
26159
26421
  // src/cli/main.ts
26160
26422
  import React13 from "react";
26161
26423
  import { render } from "ink";
@@ -26708,6 +26970,8 @@ var PROVIDER_BASE_URLS = {
26708
26970
  // Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
26709
26971
  "glm": "https://api.z.ai/api/coding/paas/v4",
26710
26972
  "minimax": "https://api.minimax.io/v1",
26973
+ // Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
26974
+ "deepseek": "https://api.deepseek.com",
26711
26975
  "openai-compatible": "https://api.x.ai/v1"
26712
26976
  };
26713
26977
  async function resolveDiscoveryBaseUrl(provider, options) {
@@ -30090,6 +30354,10 @@ var PRICES_PER_MILLION = {
30090
30354
  "MiniMax-M2.5": { input: 0.2, output: 1.1 },
30091
30355
  "MiniMax-M2": { input: 0.2, output: 1.1 },
30092
30356
  "MiniMax-M2-her": { input: 0.3, output: 1.2 },
30357
+ // DeepSeek (global platform) — estimated list prices; override via
30358
+ // ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
30359
+ "deepseek-v4-flash": { input: 0.14, output: 0.28 },
30360
+ "deepseek-v4-pro": { input: 0.55, output: 2.19 },
30093
30361
  // OpenAI (for openai-compatible fallback)
30094
30362
  "gpt-4o": { input: 2.5, output: 10 },
30095
30363
  "gpt-4o-mini": { input: 0.15, output: 0.6 },
@@ -31137,14 +31405,14 @@ ${formatSkillList(availableSkills)}`
31137
31405
  return {
31138
31406
  handled: true,
31139
31407
  kind: "provider_picker",
31140
- message: "Usage: /provider \u2014 pick from a list\n /provider <name> \u2014 switch active provider\n /provider list \u2014 print the current provider + available ids\n /provider custom <baseUrl> \u2014 set custom base URL (Ollama, LM Studio, vLLM, ...)\n /provider custom clear \u2014 clear the custom override\n /provider <name> refresh \u2014 force token refresh (v3-F)\n /provider <name> status \u2014 show key source, expiry, refresh impl (v3-F)\nAvailable: openai-compatible, minimax, glm, grok, custom"
31408
+ message: "Usage: /provider \u2014 pick from a list\n /provider <name> \u2014 switch active provider\n /provider list \u2014 print the current provider + available ids\n /provider custom <baseUrl> \u2014 set custom base URL (Ollama, LM Studio, vLLM, ...)\n /provider custom clear \u2014 clear the custom override\n /provider <name> refresh \u2014 force token refresh (v3-F)\n /provider <name> status \u2014 show key source, expiry, refresh impl (v3-F)\nAvailable: openai-compatible, minimax, glm, grok, deepseek, custom"
31141
31409
  };
31142
31410
  }
31143
31411
  if (subcommand === "list") {
31144
31412
  return {
31145
31413
  handled: true,
31146
31414
  kind: "provider_list",
31147
- message: "Usage: /provider <name> \u2014 switch active provider\nAvailable: openai-compatible, minimax, glm, grok, custom"
31415
+ message: "Usage: /provider <name> \u2014 switch active provider\nAvailable: openai-compatible, minimax, glm, grok, deepseek, custom"
31148
31416
  };
31149
31417
  }
31150
31418
  if (subcommand === "custom") {
@@ -31629,14 +31897,23 @@ async function handleUpdateCheck(ctx) {
31629
31897
  Run \`/update --yes\` to install. You'll need to restart manually after.`
31630
31898
  );
31631
31899
  } else {
31632
- appendSystem(ctx.setMessages, `[update] up to date (${info.currentVersion})`);
31900
+ appendSystem(
31901
+ ctx.setMessages,
31902
+ `[update] up to date (${info.currentVersion})`
31903
+ );
31633
31904
  }
31634
31905
  } catch (err) {
31635
- appendSystem(ctx.setMessages, `[update error] ${err instanceof Error ? err.message : String(err)}`);
31906
+ appendSystem(
31907
+ ctx.setMessages,
31908
+ `[update error] ${err instanceof Error ? err.message : String(err)}`
31909
+ );
31636
31910
  }
31637
31911
  }
31638
31912
  async function handleUpdatePerform(ctx) {
31639
- appendSystem(ctx.setMessages, "[update] running `npm install -g zelari-code@latest`...");
31913
+ appendSystem(
31914
+ ctx.setMessages,
31915
+ "[update] running `npm install -g zelari-code@latest`..."
31916
+ );
31640
31917
  try {
31641
31918
  const { performUpdate: performUpdate2 } = await Promise.resolve().then(() => (init_updater(), updater_exports));
31642
31919
  const res = await performUpdate2();
@@ -31649,18 +31926,48 @@ Please restart zelari-code manually to use the new version.
31649
31926
  (exit with /exit or Ctrl+C, then run \`zelari-code\` again)`
31650
31927
  );
31651
31928
  } else {
31929
+ const output = res.output?.trim() || "(empty)";
31930
+ const hint = buildUpdateFailureHint(
31931
+ res.error ?? "",
31932
+ output,
31933
+ res.exitCode
31934
+ );
31652
31935
  appendSystem(
31653
31936
  ctx.setMessages,
31654
31937
  `[update] \u274C failed: ${res.error ?? "unknown error"}
31938
+ exit code: ${res.exitCode ?? "n/a"}
31655
31939
 
31656
31940
  npm output:
31657
- ${res.output || "(empty)"}`
31941
+ ${output}
31942
+
31943
+ ` + hint
31658
31944
  );
31659
31945
  }
31660
31946
  } catch (err) {
31661
- appendSystem(ctx.setMessages, `[update error] ${err instanceof Error ? err.message : String(err)}`);
31947
+ appendSystem(
31948
+ ctx.setMessages,
31949
+ `[update error] ${err instanceof Error ? err.message : String(err)}`
31950
+ );
31662
31951
  }
31663
31952
  }
31953
+ function buildUpdateFailureHint(error51, output, exitCode) {
31954
+ const haystack = `${error51}
31955
+ ${output}`.toLowerCase();
31956
+ const isWin = process.platform === "win32";
31957
+ if (haystack.includes("eresolve") || haystack.includes("epeerinvalid") || haystack.includes("peer dep")) {
31958
+ return "\u{1F4A1} hint: peer-dependency conflict. Try:\n npm install -g zelari-code@latest --legacy-peer-deps\n or: npm install -g zelari-code@latest --force";
31959
+ }
31960
+ if (haystack.includes("eacces") || haystack.includes("eperm")) {
31961
+ return "\u{1F4A1} hint: permission denied. On macOS/Linux, avoid sudo with npm:\n https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally\n On Windows, re-run the terminal as Administrator.";
31962
+ }
31963
+ if (haystack.includes("enoent") && haystack.includes("npm")) {
31964
+ return '\u{1F4A1} hint: `npm` was not found on PATH for the spawned shell.\n Re-open the terminal so it picks up your current PATH, or set:\n export PATH="$(npm prefix -g)/bin:$PATH" (POSIX)\n $env:Path = "$(npm prefix -g);$env:Path" (PowerShell)';
31965
+ }
31966
+ if (haystack.includes("zelari-code") && (haystack.includes("not found") || haystack.includes("eexist") || haystack.includes("ebusy") || haystack.includes("shim") || exitCode === 0)) {
31967
+ return "\u{1F4A1} hint: the install may have completed but the global bin shim\n is missing or stale (common on Windows + npm 10/11 after major\n upgrades). To repair:\n npm install -g zelari-code@latest --force\n Then re-open the terminal. If `zelari-code` is still not found,\n run the doctor: `zelari-code doctor`";
31968
+ }
31969
+ 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" : "");
31970
+ }
31664
31971
 
31665
31972
  // src/cli/slashHandlers/promoteMember.ts
31666
31973
  import { promises as fs15 } from "node:fs";
@@ -32053,15 +32360,15 @@ async function validateApiKey(providerId, apiKey, options = {}) {
32053
32360
 
32054
32361
  // src/cli/slashHandlers/provider.ts
32055
32362
  init_providerConfig();
32056
- var UNKNOWN_PROVIDER_MSG = (id) => `[provider] unknown: ${id}. Available: openai-compatible, minimax, glm, grok, custom`;
32057
- var DISCOVERABLE_PROVIDERS = ["grok", "glm", "minimax", "openai-compatible"];
32363
+ var UNKNOWN_PROVIDER_MSG = (id) => `[provider] unknown: ${id}. Available: openai-compatible, minimax, glm, grok, deepseek, custom`;
32364
+ var DISCOVERABLE_PROVIDERS = ["grok", "glm", "minimax", "deepseek", "openai-compatible"];
32058
32365
  function handleProviderList(ctx) {
32059
32366
  const list = getActiveProvider();
32060
32367
  const customEp = getCustomEndpoint(list.id);
32061
32368
  const epHint = customEp ? ` \u2014 custom endpoint: ${customEp}` : "";
32062
32369
  appendSystem(
32063
32370
  ctx.setMessages,
32064
- `[provider] current: ${list.displayName} (model: ${ctx.activeModel})${epHint} \u2014 available: openai-compatible, minimax, glm, grok, custom`
32371
+ `[provider] current: ${list.displayName} (model: ${ctx.activeModel})${epHint} \u2014 available: openai-compatible, minimax, glm, grok, deepseek, custom`
32065
32372
  );
32066
32373
  }
32067
32374
  function handleProviderSet(ctx, providerId) {
@@ -32940,7 +33247,8 @@ var providerDefaults = {
32940
33247
  "openai-compatible": "grok-4",
32941
33248
  "grok": "grok-4",
32942
33249
  "minimax": "MiniMax-chat-latest",
32943
- "glm": "glm-4.5"
33250
+ "glm": "glm-4.5",
33251
+ "deepseek": "deepseek-v4-pro"
32944
33252
  };
32945
33253
  function App() {
32946
33254
  const [input, setInput] = useState8("");
@@ -32970,7 +33278,7 @@ function App() {
32970
33278
  );
32971
33279
  useEffect7(() => {
32972
33280
  const id = activeProviderSpec.id;
32973
- if (!["grok", "glm", "minimax", "openai-compatible"].includes(id)) return;
33281
+ if (!["grok", "glm", "minimax", "deepseek", "openai-compatible"].includes(id)) return;
32974
33282
  if (!isModelsCacheStale(id)) return;
32975
33283
  discoverModelsInBackground(id, {});
32976
33284
  }, []);
@@ -33891,9 +34199,14 @@ function pickRootComponent() {
33891
34199
  console.log(`zelari-code v${VERSION}`);
33892
34200
  process.exit(0);
33893
34201
  }
34202
+ if (argv.includes("--doctor") || argv.includes("doctor")) {
34203
+ const { runDoctor: runDoctor2 } = (init_doctor(), __toCommonJS(doctor_exports));
34204
+ const healthy = runDoctor2();
34205
+ process.exit(healthy ? 0 : 1);
34206
+ }
33894
34207
  if (argv.includes("--help") || argv.includes("-h")) {
33895
34208
  console.log(
33896
- "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 --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 ANATHEMA_DEV=1 Disable background update check\n"
34209
+ "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 --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 ANATHEMA_DEV=1 Disable background update check\n"
33897
34210
  );
33898
34211
  process.exit(0);
33899
34212
  }