reasonix 0.4.15 → 0.4.17
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 +22 -0
- package/dist/cli/{chunk-2P2MZLCE.js → chunk-3YQRWFES.js} +56 -6
- package/dist/cli/chunk-3YQRWFES.js.map +1 -0
- package/dist/cli/index.js +793 -258
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-MMANQ36Z.js → prompt-HK5XLH55.js} +2 -2
- package/dist/index.d.ts +155 -3
- package/dist/index.js +317 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-2P2MZLCE.js.map +0 -1
- /package/dist/cli/{prompt-MMANQ36Z.js.map → prompt-HK5XLH55.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
PROJECT_MEMORY_FILE,
|
|
4
|
+
applyProjectMemory,
|
|
5
|
+
memoryEnabled,
|
|
6
|
+
readProjectMemory
|
|
7
|
+
} from "./chunk-3YQRWFES.js";
|
|
3
8
|
|
|
4
9
|
// src/cli/index.ts
|
|
5
10
|
import { Command } from "commander";
|
|
@@ -44,6 +49,21 @@ function saveApiKey(key, path = defaultConfigPath()) {
|
|
|
44
49
|
cfg.apiKey = key.trim();
|
|
45
50
|
writeConfig(cfg, path);
|
|
46
51
|
}
|
|
52
|
+
function loadProjectShellAllowed(rootDir, path = defaultConfigPath()) {
|
|
53
|
+
const cfg = readConfig(path);
|
|
54
|
+
return cfg.projects?.[rootDir]?.shellAllowed ?? [];
|
|
55
|
+
}
|
|
56
|
+
function addProjectShellAllowed(rootDir, prefix, path = defaultConfigPath()) {
|
|
57
|
+
const trimmed = prefix.trim();
|
|
58
|
+
if (!trimmed) return;
|
|
59
|
+
const cfg = readConfig(path);
|
|
60
|
+
if (!cfg.projects) cfg.projects = {};
|
|
61
|
+
if (!cfg.projects[rootDir]) cfg.projects[rootDir] = {};
|
|
62
|
+
const existing = cfg.projects[rootDir].shellAllowed ?? [];
|
|
63
|
+
if (existing.includes(trimmed)) return;
|
|
64
|
+
cfg.projects[rootDir].shellAllowed = [...existing, trimmed];
|
|
65
|
+
writeConfig(cfg, path);
|
|
66
|
+
}
|
|
47
67
|
function isPlausibleKey(key) {
|
|
48
68
|
const trimmed = key.trim();
|
|
49
69
|
return /^sk-[A-Za-z0-9_-]{16,}$/.test(trimmed);
|
|
@@ -103,8 +123,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
103
123
|
}
|
|
104
124
|
function sleep(ms, signal) {
|
|
105
125
|
if (ms <= 0) return Promise.resolve();
|
|
106
|
-
return new Promise((
|
|
107
|
-
const timer = setTimeout(
|
|
126
|
+
return new Promise((resolve6, reject) => {
|
|
127
|
+
const timer = setTimeout(resolve6, ms);
|
|
108
128
|
if (signal) {
|
|
109
129
|
const onAbort = () => {
|
|
110
130
|
clearTimeout(timer);
|
|
@@ -1544,8 +1564,8 @@ var CacheFirstLoop = class {
|
|
|
1544
1564
|
}
|
|
1545
1565
|
);
|
|
1546
1566
|
for (let k = 0; k < budget; k++) {
|
|
1547
|
-
const sample = queue.shift() ?? await new Promise((
|
|
1548
|
-
waiter =
|
|
1567
|
+
const sample = queue.shift() ?? await new Promise((resolve6) => {
|
|
1568
|
+
waiter = resolve6;
|
|
1549
1569
|
});
|
|
1550
1570
|
yield {
|
|
1551
1571
|
turn: this._turn,
|
|
@@ -2239,6 +2259,221 @@ function lineDiff(a, b) {
|
|
|
2239
2259
|
return out;
|
|
2240
2260
|
}
|
|
2241
2261
|
|
|
2262
|
+
// src/tools/shell.ts
|
|
2263
|
+
import { spawn } from "child_process";
|
|
2264
|
+
import * as pathMod2 from "path";
|
|
2265
|
+
var DEFAULT_TIMEOUT_SEC = 60;
|
|
2266
|
+
var DEFAULT_MAX_OUTPUT_CHARS = 32e3;
|
|
2267
|
+
var BUILTIN_ALLOWLIST = [
|
|
2268
|
+
// Repo inspection
|
|
2269
|
+
"git status",
|
|
2270
|
+
"git diff",
|
|
2271
|
+
"git log",
|
|
2272
|
+
"git show",
|
|
2273
|
+
"git blame",
|
|
2274
|
+
"git branch",
|
|
2275
|
+
"git remote",
|
|
2276
|
+
"git rev-parse",
|
|
2277
|
+
"git config --get",
|
|
2278
|
+
// Filesystem inspection
|
|
2279
|
+
"ls",
|
|
2280
|
+
"pwd",
|
|
2281
|
+
"cat",
|
|
2282
|
+
"head",
|
|
2283
|
+
"tail",
|
|
2284
|
+
"wc",
|
|
2285
|
+
"file",
|
|
2286
|
+
"tree",
|
|
2287
|
+
"find",
|
|
2288
|
+
"grep",
|
|
2289
|
+
"rg",
|
|
2290
|
+
// Language version probes
|
|
2291
|
+
"node --version",
|
|
2292
|
+
"node -v",
|
|
2293
|
+
"npm --version",
|
|
2294
|
+
"npx --version",
|
|
2295
|
+
"python --version",
|
|
2296
|
+
"python3 --version",
|
|
2297
|
+
"cargo --version",
|
|
2298
|
+
"go version",
|
|
2299
|
+
"rustc --version",
|
|
2300
|
+
"deno --version",
|
|
2301
|
+
"bun --version",
|
|
2302
|
+
// Test runners (non-destructive by convention)
|
|
2303
|
+
"npm test",
|
|
2304
|
+
"npm run test",
|
|
2305
|
+
"npx vitest run",
|
|
2306
|
+
"npx vitest",
|
|
2307
|
+
"npx jest",
|
|
2308
|
+
"pytest",
|
|
2309
|
+
"python -m pytest",
|
|
2310
|
+
"cargo test",
|
|
2311
|
+
"cargo check",
|
|
2312
|
+
"cargo clippy",
|
|
2313
|
+
"go test",
|
|
2314
|
+
"go vet",
|
|
2315
|
+
"deno test",
|
|
2316
|
+
"bun test",
|
|
2317
|
+
// Linters / typecheckers (read-only by convention)
|
|
2318
|
+
"npm run lint",
|
|
2319
|
+
"npm run typecheck",
|
|
2320
|
+
"npx tsc --noEmit",
|
|
2321
|
+
"npx biome check",
|
|
2322
|
+
"npx eslint",
|
|
2323
|
+
"npx prettier --check",
|
|
2324
|
+
"ruff",
|
|
2325
|
+
"mypy"
|
|
2326
|
+
];
|
|
2327
|
+
function tokenizeCommand(cmd) {
|
|
2328
|
+
const out = [];
|
|
2329
|
+
let cur = "";
|
|
2330
|
+
let quote = null;
|
|
2331
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
2332
|
+
const ch = cmd[i];
|
|
2333
|
+
if (quote) {
|
|
2334
|
+
if (ch === quote) {
|
|
2335
|
+
quote = null;
|
|
2336
|
+
} else if (ch === "\\" && quote === '"' && i + 1 < cmd.length) {
|
|
2337
|
+
cur += cmd[++i];
|
|
2338
|
+
} else {
|
|
2339
|
+
cur += ch;
|
|
2340
|
+
}
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
if (ch === '"' || ch === "'") {
|
|
2344
|
+
quote = ch;
|
|
2345
|
+
continue;
|
|
2346
|
+
}
|
|
2347
|
+
if (ch === " " || ch === " ") {
|
|
2348
|
+
if (cur.length > 0) {
|
|
2349
|
+
out.push(cur);
|
|
2350
|
+
cur = "";
|
|
2351
|
+
}
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
cur += ch;
|
|
2355
|
+
}
|
|
2356
|
+
if (quote) throw new Error(`unclosed ${quote} in command`);
|
|
2357
|
+
if (cur.length > 0) out.push(cur);
|
|
2358
|
+
return out;
|
|
2359
|
+
}
|
|
2360
|
+
function isAllowed(cmd, extra = []) {
|
|
2361
|
+
const normalized = cmd.trim().replace(/\s+/g, " ");
|
|
2362
|
+
const allowlist = [...BUILTIN_ALLOWLIST, ...extra];
|
|
2363
|
+
for (const prefix of allowlist) {
|
|
2364
|
+
if (normalized === prefix) return true;
|
|
2365
|
+
if (normalized.startsWith(`${prefix} `)) return true;
|
|
2366
|
+
}
|
|
2367
|
+
return false;
|
|
2368
|
+
}
|
|
2369
|
+
async function runCommand(cmd, opts) {
|
|
2370
|
+
const argv = tokenizeCommand(cmd);
|
|
2371
|
+
if (argv.length === 0) throw new Error("run_command: empty command");
|
|
2372
|
+
const timeoutMs = (opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
|
|
2373
|
+
const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
2374
|
+
const spawnOpts = {
|
|
2375
|
+
cwd: opts.cwd,
|
|
2376
|
+
shell: false,
|
|
2377
|
+
// no shell-expansion — see header comment
|
|
2378
|
+
windowsHide: true,
|
|
2379
|
+
env: process.env
|
|
2380
|
+
};
|
|
2381
|
+
return await new Promise((resolve6, reject) => {
|
|
2382
|
+
let child;
|
|
2383
|
+
try {
|
|
2384
|
+
child = spawn(argv[0], argv.slice(1), spawnOpts);
|
|
2385
|
+
} catch (err) {
|
|
2386
|
+
reject(err);
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
let buf = "";
|
|
2390
|
+
let timedOut = false;
|
|
2391
|
+
const killTimer = setTimeout(() => {
|
|
2392
|
+
timedOut = true;
|
|
2393
|
+
child.kill("SIGKILL");
|
|
2394
|
+
}, timeoutMs);
|
|
2395
|
+
const onAbort = () => child.kill("SIGKILL");
|
|
2396
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2397
|
+
const onData = (chunk) => {
|
|
2398
|
+
buf += chunk.toString();
|
|
2399
|
+
if (buf.length > maxChars * 2) buf = `${buf.slice(0, maxChars * 2)}`;
|
|
2400
|
+
};
|
|
2401
|
+
child.stdout?.on("data", onData);
|
|
2402
|
+
child.stderr?.on("data", onData);
|
|
2403
|
+
child.on("error", (err) => {
|
|
2404
|
+
clearTimeout(killTimer);
|
|
2405
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
2406
|
+
reject(err);
|
|
2407
|
+
});
|
|
2408
|
+
child.on("close", (code) => {
|
|
2409
|
+
clearTimeout(killTimer);
|
|
2410
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
2411
|
+
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
2412
|
+
|
|
2413
|
+
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
2414
|
+
resolve6({ exitCode: code, output, timedOut });
|
|
2415
|
+
});
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
var NeedsConfirmationError = class extends Error {
|
|
2419
|
+
command;
|
|
2420
|
+
constructor(command) {
|
|
2421
|
+
super(
|
|
2422
|
+
`run_command: "${command}" needs the user's approval before it runs. STOP calling tools now \u2014 the TUI has already prompted the user to press y (run) or n (deny). Wait for their next message; it will either be the command's output (if they approved) or an instruction to continue without it (if they denied). Don't retry the command or call other shell commands in the meantime.`
|
|
2423
|
+
);
|
|
2424
|
+
this.name = "NeedsConfirmationError";
|
|
2425
|
+
this.command = command;
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
function registerShellTools(registry, opts) {
|
|
2429
|
+
const rootDir = pathMod2.resolve(opts.rootDir);
|
|
2430
|
+
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
2431
|
+
const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
2432
|
+
const extraAllowed = opts.extraAllowed ?? [];
|
|
2433
|
+
const allowAll = opts.allowAll ?? false;
|
|
2434
|
+
registry.register({
|
|
2435
|
+
name: "run_command",
|
|
2436
|
+
description: "Run a shell command in the project root and return its combined stdout+stderr. Read-only and test commands (git status, ls, npm test, pytest, cargo test, grep, etc.) run immediately. Anything that could mutate state (npm install, git commit, rm, chmod) is refused and the user has to confirm in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
|
|
2437
|
+
parameters: {
|
|
2438
|
+
type: "object",
|
|
2439
|
+
properties: {
|
|
2440
|
+
command: {
|
|
2441
|
+
type: "string",
|
|
2442
|
+
description: "Full command line, e.g. 'npm test' or 'git diff src/foo.ts'. Tokenized with POSIX-ish quoting; no shell expansion, no pipes, no redirects."
|
|
2443
|
+
},
|
|
2444
|
+
timeoutSec: {
|
|
2445
|
+
type: "integer",
|
|
2446
|
+
description: `Override the default ${timeoutSec}s timeout for a single command.`
|
|
2447
|
+
}
|
|
2448
|
+
},
|
|
2449
|
+
required: ["command"]
|
|
2450
|
+
},
|
|
2451
|
+
fn: async (args, ctx) => {
|
|
2452
|
+
const cmd = args.command.trim();
|
|
2453
|
+
if (!cmd) throw new Error("run_command: empty command");
|
|
2454
|
+
if (!allowAll && !isAllowed(cmd, extraAllowed)) {
|
|
2455
|
+
throw new NeedsConfirmationError(cmd);
|
|
2456
|
+
}
|
|
2457
|
+
const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
|
|
2458
|
+
const result = await runCommand(cmd, {
|
|
2459
|
+
cwd: rootDir,
|
|
2460
|
+
timeoutSec: effectiveTimeout,
|
|
2461
|
+
maxOutputChars,
|
|
2462
|
+
signal: ctx?.signal
|
|
2463
|
+
});
|
|
2464
|
+
return formatCommandResult(cmd, result);
|
|
2465
|
+
}
|
|
2466
|
+
});
|
|
2467
|
+
return registry;
|
|
2468
|
+
}
|
|
2469
|
+
function formatCommandResult(cmd, r) {
|
|
2470
|
+
const header = r.timedOut ? `$ ${cmd}
|
|
2471
|
+
[killed after timeout]` : `$ ${cmd}
|
|
2472
|
+
[exit ${r.exitCode ?? "?"}]`;
|
|
2473
|
+
return r.output ? `${header}
|
|
2474
|
+
${r.output}` : header;
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2242
2477
|
// src/tools/web.ts
|
|
2243
2478
|
var DEFAULT_FETCH_MAX_CHARS = 32e3;
|
|
2244
2479
|
var DEFAULT_FETCH_TIMEOUT_MS = 15e3;
|
|
@@ -2422,11 +2657,11 @@ ${i + 1}. ${r.title}`);
|
|
|
2422
2657
|
|
|
2423
2658
|
// src/env.ts
|
|
2424
2659
|
import { readFileSync as readFileSync3 } from "fs";
|
|
2425
|
-
import { resolve as
|
|
2660
|
+
import { resolve as resolve3 } from "path";
|
|
2426
2661
|
function loadDotenv(path = ".env") {
|
|
2427
2662
|
let raw;
|
|
2428
2663
|
try {
|
|
2429
|
-
raw = readFileSync3(
|
|
2664
|
+
raw = readFileSync3(resolve3(process.cwd(), path), "utf8");
|
|
2430
2665
|
} catch {
|
|
2431
2666
|
return;
|
|
2432
2667
|
}
|
|
@@ -3139,7 +3374,7 @@ var McpClient = class {
|
|
|
3139
3374
|
const id = this.nextId++;
|
|
3140
3375
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
3141
3376
|
let abortHandler = null;
|
|
3142
|
-
const promise = new Promise((
|
|
3377
|
+
const promise = new Promise((resolve6, reject) => {
|
|
3143
3378
|
const timeout = setTimeout(() => {
|
|
3144
3379
|
this.pending.delete(id);
|
|
3145
3380
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -3148,7 +3383,7 @@ var McpClient = class {
|
|
|
3148
3383
|
);
|
|
3149
3384
|
}, this.requestTimeoutMs);
|
|
3150
3385
|
this.pending.set(id, {
|
|
3151
|
-
resolve:
|
|
3386
|
+
resolve: resolve6,
|
|
3152
3387
|
reject,
|
|
3153
3388
|
timeout
|
|
3154
3389
|
});
|
|
@@ -3230,7 +3465,7 @@ var McpClient = class {
|
|
|
3230
3465
|
};
|
|
3231
3466
|
|
|
3232
3467
|
// src/mcp/stdio.ts
|
|
3233
|
-
import { spawn } from "child_process";
|
|
3468
|
+
import { spawn as spawn2 } from "child_process";
|
|
3234
3469
|
var StdioTransport = class {
|
|
3235
3470
|
child;
|
|
3236
3471
|
queue = [];
|
|
@@ -3245,14 +3480,14 @@ var StdioTransport = class {
|
|
|
3245
3480
|
opts.command,
|
|
3246
3481
|
...(opts.args ?? []).map((a) => quoteArg(a, process.platform === "win32"))
|
|
3247
3482
|
].join(" ");
|
|
3248
|
-
this.child =
|
|
3483
|
+
this.child = spawn2(line, [], {
|
|
3249
3484
|
env,
|
|
3250
3485
|
cwd: opts.cwd,
|
|
3251
3486
|
stdio: ["pipe", "pipe", "inherit"],
|
|
3252
3487
|
shell: true
|
|
3253
3488
|
});
|
|
3254
3489
|
} else {
|
|
3255
|
-
this.child =
|
|
3490
|
+
this.child = spawn2(opts.command, opts.args ?? [], {
|
|
3256
3491
|
env,
|
|
3257
3492
|
cwd: opts.cwd,
|
|
3258
3493
|
stdio: ["pipe", "pipe", "inherit"]
|
|
@@ -3271,12 +3506,12 @@ var StdioTransport = class {
|
|
|
3271
3506
|
}
|
|
3272
3507
|
async send(message) {
|
|
3273
3508
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
3274
|
-
return new Promise((
|
|
3509
|
+
return new Promise((resolve6, reject) => {
|
|
3275
3510
|
const line = `${JSON.stringify(message)}
|
|
3276
3511
|
`;
|
|
3277
3512
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
3278
3513
|
if (err) reject(err);
|
|
3279
|
-
else
|
|
3514
|
+
else resolve6();
|
|
3280
3515
|
});
|
|
3281
3516
|
});
|
|
3282
3517
|
}
|
|
@@ -3287,8 +3522,8 @@ var StdioTransport = class {
|
|
|
3287
3522
|
continue;
|
|
3288
3523
|
}
|
|
3289
3524
|
if (this.closed) return;
|
|
3290
|
-
const next = await new Promise((
|
|
3291
|
-
this.waiters.push(
|
|
3525
|
+
const next = await new Promise((resolve6) => {
|
|
3526
|
+
this.waiters.push(resolve6);
|
|
3292
3527
|
});
|
|
3293
3528
|
if (next === null) return;
|
|
3294
3529
|
yield next;
|
|
@@ -3354,8 +3589,8 @@ var SseTransport = class {
|
|
|
3354
3589
|
constructor(opts) {
|
|
3355
3590
|
this.url = opts.url;
|
|
3356
3591
|
this.headers = opts.headers ?? {};
|
|
3357
|
-
this.endpointReady = new Promise((
|
|
3358
|
-
this.resolveEndpoint =
|
|
3592
|
+
this.endpointReady = new Promise((resolve6, reject) => {
|
|
3593
|
+
this.resolveEndpoint = resolve6;
|
|
3359
3594
|
this.rejectEndpoint = reject;
|
|
3360
3595
|
});
|
|
3361
3596
|
this.endpointReady.catch(() => void 0);
|
|
@@ -3382,8 +3617,8 @@ var SseTransport = class {
|
|
|
3382
3617
|
continue;
|
|
3383
3618
|
}
|
|
3384
3619
|
if (this.closed) return;
|
|
3385
|
-
const next = await new Promise((
|
|
3386
|
-
this.waiters.push(
|
|
3620
|
+
const next = await new Promise((resolve6) => {
|
|
3621
|
+
this.waiters.push(resolve6);
|
|
3387
3622
|
});
|
|
3388
3623
|
if (next === null) return;
|
|
3389
3624
|
yield next;
|
|
@@ -3583,7 +3818,7 @@ async function trySection(load) {
|
|
|
3583
3818
|
|
|
3584
3819
|
// src/code/edit-blocks.ts
|
|
3585
3820
|
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3586
|
-
import { dirname as dirname4, resolve as
|
|
3821
|
+
import { dirname as dirname4, resolve as resolve4 } from "path";
|
|
3587
3822
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
3588
3823
|
function parseEditBlocks(text) {
|
|
3589
3824
|
const out = [];
|
|
@@ -3601,8 +3836,8 @@ function parseEditBlocks(text) {
|
|
|
3601
3836
|
return out;
|
|
3602
3837
|
}
|
|
3603
3838
|
function applyEditBlock(block, rootDir) {
|
|
3604
|
-
const absRoot =
|
|
3605
|
-
const absTarget =
|
|
3839
|
+
const absRoot = resolve4(rootDir);
|
|
3840
|
+
const absTarget = resolve4(absRoot, block.path);
|
|
3606
3841
|
if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
|
|
3607
3842
|
return {
|
|
3608
3843
|
path: block.path,
|
|
@@ -3652,13 +3887,13 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
3652
3887
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
3653
3888
|
}
|
|
3654
3889
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
3655
|
-
const absRoot =
|
|
3890
|
+
const absRoot = resolve4(rootDir);
|
|
3656
3891
|
const seen = /* @__PURE__ */ new Set();
|
|
3657
3892
|
const snapshots = [];
|
|
3658
3893
|
for (const b of blocks) {
|
|
3659
3894
|
if (seen.has(b.path)) continue;
|
|
3660
3895
|
seen.add(b.path);
|
|
3661
|
-
const abs =
|
|
3896
|
+
const abs = resolve4(absRoot, b.path);
|
|
3662
3897
|
if (!existsSync2(abs)) {
|
|
3663
3898
|
snapshots.push({ path: b.path, prevContent: null });
|
|
3664
3899
|
continue;
|
|
@@ -3672,9 +3907,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
3672
3907
|
return snapshots;
|
|
3673
3908
|
}
|
|
3674
3909
|
function restoreSnapshots(snapshots, rootDir) {
|
|
3675
|
-
const absRoot =
|
|
3910
|
+
const absRoot = resolve4(rootDir);
|
|
3676
3911
|
return snapshots.map((snap) => {
|
|
3677
|
-
const abs =
|
|
3912
|
+
const abs = resolve4(absRoot, snap.path);
|
|
3678
3913
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
|
|
3679
3914
|
return {
|
|
3680
3915
|
path: snap.path,
|
|
@@ -3707,15 +3942,16 @@ function sep() {
|
|
|
3707
3942
|
}
|
|
3708
3943
|
|
|
3709
3944
|
// src/index.ts
|
|
3710
|
-
var VERSION = "0.4.
|
|
3945
|
+
var VERSION = "0.4.17";
|
|
3711
3946
|
|
|
3712
3947
|
// src/cli/commands/chat.tsx
|
|
3948
|
+
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
3713
3949
|
import { render } from "ink";
|
|
3714
|
-
import
|
|
3950
|
+
import React13, { useState as useState6 } from "react";
|
|
3715
3951
|
|
|
3716
3952
|
// src/cli/ui/App.tsx
|
|
3717
|
-
import { Box as
|
|
3718
|
-
import
|
|
3953
|
+
import { Box as Box9, Static, Text as Text9, useApp, useInput as useInput3 } from "ink";
|
|
3954
|
+
import React10, { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState4 } from "react";
|
|
3719
3955
|
|
|
3720
3956
|
// src/cli/ui/EventLog.tsx
|
|
3721
3957
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
@@ -3788,7 +4024,7 @@ function stripMath(s) {
|
|
|
3788
4024
|
(_m, n, k) => `C(${n.trim()},${k.trim()})`
|
|
3789
4025
|
).replace(/\\sqrt\s*\{([^{}]+)\}/g, (_m, g) => `\u221A(${g.trim()})`).replace(/\\boxed\s*\{([^{}]+)\}/g, (_m, g) => `\u3010${g.trim()}\u3011`).replace(/\\text\s*\{([^{}]+)\}/g, (_m, g) => g.trim()).replace(/\\overline\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0304`).replace(/\\hat\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0302`).replace(/\\vec\s*\{([^{}]+)\}/g, (_m, g) => `\u2192${g.trim()}`).replace(/\\cdot/g, "\xB7").replace(/\\times/g, "\xD7").replace(/\\div/g, "\xF7").replace(/\\pm/g, "\xB1").replace(/\\mp/g, "\u2213").replace(/\\leq/g, "\u2264").replace(/\\geq/g, "\u2265").replace(/\\neq/g, "\u2260").replace(/\\approx/g, "\u2248").replace(/\\in\b/g, "\u2208").replace(/\\notin\b/g, "\u2209").replace(/\\infty/g, "\u221E").replace(/\\sum\b/g, "\u03A3").replace(/\\prod\b/g, "\u03A0").replace(/\\int\b/g, "\u222B").replace(/\\alpha/g, "\u03B1").replace(/\\beta/g, "\u03B2").replace(/\\gamma/g, "\u03B3").replace(/\\delta/g, "\u03B4").replace(/\\theta/g, "\u03B8").replace(/\\lambda/g, "\u03BB").replace(/\\mu/g, "\u03BC").replace(/\\pi/g, "\u03C0").replace(/\\sigma/g, "\u03C3").replace(/\\phi/g, "\u03C6").replace(/\\omega/g, "\u03C9").replace(/\\implies\b/g, "\u21D2").replace(/\\iff\b/g, "\u21D4").replace(/\\to\b/g, "\u2192").replace(/\\rightarrow/g, "\u2192").replace(/\\Rightarrow/g, "\u21D2").replace(/\\leftarrow/g, "\u2190").replace(/\\Leftarrow/g, "\u21D0").replace(/\\ldots/g, "\u2026").replace(/\\cdots/g, "\u22EF").replace(/\\quad/g, " ").replace(/\\qquad/g, " ").replace(/\\,/g, " ").replace(/\\;/g, " ").replace(/\\!/g, "").replace(/\\\\/g, "\n").replace(/\^\{([\w+-]+)\}/g, (_m, g) => toSuperscript(g)).replace(/\^([0-9+\-n])/g, (_m, g) => toSuperscript(g)).replace(/_\{([\w+-]+)\}/g, (_m, g) => toSubscript(g)).replace(/_([0-9+\-])/g, (_m, g) => toSubscript(g)).replace(/\\[a-zA-Z]+\s*\{([^{}]+)\}\s*\{([^{}]+)\}/g, "($1)/($2)").replace(/\\[a-zA-Z]+\s*\{([^{}]+)\}/g, "$1").replace(/\\[a-zA-Z]+/g, "").replace(/[ \t]{2,}/g, " ");
|
|
3790
4026
|
}
|
|
3791
|
-
var INLINE_RE = /(\*\*([^*\n]+?)
|
|
4027
|
+
var INLINE_RE = /(\*\*([^*\n]+?)\*\*|```([^\n]+?)```|`([^`\n]+?)`|(?<![*\w])\*([^*\n]+?)\*(?!\w))/g;
|
|
3792
4028
|
function InlineMd({ text }) {
|
|
3793
4029
|
const parts = [];
|
|
3794
4030
|
let last = 0;
|
|
@@ -3803,12 +4039,17 @@ function InlineMd({ text }) {
|
|
|
3803
4039
|
/* @__PURE__ */ React2.createElement(Text2, { key: `b${idx++}`, bold: true }, m[2])
|
|
3804
4040
|
);
|
|
3805
4041
|
} else if (m[3] !== void 0) {
|
|
4042
|
+
const stripped = m[3].replace(/^(\w+)\s+/, "");
|
|
3806
4043
|
parts.push(
|
|
3807
|
-
/* @__PURE__ */ React2.createElement(Text2, { key: `c${idx++}`, color: "yellow" },
|
|
4044
|
+
/* @__PURE__ */ React2.createElement(Text2, { key: `c${idx++}`, color: "yellow" }, stripped)
|
|
3808
4045
|
);
|
|
3809
4046
|
} else if (m[4] !== void 0) {
|
|
3810
4047
|
parts.push(
|
|
3811
|
-
/* @__PURE__ */ React2.createElement(Text2, { key: `
|
|
4048
|
+
/* @__PURE__ */ React2.createElement(Text2, { key: `c${idx++}`, color: "yellow" }, m[4])
|
|
4049
|
+
);
|
|
4050
|
+
} else if (m[5] !== void 0) {
|
|
4051
|
+
parts.push(
|
|
4052
|
+
/* @__PURE__ */ React2.createElement(Text2, { key: `i${idx++}`, italic: true }, m[5])
|
|
3812
4053
|
);
|
|
3813
4054
|
}
|
|
3814
4055
|
last = start + m[0].length;
|
|
@@ -3826,6 +4067,7 @@ function parseBlocks(raw) {
|
|
|
3826
4067
|
let codeLang = "";
|
|
3827
4068
|
let codeBuf = [];
|
|
3828
4069
|
let listBuf = null;
|
|
4070
|
+
let codeFence = "";
|
|
3829
4071
|
const flushPara = () => {
|
|
3830
4072
|
if (para.length) {
|
|
3831
4073
|
out.push({ kind: "paragraph", text: para.join(" ") });
|
|
@@ -3871,22 +4113,37 @@ function parseBlocks(raw) {
|
|
|
3871
4113
|
para.push(filename);
|
|
3872
4114
|
}
|
|
3873
4115
|
}
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
if (
|
|
4116
|
+
if (!inCode) {
|
|
4117
|
+
const open = line.match(/^ {0,3}(`{3,})(\w*)\s*(.*)$/);
|
|
4118
|
+
if (open) {
|
|
4119
|
+
const fence = open[1];
|
|
4120
|
+
const lang = open[2] ?? "";
|
|
4121
|
+
const rest = open[3] ?? "";
|
|
4122
|
+
const closeOnSame = rest.match(new RegExp(`^(.*?)${fence}\\s*$`));
|
|
4123
|
+
if (closeOnSame) {
|
|
4124
|
+
flushPara();
|
|
4125
|
+
flushList();
|
|
4126
|
+
out.push({ kind: "code", lang, text: (closeOnSame[1] ?? "").trim() });
|
|
4127
|
+
continue;
|
|
4128
|
+
}
|
|
4129
|
+
flushPara();
|
|
4130
|
+
flushList();
|
|
4131
|
+
inCode = true;
|
|
4132
|
+
codeLang = lang;
|
|
4133
|
+
codeFence = fence;
|
|
4134
|
+
if (rest.length > 0) codeBuf.push(rest);
|
|
4135
|
+
continue;
|
|
4136
|
+
}
|
|
4137
|
+
} else {
|
|
4138
|
+
const close = line.match(/^ {0,3}(`{3,})\s*$/);
|
|
4139
|
+
if (close && close[1].length >= codeFence.length) {
|
|
3877
4140
|
out.push({ kind: "code", lang: codeLang, text: codeBuf.join("\n") });
|
|
3878
4141
|
codeBuf = [];
|
|
3879
4142
|
codeLang = "";
|
|
4143
|
+
codeFence = "";
|
|
3880
4144
|
inCode = false;
|
|
3881
|
-
|
|
3882
|
-
flushPara();
|
|
3883
|
-
flushList();
|
|
3884
|
-
inCode = true;
|
|
3885
|
-
codeLang = fence[1] ?? "";
|
|
4145
|
+
continue;
|
|
3886
4146
|
}
|
|
3887
|
-
continue;
|
|
3888
|
-
}
|
|
3889
|
-
if (inCode) {
|
|
3890
4147
|
codeBuf.push(rawLine);
|
|
3891
4148
|
continue;
|
|
3892
4149
|
}
|
|
@@ -4214,7 +4471,7 @@ function processMultilineKey(value, cursor, key) {
|
|
|
4214
4471
|
}
|
|
4215
4472
|
return { next: null, cursor: null, submit: true, submitValue: value };
|
|
4216
4473
|
}
|
|
4217
|
-
if (key.backspace) {
|
|
4474
|
+
if (key.backspace || key.delete || key.input === "\x7F" || key.input === "\b") {
|
|
4218
4475
|
if (cursor === 0) return NOOP;
|
|
4219
4476
|
return {
|
|
4220
4477
|
next: value.slice(0, cursor - 1) + value.slice(cursor),
|
|
@@ -4222,14 +4479,6 @@ function processMultilineKey(value, cursor, key) {
|
|
|
4222
4479
|
submit: false
|
|
4223
4480
|
};
|
|
4224
4481
|
}
|
|
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
|
-
};
|
|
4232
|
-
}
|
|
4233
4482
|
if ((key.ctrl || key.meta) && key.input.length === 0) return NOOP;
|
|
4234
4483
|
if (key.ctrl || key.meta) return NOOP;
|
|
4235
4484
|
if (key.input.length > 0) {
|
|
@@ -4371,16 +4620,177 @@ function LineWithCursor({
|
|
|
4371
4620
|
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
4621
|
}
|
|
4373
4622
|
|
|
4623
|
+
// src/cli/ui/ShellConfirm.tsx
|
|
4624
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
4625
|
+
import React7 from "react";
|
|
4626
|
+
|
|
4627
|
+
// src/cli/ui/Select.tsx
|
|
4628
|
+
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
4629
|
+
import React6, { useState as useState3 } from "react";
|
|
4630
|
+
function SingleSelect({
|
|
4631
|
+
items,
|
|
4632
|
+
initialValue,
|
|
4633
|
+
onSubmit,
|
|
4634
|
+
onCancel
|
|
4635
|
+
}) {
|
|
4636
|
+
const initialIndex = Math.max(
|
|
4637
|
+
0,
|
|
4638
|
+
items.findIndex((i) => i.value === initialValue && !i.disabled)
|
|
4639
|
+
);
|
|
4640
|
+
const [index, setIndex] = useState3(initialIndex === -1 ? 0 : initialIndex);
|
|
4641
|
+
useInput2((_input, key) => {
|
|
4642
|
+
if (key.upArrow) {
|
|
4643
|
+
setIndex((i) => findNextEnabled(items, i, -1));
|
|
4644
|
+
} else if (key.downArrow) {
|
|
4645
|
+
setIndex((i) => findNextEnabled(items, i, 1));
|
|
4646
|
+
} else if (key.return) {
|
|
4647
|
+
const chosen = items[index];
|
|
4648
|
+
if (chosen && !chosen.disabled) onSubmit(chosen.value);
|
|
4649
|
+
} else if (key.escape && onCancel) {
|
|
4650
|
+
onCancel();
|
|
4651
|
+
}
|
|
4652
|
+
});
|
|
4653
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React6.createElement(
|
|
4654
|
+
SelectRow,
|
|
4655
|
+
{
|
|
4656
|
+
key: item.value,
|
|
4657
|
+
item,
|
|
4658
|
+
active: i === index,
|
|
4659
|
+
marker: i === index ? "\u25B8" : " "
|
|
4660
|
+
}
|
|
4661
|
+
)));
|
|
4662
|
+
}
|
|
4663
|
+
function MultiSelect({
|
|
4664
|
+
items,
|
|
4665
|
+
initialSelected = [],
|
|
4666
|
+
onSubmit,
|
|
4667
|
+
onCancel,
|
|
4668
|
+
footer
|
|
4669
|
+
}) {
|
|
4670
|
+
const [index, setIndex] = useState3(() => {
|
|
4671
|
+
const first = items.findIndex((i) => !i.disabled);
|
|
4672
|
+
return first === -1 ? 0 : first;
|
|
4673
|
+
});
|
|
4674
|
+
const [selected, setSelected] = useState3(new Set(initialSelected));
|
|
4675
|
+
useInput2((input, key) => {
|
|
4676
|
+
if (key.upArrow) {
|
|
4677
|
+
setIndex((i) => findNextEnabled(items, i, -1));
|
|
4678
|
+
} else if (key.downArrow) {
|
|
4679
|
+
setIndex((i) => findNextEnabled(items, i, 1));
|
|
4680
|
+
} else if (input === " ") {
|
|
4681
|
+
const item = items[index];
|
|
4682
|
+
if (!item || item.disabled) return;
|
|
4683
|
+
setSelected((prev) => {
|
|
4684
|
+
const next = new Set(prev);
|
|
4685
|
+
if (next.has(item.value)) next.delete(item.value);
|
|
4686
|
+
else next.add(item.value);
|
|
4687
|
+
return next;
|
|
4688
|
+
});
|
|
4689
|
+
} else if (key.return) {
|
|
4690
|
+
const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);
|
|
4691
|
+
onSubmit(ordered);
|
|
4692
|
+
} else if (key.escape && onCancel) {
|
|
4693
|
+
onCancel();
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
4696
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, items.map((item, i) => {
|
|
4697
|
+
const checked = selected.has(item.value);
|
|
4698
|
+
const marker = checked ? "[x]" : "[ ]";
|
|
4699
|
+
return /* @__PURE__ */ React6.createElement(
|
|
4700
|
+
SelectRow,
|
|
4701
|
+
{
|
|
4702
|
+
key: item.value,
|
|
4703
|
+
item,
|
|
4704
|
+
active: i === index,
|
|
4705
|
+
marker: `${i === index ? "\u25B8" : " "} ${marker}`
|
|
4706
|
+
}
|
|
4707
|
+
);
|
|
4708
|
+
}), footer ? /* @__PURE__ */ React6.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, footer)) : null);
|
|
4709
|
+
}
|
|
4710
|
+
function SelectRow({
|
|
4711
|
+
item,
|
|
4712
|
+
active,
|
|
4713
|
+
marker
|
|
4714
|
+
}) {
|
|
4715
|
+
const color = item.disabled ? "gray" : active ? "cyan" : void 0;
|
|
4716
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text5, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, item.hint)) : null);
|
|
4717
|
+
}
|
|
4718
|
+
function findNextEnabled(items, from, step) {
|
|
4719
|
+
if (items.length === 0) return 0;
|
|
4720
|
+
let i = from;
|
|
4721
|
+
for (let tries = 0; tries < items.length; tries++) {
|
|
4722
|
+
i = (i + step + items.length) % items.length;
|
|
4723
|
+
if (!items[i]?.disabled) return i;
|
|
4724
|
+
}
|
|
4725
|
+
return from;
|
|
4726
|
+
}
|
|
4727
|
+
|
|
4728
|
+
// src/cli/ui/ShellConfirm.tsx
|
|
4729
|
+
function ShellConfirm({ command, allowPrefix, onChoose }) {
|
|
4730
|
+
return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(Box6, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "yellow" }, "\u25B8 model wants to run a shell command")), /* @__PURE__ */ React7.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "$ "), /* @__PURE__ */ React7.createElement(Text6, { color: "cyan" }, command))), /* @__PURE__ */ React7.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(
|
|
4731
|
+
SingleSelect,
|
|
4732
|
+
{
|
|
4733
|
+
initialValue: "run_once",
|
|
4734
|
+
items: [
|
|
4735
|
+
{
|
|
4736
|
+
value: "run_once",
|
|
4737
|
+
label: "Run once",
|
|
4738
|
+
hint: "Execute this command, don't remember it."
|
|
4739
|
+
},
|
|
4740
|
+
{
|
|
4741
|
+
value: "always_allow",
|
|
4742
|
+
label: `Always allow "${allowPrefix}" in this project`,
|
|
4743
|
+
hint: "Save the prefix to ~/.reasonix/config.json; future matches auto-run."
|
|
4744
|
+
},
|
|
4745
|
+
{
|
|
4746
|
+
value: "deny",
|
|
4747
|
+
label: "Deny",
|
|
4748
|
+
hint: "Tell the model the user refused; it will continue without this command."
|
|
4749
|
+
}
|
|
4750
|
+
],
|
|
4751
|
+
onSubmit: (v) => onChoose(v)
|
|
4752
|
+
}
|
|
4753
|
+
)));
|
|
4754
|
+
}
|
|
4755
|
+
function derivePrefix(command) {
|
|
4756
|
+
const tokens = command.trim().split(/\s+/).filter(Boolean);
|
|
4757
|
+
if (tokens.length === 0) return "";
|
|
4758
|
+
if (tokens.length === 1) return tokens[0];
|
|
4759
|
+
const first = tokens[0];
|
|
4760
|
+
const TWO_TOKEN_WRAPPERS = /* @__PURE__ */ new Set([
|
|
4761
|
+
"npm",
|
|
4762
|
+
"npx",
|
|
4763
|
+
"pnpm",
|
|
4764
|
+
"yarn",
|
|
4765
|
+
"bun",
|
|
4766
|
+
"git",
|
|
4767
|
+
"cargo",
|
|
4768
|
+
"go",
|
|
4769
|
+
"docker",
|
|
4770
|
+
"kubectl",
|
|
4771
|
+
"python",
|
|
4772
|
+
"python3",
|
|
4773
|
+
"deno",
|
|
4774
|
+
"pip",
|
|
4775
|
+
"pip3",
|
|
4776
|
+
"make",
|
|
4777
|
+
"rake",
|
|
4778
|
+
"bundle",
|
|
4779
|
+
"gem"
|
|
4780
|
+
]);
|
|
4781
|
+
return TWO_TOKEN_WRAPPERS.has(first) ? `${first} ${tokens[1]}` : first;
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4374
4784
|
// src/cli/ui/SlashSuggestions.tsx
|
|
4375
|
-
import { Box as
|
|
4376
|
-
import
|
|
4785
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
4786
|
+
import React8 from "react";
|
|
4377
4787
|
function SlashSuggestions({
|
|
4378
4788
|
matches,
|
|
4379
4789
|
selectedIndex
|
|
4380
4790
|
}) {
|
|
4381
4791
|
if (matches === null) return null;
|
|
4382
4792
|
if (matches.length === 0) {
|
|
4383
|
-
return /* @__PURE__ */
|
|
4793
|
+
return /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
|
|
4384
4794
|
}
|
|
4385
4795
|
const MAX = 8;
|
|
4386
4796
|
const total = matches.length;
|
|
@@ -4388,21 +4798,21 @@ function SlashSuggestions({
|
|
|
4388
4798
|
const shown = matches.slice(windowStart, windowStart + MAX);
|
|
4389
4799
|
const hiddenAbove = windowStart;
|
|
4390
4800
|
const hiddenBelow = total - windowStart - shown.length;
|
|
4391
|
-
return /* @__PURE__ */
|
|
4801
|
+
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React8.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2191/\u2193 navigate \xB7 Tab or Enter to pick"));
|
|
4392
4802
|
}
|
|
4393
4803
|
function SuggestionRow({ spec, isSelected }) {
|
|
4394
4804
|
const marker = isSelected ? "\u25B8" : " ";
|
|
4395
4805
|
const name = `/${spec.cmd}`;
|
|
4396
4806
|
const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
|
|
4397
4807
|
if (isSelected) {
|
|
4398
|
-
return /* @__PURE__ */
|
|
4808
|
+
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React8.createElement(Text7, { color: "cyan" }, " ", spec.summary));
|
|
4399
4809
|
}
|
|
4400
|
-
return /* @__PURE__ */
|
|
4810
|
+
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
|
|
4401
4811
|
}
|
|
4402
4812
|
|
|
4403
4813
|
// src/cli/ui/StatsPanel.tsx
|
|
4404
|
-
import { Box as
|
|
4405
|
-
import
|
|
4814
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
4815
|
+
import React9 from "react";
|
|
4406
4816
|
function StatsPanel({
|
|
4407
4817
|
summary,
|
|
4408
4818
|
model,
|
|
@@ -4417,7 +4827,7 @@ function StatsPanel({
|
|
|
4417
4827
|
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
4418
4828
|
const ctxRatio = summary.lastPromptTokens / ctxMax;
|
|
4419
4829
|
const ctxColor = ctxRatio >= 0.8 ? "red" : ctxRatio >= 0.5 ? "yellow" : void 0;
|
|
4420
|
-
return /* @__PURE__ */
|
|
4830
|
+
return /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Box8, { justifyContent: "space-between" }, /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan", bold: true }, "Reasonix"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \xB7 model "), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, model), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \xB7 prefix "), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, prefixHash), harvestOn ? /* @__PURE__ */ React9.createElement(Text8, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, " \xB7 branch", branchBudget) : null), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "turns ", summary.turns, " \xB7 type /help")), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "cache hit "), /* @__PURE__ */ React9.createElement(Text8, { color: hitColor, bold: true }, hitPct, "%")), /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "cost "), /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, "$", summary.totalCostUsd.toFixed(6)), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " (in ", "$", summary.totalInputCostUsd.toFixed(6), " \xB7 out ", "$", summary.totalOutputCostUsd.toFixed(6), ")")), summary.lastPromptTokens > 0 ? /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "ctx "), /* @__PURE__ */ React9.createElement(Text8, { color: ctxColor, bold: ctxColor !== void 0 }, formatTokens(summary.lastPromptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " (", (ctxRatio * 100).toFixed(0), "%)"), ctxRatio >= 0.8 ? /* @__PURE__ */ React9.createElement(Text8, { color: "red", bold: true }, " ", "\xB7 /compact") : null) : null, balance ? /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "balance "), /* @__PURE__ */ React9.createElement(Text8, { 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));
|
|
4421
4831
|
}
|
|
4422
4832
|
function formatTokens(n) {
|
|
4423
4833
|
if (n < 1e3) return String(n);
|
|
@@ -4440,6 +4850,7 @@ var SLASH_COMMANDS = [
|
|
|
4440
4850
|
{ cmd: "branch", argsHint: "<N|off>", summary: "run N parallel samples per turn (N>=2)" },
|
|
4441
4851
|
{ cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
|
|
4442
4852
|
{ cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
|
|
4853
|
+
{ cmd: "memory", summary: "show the project's REASONIX.md (pinned into the system prompt)" },
|
|
4443
4854
|
{ cmd: "think", summary: "dump the last turn's full R1 reasoning (reasoner only)" },
|
|
4444
4855
|
{ cmd: "retry", summary: "truncate & resend your last message (fresh sample)" },
|
|
4445
4856
|
{ cmd: "compact", argsHint: "[cap]", summary: "shrink oversized tool results in the log" },
|
|
@@ -4508,6 +4919,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
4508
4919
|
" /compact [cap] shrink large tool results in history (default 4k/result)",
|
|
4509
4920
|
" /think dump the most recent turn's full R1 reasoning (reasoner only)",
|
|
4510
4921
|
" /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
|
|
4922
|
+
" /memory show the project's REASONIX.md (pinned into the system prompt)",
|
|
4511
4923
|
" /retry truncate & resend your last message (fresh sample from the model)",
|
|
4512
4924
|
" /apply (code mode) commit the pending edit blocks to disk",
|
|
4513
4925
|
" /discard (code mode) drop pending edits without writing",
|
|
@@ -4589,6 +5001,45 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
4589
5001
|
resubmit: prev
|
|
4590
5002
|
};
|
|
4591
5003
|
}
|
|
5004
|
+
case "memory": {
|
|
5005
|
+
if (!memoryEnabled()) {
|
|
5006
|
+
return {
|
|
5007
|
+
info: "project memory is disabled (REASONIX_MEMORY=off in env). Unset the var to re-enable; no REASONIX.md will be pinned in the meantime."
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
5010
|
+
if (!ctx.memoryRoot) {
|
|
5011
|
+
return {
|
|
5012
|
+
info: "no project root on this session \u2014 `/memory` needs a working directory to resolve REASONIX.md from."
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
const mem = readProjectMemory(ctx.memoryRoot);
|
|
5016
|
+
if (!mem) {
|
|
5017
|
+
return {
|
|
5018
|
+
info: [
|
|
5019
|
+
`no ${PROJECT_MEMORY_FILE} in ${ctx.memoryRoot}.`,
|
|
5020
|
+
"",
|
|
5021
|
+
"Project memory is an optional file you pin notes into \u2014 project conventions,",
|
|
5022
|
+
"things the model keeps forgetting, domain glossary, setup gotchas. When present,",
|
|
5023
|
+
"its contents are appended to the system prompt (the immutable-prefix region)",
|
|
5024
|
+
"so every turn sees it without eating per-turn context, and the prefix cache stays",
|
|
5025
|
+
"warm as long as the file is stable.",
|
|
5026
|
+
"",
|
|
5027
|
+
`Create it with: echo "# Project notes for Reasonix" > ${PROJECT_MEMORY_FILE}`,
|
|
5028
|
+
"Re-launch (or `/new`) to pick up changes \u2014 the prefix is hashed at session start."
|
|
5029
|
+
].join("\n")
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
5032
|
+
const header = mem.truncated ? `\u25B8 project memory: ${mem.path} (${mem.originalChars.toLocaleString()} chars, truncated for the prefix)` : `\u25B8 project memory: ${mem.path} (${mem.originalChars.toLocaleString()} chars)`;
|
|
5033
|
+
return {
|
|
5034
|
+
info: [
|
|
5035
|
+
header,
|
|
5036
|
+
"",
|
|
5037
|
+
mem.content,
|
|
5038
|
+
"",
|
|
5039
|
+
"Changes take effect on the next launch or `/new` \u2014 the system prompt is hashed once per session to keep the prefix cache warm."
|
|
5040
|
+
].join("\n")
|
|
5041
|
+
};
|
|
5042
|
+
}
|
|
4592
5043
|
case "think":
|
|
4593
5044
|
case "reasoning": {
|
|
4594
5045
|
const raw = loop.scratch.reasoning;
|
|
@@ -4881,23 +5332,25 @@ function App({
|
|
|
4881
5332
|
codeMode
|
|
4882
5333
|
}) {
|
|
4883
5334
|
const { exit } = useApp();
|
|
4884
|
-
const [historical, setHistorical] =
|
|
4885
|
-
const [streaming, setStreaming] =
|
|
4886
|
-
const [input, setInput] =
|
|
4887
|
-
const [busy, setBusy] =
|
|
5335
|
+
const [historical, setHistorical] = useState4([]);
|
|
5336
|
+
const [streaming, setStreaming] = useState4(null);
|
|
5337
|
+
const [input, setInput] = useState4("");
|
|
5338
|
+
const [busy, setBusy] = useState4(false);
|
|
4888
5339
|
const abortedThisTurn = useRef2(false);
|
|
4889
|
-
const [ongoingTool, setOngoingTool] =
|
|
4890
|
-
const [toolProgress, setToolProgress] =
|
|
4891
|
-
const [statusLine, setStatusLine] =
|
|
4892
|
-
const [balance, setBalance] =
|
|
5340
|
+
const [ongoingTool, setOngoingTool] = useState4(null);
|
|
5341
|
+
const [toolProgress, setToolProgress] = useState4(null);
|
|
5342
|
+
const [statusLine, setStatusLine] = useState4(null);
|
|
5343
|
+
const [balance, setBalance] = useState4(null);
|
|
4893
5344
|
const lastEditSnapshots = useRef2(null);
|
|
4894
5345
|
const pendingEdits = useRef2([]);
|
|
5346
|
+
const [pendingShell, setPendingShell] = useState4(null);
|
|
5347
|
+
const [queuedSubmit, setQueuedSubmit] = useState4(null);
|
|
4895
5348
|
const promptHistory = useRef2([]);
|
|
4896
5349
|
const historyCursor = useRef2(-1);
|
|
4897
5350
|
const assistantIterCounter = useRef2(0);
|
|
4898
5351
|
const toolHistoryRef = useRef2([]);
|
|
4899
|
-
const [slashSelected, setSlashSelected] =
|
|
4900
|
-
const [summary, setSummary] =
|
|
5352
|
+
const [slashSelected, setSlashSelected] = useState4(0);
|
|
5353
|
+
const [summary, setSummary] = useState4({
|
|
4901
5354
|
turns: 0,
|
|
4902
5355
|
totalCostUsd: 0,
|
|
4903
5356
|
totalInputCostUsd: 0,
|
|
@@ -5002,7 +5455,7 @@ function App({
|
|
|
5002
5455
|
]);
|
|
5003
5456
|
}
|
|
5004
5457
|
}, [session, loop]);
|
|
5005
|
-
|
|
5458
|
+
useInput3((_input, key) => {
|
|
5006
5459
|
if (key.escape && busy) {
|
|
5007
5460
|
if (abortedThisTurn.current) return;
|
|
5008
5461
|
abortedThisTurn.current = true;
|
|
@@ -5010,6 +5463,7 @@ function App({
|
|
|
5010
5463
|
return;
|
|
5011
5464
|
}
|
|
5012
5465
|
if (busy) return;
|
|
5466
|
+
if (pendingShell) return;
|
|
5013
5467
|
if (slashMatches && slashMatches.length > 0) {
|
|
5014
5468
|
if (key.upArrow) {
|
|
5015
5469
|
setSlashSelected((i) => Math.max(0, i - 1));
|
|
@@ -5112,7 +5566,8 @@ function App({
|
|
|
5112
5566
|
codeDiscard: codeMode ? codeDiscard : void 0,
|
|
5113
5567
|
codeRoot: codeMode?.rootDir,
|
|
5114
5568
|
pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
|
|
5115
|
-
toolHistory: () => toolHistoryRef.current
|
|
5569
|
+
toolHistory: () => toolHistoryRef.current,
|
|
5570
|
+
memoryRoot: codeMode?.rootDir ?? process.cwd()
|
|
5116
5571
|
});
|
|
5117
5572
|
if (result.exit) {
|
|
5118
5573
|
transcriptRef.current?.end();
|
|
@@ -5279,6 +5734,15 @@ function App({
|
|
|
5279
5734
|
toolName: ev.toolName
|
|
5280
5735
|
}
|
|
5281
5736
|
]);
|
|
5737
|
+
if (codeMode && ev.toolName === "run_command" && ev.content.includes('"NeedsConfirmationError:') && ev.toolArgs) {
|
|
5738
|
+
try {
|
|
5739
|
+
const parsed = JSON.parse(ev.toolArgs);
|
|
5740
|
+
if (typeof parsed.command === "string" && parsed.command.trim()) {
|
|
5741
|
+
setPendingShell(parsed.command.trim());
|
|
5742
|
+
}
|
|
5743
|
+
} catch {
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5282
5746
|
} else if (ev.role === "error") {
|
|
5283
5747
|
setHistorical((prev) => [
|
|
5284
5748
|
...prev,
|
|
@@ -5323,7 +5787,68 @@ function App({
|
|
|
5323
5787
|
writeTranscript
|
|
5324
5788
|
]
|
|
5325
5789
|
);
|
|
5326
|
-
|
|
5790
|
+
const handleShellConfirm = useCallback(
|
|
5791
|
+
async (choice) => {
|
|
5792
|
+
const cmd = pendingShell;
|
|
5793
|
+
if (!cmd || !codeMode) return;
|
|
5794
|
+
setPendingShell(null);
|
|
5795
|
+
let synthetic;
|
|
5796
|
+
if (choice === "deny") {
|
|
5797
|
+
setHistorical((prev) => [
|
|
5798
|
+
...prev,
|
|
5799
|
+
{ id: `sh-deny-${Date.now()}`, role: "info", text: `\u25B8 denied: ${cmd}` }
|
|
5800
|
+
]);
|
|
5801
|
+
synthetic = `I denied running \`${cmd}\`. Please continue without running it.`;
|
|
5802
|
+
} else {
|
|
5803
|
+
if (choice === "always_allow") {
|
|
5804
|
+
const prefix = derivePrefix(cmd);
|
|
5805
|
+
addProjectShellAllowed(codeMode.rootDir, prefix);
|
|
5806
|
+
setHistorical((prev) => [
|
|
5807
|
+
...prev,
|
|
5808
|
+
{
|
|
5809
|
+
id: `sh-allow-${Date.now()}`,
|
|
5810
|
+
role: "info",
|
|
5811
|
+
text: `\u25B8 always allowed "${prefix}" for ${codeMode.rootDir}`
|
|
5812
|
+
}
|
|
5813
|
+
]);
|
|
5814
|
+
}
|
|
5815
|
+
setHistorical((prev) => [
|
|
5816
|
+
...prev,
|
|
5817
|
+
{ id: `sh-run-${Date.now()}`, role: "info", text: `\u25B8 running: ${cmd}` }
|
|
5818
|
+
]);
|
|
5819
|
+
let body;
|
|
5820
|
+
try {
|
|
5821
|
+
const res = await runCommand(cmd, { cwd: codeMode.rootDir });
|
|
5822
|
+
body = formatCommandResult(cmd, res);
|
|
5823
|
+
} catch (err) {
|
|
5824
|
+
body = `$ ${cmd}
|
|
5825
|
+
[failed to spawn] ${err.message}`;
|
|
5826
|
+
}
|
|
5827
|
+
setHistorical((prev) => [
|
|
5828
|
+
...prev,
|
|
5829
|
+
{ id: `sh-out-${Date.now()}`, role: "info", text: body }
|
|
5830
|
+
]);
|
|
5831
|
+
synthetic = `I ran the command you requested. Output:
|
|
5832
|
+
|
|
5833
|
+
${body}`;
|
|
5834
|
+
}
|
|
5835
|
+
if (busy) {
|
|
5836
|
+
loop.abort();
|
|
5837
|
+
setQueuedSubmit(synthetic);
|
|
5838
|
+
} else {
|
|
5839
|
+
await handleSubmit(synthetic);
|
|
5840
|
+
}
|
|
5841
|
+
},
|
|
5842
|
+
[pendingShell, codeMode, handleSubmit, busy, loop]
|
|
5843
|
+
);
|
|
5844
|
+
useEffect2(() => {
|
|
5845
|
+
if (!busy && queuedSubmit !== null) {
|
|
5846
|
+
const text = queuedSubmit;
|
|
5847
|
+
setQueuedSubmit(null);
|
|
5848
|
+
void handleSubmit(text);
|
|
5849
|
+
}
|
|
5850
|
+
}, [busy, queuedSubmit, handleSubmit]);
|
|
5851
|
+
return /* @__PURE__ */ React10.createElement(TickerProvider, { disabled: PLAIN_UI }, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(
|
|
5327
5852
|
StatsPanel,
|
|
5328
5853
|
{
|
|
5329
5854
|
summary,
|
|
@@ -5333,13 +5858,28 @@ function App({
|
|
|
5333
5858
|
branchBudget: loop.branchOptions.budget,
|
|
5334
5859
|
balance
|
|
5335
5860
|
}
|
|
5336
|
-
), /* @__PURE__ */
|
|
5861
|
+
), /* @__PURE__ */ React10.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React10.createElement(EventRow, { key: item.id, event: item })), !PLAIN_UI && !pendingShell && streaming ? /* @__PURE__ */ React10.createElement(Box9, { marginY: 1 }, /* @__PURE__ */ React10.createElement(EventRow, { event: streaming })) : null, !PLAIN_UI && !pendingShell && ongoingTool ? /* @__PURE__ */ React10.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !ongoingTool && statusLine ? /* @__PURE__ */ React10.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && !pendingShell && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React10.createElement(StatusRow, { text: "processing\u2026" }) : null, pendingShell ? /* @__PURE__ */ React10.createElement(
|
|
5862
|
+
ShellConfirm,
|
|
5863
|
+
{
|
|
5864
|
+
command: pendingShell,
|
|
5865
|
+
allowPrefix: derivePrefix(pendingShell),
|
|
5866
|
+
onChoose: handleShellConfirm
|
|
5867
|
+
}
|
|
5868
|
+
) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
|
|
5869
|
+
PromptInput,
|
|
5870
|
+
{
|
|
5871
|
+
value: input,
|
|
5872
|
+
onChange: setInput,
|
|
5873
|
+
onSubmit: handleSubmit,
|
|
5874
|
+
disabled: busy
|
|
5875
|
+
}
|
|
5876
|
+
), /* @__PURE__ */ React10.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }))));
|
|
5337
5877
|
}
|
|
5338
5878
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5339
5879
|
function StatusRow({ text }) {
|
|
5340
5880
|
const tick = useTick();
|
|
5341
5881
|
const elapsed = useElapsedSeconds();
|
|
5342
|
-
return /* @__PURE__ */
|
|
5882
|
+
return /* @__PURE__ */ React10.createElement(Box9, { marginY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${elapsed}s`));
|
|
5343
5883
|
}
|
|
5344
5884
|
function OngoingToolRow({
|
|
5345
5885
|
tool,
|
|
@@ -5348,7 +5888,7 @@ function OngoingToolRow({
|
|
|
5348
5888
|
const tick = useTick();
|
|
5349
5889
|
const elapsed = useElapsedSeconds();
|
|
5350
5890
|
const summary = summarizeToolArgs(tool.name, tool.args);
|
|
5351
|
-
return /* @__PURE__ */
|
|
5891
|
+
return /* @__PURE__ */ React10.createElement(Box9, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, summary)) : null);
|
|
5352
5892
|
}
|
|
5353
5893
|
function renderProgressLine(p) {
|
|
5354
5894
|
const msg = p.message ? ` ${p.message}` : "";
|
|
@@ -5443,13 +5983,60 @@ function describeRepair(repair) {
|
|
|
5443
5983
|
return parts.length ? `[repair] ${parts.join(", ")}` : "";
|
|
5444
5984
|
}
|
|
5445
5985
|
|
|
5986
|
+
// src/cli/ui/SessionPicker.tsx
|
|
5987
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
5988
|
+
import React11 from "react";
|
|
5989
|
+
function SessionPicker({
|
|
5990
|
+
sessionName,
|
|
5991
|
+
messageCount,
|
|
5992
|
+
lastActive,
|
|
5993
|
+
onChoose
|
|
5994
|
+
}) {
|
|
5995
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` \xB7 last active ${relativeTime(lastActive)}`)), /* @__PURE__ */ React11.createElement(
|
|
5996
|
+
SingleSelect,
|
|
5997
|
+
{
|
|
5998
|
+
initialValue: "new",
|
|
5999
|
+
items: [
|
|
6000
|
+
{
|
|
6001
|
+
value: "new",
|
|
6002
|
+
label: "Start new conversation",
|
|
6003
|
+
hint: "Previous messages kept on disk; your turn starts fresh."
|
|
6004
|
+
},
|
|
6005
|
+
{
|
|
6006
|
+
value: "resume",
|
|
6007
|
+
label: "Resume",
|
|
6008
|
+
hint: `Continue where you left off (${messageCount} messages in context).`
|
|
6009
|
+
},
|
|
6010
|
+
{
|
|
6011
|
+
value: "delete",
|
|
6012
|
+
label: "Delete and start new",
|
|
6013
|
+
hint: "Wipes the session file irreversibly. Other sessions untouched."
|
|
6014
|
+
}
|
|
6015
|
+
],
|
|
6016
|
+
onSubmit: (v) => onChoose(v)
|
|
6017
|
+
}
|
|
6018
|
+
), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "\u2191\u2193 to move \xB7 Enter to pick")));
|
|
6019
|
+
}
|
|
6020
|
+
function relativeTime(date) {
|
|
6021
|
+
const ms = Date.now() - date.getTime();
|
|
6022
|
+
const mins = Math.floor(ms / 6e4);
|
|
6023
|
+
if (mins < 1) return "just now";
|
|
6024
|
+
if (mins < 60) return `${mins}m ago`;
|
|
6025
|
+
const hours = Math.floor(mins / 60);
|
|
6026
|
+
if (hours < 24) return `${hours}h ago`;
|
|
6027
|
+
const days = Math.floor(hours / 24);
|
|
6028
|
+
if (days === 1) return "yesterday";
|
|
6029
|
+
if (days < 7) return `${days}d ago`;
|
|
6030
|
+
return date.toISOString().slice(0, 10);
|
|
6031
|
+
}
|
|
6032
|
+
|
|
5446
6033
|
// src/cli/ui/Setup.tsx
|
|
5447
|
-
import { Box as
|
|
6034
|
+
import { Box as Box11, Text as Text11, useApp as useApp2 } from "ink";
|
|
5448
6035
|
import TextInput from "ink-text-input";
|
|
5449
|
-
import
|
|
6036
|
+
import React12, { useState as useState5 } from "react";
|
|
5450
6037
|
function Setup({ onReady }) {
|
|
5451
|
-
const [value, setValue] =
|
|
5452
|
-
const [error, setError] =
|
|
6038
|
+
const [value, setValue] = useState5("");
|
|
6039
|
+
const [error, setError] = useState5(null);
|
|
5453
6040
|
const { exit } = useApp2();
|
|
5454
6041
|
const handleSubmit = (raw) => {
|
|
5455
6042
|
const trimmed = raw.trim();
|
|
@@ -5470,7 +6057,7 @@ function Setup({ onReady }) {
|
|
|
5470
6057
|
}
|
|
5471
6058
|
onReady(trimmed);
|
|
5472
6059
|
};
|
|
5473
|
-
return /* @__PURE__ */
|
|
6060
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React12.createElement(
|
|
5474
6061
|
TextInput,
|
|
5475
6062
|
{
|
|
5476
6063
|
value,
|
|
@@ -5479,14 +6066,23 @@ function Setup({ onReady }) {
|
|
|
5479
6066
|
mask: "\u2022",
|
|
5480
6067
|
placeholder: "sk-..."
|
|
5481
6068
|
}
|
|
5482
|
-
)), error ? /* @__PURE__ */
|
|
6069
|
+
)), error ? /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red" }, error)) : value ? /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "(Type /exit to abort.)")));
|
|
5483
6070
|
}
|
|
5484
6071
|
|
|
5485
6072
|
// src/cli/commands/chat.tsx
|
|
5486
|
-
function Root({
|
|
5487
|
-
|
|
6073
|
+
function Root({
|
|
6074
|
+
initialKey,
|
|
6075
|
+
tools,
|
|
6076
|
+
mcpSpecs,
|
|
6077
|
+
mcpServers,
|
|
6078
|
+
progressSink,
|
|
6079
|
+
sessionPreview,
|
|
6080
|
+
...appProps
|
|
6081
|
+
}) {
|
|
6082
|
+
const [key, setKey] = useState6(initialKey);
|
|
6083
|
+
const [pending, setPending] = useState6(sessionPreview);
|
|
5488
6084
|
if (!key) {
|
|
5489
|
-
return /* @__PURE__ */
|
|
6085
|
+
return /* @__PURE__ */ React13.createElement(
|
|
5490
6086
|
Setup,
|
|
5491
6087
|
{
|
|
5492
6088
|
onReady: (k) => {
|
|
@@ -5497,7 +6093,23 @@ function Root({ initialKey, tools, mcpSpecs, mcpServers, progressSink, ...appPro
|
|
|
5497
6093
|
);
|
|
5498
6094
|
}
|
|
5499
6095
|
process.env.DEEPSEEK_API_KEY = key;
|
|
5500
|
-
|
|
6096
|
+
if (pending && appProps.session) {
|
|
6097
|
+
return /* @__PURE__ */ React13.createElement(
|
|
6098
|
+
SessionPicker,
|
|
6099
|
+
{
|
|
6100
|
+
sessionName: appProps.session,
|
|
6101
|
+
messageCount: pending.messageCount,
|
|
6102
|
+
lastActive: pending.lastActive,
|
|
6103
|
+
onChoose: (choice) => {
|
|
6104
|
+
if (choice === "new" || choice === "delete") {
|
|
6105
|
+
rewriteSession(appProps.session, []);
|
|
6106
|
+
}
|
|
6107
|
+
setPending(void 0);
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
);
|
|
6111
|
+
}
|
|
6112
|
+
return /* @__PURE__ */ React13.createElement(
|
|
5501
6113
|
App,
|
|
5502
6114
|
{
|
|
5503
6115
|
model: appProps.model,
|
|
@@ -5584,8 +6196,19 @@ async function chatCommand(opts) {
|
|
|
5584
6196
|
if (!tools) tools = new ToolRegistry();
|
|
5585
6197
|
registerWebTools(tools);
|
|
5586
6198
|
}
|
|
6199
|
+
let sessionPreview;
|
|
6200
|
+
if (opts.session && !opts.forceResume && !opts.forceNew) {
|
|
6201
|
+
const prior = loadSessionMessages(opts.session);
|
|
6202
|
+
if (prior.length > 0) {
|
|
6203
|
+
const p = sessionPath(opts.session);
|
|
6204
|
+
const mtime = existsSync3(p) ? statSync2(p).mtime : /* @__PURE__ */ new Date();
|
|
6205
|
+
sessionPreview = { messageCount: prior.length, lastActive: mtime };
|
|
6206
|
+
}
|
|
6207
|
+
} else if (opts.session && opts.forceNew) {
|
|
6208
|
+
rewriteSession(opts.session, []);
|
|
6209
|
+
}
|
|
5587
6210
|
const { waitUntilExit } = render(
|
|
5588
|
-
/* @__PURE__ */
|
|
6211
|
+
/* @__PURE__ */ React13.createElement(
|
|
5589
6212
|
Root,
|
|
5590
6213
|
{
|
|
5591
6214
|
initialKey,
|
|
@@ -5593,6 +6216,7 @@ async function chatCommand(opts) {
|
|
|
5593
6216
|
mcpSpecs,
|
|
5594
6217
|
mcpServers,
|
|
5595
6218
|
progressSink,
|
|
6219
|
+
sessionPreview,
|
|
5596
6220
|
...opts
|
|
5597
6221
|
}
|
|
5598
6222
|
),
|
|
@@ -5608,15 +6232,21 @@ async function chatCommand(opts) {
|
|
|
5608
6232
|
}
|
|
5609
6233
|
|
|
5610
6234
|
// src/cli/commands/code.tsx
|
|
5611
|
-
import { basename, resolve as
|
|
6235
|
+
import { basename, resolve as resolve5 } from "path";
|
|
5612
6236
|
async function codeCommand(opts = {}) {
|
|
5613
|
-
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-
|
|
5614
|
-
const rootDir =
|
|
6237
|
+
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-HK5XLH55.js");
|
|
6238
|
+
const rootDir = resolve5(opts.dir ?? process.cwd());
|
|
5615
6239
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
|
|
5616
6240
|
const tools = new ToolRegistry();
|
|
5617
6241
|
registerFilesystemTools(tools, { rootDir });
|
|
6242
|
+
registerShellTools(tools, {
|
|
6243
|
+
rootDir,
|
|
6244
|
+
// Per-project "always allow" list persisted from prior ShellConfirm
|
|
6245
|
+
// choices; merged on top of the built-in allowlist in shell.ts.
|
|
6246
|
+
extraAllowed: loadProjectShellAllowed(rootDir)
|
|
6247
|
+
});
|
|
5618
6248
|
process.stderr.write(
|
|
5619
|
-
`\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native
|
|
6249
|
+
`\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)
|
|
5620
6250
|
`
|
|
5621
6251
|
);
|
|
5622
6252
|
await chatCommand({
|
|
@@ -5627,7 +6257,9 @@ async function codeCommand(opts = {}) {
|
|
|
5627
6257
|
transcript: opts.transcript,
|
|
5628
6258
|
session,
|
|
5629
6259
|
seedTools: tools,
|
|
5630
|
-
codeMode: { rootDir }
|
|
6260
|
+
codeMode: { rootDir },
|
|
6261
|
+
forceResume: opts.forceResume,
|
|
6262
|
+
forceNew: opts.forceNew
|
|
5631
6263
|
});
|
|
5632
6264
|
}
|
|
5633
6265
|
|
|
@@ -5635,34 +6267,34 @@ async function codeCommand(opts = {}) {
|
|
|
5635
6267
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
5636
6268
|
import { basename as basename2 } from "path";
|
|
5637
6269
|
import { render as render2 } from "ink";
|
|
5638
|
-
import
|
|
6270
|
+
import React16 from "react";
|
|
5639
6271
|
|
|
5640
6272
|
// src/cli/ui/DiffApp.tsx
|
|
5641
|
-
import { Box as
|
|
5642
|
-
import
|
|
6273
|
+
import { Box as Box13, Static as Static2, Text as Text13, useApp as useApp3, useInput as useInput4 } from "ink";
|
|
6274
|
+
import React15, { useState as useState7 } from "react";
|
|
5643
6275
|
|
|
5644
6276
|
// src/cli/ui/RecordView.tsx
|
|
5645
|
-
import { Box as
|
|
5646
|
-
import
|
|
6277
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
6278
|
+
import React14 from "react";
|
|
5647
6279
|
function RecordView({ rec, compact = false }) {
|
|
5648
6280
|
const toolArgsMax = compact ? 120 : 200;
|
|
5649
6281
|
const toolContentMax = compact ? 200 : 400;
|
|
5650
6282
|
if (rec.role === "user") {
|
|
5651
|
-
return /* @__PURE__ */
|
|
6283
|
+
return /* @__PURE__ */ React14.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text12, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React14.createElement(Text12, null, rec.content));
|
|
5652
6284
|
}
|
|
5653
6285
|
if (rec.role === "assistant_final") {
|
|
5654
|
-
return /* @__PURE__ */
|
|
6286
|
+
return /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React14.createElement(Box12, null, /* @__PURE__ */ React14.createElement(Text12, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React14.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React14.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React14.createElement(Text12, null, rec.content) : /* @__PURE__ */ React14.createElement(Text12, { dimColor: true, italic: true }, "(tool-call response only)"));
|
|
5655
6287
|
}
|
|
5656
6288
|
if (rec.role === "tool") {
|
|
5657
|
-
return /* @__PURE__ */
|
|
6289
|
+
return /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text12, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
|
|
5658
6290
|
}
|
|
5659
6291
|
if (rec.role === "error") {
|
|
5660
|
-
return /* @__PURE__ */
|
|
6292
|
+
return /* @__PURE__ */ React14.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text12, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React14.createElement(Text12, { color: "red" }, rec.error ?? rec.content));
|
|
5661
6293
|
}
|
|
5662
6294
|
if (rec.role === "done" || rec.role === "assistant_delta") {
|
|
5663
6295
|
return null;
|
|
5664
6296
|
}
|
|
5665
|
-
return /* @__PURE__ */
|
|
6297
|
+
return /* @__PURE__ */ React14.createElement(Box12, null, /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, "[", rec.role, "] ", rec.content));
|
|
5666
6298
|
}
|
|
5667
6299
|
function CacheBadge({ usage }) {
|
|
5668
6300
|
const hit = usage.prompt_cache_hit_tokens ?? 0;
|
|
@@ -5671,7 +6303,7 @@ function CacheBadge({ usage }) {
|
|
|
5671
6303
|
if (total === 0) return null;
|
|
5672
6304
|
const pct2 = hit / total * 100;
|
|
5673
6305
|
const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
|
|
5674
|
-
return /* @__PURE__ */
|
|
6306
|
+
return /* @__PURE__ */ React14.createElement(Text12, null, /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React14.createElement(Text12, { color }, pct2.toFixed(1), "%"));
|
|
5675
6307
|
}
|
|
5676
6308
|
function truncate3(s, max) {
|
|
5677
6309
|
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
|
|
@@ -5682,8 +6314,8 @@ function DiffApp({ report }) {
|
|
|
5682
6314
|
const { exit } = useApp3();
|
|
5683
6315
|
const maxIdx = Math.max(0, report.pairs.length - 1);
|
|
5684
6316
|
const initialIdx = report.firstDivergenceTurn ? report.pairs.findIndex((p) => p.turn === report.firstDivergenceTurn) : 0;
|
|
5685
|
-
const [idx, setIdx] =
|
|
5686
|
-
|
|
6317
|
+
const [idx, setIdx] = useState7(Math.max(0, initialIdx));
|
|
6318
|
+
useInput4((input, key) => {
|
|
5687
6319
|
if (input === "q" || key.ctrl && input === "c") {
|
|
5688
6320
|
exit();
|
|
5689
6321
|
return;
|
|
@@ -5705,7 +6337,7 @@ function DiffApp({ report }) {
|
|
|
5705
6337
|
}
|
|
5706
6338
|
});
|
|
5707
6339
|
const pair = report.pairs[idx];
|
|
5708
|
-
return /* @__PURE__ */
|
|
6340
|
+
return /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(DiffHeader, { report }), /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React15.createElement(Text13, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React15.createElement(Text13, null, pair ? /* @__PURE__ */ React15.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React15.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React15.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text13, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React15.createElement(Text13, null, pair.divergenceNote)) : null, /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "j"), "/", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "k"), "/", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "N"), "/", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "g"), "/", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React15.createElement(Text13, { bold: true }, "q"), " ", "quit")));
|
|
5709
6341
|
}
|
|
5710
6342
|
function DiffHeader({ report }) {
|
|
5711
6343
|
const a = report.a;
|
|
@@ -5723,15 +6355,15 @@ function DiffHeader({ report }) {
|
|
|
5723
6355
|
} else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
|
|
5724
6356
|
prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
|
|
5725
6357
|
}
|
|
5726
|
-
return /* @__PURE__ */
|
|
6358
|
+
return /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Box13, { justifyContent: "space-between" }, /* @__PURE__ */ React15.createElement(Text13, null, /* @__PURE__ */ React15.createElement(Text13, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React15.createElement(Text13, { color: "blue" }, a.label), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, " vs B="), /* @__PURE__ */ React15.createElement(Text13, { color: "magenta" }, b.label)), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React15.createElement(Text13, null, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, "cache "), /* @__PURE__ */ React15.createElement(Text13, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React15.createElement(Text13, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React15.createElement(Text13, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React15.createElement(Text13, null, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, "cost "), /* @__PURE__ */ React15.createElement(Text13, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React15.createElement(Text13, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React15.createElement(Text13, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React15.createElement(Text13, null, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, "model calls "), /* @__PURE__ */ React15.createElement(Text13, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true, italic: true }, prefixLine)) : null);
|
|
5727
6359
|
}
|
|
5728
6360
|
function Pane({
|
|
5729
6361
|
label,
|
|
5730
6362
|
headerColor,
|
|
5731
6363
|
records
|
|
5732
6364
|
}) {
|
|
5733
|
-
return /* @__PURE__ */
|
|
5734
|
-
|
|
6365
|
+
return /* @__PURE__ */ React15.createElement(
|
|
6366
|
+
Box13,
|
|
5735
6367
|
{
|
|
5736
6368
|
flexDirection: "column",
|
|
5737
6369
|
flexGrow: 1,
|
|
@@ -5739,21 +6371,21 @@ function Pane({
|
|
|
5739
6371
|
borderStyle: "single",
|
|
5740
6372
|
borderColor: headerColor
|
|
5741
6373
|
},
|
|
5742
|
-
/* @__PURE__ */
|
|
5743
|
-
records.length === 0 ? /* @__PURE__ */
|
|
6374
|
+
/* @__PURE__ */ React15.createElement(Text13, { color: headerColor, bold: true }, label),
|
|
6375
|
+
records.length === 0 ? /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React15.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React15.createElement(RecordView, { key, rec, compact: true }))
|
|
5744
6376
|
);
|
|
5745
6377
|
}
|
|
5746
6378
|
function KindBadge({ kind }) {
|
|
5747
6379
|
if (kind === "match") {
|
|
5748
|
-
return /* @__PURE__ */
|
|
6380
|
+
return /* @__PURE__ */ React15.createElement(Text13, { color: "green" }, "\u2713 match");
|
|
5749
6381
|
}
|
|
5750
6382
|
if (kind === "diverge") {
|
|
5751
|
-
return /* @__PURE__ */
|
|
6383
|
+
return /* @__PURE__ */ React15.createElement(Text13, { color: "yellow" }, "\u2605 diverge");
|
|
5752
6384
|
}
|
|
5753
6385
|
if (kind === "only_in_a") {
|
|
5754
|
-
return /* @__PURE__ */
|
|
6386
|
+
return /* @__PURE__ */ React15.createElement(Text13, { color: "blue" }, "\u2190 only in A");
|
|
5755
6387
|
}
|
|
5756
|
-
return /* @__PURE__ */
|
|
6388
|
+
return /* @__PURE__ */ React15.createElement(Text13, { color: "magenta" }, "\u2192 only in B");
|
|
5757
6389
|
}
|
|
5758
6390
|
function paneRecords(pair, side) {
|
|
5759
6391
|
if (!pair) return [];
|
|
@@ -5784,7 +6416,7 @@ markdown report written to ${opts.mdPath}`);
|
|
|
5784
6416
|
return;
|
|
5785
6417
|
}
|
|
5786
6418
|
if (wantTui) {
|
|
5787
|
-
const { waitUntilExit } = render2(
|
|
6419
|
+
const { waitUntilExit } = render2(React16.createElement(DiffApp, { report }), {
|
|
5788
6420
|
exitOnCtrlC: true,
|
|
5789
6421
|
patchConsole: false
|
|
5790
6422
|
});
|
|
@@ -5925,16 +6557,16 @@ function pad(s, width) {
|
|
|
5925
6557
|
|
|
5926
6558
|
// src/cli/commands/replay.ts
|
|
5927
6559
|
import { render as render3 } from "ink";
|
|
5928
|
-
import
|
|
6560
|
+
import React18 from "react";
|
|
5929
6561
|
|
|
5930
6562
|
// src/cli/ui/ReplayApp.tsx
|
|
5931
|
-
import { Box as
|
|
5932
|
-
import
|
|
6563
|
+
import { Box as Box14, Static as Static3, Text as Text14, useApp as useApp4, useInput as useInput5 } from "ink";
|
|
6564
|
+
import React17, { useMemo as useMemo2, useState as useState8 } from "react";
|
|
5933
6565
|
function ReplayApp({ meta, pages }) {
|
|
5934
6566
|
const { exit } = useApp4();
|
|
5935
6567
|
const maxIdx = Math.max(0, pages.length - 1);
|
|
5936
|
-
const [idx, setIdx] =
|
|
5937
|
-
|
|
6568
|
+
const [idx, setIdx] = useState8(maxIdx);
|
|
6569
|
+
useInput5((input, key) => {
|
|
5938
6570
|
if (input === "q" || key.ctrl && input === "c") {
|
|
5939
6571
|
exit();
|
|
5940
6572
|
return;
|
|
@@ -5968,14 +6600,14 @@ function ReplayApp({ meta, pages }) {
|
|
|
5968
6600
|
const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
|
|
5969
6601
|
const currentPage = pages[idx];
|
|
5970
6602
|
const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
|
|
5971
|
-
return /* @__PURE__ */
|
|
6603
|
+
return /* @__PURE__ */ React17.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React17.createElement(
|
|
5972
6604
|
StatsPanel,
|
|
5973
6605
|
{
|
|
5974
6606
|
summary,
|
|
5975
6607
|
model: cumStats.models[0] ?? meta?.model ?? "?",
|
|
5976
6608
|
prefixHash
|
|
5977
6609
|
}
|
|
5978
|
-
), /* @__PURE__ */
|
|
6610
|
+
), /* @__PURE__ */ React17.createElement(Box14, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React17.createElement(Box14, { justifyContent: "space-between" }, /* @__PURE__ */ React17.createElement(Text14, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React17.createElement(Text14, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React17.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React17.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React17.createElement(Text14, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React17.createElement(Box14, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React17.createElement(Text14, { dimColor: true }, /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "j"), "/", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "k"), "/", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React17.createElement(Text14, { bold: true }, "q"), " quit")));
|
|
5979
6611
|
}
|
|
5980
6612
|
|
|
5981
6613
|
// src/cli/commands/replay.ts
|
|
@@ -5987,7 +6619,7 @@ async function replayCommand(opts) {
|
|
|
5987
6619
|
}
|
|
5988
6620
|
const { parsed } = replayFromFile(opts.path);
|
|
5989
6621
|
const pages = groupRecordsByTurn(parsed.records);
|
|
5990
|
-
const { waitUntilExit } = render3(
|
|
6622
|
+
const { waitUntilExit } = render3(React18.createElement(ReplayApp, { meta: parsed.meta, pages }), {
|
|
5991
6623
|
exitOnCtrlC: true,
|
|
5992
6624
|
patchConsole: false
|
|
5993
6625
|
});
|
|
@@ -6114,7 +6746,7 @@ async function ensureApiKey() {
|
|
|
6114
6746
|
rl.close();
|
|
6115
6747
|
}
|
|
6116
6748
|
}
|
|
6117
|
-
async function
|
|
6749
|
+
async function runCommand2(opts) {
|
|
6118
6750
|
loadDotenv();
|
|
6119
6751
|
const apiKey = await ensureApiKey();
|
|
6120
6752
|
process.env.DEEPSEEK_API_KEY = apiKey;
|
|
@@ -6289,113 +6921,12 @@ function truncate4(s, max) {
|
|
|
6289
6921
|
|
|
6290
6922
|
// src/cli/commands/setup.tsx
|
|
6291
6923
|
import { render as render4 } from "ink";
|
|
6292
|
-
import
|
|
6924
|
+
import React20 from "react";
|
|
6293
6925
|
|
|
6294
6926
|
// src/cli/ui/Wizard.tsx
|
|
6295
|
-
import { Box as
|
|
6927
|
+
import { Box as Box15, Text as Text15, useApp as useApp5, useInput as useInput6 } from "ink";
|
|
6296
6928
|
import TextInput2 from "ink-text-input";
|
|
6297
|
-
import
|
|
6298
|
-
|
|
6299
|
-
// src/cli/ui/Select.tsx
|
|
6300
|
-
import { Box as Box12, Text as Text12, useInput as useInput5 } from "ink";
|
|
6301
|
-
import React16, { useState as useState8 } from "react";
|
|
6302
|
-
function SingleSelect({
|
|
6303
|
-
items,
|
|
6304
|
-
initialValue,
|
|
6305
|
-
onSubmit,
|
|
6306
|
-
onCancel
|
|
6307
|
-
}) {
|
|
6308
|
-
const initialIndex = Math.max(
|
|
6309
|
-
0,
|
|
6310
|
-
items.findIndex((i) => i.value === initialValue && !i.disabled)
|
|
6311
|
-
);
|
|
6312
|
-
const [index, setIndex] = useState8(initialIndex === -1 ? 0 : initialIndex);
|
|
6313
|
-
useInput5((_input, key) => {
|
|
6314
|
-
if (key.upArrow) {
|
|
6315
|
-
setIndex((i) => findNextEnabled(items, i, -1));
|
|
6316
|
-
} else if (key.downArrow) {
|
|
6317
|
-
setIndex((i) => findNextEnabled(items, i, 1));
|
|
6318
|
-
} else if (key.return) {
|
|
6319
|
-
const chosen = items[index];
|
|
6320
|
-
if (chosen && !chosen.disabled) onSubmit(chosen.value);
|
|
6321
|
-
} else if (key.escape && onCancel) {
|
|
6322
|
-
onCancel();
|
|
6323
|
-
}
|
|
6324
|
-
});
|
|
6325
|
-
return /* @__PURE__ */ React16.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React16.createElement(
|
|
6326
|
-
SelectRow,
|
|
6327
|
-
{
|
|
6328
|
-
key: item.value,
|
|
6329
|
-
item,
|
|
6330
|
-
active: i === index,
|
|
6331
|
-
marker: i === index ? "\u25B8" : " "
|
|
6332
|
-
}
|
|
6333
|
-
)));
|
|
6334
|
-
}
|
|
6335
|
-
function MultiSelect({
|
|
6336
|
-
items,
|
|
6337
|
-
initialSelected = [],
|
|
6338
|
-
onSubmit,
|
|
6339
|
-
onCancel,
|
|
6340
|
-
footer
|
|
6341
|
-
}) {
|
|
6342
|
-
const [index, setIndex] = useState8(() => {
|
|
6343
|
-
const first = items.findIndex((i) => !i.disabled);
|
|
6344
|
-
return first === -1 ? 0 : first;
|
|
6345
|
-
});
|
|
6346
|
-
const [selected, setSelected] = useState8(new Set(initialSelected));
|
|
6347
|
-
useInput5((input, key) => {
|
|
6348
|
-
if (key.upArrow) {
|
|
6349
|
-
setIndex((i) => findNextEnabled(items, i, -1));
|
|
6350
|
-
} else if (key.downArrow) {
|
|
6351
|
-
setIndex((i) => findNextEnabled(items, i, 1));
|
|
6352
|
-
} else if (input === " ") {
|
|
6353
|
-
const item = items[index];
|
|
6354
|
-
if (!item || item.disabled) return;
|
|
6355
|
-
setSelected((prev) => {
|
|
6356
|
-
const next = new Set(prev);
|
|
6357
|
-
if (next.has(item.value)) next.delete(item.value);
|
|
6358
|
-
else next.add(item.value);
|
|
6359
|
-
return next;
|
|
6360
|
-
});
|
|
6361
|
-
} else if (key.return) {
|
|
6362
|
-
const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);
|
|
6363
|
-
onSubmit(ordered);
|
|
6364
|
-
} else if (key.escape && onCancel) {
|
|
6365
|
-
onCancel();
|
|
6366
|
-
}
|
|
6367
|
-
});
|
|
6368
|
-
return /* @__PURE__ */ React16.createElement(Box12, { flexDirection: "column" }, items.map((item, i) => {
|
|
6369
|
-
const checked = selected.has(item.value);
|
|
6370
|
-
const marker = checked ? "[x]" : "[ ]";
|
|
6371
|
-
return /* @__PURE__ */ React16.createElement(
|
|
6372
|
-
SelectRow,
|
|
6373
|
-
{
|
|
6374
|
-
key: item.value,
|
|
6375
|
-
item,
|
|
6376
|
-
active: i === index,
|
|
6377
|
-
marker: `${i === index ? "\u25B8" : " "} ${marker}`
|
|
6378
|
-
}
|
|
6379
|
-
);
|
|
6380
|
-
}), footer ? /* @__PURE__ */ React16.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text12, { dimColor: true }, footer)) : null);
|
|
6381
|
-
}
|
|
6382
|
-
function SelectRow({
|
|
6383
|
-
item,
|
|
6384
|
-
active,
|
|
6385
|
-
marker
|
|
6386
|
-
}) {
|
|
6387
|
-
const color = item.disabled ? "gray" : active ? "cyan" : void 0;
|
|
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);
|
|
6389
|
-
}
|
|
6390
|
-
function findNextEnabled(items, from, step) {
|
|
6391
|
-
if (items.length === 0) return 0;
|
|
6392
|
-
let i = from;
|
|
6393
|
-
for (let tries = 0; tries < items.length; tries++) {
|
|
6394
|
-
i = (i + step + items.length) % items.length;
|
|
6395
|
-
if (!items[i]?.disabled) return i;
|
|
6396
|
-
}
|
|
6397
|
-
return from;
|
|
6398
|
-
}
|
|
6929
|
+
import React19, { useState as useState9 } from "react";
|
|
6399
6930
|
|
|
6400
6931
|
// src/cli/ui/presets.ts
|
|
6401
6932
|
var PRESETS = {
|
|
@@ -6434,7 +6965,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6434
6965
|
if (key.escape && step !== "saved" && onCancel) onCancel();
|
|
6435
6966
|
});
|
|
6436
6967
|
if (step === "apiKey") {
|
|
6437
|
-
return /* @__PURE__ */
|
|
6968
|
+
return /* @__PURE__ */ React19.createElement(
|
|
6438
6969
|
ApiKeyStep,
|
|
6439
6970
|
{
|
|
6440
6971
|
onSubmit: (key) => {
|
|
@@ -6448,7 +6979,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6448
6979
|
);
|
|
6449
6980
|
}
|
|
6450
6981
|
if (step === "preset") {
|
|
6451
|
-
return /* @__PURE__ */
|
|
6982
|
+
return /* @__PURE__ */ React19.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React19.createElement(
|
|
6452
6983
|
SingleSelect,
|
|
6453
6984
|
{
|
|
6454
6985
|
items: presetItems(),
|
|
@@ -6458,10 +6989,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6458
6989
|
setStep("mcp");
|
|
6459
6990
|
}
|
|
6460
6991
|
}
|
|
6461
|
-
), /* @__PURE__ */
|
|
6992
|
+
), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
|
|
6462
6993
|
}
|
|
6463
6994
|
if (step === "mcp") {
|
|
6464
|
-
return /* @__PURE__ */
|
|
6995
|
+
return /* @__PURE__ */ React19.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React19.createElement(
|
|
6465
6996
|
MultiSelect,
|
|
6466
6997
|
{
|
|
6467
6998
|
items: mcpItems(),
|
|
@@ -6486,7 +7017,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6486
7017
|
}
|
|
6487
7018
|
const currentName = pending[0];
|
|
6488
7019
|
const entry = CATALOG_BY_NAME.get(currentName);
|
|
6489
|
-
return /* @__PURE__ */
|
|
7020
|
+
return /* @__PURE__ */ React19.createElement(
|
|
6490
7021
|
McpArgsStep,
|
|
6491
7022
|
{
|
|
6492
7023
|
entry,
|
|
@@ -6504,7 +7035,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6504
7035
|
}
|
|
6505
7036
|
if (step === "review") {
|
|
6506
7037
|
const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
|
|
6507
|
-
return /* @__PURE__ */
|
|
7038
|
+
return /* @__PURE__ */ React19.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React19.createElement(Box15, { flexDirection: "column" }, /* @__PURE__ */ React19.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React19.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React19.createElement(
|
|
6508
7039
|
SummaryLine,
|
|
6509
7040
|
{
|
|
6510
7041
|
label: "MCP",
|
|
@@ -6512,8 +7043,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6512
7043
|
}
|
|
6513
7044
|
), specs.map((spec, i) => (
|
|
6514
7045
|
// biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
|
|
6515
|
-
/* @__PURE__ */
|
|
6516
|
-
)), /* @__PURE__ */
|
|
7046
|
+
/* @__PURE__ */ React19.createElement(Box15, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "\xB7 ", spec))
|
|
7047
|
+
)), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { color: "red" }, error)) : null, /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React19.createElement(
|
|
6517
7048
|
ReviewConfirm,
|
|
6518
7049
|
{
|
|
6519
7050
|
onConfirm: () => {
|
|
@@ -6539,7 +7070,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
6539
7070
|
}
|
|
6540
7071
|
));
|
|
6541
7072
|
}
|
|
6542
|
-
return /* @__PURE__ */
|
|
7073
|
+
return /* @__PURE__ */ React19.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React19.createElement(ExitOnEnter, { onExit: exit }));
|
|
6543
7074
|
}
|
|
6544
7075
|
function ApiKeyStep({
|
|
6545
7076
|
onSubmit,
|
|
@@ -6547,7 +7078,7 @@ function ApiKeyStep({
|
|
|
6547
7078
|
onError
|
|
6548
7079
|
}) {
|
|
6549
7080
|
const [value, setValue] = useState9("");
|
|
6550
|
-
return /* @__PURE__ */
|
|
7081
|
+
return /* @__PURE__ */ React19.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React19.createElement(
|
|
6551
7082
|
TextInput2,
|
|
6552
7083
|
{
|
|
6553
7084
|
value,
|
|
@@ -6564,7 +7095,7 @@ function ApiKeyStep({
|
|
|
6564
7095
|
mask: "\u2022",
|
|
6565
7096
|
placeholder: "sk-..."
|
|
6566
7097
|
}
|
|
6567
|
-
)), error ? /* @__PURE__ */
|
|
7098
|
+
)), error ? /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { color: "red" }, error)) : value ? /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "preview: ", redactKey(value))) : null);
|
|
6568
7099
|
}
|
|
6569
7100
|
function McpArgsStep({
|
|
6570
7101
|
entry,
|
|
@@ -6573,7 +7104,7 @@ function McpArgsStep({
|
|
|
6573
7104
|
onError
|
|
6574
7105
|
}) {
|
|
6575
7106
|
const [value, setValue] = useState9("");
|
|
6576
|
-
return /* @__PURE__ */
|
|
7107
|
+
return /* @__PURE__ */ React19.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React19.createElement(Box15, { flexDirection: "column" }, /* @__PURE__ */ React19.createElement(Text15, null, entry.summary), entry.note ? /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, null, "Required parameter: "), /* @__PURE__ */ React19.createElement(Text15, { bold: true }, entry.userArgs)), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React19.createElement(
|
|
6577
7108
|
TextInput2,
|
|
6578
7109
|
{
|
|
6579
7110
|
value,
|
|
@@ -6589,7 +7120,7 @@ function McpArgsStep({
|
|
|
6589
7120
|
},
|
|
6590
7121
|
placeholder: placeholderFor(entry)
|
|
6591
7122
|
}
|
|
6592
|
-
)), error ? /* @__PURE__ */
|
|
7123
|
+
)), error ? /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { color: "red" }, error)) : null));
|
|
6593
7124
|
}
|
|
6594
7125
|
function ReviewConfirm({ onConfirm }) {
|
|
6595
7126
|
useInput6((_i, key) => {
|
|
@@ -6609,10 +7140,10 @@ function StepFrame({
|
|
|
6609
7140
|
total,
|
|
6610
7141
|
children
|
|
6611
7142
|
}) {
|
|
6612
|
-
return /* @__PURE__ */
|
|
7143
|
+
return /* @__PURE__ */ React19.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React19.createElement(Box15, null, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React19.createElement(Box15, { marginTop: 1, flexDirection: "column" }, children));
|
|
6613
7144
|
}
|
|
6614
7145
|
function SummaryLine({ label, value }) {
|
|
6615
|
-
return /* @__PURE__ */
|
|
7146
|
+
return /* @__PURE__ */ React19.createElement(Box15, null, /* @__PURE__ */ React19.createElement(Text15, null, label.padEnd(12)), /* @__PURE__ */ React19.createElement(Text15, { bold: true }, value));
|
|
6616
7147
|
}
|
|
6617
7148
|
function presetItems() {
|
|
6618
7149
|
return ["fast", "smart", "max"].map((name) => ({
|
|
@@ -6668,7 +7199,7 @@ async function setupCommand(_opts = {}) {
|
|
|
6668
7199
|
const existingKey = loadApiKey();
|
|
6669
7200
|
const existing = readConfig();
|
|
6670
7201
|
const { waitUntilExit, unmount } = render4(
|
|
6671
|
-
/* @__PURE__ */
|
|
7202
|
+
/* @__PURE__ */ React20.createElement(
|
|
6672
7203
|
Wizard,
|
|
6673
7204
|
{
|
|
6674
7205
|
existingApiKey: existingKey,
|
|
@@ -6686,9 +7217,9 @@ async function setupCommand(_opts = {}) {
|
|
|
6686
7217
|
}
|
|
6687
7218
|
|
|
6688
7219
|
// src/cli/commands/stats.ts
|
|
6689
|
-
import { existsSync as
|
|
7220
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
6690
7221
|
function statsCommand(opts) {
|
|
6691
|
-
if (!
|
|
7222
|
+
if (!existsSync4(opts.transcript)) {
|
|
6692
7223
|
console.error(`no such transcript: ${opts.transcript}`);
|
|
6693
7224
|
process.exit(1);
|
|
6694
7225
|
}
|
|
@@ -6763,7 +7294,7 @@ program.action(async () => {
|
|
|
6763
7294
|
const defaults = resolveDefaults({});
|
|
6764
7295
|
await chatCommand({
|
|
6765
7296
|
model: defaults.model,
|
|
6766
|
-
system: DEFAULT_SYSTEM,
|
|
7297
|
+
system: applyProjectMemory(DEFAULT_SYSTEM, process.cwd()),
|
|
6767
7298
|
harvest: defaults.harvest,
|
|
6768
7299
|
branch: defaults.branch,
|
|
6769
7300
|
session: defaults.session,
|
|
@@ -6775,12 +7306,14 @@ program.command("setup").description("Interactive wizard \u2014 API key, preset,
|
|
|
6775
7306
|
});
|
|
6776
7307
|
program.command("code [dir]").description(
|
|
6777
7308
|
"Code-editing chat \u2014 filesystem MCP auto-bridged at <dir> (default: cwd), coding system prompt, smart preset. Model proposes SEARCH/REPLACE blocks; Reasonix applies them to disk."
|
|
6778
|
-
).option("-m, --model <id>", "Override default reasoner model").option("--no-session", "Disable session persistence for this run").option("--transcript <path>", "Write a JSONL transcript to this path").action(async (dir, opts) => {
|
|
7309
|
+
).option("-m, --model <id>", "Override default reasoner model").option("--no-session", "Disable session persistence for this run").option("-r, --resume", "Skip the session picker \u2014 always continue prior messages").option("-n, --new", "Skip the session picker \u2014 always wipe prior messages and start fresh").option("--transcript <path>", "Write a JSONL transcript to this path").action(async (dir, opts) => {
|
|
6779
7310
|
await codeCommand({
|
|
6780
7311
|
dir,
|
|
6781
7312
|
model: opts.model,
|
|
6782
7313
|
noSession: opts.session === false,
|
|
6783
|
-
transcript: opts.transcript
|
|
7314
|
+
transcript: opts.transcript,
|
|
7315
|
+
forceResume: !!opts.resume,
|
|
7316
|
+
forceNew: !!opts.new
|
|
6784
7317
|
});
|
|
6785
7318
|
});
|
|
6786
7319
|
program.command("chat").description("Interactive Ink TUI with live cache/cost panel.").option("-m, --model <id>", "DeepSeek model id (overrides preset)").option("-s, --system <prompt>", "System prompt (pinned in the immutable prefix)", DEFAULT_SYSTEM).option("--transcript <path>", "Write a JSONL transcript to this path").option(
|
|
@@ -6793,7 +7326,7 @@ program.command("chat").description("Interactive Ink TUI with live cache/cost pa
|
|
|
6793
7326
|
"--branch <n>",
|
|
6794
7327
|
"Self-consistency: run N parallel samples per turn and pick the most confident (disables streaming; enables harvest)",
|
|
6795
7328
|
(v) => Number.parseInt(v, 10)
|
|
6796
|
-
).option("--session <name>", "Use a named session (default: from config, usually 'default').").option("--no-session", "Disable session persistence for this run (ephemeral chat)").option(
|
|
7329
|
+
).option("--session <name>", "Use a named session (default: from config, usually 'default').").option("--no-session", "Disable session persistence for this run (ephemeral chat)").option("-r, --resume", "Skip the session picker \u2014 always continue prior messages").option("-n, --new", "Skip the session picker \u2014 always wipe prior messages and start fresh").option(
|
|
6797
7330
|
"--mcp <spec>",
|
|
6798
7331
|
'MCP server spec; repeatable. "name=cmd args...", "cmd args...", or a URL (http/https \u2192 SSE transport). Overrides config.mcp when provided.',
|
|
6799
7332
|
(value, previous = []) => [...previous, value],
|
|
@@ -6813,13 +7346,15 @@ program.command("chat").description("Interactive Ink TUI with live cache/cost pa
|
|
|
6813
7346
|
});
|
|
6814
7347
|
await chatCommand({
|
|
6815
7348
|
model: defaults.model,
|
|
6816
|
-
system: opts.system,
|
|
7349
|
+
system: applyProjectMemory(opts.system, process.cwd()),
|
|
6817
7350
|
transcript: opts.transcript,
|
|
6818
7351
|
harvest: defaults.harvest,
|
|
6819
7352
|
branch: defaults.branch,
|
|
6820
7353
|
session: defaults.session,
|
|
6821
7354
|
mcp: defaults.mcp,
|
|
6822
|
-
mcpPrefix: opts.mcpPrefix
|
|
7355
|
+
mcpPrefix: opts.mcpPrefix,
|
|
7356
|
+
forceResume: !!opts.resume,
|
|
7357
|
+
forceNew: !!opts.new
|
|
6823
7358
|
});
|
|
6824
7359
|
});
|
|
6825
7360
|
program.command("run <task>").description("Run a single task non-interactively, streaming output.").option("-m, --model <id>", "DeepSeek model id (overrides preset)").option("-s, --system <prompt>", "System prompt", DEFAULT_SYSTEM).option("--preset <name>", "Bundle of model + harvest + branch: fast | smart | max").option("--harvest", "Extract typed plan state from R1 reasoning (Pillar 2)").option(
|
|
@@ -6843,10 +7378,10 @@ program.command("run <task>").description("Run a single task non-interactively,
|
|
|
6843
7378
|
preset: opts.preset,
|
|
6844
7379
|
noConfig: opts.config === false
|
|
6845
7380
|
});
|
|
6846
|
-
await
|
|
7381
|
+
await runCommand2({
|
|
6847
7382
|
task,
|
|
6848
7383
|
model: defaults.model,
|
|
6849
|
-
system: opts.system,
|
|
7384
|
+
system: applyProjectMemory(opts.system, process.cwd()),
|
|
6850
7385
|
harvest: defaults.harvest,
|
|
6851
7386
|
branch: defaults.branch,
|
|
6852
7387
|
transcript: opts.transcript,
|