reasonix 0.12.20 → 0.12.21

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.
package/dist/cli/index.js CHANGED
@@ -181,8 +181,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
181
181
  }
182
182
  function sleep(ms, signal) {
183
183
  if (ms <= 0) return Promise.resolve();
184
- return new Promise((resolve13, reject) => {
185
- const timer = setTimeout(resolve13, ms);
184
+ return new Promise((resolve14, reject) => {
185
+ const timer = setTimeout(resolve14, ms);
186
186
  if (signal) {
187
187
  const onAbort = () => {
188
188
  clearTimeout(timer);
@@ -668,7 +668,7 @@ function matchesTool(hook, toolName) {
668
668
  }
669
669
  var HOOK_OUTPUT_CAP_BYTES = 256 * 1024;
670
670
  function defaultSpawner(input) {
671
- return new Promise((resolve13) => {
671
+ return new Promise((resolve14) => {
672
672
  const child = spawn(input.command, {
673
673
  cwd: input.cwd,
674
674
  shell: true,
@@ -713,7 +713,7 @@ function defaultSpawner(input) {
713
713
  child.stderr.on("data", (chunk) => onChunk("stderr", chunk));
714
714
  child.once("error", (err) => {
715
715
  clearTimeout(timer);
716
- resolve13({
716
+ resolve14({
717
717
  exitCode: null,
718
718
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
719
719
  stderr: Buffer.concat(stderrChunks).toString("utf8"),
@@ -724,7 +724,7 @@ function defaultSpawner(input) {
724
724
  });
725
725
  child.once("close", (code) => {
726
726
  clearTimeout(timer);
727
- resolve13({
727
+ resolve14({
728
728
  exitCode: code,
729
729
  stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(),
730
730
  stderr: Buffer.concat(stderrChunks).toString("utf8").trim(),
@@ -2646,8 +2646,8 @@ var CacheFirstLoop = class {
2646
2646
  }
2647
2647
  );
2648
2648
  for (let k = 0; k < budget2; k++) {
2649
- const sample = queue.shift() ?? await new Promise((resolve13) => {
2650
- waiter = resolve13;
2649
+ const sample = queue.shift() ?? await new Promise((resolve14) => {
2650
+ waiter = resolve14;
2651
2651
  });
2652
2652
  yield {
2653
2653
  turn: this._turn,
@@ -5444,7 +5444,7 @@ async function runCommand(cmd, opts) {
5444
5444
  };
5445
5445
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
5446
5446
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
5447
- return await new Promise((resolve13, reject) => {
5447
+ return await new Promise((resolve14, reject) => {
5448
5448
  let child;
5449
5449
  try {
5450
5450
  child = spawn3(bin, args, effectiveSpawnOpts);
@@ -5489,7 +5489,7 @@ async function runCommand(cmd, opts) {
5489
5489
  const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
5490
5490
 
5491
5491
  [\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
5492
- resolve13({ exitCode: code, output, timedOut });
5492
+ resolve14({ exitCode: code, output, timedOut });
5493
5493
  });
5494
5494
  });
5495
5495
  }
@@ -6832,7 +6832,7 @@ var McpClient = class {
6832
6832
  const id = this.nextId++;
6833
6833
  const frame = { jsonrpc: "2.0", id, method, params };
6834
6834
  let abortHandler = null;
6835
- const promise = new Promise((resolve13, reject) => {
6835
+ const promise = new Promise((resolve14, reject) => {
6836
6836
  const timeout = setTimeout(() => {
6837
6837
  this.pending.delete(id);
6838
6838
  if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
@@ -6841,7 +6841,7 @@ var McpClient = class {
6841
6841
  );
6842
6842
  }, this.requestTimeoutMs);
6843
6843
  this.pending.set(id, {
6844
- resolve: resolve13,
6844
+ resolve: resolve14,
6845
6845
  reject,
6846
6846
  timeout
6847
6847
  });
@@ -6964,12 +6964,12 @@ var StdioTransport = class {
6964
6964
  }
6965
6965
  async send(message) {
6966
6966
  if (this.closed) throw new Error("MCP transport is closed");
6967
- return new Promise((resolve13, reject) => {
6967
+ return new Promise((resolve14, reject) => {
6968
6968
  const line = `${JSON.stringify(message)}
6969
6969
  `;
6970
6970
  this.child.stdin.write(line, "utf8", (err) => {
6971
6971
  if (err) reject(err);
6972
- else resolve13();
6972
+ else resolve14();
6973
6973
  });
6974
6974
  });
6975
6975
  }
@@ -6980,8 +6980,8 @@ var StdioTransport = class {
6980
6980
  continue;
6981
6981
  }
6982
6982
  if (this.closed) return;
6983
- const next = await new Promise((resolve13) => {
6984
- this.waiters.push(resolve13);
6983
+ const next = await new Promise((resolve14) => {
6984
+ this.waiters.push(resolve14);
6985
6985
  });
6986
6986
  if (next === null) return;
6987
6987
  yield next;
@@ -7047,8 +7047,8 @@ var SseTransport = class {
7047
7047
  constructor(opts) {
7048
7048
  this.url = opts.url;
7049
7049
  this.headers = opts.headers ?? {};
7050
- this.endpointReady = new Promise((resolve13, reject) => {
7051
- this.resolveEndpoint = resolve13;
7050
+ this.endpointReady = new Promise((resolve14, reject) => {
7051
+ this.resolveEndpoint = resolve14;
7052
7052
  this.rejectEndpoint = reject;
7053
7053
  });
7054
7054
  this.endpointReady.catch(() => void 0);
@@ -7075,8 +7075,8 @@ var SseTransport = class {
7075
7075
  continue;
7076
7076
  }
7077
7077
  if (this.closed) return;
7078
- const next = await new Promise((resolve13) => {
7079
- this.waiters.push(resolve13);
7078
+ const next = await new Promise((resolve14) => {
7079
+ this.waiters.push(resolve14);
7080
7080
  });
7081
7081
  if (next === null) return;
7082
7082
  yield next;
@@ -7263,8 +7263,8 @@ var StreamableHttpTransport = class {
7263
7263
  continue;
7264
7264
  }
7265
7265
  if (this.closed) return;
7266
- const next = await new Promise((resolve13) => {
7267
- this.waiters.push(resolve13);
7266
+ const next = await new Promise((resolve14) => {
7267
+ this.waiters.push(resolve14);
7268
7268
  });
7269
7269
  if (next === null) return;
7270
7270
  yield next;
@@ -9732,7 +9732,7 @@ async function startOllamaDaemon(opts = {}) {
9732
9732
  return { ready: false, pid };
9733
9733
  }
9734
9734
  async function pullOllamaModel(modelName, opts = {}) {
9735
- return new Promise((resolve13) => {
9735
+ return new Promise((resolve14) => {
9736
9736
  const child = spawn5("ollama", ["pull", modelName], {
9737
9737
  stdio: ["ignore", "pipe", "pipe"],
9738
9738
  windowsHide: true
@@ -9744,8 +9744,8 @@ async function pullOllamaModel(modelName, opts = {}) {
9744
9744
  }
9745
9745
  streamLines(child.stdout, (l) => opts.onLine?.(l, "stdout"));
9746
9746
  streamLines(child.stderr, (l) => opts.onLine?.(l, "stderr"));
9747
- child.once("exit", (code) => resolve13(code ?? -1));
9748
- child.once("error", () => resolve13(-1));
9747
+ child.once("exit", (code) => resolve14(code ?? -1));
9748
+ child.once("error", () => resolve14(-1));
9749
9749
  });
9750
9750
  }
9751
9751
  function streamLines(stream, cb) {
@@ -10590,7 +10590,7 @@ var MAX_BODY_BYTES = 256 * 1024;
10590
10590
  async function readBody(req) {
10591
10591
  let total = 0;
10592
10592
  const chunks = [];
10593
- return new Promise((resolve13, reject) => {
10593
+ return new Promise((resolve14, reject) => {
10594
10594
  req.on("data", (chunk) => {
10595
10595
  total += chunk.length;
10596
10596
  if (total > MAX_BODY_BYTES) {
@@ -10600,7 +10600,7 @@ async function readBody(req) {
10600
10600
  }
10601
10601
  chunks.push(chunk);
10602
10602
  });
10603
- req.on("end", () => resolve13(Buffer.concat(chunks).toString("utf8")));
10603
+ req.on("end", () => resolve14(Buffer.concat(chunks).toString("utf8")));
10604
10604
  req.on("error", reject);
10605
10605
  });
10606
10606
  }
@@ -10677,7 +10677,7 @@ function startDashboardServer(ctx, opts = {}) {
10677
10677
  const token = opts.token ?? mintToken();
10678
10678
  const host = opts.host ?? "127.0.0.1";
10679
10679
  const port = opts.port ?? 0;
10680
- return new Promise((resolve13, reject) => {
10680
+ return new Promise((resolve14, reject) => {
10681
10681
  const server = createServer((req, res) => {
10682
10682
  dispatch(req, res, ctx, token).catch((err) => {
10683
10683
  if (!res.headersSent) {
@@ -10698,7 +10698,7 @@ function startDashboardServer(ctx, opts = {}) {
10698
10698
  server.close(() => doneResolve());
10699
10699
  setTimeout(() => server.closeAllConnections?.(), 1e3).unref();
10700
10700
  });
10701
- resolve13({ url, token, port: finalPort, close });
10701
+ resolve14({ url, token, port: finalPort, close });
10702
10702
  });
10703
10703
  });
10704
10704
  }
@@ -11314,8 +11314,8 @@ function SelectRow({
11314
11314
  active,
11315
11315
  marker
11316
11316
  }) {
11317
- const color = item.disabled ? "gray" : active ? "cyan" : void 0;
11318
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React4.createElement(Box3, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, item.hint)) : null);
11317
+ const color2 = item.disabled ? "gray" : active ? "cyan" : void 0;
11318
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { color: color2 }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React4.createElement(Box3, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, item.hint)) : null);
11319
11319
  }
11320
11320
  function findNextEnabled(items, from, step) {
11321
11321
  if (items.length === 0) return 0;
@@ -11530,13 +11530,13 @@ function EditConfirm({ block, onChoose }) {
11530
11530
  ) : null,
11531
11531
  /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, visibleLines.map((line, i) => {
11532
11532
  const trimmed = line.trimStart();
11533
- const color = trimmed.startsWith("+") ? "#4ade80" : trimmed.startsWith("-") ? "#f87171" : void 0;
11534
- const dim = !color;
11533
+ const color2 = trimmed.startsWith("+") ? "#4ade80" : trimmed.startsWith("-") ? "#f87171" : void 0;
11534
+ const dim = !color2;
11535
11535
  return /* @__PURE__ */ React6.createElement(
11536
11536
  Text4,
11537
11537
  {
11538
11538
  key: `diff-${effectiveScroll}-${i}`,
11539
- color,
11539
+ color: color2,
11540
11540
  dimColor: dim
11541
11541
  },
11542
11542
  line
@@ -11570,7 +11570,7 @@ function PlanStateBlock({ planState }) {
11570
11570
  if (planState.rejectedPaths.length)
11571
11571
  fields.push(["rejected", planState.rejectedPaths, "#94a3b8", true]);
11572
11572
  if (fields.length === 0) return null;
11573
- return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", marginBottom: 1 }, fields.map(([label, items, color, dim]) => /* @__PURE__ */ React7.createElement(Box6, { key: label }, /* @__PURE__ */ React7.createElement(Text5, { backgroundColor: color, color: "black", bold: true, dimColor: dim }, ` ${label} ${items.length} `), /* @__PURE__ */ React7.createElement(Text5, null, " "), /* @__PURE__ */ React7.createElement(Text5, { dimColor: dim }, items.join(" \xB7 ")))));
11573
+ return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", marginBottom: 1 }, fields.map(([label, items, color2, dim]) => /* @__PURE__ */ React7.createElement(Box6, { key: label }, /* @__PURE__ */ React7.createElement(Text5, { backgroundColor: color2, color: "black", bold: true, dimColor: dim }, ` ${label} ${items.length} `), /* @__PURE__ */ React7.createElement(Text5, null, " "), /* @__PURE__ */ React7.createElement(Text5, { dimColor: dim }, items.join(" \xB7 ")))));
11574
11574
  }
11575
11575
 
11576
11576
  // src/cli/ui/PlanStepList.tsx
@@ -12481,8 +12481,8 @@ function gradientCells(width, glyph = GLYPH.block) {
12481
12481
  const t2 = width === 1 ? 0 : i * last / (width - 1);
12482
12482
  const lo = Math.floor(t2);
12483
12483
  const hi = Math.min(last, lo + 1);
12484
- const color = t2 - lo < 0.5 ? GRADIENT[lo] : GRADIENT[hi];
12485
- cells.push({ ch: glyph, color });
12484
+ const color2 = t2 - lo < 0.5 ? GRADIENT[lo] : GRADIENT[hi];
12485
+ cells.push({ ch: glyph, color: color2 });
12486
12486
  }
12487
12487
  return cells;
12488
12488
  }
@@ -12659,9 +12659,9 @@ var ROLE_GLYPH = {
12659
12659
  };
12660
12660
  function RoleGlyph({
12661
12661
  glyph,
12662
- color
12662
+ color: color2
12663
12663
  }) {
12664
- return /* @__PURE__ */ React11.createElement(Text8, { color, bold: true }, glyph);
12664
+ return /* @__PURE__ */ React11.createElement(Text8, { color: color2, bold: true }, glyph);
12665
12665
  }
12666
12666
  function ToolPill({ label, status: status2 }) {
12667
12667
  const bg = status2 === "err" ? "red" : "yellow";
@@ -14175,8 +14175,8 @@ function StatsPanel({
14175
14175
  }
14176
14176
  function BudgetRow({ spent, cap }) {
14177
14177
  const pct2 = Math.max(0, spent / cap * 100);
14178
- const color = pct2 >= 100 ? "#f87171" : pct2 >= 80 ? "#fbbf24" : "#94a3b8";
14179
- return /* @__PURE__ */ React21.createElement(Box19, null, /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " budget "), /* @__PURE__ */ React21.createElement(Text17, { color }, `$${spent.toFixed(4)} / $${cap.toFixed(2)}`, /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, ` (${pct2.toFixed(0)}%)`)));
14178
+ const color2 = pct2 >= 100 ? "#f87171" : pct2 >= 80 ? "#fbbf24" : "#94a3b8";
14179
+ return /* @__PURE__ */ React21.createElement(Box19, null, /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " budget "), /* @__PURE__ */ React21.createElement(Text17, { color: color2 }, `$${spent.toFixed(4)} / $${cap.toFixed(2)}`, /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, ` (${pct2.toFixed(0)}%)`)));
14180
14180
  }
14181
14181
  function Header({
14182
14182
  model: model2,
@@ -14246,9 +14246,9 @@ function ContextCell({
14246
14246
  if (promptTokens === 0) {
14247
14247
  return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u25A3 ctx "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u2014 (no turns yet)"));
14248
14248
  }
14249
- const color = ratio >= 0.8 ? COLOR.err : ratio >= 0.6 ? COLOR.warn : COLOR.ok;
14249
+ const color2 = ratio >= 0.8 ? COLOR.err : ratio >= 0.6 ? COLOR.warn : COLOR.ok;
14250
14250
  const pct2 = Math.round(ratio * 100);
14251
- return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25A3 ctx "), /* @__PURE__ */ React21.createElement(Bar, { ratio, color, cells: showBar ? 14 : 10 }), /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.err, bold: true }, " \xB7 /compact") : null);
14251
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25A3 ctx "), /* @__PURE__ */ React21.createElement(Bar, { ratio, color: color2, cells: showBar ? 14 : 10 }), /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color: color2, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.err, bold: true }, " \xB7 /compact") : null);
14252
14252
  }
14253
14253
  function CacheCell({
14254
14254
  hitRatio,
@@ -14262,8 +14262,8 @@ function CacheCell({
14262
14262
  if (coldStart) {
14263
14263
  return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true, italic: true }, "(cold start)"));
14264
14264
  }
14265
- const color = hitRatio >= 0.7 ? COLOR.ok : hitRatio >= 0.4 ? COLOR.warn : COLOR.err;
14266
- return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, pct2, "%"));
14265
+ const color2 = hitRatio >= 0.7 ? COLOR.ok : hitRatio >= 0.4 ? COLOR.warn : COLOR.err;
14266
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { color: color2, bold: true }, pct2, "%"));
14267
14267
  }
14268
14268
  function turnCostColor(cost) {
14269
14269
  if (cost <= 0) return void 0;
@@ -14289,16 +14289,16 @@ function CostCell({
14289
14289
  return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25F4 turn "), /* @__PURE__ */ React21.createElement(Text17, { color: turnColor, bold: !coldStart, dimColor: coldStart }, "$", summary.lastTurnCostUsd.toFixed(4)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " \xB7 session "), /* @__PURE__ */ React21.createElement(Text17, { color: sessionColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(4)));
14290
14290
  }
14291
14291
  function BalanceCell({ balance }) {
14292
- const color = balance.total < 1 ? COLOR.err : balance.total < 5 ? COLOR.warn : COLOR.ok;
14293
- return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25D0 balance "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
14292
+ const color2 = balance.total < 1 ? COLOR.err : balance.total < 5 ? COLOR.warn : COLOR.ok;
14293
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25D0 balance "), /* @__PURE__ */ React21.createElement(Text17, { color: color2, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
14294
14294
  }
14295
14295
  function Bar({
14296
14296
  ratio,
14297
- color,
14297
+ color: color2,
14298
14298
  cells = 14
14299
14299
  }) {
14300
14300
  const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));
14301
- return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color }, "\u25B0".repeat(filled)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u25B1".repeat(cells - filled)));
14301
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: color2 }, "\u25B0".repeat(filled)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u25B1".repeat(cells - filled)));
14302
14302
  }
14303
14303
  function formatTokens(n) {
14304
14304
  if (n < 1024) return String(n);
@@ -18224,11 +18224,11 @@ function App({
18224
18224
  if (key.escape && busy) {
18225
18225
  if (abortedThisTurn.current) return;
18226
18226
  abortedThisTurn.current = true;
18227
- const resolve13 = editReviewResolveRef.current;
18228
- if (resolve13) {
18227
+ const resolve14 = editReviewResolveRef.current;
18228
+ if (resolve14) {
18229
18229
  editReviewResolveRef.current = null;
18230
18230
  setPendingEditReview(null);
18231
- resolve13("reject");
18231
+ resolve14("reject");
18232
18232
  }
18233
18233
  if (activeLoopRef.current) stopLoop();
18234
18234
  loop2.abort();
@@ -18694,11 +18694,11 @@ function App({
18694
18694
  handleStagedInputSubmitRef.current(text ?? "", { plan: plan2, mode: choice }).catch(() => void 0);
18695
18695
  },
18696
18696
  resolveEditReview: (choice) => {
18697
- const resolve13 = editReviewResolveRef.current;
18698
- if (resolve13) {
18697
+ const resolve14 = editReviewResolveRef.current;
18698
+ if (resolve14) {
18699
18699
  editReviewResolveRef.current = null;
18700
18700
  setPendingEditReview(null);
18701
- resolve13(choice);
18701
+ resolve14(choice);
18702
18702
  }
18703
18703
  },
18704
18704
  resolveWorkspaceConfirm: (choice) => {
@@ -20224,10 +20224,10 @@ Continue executing from the next pending step. Call mark_step_complete after eac
20224
20224
  {
20225
20225
  block: pendingEditReview,
20226
20226
  onChoose: (choice) => {
20227
- const resolve13 = editReviewResolveRef.current;
20228
- if (resolve13) {
20227
+ const resolve14 = editReviewResolveRef.current;
20228
+ if (resolve14) {
20229
20229
  editReviewResolveRef.current = null;
20230
- resolve13(choice);
20230
+ resolve14(choice);
20231
20231
  }
20232
20232
  }
20233
20233
  }
@@ -20626,8 +20626,8 @@ function CacheBadge({ usage }) {
20626
20626
  const total = hit + miss;
20627
20627
  if (total === 0) return null;
20628
20628
  const pct2 = hit / total * 100;
20629
- const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
20630
- return /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React28.createElement(Text23, { color }, pct2.toFixed(1), "%"));
20629
+ const color2 = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
20630
+ return /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React28.createElement(Text23, { color: color2 }, pct2.toFixed(1), "%"));
20631
20631
  }
20632
20632
  function truncate2(s, max) {
20633
20633
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -20750,8 +20750,313 @@ markdown report written to ${opts.mdPath}`);
20750
20750
  console.log(renderSummaryTable(report));
20751
20751
  }
20752
20752
 
20753
+ // src/cli/commands/doctor.ts
20754
+ import { existsSync as existsSync23, statSync as statSync14 } from "fs";
20755
+ import { homedir as homedir10 } from "os";
20756
+ import { dirname as dirname16, join as join21, resolve as resolve12 } from "path";
20757
+ var TTY = process.stdout.isTTY && process.env.TERM !== "dumb";
20758
+ function color(text, code) {
20759
+ if (!TTY) return text;
20760
+ return `\x1B[${code}m${text}\x1B[0m`;
20761
+ }
20762
+ function badge(level) {
20763
+ if (level === "ok") return color("\u2713", "32");
20764
+ if (level === "warn") return color("\u26A0", "33");
20765
+ return color("\u2717", "31");
20766
+ }
20767
+ function tail4(s) {
20768
+ return s.length <= 4 ? s : `\u2026${s.slice(-4)}`;
20769
+ }
20770
+ function fmtBytes(n) {
20771
+ if (n < 1024) return `${n} B`;
20772
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
20773
+ return `${(n / 1024 / 1024).toFixed(1)} MB`;
20774
+ }
20775
+ async function checkApiKey() {
20776
+ const fromEnv = process.env.DEEPSEEK_API_KEY;
20777
+ if (fromEnv) {
20778
+ return {
20779
+ label: "api key ",
20780
+ level: "ok",
20781
+ detail: `set via env DEEPSEEK_API_KEY (${tail4(fromEnv)})`
20782
+ };
20783
+ }
20784
+ try {
20785
+ const cfg = readConfig();
20786
+ if (cfg.apiKey) {
20787
+ return {
20788
+ label: "api key ",
20789
+ level: "ok",
20790
+ detail: `from ${defaultConfigPath()} (${tail4(cfg.apiKey)})`
20791
+ };
20792
+ }
20793
+ } catch {
20794
+ }
20795
+ return {
20796
+ label: "api key ",
20797
+ level: "fail",
20798
+ detail: "not set \u2014 `reasonix setup` to save one, or export DEEPSEEK_API_KEY. Get a key at https://platform.deepseek.com/api_keys"
20799
+ };
20800
+ }
20801
+ async function checkConfig() {
20802
+ const path5 = defaultConfigPath();
20803
+ if (!existsSync23(path5)) {
20804
+ return {
20805
+ label: "config ",
20806
+ level: "warn",
20807
+ detail: "missing \u2014 running with library defaults. `reasonix setup` writes one."
20808
+ };
20809
+ }
20810
+ try {
20811
+ const cfg = readConfig(path5);
20812
+ const parts = [];
20813
+ if (cfg.preset) parts.push(`preset=${cfg.preset}`);
20814
+ if (cfg.editMode) parts.push(`editMode=${cfg.editMode}`);
20815
+ if (cfg.mcp && cfg.mcp.length > 0) parts.push(`mcp=${cfg.mcp.length}`);
20816
+ return {
20817
+ label: "config ",
20818
+ level: "ok",
20819
+ detail: `${path5}${parts.length ? ` (${parts.join(", ")})` : ""}`
20820
+ };
20821
+ } catch (err) {
20822
+ return {
20823
+ label: "config ",
20824
+ level: "fail",
20825
+ detail: `${path5} unreadable \u2014 ${err.message}`
20826
+ };
20827
+ }
20828
+ }
20829
+ async function checkApiReach() {
20830
+ const key = process.env.DEEPSEEK_API_KEY ?? readConfig().apiKey;
20831
+ if (!key) {
20832
+ return {
20833
+ label: "api reach ",
20834
+ level: "warn",
20835
+ detail: "skipped \u2014 no api key to test with"
20836
+ };
20837
+ }
20838
+ try {
20839
+ const client = new DeepSeekClient({ apiKey: key });
20840
+ const ctl = new AbortController();
20841
+ const timer = setTimeout(() => ctl.abort(), 8e3);
20842
+ let balance;
20843
+ try {
20844
+ balance = await client.getBalance({ signal: ctl.signal });
20845
+ } finally {
20846
+ clearTimeout(timer);
20847
+ }
20848
+ if (!balance) {
20849
+ return {
20850
+ label: "api reach ",
20851
+ level: "fail",
20852
+ detail: "/user/balance returned null \u2014 auth failed or network blocked"
20853
+ };
20854
+ }
20855
+ if (!balance.is_available) {
20856
+ const info2 = balance.balance_infos[0];
20857
+ return {
20858
+ label: "api reach ",
20859
+ level: "warn",
20860
+ detail: `account flagged not-available${info2 ? ` (${info2.total_balance} ${info2.currency})` : ""} \u2014 top up or check your dashboard`
20861
+ };
20862
+ }
20863
+ const info = balance.balance_infos[0];
20864
+ return {
20865
+ label: "api reach ",
20866
+ level: "ok",
20867
+ detail: info ? `/user/balance ok \u2014 ${info.total_balance} ${info.currency}` : "/user/balance ok"
20868
+ };
20869
+ } catch (err) {
20870
+ return {
20871
+ label: "api reach ",
20872
+ level: "fail",
20873
+ detail: `${err.message}`
20874
+ };
20875
+ }
20876
+ }
20877
+ async function checkTokenizer() {
20878
+ const candidates = [
20879
+ join21(
20880
+ dirname16(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1")),
20881
+ "..",
20882
+ "..",
20883
+ "..",
20884
+ "data",
20885
+ "deepseek-tokenizer.json.gz"
20886
+ ),
20887
+ join21(process.cwd(), "data", "deepseek-tokenizer.json.gz")
20888
+ ];
20889
+ for (const p of candidates) {
20890
+ if (existsSync23(p)) {
20891
+ try {
20892
+ const stat2 = statSync14(p);
20893
+ return {
20894
+ label: "tokenizer ",
20895
+ level: "ok",
20896
+ detail: `${p} (${fmtBytes(stat2.size)})`
20897
+ };
20898
+ } catch {
20899
+ }
20900
+ }
20901
+ }
20902
+ return {
20903
+ label: "tokenizer ",
20904
+ level: "warn",
20905
+ detail: "data/deepseek-tokenizer.json.gz not found \u2014 token counts will fall back to char heuristics"
20906
+ };
20907
+ }
20908
+ async function checkSessions() {
20909
+ try {
20910
+ const list = listSessions();
20911
+ if (list.length === 0) {
20912
+ return {
20913
+ label: "sessions ",
20914
+ level: "ok",
20915
+ detail: "0 saved"
20916
+ };
20917
+ }
20918
+ const totalBytes = list.reduce((s, e) => s + e.size, 0);
20919
+ const oldest = list[list.length - 1];
20920
+ const ageDays = Math.floor((Date.now() - oldest.mtime.getTime()) / (24 * 60 * 60 * 1e3));
20921
+ const stale = list.filter(
20922
+ (e) => Date.now() - e.mtime.getTime() >= 90 * 24 * 60 * 60 * 1e3
20923
+ ).length;
20924
+ const detail = `${list.length} saved \xB7 ${fmtBytes(totalBytes)} \xB7 oldest ${ageDays}d`;
20925
+ if (stale > 0) {
20926
+ return {
20927
+ label: "sessions ",
20928
+ level: "warn",
20929
+ detail: `${detail} \xB7 ${stale} idle \u226590d (run /prune-sessions)`
20930
+ };
20931
+ }
20932
+ return { label: "sessions ", level: "ok", detail };
20933
+ } catch (err) {
20934
+ return {
20935
+ label: "sessions ",
20936
+ level: "warn",
20937
+ detail: `cannot list \u2014 ${err.message}`
20938
+ };
20939
+ }
20940
+ }
20941
+ async function checkHooks(projectRoot) {
20942
+ try {
20943
+ const all = loadHooks({ projectRoot });
20944
+ const global = all.filter((h) => h.scope === "global").length;
20945
+ const project = all.filter((h) => h.scope === "project").length;
20946
+ return {
20947
+ label: "hooks ",
20948
+ level: "ok",
20949
+ detail: `${global} global, ${project} project`
20950
+ };
20951
+ } catch (err) {
20952
+ return {
20953
+ label: "hooks ",
20954
+ level: "warn",
20955
+ detail: `couldn't parse settings.json \u2014 ${err.message}`
20956
+ };
20957
+ }
20958
+ }
20959
+ async function checkOllama(projectRoot) {
20960
+ let exists = false;
20961
+ try {
20962
+ exists = await indexExists(projectRoot);
20963
+ } catch {
20964
+ }
20965
+ if (!exists) {
20966
+ return {
20967
+ label: "ollama ",
20968
+ level: "ok",
20969
+ detail: "not in use (no semantic index built; `reasonix index` to enable)"
20970
+ };
20971
+ }
20972
+ try {
20973
+ const status2 = await checkOllamaStatus(process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text");
20974
+ if (!status2.binaryFound) {
20975
+ return {
20976
+ label: "ollama ",
20977
+ level: "warn",
20978
+ detail: "binary not on PATH \u2014 semantic_search will fail; install from https://ollama.com"
20979
+ };
20980
+ }
20981
+ if (!status2.daemonRunning) {
20982
+ return {
20983
+ label: "ollama ",
20984
+ level: "warn",
20985
+ detail: "daemon not running \u2014 `ollama serve` (or just call /semantic in TUI to auto-start)"
20986
+ };
20987
+ }
20988
+ if (!status2.modelPulled) {
20989
+ return {
20990
+ label: "ollama ",
20991
+ level: "warn",
20992
+ detail: `model ${status2.modelName} not pulled \u2014 \`ollama pull ${status2.modelName}\``
20993
+ };
20994
+ }
20995
+ return {
20996
+ label: "ollama ",
20997
+ level: "ok",
20998
+ detail: `daemon up \xB7 model ${status2.modelName} ready`
20999
+ };
21000
+ } catch (err) {
21001
+ return {
21002
+ label: "ollama ",
21003
+ level: "warn",
21004
+ detail: `probe failed \u2014 ${err.message}`
21005
+ };
21006
+ }
21007
+ }
21008
+ async function checkProject(projectRoot) {
21009
+ const markers = [".git", "REASONIX.md", "package.json", "pyproject.toml", "Cargo.toml", "go.mod"];
21010
+ const found = markers.filter((m) => existsSync23(join21(projectRoot, m)));
21011
+ if (found.length === 0) {
21012
+ return {
21013
+ label: "project ",
21014
+ level: "warn",
21015
+ detail: `${projectRoot} has none of: ${markers.slice(0, 3).join(", ")} \u2026 \u2014 \`reasonix code\` will still run, but @-mentions and project memory have nothing to anchor`
21016
+ };
21017
+ }
21018
+ return {
21019
+ label: "project ",
21020
+ level: "ok",
21021
+ detail: `${projectRoot} (${found.join(", ")})`
21022
+ };
21023
+ }
21024
+ async function doctorCommand() {
21025
+ loadDotenv();
21026
+ const projectRoot = resolve12(process.cwd());
21027
+ console.log(`${color(`reasonix ${VERSION} \xB7 doctor`, "1")} (cwd: ${projectRoot})`);
21028
+ console.log(` home: ${homedir10()}`);
21029
+ console.log("");
21030
+ const checks = await Promise.all([
21031
+ checkApiKey(),
21032
+ checkConfig(),
21033
+ checkApiReach(),
21034
+ checkTokenizer(),
21035
+ checkSessions(),
21036
+ checkHooks(projectRoot),
21037
+ checkOllama(projectRoot),
21038
+ checkProject(projectRoot)
21039
+ ]);
21040
+ for (const c of checks) {
21041
+ console.log(` ${badge(c.level)} ${c.label} ${c.detail}`);
21042
+ }
21043
+ const ok = checks.filter((c) => c.level === "ok").length;
21044
+ const warn = checks.filter((c) => c.level === "warn").length;
21045
+ const fail = checks.filter((c) => c.level === "fail").length;
21046
+ console.log("");
21047
+ const summary = `${ok} ok \xB7 ${warn} warn \xB7 ${fail} fail`;
21048
+ if (fail > 0) {
21049
+ console.log(color(summary, "31"));
21050
+ process.exit(1);
21051
+ } else if (warn > 0) {
21052
+ console.log(color(summary, "33"));
21053
+ } else {
21054
+ console.log(color(summary, "32"));
21055
+ }
21056
+ }
21057
+
20753
21058
  // src/cli/commands/index.ts
20754
- import { resolve as resolve12 } from "path";
21059
+ import { resolve as resolve13 } from "path";
20755
21060
 
20756
21061
  // src/index/semantic/preflight.ts
20757
21062
  import { stdin as stdin2, stdout } from "process";
@@ -20825,7 +21130,7 @@ async function confirm(question, defaultYes) {
20825
21130
 
20826
21131
  // src/cli/commands/index.ts
20827
21132
  async function indexCommand(opts = {}) {
20828
- const root = resolve12(opts.dir ?? process.cwd());
21133
+ const root = resolve13(opts.dir ?? process.cwd());
20829
21134
  const tty = process.stderr.isTTY === true && process.stdin.isTTY === true;
20830
21135
  const model2 = opts.model ?? process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text";
20831
21136
  const preflightOk = await ollamaPreflight({
@@ -21747,13 +22052,13 @@ function planUpdate(input) {
21747
22052
  };
21748
22053
  }
21749
22054
  function defaultSpawn(argv) {
21750
- return new Promise((resolve13, reject) => {
22055
+ return new Promise((resolve14, reject) => {
21751
22056
  const child = spawn6(argv[0], argv.slice(1), {
21752
22057
  stdio: "inherit",
21753
22058
  shell: process.platform === "win32"
21754
22059
  });
21755
22060
  child.once("error", reject);
21756
- child.once("exit", (code) => resolve13(code ?? 1));
22061
+ child.once("exit", (code) => resolve14(code ?? 1));
21757
22062
  });
21758
22063
  }
21759
22064
  async function updateCommand(opts = {}) {
@@ -22038,6 +22343,11 @@ program.command("stats [transcript]").description(
22038
22343
  ).action((transcript) => {
22039
22344
  statsCommand({ transcript });
22040
22345
  });
22346
+ program.command("doctor").description(
22347
+ "One-command health check \u2014 API key, config, /user/balance reachability, tokenizer, sessions, hooks, Ollama (if used), project markers. Exit 1 on any fail; 0 on warn / clean."
22348
+ ).action(async () => {
22349
+ await doctorCommand();
22350
+ });
22041
22351
  program.command("sessions [name]").description("List saved chat sessions, or inspect one by name.").option("-v, --verbose", "Include system prompts + tool-call metadata when inspecting").action((name, opts) => {
22042
22352
  sessionsCommand({ name, verbose: !!opts.verbose });
22043
22353
  });