reasonix 0.4.5 → 0.4.6

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
@@ -2276,6 +2276,9 @@ var McpClient = class {
2276
2276
  readerStarted = false;
2277
2277
  initialized = false;
2278
2278
  _serverCapabilities = {};
2279
+ _serverInfo = { name: "", version: "" };
2280
+ _protocolVersion = "";
2281
+ _instructions;
2279
2282
  constructor(opts) {
2280
2283
  this.transport = opts.transport;
2281
2284
  this.clientInfo = opts.clientInfo ?? { name: "reasonix", version: "0.3.0-dev" };
@@ -2285,6 +2288,18 @@ var McpClient = class {
2285
2288
  get serverCapabilities() {
2286
2289
  return this._serverCapabilities;
2287
2290
  }
2291
+ /** Server's self-reported name + version, available after initialize(). */
2292
+ get serverInfo() {
2293
+ return this._serverInfo;
2294
+ }
2295
+ /** Protocol version the server agreed to during the handshake. */
2296
+ get protocolVersion() {
2297
+ return this._protocolVersion;
2298
+ }
2299
+ /** Optional free-form instructions the server provides at handshake. */
2300
+ get serverInstructions() {
2301
+ return this._instructions;
2302
+ }
2288
2303
  /**
2289
2304
  * Complete the initialize → initialized handshake. Must be called
2290
2305
  * before any other method (otherwise compliant servers reject).
@@ -2303,6 +2318,9 @@ var McpClient = class {
2303
2318
  clientInfo: this.clientInfo
2304
2319
  });
2305
2320
  this._serverCapabilities = result.capabilities ?? {};
2321
+ this._serverInfo = result.serverInfo ?? { name: "", version: "" };
2322
+ this._protocolVersion = result.protocolVersion ?? "";
2323
+ this._instructions = result.instructions;
2306
2324
  await this.transport.send({
2307
2325
  jsonrpc: "2.0",
2308
2326
  method: "notifications/initialized"
@@ -2750,6 +2768,36 @@ function parseMcpSpec(input) {
2750
2768
  return { transport: "stdio", name, command, args };
2751
2769
  }
2752
2770
 
2771
+ // src/mcp/inspect.ts
2772
+ async function inspectMcpServer(client) {
2773
+ const tools = await trySection(() => client.listTools().then((r) => r.tools));
2774
+ const resources = await trySection(
2775
+ () => client.listResources().then((r) => r.resources)
2776
+ );
2777
+ const prompts = await trySection(() => client.listPrompts().then((r) => r.prompts));
2778
+ return {
2779
+ protocolVersion: client.protocolVersion || "(unknown)",
2780
+ serverInfo: client.serverInfo,
2781
+ capabilities: client.serverCapabilities ?? {},
2782
+ instructions: client.serverInstructions,
2783
+ tools,
2784
+ resources,
2785
+ prompts
2786
+ };
2787
+ }
2788
+ async function trySection(load) {
2789
+ try {
2790
+ const items = await load();
2791
+ return { supported: true, items };
2792
+ } catch (err) {
2793
+ const msg = err.message ?? String(err);
2794
+ if (/-32601/.test(msg) || /method not found/i.test(msg)) {
2795
+ return { supported: false, reason: "method not found (-32601)" };
2796
+ }
2797
+ return { supported: false, reason: msg };
2798
+ }
2799
+ }
2800
+
2753
2801
  // src/code/edit-blocks.ts
2754
2802
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
2755
2803
  import { dirname as dirname3, resolve as resolve2 } from "path";
@@ -2880,11 +2928,11 @@ var VERSION = "0.4.3";
2880
2928
 
2881
2929
  // src/cli/commands/chat.tsx
2882
2930
  import { render } from "ink";
2883
- import React8, { useState as useState4 } from "react";
2931
+ import React9, { useState as useState4 } from "react";
2884
2932
 
2885
2933
  // src/cli/ui/App.tsx
2886
- import { Box as Box6, Static, Text as Text6, useApp, useInput } from "ink";
2887
- import React6, { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
2934
+ import { Box as Box7, Static, Text as Text7, useApp, useInput } from "ink";
2935
+ import React7, { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
2888
2936
 
2889
2937
  // src/cli/ui/EventLog.tsx
2890
2938
  import { Box as Box3, Text as Text3 } from "ink";
@@ -3246,9 +3294,38 @@ function PromptInput({
3246
3294
  ));
3247
3295
  }
3248
3296
 
3249
- // src/cli/ui/StatsPanel.tsx
3297
+ // src/cli/ui/SlashSuggestions.tsx
3250
3298
  import { Box as Box5, Text as Text5 } from "ink";
3251
3299
  import React5 from "react";
3300
+ function SlashSuggestions({
3301
+ matches,
3302
+ selectedIndex
3303
+ }) {
3304
+ if (matches === null) return null;
3305
+ if (matches.length === 0) {
3306
+ return /* @__PURE__ */ React5.createElement(Box5, { paddingX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
3307
+ }
3308
+ const MAX = 8;
3309
+ const total = matches.length;
3310
+ const windowStart = total <= MAX ? 0 : Math.max(0, Math.min(selectedIndex - Math.floor(MAX / 2), total - MAX));
3311
+ const shown = matches.slice(windowStart, windowStart + MAX);
3312
+ const hiddenAbove = windowStart;
3313
+ const hiddenBelow = total - windowStart - shown.length;
3314
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React5.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2191/\u2193 navigate \xB7 Tab or Enter to pick"));
3315
+ }
3316
+ function SuggestionRow({ spec, isSelected }) {
3317
+ const marker = isSelected ? "\u25B8" : " ";
3318
+ const name = `/${spec.cmd}`;
3319
+ const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
3320
+ if (isSelected) {
3321
+ return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, " ", spec.summary));
3322
+ }
3323
+ return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
3324
+ }
3325
+
3326
+ // src/cli/ui/StatsPanel.tsx
3327
+ import { Box as Box6, Text as Text6 } from "ink";
3328
+ import React6 from "react";
3252
3329
  function StatsPanel({
3253
3330
  summary,
3254
3331
  model,
@@ -3262,7 +3339,7 @@ function StatsPanel({
3262
3339
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
3263
3340
  const ctxRatio = summary.lastPromptTokens / ctxMax;
3264
3341
  const ctxColor = ctxRatio >= 0.8 ? "red" : ctxRatio >= 0.5 ? "yellow" : void 0;
3265
- return /* @__PURE__ */ React5.createElement(Box5, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React5.createElement(Box5, { justifyContent: "space-between" }, /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan", bold: true }, "Reasonix"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 model "), /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, model), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \xB7 prefix "), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, prefixHash), harvestOn ? /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, " \xB7 branch", branchBudget) : null), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "turns ", summary.turns, " \xB7 type /help")), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "cache hit "), /* @__PURE__ */ React5.createElement(Text5, { color: hitColor, bold: true }, hitPct, "%")), /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "cost "), /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, "$", summary.totalCostUsd.toFixed(6))), /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "vs Claude "), /* @__PURE__ */ React5.createElement(Text5, null, "$", summary.claudeEquivalentUsd.toFixed(6))), /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "saving "), /* @__PURE__ */ React5.createElement(Text5, { color: "green", bold: true }, summary.savingsVsClaudePct.toFixed(1), "%")), summary.lastPromptTokens > 0 ? /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "ctx "), /* @__PURE__ */ React5.createElement(Text5, { color: ctxColor, bold: ctxColor !== void 0 }, formatTokens(summary.lastPromptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (", (ctxRatio * 100).toFixed(0), "%)"), ctxRatio >= 0.8 ? /* @__PURE__ */ React5.createElement(Text5, { color: "red", bold: true }, " ", "\xB7 /compact") : null) : null));
3342
+ return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React6.createElement(Box6, { justifyContent: "space-between" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan", bold: true }, "Reasonix"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 model "), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, model), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " \xB7 prefix "), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, prefixHash), harvestOn ? /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, " \xB7 branch", branchBudget) : null), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "turns ", summary.turns, " \xB7 type /help")), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "cache hit "), /* @__PURE__ */ React6.createElement(Text6, { color: hitColor, bold: true }, hitPct, "%")), /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "cost "), /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "$", summary.totalCostUsd.toFixed(6))), /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "vs Claude "), /* @__PURE__ */ React6.createElement(Text6, null, "$", summary.claudeEquivalentUsd.toFixed(6))), /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "saving "), /* @__PURE__ */ React6.createElement(Text6, { color: "green", bold: true }, summary.savingsVsClaudePct.toFixed(1), "%")), summary.lastPromptTokens > 0 ? /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "ctx "), /* @__PURE__ */ React6.createElement(Text6, { color: ctxColor, bold: ctxColor !== void 0 }, formatTokens(summary.lastPromptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (", (ctxRatio * 100).toFixed(0), "%)"), ctxRatio >= 0.8 ? /* @__PURE__ */ React6.createElement(Text6, { color: "red", bold: true }, " ", "\xB7 /compact") : null) : null));
3266
3343
  }
3267
3344
  function formatTokens(n) {
3268
3345
  if (n < 1e3) return String(n);
@@ -3272,6 +3349,45 @@ function formatTokens(n) {
3272
3349
 
3273
3350
  // src/cli/ui/slash.ts
3274
3351
  import { spawnSync } from "child_process";
3352
+ var SLASH_COMMANDS = [
3353
+ { cmd: "help", summary: "show the full command reference" },
3354
+ { cmd: "status", summary: "current model, flags, context, session" },
3355
+ {
3356
+ cmd: "preset",
3357
+ argsHint: "<fast|smart|max>",
3358
+ summary: "one-tap model + harvest + branch bundle"
3359
+ },
3360
+ { cmd: "model", argsHint: "<id>", summary: "switch DeepSeek model id" },
3361
+ { cmd: "harvest", argsHint: "[on|off]", summary: "toggle Pillar-2 plan-state extraction" },
3362
+ { cmd: "branch", argsHint: "<N|off>", summary: "run N parallel samples per turn (N>=2)" },
3363
+ { cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
3364
+ { cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
3365
+ { cmd: "think", summary: "dump the last turn's full R1 reasoning (reasoner only)" },
3366
+ { cmd: "retry", summary: "truncate & resend your last message (fresh sample)" },
3367
+ { cmd: "compact", argsHint: "[cap]", summary: "shrink oversized tool results in the log" },
3368
+ { cmd: "sessions", summary: "list saved sessions (current marked with \u25B8)" },
3369
+ { cmd: "forget", summary: "delete the current session from disk" },
3370
+ { cmd: "setup", summary: "reminds you to exit and run `reasonix setup`" },
3371
+ { cmd: "clear", summary: "clear the visible scrollback (log + session kept)" },
3372
+ { cmd: "exit", summary: "quit the TUI" },
3373
+ // Code-mode only
3374
+ { cmd: "apply", summary: "commit pending edit blocks to disk", contextual: "code" },
3375
+ { cmd: "discard", summary: "drop pending edit blocks without writing", contextual: "code" },
3376
+ { cmd: "undo", summary: "roll back the last applied edit batch", contextual: "code" },
3377
+ {
3378
+ cmd: "commit",
3379
+ argsHint: '"msg"',
3380
+ summary: "git add -A && git commit -m ...",
3381
+ contextual: "code"
3382
+ }
3383
+ ];
3384
+ function suggestSlashCommands(prefix, codeMode = false) {
3385
+ const p = prefix.toLowerCase();
3386
+ return SLASH_COMMANDS.filter((c) => {
3387
+ if (c.contextual === "code" && !codeMode) return false;
3388
+ return c.cmd.startsWith(p);
3389
+ });
3390
+ }
3275
3391
  function parseSlash(text) {
3276
3392
  if (!text.startsWith("/")) return null;
3277
3393
  const parts = text.slice(1).trim().split(/\s+/);
@@ -3323,13 +3439,34 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3323
3439
  ].join("\n")
3324
3440
  };
3325
3441
  case "mcp": {
3442
+ const servers = ctx.mcpServers ?? [];
3326
3443
  const specs = ctx.mcpSpecs ?? [];
3327
3444
  const toolSpecs = loop.prefix.toolSpecs ?? [];
3328
- if (specs.length === 0 && toolSpecs.length === 0) {
3445
+ if (servers.length === 0 && specs.length === 0 && toolSpecs.length === 0) {
3329
3446
  return {
3330
3447
  info: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog.'
3331
3448
  };
3332
3449
  }
3450
+ if (servers.length > 0) {
3451
+ const lines2 = [];
3452
+ for (const s of servers) {
3453
+ const { report } = s;
3454
+ const serverName = report.serverInfo.name || "(unknown)";
3455
+ const serverVer = report.serverInfo.version ? ` v${report.serverInfo.version}` : "";
3456
+ lines2.push(`[${s.label}] ${serverName}${serverVer} \u2014 ${s.spec}`);
3457
+ lines2.push(` tools ${s.toolCount}`);
3458
+ appendSection(lines2, "resources", report.resources);
3459
+ appendSection(lines2, "prompts ", report.prompts);
3460
+ lines2.push("");
3461
+ }
3462
+ lines2.push(
3463
+ "Chat mode consumes tools today; resources+prompts are surfaced here for awareness."
3464
+ );
3465
+ lines2.push(
3466
+ "Full catalog: `reasonix mcp list` \xB7 deeper diagnosis: `reasonix mcp inspect <spec>`."
3467
+ );
3468
+ return { info: lines2.join("\n") };
3469
+ }
3333
3470
  const lines = [];
3334
3471
  if (specs.length > 0) {
3335
3472
  lines.push(`MCP servers (${specs.length}):`);
@@ -3559,6 +3696,22 @@ ${entry.text}`
3559
3696
  return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
3560
3697
  }
3561
3698
  }
3699
+ function appendSection(lines, label, section) {
3700
+ if (!section || !section.supported) {
3701
+ lines.push(
3702
+ ` ${label.trim()} ${section?.supported === false ? "(not supported)" : "(none)"}`
3703
+ );
3704
+ return;
3705
+ }
3706
+ const names = section.items.map((i) => i.name);
3707
+ if (names.length === 0) {
3708
+ lines.push(` ${label.trim()} (none)`);
3709
+ return;
3710
+ }
3711
+ const head = names.slice(0, 5).join(", ");
3712
+ const more = names.length > 5 ? ` +${names.length - 5} more` : "";
3713
+ lines.push(` ${label.trim()} ${names.length} [${head}${more}]`);
3714
+ }
3562
3715
  function formatToolList(history) {
3563
3716
  const total = history.length;
3564
3717
  const header = `Tool calls in this session (${total}, most recent first):`;
@@ -3631,6 +3784,7 @@ function App({
3631
3784
  session,
3632
3785
  tools,
3633
3786
  mcpSpecs,
3787
+ mcpServers,
3634
3788
  codeMode
3635
3789
  }) {
3636
3790
  const { exit } = useApp();
@@ -3645,6 +3799,7 @@ function App({
3645
3799
  const promptHistory = useRef([]);
3646
3800
  const historyCursor = useRef(-1);
3647
3801
  const toolHistoryRef = useRef([]);
3802
+ const [slashSelected, setSlashSelected] = useState2(0);
3648
3803
  const [summary, setSummary] = useState2({
3649
3804
  turns: 0,
3650
3805
  totalCostUsd: 0,
@@ -3667,6 +3822,17 @@ function App({
3667
3822
  transcriptRef.current?.end();
3668
3823
  };
3669
3824
  }, []);
3825
+ const slashMatches = useMemo(() => {
3826
+ if (!input.startsWith("/") || input.includes(" ")) return null;
3827
+ return suggestSlashCommands(input.slice(1), !!codeMode);
3828
+ }, [input, codeMode]);
3829
+ useEffect2(() => {
3830
+ setSlashSelected((prev) => {
3831
+ if (!slashMatches || slashMatches.length === 0) return 0;
3832
+ if (prev >= slashMatches.length) return slashMatches.length - 1;
3833
+ return prev;
3834
+ });
3835
+ }, [slashMatches]);
3670
3836
  const loopRef = useRef(null);
3671
3837
  const loop = useMemo(() => {
3672
3838
  if (loopRef.current) return loopRef.current;
@@ -3720,6 +3886,21 @@ function App({
3720
3886
  return;
3721
3887
  }
3722
3888
  if (busy) return;
3889
+ if (slashMatches && slashMatches.length > 0) {
3890
+ if (key.upArrow) {
3891
+ setSlashSelected((i) => Math.max(0, i - 1));
3892
+ return;
3893
+ }
3894
+ if (key.downArrow) {
3895
+ setSlashSelected((i) => Math.min(slashMatches.length - 1, i + 1));
3896
+ return;
3897
+ }
3898
+ if (key.tab) {
3899
+ const sel = slashMatches[slashSelected] ?? slashMatches[0];
3900
+ if (sel) setInput(`/${sel.cmd}`);
3901
+ return;
3902
+ }
3903
+ }
3723
3904
  const hist = promptHistory.current;
3724
3905
  if (key.upArrow) {
3725
3906
  if (hist.length === 0) return;
@@ -3778,6 +3959,15 @@ function App({
3778
3959
  async (raw) => {
3779
3960
  let text = raw.trim();
3780
3961
  if (!text || busy) return;
3962
+ if (text.startsWith("/") && !text.includes(" ")) {
3963
+ const typed = text.slice(1).toLowerCase();
3964
+ const matches = suggestSlashCommands(typed, !!codeMode);
3965
+ const exact = matches.find((m) => m.cmd === typed);
3966
+ if (!exact && matches.length > 0) {
3967
+ const chosen = matches[slashSelected] ?? matches[0];
3968
+ if (chosen) text = `/${chosen.cmd}`;
3969
+ }
3970
+ }
3781
3971
  setInput("");
3782
3972
  historyCursor.current = -1;
3783
3973
  if (codeMode && pendingEdits.current.length > 0 && (text === "y" || text === "n")) {
@@ -3790,6 +3980,7 @@ function App({
3790
3980
  if (slash) {
3791
3981
  const result = handleSlash(slash.cmd, slash.args, loop, {
3792
3982
  mcpSpecs,
3983
+ mcpServers,
3793
3984
  codeUndo: codeMode ? codeUndo : void 0,
3794
3985
  codeApply: codeMode ? codeApply : void 0,
3795
3986
  codeDiscard: codeMode ? codeDiscard : void 0,
@@ -3942,9 +4133,21 @@ function App({
3942
4133
  setBusy(false);
3943
4134
  }
3944
4135
  },
3945
- [busy, codeApply, codeDiscard, codeMode, codeUndo, exit, loop, mcpSpecs, writeTranscript]
4136
+ [
4137
+ busy,
4138
+ codeApply,
4139
+ codeDiscard,
4140
+ codeMode,
4141
+ codeUndo,
4142
+ exit,
4143
+ loop,
4144
+ mcpSpecs,
4145
+ mcpServers,
4146
+ slashSelected,
4147
+ writeTranscript
4148
+ ]
3946
4149
  );
3947
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(
4150
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
3948
4151
  StatsPanel,
3949
4152
  {
3950
4153
  summary,
@@ -3953,7 +4156,7 @@ function App({
3953
4156
  harvestOn: loop.harvestEnabled,
3954
4157
  branchBudget: loop.branchOptions.budget
3955
4158
  }
3956
- ), /* @__PURE__ */ React6.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React6.createElement(EventRow, { key: item.id, event: item })), streaming ? /* @__PURE__ */ React6.createElement(Box6, { marginY: 1 }, /* @__PURE__ */ React6.createElement(EventRow, { event: streaming })) : null, ongoingTool ? /* @__PURE__ */ React6.createElement(OngoingToolRow, { tool: ongoingTool }) : null, /* @__PURE__ */ React6.createElement(PromptInput, { value: input, onChange: setInput, onSubmit: handleSubmit, disabled: busy }), /* @__PURE__ */ React6.createElement(CommandStrip, { codeMode: !!codeMode }));
4159
+ ), /* @__PURE__ */ React7.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React7.createElement(EventRow, { key: item.id, event: item })), streaming ? /* @__PURE__ */ React7.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React7.createElement(EventRow, { event: streaming })) : null, ongoingTool ? /* @__PURE__ */ React7.createElement(OngoingToolRow, { tool: ongoingTool }) : null, /* @__PURE__ */ React7.createElement(PromptInput, { value: input, onChange: setInput, onSubmit: handleSubmit, disabled: busy }), /* @__PURE__ */ React7.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }));
3957
4160
  }
3958
4161
  function OngoingToolRow({ tool }) {
3959
4162
  const [tick, setTick] = useState2(0);
@@ -3969,7 +4172,7 @@ function OngoingToolRow({ tool }) {
3969
4172
  }, []);
3970
4173
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3971
4174
  const summary = summarizeToolArgs(tool.name, tool.args);
3972
- return /* @__PURE__ */ React6.createElement(Box6, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, frames[tick % frames.length]), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, ` ${elapsed}s`)), summary ? /* @__PURE__ */ React6.createElement(Box6, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, summary)) : null);
4175
+ return /* @__PURE__ */ React7.createElement(Box7, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, frames[tick % frames.length]), /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, ` ${elapsed}s`)), summary ? /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2 }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, summary)) : null);
3973
4176
  }
3974
4177
  function summarizeToolArgs(name, args) {
3975
4178
  if (!args || args === "{}") return "";
@@ -4011,9 +4214,6 @@ function summarizeToolArgs(name, args) {
4011
4214
  }
4012
4215
  return args.length > 80 ? `${args.slice(0, 80)}\u2026` : args;
4013
4216
  }
4014
- function CommandStrip({ codeMode }) {
4015
- return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "/help \xB7 /preset ", "<fast|smart|max>", " \xB7 /mcp \xB7 /compact \xB7 /sessions \xB7 /setup \xB7 /clear \xB7 /exit"), codeMode ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, 'code mode: /apply (y) \xB7 /discard (n) \xB7 /undo \xB7 /commit "msg" \u2014 edits NEVER write without /apply') : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2191/\u2193 recall prompts \xB7 /retry re-send last \xB7 /think see R1 reasoning \xB7 /tool N full tool output"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
4016
- }
4017
4217
  function formatEditResults(results) {
4018
4218
  const lines = results.map((r) => {
4019
4219
  const mark = r.status === "applied" || r.status === "created" ? "\u2713" : "\u2717";
@@ -4056,9 +4256,9 @@ function describeRepair(repair) {
4056
4256
  }
4057
4257
 
4058
4258
  // src/cli/ui/Setup.tsx
4059
- import { Box as Box7, Text as Text7, useApp as useApp2 } from "ink";
4259
+ import { Box as Box8, Text as Text8, useApp as useApp2 } from "ink";
4060
4260
  import TextInput2 from "ink-text-input";
4061
- import React7, { useState as useState3 } from "react";
4261
+ import React8, { useState as useState3 } from "react";
4062
4262
  function Setup({ onReady }) {
4063
4263
  const [value, setValue] = useState3("");
4064
4264
  const [error, setError] = useState3(null);
@@ -4082,7 +4282,7 @@ function Setup({ onReady }) {
4082
4282
  }
4083
4283
  onReady(trimmed);
4084
4284
  };
4085
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React7.createElement(
4285
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React8.createElement(
4086
4286
  TextInput2,
4087
4287
  {
4088
4288
  value,
@@ -4091,14 +4291,14 @@ function Setup({ onReady }) {
4091
4291
  mask: "\u2022",
4092
4292
  placeholder: "sk-..."
4093
4293
  }
4094
- )), error ? /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, error)) : value ? /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(Type /exit to abort.)")));
4294
+ )), error ? /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, error)) : value ? /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "(Type /exit to abort.)")));
4095
4295
  }
4096
4296
 
4097
4297
  // src/cli/commands/chat.tsx
4098
- function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
4298
+ function Root({ initialKey, tools, mcpSpecs, mcpServers, ...appProps }) {
4099
4299
  const [key, setKey] = useState4(initialKey);
4100
4300
  if (!key) {
4101
- return /* @__PURE__ */ React8.createElement(
4301
+ return /* @__PURE__ */ React9.createElement(
4102
4302
  Setup,
4103
4303
  {
4104
4304
  onReady: (k) => {
@@ -4109,7 +4309,7 @@ function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
4109
4309
  );
4110
4310
  }
4111
4311
  process.env.DEEPSEEK_API_KEY = key;
4112
- return /* @__PURE__ */ React8.createElement(
4312
+ return /* @__PURE__ */ React9.createElement(
4113
4313
  App,
4114
4314
  {
4115
4315
  model: appProps.model,
@@ -4120,6 +4320,7 @@ function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
4120
4320
  session: appProps.session,
4121
4321
  tools,
4122
4322
  mcpSpecs,
4323
+ mcpServers,
4123
4324
  codeMode: appProps.codeMode
4124
4325
  }
4125
4326
  );
@@ -4131,6 +4332,7 @@ async function chatCommand(opts) {
4131
4332
  const clients = [];
4132
4333
  const successfulSpecs = [];
4133
4334
  const failedSpecs = [];
4335
+ const mcpServers = [];
4134
4336
  let tools;
4135
4337
  if (requestedSpecs.length > 0) {
4136
4338
  tools = new ToolRegistry();
@@ -4142,6 +4344,19 @@ async function chatCommand(opts) {
4142
4344
  const mcp2 = new McpClient({ transport });
4143
4345
  await mcp2.initialize();
4144
4346
  const bridge = await bridgeMcpTools(mcp2, { registry: tools, namePrefix: prefix });
4347
+ let report;
4348
+ try {
4349
+ report = await inspectMcpServer(mcp2);
4350
+ } catch {
4351
+ report = {
4352
+ protocolVersion: mcp2.protocolVersion,
4353
+ serverInfo: mcp2.serverInfo,
4354
+ capabilities: mcp2.serverCapabilities ?? {},
4355
+ tools: { supported: true, items: [] },
4356
+ resources: { supported: false, reason: "inspect failed" },
4357
+ prompts: { supported: false, reason: "inspect failed" }
4358
+ };
4359
+ }
4145
4360
  const label = spec.name ?? "anon";
4146
4361
  const source = spec.transport === "sse" ? spec.url : `${spec.command} ${spec.args.join(" ")}`;
4147
4362
  process.stderr.write(
@@ -4150,6 +4365,12 @@ async function chatCommand(opts) {
4150
4365
  );
4151
4366
  clients.push(mcp2);
4152
4367
  successfulSpecs.push(raw);
4368
+ mcpServers.push({
4369
+ label,
4370
+ spec: raw,
4371
+ toolCount: bridge.registeredNames.length,
4372
+ report
4373
+ });
4153
4374
  } catch (err) {
4154
4375
  const reason = err.message;
4155
4376
  failedSpecs.push({ spec: raw, reason });
@@ -4166,7 +4387,16 @@ async function chatCommand(opts) {
4166
4387
  }
4167
4388
  const mcpSpecs = successfulSpecs;
4168
4389
  const { waitUntilExit } = render(
4169
- /* @__PURE__ */ React8.createElement(Root, { initialKey, tools, mcpSpecs, ...opts }),
4390
+ /* @__PURE__ */ React9.createElement(
4391
+ Root,
4392
+ {
4393
+ initialKey,
4394
+ tools,
4395
+ mcpSpecs,
4396
+ mcpServers,
4397
+ ...opts
4398
+ }
4399
+ ),
4170
4400
  { exitOnCtrlC: true }
4171
4401
  );
4172
4402
  try {
@@ -4206,34 +4436,34 @@ function quoteIfNeeded(s) {
4206
4436
  import { writeFileSync as writeFileSync4 } from "fs";
4207
4437
  import { basename as basename2 } from "path";
4208
4438
  import { render as render2 } from "ink";
4209
- import React11 from "react";
4439
+ import React12 from "react";
4210
4440
 
4211
4441
  // src/cli/ui/DiffApp.tsx
4212
- import { Box as Box9, Static as Static2, Text as Text9, useApp as useApp3, useInput as useInput2 } from "ink";
4213
- import React10, { useState as useState5 } from "react";
4442
+ import { Box as Box10, Static as Static2, Text as Text10, useApp as useApp3, useInput as useInput2 } from "ink";
4443
+ import React11, { useState as useState5 } from "react";
4214
4444
 
4215
4445
  // src/cli/ui/RecordView.tsx
4216
- import { Box as Box8, Text as Text8 } from "ink";
4217
- import React9 from "react";
4446
+ import { Box as Box9, Text as Text9 } from "ink";
4447
+ import React10 from "react";
4218
4448
  function RecordView({ rec, compact = false }) {
4219
4449
  const toolArgsMax = compact ? 120 : 200;
4220
4450
  const toolContentMax = compact ? 200 : 400;
4221
4451
  if (rec.role === "user") {
4222
- return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React9.createElement(Text8, null, rec.content));
4452
+ return /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React10.createElement(Text9, null, rec.content));
4223
4453
  }
4224
4454
  if (rec.role === "assistant_final") {
4225
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React9.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React9.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React9.createElement(Text8, null, rec.content) : /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, italic: true }, "(tool-call response only)"));
4455
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React10.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React10.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React10.createElement(Text9, null, rec.content) : /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, italic: true }, "(tool-call response only)"));
4226
4456
  }
4227
4457
  if (rec.role === "tool") {
4228
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
4458
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
4229
4459
  }
4230
4460
  if (rec.role === "error") {
4231
- return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, rec.error ?? rec.content));
4461
+ return /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React10.createElement(Text9, { color: "red" }, rec.error ?? rec.content));
4232
4462
  }
4233
4463
  if (rec.role === "done" || rec.role === "assistant_delta") {
4234
4464
  return null;
4235
4465
  }
4236
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "[", rec.role, "] ", rec.content));
4466
+ return /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "[", rec.role, "] ", rec.content));
4237
4467
  }
4238
4468
  function CacheBadge({ usage }) {
4239
4469
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -4242,7 +4472,7 @@ function CacheBadge({ usage }) {
4242
4472
  if (total === 0) return null;
4243
4473
  const pct2 = hit / total * 100;
4244
4474
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
4245
- return /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React9.createElement(Text8, { color }, pct2.toFixed(1), "%"));
4475
+ return /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React10.createElement(Text9, { color }, pct2.toFixed(1), "%"));
4246
4476
  }
4247
4477
  function truncate3(s, max) {
4248
4478
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -4276,7 +4506,7 @@ function DiffApp({ report }) {
4276
4506
  }
4277
4507
  });
4278
4508
  const pair = report.pairs[idx];
4279
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(DiffHeader, { report }), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React10.createElement(Text9, null, pair ? /* @__PURE__ */ React10.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React10.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React10.createElement(Text9, null, pair.divergenceNote)) : null, /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "j"), "/", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "k"), "/", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "N"), "/", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "g"), "/", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "q"), " ", "quit")));
4509
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(DiffHeader, { report }), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React11.createElement(Text10, null, pair ? /* @__PURE__ */ React11.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React11.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React11.createElement(Text10, null, pair.divergenceNote)) : null, /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "j"), "/", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "k"), "/", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "N"), "/", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "g"), "/", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "q"), " ", "quit")));
4280
4510
  }
4281
4511
  function DiffHeader({ report }) {
4282
4512
  const a = report.a;
@@ -4294,15 +4524,15 @@ function DiffHeader({ report }) {
4294
4524
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
4295
4525
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
4296
4526
  }
4297
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(Box9, { justifyContent: "space-between" }, /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, a.label), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " vs B="), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, b.label)), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "cache "), /* @__PURE__ */ React10.createElement(Text9, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React10.createElement(Text9, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React10.createElement(Text9, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "cost "), /* @__PURE__ */ React10.createElement(Text9, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React10.createElement(Text9, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React10.createElement(Text9, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React10.createElement(Text9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "model calls "), /* @__PURE__ */ React10.createElement(Text9, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, italic: true }, prefixLine)) : null);
4527
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Box10, { justifyContent: "space-between" }, /* @__PURE__ */ React11.createElement(Text10, null, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, a.label), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " vs B="), /* @__PURE__ */ React11.createElement(Text10, { color: "magenta" }, b.label)), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React11.createElement(Text10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "cache "), /* @__PURE__ */ React11.createElement(Text10, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React11.createElement(Text10, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React11.createElement(Text10, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React11.createElement(Text10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "cost "), /* @__PURE__ */ React11.createElement(Text10, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React11.createElement(Text10, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React11.createElement(Text10, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React11.createElement(Text10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "model calls "), /* @__PURE__ */ React11.createElement(Text10, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true, italic: true }, prefixLine)) : null);
4298
4528
  }
4299
4529
  function Pane({
4300
4530
  label,
4301
4531
  headerColor,
4302
4532
  records
4303
4533
  }) {
4304
- return /* @__PURE__ */ React10.createElement(
4305
- Box9,
4534
+ return /* @__PURE__ */ React11.createElement(
4535
+ Box10,
4306
4536
  {
4307
4537
  flexDirection: "column",
4308
4538
  flexGrow: 1,
@@ -4310,21 +4540,21 @@ function Pane({
4310
4540
  borderStyle: "single",
4311
4541
  borderColor: headerColor
4312
4542
  },
4313
- /* @__PURE__ */ React10.createElement(Text9, { color: headerColor, bold: true }, label),
4314
- records.length === 0 ? /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React10.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React10.createElement(RecordView, { key, rec, compact: true }))
4543
+ /* @__PURE__ */ React11.createElement(Text10, { color: headerColor, bold: true }, label),
4544
+ records.length === 0 ? /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React11.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React11.createElement(RecordView, { key, rec, compact: true }))
4315
4545
  );
4316
4546
  }
4317
4547
  function KindBadge({ kind }) {
4318
4548
  if (kind === "match") {
4319
- return /* @__PURE__ */ React10.createElement(Text9, { color: "green" }, "\u2713 match");
4549
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "green" }, "\u2713 match");
4320
4550
  }
4321
4551
  if (kind === "diverge") {
4322
- return /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u2605 diverge");
4552
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u2605 diverge");
4323
4553
  }
4324
4554
  if (kind === "only_in_a") {
4325
- return /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u2190 only in A");
4555
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u2190 only in A");
4326
4556
  }
4327
- return /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, "\u2192 only in B");
4557
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "magenta" }, "\u2192 only in B");
4328
4558
  }
4329
4559
  function paneRecords(pair, side) {
4330
4560
  if (!pair) return [];
@@ -4355,7 +4585,7 @@ markdown report written to ${opts.mdPath}`);
4355
4585
  return;
4356
4586
  }
4357
4587
  if (wantTui) {
4358
- const { waitUntilExit } = render2(React11.createElement(DiffApp, { report }), {
4588
+ const { waitUntilExit } = render2(React12.createElement(DiffApp, { report }), {
4359
4589
  exitOnCtrlC: true
4360
4590
  });
4361
4591
  await waitUntilExit();
@@ -4364,6 +4594,70 @@ markdown report written to ${opts.mdPath}`);
4364
4594
  console.log(renderSummaryTable(report));
4365
4595
  }
4366
4596
 
4597
+ // src/cli/commands/mcp-inspect.ts
4598
+ async function mcpInspectCommand(opts) {
4599
+ const spec = parseMcpSpec(opts.spec);
4600
+ const transport = spec.transport === "sse" ? new SseTransport({ url: spec.url }) : new StdioTransport({ command: spec.command, args: spec.args });
4601
+ const client = new McpClient({ transport });
4602
+ try {
4603
+ await client.initialize();
4604
+ const report = await inspectMcpServer(client);
4605
+ if (opts.json) {
4606
+ console.log(JSON.stringify(report, null, 2));
4607
+ } else {
4608
+ console.log(formatReport(spec.name ?? "(anon)", report));
4609
+ }
4610
+ } finally {
4611
+ await client.close();
4612
+ }
4613
+ }
4614
+ function formatReport(nsName, r) {
4615
+ const lines = [];
4616
+ lines.push(`MCP server [${nsName}]`);
4617
+ lines.push(
4618
+ ` server ${r.serverInfo.name || "(unknown)"}${r.serverInfo.version ? ` v${r.serverInfo.version}` : ""}`
4619
+ );
4620
+ lines.push(` protocol ${r.protocolVersion}`);
4621
+ const capKeys = Object.keys(r.capabilities);
4622
+ lines.push(` caps ${capKeys.length > 0 ? capKeys.join(", ") : "(none advertised)"}`);
4623
+ if (r.instructions) {
4624
+ lines.push(` notes ${r.instructions.trim().slice(0, 200)}`);
4625
+ }
4626
+ lines.push("");
4627
+ lines.push(formatSection("Tools", r.tools, toolLine));
4628
+ lines.push(formatSection("Resources", r.resources, resourceLine));
4629
+ lines.push(formatSection("Prompts", r.prompts, promptLine));
4630
+ return lines.join("\n");
4631
+ }
4632
+ function formatSection(title, section, render5) {
4633
+ if (!section.supported) {
4634
+ return `${title}: (not supported \u2014 ${section.reason})`;
4635
+ }
4636
+ if (section.items.length === 0) {
4637
+ return `${title}: (none)`;
4638
+ }
4639
+ const lines = [`${title} (${section.items.length}):`];
4640
+ for (const item of section.items) lines.push(` ${render5(item)}`);
4641
+ return lines.join("\n");
4642
+ }
4643
+ function toolLine(t) {
4644
+ const desc = t.description ? ` \u2014 ${oneLine(t.description, 80)}` : "";
4645
+ return `\xB7 ${t.name}${desc}`;
4646
+ }
4647
+ function resourceLine(r) {
4648
+ const mime = r.mimeType ? ` [${r.mimeType}]` : "";
4649
+ return `\xB7 ${r.name}${mime} ${r.uri}`;
4650
+ }
4651
+ function promptLine(p) {
4652
+ const argPart = p.arguments && p.arguments.length > 0 ? ` (${p.arguments.map((a) => a.required ? a.name : `${a.name}?`).join(", ")})` : "";
4653
+ const desc = p.description ? ` \u2014 ${oneLine(p.description, 80)}` : "";
4654
+ return `\xB7 ${p.name}${argPart}${desc}`;
4655
+ }
4656
+ function oneLine(s, max) {
4657
+ const flat = s.replace(/\s+/g, " ").trim();
4658
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}\u2026`;
4659
+ }
4660
+
4367
4661
  // src/mcp/catalog.ts
4368
4662
  var MCP_CATALOG = [
4369
4663
  {
@@ -4431,11 +4725,11 @@ function pad(s, width) {
4431
4725
 
4432
4726
  // src/cli/commands/replay.ts
4433
4727
  import { render as render3 } from "ink";
4434
- import React13 from "react";
4728
+ import React14 from "react";
4435
4729
 
4436
4730
  // src/cli/ui/ReplayApp.tsx
4437
- import { Box as Box10, Static as Static3, Text as Text10, useApp as useApp4, useInput as useInput3 } from "ink";
4438
- import React12, { useMemo as useMemo2, useState as useState6 } from "react";
4731
+ import { Box as Box11, Static as Static3, Text as Text11, useApp as useApp4, useInput as useInput3 } from "ink";
4732
+ import React13, { useMemo as useMemo2, useState as useState6 } from "react";
4439
4733
  function ReplayApp({ meta, pages }) {
4440
4734
  const { exit } = useApp4();
4441
4735
  const maxIdx = Math.max(0, pages.length - 1);
@@ -4472,14 +4766,14 @@ function ReplayApp({ meta, pages }) {
4472
4766
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
4473
4767
  const currentPage = pages[idx];
4474
4768
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
4475
- return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(
4769
+ return /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(
4476
4770
  StatsPanel,
4477
4771
  {
4478
4772
  summary,
4479
4773
  model: cumStats.models[0] ?? meta?.model ?? "?",
4480
4774
  prefixHash
4481
4775
  }
4482
- ), /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React12.createElement(Box10, { justifyContent: "space-between" }, /* @__PURE__ */ React12.createElement(Text10, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React12.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React12.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React12.createElement(Text10, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "j"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "k"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "q"), " quit")));
4776
+ ), /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React13.createElement(Box11, { justifyContent: "space-between" }, /* @__PURE__ */ React13.createElement(Text11, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React13.createElement(Text11, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React13.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React13.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React13.createElement(Text11, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React13.createElement(Box11, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React13.createElement(Text11, { dimColor: true }, /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "j"), "/", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "k"), "/", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "q"), " quit")));
4483
4777
  }
4484
4778
 
4485
4779
  // src/cli/commands/replay.ts
@@ -4491,7 +4785,7 @@ async function replayCommand(opts) {
4491
4785
  }
4492
4786
  const { parsed } = replayFromFile(opts.path);
4493
4787
  const pages = groupRecordsByTurn(parsed.records);
4494
- const { waitUntilExit } = render3(React13.createElement(ReplayApp, { meta: parsed.meta, pages }), {
4788
+ const { waitUntilExit } = render3(React14.createElement(ReplayApp, { meta: parsed.meta, pages }), {
4495
4789
  exitOnCtrlC: true
4496
4790
  });
4497
4791
  await waitUntilExit();
@@ -4543,7 +4837,7 @@ function sliceRecords(records, opts) {
4543
4837
  function renderRecord(rec) {
4544
4838
  const turn = `[t${rec.turn}]`;
4545
4839
  if (rec.role === "user") {
4546
- console.log(`${turn} USER: ${oneLine(rec.content)}`);
4840
+ console.log(`${turn} USER: ${oneLine2(rec.content)}`);
4547
4841
  } else if (rec.role === "assistant_final") {
4548
4842
  const cost = rec.cost !== void 0 ? ` $${rec.cost.toFixed(6)}` : "";
4549
4843
  const cache = rec.usage && (rec.usage.prompt_cache_hit_tokens !== void 0 || rec.usage.prompt_cache_miss_tokens !== void 0) ? (() => {
@@ -4552,7 +4846,7 @@ function renderRecord(rec) {
4552
4846
  const total = hit + miss;
4553
4847
  return total > 0 ? ` cache=${(hit / total * 100).toFixed(1)}%` : "";
4554
4848
  })() : "";
4555
- console.log(`${turn} AGENT:${cost}${cache} ${oneLine(rec.content)}`);
4849
+ console.log(`${turn} AGENT:${cost}${cache} ${oneLine2(rec.content)}`);
4556
4850
  if (rec.planState) {
4557
4851
  const ps = rec.planState;
4558
4852
  if (ps.subgoals.length)
@@ -4569,16 +4863,16 @@ function renderRecord(rec) {
4569
4863
  );
4570
4864
  }
4571
4865
  } else if (rec.role === "tool") {
4572
- const args = rec.args ? ` args=${oneLine(rec.args, 80)}` : "";
4573
- console.log(`${turn} TOOL ${rec.tool ?? "?"}:${args} \u2192 ${oneLine(rec.content, 120)}`);
4866
+ const args = rec.args ? ` args=${oneLine2(rec.args, 80)}` : "";
4867
+ console.log(`${turn} TOOL ${rec.tool ?? "?"}:${args} \u2192 ${oneLine2(rec.content, 120)}`);
4574
4868
  } else if (rec.role === "error") {
4575
4869
  console.log(`${turn} ERROR: ${rec.error ?? rec.content}`);
4576
4870
  } else if (rec.role === "done") {
4577
4871
  } else {
4578
- console.log(`${turn} ${rec.role}: ${oneLine(rec.content)}`);
4872
+ console.log(`${turn} ${rec.role}: ${oneLine2(rec.content)}`);
4579
4873
  }
4580
4874
  }
4581
- function oneLine(s, max = 200) {
4875
+ function oneLine2(s, max = 200) {
4582
4876
  const collapsed = s.replace(/\s+/g, " ").trim();
4583
4877
  return collapsed.length > max ? `${collapsed.slice(0, max)}\u2026` : collapsed;
4584
4878
  }
@@ -4764,7 +5058,7 @@ function inspectSession(name, verbose) {
4764
5058
  function renderMessage(msg, turnIdx, verbose) {
4765
5059
  const turn = turnIdx > 0 ? `[t${turnIdx}]` : "[start]";
4766
5060
  const content = typeof msg.content === "string" ? msg.content : "";
4767
- const flat = oneLine2(content);
5061
+ const flat = oneLine3(content);
4768
5062
  if (msg.role === "user") {
4769
5063
  console.log(`${turn} USER: ${flat}`);
4770
5064
  } else if (msg.role === "assistant") {
@@ -4782,7 +5076,7 @@ function renderMessage(msg, turnIdx, verbose) {
4782
5076
  if (verbose) console.log(`${turn} SYSTEM: ${truncate4(flat, 160)}`);
4783
5077
  }
4784
5078
  }
4785
- function oneLine2(s, max = 200) {
5079
+ function oneLine3(s, max = 200) {
4786
5080
  const collapsed = s.replace(/\s+/g, " ").trim();
4787
5081
  return collapsed.length > max ? `${collapsed.slice(0, max)}\u2026` : collapsed;
4788
5082
  }
@@ -4792,16 +5086,16 @@ function truncate4(s, max) {
4792
5086
 
4793
5087
  // src/cli/commands/setup.tsx
4794
5088
  import { render as render4 } from "ink";
4795
- import React16 from "react";
5089
+ import React17 from "react";
4796
5090
 
4797
5091
  // src/cli/ui/Wizard.tsx
4798
- import { Box as Box12, Text as Text12, useApp as useApp5, useInput as useInput5 } from "ink";
5092
+ import { Box as Box13, Text as Text13, useApp as useApp5, useInput as useInput5 } from "ink";
4799
5093
  import TextInput3 from "ink-text-input";
4800
- import React15, { useState as useState8 } from "react";
5094
+ import React16, { useState as useState8 } from "react";
4801
5095
 
4802
5096
  // src/cli/ui/Select.tsx
4803
- import { Box as Box11, Text as Text11, useInput as useInput4 } from "ink";
4804
- import React14, { useState as useState7 } from "react";
5097
+ import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
5098
+ import React15, { useState as useState7 } from "react";
4805
5099
  function SingleSelect({
4806
5100
  items,
4807
5101
  initialValue,
@@ -4825,7 +5119,7 @@ function SingleSelect({
4825
5119
  onCancel();
4826
5120
  }
4827
5121
  });
4828
- return /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React14.createElement(
5122
+ return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React15.createElement(
4829
5123
  SelectRow,
4830
5124
  {
4831
5125
  key: item.value,
@@ -4868,10 +5162,10 @@ function MultiSelect({
4868
5162
  onCancel();
4869
5163
  }
4870
5164
  });
4871
- return /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column" }, items.map((item, i) => {
5165
+ return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => {
4872
5166
  const checked = selected.has(item.value);
4873
5167
  const marker = checked ? "[x]" : "[ ]";
4874
- return /* @__PURE__ */ React14.createElement(
5168
+ return /* @__PURE__ */ React15.createElement(
4875
5169
  SelectRow,
4876
5170
  {
4877
5171
  key: item.value,
@@ -4880,7 +5174,7 @@ function MultiSelect({
4880
5174
  marker: `${i === index ? "\u25B8" : " "} ${marker}`
4881
5175
  }
4882
5176
  );
4883
- }), footer ? /* @__PURE__ */ React14.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text11, { dimColor: true }, footer)) : null);
5177
+ }), footer ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, footer)) : null);
4884
5178
  }
4885
5179
  function SelectRow({
4886
5180
  item,
@@ -4888,7 +5182,7 @@ function SelectRow({
4888
5182
  marker
4889
5183
  }) {
4890
5184
  const color = item.disabled ? "gray" : active ? "cyan" : void 0;
4891
- return /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Box11, null, /* @__PURE__ */ React14.createElement(Text11, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React14.createElement(Box11, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React14.createElement(Text11, { dimColor: true }, item.hint)) : null);
5185
+ return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Box12, null, /* @__PURE__ */ React15.createElement(Text12, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React15.createElement(Box12, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, item.hint)) : null);
4892
5186
  }
4893
5187
  function findNextEnabled(items, from, step) {
4894
5188
  if (items.length === 0) return 0;
@@ -4937,7 +5231,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4937
5231
  if (key.escape && step !== "saved" && onCancel) onCancel();
4938
5232
  });
4939
5233
  if (step === "apiKey") {
4940
- return /* @__PURE__ */ React15.createElement(
5234
+ return /* @__PURE__ */ React16.createElement(
4941
5235
  ApiKeyStep,
4942
5236
  {
4943
5237
  onSubmit: (key) => {
@@ -4951,7 +5245,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4951
5245
  );
4952
5246
  }
4953
5247
  if (step === "preset") {
4954
- return /* @__PURE__ */ React15.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React15.createElement(
5248
+ return /* @__PURE__ */ React16.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React16.createElement(
4955
5249
  SingleSelect,
4956
5250
  {
4957
5251
  items: presetItems(),
@@ -4961,10 +5255,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4961
5255
  setStep("mcp");
4962
5256
  }
4963
5257
  }
4964
- ), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
5258
+ ), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
4965
5259
  }
4966
5260
  if (step === "mcp") {
4967
- return /* @__PURE__ */ React15.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React15.createElement(
5261
+ return /* @__PURE__ */ React16.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React16.createElement(
4968
5262
  MultiSelect,
4969
5263
  {
4970
5264
  items: mcpItems(),
@@ -4989,7 +5283,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4989
5283
  }
4990
5284
  const currentName = pending[0];
4991
5285
  const entry = CATALOG_BY_NAME.get(currentName);
4992
- return /* @__PURE__ */ React15.createElement(
5286
+ return /* @__PURE__ */ React16.createElement(
4993
5287
  McpArgsStep,
4994
5288
  {
4995
5289
  entry,
@@ -5007,7 +5301,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
5007
5301
  }
5008
5302
  if (step === "review") {
5009
5303
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
5010
- return /* @__PURE__ */ React15.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React15.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React15.createElement(
5304
+ return /* @__PURE__ */ React16.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React16.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React16.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React16.createElement(
5011
5305
  SummaryLine,
5012
5306
  {
5013
5307
  label: "MCP",
@@ -5015,8 +5309,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
5015
5309
  }
5016
5310
  ), specs.map((spec, i) => (
5017
5311
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
5018
- /* @__PURE__ */ React15.createElement(Box12, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "\xB7 ", spec))
5019
- )), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { color: "red" }, error)) : null, /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React15.createElement(
5312
+ /* @__PURE__ */ React16.createElement(Box13, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "\xB7 ", spec))
5313
+ )), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { color: "red" }, error)) : null, /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React16.createElement(
5020
5314
  ReviewConfirm,
5021
5315
  {
5022
5316
  onConfirm: () => {
@@ -5042,7 +5336,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
5042
5336
  }
5043
5337
  ));
5044
5338
  }
5045
- return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text12, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React15.createElement(ExitOnEnter, { onExit: exit }));
5339
+ return /* @__PURE__ */ React16.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text13, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React16.createElement(ExitOnEnter, { onExit: exit }));
5046
5340
  }
5047
5341
  function ApiKeyStep({
5048
5342
  onSubmit,
@@ -5050,7 +5344,7 @@ function ApiKeyStep({
5050
5344
  onError
5051
5345
  }) {
5052
5346
  const [value, setValue] = useState8("");
5053
- return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text12, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React15.createElement(
5347
+ return /* @__PURE__ */ React16.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text13, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React16.createElement(
5054
5348
  TextInput3,
5055
5349
  {
5056
5350
  value,
@@ -5067,7 +5361,7 @@ function ApiKeyStep({
5067
5361
  mask: "\u2022",
5068
5362
  placeholder: "sk-..."
5069
5363
  }
5070
- )), error ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { color: "red" }, error)) : value ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "preview: ", redactKey(value))) : null);
5364
+ )), error ? /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { color: "red" }, error)) : value ? /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "preview: ", redactKey(value))) : null);
5071
5365
  }
5072
5366
  function McpArgsStep({
5073
5367
  entry,
@@ -5076,7 +5370,7 @@ function McpArgsStep({
5076
5370
  onError
5077
5371
  }) {
5078
5372
  const [value, setValue] = useState8("");
5079
- return /* @__PURE__ */ React15.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Text12, null, entry.summary), entry.note ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, null, "Required parameter: "), /* @__PURE__ */ React15.createElement(Text12, { bold: true }, entry.userArgs)), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React15.createElement(
5373
+ return /* @__PURE__ */ React16.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React16.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text13, null, entry.summary), entry.note ? /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, null, "Required parameter: "), /* @__PURE__ */ React16.createElement(Text13, { bold: true }, entry.userArgs)), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React16.createElement(
5080
5374
  TextInput3,
5081
5375
  {
5082
5376
  value,
@@ -5092,7 +5386,7 @@ function McpArgsStep({
5092
5386
  },
5093
5387
  placeholder: placeholderFor(entry)
5094
5388
  }
5095
- )), error ? /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text12, { color: "red" }, error)) : null));
5389
+ )), error ? /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text13, { color: "red" }, error)) : null));
5096
5390
  }
