reasonix 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/{chunk-DVBNMXA6.js → chunk-WRG56OKI.js} +130 -2
- package/dist/cli/chunk-WRG56OKI.js.map +1 -0
- package/dist/cli/index.js +692 -209
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-POARCKKR.js → prompt-LJ44NWSU.js} +2 -2
- package/dist/index.d.ts +33 -13
- package/dist/index.js +133 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-DVBNMXA6.js.map +0 -1
- /package/dist/cli/{prompt-POARCKKR.js.map → prompt-LJ44NWSU.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
memoryEnabled,
|
|
11
11
|
readProjectMemory,
|
|
12
12
|
sanitizeMemoryName
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WRG56OKI.js";
|
|
14
14
|
|
|
15
15
|
// src/cli/index.ts
|
|
16
16
|
import { Command } from "commander";
|
|
@@ -3174,7 +3174,7 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3174
3174
|
const ignore = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
3175
3175
|
const rootAbs = resolve(root);
|
|
3176
3176
|
const out = [];
|
|
3177
|
-
const
|
|
3177
|
+
const walk3 = (dirAbs, dirRel) => {
|
|
3178
3178
|
if (out.length >= maxResults) return;
|
|
3179
3179
|
let entries;
|
|
3180
3180
|
try {
|
|
@@ -3188,7 +3188,7 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3188
3188
|
const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
|
|
3189
3189
|
if (ent.isDirectory()) {
|
|
3190
3190
|
if (ent.name.startsWith(".") || ignore.has(ent.name)) continue;
|
|
3191
|
-
|
|
3191
|
+
walk3(join5(dirAbs, ent.name), relPath);
|
|
3192
3192
|
} else if (ent.isFile()) {
|
|
3193
3193
|
let mtimeMs = 0;
|
|
3194
3194
|
try {
|
|
@@ -3199,7 +3199,7 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3199
3199
|
}
|
|
3200
3200
|
}
|
|
3201
3201
|
};
|
|
3202
|
-
|
|
3202
|
+
walk3(rootAbs, "");
|
|
3203
3203
|
return out;
|
|
3204
3204
|
}
|
|
3205
3205
|
var AT_PICKER_PREFIX = /(?:^|\s)@([a-zA-Z0-9_./\\-]*)$/;
|
|
@@ -3338,6 +3338,108 @@ var defaultFs = {
|
|
|
3338
3338
|
},
|
|
3339
3339
|
read: (p) => readFileSync5(p, "utf8")
|
|
3340
3340
|
};
|
|
3341
|
+
var AT_URL_PATTERN = /(?<=^|\s)@(https?:\/\/\S+)/g;
|
|
3342
|
+
var DEFAULT_AT_URL_MAX_CHARS = 32e3;
|
|
3343
|
+
async function expandAtUrls(text, opts = {}) {
|
|
3344
|
+
const maxChars = opts.maxChars ?? DEFAULT_AT_URL_MAX_CHARS;
|
|
3345
|
+
const fetcher = opts.fetcher;
|
|
3346
|
+
if (!fetcher) {
|
|
3347
|
+
throw new Error("expandAtUrls: fetcher option is required (wire src/tools/web.ts:webFetch)");
|
|
3348
|
+
}
|
|
3349
|
+
const seen = /* @__PURE__ */ new Map();
|
|
3350
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
3351
|
+
const order = [];
|
|
3352
|
+
for (const match of text.matchAll(AT_URL_PATTERN)) {
|
|
3353
|
+
const rawUrl = match[1] ?? "";
|
|
3354
|
+
const url = stripUrlTail(rawUrl);
|
|
3355
|
+
if (!url) continue;
|
|
3356
|
+
if (seen.has(url)) continue;
|
|
3357
|
+
const cached2 = opts.cache?.get(url);
|
|
3358
|
+
if (cached2) {
|
|
3359
|
+
seen.set(url, cached2);
|
|
3360
|
+
if (cached2.body) bodies.set(url, cached2.body);
|
|
3361
|
+
order.push(url);
|
|
3362
|
+
continue;
|
|
3363
|
+
}
|
|
3364
|
+
let expansion;
|
|
3365
|
+
let body = "";
|
|
3366
|
+
try {
|
|
3367
|
+
const page = await fetcher(url, {
|
|
3368
|
+
maxChars,
|
|
3369
|
+
timeoutMs: opts.timeoutMs,
|
|
3370
|
+
signal: opts.signal
|
|
3371
|
+
});
|
|
3372
|
+
body = page.text;
|
|
3373
|
+
expansion = {
|
|
3374
|
+
token: `@${url}`,
|
|
3375
|
+
url,
|
|
3376
|
+
ok: true,
|
|
3377
|
+
title: page.title,
|
|
3378
|
+
chars: body.length,
|
|
3379
|
+
truncated: page.truncated
|
|
3380
|
+
};
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
const message = err.message ?? String(err);
|
|
3383
|
+
let skip = "fetch-error";
|
|
3384
|
+
if (/aborted|timeout/i.test(message)) skip = "timeout";
|
|
3385
|
+
else if (/40\d|forbidden|access denied|captcha/i.test(message)) skip = "blocked";
|
|
3386
|
+
expansion = {
|
|
3387
|
+
token: `@${url}`,
|
|
3388
|
+
url,
|
|
3389
|
+
ok: false,
|
|
3390
|
+
skip,
|
|
3391
|
+
error: message
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
seen.set(url, expansion);
|
|
3395
|
+
if (body) bodies.set(url, body);
|
|
3396
|
+
if (opts.cache) opts.cache.set(url, { ...expansion, body });
|
|
3397
|
+
order.push(url);
|
|
3398
|
+
}
|
|
3399
|
+
if (seen.size === 0) return { text, expansions: [] };
|
|
3400
|
+
const expansions = order.map((u) => seen.get(u)).filter(Boolean);
|
|
3401
|
+
const blocks = [];
|
|
3402
|
+
for (const ex of expansions) {
|
|
3403
|
+
if (ex.ok) {
|
|
3404
|
+
const titleAttr = ex.title ? ` title="${escapeAttr(ex.title)}"` : "";
|
|
3405
|
+
const truncTag = ex.truncated ? ' truncated="true"' : "";
|
|
3406
|
+
const body = bodies.get(ex.url) ?? "";
|
|
3407
|
+
blocks.push(`<url href="${ex.url}"${titleAttr}${truncTag}>
|
|
3408
|
+
${body}
|
|
3409
|
+
</url>`);
|
|
3410
|
+
} else {
|
|
3411
|
+
const reasonAttr = ex.skip ?? "fetch-error";
|
|
3412
|
+
blocks.push(`<url href="${ex.url}" skipped="${reasonAttr}" />`);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
const augmented = `${text}
|
|
3416
|
+
|
|
3417
|
+
[Referenced URLs]
|
|
3418
|
+
${blocks.join("\n\n")}`;
|
|
3419
|
+
return { text: augmented, expansions };
|
|
3420
|
+
}
|
|
3421
|
+
function stripUrlTail(raw) {
|
|
3422
|
+
let s = raw;
|
|
3423
|
+
while (s.length > 0) {
|
|
3424
|
+
const last = s[s.length - 1];
|
|
3425
|
+
if (".,;:!?".includes(last)) {
|
|
3426
|
+
s = s.slice(0, -1);
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3429
|
+
if (")]}>".includes(last)) {
|
|
3430
|
+
const open = { ")": "(", "]": "[", "}": "{", ">": "<" }[last];
|
|
3431
|
+
if (!s.includes(open)) {
|
|
3432
|
+
s = s.slice(0, -1);
|
|
3433
|
+
continue;
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
break;
|
|
3437
|
+
}
|
|
3438
|
+
return s;
|
|
3439
|
+
}
|
|
3440
|
+
function escapeAttr(s) {
|
|
3441
|
+
return s.replace(/"/g, """).replace(/[\r\n]+/g, " ").trim();
|
|
3442
|
+
}
|
|
3341
3443
|
|
|
3342
3444
|
// src/tools/filesystem.ts
|
|
3343
3445
|
import { promises as fs } from "fs";
|
|
@@ -3564,7 +3666,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3564
3666
|
let totalBytes = 0;
|
|
3565
3667
|
let truncated = false;
|
|
3566
3668
|
const PER_DIR_CHILD_CAP = 50;
|
|
3567
|
-
const
|
|
3669
|
+
const walk3 = async (dir, depth) => {
|
|
3568
3670
|
if (truncated) return;
|
|
3569
3671
|
if (depth > maxDepth) return;
|
|
3570
3672
|
let entries;
|
|
@@ -3604,11 +3706,11 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3604
3706
|
lines.push(line);
|
|
3605
3707
|
emitted++;
|
|
3606
3708
|
if (e.isDirectory() && !skip) {
|
|
3607
|
-
await
|
|
3709
|
+
await walk3(pathMod.join(dir, e.name), depth + 1);
|
|
3608
3710
|
}
|
|
3609
3711
|
}
|
|
3610
3712
|
};
|
|
3611
|
-
await
|
|
3713
|
+
await walk3(startAbs, 0);
|
|
3612
3714
|
return lines.join("\n") || "(empty tree)";
|
|
3613
3715
|
}
|
|
3614
3716
|
});
|
|
@@ -3638,7 +3740,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3638
3740
|
}
|
|
3639
3741
|
const matches = [];
|
|
3640
3742
|
let totalBytes = 0;
|
|
3641
|
-
const
|
|
3743
|
+
const walk3 = async (dir) => {
|
|
3642
3744
|
let entries;
|
|
3643
3745
|
try {
|
|
3644
3746
|
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
@@ -3658,10 +3760,10 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3658
3760
|
matches.push(rel);
|
|
3659
3761
|
totalBytes += rel.length + 1;
|
|
3660
3762
|
}
|
|
3661
|
-
if (e.isDirectory()) await
|
|
3763
|
+
if (e.isDirectory()) await walk3(full);
|
|
3662
3764
|
}
|
|
3663
3765
|
};
|
|
3664
|
-
await
|
|
3766
|
+
await walk3(startAbs);
|
|
3665
3767
|
return matches.length === 0 ? "(no matches)" : matches.join("\n");
|
|
3666
3768
|
}
|
|
3667
3769
|
});
|
|
@@ -3711,7 +3813,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3711
3813
|
let totalBytes = 0;
|
|
3712
3814
|
let scanned = 0;
|
|
3713
3815
|
let truncated = false;
|
|
3714
|
-
const
|
|
3816
|
+
const walk3 = async (dir) => {
|
|
3715
3817
|
if (truncated) return;
|
|
3716
3818
|
let entries;
|
|
3717
3819
|
try {
|
|
@@ -3723,7 +3825,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3723
3825
|
if (truncated) return;
|
|
3724
3826
|
if (e.isDirectory()) {
|
|
3725
3827
|
if (!includeDeps && SKIP_DIR_NAMES.has(e.name)) continue;
|
|
3726
|
-
await
|
|
3828
|
+
await walk3(pathMod.join(dir, e.name));
|
|
3727
3829
|
continue;
|
|
3728
3830
|
}
|
|
3729
3831
|
if (!e.isFile()) continue;
|
|
@@ -3766,7 +3868,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
|
|
|
3766
3868
|
scanned++;
|
|
3767
3869
|
}
|
|
3768
3870
|
};
|
|
3769
|
-
await
|
|
3871
|
+
await walk3(startAbs);
|
|
3770
3872
|
if (matches.length === 0) {
|
|
3771
3873
|
return scanned === 0 ? "(no files scanned \u2014 path empty or all files filtered out)" : `(no matches across ${scanned} file${scanned === 1 ? "" : "s"})`;
|
|
3772
3874
|
}
|
|
@@ -4531,9 +4633,9 @@ async function spawnSubagent(opts) {
|
|
|
4531
4633
|
usage
|
|
4532
4634
|
};
|
|
4533
4635
|
}
|
|
4534
|
-
function aggregateChildUsage(
|
|
4636
|
+
function aggregateChildUsage(loop2) {
|
|
4535
4637
|
const agg = new Usage();
|
|
4536
|
-
for (const t of
|
|
4638
|
+
for (const t of loop2.stats.turns) {
|
|
4537
4639
|
agg.promptTokens += t.usage.promptTokens;
|
|
4538
4640
|
agg.completionTokens += t.usage.completionTokens;
|
|
4539
4641
|
agg.totalTokens += t.usage.totalTokens;
|
|
@@ -5204,7 +5306,7 @@ function registerShellTools(registry, opts) {
|
|
|
5204
5306
|
const snapshot2 = opts.extraAllowed ?? [];
|
|
5205
5307
|
return () => snapshot2;
|
|
5206
5308
|
})();
|
|
5207
|
-
const
|
|
5309
|
+
const isAllowAll = typeof opts.allowAll === "function" ? opts.allowAll : () => opts.allowAll === true;
|
|
5208
5310
|
registry.register({
|
|
5209
5311
|
name: "run_command",
|
|
5210
5312
|
description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022 ONE process per call, NO shell expansion. `&&`, `||`, `|`, `;`, `>`, `<`, `2>&1` are all rejected up-front \u2014 split into separate calls and combine results in reasoning. Example: instead of `grep foo *.ts | wc -l`, use `grep -c foo *.ts`; instead of `cd sub && npm test`, use `npm test --prefix sub` (or whatever --cwd flag the binary accepts).\n\u2022 `cd` DOES NOT PERSIST between calls \u2014 each call spawns a fresh process rooted at the project. If a tool needs a subdirectory, pass it via the tool's own flag (`npm --prefix`, `cargo -C`, `git -C`, `pytest tests/\u2026`), NOT via a preceding `cd`.\n\u2022 Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) \u2014 they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\n\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
|
|
@@ -5213,7 +5315,7 @@ function registerShellTools(registry, opts) {
|
|
|
5213
5315
|
// during planning. Anything that would otherwise trigger a
|
|
5214
5316
|
// confirmation prompt is treated as "not read-only" and bounced.
|
|
5215
5317
|
readOnlyCheck: (args) => {
|
|
5216
|
-
if (
|
|
5318
|
+
if (isAllowAll()) return true;
|
|
5217
5319
|
const cmd = typeof args?.command === "string" ? args.command.trim() : "";
|
|
5218
5320
|
if (!cmd) return false;
|
|
5219
5321
|
return isAllowed(cmd, getExtraAllowed());
|
|
@@ -5235,7 +5337,7 @@ function registerShellTools(registry, opts) {
|
|
|
5235
5337
|
fn: async (args, ctx) => {
|
|
5236
5338
|
const cmd = args.command.trim();
|
|
5237
5339
|
if (!cmd) throw new Error("run_command: empty command");
|
|
5238
|
-
if (!
|
|
5340
|
+
if (!isAllowAll() && !isAllowed(cmd, getExtraAllowed())) {
|
|
5239
5341
|
throw new NeedsConfirmationError(cmd);
|
|
5240
5342
|
}
|
|
5241
5343
|
const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
|
|
@@ -5268,7 +5370,7 @@ function registerShellTools(registry, opts) {
|
|
|
5268
5370
|
fn: async (args, ctx) => {
|
|
5269
5371
|
const cmd = args.command.trim();
|
|
5270
5372
|
if (!cmd) throw new Error("run_background: empty command");
|
|
5271
|
-
if (!
|
|
5373
|
+
if (!isAllowAll() && !isAllowed(cmd, getExtraAllowed())) {
|
|
5272
5374
|
throw new NeedsConfirmationError(cmd);
|
|
5273
5375
|
}
|
|
5274
5376
|
const result = await jobs2.start(cmd, {
|
|
@@ -7116,7 +7218,7 @@ import { render } from "ink";
|
|
|
7116
7218
|
import React26, { useState as useState12 } from "react";
|
|
7117
7219
|
|
|
7118
7220
|
// src/cli/ui/App.tsx
|
|
7119
|
-
import { Box as Box21, Static, useApp, useStdout as useStdout8 } from "ink";
|
|
7221
|
+
import { Box as Box21, Static, Text as Text19, useApp, useStdout as useStdout8 } from "ink";
|
|
7120
7222
|
import React23, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState10 } from "react";
|
|
7121
7223
|
|
|
7122
7224
|
// src/code/pending-edits.ts
|
|
@@ -9467,10 +9569,9 @@ function ModeStatusBar({
|
|
|
9467
9569
|
if (planMode) {
|
|
9468
9570
|
return /* @__PURE__ */ React12.createElement(ModeBarFrame, null, /* @__PURE__ */ React12.createElement(ModePill, { label: "PLAN MODE", bg: "red", flash }), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, " writes gated \xB7 /plan off to leave"), jobsTag);
|
|
9469
9571
|
}
|
|
9470
|
-
const
|
|
9471
|
-
const
|
|
9472
|
-
const
|
|
9473
|
-
const mid = isAuto ? "edits land now \xB7 u to undo" : pendingCount > 0 ? `${pendingCount} queued \xB7 y apply \xB7 n discard` : "edits queued \xB7 y apply \xB7 n discard";
|
|
9572
|
+
const label = editMode === "yolo" ? "YOLO" : editMode === "auto" ? "AUTO" : "REVIEW";
|
|
9573
|
+
const bg = editMode === "yolo" ? "red" : editMode === "auto" ? "magenta" : "cyan";
|
|
9574
|
+
const mid = editMode === "yolo" ? "edits + shell auto \xB7 /undo to roll back" : editMode === "auto" ? "edits land now \xB7 u to undo" : pendingCount > 0 ? `${pendingCount} queued \xB7 y apply \xB7 n discard` : "edits queued \xB7 y apply \xB7 n discard";
|
|
9474
9575
|
return /* @__PURE__ */ React12.createElement(ModeBarFrame, null, /* @__PURE__ */ React12.createElement(ModePill, { label, bg, flash }), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, ` ${mid} \xB7 Shift+Tab to flip`), jobsTag);
|
|
9475
9576
|
}
|
|
9476
9577
|
function ModeBarFrame({ children }) {
|
|
@@ -10927,13 +11028,21 @@ function describeRepair(repair) {
|
|
|
10927
11028
|
}
|
|
10928
11029
|
|
|
10929
11030
|
// src/cli/ui/hash-memory.ts
|
|
10930
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
10931
|
-
import {
|
|
10932
|
-
|
|
11031
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
11032
|
+
import { homedir as homedir6 } from "os";
|
|
11033
|
+
import { dirname as dirname10, join as join12 } from "path";
|
|
11034
|
+
var PROJECT_HEADER = `# Reasonix project memory
|
|
10933
11035
|
|
|
10934
11036
|
Notes the user pinned via the \`#\` prompt prefix. The whole file is
|
|
10935
11037
|
loaded into the immutable system prefix every session \u2014 keep it terse.
|
|
10936
11038
|
|
|
11039
|
+
`;
|
|
11040
|
+
var GLOBAL_HEADER = `# Reasonix global memory
|
|
11041
|
+
|
|
11042
|
+
Cross-project notes the user pinned via the \`#g\` prompt prefix. Loaded
|
|
11043
|
+
into every Reasonix session's prefix regardless of working directory.
|
|
11044
|
+
Private to this machine \u2014 not committed anywhere.
|
|
11045
|
+
|
|
10937
11046
|
`;
|
|
10938
11047
|
function detectHashMemory(text) {
|
|
10939
11048
|
if (text.startsWith("\\#")) {
|
|
@@ -10941,18 +11050,36 @@ function detectHashMemory(text) {
|
|
|
10941
11050
|
}
|
|
10942
11051
|
if (!text.startsWith("#")) return null;
|
|
10943
11052
|
if (text.startsWith("##")) return null;
|
|
11053
|
+
if (/^#g\s*$/.test(text)) return null;
|
|
11054
|
+
const globalMatch = /^#g\s+(.+)$/s.exec(text);
|
|
11055
|
+
if (globalMatch) {
|
|
11056
|
+
const body2 = globalMatch[1].trim();
|
|
11057
|
+
if (!body2) return null;
|
|
11058
|
+
return { kind: "memory-global", note: body2 };
|
|
11059
|
+
}
|
|
10944
11060
|
const body = text.slice(1).trim();
|
|
10945
11061
|
if (!body) return null;
|
|
10946
11062
|
return { kind: "memory", note: body };
|
|
10947
11063
|
}
|
|
10948
11064
|
function appendProjectMemory(rootDir, note) {
|
|
10949
|
-
|
|
11065
|
+
return appendBulletToFile(join12(rootDir, PROJECT_MEMORY_FILE), note, PROJECT_HEADER);
|
|
11066
|
+
}
|
|
11067
|
+
var GLOBAL_MEMORY_DIR = ".reasonix";
|
|
11068
|
+
var GLOBAL_MEMORY_FILE = "REASONIX.md";
|
|
11069
|
+
function globalMemoryPath(homeDir = homedir6()) {
|
|
11070
|
+
return join12(homeDir, GLOBAL_MEMORY_DIR, GLOBAL_MEMORY_FILE);
|
|
11071
|
+
}
|
|
11072
|
+
function appendGlobalMemory(note, homeDir) {
|
|
11073
|
+
return appendBulletToFile(globalMemoryPath(homeDir), note, GLOBAL_HEADER);
|
|
11074
|
+
}
|
|
11075
|
+
function appendBulletToFile(path, note, newFileHeader) {
|
|
10950
11076
|
const trimmed = note.trim();
|
|
10951
11077
|
if (!trimmed) throw new Error("note body cannot be empty");
|
|
10952
11078
|
const bullet = `- ${trimmed}
|
|
10953
11079
|
`;
|
|
10954
11080
|
if (!existsSync11(path)) {
|
|
10955
|
-
|
|
11081
|
+
mkdirSync8(dirname10(path), { recursive: true });
|
|
11082
|
+
writeFileSync7(path, `${newFileHeader}${bullet}`, "utf8");
|
|
10956
11083
|
return { path, created: true };
|
|
10957
11084
|
}
|
|
10958
11085
|
let prefix = "";
|
|
@@ -10965,6 +11092,65 @@ function appendProjectMemory(rootDir, note) {
|
|
|
10965
11092
|
return { path, created: false };
|
|
10966
11093
|
}
|
|
10967
11094
|
|
|
11095
|
+
// src/cli/ui/loop.ts
|
|
11096
|
+
var MIN_LOOP_INTERVAL_MS = 5e3;
|
|
11097
|
+
var MAX_LOOP_INTERVAL_MS = 6 * 60 * 6e4;
|
|
11098
|
+
function parseLoopInterval(raw) {
|
|
11099
|
+
const s = raw.trim().toLowerCase();
|
|
11100
|
+
if (!s) return null;
|
|
11101
|
+
const m = /^([0-9]+(?:\.[0-9]+)?)(s|sec|secs|m|min|mins|h|hr|hrs)?$/.exec(s);
|
|
11102
|
+
if (!m) return null;
|
|
11103
|
+
const n = Number.parseFloat(m[1] ?? "");
|
|
11104
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
11105
|
+
const unit = m[2] ?? "s";
|
|
11106
|
+
let ms;
|
|
11107
|
+
if (unit === "s" || unit === "sec" || unit === "secs") ms = Math.round(n * 1e3);
|
|
11108
|
+
else if (unit === "m" || unit === "min" || unit === "mins") ms = Math.round(n * 6e4);
|
|
11109
|
+
else if (unit === "h" || unit === "hr" || unit === "hrs") ms = Math.round(n * 60 * 6e4);
|
|
11110
|
+
else return null;
|
|
11111
|
+
if (ms < MIN_LOOP_INTERVAL_MS) return null;
|
|
11112
|
+
if (ms > MAX_LOOP_INTERVAL_MS) return null;
|
|
11113
|
+
return { ms };
|
|
11114
|
+
}
|
|
11115
|
+
function parseLoopCommand(args) {
|
|
11116
|
+
if (args.length === 0) return { kind: "status" };
|
|
11117
|
+
const first = (args[0] ?? "").toLowerCase();
|
|
11118
|
+
if (args.length === 1 && (first === "stop" || first === "off" || first === "cancel")) {
|
|
11119
|
+
return { kind: "stop" };
|
|
11120
|
+
}
|
|
11121
|
+
const interval = parseLoopInterval(args[0] ?? "");
|
|
11122
|
+
if (!interval) {
|
|
11123
|
+
return {
|
|
11124
|
+
kind: "error",
|
|
11125
|
+
message: "usage: /loop <interval> <prompt> (interval = 5s..6h, e.g. 30s, 5m, 1h)\n /loop stop (cancel an active loop)\n /loop (show active-loop status)"
|
|
11126
|
+
};
|
|
11127
|
+
}
|
|
11128
|
+
const prompt = args.slice(1).join(" ").trim();
|
|
11129
|
+
if (!prompt) {
|
|
11130
|
+
return {
|
|
11131
|
+
kind: "error",
|
|
11132
|
+
message: `usage: /loop ${args[0]} <prompt> \u2014 interval is fine but the prompt is missing.`
|
|
11133
|
+
};
|
|
11134
|
+
}
|
|
11135
|
+
return { kind: "start", intervalMs: interval.ms, prompt };
|
|
11136
|
+
}
|
|
11137
|
+
function formatLoopStatus(prompt, nextFireMs, iter) {
|
|
11138
|
+
const preview = prompt.length > 36 ? `${prompt.slice(0, 33)}\u2026` : prompt;
|
|
11139
|
+
const when = nextFireMs <= 0 ? "firing now" : `next in ${formatDuration2(nextFireMs)}`;
|
|
11140
|
+
return `loop: \`${preview}\` \xB7 ${when} \xB7 iter ${iter}`;
|
|
11141
|
+
}
|
|
11142
|
+
function formatDuration2(ms) {
|
|
11143
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
11144
|
+
const totalSec = Math.round(ms / 1e3);
|
|
11145
|
+
if (totalSec < 60) return `${totalSec}s`;
|
|
11146
|
+
const m = Math.floor(totalSec / 60);
|
|
11147
|
+
const s = totalSec % 60;
|
|
11148
|
+
if (m < 60) return s === 0 ? `${m}m` : `${m}m${s}s`;
|
|
11149
|
+
const h = Math.floor(m / 60);
|
|
11150
|
+
const mm = m % 60;
|
|
11151
|
+
return mm === 0 ? `${h}h` : `${h}h${mm}m`;
|
|
11152
|
+
}
|
|
11153
|
+
|
|
10968
11154
|
// src/cli/ui/mcp-browse.ts
|
|
10969
11155
|
function formatResourceList(servers) {
|
|
10970
11156
|
const lines = [];
|
|
@@ -11270,6 +11456,11 @@ var SLASH_COMMANDS = [
|
|
|
11270
11456
|
{ cmd: "setup", summary: "reminds you to exit and run `reasonix setup`" },
|
|
11271
11457
|
{ cmd: "clear", summary: "clear visible scrollback only (log/context kept)" },
|
|
11272
11458
|
{ cmd: "new", summary: "start a fresh conversation (clear context + scrollback)" },
|
|
11459
|
+
{
|
|
11460
|
+
cmd: "loop",
|
|
11461
|
+
argsHint: "<5s..6h> <prompt> \xB7 stop \xB7 (no args = status)",
|
|
11462
|
+
summary: "auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop"
|
|
11463
|
+
},
|
|
11273
11464
|
{ cmd: "exit", summary: "quit the TUI" },
|
|
11274
11465
|
// Code-mode only
|
|
11275
11466
|
{
|
|
@@ -11284,6 +11475,11 @@ var SLASH_COMMANDS = [
|
|
|
11284
11475
|
summary: "drop pending edit blocks without writing (no arg \u2192 all; indices \u2192 that subset)",
|
|
11285
11476
|
contextual: "code"
|
|
11286
11477
|
},
|
|
11478
|
+
{
|
|
11479
|
+
cmd: "walk",
|
|
11480
|
+
summary: "step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)",
|
|
11481
|
+
contextual: "code"
|
|
11482
|
+
},
|
|
11287
11483
|
{ cmd: "undo", summary: "roll back the last applied edit batch", contextual: "code" },
|
|
11288
11484
|
{
|
|
11289
11485
|
cmd: "history",
|
|
@@ -11316,10 +11512,10 @@ var SLASH_COMMANDS = [
|
|
|
11316
11512
|
},
|
|
11317
11513
|
{
|
|
11318
11514
|
cmd: "mode",
|
|
11319
|
-
argsHint: "[review|auto]",
|
|
11320
|
-
summary: "edit-gate: review (queue
|
|
11515
|
+
argsHint: "[review|auto|yolo]",
|
|
11516
|
+
summary: "edit-gate: review (queue) \xB7 auto (apply+undo) \xB7 yolo (apply+auto-shell). Shift+Tab cycles.",
|
|
11321
11517
|
contextual: "code",
|
|
11322
|
-
argCompleter: ["review", "auto"]
|
|
11518
|
+
argCompleter: ["review", "auto", "yolo"]
|
|
11323
11519
|
},
|
|
11324
11520
|
{ cmd: "jobs", summary: "list background jobs started by run_background", contextual: "code" },
|
|
11325
11521
|
{
|
|
@@ -11500,7 +11696,7 @@ function pad(s, width, align = "left") {
|
|
|
11500
11696
|
}
|
|
11501
11697
|
|
|
11502
11698
|
// src/cli/ui/slash/handlers/admin.ts
|
|
11503
|
-
var hooks = (args,
|
|
11699
|
+
var hooks = (args, loop2, ctx) => {
|
|
11504
11700
|
const sub = (args[0] ?? "").toLowerCase();
|
|
11505
11701
|
if (sub === "reload") {
|
|
11506
11702
|
if (!ctx.reloadHooks) {
|
|
@@ -11516,7 +11712,7 @@ var hooks = (args, loop, ctx) => {
|
|
|
11516
11712
|
info: "usage: /hooks list active hooks\n /hooks reload re-read settings.json files"
|
|
11517
11713
|
};
|
|
11518
11714
|
}
|
|
11519
|
-
const all =
|
|
11715
|
+
const all = loop2.hooks;
|
|
11520
11716
|
const projPath = ctx.codeRoot ? projectSettingsPath(ctx.codeRoot) : void 0;
|
|
11521
11717
|
const globPath = globalSettingsPath();
|
|
11522
11718
|
if (all.length === 0) {
|
|
@@ -11618,8 +11814,8 @@ var clear = () => ({
|
|
|
11618
11814
|
clear: true,
|
|
11619
11815
|
info: "\u25B8 terminal cleared (viewport + scrollback). Context (message log) is intact \u2014 next turn still sees everything. Use /new to start fresh, or /forget to delete the session entirely."
|
|
11620
11816
|
});
|
|
11621
|
-
var resetLog = (_args,
|
|
11622
|
-
const { dropped } =
|
|
11817
|
+
var resetLog = (_args, loop2) => {
|
|
11818
|
+
const { dropped } = loop2.clearLog();
|
|
11623
11819
|
return {
|
|
11624
11820
|
clear: true,
|
|
11625
11821
|
info: `\u25B8 new conversation \u2014 dropped ${dropped} message(s) from context. Same session, fresh slate.`
|
|
@@ -11647,9 +11843,13 @@ var keys = () => ({
|
|
|
11647
11843
|
" /<name> slash command; Tab/Enter picks from the suggestion list",
|
|
11648
11844
|
" @<path> inline a file under [Referenced files] (code mode).",
|
|
11649
11845
|
" Trailing `@\u2026` opens a file picker; \u2191/\u2193 navigate, Tab/Enter pick.",
|
|
11846
|
+
" @https://... fetch the URL, strip HTML, inline under [Referenced URLs].",
|
|
11847
|
+
" Cached per session \u2014 same URL twice fetches once.",
|
|
11650
11848
|
" !<cmd> run <cmd> as shell in the sandbox root; output goes into context",
|
|
11651
11849
|
" so the model sees it next turn. No allowlist gate.",
|
|
11652
|
-
" #<note> append <note> to REASONIX.md
|
|
11850
|
+
" #<note> append <note> to <project>/REASONIX.md (committable, team-shared).",
|
|
11851
|
+
" #g <note> append <note> to ~/.reasonix/REASONIX.md (global, never committed).",
|
|
11852
|
+
" Both pin into the immutable prefix every future session.",
|
|
11653
11853
|
" Use `\\#literal` if you actually want a `#` heading sent to the model.",
|
|
11654
11854
|
"",
|
|
11655
11855
|
"Pickers (slash + @-mention):",
|
|
@@ -11691,13 +11891,14 @@ var help = () => ({
|
|
|
11691
11891
|
" /retry truncate & resend your last message (fresh sample from the model)",
|
|
11692
11892
|
" /apply [N|1,3|1-4] (code mode) commit pending edit blocks (no arg \u2192 all; index \u2192 subset)",
|
|
11693
11893
|
" /discard [N|1,3|1-4] (code mode) drop pending edits (no arg \u2192 all; index \u2192 subset)",
|
|
11894
|
+
" /walk (code mode) step through pending edits one block at a time (y/n per block, a apply rest, A flip AUTO)",
|
|
11694
11895
|
" /undo (code mode) roll back the latest non-undone edit batch",
|
|
11695
11896
|
" /history (code mode) list every edit batch this session",
|
|
11696
11897
|
" /show [id] (code mode) dump a stored edit diff (newest when id omitted)",
|
|
11697
11898
|
' /commit "msg" (code mode) git add -A && git commit -m "msg"',
|
|
11698
11899
|
" /plan [on|off] (code mode) toggle read-only plan mode; writes gated behind submit_plan + your approval",
|
|
11699
11900
|
" /apply-plan (code mode) force-approve pending/in-text plan (fallback)",
|
|
11700
|
-
" /mode [review|auto]
|
|
11901
|
+
" /mode [review|auto|yolo] (code mode) review = queue \xB7 auto = apply+undo banner \xB7 yolo = apply+auto-shell. Shift+Tab cycles all three.",
|
|
11701
11902
|
" /jobs (code mode) list background processes (run_background) \u2014 running and exited",
|
|
11702
11903
|
" /kill <id> (code mode) stop a background job by id (SIGTERM \u2192 SIGKILL)",
|
|
11703
11904
|
" /logs <id> [lines] (code mode) tail a background job's output (default 80 lines)",
|
|
@@ -11705,6 +11906,7 @@ var help = () => ({
|
|
|
11705
11906
|
" /forget delete the current session from disk",
|
|
11706
11907
|
" /new start fresh: drop all context + clear scrollback",
|
|
11707
11908
|
" /clear clear displayed scrollback only (context kept \u2014 model still sees it)",
|
|
11909
|
+
" /loop <interval> <prompt> auto-resubmit <prompt> every <interval> (5s..6h). /loop stop \xB7 type anything to cancel.",
|
|
11708
11910
|
" /exit quit",
|
|
11709
11911
|
"",
|
|
11710
11912
|
"Shell shortcut:",
|
|
@@ -11714,15 +11916,22 @@ var help = () => ({
|
|
|
11714
11916
|
" Example: !git status !ls src/ !npm test",
|
|
11715
11917
|
"",
|
|
11716
11918
|
"Quick memory:",
|
|
11717
|
-
" #<note> append <note> to REASONIX.md (
|
|
11718
|
-
"
|
|
11719
|
-
"
|
|
11919
|
+
" #<note> append <note> to <project>/REASONIX.md (committable).",
|
|
11920
|
+
" Example: #findByEmail must be case-insensitive",
|
|
11921
|
+
" #g <note> append <note> to ~/.reasonix/REASONIX.md (global, never committed).",
|
|
11922
|
+
" Example: #g always run pnpm not npm",
|
|
11923
|
+
" Both pin into every future session's prefix. Faster than /memory.",
|
|
11720
11924
|
" Use `\\#text` to send a literal `#text` to the model.",
|
|
11721
11925
|
"",
|
|
11722
11926
|
"File references (code mode):",
|
|
11723
11927
|
" @path/to/file inline file content under [Referenced files] on send.",
|
|
11724
11928
|
" Type `@` to open the picker (\u2191\u2193 navigate, Tab/Enter pick).",
|
|
11725
11929
|
"",
|
|
11930
|
+
"URL references:",
|
|
11931
|
+
" @https://example.com fetch the URL, strip HTML, inline under [Referenced URLs].",
|
|
11932
|
+
" Same URL twice in one session fetches once (in-mem cache).",
|
|
11933
|
+
" Trailing sentence punctuation (./,/)) is stripped automatically.",
|
|
11934
|
+
"",
|
|
11726
11935
|
"Presets (branch + harvest are NEVER auto-enabled \u2014 opt-in only):",
|
|
11727
11936
|
" fast v4-flash \xB7 effort=high cheapest \xB7 quick Q&A, one-line edits",
|
|
11728
11937
|
" smart v4-flash \xB7 effort=max \u2190 default \xB7 day-to-day coding",
|
|
@@ -11742,8 +11951,8 @@ var help = () => ({
|
|
|
11742
11951
|
var setup = () => ({
|
|
11743
11952
|
info: "To reconfigure (preset, MCP servers, API key), exit this chat and run `reasonix setup`. Changes take effect on next launch."
|
|
11744
11953
|
});
|
|
11745
|
-
var retry = (_args,
|
|
11746
|
-
const prev =
|
|
11954
|
+
var retry = (_args, loop2) => {
|
|
11955
|
+
const prev = loop2.retryLastUser();
|
|
11747
11956
|
if (!prev) {
|
|
11748
11957
|
return {
|
|
11749
11958
|
info: "nothing to retry \u2014 no prior user message in this session's log."
|
|
@@ -11755,6 +11964,37 @@ var retry = (_args, loop) => {
|
|
|
11755
11964
|
resubmit: prev
|
|
11756
11965
|
};
|
|
11757
11966
|
};
|
|
11967
|
+
var loop = (args, _loop, ctx) => {
|
|
11968
|
+
if (!ctx.startLoop || !ctx.stopLoop || !ctx.getLoopStatus) {
|
|
11969
|
+
return {
|
|
11970
|
+
info: "/loop is only available in the interactive TUI (not in run/replay)."
|
|
11971
|
+
};
|
|
11972
|
+
}
|
|
11973
|
+
const cmd = parseLoopCommand(args);
|
|
11974
|
+
if (cmd.kind === "error") return { info: cmd.message };
|
|
11975
|
+
if (cmd.kind === "stop") {
|
|
11976
|
+
const wasActive = ctx.getLoopStatus() !== null;
|
|
11977
|
+
ctx.stopLoop();
|
|
11978
|
+
return {
|
|
11979
|
+
info: wasActive ? "\u25B8 loop stopped." : "no active loop to stop."
|
|
11980
|
+
};
|
|
11981
|
+
}
|
|
11982
|
+
if (cmd.kind === "status") {
|
|
11983
|
+
const status2 = ctx.getLoopStatus();
|
|
11984
|
+
if (!status2) {
|
|
11985
|
+
return {
|
|
11986
|
+
info: "no active loop. Start one with `/loop <interval> <prompt>` (e.g. /loop 30s npm test).\nCancels on: /loop stop \xB7 Esc \xB7 /clear \xB7 /new \xB7 any user-typed prompt."
|
|
11987
|
+
};
|
|
11988
|
+
}
|
|
11989
|
+
return { info: `\u25B8 ${formatLoopStatus(status2.prompt, status2.nextFireMs, status2.iter)}` };
|
|
11990
|
+
}
|
|
11991
|
+
ctx.startLoop(cmd.intervalMs, cmd.prompt);
|
|
11992
|
+
return {
|
|
11993
|
+
info: `\u25B8 loop started \u2014 re-submitting "${cmd.prompt}" every ${formatDuration2(
|
|
11994
|
+
cmd.intervalMs
|
|
11995
|
+
)}. Type anything (or /loop stop) to cancel.`
|
|
11996
|
+
};
|
|
11997
|
+
};
|
|
11758
11998
|
var handlers2 = {
|
|
11759
11999
|
exit,
|
|
11760
12000
|
quit: exit,
|
|
@@ -11765,7 +12005,8 @@ var handlers2 = {
|
|
|
11765
12005
|
help,
|
|
11766
12006
|
"?": help,
|
|
11767
12007
|
setup,
|
|
11768
|
-
retry
|
|
12008
|
+
retry,
|
|
12009
|
+
loop
|
|
11769
12010
|
};
|
|
11770
12011
|
|
|
11771
12012
|
// src/cli/ui/slash/helpers.ts
|
|
@@ -11961,15 +12202,17 @@ var mode = (args, _loop, ctx) => {
|
|
|
11961
12202
|
let target;
|
|
11962
12203
|
if (raw === "review") target = "review";
|
|
11963
12204
|
else if (raw === "auto") target = "auto";
|
|
12205
|
+
else if (raw === "yolo") target = "yolo";
|
|
11964
12206
|
else if (raw === "") {
|
|
11965
|
-
target = current === "
|
|
12207
|
+
target = current === "review" ? "auto" : current === "auto" ? "yolo" : "review";
|
|
11966
12208
|
} else {
|
|
11967
|
-
return {
|
|
12209
|
+
return {
|
|
12210
|
+
info: "usage: /mode <review|auto|yolo> (Shift+Tab also cycles)"
|
|
12211
|
+
};
|
|
11968
12212
|
}
|
|
11969
12213
|
ctx.setEditMode(target);
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
};
|
|
12214
|
+
const banner = target === "yolo" ? "\u25B8 edit mode: YOLO \u2014 edits AND shell commands auto-run with no prompt. /undo still rolls back edits. Use carefully." : target === "auto" ? "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo, or /undo later. Shell commands still ask." : "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)";
|
|
12215
|
+
return { info: banner };
|
|
11973
12216
|
};
|
|
11974
12217
|
var commit = (args, _loop, ctx) => {
|
|
11975
12218
|
if (!ctx.codeRoot) {
|
|
@@ -11986,6 +12229,14 @@ var commit = (args, _loop, ctx) => {
|
|
|
11986
12229
|
}
|
|
11987
12230
|
return runGitCommit(ctx.codeRoot, message);
|
|
11988
12231
|
};
|
|
12232
|
+
var walk2 = (_args, _loop, ctx) => {
|
|
12233
|
+
if (!ctx.startWalkthrough) {
|
|
12234
|
+
return {
|
|
12235
|
+
info: "/walk is only available inside `reasonix code`."
|
|
12236
|
+
};
|
|
12237
|
+
}
|
|
12238
|
+
return { info: ctx.startWalkthrough() };
|
|
12239
|
+
};
|
|
11989
12240
|
var handlers3 = {
|
|
11990
12241
|
undo,
|
|
11991
12242
|
history,
|
|
@@ -11996,7 +12247,8 @@ var handlers3 = {
|
|
|
11996
12247
|
"apply-plan": applyPlan,
|
|
11997
12248
|
applyplan: applyPlan,
|
|
11998
12249
|
mode,
|
|
11999
|
-
commit
|
|
12250
|
+
commit,
|
|
12251
|
+
walk: walk2
|
|
12000
12252
|
};
|
|
12001
12253
|
|
|
12002
12254
|
// src/cli/ui/slash/handlers/jobs.ts
|
|
@@ -12061,10 +12313,10 @@ var handlers4 = {
|
|
|
12061
12313
|
};
|
|
12062
12314
|
|
|
12063
12315
|
// src/cli/ui/slash/handlers/mcp.ts
|
|
12064
|
-
var mcp = (_args,
|
|
12316
|
+
var mcp = (_args, loop2, ctx) => {
|
|
12065
12317
|
const servers = ctx.mcpServers ?? [];
|
|
12066
12318
|
const specs = ctx.mcpSpecs ?? [];
|
|
12067
|
-
const toolSpecs =
|
|
12319
|
+
const toolSpecs = loop2.prefix.toolSpecs ?? [];
|
|
12068
12320
|
if (servers.length === 0 && specs.length === 0 && toolSpecs.length === 0) {
|
|
12069
12321
|
return {
|
|
12070
12322
|
info: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog.'
|
|
@@ -12253,14 +12505,14 @@ var memory = (args, _loop, ctx) => {
|
|
|
12253
12505
|
var handlers6 = { memory };
|
|
12254
12506
|
|
|
12255
12507
|
// src/cli/ui/slash/handlers/model.ts
|
|
12256
|
-
var model = (args,
|
|
12508
|
+
var model = (args, loop2, ctx) => {
|
|
12257
12509
|
const id = args[0];
|
|
12258
12510
|
const known = ctx.models ?? null;
|
|
12259
12511
|
if (!id) {
|
|
12260
12512
|
const hint = known && known.length > 0 ? known.join(" | ") : "try deepseek-v4-flash or deepseek-v4-pro \u2014 run /models to fetch the live list";
|
|
12261
12513
|
return { info: `usage: /model <id> (${hint})` };
|
|
12262
12514
|
}
|
|
12263
|
-
|
|
12515
|
+
loop2.configure({ model: id });
|
|
12264
12516
|
if (known && known.length > 0 && !known.includes(id)) {
|
|
12265
12517
|
return {
|
|
12266
12518
|
info: `model \u2192 ${id} (\u26A0 not in the fetched catalog: ${known.join(", ")}. If this is wrong the next call will 400 \u2014 run /models to refresh.)`
|
|
@@ -12268,7 +12520,7 @@ var model = (args, loop, ctx) => {
|
|
|
12268
12520
|
}
|
|
12269
12521
|
return { info: `model \u2192 ${id}` };
|
|
12270
12522
|
};
|
|
12271
|
-
var models = (_args,
|
|
12523
|
+
var models = (_args, loop2, ctx) => {
|
|
12272
12524
|
const list = ctx.models ?? null;
|
|
12273
12525
|
if (list === null) {
|
|
12274
12526
|
ctx.refreshModels?.();
|
|
@@ -12281,7 +12533,7 @@ var models = (_args, loop, ctx) => {
|
|
|
12281
12533
|
info: "DeepSeek /models returned an empty list. Try /models again, or check your account status at api-docs.deepseek.com."
|
|
12282
12534
|
};
|
|
12283
12535
|
}
|
|
12284
|
-
const current =
|
|
12536
|
+
const current = loop2.model;
|
|
12285
12537
|
const lines = list.map((id) => id === current ? `\u25B8 ${id} (current)` : ` ${id}`);
|
|
12286
12538
|
return {
|
|
12287
12539
|
info: [
|
|
@@ -12293,18 +12545,18 @@ var models = (_args, loop, ctx) => {
|
|
|
12293
12545
|
].join("\n")
|
|
12294
12546
|
};
|
|
12295
12547
|
};
|
|
12296
|
-
var harvest2 = (args,
|
|
12548
|
+
var harvest2 = (args, loop2) => {
|
|
12297
12549
|
const arg = (args[0] ?? "").toLowerCase();
|
|
12298
|
-
const on = arg === "" ? !
|
|
12299
|
-
|
|
12300
|
-
if (
|
|
12550
|
+
const on = arg === "" ? !loop2.harvestEnabled : arg === "on" || arg === "true" || arg === "1";
|
|
12551
|
+
loop2.configure({ harvest: on });
|
|
12552
|
+
if (loop2.harvestEnabled) {
|
|
12301
12553
|
return {
|
|
12302
12554
|
info: "harvest \u2192 on (Pillar-2 plan-state extraction \xB7 +1 cheap flash call per turn \xB7 opt-in only; no preset turns it on)"
|
|
12303
12555
|
};
|
|
12304
12556
|
}
|
|
12305
12557
|
return { info: "harvest \u2192 off" };
|
|
12306
12558
|
};
|
|
12307
|
-
var preset = (args,
|
|
12559
|
+
var preset = (args, loop2) => {
|
|
12308
12560
|
const name = (args[0] ?? "").toLowerCase();
|
|
12309
12561
|
const applyAndPersist = (effort2) => {
|
|
12310
12562
|
try {
|
|
@@ -12313,7 +12565,7 @@ var preset = (args, loop) => {
|
|
|
12313
12565
|
}
|
|
12314
12566
|
};
|
|
12315
12567
|
if (name === "fast" || name === "default") {
|
|
12316
|
-
|
|
12568
|
+
loop2.configure({
|
|
12317
12569
|
model: "deepseek-v4-flash",
|
|
12318
12570
|
reasoningEffort: "high",
|
|
12319
12571
|
harvest: false,
|
|
@@ -12323,7 +12575,7 @@ var preset = (args, loop) => {
|
|
|
12323
12575
|
return { info: "preset \u2192 fast (v4-flash \xB7 effort=high \xB7 cheapest)" };
|
|
12324
12576
|
}
|
|
12325
12577
|
if (name === "smart") {
|
|
12326
|
-
|
|
12578
|
+
loop2.configure({
|
|
12327
12579
|
model: "deepseek-v4-flash",
|
|
12328
12580
|
reasoningEffort: "max",
|
|
12329
12581
|
harvest: false,
|
|
@@ -12333,7 +12585,7 @@ var preset = (args, loop) => {
|
|
|
12333
12585
|
return { info: "preset \u2192 smart (v4-flash \xB7 effort=max \xB7 default \xB7 ~1.5\xD7 fast)" };
|
|
12334
12586
|
}
|
|
12335
12587
|
if (name === "max" || name === "best") {
|
|
12336
|
-
|
|
12588
|
+
loop2.configure({
|
|
12337
12589
|
model: "deepseek-v4-pro",
|
|
12338
12590
|
reasoningEffort: "max",
|
|
12339
12591
|
harvest: false,
|
|
@@ -12346,10 +12598,10 @@ var preset = (args, loop) => {
|
|
|
12346
12598
|
}
|
|
12347
12599
|
return { info: "usage: /preset <fast|smart|max>" };
|
|
12348
12600
|
};
|
|
12349
|
-
var branch = (args,
|
|
12601
|
+
var branch = (args, loop2) => {
|
|
12350
12602
|
const raw = (args[0] ?? "").toLowerCase();
|
|
12351
12603
|
if (raw === "" || raw === "off" || raw === "0" || raw === "1") {
|
|
12352
|
-
|
|
12604
|
+
loop2.configure({ branch: 1 });
|
|
12353
12605
|
return { info: "branch \u2192 off" };
|
|
12354
12606
|
}
|
|
12355
12607
|
const n = Number.parseInt(raw, 10);
|
|
@@ -12359,36 +12611,36 @@ var branch = (args, loop) => {
|
|
|
12359
12611
|
if (n > 8) {
|
|
12360
12612
|
return { info: "branch budget capped at 8 to prevent runaway cost" };
|
|
12361
12613
|
}
|
|
12362
|
-
|
|
12614
|
+
loop2.configure({ branch: n });
|
|
12363
12615
|
return {
|
|
12364
12616
|
info: `branch \u2192 ${n} (runs ${n} parallel samples per turn \xB7 ${n}\xD7 per-turn cost \xB7 streaming disabled \xB7 manual only, no preset enables branching)`
|
|
12365
12617
|
};
|
|
12366
12618
|
};
|
|
12367
|
-
var effort = (args,
|
|
12619
|
+
var effort = (args, loop2) => {
|
|
12368
12620
|
const raw = (args[0] ?? "").toLowerCase();
|
|
12369
12621
|
if (raw === "") {
|
|
12370
12622
|
return {
|
|
12371
|
-
info: `reasoning_effort \u2192 ${
|
|
12623
|
+
info: `reasoning_effort \u2192 ${loop2.reasoningEffort} (use /effort high for cheaper/faster, /effort max for the agent-class default \xB7 persisted across relaunches)`
|
|
12372
12624
|
};
|
|
12373
12625
|
}
|
|
12374
12626
|
if (raw !== "high" && raw !== "max") {
|
|
12375
12627
|
return { info: "usage: /effort <high|max>" };
|
|
12376
12628
|
}
|
|
12377
|
-
|
|
12629
|
+
loop2.configure({ reasoningEffort: raw });
|
|
12378
12630
|
try {
|
|
12379
12631
|
saveReasoningEffort(raw);
|
|
12380
12632
|
} catch {
|
|
12381
12633
|
}
|
|
12382
12634
|
return { info: `reasoning_effort \u2192 ${raw} (persisted)` };
|
|
12383
12635
|
};
|
|
12384
|
-
var pro = (args,
|
|
12636
|
+
var pro = (args, loop2, ctx) => {
|
|
12385
12637
|
const arg = (args[0] ?? "").toLowerCase();
|
|
12386
12638
|
if (arg === "off" || arg === "cancel" || arg === "disarm") {
|
|
12387
|
-
if (!
|
|
12639
|
+
if (!loop2.proArmed) {
|
|
12388
12640
|
return { info: "nothing armed \u2014 /pro with no args will arm pro for your next turn" };
|
|
12389
12641
|
}
|
|
12390
12642
|
if (ctx.disarmPro) ctx.disarmPro();
|
|
12391
|
-
else
|
|
12643
|
+
else loop2.disarmPro();
|
|
12392
12644
|
return { info: "\u25B8 /pro disarmed \u2014 next turn falls back to the current preset" };
|
|
12393
12645
|
}
|
|
12394
12646
|
if (arg && arg !== "on" && arg !== "arm") {
|
|
@@ -12397,7 +12649,7 @@ var pro = (args, loop, ctx) => {
|
|
|
12397
12649
|
};
|
|
12398
12650
|
}
|
|
12399
12651
|
if (ctx.armPro) ctx.armPro();
|
|
12400
|
-
else
|
|
12652
|
+
else loop2.armProForNextTurn();
|
|
12401
12653
|
return {
|
|
12402
12654
|
info: `\u25B8 /pro armed \u2014 your NEXT message runs on ${ESCALATION_MODEL_ID} regardless of preset. Auto-disarms after one turn. Use /preset max for a persistent switch.`
|
|
12403
12655
|
};
|
|
@@ -12414,8 +12666,8 @@ var handlers7 = {
|
|
|
12414
12666
|
};
|
|
12415
12667
|
|
|
12416
12668
|
// src/cli/ui/slash/handlers/observability.ts
|
|
12417
|
-
var think = (_args,
|
|
12418
|
-
const raw =
|
|
12669
|
+
var think = (_args, loop2) => {
|
|
12670
|
+
const raw = loop2.scratch.reasoning;
|
|
12419
12671
|
if (!raw || !raw.trim()) {
|
|
12420
12672
|
return {
|
|
12421
12673
|
info: "no reasoning cached. `/think` shows the full thinking-mode thought for the most recent turn \u2014 only thinking-mode models (deepseek-v4-flash / -v4-pro / -reasoner) produce it, and only once the turn completes."
|
|
@@ -12457,10 +12709,10 @@ var tool = (args, _loop, ctx) => {
|
|
|
12457
12709
|
${entry.text}`
|
|
12458
12710
|
};
|
|
12459
12711
|
};
|
|
12460
|
-
var context = (_args,
|
|
12461
|
-
const systemTokens = countTokens(
|
|
12462
|
-
const toolsTokens = countTokens(JSON.stringify(
|
|
12463
|
-
const entries =
|
|
12712
|
+
var context = (_args, loop2) => {
|
|
12713
|
+
const systemTokens = countTokens(loop2.prefix.system);
|
|
12714
|
+
const toolsTokens = countTokens(JSON.stringify(loop2.prefix.toolSpecs));
|
|
12715
|
+
const entries = loop2.log.toMessages();
|
|
12464
12716
|
let userTokens = 0;
|
|
12465
12717
|
let assistantTokens = 0;
|
|
12466
12718
|
let toolResultTokens = 0;
|
|
@@ -12485,7 +12737,7 @@ var context = (_args, loop) => {
|
|
|
12485
12737
|
}
|
|
12486
12738
|
const logTokens = userTokens + assistantTokens + toolResultTokens + toolCallTokens;
|
|
12487
12739
|
const total = systemTokens + toolsTokens + logTokens;
|
|
12488
|
-
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[
|
|
12740
|
+
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[loop2.model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
12489
12741
|
const pct2 = (n) => total > 0 ? `${Math.round(n / total * 100)}%`.padStart(4) : " 0%";
|
|
12490
12742
|
const row2 = (label, n, note = "") => ` ${label.padEnd(20)}${compactNum(n).padStart(8)} tokens ${pct2(n)}${note ? ` ${note}` : ""}`;
|
|
12491
12743
|
const lines = [
|
|
@@ -12494,7 +12746,7 @@ var context = (_args, loop) => {
|
|
|
12494
12746
|
)}% of window)`,
|
|
12495
12747
|
"",
|
|
12496
12748
|
row2("system prompt", systemTokens),
|
|
12497
|
-
row2("tool specs", toolsTokens, `(${
|
|
12749
|
+
row2("tool specs", toolsTokens, `(${loop2.prefix.toolSpecs.length} tools)`),
|
|
12498
12750
|
row2("log (all turns)", logTokens, `(${entries.length} messages)`),
|
|
12499
12751
|
` user ${compactNum(userTokens).padStart(8)} tokens`,
|
|
12500
12752
|
` assistant ${compactNum(assistantTokens).padStart(8)} tokens`,
|
|
@@ -12517,23 +12769,23 @@ var context = (_args, loop) => {
|
|
|
12517
12769
|
);
|
|
12518
12770
|
return { info: lines.join("\n") };
|
|
12519
12771
|
};
|
|
12520
|
-
var status = (_args,
|
|
12521
|
-
const branchBudget =
|
|
12522
|
-
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[
|
|
12523
|
-
const lastPromptTokens =
|
|
12772
|
+
var status = (_args, loop2, ctx) => {
|
|
12773
|
+
const branchBudget = loop2.branchOptions.budget ?? 1;
|
|
12774
|
+
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[loop2.model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
12775
|
+
const lastPromptTokens = loop2.stats.summary().lastPromptTokens;
|
|
12524
12776
|
const ctxPct = ctxMax > 0 ? Math.round(lastPromptTokens / ctxMax * 100) : 0;
|
|
12525
12777
|
const ctxLine = lastPromptTokens > 0 ? ` ctx ${compactNum(lastPromptTokens)}/${compactNum(ctxMax)} (${ctxPct}%)` : " ctx no turns yet";
|
|
12526
12778
|
const pending = ctx.pendingEditCount ?? 0;
|
|
12527
|
-
const sessionLine =
|
|
12779
|
+
const sessionLine = loop2.sessionName ? ` session "${loop2.sessionName}" \xB7 ${loop2.log.length} messages in log (resumed ${loop2.resumedMessageCount})` : " session (ephemeral \u2014 no persistence)";
|
|
12528
12780
|
const mcpCount = ctx.mcpSpecs?.length ?? 0;
|
|
12529
|
-
const toolCount =
|
|
12781
|
+
const toolCount = loop2.prefix.toolSpecs.length;
|
|
12530
12782
|
const mcpLine = ` mcp ${mcpCount} server(s), ${toolCount} tool(s) in registry`;
|
|
12531
12783
|
const pendingLine = pending > 0 ? ` edits ${pending} pending (/apply to commit, /discard to drop)` : "";
|
|
12532
12784
|
const planLine = ctx.planMode ? " plan ON \u2014 writes gated (submit_plan + approval)" : "";
|
|
12533
|
-
const modeLine = ctx.editMode === "auto" ? " mode AUTO \u2014 edits apply immediately (u to undo within 5s \xB7 Shift+Tab to flip)" : ctx.editMode === "review" ? " mode review \u2014 edits queue for /apply or y (Shift+Tab to flip)" : "";
|
|
12785
|
+
const modeLine = ctx.editMode === "yolo" ? " mode YOLO \u2014 edits + shell auto-run with no prompt (/undo still rolls back \xB7 Shift+Tab to flip)" : ctx.editMode === "auto" ? " mode AUTO \u2014 edits apply immediately (u to undo within 5s \xB7 Shift+Tab to flip)" : ctx.editMode === "review" ? " mode review \u2014 edits queue for /apply or y (Shift+Tab to flip)" : "";
|
|
12534
12786
|
const lines = [
|
|
12535
|
-
` model ${
|
|
12536
|
-
` flags harvest=${
|
|
12787
|
+
` model ${loop2.model}`,
|
|
12788
|
+
` flags harvest=${loop2.harvestEnabled ? "on" : "off"} \xB7 branch=${branchBudget > 1 ? branchBudget : "off"} \xB7 stream=${loop2.stream ? "on" : "off"} \xB7 effort=${loop2.reasoningEffort}`,
|
|
12537
12789
|
ctxLine,
|
|
12538
12790
|
mcpLine,
|
|
12539
12791
|
sessionLine
|
|
@@ -12543,10 +12795,10 @@ var status = (_args, loop, ctx) => {
|
|
|
12543
12795
|
if (modeLine) lines.push(modeLine);
|
|
12544
12796
|
return { info: lines.join("\n") };
|
|
12545
12797
|
};
|
|
12546
|
-
var compact = (args,
|
|
12798
|
+
var compact = (args, loop2) => {
|
|
12547
12799
|
const tight = Number.parseInt(args[0] ?? "", 10);
|
|
12548
12800
|
const cap = Number.isFinite(tight) && tight >= 100 ? tight : 4e3;
|
|
12549
|
-
const { healedCount, tokensSaved, charsSaved } =
|
|
12801
|
+
const { healedCount, tokensSaved, charsSaved } = loop2.compact(cap);
|
|
12550
12802
|
if (healedCount === 0) {
|
|
12551
12803
|
return {
|
|
12552
12804
|
info: `\u25B8 nothing to compact \u2014 no tool result or tool-call args in history exceed ${cap.toLocaleString()} tokens.`
|
|
@@ -12567,8 +12819,8 @@ var handlers8 = {
|
|
|
12567
12819
|
|
|
12568
12820
|
// src/cli/ui/slash/handlers/plans.ts
|
|
12569
12821
|
import { basename } from "path";
|
|
12570
|
-
var plans = (_args,
|
|
12571
|
-
const sessionName =
|
|
12822
|
+
var plans = (_args, loop2) => {
|
|
12823
|
+
const sessionName = loop2.sessionName;
|
|
12572
12824
|
if (!sessionName) {
|
|
12573
12825
|
return {
|
|
12574
12826
|
info: "no session attached \u2014 `/plans` is per-session. Run `reasonix code` in a project to get a session."
|
|
@@ -12609,8 +12861,8 @@ var plans = (_args, loop) => {
|
|
|
12609
12861
|
}
|
|
12610
12862
|
return { info: lines.join("\n") };
|
|
12611
12863
|
};
|
|
12612
|
-
var replay = (args,
|
|
12613
|
-
const sessionName =
|
|
12864
|
+
var replay = (args, loop2) => {
|
|
12865
|
+
const sessionName = loop2.sessionName;
|
|
12614
12866
|
if (!sessionName) {
|
|
12615
12867
|
return {
|
|
12616
12868
|
info: "no session attached \u2014 `/replay` is per-session. Run `reasonix code` in a project to get a session."
|
|
@@ -12650,7 +12902,7 @@ var handlers9 = {
|
|
|
12650
12902
|
};
|
|
12651
12903
|
|
|
12652
12904
|
// src/cli/ui/slash/handlers/sessions.ts
|
|
12653
|
-
var sessions = (_args,
|
|
12905
|
+
var sessions = (_args, loop2) => {
|
|
12654
12906
|
const items = listSessions();
|
|
12655
12907
|
if (items.length === 0) {
|
|
12656
12908
|
return {
|
|
@@ -12661,7 +12913,7 @@ var sessions = (_args, loop) => {
|
|
|
12661
12913
|
for (const s of items) {
|
|
12662
12914
|
const sizeKb = (s.size / 1024).toFixed(1);
|
|
12663
12915
|
const when = s.mtime.toISOString().replace("T", " ").slice(0, 16);
|
|
12664
|
-
const marker = s.name ===
|
|
12916
|
+
const marker = s.name === loop2.sessionName ? "\u25B8" : " ";
|
|
12665
12917
|
lines.push(
|
|
12666
12918
|
` ${marker} ${s.name.padEnd(22)} ${String(s.messageCount).padStart(5)} msgs ${sizeKb.padStart(7)} KB ${when}`
|
|
12667
12919
|
);
|
|
@@ -12670,11 +12922,11 @@ var sessions = (_args, loop) => {
|
|
|
12670
12922
|
lines.push("Resume with: reasonix chat --session <name>");
|
|
12671
12923
|
return { info: lines.join("\n") };
|
|
12672
12924
|
};
|
|
12673
|
-
var forget = (_args,
|
|
12674
|
-
if (!
|
|
12925
|
+
var forget = (_args, loop2) => {
|
|
12926
|
+
if (!loop2.sessionName) {
|
|
12675
12927
|
return { info: "not in a session \u2014 nothing to forget" };
|
|
12676
12928
|
}
|
|
12677
|
-
const name =
|
|
12929
|
+
const name = loop2.sessionName;
|
|
12678
12930
|
const ok = deleteSession(name);
|
|
12679
12931
|
return {
|
|
12680
12932
|
info: ok ? `\u25B8 deleted session "${name}" \u2014 current screen still shows the conversation, but next launch starts fresh` : `could not delete session "${name}" (already gone?)`
|
|
@@ -12775,9 +13027,9 @@ var HANDLERS = {
|
|
|
12775
13027
|
...handlers10,
|
|
12776
13028
|
...handlers11
|
|
12777
13029
|
};
|
|
12778
|
-
function handleSlash(cmd, args,
|
|
13030
|
+
function handleSlash(cmd, args, loop2, ctx = {}) {
|
|
12779
13031
|
const h = HANDLERS[cmd];
|
|
12780
|
-
if (h) return h(args,
|
|
13032
|
+
if (h) return h(args, loop2, ctx);
|
|
12781
13033
|
return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
|
|
12782
13034
|
}
|
|
12783
13035
|
|
|
@@ -13129,14 +13381,14 @@ function useEditHistory(codeMode) {
|
|
|
13129
13381
|
|
|
13130
13382
|
// src/cli/ui/useSessionInfo.ts
|
|
13131
13383
|
import { useCallback as useCallback3, useEffect as useEffect4, useState as useState8 } from "react";
|
|
13132
|
-
function useSessionInfo(
|
|
13384
|
+
function useSessionInfo(loop2) {
|
|
13133
13385
|
const [balance, setBalance] = useState8(null);
|
|
13134
13386
|
const [models2, setModels] = useState8(null);
|
|
13135
13387
|
const [latestVersion, setLatestVersion] = useState8(null);
|
|
13136
13388
|
useEffect4(() => {
|
|
13137
13389
|
let cancelled = false;
|
|
13138
13390
|
void (async () => {
|
|
13139
|
-
const bal = await
|
|
13391
|
+
const bal = await loop2.client.getBalance().catch(() => null);
|
|
13140
13392
|
if (cancelled || !bal || !bal.balance_infos.length) return;
|
|
13141
13393
|
const primary = bal.balance_infos[0];
|
|
13142
13394
|
setBalance({ currency: primary.currency, total: Number(primary.total_balance) });
|
|
@@ -13144,18 +13396,18 @@ function useSessionInfo(loop) {
|
|
|
13144
13396
|
return () => {
|
|
13145
13397
|
cancelled = true;
|
|
13146
13398
|
};
|
|
13147
|
-
}, [
|
|
13399
|
+
}, [loop2]);
|
|
13148
13400
|
useEffect4(() => {
|
|
13149
13401
|
let cancelled = false;
|
|
13150
13402
|
void (async () => {
|
|
13151
|
-
const list = await
|
|
13403
|
+
const list = await loop2.client.listModels().catch(() => null);
|
|
13152
13404
|
if (cancelled || !list) return;
|
|
13153
13405
|
setModels(list.data.map((m) => m.id));
|
|
13154
13406
|
})();
|
|
13155
13407
|
return () => {
|
|
13156
13408
|
cancelled = true;
|
|
13157
13409
|
};
|
|
13158
|
-
}, [
|
|
13410
|
+
}, [loop2]);
|
|
13159
13411
|
useEffect4(() => {
|
|
13160
13412
|
let cancelled = false;
|
|
13161
13413
|
void (async () => {
|
|
@@ -13170,19 +13422,19 @@ function useSessionInfo(loop) {
|
|
|
13170
13422
|
const updateAvailable = latestVersion && compareVersions(VERSION, latestVersion) < 0 ? latestVersion : null;
|
|
13171
13423
|
const refreshBalance = useCallback3(() => {
|
|
13172
13424
|
void (async () => {
|
|
13173
|
-
const bal = await
|
|
13425
|
+
const bal = await loop2.client.getBalance().catch(() => null);
|
|
13174
13426
|
if (bal?.balance_infos.length) {
|
|
13175
13427
|
const p = bal.balance_infos[0];
|
|
13176
13428
|
setBalance({ currency: p.currency, total: Number(p.total_balance) });
|
|
13177
13429
|
}
|
|
13178
13430
|
})();
|
|
13179
|
-
}, [
|
|
13431
|
+
}, [loop2]);
|
|
13180
13432
|
const refreshModels = useCallback3(() => {
|
|
13181
13433
|
void (async () => {
|
|
13182
|
-
const list = await
|
|
13434
|
+
const list = await loop2.client.listModels().catch(() => null);
|
|
13183
13435
|
if (list) setModels(list.data.map((m) => m.id));
|
|
13184
13436
|
})();
|
|
13185
|
-
}, [
|
|
13437
|
+
}, [loop2]);
|
|
13186
13438
|
const refreshLatestVersion = useCallback3(() => {
|
|
13187
13439
|
void (async () => {
|
|
13188
13440
|
const fresh = await getLatestVersion({ force: true });
|
|
@@ -13260,6 +13512,17 @@ function useSubagent({ session, setHistorical }) {
|
|
|
13260
13512
|
// src/cli/ui/App.tsx
|
|
13261
13513
|
var FLUSH_INTERVAL_MS = 100;
|
|
13262
13514
|
var PLAIN_UI = process.env.REASONIX_UI === "plain";
|
|
13515
|
+
function LoopStatusRow({
|
|
13516
|
+
loop: loop2
|
|
13517
|
+
}) {
|
|
13518
|
+
const [, setTick] = React23.useState(0);
|
|
13519
|
+
React23.useEffect(() => {
|
|
13520
|
+
const id = setInterval(() => setTick((t) => t + 1), 1e3);
|
|
13521
|
+
return () => clearInterval(id);
|
|
13522
|
+
}, []);
|
|
13523
|
+
const nextFireMs = Math.max(0, loop2.nextFireAt - Date.now());
|
|
13524
|
+
return /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { color: "cyan" }, `\u25B8 ${formatLoopStatus(loop2.prompt, nextFireMs, loop2.iter)} \xB7 /loop stop or type to cancel`));
|
|
13525
|
+
}
|
|
13263
13526
|
function App({
|
|
13264
13527
|
model: model2,
|
|
13265
13528
|
system,
|
|
@@ -13279,6 +13542,9 @@ function App({
|
|
|
13279
13542
|
const [input, setInput] = useState10("");
|
|
13280
13543
|
const [busy, setBusy] = useState10(false);
|
|
13281
13544
|
const abortedThisTurn = useRef6(false);
|
|
13545
|
+
useEffect6(() => {
|
|
13546
|
+
busyRef.current = busy;
|
|
13547
|
+
}, [busy]);
|
|
13282
13548
|
const [ongoingTool, setOngoingTool] = useState10(null);
|
|
13283
13549
|
const [toolProgress, setToolProgress] = useState10(null);
|
|
13284
13550
|
const { stdout: stdout2 } = useStdout8();
|
|
@@ -13332,6 +13598,7 @@ function App({
|
|
|
13332
13598
|
const [pendingCount, setPendingCount] = useState10(0);
|
|
13333
13599
|
const syncPendingCount = useCallback4(() => {
|
|
13334
13600
|
setPendingCount(pendingEdits.current.length);
|
|
13601
|
+
setPendingTick((t) => t + 1);
|
|
13335
13602
|
}, []);
|
|
13336
13603
|
const [editMode, setEditMode] = useState10(() => codeMode ? loadEditMode() : "review");
|
|
13337
13604
|
const editModeRef = useRef6(editMode);
|
|
@@ -13340,6 +13607,8 @@ function App({
|
|
|
13340
13607
|
if (codeMode) saveEditMode(editMode);
|
|
13341
13608
|
}, [editMode, codeMode]);
|
|
13342
13609
|
const [pendingEditReview, setPendingEditReview] = useState10(null);
|
|
13610
|
+
const [walkthroughActive, setWalkthroughActive] = useState10(false);
|
|
13611
|
+
const [pendingTick, setPendingTick] = useState10(0);
|
|
13343
13612
|
const editReviewResolveRef = useRef6(null);
|
|
13344
13613
|
const turnEditPolicyRef = useRef6("ask");
|
|
13345
13614
|
const [modeFlash, setModeFlash] = useState10(false);
|
|
@@ -13370,6 +13639,16 @@ function App({
|
|
|
13370
13639
|
const promptHistory = useRef6([]);
|
|
13371
13640
|
const historyCursor = useRef6(-1);
|
|
13372
13641
|
const assistantIterCounter = useRef6(0);
|
|
13642
|
+
const atUrlCache = useRef6(/* @__PURE__ */ new Map());
|
|
13643
|
+
const [activeLoop, setActiveLoop] = useState10(null);
|
|
13644
|
+
const loopTimerRef = useRef6(null);
|
|
13645
|
+
const handleSubmitRef = useRef6(null);
|
|
13646
|
+
const busyRef = useRef6(false);
|
|
13647
|
+
const activeLoopRef = useRef6(activeLoop);
|
|
13648
|
+
const loopFiringRef = useRef6(false);
|
|
13649
|
+
useEffect6(() => {
|
|
13650
|
+
activeLoopRef.current = activeLoop;
|
|
13651
|
+
}, [activeLoop]);
|
|
13373
13652
|
const toolHistoryRef = useRef6([]);
|
|
13374
13653
|
const planStepsRef = useRef6(null);
|
|
13375
13654
|
const completedStepIdsRef = useRef6(/* @__PURE__ */ new Set());
|
|
@@ -13414,7 +13693,7 @@ function App({
|
|
|
13414
13693
|
};
|
|
13415
13694
|
}, []);
|
|
13416
13695
|
const loopRef = useRef6(null);
|
|
13417
|
-
const
|
|
13696
|
+
const loop2 = useMemo3(() => {
|
|
13418
13697
|
if (loopRef.current) return loopRef.current;
|
|
13419
13698
|
const client = new DeepSeekClient();
|
|
13420
13699
|
if (tools && !tools.has("run_skill")) {
|
|
@@ -13463,8 +13742,8 @@ function App({
|
|
|
13463
13742
|
return l;
|
|
13464
13743
|
}, [model2, system, harvest3, branch2, session, tools, codeMode]);
|
|
13465
13744
|
useEffect6(() => {
|
|
13466
|
-
|
|
13467
|
-
}, [
|
|
13745
|
+
loop2.hooks = hookList;
|
|
13746
|
+
}, [loop2, hookList]);
|
|
13468
13747
|
const {
|
|
13469
13748
|
balance,
|
|
13470
13749
|
models: models2,
|
|
@@ -13473,7 +13752,7 @@ function App({
|
|
|
13473
13752
|
refreshBalance,
|
|
13474
13753
|
refreshModels,
|
|
13475
13754
|
refreshLatestVersion
|
|
13476
|
-
} = useSessionInfo(
|
|
13755
|
+
} = useSessionInfo(loop2);
|
|
13477
13756
|
const {
|
|
13478
13757
|
slashMatches,
|
|
13479
13758
|
slashSelected,
|
|
@@ -13516,13 +13795,13 @@ function App({
|
|
|
13516
13795
|
text: "\u25B8 ephemeral chat (no session persistence) \u2014 drop --no-session to enable"
|
|
13517
13796
|
}
|
|
13518
13797
|
]);
|
|
13519
|
-
} else if (
|
|
13798
|
+
} else if (loop2.resumedMessageCount > 0) {
|
|
13520
13799
|
setHistorical((prev) => [
|
|
13521
13800
|
...prev,
|
|
13522
13801
|
{
|
|
13523
13802
|
id: `sys-resume-${Date.now()}`,
|
|
13524
13803
|
role: "info",
|
|
13525
|
-
text: `\u25B8 resumed session "${session}" with ${
|
|
13804
|
+
text: `\u25B8 resumed session "${session}" with ${loop2.resumedMessageCount} prior messages \xB7 /forget to start over \xB7 /sessions to list`
|
|
13526
13805
|
}
|
|
13527
13806
|
]);
|
|
13528
13807
|
} else {
|
|
@@ -13585,7 +13864,7 @@ function App({
|
|
|
13585
13864
|
]);
|
|
13586
13865
|
markEditModeHintShown();
|
|
13587
13866
|
}
|
|
13588
|
-
}, [session,
|
|
13867
|
+
}, [session, loop2, codeMode, syncPendingCount]);
|
|
13589
13868
|
const quitProcess = useCallback4(() => {
|
|
13590
13869
|
transcriptRef.current?.end();
|
|
13591
13870
|
process.exit(0);
|
|
@@ -13615,25 +13894,40 @@ function App({
|
|
|
13615
13894
|
setPendingEditReview(null);
|
|
13616
13895
|
resolve8("reject");
|
|
13617
13896
|
}
|
|
13618
|
-
|
|
13897
|
+
if (activeLoopRef.current) stopLoop();
|
|
13898
|
+
loop2.abort();
|
|
13899
|
+
return;
|
|
13900
|
+
}
|
|
13901
|
+
if (key.escape && !busy && activeLoopRef.current) {
|
|
13902
|
+
stopLoop();
|
|
13619
13903
|
return;
|
|
13620
13904
|
}
|
|
13621
|
-
if (
|
|
13905
|
+
if (key.escape && walkthroughActive) {
|
|
13906
|
+
setWalkthroughActive(false);
|
|
13907
|
+
const remaining = pendingEdits.current.length;
|
|
13908
|
+
setHistorical((prev) => [
|
|
13909
|
+
...prev,
|
|
13910
|
+
{
|
|
13911
|
+
id: `walk-esc-${Date.now()}`,
|
|
13912
|
+
role: "info",
|
|
13913
|
+
text: remaining > 0 ? `\u25B8 walk cancelled \u2014 ${remaining} block(s) still pending.` : "\u25B8 walk cancelled."
|
|
13914
|
+
}
|
|
13915
|
+
]);
|
|
13916
|
+
return;
|
|
13917
|
+
}
|
|
13918
|
+
if (codeMode && key.shift && key.tab && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision) {
|
|
13622
13919
|
setEditMode((m) => {
|
|
13623
|
-
const next = m === "
|
|
13920
|
+
const next = m === "review" ? "auto" : m === "auto" ? "yolo" : "review";
|
|
13921
|
+
const message = next === "yolo" ? "\u25B8 edit mode: YOLO \u2014 edits AND shell commands auto-run. /undo still rolls back edits. Use carefully." : next === "auto" ? "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo. Shell commands still ask." : "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)";
|
|
13624
13922
|
setHistorical((prev) => [
|
|
13625
13923
|
...prev,
|
|
13626
|
-
{
|
|
13627
|
-
id: `mode-${Date.now()}`,
|
|
13628
|
-
role: "info",
|
|
13629
|
-
text: next === "auto" ? "\u25B8 edit mode: AUTO \u2014 edits apply immediately; press u within 5s to undo" : "\u25B8 edit mode: review \u2014 edits queue for /apply (or y) / /discard (or n)"
|
|
13630
|
-
}
|
|
13924
|
+
{ id: `mode-${Date.now()}`, role: "info", text: message }
|
|
13631
13925
|
]);
|
|
13632
13926
|
return next;
|
|
13633
13927
|
});
|
|
13634
13928
|
return;
|
|
13635
13929
|
}
|
|
13636
|
-
if (codeMode && input.length === 0 && (chKey === "u" || chKey === "U") && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision && // Fire when EITHER the banner is up OR there's any non-undone
|
|
13930
|
+
if (codeMode && input.length === 0 && (chKey === "u" || chKey === "U") && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision && // Fire when EITHER the banner is up OR there's any non-undone
|
|
13637
13931
|
// history entry — the keybind is useful long after the 5-second
|
|
13638
13932
|
// banner expires, which users rightly want.
|
|
13639
13933
|
(undoBanner || hasUndoable())) {
|
|
@@ -13734,7 +14028,7 @@ function App({
|
|
|
13734
14028
|
}
|
|
13735
14029
|
return formatEditResults(results);
|
|
13736
14030
|
};
|
|
13737
|
-
if (editModeRef.current === "auto") return applyNow();
|
|
14031
|
+
if (editModeRef.current === "auto" || editModeRef.current === "yolo") return applyNow();
|
|
13738
14032
|
if (turnEditPolicyRef.current === "apply-all") return applyNow();
|
|
13739
14033
|
const choice = await new Promise((resolveChoice) => {
|
|
13740
14034
|
editReviewResolveRef.current = resolveChoice;
|
|
@@ -13827,7 +14121,7 @@ function App({
|
|
|
13827
14121
|
},
|
|
13828
14122
|
[session, syncPendingCount]
|
|
13829
14123
|
);
|
|
13830
|
-
const prefixHash =
|
|
14124
|
+
const prefixHash = loop2.prefix.fingerprint;
|
|
13831
14125
|
const writeTranscript = useCallback4(
|
|
13832
14126
|
(ev) => {
|
|
13833
14127
|
const stream = transcriptRef.current;
|
|
@@ -13846,10 +14140,98 @@ function App({
|
|
|
13846
14140
|
const clearPendingPlan = useCallback4(() => {
|
|
13847
14141
|
setPendingPlan(null);
|
|
13848
14142
|
}, []);
|
|
14143
|
+
const stopLoop = useCallback4(() => {
|
|
14144
|
+
if (loopTimerRef.current) {
|
|
14145
|
+
clearTimeout(loopTimerRef.current);
|
|
14146
|
+
loopTimerRef.current = null;
|
|
14147
|
+
}
|
|
14148
|
+
setActiveLoop((cur) => {
|
|
14149
|
+
if (!cur) return cur;
|
|
14150
|
+
setHistorical((prev) => [
|
|
14151
|
+
...prev,
|
|
14152
|
+
{
|
|
14153
|
+
id: `loop-stop-${Date.now()}`,
|
|
14154
|
+
role: "info",
|
|
14155
|
+
text: `\u25B8 loop stopped (after ${cur.iter} iter${cur.iter === 1 ? "" : "s"}).`
|
|
14156
|
+
}
|
|
14157
|
+
]);
|
|
14158
|
+
return null;
|
|
14159
|
+
});
|
|
14160
|
+
}, []);
|
|
14161
|
+
const startLoop = useCallback4((intervalMs, prompt) => {
|
|
14162
|
+
if (loopTimerRef.current) {
|
|
14163
|
+
clearTimeout(loopTimerRef.current);
|
|
14164
|
+
loopTimerRef.current = null;
|
|
14165
|
+
}
|
|
14166
|
+
setActiveLoop({
|
|
14167
|
+
prompt,
|
|
14168
|
+
intervalMs,
|
|
14169
|
+
nextFireAt: Date.now() + intervalMs,
|
|
14170
|
+
iter: 0
|
|
14171
|
+
});
|
|
14172
|
+
}, []);
|
|
14173
|
+
const startWalkthrough = useCallback4(() => {
|
|
14174
|
+
if (!codeMode) {
|
|
14175
|
+
return "/walk is only available inside `reasonix code`.";
|
|
14176
|
+
}
|
|
14177
|
+
if (pendingEdits.current.length === 0) {
|
|
14178
|
+
return "nothing pending \u2014 nothing to walk through.";
|
|
14179
|
+
}
|
|
14180
|
+
setWalkthroughActive(true);
|
|
14181
|
+
return `\u25B8 walking ${pendingEdits.current.length} edit block(s) \u2014 y apply \xB7 n reject \xB7 a apply rest \xB7 A flip to AUTO \xB7 Esc cancels (keeps remaining queued).`;
|
|
14182
|
+
}, [codeMode]);
|
|
14183
|
+
const handleWalkChoice = useCallback4(
|
|
14184
|
+
(choice) => {
|
|
14185
|
+
if (choice === "apply") {
|
|
14186
|
+
const out = codeApply([1]);
|
|
14187
|
+
setHistorical((prev) => [...prev, { id: `walk-${Date.now()}`, role: "info", text: out }]);
|
|
14188
|
+
} else if (choice === "reject") {
|
|
14189
|
+
const out = codeDiscard([1]);
|
|
14190
|
+
setHistorical((prev) => [...prev, { id: `walk-${Date.now()}`, role: "info", text: out }]);
|
|
14191
|
+
} else if (choice === "apply-rest-of-turn") {
|
|
14192
|
+
const out = codeApply();
|
|
14193
|
+
setHistorical((prev) => [...prev, { id: `walk-${Date.now()}`, role: "info", text: out }]);
|
|
14194
|
+
setWalkthroughActive(false);
|
|
14195
|
+
return;
|
|
14196
|
+
} else if (choice === "flip-to-auto") {
|
|
14197
|
+
setEditMode("auto");
|
|
14198
|
+
saveEditMode("auto");
|
|
14199
|
+
const out = codeApply([1]);
|
|
14200
|
+
setHistorical((prev) => [
|
|
14201
|
+
...prev,
|
|
14202
|
+
{ id: `walk-${Date.now()}`, role: "info", text: out },
|
|
14203
|
+
{
|
|
14204
|
+
id: `walk-flip-${Date.now()}`,
|
|
14205
|
+
role: "info",
|
|
14206
|
+
text: "\u25B8 flipped to AUTO mode \u2014 future edits will apply immediately. Walk exited."
|
|
14207
|
+
}
|
|
14208
|
+
]);
|
|
14209
|
+
setWalkthroughActive(false);
|
|
14210
|
+
return;
|
|
14211
|
+
}
|
|
14212
|
+
if (pendingEdits.current.length === 0) setWalkthroughActive(false);
|
|
14213
|
+
},
|
|
14214
|
+
[codeApply, codeDiscard]
|
|
14215
|
+
);
|
|
14216
|
+
const getLoopStatus = useCallback4(() => {
|
|
14217
|
+
const cur = activeLoopRef.current;
|
|
14218
|
+
if (!cur) return null;
|
|
14219
|
+
return {
|
|
14220
|
+
prompt: cur.prompt,
|
|
14221
|
+
intervalMs: cur.intervalMs,
|
|
14222
|
+
iter: cur.iter,
|
|
14223
|
+
nextFireMs: Math.max(0, cur.nextFireAt - Date.now())
|
|
14224
|
+
};
|
|
14225
|
+
}, []);
|
|
13849
14226
|
const handleSubmit = useCallback4(
|
|
13850
14227
|
async (raw) => {
|
|
13851
14228
|
let text = raw.trim();
|
|
13852
|
-
if (!text
|
|
14229
|
+
if (!text) return;
|
|
14230
|
+
if (activeLoopRef.current && !loopFiringRef.current) {
|
|
14231
|
+
stopLoop();
|
|
14232
|
+
}
|
|
14233
|
+
loopFiringRef.current = false;
|
|
14234
|
+
if (busy) return;
|
|
13853
14235
|
if (atMatches && atMatches.length > 0 && atPicker) {
|
|
13854
14236
|
const sel = atMatches[atSelected] ?? atMatches[0];
|
|
13855
14237
|
if (sel) {
|
|
@@ -13882,18 +14264,20 @@ function App({
|
|
|
13882
14264
|
return;
|
|
13883
14265
|
}
|
|
13884
14266
|
const hashParse = detectHashMemory(text);
|
|
13885
|
-
if (hashParse?.kind === "memory") {
|
|
14267
|
+
if (hashParse?.kind === "memory" || hashParse?.kind === "memory-global") {
|
|
14268
|
+
const isGlobal = hashParse.kind === "memory-global";
|
|
13886
14269
|
const memRoot = codeMode?.rootDir ?? process.cwd();
|
|
13887
14270
|
promptHistory.current.push(text);
|
|
13888
14271
|
try {
|
|
13889
|
-
const result = appendProjectMemory(memRoot, hashParse.note);
|
|
14272
|
+
const result = isGlobal ? appendGlobalMemory(hashParse.note) : appendProjectMemory(memRoot, hashParse.note);
|
|
13890
14273
|
const verb = result.created ? "created" : "appended to";
|
|
14274
|
+
const scopeTag = isGlobal ? "global" : "project";
|
|
13891
14275
|
setHistorical((prev) => [
|
|
13892
14276
|
...prev,
|
|
13893
14277
|
{
|
|
13894
14278
|
id: `hash-${Date.now()}`,
|
|
13895
14279
|
role: "info",
|
|
13896
|
-
text: `\u25B8 noted \u2014 ${verb} ${result.path}`
|
|
14280
|
+
text: `\u25B8 noted (${scopeTag}) \u2014 ${verb} ${result.path}`
|
|
13897
14281
|
}
|
|
13898
14282
|
]);
|
|
13899
14283
|
} catch (err) {
|
|
@@ -13936,7 +14320,7 @@ function App({
|
|
|
13936
14320
|
...prev,
|
|
13937
14321
|
{ id: `bang-o-${Date.now()}`, role: "info", text: formatted }
|
|
13938
14322
|
]);
|
|
13939
|
-
|
|
14323
|
+
loop2.appendAndPersist({
|
|
13940
14324
|
role: "user",
|
|
13941
14325
|
content: formatBangUserMessage(bangCmd, formatted)
|
|
13942
14326
|
});
|
|
@@ -13968,7 +14352,7 @@ function App({
|
|
|
13968
14352
|
}
|
|
13969
14353
|
const slash = parseSlash(text);
|
|
13970
14354
|
if (slash) {
|
|
13971
|
-
const result = handleSlash(slash.cmd, slash.args,
|
|
14355
|
+
const result = handleSlash(slash.cmd, slash.args, loop2, {
|
|
13972
14356
|
mcpSpecs,
|
|
13973
14357
|
mcpServers,
|
|
13974
14358
|
codeUndo: codeMode ? codeUndo : void 0,
|
|
@@ -13986,13 +14370,17 @@ function App({
|
|
|
13986
14370
|
editMode: codeMode ? editMode : void 0,
|
|
13987
14371
|
setEditMode: codeMode ? setEditMode : void 0,
|
|
13988
14372
|
armPro: () => {
|
|
13989
|
-
|
|
14373
|
+
loop2.armProForNextTurn();
|
|
13990
14374
|
setProArmed(true);
|
|
13991
14375
|
},
|
|
13992
14376
|
disarmPro: () => {
|
|
13993
|
-
|
|
14377
|
+
loop2.disarmPro();
|
|
13994
14378
|
setProArmed(false);
|
|
13995
14379
|
},
|
|
14380
|
+
startLoop,
|
|
14381
|
+
stopLoop,
|
|
14382
|
+
getLoopStatus,
|
|
14383
|
+
startWalkthrough: codeMode ? startWalkthrough : void 0,
|
|
13996
14384
|
jobs: codeMode?.jobs,
|
|
13997
14385
|
postInfo: (text2) => setHistorical((prev) => [
|
|
13998
14386
|
...prev,
|
|
@@ -14009,6 +14397,7 @@ function App({
|
|
|
14009
14397
|
refreshModels
|
|
14010
14398
|
});
|
|
14011
14399
|
if (result.exit) {
|
|
14400
|
+
if (activeLoopRef.current) stopLoop();
|
|
14012
14401
|
transcriptRef.current?.end();
|
|
14013
14402
|
exit2();
|
|
14014
14403
|
return;
|
|
@@ -14027,6 +14416,7 @@ function App({
|
|
|
14027
14416
|
clearPendingEdits(session ?? null);
|
|
14028
14417
|
syncPendingCount();
|
|
14029
14418
|
}
|
|
14419
|
+
if (activeLoopRef.current) stopLoop();
|
|
14030
14420
|
return;
|
|
14031
14421
|
}
|
|
14032
14422
|
if (result.clear) {
|
|
@@ -14037,6 +14427,7 @@ function App({
|
|
|
14037
14427
|
clearPendingEdits(session ?? null);
|
|
14038
14428
|
syncPendingCount();
|
|
14039
14429
|
}
|
|
14430
|
+
if (activeLoopRef.current) stopLoop();
|
|
14040
14431
|
return;
|
|
14041
14432
|
}
|
|
14042
14433
|
if (result.info) {
|
|
@@ -14165,8 +14556,47 @@ function App({
|
|
|
14165
14556
|
}
|
|
14166
14557
|
}
|
|
14167
14558
|
}
|
|
14559
|
+
if (/(?:^|\s)@https?:\/\//.test(text)) {
|
|
14560
|
+
try {
|
|
14561
|
+
const urlExpanded = await expandAtUrls(modelInput, {
|
|
14562
|
+
fetcher: webFetch,
|
|
14563
|
+
cache: atUrlCache.current
|
|
14564
|
+
});
|
|
14565
|
+
if (urlExpanded.expansions.length > 0) {
|
|
14566
|
+
modelInput = urlExpanded.text;
|
|
14567
|
+
const inlined = urlExpanded.expansions.filter((ex) => ex.ok).map((ex) => {
|
|
14568
|
+
const tag = ex.title ? `${ex.title} (${ex.url})` : ex.url;
|
|
14569
|
+
const trunc = ex.truncated ? " \xB7 truncated" : "";
|
|
14570
|
+
return `${tag} \xB7 ${(ex.chars ?? 0).toLocaleString()} chars${trunc}`;
|
|
14571
|
+
});
|
|
14572
|
+
const skipped = urlExpanded.expansions.filter((ex) => !ex.ok).map((ex) => `${ex.url} (${ex.skip ?? "fetch-error"})`);
|
|
14573
|
+
const parts = [];
|
|
14574
|
+
if (inlined.length > 0) parts.push(`inlined ${inlined.join("; ")}`);
|
|
14575
|
+
if (skipped.length > 0) parts.push(`skipped ${skipped.join("; ")}`);
|
|
14576
|
+
if (parts.length > 0) {
|
|
14577
|
+
setHistorical((prev) => [
|
|
14578
|
+
...prev,
|
|
14579
|
+
{
|
|
14580
|
+
id: `aturl-${Date.now()}`,
|
|
14581
|
+
role: "info",
|
|
14582
|
+
text: `\u25B8 @url: ${parts.join("; ")}`
|
|
14583
|
+
}
|
|
14584
|
+
]);
|
|
14585
|
+
}
|
|
14586
|
+
}
|
|
14587
|
+
} catch (err) {
|
|
14588
|
+
setHistorical((prev) => [
|
|
14589
|
+
...prev,
|
|
14590
|
+
{
|
|
14591
|
+
id: `aturl-e-${Date.now()}`,
|
|
14592
|
+
role: "warning",
|
|
14593
|
+
text: `@url expansion failed: ${err.message}`
|
|
14594
|
+
}
|
|
14595
|
+
]);
|
|
14596
|
+
}
|
|
14597
|
+
}
|
|
14168
14598
|
try {
|
|
14169
|
-
for await (const ev of
|
|
14599
|
+
for await (const ev of loop2.step(modelInput)) {
|
|
14170
14600
|
writeTranscript(ev);
|
|
14171
14601
|
if (ev.role !== "status") {
|
|
14172
14602
|
setStatusLine((cur) => cur ? null : cur);
|
|
@@ -14206,7 +14636,7 @@ function App({
|
|
|
14206
14636
|
flush();
|
|
14207
14637
|
const repairNote = ev.repair ? describeRepair(ev.repair) : "";
|
|
14208
14638
|
setStreaming(null);
|
|
14209
|
-
setSummary(
|
|
14639
|
+
setSummary(loop2.stats.summary());
|
|
14210
14640
|
if (ev.stats?.usage) {
|
|
14211
14641
|
appendUsage({
|
|
14212
14642
|
session: session ?? null,
|
|
@@ -14240,7 +14670,7 @@ function App({
|
|
|
14240
14670
|
if (codeMode && finalText && !ev.forcedSummary) {
|
|
14241
14671
|
const blocks = parseEditBlocks(finalText);
|
|
14242
14672
|
if (blocks.length > 0) {
|
|
14243
|
-
if (editModeRef.current === "auto") {
|
|
14673
|
+
if (editModeRef.current === "auto" || editModeRef.current === "yolo") {
|
|
14244
14674
|
const snaps = snapshotBeforeEdits(blocks, codeMode.rootDir);
|
|
14245
14675
|
const results = applyEditBlocks(blocks, codeMode.rootDir);
|
|
14246
14676
|
const good = results.some(
|
|
@@ -14452,7 +14882,7 @@ function App({
|
|
|
14452
14882
|
event: "Stop",
|
|
14453
14883
|
cwd: hookCwd,
|
|
14454
14884
|
lastAssistantText: streamRef.text,
|
|
14455
|
-
turn:
|
|
14885
|
+
turn: loop2.stats.summary().turns
|
|
14456
14886
|
}
|
|
14457
14887
|
});
|
|
14458
14888
|
for (const o of stopReport.outcomes) {
|
|
@@ -14473,7 +14903,7 @@ function App({
|
|
|
14473
14903
|
setOngoingTool(null);
|
|
14474
14904
|
setToolProgress(null);
|
|
14475
14905
|
setStatusLine(null);
|
|
14476
|
-
setSummary(
|
|
14906
|
+
setSummary(loop2.stats.summary());
|
|
14477
14907
|
setBusy(false);
|
|
14478
14908
|
setTurnOnPro(false);
|
|
14479
14909
|
refreshBalance();
|
|
@@ -14491,7 +14921,7 @@ function App({
|
|
|
14491
14921
|
exit2,
|
|
14492
14922
|
hookCwd,
|
|
14493
14923
|
hookList,
|
|
14494
|
-
|
|
14924
|
+
loop2,
|
|
14495
14925
|
latestVersion,
|
|
14496
14926
|
mcpSpecs,
|
|
14497
14927
|
mcpServers,
|
|
@@ -14520,9 +14950,51 @@ function App({
|
|
|
14520
14950
|
refreshModels,
|
|
14521
14951
|
proArmed,
|
|
14522
14952
|
persistPlanState,
|
|
14523
|
-
stdout2
|
|
14953
|
+
stdout2,
|
|
14954
|
+
stopLoop,
|
|
14955
|
+
startLoop,
|
|
14956
|
+
getLoopStatus,
|
|
14957
|
+
startWalkthrough
|
|
14524
14958
|
]
|
|
14525
14959
|
);
|
|
14960
|
+
useEffect6(() => {
|
|
14961
|
+
handleSubmitRef.current = handleSubmit;
|
|
14962
|
+
}, [handleSubmit]);
|
|
14963
|
+
useEffect6(() => {
|
|
14964
|
+
if (!activeLoop) return;
|
|
14965
|
+
const delay = Math.max(0, activeLoop.nextFireAt - Date.now());
|
|
14966
|
+
const timer = setTimeout(async () => {
|
|
14967
|
+
loopTimerRef.current = null;
|
|
14968
|
+
if (busyRef.current) {
|
|
14969
|
+
setActiveLoop((cur2) => cur2 ? { ...cur2, nextFireAt: Date.now() + 1e3 } : cur2);
|
|
14970
|
+
return;
|
|
14971
|
+
}
|
|
14972
|
+
const cur = activeLoopRef.current;
|
|
14973
|
+
if (!cur) return;
|
|
14974
|
+
const nextIter = cur.iter + 1;
|
|
14975
|
+
setActiveLoop(
|
|
14976
|
+
(c) => c ? { ...c, iter: nextIter, nextFireAt: Date.now() + cur.intervalMs } : c
|
|
14977
|
+
);
|
|
14978
|
+
setHistorical((prev) => [
|
|
14979
|
+
...prev,
|
|
14980
|
+
{
|
|
14981
|
+
id: `loop-fire-${Date.now()}`,
|
|
14982
|
+
role: "info",
|
|
14983
|
+
text: `\u25B8 /loop iter ${nextIter} \u2192 ${cur.prompt}`
|
|
14984
|
+
}
|
|
14985
|
+
]);
|
|
14986
|
+
loopFiringRef.current = true;
|
|
14987
|
+
try {
|
|
14988
|
+
await handleSubmitRef.current?.(cur.prompt);
|
|
14989
|
+
} catch {
|
|
14990
|
+
stopLoop();
|
|
14991
|
+
} finally {
|
|
14992
|
+
loopFiringRef.current = false;
|
|
14993
|
+
}
|
|
14994
|
+
}, delay);
|
|
14995
|
+
loopTimerRef.current = timer;
|
|
14996
|
+
return () => clearTimeout(timer);
|
|
14997
|
+
}, [activeLoop, stopLoop]);
|
|
14526
14998
|
const handleShellConfirm = useCallback4(
|
|
14527
14999
|
async (choice) => {
|
|
14528
15000
|
const pending = pendingShell;
|
|
@@ -14612,13 +15084,13 @@ ${body}`;
|
|
|
14612
15084
|
}
|
|
14613
15085
|
}
|
|
14614
15086
|
if (busy) {
|
|
14615
|
-
|
|
15087
|
+
loop2.abort();
|
|
14616
15088
|
setQueuedSubmit(synthetic);
|
|
14617
15089
|
} else {
|
|
14618
15090
|
await handleSubmit(synthetic);
|
|
14619
15091
|
}
|
|
14620
15092
|
},
|
|
14621
|
-
[pendingShell, codeMode, handleSubmit, busy,
|
|
15093
|
+
[pendingShell, codeMode, handleSubmit, busy, loop2]
|
|
14622
15094
|
);
|
|
14623
15095
|
useEffect6(() => {
|
|
14624
15096
|
if (!busy && queuedSubmit !== null) {
|
|
@@ -14656,13 +15128,13 @@ ${body}`;
|
|
|
14656
15128
|
{ id: `plan-${choice}-${Date.now()}`, role: "info", text: marker }
|
|
14657
15129
|
]);
|
|
14658
15130
|
if (busy) {
|
|
14659
|
-
|
|
15131
|
+
loop2.abort();
|
|
14660
15132
|
setQueuedSubmit(synthetic);
|
|
14661
15133
|
} else {
|
|
14662
15134
|
await handleSubmit(synthetic);
|
|
14663
15135
|
}
|
|
14664
15136
|
},
|
|
14665
|
-
[pendingPlan, togglePlanMode, busy,
|
|
15137
|
+
[pendingPlan, togglePlanMode, busy, loop2, handleSubmit, persistPlanState]
|
|
14666
15138
|
);
|
|
14667
15139
|
const handlePlanConfirmRef = useRef6(handlePlanConfirm);
|
|
14668
15140
|
useEffect6(() => {
|
|
@@ -14713,13 +15185,13 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
|
|
|
14713
15185
|
{ id: `plan-${staged.mode}-${Date.now()}`, role: "info", text: marker }
|
|
14714
15186
|
]);
|
|
14715
15187
|
if (busy) {
|
|
14716
|
-
|
|
15188
|
+
loop2.abort();
|
|
14717
15189
|
setQueuedSubmit(synthetic);
|
|
14718
15190
|
} else {
|
|
14719
15191
|
await handleSubmit(synthetic);
|
|
14720
15192
|
}
|
|
14721
15193
|
},
|
|
14722
|
-
[stagedInput, togglePlanMode, busy,
|
|
15194
|
+
[stagedInput, togglePlanMode, busy, loop2, handleSubmit]
|
|
14723
15195
|
);
|
|
14724
15196
|
const handleStagedInputCancel = useCallback4(() => {
|
|
14725
15197
|
if (stagedInput?.plan) setPendingPlan(stagedInput.plan);
|
|
@@ -14748,13 +15220,13 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
|
|
|
14748
15220
|
{ id: `cp-${choice}-${Date.now()}`, role: "info", text: marker }
|
|
14749
15221
|
]);
|
|
14750
15222
|
if (busy) {
|
|
14751
|
-
|
|
15223
|
+
loop2.abort();
|
|
14752
15224
|
setQueuedSubmit(synthetic);
|
|
14753
15225
|
} else {
|
|
14754
15226
|
await handleSubmit(synthetic);
|
|
14755
15227
|
}
|
|
14756
15228
|
},
|
|
14757
|
-
[pendingCheckpoint, busy,
|
|
15229
|
+
[pendingCheckpoint, busy, loop2, handleSubmit]
|
|
14758
15230
|
);
|
|
14759
15231
|
const handleCheckpointConfirmRef = useRef6(handleCheckpointConfirm);
|
|
14760
15232
|
useEffect6(() => {
|
|
@@ -14782,13 +15254,13 @@ If the feedback only tweaks how you execute (extra constraints, style preference
|
|
|
14782
15254
|
{ id: `cp-revise-${Date.now()}`, role: "info", text: marker }
|
|
14783
15255
|
]);
|
|
14784
15256
|
if (busy) {
|
|
14785
|
-
|
|
15257
|
+
loop2.abort();
|
|
14786
15258
|
setQueuedSubmit(synthetic);
|
|
14787
15259
|
} else {
|
|
14788
15260
|
await handleSubmit(synthetic);
|
|
14789
15261
|
}
|
|
14790
15262
|
},
|
|
14791
|
-
[stagedCheckpointRevise, busy,
|
|
15263
|
+
[stagedCheckpointRevise, busy, loop2, handleSubmit]
|
|
14792
15264
|
);
|
|
14793
15265
|
const handleCheckpointReviseCancel = useCallback4(() => {
|
|
14794
15266
|
const snap = stagedCheckpointRevise;
|
|
@@ -14811,7 +15283,7 @@ If the feedback only tweaks how you execute (extra constraints, style preference
|
|
|
14811
15283
|
{ id: `choice-cancel-${Date.now()}`, role: "info", text: "\u25B8 choice cancelled" }
|
|
14812
15284
|
]);
|
|
14813
15285
|
if (busy) {
|
|
14814
|
-
|
|
15286
|
+
loop2.abort();
|
|
14815
15287
|
setQueuedSubmit(synthetic2);
|
|
14816
15288
|
} else {
|
|
14817
15289
|
await handleSubmit(synthetic2);
|
|
@@ -14826,13 +15298,13 @@ If the feedback only tweaks how you execute (extra constraints, style preference
|
|
|
14826
15298
|
{ id: `choice-pick-${Date.now()}`, role: "info", text: `\u25B8 chose ${label}` }
|
|
14827
15299
|
]);
|
|
14828
15300
|
if (busy) {
|
|
14829
|
-
|
|
15301
|
+
loop2.abort();
|
|
14830
15302
|
setQueuedSubmit(synthetic);
|
|
14831
15303
|
} else {
|
|
14832
15304
|
await handleSubmit(synthetic);
|
|
14833
15305
|
}
|
|
14834
15306
|
},
|
|
14835
|
-
[pendingChoice, busy,
|
|
15307
|
+
[pendingChoice, busy, loop2, handleSubmit]
|
|
14836
15308
|
);
|
|
14837
15309
|
const handleChoiceConfirmRef = useRef6(handleChoiceConfirm);
|
|
14838
15310
|
useEffect6(() => {
|
|
@@ -14857,13 +15329,13 @@ Read it carefully and proceed \u2014 don't snap back to the options you listed u
|
|
|
14857
15329
|
{ id: `choice-custom-${Date.now()}`, role: "info", text: marker }
|
|
14858
15330
|
]);
|
|
14859
15331
|
if (busy) {
|
|
14860
|
-
|
|
15332
|
+
loop2.abort();
|
|
14861
15333
|
setQueuedSubmit(synthetic);
|
|
14862
15334
|
} else {
|
|
14863
15335
|
await handleSubmit(synthetic);
|
|
14864
15336
|
}
|
|
14865
15337
|
},
|
|
14866
|
-
[busy,
|
|
15338
|
+
[busy, loop2, handleSubmit]
|
|
14867
15339
|
);
|
|
14868
15340
|
const handleChoiceCustomCancel = useCallback4(() => {
|
|
14869
15341
|
const snap = stagedChoiceCustom;
|
|
@@ -14882,7 +15354,7 @@ Read it carefully and proceed \u2014 don't snap back to the options you listed u
|
|
|
14882
15354
|
{ id: `revise-reject-${Date.now()}`, role: "info", text: "\u25B8 revision rejected" }
|
|
14883
15355
|
]);
|
|
14884
15356
|
if (busy) {
|
|
14885
|
-
|
|
15357
|
+
loop2.abort();
|
|
14886
15358
|
setQueuedSubmit(synthetic2);
|
|
14887
15359
|
} else {
|
|
14888
15360
|
await handleSubmit(synthetic2);
|
|
@@ -14917,13 +15389,13 @@ ${snap.remainingSteps.map((s, i) => ` ${i + 1}. ${s.id} \xB7 ${s.title} \u2014
|
|
|
14917
15389
|
|
|
14918
15390
|
Continue executing from the next pending step. Call mark_step_complete after each one as before.`;
|
|
14919
15391
|
if (busy) {
|
|
14920
|
-
|
|
15392
|
+
loop2.abort();
|
|
14921
15393
|
setQueuedSubmit(synthetic);
|
|
14922
15394
|
} else {
|
|
14923
15395
|
await handleSubmit(synthetic);
|
|
14924
15396
|
}
|
|
14925
15397
|
},
|
|
14926
|
-
[pendingRevision, busy,
|
|
15398
|
+
[pendingRevision, busy, loop2, handleSubmit, persistPlanState]
|
|
14927
15399
|
);
|
|
14928
15400
|
const handleReviseConfirmRef = useRef6(handleReviseConfirm);
|
|
14929
15401
|
useEffect6(() => {
|
|
@@ -14936,17 +15408,17 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
14936
15408
|
return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(
|
|
14937
15409
|
TickerProvider,
|
|
14938
15410
|
{
|
|
14939
|
-
disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingEditReview || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
|
|
15411
|
+
disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingEditReview || walkthroughActive || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
|
|
14940
15412
|
},
|
|
14941
15413
|
/* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column" }, /* @__PURE__ */ React23.createElement(
|
|
14942
15414
|
StatsPanel,
|
|
14943
15415
|
{
|
|
14944
15416
|
summary,
|
|
14945
|
-
model:
|
|
15417
|
+
model: loop2.model,
|
|
14946
15418
|
prefixHash,
|
|
14947
|
-
harvestOn:
|
|
14948
|
-
branchBudget:
|
|
14949
|
-
reasoningEffort:
|
|
15419
|
+
harvestOn: loop2.harvestEnabled,
|
|
15420
|
+
branchBudget: loop2.branchOptions.budget,
|
|
15421
|
+
reasoningEffort: loop2.reasoningEffort,
|
|
14950
15422
|
planMode,
|
|
14951
15423
|
editMode: codeMode ? editMode : void 0,
|
|
14952
15424
|
balance,
|
|
@@ -15035,6 +15507,13 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15035
15507
|
}
|
|
15036
15508
|
}
|
|
15037
15509
|
}
|
|
15510
|
+
) : walkthroughActive && pendingEdits.current.length > 0 ? /* @__PURE__ */ React23.createElement(
|
|
15511
|
+
EditConfirm,
|
|
15512
|
+
{
|
|
15513
|
+
key: `walk-${pendingTick}`,
|
|
15514
|
+
block: pendingEdits.current[0],
|
|
15515
|
+
onChoose: handleWalkChoice
|
|
15516
|
+
}
|
|
15038
15517
|
) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, codeMode ? /* @__PURE__ */ React23.createElement(
|
|
15039
15518
|
ModeStatusBar,
|
|
15040
15519
|
{
|
|
@@ -15045,7 +15524,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15045
15524
|
undoArmed: !!undoBanner || hasUndoable(),
|
|
15046
15525
|
jobs: codeMode.jobs
|
|
15047
15526
|
}
|
|
15048
|
-
) : null, /* @__PURE__ */ React23.createElement(
|
|
15527
|
+
) : null, activeLoop ? /* @__PURE__ */ React23.createElement(LoopStatusRow, { loop: activeLoop }) : null, /* @__PURE__ */ React23.createElement(
|
|
15049
15528
|
PromptInput,
|
|
15050
15529
|
{
|
|
15051
15530
|
value: input,
|
|
@@ -15076,7 +15555,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15076
15555
|
}
|
|
15077
15556
|
|
|
15078
15557
|
// src/cli/ui/SessionPicker.tsx
|
|
15079
|
-
import { Box as Box22, Text as
|
|
15558
|
+
import { Box as Box22, Text as Text20 } from "ink";
|
|
15080
15559
|
import React24 from "react";
|
|
15081
15560
|
function SessionPicker({
|
|
15082
15561
|
sessionName,
|
|
@@ -15084,7 +15563,7 @@ function SessionPicker({
|
|
|
15084
15563
|
lastActive,
|
|
15085
15564
|
onChoose
|
|
15086
15565
|
}) {
|
|
15087
|
-
return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React24.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(
|
|
15566
|
+
return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React24.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(Text20, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React24.createElement(Text20, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React24.createElement(
|
|
15088
15567
|
SingleSelect,
|
|
15089
15568
|
{
|
|
15090
15569
|
initialValue: "new",
|
|
@@ -15107,7 +15586,7 @@ function SessionPicker({
|
|
|
15107
15586
|
],
|
|
15108
15587
|
onSubmit: (v) => onChoose(v)
|
|
15109
15588
|
}
|
|
15110
|
-
), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(
|
|
15589
|
+
), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text20, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
|
|
15111
15590
|
}
|
|
15112
15591
|
function relativeTime2(date) {
|
|
15113
15592
|
const ms = Date.now() - date.getTime();
|
|
@@ -15123,7 +15602,7 @@ function relativeTime2(date) {
|
|
|
15123
15602
|
}
|
|
15124
15603
|
|
|
15125
15604
|
// src/cli/ui/Setup.tsx
|
|
15126
|
-
import { Box as Box23, Text as
|
|
15605
|
+
import { Box as Box23, Text as Text21, useApp as useApp2 } from "ink";
|
|
15127
15606
|
import TextInput from "ink-text-input";
|
|
15128
15607
|
import React25, { useState as useState11 } from "react";
|
|
15129
15608
|
function Setup({ onReady }) {
|
|
@@ -15149,7 +15628,7 @@ function Setup({ onReady }) {
|
|
|
15149
15628
|
}
|
|
15150
15629
|
onReady(trimmed);
|
|
15151
15630
|
};
|
|
15152
|
-
return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React25.createElement(
|
|
15631
|
+
return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React25.createElement(
|
|
15153
15632
|
TextInput,
|
|
15154
15633
|
{
|
|
15155
15634
|
value,
|
|
@@ -15158,7 +15637,7 @@ function Setup({ onReady }) {
|
|
|
15158
15637
|
mask: "\u2022",
|
|
15159
15638
|
placeholder: "sk-..."
|
|
15160
15639
|
}
|
|
15161
|
-
)), error ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(
|
|
15640
|
+
)), error ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { color: "red" }, error)) : value ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "(Type /exit to abort.)")));
|
|
15162
15641
|
}
|
|
15163
15642
|
|
|
15164
15643
|
// src/cli/commands/chat.tsx
|
|
@@ -15332,7 +15811,7 @@ async function chatCommand(opts) {
|
|
|
15332
15811
|
// src/cli/commands/code.tsx
|
|
15333
15812
|
import { basename as basename2, resolve as resolve7 } from "path";
|
|
15334
15813
|
async function codeCommand(opts = {}) {
|
|
15335
|
-
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-
|
|
15814
|
+
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-LJ44NWSU.js");
|
|
15336
15815
|
const rootDir = resolve7(opts.dir ?? process.cwd());
|
|
15337
15816
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename2(rootDir))}`;
|
|
15338
15817
|
const tools = new ToolRegistry();
|
|
@@ -15346,6 +15825,10 @@ async function codeCommand(opts = {}) {
|
|
|
15346
15825
|
// via ShellConfirm mid-session takes effect on the next shell call
|
|
15347
15826
|
// instead of waiting for `/new` or a relaunch.
|
|
15348
15827
|
extraAllowed: () => loadProjectShellAllowed(rootDir),
|
|
15828
|
+
// `yolo` edit-mode disables shell confirmations entirely. Re-read
|
|
15829
|
+
// from config on each dispatch so /mode yolo (or Shift+Tab cycling
|
|
15830
|
+
// through to it) flips the gate live without forcing a relaunch.
|
|
15831
|
+
allowAll: () => loadEditMode() === "yolo",
|
|
15349
15832
|
jobs: jobs2
|
|
15350
15833
|
});
|
|
15351
15834
|
registerPlanTool(tools);
|
|
@@ -15378,32 +15861,32 @@ import { render as render2 } from "ink";
|
|
|
15378
15861
|
import React29 from "react";
|
|
15379
15862
|
|
|
15380
15863
|
// src/cli/ui/DiffApp.tsx
|
|
15381
|
-
import { Box as Box25, Static as Static2, Text as
|
|
15864
|
+
import { Box as Box25, Static as Static2, Text as Text23, useApp as useApp3, useInput } from "ink";
|
|
15382
15865
|
import React28, { useState as useState13 } from "react";
|
|
15383
15866
|
|
|
15384
15867
|
// src/cli/ui/RecordView.tsx
|
|
15385
|
-
import { Box as Box24, Text as
|
|
15868
|
+
import { Box as Box24, Text as Text22 } from "ink";
|
|
15386
15869
|
import React27 from "react";
|
|
15387
15870
|
function RecordView({ rec, compact: compact2 = false }) {
|
|
15388
15871
|
const toolArgsMax = compact2 ? 120 : 200;
|
|
15389
15872
|
const toolContentMax = compact2 ? 200 : 400;
|
|
15390
15873
|
if (rec.role === "user") {
|
|
15391
15874
|
const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
|
|
15392
|
-
return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(
|
|
15875
|
+
return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React27.createElement(Text22, null, content));
|
|
15393
15876
|
}
|
|
15394
15877
|
if (rec.role === "assistant_final") {
|
|
15395
|
-
return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(
|
|
15878
|
+
return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React27.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React27.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React27.createElement(Text22, null, rec.content) : /* @__PURE__ */ React27.createElement(Text22, { dimColor: true, italic: true }, "(tool-call response only)"));
|
|
15396
15879
|
}
|
|
15397
15880
|
if (rec.role === "tool") {
|
|
15398
|
-
return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(
|
|
15881
|
+
return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
|
|
15399
15882
|
}
|
|
15400
15883
|
if (rec.role === "error") {
|
|
15401
|
-
return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(
|
|
15884
|
+
return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React27.createElement(Text22, { color: "red" }, rec.error ?? rec.content));
|
|
15402
15885
|
}
|
|
15403
15886
|
if (rec.role === "done" || rec.role === "assistant_delta") {
|
|
15404
15887
|
return null;
|
|
15405
15888
|
}
|
|
15406
|
-
return /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(
|
|
15889
|
+
return /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, "[", rec.role, "] ", rec.content));
|
|
15407
15890
|
}
|
|
15408
15891
|
function CacheBadge({ usage }) {
|
|
15409
15892
|
const hit = usage.prompt_cache_hit_tokens ?? 0;
|
|
@@ -15412,7 +15895,7 @@ function CacheBadge({ usage }) {
|
|
|
15412
15895
|
if (total === 0) return null;
|
|
15413
15896
|
const pct2 = hit / total * 100;
|
|
15414
15897
|
const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
|
|
15415
|
-
return /* @__PURE__ */ React27.createElement(
|
|
15898
|
+
return /* @__PURE__ */ React27.createElement(Text22, null, /* @__PURE__ */ React27.createElement(Text22, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React27.createElement(Text22, { color }, pct2.toFixed(1), "%"));
|
|
15416
15899
|
}
|
|
15417
15900
|
function truncate2(s, max) {
|
|
15418
15901
|
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
|
|
@@ -15446,7 +15929,7 @@ function DiffApp({ report }) {
|
|
|
15446
15929
|
}
|
|
15447
15930
|
});
|
|
15448
15931
|
const pair = report.pairs[idx];
|
|
15449
|
-
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React28.createElement(DiffHeader, { report }), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(
|
|
15932
|
+
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React28.createElement(DiffHeader, { report }), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text23, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React28.createElement(Text23, null, pair ? /* @__PURE__ */ React28.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React28.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React28.createElement(Text23, null, pair.divergenceNote)) : null, /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "j"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "k"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "N"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "g"), "/", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React28.createElement(Text23, { bold: true }, "q"), " ", "quit")));
|
|
15450
15933
|
}
|
|
15451
15934
|
function DiffHeader({ report }) {
|
|
15452
15935
|
const a = report.a;
|
|
@@ -15464,7 +15947,7 @@ function DiffHeader({ report }) {
|
|
|
15464
15947
|
} else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
|
|
15465
15948
|
prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
|
|
15466
15949
|
}
|
|
15467
|
-
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React28.createElement(Box25, { justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(
|
|
15950
|
+
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React28.createElement(Box25, { justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React28.createElement(Text23, { color: "blue" }, a.label), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " vs B="), /* @__PURE__ */ React28.createElement(Text23, { color: "magenta" }, b.label)), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "cache "), /* @__PURE__ */ React28.createElement(Text23, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text23, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text23, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "cost "), /* @__PURE__ */ React28.createElement(Text23, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text23, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text23, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "model calls "), /* @__PURE__ */ React28.createElement(Text23, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, prefixLine)) : null);
|
|
15468
15951
|
}
|
|
15469
15952
|
function Pane({
|
|
15470
15953
|
label,
|
|
@@ -15480,21 +15963,21 @@ function Pane({
|
|
|
15480
15963
|
borderStyle: "single",
|
|
15481
15964
|
borderColor: headerColor
|
|
15482
15965
|
},
|
|
15483
|
-
/* @__PURE__ */ React28.createElement(
|
|
15484
|
-
records.length === 0 ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(
|
|
15966
|
+
/* @__PURE__ */ React28.createElement(Text23, { color: headerColor, bold: true }, label),
|
|
15967
|
+
records.length === 0 ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React28.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React28.createElement(RecordView, { key, rec, compact: true }))
|
|
15485
15968
|
);
|
|
15486
15969
|
}
|
|
15487
15970
|
function KindBadge({ kind }) {
|
|
15488
15971
|
if (kind === "match") {
|
|
15489
|
-
return /* @__PURE__ */ React28.createElement(
|
|
15972
|
+
return /* @__PURE__ */ React28.createElement(Text23, { color: "green" }, "\u2713 match");
|
|
15490
15973
|
}
|
|
15491
15974
|
if (kind === "diverge") {
|
|
15492
|
-
return /* @__PURE__ */ React28.createElement(
|
|
15975
|
+
return /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "\u2605 diverge");
|
|
15493
15976
|
}
|
|
15494
15977
|
if (kind === "only_in_a") {
|
|
15495
|
-
return /* @__PURE__ */ React28.createElement(
|
|
15978
|
+
return /* @__PURE__ */ React28.createElement(Text23, { color: "blue" }, "\u2190 only in A");
|
|
15496
15979
|
}
|
|
15497
|
-
return /* @__PURE__ */ React28.createElement(
|
|
15980
|
+
return /* @__PURE__ */ React28.createElement(Text23, { color: "magenta" }, "\u2192 only in B");
|
|
15498
15981
|
}
|
|
15499
15982
|
function paneRecords(pair, side) {
|
|
15500
15983
|
if (!pair) return [];
|
|
@@ -15669,7 +16152,7 @@ import { render as render3 } from "ink";
|
|
|
15669
16152
|
import React31 from "react";
|
|
15670
16153
|
|
|
15671
16154
|
// src/cli/ui/ReplayApp.tsx
|
|
15672
|
-
import { Box as Box26, Static as Static3, Text as
|
|
16155
|
+
import { Box as Box26, Static as Static3, Text as Text24, useApp as useApp4, useInput as useInput2 } from "ink";
|
|
15673
16156
|
import React30, { useMemo as useMemo4, useState as useState14 } from "react";
|
|
15674
16157
|
function ReplayApp({ meta, pages }) {
|
|
15675
16158
|
const { exit: exit2 } = useApp4();
|
|
@@ -15717,7 +16200,7 @@ function ReplayApp({ meta, pages }) {
|
|
|
15717
16200
|
model: cumStats.models[0] ?? meta?.model ?? "?",
|
|
15718
16201
|
prefixHash
|
|
15719
16202
|
}
|
|
15720
|
-
), /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React30.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React30.createElement(
|
|
16203
|
+
), /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React30.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React30.createElement(Text24, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React30.createElement(Text24, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React30.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React30.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React30.createElement(Text24, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React30.createElement(Box26, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React30.createElement(Text24, { dimColor: true }, /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "j"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "k"), "/", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React30.createElement(Text24, { bold: true }, "q"), " quit")));
|
|
15721
16204
|
}
|
|
15722
16205
|
|
|
15723
16206
|
// src/cli/commands/replay.ts
|
|
@@ -15896,7 +16379,7 @@ async function runCommand2(opts) {
|
|
|
15896
16379
|
system: opts.system,
|
|
15897
16380
|
toolSpecs: tools?.specs()
|
|
15898
16381
|
});
|
|
15899
|
-
const
|
|
16382
|
+
const loop2 = new CacheFirstLoop({
|
|
15900
16383
|
client,
|
|
15901
16384
|
prefix,
|
|
15902
16385
|
tools,
|
|
@@ -15921,7 +16404,7 @@ async function runCommand2(opts) {
|
|
|
15921
16404
|
});
|
|
15922
16405
|
}
|
|
15923
16406
|
try {
|
|
15924
|
-
for await (const ev of
|
|
16407
|
+
for await (const ev of loop2.step(opts.task)) {
|
|
15925
16408
|
if (ev.role === "assistant_delta" && ev.content) process.stdout.write(ev.content);
|
|
15926
16409
|
if (ev.role === "tool") process.stdout.write(`
|
|
15927
16410
|
[tool ${ev.toolName}] ${ev.content}
|
|
@@ -15940,7 +16423,7 @@ async function runCommand2(opts) {
|
|
|
15940
16423
|
} finally {
|
|
15941
16424
|
transcriptStream?.end();
|
|
15942
16425
|
}
|
|
15943
|
-
const s =
|
|
16426
|
+
const s = loop2.stats.summary();
|
|
15944
16427
|
process.stdout.write(
|
|
15945
16428
|
`
|
|
15946
16429
|
\u2014 turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%
|
|
@@ -16037,7 +16520,7 @@ import { render as render4 } from "ink";
|
|
|
16037
16520
|
import React33 from "react";
|
|
16038
16521
|
|
|
16039
16522
|
// src/cli/ui/Wizard.tsx
|
|
16040
|
-
import { Box as Box27, Text as
|
|
16523
|
+
import { Box as Box27, Text as Text25, useApp as useApp5, useInput as useInput3 } from "ink";
|
|
16041
16524
|
import TextInput2 from "ink-text-input";
|
|
16042
16525
|
import React32, { useState as useState15 } from "react";
|
|
16043
16526
|
|
|
@@ -16111,7 +16594,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
16111
16594
|
setStep("mcp");
|
|
16112
16595
|
}
|
|
16113
16596
|
}
|
|
16114
|
-
), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16597
|
+
), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
|
|
16115
16598
|
}
|
|
16116
16599
|
if (step === "mcp") {
|
|
16117
16600
|
return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(
|
|
@@ -16165,8 +16648,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
16165
16648
|
}
|
|
16166
16649
|
), specs.map((spec, i) => (
|
|
16167
16650
|
// biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
|
|
16168
|
-
/* @__PURE__ */ React32.createElement(Box27, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React32.createElement(
|
|
16169
|
-
)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16651
|
+
/* @__PURE__ */ React32.createElement(Box27, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "\xB7 ", spec))
|
|
16652
|
+
)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React32.createElement(
|
|
16170
16653
|
ReviewConfirm,
|
|
16171
16654
|
{
|
|
16172
16655
|
onConfirm: () => {
|
|
@@ -16192,7 +16675,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
16192
16675
|
}
|
|
16193
16676
|
));
|
|
16194
16677
|
}
|
|
16195
|
-
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16678
|
+
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React32.createElement(ExitOnEnter, { onExit: exit2 }));
|
|
16196
16679
|
}
|
|
16197
16680
|
function ApiKeyStep({
|
|
16198
16681
|
onSubmit,
|
|
@@ -16200,7 +16683,7 @@ function ApiKeyStep({
|
|
|
16200
16683
|
onError
|
|
16201
16684
|
}) {
|
|
16202
16685
|
const [value, setValue] = useState15("");
|
|
16203
|
-
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16686
|
+
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React32.createElement(
|
|
16204
16687
|
TextInput2,
|
|
16205
16688
|
{
|
|
16206
16689
|
value,
|
|
@@ -16217,7 +16700,7 @@ function ApiKeyStep({
|
|
|
16217
16700
|
mask: "\u2022",
|
|
16218
16701
|
placeholder: "sk-..."
|
|
16219
16702
|
}
|
|
16220
|
-
)), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16703
|
+
)), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : value ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "preview: ", redactKey(value))) : null);
|
|
16221
16704
|
}
|
|
16222
16705
|
function McpArgsStep({
|
|
16223
16706
|
entry,
|
|
@@ -16226,7 +16709,7 @@ function McpArgsStep({
|
|
|
16226
16709
|
onError
|
|
16227
16710
|
}) {
|
|
16228
16711
|
const [value, setValue] = useState15("");
|
|
16229
|
-
return /* @__PURE__ */ React32.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(
|
|
16712
|
+
return /* @__PURE__ */ React32.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(Text25, null, entry.summary), entry.note ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, null, "Required parameter: "), /* @__PURE__ */ React32.createElement(Text25, { bold: true }, entry.userArgs)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React32.createElement(
|
|
16230
16713
|
TextInput2,
|
|
16231
16714
|
{
|
|
16232
16715
|
value,
|
|
@@ -16242,7 +16725,7 @@ function McpArgsStep({
|
|
|
16242
16725
|
},
|
|
16243
16726
|
placeholder: placeholderFor(entry)
|
|
16244
16727
|
}
|
|
16245
|
-
)), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(
|
|
16728
|
+
)), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text25, { color: "red" }, error)) : null));
|
|
16246
16729
|
}
|
|
16247
16730
|
function ReviewConfirm({ onConfirm }) {
|
|
16248
16731
|
useInput3((_i, key) => {
|
|
@@ -16262,10 +16745,10 @@ function StepFrame({
|
|
|
16262
16745
|
total,
|
|
16263
16746
|
children
|
|
16264
16747
|
}) {
|
|
16265
|
-
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(
|
|
16748
|
+
return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text25, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React32.createElement(Text25, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1, flexDirection: "column" }, children));
|
|
16266
16749
|
}
|
|
16267
16750
|
function SummaryLine({ label, value }) {
|
|
16268
|
-
return /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(
|
|
16751
|
+
return /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text25, null, label.padEnd(12)), /* @__PURE__ */ React32.createElement(Text25, { bold: true }, value));
|
|
16269
16752
|
}
|
|
16270
16753
|
function presetItems() {
|
|
16271
16754
|
return ["fast", "smart", "max"].map((name) => ({
|