reasonix 0.4.13 → 0.4.15
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/README.md +433 -163
- package/dist/cli/index.js +499 -198
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +101 -2
- package/dist/index.js +188 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -32,6 +32,13 @@ function loadApiKey(path = defaultConfigPath()) {
|
|
|
32
32
|
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
33
33
|
return readConfig(path).apiKey;
|
|
34
34
|
}
|
|
35
|
+
function searchEnabled(path = defaultConfigPath()) {
|
|
36
|
+
const env = process.env.REASONIX_SEARCH;
|
|
37
|
+
if (env === "off" || env === "false" || env === "0") return false;
|
|
38
|
+
const cfg = readConfig(path).search;
|
|
39
|
+
if (cfg === false) return false;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
35
42
|
function saveApiKey(key, path = defaultConfigPath()) {
|
|
36
43
|
const cfg = readConfig(path);
|
|
37
44
|
cfg.apiKey = key.trim();
|
|
@@ -2232,6 +2239,187 @@ function lineDiff(a, b) {
|
|
|
2232
2239
|
return out;
|
|
2233
2240
|
}
|
|
2234
2241
|
|
|
2242
|
+
// src/tools/web.ts
|
|
2243
|
+
var DEFAULT_FETCH_MAX_CHARS = 32e3;
|
|
2244
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 15e3;
|
|
2245
|
+
var DEFAULT_TOPK = 5;
|
|
2246
|
+
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
2247
|
+
var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
|
|
2248
|
+
async function webSearch(query, opts = {}) {
|
|
2249
|
+
const topK = Math.max(1, Math.min(10, opts.topK ?? DEFAULT_TOPK));
|
|
2250
|
+
const resp = await fetch(`${MOJEEK_ENDPOINT}?q=${encodeURIComponent(query)}`, {
|
|
2251
|
+
headers: {
|
|
2252
|
+
"User-Agent": USER_AGENT,
|
|
2253
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9",
|
|
2254
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
2255
|
+
},
|
|
2256
|
+
signal: opts.signal,
|
|
2257
|
+
redirect: "follow"
|
|
2258
|
+
});
|
|
2259
|
+
if (!resp.ok) throw new Error(`web_search ${resp.status}`);
|
|
2260
|
+
const html = await resp.text();
|
|
2261
|
+
const results = parseMojeekResults(html).slice(0, topK);
|
|
2262
|
+
if (results.length === 0) {
|
|
2263
|
+
if (/no results found|did not match any documents/i.test(html)) return [];
|
|
2264
|
+
if (/captcha|verify you are human|access denied|forbidden/i.test(html)) {
|
|
2265
|
+
throw new Error("web_search: Mojeek anti-bot page \u2014 rate-limited or blocked");
|
|
2266
|
+
}
|
|
2267
|
+
throw new Error(
|
|
2268
|
+
`web_search: 0 results but response doesn't look like a real empty page (${html.length} chars, first 120: ${html.slice(0, 120).replace(/\s+/g, " ")})`
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
return results;
|
|
2272
|
+
}
|
|
2273
|
+
function parseMojeekResults(html) {
|
|
2274
|
+
const titles = [];
|
|
2275
|
+
const titleAnchorRe = /<a\b[^>]*\bclass="title"[^>]*>[\s\S]*?<\/a>/g;
|
|
2276
|
+
let m;
|
|
2277
|
+
while (true) {
|
|
2278
|
+
m = titleAnchorRe.exec(html);
|
|
2279
|
+
if (m === null) break;
|
|
2280
|
+
titles.push(m[0]);
|
|
2281
|
+
}
|
|
2282
|
+
const snippets = [];
|
|
2283
|
+
const snippetRe = /<p\b[^>]*\bclass="s"[^>]*>([\s\S]*?)<\/p>/g;
|
|
2284
|
+
while (true) {
|
|
2285
|
+
m = snippetRe.exec(html);
|
|
2286
|
+
if (m === null) break;
|
|
2287
|
+
snippets.push(m[1] ?? "");
|
|
2288
|
+
}
|
|
2289
|
+
const hrefRe = /href="([^"]+)"/;
|
|
2290
|
+
const innerRe = /<a\b[^>]*>([\s\S]*?)<\/a>/;
|
|
2291
|
+
const results = [];
|
|
2292
|
+
for (let i = 0; i < titles.length; i++) {
|
|
2293
|
+
const anchor = titles[i];
|
|
2294
|
+
const hrefMatch = anchor.match(hrefRe);
|
|
2295
|
+
const innerMatch = anchor.match(innerRe);
|
|
2296
|
+
if (!hrefMatch?.[1]) continue;
|
|
2297
|
+
results.push({
|
|
2298
|
+
title: decodeHtmlEntities(stripHtml(innerMatch?.[1] ?? "")).trim(),
|
|
2299
|
+
url: hrefMatch[1],
|
|
2300
|
+
snippet: decodeHtmlEntities(stripHtml(snippets[i] ?? "")).replace(/\s+/g, " ").trim()
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
return results;
|
|
2304
|
+
}
|
|
2305
|
+
async function webFetch(url, opts = {}) {
|
|
2306
|
+
const maxChars = opts.maxChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
2307
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
2308
|
+
const ctl = new AbortController();
|
|
2309
|
+
const timer = setTimeout(() => ctl.abort(), timeoutMs);
|
|
2310
|
+
const cancel = () => ctl.abort();
|
|
2311
|
+
opts.signal?.addEventListener("abort", cancel, { once: true });
|
|
2312
|
+
let resp;
|
|
2313
|
+
try {
|
|
2314
|
+
resp = await fetch(url, {
|
|
2315
|
+
headers: { "User-Agent": USER_AGENT, Accept: "text/html,text/plain,*/*" },
|
|
2316
|
+
signal: ctl.signal,
|
|
2317
|
+
redirect: "follow"
|
|
2318
|
+
});
|
|
2319
|
+
} finally {
|
|
2320
|
+
clearTimeout(timer);
|
|
2321
|
+
opts.signal?.removeEventListener("abort", cancel);
|
|
2322
|
+
}
|
|
2323
|
+
if (!resp.ok) throw new Error(`web_fetch ${resp.status} for ${url}`);
|
|
2324
|
+
const contentType = resp.headers.get("content-type") ?? "";
|
|
2325
|
+
const raw = await resp.text();
|
|
2326
|
+
const title = extractTitle(raw);
|
|
2327
|
+
const text = contentType.includes("text/html") ? htmlToText(raw) : raw;
|
|
2328
|
+
const truncated = text.length > maxChars;
|
|
2329
|
+
const finalText = truncated ? `${text.slice(0, maxChars)}
|
|
2330
|
+
|
|
2331
|
+
[\u2026 truncated ${text.length - maxChars} chars \u2026]` : text;
|
|
2332
|
+
return { url, title, text: finalText, truncated };
|
|
2333
|
+
}
|
|
2334
|
+
function htmlToText(html) {
|
|
2335
|
+
let s = html;
|
|
2336
|
+
s = s.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
2337
|
+
s = s.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
2338
|
+
s = s.replace(/<noscript[\s\S]*?<\/noscript>/gi, "");
|
|
2339
|
+
s = s.replace(/<nav[\s\S]*?<\/nav>/gi, "");
|
|
2340
|
+
s = s.replace(/<footer[\s\S]*?<\/footer>/gi, "");
|
|
2341
|
+
s = s.replace(/<aside[\s\S]*?<\/aside>/gi, "");
|
|
2342
|
+
s = s.replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
2343
|
+
s = s.replace(/<\/?(p|div|br|h[1-6]|li|tr|section|article)\b[^>]*>/gi, "\n");
|
|
2344
|
+
s = s.replace(/<[^>]+>/g, "");
|
|
2345
|
+
s = decodeHtmlEntities(s);
|
|
2346
|
+
s = s.replace(/[ \t]+/g, " ");
|
|
2347
|
+
s = s.replace(/\n[ \t]+/g, "\n");
|
|
2348
|
+
s = s.replace(/\n{3,}/g, "\n\n");
|
|
2349
|
+
return s.trim();
|
|
2350
|
+
}
|
|
2351
|
+
function stripHtml(s) {
|
|
2352
|
+
return s.replace(/<[^>]+>/g, "");
|
|
2353
|
+
}
|
|
2354
|
+
function decodeHtmlEntities(s) {
|
|
2355
|
+
return s.replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
2356
|
+
}
|
|
2357
|
+
function extractTitle(html) {
|
|
2358
|
+
const m = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
2359
|
+
if (!m?.[1]) return void 0;
|
|
2360
|
+
return m[1].replace(/\s+/g, " ").trim() || void 0;
|
|
2361
|
+
}
|
|
2362
|
+
function registerWebTools(registry, opts = {}) {
|
|
2363
|
+
const defaultTopK = opts.defaultTopK ?? DEFAULT_TOPK;
|
|
2364
|
+
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
2365
|
+
registry.register({
|
|
2366
|
+
name: "web_search",
|
|
2367
|
+
description: "Search the public web. Returns ranked results with title, url, and snippet. Use this when the question needs information more current than your training data, when you're unsure of a factual detail, or when the user asks about a specific webpage/library/release you haven't seen.",
|
|
2368
|
+
parameters: {
|
|
2369
|
+
type: "object",
|
|
2370
|
+
properties: {
|
|
2371
|
+
query: { type: "string", description: "Natural-language search query." },
|
|
2372
|
+
topK: {
|
|
2373
|
+
type: "integer",
|
|
2374
|
+
description: `Number of results to return (1..10). Default ${defaultTopK}.`
|
|
2375
|
+
}
|
|
2376
|
+
},
|
|
2377
|
+
required: ["query"]
|
|
2378
|
+
},
|
|
2379
|
+
fn: async (args, ctx) => {
|
|
2380
|
+
const results = await webSearch(args.query, {
|
|
2381
|
+
topK: args.topK ?? defaultTopK,
|
|
2382
|
+
signal: ctx?.signal
|
|
2383
|
+
});
|
|
2384
|
+
return formatSearchResults(args.query, results);
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
registry.register({
|
|
2388
|
+
name: "web_fetch",
|
|
2389
|
+
description: "Download a URL and return its visible text content (HTML pages get scripts/styles/nav stripped). Truncated at the tool-result cap. Use after web_search when a snippet isn't enough.",
|
|
2390
|
+
parameters: {
|
|
2391
|
+
type: "object",
|
|
2392
|
+
properties: {
|
|
2393
|
+
url: { type: "string", description: "Absolute http:// or https:// URL." }
|
|
2394
|
+
},
|
|
2395
|
+
required: ["url"]
|
|
2396
|
+
},
|
|
2397
|
+
fn: async (args, ctx) => {
|
|
2398
|
+
if (!/^https?:\/\//i.test(args.url)) {
|
|
2399
|
+
throw new Error("web_fetch: url must start with http:// or https://");
|
|
2400
|
+
}
|
|
2401
|
+
const page = await webFetch(args.url, { maxChars: maxFetchChars, signal: ctx?.signal });
|
|
2402
|
+
const header = page.title ? `${page.title}
|
|
2403
|
+
${page.url}` : page.url;
|
|
2404
|
+
return `${header}
|
|
2405
|
+
|
|
2406
|
+
${page.text}`;
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2409
|
+
return registry;
|
|
2410
|
+
}
|
|
2411
|
+
function formatSearchResults(query, results) {
|
|
2412
|
+
const lines = [`query: ${query}`, `
|
|
2413
|
+
results (${results.length}):`];
|
|
2414
|
+
results.forEach((r, i) => {
|
|
2415
|
+
lines.push(`
|
|
2416
|
+
${i + 1}. ${r.title}`);
|
|
2417
|
+
lines.push(` ${r.url}`);
|
|
2418
|
+
if (r.snippet) lines.push(` ${r.snippet}`);
|
|
2419
|
+
});
|
|
2420
|
+
return lines.join("\n");
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2235
2423
|
// src/env.ts
|
|
2236
2424
|
import { readFileSync as readFileSync3 } from "fs";
|
|
2237
2425
|
import { resolve as resolve2 } from "path";
|
|
@@ -3519,19 +3707,19 @@ function sep() {
|
|
|
3519
3707
|
}
|
|
3520
3708
|
|
|
3521
3709
|
// src/index.ts
|
|
3522
|
-
var VERSION = "0.4.
|
|
3710
|
+
var VERSION = "0.4.15";
|
|
3523
3711
|
|
|
3524
3712
|
// src/cli/commands/chat.tsx
|
|
3525
3713
|
import { render } from "ink";
|
|
3526
|
-
import
|
|
3714
|
+
import React10, { useState as useState5 } from "react";
|
|
3527
3715
|
|
|
3528
3716
|
// src/cli/ui/App.tsx
|
|
3529
3717
|
import { Box as Box7, Static, Text as Text7, useApp, useInput as useInput2 } from "ink";
|
|
3530
|
-
import
|
|
3718
|
+
import React8, { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
3531
3719
|
|
|
3532
3720
|
// src/cli/ui/EventLog.tsx
|
|
3533
3721
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
3534
|
-
import
|
|
3722
|
+
import React4 from "react";
|
|
3535
3723
|
|
|
3536
3724
|
// src/cli/ui/PlanStateBlock.tsx
|
|
3537
3725
|
import { Box, Text } from "ink";
|
|
@@ -3842,45 +4030,67 @@ function Markdown({ text }) {
|
|
|
3842
4030
|
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", gap: 1 }, blocks.map((b, i) => /* @__PURE__ */ React2.createElement(BlockView, { key: `${i}-${b.kind}`, block: b })));
|
|
3843
4031
|
}
|
|
3844
4032
|
|
|
4033
|
+
// src/cli/ui/ticker.tsx
|
|
4034
|
+
import React3, { createContext, useContext, useEffect, useState } from "react";
|
|
4035
|
+
var TICK_MS = 120;
|
|
4036
|
+
var TickContext = createContext(0);
|
|
4037
|
+
function TickerProvider({ children, disabled }) {
|
|
4038
|
+
const [tick, setTick] = useState(0);
|
|
4039
|
+
useEffect(() => {
|
|
4040
|
+
if (disabled) return;
|
|
4041
|
+
const id = setInterval(() => setTick((t) => t + 1), TICK_MS);
|
|
4042
|
+
return () => clearInterval(id);
|
|
4043
|
+
}, [disabled]);
|
|
4044
|
+
return /* @__PURE__ */ React3.createElement(TickContext.Provider, { value: tick }, children);
|
|
4045
|
+
}
|
|
4046
|
+
function useTick() {
|
|
4047
|
+
return useContext(TickContext);
|
|
4048
|
+
}
|
|
4049
|
+
function useElapsedSeconds() {
|
|
4050
|
+
const [start] = useState(() => Date.now());
|
|
4051
|
+
useTick();
|
|
4052
|
+
return Math.floor((Date.now() - start) / 1e3);
|
|
4053
|
+
}
|
|
4054
|
+
|
|
3845
4055
|
// src/cli/ui/EventLog.tsx
|
|
3846
|
-
var EventRow =
|
|
4056
|
+
var EventRow = React4.memo(function EventRow2({ event }) {
|
|
3847
4057
|
if (event.role === "user") {
|
|
3848
|
-
return /* @__PURE__ */
|
|
4058
|
+
return /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React4.createElement(Text3, null, event.text));
|
|
3849
4059
|
}
|
|
3850
4060
|
if (event.role === "assistant") {
|
|
3851
|
-
if (event.streaming) return /* @__PURE__ */
|
|
3852
|
-
return /* @__PURE__ */
|
|
4061
|
+
if (event.streaming) return /* @__PURE__ */ React4.createElement(StreamingAssistant, { event });
|
|
4062
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "green" }, "assistant")), event.branch ? /* @__PURE__ */ React4.createElement(BranchBlock, { branch: event.branch }) : null, event.reasoning ? /* @__PURE__ */ React4.createElement(ReasoningBlock, { reasoning: event.reasoning }) : null, !isPlanStateEmpty(event.planState) ? /* @__PURE__ */ React4.createElement(PlanStateBlock, { planState: event.planState }) : null, event.text ? /* @__PURE__ */ React4.createElement(Markdown, { text: event.text }) : /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, "(no content)"), event.stats ? /* @__PURE__ */ React4.createElement(StatsLine, { stats: event.stats }) : null, event.repair ? /* @__PURE__ */ React4.createElement(Text3, { color: "magenta" }, event.repair) : null);
|
|
3853
4063
|
}
|
|
3854
4064
|
if (event.role === "tool") {
|
|
3855
4065
|
const isError = event.text.startsWith("ERROR:");
|
|
3856
4066
|
const color = isError ? "red" : "yellow";
|
|
3857
4067
|
const marker = isError ? "\u2717" : "\u2192";
|
|
3858
4068
|
const isEditFile = (event.toolName === "edit_file" || event.toolName?.endsWith("_edit_file")) && !isError;
|
|
3859
|
-
return /* @__PURE__ */
|
|
4069
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text3, { color }, `tool<${event.toolName ?? "?"}> ${marker}`), isEditFile ? /* @__PURE__ */ React4.createElement(EditFileDiff, { text: event.text }) : /* @__PURE__ */ React4.createElement(Text3, { color: isError ? "red" : void 0, dimColor: !isError }, " ", truncate2(event.text, 400)));
|
|
3860
4070
|
}
|
|
3861
4071
|
if (event.role === "error") {
|
|
3862
|
-
return /* @__PURE__ */
|
|
4072
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text3, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React4.createElement(Text3, { color: "red" }, event.text));
|
|
3863
4073
|
}
|
|
3864
4074
|
if (event.role === "info") {
|
|
3865
|
-
return /* @__PURE__ */
|
|
4075
|
+
return /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, event.text));
|
|
3866
4076
|
}
|
|
3867
4077
|
if (event.role === "warning") {
|
|
3868
|
-
return /* @__PURE__ */
|
|
4078
|
+
return /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { color: "yellow" }, "\u25B8 "), /* @__PURE__ */ React4.createElement(Text3, { color: "yellow" }, event.text));
|
|
3869
4079
|
}
|
|
3870
|
-
return /* @__PURE__ */
|
|
4080
|
+
return /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, null, event.text));
|
|
3871
4081
|
});
|
|
3872
4082
|
function EditFileDiff({ text }) {
|
|
3873
4083
|
const lines = text.split(/\r?\n/);
|
|
3874
4084
|
const [statusHeader, hunkHeader, ...body] = lines;
|
|
3875
|
-
return /* @__PURE__ */
|
|
4085
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, ` ${statusHeader ?? ""}`), hunkHeader !== void 0 ? /* @__PURE__ */ React4.createElement(Text3, { color: "cyan", bold: true }, hunkHeader) : null, body.map((line, i) => {
|
|
3876
4086
|
const key = `${i}-${line.slice(0, 32)}`;
|
|
3877
4087
|
if (line.startsWith("- ")) {
|
|
3878
|
-
return /* @__PURE__ */
|
|
4088
|
+
return /* @__PURE__ */ React4.createElement(Text3, { key, color: "red" }, line);
|
|
3879
4089
|
}
|
|
3880
4090
|
if (line.startsWith("+ ")) {
|
|
3881
|
-
return /* @__PURE__ */
|
|
4091
|
+
return /* @__PURE__ */ React4.createElement(Text3, { key, color: "green" }, line);
|
|
3882
4092
|
}
|
|
3883
|
-
return /* @__PURE__ */
|
|
4093
|
+
return /* @__PURE__ */ React4.createElement(Text3, { key, dimColor: true }, line);
|
|
3884
4094
|
}));
|
|
3885
4095
|
}
|
|
3886
4096
|
function BranchBlock({ branch }) {
|
|
@@ -3889,33 +4099,28 @@ function BranchBlock({ branch }) {
|
|
|
3889
4099
|
const t = (branch.temperatures[i] ?? 0).toFixed(1);
|
|
3890
4100
|
return `${marker} #${i} T=${t} u=${u}`;
|
|
3891
4101
|
}).join(" ");
|
|
3892
|
-
return /* @__PURE__ */
|
|
4102
|
+
return /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { color: "blue" }, "\u{1F500} branched ", /* @__PURE__ */ React4.createElement(Text3, { bold: true }, branch.budget), ` samples \u2192 picked #${branch.chosenIndex} `, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, per)));
|
|
3893
4103
|
}
|
|
3894
4104
|
function ReasoningBlock({ reasoning }) {
|
|
3895
4105
|
const max = 260;
|
|
3896
4106
|
const flat = reasoning.replace(/\s+/g, " ").trim();
|
|
3897
4107
|
const preview = flat.length <= max ? flat : `\u2026 (+${flat.length - max} earlier chars) ${flat.slice(-max)}`;
|
|
3898
|
-
return /* @__PURE__ */
|
|
4108
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true, italic: true }, "\u21B3 thinking: ", preview));
|
|
3899
4109
|
}
|
|
3900
4110
|
function Elapsed() {
|
|
3901
|
-
const
|
|
3902
|
-
useEffect(() => {
|
|
3903
|
-
const start = Date.now();
|
|
3904
|
-
const id = setInterval(() => setS(Math.floor((Date.now() - start) / 1e3)), 1e3);
|
|
3905
|
-
return () => clearInterval(id);
|
|
3906
|
-
}, []);
|
|
4111
|
+
const s = useElapsedSeconds();
|
|
3907
4112
|
const mm = String(Math.floor(s / 60)).padStart(2, "0");
|
|
3908
4113
|
const ss = String(s % 60).padStart(2, "0");
|
|
3909
|
-
return /* @__PURE__ */
|
|
4114
|
+
return /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, `${mm}:${ss}`);
|
|
3910
4115
|
}
|
|
3911
4116
|
function StreamingAssistant({ event }) {
|
|
3912
4117
|
if (event.branchProgress) {
|
|
3913
4118
|
const p = event.branchProgress;
|
|
3914
4119
|
if (p.completed === 0) {
|
|
3915
|
-
return /* @__PURE__ */
|
|
4120
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "green" }, "assistant", " "), /* @__PURE__ */ React4.createElement(Text3, { color: "blue" }, "\u{1F500} launching ", p.total, " parallel samples (R1 thinking in parallel)\u2026", " "), /* @__PURE__ */ React4.createElement(Elapsed, null)), /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, " ", "spread across T=0.0/0.5/1.0 \xB7 typical wait 30-90s for reasoner"));
|
|
3916
4121
|
}
|
|
3917
4122
|
const pct2 = Math.round(p.completed / p.total * 100);
|
|
3918
|
-
return /* @__PURE__ */
|
|
4123
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "green" }, "assistant", " "), /* @__PURE__ */ React4.createElement(Text3, { color: "blue" }, "\u{1F500} branching ", p.completed, "/", p.total, " (", pct2, "%)", " "), /* @__PURE__ */ React4.createElement(Elapsed, null)), /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, " latest #", p.latestIndex, " T=", p.latestTemperature.toFixed(1), " u=", p.latestUncertainties, p.completed < p.total ? " \xB7 waiting for other samples\u2026" : " \xB7 selecting winner\u2026"));
|
|
3919
4124
|
}
|
|
3920
4125
|
const tail = lastLine(event.text, 140);
|
|
3921
4126
|
const reasoningTail = event.reasoning ? lastLine(event.reasoning, 120) : "";
|
|
@@ -3943,16 +4148,12 @@ function StreamingAssistant({ event }) {
|
|
|
3943
4148
|
label = parts.join(" \xB7 ");
|
|
3944
4149
|
labelColor = "green";
|
|
3945
4150
|
}
|
|
3946
|
-
return /* @__PURE__ */
|
|
4151
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "green" }, "assistant", " "), /* @__PURE__ */ React4.createElement(Pulse, null), /* @__PURE__ */ React4.createElement(Text3, { color: labelColor }, ` ${label} `), /* @__PURE__ */ React4.createElement(Elapsed, null)), reasoningTail ? /* @__PURE__ */ React4.createElement(Text3, { dimColor: true, italic: true }, "\u21B3 thinking: ", reasoningTail) : null, tail ? /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, "\u25B8 ", tail) : preFirstByte ? /* @__PURE__ */ React4.createElement(Text3, { dimColor: true, italic: true }, " connection open, first byte typically in 5-60s depending on model + load") : reasoningOnly ? /* @__PURE__ */ React4.createElement(Text3, { color: "yellow", dimColor: true }, " R1 is thinking before it speaks \u2014 body text starts when reasoning completes (typically 20-90s).") : toolCallOnly ? /* @__PURE__ */ React4.createElement(Text3, { color: "magenta", dimColor: true }, " tool-call arguments streaming \u2014 the model is about to dispatch a tool") : event.reasoning ? /* @__PURE__ */ React4.createElement(Text3, { color: "yellow", dimColor: true }, " R1 still reasoning \u2014 body text or tool call arrives when thinking completes") : null);
|
|
3947
4152
|
}
|
|
3948
4153
|
function Pulse() {
|
|
3949
|
-
const
|
|
3950
|
-
useEffect(() => {
|
|
3951
|
-
const id = setInterval(() => setTick((t) => t + 1), 500);
|
|
3952
|
-
return () => clearInterval(id);
|
|
3953
|
-
}, []);
|
|
4154
|
+
const tick = useTick();
|
|
3954
4155
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3955
|
-
return /* @__PURE__ */
|
|
4156
|
+
return /* @__PURE__ */ React4.createElement(Text3, { color: "cyan" }, frames[Math.floor(tick / 4) % frames.length]);
|
|
3956
4157
|
}
|
|
3957
4158
|
function lastLine(s, maxChars) {
|
|
3958
4159
|
const flat = s.replace(/\s+/g, " ").trim();
|
|
@@ -3961,7 +4162,7 @@ function lastLine(s, maxChars) {
|
|
|
3961
4162
|
}
|
|
3962
4163
|
function StatsLine({ stats }) {
|
|
3963
4164
|
const hit = (stats.cacheHitRatio * 100).toFixed(1);
|
|
3964
|
-
return /* @__PURE__ */
|
|
4165
|
+
return /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, " \u21B3 cache ", hit, "% \xB7 tokens ", stats.usage.promptTokens, "\u2192", stats.usage.completionTokens, " \xB7 $", stats.cost.toFixed(6));
|
|
3965
4166
|
}
|
|
3966
4167
|
function truncate2(s, max) {
|
|
3967
4168
|
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
|
|
@@ -3969,40 +4170,119 @@ function truncate2(s, max) {
|
|
|
3969
4170
|
|
|
3970
4171
|
// src/cli/ui/PromptInput.tsx
|
|
3971
4172
|
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
3972
|
-
import
|
|
4173
|
+
import React5, { useRef, useState as useState2 } from "react";
|
|
3973
4174
|
|
|
3974
4175
|
// src/cli/ui/multiline-keys.ts
|
|
3975
4176
|
var BACKSLASH_SUFFIX = /\\$/;
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4177
|
+
var NOOP = { next: null, cursor: null, submit: false };
|
|
4178
|
+
function processMultilineKey(value, cursor, key) {
|
|
4179
|
+
if (key.tab || key.escape || key.pageUp || key.pageDown) {
|
|
4180
|
+
return NOOP;
|
|
4181
|
+
}
|
|
4182
|
+
if (value.length === 0 && (key.upArrow || key.downArrow)) {
|
|
4183
|
+
return NOOP;
|
|
4184
|
+
}
|
|
4185
|
+
if (key.leftArrow) {
|
|
4186
|
+
return { next: null, cursor: Math.max(0, cursor - 1), submit: false };
|
|
4187
|
+
}
|
|
4188
|
+
if (key.rightArrow) {
|
|
4189
|
+
return { next: null, cursor: Math.min(value.length, cursor + 1), submit: false };
|
|
4190
|
+
}
|
|
4191
|
+
if (key.upArrow) {
|
|
4192
|
+
const moved = moveCursorUp(value, cursor);
|
|
4193
|
+
return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
|
|
4194
|
+
}
|
|
4195
|
+
if (key.downArrow) {
|
|
4196
|
+
const moved = moveCursorDown(value, cursor);
|
|
4197
|
+
return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
|
|
4198
|
+
}
|
|
4199
|
+
if (key.ctrl && key.input === "a") {
|
|
4200
|
+
return { next: null, cursor: startOfLine(value, cursor), submit: false };
|
|
4201
|
+
}
|
|
4202
|
+
if (key.ctrl && key.input === "e") {
|
|
4203
|
+
return { next: null, cursor: endOfLine(value, cursor), submit: false };
|
|
3979
4204
|
}
|
|
3980
4205
|
if (key.input === "\n" || key.ctrl && key.input === "j") {
|
|
3981
|
-
return
|
|
3982
|
-
`, submit: false };
|
|
4206
|
+
return insertAt(value, cursor, "\n");
|
|
3983
4207
|
}
|
|
3984
4208
|
if (key.return) {
|
|
3985
|
-
if (key.shift)
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
return { next: `${value.slice(0, -1)}
|
|
3991
|
-
`, submit: false };
|
|
4209
|
+
if (key.shift) return insertAt(value, cursor, "\n");
|
|
4210
|
+
if (cursor === value.length && BACKSLASH_SUFFIX.test(value)) {
|
|
4211
|
+
const replaced = `${value.slice(0, -1)}
|
|
4212
|
+
`;
|
|
4213
|
+
return { next: replaced, cursor: replaced.length, submit: false };
|
|
3992
4214
|
}
|
|
3993
|
-
return { next: null, submit: true, submitValue: value };
|
|
4215
|
+
return { next: null, cursor: null, submit: true, submitValue: value };
|
|
3994
4216
|
}
|
|
3995
|
-
if (key.backspace
|
|
3996
|
-
if (
|
|
3997
|
-
return {
|
|
4217
|
+
if (key.backspace) {
|
|
4218
|
+
if (cursor === 0) return NOOP;
|
|
4219
|
+
return {
|
|
4220
|
+
next: value.slice(0, cursor - 1) + value.slice(cursor),
|
|
4221
|
+
cursor: cursor - 1,
|
|
4222
|
+
submit: false
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
if (key.delete) {
|
|
4226
|
+
if (cursor === value.length) return NOOP;
|
|
4227
|
+
return {
|
|
4228
|
+
next: value.slice(0, cursor) + value.slice(cursor + 1),
|
|
4229
|
+
cursor,
|
|
4230
|
+
submit: false
|
|
4231
|
+
};
|
|
3998
4232
|
}
|
|
3999
|
-
if ((key.ctrl || key.meta) && key.input.length === 0)
|
|
4000
|
-
|
|
4233
|
+
if ((key.ctrl || key.meta) && key.input.length === 0) return NOOP;
|
|
4234
|
+
if (key.ctrl || key.meta) return NOOP;
|
|
4235
|
+
if (key.input.length > 0) {
|
|
4236
|
+
return insertAt(value, cursor, key.input);
|
|
4001
4237
|
}
|
|
4002
|
-
|
|
4003
|
-
|
|
4238
|
+
return NOOP;
|
|
4239
|
+
}
|
|
4240
|
+
function insertAt(value, cursor, insert) {
|
|
4241
|
+
return {
|
|
4242
|
+
next: value.slice(0, cursor) + insert + value.slice(cursor),
|
|
4243
|
+
cursor: cursor + insert.length,
|
|
4244
|
+
submit: false
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
function lineAndColumn(value, cursor) {
|
|
4248
|
+
let line = 0;
|
|
4249
|
+
let col = 0;
|
|
4250
|
+
const n = Math.min(cursor, value.length);
|
|
4251
|
+
for (let i = 0; i < n; i++) {
|
|
4252
|
+
if (value[i] === "\n") {
|
|
4253
|
+
line++;
|
|
4254
|
+
col = 0;
|
|
4255
|
+
} else {
|
|
4256
|
+
col++;
|
|
4257
|
+
}
|
|
4004
4258
|
}
|
|
4005
|
-
return {
|
|
4259
|
+
return { line, col };
|
|
4260
|
+
}
|
|
4261
|
+
function startOfLine(value, cursor) {
|
|
4262
|
+
return value.lastIndexOf("\n", cursor - 1) + 1;
|
|
4263
|
+
}
|
|
4264
|
+
function endOfLine(value, cursor) {
|
|
4265
|
+
const nl = value.indexOf("\n", cursor);
|
|
4266
|
+
return nl === -1 ? value.length : nl;
|
|
4267
|
+
}
|
|
4268
|
+
function moveCursorUp(value, cursor) {
|
|
4269
|
+
const curStart = startOfLine(value, cursor);
|
|
4270
|
+
if (curStart === 0) return cursor;
|
|
4271
|
+
const col = cursor - curStart;
|
|
4272
|
+
const prevEnd = curStart - 1;
|
|
4273
|
+
const prevStart = value.lastIndexOf("\n", prevEnd - 1) + 1;
|
|
4274
|
+
const prevLen = prevEnd - prevStart;
|
|
4275
|
+
return prevStart + Math.min(col, prevLen);
|
|
4276
|
+
}
|
|
4277
|
+
function moveCursorDown(value, cursor) {
|
|
4278
|
+
const nextNl = value.indexOf("\n", cursor);
|
|
4279
|
+
if (nextNl === -1) return cursor;
|
|
4280
|
+
const curStart = startOfLine(value, cursor);
|
|
4281
|
+
const col = cursor - curStart;
|
|
4282
|
+
const nextStart = nextNl + 1;
|
|
4283
|
+
const followingNl = value.indexOf("\n", nextStart);
|
|
4284
|
+
const nextLen = (followingNl === -1 ? value.length : followingNl) - nextStart;
|
|
4285
|
+
return nextStart + Math.min(col, nextLen);
|
|
4006
4286
|
}
|
|
4007
4287
|
|
|
4008
4288
|
// src/cli/ui/PromptInput.tsx
|
|
@@ -4013,19 +4293,19 @@ function PromptInput({
|
|
|
4013
4293
|
disabled,
|
|
4014
4294
|
placeholder
|
|
4015
4295
|
}) {
|
|
4016
|
-
const [
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4296
|
+
const [cursor, setCursor] = useState2(value.length);
|
|
4297
|
+
const lastLocalValueRef = useRef(value);
|
|
4298
|
+
if (value !== lastLocalValueRef.current) {
|
|
4299
|
+
lastLocalValueRef.current = value;
|
|
4300
|
+
if (cursor !== value.length) {
|
|
4301
|
+
setCursor(value.length);
|
|
4021
4302
|
}
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
}, [disabled]);
|
|
4303
|
+
}
|
|
4304
|
+
const tick = useTick();
|
|
4305
|
+
const showCursor = disabled ? false : Math.floor(tick / 4) % 2 === 0;
|
|
4026
4306
|
useInput(
|
|
4027
4307
|
(input, key) => {
|
|
4028
|
-
const
|
|
4308
|
+
const ke = {
|
|
4029
4309
|
input,
|
|
4030
4310
|
return: key.return,
|
|
4031
4311
|
shift: key.shift,
|
|
@@ -4042,8 +4322,14 @@ function PromptInput({
|
|
|
4042
4322
|
pageUp: key.pageUp,
|
|
4043
4323
|
pageDown: key.pageDown
|
|
4044
4324
|
};
|
|
4045
|
-
const action = processMultilineKey(value,
|
|
4046
|
-
if (action.next !== null)
|
|
4325
|
+
const action = processMultilineKey(value, cursor, ke);
|
|
4326
|
+
if (action.next !== null) {
|
|
4327
|
+
lastLocalValueRef.current = action.next;
|
|
4328
|
+
onChange(action.next);
|
|
4329
|
+
}
|
|
4330
|
+
if (action.cursor !== null) {
|
|
4331
|
+
setCursor(action.cursor);
|
|
4332
|
+
}
|
|
4047
4333
|
if (action.submit) onSubmit(action.submitValue ?? value);
|
|
4048
4334
|
},
|
|
4049
4335
|
{ isActive: !disabled }
|
|
@@ -4051,27 +4337,50 @@ function PromptInput({
|
|
|
4051
4337
|
const effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026" : placeholder ?? "type a message, or /command \xB7 Ctrl+J for newline";
|
|
4052
4338
|
const lines = value.length > 0 ? value.split("\n") : [""];
|
|
4053
4339
|
const borderColor = disabled ? "gray" : "cyan";
|
|
4054
|
-
|
|
4055
|
-
|
|
4340
|
+
const { line: cursorLine, col: cursorCol } = lineAndColumn(value, cursor);
|
|
4341
|
+
return /* @__PURE__ */ React5.createElement(Box4, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "column" }, lines.map((line, i) => {
|
|
4056
4342
|
const isFirst = i === 0;
|
|
4057
4343
|
const showPlaceholder = isFirst && value.length === 0;
|
|
4344
|
+
const isCursorLine = i === cursorLine;
|
|
4058
4345
|
return (
|
|
4059
4346
|
// biome-ignore lint/suspicious/noArrayIndexKey: stable by construction — lines are derived from `value.split("\n")` and never reordered
|
|
4060
|
-
/* @__PURE__ */
|
|
4347
|
+
/* @__PURE__ */ React5.createElement(Box4, { key: i }, isFirst ? /* @__PURE__ */ React5.createElement(Text4, { bold: true, color: borderColor }, "you \u203A", " ") : /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, " "), showPlaceholder ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, isCursorLine && !disabled ? /* @__PURE__ */ React5.createElement(Text4, { color: borderColor }, showCursor ? "\u258C" : " ") : null, /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, effectivePlaceholder)) : isCursorLine && !disabled ? /* @__PURE__ */ React5.createElement(
|
|
4348
|
+
LineWithCursor,
|
|
4349
|
+
{
|
|
4350
|
+
line,
|
|
4351
|
+
col: cursorCol,
|
|
4352
|
+
showCursor,
|
|
4353
|
+
borderColor
|
|
4354
|
+
}
|
|
4355
|
+
) : /* @__PURE__ */ React5.createElement(Text4, null, line))
|
|
4061
4356
|
);
|
|
4062
4357
|
}));
|
|
4063
4358
|
}
|
|
4359
|
+
function LineWithCursor({
|
|
4360
|
+
line,
|
|
4361
|
+
col,
|
|
4362
|
+
showCursor,
|
|
4363
|
+
borderColor
|
|
4364
|
+
}) {
|
|
4365
|
+
const before = line.slice(0, col);
|
|
4366
|
+
const atCursor = line.slice(col, col + 1);
|
|
4367
|
+
const after = line.slice(col + 1);
|
|
4368
|
+
if (atCursor.length === 0) {
|
|
4369
|
+
return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text4, null, before), /* @__PURE__ */ React5.createElement(Text4, { color: borderColor }, showCursor ? "\u258C" : " "));
|
|
4370
|
+
}
|
|
4371
|
+
return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text4, null, before), /* @__PURE__ */ React5.createElement(Text4, { inverse: showCursor }, atCursor), /* @__PURE__ */ React5.createElement(Text4, null, after));
|
|
4372
|
+
}
|
|
4064
4373
|
|
|
4065
4374
|
// src/cli/ui/SlashSuggestions.tsx
|
|
4066
4375
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
4067
|
-
import
|
|
4376
|
+
import React6 from "react";
|
|
4068
4377
|
function SlashSuggestions({
|
|
4069
4378
|
matches,
|
|
4070
4379
|
selectedIndex
|
|
4071
4380
|
}) {
|
|
4072
4381
|
if (matches === null) return null;
|
|
4073
4382
|
if (matches.length === 0) {
|
|
4074
|
-
return /* @__PURE__ */
|
|
4383
|
+
return /* @__PURE__ */ React6.createElement(Box5, { paddingX: 1 }, /* @__PURE__ */ React6.createElement(Text5, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
|
|
4075
4384
|
}
|
|
4076
4385
|
const MAX = 8;
|
|
4077
4386
|
const total = matches.length;
|
|
@@ -4079,21 +4388,21 @@ function SlashSuggestions({
|
|
|
4079
4388
|
const shown = matches.slice(windowStart, windowStart + MAX);
|
|
4080
4389
|
const hiddenAbove = windowStart;
|
|
4081
4390
|
const hiddenBelow = total - windowStart - shown.length;
|
|
4082
|
-
return /* @__PURE__ */
|
|
4391
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React6.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, " \u2191/\u2193 navigate \xB7 Tab or Enter to pick"));
|
|
4083
4392
|
}
|
|
4084
4393
|
function SuggestionRow({ spec, isSelected }) {
|
|
4085
4394
|
const marker = isSelected ? "\u25B8" : " ";
|
|
4086
4395
|
const name = `/${spec.cmd}`;
|
|
4087
4396
|
const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
|
|
4088
4397
|
if (isSelected) {
|
|
4089
|
-
return /* @__PURE__ */
|
|
4398
|
+
return /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text5, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React6.createElement(Text5, { color: "cyan" }, " ", spec.summary));
|
|
4090
4399
|
}
|
|
4091
|
-
return /* @__PURE__ */
|
|
4400
|
+
return /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
|
|
4092
4401
|
}
|
|
4093
4402
|
|
|
4094
4403
|
// src/cli/ui/StatsPanel.tsx
|
|
4095
4404
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
4096
|
-
import
|
|
4405
|
+
import React7 from "react";
|
|
4097
4406
|
function StatsPanel({
|
|
4098
4407
|
summary,
|
|
4099
4408
|
model,
|
|
@@ -4108,7 +4417,7 @@ function StatsPanel({
|
|
|
4108
4417
|
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
4109
4418
|
const ctxRatio = summary.lastPromptTokens / ctxMax;
|
|
4110
4419
|
const ctxColor = ctxRatio >= 0.8 ? "red" : ctxRatio >= 0.5 ? "yellow" : void 0;
|
|
4111
|
-
return /* @__PURE__ */
|
|
4420
|
+
return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Box6, { justifyContent: "space-between" }, /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { color: "cyan", bold: true }, "Reasonix"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " \xB7 model "), /* @__PURE__ */ React7.createElement(Text6, { color: "yellow" }, model), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " \xB7 prefix "), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, prefixHash), harvestOn ? /* @__PURE__ */ React7.createElement(Text6, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React7.createElement(Text6, { color: "blue" }, " \xB7 branch", branchBudget) : null), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "turns ", summary.turns, " \xB7 type /help")), /* @__PURE__ */ React7.createElement(Box6, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "cache hit "), /* @__PURE__ */ React7.createElement(Text6, { color: hitColor, bold: true }, hitPct, "%")), /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "cost "), /* @__PURE__ */ React7.createElement(Text6, { color: "green", bold: true }, "$", summary.totalCostUsd.toFixed(6)), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " (in ", "$", summary.totalInputCostUsd.toFixed(6), " \xB7 out ", "$", summary.totalOutputCostUsd.toFixed(6), ")")), summary.lastPromptTokens > 0 ? /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "ctx "), /* @__PURE__ */ React7.createElement(Text6, { color: ctxColor, bold: ctxColor !== void 0 }, formatTokens(summary.lastPromptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " (", (ctxRatio * 100).toFixed(0), "%)"), ctxRatio >= 0.8 ? /* @__PURE__ */ React7.createElement(Text6, { color: "red", bold: true }, " ", "\xB7 /compact") : null) : null, balance ? /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "balance "), /* @__PURE__ */ React7.createElement(Text6, { color: balance.total < 1 ? "red" : balance.total < 5 ? "yellow" : "green", bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : "")) : null));
|
|
4112
4421
|
}
|
|
4113
4422
|
function formatTokens(n) {
|
|
4114
4423
|
if (n < 1e3) return String(n);
|
|
@@ -4556,7 +4865,8 @@ function gitTail(res) {
|
|
|
4556
4865
|
}
|
|
4557
4866
|
|
|
4558
4867
|
// src/cli/ui/App.tsx
|
|
4559
|
-
var FLUSH_INTERVAL_MS =
|
|
4868
|
+
var FLUSH_INTERVAL_MS = 100;
|
|
4869
|
+
var PLAIN_UI = process.env.REASONIX_UI === "plain";
|
|
4560
4870
|
function App({
|
|
4561
4871
|
model,
|
|
4562
4872
|
system,
|
|
@@ -4575,17 +4885,17 @@ function App({
|
|
|
4575
4885
|
const [streaming, setStreaming] = useState3(null);
|
|
4576
4886
|
const [input, setInput] = useState3("");
|
|
4577
4887
|
const [busy, setBusy] = useState3(false);
|
|
4578
|
-
const abortedThisTurn =
|
|
4888
|
+
const abortedThisTurn = useRef2(false);
|
|
4579
4889
|
const [ongoingTool, setOngoingTool] = useState3(null);
|
|
4580
4890
|
const [toolProgress, setToolProgress] = useState3(null);
|
|
4581
4891
|
const [statusLine, setStatusLine] = useState3(null);
|
|
4582
4892
|
const [balance, setBalance] = useState3(null);
|
|
4583
|
-
const lastEditSnapshots =
|
|
4584
|
-
const pendingEdits =
|
|
4585
|
-
const promptHistory =
|
|
4586
|
-
const historyCursor =
|
|
4587
|
-
const assistantIterCounter =
|
|
4588
|
-
const toolHistoryRef =
|
|
4893
|
+
const lastEditSnapshots = useRef2(null);
|
|
4894
|
+
const pendingEdits = useRef2([]);
|
|
4895
|
+
const promptHistory = useRef2([]);
|
|
4896
|
+
const historyCursor = useRef2(-1);
|
|
4897
|
+
const assistantIterCounter = useRef2(0);
|
|
4898
|
+
const toolHistoryRef = useRef2([]);
|
|
4589
4899
|
const [slashSelected, setSlashSelected] = useState3(0);
|
|
4590
4900
|
const [summary, setSummary] = useState3({
|
|
4591
4901
|
turns: 0,
|
|
@@ -4597,7 +4907,7 @@ function App({
|
|
|
4597
4907
|
cacheHitRatio: 0,
|
|
4598
4908
|
lastPromptTokens: 0
|
|
4599
4909
|
});
|
|
4600
|
-
const transcriptRef =
|
|
4910
|
+
const transcriptRef = useRef2(null);
|
|
4601
4911
|
if (transcript && !transcriptRef.current) {
|
|
4602
4912
|
transcriptRef.current = openTranscriptFile(transcript, {
|
|
4603
4913
|
version: 1,
|
|
@@ -4606,7 +4916,7 @@ function App({
|
|
|
4606
4916
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4607
4917
|
});
|
|
4608
4918
|
}
|
|
4609
|
-
|
|
4919
|
+
useEffect2(() => {
|
|
4610
4920
|
return () => {
|
|
4611
4921
|
transcriptRef.current?.end();
|
|
4612
4922
|
};
|
|
@@ -4615,14 +4925,14 @@ function App({
|
|
|
4615
4925
|
if (!input.startsWith("/") || input.includes(" ")) return null;
|
|
4616
4926
|
return suggestSlashCommands(input.slice(1), !!codeMode);
|
|
4617
4927
|
}, [input, codeMode]);
|
|
4618
|
-
|
|
4928
|
+
useEffect2(() => {
|
|
4619
4929
|
setSlashSelected((prev) => {
|
|
4620
4930
|
if (!slashMatches || slashMatches.length === 0) return 0;
|
|
4621
4931
|
if (prev >= slashMatches.length) return slashMatches.length - 1;
|
|
4622
4932
|
return prev;
|
|
4623
4933
|
});
|
|
4624
4934
|
}, [slashMatches]);
|
|
4625
|
-
const loopRef =
|
|
4935
|
+
const loopRef = useRef2(null);
|
|
4626
4936
|
const loop = useMemo(() => {
|
|
4627
4937
|
if (loopRef.current) return loopRef.current;
|
|
4628
4938
|
const client = new DeepSeekClient();
|
|
@@ -4634,7 +4944,7 @@ function App({
|
|
|
4634
4944
|
loopRef.current = l;
|
|
4635
4945
|
return l;
|
|
4636
4946
|
}, [model, system, harvest2, branch, session, tools]);
|
|
4637
|
-
|
|
4947
|
+
useEffect2(() => {
|
|
4638
4948
|
let cancelled = false;
|
|
4639
4949
|
void (async () => {
|
|
4640
4950
|
const bal = await loop.client.getBalance().catch(() => null);
|
|
@@ -4646,7 +4956,7 @@ function App({
|
|
|
4646
4956
|
cancelled = true;
|
|
4647
4957
|
};
|
|
4648
4958
|
}, [loop]);
|
|
4649
|
-
|
|
4959
|
+
useEffect2(() => {
|
|
4650
4960
|
if (!progressSink) return;
|
|
4651
4961
|
progressSink.current = (info) => {
|
|
4652
4962
|
setToolProgress({
|
|
@@ -4659,8 +4969,8 @@ function App({
|
|
|
4659
4969
|
if (progressSink.current) progressSink.current = null;
|
|
4660
4970
|
};
|
|
4661
4971
|
}, [progressSink]);
|
|
4662
|
-
const sessionBannerShown =
|
|
4663
|
-
|
|
4972
|
+
const sessionBannerShown = useRef2(false);
|
|
4973
|
+
useEffect2(() => {
|
|
4664
4974
|
if (sessionBannerShown.current) return;
|
|
4665
4975
|
sessionBannerShown.current = true;
|
|
4666
4976
|
if (!session) {
|
|
@@ -4715,20 +5025,22 @@ function App({
|
|
|
4715
5025
|
return;
|
|
4716
5026
|
}
|
|
4717
5027
|
}
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
if (
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
if (
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
5028
|
+
if (input.length === 0) {
|
|
5029
|
+
const hist = promptHistory.current;
|
|
5030
|
+
if (key.upArrow) {
|
|
5031
|
+
if (hist.length === 0) return;
|
|
5032
|
+
const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
|
|
5033
|
+
historyCursor.current = nextCursor;
|
|
5034
|
+
setInput(hist[hist.length - 1 - nextCursor] ?? "");
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
5037
|
+
if (key.downArrow) {
|
|
5038
|
+
if (historyCursor.current < 0) return;
|
|
5039
|
+
const nextCursor = historyCursor.current - 1;
|
|
5040
|
+
historyCursor.current = nextCursor;
|
|
5041
|
+
setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
|
|
5042
|
+
return;
|
|
5043
|
+
}
|
|
4732
5044
|
}
|
|
4733
5045
|
});
|
|
4734
5046
|
const codeUndo = useCallback(() => {
|
|
@@ -4869,7 +5181,7 @@ function App({
|
|
|
4869
5181
|
streaming: true
|
|
4870
5182
|
});
|
|
4871
5183
|
};
|
|
4872
|
-
const timer = setInterval(flush, FLUSH_INTERVAL_MS);
|
|
5184
|
+
const timer = PLAIN_UI ? null : setInterval(flush, FLUSH_INTERVAL_MS);
|
|
4873
5185
|
try {
|
|
4874
5186
|
for await (const ev of loop.step(text)) {
|
|
4875
5187
|
writeTranscript(ev);
|
|
@@ -4981,7 +5293,7 @@ function App({
|
|
|
4981
5293
|
}
|
|
4982
5294
|
flush();
|
|
4983
5295
|
} finally {
|
|
4984
|
-
clearInterval(timer);
|
|
5296
|
+
if (timer) clearInterval(timer);
|
|
4985
5297
|
setStreaming(null);
|
|
4986
5298
|
setOngoingTool(null);
|
|
4987
5299
|
setToolProgress(null);
|
|
@@ -5011,7 +5323,7 @@ function App({
|
|
|
5011
5323
|
writeTranscript
|
|
5012
5324
|
]
|
|
5013
5325
|
);
|
|
5014
|
-
return /* @__PURE__ */
|
|
5326
|
+
return /* @__PURE__ */ React8.createElement(TickerProvider, { disabled: PLAIN_UI }, /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(
|
|
5015
5327
|
StatsPanel,
|
|
5016
5328
|
{
|
|
5017
5329
|
summary,
|
|
@@ -5021,41 +5333,22 @@ function App({
|
|
|
5021
5333
|
branchBudget: loop.branchOptions.budget,
|
|
5022
5334
|
balance
|
|
5023
5335
|
}
|
|
5024
|
-
), /* @__PURE__ */
|
|
5336
|
+
), /* @__PURE__ */ React8.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React8.createElement(EventRow, { key: item.id, event: item })), !PLAIN_UI && streaming ? /* @__PURE__ */ React8.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React8.createElement(EventRow, { event: streaming })) : null, !PLAIN_UI && ongoingTool ? /* @__PURE__ */ React8.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !ongoingTool && statusLine ? /* @__PURE__ */ React8.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React8.createElement(StatusRow, { text: "processing\u2026" }) : null, /* @__PURE__ */ React8.createElement(PromptInput, { value: input, onChange: setInput, onSubmit: handleSubmit, disabled: busy }), /* @__PURE__ */ React8.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected })));
|
|
5025
5337
|
}
|
|
5338
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5026
5339
|
function StatusRow({ text }) {
|
|
5027
|
-
const
|
|
5028
|
-
const
|
|
5029
|
-
|
|
5030
|
-
const start = Date.now();
|
|
5031
|
-
const frameId = setInterval(() => setTick((t) => t + 1), 120);
|
|
5032
|
-
const secId = setInterval(() => setElapsed(Math.floor((Date.now() - start) / 1e3)), 1e3);
|
|
5033
|
-
return () => {
|
|
5034
|
-
clearInterval(frameId);
|
|
5035
|
-
clearInterval(secId);
|
|
5036
|
-
};
|
|
5037
|
-
}, []);
|
|
5038
|
-
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5039
|
-
return /* @__PURE__ */ React7.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "magenta" }, frames[tick % frames.length]), /* @__PURE__ */ React7.createElement(Text7, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, ` ${elapsed}s`));
|
|
5340
|
+
const tick = useTick();
|
|
5341
|
+
const elapsed = useElapsedSeconds();
|
|
5342
|
+
return /* @__PURE__ */ React8.createElement(Box7, { marginY: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React8.createElement(Text7, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` ${elapsed}s`));
|
|
5040
5343
|
}
|
|
5041
5344
|
function OngoingToolRow({
|
|
5042
5345
|
tool,
|
|
5043
5346
|
progress
|
|
5044
5347
|
}) {
|
|
5045
|
-
const
|
|
5046
|
-
const
|
|
5047
|
-
useEffect3(() => {
|
|
5048
|
-
const start = Date.now();
|
|
5049
|
-
const frameId = setInterval(() => setTick((t) => t + 1), 120);
|
|
5050
|
-
const secId = setInterval(() => setElapsed(Math.floor((Date.now() - start) / 1e3)), 1e3);
|
|
5051
|
-
return () => {
|
|
5052
|
-
clearInterval(frameId);
|
|
5053
|
-
clearInterval(secId);
|
|
5054
|
-
};
|
|
5055
|
-
}, []);
|
|
5056
|
-
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5348
|
+
const tick = useTick();
|
|
5349
|
+
const elapsed = useElapsedSeconds();
|
|
5057
5350
|
const summary = summarizeToolArgs(tool.name, tool.args);
|
|
5058
|
-
return /* @__PURE__ */
|
|
5351
|
+
return /* @__PURE__ */ React8.createElement(Box7, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2 }, /* @__PURE__ */ React8.createElement(Text7, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, summary)) : null);
|
|
5059
5352
|
}
|
|
5060
5353
|
function renderProgressLine(p) {
|
|
5061
5354
|
const msg = p.message ? ` ${p.message}` : "";
|
|
@@ -5153,7 +5446,7 @@ function describeRepair(repair) {
|
|
|
5153
5446
|
// src/cli/ui/Setup.tsx
|
|
5154
5447
|
import { Box as Box8, Text as Text8, useApp as useApp2 } from "ink";
|
|
5155
5448
|
import TextInput from "ink-text-input";
|
|
5156
|
-
import
|
|
5449
|
+
import React9, { useState as useState4 } from "react";
|
|
5157
5450
|
function Setup({ onReady }) {
|
|
5158
5451
|
const [value, setValue] = useState4("");
|
|
5159
5452
|
const [error, setError] = useState4(null);
|
|
@@ -5177,7 +5470,7 @@ function Setup({ onReady }) {
|
|
|
5177
5470
|
}
|
|
5178
5471
|
onReady(trimmed);
|
|
5179
5472
|
};
|
|
5180
|
-
return /* @__PURE__ */
|
|
5473
|
+
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React9.createElement(
|
|
5181
5474
|
TextInput,
|
|
5182
5475
|
{
|
|
5183
5476
|
value,
|
|
@@ -5186,14 +5479,14 @@ function Setup({ onReady }) {
|
|
|
5186
5479
|
mask: "\u2022",
|
|
5187
5480
|
placeholder: "sk-..."
|
|
5188
5481
|
}
|
|
5189
|
-
)), error ? /* @__PURE__ */
|
|
5482
|
+
)), error ? /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, error)) : value ? /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "(Type /exit to abort.)")));
|
|
5190
5483
|
}
|
|
5191
5484
|
|
|
5192
5485
|
// src/cli/commands/chat.tsx
|
|
5193
5486
|
function Root({ initialKey, tools, mcpSpecs, mcpServers, progressSink, ...appProps }) {
|
|
5194
5487
|
const [key, setKey] = useState5(initialKey);
|
|
5195
5488
|
if (!key) {
|
|
5196
|
-
return /* @__PURE__ */
|
|
5489
|
+
return /* @__PURE__ */ React10.createElement(
|
|
5197
5490
|
Setup,
|
|
5198
5491
|
{
|
|
5199
5492
|
onReady: (k) => {
|
|
@@ -5204,7 +5497,7 @@ function Root({ initialKey, tools, mcpSpecs, mcpServers, progressSink, ...appPro
|
|
|
5204
5497
|
);
|
|
5205
5498
|
}
|
|
5206
5499
|
process.env.DEEPSEEK_API_KEY = key;
|
|
5207
|
-
return /* @__PURE__ */
|
|
5500
|
+
return /* @__PURE__ */ React10.createElement(
|
|
5208
5501
|
App,
|
|
5209
5502
|
{
|
|
5210
5503
|
model: appProps.model,
|
|
@@ -5287,8 +5580,12 @@ async function chatCommand(opts) {
|
|
|
5287
5580
|
}
|
|
5288
5581
|
}
|
|
5289
5582
|
const mcpSpecs = successfulSpecs;
|
|
5583
|
+
if (searchEnabled()) {
|
|
5584
|
+
if (!tools) tools = new ToolRegistry();
|
|
5585
|
+
registerWebTools(tools);
|
|
5586
|
+
}
|
|
5290
5587
|
const { waitUntilExit } = render(
|
|
5291
|
-
/* @__PURE__ */
|
|
5588
|
+
/* @__PURE__ */ React10.createElement(
|
|
5292
5589
|
Root,
|
|
5293
5590
|
{
|
|
5294
5591
|
initialKey,
|
|
@@ -5299,7 +5596,9 @@ async function chatCommand(opts) {
|
|
|
5299
5596
|
...opts
|
|
5300
5597
|
}
|
|
5301
5598
|
),
|
|
5302
|
-
|
|
5599
|
+
// patchConsole:false — we never log to console during the TUI, and the
|
|
5600
|
+
// patch is a known redraw-glitch source on winpty/MINTTY terminals.
|
|
5601
|
+
{ exitOnCtrlC: true, patchConsole: false }
|
|
5303
5602
|
);
|
|
5304
5603
|
try {
|
|
5305
5604
|
await waitUntilExit();
|
|
@@ -5336,34 +5635,34 @@ async function codeCommand(opts = {}) {
|
|
|
5336
5635
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
5337
5636
|
import { basename as basename2 } from "path";
|
|
5338
5637
|
import { render as render2 } from "ink";
|
|
5339
|
-
import
|
|
5638
|
+
import React13 from "react";
|
|
5340
5639
|
|
|
5341
5640
|
// src/cli/ui/DiffApp.tsx
|
|
5342
5641
|
import { Box as Box10, Static as Static2, Text as Text10, useApp as useApp3, useInput as useInput3 } from "ink";
|
|
5343
|
-
import
|
|
5642
|
+
import React12, { useState as useState6 } from "react";
|
|
5344
5643
|
|
|
5345
5644
|
// src/cli/ui/RecordView.tsx
|
|
5346
5645
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
5347
|
-
import
|
|
5646
|
+
import React11 from "react";
|
|
5348
5647
|
function RecordView({ rec, compact = false }) {
|
|
5349
5648
|
const toolArgsMax = compact ? 120 : 200;
|
|
5350
5649
|
const toolContentMax = compact ? 200 : 400;
|
|
5351
5650
|
if (rec.role === "user") {
|
|
5352
|
-
return /* @__PURE__ */
|
|
5651
|
+
return /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text9, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React11.createElement(Text9, null, rec.content));
|
|
5353
5652
|
}
|
|
5354
5653
|
if (rec.role === "assistant_final") {
|
|
5355
|
-
return /* @__PURE__ */
|
|
5654
|
+
return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text9, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React11.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React11.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React11.createElement(Text9, null, rec.content) : /* @__PURE__ */ React11.createElement(Text9, { dimColor: true, italic: true }, "(tool-call response only)"));
|
|
5356
5655
|
}
|
|
5357
5656
|
if (rec.role === "tool") {
|
|
5358
|
-
return /* @__PURE__ */
|
|
5657
|
+
return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
|
|
5359
5658
|
}
|
|
5360
5659
|
if (rec.role === "error") {
|
|
5361
|
-
return /* @__PURE__ */
|
|
5660
|
+
return /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React11.createElement(Text9, { color: "red" }, rec.error ?? rec.content));
|
|
5362
5661
|
}
|
|
5363
5662
|
if (rec.role === "done" || rec.role === "assistant_delta") {
|
|
5364
5663
|
return null;
|
|
5365
5664
|
}
|
|
5366
|
-
return /* @__PURE__ */
|
|
5665
|
+
return /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, "[", rec.role, "] ", rec.content));
|
|
5367
5666
|
}
|
|
5368
5667
|
function CacheBadge({ usage }) {
|
|
5369
5668
|
const hit = usage.prompt_cache_hit_tokens ?? 0;
|
|
@@ -5372,7 +5671,7 @@ function CacheBadge({ usage }) {
|
|
|
5372
5671
|
if (total === 0) return null;
|
|
5373
5672
|
const pct2 = hit / total * 100;
|
|
5374
5673
|
const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
|
|
5375
|
-
return /* @__PURE__ */
|
|
5674
|
+
return /* @__PURE__ */ React11.createElement(Text9, null, /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React11.createElement(Text9, { color }, pct2.toFixed(1), "%"));
|
|
5376
5675
|
}
|
|
5377
5676
|
function truncate3(s, max) {
|
|
5378
5677
|
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
|
|
@@ -5406,7 +5705,7 @@ function DiffApp({ report }) {
|
|
|
5406
5705
|
}
|
|
5407
5706
|
});
|
|
5408
5707
|
const pair = report.pairs[idx];
|
|
5409
|
-
return /* @__PURE__ */
|
|
5708
|
+
return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(DiffHeader, { report }), /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React12.createElement(Text10, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React12.createElement(Text10, null, pair ? /* @__PURE__ */ React12.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React12.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React12.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text10, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React12.createElement(Text10, null, pair.divergenceNote)) : null, /* @__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"), " next \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "k"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "N"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "g"), "/", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React12.createElement(Text10, { bold: true }, "q"), " ", "quit")));
|
|
5410
5709
|
}
|
|
5411
5710
|
function DiffHeader({ report }) {
|
|
5412
5711
|
const a = report.a;
|
|
@@ -5424,14 +5723,14 @@ function DiffHeader({ report }) {
|
|
|
5424
5723
|
} else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
|
|
5425
5724
|
prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
|
|
5426
5725
|
}
|
|
5427
|
-
return /* @__PURE__ */
|
|
5726
|
+
return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Box10, { justifyContent: "space-between" }, /* @__PURE__ */ React12.createElement(Text10, null, /* @__PURE__ */ React12.createElement(Text10, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React12.createElement(Text10, { color: "blue" }, a.label), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, " vs B="), /* @__PURE__ */ React12.createElement(Text10, { color: "magenta" }, b.label)), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React12.createElement(Text10, null, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, "cache "), /* @__PURE__ */ React12.createElement(Text10, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React12.createElement(Text10, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React12.createElement(Text10, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React12.createElement(Text10, null, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, "cost "), /* @__PURE__ */ React12.createElement(Text10, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React12.createElement(Text10, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React12.createElement(Text10, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React12.createElement(Text10, null, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, "model calls "), /* @__PURE__ */ React12.createElement(Text10, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true, italic: true }, prefixLine)) : null);
|
|
5428
5727
|
}
|
|
5429
5728
|
function Pane({
|
|
5430
5729
|
label,
|
|
5431
5730
|
headerColor,
|
|
5432
5731
|
records
|
|
5433
5732
|
}) {
|
|
5434
|
-
return /* @__PURE__ */
|
|
5733
|
+
return /* @__PURE__ */ React12.createElement(
|
|
5435
5734
|
Box10,
|
|
5436
5735
|
{
|
|
5437
5736
|
flexDirection: "column",
|
|
@@ -5440,21 +5739,21 @@ function Pane({
|
|
|
5440
5739
|
borderStyle: "single",
|
|
5441
5740
|
borderColor: headerColor
|
|
5442
5741
|
},
|
|
5443
|
-
/* @__PURE__ */
|
|
5444
|
-
records.length === 0 ? /* @__PURE__ */
|
|
5742
|
+
/* @__PURE__ */ React12.createElement(Text10, { color: headerColor, bold: true }, label),
|
|
5743
|
+
records.length === 0 ? /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text10, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React12.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React12.createElement(RecordView, { key, rec, compact: true }))
|
|
5445
5744
|
);
|
|
5446
5745
|
}
|
|
5447
5746
|
function KindBadge({ kind }) {
|
|
5448
5747
|
if (kind === "match") {
|
|
5449
|
-
return /* @__PURE__ */
|
|
5748
|
+
return /* @__PURE__ */ React12.createElement(Text10, { color: "green" }, "\u2713 match");
|
|
5450
5749
|
}
|
|
5451
5750
|
if (kind === "diverge") {
|
|
5452
|
-
return /* @__PURE__ */
|
|
5751
|
+
return /* @__PURE__ */ React12.createElement(Text10, { color: "yellow" }, "\u2605 diverge");
|
|
5453
5752
|
}
|
|
5454
5753
|
if (kind === "only_in_a") {
|
|
5455
|
-
return /* @__PURE__ */
|
|
5754
|
+
return /* @__PURE__ */ React12.createElement(Text10, { color: "blue" }, "\u2190 only in A");
|
|
5456
5755
|
}
|
|
5457
|
-
return /* @__PURE__ */
|
|
5756
|
+
return /* @__PURE__ */ React12.createElement(Text10, { color: "magenta" }, "\u2192 only in B");
|
|
5458
5757
|
}
|
|
5459
5758
|
function paneRecords(pair, side) {
|
|
5460
5759
|
if (!pair) return [];
|
|
@@ -5485,8 +5784,9 @@ markdown report written to ${opts.mdPath}`);
|
|
|
5485
5784
|
return;
|
|
5486
5785
|
}
|
|
5487
5786
|
if (wantTui) {
|
|
5488
|
-
const { waitUntilExit } = render2(
|
|
5489
|
-
exitOnCtrlC: true
|
|
5787
|
+
const { waitUntilExit } = render2(React13.createElement(DiffApp, { report }), {
|
|
5788
|
+
exitOnCtrlC: true,
|
|
5789
|
+
patchConsole: false
|
|
5490
5790
|
});
|
|
5491
5791
|
await waitUntilExit();
|
|
5492
5792
|
return;
|
|
@@ -5625,11 +5925,11 @@ function pad(s, width) {
|
|
|
5625
5925
|
|
|
5626
5926
|
// src/cli/commands/replay.ts
|
|
5627
5927
|
import { render as render3 } from "ink";
|
|
5628
|
-
import
|
|
5928
|
+
import React15 from "react";
|
|
5629
5929
|
|
|
5630
5930
|
// src/cli/ui/ReplayApp.tsx
|
|
5631
5931
|
import { Box as Box11, Static as Static3, Text as Text11, useApp as useApp4, useInput as useInput4 } from "ink";
|
|
5632
|
-
import
|
|
5932
|
+
import React14, { useMemo as useMemo2, useState as useState7 } from "react";
|
|
5633
5933
|
function ReplayApp({ meta, pages }) {
|
|
5634
5934
|
const { exit } = useApp4();
|
|
5635
5935
|
const maxIdx = Math.max(0, pages.length - 1);
|
|
@@ -5668,14 +5968,14 @@ function ReplayApp({ meta, pages }) {
|
|
|
5668
5968
|
const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
|
|
5669
5969
|
const currentPage = pages[idx];
|
|
5670
5970
|
const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
|
|
5671
|
-
return /* @__PURE__ */
|
|
5971
|
+
return /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React14.createElement(
|
|
5672
5972
|
StatsPanel,
|
|
5673
5973
|
{
|
|
5674
5974
|
summary,
|
|
5675
5975
|
model: cumStats.models[0] ?? meta?.model ?? "?",
|
|
5676
5976
|
prefixHash
|
|
5677
5977
|
}
|
|
5678
|
-
), /* @__PURE__ */
|
|
5978
|
+
), /* @__PURE__ */ React14.createElement(Box11, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React14.createElement(Box11, { justifyContent: "space-between" }, /* @__PURE__ */ React14.createElement(Text11, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React14.createElement(Text11, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React14.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React14.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React14.createElement(Text11, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React14.createElement(Box11, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React14.createElement(Text11, { dimColor: true }, /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "j"), "/", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "k"), "/", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React14.createElement(Text11, { bold: true }, "q"), " quit")));
|
|
5679
5979
|
}
|
|
5680
5980
|
|
|
5681
5981
|
// src/cli/commands/replay.ts
|
|
@@ -5687,8 +5987,9 @@ async function replayCommand(opts) {
|
|
|
5687
5987
|
}
|
|
5688
5988
|
const { parsed } = replayFromFile(opts.path);
|
|
5689
5989
|
const pages = groupRecordsByTurn(parsed.records);
|
|
5690
|
-
const { waitUntilExit } = render3(
|
|
5691
|
-
exitOnCtrlC: true
|
|
5990
|
+
const { waitUntilExit } = render3(React15.createElement(ReplayApp, { meta: parsed.meta, pages }), {
|
|
5991
|
+
exitOnCtrlC: true,
|
|
5992
|
+
patchConsole: false
|
|
5692
5993
|
});
|
|
5693
5994
|
await waitUntilExit();
|
|
5694
5995
|
}
|
|
@@ -5988,16 +6289,16 @@ function truncate4(s, max) {
|
|
|
5988
6289
|
|
|
5989
6290
|
// src/cli/commands/setup.tsx
|
|
5990
6291
|
import { render as render4 } from "ink";
|
|
5991
|
-
import
|
|
6292
|
+
import React18 from "react";
|
|
5992
6293
|
|
|
5993
6294
|
// src/cli/ui/Wizard.tsx
|
|
5994
6295
|
import { Box as Box13, Text as Text13, useApp as useApp5, useInput as useInput6 } from "ink";
|
|
5995
6296
|
import TextInput2 from "ink-text-input";
|
|
5996
|
-
import
|
|
6297
|
+
import React17, { useState as useState9 } from "react";
|
|
5997
6298
|
|
|
5998
6299
|
// src/cli/ui/Select.tsx
|
|
5999
6300
|
import { Box as Box12, Text as Text12, useInput as useInput5 } from "ink";
|
|
6000
|
-
import
|
|
6301
|
+
import React16, { useState as useState8 } from "react";
|
|
6001
6302
|
function SingleSelect({
|
|
6002
6303
|
items,
|
|
6003
6304
|
initialValue,
|
|
@@ -6021,7 +6322,7 @@ function SingleSelect({
|
|
|
6021
6322
|
onCancel();
|
|
6022
6323
|
}
|
|
6023
6324
|
});
|
|
6024
|
-
return /* @__PURE__ */
|
|
6325
|
+
return /* @__PURE__ */ React16.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React16.createElement(
|
|
6025
6326
|
SelectRow,
|
|
6026
6327
|
{
|
|
6027
6328
|
key: item.value,
|
|
@@ -6064,10 +6365,10 @@ function MultiSelect({
|
|
|
6064
6365
|
onCancel();
|
|
6065
6366
|
}
|
|
6066
6367
|
});
|
|
6067
|
-
return /* @__PURE__ */
|
|
6368
|
+
return /* @__PURE__ */ React16.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => {
|
|
6068
6369
|
const checked = selected.has(item.value);
|
|
6069
6370
|
const marker = checked ? "[x]" : "[ ]";
|
|
6070
|
-
return /* @__PURE__ */
|
|
6371
|
+
return /* @__PURE__ */ React16.createElement(
|
|
6071
6372
|
SelectRow,
|
|
6072
6373
|
{
|
|
6073
6374
|
key: item.value,
|
|
@@ -6076,7 +6377,7 @@ function MultiSelect({
|
|
|
6076
6377
|
marker: `${i === index ? "\u25B8" : " "} ${marker}`
|
|
6077
6378
|
}
|
|
6078
6379
|
);
|
|
6079
|
-
}), footer ? /* @__PURE__ */
|
|
6380
|
+
}), footer ? /* @__PURE__ */ React16.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text12, { dimColor: true }, footer)) : null);
|
|
6080
6381
|
}
|
|
6081
6382
|
function SelectRow({
|
|
6082
6383
|
item,
|
|
@@ -6084,7 +6385,7 @@ function SelectRow({
|
|
|
6084
6385
|
marker
|
|
6085
6386
|
}) {
|
|
6086
6387
|
const color = item.disabled ? "gray" : active ? "cyan" : void 0;
|
|
6087
|
-
return /* @__PURE__ */
|
|
6388
|
+
return /* @__PURE__ */ React16.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Box12, null, /* @__PURE__ */ React16.createElement(Text12, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React16.createElement(Box12, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React16.createElement(Text12, { dimColor: true }, item.hint)) : null);
|
|
6088
6389
|
}
|
|
6089
6390
|
function findNextEnabled(items, from, step) {
|
|
6090
6391
|
if (items.length === 0) return 0;
|
|
@@ -6133,7 +6434,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6133
6434
|
if (key.escape && step !== "saved" && onCancel) onCancel();
|
|
6134
6435
|
});
|
|
6135
6436
|
if (step === "apiKey") {
|
|
6136
|
-
return /* @__PURE__ */
|
|
6437
|
+
return /* @__PURE__ */ React17.createElement(
|
|
6137
6438
|
ApiKeyStep,
|
|
6138
6439
|
{
|
|
6139
6440
|
onSubmit: (key) => {
|
|
@@ -6147,7 +6448,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6147
6448
|
);
|
|
6148
6449
|
}
|
|
6149
6450
|
if (step === "preset") {
|
|
6150
|
-
return /* @__PURE__ */
|
|
6451
|
+
return /* @__PURE__ */ React17.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React17.createElement(
|
|
6151
6452
|
SingleSelect,
|
|
6152
6453
|
{
|
|
6153
6454
|
items: presetItems(),
|
|
@@ -6157,10 +6458,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6157
6458
|
setStep("mcp");
|
|
6158
6459
|
}
|
|
6159
6460
|
}
|
|
6160
|
-
), /* @__PURE__ */
|
|
6461
|
+
), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
|
|
6161
6462
|
}
|
|
6162
6463
|
if (step === "mcp") {
|
|
6163
|
-
return /* @__PURE__ */
|
|
6464
|
+
return /* @__PURE__ */ React17.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React17.createElement(
|
|
6164
6465
|
MultiSelect,
|
|
6165
6466
|
{
|
|
6166
6467
|
items: mcpItems(),
|
|
@@ -6185,7 +6486,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6185
6486
|
}
|
|
6186
6487
|
const currentName = pending[0];
|
|
6187
6488
|
const entry = CATALOG_BY_NAME.get(currentName);
|
|
6188
|
-
return /* @__PURE__ */
|
|
6489
|
+
return /* @__PURE__ */ React17.createElement(
|
|
6189
6490
|
McpArgsStep,
|
|
6190
6491
|
{
|
|
6191
6492
|
entry,
|
|
@@ -6203,7 +6504,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6203
6504
|
}
|
|
6204
6505
|
if (step === "review") {
|
|
6205
6506
|
const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
|
|
6206
|
-
return /* @__PURE__ */
|
|
6507
|
+
return /* @__PURE__ */ React17.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React17.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React17.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React17.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React17.createElement(
|
|
6207
6508
|
SummaryLine,
|
|
6208
6509
|
{
|
|
6209
6510
|
label: "MCP",
|
|
@@ -6211,8 +6512,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6211
6512
|
}
|
|
6212
6513
|
), specs.map((spec, i) => (
|
|
6213
6514
|
// biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
|
|
6214
|
-
/* @__PURE__ */
|
|
6215
|
-
)), /* @__PURE__ */
|
|
6515
|
+
/* @__PURE__ */ React17.createElement(Box13, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "\xB7 ", spec))
|
|
6516
|
+
)), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { color: "red" }, error)) : null, /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React17.createElement(
|
|
6216
6517
|
ReviewConfirm,
|
|
6217
6518
|
{
|
|
6218
6519
|
onConfirm: () => {
|
|
@@ -6238,7 +6539,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6238
6539
|
}
|
|
6239
6540
|
));
|
|
6240
6541
|
}
|
|
6241
|
-
return /* @__PURE__ */
|
|
6542
|
+
return /* @__PURE__ */ React17.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React17.createElement(ExitOnEnter, { onExit: exit }));
|
|
6242
6543
|
}
|
|
6243
6544
|
function ApiKeyStep({
|
|
6244
6545
|
onSubmit,
|
|
@@ -6246,7 +6547,7 @@ function ApiKeyStep({
|
|
|
6246
6547
|
onError
|
|
6247
6548
|
}) {
|
|
6248
6549
|
const [value, setValue] = useState9("");
|
|
6249
|
-
return /* @__PURE__ */
|
|
6550
|
+
return /* @__PURE__ */ React17.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React17.createElement(
|
|
6250
6551
|
TextInput2,
|
|
6251
6552
|
{
|
|
6252
6553
|
value,
|
|
@@ -6263,7 +6564,7 @@ function ApiKeyStep({
|
|
|
6263
6564
|
mask: "\u2022",
|
|
6264
6565
|
placeholder: "sk-..."
|
|
6265
6566
|
}
|
|
6266
|
-
)), error ? /* @__PURE__ */
|
|
6567
|
+
)), error ? /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { color: "red" }, error)) : value ? /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "preview: ", redactKey(value))) : null);
|
|
6267
6568
|
}
|
|
6268
6569
|
function McpArgsStep({
|
|
6269
6570
|
entry,
|
|
@@ -6272,7 +6573,7 @@ function McpArgsStep({
|
|
|
6272
6573
|
onError
|
|
6273
6574
|
}) {
|
|
6274
6575
|
const [value, setValue] = useState9("");
|
|
6275
|
-
return /* @__PURE__ */
|
|
6576
|
+
return /* @__PURE__ */ React17.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React17.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React17.createElement(Text13, null, entry.summary), entry.note ? /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, null, "Required parameter: "), /* @__PURE__ */ React17.createElement(Text13, { bold: true }, entry.userArgs)), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React17.createElement(
|
|
6276
6577
|
TextInput2,
|
|
6277
6578
|
{
|
|
6278
6579
|
value,
|
|
@@ -6288,7 +6589,7 @@ function McpArgsStep({
|
|
|
6288
6589
|
},
|
|
6289
6590
|
placeholder: placeholderFor(entry)
|
|
6290
6591
|
}
|
|
6291
|
-
)), error ? /* @__PURE__ */
|
|
6592
|
+
)), error ? /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text13, { color: "red" }, error)) : null));
|
|
6292
6593
|
}
|
|
6293
6594
|
function ReviewConfirm({ onConfirm }) {
|
|
6294
6595
|
useInput6((_i, key) => {
|
|
@@ -6308,10 +6609,10 @@ function StepFrame({
|
|
|
6308
6609
|
total,
|
|
6309
6610
|
children
|
|
6310
6611
|
}) {
|
|
6311
|
-
return /* @__PURE__ */
|
|
6612
|
+
return /* @__PURE__ */ React17.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Box13, null, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React17.createElement(Box13, { marginTop: 1, flexDirection: "column" }, children));
|
|
6312
6613
|
}
|
|
6313
6614
|
function SummaryLine({ label, value }) {
|
|
6314
|
-
return /* @__PURE__ */
|
|
6615
|
+
return /* @__PURE__ */ React17.createElement(Box13, null, /* @__PURE__ */ React17.createElement(Text13, null, label.padEnd(12)), /* @__PURE__ */ React17.createElement(Text13, { bold: true }, value));
|
|
6315
6616
|
}
|
|
6316
6617
|
function presetItems() {
|
|
6317
6618
|
return ["fast", "smart", "max"].map((name) => ({
|
|
@@ -6367,7 +6668,7 @@ async function setupCommand(_opts = {}) {
|
|
|
6367
6668
|
const existingKey = loadApiKey();
|
|
6368
6669
|
const existing = readConfig();
|
|
6369
6670
|
const { waitUntilExit, unmount } = render4(
|
|
6370
|
-
/* @__PURE__ */
|
|
6671
|
+
/* @__PURE__ */ React18.createElement(
|
|
6371
6672
|
Wizard,
|
|
6372
6673
|
{
|
|
6373
6674
|
existingApiKey: existingKey,
|
|
@@ -6379,7 +6680,7 @@ async function setupCommand(_opts = {}) {
|
|
|
6379
6680
|
}
|
|
6380
6681
|
}
|
|
6381
6682
|
),
|
|
6382
|
-
{ exitOnCtrlC: true }
|
|
6683
|
+
{ exitOnCtrlC: true, patchConsole: false }
|
|
6383
6684
|
);
|
|
6384
6685
|
await waitUntilExit();
|
|
6385
6686
|
}
|