5097
5391
  function ReviewConfirm({ onConfirm }) {
5098
5392
  useInput5((_i, key) => {
@@ -5112,10 +5406,10 @@ function StepFrame({
5112
5406
  total,
5113
5407
  children
5114
5408
  }) {
5115
- return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Box12, null, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React15.createElement(Text12, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React15.createElement(Box12, { marginTop: 1, flexDirection: "column" }, children));
5409
+ return /* @__PURE__ */ React16.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Box13, null, /* @__PURE__ */ React16.createElement(Text13, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React16.createElement(Text13, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React16.createElement(Box13, { marginTop: 1, flexDirection: "column" }, children));
5116
5410
  }
5117
5411
  function SummaryLine({ label, value }) {
5118
- return /* @__PURE__ */ React15.createElement(Box12, null, /* @__PURE__ */ React15.createElement(Text12, null, label.padEnd(12)), /* @__PURE__ */ React15.createElement(Text12, { bold: true }, value));
5412
+ return /* @__PURE__ */ React16.createElement(Box13, null, /* @__PURE__ */ React16.createElement(Text13, null, label.padEnd(12)), /* @__PURE__ */ React16.createElement(Text13, { bold: true }, value));
5119
5413
  }
5120
5414
  function presetItems() {
5121
5415
  return ["fast", "smart", "max"].map((name) => ({
@@ -5171,7 +5465,7 @@ async function setupCommand(_opts = {}) {
5171
5465
  const existingKey = loadApiKey();
5172
5466
  const existing = readConfig();
5173
5467
  const { waitUntilExit, unmount } = render4(
5174
- /* @__PURE__ */ React16.createElement(
5468
+ /* @__PURE__ */ React17.createElement(
5175
5469
  Wizard,
5176
5470
  {
5177
5471
  existingApiKey: existingKey,
@@ -5390,6 +5684,17 @@ var mcp = program.command("mcp").description("Model Context Protocol helpers \u2
5390
5684
  mcp.command("list").description("Show a curated catalog of popular MCP servers with ready-to-use --mcp commands.").option("--json", "Emit the catalog as JSON instead of the human-readable table").action((opts) => {
5391
5685
  mcpListCommand({ json: !!opts.json });
5392
5686
  });
5687
+ mcp.command("inspect <spec>").description(
5688
+ 'Connect to one MCP server and print its server info + tools/resources/prompts. <spec> takes the same forms as --mcp ("name=cmd args", "cmd args", or an SSE URL).'
5689
+ ).option("--json", "Emit the full inspection report as JSON instead of the human-readable summary").action(async (spec, opts) => {
5690
+ try {
5691
+ await mcpInspectCommand({ spec, json: !!opts.json });
5692
+ } catch (err) {
5693
+ process.stderr.write(`mcp inspect failed: ${err.message}
5694
+ `);
5695
+ process.exit(1);
5696
+ }
5697
+ });
5393
5698
  program.command("version").description("Print Reasonix version.").action(versionCommand);
5394
5699
  program.parseAsync(process.argv).catch((err) => {
5395
5700
  console.error(err);