reasonix 0.11.0 → 0.11.1

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
@@ -10,7 +10,7 @@ import {
10
10
  memoryEnabled,
11
11
  readProjectMemory,
12
12
  sanitizeMemoryName
13
- } from "./chunk-GXABXQMU.js";
13
+ } from "./chunk-JDVY4JDU.js";
14
14
 
15
15
  // src/cli/index.ts
16
16
  import { Command } from "commander";
@@ -156,8 +156,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
156
156
  }
157
157
  function sleep(ms, signal) {
158
158
  if (ms <= 0) return Promise.resolve();
159
- return new Promise((resolve9, reject) => {
160
- const timer = setTimeout(resolve9, ms);
159
+ return new Promise((resolve12, reject) => {
160
+ const timer = setTimeout(resolve12, ms);
161
161
  if (signal) {
162
162
  const onAbort = () => {
163
163
  clearTimeout(timer);
@@ -642,7 +642,7 @@ function matchesTool(hook, toolName) {
642
642
  }
643
643
  }
644
644
  function defaultSpawner(input) {
645
- return new Promise((resolve9) => {
645
+ return new Promise((resolve12) => {
646
646
  const child = spawn(input.command, {
647
647
  cwd: input.cwd,
648
648
  shell: true,
@@ -669,7 +669,7 @@ function defaultSpawner(input) {
669
669
  });
670
670
  child.once("error", (err) => {
671
671
  clearTimeout(timer);
672
- resolve9({
672
+ resolve12({
673
673
  exitCode: null,
674
674
  stdout: stdout3,
675
675
  stderr,
@@ -679,7 +679,7 @@ function defaultSpawner(input) {
679
679
  });
680
680
  child.once("close", (code) => {
681
681
  clearTimeout(timer);
682
- resolve9({
682
+ resolve12({
683
683
  exitCode: code,
684
684
  stdout: stdout3.trim(),
685
685
  stderr: stderr.trim(),
@@ -720,8 +720,8 @@ async function runHooks(opts) {
720
720
  for (const hook of matching) {
721
721
  const start = Date.now();
722
722
  const timeoutMs = hook.timeout ?? DEFAULT_TIMEOUTS_MS[event];
723
- const cwd = hook.cwd ?? opts.payload.cwd;
724
- const raw = await spawner({ command: hook.command, cwd, stdin: stdin4, timeoutMs });
723
+ const cwd2 = hook.cwd ?? opts.payload.cwd;
724
+ const raw = await spawner({ command: hook.command, cwd: cwd2, stdin: stdin4, timeoutMs });
725
725
  const decision = decideOutcome(event, raw);
726
726
  outcomes.push({
727
727
  hook,
@@ -1867,7 +1867,11 @@ var CacheFirstLoop = class {
1867
1867
  * tool call is one array length check.
1868
1868
  */
1869
1869
  hooks;
1870
- /** `cwd` reported to hook stdin. Resolved once at construction. */
1870
+ /**
1871
+ * `cwd` reported to hook stdin. Mutable so `/cwd` can switch the
1872
+ * working directory mid-session — the App keeps it in sync with
1873
+ * the same currentRootDir that drives tool re-registration.
1874
+ */
1871
1875
  hookCwd;
1872
1876
  /** Number of messages that were pre-loaded from the session file. */
1873
1877
  resumedMessageCount;
@@ -2344,6 +2348,7 @@ var CacheFirstLoop = class {
2344
2348
  };
2345
2349
  this.autoCompactToolResultsOnTurnEnd();
2346
2350
  yield { turn: this._turn, role: "done", content: stoppedMsg };
2351
+ this._turnAbort = new AbortController();
2347
2352
  return;
2348
2353
  }
2349
2354
  if (iter > 0) {
@@ -2437,8 +2442,8 @@ var CacheFirstLoop = class {
2437
2442
  }
2438
2443
  );
2439
2444
  for (let k = 0; k < budget; k++) {
2440
- const sample = queue.shift() ?? await new Promise((resolve9) => {
2441
- waiter = resolve9;
2445
+ const sample = queue.shift() ?? await new Promise((resolve12) => {
2446
+ waiter = resolve12;
2442
2447
  });
2443
2448
  yield {
2444
2449
  turn: this._turn,
@@ -5168,7 +5173,7 @@ async function runCommand(cmd, opts) {
5168
5173
  };
5169
5174
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
5170
5175
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
5171
- return await new Promise((resolve9, reject) => {
5176
+ return await new Promise((resolve12, reject) => {
5172
5177
  let child;
5173
5178
  try {
5174
5179
  child = spawn3(bin, args, effectiveSpawnOpts);
@@ -5176,7 +5181,9 @@ async function runCommand(cmd, opts) {
5176
5181
  reject(err);
5177
5182
  return;
5178
5183
  }
5179
- let buf = "";
5184
+ const chunks = [];
5185
+ let totalBytes = 0;
5186
+ const byteCap = maxChars * 2 * 4;
5180
5187
  let timedOut = false;
5181
5188
  const killTimer = setTimeout(() => {
5182
5189
  timedOut = true;
@@ -5185,8 +5192,16 @@ async function runCommand(cmd, opts) {
5185
5192
  const onAbort = () => child.kill("SIGKILL");
5186
5193
  opts.signal?.addEventListener("abort", onAbort, { once: true });
5187
5194
  const onData = (chunk) => {
5188
- buf += chunk.toString();
5189
- if (buf.length > maxChars * 2) buf = `${buf.slice(0, maxChars * 2)}`;
5195
+ const b = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
5196
+ if (totalBytes >= byteCap) return;
5197
+ const remaining = byteCap - totalBytes;
5198
+ if (b.length > remaining) {
5199
+ chunks.push(b.subarray(0, remaining));
5200
+ totalBytes = byteCap;
5201
+ } else {
5202
+ chunks.push(b);
5203
+ totalBytes += b.length;
5204
+ }
5190
5205
  };
5191
5206
  child.stdout?.on("data", onData);
5192
5207
  child.stderr?.on("data", onData);
@@ -5198,13 +5213,29 @@ async function runCommand(cmd, opts) {
5198
5213
  child.on("close", (code) => {
5199
5214
  clearTimeout(killTimer);
5200
5215
  opts.signal?.removeEventListener("abort", onAbort);
5216
+ const merged = Buffer.concat(chunks);
5217
+ const buf = smartDecodeOutput(merged);
5201
5218
  const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
5202
5219
 
5203
5220
  [\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
5204
- resolve9({ exitCode: code, output, timedOut });
5221
+ resolve12({ exitCode: code, output, timedOut });
5205
5222
  });
5206
5223
  });
5207
5224
  }
5225
+ function smartDecodeOutput(buf) {
5226
+ if (buf.length === 0) return "";
5227
+ try {
5228
+ return new TextDecoder("utf-8", { fatal: true }).decode(buf);
5229
+ } catch {
5230
+ }
5231
+ if (process.platform === "win32") {
5232
+ try {
5233
+ return new TextDecoder("gb18030").decode(buf);
5234
+ } catch {
5235
+ }
5236
+ }
5237
+ return buf.toString("utf8");
5238
+ }
5208
5239
  function resolveExecutable(cmd, opts = {}) {
5209
5240
  const platform = opts.platform ?? process.platform;
5210
5241
  if (platform !== "win32") return cmd;
@@ -6391,7 +6422,7 @@ var McpClient = class {
6391
6422
  const id = this.nextId++;
6392
6423
  const frame = { jsonrpc: "2.0", id, method, params };
6393
6424
  let abortHandler = null;
6394
- const promise = new Promise((resolve9, reject) => {
6425
+ const promise = new Promise((resolve12, reject) => {
6395
6426
  const timeout = setTimeout(() => {
6396
6427
  this.pending.delete(id);
6397
6428
  if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
@@ -6400,7 +6431,7 @@ var McpClient = class {
6400
6431
  );
6401
6432
  }, this.requestTimeoutMs);
6402
6433
  this.pending.set(id, {
6403
- resolve: resolve9,
6434
+ resolve: resolve12,
6404
6435
  reject,
6405
6436
  timeout
6406
6437
  });
@@ -6523,12 +6554,12 @@ var StdioTransport = class {
6523
6554
  }
6524
6555
  async send(message) {
6525
6556
  if (this.closed) throw new Error("MCP transport is closed");
6526
- return new Promise((resolve9, reject) => {
6557
+ return new Promise((resolve12, reject) => {
6527
6558
  const line = `${JSON.stringify(message)}
6528
6559
  `;
6529
6560
  this.child.stdin.write(line, "utf8", (err) => {
6530
6561
  if (err) reject(err);
6531
- else resolve9();
6562
+ else resolve12();
6532
6563
  });
6533
6564
  });
6534
6565
  }
@@ -6539,8 +6570,8 @@ var StdioTransport = class {
6539
6570
  continue;
6540
6571
  }
6541
6572
  if (this.closed) return;
6542
- const next = await new Promise((resolve9) => {
6543
- this.waiters.push(resolve9);
6573
+ const next = await new Promise((resolve12) => {
6574
+ this.waiters.push(resolve12);
6544
6575
  });
6545
6576
  if (next === null) return;
6546
6577
  yield next;
@@ -6606,8 +6637,8 @@ var SseTransport = class {
6606
6637
  constructor(opts) {
6607
6638
  this.url = opts.url;
6608
6639
  this.headers = opts.headers ?? {};
6609
- this.endpointReady = new Promise((resolve9, reject) => {
6610
- this.resolveEndpoint = resolve9;
6640
+ this.endpointReady = new Promise((resolve12, reject) => {
6641
+ this.resolveEndpoint = resolve12;
6611
6642
  this.rejectEndpoint = reject;
6612
6643
  });
6613
6644
  this.endpointReady.catch(() => void 0);
@@ -6634,8 +6665,8 @@ var SseTransport = class {
6634
6665
  continue;
6635
6666
  }
6636
6667
  if (this.closed) return;
6637
- const next = await new Promise((resolve9) => {
6638
- this.waiters.push(resolve9);
6668
+ const next = await new Promise((resolve12) => {
6669
+ this.waiters.push(resolve12);
6639
6670
  });
6640
6671
  if (next === null) return;
6641
6672
  yield next;
@@ -7223,13 +7254,14 @@ function formatLogSize(path5 = defaultUsageLogPath()) {
7223
7254
  }
7224
7255
 
7225
7256
  // src/cli/commands/chat.tsx
7226
- import { existsSync as existsSync13, statSync as statSync7 } from "fs";
7257
+ import { existsSync as existsSync15, statSync as statSync9 } from "fs";
7227
7258
  import { render } from "ink";
7228
- import React26, { useState as useState12 } from "react";
7259
+ import React27, { useState as useState12 } from "react";
7229
7260
 
7230
7261
  // src/cli/ui/App.tsx
7231
- import { Box as Box21, Static, Text as Text19, useApp, useStdout as useStdout8 } from "ink";
7232
- import React23, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState10 } from "react";
7262
+ import * as pathMod6 from "path";
7263
+ import { Box as Box22, Static, Text as Text20, useApp, useStdout as useStdout8 } from "ink";
7264
+ import React24, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState10 } from "react";
7233
7265
 
7234
7266
  // src/code/pending-edits.ts
7235
7267
  import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "fs";
@@ -7521,6 +7553,60 @@ ${skill2.body}${argsBlock}`;
7521
7553
  return registry;
7522
7554
  }
7523
7555
 
7556
+ // src/tools/workspace.ts
7557
+ import { existsSync as existsSync11, statSync as statSync6 } from "fs";
7558
+ import * as pathMod4 from "path";
7559
+ var WorkspaceConfirmationError = class extends Error {
7560
+ path;
7561
+ constructor(path5) {
7562
+ super(
7563
+ `change_workspace: switching to "${path5}" needs the user's approval before it takes effect. STOP calling tools now \u2014 the TUI has already prompted the user to press Enter (switch) or Esc (deny). Wait for their next message; it will either confirm the switch (and your subsequent file/shell tools will resolve against the new root) or tell you to continue without changing directories.`
7564
+ );
7565
+ this.name = "WorkspaceConfirmationError";
7566
+ this.path = path5;
7567
+ }
7568
+ };
7569
+ function registerWorkspaceTool(registry) {
7570
+ registry.register({
7571
+ name: "change_workspace",
7572
+ description: "Switch the session's working directory to a different project root. Re-registers filesystem / shell / memory tools against the new path so subsequent file reads, edits, and run_command calls all land there. EVERY switch requires explicit user approval via a modal \u2014 do NOT batch switches or chain a switch with subsequent tool calls before the user has confirmed. Use ONLY when the user explicitly asked to change directory or open a different project; never use to 'preview' a sibling repo. MCP servers stay anchored to the original launch root (their child processes can't be reconnected mid-session); the modal warns the user about this.",
7573
+ parameters: {
7574
+ type: "object",
7575
+ required: ["path"],
7576
+ properties: {
7577
+ path: {
7578
+ type: "string",
7579
+ description: "Target directory. Absolute paths land verbatim. Leading `~` expands to the user's home. Relative paths resolve against the user's launch cwd (not the current session root, so paths the user typed in chat resolve where they expect)."
7580
+ }
7581
+ }
7582
+ },
7583
+ fn: (rawArgs) => {
7584
+ const args = rawArgs ?? {};
7585
+ if (typeof args.path !== "string" || args.path.trim() === "") {
7586
+ throw new Error("change_workspace: `path` must be a non-empty string");
7587
+ }
7588
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
7589
+ const expanded = args.path.startsWith("~") && home ? pathMod4.join(home, args.path.slice(1)) : args.path;
7590
+ const abs = pathMod4.resolve(expanded);
7591
+ if (!existsSync11(abs)) {
7592
+ throw new Error(`change_workspace: path does not exist \u2014 ${abs}`);
7593
+ }
7594
+ try {
7595
+ if (!statSync6(abs).isDirectory()) {
7596
+ throw new Error(`change_workspace: not a directory \u2014 ${abs}`);
7597
+ }
7598
+ } catch (err) {
7599
+ if (err.code === "ENOENT") {
7600
+ throw new Error(`change_workspace: path does not exist \u2014 ${abs}`);
7601
+ }
7602
+ throw err;
7603
+ }
7604
+ throw new WorkspaceConfirmationError(abs);
7605
+ }
7606
+ });
7607
+ return registry;
7608
+ }
7609
+
7524
7610
  // src/cli/ui/AtMentionSuggestions.tsx
7525
7611
  import { Box, Text } from "ink";
7526
7612
  import React from "react";
@@ -8315,8 +8401,8 @@ function RiskLegend() {
8315
8401
  var PlanStepList = React8.memo(PlanStepListInner);
8316
8402
 
8317
8403
  // src/cli/ui/markdown.tsx
8318
- import { readFileSync as readFileSync13, statSync as statSync6 } from "fs";
8319
- import { isAbsolute as isAbsolute4, join as join11 } from "path";
8404
+ import { readFileSync as readFileSync13, statSync as statSync7 } from "fs";
8405
+ import { isAbsolute as isAbsolute4, join as join12 } from "path";
8320
8406
  import { Box as Box8, Text as Text7 } from "ink";
8321
8407
  import React9 from "react";
8322
8408
  var SUPERSCRIPT = {
@@ -8358,7 +8444,32 @@ function toSubscript(s) {
8358
8444
  for (const c of s) out += SUBSCRIPT[c] ?? c;
8359
8445
  return out;
8360
8446
  }
8447
+ var HAS_MATH_RE = new RegExp(
8448
+ [
8449
+ "\\$",
8450
+ // dollar-delimited (block or inline)
8451
+ "\\\\[([]",
8452
+ // \( or \[
8453
+ "\\\\[a-zA-Z]+\\s*\\{",
8454
+ // \anyCommand{...} — covers catch-all braced transforms
8455
+ // Bare (no-brace) LaTeX commands the pipeline knows how to handle.
8456
+ // Listed explicitly because a generic `\\[a-zA-Z]+` would also match
8457
+ // Windows paths (`F:\TEST1`) and re-introduce the bug we're fixing.
8458
+ "\\\\(?:cdot|times|div|pm|mp|leq|geq|neq|approx|in|notin|infty|sum|prod|int|alpha|beta|gamma|delta|theta|lambda|mu|pi|sigma|phi|omega|implies|iff|to|rightarrow|leftarrow|Rightarrow|Leftarrow|ldots|cdots|quad|qquad)(?![a-zA-Z])",
8459
+ "[\\^_]\\{",
8460
+ // LaTeX braced super/subscript: ^{2}, _{ij}
8461
+ "\\^[0-9+\\-n](?![A-Za-z])",
8462
+ // LaTeX single-char super: ^2, ^-, ^n
8463
+ "_[0-9+\\-](?![A-Za-z])",
8464
+ // LaTeX single-char sub: _1, _+, _-
8465
+ "\\^[A-Za-z0-9+\\-]+\\^",
8466
+ // Pandoc super: ^2^, ^abc^
8467
+ "(?<!~)~[A-Za-z0-9+\\-]+~(?!~)"
8468
+ // Pandoc sub: ~2~ (lookarounds avoid ~~strike~~)
8469
+ ].join("|")
8470
+ );
8361
8471
  function stripMath(s) {
8472
+ if (!HAS_MATH_RE.test(s)) return s;
8362
8473
  return s.replace(/\$\$([\s\S]+?)\$\$/g, (_m, c) => `
8363
8474
 
8364
8475
  ${c.trim()}
@@ -8532,7 +8643,7 @@ function validateCitation(url, projectRoot) {
8532
8643
  const parts = parseCitationUrl(url);
8533
8644
  if (!parts || !parts.path) return { ok: false, reason: "empty path" };
8534
8645
  const normalized = parts.path.replace(/^[/\\]+/, "");
8535
- const baseFullPath = isAbsolute4(normalized) ? normalized : join11(projectRoot, normalized);
8646
+ const baseFullPath = isAbsolute4(normalized) ? normalized : join12(projectRoot, normalized);
8536
8647
  const siblings = SIBLING_EXTENSIONS.get(extOf(baseFullPath)) ?? [];
8537
8648
  const candidates = [
8538
8649
  baseFullPath,
@@ -8542,7 +8653,7 @@ function validateCitation(url, projectRoot) {
8542
8653
  let stat = null;
8543
8654
  for (const candidate of candidates) {
8544
8655
  try {
8545
- stat = statSync6(candidate);
8656
+ stat = statSync7(candidate);
8546
8657
  fullPath = candidate;
8547
8658
  break;
8548
8659
  } catch {
@@ -10945,6 +11056,39 @@ function Hint({ cmd, desc }) {
10945
11056
  return /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: COLOR.accent }, cmd.padEnd(8)), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, ` ${desc}`));
10946
11057
  }
10947
11058
 
11059
+ // src/cli/ui/WorkspaceConfirm.tsx
11060
+ import { Box as Box21, Text as Text19 } from "ink";
11061
+ import React23 from "react";
11062
+ function WorkspaceConfirm({
11063
+ path: path5,
11064
+ currentRoot,
11065
+ mcpServerCount,
11066
+ onChoose
11067
+ }) {
11068
+ const subtitle = mcpServerCount > 0 ? `MCP servers (${mcpServerCount}) stay anchored to the original launch root.` : "Re-registers filesystem / shell / memory tools at the new path.";
11069
+ return /* @__PURE__ */ React23.createElement(ModalCard, { accent: "#f59e0b", icon: "\u21C4", title: "switch workspace", subtitle }, /* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "from "), /* @__PURE__ */ React23.createElement(Text19, { color: "#a3a3a3" }, currentRoot)), /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "to "), /* @__PURE__ */ React23.createElement(Text19, { color: "#67e8f9", bold: true }, path5))), /* @__PURE__ */ React23.createElement(
11070
+ SingleSelect,
11071
+ {
11072
+ initialValue: "switch",
11073
+ items: [
11074
+ {
11075
+ value: "switch",
11076
+ label: "Switch",
11077
+ hint: "Re-register filesystem / shell / memory tools against the new root."
11078
+ },
11079
+ {
11080
+ value: "deny",
11081
+ label: "Deny",
11082
+ hint: "Tell the model the user refused; it will continue without changing directories."
11083
+ }
11084
+ ],
11085
+ onSubmit: (v) => onChoose(v),
11086
+ onCancel: () => onChoose("deny"),
11087
+ footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] deny"
11088
+ }
11089
+ ));
11090
+ }
11091
+
10948
11092
  // src/cli/ui/bang.ts
10949
11093
  function detectBangCommand(text) {
10950
11094
  if (!text.startsWith("!")) return null;
@@ -11038,9 +11182,9 @@ function describeRepair(repair) {
11038
11182
  }
11039
11183
 
11040
11184
  // src/cli/ui/hash-memory.ts
11041
- import { appendFileSync as appendFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
11185
+ import { appendFileSync as appendFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
11042
11186
  import { homedir as homedir6 } from "os";
11043
- import { dirname as dirname10, join as join12 } from "path";
11187
+ import { dirname as dirname10, join as join13 } from "path";
11044
11188
  var PROJECT_HEADER = `# Reasonix project memory
11045
11189
 
11046
11190
  Notes the user pinned via the \`#\` prompt prefix. The whole file is
@@ -11072,12 +11216,12 @@ function detectHashMemory(text) {
11072
11216
  return { kind: "memory", note: body };
11073
11217
  }
11074
11218
  function appendProjectMemory(rootDir, note) {
11075
- return appendBulletToFile(join12(rootDir, PROJECT_MEMORY_FILE), note, PROJECT_HEADER);
11219
+ return appendBulletToFile(join13(rootDir, PROJECT_MEMORY_FILE), note, PROJECT_HEADER);
11076
11220
  }
11077
11221
  var GLOBAL_MEMORY_DIR = ".reasonix";
11078
11222
  var GLOBAL_MEMORY_FILE = "REASONIX.md";
11079
11223
  function globalMemoryPath(homeDir = homedir6()) {
11080
- return join12(homeDir, GLOBAL_MEMORY_DIR, GLOBAL_MEMORY_FILE);
11224
+ return join13(homeDir, GLOBAL_MEMORY_DIR, GLOBAL_MEMORY_FILE);
11081
11225
  }
11082
11226
  function appendGlobalMemory(note, homeDir) {
11083
11227
  return appendBulletToFile(globalMemoryPath(homeDir), note, GLOBAL_HEADER);
@@ -11087,7 +11231,7 @@ function appendBulletToFile(path5, note, newFileHeader) {
11087
11231
  if (!trimmed) throw new Error("note body cannot be empty");
11088
11232
  const bullet = `- ${trimmed}
11089
11233
  `;
11090
- if (!existsSync11(path5)) {
11234
+ if (!existsSync12(path5)) {
11091
11235
  mkdirSync8(dirname10(path5), { recursive: true });
11092
11236
  writeFileSync7(path5, `${newFileHeader}${bullet}`, "utf8");
11093
11237
  return { path: path5, created: true };
@@ -11435,6 +11579,11 @@ var SLASH_COMMANDS = [
11435
11579
  argsHint: "[reload]",
11436
11580
  summary: "list active hooks (settings.json under .reasonix/) \xB7 reload re-reads from disk"
11437
11581
  },
11582
+ {
11583
+ cmd: "cwd",
11584
+ argsHint: "<path>",
11585
+ summary: "switch session working directory (re-registers code tools, reloads hooks; MCP servers stay)"
11586
+ },
11438
11587
  {
11439
11588
  cmd: "update",
11440
11589
  summary: "show current vs latest version + the shell command to upgrade"
@@ -11581,8 +11730,12 @@ function parseSlash(text) {
11581
11730
  return { cmd, args: parts.slice(1) };
11582
11731
  }
11583
11732
 
11733
+ // src/cli/ui/slash/handlers/admin.ts
11734
+ import { existsSync as existsSync14, statSync as statSync8 } from "fs";
11735
+ import * as pathMod5 from "path";
11736
+
11584
11737
  // src/cli/commands/stats.ts
11585
- import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
11738
+ import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
11586
11739
  function statsCommand(opts) {
11587
11740
  if (opts.transcript) {
11588
11741
  transcriptSummary(opts.transcript);
@@ -11591,7 +11744,7 @@ function statsCommand(opts) {
11591
11744
  dashboard(opts);
11592
11745
  }
11593
11746
  function transcriptSummary(path5) {
11594
- if (!existsSync12(path5)) {
11747
+ if (!existsSync13(path5)) {
11595
11748
  console.error(`no such transcript: ${path5}`);
11596
11749
  process.exit(1);
11597
11750
  }
@@ -11815,9 +11968,51 @@ var stats = () => {
11815
11968
  const agg = aggregateUsage(records);
11816
11969
  return { info: renderDashboard(agg, path5) };
11817
11970
  };
11971
+ var cwd = (args, _loop, ctx) => {
11972
+ if (!ctx.setCwd) {
11973
+ return {
11974
+ info: "/cwd is not available in this context (no setCwd callback wired)."
11975
+ };
11976
+ }
11977
+ const raw = (args[0] ?? "").trim();
11978
+ if (!raw) {
11979
+ return {
11980
+ info: "usage: /cwd <path> (absolute or relative, ~ expands to home)"
11981
+ };
11982
+ }
11983
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11984
+ const expanded = raw.startsWith("~") && home ? pathMod5.join(home, raw.slice(1)) : raw;
11985
+ const abs = pathMod5.resolve(expanded);
11986
+ if (!existsSync14(abs)) {
11987
+ return { info: `\u25B8 /cwd: path does not exist \u2014 ${abs}` };
11988
+ }
11989
+ let isDir = false;
11990
+ try {
11991
+ isDir = statSync8(abs).isDirectory();
11992
+ } catch {
11993
+ }
11994
+ if (!isDir) {
11995
+ return { info: `\u25B8 /cwd: not a directory \u2014 ${abs}` };
11996
+ }
11997
+ let info;
11998
+ try {
11999
+ info = ctx.setCwd(abs);
12000
+ } catch (err) {
12001
+ return { info: `\u25B8 /cwd failed: ${err.message}` };
12002
+ }
12003
+ const lines = [info];
12004
+ if (ctx.mcpServers && ctx.mcpServers.length > 0) {
12005
+ lines.push(
12006
+ ` note: ${ctx.mcpServers.length} MCP server(s) still anchored to the original cwd \u2014`,
12007
+ " their tools won't follow this switch. Restart the session for full reset."
12008
+ );
12009
+ }
12010
+ return { info: lines.join("\n") };
12011
+ };
11818
12012
  var handlers = {
11819
12013
  hook: hooks,
11820
12014
  hooks,
12015
+ cwd,
11821
12016
  update,
11822
12017
  stats
11823
12018
  };
@@ -13173,7 +13368,7 @@ async function startOllamaDaemon(opts = {}) {
13173
13368
  return { ready: false, pid };
13174
13369
  }
13175
13370
  async function pullOllamaModel(modelName, opts = {}) {
13176
- return new Promise((resolve9) => {
13371
+ return new Promise((resolve12) => {
13177
13372
  const child = spawn5("ollama", ["pull", modelName], {
13178
13373
  stdio: ["ignore", "pipe", "pipe"],
13179
13374
  windowsHide: true
@@ -13185,8 +13380,8 @@ async function pullOllamaModel(modelName, opts = {}) {
13185
13380
  }
13186
13381
  streamLines(child.stdout, (l) => opts.onLine?.(l, "stdout"));
13187
13382
  streamLines(child.stderr, (l) => opts.onLine?.(l, "stderr"));
13188
- child.once("exit", (code) => resolve9(code ?? -1));
13189
- child.once("error", () => resolve9(-1));
13383
+ child.once("exit", (code) => resolve12(code ?? -1));
13384
+ child.once("error", () => resolve12(-1));
13190
13385
  });
13191
13386
  }
13192
13387
  function streamLines(stream, cb) {
@@ -13426,6 +13621,7 @@ function useCompletionPickers({
13426
13621
  input,
13427
13622
  setInput,
13428
13623
  codeMode,
13624
+ rootDir,
13429
13625
  models: models2,
13430
13626
  mcpServers
13431
13627
  }) {
@@ -13443,13 +13639,13 @@ function useCompletionPickers({
13443
13639
  }, [slashMatches]);
13444
13640
  const [atSelected, setAtSelected] = useState6(0);
13445
13641
  const atFiles = useMemo2(() => {
13446
- if (!codeMode?.rootDir) return [];
13642
+ if (!codeMode) return [];
13447
13643
  try {
13448
- return listFilesWithStatsSync(codeMode.rootDir, { maxResults: 500 });
13644
+ return listFilesWithStatsSync(rootDir, { maxResults: 500 });
13449
13645
  } catch {
13450
13646
  return [];
13451
13647
  }
13452
- }, [codeMode?.rootDir]);
13648
+ }, [codeMode, rootDir]);
13453
13649
  const recentFilesRef = useRef3([]);
13454
13650
  const recordRecentFile = useCallback((p) => {
13455
13651
  const list = recentFilesRef.current;
@@ -13459,10 +13655,10 @@ function useCompletionPickers({
13459
13655
  if (list.length > 20) list.length = 20;
13460
13656
  }, []);
13461
13657
  const atPicker = useMemo2(() => {
13462
- if (!codeMode?.rootDir) return null;
13658
+ if (!codeMode) return null;
13463
13659
  if (slashMatches !== null) return null;
13464
13660
  return detectAtPicker(input);
13465
- }, [codeMode?.rootDir, input, slashMatches]);
13661
+ }, [codeMode, input, slashMatches]);
13466
13662
  const atMatches = useMemo2(() => {
13467
13663
  if (!atPicker) return null;
13468
13664
  return rankPickerCandidates(atFiles, atPicker.query, {
@@ -13902,13 +14098,13 @@ var PLAIN_UI = process.env.REASONIX_UI === "plain";
13902
14098
  function LoopStatusRow({
13903
14099
  loop: loop2
13904
14100
  }) {
13905
- const [, setTick] = React23.useState(0);
13906
- React23.useEffect(() => {
14101
+ const [, setTick] = React24.useState(0);
14102
+ React24.useEffect(() => {
13907
14103
  const id = setInterval(() => setTick((t2) => t2 + 1), 1e3);
13908
14104
  return () => clearInterval(id);
13909
14105
  }, []);
13910
14106
  const nextFireMs = Math.max(0, loop2.nextFireAt - Date.now());
13911
- return /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { color: "cyan" }, `\u25B8 ${formatLoopStatus(loop2.prompt, nextFireMs, loop2.iter)} \xB7 /loop stop or type to cancel`));
14107
+ return /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text20, { color: "cyan" }, `\u25B8 ${formatLoopStatus(loop2.prompt, nextFireMs, loop2.iter)} \xB7 /loop stop or type to cancel`));
13912
14108
  }
13913
14109
  function App({
13914
14110
  model: model2,
@@ -13967,10 +14163,12 @@ function App({
13967
14163
  setHistorical
13968
14164
  });
13969
14165
  const [statusLine, setStatusLine] = useState10(null);
14166
+ const [currentRootDir, setCurrentRootDir] = useState10(
14167
+ () => codeMode?.rootDir ?? process.cwd()
14168
+ );
13970
14169
  const [hookList, setHookList] = useState10(
13971
14170
  () => loadHooks({ projectRoot: codeMode?.rootDir })
13972
14171
  );
13973
- const hookCwd = codeMode?.rootDir ?? process.cwd();
13974
14172
  const {
13975
14173
  undoBanner,
13976
14174
  recordEdit,
@@ -14012,6 +14210,7 @@ function App({
14012
14210
  }, 1200);
14013
14211
  }, [editMode]);
14014
14212
  const [pendingShell, setPendingShell] = useState10(null);
14213
+ const [pendingWorkspace, setPendingWorkspace] = useState10(null);
14015
14214
  const [pendingPlan, setPendingPlan] = useState10(null);
14016
14215
  const [stagedInput, setStagedInput] = useState10(null);
14017
14216
  const [pendingCheckpoint, setPendingCheckpoint] = useState10(null);
@@ -14106,6 +14305,9 @@ function App({
14106
14305
  }
14107
14306
  });
14108
14307
  }
14308
+ if (tools && !tools.has("change_workspace")) {
14309
+ registerWorkspaceTool(tools);
14310
+ }
14109
14311
  const prefix = new ImmutablePrefix({
14110
14312
  system,
14111
14313
  toolSpecs: tools?.specs()
@@ -14119,7 +14321,7 @@ function App({
14119
14321
  branch: branch2,
14120
14322
  session,
14121
14323
  hooks: hookList,
14122
- hookCwd,
14324
+ hookCwd: currentRootDir,
14123
14325
  // Restore the user's last-chosen effort cap. Without this a
14124
14326
  // `/effort high` silently reverted to `max` on relaunch — the
14125
14327
  // loop's constructor default wins over persisted state.
@@ -14131,6 +14333,49 @@ function App({
14131
14333
  useEffect6(() => {
14132
14334
  loop2.hooks = hookList;
14133
14335
  }, [loop2, hookList]);
14336
+ const applyCwdChange = useCallback4(
14337
+ (newRoot) => {
14338
+ setCurrentRootDir(newRoot);
14339
+ const fresh = loadHooks({ projectRoot: codeMode ? newRoot : void 0 });
14340
+ setHookList(fresh);
14341
+ const codeRebound = codeMode?.reregisterTools !== void 0;
14342
+ if (codeMode?.reregisterTools) {
14343
+ codeMode.reregisterTools(newRoot);
14344
+ }
14345
+ if (tools) {
14346
+ registerSkillTools(tools, {
14347
+ projectRoot: codeMode ? newRoot : void 0,
14348
+ subagentRunner: async (skill2, task) => {
14349
+ const result = await spawnSubagent({
14350
+ client: loop2.client,
14351
+ parentRegistry: tools,
14352
+ system: skill2.body,
14353
+ task,
14354
+ model: skill2.model,
14355
+ sink: subagentSinkRef.current,
14356
+ skillName: skill2.name
14357
+ });
14358
+ return formatSubagentResult(result);
14359
+ }
14360
+ });
14361
+ }
14362
+ const lines = [`\u25B8 cwd \u2192 ${newRoot}`, ` hooks reloaded (${fresh.length} active)`];
14363
+ if (codeMode) {
14364
+ lines.push(
14365
+ codeRebound ? " filesystem / shell / memory tools rebound to new root" : " warning: reregisterTools callback missing \u2014 tool sandbox unchanged"
14366
+ );
14367
+ lines.push(
14368
+ " note: system prompt context (gitignore, REASONIX.md stack) was",
14369
+ " baked at session start and still references the original root."
14370
+ );
14371
+ }
14372
+ return lines.join("\n");
14373
+ },
14374
+ [codeMode, loop2, tools, subagentSinkRef]
14375
+ );
14376
+ useEffect6(() => {
14377
+ loop2.hookCwd = currentRootDir;
14378
+ }, [loop2, currentRootDir]);
14134
14379
  const {
14135
14380
  balance,
14136
14381
  models: models2,
@@ -14155,7 +14400,14 @@ function App({
14155
14400
  slashArgSelected,
14156
14401
  setSlashArgSelected,
14157
14402
  pickSlashArg
14158
- } = useCompletionPickers({ input, setInput, codeMode, models: models2, mcpServers });
14403
+ } = useCompletionPickers({
14404
+ input,
14405
+ setInput,
14406
+ codeMode,
14407
+ rootDir: currentRootDir,
14408
+ models: models2,
14409
+ mcpServers
14410
+ });
14159
14411
  useEffect6(() => {
14160
14412
  if (!progressSink) return;
14161
14413
  progressSink.current = (info) => {
@@ -14275,11 +14527,11 @@ function App({
14275
14527
  if (key.escape && busy) {
14276
14528
  if (abortedThisTurn.current) return;
14277
14529
  abortedThisTurn.current = true;
14278
- const resolve9 = editReviewResolveRef.current;
14279
- if (resolve9) {
14530
+ const resolve12 = editReviewResolveRef.current;
14531
+ if (resolve12) {
14280
14532
  editReviewResolveRef.current = null;
14281
14533
  setPendingEditReview(null);
14282
- resolve9("reject");
14534
+ resolve12("reject");
14283
14535
  }
14284
14536
  if (activeLoopRef.current) stopLoop();
14285
14537
  loop2.abort();
@@ -14302,7 +14554,7 @@ function App({
14302
14554
  ]);
14303
14555
  return;
14304
14556
  }
14305
- if (codeMode && key.shift && key.tab && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision) {
14557
+ if (codeMode && key.shift && key.tab && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision) {
14306
14558
  setEditMode((m) => {
14307
14559
  const next = m === "review" ? "auto" : m === "auto" ? "yolo" : "review";
14308
14560
  const message = next === "yolo" ? "\u25B8 edit mode: YOLO \u2014 edits AND shell commands auto-run. /undo still rolls back edits. Use carefully." : next === "auto" ? "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo. Shell commands still ask." : "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)";
@@ -14314,7 +14566,7 @@ function App({
14314
14566
  });
14315
14567
  return;
14316
14568
  }
14317
- if (codeMode && input.length === 0 && (chKey === "u" || chKey === "U") && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision && // Fire when EITHER the banner is up OR there's any non-undone
14569
+ if (codeMode && input.length === 0 && (chKey === "u" || chKey === "U") && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision && // Fire when EITHER the banner is up OR there's any non-undone
14318
14570
  // history entry — the keybind is useful long after the 5-second
14319
14571
  // banner expires, which users rightly want.
14320
14572
  (undoBanner || hasUndoable())) {
@@ -14403,11 +14655,11 @@ function App({
14403
14655
  block = { path: relPath, search, replace, offset: 0 };
14404
14656
  } else {
14405
14657
  const content = typeof args.content === "string" ? args.content : "";
14406
- block = toWholeFileEditBlock(relPath, content, codeMode.rootDir);
14658
+ block = toWholeFileEditBlock(relPath, content, currentRootDir);
14407
14659
  }
14408
14660
  const applyNow = () => {
14409
- const snaps = snapshotBeforeEdits([block], codeMode.rootDir);
14410
- const results = applyEditBlocks([block], codeMode.rootDir);
14661
+ const snaps = snapshotBeforeEdits([block], currentRootDir);
14662
+ const results = applyEditBlocks([block], currentRootDir);
14411
14663
  const good = results.some((r) => r.status === "applied" || r.status === "created");
14412
14664
  if (good) {
14413
14665
  recordEdit("auto", [block], results, snaps);
@@ -14476,8 +14728,8 @@ function App({
14476
14728
  if (selected.length === 0) {
14477
14729
  return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
14478
14730
  }
14479
- const snaps = snapshotBeforeEdits(selected, codeMode.rootDir);
14480
- const results = applyEditBlocks(selected, codeMode.rootDir);
14731
+ const snaps = snapshotBeforeEdits(selected, currentRootDir);
14732
+ const results = applyEditBlocks(selected, currentRootDir);
14481
14733
  const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
14482
14734
  if (anyApplied) recordEdit("review-apply", selected, results, snaps);
14483
14735
  pendingEdits.current = remaining;
@@ -14488,7 +14740,7 @@ function App({
14488
14740
  \u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
14489
14741
  return formatEditResults(results) + tail;
14490
14742
  },
14491
- [codeMode, session, syncPendingCount, recordEdit]
14743
+ [codeMode, currentRootDir, session, syncPendingCount, recordEdit]
14492
14744
  );
14493
14745
  const codeDiscard = useCallback4(
14494
14746
  (indices) => {
@@ -14653,7 +14905,7 @@ function App({
14653
14905
  const hashParse = detectHashMemory(text);
14654
14906
  if (hashParse?.kind === "memory" || hashParse?.kind === "memory-global") {
14655
14907
  const isGlobal = hashParse.kind === "memory-global";
14656
- const memRoot = codeMode?.rootDir ?? process.cwd();
14908
+ const memRoot = currentRootDir;
14657
14909
  promptHistory.current.push(text);
14658
14910
  try {
14659
14911
  const result = isGlobal ? appendGlobalMemory(hashParse.note) : appendProjectMemory(memRoot, hashParse.note);
@@ -14684,7 +14936,7 @@ function App({
14684
14936
  }
14685
14937
  const bangCmd = detectBangCommand(text);
14686
14938
  if (bangCmd !== null) {
14687
- const bangRoot = codeMode?.rootDir ?? process.cwd();
14939
+ const bangRoot = currentRootDir;
14688
14940
  promptHistory.current.push(text);
14689
14941
  setHistorical((prev) => [
14690
14942
  ...prev,
@@ -14747,10 +14999,10 @@ function App({
14747
14999
  codeDiscard: codeMode ? codeDiscard : void 0,
14748
15000
  codeHistory: codeMode ? codeHistory : void 0,
14749
15001
  codeShowEdit: codeMode ? codeShowEdit : void 0,
14750
- codeRoot: codeMode?.rootDir,
15002
+ codeRoot: codeMode ? currentRootDir : void 0,
14751
15003
  pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
14752
15004
  toolHistory: () => toolHistoryRef.current,
14753
- memoryRoot: codeMode?.rootDir ?? process.cwd(),
15005
+ memoryRoot: currentRootDir,
14754
15006
  planMode,
14755
15007
  setPlanMode: codeMode ? togglePlanMode : void 0,
14756
15008
  clearPendingPlan: codeMode ? clearPendingPlan : void 0,
@@ -14774,10 +15026,11 @@ function App({
14774
15026
  { id: `sys-late-${Date.now()}-${Math.random()}`, role: "info", text: text2 }
14775
15027
  ]),
14776
15028
  reloadHooks: () => {
14777
- const fresh = loadHooks({ projectRoot: codeMode?.rootDir });
15029
+ const fresh = loadHooks({ projectRoot: codeMode ? currentRootDir : void 0 });
14778
15030
  setHookList(fresh);
14779
15031
  return fresh.length;
14780
15032
  },
15033
+ setCwd: (newRoot) => applyCwdChange(newRoot),
14781
15034
  latestVersion,
14782
15035
  refreshLatestVersion,
14783
15036
  models: models2,
@@ -14858,7 +15111,7 @@ function App({
14858
15111
  if (hookList.some((h) => h.event === "UserPromptSubmit")) {
14859
15112
  const promptReport = await runHooks({
14860
15113
  hooks: hookList,
14861
- payload: { event: "UserPromptSubmit", cwd: hookCwd, prompt: text }
15114
+ payload: { event: "UserPromptSubmit", cwd: currentRootDir, prompt: text }
14862
15115
  });
14863
15116
  if (promptReport.outcomes.length > 0) {
14864
15117
  setHistorical((prev) => [
@@ -14926,8 +15179,8 @@ function App({
14926
15179
  };
14927
15180
  const timer = PLAIN_UI ? null : setInterval(flush, FLUSH_INTERVAL_MS);
14928
15181
  let modelInput = text;
14929
- if (codeMode?.rootDir) {
14930
- const expanded = expandAtMentions(text, codeMode.rootDir);
15182
+ if (codeMode) {
15183
+ const expanded = expandAtMentions(text, currentRootDir);
14931
15184
  if (expanded.expansions.length > 0) {
14932
15185
  modelInput = expanded.text;
14933
15186
  const inlined = expanded.expansions.filter((ex) => ex.ok).map((ex) => `${ex.path} (${(ex.bytes ?? 0).toLocaleString()} bytes)`);
@@ -15058,8 +15311,8 @@ function App({
15058
15311
  const blocks = parseEditBlocks(finalText);
15059
15312
  if (blocks.length > 0) {
15060
15313
  if (editModeRef.current === "auto" || editModeRef.current === "yolo") {
15061
- const snaps = snapshotBeforeEdits(blocks, codeMode.rootDir);
15062
- const results = applyEditBlocks(blocks, codeMode.rootDir);
15314
+ const snaps = snapshotBeforeEdits(blocks, currentRootDir);
15315
+ const results = applyEditBlocks(blocks, currentRootDir);
15063
15316
  const good = results.some(
15064
15317
  (r) => r.status === "applied" || r.status === "created"
15065
15318
  );
@@ -15145,6 +15398,18 @@ function App({
15145
15398
  } catch {
15146
15399
  }
15147
15400
  }
15401
+ if (ev.toolName === "change_workspace" && ev.content.includes('"WorkspaceConfirmationError:') && ev.toolArgs) {
15402
+ try {
15403
+ const parsed = JSON.parse(ev.toolArgs);
15404
+ if (typeof parsed.path === "string" && parsed.path.trim()) {
15405
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
15406
+ const expanded = parsed.path.startsWith("~") && home ? pathMod6.join(home, parsed.path.slice(1)) : parsed.path;
15407
+ const abs = pathMod6.resolve(expanded);
15408
+ setPendingWorkspace({ path: abs });
15409
+ }
15410
+ } catch {
15411
+ }
15412
+ }
15148
15413
  if (codeMode && ev.toolName === "submit_plan" && ev.content.includes('"PlanProposedError:')) {
15149
15414
  try {
15150
15415
  const parsed = JSON.parse(ev.content);
@@ -15267,7 +15532,7 @@ function App({
15267
15532
  hooks: hookList,
15268
15533
  payload: {
15269
15534
  event: "Stop",
15270
- cwd: hookCwd,
15535
+ cwd: currentRootDir,
15271
15536
  lastAssistantText: streamRef.text,
15272
15537
  turn: loop2.stats.summary().turns
15273
15538
  }
@@ -15305,8 +15570,8 @@ function App({
15305
15570
  codeMode,
15306
15571
  codeShowEdit,
15307
15572
  codeUndo,
15573
+ currentRootDir,
15308
15574
  exit2,
15309
- hookCwd,
15310
15575
  hookList,
15311
15576
  loop2,
15312
15577
  latestVersion,
@@ -15341,7 +15606,8 @@ function App({
15341
15606
  stopLoop,
15342
15607
  startLoop,
15343
15608
  getLoopStatus,
15344
- startWalkthrough
15609
+ startWalkthrough,
15610
+ applyCwdChange
15345
15611
  ]
15346
15612
  );
15347
15613
  useEffect6(() => {
@@ -15398,13 +15664,13 @@ function App({
15398
15664
  } else {
15399
15665
  if (choice === "always_allow") {
15400
15666
  const prefix = derivePrefix(cmd);
15401
- addProjectShellAllowed(codeMode.rootDir, prefix);
15667
+ addProjectShellAllowed(currentRootDir, prefix);
15402
15668
  setHistorical((prev) => [
15403
15669
  ...prev,
15404
15670
  {
15405
15671
  id: `sh-allow-${Date.now()}`,
15406
15672
  role: "info",
15407
- text: `\u25B8 always allowed "${prefix}" for ${codeMode.rootDir}`
15673
+ text: `\u25B8 always allowed "${prefix}" for ${currentRootDir}`
15408
15674
  }
15409
15675
  ]);
15410
15676
  }
@@ -15421,7 +15687,7 @@ function App({
15421
15687
  let jobId = null;
15422
15688
  let preview = "";
15423
15689
  try {
15424
- const res = await codeMode.jobs.start(cmd, { cwd: codeMode.rootDir });
15690
+ const res = await codeMode.jobs.start(cmd, { cwd: currentRootDir });
15425
15691
  startedOk = true;
15426
15692
  jobId = res.jobId;
15427
15693
  preview = res.preview;
@@ -15455,7 +15721,7 @@ ${msg}`;
15455
15721
  } else {
15456
15722
  let body;
15457
15723
  try {
15458
- const res = await runCommand(cmd, { cwd: codeMode.rootDir });
15724
+ const res = await runCommand(cmd, { cwd: currentRootDir });
15459
15725
  body = formatCommandResult(cmd, res);
15460
15726
  } catch (err) {
15461
15727
  body = `$ ${cmd}
@@ -15477,7 +15743,7 @@ ${body}`;
15477
15743
  await handleSubmit(synthetic);
15478
15744
  }
15479
15745
  },
15480
- [pendingShell, codeMode, handleSubmit, busy, loop2]
15746
+ [pendingShell, codeMode, currentRootDir, handleSubmit, busy, loop2]
15481
15747
  );
15482
15748
  useEffect6(() => {
15483
15749
  if (!busy && queuedSubmit !== null) {
@@ -15486,6 +15752,40 @@ ${body}`;
15486
15752
  void handleSubmit(text);
15487
15753
  }
15488
15754
  }, [busy, queuedSubmit, handleSubmit]);
15755
+ const handleWorkspaceConfirm = useCallback4(
15756
+ async (choice) => {
15757
+ const pending = pendingWorkspace;
15758
+ if (!pending) return;
15759
+ const target = pending.path;
15760
+ setPendingWorkspace(null);
15761
+ let synthetic;
15762
+ if (choice === "deny") {
15763
+ setHistorical((prev) => [
15764
+ ...prev,
15765
+ {
15766
+ id: `ws-deny-${Date.now()}`,
15767
+ role: "info",
15768
+ text: `\u25B8 denied workspace switch: ${target}`
15769
+ }
15770
+ ]);
15771
+ synthetic = `I denied switching the workspace to \`${target}\`. Please continue without changing directories.`;
15772
+ } else {
15773
+ const info = applyCwdChange(target);
15774
+ setHistorical((prev) => [
15775
+ ...prev,
15776
+ { id: `ws-switch-${Date.now()}`, role: "info", text: info }
15777
+ ]);
15778
+ synthetic = `I approved the workspace switch. The session is now rooted at \`${target}\` \u2014 your filesystem / shell / memory tools resolve against that path on every subsequent call. Continue with my original request from this new root.`;
15779
+ }
15780
+ if (busy) {
15781
+ loop2.abort();
15782
+ setQueuedSubmit(synthetic);
15783
+ } else {
15784
+ await handleSubmit(synthetic);
15785
+ }
15786
+ },
15787
+ [pendingWorkspace, applyCwdChange, busy, loop2, handleSubmit]
15788
+ );
15489
15789
  const handlePlanConfirm = useCallback4(
15490
15790
  async (choice) => {
15491
15791
  const hadPendingPlan = pendingPlan !== null;
@@ -15792,12 +16092,12 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15792
16092
  async (choice) => handleReviseConfirmRef.current(choice),
15793
16093
  []
15794
16094
  );
15795
- return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(
16095
+ return /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
15796
16096
  TickerProvider,
15797
16097
  {
15798
- disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingEditReview || walkthroughActive || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
16098
+ disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingWorkspace || !!pendingEditReview || walkthroughActive || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
15799
16099
  },
15800
- /* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column" }, /* @__PURE__ */ React23.createElement(
16100
+ /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React24.createElement(
15801
16101
  StatsPanel,
15802
16102
  {
15803
16103
  summary,
@@ -15814,28 +16114,28 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15814
16114
  proArmed,
15815
16115
  escalated: turnOnPro
15816
16116
  }
15817
- ), /* @__PURE__ */ React23.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React23.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !historical.some((e) => e.role === "user" || e.role === "assistant") && !busy && !streaming ? /* @__PURE__ */ React23.createElement(WelcomeBanner, { inCodeMode: !!codeMode }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React23.createElement(Box21, { marginY: 1 }, /* @__PURE__ */ React23.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React23.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React23.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React23.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React23.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React23.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React23.createElement(
16117
+ ), /* @__PURE__ */ React24.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React24.createElement(EventRow, { key: item.id, event: item, projectRoot: currentRootDir })), !historical.some((e) => e.role === "user" || e.role === "assistant") && !busy && !streaming ? /* @__PURE__ */ React24.createElement(WelcomeBanner, { inCodeMode: !!codeMode }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React24.createElement(Box22, { marginY: 1 }, /* @__PURE__ */ React24.createElement(EventRow, { event: streaming, projectRoot: currentRootDir })) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React24.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React24.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React24.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React24.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React24.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React24.createElement(
15818
16118
  PlanRefineInput,
15819
16119
  {
15820
16120
  mode: stagedInput.mode,
15821
16121
  onSubmit: handleStagedInputSubmit,
15822
16122
  onCancel: handleStagedInputCancel
15823
16123
  }
15824
- ) : stagedCheckpointRevise ? /* @__PURE__ */ React23.createElement(
16124
+ ) : stagedCheckpointRevise ? /* @__PURE__ */ React24.createElement(
15825
16125
  PlanRefineInput,
15826
16126
  {
15827
16127
  mode: "checkpoint-revise",
15828
16128
  onSubmit: handleCheckpointReviseSubmit,
15829
16129
  onCancel: handleCheckpointReviseCancel
15830
16130
  }
15831
- ) : stagedChoiceCustom ? /* @__PURE__ */ React23.createElement(
16131
+ ) : stagedChoiceCustom ? /* @__PURE__ */ React24.createElement(
15832
16132
  PlanRefineInput,
15833
16133
  {
15834
16134
  mode: "choice-custom",
15835
16135
  onSubmit: handleChoiceCustomSubmit,
15836
16136
  onCancel: handleChoiceCustomCancel
15837
16137
  }
15838
- ) : pendingChoice ? /* @__PURE__ */ React23.createElement(
16138
+ ) : pendingChoice ? /* @__PURE__ */ React24.createElement(
15839
16139
  ChoiceConfirm,
15840
16140
  {
15841
16141
  question: pendingChoice.question,
@@ -15843,7 +16143,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15843
16143
  allowCustom: pendingChoice.allowCustom,
15844
16144
  onChoose: stableHandleChoiceConfirm
15845
16145
  }
15846
- ) : pendingRevision ? /* @__PURE__ */ React23.createElement(
16146
+ ) : pendingRevision ? /* @__PURE__ */ React24.createElement(
15847
16147
  PlanReviseConfirm,
15848
16148
  {
15849
16149
  reason: pendingRevision.reason,
@@ -15854,7 +16154,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15854
16154
  summary: pendingRevision.summary,
15855
16155
  onChoose: stableHandleReviseConfirm
15856
16156
  }
15857
- ) : pendingCheckpoint ? /* @__PURE__ */ React23.createElement(
16157
+ ) : pendingCheckpoint ? /* @__PURE__ */ React24.createElement(
15858
16158
  PlanCheckpointConfirm,
15859
16159
  {
15860
16160
  stepId: pendingCheckpoint.stepId,
@@ -15865,16 +16165,16 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15865
16165
  completedStepIds: completedStepIdsRef.current,
15866
16166
  onChoose: stableHandleCheckpointConfirm
15867
16167
  }
15868
- ) : pendingPlan ? /* @__PURE__ */ React23.createElement(
16168
+ ) : pendingPlan ? /* @__PURE__ */ React24.createElement(
15869
16169
  PlanConfirm,
15870
16170
  {
15871
16171
  plan: pendingPlan,
15872
16172
  steps: planStepsRef.current ?? void 0,
15873
16173
  summary: planSummaryRef.current ?? void 0,
15874
16174
  onChoose: stableHandlePlanConfirm,
15875
- projectRoot: hookCwd
16175
+ projectRoot: currentRootDir
15876
16176
  }
15877
- ) : pendingShell ? /* @__PURE__ */ React23.createElement(
16177
+ ) : pendingShell ? /* @__PURE__ */ React24.createElement(
15878
16178
  ShellConfirm,
15879
16179
  {
15880
16180
  command: pendingShell.command,
@@ -15882,26 +16182,34 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15882
16182
  kind: pendingShell.kind,
15883
16183
  onChoose: handleShellConfirm
15884
16184
  }
15885
- ) : pendingEditReview ? /* @__PURE__ */ React23.createElement(
16185
+ ) : pendingWorkspace ? /* @__PURE__ */ React24.createElement(
16186
+ WorkspaceConfirm,
16187
+ {
16188
+ path: pendingWorkspace.path,
16189
+ currentRoot: currentRootDir,
16190
+ mcpServerCount: mcpServers?.length ?? 0,
16191
+ onChoose: handleWorkspaceConfirm
16192
+ }
16193
+ ) : pendingEditReview ? /* @__PURE__ */ React24.createElement(
15886
16194
  EditConfirm,
15887
16195
  {
15888
16196
  block: pendingEditReview,
15889
16197
  onChoose: (choice) => {
15890
- const resolve9 = editReviewResolveRef.current;
15891
- if (resolve9) {
16198
+ const resolve12 = editReviewResolveRef.current;
16199
+ if (resolve12) {
15892
16200
  editReviewResolveRef.current = null;
15893
- resolve9(choice);
16201
+ resolve12(choice);
15894
16202
  }
15895
16203
  }
15896
16204
  }
15897
- ) : walkthroughActive && pendingEdits.current.length > 0 ? /* @__PURE__ */ React23.createElement(
16205
+ ) : walkthroughActive && pendingEdits.current.length > 0 ? /* @__PURE__ */ React24.createElement(
15898
16206
  EditConfirm,
15899
16207
  {
15900
16208
  key: `walk-${pendingTick}`,
15901
16209
  block: pendingEdits.current[0],
15902
16210
  onChoose: handleWalkChoice
15903
16211
  }
15904
- ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, codeMode ? /* @__PURE__ */ React23.createElement(
16212
+ ) : /* @__PURE__ */ React24.createElement(React24.Fragment, null, codeMode ? /* @__PURE__ */ React24.createElement(
15905
16213
  ModeStatusBar,
15906
16214
  {
15907
16215
  editMode,
@@ -15911,7 +16219,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15911
16219
  undoArmed: !!undoBanner || hasUndoable(),
15912
16220
  jobs: codeMode.jobs
15913
16221
  }
15914
- ) : null, activeLoop ? /* @__PURE__ */ React23.createElement(LoopStatusRow, { loop: activeLoop }) : null, /* @__PURE__ */ React23.createElement(
16222
+ ) : null, activeLoop ? /* @__PURE__ */ React24.createElement(LoopStatusRow, { loop: activeLoop }) : null, /* @__PURE__ */ React24.createElement(
15915
16223
  PromptInput,
15916
16224
  {
15917
16225
  value: input,
@@ -15921,14 +16229,14 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15921
16229
  onHistoryPrev: recallPrev,
15922
16230
  onHistoryNext: recallNext
15923
16231
  }
15924
- ), /* @__PURE__ */ React23.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React23.createElement(
16232
+ ), /* @__PURE__ */ React24.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React24.createElement(
15925
16233
  AtMentionSuggestions,
15926
16234
  {
15927
16235
  matches: atMatches,
15928
16236
  selectedIndex: atSelected,
15929
16237
  query: atPicker?.query ?? ""
15930
16238
  }
15931
- ), slashArgContext ? /* @__PURE__ */ React23.createElement(
16239
+ ), slashArgContext ? /* @__PURE__ */ React24.createElement(
15932
16240
  SlashArgPicker,
15933
16241
  {
15934
16242
  matches: slashArgMatches,
@@ -15942,15 +16250,15 @@ Continue executing from the next pending step. Call mark_step_complete after eac
15942
16250
  }
15943
16251
 
15944
16252
  // src/cli/ui/SessionPicker.tsx
15945
- import { Box as Box22, Text as Text20 } from "ink";
15946
- import React24 from "react";
16253
+ import { Box as Box23, Text as Text21 } from "ink";
16254
+ import React25 from "react";
15947
16255
  function SessionPicker({
15948
16256
  sessionName,
15949
16257
  messageCount,
15950
16258
  lastActive,
15951
16259
  onChoose
15952
16260
  }) {
15953
- return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React24.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(Text20, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React24.createElement(Text20, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React24.createElement(
16261
+ return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React25.createElement(Box23, { marginBottom: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React25.createElement(
15954
16262
  SingleSelect,
15955
16263
  {
15956
16264
  initialValue: "new",
@@ -15973,7 +16281,7 @@ function SessionPicker({
15973
16281
  ],
15974
16282
  onSubmit: (v) => onChoose(v)
15975
16283
  }
15976
- ), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text20, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
16284
+ ), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
15977
16285
  }
15978
16286
  function relativeTime2(date) {
15979
16287
  const ms = Date.now() - date.getTime();
@@ -15989,9 +16297,9 @@ function relativeTime2(date) {
15989
16297
  }
15990
16298
 
15991
16299
  // src/cli/ui/Setup.tsx
15992
- import { Box as Box23, Text as Text21, useApp as useApp2 } from "ink";
16300
+ import { Box as Box24, Text as Text22, useApp as useApp2 } from "ink";
15993
16301
  import TextInput from "ink-text-input";
15994
- import React25, { useState as useState11 } from "react";
16302
+ import React26, { useState as useState11 } from "react";
15995
16303
  function Setup({ onReady }) {
15996
16304
  const [value, setValue] = useState11("");
15997
16305
  const [error, setError] = useState11(null);
@@ -16015,7 +16323,7 @@ function Setup({ onReady }) {
16015
16323
  }
16016
16324
  onReady(trimmed);
16017
16325
  };
16018
- return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React25.createElement(
16326
+ return /* @__PURE__ */ React26.createElement(Box24, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React26.createElement(
16019
16327
  TextInput,
16020
16328
  {
16021
16329
  value,
@@ -16024,7 +16332,7 @@ function Setup({ onReady }) {
16024
16332
  mask: "\u2022",
16025
16333
  placeholder: "sk-..."
16026
16334
  }
16027
- )), error ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { color: "red" }, error)) : value ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "(Type /exit to abort.)")));
16335
+ )), error ? /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { color: "red" }, error)) : value ? /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "(Type /exit to abort.)")));
16028
16336
  }
16029
16337
 
16030
16338
  // src/cli/commands/chat.tsx
@@ -16040,7 +16348,7 @@ function Root({
16040
16348
  const [key, setKey] = useState12(initialKey);
16041
16349
  const [pending, setPending] = useState12(sessionPreview);
16042
16350
  if (!key) {
16043
- return /* @__PURE__ */ React26.createElement(
16351
+ return /* @__PURE__ */ React27.createElement(
16044
16352
  Setup,
16045
16353
  {
16046
16354
  onReady: (k) => {
@@ -16052,7 +16360,7 @@ function Root({
16052
16360
  }
16053
16361
  process.env.DEEPSEEK_API_KEY = key;
16054
16362
  if (pending && appProps.session) {
16055
- return /* @__PURE__ */ React26.createElement(KeystrokeProvider, null, /* @__PURE__ */ React26.createElement(
16363
+ return /* @__PURE__ */ React27.createElement(KeystrokeProvider, null, /* @__PURE__ */ React27.createElement(
16056
16364
  SessionPicker,
16057
16365
  {
16058
16366
  sessionName: appProps.session,
@@ -16067,7 +16375,7 @@ function Root({
16067
16375
  }
16068
16376
  ));
16069
16377
  }
16070
- return /* @__PURE__ */ React26.createElement(KeystrokeProvider, null, /* @__PURE__ */ React26.createElement(
16378
+ return /* @__PURE__ */ React27.createElement(KeystrokeProvider, null, /* @__PURE__ */ React27.createElement(
16071
16379
  App,
16072
16380
  {
16073
16381
  model: appProps.model,
@@ -16165,14 +16473,14 @@ async function chatCommand(opts) {
16165
16473
  const prior = loadSessionMessages(opts.session);
16166
16474
  if (prior.length > 0) {
16167
16475
  const p = sessionPath(opts.session);
16168
- const mtime = existsSync13(p) ? statSync7(p).mtime : /* @__PURE__ */ new Date();
16476
+ const mtime = existsSync15(p) ? statSync9(p).mtime : /* @__PURE__ */ new Date();
16169
16477
  sessionPreview = { messageCount: prior.length, lastActive: mtime };
16170
16478
  }
16171
16479
  } else if (opts.session && opts.forceNew) {
16172
16480
  rewriteSession(opts.session, []);
16173
16481
  }
16174
16482
  const { waitUntilExit } = render(
16175
- /* @__PURE__ */ React26.createElement(
16483
+ /* @__PURE__ */ React27.createElement(
16176
16484
  Root,
16177
16485
  {
16178
16486
  initialKey,
@@ -16196,7 +16504,7 @@ async function chatCommand(opts) {
16196
16504
  }
16197
16505
 
16198
16506
  // src/cli/commands/code.tsx
16199
- import { basename as basename2, resolve as resolve7 } from "path";
16507
+ import { basename as basename2, resolve as resolve10 } from "path";
16200
16508
 
16201
16509
  // src/index/semantic/builder.ts
16202
16510
  import { promises as fs5 } from "fs";
@@ -16843,29 +17151,32 @@ async function bootstrapSemanticSearchInCodeMode(registry, rootDir, opts = {}) {
16843
17151
 
16844
17152
  // src/cli/commands/code.tsx
16845
17153
  async function codeCommand(opts = {}) {
16846
- const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-FMYQ7IDW.js");
16847
- const rootDir = resolve7(opts.dir ?? process.cwd());
17154
+ const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-YRY4HPMZ.js");
17155
+ const rootDir = resolve10(opts.dir ?? process.cwd());
16848
17156
  const session = opts.noSession ? void 0 : `code-${sanitizeName(basename2(rootDir))}`;
16849
17157
  const tools = new ToolRegistry();
16850
- registerFilesystemTools(tools, { rootDir });
16851
17158
  const jobs2 = new JobRegistry();
16852
- registerShellTools(tools, {
16853
- rootDir,
16854
- // Per-project "always allow" list persisted from prior ShellConfirm
16855
- // choices; merged on top of the built-in allowlist in shell.ts.
16856
- // GETTER form — re-read every dispatch so a prefix the user adds
16857
- // via ShellConfirm mid-session takes effect on the next shell call
16858
- // instead of waiting for `/new` or a relaunch.
16859
- extraAllowed: () => loadProjectShellAllowed(rootDir),
16860
- // `yolo` edit-mode disables shell confirmations entirely. Re-read
16861
- // from config on each dispatch so /mode yolo (or Shift+Tab cycling
16862
- // through to it) flips the gate live without forcing a relaunch.
16863
- allowAll: () => loadEditMode() === "yolo",
16864
- jobs: jobs2
16865
- });
17159
+ const registerRootedTools = (root) => {
17160
+ registerFilesystemTools(tools, { rootDir: root });
17161
+ registerShellTools(tools, {
17162
+ rootDir: root,
17163
+ // Per-project "always allow" list persisted from prior ShellConfirm
17164
+ // choices; merged on top of the built-in allowlist in shell.ts.
17165
+ // GETTER form re-read every dispatch so a prefix the user adds
17166
+ // via ShellConfirm mid-session takes effect on the next shell call
17167
+ // instead of waiting for `/new` or a relaunch.
17168
+ extraAllowed: () => loadProjectShellAllowed(root),
17169
+ // `yolo` edit-mode disables shell confirmations entirely. Re-read
17170
+ // from config on each dispatch so /mode yolo (or Shift+Tab cycling
17171
+ // through to it) flips the gate live without forcing a relaunch.
17172
+ allowAll: () => loadEditMode() === "yolo",
17173
+ jobs: jobs2
17174
+ });
17175
+ registerMemoryTools(tools, { projectRoot: root });
17176
+ };
17177
+ registerRootedTools(rootDir);
16866
17178
  registerPlanTool(tools);
16867
17179
  registerChoiceTool(tools);
16868
- registerMemoryTools(tools, { projectRoot: rootDir });
16869
17180
  const semantic2 = await bootstrapSemanticSearchInCodeMode(tools, rootDir);
16870
17181
  process.stderr.write(
16871
17182
  `\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)${semantic2.enabled ? " \xB7 semantic_search on" : ""}
@@ -16881,7 +17192,7 @@ async function codeCommand(opts = {}) {
16881
17192
  transcript: opts.transcript,
16882
17193
  session,
16883
17194
  seedTools: tools,
16884
- codeMode: { rootDir, jobs: jobs2 },
17195
+ codeMode: { rootDir, jobs: jobs2, reregisterTools: registerRootedTools },
16885
17196
  forceResume: opts.forceResume,
16886
17197
  forceNew: opts.forceNew
16887
17198
  });
@@ -16891,35 +17202,35 @@ async function codeCommand(opts = {}) {
16891
17202
  import { writeFileSync as writeFileSync8 } from "fs";
16892
17203
  import { basename as basename3 } from "path";
16893
17204
  import { render as render2 } from "ink";
16894
- import React29 from "react";
17205
+ import React30 from "react";
16895
17206
 
16896
17207
  // src/cli/ui/DiffApp.tsx
16897
- import { Box as Box25, Static as Static2, Text as Text23, useApp as useApp3, useInput } from "ink";
16898
- import React28, { useState as useState13 } from "react";
17208
+ import { Box as Box26, Static as Static2, Text as Text24, useApp as useApp3, useInput } from "ink";
17209
+ import React29, { useState as useState13 } from "react";
16899
17210
 
16900
17211
  // src/cli/ui/RecordView.tsx
16901
- import { Box as Box24, Text as Text22 } from "ink";
16902
- import React27 from "react";
17212
+ import { Box as Box25, Text as Text23 } from "ink";
17213
+ import React28 from "react";
16903
17214
  function RecordView({ rec, compact: compact2 = false }) {
16904
17215
  const toolArgsMax = compact2 ? 120 : 200;
16905
17216
  const toolContentMax = compact2 ? 200 : 400;
16906
17217
  if (rec.role === "user") {
16907
17218
  const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
16908
- return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React27.createElement(Text22, null, content));
17219
+ return /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React28.createElement(Text23, null, content));
16909
17220
  }
16910
17221
  if (rec.role === "assistant_final") {
16911
- return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React27.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React27.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React27.createElement(Text22, null, rec.content) : /* @__PURE__ */ React27.createElement(Text22, { dimColor: true, italic: true }, "(tool-call response only)"));
17222
+ return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Box25, null, /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React28.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React28.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React28.createElement(Text23, null, rec.content) : /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, "(tool-call response only)"));
16912
17223
  }
16913
17224
  if (rec.role === "tool") {
16914
- return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
17225
+ return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
16915
17226
  }
16916
17227
  if (rec.role === "error") {
16917
- return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React27.createElement(Text22, { color: "red" }, rec.error ?? rec.content));
17228
+ return /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React28.createElement(Text23, { color: "red" }, rec.error ?? rec.content));
16918
17229
  }
16919
17230
  if (rec.role === "done" || rec.role === "assistant_delta") {
16920
17231
  return null;
16921
17232
  }
16922
- return /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, "[", rec.role, "] ", rec.content));
17233
+ return /* @__PURE__ */ React28.createElement(Box25, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "[", rec.role, "] ", rec.content));
16923
17234
  }
16924
17235
  function CacheBadge({ usage }) {
16925
17236
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -16928,7 +17239,7 @@ function CacheBadge({ usage }) {
16928
17239
  if (total === 0) return null;
16929
17240
  const pct2 = hit / total * 100;
16930
17241
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
16931
- return /* @__PURE__ */ React27.createElement(Text22, null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React27.createElement(Text22, { color }, pct2.toFixed(1), "%"));
17242
+ return /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React28.createElement(Text23, { color }, pct2.toFixed(1), "%"));
16932
17243
  }
16933
17244
  function truncate2(s, max) {
16934
17245
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -16962,7 +17273,7 @@ function DiffApp({ report }) {
16962
17273
  }
16963
17274
  });
16964
17275
  const pair = report.pairs[idx];
16965
- return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React28.createElement(DiffHeader, { report }), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text23, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React28.createElement(Text23, null, pair ? /* @__PURE__ */ React28.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React28.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React28.createElement(Text23, null, pair.divergenceNote)) : null, /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "j"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "k"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "N"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "g"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "q"), " ", "quit")));
17276
+ return /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React29.createElement(DiffHeader, { report }), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React29.createElement(Text24, null, pair ? /* @__PURE__ */ React29.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React29.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React29.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React29.createElement(Text24, null, pair.divergenceNote)) : null, /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "j"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "k"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "N"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "g"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "q"), " ", "quit")));
16966
17277
  }
16967
17278
  function DiffHeader({ report }) {
16968
17279
  const a = report.a;
@@ -16980,15 +17291,15 @@ function DiffHeader({ report }) {
16980
17291
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
16981
17292
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
16982
17293
  }
16983
- return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React28.createElement(Box25, { justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React28.createElement(Text23, { color: "blue" }, a.label), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " vs B="), /* @__PURE__ */ React28.createElement(Text23, { color: "magenta" }, b.label)), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "cache "), /* @__PURE__ */ React28.createElement(Text23, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text23, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text23, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "cost "), /* @__PURE__ */ React28.createElement(Text23, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text23, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text23, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "model calls "), /* @__PURE__ */ React28.createElement(Text23, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, prefixLine)) : null);
17294
+ return /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React29.createElement(Text24, { color: "blue" }, a.label), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " vs B="), /* @__PURE__ */ React29.createElement(Text24, { color: "magenta" }, b.label)), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "cache "), /* @__PURE__ */ React29.createElement(Text24, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React29.createElement(Text24, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React29.createElement(Text24, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "cost "), /* @__PURE__ */ React29.createElement(Text24, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React29.createElement(Text24, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React29.createElement(Text24, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "model calls "), /* @__PURE__ */ React29.createElement(Text24, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true, italic: true }, prefixLine)) : null);
16984
17295
  }
16985
17296
  function Pane({
16986
17297
  label,
16987
17298
  headerColor,
16988
17299
  records
16989
17300
  }) {
16990
- return /* @__PURE__ */ React28.createElement(
16991
- Box25,
17301
+ return /* @__PURE__ */ React29.createElement(
17302
+ Box26,
16992
17303
  {
16993
17304
  flexDirection: "column",
16994
17305
  flexGrow: 1,
@@ -16996,21 +17307,21 @@ function Pane({
16996
17307
  borderStyle: "single",
16997
17308
  borderColor: headerColor
16998
17309
  },
16999
- /* @__PURE__ */ React28.createElement(Text23, { color: headerColor, bold: true }, label),
17000
- records.length === 0 ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React28.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React28.createElement(RecordView, { key, rec, compact: true }))
17310
+ /* @__PURE__ */ React29.createElement(Text24, { color: headerColor, bold: true }, label),
17311
+ records.length === 0 ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React29.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React29.createElement(RecordView, { key, rec, compact: true }))
17001
17312
  );
17002
17313
  }
17003
17314
  function KindBadge({ kind }) {
17004
17315
  if (kind === "match") {
17005
- return /* @__PURE__ */ React28.createElement(Text23, { color: "green" }, "\u2713 match");
17316
+ return /* @__PURE__ */ React29.createElement(Text24, { color: "green" }, "\u2713 match");
17006
17317
  }
17007
17318
  if (kind === "diverge") {
17008
- return /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "\u2605 diverge");
17319
+ return /* @__PURE__ */ React29.createElement(Text24, { color: "yellow" }, "\u2605 diverge");
17009
17320
  }
17010
17321
  if (kind === "only_in_a") {
17011
- return /* @__PURE__ */ React28.createElement(Text23, { color: "blue" }, "\u2190 only in A");
17322
+ return /* @__PURE__ */ React29.createElement(Text24, { color: "blue" }, "\u2190 only in A");
17012
17323
  }
17013
- return /* @__PURE__ */ React28.createElement(Text23, { color: "magenta" }, "\u2192 only in B");
17324
+ return /* @__PURE__ */ React29.createElement(Text24, { color: "magenta" }, "\u2192 only in B");
17014
17325
  }
17015
17326
  function paneRecords(pair, side) {
17016
17327
  if (!pair) return [];
@@ -17041,7 +17352,7 @@ markdown report written to ${opts.mdPath}`);
17041
17352
  return;
17042
17353
  }
17043
17354
  if (wantTui) {
17044
- const { waitUntilExit } = render2(React29.createElement(DiffApp, { report }), {
17355
+ const { waitUntilExit } = render2(React30.createElement(DiffApp, { report }), {
17045
17356
  exitOnCtrlC: true,
17046
17357
  patchConsole: false
17047
17358
  });
@@ -17052,7 +17363,7 @@ markdown report written to ${opts.mdPath}`);
17052
17363
  }
17053
17364
 
17054
17365
  // src/cli/commands/index.ts
17055
- import { resolve as resolve8 } from "path";
17366
+ import { resolve as resolve11 } from "path";
17056
17367
 
17057
17368
  // src/index/semantic/preflight.ts
17058
17369
  import { stdin as stdin2, stdout } from "process";
@@ -17126,7 +17437,7 @@ async function confirm(question, defaultYes) {
17126
17437
 
17127
17438
  // src/cli/commands/index.ts
17128
17439
  async function indexCommand(opts = {}) {
17129
- const root = resolve8(opts.dir ?? process.cwd());
17440
+ const root = resolve11(opts.dir ?? process.cwd());
17130
17441
  const tty = process.stderr.isTTY === true && process.stdin.isTTY === true;
17131
17442
  const model2 = opts.model ?? process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text";
17132
17443
  const preflightOk = await ollamaPreflight({
@@ -17376,11 +17687,11 @@ function pad2(s, width) {
17376
17687
 
17377
17688
  // src/cli/commands/replay.ts
17378
17689
  import { render as render3 } from "ink";
17379
- import React31 from "react";
17690
+ import React32 from "react";
17380
17691
 
17381
17692
  // src/cli/ui/ReplayApp.tsx
17382
- import { Box as Box26, Static as Static3, Text as Text24, useApp as useApp4, useInput as useInput2 } from "ink";
17383
- import React30, { useMemo as useMemo4, useState as useState14 } from "react";
17693
+ import { Box as Box27, Static as Static3, Text as Text25, useApp as useApp4, useInput as useInput2 } from "ink";
17694
+ import React31, { useMemo as useMemo4, useState as useState14 } from "react";
17384
17695
  function ReplayApp({ meta, pages }) {
17385
17696
  const { exit: exit2 } = useApp4();
17386
17697
  const maxIdx = Math.max(0, pages.length - 1);
@@ -17420,14 +17731,14 @@ function ReplayApp({ meta, pages }) {
17420
17731
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
17421
17732
  const currentPage = pages[idx];
17422
17733
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
17423
- return /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React30.createElement(
17734
+ return /* @__PURE__ */ React31.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React31.createElement(
17424
17735
  StatsPanel,
17425
17736
  {
17426
17737
  summary,
17427
17738
  model: cumStats.models[0] ?? meta?.model ?? "?",
17428
17739
  prefixHash
17429
17740
  }
17430
- ), /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React30.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React30.createElement(Text24, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React30.createElement(Text24, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React30.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React30.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React30.createElement(Text24, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React30.createElement(Box26, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React30.createElement(Text24, { dimColor: true }, /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "j"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "k"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "q"), " quit")));
17741
+ ), /* @__PURE__ */ React31.createElement(Box27, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React31.createElement(Box27, { justifyContent: "space-between" }, /* @__PURE__ */ React31.createElement(Text25, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React31.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React31.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React31.createElement(Text25, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React31.createElement(Box27, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "j"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "k"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "q"), " quit")));
17431
17742
  }
17432
17743
 
17433
17744
  // src/cli/commands/replay.ts
@@ -17439,7 +17750,7 @@ async function replayCommand(opts) {
17439
17750
  }
17440
17751
  const { parsed } = replayFromFile(opts.path);
17441
17752
  const pages = groupRecordsByTurn(parsed.records);
17442
- const { waitUntilExit } = render3(React31.createElement(ReplayApp, { meta: parsed.meta, pages }), {
17753
+ const { waitUntilExit } = render3(React32.createElement(ReplayApp, { meta: parsed.meta, pages }), {
17443
17754
  exitOnCtrlC: true,
17444
17755
  patchConsole: false
17445
17756
  });
@@ -17744,12 +18055,12 @@ function truncate3(s, max) {
17744
18055
 
17745
18056
  // src/cli/commands/setup.tsx
17746
18057
  import { render as render4 } from "ink";
17747
- import React33 from "react";
18058
+ import React34 from "react";
17748
18059
 
17749
18060
  // src/cli/ui/Wizard.tsx
17750
- import { Box as Box27, Text as Text25, useApp as useApp5, useInput as useInput3 } from "ink";
18061
+ import { Box as Box28, Text as Text26, useApp as useApp5, useInput as useInput3 } from "ink";
17751
18062
  import TextInput2 from "ink-text-input";
17752
- import React32, { useState as useState15 } from "react";
18063
+ import React33, { useState as useState15 } from "react";
17753
18064
 
17754
18065
  // src/cli/ui/presets.ts
17755
18066
  var PRESETS = {
@@ -17797,7 +18108,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17797
18108
  if (key.escape && step !== "saved" && onCancel) onCancel();
17798
18109
  });
17799
18110
  if (step === "apiKey") {
17800
- return /* @__PURE__ */ React32.createElement(
18111
+ return /* @__PURE__ */ React33.createElement(
17801
18112
  ApiKeyStep,
17802
18113
  {
17803
18114
  onSubmit: (key) => {
@@ -17811,7 +18122,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17811
18122
  );
17812
18123
  }
17813
18124
  if (step === "preset") {
17814
- return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React32.createElement(
18125
+ return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React33.createElement(
17815
18126
  SingleSelect,
17816
18127
  {
17817
18128
  items: presetItems(),
@@ -17821,10 +18132,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17821
18132
  setStep("mcp");
17822
18133
  }
17823
18134
  }
17824
- ), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
18135
+ ), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
17825
18136
  }
17826
18137
  if (step === "mcp") {
17827
- return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(
18138
+ return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React33.createElement(
17828
18139
  MultiSelect,
17829
18140
  {
17830
18141
  items: mcpItems(),
@@ -17849,7 +18160,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17849
18160
  }
17850
18161
  const currentName = pending[0];
17851
18162
  const entry = CATALOG_BY_NAME.get(currentName);
17852
- return /* @__PURE__ */ React32.createElement(
18163
+ return /* @__PURE__ */ React33.createElement(
17853
18164
  McpArgsStep,
17854
18165
  {
17855
18166
  entry,
@@ -17867,7 +18178,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17867
18178
  }
17868
18179
  if (step === "review") {
17869
18180
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
17870
- return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React32.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React32.createElement(
18181
+ return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column" }, /* @__PURE__ */ React33.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React33.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React33.createElement(
17871
18182
  SummaryLine,
17872
18183
  {
17873
18184
  label: "MCP",
@@ -17875,8 +18186,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17875
18186
  }
17876
18187
  ), specs.map((spec, i) => (
17877
18188
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
17878
- /* @__PURE__ */ React32.createElement(Box27, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "\xB7 ", spec))
17879
- )), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React32.createElement(
18189
+ /* @__PURE__ */ React33.createElement(Box28, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "\xB7 ", spec))
18190
+ )), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : null, /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React33.createElement(
17880
18191
  ReviewConfirm,
17881
18192
  {
17882
18193
  onConfirm: () => {
@@ -17902,7 +18213,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
17902
18213
  }
17903
18214
  ));
17904
18215
  }
17905
- return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React32.createElement(ExitOnEnter, { onExit: exit2 }));
18216
+ return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React33.createElement(ExitOnEnter, { onExit: exit2 }));
17906
18217
  }
17907
18218
  function ApiKeyStep({
17908
18219
  onSubmit,
@@ -17910,7 +18221,7 @@ function ApiKeyStep({
17910
18221
  onError
17911
18222
  }) {
17912
18223
  const [value, setValue] = useState15("");
17913
- return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React32.createElement(
18224
+ return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React33.createElement(
17914
18225
  TextInput2,
17915
18226
  {
17916
18227
  value,
@@ -17927,7 +18238,7 @@ function ApiKeyStep({
17927
18238
  mask: "\u2022",
17928
18239
  placeholder: "sk-..."
17929
18240
  }
17930
- )), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : value ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "preview: ", redactKey(value))) : null);
18241
+ )), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : value ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "preview: ", redactKey(value))) : null);
17931
18242
  }
17932
18243
  function McpArgsStep({
17933
18244
  entry,
@@ -17936,7 +18247,7 @@ function McpArgsStep({
17936
18247
  onError
17937
18248
  }) {
17938
18249
  const [value, setValue] = useState15("");
17939
- return /* @__PURE__ */ React32.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(Text25, null, entry.summary), entry.note ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Required parameter: "), /* @__PURE__ */ React32.createElement(Text25, { bold: true }, entry.userArgs)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React32.createElement(
18250
+ return /* @__PURE__ */ React33.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column" }, /* @__PURE__ */ React33.createElement(Text26, null, entry.summary), entry.note ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Required parameter: "), /* @__PURE__ */ React33.createElement(Text26, { bold: true }, entry.userArgs)), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React33.createElement(
17940
18251
  TextInput2,
17941
18252
  {
17942
18253
  value,
@@ -17952,7 +18263,7 @@ function McpArgsStep({
17952
18263
  },
17953
18264
  placeholder: placeholderFor(entry)
17954
18265
  }
17955
- )), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : null));
18266
+ )), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : null));
17956
18267
  }
17957
18268
  function ReviewConfirm({ onConfirm }) {
17958
18269
  useInput3((_i, key) => {
@@ -17972,10 +18283,10 @@ function StepFrame({
17972
18283
  total,
17973
18284
  children
17974
18285
  }) {
17975
- return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1, flexDirection: "column" }, children));
18286
+ return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Box28, null, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1, flexDirection: "column" }, children));
17976
18287
  }
17977
18288
  function SummaryLine({ label, value }) {
17978
- return /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text25, null, label.padEnd(12)), /* @__PURE__ */ React32.createElement(Text25, { bold: true }, value));
18289
+ return /* @__PURE__ */ React33.createElement(Box28, null, /* @__PURE__ */ React33.createElement(Text26, null, label.padEnd(12)), /* @__PURE__ */ React33.createElement(Text26, { bold: true }, value));
17979
18290
  }
17980
18291
  function presetItems() {
17981
18292
  return ["fast", "smart", "max"].map((name) => ({
@@ -18031,7 +18342,7 @@ async function setupCommand(_opts = {}) {
18031
18342
  const existingKey = loadApiKey();
18032
18343
  const existing = readConfig();
18033
18344
  const { waitUntilExit, unmount } = render4(
18034
- /* @__PURE__ */ React33.createElement(
18345
+ /* @__PURE__ */ React34.createElement(
18035
18346
  Wizard,
18036
18347
  {
18037
18348
  existingApiKey: existingKey,
@@ -18079,13 +18390,13 @@ function planUpdate(input) {
18079
18390
  };
18080
18391
  }
18081
18392
  function defaultSpawn(argv) {
18082
- return new Promise((resolve9, reject) => {
18393
+ return new Promise((resolve12, reject) => {
18083
18394
  const child = spawn6(argv[0], argv.slice(1), {
18084
18395
  stdio: "inherit",
18085
18396
  shell: process.platform === "win32"
18086
18397
  });
18087
18398
  child.once("error", reject);
18088
- child.once("exit", (code) => resolve9(code ?? 1));
18399
+ child.once("exit", (code) => resolve12(code ?? 1));
18089
18400
  });
18090
18401
  }
18091
18402
  async function updateCommand(opts = {}) {