@cryer/star-cli 0.3.5 → 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 +6 -3
- package/README.zh-CN.md +6 -3
- package/dist/{chunk-OIDB2E5J.js → chunk-I6FDXAWX.js} +19 -3
- package/dist/chunk-I6FDXAWX.js.map +1 -0
- package/dist/{loop-H5RYTO2O.js → loop-XY3TE5NN.js} +2 -2
- package/dist/main.js +799 -112
- 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-OIDB2E5J.js.map +0 -1
- package/dist/stream-H4BYBVAZ.js.map +0 -1
- /package/dist/{loop-H5RYTO2O.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";
|
|
@@ -3184,17 +3455,295 @@ async function buildUndoTreeDiffs(cwd, tree) {
|
|
|
3184
3455
|
}
|
|
3185
3456
|
|
|
3186
3457
|
// src/cli/components/ConnectWizard.tsx
|
|
3187
|
-
import { Box as Box4, Text as Text4
|
|
3188
|
-
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
|
+
}
|
|
3189
3738
|
|
|
3190
3739
|
// src/cli/components/SelectPrompt.tsx
|
|
3191
|
-
import { Box, Text
|
|
3192
|
-
import { useRef, useState } from "react";
|
|
3740
|
+
import { Box, Text } from "ink";
|
|
3741
|
+
import { useRef as useRef2, useState } from "react";
|
|
3193
3742
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3194
3743
|
var SELECT_PROMPT_MAX_VISIBLE = 9;
|
|
3195
3744
|
function SelectPrompt({ title, options, onSelect, onCancel }) {
|
|
3196
3745
|
const [index, setIndex] = useState(0);
|
|
3197
|
-
const indexRef =
|
|
3746
|
+
const indexRef = useRef2(0);
|
|
3198
3747
|
const move = (delta) => {
|
|
3199
3748
|
indexRef.current = Math.max(0, Math.min(indexRef.current + delta, options.length - 1));
|
|
3200
3749
|
setIndex(indexRef.current);
|
|
@@ -3255,12 +3804,12 @@ function SelectPrompt({ title, options, onSelect, onCancel }) {
|
|
|
3255
3804
|
}
|
|
3256
3805
|
|
|
3257
3806
|
// src/cli/components/TextField.tsx
|
|
3258
|
-
import { Box as Box3, Text as Text3
|
|
3259
|
-
import { useRef as
|
|
3807
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
3808
|
+
import { useRef as useRef4, useState as useState3 } from "react";
|
|
3260
3809
|
|
|
3261
3810
|
// src/cli/components/InputBox.tsx
|
|
3262
|
-
import { Box as Box2, Text as Text2,
|
|
3263
|
-
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";
|
|
3264
3813
|
|
|
3265
3814
|
// src/cli/path-suggest.ts
|
|
3266
3815
|
import { readdir as readdir2 } from "fs/promises";
|
|
@@ -3410,25 +3959,25 @@ function InputBox({
|
|
|
3410
3959
|
const [highlight, setHighlight] = useState2(0);
|
|
3411
3960
|
const [suggestionsDismissed, setSuggestionsDismissed] = useState2(false);
|
|
3412
3961
|
const [pathSuggestions, setPathSuggestions] = useState2([]);
|
|
3413
|
-
const historyIndexRef =
|
|
3414
|
-
const draftRef =
|
|
3415
|
-
const refillSeqRef =
|
|
3962
|
+
const historyIndexRef = useRef3(null);
|
|
3963
|
+
const draftRef = useRef3("");
|
|
3964
|
+
const refillSeqRef = useRef3(0);
|
|
3416
3965
|
const [search, setSearch] = useState2(null);
|
|
3417
|
-
const searchSavedRef =
|
|
3418
|
-
const pastesRef =
|
|
3419
|
-
const pasteSeqRef =
|
|
3420
|
-
const bracketRef =
|
|
3421
|
-
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(
|
|
3422
3971
|
null
|
|
3423
3972
|
);
|
|
3424
|
-
const valueRef =
|
|
3425
|
-
const cursorRef =
|
|
3426
|
-
|
|
3973
|
+
const valueRef = useRef3(value);
|
|
3974
|
+
const cursorRef = useRef3(cursor);
|
|
3975
|
+
useEffect2(() => {
|
|
3427
3976
|
valueRef.current = value;
|
|
3428
3977
|
cursorRef.current = cursor;
|
|
3429
3978
|
});
|
|
3430
3979
|
const { stdout } = useStdout();
|
|
3431
|
-
|
|
3980
|
+
useEffect2(() => {
|
|
3432
3981
|
if (!stdout?.isTTY) return;
|
|
3433
3982
|
stdout.write("\x1B[?2004h");
|
|
3434
3983
|
return () => {
|
|
@@ -3517,7 +4066,7 @@ function InputBox({
|
|
|
3517
4066
|
}
|
|
3518
4067
|
}
|
|
3519
4068
|
};
|
|
3520
|
-
|
|
4069
|
+
useEffect2(() => {
|
|
3521
4070
|
if (refill && refill.seq !== refillSeqRef.current) {
|
|
3522
4071
|
refillSeqRef.current = refill.seq;
|
|
3523
4072
|
edit(refill.text, refill.text.length);
|
|
@@ -3525,7 +4074,7 @@ function InputBox({
|
|
|
3525
4074
|
});
|
|
3526
4075
|
const suggestionsEnabled = !isStreaming && !disabled && !suggestionsDismissed && !search;
|
|
3527
4076
|
const suggestions = suggestionsEnabled && commands ? filterCommands(value, commands) : [];
|
|
3528
|
-
|
|
4077
|
+
useEffect2(() => {
|
|
3529
4078
|
if (!suggestionsEnabled || !cwd) {
|
|
3530
4079
|
setPathSuggestions([]);
|
|
3531
4080
|
return;
|
|
@@ -3563,7 +4112,7 @@ function InputBox({
|
|
|
3563
4112
|
token.start + replacement.length
|
|
3564
4113
|
);
|
|
3565
4114
|
};
|
|
3566
|
-
|
|
4115
|
+
useInput((input, key) => {
|
|
3567
4116
|
if (!disabled) {
|
|
3568
4117
|
const bracket = bracketRef.current;
|
|
3569
4118
|
const feedable = input.length > 0 && !key.ctrl && !key.meta;
|
|
@@ -3796,9 +4345,9 @@ ${value.slice(cursor)}`, cursor + 1);
|
|
|
3796
4345
|
const after = value.slice(cursor + 1);
|
|
3797
4346
|
const cursorChar = at === "\n" || at === void 0 || at === PASTE_DELIM ? " " : at;
|
|
3798
4347
|
const tail = (at === "\n" ? "\n" : "") + stylePasteTokens(after);
|
|
3799
|
-
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}`;
|
|
3800
4349
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
3801
|
-
/* @__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 }) }),
|
|
3802
4351
|
suggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: suggestions.map((cmd, index) => {
|
|
3803
4352
|
const label = cmd.usage ?? `/${cmd.name}`;
|
|
3804
4353
|
return index === activeIndex ? /* @__PURE__ */ jsx2(Text2, { bold: true, inverse: true, children: `${label} - ${cmd.description}` }, cmd.name) : /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
@@ -3827,8 +4376,8 @@ function TextField({
|
|
|
3827
4376
|
onCancel
|
|
3828
4377
|
}) {
|
|
3829
4378
|
const [value, setValue] = useState3(initialValue);
|
|
3830
|
-
const valueRef =
|
|
3831
|
-
const bracketRef =
|
|
4379
|
+
const valueRef = useRef4(initialValue);
|
|
4380
|
+
const bracketRef = useRef4({ active: false, buffer: "", pending: "" });
|
|
3832
4381
|
const flushBracket = () => {
|
|
3833
4382
|
const bracket = bracketRef.current;
|
|
3834
4383
|
const content = bracket.buffer.replace(/[\r\n]+/g, "");
|
|
@@ -3840,7 +4389,7 @@ function TextField({
|
|
|
3840
4389
|
setValue(valueRef.current);
|
|
3841
4390
|
}
|
|
3842
4391
|
};
|
|
3843
|
-
|
|
4392
|
+
useInput((input, key) => {
|
|
3844
4393
|
if (key.escape) {
|
|
3845
4394
|
onCancel();
|
|
3846
4395
|
return;
|
|
@@ -3879,7 +4428,7 @@ function TextField({
|
|
|
3879
4428
|
// src/cli/components/ConnectWizard.tsx
|
|
3880
4429
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3881
4430
|
function YN({ question, onAnswer }) {
|
|
3882
|
-
|
|
4431
|
+
useInput((input, key) => {
|
|
3883
4432
|
const ch = input.toLowerCase();
|
|
3884
4433
|
if (ch === "y") onAnswer(true);
|
|
3885
4434
|
else if (ch === "n" || key.escape) onAnswer(false);
|
|
@@ -3909,8 +4458,8 @@ function ConnectWizard({
|
|
|
3909
4458
|
const [error, setError] = useState4(null);
|
|
3910
4459
|
const [busy, setBusy] = useState4(false);
|
|
3911
4460
|
const [notes, setNotes] = useState4([]);
|
|
3912
|
-
const busyRef =
|
|
3913
|
-
const notesRef =
|
|
4461
|
+
const busyRef = useRef5(false);
|
|
4462
|
+
const notesRef = useRef5([]);
|
|
3914
4463
|
const pushNote = (note) => {
|
|
3915
4464
|
notesRef.current = [...notesRef.current, note];
|
|
3916
4465
|
setNotes(notesRef.current);
|
|
@@ -4093,10 +4642,10 @@ function ConnectWizard({
|
|
|
4093
4642
|
}
|
|
4094
4643
|
|
|
4095
4644
|
// src/cli/components/ExitPlanPrompt.tsx
|
|
4096
|
-
import { Box as Box5, Text as Text5
|
|
4645
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
4097
4646
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4098
4647
|
function ExitPlanPrompt({ onDecision }) {
|
|
4099
|
-
|
|
4648
|
+
useInput((input, key) => {
|
|
4100
4649
|
const ch = input.toLowerCase();
|
|
4101
4650
|
if (ch === "y") onDecision("yes");
|
|
4102
4651
|
else if (ch === "n" || key.escape) onDecision("no");
|
|
@@ -4109,7 +4658,7 @@ function ExitPlanPrompt({ onDecision }) {
|
|
|
4109
4658
|
}
|
|
4110
4659
|
|
|
4111
4660
|
// src/cli/components/MessageList.tsx
|
|
4112
|
-
import { Box as Box6, Static, Text as Text7 } from "ink";
|
|
4661
|
+
import { Box as Box6, Static, Text as Text7, useStdout as useStdout2 } from "ink";
|
|
4113
4662
|
import { memo } from "react";
|
|
4114
4663
|
|
|
4115
4664
|
// src/cli/components/DiffLines.tsx
|
|
@@ -4141,26 +4690,44 @@ function DiffLines({ lines }) {
|
|
|
4141
4690
|
// src/cli/components/MessageList.tsx
|
|
4142
4691
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4143
4692
|
var roleStyles = {
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
system: { label: "system", color: "yellow" },
|
|
4147
|
-
tool: { label: "tool", color: "magenta" }
|
|
4693
|
+
assistant: { label: "\u2726 star", color: "green" },
|
|
4694
|
+
system: { label: "\u25CF system", color: "yellow" }
|
|
4148
4695
|
};
|
|
4149
4696
|
var interruptedMarker = (text) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: text.length > 0 ? " [interrupted]" : "[interrupted]" });
|
|
4150
4697
|
var MessageList = memo(function MessageList2({ messages }) {
|
|
4698
|
+
const { stdout } = useStdout2();
|
|
4699
|
+
const termWidth = stdout.columns ?? 80;
|
|
4151
4700
|
return /* @__PURE__ */ jsx7(Static, { items: messages, children: (message) => {
|
|
4152
4701
|
const marginBottom = message.tight ? 0 : 1;
|
|
4153
4702
|
if (message.role === "assistant-cont") {
|
|
4154
4703
|
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginBottom, children: /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
4155
|
-
message.text,
|
|
4704
|
+
renderMarkdown(message.text, "32", termWidth),
|
|
4156
4705
|
message.interrupted && interruptedMarker(message.text)
|
|
4157
4706
|
] }) }, message.id);
|
|
4158
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
|
+
}
|
|
4159
4726
|
const style = roleStyles[message.role];
|
|
4160
4727
|
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", marginBottom, children: [
|
|
4161
4728
|
/* @__PURE__ */ jsx7(Text7, { bold: true, color: style.color, children: style.label }),
|
|
4162
4729
|
/* @__PURE__ */ jsxs7(Text7, { color: style.color, children: [
|
|
4163
|
-
message.text,
|
|
4730
|
+
message.role === "assistant" ? renderMarkdown(message.text, "32", termWidth) : message.text,
|
|
4164
4731
|
message.interrupted && interruptedMarker(message.text)
|
|
4165
4732
|
] }),
|
|
4166
4733
|
message.diff && /* @__PURE__ */ jsx7(DiffLines, { lines: message.diff }),
|
|
@@ -4170,7 +4737,7 @@ var MessageList = memo(function MessageList2({ messages }) {
|
|
|
4170
4737
|
});
|
|
4171
4738
|
|
|
4172
4739
|
// src/cli/components/PermissionPrompt.tsx
|
|
4173
|
-
import { Box as Box7, Text as Text8
|
|
4740
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
4174
4741
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4175
4742
|
var BASH_COMMAND_MAX = 1e3;
|
|
4176
4743
|
function middleTruncate(text, max) {
|
|
@@ -4184,7 +4751,7 @@ function bashCommand(args) {
|
|
|
4184
4751
|
return typeof command === "string" ? command : null;
|
|
4185
4752
|
}
|
|
4186
4753
|
function PermissionPrompt({ request, preview, onDecision }) {
|
|
4187
|
-
|
|
4754
|
+
useInput((input) => {
|
|
4188
4755
|
const ch = input.toLowerCase();
|
|
4189
4756
|
if (ch === "y") onDecision("yes");
|
|
4190
4757
|
else if (ch === "n") onDecision("no");
|
|
@@ -4211,7 +4778,7 @@ function PermissionPrompt({ request, preview, onDecision }) {
|
|
|
4211
4778
|
}
|
|
4212
4779
|
|
|
4213
4780
|
// src/cli/components/RewindConfirmPrompt.tsx
|
|
4214
|
-
import { Box as Box8, Text as Text9
|
|
4781
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
4215
4782
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4216
4783
|
function RewindConfirmPrompt({
|
|
4217
4784
|
summary,
|
|
@@ -4220,7 +4787,7 @@ function RewindConfirmPrompt({
|
|
|
4220
4787
|
diffs,
|
|
4221
4788
|
onDecision
|
|
4222
4789
|
}) {
|
|
4223
|
-
|
|
4790
|
+
useInput((input, key) => {
|
|
4224
4791
|
const ch = input.toLowerCase();
|
|
4225
4792
|
if (ch === "y") onDecision("yes");
|
|
4226
4793
|
else if (ch === "n" || key.escape) onDecision("no");
|
|
@@ -4241,8 +4808,74 @@ function RewindConfirmPrompt({
|
|
|
4241
4808
|
}
|
|
4242
4809
|
|
|
4243
4810
|
// src/cli/components/StatusBar.tsx
|
|
4244
|
-
import { Box as Box9, Text as Text10, useStdout as
|
|
4811
|
+
import { Box as Box9, Text as Text10, useStdout as useStdout3 } from "ink";
|
|
4245
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
|
|
4246
4879
|
import { jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4247
4880
|
var BG_LABEL_MAX = 40;
|
|
4248
4881
|
var WIDE_COLUMNS = 120;
|
|
@@ -4262,12 +4895,13 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4262
4895
|
sessionCostUsd,
|
|
4263
4896
|
backgroundTasks = []
|
|
4264
4897
|
}) {
|
|
4265
|
-
const { stdout } =
|
|
4898
|
+
const { stdout } = useStdout3();
|
|
4266
4899
|
const wide = (stdout?.columns ?? 80) >= WIDE_COLUMNS;
|
|
4267
4900
|
const joined = backgroundTasks.map((label) => toTerminalSafe(label)).join(", ");
|
|
4268
4901
|
const labels = joined.length > BG_LABEL_MAX ? `${joined.slice(0, BG_LABEL_MAX)}\u2026` : joined;
|
|
4269
4902
|
return /* @__PURE__ */ jsxs10(Box9, { justifyContent: "space-between", children: [
|
|
4270
4903
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
4904
|
+
folderIcon,
|
|
4271
4905
|
cwdLabel(cwd, wide),
|
|
4272
4906
|
gitBranch ? ` [${gitBranch}]` : ""
|
|
4273
4907
|
] }),
|
|
@@ -4279,7 +4913,7 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4279
4913
|
")"
|
|
4280
4914
|
] }),
|
|
4281
4915
|
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
4282
|
-
"
|
|
4916
|
+
"\u2726 ",
|
|
4283
4917
|
model
|
|
4284
4918
|
] }),
|
|
4285
4919
|
contextPercent != null && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
@@ -4307,23 +4941,24 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
4307
4941
|
});
|
|
4308
4942
|
|
|
4309
4943
|
// src/cli/components/StreamingMessage.tsx
|
|
4310
|
-
import { Box as Box10, Text as Text11 } from "ink";
|
|
4944
|
+
import { Box as Box10, Text as Text11, useStdout as useStdout4 } from "ink";
|
|
4311
4945
|
import { memo as memo3 } from "react";
|
|
4312
4946
|
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4313
4947
|
var StreamingMessage = memo3(function StreamingMessage2({
|
|
4314
4948
|
text,
|
|
4315
4949
|
continuation
|
|
4316
4950
|
}) {
|
|
4951
|
+
const { stdout } = useStdout4();
|
|
4317
4952
|
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
4318
|
-
!continuation && /* @__PURE__ */ jsx10(Text11, { bold: true, color: "green", children: "star" }),
|
|
4319
|
-
/* @__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) })
|
|
4320
4955
|
] });
|
|
4321
4956
|
});
|
|
4322
4957
|
|
|
4323
4958
|
// src/cli/components/ThinkingIndicator.tsx
|
|
4324
4959
|
import { Box as Box11, Text as Text12 } from "ink";
|
|
4325
4960
|
import { jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4326
|
-
var SPINNER_FRAMES = ["
|
|
4961
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4327
4962
|
var REASONING_TAIL_LENGTH = 200;
|
|
4328
4963
|
var THOUGHT_SUMMARY_LENGTH = 100;
|
|
4329
4964
|
function truncateTail(text, max) {
|
|
@@ -4338,14 +4973,15 @@ function ThinkingIndicator({
|
|
|
4338
4973
|
}) {
|
|
4339
4974
|
const tail = reasoning ? truncateTail(reasoning, REASONING_TAIL_LENGTH) : "";
|
|
4340
4975
|
const spinner = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
4976
|
+
const label = activity ?? `${thinkingIcon} star is thinking\u2026`;
|
|
4341
4977
|
return /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", children: [
|
|
4342
4978
|
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
4343
4979
|
spinner,
|
|
4344
4980
|
" ",
|
|
4345
|
-
|
|
4981
|
+
label
|
|
4346
4982
|
] }),
|
|
4347
4983
|
tail !== "" && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
4348
|
-
"
|
|
4984
|
+
" ",
|
|
4349
4985
|
tail
|
|
4350
4986
|
] })
|
|
4351
4987
|
] });
|
|
@@ -4355,9 +4991,9 @@ function ThinkingIndicator({
|
|
|
4355
4991
|
import { Box as Box12, Text as Text13 } from "ink";
|
|
4356
4992
|
import { jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4357
4993
|
var SYMBOLS = {
|
|
4358
|
-
pending: "
|
|
4359
|
-
in_progress: "
|
|
4360
|
-
done: "
|
|
4994
|
+
pending: "\u25CB",
|
|
4995
|
+
in_progress: "\u25D0",
|
|
4996
|
+
done: "\u25CF"
|
|
4361
4997
|
};
|
|
4362
4998
|
var MAX_VISIBLE_TODOS = 10;
|
|
4363
4999
|
function visibleTodoWindow(todos, max = MAX_VISIBLE_TODOS) {
|
|
@@ -4384,6 +5020,7 @@ function TodoPanel({ todos }) {
|
|
|
4384
5020
|
Text13,
|
|
4385
5021
|
{
|
|
4386
5022
|
dimColor: todo.status === "done",
|
|
5023
|
+
strikethrough: todo.status === "done",
|
|
4387
5024
|
color: todo.status === "in_progress" ? "cyan" : void 0,
|
|
4388
5025
|
children: [
|
|
4389
5026
|
SYMBOLS[todo.status],
|
|
@@ -4403,7 +5040,7 @@ function TodoPanel({ todos }) {
|
|
|
4403
5040
|
|
|
4404
5041
|
// src/cli/components/ToolCallCard.tsx
|
|
4405
5042
|
function formatToolCard(card) {
|
|
4406
|
-
const header = `${card.name} ${card.argsSummary}${card.isError ? " [error]" : ""}`;
|
|
5043
|
+
const header = `${toolIcon(card.name)} ${card.name} ${card.argsSummary}${card.isError ? " [error]" : ""}`;
|
|
4407
5044
|
if (card.result === void 0) return header;
|
|
4408
5045
|
const preview = previewLines(toTerminalSafe(card.result), 10);
|
|
4409
5046
|
return `${header}
|
|
@@ -4631,6 +5268,7 @@ function formatUsage(usage) {
|
|
|
4631
5268
|
var SESSION_PICKER_CAP = 20;
|
|
4632
5269
|
var SESSION_PREVIEW_MAX = 60;
|
|
4633
5270
|
var MAX_VISIBLE_QUEUED = 5;
|
|
5271
|
+
var REASONING_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "max"];
|
|
4634
5272
|
function Repl({
|
|
4635
5273
|
backend,
|
|
4636
5274
|
model,
|
|
@@ -4662,47 +5300,47 @@ function Repl({
|
|
|
4662
5300
|
const [pendingRewind, setPendingRewindState] = useState5(null);
|
|
4663
5301
|
const [picker, setPickerState] = useState5(null);
|
|
4664
5302
|
const [pendingConnect, setPendingConnectState] = useState5(null);
|
|
4665
|
-
const backendRef =
|
|
4666
|
-
const nextIdRef =
|
|
4667
|
-
const abortRef =
|
|
4668
|
-
const streamedRef =
|
|
4669
|
-
const lastCommittedLenRef =
|
|
4670
|
-
const thinkingRef =
|
|
4671
|
-
const reasoningRef =
|
|
4672
|
-
const tickerStopRef =
|
|
4673
|
-
const flushedRef =
|
|
4674
|
-
const toolCardsRef =
|
|
4675
|
-
const pendingRef =
|
|
4676
|
-
const permissionChainRef =
|
|
4677
|
-
const alwaysAllowedRef =
|
|
4678
|
-
const prevModeRef =
|
|
4679
|
-
const planApprovalRef =
|
|
4680
|
-
const pendingRewindRef =
|
|
4681
|
-
const pickerRef =
|
|
4682
|
-
const pendingConnectRef =
|
|
4683
|
-
const modelNameRef =
|
|
4684
|
-
const sessionStoreRef =
|
|
4685
|
-
const configRef =
|
|
4686
|
-
|
|
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(() => {
|
|
4687
5325
|
configRef.current = config;
|
|
4688
5326
|
}, [config]);
|
|
4689
|
-
const usageRef =
|
|
4690
|
-
const turnChunksRef =
|
|
4691
|
-
const messagesRef =
|
|
5327
|
+
const usageRef = useRef6(initialUsage ? { ...initialUsage } : emptyUsage());
|
|
5328
|
+
const turnChunksRef = useRef6(0);
|
|
5329
|
+
const messagesRef = useRef6(initialDisplay);
|
|
4692
5330
|
const [queueItems, setQueueItems] = useState5([]);
|
|
4693
|
-
const queueRef =
|
|
5331
|
+
const queueRef = useRef6(new PromptQueue());
|
|
4694
5332
|
const [pendingImages, setPendingImages] = useState5([]);
|
|
4695
|
-
const pendingImagesRef =
|
|
4696
|
-
const imageSeqRef =
|
|
5333
|
+
const pendingImagesRef = useRef6([]);
|
|
5334
|
+
const imageSeqRef = useRef6(1);
|
|
4697
5335
|
const [inputRefill, setInputRefill] = useState5();
|
|
4698
|
-
const refillSeqRef =
|
|
4699
|
-
const lastEscRef =
|
|
5336
|
+
const refillSeqRef = useRef6(0);
|
|
5337
|
+
const lastEscRef = useRef6(null);
|
|
4700
5338
|
const [gitBranch, setGitBranch] = useState5(null);
|
|
4701
5339
|
const [contextPercent, setContextPercent] = useState5(null);
|
|
4702
5340
|
const [initialHistory] = useState5(() => loadHistory());
|
|
4703
|
-
const budgetWarnedRef =
|
|
4704
|
-
const budgetExceededRef =
|
|
4705
|
-
const runStreamRef =
|
|
5341
|
+
const budgetWarnedRef = useRef6(false);
|
|
5342
|
+
const budgetExceededRef = useRef6(false);
|
|
5343
|
+
const runStreamRef = useRef6(
|
|
4706
5344
|
null
|
|
4707
5345
|
);
|
|
4708
5346
|
const pushMessage = useCallback(
|
|
@@ -4882,7 +5520,7 @@ function Repl({
|
|
|
4882
5520
|
},
|
|
4883
5521
|
[setPendingPermission, cwd, pushMessage]
|
|
4884
5522
|
);
|
|
4885
|
-
|
|
5523
|
+
useEffect3(() => {
|
|
4886
5524
|
attachConfirmHandler(backendRef.current);
|
|
4887
5525
|
return () => {
|
|
4888
5526
|
tickerStopRef.current?.();
|
|
@@ -4890,19 +5528,19 @@ function Repl({
|
|
|
4890
5528
|
pendingRef.current?.resolve(false);
|
|
4891
5529
|
};
|
|
4892
5530
|
}, [attachConfirmHandler]);
|
|
4893
|
-
|
|
5531
|
+
useEffect3(() => {
|
|
4894
5532
|
if (process.env.STAR_NO_UPDATE_CHECK === "1") return;
|
|
4895
5533
|
void checkForUpdate(VERSION).then((message) => {
|
|
4896
5534
|
if (message) pushMessage("system", message);
|
|
4897
5535
|
});
|
|
4898
5536
|
}, [pushMessage]);
|
|
4899
5537
|
const resumedAtStart = initialMessages !== void 0;
|
|
4900
|
-
|
|
5538
|
+
useEffect3(() => {
|
|
4901
5539
|
if (!resumedAtStart) return;
|
|
4902
5540
|
loadTodos(cwd).then(setTodos).catch(() => {
|
|
4903
5541
|
});
|
|
4904
5542
|
}, [cwd, resumedAtStart]);
|
|
4905
|
-
|
|
5543
|
+
useEffect3(() => {
|
|
4906
5544
|
const onUpdate = (task) => {
|
|
4907
5545
|
setBgLabels(runningTaskLabels());
|
|
4908
5546
|
pushMessage(
|
|
@@ -4985,7 +5623,7 @@ function Repl({
|
|
|
4985
5623
|
}
|
|
4986
5624
|
exit();
|
|
4987
5625
|
}, [exit, pushMessage]);
|
|
4988
|
-
|
|
5626
|
+
useInput((_input, key) => {
|
|
4989
5627
|
if (key.escape) {
|
|
4990
5628
|
const busy = abortRef.current !== null || pendingRef.current !== null || planApprovalRef.current || pendingRewindRef.current !== null || pendingConnectRef.current !== null || pickerRef.current !== null;
|
|
4991
5629
|
if (busy) {
|
|
@@ -5044,7 +5682,8 @@ function Repl({
|
|
|
5044
5682
|
cwd,
|
|
5045
5683
|
system: SYSTEM_PROMPT,
|
|
5046
5684
|
sessionStore: sessionStoreRef.current,
|
|
5047
|
-
contextMaxTokens: contextWindowTokens(config, name)
|
|
5685
|
+
contextMaxTokens: contextWindowTokens(config, name),
|
|
5686
|
+
providerMetadata: reasoningEffortMetadata(config, name)
|
|
5048
5687
|
});
|
|
5049
5688
|
const prev = backendRef.current;
|
|
5050
5689
|
if (prev instanceof AgentLoop) {
|
|
@@ -5061,6 +5700,42 @@ function Repl({
|
|
|
5061
5700
|
},
|
|
5062
5701
|
[config, cwd, attachConfirmHandler]
|
|
5063
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
|
+
);
|
|
5064
5739
|
const handleConnectSetDefault = useCallback(
|
|
5065
5740
|
async (modelName2) => {
|
|
5066
5741
|
saveDefaultModel(modelName2);
|
|
@@ -5170,7 +5845,7 @@ function Repl({
|
|
|
5170
5845
|
setThinking(false);
|
|
5171
5846
|
if (reasoningRef.current.length > 0) {
|
|
5172
5847
|
setThoughtSummary(
|
|
5173
|
-
|
|
5848
|
+
`${thinkingIcon} thought: ${truncateTail(reasoningRef.current, THOUGHT_SUMMARY_LENGTH)}`
|
|
5174
5849
|
);
|
|
5175
5850
|
}
|
|
5176
5851
|
}
|
|
@@ -5188,7 +5863,9 @@ function Repl({
|
|
|
5188
5863
|
const list = parseTodoArgs(event.args);
|
|
5189
5864
|
if (list) setTodos(list);
|
|
5190
5865
|
}
|
|
5191
|
-
setActivity(
|
|
5866
|
+
setActivity(
|
|
5867
|
+
`${toolIcon(event.name)} running ${event.name}: ${summarizeArgs(event.args, 60)}`
|
|
5868
|
+
);
|
|
5192
5869
|
} else if (event.type === "tool-result") {
|
|
5193
5870
|
const card = toolCardsRef.current.get(event.id) ?? {
|
|
5194
5871
|
id: event.id,
|
|
@@ -5337,7 +6014,7 @@ function Repl({
|
|
|
5337
6014
|
sessionCostUsd
|
|
5338
6015
|
]
|
|
5339
6016
|
);
|
|
5340
|
-
|
|
6017
|
+
useEffect3(() => {
|
|
5341
6018
|
runStreamRef.current = runStream;
|
|
5342
6019
|
}, [runStream]);
|
|
5343
6020
|
const handlePlanDecision = useCallback(
|
|
@@ -5391,7 +6068,7 @@ function Repl({
|
|
|
5391
6068
|
const models = listModels(config);
|
|
5392
6069
|
if (models.length === 0) return "No models configured.";
|
|
5393
6070
|
const lines = models.map(
|
|
5394
|
-
(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}]` : ""}`
|
|
5395
6072
|
);
|
|
5396
6073
|
return `Models (* = current):
|
|
5397
6074
|
${lines.join("\n")}`;
|
|
@@ -5410,6 +6087,7 @@ ${lines.join("\n")}`;
|
|
|
5410
6087
|
if (models.length === 0) return "No models configured.";
|
|
5411
6088
|
const options = models.map((m) => {
|
|
5412
6089
|
const parts = [`${m.provider}/${m.model}`, `${contextWindowTokens(config, m.name)} ctx`];
|
|
6090
|
+
if (m.reasoningEffort) parts.push(`effort: ${m.reasoningEffort}`);
|
|
5413
6091
|
if (m.promptPrice !== void 0 && m.completionPrice !== void 0) {
|
|
5414
6092
|
parts.push(`$${m.promptPrice}/$${m.completionPrice} per 1M tokens`);
|
|
5415
6093
|
}
|
|
@@ -5421,10 +6099,17 @@ ${lines.join("\n")}`;
|
|
|
5421
6099
|
};
|
|
5422
6100
|
});
|
|
5423
6101
|
const picked = await showPicker("Select a model", options);
|
|
5424
|
-
if (!picked
|
|
5425
|
-
|
|
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);
|
|
5426
6110
|
}
|
|
5427
|
-
|
|
6111
|
+
results.push(await pickReasoningEffort(picked));
|
|
6112
|
+
return results.join("\n");
|
|
5428
6113
|
},
|
|
5429
6114
|
pickPermissionMode: async () => {
|
|
5430
6115
|
const current = config.permissionMode;
|
|
@@ -5762,6 +6447,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
5762
6447
|
config,
|
|
5763
6448
|
cwd,
|
|
5764
6449
|
switchModel,
|
|
6450
|
+
pickReasoningEffort,
|
|
5765
6451
|
resume,
|
|
5766
6452
|
runStream,
|
|
5767
6453
|
applyMessages,
|
|
@@ -5865,7 +6551,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
5865
6551
|
})),
|
|
5866
6552
|
[registry]
|
|
5867
6553
|
);
|
|
5868
|
-
|
|
6554
|
+
useEffect3(() => {
|
|
5869
6555
|
setGitBranch(getGitSummary(cwd)?.branch ?? null);
|
|
5870
6556
|
const current = backendRef.current;
|
|
5871
6557
|
if (current instanceof AgentLoop) {
|
|
@@ -6081,7 +6767,8 @@ async function createLoop(config, modelName, cwd, sessionStore) {
|
|
|
6081
6767
|
cwd,
|
|
6082
6768
|
system: SYSTEM_PROMPT,
|
|
6083
6769
|
sessionStore,
|
|
6084
|
-
contextMaxTokens: contextWindowTokens(config, modelName)
|
|
6770
|
+
contextMaxTokens: contextWindowTokens(config, modelName),
|
|
6771
|
+
providerMetadata: reasoningEffortMetadata(config, modelName)
|
|
6085
6772
|
});
|
|
6086
6773
|
}
|
|
6087
6774
|
async function printMode(loop, prompt, cwd, json, imagePaths, sessionStore) {
|