reasonix 0.4.3 → 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
@@ -722,7 +722,19 @@ function scavengeToolCalls(reasoningContent, opts) {
722
722
  const max = opts.maxCalls ?? 4;
723
723
  const notes = [];
724
724
  const out = [];
725
- for (const candidate of iterateJsonObjects(reasoningContent)) {
725
+ for (const invoke of iterateDsmlInvokes(reasoningContent)) {
726
+ if (out.length >= max) break;
727
+ if (!opts.allowedNames.has(invoke.name)) continue;
728
+ out.push({
729
+ function: {
730
+ name: invoke.name,
731
+ arguments: JSON.stringify(invoke.args)
732
+ }
733
+ });
734
+ notes.push(`scavenged DSML call: ${invoke.name}`);
735
+ }
736
+ const nonDsml = stripDsmlBlocks(reasoningContent);
737
+ for (const candidate of iterateJsonObjects(nonDsml)) {
726
738
  if (out.length >= max) break;
727
739
  const call = coerceToToolCall(candidate, opts.allowedNames);
728
740
  if (call) {
@@ -732,6 +744,40 @@ function scavengeToolCalls(reasoningContent, opts) {
732
744
  }
733
745
  return { calls: out, notes };
734
746
  }
747
+ function stripDsmlBlocks(text) {
748
+ let out = text;
749
+ out = out.replace(/<[||]DSML[||]function_calls>[\s\S]*?<\/?[||]DSML[||]function_calls>/g, "");
750
+ out = out.replace(/<[||]DSML[||]invoke\s+[^>]*>[\s\S]*?<\/[||]DSML[||]invoke>/g, "");
751
+ return out;
752
+ }
753
+ function* iterateDsmlInvokes(text) {
754
+ const INVOKE_RE = /<[||]DSML[||]invoke\s+name="([^"]+)">([\s\S]*?)<\/[||]DSML[||]invoke>/g;
755
+ for (const match of text.matchAll(INVOKE_RE)) {
756
+ const name = match[1];
757
+ const body = match[2];
758
+ if (!name || body === void 0) continue;
759
+ yield { name, args: parseDsmlParameters(body) };
760
+ }
761
+ }
762
+ function parseDsmlParameters(body) {
763
+ const PARAM_RE = /<[||]DSML[||]parameter\s+name="([^"]+)"(?:\s+string="(true|false)")?\s*>([\s\S]*?)<\/[||]DSML[||]parameter>/g;
764
+ const args = {};
765
+ for (const m of body.matchAll(PARAM_RE)) {
766
+ const key = m[1];
767
+ const stringFlag = m[2];
768
+ const raw = (m[3] ?? "").trim();
769
+ if (!key) continue;
770
+ if (stringFlag === "false") {
771
+ try {
772
+ args[key] = JSON.parse(raw);
773
+ continue;
774
+ } catch {
775
+ }
776
+ }
777
+ args[key] = raw;
778
+ }
779
+ return args;
780
+ }
735
781
  function* iterateJsonObjects(text) {
736
782
  for (let i = 0; i < text.length; i++) {
737
783
  if (text[i] !== "{") continue;
@@ -917,14 +963,15 @@ var ToolCallRepair = class {
917
963
  this.opts = opts;
918
964
  this.storm = new StormBreaker(opts.stormWindow ?? 6, opts.stormThreshold ?? 3);
919
965
  }
920
- process(declaredCalls, reasoningContent) {
966
+ process(declaredCalls, reasoningContent, content = null) {
921
967
  const report = {
922
968
  scavenged: 0,
923
969
  truncationsFixed: 0,
924
970
  stormsBroken: 0,
925
971
  notes: []
926
972
  };
927
- const scavenged = scavengeToolCalls(reasoningContent, {
973
+ const combined = [reasoningContent ?? "", content ?? ""].filter(Boolean).join("\n");
974
+ const scavenged = scavengeToolCalls(combined || null, {
928
975
  allowedNames: this.opts.allowedToolNames,
929
976
  maxCalls: this.opts.maxScavenge ?? 4
930
977
  });
@@ -1502,7 +1549,8 @@ var CacheFirstLoop = class {
1502
1549
  const planState = preHarvestedPlanState ? preHarvestedPlanState : this.harvestEnabled ? await harvest(reasoningContent || null, this.client, this.harvestOptions) : emptyPlanState();
1503
1550
  const { calls: repairedCalls, report } = this.repair.process(
1504
1551
  toolCalls,
1505
- reasoningContent || null
1552
+ reasoningContent || null,
1553
+ assistantContent || null
1506
1554
  );
1507
1555
  this.appendAndPersist(this.assistantMessage(assistantContent, repairedCalls));
1508
1556
  yield {
@@ -2228,6 +2276,9 @@ var McpClient = class {
2228
2276
  readerStarted = false;
2229
2277
  initialized = false;
2230
2278
  _serverCapabilities = {};
2279
+ _serverInfo = { name: "", version: "" };
2280
+ _protocolVersion = "";
2281
+ _instructions;
2231
2282
  constructor(opts) {
2232
2283
  this.transport = opts.transport;
2233
2284
  this.clientInfo = opts.clientInfo ?? { name: "reasonix", version: "0.3.0-dev" };
@@ -2237,6 +2288,18 @@ var McpClient = class {
2237
2288
  get serverCapabilities() {
2238
2289
  return this._serverCapabilities;
2239
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
+ }
2240
2303
  /**
2241
2304
  * Complete the initialize → initialized handshake. Must be called
2242
2305
  * before any other method (otherwise compliant servers reject).
@@ -2246,10 +2309,18 @@ var McpClient = class {
2246
2309
  this.startReaderIfNeeded();
2247
2310
  const result = await this.request("initialize", {
2248
2311
  protocolVersion: MCP_PROTOCOL_VERSION,
2249
- capabilities: { tools: {} },
2312
+ // Advertise every method the client can consume so servers know
2313
+ // they can send listChanged notifications etc. Sub-feature flags
2314
+ // (e.g. `resources.subscribe`) are omitted — we don't implement
2315
+ // those yet and the empty object means "method-level support, no
2316
+ // sub-features."
2317
+ capabilities: { tools: {}, resources: {}, prompts: {} },
2250
2318
  clientInfo: this.clientInfo
2251
2319
  });
2252
2320
  this._serverCapabilities = result.capabilities ?? {};
2321
+ this._serverInfo = result.serverInfo ?? { name: "", version: "" };
2322
+ this._protocolVersion = result.protocolVersion ?? "";
2323
+ this._instructions = result.instructions;
2253
2324
  await this.transport.send({
2254
2325
  jsonrpc: "2.0",
2255
2326
  method: "notifications/initialized"
@@ -2270,6 +2341,45 @@ var McpClient = class {
2270
2341
  arguments: args ?? {}
2271
2342
  });
2272
2343
  }
2344
+ /**
2345
+ * List resources the server exposes. Supports a pagination cursor;
2346
+ * callers interested in the full set should loop on `nextCursor`.
2347
+ * Servers that don't support resources respond with method-not-found
2348
+ * (−32601) — we surface that as a thrown Error so callers can gate
2349
+ * on the `serverCapabilities.resources` field first.
2350
+ */
2351
+ async listResources(cursor) {
2352
+ this.assertInitialized();
2353
+ return this.request("resources/list", {
2354
+ ...cursor ? { cursor } : {}
2355
+ });
2356
+ }
2357
+ /** Read the contents of a resource by URI. */
2358
+ async readResource(uri) {
2359
+ this.assertInitialized();
2360
+ return this.request("resources/read", {
2361
+ uri
2362
+ });
2363
+ }
2364
+ /** List prompt templates the server exposes. */
2365
+ async listPrompts(cursor) {
2366
+ this.assertInitialized();
2367
+ return this.request("prompts/list", {
2368
+ ...cursor ? { cursor } : {}
2369
+ });
2370
+ }
2371
+ /**
2372
+ * Fetch a rendered prompt by name. `args` supplies values for any
2373
+ * required template arguments; the server validates. Returns messages
2374
+ * ready to prepend to the model's input.
2375
+ */
2376
+ async getPrompt(name, args) {
2377
+ this.assertInitialized();
2378
+ return this.request("prompts/get", {
2379
+ name,
2380
+ ...args ? { arguments: args } : {}
2381
+ });
2382
+ }
2273
2383
  /** Close the transport and reject any outstanding requests. */
2274
2384
  async close() {
2275
2385
  for (const [, pending] of this.pending) {
@@ -2658,6 +2768,36 @@ function parseMcpSpec(input) {
2658
2768
  return { transport: "stdio", name, command, args };
2659
2769
  }
2660
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
+
2661
2801
  // src/code/edit-blocks.ts
2662
2802
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
2663
2803
  import { dirname as dirname3, resolve as resolve2 } from "path";
@@ -2788,11 +2928,11 @@ var VERSION = "0.4.3";
2788
2928
 
2789
2929
  // src/cli/commands/chat.tsx
2790
2930
  import { render } from "ink";
2791
- import React8, { useState as useState4 } from "react";
2931
+ import React9, { useState as useState4 } from "react";
2792
2932
 
2793
2933
  // src/cli/ui/App.tsx
2794
- import { Box as Box6, Static, Text as Text6, useApp, useInput } from "ink";
2795
- 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";
2796
2936
 
2797
2937
  // src/cli/ui/EventLog.tsx
2798
2938
  import { Box as Box3, Text as Text3 } from "ink";
@@ -3154,9 +3294,38 @@ function PromptInput({
3154
3294
  ));
3155
3295
  }
3156
3296
 
3157
- // src/cli/ui/StatsPanel.tsx
3297
+ // src/cli/ui/SlashSuggestions.tsx
3158
3298
  import { Box as Box5, Text as Text5 } from "ink";
3159
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";
3160
3329
  function StatsPanel({
3161
3330
  summary,
3162
3331
  model,
@@ -3170,7 +3339,7 @@ function StatsPanel({
3170
3339
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
3171
3340
  const ctxRatio = summary.lastPromptTokens / ctxMax;
3172
3341
  const ctxColor = ctxRatio >= 0.8 ? "red" : ctxRatio >= 0.5 ? "yellow" : void 0;
3173
- 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));
3174
3343
  }
3175
3344
  function formatTokens(n) {
3176
3345
  if (n < 1e3) return String(n);
@@ -3180,6 +3349,45 @@ function formatTokens(n) {
3180
3349
 
3181
3350
  // src/cli/ui/slash.ts
3182
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
+ }
3183
3391
  function parseSlash(text) {
3184
3392
  if (!text.startsWith("/")) return null;
3185
3393
  const parts = text.slice(1).trim().split(/\s+/);
@@ -3209,6 +3417,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3209
3417
  " /setup (exit + reconfigure) \u2192 run `reasonix setup`",
3210
3418
  " /compact [cap] shrink large tool results in history (default 4k/result)",
3211
3419
  " /think dump the most recent turn's full R1 reasoning (reasoner only)",
3420
+ " /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
3212
3421
  " /retry truncate & resend your last message (fresh sample from the model)",
3213
3422
  " /apply (code mode) commit the pending edit blocks to disk",
3214
3423
  " /discard (code mode) drop pending edits without writing",
@@ -3230,13 +3439,34 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3230
3439
  ].join("\n")
3231
3440
  };
3232
3441
  case "mcp": {
3442
+ const servers = ctx.mcpServers ?? [];
3233
3443
  const specs = ctx.mcpSpecs ?? [];
3234
3444
  const toolSpecs = loop.prefix.toolSpecs ?? [];
3235
- if (specs.length === 0 && toolSpecs.length === 0) {
3445
+ if (servers.length === 0 && specs.length === 0 && toolSpecs.length === 0) {
3236
3446
  return {
3237
3447
  info: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog.'
3238
3448
  };
3239
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
+ }
3240
3470
  const lines = [];
3241
3471
  if (specs.length > 0) {
3242
3472
  lines.push(`MCP servers (${specs.length}):`);
@@ -3280,6 +3510,38 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3280
3510
 
3281
3511
  ${raw.trim()}` };
3282
3512
  }
3513
+ case "tool": {
3514
+ const history = ctx.toolHistory?.() ?? [];
3515
+ if (history.length === 0) {
3516
+ return {
3517
+ info: "no tool calls yet in this session. `/tool` lists them once the model has actually used a tool; `/tool N` dumps the full (untruncated) output of the Nth-most-recent."
3518
+ };
3519
+ }
3520
+ const raw = (args[0] ?? "").toLowerCase();
3521
+ if (raw === "" || raw === "list" || raw === "ls") {
3522
+ return { info: formatToolList(history) };
3523
+ }
3524
+ const n = Number.parseInt(raw, 10);
3525
+ if (!Number.isFinite(n) || n < 1) {
3526
+ return {
3527
+ info: "usage: /tool [N] (no arg \u2192 list; N=1 \u2192 most recent result in full, N=2 \u2192 previous, \u2026)"
3528
+ };
3529
+ }
3530
+ if (n > history.length) {
3531
+ return {
3532
+ info: `only ${history.length} tool call(s) in history \u2014 asked for #${n}. Try /tool with no arg to see the list.`
3533
+ };
3534
+ }
3535
+ const entry = history[history.length - n];
3536
+ if (!entry) {
3537
+ return { info: `could not read tool call #${n}` };
3538
+ }
3539
+ return {
3540
+ info: `\u21B3 tool<${entry.toolName}> #${n} (${entry.text.length} chars):
3541
+
3542
+ ${entry.text}`
3543
+ };
3544
+ }
3283
3545
  case "undo": {
3284
3546
  if (!ctx.codeUndo) {
3285
3547
  return {
@@ -3434,6 +3696,45 @@ ${raw.trim()}` };
3434
3696
  return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
3435
3697
  }
3436
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
+ }
3715
+ function formatToolList(history) {
3716
+ const total = history.length;
3717
+ const header = `Tool calls in this session (${total}, most recent first):`;
3718
+ const shown = Math.min(total, 10);
3719
+ const lines = [header];
3720
+ for (let i = 0; i < shown; i++) {
3721
+ const entry = history[total - 1 - i];
3722
+ if (!entry) continue;
3723
+ const idx = i + 1;
3724
+ const flat = entry.text.replace(/\s+/g, " ").trim();
3725
+ const preview = flat.length > 80 ? `${flat.slice(0, 80)}\u2026` : flat;
3726
+ const name = entry.toolName.length > 24 ? `${entry.toolName.slice(0, 23)}\u2026` : entry.toolName;
3727
+ lines.push(
3728
+ ` #${String(idx).padStart(2)} ${name.padEnd(24)} ${String(entry.text.length).padStart(6)} chars ${preview}`
3729
+ );
3730
+ }
3731
+ if (total > shown) {
3732
+ lines.push(` \u2026 (${total - shown} earlier, reach with /tool N)`);
3733
+ }
3734
+ lines.push("");
3735
+ lines.push("View full output: /tool N (N=1 \u2192 most recent)");
3736
+ return lines.join("\n");
3737
+ }
3437
3738
  function compactNum(n) {
3438
3739
  if (n < 1e3) return String(n);
3439
3740
  const k = n / 1e3;
@@ -3483,6 +3784,7 @@ function App({
3483
3784
  session,
3484
3785
  tools,
3485
3786
  mcpSpecs,
3787
+ mcpServers,
3486
3788
  codeMode
3487
3789
  }) {
3488
3790
  const { exit } = useApp();
@@ -3496,6 +3798,8 @@ function App({
3496
3798
  const pendingEdits = useRef([]);
3497
3799
  const promptHistory = useRef([]);
3498
3800
  const historyCursor = useRef(-1);
3801
+ const toolHistoryRef = useRef([]);
3802
+ const [slashSelected, setSlashSelected] = useState2(0);
3499
3803
  const [summary, setSummary] = useState2({
3500
3804
  turns: 0,
3501
3805
  totalCostUsd: 0,
@@ -3518,6 +3822,17 @@ function App({
3518
3822
  transcriptRef.current?.end();
3519
3823
  };
3520
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]);
3521
3836
  const loopRef = useRef(null);
3522
3837
  const loop = useMemo(() => {
3523
3838
  if (loopRef.current) return loopRef.current;
@@ -3571,6 +3886,21 @@ function App({
3571
3886
  return;
3572
3887
  }
3573
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
+ }
3574
3904
  const hist = promptHistory.current;
3575
3905
  if (key.upArrow) {
3576
3906
  if (hist.length === 0) return;
@@ -3629,6 +3959,15 @@ function App({
3629
3959
  async (raw) => {
3630
3960
  let text = raw.trim();
3631
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
+ }
3632
3971
  setInput("");
3633
3972
  historyCursor.current = -1;
3634
3973
  if (codeMode && pendingEdits.current.length > 0 && (text === "y" || text === "n")) {
@@ -3641,11 +3980,13 @@ function App({
3641
3980
  if (slash) {
3642
3981
  const result = handleSlash(slash.cmd, slash.args, loop, {
3643
3982
  mcpSpecs,
3983
+ mcpServers,
3644
3984
  codeUndo: codeMode ? codeUndo : void 0,
3645
3985
  codeApply: codeMode ? codeApply : void 0,
3646
3986
  codeDiscard: codeMode ? codeDiscard : void 0,
3647
3987
  codeRoot: codeMode?.rootDir,
3648
- pendingEditCount: codeMode ? pendingEdits.current.length : void 0
3988
+ pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
3989
+ toolHistory: () => toolHistoryRef.current
3649
3990
  });
3650
3991
  if (result.exit) {
3651
3992
  transcriptRef.current?.end();
@@ -3758,6 +4099,10 @@ function App({
3758
4099
  } else if (ev.role === "tool") {
3759
4100
  flush();
3760
4101
  setOngoingTool(null);
4102
+ toolHistoryRef.current.push({
4103
+ toolName: ev.toolName ?? "?",
4104
+ text: ev.content
4105
+ });
3761
4106
  setHistorical((prev) => [
3762
4107
  ...prev,
3763
4108
  {
@@ -3788,9 +4133,21 @@ function App({
3788
4133
  setBusy(false);
3789
4134
  }
3790
4135
  },
3791
- [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
+ ]
3792
4149
  );
3793
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(
4150
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
3794
4151
  StatsPanel,
3795
4152
  {
3796
4153
  summary,
@@ -3799,7 +4156,7 @@ function App({
3799
4156
  harvestOn: loop.harvestEnabled,
3800
4157
  branchBudget: loop.branchOptions.budget
3801
4158
  }
3802
- ), /* @__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 }));
3803
4160
  }
3804
4161
  function OngoingToolRow({ tool }) {
3805
4162
  const [tick, setTick] = useState2(0);
@@ -3815,7 +4172,7 @@ function OngoingToolRow({ tool }) {
3815
4172
  }, []);
3816
4173
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3817
4174
  const summary = summarizeToolArgs(tool.name, tool.args);
3818
- 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);
3819
4176
  }
3820
4177
  function summarizeToolArgs(name, args) {
3821
4178
  if (!args || args === "{}") return "";
@@ -3857,9 +4214,6 @@ function summarizeToolArgs(name, args) {
3857
4214
  }
3858
4215
  return args.length > 80 ? `${args.slice(0, 80)}\u2026` : args;
3859
4216
  }
3860
- function CommandStrip({ codeMode }) {
3861
- 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 full reasoning"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
3862
- }
3863
4217
  function formatEditResults(results) {
3864
4218
  const lines = results.map((r) => {
3865
4219
  const mark = r.status === "applied" || r.status === "created" ? "\u2713" : "\u2717";
@@ -3902,9 +4256,9 @@ function describeRepair(repair) {
3902
4256
  }
3903
4257
 
3904
4258
  // src/cli/ui/Setup.tsx
3905
- 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";
3906
4260
  import TextInput2 from "ink-text-input";
3907
- import React7, { useState as useState3 } from "react";
4261
+ import React8, { useState as useState3 } from "react";
3908
4262
  function Setup({ onReady }) {
3909
4263
  const [value, setValue] = useState3("");
3910
4264
  const [error, setError] = useState3(null);
@@ -3928,7 +4282,7 @@ function Setup({ onReady }) {
3928
4282
  }
3929
4283
  onReady(trimmed);
3930
4284
  };
3931
- 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(
3932
4286
  TextInput2,
3933
4287
  {
3934
4288
  value,
@@ -3937,14 +4291,14 @@ function Setup({ onReady }) {
3937
4291
  mask: "\u2022",
3938
4292
  placeholder: "sk-..."
3939
4293
  }
3940
- )), 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.)")));
3941
4295
  }
3942
4296
 
3943
4297
  // src/cli/commands/chat.tsx
3944
- function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
4298
+ function Root({ initialKey, tools, mcpSpecs, mcpServers, ...appProps }) {
3945
4299
  const [key, setKey] = useState4(initialKey);
3946
4300
  if (!key) {
3947
- return /* @__PURE__ */ React8.createElement(
4301
+ return /* @__PURE__ */ React9.createElement(
3948
4302
  Setup,
3949
4303
  {
3950
4304
  onReady: (k) => {
@@ -3955,7 +4309,7 @@ function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
3955
4309
  );
3956
4310
  }
3957
4311
  process.env.DEEPSEEK_API_KEY = key;
3958
- return /* @__PURE__ */ React8.createElement(
4312
+ return /* @__PURE__ */ React9.createElement(
3959
4313
  App,
3960
4314
  {
3961
4315
  model: appProps.model,
@@ -3966,6 +4320,7 @@ function Root({ initialKey, tools, mcpSpecs, ...appProps }) {
3966
4320
  session: appProps.session,
3967
4321
  tools,
3968
4322
  mcpSpecs,
4323
+ mcpServers,
3969
4324
  codeMode: appProps.codeMode
3970
4325
  }
3971
4326
  );
@@ -3977,6 +4332,7 @@ async function chatCommand(opts) {
3977
4332
  const clients = [];
3978
4333
  const successfulSpecs = [];
3979
4334
  const failedSpecs = [];
4335
+ const mcpServers = [];
3980
4336
  let tools;
3981
4337
  if (requestedSpecs.length > 0) {
3982
4338
  tools = new ToolRegistry();
@@ -3988,6 +4344,19 @@ async function chatCommand(opts) {
3988
4344
  const mcp2 = new McpClient({ transport });
3989
4345
  await mcp2.initialize();
3990
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
+ }
3991
4360
  const label = spec.name ?? "anon";
3992
4361
  const source = spec.transport === "sse" ? spec.url : `${spec.command} ${spec.args.join(" ")}`;
3993
4362
  process.stderr.write(
@@ -3996,6 +4365,12 @@ async function chatCommand(opts) {
3996
4365
  );
3997
4366
  clients.push(mcp2);
3998
4367
  successfulSpecs.push(raw);
4368
+ mcpServers.push({
4369
+ label,
4370
+ spec: raw,
4371
+ toolCount: bridge.registeredNames.length,
4372
+ report
4373
+ });
3999
4374
  } catch (err) {
4000
4375
  const reason = err.message;
4001
4376
  failedSpecs.push({ spec: raw, reason });
@@ -4012,7 +4387,16 @@ async function chatCommand(opts) {
4012
4387
  }
4013
4388
  const mcpSpecs = successfulSpecs;
4014
4389
  const { waitUntilExit } = render(
4015
- /* @__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
+ ),
4016
4400
  { exitOnCtrlC: true }
4017
4401
  );
4018
4402
  try {
@@ -4052,34 +4436,34 @@ function quoteIfNeeded(s) {
4052
4436
  import { writeFileSync as writeFileSync4 } from "fs";
4053
4437
  import { basename as basename2 } from "path";
4054
4438
  import { render as render2 } from "ink";
4055
- import React11 from "react";
4439
+ import React12 from "react";
4056
4440
 
4057
4441
  // src/cli/ui/DiffApp.tsx
4058
- import { Box as Box9, Static as Static2, Text as Text9, useApp as useApp3, useInput as useInput2 } from "ink";
4059
- 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";
4060
4444
 
4061
4445
  // src/cli/ui/RecordView.tsx
4062
- import { Box as Box8, Text as Text8 } from "ink";
4063
- import React9 from "react";
4446
+ import { Box as Box9, Text as Text9 } from "ink";
4447
+ import React10 from "react";
4064
4448
  function RecordView({ rec, compact = false }) {
4065
4449
  const toolArgsMax = compact ? 120 : 200;
4066
4450
  const toolContentMax = compact ? 200 : 400;
4067
4451
  if (rec.role === "user") {
4068
- 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));
4069
4453
  }
4070
4454
  if (rec.role === "assistant_final") {
4071
- 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)"));
4072
4456
  }
4073
4457
  if (rec.role === "tool") {
4074
- 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)));
4075
4459
  }
4076
4460
  if (rec.role === "error") {
4077
- 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));
4078
4462
  }
4079
4463
  if (rec.role === "done" || rec.role === "assistant_delta") {
4080
4464
  return null;
4081
4465
  }
4082
- 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));
4083
4467
  }
4084
4468
  function CacheBadge({ usage }) {
4085
4469
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -4088,7 +4472,7 @@ function CacheBadge({ usage }) {
4088
4472
  if (total === 0) return null;
4089
4473
  const pct2 = hit / total * 100;
4090
4474
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
4091
- 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), "%"));
4092
4476
  }
4093
4477
  function truncate3(s, max) {
4094
4478
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -4122,7 +4506,7 @@ function DiffApp({ report }) {
4122
4506
  }
4123
4507
  });
4124
4508
  const pair = report.pairs[idx];
4125
- 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")));
4126
4510
  }
4127
4511
  function DiffHeader({ report }) {
4128
4512
  const a = report.a;
@@ -4140,15 +4524,15 @@ function DiffHeader({ report }) {
4140
4524
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
4141
4525
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
4142
4526
  }
4143
- 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);
4144
4528
  }
4145
4529
  function Pane({
4146
4530
  label,
4147
4531
  headerColor,
4148
4532
  records
4149
4533
  }) {
4150
- return /* @__PURE__ */ React10.createElement(
4151
- Box9,
4534
+ return /* @__PURE__ */ React11.createElement(
4535
+ Box10,
4152
4536
  {
4153
4537
  flexDirection: "column",
4154
4538
  flexGrow: 1,
@@ -4156,21 +4540,21 @@ function Pane({
4156
4540
  borderStyle: "single",
4157
4541
  borderColor: headerColor
4158
4542
  },
4159
- /* @__PURE__ */ React10.createElement(Text9, { color: headerColor, bold: true }, label),
4160
- 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 }))
4161
4545
  );
4162
4546
  }
4163
4547
  function KindBadge({ kind }) {
4164
4548
  if (kind === "match") {
4165
- return /* @__PURE__ */ React10.createElement(Text9, { color: "green" }, "\u2713 match");
4549
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "green" }, "\u2713 match");
4166
4550
  }
4167
4551
  if (kind === "diverge") {
4168
- return /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u2605 diverge");
4552
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u2605 diverge");
4169
4553
  }
4170
4554
  if (kind === "only_in_a") {
4171
- return /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u2190 only in A");
4555
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u2190 only in A");
4172
4556
  }
4173
- return /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, "\u2192 only in B");
4557
+ return /* @__PURE__ */ React11.createElement(Text10, { color: "magenta" }, "\u2192 only in B");
4174
4558
  }
4175
4559
  function paneRecords(pair, side) {
4176
4560
  if (!pair) return [];
@@ -4201,7 +4585,7 @@ markdown report written to ${opts.mdPath}`);
4201
4585
  return;
4202
4586
  }
4203
4587
  if (wantTui) {
4204
- const { waitUntilExit } = render2(React11.createElement(DiffApp, { report }), {
4588
+ const { waitUntilExit } = render2(React12.createElement(DiffApp, { report }), {
4205
4589
  exitOnCtrlC: true
4206
4590
  });
4207
4591
  await waitUntilExit();
@@ -4210,6 +4594,70 @@ markdown report written to ${opts.mdPath}`);
4210
4594
  console.log(renderSummaryTable(report));
4211
4595
  }
4212
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
+
4213
4661
  // src/mcp/catalog.ts
4214
4662
  var MCP_CATALOG = [
4215
4663
  {
@@ -4277,11 +4725,11 @@ function pad(s, width) {
4277
4725
 
4278
4726
  // src/cli/commands/replay.ts
4279
4727
  import { render as render3 } from "ink";
4280
- import React13 from "react";
4728
+ import React14 from "react";
4281
4729
 
4282
4730
  // src/cli/ui/ReplayApp.tsx
4283
- import { Box as Box10, Static as Static3, Text as Text10, useApp as useApp4, useInput as useInput3 } from "ink";
4284
- 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";
4285
4733
  function ReplayApp({ meta, pages }) {
4286
4734
  const { exit } = useApp4();
4287
4735
  const maxIdx = Math.max(0, pages.length - 1);
@@ -4318,14 +4766,14 @@ function ReplayApp({ meta, pages }) {
4318
4766
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
4319
4767
  const currentPage = pages[idx];
4320
4768
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
4321
- return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(
4769
+ return /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(
4322
4770
  StatsPanel,
4323
4771
  {
4324
4772
  summary,
4325
4773
  model: cumStats.models[0] ?? meta?.model ?? "?",
4326
4774
  prefixHash
4327
4775
  }
4328
- ), /* @__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")));
4329
4777
  }
4330
4778
 
4331
4779
  // src/cli/commands/replay.ts
@@ -4337,7 +4785,7 @@ async function replayCommand(opts) {
4337
4785
  }
4338
4786
  const { parsed } = replayFromFile(opts.path);
4339
4787
  const pages = groupRecordsByTurn(parsed.records);
4340
- const { waitUntilExit } = render3(React13.createElement(ReplayApp, { meta: parsed.meta, pages }), {
4788
+ const { waitUntilExit } = render3(React14.createElement(ReplayApp, { meta: parsed.meta, pages }), {
4341
4789
  exitOnCtrlC: true
4342
4790
  });
4343
4791
  await waitUntilExit();
@@ -4389,7 +4837,7 @@ function sliceRecords(records, opts) {
4389
4837
  function renderRecord(rec) {
4390
4838
  const turn = `[t${rec.turn}]`;
4391
4839
  if (rec.role === "user") {
4392
- console.log(`${turn} USER: ${oneLine(rec.content)}`);
4840
+ console.log(`${turn} USER: ${oneLine2(rec.content)}`);
4393
4841
  } else if (rec.role === "assistant_final") {
4394
4842
  const cost = rec.cost !== void 0 ? ` $${rec.cost.toFixed(6)}` : "";
4395
4843
  const cache = rec.usage && (rec.usage.prompt_cache_hit_tokens !== void 0 || rec.usage.prompt_cache_miss_tokens !== void 0) ? (() => {
@@ -4398,7 +4846,7 @@ function renderRecord(rec) {
4398
4846
  const total = hit + miss;
4399
4847
  return total > 0 ? ` cache=${(hit / total * 100).toFixed(1)}%` : "";
4400
4848
  })() : "";
4401
- console.log(`${turn} AGENT:${cost}${cache} ${oneLine(rec.content)}`);
4849
+ console.log(`${turn} AGENT:${cost}${cache} ${oneLine2(rec.content)}`);
4402
4850
  if (rec.planState) {
4403
4851
  const ps = rec.planState;
4404
4852
  if (ps.subgoals.length)
@@ -4415,16 +4863,16 @@ function renderRecord(rec) {
4415
4863
  );
4416
4864
  }
4417
4865
  } else if (rec.role === "tool") {
4418
- const args = rec.args ? ` args=${oneLine(rec.args, 80)}` : "";
4419
- 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)}`);
4420
4868
  } else if (rec.role === "error") {
4421
4869
  console.log(`${turn} ERROR: ${rec.error ?? rec.content}`);
4422
4870
  } else if (rec.role === "done") {
4423
4871
  } else {
4424
- console.log(`${turn} ${rec.role}: ${oneLine(rec.content)}`);
4872
+ console.log(`${turn} ${rec.role}: ${oneLine2(rec.content)}`);
4425
4873
  }
4426
4874
  }
4427
- function oneLine(s, max = 200) {
4875
+ function oneLine2(s, max = 200) {
4428
4876
  const collapsed = s.replace(/\s+/g, " ").trim();
4429
4877
  return collapsed.length > max ? `${collapsed.slice(0, max)}\u2026` : collapsed;
4430
4878
  }
@@ -4610,7 +5058,7 @@ function inspectSession(name, verbose) {
4610
5058
  function renderMessage(msg, turnIdx, verbose) {
4611
5059
  const turn = turnIdx > 0 ? `[t${turnIdx}]` : "[start]";
4612
5060
  const content = typeof msg.content === "string" ? msg.content : "";
4613
- const flat = oneLine2(content);
5061
+ const flat = oneLine3(content);
4614
5062
  if (msg.role === "user") {
4615
5063
  console.log(`${turn} USER: ${flat}`);
4616
5064
  } else if (msg.role === "assistant") {
@@ -4628,7 +5076,7 @@ function renderMessage(msg, turnIdx, verbose) {
4628
5076
  if (verbose) console.log(`${turn} SYSTEM: ${truncate4(flat, 160)}`);
4629
5077
  }
4630
5078
  }
4631
- function oneLine2(s, max = 200) {
5079
+ function oneLine3(s, max = 200) {
4632
5080
  const collapsed = s.replace(/\s+/g, " ").trim();
4633
5081
  return collapsed.length > max ? `${collapsed.slice(0, max)}\u2026` : collapsed;
4634
5082
  }
@@ -4638,16 +5086,16 @@ function truncate4(s, max) {
4638
5086
 
4639
5087
  // src/cli/commands/setup.tsx
4640
5088
  import { render as render4 } from "ink";
4641
- import React16 from "react";
5089
+ import React17 from "react";
4642
5090
 
4643
5091
  // src/cli/ui/Wizard.tsx
4644
- 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";
4645
5093
  import TextInput3 from "ink-text-input";
4646
- import React15, { useState as useState8 } from "react";
5094
+ import React16, { useState as useState8 } from "react";
4647
5095
 
4648
5096
  // src/cli/ui/Select.tsx
4649
- import { Box as Box11, Text as Text11, useInput as useInput4 } from "ink";
4650
- 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";
4651
5099
  function SingleSelect({
4652
5100
  items,
4653
5101
  initialValue,
@@ -4671,7 +5119,7 @@ function SingleSelect({
4671
5119
  onCancel();
4672
5120
  }
4673
5121
  });
4674
- 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(
4675
5123
  SelectRow,
4676
5124
  {
4677
5125
  key: item.value,
@@ -4714,10 +5162,10 @@ function MultiSelect({
4714
5162
  onCancel();
4715
5163
  }
4716
5164
  });
4717
- return /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column" }, items.map((item, i) => {
5165
+ return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => {
4718
5166
  const checked = selected.has(item.value);
4719
5167
  const marker = checked ? "[x]" : "[ ]";
4720
- return /* @__PURE__ */ React14.createElement(
5168
+ return /* @__PURE__ */ React15.createElement(
4721
5169
  SelectRow,
4722
5170
  {
4723
5171
  key: item.value,
@@ -4726,7 +5174,7 @@ function MultiSelect({
4726
5174
  marker: `${i === index ? "\u25B8" : " "} ${marker}`
4727
5175
  }
4728
5176
  );
4729
- }), 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);
4730
5178
  }
4731
5179
  function SelectRow({
4732
5180
  item,
@@ -4734,7 +5182,7 @@ function SelectRow({
4734
5182
  marker
4735
5183
  }) {
4736
5184
  const color = item.disabled ? "gray" : active ? "cyan" : void 0;
4737
- 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);
4738
5186
  }
4739
5187
  function findNextEnabled(items, from, step) {
4740
5188
  if (items.length === 0) return 0;
@@ -4783,7 +5231,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4783
5231
  if (key.escape && step !== "saved" && onCancel) onCancel();
4784
5232
  });
4785
5233
  if (step === "apiKey") {
4786
- return /* @__PURE__ */ React15.createElement(
5234
+ return /* @__PURE__ */ React16.createElement(
4787
5235
  ApiKeyStep,
4788
5236
  {
4789
5237
  onSubmit: (key) => {
@@ -4797,7 +5245,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4797
5245
  );
4798
5246
  }
4799
5247
  if (step === "preset") {
4800
- 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(
4801
5249
  SingleSelect,
4802
5250
  {
4803
5251
  items: presetItems(),
@@ -4807,10 +5255,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4807
5255
  setStep("mcp");
4808
5256
  }
4809
5257
  }
4810
- ), /* @__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")));
4811
5259
  }
4812
5260
  if (step === "mcp") {
4813
- 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(
4814
5262
  MultiSelect,
4815
5263
  {
4816
5264
  items: mcpItems(),
@@ -4835,7 +5283,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4835
5283
  }
4836
5284
  const currentName = pending[0];
4837
5285
  const entry = CATALOG_BY_NAME.get(currentName);
4838
- return /* @__PURE__ */ React15.createElement(
5286
+ return /* @__PURE__ */ React16.createElement(
4839
5287
  McpArgsStep,
4840
5288
  {
4841
5289
  entry,
@@ -4853,7 +5301,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4853
5301
  }
4854
5302
  if (step === "review") {
4855
5303
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
4856
- 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(
4857
5305
  SummaryLine,
4858
5306
  {
4859
5307
  label: "MCP",
@@ -4861,8 +5309,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4861
5309
  }
4862
5310
  ), specs.map((spec, i) => (
4863
5311
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
4864
- /* @__PURE__ */ React15.createElement(Box12, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React15.createElement(Text12, { dimColor: true }, "\xB7 ", spec))
4865
- )), /* @__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(
4866
5314
  ReviewConfirm,
4867
5315
  {
4868
5316
  onConfirm: () => {
@@ -4888,7 +5336,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
4888
5336
  }
4889
5337
  ));
4890
5338
  }
4891
- 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 }));
4892
5340
  }
4893
5341
  function ApiKeyStep({
4894
5342
  onSubmit,
@@ -4896,7 +5344,7 @@ function ApiKeyStep({
4896
5344
  onError
4897
5345
  }) {
4898
5346
  const [value, setValue] = useState8("");
4899
- 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(
4900
5348
  TextInput3,
4901
5349
  {
4902
5350
  value,
@@ -4913,7 +5361,7 @@ function ApiKeyStep({
4913
5361
  mask: "\u2022",
4914
5362
  placeholder: "sk-..."
4915
5363
  }
4916
- )), 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);
4917
5365
  }
4918
5366
  function McpArgsStep({
4919
5367
  entry,
@@ -4922,7 +5370,7 @@ function McpArgsStep({
4922
5370
  onError
4923
5371
  }) {
4924
5372
  const [value, setValue] = useState8("");
4925
- 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(
4926
5374
  TextInput3,
4927
5375
  {
4928
5376
  value,
@@ -4938,7 +5386,7 @@ function McpArgsStep({
4938
5386
  },
4939
5387
  placeholder: placeholderFor(entry)
4940
5388
  }
4941
- )), 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));
4942
5390
  }
4943
5391
  function ReviewConfirm({ onConfirm }) {
4944
5392
  useInput5((_i, key) => {
@@ -4958,10 +5406,10 @@ function StepFrame({
4958
5406
  total,
4959
5407
  children
4960
5408
  }) {
4961
- 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));
4962
5410
  }
4963
5411
  function SummaryLine({ label, value }) {
4964
- 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));
4965
5413
  }
4966
5414
  function presetItems() {
4967
5415
  return ["fast", "smart", "max"].map((name) => ({
@@ -5017,7 +5465,7 @@ async function setupCommand(_opts = {}) {
5017
5465
  const existingKey = loadApiKey();
5018
5466
  const existing = readConfig();
5019
5467
  const { waitUntilExit, unmount } = render4(
5020
- /* @__PURE__ */ React16.createElement(
5468
+ /* @__PURE__ */ React17.createElement(
5021
5469
  Wizard,
5022
5470
  {
5023
5471
  existingApiKey: existingKey,
@@ -5236,6 +5684,17 @@ var mcp = program.command("mcp").description("Model Context Protocol helpers \u2
5236
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) => {
5237
5685
  mcpListCommand({ json: !!opts.json });
5238
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
+ });
5239
5698
  program.command("version").description("Print Reasonix version.").action(versionCommand);
5240
5699
  program.parseAsync(process.argv).catch((err) => {
5241
5700
  console.error(err);