@cryer/star-cli 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/README.zh-CN.md +10 -3
- package/dist/{chunk-K3R42LOX.js → chunk-I6FDXAWX.js} +50 -9
- package/dist/chunk-I6FDXAWX.js.map +1 -0
- package/dist/{loop-SDZVEQND.js → loop-XY3TE5NN.js} +2 -2
- package/dist/main.js +829 -120
- package/dist/main.js.map +1 -1
- package/dist/{stream-H4BYBVAZ.js → stream-EFQPULQQ.js} +4 -2
- package/dist/stream-EFQPULQQ.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-K3R42LOX.js.map +0 -1
- package/dist/stream-H4BYBVAZ.js.map +0 -1
- /package/dist/{loop-SDZVEQND.js.map → loop-XY3TE5NN.js.map} +0 -0
package/dist/main.js
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
resolveSnapshotContent,
|
|
45
45
|
rewindToSnapshot,
|
|
46
46
|
summarizeMessages
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-I6FDXAWX.js";
|
|
48
48
|
import {
|
|
49
49
|
envFilePath,
|
|
50
50
|
globalConfigPath,
|
|
@@ -57,6 +57,198 @@ import {
|
|
|
57
57
|
// src/main.tsx
|
|
58
58
|
import { Command } from "commander";
|
|
59
59
|
|
|
60
|
+
// src/cli/markdown.ts
|
|
61
|
+
var ESC = "\x1B[";
|
|
62
|
+
var RESET = `${ESC}0m`;
|
|
63
|
+
function styled(open, text, base) {
|
|
64
|
+
const reopen = base ? `${ESC}${base}m` : "";
|
|
65
|
+
return `${ESC}${open}m${text}${RESET}${reopen}`;
|
|
66
|
+
}
|
|
67
|
+
var bold = (t, b) => styled("1", t, b);
|
|
68
|
+
var dim = (t, b) => styled("2", t, b);
|
|
69
|
+
var italic = (t, b) => styled("3", t, b);
|
|
70
|
+
var underline = (t, b) => styled("4", t, b);
|
|
71
|
+
var strike = (t, b) => styled("9", t, b);
|
|
72
|
+
var yellow = (t, b) => styled("33", t, b);
|
|
73
|
+
var cyanBold = (t, b) => styled("1;36", t, b);
|
|
74
|
+
function cellWidth(text) {
|
|
75
|
+
let width = 0;
|
|
76
|
+
for (const ch of text) {
|
|
77
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
78
|
+
if (cp >= 768 && cp <= 879 || cp === 8205 || cp >= 65024 && cp <= 65039) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (cp >= 4352 && cp <= 4447 || cp >= 11904 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65376 || cp >= 65504 && cp <= 65510 || cp >= 126976 && cp <= 129791 || cp >= 131072 && cp <= 262141) {
|
|
82
|
+
width += 2;
|
|
83
|
+
} else {
|
|
84
|
+
width += 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return width;
|
|
88
|
+
}
|
|
89
|
+
var IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
|
|
90
|
+
var LINK_RE = /\[([^\]]+)\]\(([^)\s]+)\)/g;
|
|
91
|
+
var BOLD_RE = /\*\*([^*\n]+)\*\*|__([^_\n]+)__/g;
|
|
92
|
+
var STRIKE_RE = /~~([^~\n]+)~~/g;
|
|
93
|
+
var STAR_ITALIC_RE = /\*([^*\n]+)\*/g;
|
|
94
|
+
var UNDERSCORE_ITALIC_RE = /(^|[^\w])_([^_\n]+)_(?=[^\w]|$)/g;
|
|
95
|
+
function renderInline(text, base = "") {
|
|
96
|
+
return text.split(/`([^`\n]+)`/).map((segment, index) => {
|
|
97
|
+
if (index % 2 === 1) return yellow(segment, base);
|
|
98
|
+
return segment.replace(IMAGE_RE, (_m, alt) => dim(`[image: ${alt}]`, base)).replace(
|
|
99
|
+
LINK_RE,
|
|
100
|
+
(_m, label, url) => `${underline(label, base)}${dim(` (${url})`, base)}`
|
|
101
|
+
).replace(BOLD_RE, (_m, star, under) => bold(star ?? under, base)).replace(STRIKE_RE, (_m, t) => strike(t, base)).replace(STAR_ITALIC_RE, (_m, t) => italic(t, base)).replace(UNDERSCORE_ITALIC_RE, (_m, pre, t) => pre + italic(t, base));
|
|
102
|
+
}).join("");
|
|
103
|
+
}
|
|
104
|
+
var HEADING_RE = /^#{1,6}\s+(.*)$/;
|
|
105
|
+
var HR_RE = /^\s{0,3}(?:-{3,}|\*{3,}|_{3,})\s*$/;
|
|
106
|
+
var QUOTE_RE = /^\s{0,3}>\s?(.*)$/;
|
|
107
|
+
var FENCE_RE = /^\s{0,3}(```+|~~~+)/;
|
|
108
|
+
var TABLE_SEPARATOR_CELL_RE = /^:?-{3,}:?$/;
|
|
109
|
+
function isTableSeparator(line) {
|
|
110
|
+
const cells = splitTableRow(line);
|
|
111
|
+
return cells !== null && cells.length > 0 && cells.every((cell) => TABLE_SEPARATOR_CELL_RE.test(cell.trim()));
|
|
112
|
+
}
|
|
113
|
+
function splitTableRow(line) {
|
|
114
|
+
const trimmed = line.trim();
|
|
115
|
+
if (!trimmed.includes("|")) return null;
|
|
116
|
+
const stripped = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
117
|
+
return stripped.split("|").map((cell) => cell.trim());
|
|
118
|
+
}
|
|
119
|
+
var MIN_COLUMN_WIDTH = 4;
|
|
120
|
+
function wrapCell(text, width) {
|
|
121
|
+
const lines = [];
|
|
122
|
+
let current = "";
|
|
123
|
+
let currentWidth = 0;
|
|
124
|
+
for (const ch of text) {
|
|
125
|
+
const w = cellWidth(ch);
|
|
126
|
+
if (currentWidth > 0 && currentWidth + w > width) {
|
|
127
|
+
const lastSpace = current.lastIndexOf(" ");
|
|
128
|
+
if (lastSpace > 0 && ch !== " ") {
|
|
129
|
+
lines.push(current.slice(0, lastSpace));
|
|
130
|
+
current = current.slice(lastSpace + 1) + ch;
|
|
131
|
+
} else {
|
|
132
|
+
lines.push(current);
|
|
133
|
+
current = ch === " " ? "" : ch;
|
|
134
|
+
}
|
|
135
|
+
currentWidth = cellWidth(current);
|
|
136
|
+
} else {
|
|
137
|
+
current += ch;
|
|
138
|
+
currentWidth += w;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (current.length > 0 || lines.length === 0) lines.push(current);
|
|
142
|
+
return lines;
|
|
143
|
+
}
|
|
144
|
+
function renderTable(lines, base, maxWidth) {
|
|
145
|
+
const rows = [];
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
const cells = splitTableRow(line);
|
|
148
|
+
if (cells) rows.push(cells);
|
|
149
|
+
}
|
|
150
|
+
if (rows.length < 2) return lines;
|
|
151
|
+
const columnCount = Math.max(...rows.map((row) => row.length));
|
|
152
|
+
const widths = Array.from(
|
|
153
|
+
{ length: columnCount },
|
|
154
|
+
(_, column) => Math.max(1, ...rows.map((row) => cellWidth(row[column] ?? "")))
|
|
155
|
+
);
|
|
156
|
+
const totalWidth = () => 2 + widths.reduce((sum, width) => sum + width, 0) + 3 * (columnCount - 1);
|
|
157
|
+
while (totalWidth() > maxWidth) {
|
|
158
|
+
const widest = widths.indexOf(Math.max(...widths));
|
|
159
|
+
if (widths[widest] === void 0 || widths[widest] <= MIN_COLUMN_WIDTH) break;
|
|
160
|
+
widths[widest] -= 1;
|
|
161
|
+
}
|
|
162
|
+
const joiner = ` ${dim("\u2502", base)} `;
|
|
163
|
+
const formatRow = (row, style) => {
|
|
164
|
+
const wrapped = widths.map((width, column) => wrapCell(row[column] ?? "", width));
|
|
165
|
+
const height = Math.max(...wrapped.map((cell) => cell.length));
|
|
166
|
+
const out = [];
|
|
167
|
+
for (let line = 0; line < height; line++) {
|
|
168
|
+
out.push(
|
|
169
|
+
` ${widths.map((width, column) => {
|
|
170
|
+
const raw = wrapped[column]?.[line] ?? "";
|
|
171
|
+
return style(raw) + " ".repeat(Math.max(0, width - cellWidth(raw)));
|
|
172
|
+
}).join(joiner).trimEnd()}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
};
|
|
177
|
+
const separator = dim(` ${widths.map((width) => "\u2500".repeat(width)).join("\u2500\u253C\u2500")}`, base);
|
|
178
|
+
const [header, , ...body] = rows;
|
|
179
|
+
return [
|
|
180
|
+
...formatRow(header, (cell) => bold(cell, base)),
|
|
181
|
+
separator,
|
|
182
|
+
...body.flatMap((row, index) => {
|
|
183
|
+
const lines2 = formatRow(row, (cell) => renderInline(cell, base));
|
|
184
|
+
return index < body.length - 1 ? [...lines2, separator] : lines2;
|
|
185
|
+
})
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
function committableLineCount(lines) {
|
|
189
|
+
let inFence = false;
|
|
190
|
+
let safe = 0;
|
|
191
|
+
for (let i = 0; i < lines.length; i++) {
|
|
192
|
+
const line = lines[i] ?? "";
|
|
193
|
+
if (FENCE_RE.test(line)) inFence = !inFence;
|
|
194
|
+
const isPipe = line.includes("|");
|
|
195
|
+
const nextIsPipe = i + 1 >= lines.length || (lines[i + 1] ?? "").includes("|");
|
|
196
|
+
if (!inFence && !(isPipe && nextIsPipe)) safe = i + 1;
|
|
197
|
+
}
|
|
198
|
+
return safe;
|
|
199
|
+
}
|
|
200
|
+
function renderMarkdown(text, base = "", maxWidth = 80) {
|
|
201
|
+
const lines = text.split("\n");
|
|
202
|
+
const out = [];
|
|
203
|
+
let inFence = false;
|
|
204
|
+
for (let i = 0; i < lines.length; i++) {
|
|
205
|
+
const line = lines[i] ?? "";
|
|
206
|
+
if (inFence) {
|
|
207
|
+
if (FENCE_RE.test(line)) {
|
|
208
|
+
inFence = false;
|
|
209
|
+
} else {
|
|
210
|
+
out.push(`${dim(" \u258E", base)}${line}`);
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const fence = FENCE_RE.exec(line);
|
|
215
|
+
if (fence) {
|
|
216
|
+
inFence = true;
|
|
217
|
+
const lang = line.trim().slice(fence[1]?.length ?? 0).trim();
|
|
218
|
+
if (lang) out.push(dim(` \u258E ${lang}`, base));
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const heading = HEADING_RE.exec(line);
|
|
222
|
+
if (heading) {
|
|
223
|
+
out.push(cyanBold(heading[1] ?? "", base));
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (HR_RE.test(line)) {
|
|
227
|
+
out.push(dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500", base));
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const quote = QUOTE_RE.exec(line);
|
|
231
|
+
if (quote) {
|
|
232
|
+
out.push(`${dim(" \u258E", base)}${italic(renderInline(quote[1] ?? "", base), base)}`);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const headerCells = splitTableRow(line);
|
|
236
|
+
if (headerCells && i + 1 < lines.length && isTableSeparator(lines[i + 1] ?? "")) {
|
|
237
|
+
const block = [line];
|
|
238
|
+
let j = i + 1;
|
|
239
|
+
while (j < lines.length && splitTableRow(lines[j] ?? "") !== null) {
|
|
240
|
+
block.push(lines[j] ?? "");
|
|
241
|
+
j += 1;
|
|
242
|
+
}
|
|
243
|
+
out.push(...renderTable(block, base, maxWidth));
|
|
244
|
+
i = j - 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
out.push(renderInline(line, base));
|
|
248
|
+
}
|
|
249
|
+
return out.join("\n");
|
|
250
|
+
}
|
|
251
|
+
|
|
60
252
|
// src/cli/terminal-text.ts
|
|
61
253
|
var UNSAFE_CHARS = /[\x00-\x09\x0b-\x1f\x7f]/;
|
|
62
254
|
var CONTROL_CHARS = /[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
@@ -85,9 +277,11 @@ function splitCommittableLines(text, minCompleteLines) {
|
|
|
85
277
|
const lastNewline = text.lastIndexOf("\n");
|
|
86
278
|
if (lastNewline < 0) return null;
|
|
87
279
|
const complete = text.slice(0, lastNewline);
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
280
|
+
const lines = complete.split("\n");
|
|
281
|
+
const count = committableLineCount(lines);
|
|
282
|
+
if (count < minCompleteLines) return null;
|
|
283
|
+
const committed = lines.slice(0, count).join("\n");
|
|
284
|
+
return { committed, rest: text.slice(committed.length + 1) };
|
|
91
285
|
}
|
|
92
286
|
function formatStreamError(error) {
|
|
93
287
|
if (error.message.includes("Unexpected end of JSON input")) {
|
|
@@ -491,8 +685,8 @@ var UsageTracker = class {
|
|
|
491
685
|
};
|
|
492
686
|
|
|
493
687
|
// src/cli/repl.tsx
|
|
494
|
-
import { Box as Box13, Text as Text14, render, useApp
|
|
495
|
-
import { useCallback, useEffect as
|
|
688
|
+
import { Box as Box13, Text as Text14, render, useApp } from "ink";
|
|
689
|
+
import { useCallback, useEffect as useEffect3, useMemo, useRef as useRef6, useState as useState5 } from "react";
|
|
496
690
|
|
|
497
691
|
// src/config/save.ts
|
|
498
692
|
import fs from "fs";
|
|
@@ -600,6 +794,75 @@ async function savePermissionMode(mode) {
|
|
|
600
794
|
await fs.promises.mkdir(path3.dirname(filePath), { recursive: true });
|
|
601
795
|
await writeFileAtomic(filePath, upsertTopLevelKeyText(content, "permissionMode", mode));
|
|
602
796
|
}
|
|
797
|
+
function unquoteTomlString(raw) {
|
|
798
|
+
if (raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
|
|
799
|
+
try {
|
|
800
|
+
return JSON.parse(raw);
|
|
801
|
+
} catch {
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function setReasoningEffortInToml(content, modelName, effort) {
|
|
806
|
+
const lines = content.split("\n");
|
|
807
|
+
const header = /^\s*\[\[\s*models\s*\]\]\s*(?:#.*)?$/;
|
|
808
|
+
const anyHeader = /^\s*\[/;
|
|
809
|
+
const nameLine = /^\s*name\s*=\s*("(?:[^"\\]|\\.)*"|'[^']*')/;
|
|
810
|
+
for (let start = 0; start < lines.length; start++) {
|
|
811
|
+
if (!header.test(lines[start] ?? "")) continue;
|
|
812
|
+
let end = lines.length;
|
|
813
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
814
|
+
if (anyHeader.test(lines[i] ?? "")) {
|
|
815
|
+
end = i;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
let nameIndex = -1;
|
|
820
|
+
for (let i = start + 1; i < end; i++) {
|
|
821
|
+
const match = nameLine.exec(lines[i] ?? "");
|
|
822
|
+
if (match?.[1] && unquoteTomlString(match[1]) === modelName) {
|
|
823
|
+
nameIndex = i;
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (nameIndex === -1) continue;
|
|
828
|
+
const keyLine = /^\s*reasoningEffort\s*=/;
|
|
829
|
+
for (let i = start + 1; i < end; i++) {
|
|
830
|
+
if (!keyLine.test(lines[i] ?? "")) continue;
|
|
831
|
+
if (effort === void 0) {
|
|
832
|
+
lines.splice(i, 1);
|
|
833
|
+
} else {
|
|
834
|
+
const comment = /\s+#.*$/.exec(lines[i] ?? "");
|
|
835
|
+
lines[i] = `reasoningEffort = ${tomlString(effort)}${comment?.[0] ?? ""}`;
|
|
836
|
+
}
|
|
837
|
+
return lines.join("\n");
|
|
838
|
+
}
|
|
839
|
+
if (effort === void 0) return content;
|
|
840
|
+
let insertAt = nameIndex + 1;
|
|
841
|
+
for (let i = start + 1; i < end; i++) {
|
|
842
|
+
if (/^\s*model\s*=/.test(lines[i] ?? "")) {
|
|
843
|
+
insertAt = i + 1;
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
lines.splice(insertAt, 0, `reasoningEffort = ${tomlString(effort)}`);
|
|
848
|
+
return lines.join("\n");
|
|
849
|
+
}
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
async function saveReasoningEffort(modelName, effort) {
|
|
853
|
+
const filePath = globalConfigPath();
|
|
854
|
+
let content = "";
|
|
855
|
+
try {
|
|
856
|
+
content = await fs.promises.readFile(filePath, "utf8");
|
|
857
|
+
} catch (error) {
|
|
858
|
+
if (error.code !== "ENOENT") throw error;
|
|
859
|
+
}
|
|
860
|
+
const updated = setReasoningEffortInToml(content, modelName, effort);
|
|
861
|
+
if (updated === null) return false;
|
|
862
|
+
await fs.promises.mkdir(path3.dirname(filePath), { recursive: true });
|
|
863
|
+
await writeFileAtomic(filePath, updated);
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
603
866
|
|
|
604
867
|
// src/llm/provider.ts
|
|
605
868
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
@@ -734,6 +997,14 @@ function createModel(config, modelName) {
|
|
|
734
997
|
}).responses(modelConfig.model);
|
|
735
998
|
}
|
|
736
999
|
}
|
|
1000
|
+
function reasoningEffortMetadata(config, modelName) {
|
|
1001
|
+
const modelConfig = resolveModelConfig(config, modelName);
|
|
1002
|
+
if (!modelConfig.reasoningEffort) return void 0;
|
|
1003
|
+
const provider = config.providers.find((p) => p.name === modelConfig.provider);
|
|
1004
|
+
if (!provider || provider.protocol === "anthropic") return void 0;
|
|
1005
|
+
const key = provider.protocol === "openai-responses" ? "openai" : provider.name;
|
|
1006
|
+
return { [key]: { reasoningEffort: modelConfig.reasoningEffort } };
|
|
1007
|
+
}
|
|
737
1008
|
|
|
738
1009
|
// src/session/checkpoints.ts
|
|
739
1010
|
import fs2 from "fs/promises";
|
|
@@ -827,6 +1098,12 @@ function debugWarn(message) {
|
|
|
827
1098
|
if (process.env.STAR_DEBUG === "1") process.stderr.write(`[star-cli] ${message}
|
|
828
1099
|
`);
|
|
829
1100
|
}
|
|
1101
|
+
var META_WRITE_MAX_ATTEMPTS = 5;
|
|
1102
|
+
var META_WRITE_RETRY_DELAY_MS = 25;
|
|
1103
|
+
function isTransientFsError(error) {
|
|
1104
|
+
const code = error.code;
|
|
1105
|
+
return code === "EPERM" || code === "EACCES" || code === "EBUSY";
|
|
1106
|
+
}
|
|
830
1107
|
var FALLBACK_META = /* @__PURE__ */ Symbol("star.fallbackMeta");
|
|
831
1108
|
function isFallbackMeta(meta) {
|
|
832
1109
|
return meta[FALLBACK_META] === true;
|
|
@@ -901,17 +1178,33 @@ var SessionStore = class _SessionStore {
|
|
|
901
1178
|
// writers race the loop's own appends, and a truncated read once turned
|
|
902
1179
|
// fallbackMeta's empty defaults into a persisted wipe of model/cwd/usage.
|
|
903
1180
|
// A branded fallback meta is never written back for the same reason.
|
|
1181
|
+
// A write that still fails after retries is dropped rather than thrown:
|
|
1182
|
+
// meta.json is bookkeeping the cached meta rebuilds on the next write,
|
|
1183
|
+
// while the conversation itself lives in messages.jsonl — killing the
|
|
1184
|
+
// agent turn over a stale updatedAt would be the wrong trade.
|
|
904
1185
|
writeMeta(meta) {
|
|
905
1186
|
if (isFallbackMeta(meta)) return Promise.resolve();
|
|
906
1187
|
const tmp = `${this.metaPath()}.tmp-${process.pid}-${this.metaTmpSeq++}`;
|
|
907
1188
|
const run = this.metaWriteQueue.then(async () => {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
})
|
|
914
|
-
|
|
1189
|
+
for (let attempt = 0; attempt < META_WRITE_MAX_ATTEMPTS; attempt++) {
|
|
1190
|
+
try {
|
|
1191
|
+
await fs3.writeFile(tmp, JSON.stringify(meta, null, 2));
|
|
1192
|
+
await fs3.rename(tmp, this.metaPath());
|
|
1193
|
+
return;
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
if (isTransientFsError(error) && attempt + 1 < META_WRITE_MAX_ATTEMPTS) {
|
|
1196
|
+
await new Promise(
|
|
1197
|
+
(resolve) => setTimeout(resolve, META_WRITE_RETRY_DELAY_MS * (attempt + 1))
|
|
1198
|
+
);
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
await fs3.unlink(tmp).catch(() => {
|
|
1202
|
+
});
|
|
1203
|
+
debugWarn(
|
|
1204
|
+
`session ${this.id}: meta write failed (${error.code ?? String(error)}), skipping`
|
|
1205
|
+
);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
915
1208
|
}
|
|
916
1209
|
});
|
|
917
1210
|
this.metaWriteQueue = run.catch(() => {
|
|
@@ -3162,17 +3455,295 @@ async function buildUndoTreeDiffs(cwd, tree) {
|
|
|
3162
3455
|
}
|
|
3163
3456
|
|
|
3164
3457
|
// src/cli/components/ConnectWizard.tsx
|
|
3165
|
-
import { Box as Box4, Text as Text4
|
|
3166
|
-
import { useRef as
|
|
3458
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
3459
|
+
import { useRef as useRef5, useState as useState4 } from "react";
|
|
3460
|
+
|
|
3461
|
+
// src/cli/use-input.ts
|
|
3462
|
+
import { useStdin } from "ink";
|
|
3463
|
+
import { useEffect, useRef } from "react";
|
|
3464
|
+
|
|
3465
|
+
// src/cli/keypress.ts
|
|
3466
|
+
var metaKeyCodeRe = /^(?:\u001B)([a-zA-Z0-9])$/;
|
|
3467
|
+
var fnKeyRe = /^(?:\u001B+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
|
3468
|
+
var keyName = {
|
|
3469
|
+
OP: "f1",
|
|
3470
|
+
OQ: "f2",
|
|
3471
|
+
OR: "f3",
|
|
3472
|
+
OS: "f4",
|
|
3473
|
+
"[11~": "f1",
|
|
3474
|
+
"[12~": "f2",
|
|
3475
|
+
"[13~": "f3",
|
|
3476
|
+
"[14~": "f4",
|
|
3477
|
+
"[[A": "f1",
|
|
3478
|
+
"[[B": "f2",
|
|
3479
|
+
"[[C": "f3",
|
|
3480
|
+
"[[D": "f4",
|
|
3481
|
+
"[[E": "f5",
|
|
3482
|
+
"[15~": "f5",
|
|
3483
|
+
"[17~": "f6",
|
|
3484
|
+
"[18~": "f7",
|
|
3485
|
+
"[19~": "f8",
|
|
3486
|
+
"[20~": "f9",
|
|
3487
|
+
"[21~": "f10",
|
|
3488
|
+
"[23~": "f11",
|
|
3489
|
+
"[24~": "f12",
|
|
3490
|
+
"[A": "up",
|
|
3491
|
+
"[B": "down",
|
|
3492
|
+
"[C": "right",
|
|
3493
|
+
"[D": "left",
|
|
3494
|
+
"[E": "clear",
|
|
3495
|
+
"[F": "end",
|
|
3496
|
+
"[H": "home",
|
|
3497
|
+
OA: "up",
|
|
3498
|
+
OB: "down",
|
|
3499
|
+
OC: "right",
|
|
3500
|
+
OD: "left",
|
|
3501
|
+
OE: "clear",
|
|
3502
|
+
OF: "end",
|
|
3503
|
+
OH: "home",
|
|
3504
|
+
"[1~": "home",
|
|
3505
|
+
"[2~": "insert",
|
|
3506
|
+
"[3~": "delete",
|
|
3507
|
+
"[4~": "end",
|
|
3508
|
+
"[5~": "pageup",
|
|
3509
|
+
"[6~": "pagedown",
|
|
3510
|
+
"[[5~": "pageup",
|
|
3511
|
+
"[[6~": "pagedown",
|
|
3512
|
+
"[7~": "home",
|
|
3513
|
+
"[8~": "end",
|
|
3514
|
+
"[a": "up",
|
|
3515
|
+
"[b": "down",
|
|
3516
|
+
"[c": "right",
|
|
3517
|
+
"[d": "left",
|
|
3518
|
+
"[e": "clear",
|
|
3519
|
+
"[2$": "insert",
|
|
3520
|
+
"[3$": "delete",
|
|
3521
|
+
"[5$": "pageup",
|
|
3522
|
+
"[6$": "pagedown",
|
|
3523
|
+
"[7$": "home",
|
|
3524
|
+
"[8$": "end",
|
|
3525
|
+
Oa: "up",
|
|
3526
|
+
Ob: "down",
|
|
3527
|
+
Oc: "right",
|
|
3528
|
+
Od: "left",
|
|
3529
|
+
Oe: "clear",
|
|
3530
|
+
"[2^": "insert",
|
|
3531
|
+
"[3^": "delete",
|
|
3532
|
+
"[5^": "pageup",
|
|
3533
|
+
"[6^": "pagedown",
|
|
3534
|
+
"[7^": "home",
|
|
3535
|
+
"[8^": "end",
|
|
3536
|
+
"[Z": "tab"
|
|
3537
|
+
};
|
|
3538
|
+
var nonAlphanumericKeys = [...Object.values(keyName), "backspace"];
|
|
3539
|
+
var shiftKeyCodes = /* @__PURE__ */ new Set([
|
|
3540
|
+
"[a",
|
|
3541
|
+
"[b",
|
|
3542
|
+
"[c",
|
|
3543
|
+
"[d",
|
|
3544
|
+
"[e",
|
|
3545
|
+
"[2$",
|
|
3546
|
+
"[3$",
|
|
3547
|
+
"[5$",
|
|
3548
|
+
"[6$",
|
|
3549
|
+
"[7$",
|
|
3550
|
+
"[8$",
|
|
3551
|
+
"[Z"
|
|
3552
|
+
]);
|
|
3553
|
+
var ctrlKeyCodes = /* @__PURE__ */ new Set([
|
|
3554
|
+
"Oa",
|
|
3555
|
+
"Ob",
|
|
3556
|
+
"Oc",
|
|
3557
|
+
"Od",
|
|
3558
|
+
"Oe",
|
|
3559
|
+
"[2^",
|
|
3560
|
+
"[3^",
|
|
3561
|
+
"[5^",
|
|
3562
|
+
"[6^",
|
|
3563
|
+
"[7^",
|
|
3564
|
+
"[8^"
|
|
3565
|
+
]);
|
|
3566
|
+
function parseKeypress(s) {
|
|
3567
|
+
const key = {
|
|
3568
|
+
name: void 0,
|
|
3569
|
+
ctrl: false,
|
|
3570
|
+
meta: false,
|
|
3571
|
+
shift: false,
|
|
3572
|
+
option: false,
|
|
3573
|
+
sequence: s
|
|
3574
|
+
};
|
|
3575
|
+
if (s === "\r") {
|
|
3576
|
+
key.name = "return";
|
|
3577
|
+
} else if (s === "\n") {
|
|
3578
|
+
key.name = "enter";
|
|
3579
|
+
} else if (s === " ") {
|
|
3580
|
+
key.name = "tab";
|
|
3581
|
+
} else if (s === "\b" || s === "\x1B\b") {
|
|
3582
|
+
key.name = "backspace";
|
|
3583
|
+
key.meta = s.charAt(0) === "\x1B";
|
|
3584
|
+
} else if (s === "\x7F" || s === "\x1B\x7F") {
|
|
3585
|
+
key.name = "delete";
|
|
3586
|
+
key.meta = s.charAt(0) === "\x1B";
|
|
3587
|
+
} else if (s === "\x1B" || s === "\x1B\x1B") {
|
|
3588
|
+
key.name = "escape";
|
|
3589
|
+
key.meta = s.length === 2;
|
|
3590
|
+
} else if (s === " " || s === "\x1B ") {
|
|
3591
|
+
key.name = "space";
|
|
3592
|
+
key.meta = s.length === 2;
|
|
3593
|
+
} else if (s.length === 1 && s <= "") {
|
|
3594
|
+
key.name = String.fromCharCode(s.charCodeAt(0) + "a".charCodeAt(0) - 1);
|
|
3595
|
+
key.ctrl = true;
|
|
3596
|
+
} else if (s.length === 1 && s >= "0" && s <= "9") {
|
|
3597
|
+
key.name = "number";
|
|
3598
|
+
} else if (s.length === 1 && s >= "a" && s <= "z") {
|
|
3599
|
+
key.name = s;
|
|
3600
|
+
} else if (s.length === 1 && s >= "A" && s <= "Z") {
|
|
3601
|
+
key.name = s.toLowerCase();
|
|
3602
|
+
key.shift = true;
|
|
3603
|
+
}
|
|
3604
|
+
const metaMatch = key.name === void 0 ? metaKeyCodeRe.exec(s) : null;
|
|
3605
|
+
const fnMatch = key.name === void 0 && metaMatch === null ? fnKeyRe.exec(s) : null;
|
|
3606
|
+
if (metaMatch) {
|
|
3607
|
+
key.meta = true;
|
|
3608
|
+
key.shift = /^[A-Z]$/.test(metaMatch[1] ?? "");
|
|
3609
|
+
} else if (fnMatch) {
|
|
3610
|
+
const segs = [...s];
|
|
3611
|
+
if (segs[0] === "\x1B" && segs[1] === "\x1B") {
|
|
3612
|
+
key.option = true;
|
|
3613
|
+
}
|
|
3614
|
+
const code = [fnMatch[1], fnMatch[2], fnMatch[4], fnMatch[6]].filter(Boolean).join("");
|
|
3615
|
+
const modifier = Number(fnMatch[3] || fnMatch[5] || 1) - 1;
|
|
3616
|
+
key.ctrl = !!(modifier & 4);
|
|
3617
|
+
key.meta = !!(modifier & 10);
|
|
3618
|
+
key.shift = !!(modifier & 1);
|
|
3619
|
+
key.code = code;
|
|
3620
|
+
key.name = keyName[code];
|
|
3621
|
+
key.shift = shiftKeyCodes.has(code) || key.shift;
|
|
3622
|
+
key.ctrl = ctrlKeyCodes.has(code) || key.ctrl;
|
|
3623
|
+
}
|
|
3624
|
+
return key;
|
|
3625
|
+
}
|
|
3626
|
+
function splitKeypresses(chunk) {
|
|
3627
|
+
const units = [];
|
|
3628
|
+
let i = 0;
|
|
3629
|
+
const csiLength = (start) => {
|
|
3630
|
+
let j = start;
|
|
3631
|
+
while (j < chunk.length) {
|
|
3632
|
+
const code = chunk.charCodeAt(j);
|
|
3633
|
+
j += 1;
|
|
3634
|
+
if (code >= 64 && code <= 126) break;
|
|
3635
|
+
}
|
|
3636
|
+
return j;
|
|
3637
|
+
};
|
|
3638
|
+
while (i < chunk.length) {
|
|
3639
|
+
if (chunk[i] !== "\x1B") {
|
|
3640
|
+
const next2 = chunk.indexOf("\x1B", i);
|
|
3641
|
+
const end = next2 === -1 ? chunk.length : next2;
|
|
3642
|
+
units.push(chunk.slice(i, end));
|
|
3643
|
+
i = end;
|
|
3644
|
+
continue;
|
|
3645
|
+
}
|
|
3646
|
+
const next = chunk[i + 1];
|
|
3647
|
+
if (next === void 0) {
|
|
3648
|
+
units.push("\x1B");
|
|
3649
|
+
i += 1;
|
|
3650
|
+
} else if (next === "[") {
|
|
3651
|
+
const end = csiLength(i + 2);
|
|
3652
|
+
units.push(chunk.slice(i, end));
|
|
3653
|
+
i = end;
|
|
3654
|
+
} else if (next === "O") {
|
|
3655
|
+
const end = Math.min(i + 3, chunk.length);
|
|
3656
|
+
units.push(chunk.slice(i, end));
|
|
3657
|
+
i = end;
|
|
3658
|
+
} else if (next === "\x1B" && (chunk[i + 2] === "[" || chunk[i + 2] === "O")) {
|
|
3659
|
+
const end = chunk[i + 2] === "[" ? csiLength(i + 3) : Math.min(i + 4, chunk.length);
|
|
3660
|
+
units.push(chunk.slice(i, end));
|
|
3661
|
+
i = end;
|
|
3662
|
+
} else if (next === "\x1B") {
|
|
3663
|
+
units.push("\x1B");
|
|
3664
|
+
i += 1;
|
|
3665
|
+
} else {
|
|
3666
|
+
units.push(chunk.slice(i, i + 2));
|
|
3667
|
+
i += 2;
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
return units;
|
|
3671
|
+
}
|
|
3672
|
+
var INCOMPLETE_TAIL_RE = /\u001B(?:\[(?:[0-9;?]*)?|O|\u001B(?:\[(?:[0-9;?]*)?|O))$/;
|
|
3673
|
+
function splitTrailingIncomplete(chunk) {
|
|
3674
|
+
const match = INCOMPLETE_TAIL_RE.exec(chunk);
|
|
3675
|
+
if (!match) return { head: chunk, tail: "" };
|
|
3676
|
+
return { head: chunk.slice(0, chunk.length - match[0].length), tail: match[0] };
|
|
3677
|
+
}
|
|
3678
|
+
function toInputKey(unit) {
|
|
3679
|
+
const keypress = parseKeypress(unit);
|
|
3680
|
+
const key = {
|
|
3681
|
+
upArrow: keypress.name === "up",
|
|
3682
|
+
downArrow: keypress.name === "down",
|
|
3683
|
+
leftArrow: keypress.name === "left",
|
|
3684
|
+
rightArrow: keypress.name === "right",
|
|
3685
|
+
pageDown: keypress.name === "pagedown",
|
|
3686
|
+
pageUp: keypress.name === "pageup",
|
|
3687
|
+
return: keypress.name === "return",
|
|
3688
|
+
escape: keypress.name === "escape",
|
|
3689
|
+
ctrl: keypress.ctrl,
|
|
3690
|
+
shift: keypress.shift,
|
|
3691
|
+
tab: keypress.name === "tab",
|
|
3692
|
+
backspace: keypress.name === "backspace",
|
|
3693
|
+
delete: keypress.name === "delete",
|
|
3694
|
+
meta: keypress.meta || keypress.name === "escape" || keypress.option
|
|
3695
|
+
};
|
|
3696
|
+
let input = keypress.ctrl ? keypress.name ?? "" : keypress.sequence;
|
|
3697
|
+
if (nonAlphanumericKeys.includes(keypress.name ?? "")) {
|
|
3698
|
+
input = "";
|
|
3699
|
+
}
|
|
3700
|
+
if (input.startsWith("\x1B")) {
|
|
3701
|
+
input = input.slice(1);
|
|
3702
|
+
}
|
|
3703
|
+
if (input.length === 1 && /[A-Z]/.test(input)) {
|
|
3704
|
+
key.shift = true;
|
|
3705
|
+
}
|
|
3706
|
+
return { input, key };
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
// src/cli/use-input.ts
|
|
3710
|
+
function useInput(inputHandler, options = {}) {
|
|
3711
|
+
const { setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin();
|
|
3712
|
+
const pendingRef = useRef("");
|
|
3713
|
+
useEffect(() => {
|
|
3714
|
+
if (options.isActive === false) return;
|
|
3715
|
+
setRawMode(true);
|
|
3716
|
+
return () => {
|
|
3717
|
+
setRawMode(false);
|
|
3718
|
+
};
|
|
3719
|
+
}, [options.isActive, setRawMode]);
|
|
3720
|
+
useEffect(() => {
|
|
3721
|
+
if (options.isActive === false) return;
|
|
3722
|
+
const handleData = (data) => {
|
|
3723
|
+
const { head, tail } = splitTrailingIncomplete(pendingRef.current + data);
|
|
3724
|
+
pendingRef.current = tail;
|
|
3725
|
+
for (const unit of splitKeypresses(head)) {
|
|
3726
|
+
const { input, key } = toInputKey(unit);
|
|
3727
|
+
if (!(input === "c" && key.ctrl) || !internal_exitOnCtrlC) {
|
|
3728
|
+
inputHandler(input, key);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
};
|
|
3732
|
+
internal_eventEmitter?.on("input", handleData);
|
|
3733
|
+
return () => {
|
|
3734
|
+
internal_eventEmitter?.removeListener("input", handleData);
|
|
3735
|
+
};
|
|
3736
|
+
}, [options.isActive, internal_exitOnCtrlC, internal_eventEmitter, inputHandler]);
|
|
3737
|
+
}
|
|
3167
3738
|
|
|
3168
3739
|
// src/cli/components/SelectPrompt.tsx
|
|
3169
|
-
import { Box, Text
|
|
3170
|
-
import { useRef, useState } from "react";
|
|
3740
|
+
import { Box, Text } from "ink";
|
|
3741
|
+
import { useRef as useRef2, useState } from "react";
|
|
3171
3742
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3172
3743
|
var SELECT_PROMPT_MAX_VISIBLE = 9;
|
|
3173
3744
|
function SelectPrompt({ title, options, onSelect, onCancel }) {
|
|
3174
3745
|
const [index, setIndex] = useState(0);
|
|
3175
|
-
const indexRef =
|
|
3746
|
+
const indexRef = useRef2(0);
|
|
3176
3747
|
const move = (delta) => {
|
|
3177
3748
|
indexRef.current = Math.max(0, Math.min(indexRef.current + delta, options.length - 1));
|
|
3178
3749
|
setIndex(indexRef.current);
|
|
@@ -3233,12 +3804,12 @@ function SelectPrompt({ title, options, onSelect, onCancel }) {
|
|
|
3233
3804
|
}
|
|
3234
3805
|
|
|
3235
3806
|
// src/cli/components/TextField.tsx
|
|
3236
|
-
import { Box as Box3, Text as Text3
|
|
3237
|
-
import { useRef as
|
|
3807
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
3808
|
+
import { useRef as useRef4, useState as useState3 } from "react";
|
|
3238
3809
|
|
|
3239
3810
|
// src/cli/components/InputBox.tsx
|
|
3240
|
-
import { Box as Box2, Text as Text2,
|
|
3241
|
-
import { useEffect, useRef as
|
|
3811
|
+
import { Box as Box2, Text as Text2, useStdout } from "ink";
|
|
3812
|
+
import { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
3242
3813
|
|
|
3243
3814
|
// src/cli/path-suggest.ts
|
|
3244
3815
|
import { readdir as readdir2 } from "fs/promises";
|
|
@@ -3388,25 +3959,25 @@ function InputBox({
|
|
|
3388
3959
|
const [highlight, setHighlight] = useState2(0);
|
|
3389
3960
|
const [suggestionsDismissed, setSuggestionsDismissed] = useState2(false);
|
|
3390
3961
|
const [pathSuggestions, setPathSuggestions] = useState2([]);
|
|
3391
|
-
const historyIndexRef =
|
|
3392
|
-
const draftRef =
|
|
3393
|
-
const refillSeqRef =
|
|
3962
|
+
const historyIndexRef = useRef3(null);
|
|
3963
|
+
const draftRef = useRef3("");
|
|
3964
|
+
const refillSeqRef = useRef3(0);
|
|
3394
3965
|
const [search, setSearch] = useState2(null);
|
|
3395
|
-
const searchSavedRef =
|
|
3396
|
-
const pastesRef =
|
|
3397
|
-
const pasteSeqRef =
|
|
3398
|
-
const bracketRef =
|
|
3399
|
-
const lastPasteRef =
|
|
3966
|
+
const searchSavedRef = useRef3(null);
|
|
3967
|
+
const pastesRef = useRef3(/* @__PURE__ */ new Map());
|
|
3968
|
+
const pasteSeqRef = useRef3(0);
|
|
3969
|
+
const bracketRef = useRef3({ active: false, buffer: "", pending: "" });
|
|
3970
|
+
const lastPasteRef = useRef3(
|
|
3400
3971
|
null
|
|
3401
3972
|
);
|
|
3402
|
-
const valueRef =
|
|
3403
|
-
const cursorRef =
|
|
3404
|
-
|
|
3973
|
+
const valueRef = useRef3(value);
|
|
3974
|
+
const cursorRef = useRef3(cursor);
|
|
3975
|
+
useEffect2(() => {
|
|
3405
3976
|
valueRef.current = value;
|
|
3406
3977
|
cursorRef.current = cursor;
|
|
3407
3978
|
});
|
|
3408
3979
|
const { stdout } = useStdout();
|
|
3409
|
-
|
|
3980
|
+
useEffect2(() => {
|
|
3410
3981
|
if (!stdout?.isTTY) return;
|
|
3411
3982
|
stdout.write("\x1B[?2004h");
|
|
3412
3983
|
return () => {
|
|
@@ -3495,7 +4066,7 @@ function InputBox({
|
|
|
3495
4066
|
}
|
|
3496
4067
|
}
|
|
3497
4068
|
};
|
|
3498
|
-
|
|
4069
|
+
useEffect2(() => {
|
|
3499
4070
|
if (refill && refill.seq !== refillSeqRef.current) {
|
|
3500
4071
|
refillSeqRef.current = refill.seq;
|
|
3501
4072
|
edit(refill.text, refill.text.length);
|
|
@@ -3503,7 +4074,7 @@ function InputBox({
|
|
|
3503
4074
|
});
|
|
3504
4075
|
const suggestionsEnabled = !isStreaming && !disabled && !suggestionsDismissed && !search;
|
|
3505
4076
|
const suggestions = suggestionsEnabled && commands ? filterCommands(value, commands) : [];
|
|
3506
|
-
|
|
4077
|
+
useEffect2(() => {
|
|
3507
4078
|
if (!suggestionsEnabled || !cwd) {
|
|
3508
4079
|
setPathSuggestions([]);
|
|
3509
4080
|
return;
|
|
@@ -3541,7 +4112,7 @@ function InputBox({
|
|
|
3541
4112
|
token.start + replacement.length
|
|
3542
4113
|
);
|
|
3543
4114
|
};
|
|
3544
|
-
|
|
4115
|
+
useInput((input, key) => {
|
|
3545
4116
|
if (!disabled) {
|
|
3546
4117
|
const bracket = bracketRef.current;
|
|
3547
4118
|
const feedable = input.length > 0 && !key.ctrl && !key.meta;
|
|
@@ -3774,9 +4345,9 @@ ${value.slice(cursor)}`, cursor + 1);
|
|
|
3774
4345
|
const after = value.slice(cursor + 1);
|
|
3775
4346
|
const cursorChar = at === "\n" || at === void 0 || at === PASTE_DELIM ? " " : at;
|
|
3776
4347
|
const tail = (at === "\n" ? "\n" : "") + stylePasteTokens(after);
|
|
3777
|
-
const
|
|
4348
|
+
const styled2 = search ? `\x1B[36m(reverse-search) \x1B[39m'${search.query}': ${search.match === null ? "\x1B[2mno match\x1B[22m\x1B[7m \x1B[27m" : `${history[search.match]}\x1B[7m \x1B[27m`}` : `\x1B[36m\u276F \x1B[39m${stylePasteTokens(before)}\x1B[7m${cursorChar}\x1B[27m${tail}`;
|
|
3778
4349
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
3779
|
-
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { children:
|
|
4350
|
+
/* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { children: styled2 }) }),
|
|
3780
4351
|
suggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: suggestions.map((cmd, index) => {
|
|
3781
4352
|
const label = cmd.usage ?? `/${cmd.name}`;
|
|
3782
4353
|
return index === activeIndex ? /* @__PURE__ */ jsx2(Text2, { bold: true, inverse: true, children: `${label} - ${cmd.description}` }, cmd.name) : /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
@@ -3805,8 +4376,8 @@ function TextField({
|
|
|
3805
4376
|
onCancel
|
|
3806
4377
|
}) {
|
|
3807
4378
|
const [value, setValue] = useState3(initialValue);
|
|
3808
|
-
const valueRef =
|
|
3809
|
-
const bracketRef =
|
|
4379
|
+
const valueRef = useRef4(initialValue);
|
|
4380
|
+
const bracketRef = useRef4({ active: false, buffer: "", pending: "" });
|
|
3810
4381
|
const flushBracket = () => {
|
|
3811
4382
|
const bracket = bracketRef.current;
|
|
3812
4383
|
const content = bracket.buffer.replace(/[\r\n]+/g, "");
|
|
@@ -3818,7 +4389,7 @@ function TextField({
|
|
|
3818
4389
|
setValue(valueRef.current);
|
|
3819
4390
|
}
|
|
3820
4391
|
};
|
|
3821
|
-
|
|
4392
|
+
useInput((input, key) => {
|
|
3822
4393
|
if (key.escape) {
|
|
3823
4394
|
onCancel();
|
|
3824
4395
|
return;
|
|
@@ -3857,7 +4428,7 @@ function TextField({
|
|
|
3857
4428
|
// src/cli/components/ConnectWizard.tsx
|
|
3858
4429
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3859
4430
|
function YN({ question, onAnswer }) {
|
|
3860
|
-
|
|
4431
|
+
useInput((input, key) => {
|
|
3861
4432
|
const ch = input.toLowerCase();
|
|
3862
4433
|
if (ch === "y") onAnswer(true);
|
|
3863
4434
|
else if (ch === "n" || key.escape) onAnswer(false);
|
|
@@ -3887,8 +4458,8 @@ function ConnectWizard({
|
|
|
3887
4458
|
const [error, setError] = useState4(null);
|
|
3888
4459
|
const [busy, setBusy] = useState4(false);
|
|
3889
4460
|
const [notes, setNotes] = useState4([]);
|
|
3890
|
-
const busyRef =
|
|
3891
|
-
const notesRef =
|
|
4461
|
+
const busyRef = useRef5(false);
|
|
4462
|
+
const notesRef = useRef5([]);
|
|
3892
4463
|
const pushNote = (note) => {
|
|
3893
4464
|
notesRef.current = [...notesRef.current, note];
|
|
3894
4465
|
setNotes(notesRef.current);
|
|
@@ -4071,10 +4642,10 @@ function ConnectWizard({
|
|
|
4071
4642
|
}
|
|
4072
4643
|
|
|
4073
4644
|
// src/cli/components/ExitPlanPrompt.tsx
|
|
4074
|
-
import { Box as Box5, Text as Text5
|
|
4645
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
4075
4646
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4076
4647
|
function ExitPlanPrompt({ onDecision }) {
|
|
4077
|
-
|
|
4648
|
+
useInput((input, key) => {
|
|
4078
4649
|
const ch = input.toLowerCase();
|
|
4079
4650
|
if (ch === "y") onDecision("yes");
|
|
4080
4651
|
else if (ch === "n" || key.escape) onDecision("no");
|
|
@@ -4087,7 +4658,7 @@ function ExitPlanPrompt({ onDecision }) {
|
|
|
4087
4658
|
}
|
|
4088
4659
|
|
|
4089
4660
|
// src/cli/components/MessageList.tsx
|
|
4090
|
-
import { Box as Box6, Static, Text as Text7 } from "ink";
|
|
4661
|
+
import { Box as Box6, Static, Text as Text7, useStdout as useStdout2 } from "ink";
|
|
4091
4662
|
import { memo } from "react";
|
|
4092
4663
|
|
|
4093
4664
|
// src/cli/components/DiffLines.tsx
|
|
@@ -4119,26 +4690,44 @@ function DiffLines({ lines }) {
|
|
|
4119
4690
|
// src/cli/components/MessageList.tsx
|
|
4120
4691
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4121
4692
|
var roleStyles = {
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
system: { label: "system", color: "yellow" },
|
|
4125
|
-
tool: { label: "tool", color: "magenta" }
|
|
4693
|
+
assistant: { label: "\u2726 star", color: "green" },
|
|
4694
|
+
system: { label: "\u25CF system", color: "yellow" }
|
|
4126
4695
|
};
|
|
4127
4696
|
var interruptedMarker = (text) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: text.length > 0 ? " [interrupted]" : "[interrupted]" });
|
|
4128
4697
|
var MessageList = memo(function MessageList2({ messages }) {
|
|
4698
|
+
const { stdout } = useStdout2();
|
|
4699
|
+
const termWidth = stdout.columns ?? 80;
|
|
4129
4700
|
return /* @__PURE__ */ jsx7(Static, { items: messages, children: (message) => {
|
|
4130
4701
|
const marginBottom = message.tight ? 0 : 1;
|
|
4131
4702
|
if (message.role === "assistant-cont") {
|
|
4132
4703
|
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginBottom, children: /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
4133
|
-
message.text,
|
|
4704
|
+
renderMarkdown(message.text, "32", termWidth),
|
|
4134
4705
|
message.interrupted && interruptedMarker(message.text)
|
|
4135
4706
|
] }) }, message.id);
|
|
4136
4707
|
}
|
|
4708
|
+
if (message.role === "user") {
|
|
4709
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", marginBottom, children: [
|
|
4710
|
+
/* @__PURE__ */ jsxs7(Box6, { children: [
|
|
4711
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "\u276F " }),
|
|
4712
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "cyan", children: [
|
|
4713
|
+
message.text,
|
|
4714
|
+
message.interrupted && interruptedMarker(message.text)
|
|
4715
|
+
] })
|
|
4716
|
+
] }),
|
|
4717
|
+
message.note && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
4718
|
+
" ",
|
|
4719
|
+
message.note
|
|
4720
|
+
] })
|
|
4721
|
+
] }, message.id);
|
|
4722
|
+
}
|
|
4723
|
+
if (message.role === "tool") {
|
|
4724
|
+
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginBottom, children: /* @__PURE__ */ jsx7(Text7, { color: "magenta", children: message.text }) }, message.id);
|
|
4725
|
+
}
|
|
4137
4726
|
const style = roleStyles[message.role];
|
|
4138
4727
|
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", marginBottom, children: [
|
|
4139
4728
|
/* @__PURE__ */ jsx7(Text7, { bold: true, color: style.color, children: style.label }),
|
|
4140
4729
|
/* @__PURE__ */ jsxs7(Text7, { color: style.color, children: [
|
|
4141
|
-
message.text,
|
|
4730
|
+
message.role === "assistant" ? renderMarkdown(message.text, "32", termWidth) : message.text,
|
|
4142
4731
|
message.interrupted && interruptedMarker(message.text)
|
|
4143
4732
|
] }),
|
|
4144
4733
|
message.diff && /* @__PURE__ */ jsx7(DiffLines, { lines: message.diff }),
|
|
@@ -4148,7 +4737,7 @@ var MessageList = memo(function MessageList2({ messages }) {
|
|
|
4148
4737
|
});
|
|
4149
4738
|
|
|
4150
4739
|
// src/cli/components/PermissionPrompt.tsx
|
|
4151
|
-
import { Box as Box7, Text as Text8
|
|
4740
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
4152
4741
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4153
4742
|
var BASH_COMMAND_MAX = 1e3;
|
|
4154
4743
|
function middleTruncate(text, max) {
|
|
@@ -4162,7 +4751,7 @@ function bashCommand(args) {
|
|
|
4162
4751
|
return typeof command === "string" ? command : null;
|
|
4163
4752
|
}
|
|
4164
4753
|
function PermissionPrompt({ request, preview, onDecision }) {
|
|
4165
|
-
|
|
4754
|
+
useInput((input) => {
|
|
4166
4755
|
const ch = input.toLowerCase();
|
|
4167
4756
|
if (ch === "y") onDecision("yes");
|
|
4168
4757
|
else if (ch === "n") onDecision("no");
|
|
@@ -4189,7 +4778,7 @@ function PermissionPrompt({ request, preview, onDecision }) {
|
|
|
4189
4778
|
}
|
|
4190
4779
|
|
|
4191
4780
|
// src/cli/components/RewindConfirmPrompt.tsx
|
|
4192
|
-
import { Box as Box8, Text as Text9
|
|
4781
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
4193
4782
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4194
4783
|
function RewindConfirmPrompt({
|
|
4195
4784
|
summary,
|
|
@@ -4198,7 +4787,7 @@ function RewindConfirmPrompt({
|
|
|
4198
4787
|
diffs,
|
|
4199
4788
|
onDecision
|
|
4200
4789
|
}) {
|
|
4201
|
-
|
|
4790
|
+
useInput((input, key) => {
|
|
4202
4791
|
const ch = input.toLowerCase();
|
|
4203
4792
|
if (ch === "y") onDecision("yes");
|
|
4204
4793
|
else if (ch === "n" || key.escape) onDecision("no");
|
|
@@ -4219,8 +4808,74 @@ function RewindConfirmPrompt({
|
|
|
4219
4808
|
}
|
|
4220
4809
|
|
|
4221
4810
|
// src/cli/components/StatusBar.tsx
|
|
4222
|
-
import { Box as Box9, Text as Text10, useStdout as
|
|
4811
|
+
import { Box as Box9, Text as Text10, useStdout as useStdout3 } from "ink";
|
|
4223
4812
|
import { memo as memo2 } from "react";
|
|
4813
|
+
|
|
4814
|
+
// src/cli/icons.ts
|
|
4815
|
+
var EMOJI_TERM_PROGRAMS = /* @__PURE__ */ new Set([
|
|
4816
|
+
"vscode",
|
|
4817
|
+
"WezTerm",
|
|
4818
|
+
"iTerm.app",
|
|
4819
|
+
"Apple_Terminal",
|
|
4820
|
+
"ghostty",
|
|
4821
|
+
"kitty",
|
|
4822
|
+
"Hyper",
|
|
4823
|
+
"WarpTerminal"
|
|
4824
|
+
]);
|
|
4825
|
+
function resolveIconMode(env = process.env) {
|
|
4826
|
+
const override = env.STAR_ICONS;
|
|
4827
|
+
if (override === "emoji" || override === "plain") return override;
|
|
4828
|
+
if (env.WT_SESSION) return "emoji";
|
|
4829
|
+
if (EMOJI_TERM_PROGRAMS.has(env.TERM_PROGRAM ?? "")) return "emoji";
|
|
4830
|
+
const term = env.TERM ?? "";
|
|
4831
|
+
if (term.includes("kitty") || term.includes("alacritty")) return "emoji";
|
|
4832
|
+
return "plain";
|
|
4833
|
+
}
|
|
4834
|
+
var EMOJI_TOOL_ICONS = {
|
|
4835
|
+
bash: "\u{1F4BB}",
|
|
4836
|
+
read_file: "\u{1F4C4}",
|
|
4837
|
+
write_file: "\u{1F4DD}",
|
|
4838
|
+
edit_file: "\u270F\uFE0F",
|
|
4839
|
+
glob: "\u{1F4C2}",
|
|
4840
|
+
grep: "\u{1F50D}",
|
|
4841
|
+
web_search: "\u{1F50E}",
|
|
4842
|
+
web_fetch: "\u{1F310}",
|
|
4843
|
+
todo_write: "\u{1F4CB}",
|
|
4844
|
+
todo_read: "\u{1F4CB}",
|
|
4845
|
+
task_list: "\u{1F9F5}",
|
|
4846
|
+
task_output: "\u{1F9F5}",
|
|
4847
|
+
task_kill: "\u{1F6D1}",
|
|
4848
|
+
subagent: "\u{1F916}",
|
|
4849
|
+
skill: "\u2728",
|
|
4850
|
+
remember: "\u{1F9E0}"
|
|
4851
|
+
};
|
|
4852
|
+
var PLAIN_TOOL_ICONS = {
|
|
4853
|
+
bash: "$",
|
|
4854
|
+
read_file: "\u2261",
|
|
4855
|
+
write_file: "+",
|
|
4856
|
+
edit_file: "\u270E",
|
|
4857
|
+
glob: "*",
|
|
4858
|
+
grep: "/",
|
|
4859
|
+
web_search: "?",
|
|
4860
|
+
web_fetch: "\u21D3",
|
|
4861
|
+
todo_write: "\u2023",
|
|
4862
|
+
todo_read: "\u2023",
|
|
4863
|
+
task_list: "\xB6",
|
|
4864
|
+
task_output: "\xB6",
|
|
4865
|
+
task_kill: "\xD7",
|
|
4866
|
+
subagent: "\u25C9",
|
|
4867
|
+
skill: "\u2727",
|
|
4868
|
+
remember: "\u203B"
|
|
4869
|
+
};
|
|
4870
|
+
var iconMode = resolveIconMode();
|
|
4871
|
+
function toolIcon(name) {
|
|
4872
|
+
const map = iconMode === "emoji" ? EMOJI_TOOL_ICONS : PLAIN_TOOL_ICONS;
|
|
4873
|
+
return map[name] ?? (iconMode === "emoji" ? "\u{1F527}" : "\u25B8");
|
|
4874
|
+
}
|
|
4875
|
+
var thinkingIcon = iconMode === "emoji" ? "\u{1F4AD}" : "\u2727";
|
|
4876
|
+
var folderIcon = iconMode === "emoji" ? "\u{1F4C1} " : "";
|
|
4877
|
+
|
|
4878
|
+
// src/cli/components/StatusBar.tsx
|
|
4224
4879
|
import { jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4225
4880
|
var BG_LABEL_MAX = 40;
|
|
4226
4881
|
var WIDE_COLUMNS = 120;
|
|
@@ -4240,12 +4895,13 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4240
4895
|
sessionCostUsd,
|
|
4241
4896
|
backgroundTasks = []
|
|
4242
4897
|
}) {
|
|
4243
|
-
const { stdout } =
|
|
4898
|
+
const { stdout } = useStdout3();
|
|
4244
4899
|
const wide = (stdout?.columns ?? 80) >= WIDE_COLUMNS;
|
|
4245
4900
|
const joined = backgroundTasks.map((label) => toTerminalSafe(label)).join(", ");
|
|
4246
4901
|
const labels = joined.length > BG_LABEL_MAX ? `${joined.slice(0, BG_LABEL_MAX)}\u2026` : joined;
|
|
4247
4902
|
return /* @__PURE__ */ jsxs10(Box9, { justifyContent: "space-between", children: [
|
|
4248
4903
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
4904
|
+
folderIcon,
|
|
4249
4905
|
cwdLabel(cwd, wide),
|
|
4250
4906
|
gitBranch ? ` [${gitBranch}]` : ""
|
|
4251
4907
|
] }),
|
|
@@ -4257,7 +4913,7 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4257
4913
|
")"
|
|
4258
4914
|
] }),
|
|
4259
4915
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
4260
|
-
"
|
|
4916
|
+
"\u2726 ",
|
|
4261
4917
|
model
|
|
4262
4918
|
] }),
|
|
4263
4919
|
contextPercent != null && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
@@ -4285,23 +4941,24 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4285
4941
|
});
|
|
4286
4942
|
|
|
4287
4943
|
// src/cli/components/StreamingMessage.tsx
|
|
4288
|
-
import { Box as Box10, Text as Text11 } from "ink";
|
|
4944
|
+
import { Box as Box10, Text as Text11, useStdout as useStdout4 } from "ink";
|
|
4289
4945
|
import { memo as memo3 } from "react";
|
|
4290
4946
|
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4291
4947
|
var StreamingMessage = memo3(function StreamingMessage2({
|
|
4292
4948
|
text,
|
|
4293
4949
|
continuation
|
|
4294
4950
|
}) {
|
|
4951
|
+
const { stdout } = useStdout4();
|
|
4295
4952
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
4296
|
-
!continuation && /* @__PURE__ */ jsx10(Text11, { bold: true, color: "green", children: "star" }),
|
|
4297
|
-
/* @__PURE__ */ jsx10(Text11, { color: "green", children: text })
|
|
4953
|
+
!continuation && /* @__PURE__ */ jsx10(Text11, { bold: true, color: "green", children: "\u2726 star" }),
|
|
4954
|
+
/* @__PURE__ */ jsx10(Text11, { color: "green", children: renderMarkdown(text, "32", stdout.columns ?? 80) })
|
|
4298
4955
|
] });
|
|
4299
4956
|
});
|
|
4300
4957
|
|
|
4301
4958
|
// src/cli/components/ThinkingIndicator.tsx
|
|
4302
4959
|
import { Box as Box11, Text as Text12 } from "ink";
|
|
4303
4960
|
import { jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4304
|
-
var SPINNER_FRAMES = ["
|
|
4961
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4305
4962
|
var REASONING_TAIL_LENGTH = 200;
|
|
4306
4963
|
var THOUGHT_SUMMARY_LENGTH = 100;
|
|
4307
4964
|
function truncateTail(text, max) {
|
|
@@ -4316,14 +4973,15 @@ function ThinkingIndicator({
|
|
|
4316
4973
|
}) {
|
|
4317
4974
|
const tail = reasoning ? truncateTail(reasoning, REASONING_TAIL_LENGTH) : "";
|
|
4318
4975
|
const spinner = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
4976
|
+
const label = activity ?? `${thinkingIcon} star is thinking\u2026`;
|
|
4319
4977
|
return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", children: [
|
|
4320
4978
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
4321
4979
|
spinner,
|
|
4322
4980
|
" ",
|
|
4323
|
-
|
|
4981
|
+
label
|
|
4324
4982
|
] }),
|
|
4325
4983
|
tail !== "" && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
4326
|
-
"
|
|
4984
|
+
" ",
|
|
4327
4985
|
tail
|
|
4328
4986
|
] })
|
|
4329
4987
|
] });
|
|
@@ -4333,9 +4991,9 @@ function ThinkingIndicator({
|
|
|
4333
4991
|
import { Box as Box12, Text as Text13 } from "ink";
|
|
4334
4992
|
import { jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4335
4993
|
var SYMBOLS = {
|
|
4336
|
-
pending: "
|
|
4337
|
-
in_progress: "
|
|
4338
|
-
done: "
|
|
4994
|
+
pending: "\u25CB",
|
|
4995
|
+
in_progress: "\u25D0",
|
|
4996
|
+
done: "\u25CF"
|
|
4339
4997
|
};
|
|
4340
4998
|
var MAX_VISIBLE_TODOS = 10;
|
|
4341
4999
|
function visibleTodoWindow(todos, max = MAX_VISIBLE_TODOS) {
|
|
@@ -4362,6 +5020,7 @@ function TodoPanel({ todos }) {
|
|
|
4362
5020
|
Text13,
|
|
4363
5021
|
{
|
|
4364
5022
|
dimColor: todo.status === "done",
|
|
5023
|
+
strikethrough: todo.status === "done",
|
|
4365
5024
|
color: todo.status === "in_progress" ? "cyan" : void 0,
|
|
4366
5025
|
children: [
|
|
4367
5026
|
SYMBOLS[todo.status],
|
|
@@ -4381,7 +5040,7 @@ function TodoPanel({ todos }) {
|
|
|
4381
5040
|
|
|
4382
5041
|
// src/cli/components/ToolCallCard.tsx
|
|
4383
5042
|
function formatToolCard(card) {
|
|
4384
|
-
const header = `${card.name} ${card.argsSummary}${card.isError ? " [error]" : ""}`;
|
|
5043
|
+
const header = `${toolIcon(card.name)} ${card.name} ${card.argsSummary}${card.isError ? " [error]" : ""}`;
|
|
4385
5044
|
if (card.result === void 0) return header;
|
|
4386
5045
|
const preview = previewLines(toTerminalSafe(card.result), 10);
|
|
4387
5046
|
return `${header}
|
|
@@ -4609,6 +5268,7 @@ function formatUsage(usage) {
|
|
|
4609
5268
|
var SESSION_PICKER_CAP = 20;
|
|
4610
5269
|
var SESSION_PREVIEW_MAX = 60;
|
|
4611
5270
|
var MAX_VISIBLE_QUEUED = 5;
|
|
5271
|
+
var REASONING_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "max"];
|
|
4612
5272
|
function Repl({
|
|
4613
5273
|
backend,
|
|
4614
5274
|
model,
|
|
@@ -4640,47 +5300,47 @@ function Repl({
|
|
|
4640
5300
|
const [pendingRewind, setPendingRewindState] = useState5(null);
|
|
4641
5301
|
const [picker, setPickerState] = useState5(null);
|
|
4642
5302
|
const [pendingConnect, setPendingConnectState] = useState5(null);
|
|
4643
|
-
const backendRef =
|
|
4644
|
-
const nextIdRef =
|
|
4645
|
-
const abortRef =
|
|
4646
|
-
const streamedRef =
|
|
4647
|
-
const lastCommittedLenRef =
|
|
4648
|
-
const thinkingRef =
|
|
4649
|
-
const reasoningRef =
|
|
4650
|
-
const tickerStopRef =
|
|
4651
|
-
const flushedRef =
|
|
4652
|
-
const toolCardsRef =
|
|
4653
|
-
const pendingRef =
|
|
4654
|
-
const permissionChainRef =
|
|
4655
|
-
const alwaysAllowedRef =
|
|
4656
|
-
const prevModeRef =
|
|
4657
|
-
const planApprovalRef =
|
|
4658
|
-
const pendingRewindRef =
|
|
4659
|
-
const pickerRef =
|
|
4660
|
-
const pendingConnectRef =
|
|
4661
|
-
const modelNameRef =
|
|
4662
|
-
const sessionStoreRef =
|
|
4663
|
-
const configRef =
|
|
4664
|
-
|
|
5303
|
+
const backendRef = useRef6(backend);
|
|
5304
|
+
const nextIdRef = useRef6(initialDisplay.length);
|
|
5305
|
+
const abortRef = useRef6(null);
|
|
5306
|
+
const streamedRef = useRef6("");
|
|
5307
|
+
const lastCommittedLenRef = useRef6(0);
|
|
5308
|
+
const thinkingRef = useRef6(false);
|
|
5309
|
+
const reasoningRef = useRef6("");
|
|
5310
|
+
const tickerStopRef = useRef6(null);
|
|
5311
|
+
const flushedRef = useRef6({ streamed: "", reasoning: "" });
|
|
5312
|
+
const toolCardsRef = useRef6(/* @__PURE__ */ new Map());
|
|
5313
|
+
const pendingRef = useRef6(null);
|
|
5314
|
+
const permissionChainRef = useRef6(Promise.resolve());
|
|
5315
|
+
const alwaysAllowedRef = useRef6(/* @__PURE__ */ new Set());
|
|
5316
|
+
const prevModeRef = useRef6(null);
|
|
5317
|
+
const planApprovalRef = useRef6(false);
|
|
5318
|
+
const pendingRewindRef = useRef6(null);
|
|
5319
|
+
const pickerRef = useRef6(null);
|
|
5320
|
+
const pendingConnectRef = useRef6(null);
|
|
5321
|
+
const modelNameRef = useRef6(model);
|
|
5322
|
+
const sessionStoreRef = useRef6(sessionStore);
|
|
5323
|
+
const configRef = useRef6(config);
|
|
5324
|
+
useEffect3(() => {
|
|
4665
5325
|
configRef.current = config;
|
|
4666
5326
|
}, [config]);
|
|
4667
|
-
const usageRef =
|
|
4668
|
-
const turnChunksRef =
|
|
4669
|
-
const messagesRef =
|
|
5327
|
+
const usageRef = useRef6(initialUsage ? { ...initialUsage } : emptyUsage());
|
|
5328
|
+
const turnChunksRef = useRef6(0);
|
|
5329
|
+
const messagesRef = useRef6(initialDisplay);
|
|
4670
5330
|
const [queueItems, setQueueItems] = useState5([]);
|
|
4671
|
-
const queueRef =
|
|
5331
|
+
const queueRef = useRef6(new PromptQueue());
|
|
4672
5332
|
const [pendingImages, setPendingImages] = useState5([]);
|
|
4673
|
-
const pendingImagesRef =
|
|
4674
|
-
const imageSeqRef =
|
|
5333
|
+
const pendingImagesRef = useRef6([]);
|
|
5334
|
+
const imageSeqRef = useRef6(1);
|
|
4675
5335
|
const [inputRefill, setInputRefill] = useState5();
|
|
4676
|
-
const refillSeqRef =
|
|
4677
|
-
const lastEscRef =
|
|
5336
|
+
const refillSeqRef = useRef6(0);
|
|
5337
|
+
const lastEscRef = useRef6(null);
|
|
4678
5338
|
const [gitBranch, setGitBranch] = useState5(null);
|
|
4679
5339
|
const [contextPercent, setContextPercent] = useState5(null);
|
|
4680
5340
|
const [initialHistory] = useState5(() => loadHistory());
|
|
4681
|
-
const budgetWarnedRef =
|
|
4682
|
-
const budgetExceededRef =
|
|
4683
|
-
const runStreamRef =
|
|
5341
|
+
const budgetWarnedRef = useRef6(false);
|
|
5342
|
+
const budgetExceededRef = useRef6(false);
|
|
5343
|
+
const runStreamRef = useRef6(
|
|
4684
5344
|
null
|
|
4685
5345
|
);
|
|
4686
5346
|
const pushMessage = useCallback(
|
|
@@ -4860,7 +5520,7 @@ function Repl({
|
|
|
4860
5520
|
},
|
|
4861
5521
|
[setPendingPermission, cwd, pushMessage]
|
|
4862
5522
|
);
|
|
4863
|
-
|
|
5523
|
+
useEffect3(() => {
|
|
4864
5524
|
attachConfirmHandler(backendRef.current);
|
|
4865
5525
|
return () => {
|
|
4866
5526
|
tickerStopRef.current?.();
|
|
@@ -4868,19 +5528,19 @@ function Repl({
|
|
|
4868
5528
|
pendingRef.current?.resolve(false);
|
|
4869
5529
|
};
|
|
4870
5530
|
}, [attachConfirmHandler]);
|
|
4871
|
-
|
|
5531
|
+
useEffect3(() => {
|
|
4872
5532
|
if (process.env.STAR_NO_UPDATE_CHECK === "1") return;
|
|
4873
5533
|
void checkForUpdate(VERSION).then((message) => {
|
|
4874
5534
|
if (message) pushMessage("system", message);
|
|
4875
5535
|
});
|
|
4876
5536
|
}, [pushMessage]);
|
|
4877
5537
|
const resumedAtStart = initialMessages !== void 0;
|
|
4878
|
-
|
|
5538
|
+
useEffect3(() => {
|
|
4879
5539
|
if (!resumedAtStart) return;
|
|
4880
5540
|
loadTodos(cwd).then(setTodos).catch(() => {
|
|
4881
5541
|
});
|
|
4882
5542
|
}, [cwd, resumedAtStart]);
|
|
4883
|
-
|
|
5543
|
+
useEffect3(() => {
|
|
4884
5544
|
const onUpdate = (task) => {
|
|
4885
5545
|
setBgLabels(runningTaskLabels());
|
|
4886
5546
|
pushMessage(
|
|
@@ -4963,7 +5623,7 @@ function Repl({
|
|
|
4963
5623
|
}
|
|
4964
5624
|
exit();
|
|
4965
5625
|
}, [exit, pushMessage]);
|
|
4966
|
-
|
|
5626
|
+
useInput((_input, key) => {
|
|
4967
5627
|
if (key.escape) {
|
|
4968
5628
|
const busy = abortRef.current !== null || pendingRef.current !== null || planApprovalRef.current || pendingRewindRef.current !== null || pendingConnectRef.current !== null || pickerRef.current !== null;
|
|
4969
5629
|
if (busy) {
|
|
@@ -5022,7 +5682,8 @@ function Repl({
|
|
|
5022
5682
|
cwd,
|
|
5023
5683
|
system: SYSTEM_PROMPT,
|
|
5024
5684
|
sessionStore: sessionStoreRef.current,
|
|
5025
|
-
contextMaxTokens: contextWindowTokens(config, name)
|
|
5685
|
+
contextMaxTokens: contextWindowTokens(config, name),
|
|
5686
|
+
providerMetadata: reasoningEffortMetadata(config, name)
|
|
5026
5687
|
});
|
|
5027
5688
|
const prev = backendRef.current;
|
|
5028
5689
|
if (prev instanceof AgentLoop) {
|
|
@@ -5039,6 +5700,42 @@ function Repl({
|
|
|
5039
5700
|
},
|
|
5040
5701
|
[config, cwd, attachConfirmHandler]
|
|
5041
5702
|
);
|
|
5703
|
+
const pickReasoningEffort = useCallback(
|
|
5704
|
+
async (name) => {
|
|
5705
|
+
const modelConfig = config.models.find((m) => m.name === name);
|
|
5706
|
+
const current = modelConfig?.reasoningEffort;
|
|
5707
|
+
const levels = current && !REASONING_EFFORT_LEVELS.includes(current) ? [...REASONING_EFFORT_LEVELS, current] : REASONING_EFFORT_LEVELS;
|
|
5708
|
+
const options = [
|
|
5709
|
+
{
|
|
5710
|
+
value: "",
|
|
5711
|
+
label: "default",
|
|
5712
|
+
description: "send no effort field \u2014 the provider's server-side default applies",
|
|
5713
|
+
hint: current === void 0 ? "current" : void 0
|
|
5714
|
+
},
|
|
5715
|
+
...levels.map((level) => ({
|
|
5716
|
+
value: level,
|
|
5717
|
+
label: level,
|
|
5718
|
+
description: "only works if the model/provider supports this level",
|
|
5719
|
+
hint: level === current ? "current" : void 0
|
|
5720
|
+
}))
|
|
5721
|
+
];
|
|
5722
|
+
const picked = await showPicker(`Reasoning effort for "${name}"`, options);
|
|
5723
|
+
if (picked === null || picked === "" && current === void 0 || picked === current) {
|
|
5724
|
+
return `Reasoning effort unchanged${current ? ` \u2014 still "${current}"` : " \u2014 server default"}.`;
|
|
5725
|
+
}
|
|
5726
|
+
const effort = picked === "" ? void 0 : picked;
|
|
5727
|
+
if (modelConfig) modelConfig.reasoningEffort = effort;
|
|
5728
|
+
const backend2 = backendRef.current;
|
|
5729
|
+
if (backend2 instanceof AgentLoop) {
|
|
5730
|
+
backend2.setProviderMetadata(reasoningEffortMetadata(config, name));
|
|
5731
|
+
}
|
|
5732
|
+
const saved = await saveReasoningEffort(name, effort);
|
|
5733
|
+
const applied = effort === void 0 ? "server default" : `"${effort}"`;
|
|
5734
|
+
const suffix = saved ? "Saved to config." : `This session only \u2014 "${name}" has no [[models]] block in the global config file; add reasoningEffort there to persist it.`;
|
|
5735
|
+
return `Reasoning effort for "${name}" set to ${applied}. ${suffix}`;
|
|
5736
|
+
},
|
|
5737
|
+
[config, showPicker]
|
|
5738
|
+
);
|
|
5042
5739
|
const handleConnectSetDefault = useCallback(
|
|
5043
5740
|
async (modelName2) => {
|
|
5044
5741
|
saveDefaultModel(modelName2);
|
|
@@ -5148,7 +5845,7 @@ function Repl({
|
|
|
5148
5845
|
setThinking(false);
|
|
5149
5846
|
if (reasoningRef.current.length > 0) {
|
|
5150
5847
|
setThoughtSummary(
|
|
5151
|
-
|
|
5848
|
+
`${thinkingIcon} thought: ${truncateTail(reasoningRef.current, THOUGHT_SUMMARY_LENGTH)}`
|
|
5152
5849
|
);
|
|
5153
5850
|
}
|
|
5154
5851
|
}
|
|
@@ -5166,7 +5863,9 @@ function Repl({
|
|
|
5166
5863
|
const list = parseTodoArgs(event.args);
|
|
5167
5864
|
if (list) setTodos(list);
|
|
5168
5865
|
}
|
|
5169
|
-
setActivity(
|
|
5866
|
+
setActivity(
|
|
5867
|
+
`${toolIcon(event.name)} running ${event.name}: ${summarizeArgs(event.args, 60)}`
|
|
5868
|
+
);
|
|
5170
5869
|
} else if (event.type === "tool-result") {
|
|
5171
5870
|
const card = toolCardsRef.current.get(event.id) ?? {
|
|
5172
5871
|
id: event.id,
|
|
@@ -5315,7 +6014,7 @@ function Repl({
|
|
|
5315
6014
|
sessionCostUsd
|
|
5316
6015
|
]
|
|
5317
6016
|
);
|
|
5318
|
-
|
|
6017
|
+
useEffect3(() => {
|
|
5319
6018
|
runStreamRef.current = runStream;
|
|
5320
6019
|
}, [runStream]);
|
|
5321
6020
|
const handlePlanDecision = useCallback(
|
|
@@ -5369,7 +6068,7 @@ function Repl({
|
|
|
5369
6068
|
const models = listModels(config);
|
|
5370
6069
|
if (models.length === 0) return "No models configured.";
|
|
5371
6070
|
const lines = models.map(
|
|
5372
|
-
(m) => `${m.name === modelNameRef.current ? "*" : " "} ${m.name} (${m.provider}/${m.model})`
|
|
6071
|
+
(m) => `${m.name === modelNameRef.current ? "*" : " "} ${m.name} (${m.provider}/${m.model})${m.reasoningEffort ? ` [effort: ${m.reasoningEffort}]` : ""}`
|
|
5373
6072
|
);
|
|
5374
6073
|
return `Models (* = current):
|
|
5375
6074
|
${lines.join("\n")}`;
|
|
@@ -5388,6 +6087,7 @@ ${lines.join("\n")}`;
|
|
|
5388
6087
|
if (models.length === 0) return "No models configured.";
|
|
5389
6088
|
const options = models.map((m) => {
|
|
5390
6089
|
const parts = [`${m.provider}/${m.model}`, `${contextWindowTokens(config, m.name)} ctx`];
|
|
6090
|
+
if (m.reasoningEffort) parts.push(`effort: ${m.reasoningEffort}`);
|
|
5391
6091
|
if (m.promptPrice !== void 0 && m.completionPrice !== void 0) {
|
|
5392
6092
|
parts.push(`$${m.promptPrice}/$${m.completionPrice} per 1M tokens`);
|
|
5393
6093
|
}
|
|
@@ -5399,10 +6099,17 @@ ${lines.join("\n")}`;
|
|
|
5399
6099
|
};
|
|
5400
6100
|
});
|
|
5401
6101
|
const picked = await showPicker("Select a model", options);
|
|
5402
|
-
if (!picked
|
|
5403
|
-
|
|
6102
|
+
if (!picked) return `Model unchanged \u2014 still "${modelNameRef.current}".`;
|
|
6103
|
+
const results = [];
|
|
6104
|
+
if (picked === modelNameRef.current) {
|
|
6105
|
+
results.push(`Model unchanged \u2014 still "${picked}".`);
|
|
6106
|
+
} else {
|
|
6107
|
+
const switched = await switchModel(picked);
|
|
6108
|
+
if (switched.startsWith("Failed to switch model:")) return switched;
|
|
6109
|
+
results.push(switched);
|
|
5404
6110
|
}
|
|
5405
|
-
|
|
6111
|
+
results.push(await pickReasoningEffort(picked));
|
|
6112
|
+
return results.join("\n");
|
|
5406
6113
|
},
|
|
5407
6114
|
pickPermissionMode: async () => {
|
|
5408
6115
|
const current = config.permissionMode;
|
|
@@ -5719,7 +6426,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
5719
6426
|
`permissionMode: ${config.permissionMode}`,
|
|
5720
6427
|
`providers (${config.providers.length}): ${config.providers.map((p) => p.name).join(", ") || "(none)"}`,
|
|
5721
6428
|
`models (${config.models.length}): ${config.models.map((m) => m.name).join(", ") || "(none)"}`,
|
|
5722
|
-
`maxSteps: ${config.maxSteps}`,
|
|
6429
|
+
`maxSteps: ${config.maxSteps === 0 ? "unlimited" : config.maxSteps}`,
|
|
5723
6430
|
`contextMaxTokens: ${config.contextMaxTokens}`,
|
|
5724
6431
|
`compactThresholdTokens: ${config.compactThresholdTokens ?? "(context window)"}`,
|
|
5725
6432
|
`streamIdleTimeoutSec: ${config.streamIdleTimeoutSec}`,
|
|
@@ -5740,6 +6447,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
5740
6447
|
config,
|
|
5741
6448
|
cwd,
|
|
5742
6449
|
switchModel,
|
|
6450
|
+
pickReasoningEffort,
|
|
5743
6451
|
resume,
|
|
5744
6452
|
runStream,
|
|
5745
6453
|
applyMessages,
|
|
@@ -5843,7 +6551,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
5843
6551
|
})),
|
|
5844
6552
|
[registry]
|
|
5845
6553
|
);
|
|
5846
|
-
|
|
6554
|
+
useEffect3(() => {
|
|
5847
6555
|
setGitBranch(getGitSummary(cwd)?.branch ?? null);
|
|
5848
6556
|
const current = backendRef.current;
|
|
5849
6557
|
if (current instanceof AgentLoop) {
|
|
@@ -6059,7 +6767,8 @@ async function createLoop(config, modelName, cwd, sessionStore) {
|
|
|
6059
6767
|
cwd,
|
|
6060
6768
|
system: SYSTEM_PROMPT,
|
|
6061
6769
|
sessionStore,
|
|
6062
|
-
contextMaxTokens: contextWindowTokens(config, modelName)
|
|
6770
|
+
contextMaxTokens: contextWindowTokens(config, modelName),
|
|
6771
|
+
providerMetadata: reasoningEffortMetadata(config, modelName)
|
|
6063
6772
|
});
|
|
6064
6773
|
}
|
|
6065
6774
|
async function printMode(loop, prompt, cwd, json, imagePaths, sessionStore) {
|