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 +548 -89
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +142 -4
- package/dist/index.js +146 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
|
|
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
|
|
2931
|
+
import React9, { useState as useState4 } from "react";
|
|
2792
2932
|
|
|
2793
2933
|
// src/cli/ui/App.tsx
|
|
2794
|
-
import { Box as
|
|
2795
|
-
import
|
|
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/
|
|
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__ */
|
|
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
|
-
[
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
4259
|
+
import { Box as Box8, Text as Text8, useApp as useApp2 } from "ink";
|
|
3906
4260
|
import TextInput2 from "ink-text-input";
|
|
3907
|
-
import
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
4439
|
+
import React12 from "react";
|
|
4056
4440
|
|
|
4057
4441
|
// src/cli/ui/DiffApp.tsx
|
|
4058
|
-
import { Box as
|
|
4059
|
-
import
|
|
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
|
|
4063
|
-
import
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
4151
|
-
|
|
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__ */
|
|
4160
|
-
records.length === 0 ? /* @__PURE__ */
|
|
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__ */
|
|
4549
|
+
return /* @__PURE__ */ React11.createElement(Text10, { color: "green" }, "\u2713 match");
|
|
4166
4550
|
}
|
|
4167
4551
|
if (kind === "diverge") {
|
|
4168
|
-
return /* @__PURE__ */
|
|
4552
|
+
return /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u2605 diverge");
|
|
4169
4553
|
}
|
|
4170
4554
|
if (kind === "only_in_a") {
|
|
4171
|
-
return /* @__PURE__ */
|
|
4555
|
+
return /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u2190 only in A");
|
|
4172
4556
|
}
|
|
4173
|
-
return /* @__PURE__ */
|
|
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(
|
|
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
|
|
4728
|
+
import React14 from "react";
|
|
4281
4729
|
|
|
4282
4730
|
// src/cli/ui/ReplayApp.tsx
|
|
4283
|
-
import { Box as
|
|
4284
|
-
import
|
|
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__ */
|
|
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__ */
|
|
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(
|
|
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: ${
|
|
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} ${
|
|
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=${
|
|
4419
|
-
console.log(`${turn} TOOL ${rec.tool ?? "?"}:${args} \u2192 ${
|
|
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}: ${
|
|
4872
|
+
console.log(`${turn} ${rec.role}: ${oneLine2(rec.content)}`);
|
|
4425
4873
|
}
|
|
4426
4874
|
}
|
|
4427
|
-
function
|
|
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 =
|
|
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
|
|
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
|
|
5089
|
+
import React17 from "react";
|
|
4642
5090
|
|
|
4643
5091
|
// src/cli/ui/Wizard.tsx
|
|
4644
|
-
import { Box as
|
|
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
|
|
5094
|
+
import React16, { useState as useState8 } from "react";
|
|
4647
5095
|
|
|
4648
5096
|
// src/cli/ui/Select.tsx
|
|
4649
|
-
import { Box as
|
|
4650
|
-
import
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
4865
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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);
|