reasonix 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -4
- package/README.zh-CN.md +118 -3
- package/dist/cli/{chunk-GXABXQMU.js → chunk-JDVY4JDU.js} +11 -1
- package/dist/cli/chunk-JDVY4JDU.js.map +1 -0
- package/dist/cli/index.js +617 -201
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-FMYQ7IDW.js → prompt-YRY4HPMZ.js} +2 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +45 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-GXABXQMU.js.map +0 -1
- /package/dist/cli/{prompt-FMYQ7IDW.js.map → prompt-YRY4HPMZ.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-JDVY4JDU.js";
|
|
14
14
|
|
|
15
15
|
// src/cli/index.ts
|
|
16
16
|
import { Command } from "commander";
|
|
@@ -112,7 +112,7 @@ import { createParser } from "eventsource-parser";
|
|
|
112
112
|
|
|
113
113
|
// src/retry.ts
|
|
114
114
|
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
115
|
-
async function fetchWithRetry(fetchFn, url,
|
|
115
|
+
async function fetchWithRetry(fetchFn, url, init2, opts = {}) {
|
|
116
116
|
const maxAttempts = opts.maxAttempts ?? 4;
|
|
117
117
|
const initial = opts.initialBackoffMs ?? 500;
|
|
118
118
|
const cap = opts.maxBackoffMs ?? 1e4;
|
|
@@ -121,7 +121,7 @@ async function fetchWithRetry(fetchFn, url, init, opts = {}) {
|
|
|
121
121
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
122
122
|
if (opts.signal?.aborted) throw new Error("aborted");
|
|
123
123
|
try {
|
|
124
|
-
const resp = await fetchFn(url,
|
|
124
|
+
const resp = await fetchFn(url, init2);
|
|
125
125
|
if (resp.ok || !retryable.has(resp.status)) return resp;
|
|
126
126
|
if (attempt === maxAttempts - 1) return resp;
|
|
127
127
|
await resp.text().catch(() => void 0);
|
|
@@ -156,8 +156,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
156
156
|
}
|
|
157
157
|
function sleep(ms, signal) {
|
|
158
158
|
if (ms <= 0) return Promise.resolve();
|
|
159
|
-
return new Promise((
|
|
160
|
-
const timer = setTimeout(
|
|
159
|
+
return new Promise((resolve12, reject) => {
|
|
160
|
+
const timer = setTimeout(resolve12, ms);
|
|
161
161
|
if (signal) {
|
|
162
162
|
const onAbort = () => {
|
|
163
163
|
clearTimeout(timer);
|
|
@@ -642,7 +642,7 @@ function matchesTool(hook, toolName) {
|
|
|
642
642
|
}
|
|
643
643
|
}
|
|
644
644
|
function defaultSpawner(input) {
|
|
645
|
-
return new Promise((
|
|
645
|
+
return new Promise((resolve12) => {
|
|
646
646
|
const child = spawn(input.command, {
|
|
647
647
|
cwd: input.cwd,
|
|
648
648
|
shell: true,
|
|
@@ -669,7 +669,7 @@ function defaultSpawner(input) {
|
|
|
669
669
|
});
|
|
670
670
|
child.once("error", (err) => {
|
|
671
671
|
clearTimeout(timer);
|
|
672
|
-
|
|
672
|
+
resolve12({
|
|
673
673
|
exitCode: null,
|
|
674
674
|
stdout: stdout3,
|
|
675
675
|
stderr,
|
|
@@ -679,7 +679,7 @@ function defaultSpawner(input) {
|
|
|
679
679
|
});
|
|
680
680
|
child.once("close", (code) => {
|
|
681
681
|
clearTimeout(timer);
|
|
682
|
-
|
|
682
|
+
resolve12({
|
|
683
683
|
exitCode: code,
|
|
684
684
|
stdout: stdout3.trim(),
|
|
685
685
|
stderr: stderr.trim(),
|
|
@@ -720,8 +720,8 @@ async function runHooks(opts) {
|
|
|
720
720
|
for (const hook of matching) {
|
|
721
721
|
const start = Date.now();
|
|
722
722
|
const timeoutMs = hook.timeout ?? DEFAULT_TIMEOUTS_MS[event];
|
|
723
|
-
const
|
|
724
|
-
const raw = await spawner({ command: hook.command, cwd, stdin: stdin4, timeoutMs });
|
|
723
|
+
const cwd2 = hook.cwd ?? opts.payload.cwd;
|
|
724
|
+
const raw = await spawner({ command: hook.command, cwd: cwd2, stdin: stdin4, timeoutMs });
|
|
725
725
|
const decision = decideOutcome(event, raw);
|
|
726
726
|
outcomes.push({
|
|
727
727
|
hook,
|
|
@@ -1867,7 +1867,11 @@ var CacheFirstLoop = class {
|
|
|
1867
1867
|
* tool call is one array length check.
|
|
1868
1868
|
*/
|
|
1869
1869
|
hooks;
|
|
1870
|
-
/**
|
|
1870
|
+
/**
|
|
1871
|
+
* `cwd` reported to hook stdin. Mutable so `/cwd` can switch the
|
|
1872
|
+
* working directory mid-session — the App keeps it in sync with
|
|
1873
|
+
* the same currentRootDir that drives tool re-registration.
|
|
1874
|
+
*/
|
|
1871
1875
|
hookCwd;
|
|
1872
1876
|
/** Number of messages that were pre-loaded from the session file. */
|
|
1873
1877
|
resumedMessageCount;
|
|
@@ -2344,6 +2348,7 @@ var CacheFirstLoop = class {
|
|
|
2344
2348
|
};
|
|
2345
2349
|
this.autoCompactToolResultsOnTurnEnd();
|
|
2346
2350
|
yield { turn: this._turn, role: "done", content: stoppedMsg };
|
|
2351
|
+
this._turnAbort = new AbortController();
|
|
2347
2352
|
return;
|
|
2348
2353
|
}
|
|
2349
2354
|
if (iter > 0) {
|
|
@@ -2437,8 +2442,8 @@ var CacheFirstLoop = class {
|
|
|
2437
2442
|
}
|
|
2438
2443
|
);
|
|
2439
2444
|
for (let k = 0; k < budget; k++) {
|
|
2440
|
-
const sample = queue.shift() ?? await new Promise((
|
|
2441
|
-
waiter =
|
|
2445
|
+
const sample = queue.shift() ?? await new Promise((resolve12) => {
|
|
2446
|
+
waiter = resolve12;
|
|
2442
2447
|
});
|
|
2443
2448
|
yield {
|
|
2444
2449
|
turn: this._turn,
|
|
@@ -5168,7 +5173,7 @@ async function runCommand(cmd, opts) {
|
|
|
5168
5173
|
};
|
|
5169
5174
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
5170
5175
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
5171
|
-
return await new Promise((
|
|
5176
|
+
return await new Promise((resolve12, reject) => {
|
|
5172
5177
|
let child;
|
|
5173
5178
|
try {
|
|
5174
5179
|
child = spawn3(bin, args, effectiveSpawnOpts);
|
|
@@ -5176,7 +5181,9 @@ async function runCommand(cmd, opts) {
|
|
|
5176
5181
|
reject(err);
|
|
5177
5182
|
return;
|
|
5178
5183
|
}
|
|
5179
|
-
|
|
5184
|
+
const chunks = [];
|
|
5185
|
+
let totalBytes = 0;
|
|
5186
|
+
const byteCap = maxChars * 2 * 4;
|
|
5180
5187
|
let timedOut = false;
|
|
5181
5188
|
const killTimer = setTimeout(() => {
|
|
5182
5189
|
timedOut = true;
|
|
@@ -5185,8 +5192,16 @@ async function runCommand(cmd, opts) {
|
|
|
5185
5192
|
const onAbort = () => child.kill("SIGKILL");
|
|
5186
5193
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
5187
5194
|
const onData = (chunk) => {
|
|
5188
|
-
|
|
5189
|
-
if (
|
|
5195
|
+
const b = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
5196
|
+
if (totalBytes >= byteCap) return;
|
|
5197
|
+
const remaining = byteCap - totalBytes;
|
|
5198
|
+
if (b.length > remaining) {
|
|
5199
|
+
chunks.push(b.subarray(0, remaining));
|
|
5200
|
+
totalBytes = byteCap;
|
|
5201
|
+
} else {
|
|
5202
|
+
chunks.push(b);
|
|
5203
|
+
totalBytes += b.length;
|
|
5204
|
+
}
|
|
5190
5205
|
};
|
|
5191
5206
|
child.stdout?.on("data", onData);
|
|
5192
5207
|
child.stderr?.on("data", onData);
|
|
@@ -5198,13 +5213,29 @@ async function runCommand(cmd, opts) {
|
|
|
5198
5213
|
child.on("close", (code) => {
|
|
5199
5214
|
clearTimeout(killTimer);
|
|
5200
5215
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
5216
|
+
const merged = Buffer.concat(chunks);
|
|
5217
|
+
const buf = smartDecodeOutput(merged);
|
|
5201
5218
|
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
5202
5219
|
|
|
5203
5220
|
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
5204
|
-
|
|
5221
|
+
resolve12({ exitCode: code, output, timedOut });
|
|
5205
5222
|
});
|
|
5206
5223
|
});
|
|
5207
5224
|
}
|
|
5225
|
+
function smartDecodeOutput(buf) {
|
|
5226
|
+
if (buf.length === 0) return "";
|
|
5227
|
+
try {
|
|
5228
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
5229
|
+
} catch {
|
|
5230
|
+
}
|
|
5231
|
+
if (process.platform === "win32") {
|
|
5232
|
+
try {
|
|
5233
|
+
return new TextDecoder("gb18030").decode(buf);
|
|
5234
|
+
} catch {
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
5237
|
+
return buf.toString("utf8");
|
|
5238
|
+
}
|
|
5208
5239
|
function resolveExecutable(cmd, opts = {}) {
|
|
5209
5240
|
const platform = opts.platform ?? process.platform;
|
|
5210
5241
|
if (platform !== "win32") return cmd;
|
|
@@ -6391,7 +6422,7 @@ var McpClient = class {
|
|
|
6391
6422
|
const id = this.nextId++;
|
|
6392
6423
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
6393
6424
|
let abortHandler = null;
|
|
6394
|
-
const promise = new Promise((
|
|
6425
|
+
const promise = new Promise((resolve12, reject) => {
|
|
6395
6426
|
const timeout = setTimeout(() => {
|
|
6396
6427
|
this.pending.delete(id);
|
|
6397
6428
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -6400,7 +6431,7 @@ var McpClient = class {
|
|
|
6400
6431
|
);
|
|
6401
6432
|
}, this.requestTimeoutMs);
|
|
6402
6433
|
this.pending.set(id, {
|
|
6403
|
-
resolve:
|
|
6434
|
+
resolve: resolve12,
|
|
6404
6435
|
reject,
|
|
6405
6436
|
timeout
|
|
6406
6437
|
});
|
|
@@ -6523,12 +6554,12 @@ var StdioTransport = class {
|
|
|
6523
6554
|
}
|
|
6524
6555
|
async send(message) {
|
|
6525
6556
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
6526
|
-
return new Promise((
|
|
6557
|
+
return new Promise((resolve12, reject) => {
|
|
6527
6558
|
const line = `${JSON.stringify(message)}
|
|
6528
6559
|
`;
|
|
6529
6560
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
6530
6561
|
if (err) reject(err);
|
|
6531
|
-
else
|
|
6562
|
+
else resolve12();
|
|
6532
6563
|
});
|
|
6533
6564
|
});
|
|
6534
6565
|
}
|
|
@@ -6539,8 +6570,8 @@ var StdioTransport = class {
|
|
|
6539
6570
|
continue;
|
|
6540
6571
|
}
|
|
6541
6572
|
if (this.closed) return;
|
|
6542
|
-
const next = await new Promise((
|
|
6543
|
-
this.waiters.push(
|
|
6573
|
+
const next = await new Promise((resolve12) => {
|
|
6574
|
+
this.waiters.push(resolve12);
|
|
6544
6575
|
});
|
|
6545
6576
|
if (next === null) return;
|
|
6546
6577
|
yield next;
|
|
@@ -6606,8 +6637,8 @@ var SseTransport = class {
|
|
|
6606
6637
|
constructor(opts) {
|
|
6607
6638
|
this.url = opts.url;
|
|
6608
6639
|
this.headers = opts.headers ?? {};
|
|
6609
|
-
this.endpointReady = new Promise((
|
|
6610
|
-
this.resolveEndpoint =
|
|
6640
|
+
this.endpointReady = new Promise((resolve12, reject) => {
|
|
6641
|
+
this.resolveEndpoint = resolve12;
|
|
6611
6642
|
this.rejectEndpoint = reject;
|
|
6612
6643
|
});
|
|
6613
6644
|
this.endpointReady.catch(() => void 0);
|
|
@@ -6634,8 +6665,8 @@ var SseTransport = class {
|
|
|
6634
6665
|
continue;
|
|
6635
6666
|
}
|
|
6636
6667
|
if (this.closed) return;
|
|
6637
|
-
const next = await new Promise((
|
|
6638
|
-
this.waiters.push(
|
|
6668
|
+
const next = await new Promise((resolve12) => {
|
|
6669
|
+
this.waiters.push(resolve12);
|
|
6639
6670
|
});
|
|
6640
6671
|
if (next === null) return;
|
|
6641
6672
|
yield next;
|
|
@@ -7223,13 +7254,14 @@ function formatLogSize(path5 = defaultUsageLogPath()) {
|
|
|
7223
7254
|
}
|
|
7224
7255
|
|
|
7225
7256
|
// src/cli/commands/chat.tsx
|
|
7226
|
-
import { existsSync as
|
|
7257
|
+
import { existsSync as existsSync16, statSync as statSync9 } from "fs";
|
|
7227
7258
|
import { render } from "ink";
|
|
7228
|
-
import
|
|
7259
|
+
import React27, { useState as useState12 } from "react";
|
|
7229
7260
|
|
|
7230
7261
|
// src/cli/ui/App.tsx
|
|
7231
|
-
import
|
|
7232
|
-
import
|
|
7262
|
+
import * as pathMod7 from "path";
|
|
7263
|
+
import { Box as Box22, Static, Text as Text20, useApp, useStdout as useStdout8 } from "ink";
|
|
7264
|
+
import React24, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState10 } from "react";
|
|
7233
7265
|
|
|
7234
7266
|
// src/code/pending-edits.ts
|
|
7235
7267
|
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
@@ -7521,6 +7553,60 @@ ${skill2.body}${argsBlock}`;
|
|
|
7521
7553
|
return registry;
|
|
7522
7554
|
}
|
|
7523
7555
|
|
|
7556
|
+
// src/tools/workspace.ts
|
|
7557
|
+
import { existsSync as existsSync11, statSync as statSync6 } from "fs";
|
|
7558
|
+
import * as pathMod4 from "path";
|
|
7559
|
+
var WorkspaceConfirmationError = class extends Error {
|
|
7560
|
+
path;
|
|
7561
|
+
constructor(path5) {
|
|
7562
|
+
super(
|
|
7563
|
+
`change_workspace: switching to "${path5}" needs the user's approval before it takes effect. STOP calling tools now \u2014 the TUI has already prompted the user to press Enter (switch) or Esc (deny). Wait for their next message; it will either confirm the switch (and your subsequent file/shell tools will resolve against the new root) or tell you to continue without changing directories.`
|
|
7564
|
+
);
|
|
7565
|
+
this.name = "WorkspaceConfirmationError";
|
|
7566
|
+
this.path = path5;
|
|
7567
|
+
}
|
|
7568
|
+
};
|
|
7569
|
+
function registerWorkspaceTool(registry) {
|
|
7570
|
+
registry.register({
|
|
7571
|
+
name: "change_workspace",
|
|
7572
|
+
description: "Switch the session's working directory to a different project root. Re-registers filesystem / shell / memory tools against the new path so subsequent file reads, edits, and run_command calls all land there. EVERY switch requires explicit user approval via a modal \u2014 do NOT batch switches or chain a switch with subsequent tool calls before the user has confirmed. Use ONLY when the user explicitly asked to change directory or open a different project; never use to 'preview' a sibling repo. MCP servers stay anchored to the original launch root (their child processes can't be reconnected mid-session); the modal warns the user about this.",
|
|
7573
|
+
parameters: {
|
|
7574
|
+
type: "object",
|
|
7575
|
+
required: ["path"],
|
|
7576
|
+
properties: {
|
|
7577
|
+
path: {
|
|
7578
|
+
type: "string",
|
|
7579
|
+
description: "Target directory. Absolute paths land verbatim. Leading `~` expands to the user's home. Relative paths resolve against the user's launch cwd (not the current session root, so paths the user typed in chat resolve where they expect)."
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7582
|
+
},
|
|
7583
|
+
fn: (rawArgs) => {
|
|
7584
|
+
const args = rawArgs ?? {};
|
|
7585
|
+
if (typeof args.path !== "string" || args.path.trim() === "") {
|
|
7586
|
+
throw new Error("change_workspace: `path` must be a non-empty string");
|
|
7587
|
+
}
|
|
7588
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
7589
|
+
const expanded = args.path.startsWith("~") && home ? pathMod4.join(home, args.path.slice(1)) : args.path;
|
|
7590
|
+
const abs = pathMod4.resolve(expanded);
|
|
7591
|
+
if (!existsSync11(abs)) {
|
|
7592
|
+
throw new Error(`change_workspace: path does not exist \u2014 ${abs}`);
|
|
7593
|
+
}
|
|
7594
|
+
try {
|
|
7595
|
+
if (!statSync6(abs).isDirectory()) {
|
|
7596
|
+
throw new Error(`change_workspace: not a directory \u2014 ${abs}`);
|
|
7597
|
+
}
|
|
7598
|
+
} catch (err) {
|
|
7599
|
+
if (err.code === "ENOENT") {
|
|
7600
|
+
throw new Error(`change_workspace: path does not exist \u2014 ${abs}`);
|
|
7601
|
+
}
|
|
7602
|
+
throw err;
|
|
7603
|
+
}
|
|
7604
|
+
throw new WorkspaceConfirmationError(abs);
|
|
7605
|
+
}
|
|
7606
|
+
});
|
|
7607
|
+
return registry;
|
|
7608
|
+
}
|
|
7609
|
+
|
|
7524
7610
|
// src/cli/ui/AtMentionSuggestions.tsx
|
|
7525
7611
|
import { Box, Text } from "ink";
|
|
7526
7612
|
import React from "react";
|
|
@@ -8315,8 +8401,8 @@ function RiskLegend() {
|
|
|
8315
8401
|
var PlanStepList = React8.memo(PlanStepListInner);
|
|
8316
8402
|
|
|
8317
8403
|
// src/cli/ui/markdown.tsx
|
|
8318
|
-
import { readFileSync as readFileSync13, statSync as
|
|
8319
|
-
import { isAbsolute as isAbsolute4, join as
|
|
8404
|
+
import { readFileSync as readFileSync13, statSync as statSync7 } from "fs";
|
|
8405
|
+
import { isAbsolute as isAbsolute4, join as join12 } from "path";
|
|
8320
8406
|
import { Box as Box8, Text as Text7 } from "ink";
|
|
8321
8407
|
import React9 from "react";
|
|
8322
8408
|
var SUPERSCRIPT = {
|
|
@@ -8358,7 +8444,32 @@ function toSubscript(s) {
|
|
|
8358
8444
|
for (const c of s) out += SUBSCRIPT[c] ?? c;
|
|
8359
8445
|
return out;
|
|
8360
8446
|
}
|
|
8447
|
+
var HAS_MATH_RE = new RegExp(
|
|
8448
|
+
[
|
|
8449
|
+
"\\$",
|
|
8450
|
+
// dollar-delimited (block or inline)
|
|
8451
|
+
"\\\\[([]",
|
|
8452
|
+
// \( or \[
|
|
8453
|
+
"\\\\[a-zA-Z]+\\s*\\{",
|
|
8454
|
+
// \anyCommand{...} — covers catch-all braced transforms
|
|
8455
|
+
// Bare (no-brace) LaTeX commands the pipeline knows how to handle.
|
|
8456
|
+
// Listed explicitly because a generic `\\[a-zA-Z]+` would also match
|
|
8457
|
+
// Windows paths (`F:\TEST1`) and re-introduce the bug we're fixing.
|
|
8458
|
+
"\\\\(?:cdot|times|div|pm|mp|leq|geq|neq|approx|in|notin|infty|sum|prod|int|alpha|beta|gamma|delta|theta|lambda|mu|pi|sigma|phi|omega|implies|iff|to|rightarrow|leftarrow|Rightarrow|Leftarrow|ldots|cdots|quad|qquad)(?![a-zA-Z])",
|
|
8459
|
+
"[\\^_]\\{",
|
|
8460
|
+
// LaTeX braced super/subscript: ^{2}, _{ij}
|
|
8461
|
+
"\\^[0-9+\\-n](?![A-Za-z])",
|
|
8462
|
+
// LaTeX single-char super: ^2, ^-, ^n
|
|
8463
|
+
"_[0-9+\\-](?![A-Za-z])",
|
|
8464
|
+
// LaTeX single-char sub: _1, _+, _-
|
|
8465
|
+
"\\^[A-Za-z0-9+\\-]+\\^",
|
|
8466
|
+
// Pandoc super: ^2^, ^abc^
|
|
8467
|
+
"(?<!~)~[A-Za-z0-9+\\-]+~(?!~)"
|
|
8468
|
+
// Pandoc sub: ~2~ (lookarounds avoid ~~strike~~)
|
|
8469
|
+
].join("|")
|
|
8470
|
+
);
|
|
8361
8471
|
function stripMath(s) {
|
|
8472
|
+
if (!HAS_MATH_RE.test(s)) return s;
|
|
8362
8473
|
return s.replace(/\$\$([\s\S]+?)\$\$/g, (_m, c) => `
|
|
8363
8474
|
|
|
8364
8475
|
${c.trim()}
|
|
@@ -8532,7 +8643,7 @@ function validateCitation(url, projectRoot) {
|
|
|
8532
8643
|
const parts = parseCitationUrl(url);
|
|
8533
8644
|
if (!parts || !parts.path) return { ok: false, reason: "empty path" };
|
|
8534
8645
|
const normalized = parts.path.replace(/^[/\\]+/, "");
|
|
8535
|
-
const baseFullPath = isAbsolute4(normalized) ? normalized :
|
|
8646
|
+
const baseFullPath = isAbsolute4(normalized) ? normalized : join12(projectRoot, normalized);
|
|
8536
8647
|
const siblings = SIBLING_EXTENSIONS.get(extOf(baseFullPath)) ?? [];
|
|
8537
8648
|
const candidates = [
|
|
8538
8649
|
baseFullPath,
|
|
@@ -8542,7 +8653,7 @@ function validateCitation(url, projectRoot) {
|
|
|
8542
8653
|
let stat = null;
|
|
8543
8654
|
for (const candidate of candidates) {
|
|
8544
8655
|
try {
|
|
8545
|
-
stat =
|
|
8656
|
+
stat = statSync7(candidate);
|
|
8546
8657
|
fullPath = candidate;
|
|
8547
8658
|
break;
|
|
8548
8659
|
} catch {
|
|
@@ -10945,6 +11056,39 @@ function Hint({ cmd, desc }) {
|
|
|
10945
11056
|
return /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: COLOR.accent }, cmd.padEnd(8)), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, ` ${desc}`));
|
|
10946
11057
|
}
|
|
10947
11058
|
|
|
11059
|
+
// src/cli/ui/WorkspaceConfirm.tsx
|
|
11060
|
+
import { Box as Box21, Text as Text19 } from "ink";
|
|
11061
|
+
import React23 from "react";
|
|
11062
|
+
function WorkspaceConfirm({
|
|
11063
|
+
path: path5,
|
|
11064
|
+
currentRoot,
|
|
11065
|
+
mcpServerCount,
|
|
11066
|
+
onChoose
|
|
11067
|
+
}) {
|
|
11068
|
+
const subtitle = mcpServerCount > 0 ? `MCP servers (${mcpServerCount}) stay anchored to the original launch root.` : "Re-registers filesystem / shell / memory tools at the new path.";
|
|
11069
|
+
return /* @__PURE__ */ React23.createElement(ModalCard, { accent: "#f59e0b", icon: "\u21C4", title: "switch workspace", subtitle }, /* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "from "), /* @__PURE__ */ React23.createElement(Text19, { color: "#a3a3a3" }, currentRoot)), /* @__PURE__ */ React23.createElement(Box21, null, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "to "), /* @__PURE__ */ React23.createElement(Text19, { color: "#67e8f9", bold: true }, path5))), /* @__PURE__ */ React23.createElement(
|
|
11070
|
+
SingleSelect,
|
|
11071
|
+
{
|
|
11072
|
+
initialValue: "switch",
|
|
11073
|
+
items: [
|
|
11074
|
+
{
|
|
11075
|
+
value: "switch",
|
|
11076
|
+
label: "Switch",
|
|
11077
|
+
hint: "Re-register filesystem / shell / memory tools against the new root."
|
|
11078
|
+
},
|
|
11079
|
+
{
|
|
11080
|
+
value: "deny",
|
|
11081
|
+
label: "Deny",
|
|
11082
|
+
hint: "Tell the model the user refused; it will continue without changing directories."
|
|
11083
|
+
}
|
|
11084
|
+
],
|
|
11085
|
+
onSubmit: (v) => onChoose(v),
|
|
11086
|
+
onCancel: () => onChoose("deny"),
|
|
11087
|
+
footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] deny"
|
|
11088
|
+
}
|
|
11089
|
+
));
|
|
11090
|
+
}
|
|
11091
|
+
|
|
10948
11092
|
// src/cli/ui/bang.ts
|
|
10949
11093
|
function detectBangCommand(text) {
|
|
10950
11094
|
if (!text.startsWith("!")) return null;
|
|
@@ -11038,9 +11182,9 @@ function describeRepair(repair) {
|
|
|
11038
11182
|
}
|
|
11039
11183
|
|
|
11040
11184
|
// src/cli/ui/hash-memory.ts
|
|
11041
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
11185
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
11042
11186
|
import { homedir as homedir6 } from "os";
|
|
11043
|
-
import { dirname as dirname10, join as
|
|
11187
|
+
import { dirname as dirname10, join as join13 } from "path";
|
|
11044
11188
|
var PROJECT_HEADER = `# Reasonix project memory
|
|
11045
11189
|
|
|
11046
11190
|
Notes the user pinned via the \`#\` prompt prefix. The whole file is
|
|
@@ -11072,12 +11216,12 @@ function detectHashMemory(text) {
|
|
|
11072
11216
|
return { kind: "memory", note: body };
|
|
11073
11217
|
}
|
|
11074
11218
|
function appendProjectMemory(rootDir, note) {
|
|
11075
|
-
return appendBulletToFile(
|
|
11219
|
+
return appendBulletToFile(join13(rootDir, PROJECT_MEMORY_FILE), note, PROJECT_HEADER);
|
|
11076
11220
|
}
|
|
11077
11221
|
var GLOBAL_MEMORY_DIR = ".reasonix";
|
|
11078
11222
|
var GLOBAL_MEMORY_FILE = "REASONIX.md";
|
|
11079
11223
|
function globalMemoryPath(homeDir = homedir6()) {
|
|
11080
|
-
return
|
|
11224
|
+
return join13(homeDir, GLOBAL_MEMORY_DIR, GLOBAL_MEMORY_FILE);
|
|
11081
11225
|
}
|
|
11082
11226
|
function appendGlobalMemory(note, homeDir) {
|
|
11083
11227
|
return appendBulletToFile(globalMemoryPath(homeDir), note, GLOBAL_HEADER);
|
|
@@ -11087,7 +11231,7 @@ function appendBulletToFile(path5, note, newFileHeader) {
|
|
|
11087
11231
|
if (!trimmed) throw new Error("note body cannot be empty");
|
|
11088
11232
|
const bullet = `- ${trimmed}
|
|
11089
11233
|
`;
|
|
11090
|
-
if (!
|
|
11234
|
+
if (!existsSync12(path5)) {
|
|
11091
11235
|
mkdirSync8(dirname10(path5), { recursive: true });
|
|
11092
11236
|
writeFileSync7(path5, `${newFileHeader}${bullet}`, "utf8");
|
|
11093
11237
|
return { path: path5, created: true };
|
|
@@ -11435,6 +11579,11 @@ var SLASH_COMMANDS = [
|
|
|
11435
11579
|
argsHint: "[reload]",
|
|
11436
11580
|
summary: "list active hooks (settings.json under .reasonix/) \xB7 reload re-reads from disk"
|
|
11437
11581
|
},
|
|
11582
|
+
{
|
|
11583
|
+
cmd: "cwd",
|
|
11584
|
+
argsHint: "<path>",
|
|
11585
|
+
summary: "switch session working directory (re-registers code tools, reloads hooks; MCP servers stay)"
|
|
11586
|
+
},
|
|
11438
11587
|
{
|
|
11439
11588
|
cmd: "update",
|
|
11440
11589
|
summary: "show current vs latest version + the shell command to upgrade"
|
|
@@ -11477,6 +11626,13 @@ var SLASH_COMMANDS = [
|
|
|
11477
11626
|
},
|
|
11478
11627
|
{ cmd: "exit", summary: "quit the TUI" },
|
|
11479
11628
|
// Code-mode only
|
|
11629
|
+
{
|
|
11630
|
+
cmd: "init",
|
|
11631
|
+
argsHint: "[force]",
|
|
11632
|
+
summary: "scan the project and synthesize a baseline REASONIX.md (model writes; review with /apply). `force` overwrites an existing file.",
|
|
11633
|
+
contextual: "code",
|
|
11634
|
+
argCompleter: ["force"]
|
|
11635
|
+
},
|
|
11480
11636
|
{
|
|
11481
11637
|
cmd: "apply",
|
|
11482
11638
|
argsHint: "[N|N,M|N-M]",
|
|
@@ -11581,8 +11737,12 @@ function parseSlash(text) {
|
|
|
11581
11737
|
return { cmd, args: parts.slice(1) };
|
|
11582
11738
|
}
|
|
11583
11739
|
|
|
11740
|
+
// src/cli/ui/slash/handlers/admin.ts
|
|
11741
|
+
import { existsSync as existsSync14, statSync as statSync8 } from "fs";
|
|
11742
|
+
import * as pathMod5 from "path";
|
|
11743
|
+
|
|
11584
11744
|
// src/cli/commands/stats.ts
|
|
11585
|
-
import { existsSync as
|
|
11745
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
11586
11746
|
function statsCommand(opts) {
|
|
11587
11747
|
if (opts.transcript) {
|
|
11588
11748
|
transcriptSummary(opts.transcript);
|
|
@@ -11591,7 +11751,7 @@ function statsCommand(opts) {
|
|
|
11591
11751
|
dashboard(opts);
|
|
11592
11752
|
}
|
|
11593
11753
|
function transcriptSummary(path5) {
|
|
11594
|
-
if (!
|
|
11754
|
+
if (!existsSync13(path5)) {
|
|
11595
11755
|
console.error(`no such transcript: ${path5}`);
|
|
11596
11756
|
process.exit(1);
|
|
11597
11757
|
}
|
|
@@ -11815,9 +11975,51 @@ var stats = () => {
|
|
|
11815
11975
|
const agg = aggregateUsage(records);
|
|
11816
11976
|
return { info: renderDashboard(agg, path5) };
|
|
11817
11977
|
};
|
|
11978
|
+
var cwd = (args, _loop, ctx) => {
|
|
11979
|
+
if (!ctx.setCwd) {
|
|
11980
|
+
return {
|
|
11981
|
+
info: "/cwd is not available in this context (no setCwd callback wired)."
|
|
11982
|
+
};
|
|
11983
|
+
}
|
|
11984
|
+
const raw = (args[0] ?? "").trim();
|
|
11985
|
+
if (!raw) {
|
|
11986
|
+
return {
|
|
11987
|
+
info: "usage: /cwd <path> (absolute or relative, ~ expands to home)"
|
|
11988
|
+
};
|
|
11989
|
+
}
|
|
11990
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11991
|
+
const expanded = raw.startsWith("~") && home ? pathMod5.join(home, raw.slice(1)) : raw;
|
|
11992
|
+
const abs = pathMod5.resolve(expanded);
|
|
11993
|
+
if (!existsSync14(abs)) {
|
|
11994
|
+
return { info: `\u25B8 /cwd: path does not exist \u2014 ${abs}` };
|
|
11995
|
+
}
|
|
11996
|
+
let isDir = false;
|
|
11997
|
+
try {
|
|
11998
|
+
isDir = statSync8(abs).isDirectory();
|
|
11999
|
+
} catch {
|
|
12000
|
+
}
|
|
12001
|
+
if (!isDir) {
|
|
12002
|
+
return { info: `\u25B8 /cwd: not a directory \u2014 ${abs}` };
|
|
12003
|
+
}
|
|
12004
|
+
let info;
|
|
12005
|
+
try {
|
|
12006
|
+
info = ctx.setCwd(abs);
|
|
12007
|
+
} catch (err) {
|
|
12008
|
+
return { info: `\u25B8 /cwd failed: ${err.message}` };
|
|
12009
|
+
}
|
|
12010
|
+
const lines = [info];
|
|
12011
|
+
if (ctx.mcpServers && ctx.mcpServers.length > 0) {
|
|
12012
|
+
lines.push(
|
|
12013
|
+
` note: ${ctx.mcpServers.length} MCP server(s) still anchored to the original cwd \u2014`,
|
|
12014
|
+
" their tools won't follow this switch. Restart the session for full reset."
|
|
12015
|
+
);
|
|
12016
|
+
}
|
|
12017
|
+
return { info: lines.join("\n") };
|
|
12018
|
+
};
|
|
11818
12019
|
var handlers = {
|
|
11819
12020
|
hook: hooks,
|
|
11820
12021
|
hooks,
|
|
12022
|
+
cwd,
|
|
11821
12023
|
update,
|
|
11822
12024
|
stats
|
|
11823
12025
|
};
|
|
@@ -12265,6 +12467,103 @@ var handlers3 = {
|
|
|
12265
12467
|
walk: walk2
|
|
12266
12468
|
};
|
|
12267
12469
|
|
|
12470
|
+
// src/cli/ui/slash/handlers/init.ts
|
|
12471
|
+
import { existsSync as existsSync15 } from "fs";
|
|
12472
|
+
import * as pathMod6 from "path";
|
|
12473
|
+
var INIT_PROMPT = [
|
|
12474
|
+
"# Task: Initialize REASONIX.md",
|
|
12475
|
+
"",
|
|
12476
|
+
"I want you to generate a REASONIX.md at the project root that captures",
|
|
12477
|
+
"the working knowledge a future Reasonix session needs to be productive",
|
|
12478
|
+
"here. This file is auto-pinned into your system prompt every launch,",
|
|
12479
|
+
"so its size and accuracy matter.",
|
|
12480
|
+
"",
|
|
12481
|
+
"## Hard constraints (do NOT relax these)",
|
|
12482
|
+
"",
|
|
12483
|
+
"- **Length cap: \u2264 80 lines / 3KB total.** Be concise. If you can't fit a",
|
|
12484
|
+
" section, drop it.",
|
|
12485
|
+
"- **Only document things you can verify by reading files.** Do NOT",
|
|
12486
|
+
" speculate about architectural intent, future roadmap, or design",
|
|
12487
|
+
" rationale. If it isn't obvious from the code, leave it out.",
|
|
12488
|
+
"- **No placeholder text.** No 'TODO: describe X', no 'Add more here'.",
|
|
12489
|
+
" Either state a fact or omit the section.",
|
|
12490
|
+
"",
|
|
12491
|
+
"## Procedure",
|
|
12492
|
+
"",
|
|
12493
|
+
"1. Read the top of any existing README* file.",
|
|
12494
|
+
"2. Read the manifest (package.json / Cargo.toml / pyproject.toml /",
|
|
12495
|
+
" go.mod / etc.) \u2014 pick whichever exists.",
|
|
12496
|
+
"3. `directory_tree` 1-2 levels deep on the project root, skipping",
|
|
12497
|
+
" common build/dependency dirs (node_modules, dist, target, .git,",
|
|
12498
|
+
" venv, __pycache__).",
|
|
12499
|
+
"4. Identify: primary language + framework, top-level layout, test",
|
|
12500
|
+
" runner, lint/format setup, build/run/test scripts, any non-obvious",
|
|
12501
|
+
" convention with visible evidence (commit message format, import",
|
|
12502
|
+
" order, naming pattern).",
|
|
12503
|
+
"5. Write REASONIX.md with the sections below, skipping any you can't",
|
|
12504
|
+
" fill from evidence.",
|
|
12505
|
+
"",
|
|
12506
|
+
"## Sections to use (skip ones with no evidence)",
|
|
12507
|
+
"",
|
|
12508
|
+
"- **Stack** \u2014 language + framework + 3-5 key deps. One line each.",
|
|
12509
|
+
"- **Layout** \u2014 top-level dirs and what lives in each. One line each.",
|
|
12510
|
+
"- **Commands** \u2014 verbatim from `scripts` block (or equivalent):",
|
|
12511
|
+
" build / test / lint / typecheck / dev / format. Whatever exists.",
|
|
12512
|
+
"- **Conventions** \u2014 only things visible in the code. Examples:",
|
|
12513
|
+
" '*.test.ts colocated with source', 'named exports only',",
|
|
12514
|
+
" 'commits use Conventional Commits prefix'. If you can't find any",
|
|
12515
|
+
" CONVENTION evidence, omit the whole section.",
|
|
12516
|
+
"- **Watch out for** \u2014 gotchas a new contributor would benefit from",
|
|
12517
|
+
" knowing BEFORE editing. Examples: 'edit_file SEARCH must match",
|
|
12518
|
+
" byte-for-byte', 'this dir is generated, don't edit by hand'.",
|
|
12519
|
+
" Omit if you find nothing concrete.",
|
|
12520
|
+
"",
|
|
12521
|
+
"## Output",
|
|
12522
|
+
"",
|
|
12523
|
+
"Write the result to `REASONIX.md` in the project root using the",
|
|
12524
|
+
"filesystem tools (edit_file with empty SEARCH if creating new,",
|
|
12525
|
+
"write_file if overwriting). After writing, STOP \u2014 do not summarize",
|
|
12526
|
+
"what you did, do not propose follow-up tasks. The user will review",
|
|
12527
|
+
"the pending edit via /apply.",
|
|
12528
|
+
"",
|
|
12529
|
+
"Start now."
|
|
12530
|
+
].join("\n");
|
|
12531
|
+
var init = (args, _loop, ctx) => {
|
|
12532
|
+
if (!ctx.codeRoot) {
|
|
12533
|
+
return {
|
|
12534
|
+
info: [
|
|
12535
|
+
"/init only works in code mode (it needs filesystem tools).",
|
|
12536
|
+
"Run `reasonix code [path]` to start a session rooted at the",
|
|
12537
|
+
"project you want to initialize, then run /init."
|
|
12538
|
+
].join("\n")
|
|
12539
|
+
};
|
|
12540
|
+
}
|
|
12541
|
+
const force = (args[0] ?? "").toLowerCase() === "force";
|
|
12542
|
+
const target = pathMod6.join(ctx.codeRoot, "REASONIX.md");
|
|
12543
|
+
if (existsSync15(target) && !force) {
|
|
12544
|
+
return {
|
|
12545
|
+
info: [
|
|
12546
|
+
`\u25B8 REASONIX.md already exists at ${target}`,
|
|
12547
|
+
"",
|
|
12548
|
+
" /init force regenerate from scratch (overwrites)",
|
|
12549
|
+
"",
|
|
12550
|
+
" Or edit it by hand \u2014 it's just markdown. The current file is",
|
|
12551
|
+
" pinned into the system prompt every launch as-is."
|
|
12552
|
+
].join("\n")
|
|
12553
|
+
};
|
|
12554
|
+
}
|
|
12555
|
+
return {
|
|
12556
|
+
info: [
|
|
12557
|
+
"\u25B8 /init \u2014 model will scan the project and synthesize REASONIX.md.",
|
|
12558
|
+
" The result lands as a pending edit; review with /apply or /walk."
|
|
12559
|
+
].join("\n"),
|
|
12560
|
+
resubmit: INIT_PROMPT
|
|
12561
|
+
};
|
|
12562
|
+
};
|
|
12563
|
+
var handlers4 = {
|
|
12564
|
+
init
|
|
12565
|
+
};
|
|
12566
|
+
|
|
12268
12567
|
// src/cli/ui/slash/handlers/jobs.ts
|
|
12269
12568
|
var jobs = (_args, _loop, ctx) => {
|
|
12270
12569
|
if (!ctx.jobs) {
|
|
@@ -12320,7 +12619,7 @@ $ ${out.command}`;
|
|
|
12320
12619
|
return { info: out.output ? `${header2}
|
|
12321
12620
|
${out.output}` : header2 };
|
|
12322
12621
|
};
|
|
12323
|
-
var
|
|
12622
|
+
var handlers5 = {
|
|
12324
12623
|
jobs,
|
|
12325
12624
|
kill,
|
|
12326
12625
|
logs
|
|
@@ -12381,7 +12680,7 @@ var mcp = (_args, loop2, ctx) => {
|
|
|
12381
12680
|
lines.push("To change this set, exit and run `reasonix setup`.");
|
|
12382
12681
|
return { info: lines.join("\n") };
|
|
12383
12682
|
};
|
|
12384
|
-
var
|
|
12683
|
+
var handlers6 = { mcp };
|
|
12385
12684
|
|
|
12386
12685
|
// src/cli/ui/slash/handlers/memory.ts
|
|
12387
12686
|
var memory = (args, _loop, ctx) => {
|
|
@@ -12516,7 +12815,7 @@ var memory = (args, _loop, ctx) => {
|
|
|
12516
12815
|
);
|
|
12517
12816
|
return { info: parts.join("\n") };
|
|
12518
12817
|
};
|
|
12519
|
-
var
|
|
12818
|
+
var handlers7 = { memory };
|
|
12520
12819
|
|
|
12521
12820
|
// src/cli/ui/slash/handlers/model.ts
|
|
12522
12821
|
var model = (args, loop2, ctx) => {
|
|
@@ -12669,7 +12968,7 @@ var pro = (args, loop2, ctx) => {
|
|
|
12669
12968
|
};
|
|
12670
12969
|
};
|
|
12671
12970
|
var ESCALATION_MODEL_ID = "deepseek-v4-pro";
|
|
12672
|
-
var
|
|
12971
|
+
var handlers8 = {
|
|
12673
12972
|
model,
|
|
12674
12973
|
models,
|
|
12675
12974
|
harvest: harvest2,
|
|
@@ -12822,7 +13121,7 @@ var compact = (args, loop2) => {
|
|
|
12822
13121
|
info: `\u25B8 compacted ${healedCount} payload(s) to ${cap.toLocaleString()} tokens each (tool results + tool-call args), saved ${tokensSaved.toLocaleString()} tokens (${charsSaved.toLocaleString()} chars). Session file rewritten.`
|
|
12823
13122
|
};
|
|
12824
13123
|
};
|
|
12825
|
-
var
|
|
13124
|
+
var handlers9 = {
|
|
12826
13125
|
think,
|
|
12827
13126
|
reasoning: think,
|
|
12828
13127
|
tool,
|
|
@@ -12910,7 +13209,7 @@ var replay = (args, loop2) => {
|
|
|
12910
13209
|
}
|
|
12911
13210
|
};
|
|
12912
13211
|
};
|
|
12913
|
-
var
|
|
13212
|
+
var handlers10 = {
|
|
12914
13213
|
plans,
|
|
12915
13214
|
replay
|
|
12916
13215
|
};
|
|
@@ -13173,7 +13472,7 @@ async function startOllamaDaemon(opts = {}) {
|
|
|
13173
13472
|
return { ready: false, pid };
|
|
13174
13473
|
}
|
|
13175
13474
|
async function pullOllamaModel(modelName, opts = {}) {
|
|
13176
|
-
return new Promise((
|
|
13475
|
+
return new Promise((resolve12) => {
|
|
13177
13476
|
const child = spawn5("ollama", ["pull", modelName], {
|
|
13178
13477
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13179
13478
|
windowsHide: true
|
|
@@ -13185,8 +13484,8 @@ async function pullOllamaModel(modelName, opts = {}) {
|
|
|
13185
13484
|
}
|
|
13186
13485
|
streamLines(child.stdout, (l) => opts.onLine?.(l, "stdout"));
|
|
13187
13486
|
streamLines(child.stderr, (l) => opts.onLine?.(l, "stderr"));
|
|
13188
|
-
child.once("exit", (code) =>
|
|
13189
|
-
child.once("error", () =>
|
|
13487
|
+
child.once("exit", (code) => resolve12(code ?? -1));
|
|
13488
|
+
child.once("error", () => resolve12(-1));
|
|
13190
13489
|
});
|
|
13191
13490
|
}
|
|
13192
13491
|
function streamLines(stream, cb) {
|
|
@@ -13283,7 +13582,7 @@ async function readIndexMeta(rootDir) {
|
|
|
13283
13582
|
return null;
|
|
13284
13583
|
}
|
|
13285
13584
|
}
|
|
13286
|
-
var
|
|
13585
|
+
var handlers11 = {
|
|
13287
13586
|
semantic
|
|
13288
13587
|
};
|
|
13289
13588
|
|
|
@@ -13318,7 +13617,7 @@ var forget = (_args, loop2) => {
|
|
|
13318
13617
|
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?)`
|
|
13319
13618
|
};
|
|
13320
13619
|
};
|
|
13321
|
-
var
|
|
13620
|
+
var handlers12 = {
|
|
13322
13621
|
sessions,
|
|
13323
13622
|
forget
|
|
13324
13623
|
};
|
|
@@ -13394,7 +13693,7 @@ ${found.body}${argsLine}`;
|
|
|
13394
13693
|
resubmit: payload
|
|
13395
13694
|
};
|
|
13396
13695
|
};
|
|
13397
|
-
var
|
|
13696
|
+
var handlers13 = {
|
|
13398
13697
|
skill,
|
|
13399
13698
|
skills: skill
|
|
13400
13699
|
};
|
|
@@ -13412,7 +13711,8 @@ var HANDLERS = {
|
|
|
13412
13711
|
...handlers9,
|
|
13413
13712
|
...handlers10,
|
|
13414
13713
|
...handlers11,
|
|
13415
|
-
...handlers12
|
|
13714
|
+
...handlers12,
|
|
13715
|
+
...handlers13
|
|
13416
13716
|
};
|
|
13417
13717
|
function handleSlash(cmd, args, loop2, ctx = {}) {
|
|
13418
13718
|
const h = HANDLERS[cmd];
|
|
@@ -13426,6 +13726,7 @@ function useCompletionPickers({
|
|
|
13426
13726
|
input,
|
|
13427
13727
|
setInput,
|
|
13428
13728
|
codeMode,
|
|
13729
|
+
rootDir,
|
|
13429
13730
|
models: models2,
|
|
13430
13731
|
mcpServers
|
|
13431
13732
|
}) {
|
|
@@ -13443,13 +13744,13 @@ function useCompletionPickers({
|
|
|
13443
13744
|
}, [slashMatches]);
|
|
13444
13745
|
const [atSelected, setAtSelected] = useState6(0);
|
|
13445
13746
|
const atFiles = useMemo2(() => {
|
|
13446
|
-
if (!codeMode
|
|
13747
|
+
if (!codeMode) return [];
|
|
13447
13748
|
try {
|
|
13448
|
-
return listFilesWithStatsSync(
|
|
13749
|
+
return listFilesWithStatsSync(rootDir, { maxResults: 500 });
|
|
13449
13750
|
} catch {
|
|
13450
13751
|
return [];
|
|
13451
13752
|
}
|
|
13452
|
-
}, [codeMode
|
|
13753
|
+
}, [codeMode, rootDir]);
|
|
13453
13754
|
const recentFilesRef = useRef3([]);
|
|
13454
13755
|
const recordRecentFile = useCallback((p) => {
|
|
13455
13756
|
const list = recentFilesRef.current;
|
|
@@ -13459,10 +13760,10 @@ function useCompletionPickers({
|
|
|
13459
13760
|
if (list.length > 20) list.length = 20;
|
|
13460
13761
|
}, []);
|
|
13461
13762
|
const atPicker = useMemo2(() => {
|
|
13462
|
-
if (!codeMode
|
|
13763
|
+
if (!codeMode) return null;
|
|
13463
13764
|
if (slashMatches !== null) return null;
|
|
13464
13765
|
return detectAtPicker(input);
|
|
13465
|
-
}, [codeMode
|
|
13766
|
+
}, [codeMode, input, slashMatches]);
|
|
13466
13767
|
const atMatches = useMemo2(() => {
|
|
13467
13768
|
if (!atPicker) return null;
|
|
13468
13769
|
return rankPickerCandidates(atFiles, atPicker.query, {
|
|
@@ -13902,13 +14203,13 @@ var PLAIN_UI = process.env.REASONIX_UI === "plain";
|
|
|
13902
14203
|
function LoopStatusRow({
|
|
13903
14204
|
loop: loop2
|
|
13904
14205
|
}) {
|
|
13905
|
-
const [, setTick] =
|
|
13906
|
-
|
|
14206
|
+
const [, setTick] = React24.useState(0);
|
|
14207
|
+
React24.useEffect(() => {
|
|
13907
14208
|
const id = setInterval(() => setTick((t2) => t2 + 1), 1e3);
|
|
13908
14209
|
return () => clearInterval(id);
|
|
13909
14210
|
}, []);
|
|
13910
14211
|
const nextFireMs = Math.max(0, loop2.nextFireAt - Date.now());
|
|
13911
|
-
return /* @__PURE__ */
|
|
14212
|
+
return /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text20, { color: "cyan" }, `\u25B8 ${formatLoopStatus(loop2.prompt, nextFireMs, loop2.iter)} \xB7 /loop stop or type to cancel`));
|
|
13912
14213
|
}
|
|
13913
14214
|
function App({
|
|
13914
14215
|
model: model2,
|
|
@@ -13967,10 +14268,12 @@ function App({
|
|
|
13967
14268
|
setHistorical
|
|
13968
14269
|
});
|
|
13969
14270
|
const [statusLine, setStatusLine] = useState10(null);
|
|
14271
|
+
const [currentRootDir, setCurrentRootDir] = useState10(
|
|
14272
|
+
() => codeMode?.rootDir ?? process.cwd()
|
|
14273
|
+
);
|
|
13970
14274
|
const [hookList, setHookList] = useState10(
|
|
13971
14275
|
() => loadHooks({ projectRoot: codeMode?.rootDir })
|
|
13972
14276
|
);
|
|
13973
|
-
const hookCwd = codeMode?.rootDir ?? process.cwd();
|
|
13974
14277
|
const {
|
|
13975
14278
|
undoBanner,
|
|
13976
14279
|
recordEdit,
|
|
@@ -14012,6 +14315,7 @@ function App({
|
|
|
14012
14315
|
}, 1200);
|
|
14013
14316
|
}, [editMode]);
|
|
14014
14317
|
const [pendingShell, setPendingShell] = useState10(null);
|
|
14318
|
+
const [pendingWorkspace, setPendingWorkspace] = useState10(null);
|
|
14015
14319
|
const [pendingPlan, setPendingPlan] = useState10(null);
|
|
14016
14320
|
const [stagedInput, setStagedInput] = useState10(null);
|
|
14017
14321
|
const [pendingCheckpoint, setPendingCheckpoint] = useState10(null);
|
|
@@ -14106,6 +14410,9 @@ function App({
|
|
|
14106
14410
|
}
|
|
14107
14411
|
});
|
|
14108
14412
|
}
|
|
14413
|
+
if (tools && !tools.has("change_workspace")) {
|
|
14414
|
+
registerWorkspaceTool(tools);
|
|
14415
|
+
}
|
|
14109
14416
|
const prefix = new ImmutablePrefix({
|
|
14110
14417
|
system,
|
|
14111
14418
|
toolSpecs: tools?.specs()
|
|
@@ -14119,7 +14426,7 @@ function App({
|
|
|
14119
14426
|
branch: branch2,
|
|
14120
14427
|
session,
|
|
14121
14428
|
hooks: hookList,
|
|
14122
|
-
hookCwd,
|
|
14429
|
+
hookCwd: currentRootDir,
|
|
14123
14430
|
// Restore the user's last-chosen effort cap. Without this a
|
|
14124
14431
|
// `/effort high` silently reverted to `max` on relaunch — the
|
|
14125
14432
|
// loop's constructor default wins over persisted state.
|
|
@@ -14131,6 +14438,49 @@ function App({
|
|
|
14131
14438
|
useEffect6(() => {
|
|
14132
14439
|
loop2.hooks = hookList;
|
|
14133
14440
|
}, [loop2, hookList]);
|
|
14441
|
+
const applyCwdChange = useCallback4(
|
|
14442
|
+
(newRoot) => {
|
|
14443
|
+
setCurrentRootDir(newRoot);
|
|
14444
|
+
const fresh = loadHooks({ projectRoot: codeMode ? newRoot : void 0 });
|
|
14445
|
+
setHookList(fresh);
|
|
14446
|
+
const codeRebound = codeMode?.reregisterTools !== void 0;
|
|
14447
|
+
if (codeMode?.reregisterTools) {
|
|
14448
|
+
codeMode.reregisterTools(newRoot);
|
|
14449
|
+
}
|
|
14450
|
+
if (tools) {
|
|
14451
|
+
registerSkillTools(tools, {
|
|
14452
|
+
projectRoot: codeMode ? newRoot : void 0,
|
|
14453
|
+
subagentRunner: async (skill2, task) => {
|
|
14454
|
+
const result = await spawnSubagent({
|
|
14455
|
+
client: loop2.client,
|
|
14456
|
+
parentRegistry: tools,
|
|
14457
|
+
system: skill2.body,
|
|
14458
|
+
task,
|
|
14459
|
+
model: skill2.model,
|
|
14460
|
+
sink: subagentSinkRef.current,
|
|
14461
|
+
skillName: skill2.name
|
|
14462
|
+
});
|
|
14463
|
+
return formatSubagentResult(result);
|
|
14464
|
+
}
|
|
14465
|
+
});
|
|
14466
|
+
}
|
|
14467
|
+
const lines = [`\u25B8 cwd \u2192 ${newRoot}`, ` hooks reloaded (${fresh.length} active)`];
|
|
14468
|
+
if (codeMode) {
|
|
14469
|
+
lines.push(
|
|
14470
|
+
codeRebound ? " filesystem / shell / memory tools rebound to new root" : " warning: reregisterTools callback missing \u2014 tool sandbox unchanged"
|
|
14471
|
+
);
|
|
14472
|
+
lines.push(
|
|
14473
|
+
" note: system prompt context (gitignore, REASONIX.md stack) was",
|
|
14474
|
+
" baked at session start and still references the original root."
|
|
14475
|
+
);
|
|
14476
|
+
}
|
|
14477
|
+
return lines.join("\n");
|
|
14478
|
+
},
|
|
14479
|
+
[codeMode, loop2, tools, subagentSinkRef]
|
|
14480
|
+
);
|
|
14481
|
+
useEffect6(() => {
|
|
14482
|
+
loop2.hookCwd = currentRootDir;
|
|
14483
|
+
}, [loop2, currentRootDir]);
|
|
14134
14484
|
const {
|
|
14135
14485
|
balance,
|
|
14136
14486
|
models: models2,
|
|
@@ -14155,7 +14505,14 @@ function App({
|
|
|
14155
14505
|
slashArgSelected,
|
|
14156
14506
|
setSlashArgSelected,
|
|
14157
14507
|
pickSlashArg
|
|
14158
|
-
} = useCompletionPickers({
|
|
14508
|
+
} = useCompletionPickers({
|
|
14509
|
+
input,
|
|
14510
|
+
setInput,
|
|
14511
|
+
codeMode,
|
|
14512
|
+
rootDir: currentRootDir,
|
|
14513
|
+
models: models2,
|
|
14514
|
+
mcpServers
|
|
14515
|
+
});
|
|
14159
14516
|
useEffect6(() => {
|
|
14160
14517
|
if (!progressSink) return;
|
|
14161
14518
|
progressSink.current = (info) => {
|
|
@@ -14275,11 +14632,11 @@ function App({
|
|
|
14275
14632
|
if (key.escape && busy) {
|
|
14276
14633
|
if (abortedThisTurn.current) return;
|
|
14277
14634
|
abortedThisTurn.current = true;
|
|
14278
|
-
const
|
|
14279
|
-
if (
|
|
14635
|
+
const resolve12 = editReviewResolveRef.current;
|
|
14636
|
+
if (resolve12) {
|
|
14280
14637
|
editReviewResolveRef.current = null;
|
|
14281
14638
|
setPendingEditReview(null);
|
|
14282
|
-
|
|
14639
|
+
resolve12("reject");
|
|
14283
14640
|
}
|
|
14284
14641
|
if (activeLoopRef.current) stopLoop();
|
|
14285
14642
|
loop2.abort();
|
|
@@ -14302,7 +14659,7 @@ function App({
|
|
|
14302
14659
|
]);
|
|
14303
14660
|
return;
|
|
14304
14661
|
}
|
|
14305
|
-
if (codeMode && key.shift && key.tab && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision) {
|
|
14662
|
+
if (codeMode && key.shift && key.tab && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision) {
|
|
14306
14663
|
setEditMode((m) => {
|
|
14307
14664
|
const next = m === "review" ? "auto" : m === "auto" ? "yolo" : "review";
|
|
14308
14665
|
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)";
|
|
@@ -14314,7 +14671,7 @@ function App({
|
|
|
14314
14671
|
});
|
|
14315
14672
|
return;
|
|
14316
14673
|
}
|
|
14317
|
-
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
|
|
14674
|
+
if (codeMode && input.length === 0 && (chKey === "u" || chKey === "U") && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !walkthroughActive && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision && // Fire when EITHER the banner is up OR there's any non-undone
|
|
14318
14675
|
// history entry — the keybind is useful long after the 5-second
|
|
14319
14676
|
// banner expires, which users rightly want.
|
|
14320
14677
|
(undoBanner || hasUndoable())) {
|
|
@@ -14403,11 +14760,11 @@ function App({
|
|
|
14403
14760
|
block = { path: relPath, search, replace, offset: 0 };
|
|
14404
14761
|
} else {
|
|
14405
14762
|
const content = typeof args.content === "string" ? args.content : "";
|
|
14406
|
-
block = toWholeFileEditBlock(relPath, content,
|
|
14763
|
+
block = toWholeFileEditBlock(relPath, content, currentRootDir);
|
|
14407
14764
|
}
|
|
14408
14765
|
const applyNow = () => {
|
|
14409
|
-
const snaps = snapshotBeforeEdits([block],
|
|
14410
|
-
const results = applyEditBlocks([block],
|
|
14766
|
+
const snaps = snapshotBeforeEdits([block], currentRootDir);
|
|
14767
|
+
const results = applyEditBlocks([block], currentRootDir);
|
|
14411
14768
|
const good = results.some((r) => r.status === "applied" || r.status === "created");
|
|
14412
14769
|
if (good) {
|
|
14413
14770
|
recordEdit("auto", [block], results, snaps);
|
|
@@ -14476,8 +14833,8 @@ function App({
|
|
|
14476
14833
|
if (selected.length === 0) {
|
|
14477
14834
|
return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
|
|
14478
14835
|
}
|
|
14479
|
-
const snaps = snapshotBeforeEdits(selected,
|
|
14480
|
-
const results = applyEditBlocks(selected,
|
|
14836
|
+
const snaps = snapshotBeforeEdits(selected, currentRootDir);
|
|
14837
|
+
const results = applyEditBlocks(selected, currentRootDir);
|
|
14481
14838
|
const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
|
|
14482
14839
|
if (anyApplied) recordEdit("review-apply", selected, results, snaps);
|
|
14483
14840
|
pendingEdits.current = remaining;
|
|
@@ -14488,7 +14845,7 @@ function App({
|
|
|
14488
14845
|
\u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
|
|
14489
14846
|
return formatEditResults(results) + tail;
|
|
14490
14847
|
},
|
|
14491
|
-
[codeMode, session, syncPendingCount, recordEdit]
|
|
14848
|
+
[codeMode, currentRootDir, session, syncPendingCount, recordEdit]
|
|
14492
14849
|
);
|
|
14493
14850
|
const codeDiscard = useCallback4(
|
|
14494
14851
|
(indices) => {
|
|
@@ -14653,7 +15010,7 @@ function App({
|
|
|
14653
15010
|
const hashParse = detectHashMemory(text);
|
|
14654
15011
|
if (hashParse?.kind === "memory" || hashParse?.kind === "memory-global") {
|
|
14655
15012
|
const isGlobal = hashParse.kind === "memory-global";
|
|
14656
|
-
const memRoot =
|
|
15013
|
+
const memRoot = currentRootDir;
|
|
14657
15014
|
promptHistory.current.push(text);
|
|
14658
15015
|
try {
|
|
14659
15016
|
const result = isGlobal ? appendGlobalMemory(hashParse.note) : appendProjectMemory(memRoot, hashParse.note);
|
|
@@ -14684,7 +15041,7 @@ function App({
|
|
|
14684
15041
|
}
|
|
14685
15042
|
const bangCmd = detectBangCommand(text);
|
|
14686
15043
|
if (bangCmd !== null) {
|
|
14687
|
-
const bangRoot =
|
|
15044
|
+
const bangRoot = currentRootDir;
|
|
14688
15045
|
promptHistory.current.push(text);
|
|
14689
15046
|
setHistorical((prev) => [
|
|
14690
15047
|
...prev,
|
|
@@ -14747,10 +15104,10 @@ function App({
|
|
|
14747
15104
|
codeDiscard: codeMode ? codeDiscard : void 0,
|
|
14748
15105
|
codeHistory: codeMode ? codeHistory : void 0,
|
|
14749
15106
|
codeShowEdit: codeMode ? codeShowEdit : void 0,
|
|
14750
|
-
codeRoot: codeMode
|
|
15107
|
+
codeRoot: codeMode ? currentRootDir : void 0,
|
|
14751
15108
|
pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
|
|
14752
15109
|
toolHistory: () => toolHistoryRef.current,
|
|
14753
|
-
memoryRoot:
|
|
15110
|
+
memoryRoot: currentRootDir,
|
|
14754
15111
|
planMode,
|
|
14755
15112
|
setPlanMode: codeMode ? togglePlanMode : void 0,
|
|
14756
15113
|
clearPendingPlan: codeMode ? clearPendingPlan : void 0,
|
|
@@ -14774,10 +15131,11 @@ function App({
|
|
|
14774
15131
|
{ id: `sys-late-${Date.now()}-${Math.random()}`, role: "info", text: text2 }
|
|
14775
15132
|
]),
|
|
14776
15133
|
reloadHooks: () => {
|
|
14777
|
-
const fresh = loadHooks({ projectRoot: codeMode
|
|
15134
|
+
const fresh = loadHooks({ projectRoot: codeMode ? currentRootDir : void 0 });
|
|
14778
15135
|
setHookList(fresh);
|
|
14779
15136
|
return fresh.length;
|
|
14780
15137
|
},
|
|
15138
|
+
setCwd: (newRoot) => applyCwdChange(newRoot),
|
|
14781
15139
|
latestVersion,
|
|
14782
15140
|
refreshLatestVersion,
|
|
14783
15141
|
models: models2,
|
|
@@ -14858,7 +15216,7 @@ function App({
|
|
|
14858
15216
|
if (hookList.some((h) => h.event === "UserPromptSubmit")) {
|
|
14859
15217
|
const promptReport = await runHooks({
|
|
14860
15218
|
hooks: hookList,
|
|
14861
|
-
payload: { event: "UserPromptSubmit", cwd:
|
|
15219
|
+
payload: { event: "UserPromptSubmit", cwd: currentRootDir, prompt: text }
|
|
14862
15220
|
});
|
|
14863
15221
|
if (promptReport.outcomes.length > 0) {
|
|
14864
15222
|
setHistorical((prev) => [
|
|
@@ -14926,8 +15284,8 @@ function App({
|
|
|
14926
15284
|
};
|
|
14927
15285
|
const timer = PLAIN_UI ? null : setInterval(flush, FLUSH_INTERVAL_MS);
|
|
14928
15286
|
let modelInput = text;
|
|
14929
|
-
if (codeMode
|
|
14930
|
-
const expanded = expandAtMentions(text,
|
|
15287
|
+
if (codeMode) {
|
|
15288
|
+
const expanded = expandAtMentions(text, currentRootDir);
|
|
14931
15289
|
if (expanded.expansions.length > 0) {
|
|
14932
15290
|
modelInput = expanded.text;
|
|
14933
15291
|
const inlined = expanded.expansions.filter((ex) => ex.ok).map((ex) => `${ex.path} (${(ex.bytes ?? 0).toLocaleString()} bytes)`);
|
|
@@ -15058,8 +15416,8 @@ function App({
|
|
|
15058
15416
|
const blocks = parseEditBlocks(finalText);
|
|
15059
15417
|
if (blocks.length > 0) {
|
|
15060
15418
|
if (editModeRef.current === "auto" || editModeRef.current === "yolo") {
|
|
15061
|
-
const snaps = snapshotBeforeEdits(blocks,
|
|
15062
|
-
const results = applyEditBlocks(blocks,
|
|
15419
|
+
const snaps = snapshotBeforeEdits(blocks, currentRootDir);
|
|
15420
|
+
const results = applyEditBlocks(blocks, currentRootDir);
|
|
15063
15421
|
const good = results.some(
|
|
15064
15422
|
(r) => r.status === "applied" || r.status === "created"
|
|
15065
15423
|
);
|
|
@@ -15145,6 +15503,18 @@ function App({
|
|
|
15145
15503
|
} catch {
|
|
15146
15504
|
}
|
|
15147
15505
|
}
|
|
15506
|
+
if (ev.toolName === "change_workspace" && ev.content.includes('"WorkspaceConfirmationError:') && ev.toolArgs) {
|
|
15507
|
+
try {
|
|
15508
|
+
const parsed = JSON.parse(ev.toolArgs);
|
|
15509
|
+
if (typeof parsed.path === "string" && parsed.path.trim()) {
|
|
15510
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
15511
|
+
const expanded = parsed.path.startsWith("~") && home ? pathMod7.join(home, parsed.path.slice(1)) : parsed.path;
|
|
15512
|
+
const abs = pathMod7.resolve(expanded);
|
|
15513
|
+
setPendingWorkspace({ path: abs });
|
|
15514
|
+
}
|
|
15515
|
+
} catch {
|
|
15516
|
+
}
|
|
15517
|
+
}
|
|
15148
15518
|
if (codeMode && ev.toolName === "submit_plan" && ev.content.includes('"PlanProposedError:')) {
|
|
15149
15519
|
try {
|
|
15150
15520
|
const parsed = JSON.parse(ev.content);
|
|
@@ -15267,7 +15637,7 @@ function App({
|
|
|
15267
15637
|
hooks: hookList,
|
|
15268
15638
|
payload: {
|
|
15269
15639
|
event: "Stop",
|
|
15270
|
-
cwd:
|
|
15640
|
+
cwd: currentRootDir,
|
|
15271
15641
|
lastAssistantText: streamRef.text,
|
|
15272
15642
|
turn: loop2.stats.summary().turns
|
|
15273
15643
|
}
|
|
@@ -15305,8 +15675,8 @@ function App({
|
|
|
15305
15675
|
codeMode,
|
|
15306
15676
|
codeShowEdit,
|
|
15307
15677
|
codeUndo,
|
|
15678
|
+
currentRootDir,
|
|
15308
15679
|
exit2,
|
|
15309
|
-
hookCwd,
|
|
15310
15680
|
hookList,
|
|
15311
15681
|
loop2,
|
|
15312
15682
|
latestVersion,
|
|
@@ -15341,7 +15711,8 @@ function App({
|
|
|
15341
15711
|
stopLoop,
|
|
15342
15712
|
startLoop,
|
|
15343
15713
|
getLoopStatus,
|
|
15344
|
-
startWalkthrough
|
|
15714
|
+
startWalkthrough,
|
|
15715
|
+
applyCwdChange
|
|
15345
15716
|
]
|
|
15346
15717
|
);
|
|
15347
15718
|
useEffect6(() => {
|
|
@@ -15398,13 +15769,13 @@ function App({
|
|
|
15398
15769
|
} else {
|
|
15399
15770
|
if (choice === "always_allow") {
|
|
15400
15771
|
const prefix = derivePrefix(cmd);
|
|
15401
|
-
addProjectShellAllowed(
|
|
15772
|
+
addProjectShellAllowed(currentRootDir, prefix);
|
|
15402
15773
|
setHistorical((prev) => [
|
|
15403
15774
|
...prev,
|
|
15404
15775
|
{
|
|
15405
15776
|
id: `sh-allow-${Date.now()}`,
|
|
15406
15777
|
role: "info",
|
|
15407
|
-
text: `\u25B8 always allowed "${prefix}" for ${
|
|
15778
|
+
text: `\u25B8 always allowed "${prefix}" for ${currentRootDir}`
|
|
15408
15779
|
}
|
|
15409
15780
|
]);
|
|
15410
15781
|
}
|
|
@@ -15421,7 +15792,7 @@ function App({
|
|
|
15421
15792
|
let jobId = null;
|
|
15422
15793
|
let preview = "";
|
|
15423
15794
|
try {
|
|
15424
|
-
const res = await codeMode.jobs.start(cmd, { cwd:
|
|
15795
|
+
const res = await codeMode.jobs.start(cmd, { cwd: currentRootDir });
|
|
15425
15796
|
startedOk = true;
|
|
15426
15797
|
jobId = res.jobId;
|
|
15427
15798
|
preview = res.preview;
|
|
@@ -15455,7 +15826,7 @@ ${msg}`;
|
|
|
15455
15826
|
} else {
|
|
15456
15827
|
let body;
|
|
15457
15828
|
try {
|
|
15458
|
-
const res = await runCommand(cmd, { cwd:
|
|
15829
|
+
const res = await runCommand(cmd, { cwd: currentRootDir });
|
|
15459
15830
|
body = formatCommandResult(cmd, res);
|
|
15460
15831
|
} catch (err) {
|
|
15461
15832
|
body = `$ ${cmd}
|
|
@@ -15477,7 +15848,7 @@ ${body}`;
|
|
|
15477
15848
|
await handleSubmit(synthetic);
|
|
15478
15849
|
}
|
|
15479
15850
|
},
|
|
15480
|
-
[pendingShell, codeMode, handleSubmit, busy, loop2]
|
|
15851
|
+
[pendingShell, codeMode, currentRootDir, handleSubmit, busy, loop2]
|
|
15481
15852
|
);
|
|
15482
15853
|
useEffect6(() => {
|
|
15483
15854
|
if (!busy && queuedSubmit !== null) {
|
|
@@ -15486,6 +15857,40 @@ ${body}`;
|
|
|
15486
15857
|
void handleSubmit(text);
|
|
15487
15858
|
}
|
|
15488
15859
|
}, [busy, queuedSubmit, handleSubmit]);
|
|
15860
|
+
const handleWorkspaceConfirm = useCallback4(
|
|
15861
|
+
async (choice) => {
|
|
15862
|
+
const pending = pendingWorkspace;
|
|
15863
|
+
if (!pending) return;
|
|
15864
|
+
const target = pending.path;
|
|
15865
|
+
setPendingWorkspace(null);
|
|
15866
|
+
let synthetic;
|
|
15867
|
+
if (choice === "deny") {
|
|
15868
|
+
setHistorical((prev) => [
|
|
15869
|
+
...prev,
|
|
15870
|
+
{
|
|
15871
|
+
id: `ws-deny-${Date.now()}`,
|
|
15872
|
+
role: "info",
|
|
15873
|
+
text: `\u25B8 denied workspace switch: ${target}`
|
|
15874
|
+
}
|
|
15875
|
+
]);
|
|
15876
|
+
synthetic = `I denied switching the workspace to \`${target}\`. Please continue without changing directories.`;
|
|
15877
|
+
} else {
|
|
15878
|
+
const info = applyCwdChange(target);
|
|
15879
|
+
setHistorical((prev) => [
|
|
15880
|
+
...prev,
|
|
15881
|
+
{ id: `ws-switch-${Date.now()}`, role: "info", text: info }
|
|
15882
|
+
]);
|
|
15883
|
+
synthetic = `I approved the workspace switch. The session is now rooted at \`${target}\` \u2014 your filesystem / shell / memory tools resolve against that path on every subsequent call. Continue with my original request from this new root.`;
|
|
15884
|
+
}
|
|
15885
|
+
if (busy) {
|
|
15886
|
+
loop2.abort();
|
|
15887
|
+
setQueuedSubmit(synthetic);
|
|
15888
|
+
} else {
|
|
15889
|
+
await handleSubmit(synthetic);
|
|
15890
|
+
}
|
|
15891
|
+
},
|
|
15892
|
+
[pendingWorkspace, applyCwdChange, busy, loop2, handleSubmit]
|
|
15893
|
+
);
|
|
15489
15894
|
const handlePlanConfirm = useCallback4(
|
|
15490
15895
|
async (choice) => {
|
|
15491
15896
|
const hadPendingPlan = pendingPlan !== null;
|
|
@@ -15792,12 +16197,12 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15792
16197
|
async (choice) => handleReviseConfirmRef.current(choice),
|
|
15793
16198
|
[]
|
|
15794
16199
|
);
|
|
15795
|
-
return /* @__PURE__ */
|
|
16200
|
+
return /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
|
|
15796
16201
|
TickerProvider,
|
|
15797
16202
|
{
|
|
15798
|
-
disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingEditReview || walkthroughActive || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
|
|
16203
|
+
disabled: PLAIN_UI || isResizing || !!pendingPlan || !!pendingShell || !!pendingWorkspace || !!pendingEditReview || walkthroughActive || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
|
|
15799
16204
|
},
|
|
15800
|
-
/* @__PURE__ */
|
|
16205
|
+
/* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column" }, /* @__PURE__ */ React24.createElement(
|
|
15801
16206
|
StatsPanel,
|
|
15802
16207
|
{
|
|
15803
16208
|
summary,
|
|
@@ -15814,28 +16219,28 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15814
16219
|
proArmed,
|
|
15815
16220
|
escalated: turnOnPro
|
|
15816
16221
|
}
|
|
15817
|
-
), /* @__PURE__ */
|
|
16222
|
+
), /* @__PURE__ */ React24.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React24.createElement(EventRow, { key: item.id, event: item, projectRoot: currentRootDir })), !historical.some((e) => e.role === "user" || e.role === "assistant") && !busy && !streaming ? /* @__PURE__ */ React24.createElement(WelcomeBanner, { inCodeMode: !!codeMode }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React24.createElement(Box22, { marginY: 1 }, /* @__PURE__ */ React24.createElement(EventRow, { event: streaming, projectRoot: currentRootDir })) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React24.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React24.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React24.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React24.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingWorkspace && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React24.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React24.createElement(
|
|
15818
16223
|
PlanRefineInput,
|
|
15819
16224
|
{
|
|
15820
16225
|
mode: stagedInput.mode,
|
|
15821
16226
|
onSubmit: handleStagedInputSubmit,
|
|
15822
16227
|
onCancel: handleStagedInputCancel
|
|
15823
16228
|
}
|
|
15824
|
-
) : stagedCheckpointRevise ? /* @__PURE__ */
|
|
16229
|
+
) : stagedCheckpointRevise ? /* @__PURE__ */ React24.createElement(
|
|
15825
16230
|
PlanRefineInput,
|
|
15826
16231
|
{
|
|
15827
16232
|
mode: "checkpoint-revise",
|
|
15828
16233
|
onSubmit: handleCheckpointReviseSubmit,
|
|
15829
16234
|
onCancel: handleCheckpointReviseCancel
|
|
15830
16235
|
}
|
|
15831
|
-
) : stagedChoiceCustom ? /* @__PURE__ */
|
|
16236
|
+
) : stagedChoiceCustom ? /* @__PURE__ */ React24.createElement(
|
|
15832
16237
|
PlanRefineInput,
|
|
15833
16238
|
{
|
|
15834
16239
|
mode: "choice-custom",
|
|
15835
16240
|
onSubmit: handleChoiceCustomSubmit,
|
|
15836
16241
|
onCancel: handleChoiceCustomCancel
|
|
15837
16242
|
}
|
|
15838
|
-
) : pendingChoice ? /* @__PURE__ */
|
|
16243
|
+
) : pendingChoice ? /* @__PURE__ */ React24.createElement(
|
|
15839
16244
|
ChoiceConfirm,
|
|
15840
16245
|
{
|
|
15841
16246
|
question: pendingChoice.question,
|
|
@@ -15843,7 +16248,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15843
16248
|
allowCustom: pendingChoice.allowCustom,
|
|
15844
16249
|
onChoose: stableHandleChoiceConfirm
|
|
15845
16250
|
}
|
|
15846
|
-
) : pendingRevision ? /* @__PURE__ */
|
|
16251
|
+
) : pendingRevision ? /* @__PURE__ */ React24.createElement(
|
|
15847
16252
|
PlanReviseConfirm,
|
|
15848
16253
|
{
|
|
15849
16254
|
reason: pendingRevision.reason,
|
|
@@ -15854,7 +16259,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15854
16259
|
summary: pendingRevision.summary,
|
|
15855
16260
|
onChoose: stableHandleReviseConfirm
|
|
15856
16261
|
}
|
|
15857
|
-
) : pendingCheckpoint ? /* @__PURE__ */
|
|
16262
|
+
) : pendingCheckpoint ? /* @__PURE__ */ React24.createElement(
|
|
15858
16263
|
PlanCheckpointConfirm,
|
|
15859
16264
|
{
|
|
15860
16265
|
stepId: pendingCheckpoint.stepId,
|
|
@@ -15865,16 +16270,16 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15865
16270
|
completedStepIds: completedStepIdsRef.current,
|
|
15866
16271
|
onChoose: stableHandleCheckpointConfirm
|
|
15867
16272
|
}
|
|
15868
|
-
) : pendingPlan ? /* @__PURE__ */
|
|
16273
|
+
) : pendingPlan ? /* @__PURE__ */ React24.createElement(
|
|
15869
16274
|
PlanConfirm,
|
|
15870
16275
|
{
|
|
15871
16276
|
plan: pendingPlan,
|
|
15872
16277
|
steps: planStepsRef.current ?? void 0,
|
|
15873
16278
|
summary: planSummaryRef.current ?? void 0,
|
|
15874
16279
|
onChoose: stableHandlePlanConfirm,
|
|
15875
|
-
projectRoot:
|
|
16280
|
+
projectRoot: currentRootDir
|
|
15876
16281
|
}
|
|
15877
|
-
) : pendingShell ? /* @__PURE__ */
|
|
16282
|
+
) : pendingShell ? /* @__PURE__ */ React24.createElement(
|
|
15878
16283
|
ShellConfirm,
|
|
15879
16284
|
{
|
|
15880
16285
|
command: pendingShell.command,
|
|
@@ -15882,26 +16287,34 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15882
16287
|
kind: pendingShell.kind,
|
|
15883
16288
|
onChoose: handleShellConfirm
|
|
15884
16289
|
}
|
|
15885
|
-
) :
|
|
16290
|
+
) : pendingWorkspace ? /* @__PURE__ */ React24.createElement(
|
|
16291
|
+
WorkspaceConfirm,
|
|
16292
|
+
{
|
|
16293
|
+
path: pendingWorkspace.path,
|
|
16294
|
+
currentRoot: currentRootDir,
|
|
16295
|
+
mcpServerCount: mcpServers?.length ?? 0,
|
|
16296
|
+
onChoose: handleWorkspaceConfirm
|
|
16297
|
+
}
|
|
16298
|
+
) : pendingEditReview ? /* @__PURE__ */ React24.createElement(
|
|
15886
16299
|
EditConfirm,
|
|
15887
16300
|
{
|
|
15888
16301
|
block: pendingEditReview,
|
|
15889
16302
|
onChoose: (choice) => {
|
|
15890
|
-
const
|
|
15891
|
-
if (
|
|
16303
|
+
const resolve12 = editReviewResolveRef.current;
|
|
16304
|
+
if (resolve12) {
|
|
15892
16305
|
editReviewResolveRef.current = null;
|
|
15893
|
-
|
|
16306
|
+
resolve12(choice);
|
|
15894
16307
|
}
|
|
15895
16308
|
}
|
|
15896
16309
|
}
|
|
15897
|
-
) : walkthroughActive && pendingEdits.current.length > 0 ? /* @__PURE__ */
|
|
16310
|
+
) : walkthroughActive && pendingEdits.current.length > 0 ? /* @__PURE__ */ React24.createElement(
|
|
15898
16311
|
EditConfirm,
|
|
15899
16312
|
{
|
|
15900
16313
|
key: `walk-${pendingTick}`,
|
|
15901
16314
|
block: pendingEdits.current[0],
|
|
15902
16315
|
onChoose: handleWalkChoice
|
|
15903
16316
|
}
|
|
15904
|
-
) : /* @__PURE__ */
|
|
16317
|
+
) : /* @__PURE__ */ React24.createElement(React24.Fragment, null, codeMode ? /* @__PURE__ */ React24.createElement(
|
|
15905
16318
|
ModeStatusBar,
|
|
15906
16319
|
{
|
|
15907
16320
|
editMode,
|
|
@@ -15911,7 +16324,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15911
16324
|
undoArmed: !!undoBanner || hasUndoable(),
|
|
15912
16325
|
jobs: codeMode.jobs
|
|
15913
16326
|
}
|
|
15914
|
-
) : null, activeLoop ? /* @__PURE__ */
|
|
16327
|
+
) : null, activeLoop ? /* @__PURE__ */ React24.createElement(LoopStatusRow, { loop: activeLoop }) : null, /* @__PURE__ */ React24.createElement(
|
|
15915
16328
|
PromptInput,
|
|
15916
16329
|
{
|
|
15917
16330
|
value: input,
|
|
@@ -15921,14 +16334,14 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15921
16334
|
onHistoryPrev: recallPrev,
|
|
15922
16335
|
onHistoryNext: recallNext
|
|
15923
16336
|
}
|
|
15924
|
-
), /* @__PURE__ */
|
|
16337
|
+
), /* @__PURE__ */ React24.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React24.createElement(
|
|
15925
16338
|
AtMentionSuggestions,
|
|
15926
16339
|
{
|
|
15927
16340
|
matches: atMatches,
|
|
15928
16341
|
selectedIndex: atSelected,
|
|
15929
16342
|
query: atPicker?.query ?? ""
|
|
15930
16343
|
}
|
|
15931
|
-
), slashArgContext ? /* @__PURE__ */
|
|
16344
|
+
), slashArgContext ? /* @__PURE__ */ React24.createElement(
|
|
15932
16345
|
SlashArgPicker,
|
|
15933
16346
|
{
|
|
15934
16347
|
matches: slashArgMatches,
|
|
@@ -15942,15 +16355,15 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
15942
16355
|
}
|
|
15943
16356
|
|
|
15944
16357
|
// src/cli/ui/SessionPicker.tsx
|
|
15945
|
-
import { Box as
|
|
15946
|
-
import
|
|
16358
|
+
import { Box as Box23, Text as Text21 } from "ink";
|
|
16359
|
+
import React25 from "react";
|
|
15947
16360
|
function SessionPicker({
|
|
15948
16361
|
sessionName,
|
|
15949
16362
|
messageCount,
|
|
15950
16363
|
lastActive,
|
|
15951
16364
|
onChoose
|
|
15952
16365
|
}) {
|
|
15953
|
-
return /* @__PURE__ */
|
|
16366
|
+
return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React25.createElement(Box23, { marginBottom: 1 }, /* @__PURE__ */ React25.createElement(Text21, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React25.createElement(
|
|
15954
16367
|
SingleSelect,
|
|
15955
16368
|
{
|
|
15956
16369
|
initialValue: "new",
|
|
@@ -15973,7 +16386,7 @@ function SessionPicker({
|
|
|
15973
16386
|
],
|
|
15974
16387
|
onSubmit: (v) => onChoose(v)
|
|
15975
16388
|
}
|
|
15976
|
-
), /* @__PURE__ */
|
|
16389
|
+
), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text21, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
|
|
15977
16390
|
}
|
|
15978
16391
|
function relativeTime2(date) {
|
|
15979
16392
|
const ms = Date.now() - date.getTime();
|
|
@@ -15989,9 +16402,9 @@ function relativeTime2(date) {
|
|
|
15989
16402
|
}
|
|
15990
16403
|
|
|
15991
16404
|
// src/cli/ui/Setup.tsx
|
|
15992
|
-
import { Box as
|
|
16405
|
+
import { Box as Box24, Text as Text22, useApp as useApp2 } from "ink";
|
|
15993
16406
|
import TextInput from "ink-text-input";
|
|
15994
|
-
import
|
|
16407
|
+
import React26, { useState as useState11 } from "react";
|
|
15995
16408
|
function Setup({ onReady }) {
|
|
15996
16409
|
const [value, setValue] = useState11("");
|
|
15997
16410
|
const [error, setError] = useState11(null);
|
|
@@ -16015,7 +16428,7 @@ function Setup({ onReady }) {
|
|
|
16015
16428
|
}
|
|
16016
16429
|
onReady(trimmed);
|
|
16017
16430
|
};
|
|
16018
|
-
return /* @__PURE__ */
|
|
16431
|
+
return /* @__PURE__ */ React26.createElement(Box24, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React26.createElement(
|
|
16019
16432
|
TextInput,
|
|
16020
16433
|
{
|
|
16021
16434
|
value,
|
|
@@ -16024,7 +16437,7 @@ function Setup({ onReady }) {
|
|
|
16024
16437
|
mask: "\u2022",
|
|
16025
16438
|
placeholder: "sk-..."
|
|
16026
16439
|
}
|
|
16027
|
-
)), error ? /* @__PURE__ */
|
|
16440
|
+
)), error ? /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { color: "red" }, error)) : value ? /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React26.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "(Type /exit to abort.)")));
|
|
16028
16441
|
}
|
|
16029
16442
|
|
|
16030
16443
|
// src/cli/commands/chat.tsx
|
|
@@ -16040,7 +16453,7 @@ function Root({
|
|
|
16040
16453
|
const [key, setKey] = useState12(initialKey);
|
|
16041
16454
|
const [pending, setPending] = useState12(sessionPreview);
|
|
16042
16455
|
if (!key) {
|
|
16043
|
-
return /* @__PURE__ */
|
|
16456
|
+
return /* @__PURE__ */ React27.createElement(
|
|
16044
16457
|
Setup,
|
|
16045
16458
|
{
|
|
16046
16459
|
onReady: (k) => {
|
|
@@ -16052,7 +16465,7 @@ function Root({
|
|
|
16052
16465
|
}
|
|
16053
16466
|
process.env.DEEPSEEK_API_KEY = key;
|
|
16054
16467
|
if (pending && appProps.session) {
|
|
16055
|
-
return /* @__PURE__ */
|
|
16468
|
+
return /* @__PURE__ */ React27.createElement(KeystrokeProvider, null, /* @__PURE__ */ React27.createElement(
|
|
16056
16469
|
SessionPicker,
|
|
16057
16470
|
{
|
|
16058
16471
|
sessionName: appProps.session,
|
|
@@ -16067,7 +16480,7 @@ function Root({
|
|
|
16067
16480
|
}
|
|
16068
16481
|
));
|
|
16069
16482
|
}
|
|
16070
|
-
return /* @__PURE__ */
|
|
16483
|
+
return /* @__PURE__ */ React27.createElement(KeystrokeProvider, null, /* @__PURE__ */ React27.createElement(
|
|
16071
16484
|
App,
|
|
16072
16485
|
{
|
|
16073
16486
|
model: appProps.model,
|
|
@@ -16165,14 +16578,14 @@ async function chatCommand(opts) {
|
|
|
16165
16578
|
const prior = loadSessionMessages(opts.session);
|
|
16166
16579
|
if (prior.length > 0) {
|
|
16167
16580
|
const p = sessionPath(opts.session);
|
|
16168
|
-
const mtime =
|
|
16581
|
+
const mtime = existsSync16(p) ? statSync9(p).mtime : /* @__PURE__ */ new Date();
|
|
16169
16582
|
sessionPreview = { messageCount: prior.length, lastActive: mtime };
|
|
16170
16583
|
}
|
|
16171
16584
|
} else if (opts.session && opts.forceNew) {
|
|
16172
16585
|
rewriteSession(opts.session, []);
|
|
16173
16586
|
}
|
|
16174
16587
|
const { waitUntilExit } = render(
|
|
16175
|
-
/* @__PURE__ */
|
|
16588
|
+
/* @__PURE__ */ React27.createElement(
|
|
16176
16589
|
Root,
|
|
16177
16590
|
{
|
|
16178
16591
|
initialKey,
|
|
@@ -16196,7 +16609,7 @@ async function chatCommand(opts) {
|
|
|
16196
16609
|
}
|
|
16197
16610
|
|
|
16198
16611
|
// src/cli/commands/code.tsx
|
|
16199
|
-
import { basename as basename2, resolve as
|
|
16612
|
+
import { basename as basename2, resolve as resolve10 } from "path";
|
|
16200
16613
|
|
|
16201
16614
|
// src/index/semantic/builder.ts
|
|
16202
16615
|
import { promises as fs5 } from "fs";
|
|
@@ -16843,29 +17256,32 @@ async function bootstrapSemanticSearchInCodeMode(registry, rootDir, opts = {}) {
|
|
|
16843
17256
|
|
|
16844
17257
|
// src/cli/commands/code.tsx
|
|
16845
17258
|
async function codeCommand(opts = {}) {
|
|
16846
|
-
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-
|
|
16847
|
-
const rootDir =
|
|
17259
|
+
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-YRY4HPMZ.js");
|
|
17260
|
+
const rootDir = resolve10(opts.dir ?? process.cwd());
|
|
16848
17261
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename2(rootDir))}`;
|
|
16849
17262
|
const tools = new ToolRegistry();
|
|
16850
|
-
registerFilesystemTools(tools, { rootDir });
|
|
16851
17263
|
const jobs2 = new JobRegistry();
|
|
16852
|
-
|
|
16853
|
-
rootDir
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
17264
|
+
const registerRootedTools = (root) => {
|
|
17265
|
+
registerFilesystemTools(tools, { rootDir: root });
|
|
17266
|
+
registerShellTools(tools, {
|
|
17267
|
+
rootDir: root,
|
|
17268
|
+
// Per-project "always allow" list persisted from prior ShellConfirm
|
|
17269
|
+
// choices; merged on top of the built-in allowlist in shell.ts.
|
|
17270
|
+
// GETTER form — re-read every dispatch so a prefix the user adds
|
|
17271
|
+
// via ShellConfirm mid-session takes effect on the next shell call
|
|
17272
|
+
// instead of waiting for `/new` or a relaunch.
|
|
17273
|
+
extraAllowed: () => loadProjectShellAllowed(root),
|
|
17274
|
+
// `yolo` edit-mode disables shell confirmations entirely. Re-read
|
|
17275
|
+
// from config on each dispatch so /mode yolo (or Shift+Tab cycling
|
|
17276
|
+
// through to it) flips the gate live without forcing a relaunch.
|
|
17277
|
+
allowAll: () => loadEditMode() === "yolo",
|
|
17278
|
+
jobs: jobs2
|
|
17279
|
+
});
|
|
17280
|
+
registerMemoryTools(tools, { projectRoot: root });
|
|
17281
|
+
};
|
|
17282
|
+
registerRootedTools(rootDir);
|
|
16866
17283
|
registerPlanTool(tools);
|
|
16867
17284
|
registerChoiceTool(tools);
|
|
16868
|
-
registerMemoryTools(tools, { projectRoot: rootDir });
|
|
16869
17285
|
const semantic2 = await bootstrapSemanticSearchInCodeMode(tools, rootDir);
|
|
16870
17286
|
process.stderr.write(
|
|
16871
17287
|
`\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)${semantic2.enabled ? " \xB7 semantic_search on" : ""}
|
|
@@ -16881,7 +17297,7 @@ async function codeCommand(opts = {}) {
|
|
|
16881
17297
|
transcript: opts.transcript,
|
|
16882
17298
|
session,
|
|
16883
17299
|
seedTools: tools,
|
|
16884
|
-
codeMode: { rootDir, jobs: jobs2 },
|
|
17300
|
+
codeMode: { rootDir, jobs: jobs2, reregisterTools: registerRootedTools },
|
|
16885
17301
|
forceResume: opts.forceResume,
|
|
16886
17302
|
forceNew: opts.forceNew
|
|
16887
17303
|
});
|
|
@@ -16891,35 +17307,35 @@ async function codeCommand(opts = {}) {
|
|
|
16891
17307
|
import { writeFileSync as writeFileSync8 } from "fs";
|
|
16892
17308
|
import { basename as basename3 } from "path";
|
|
16893
17309
|
import { render as render2 } from "ink";
|
|
16894
|
-
import
|
|
17310
|
+
import React30 from "react";
|
|
16895
17311
|
|
|
16896
17312
|
// src/cli/ui/DiffApp.tsx
|
|
16897
|
-
import { Box as
|
|
16898
|
-
import
|
|
17313
|
+
import { Box as Box26, Static as Static2, Text as Text24, useApp as useApp3, useInput } from "ink";
|
|
17314
|
+
import React29, { useState as useState13 } from "react";
|
|
16899
17315
|
|
|
16900
17316
|
// src/cli/ui/RecordView.tsx
|
|
16901
|
-
import { Box as
|
|
16902
|
-
import
|
|
17317
|
+
import { Box as Box25, Text as Text23 } from "ink";
|
|
17318
|
+
import React28 from "react";
|
|
16903
17319
|
function RecordView({ rec, compact: compact2 = false }) {
|
|
16904
17320
|
const toolArgsMax = compact2 ? 120 : 200;
|
|
16905
17321
|
const toolContentMax = compact2 ? 200 : 400;
|
|
16906
17322
|
if (rec.role === "user") {
|
|
16907
17323
|
const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
|
|
16908
|
-
return /* @__PURE__ */
|
|
17324
|
+
return /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React28.createElement(Text23, null, content));
|
|
16909
17325
|
}
|
|
16910
17326
|
if (rec.role === "assistant_final") {
|
|
16911
|
-
return /* @__PURE__ */
|
|
17327
|
+
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Box25, null, /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React28.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React28.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React28.createElement(Text23, null, rec.content) : /* @__PURE__ */ React28.createElement(Text23, { dimColor: true, italic: true }, "(tool-call response only)"));
|
|
16912
17328
|
}
|
|
16913
17329
|
if (rec.role === "tool") {
|
|
16914
|
-
return /* @__PURE__ */
|
|
17330
|
+
return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
|
|
16915
17331
|
}
|
|
16916
17332
|
if (rec.role === "error") {
|
|
16917
|
-
return /* @__PURE__ */
|
|
17333
|
+
return /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React28.createElement(Text23, { color: "red" }, rec.error ?? rec.content));
|
|
16918
17334
|
}
|
|
16919
17335
|
if (rec.role === "done" || rec.role === "assistant_delta") {
|
|
16920
17336
|
return null;
|
|
16921
17337
|
}
|
|
16922
|
-
return /* @__PURE__ */
|
|
17338
|
+
return /* @__PURE__ */ React28.createElement(Box25, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, "[", rec.role, "] ", rec.content));
|
|
16923
17339
|
}
|
|
16924
17340
|
function CacheBadge({ usage }) {
|
|
16925
17341
|
const hit = usage.prompt_cache_hit_tokens ?? 0;
|
|
@@ -16928,7 +17344,7 @@ function CacheBadge({ usage }) {
|
|
|
16928
17344
|
if (total === 0) return null;
|
|
16929
17345
|
const pct2 = hit / total * 100;
|
|
16930
17346
|
const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
|
|
16931
|
-
return /* @__PURE__ */
|
|
17347
|
+
return /* @__PURE__ */ React28.createElement(Text23, null, /* @__PURE__ */ React28.createElement(Text23, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React28.createElement(Text23, { color }, pct2.toFixed(1), "%"));
|
|
16932
17348
|
}
|
|
16933
17349
|
function truncate2(s, max) {
|
|
16934
17350
|
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
|
|
@@ -16962,7 +17378,7 @@ function DiffApp({ report }) {
|
|
|
16962
17378
|
}
|
|
16963
17379
|
});
|
|
16964
17380
|
const pair = report.pairs[idx];
|
|
16965
|
-
return /* @__PURE__ */
|
|
17381
|
+
return /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React29.createElement(DiffHeader, { report }), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React29.createElement(Text24, null, pair ? /* @__PURE__ */ React29.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React29.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React29.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React29.createElement(Text24, null, pair.divergenceNote)) : null, /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "j"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "k"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "N"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "g"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "q"), " ", "quit")));
|
|
16966
17382
|
}
|
|
16967
17383
|
function DiffHeader({ report }) {
|
|
16968
17384
|
const a = report.a;
|
|
@@ -16980,15 +17396,15 @@ function DiffHeader({ report }) {
|
|
|
16980
17396
|
} else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
|
|
16981
17397
|
prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
|
|
16982
17398
|
}
|
|
16983
|
-
return /* @__PURE__ */
|
|
17399
|
+
return /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React29.createElement(Text24, { color: "blue" }, a.label), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " vs B="), /* @__PURE__ */ React29.createElement(Text24, { color: "magenta" }, b.label)), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "cache "), /* @__PURE__ */ React29.createElement(Text24, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React29.createElement(Text24, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React29.createElement(Text24, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "cost "), /* @__PURE__ */ React29.createElement(Text24, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React29.createElement(Text24, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React29.createElement(Text24, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React29.createElement(Text24, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "model calls "), /* @__PURE__ */ React29.createElement(Text24, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true, italic: true }, prefixLine)) : null);
|
|
16984
17400
|
}
|
|
16985
17401
|
function Pane({
|
|
16986
17402
|
label,
|
|
16987
17403
|
headerColor,
|
|
16988
17404
|
records
|
|
16989
17405
|
}) {
|
|
16990
|
-
return /* @__PURE__ */
|
|
16991
|
-
|
|
17406
|
+
return /* @__PURE__ */ React29.createElement(
|
|
17407
|
+
Box26,
|
|
16992
17408
|
{
|
|
16993
17409
|
flexDirection: "column",
|
|
16994
17410
|
flexGrow: 1,
|
|
@@ -16996,21 +17412,21 @@ function Pane({
|
|
|
16996
17412
|
borderStyle: "single",
|
|
16997
17413
|
borderColor: headerColor
|
|
16998
17414
|
},
|
|
16999
|
-
/* @__PURE__ */
|
|
17000
|
-
records.length === 0 ? /* @__PURE__ */
|
|
17415
|
+
/* @__PURE__ */ React29.createElement(Text24, { color: headerColor, bold: true }, label),
|
|
17416
|
+
records.length === 0 ? /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React29.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React29.createElement(RecordView, { key, rec, compact: true }))
|
|
17001
17417
|
);
|
|
17002
17418
|
}
|
|
17003
17419
|
function KindBadge({ kind }) {
|
|
17004
17420
|
if (kind === "match") {
|
|
17005
|
-
return /* @__PURE__ */
|
|
17421
|
+
return /* @__PURE__ */ React29.createElement(Text24, { color: "green" }, "\u2713 match");
|
|
17006
17422
|
}
|
|
17007
17423
|
if (kind === "diverge") {
|
|
17008
|
-
return /* @__PURE__ */
|
|
17424
|
+
return /* @__PURE__ */ React29.createElement(Text24, { color: "yellow" }, "\u2605 diverge");
|
|
17009
17425
|
}
|
|
17010
17426
|
if (kind === "only_in_a") {
|
|
17011
|
-
return /* @__PURE__ */
|
|
17427
|
+
return /* @__PURE__ */ React29.createElement(Text24, { color: "blue" }, "\u2190 only in A");
|
|
17012
17428
|
}
|
|
17013
|
-
return /* @__PURE__ */
|
|
17429
|
+
return /* @__PURE__ */ React29.createElement(Text24, { color: "magenta" }, "\u2192 only in B");
|
|
17014
17430
|
}
|
|
17015
17431
|
function paneRecords(pair, side) {
|
|
17016
17432
|
if (!pair) return [];
|
|
@@ -17041,7 +17457,7 @@ markdown report written to ${opts.mdPath}`);
|
|
|
17041
17457
|
return;
|
|
17042
17458
|
}
|
|
17043
17459
|
if (wantTui) {
|
|
17044
|
-
const { waitUntilExit } = render2(
|
|
17460
|
+
const { waitUntilExit } = render2(React30.createElement(DiffApp, { report }), {
|
|
17045
17461
|
exitOnCtrlC: true,
|
|
17046
17462
|
patchConsole: false
|
|
17047
17463
|
});
|
|
@@ -17052,7 +17468,7 @@ markdown report written to ${opts.mdPath}`);
|
|
|
17052
17468
|
}
|
|
17053
17469
|
|
|
17054
17470
|
// src/cli/commands/index.ts
|
|
17055
|
-
import { resolve as
|
|
17471
|
+
import { resolve as resolve11 } from "path";
|
|
17056
17472
|
|
|
17057
17473
|
// src/index/semantic/preflight.ts
|
|
17058
17474
|
import { stdin as stdin2, stdout } from "process";
|
|
@@ -17126,7 +17542,7 @@ async function confirm(question, defaultYes) {
|
|
|
17126
17542
|
|
|
17127
17543
|
// src/cli/commands/index.ts
|
|
17128
17544
|
async function indexCommand(opts = {}) {
|
|
17129
|
-
const root =
|
|
17545
|
+
const root = resolve11(opts.dir ?? process.cwd());
|
|
17130
17546
|
const tty = process.stderr.isTTY === true && process.stdin.isTTY === true;
|
|
17131
17547
|
const model2 = opts.model ?? process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text";
|
|
17132
17548
|
const preflightOk = await ollamaPreflight({
|
|
@@ -17376,11 +17792,11 @@ function pad2(s, width) {
|
|
|
17376
17792
|
|
|
17377
17793
|
// src/cli/commands/replay.ts
|
|
17378
17794
|
import { render as render3 } from "ink";
|
|
17379
|
-
import
|
|
17795
|
+
import React32 from "react";
|
|
17380
17796
|
|
|
17381
17797
|
// src/cli/ui/ReplayApp.tsx
|
|
17382
|
-
import { Box as
|
|
17383
|
-
import
|
|
17798
|
+
import { Box as Box27, Static as Static3, Text as Text25, useApp as useApp4, useInput as useInput2 } from "ink";
|
|
17799
|
+
import React31, { useMemo as useMemo4, useState as useState14 } from "react";
|
|
17384
17800
|
function ReplayApp({ meta, pages }) {
|
|
17385
17801
|
const { exit: exit2 } = useApp4();
|
|
17386
17802
|
const maxIdx = Math.max(0, pages.length - 1);
|
|
@@ -17420,14 +17836,14 @@ function ReplayApp({ meta, pages }) {
|
|
|
17420
17836
|
const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
|
|
17421
17837
|
const currentPage = pages[idx];
|
|
17422
17838
|
const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
|
|
17423
|
-
return /* @__PURE__ */
|
|
17839
|
+
return /* @__PURE__ */ React31.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React31.createElement(
|
|
17424
17840
|
StatsPanel,
|
|
17425
17841
|
{
|
|
17426
17842
|
summary,
|
|
17427
17843
|
model: cumStats.models[0] ?? meta?.model ?? "?",
|
|
17428
17844
|
prefixHash
|
|
17429
17845
|
}
|
|
17430
|
-
), /* @__PURE__ */
|
|
17846
|
+
), /* @__PURE__ */ React31.createElement(Box27, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React31.createElement(Box27, { justifyContent: "space-between" }, /* @__PURE__ */ React31.createElement(Text25, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React31.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React31.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React31.createElement(Text25, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React31.createElement(Box27, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "j"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "k"), "/", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React31.createElement(Text25, { bold: true }, "q"), " quit")));
|
|
17431
17847
|
}
|
|
17432
17848
|
|
|
17433
17849
|
// src/cli/commands/replay.ts
|
|
@@ -17439,7 +17855,7 @@ async function replayCommand(opts) {
|
|
|
17439
17855
|
}
|
|
17440
17856
|
const { parsed } = replayFromFile(opts.path);
|
|
17441
17857
|
const pages = groupRecordsByTurn(parsed.records);
|
|
17442
|
-
const { waitUntilExit } = render3(
|
|
17858
|
+
const { waitUntilExit } = render3(React32.createElement(ReplayApp, { meta: parsed.meta, pages }), {
|
|
17443
17859
|
exitOnCtrlC: true,
|
|
17444
17860
|
patchConsole: false
|
|
17445
17861
|
});
|
|
@@ -17744,12 +18160,12 @@ function truncate3(s, max) {
|
|
|
17744
18160
|
|
|
17745
18161
|
// src/cli/commands/setup.tsx
|
|
17746
18162
|
import { render as render4 } from "ink";
|
|
17747
|
-
import
|
|
18163
|
+
import React34 from "react";
|
|
17748
18164
|
|
|
17749
18165
|
// src/cli/ui/Wizard.tsx
|
|
17750
|
-
import { Box as
|
|
18166
|
+
import { Box as Box28, Text as Text26, useApp as useApp5, useInput as useInput3 } from "ink";
|
|
17751
18167
|
import TextInput2 from "ink-text-input";
|
|
17752
|
-
import
|
|
18168
|
+
import React33, { useState as useState15 } from "react";
|
|
17753
18169
|
|
|
17754
18170
|
// src/cli/ui/presets.ts
|
|
17755
18171
|
var PRESETS = {
|
|
@@ -17797,7 +18213,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17797
18213
|
if (key.escape && step !== "saved" && onCancel) onCancel();
|
|
17798
18214
|
});
|
|
17799
18215
|
if (step === "apiKey") {
|
|
17800
|
-
return /* @__PURE__ */
|
|
18216
|
+
return /* @__PURE__ */ React33.createElement(
|
|
17801
18217
|
ApiKeyStep,
|
|
17802
18218
|
{
|
|
17803
18219
|
onSubmit: (key) => {
|
|
@@ -17811,7 +18227,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17811
18227
|
);
|
|
17812
18228
|
}
|
|
17813
18229
|
if (step === "preset") {
|
|
17814
|
-
return /* @__PURE__ */
|
|
18230
|
+
return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React33.createElement(
|
|
17815
18231
|
SingleSelect,
|
|
17816
18232
|
{
|
|
17817
18233
|
items: presetItems(),
|
|
@@ -17821,10 +18237,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17821
18237
|
setStep("mcp");
|
|
17822
18238
|
}
|
|
17823
18239
|
}
|
|
17824
|
-
), /* @__PURE__ */
|
|
18240
|
+
), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
|
|
17825
18241
|
}
|
|
17826
18242
|
if (step === "mcp") {
|
|
17827
|
-
return /* @__PURE__ */
|
|
18243
|
+
return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React33.createElement(
|
|
17828
18244
|
MultiSelect,
|
|
17829
18245
|
{
|
|
17830
18246
|
items: mcpItems(),
|
|
@@ -17849,7 +18265,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17849
18265
|
}
|
|
17850
18266
|
const currentName = pending[0];
|
|
17851
18267
|
const entry = CATALOG_BY_NAME.get(currentName);
|
|
17852
|
-
return /* @__PURE__ */
|
|
18268
|
+
return /* @__PURE__ */ React33.createElement(
|
|
17853
18269
|
McpArgsStep,
|
|
17854
18270
|
{
|
|
17855
18271
|
entry,
|
|
@@ -17867,7 +18283,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17867
18283
|
}
|
|
17868
18284
|
if (step === "review") {
|
|
17869
18285
|
const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
|
|
17870
|
-
return /* @__PURE__ */
|
|
18286
|
+
return /* @__PURE__ */ React33.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column" }, /* @__PURE__ */ React33.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React33.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React33.createElement(
|
|
17871
18287
|
SummaryLine,
|
|
17872
18288
|
{
|
|
17873
18289
|
label: "MCP",
|
|
@@ -17875,8 +18291,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17875
18291
|
}
|
|
17876
18292
|
), specs.map((spec, i) => (
|
|
17877
18293
|
// biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
|
|
17878
|
-
/* @__PURE__ */
|
|
17879
|
-
)), /* @__PURE__ */
|
|
18294
|
+
/* @__PURE__ */ React33.createElement(Box28, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "\xB7 ", spec))
|
|
18295
|
+
)), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : null, /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React33.createElement(
|
|
17880
18296
|
ReviewConfirm,
|
|
17881
18297
|
{
|
|
17882
18298
|
onConfirm: () => {
|
|
@@ -17902,7 +18318,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
17902
18318
|
}
|
|
17903
18319
|
));
|
|
17904
18320
|
}
|
|
17905
|
-
return /* @__PURE__ */
|
|
18321
|
+
return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React33.createElement(ExitOnEnter, { onExit: exit2 }));
|
|
17906
18322
|
}
|
|
17907
18323
|
function ApiKeyStep({
|
|
17908
18324
|
onSubmit,
|
|
@@ -17910,7 +18326,7 @@ function ApiKeyStep({
|
|
|
17910
18326
|
onError
|
|
17911
18327
|
}) {
|
|
17912
18328
|
const [value, setValue] = useState15("");
|
|
17913
|
-
return /* @__PURE__ */
|
|
18329
|
+
return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React33.createElement(
|
|
17914
18330
|
TextInput2,
|
|
17915
18331
|
{
|
|
17916
18332
|
value,
|
|
@@ -17927,7 +18343,7 @@ function ApiKeyStep({
|
|
|
17927
18343
|
mask: "\u2022",
|
|
17928
18344
|
placeholder: "sk-..."
|
|
17929
18345
|
}
|
|
17930
|
-
)), error ? /* @__PURE__ */
|
|
18346
|
+
)), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : value ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "preview: ", redactKey(value))) : null);
|
|
17931
18347
|
}
|
|
17932
18348
|
function McpArgsStep({
|
|
17933
18349
|
entry,
|
|
@@ -17936,7 +18352,7 @@ function McpArgsStep({
|
|
|
17936
18352
|
onError
|
|
17937
18353
|
}) {
|
|
17938
18354
|
const [value, setValue] = useState15("");
|
|
17939
|
-
return /* @__PURE__ */
|
|
18355
|
+
return /* @__PURE__ */ React33.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column" }, /* @__PURE__ */ React33.createElement(Text26, null, entry.summary), entry.note ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, null, "Required parameter: "), /* @__PURE__ */ React33.createElement(Text26, { bold: true }, entry.userArgs)), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React33.createElement(
|
|
17940
18356
|
TextInput2,
|
|
17941
18357
|
{
|
|
17942
18358
|
value,
|
|
@@ -17952,7 +18368,7 @@ function McpArgsStep({
|
|
|
17952
18368
|
},
|
|
17953
18369
|
placeholder: placeholderFor(entry)
|
|
17954
18370
|
}
|
|
17955
|
-
)), error ? /* @__PURE__ */
|
|
18371
|
+
)), error ? /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React33.createElement(Text26, { color: "red" }, error)) : null));
|
|
17956
18372
|
}
|
|
17957
18373
|
function ReviewConfirm({ onConfirm }) {
|
|
17958
18374
|
useInput3((_i, key) => {
|
|
@@ -17972,10 +18388,10 @@ function StepFrame({
|
|
|
17972
18388
|
total,
|
|
17973
18389
|
children
|
|
17974
18390
|
}) {
|
|
17975
|
-
return /* @__PURE__ */
|
|
18391
|
+
return /* @__PURE__ */ React33.createElement(Box28, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React33.createElement(Box28, null, /* @__PURE__ */ React33.createElement(Text26, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React33.createElement(Text26, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React33.createElement(Box28, { marginTop: 1, flexDirection: "column" }, children));
|
|
17976
18392
|
}
|
|
17977
18393
|
function SummaryLine({ label, value }) {
|
|
17978
|
-
return /* @__PURE__ */
|
|
18394
|
+
return /* @__PURE__ */ React33.createElement(Box28, null, /* @__PURE__ */ React33.createElement(Text26, null, label.padEnd(12)), /* @__PURE__ */ React33.createElement(Text26, { bold: true }, value));
|
|
17979
18395
|
}
|
|
17980
18396
|
function presetItems() {
|
|
17981
18397
|
return ["fast", "smart", "max"].map((name) => ({
|
|
@@ -18031,7 +18447,7 @@ async function setupCommand(_opts = {}) {
|
|
|
18031
18447
|
const existingKey = loadApiKey();
|
|
18032
18448
|
const existing = readConfig();
|
|
18033
18449
|
const { waitUntilExit, unmount } = render4(
|
|
18034
|
-
/* @__PURE__ */
|
|
18450
|
+
/* @__PURE__ */ React34.createElement(
|
|
18035
18451
|
Wizard,
|
|
18036
18452
|
{
|
|
18037
18453
|
existingApiKey: existingKey,
|
|
@@ -18079,13 +18495,13 @@ function planUpdate(input) {
|
|
|
18079
18495
|
};
|
|
18080
18496
|
}
|
|
18081
18497
|
function defaultSpawn(argv) {
|
|
18082
|
-
return new Promise((
|
|
18498
|
+
return new Promise((resolve12, reject) => {
|
|
18083
18499
|
const child = spawn6(argv[0], argv.slice(1), {
|
|
18084
18500
|
stdio: "inherit",
|
|
18085
18501
|
shell: process.platform === "win32"
|
|
18086
18502
|
});
|
|
18087
18503
|
child.once("error", reject);
|
|
18088
|
-
child.once("exit", (code) =>
|
|
18504
|
+
child.once("exit", (code) => resolve12(code ?? 1));
|
|
18089
18505
|
});
|
|
18090
18506
|
}
|
|
18091
18507
|
async function updateCommand(opts = {}) {
|