@oxbit/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/dist/assets/{index-BrBnidfI.js → index-T_6HaJx-.js} +496 -155
- package/agent/dist/assets/memory-Bdy8hvTl.js +135 -0
- package/agent/dist/index.js +1190 -303
- package/package.json +1 -1
- package/runtime/dist/index.js +1 -1
- package/runtime/package.json +1 -1
- package/web/dist/assets/{index-CUIuo9fs.js → index-W9bbEyD_.js} +159 -158
- package/web/dist/assets/{typescript-BU_Up1oE.js → typescript-DTI4y3t9.js} +2 -2
- package/web/dist/index.html +1 -1
|
@@ -1,17 +1,120 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Box, Text, renderToString, useAnimation, measureElement, useInput, useWindowSize, usePaste, Static, render } from "ink";
|
|
4
4
|
import { createContext, useContext, useMemo, useState, useRef, useEffect, useSyncExternalStore } from "react";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { dirname } from "node:path";
|
|
5
|
+
import { c as contextLegend, a as contextGrid, f as formatTokens, b as contextNotes, d as formatPercent, M as MODES } from "../index.js";
|
|
6
|
+
import { memoryTuiConnection, fileHistory } from "./memory-Bdy8hvTl.js";
|
|
8
7
|
import "node:util";
|
|
9
8
|
import "node:readline";
|
|
10
9
|
import "node:fs/promises";
|
|
10
|
+
import "node:path";
|
|
11
11
|
import "node:crypto";
|
|
12
12
|
import "node:os";
|
|
13
13
|
import "node:child_process";
|
|
14
14
|
import "node:http";
|
|
15
|
+
import "node:fs";
|
|
16
|
+
const THEMES = {
|
|
17
|
+
dark: {
|
|
18
|
+
name: "dark",
|
|
19
|
+
accent: "#8ab4f8",
|
|
20
|
+
muted: "gray",
|
|
21
|
+
user: "#c8d3f5",
|
|
22
|
+
success: "#9ece6a",
|
|
23
|
+
error: "#f7768e",
|
|
24
|
+
warning: "#e0af68",
|
|
25
|
+
link: "#7dcfff",
|
|
26
|
+
code: "#e0af68",
|
|
27
|
+
keyword: "#bb9af7",
|
|
28
|
+
string: "#9ece6a",
|
|
29
|
+
number: "#ff9e64",
|
|
30
|
+
added: "#9ece6a",
|
|
31
|
+
removed: "#f7768e",
|
|
32
|
+
addedBg: "#1d3325",
|
|
33
|
+
removedBg: "#3b1f27",
|
|
34
|
+
border: "gray",
|
|
35
|
+
modes: { ask: "gray", "accept-edits": "#bb9af7", "full-auto": "#e0af68" }
|
|
36
|
+
},
|
|
37
|
+
light: {
|
|
38
|
+
name: "light",
|
|
39
|
+
accent: "#1d4ed8",
|
|
40
|
+
muted: "gray",
|
|
41
|
+
user: "#1e293b",
|
|
42
|
+
success: "#15803d",
|
|
43
|
+
error: "#b91c1c",
|
|
44
|
+
warning: "#b45309",
|
|
45
|
+
link: "#0369a1",
|
|
46
|
+
code: "#9a3412",
|
|
47
|
+
keyword: "#7c3aed",
|
|
48
|
+
string: "#15803d",
|
|
49
|
+
number: "#c2410c",
|
|
50
|
+
added: "#166534",
|
|
51
|
+
removed: "#991b1b",
|
|
52
|
+
addedBg: "#dcfce7",
|
|
53
|
+
removedBg: "#fee2e2",
|
|
54
|
+
border: "gray",
|
|
55
|
+
modes: { ask: "gray", "accept-edits": "#7c3aed", "full-auto": "#b45309" }
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function resolveTheme(choice = "auto", env = process.env) {
|
|
59
|
+
if (choice !== "auto") return THEMES[choice];
|
|
60
|
+
const background = Number(env.COLORFGBG?.split(";").at(-1));
|
|
61
|
+
return THEMES[Number.isInteger(background) && (background === 7 || background >= 9) ? "light" : "dark"];
|
|
62
|
+
}
|
|
63
|
+
const ThemeContext = createContext(THEMES.dark);
|
|
64
|
+
const useTheme = () => useContext(ThemeContext);
|
|
65
|
+
const COLUMNS = 20;
|
|
66
|
+
const GRID_WIDTH = COLUMNS * 2 - 1;
|
|
67
|
+
const glyph = (id) => id === "free" ? "□" : id === "buffer" ? "⊠" : "■";
|
|
68
|
+
const colors = (theme) => ({
|
|
69
|
+
system: theme.accent,
|
|
70
|
+
instructions: theme.keyword,
|
|
71
|
+
skills: theme.link,
|
|
72
|
+
context: theme.user,
|
|
73
|
+
tools: theme.number,
|
|
74
|
+
mcp: theme.removed,
|
|
75
|
+
messages: theme.success,
|
|
76
|
+
free: theme.muted,
|
|
77
|
+
buffer: theme.warning
|
|
78
|
+
});
|
|
79
|
+
function ContextView({ usage, width: width2 }) {
|
|
80
|
+
const theme = useTheme();
|
|
81
|
+
const known = colors(theme);
|
|
82
|
+
const spare = [theme.code, theme.error, theme.string, theme.added];
|
|
83
|
+
const legend = contextLegend(usage).map((row, index) => ({ ...row, color: known[row.id] ?? spare[index % spare.length] }));
|
|
84
|
+
const color = new Map(legend.map((row) => [row.id, row.color]));
|
|
85
|
+
const grid = contextGrid(usage, COLUMNS * 10);
|
|
86
|
+
const rows = Array.from({ length: grid.length / COLUMNS }, (_, row) => grid.slice(row * COLUMNS, (row + 1) * COLUMNS));
|
|
87
|
+
const label = Math.max(...legend.map((row) => row.label.length));
|
|
88
|
+
const counts = legend.map((row) => formatTokens(row.tokens));
|
|
89
|
+
const digits = Math.max(...counts.map((count) => count.length));
|
|
90
|
+
const side = rows.length > 0 && width2 >= GRID_WIDTH + 3 + label + digits + 12;
|
|
91
|
+
const { header, footer } = contextNotes(usage);
|
|
92
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, width: width2, children: [
|
|
93
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Text, { color: theme.accent, children: "●" }),
|
|
95
|
+
" ",
|
|
96
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: header })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: side ? "row" : "column", marginTop: 1, paddingLeft: 2, children: [
|
|
99
|
+
rows.length ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: GRID_WIDTH, flexShrink: 0, marginRight: side ? 3 : 0, marginBottom: side ? 0 : 1, children: rows.map((cells2, index) => /* @__PURE__ */ jsx(Text, { children: cells2.map((id, at) => /* @__PURE__ */ jsxs(Text, { color: color.get(id), children: [
|
|
100
|
+
at ? " " : "",
|
|
101
|
+
glyph(id)
|
|
102
|
+
] }, at)) }, index)) }) : null,
|
|
103
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: legend.map((row, index) => /* @__PURE__ */ jsxs(Text, { wrap: "truncate-end", children: [
|
|
104
|
+
/* @__PURE__ */ jsx(Text, { color: row.color, children: glyph(row.id) }),
|
|
105
|
+
" ",
|
|
106
|
+
row.label.padEnd(label),
|
|
107
|
+
" ",
|
|
108
|
+
counts[index].padStart(digits),
|
|
109
|
+
usage.window ? /* @__PURE__ */ jsxs(Text, { color: theme.muted, children: [
|
|
110
|
+
" ",
|
|
111
|
+
formatPercent(row.tokens, usage.window).padStart(6)
|
|
112
|
+
] }) : null
|
|
113
|
+
] }, index)) })
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: footer.map((line, index) => /* @__PURE__ */ jsx(Text, { color: theme.muted, wrap: "wrap", children: line }, index)) })
|
|
116
|
+
] });
|
|
117
|
+
}
|
|
15
118
|
const FENCE = /^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)/;
|
|
16
119
|
const HEADING = /^ {0,3}(#{1,6})(?:\s+(.*?))?\s*#*\s*$/;
|
|
17
120
|
const RULE = /^ {0,3}([-*_])(?:\s*\1){2,}\s*$/;
|
|
@@ -271,55 +374,6 @@ function highlight(line, lang) {
|
|
|
271
374
|
if (last < line.length) tokens.push({ text: line.slice(last) });
|
|
272
375
|
return tokens;
|
|
273
376
|
}
|
|
274
|
-
const THEMES = {
|
|
275
|
-
dark: {
|
|
276
|
-
name: "dark",
|
|
277
|
-
accent: "#8ab4f8",
|
|
278
|
-
muted: "gray",
|
|
279
|
-
user: "#c8d3f5",
|
|
280
|
-
success: "#9ece6a",
|
|
281
|
-
error: "#f7768e",
|
|
282
|
-
warning: "#e0af68",
|
|
283
|
-
link: "#7dcfff",
|
|
284
|
-
code: "#e0af68",
|
|
285
|
-
keyword: "#bb9af7",
|
|
286
|
-
string: "#9ece6a",
|
|
287
|
-
number: "#ff9e64",
|
|
288
|
-
added: "#9ece6a",
|
|
289
|
-
removed: "#f7768e",
|
|
290
|
-
addedBg: "#1d3325",
|
|
291
|
-
removedBg: "#3b1f27",
|
|
292
|
-
border: "gray",
|
|
293
|
-
modes: { ask: "gray", "accept-edits": "#bb9af7", "full-auto": "#e0af68" }
|
|
294
|
-
},
|
|
295
|
-
light: {
|
|
296
|
-
name: "light",
|
|
297
|
-
accent: "#1d4ed8",
|
|
298
|
-
muted: "gray",
|
|
299
|
-
user: "#1e293b",
|
|
300
|
-
success: "#15803d",
|
|
301
|
-
error: "#b91c1c",
|
|
302
|
-
warning: "#b45309",
|
|
303
|
-
link: "#0369a1",
|
|
304
|
-
code: "#9a3412",
|
|
305
|
-
keyword: "#7c3aed",
|
|
306
|
-
string: "#15803d",
|
|
307
|
-
number: "#c2410c",
|
|
308
|
-
added: "#166534",
|
|
309
|
-
removed: "#991b1b",
|
|
310
|
-
addedBg: "#dcfce7",
|
|
311
|
-
removedBg: "#fee2e2",
|
|
312
|
-
border: "gray",
|
|
313
|
-
modes: { ask: "gray", "accept-edits": "#7c3aed", "full-auto": "#b45309" }
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
function resolveTheme(choice = "auto", env = process.env) {
|
|
317
|
-
if (choice !== "auto") return THEMES[choice];
|
|
318
|
-
const background = Number(env.COLORFGBG?.split(";").at(-1));
|
|
319
|
-
return THEMES[Number.isInteger(background) && (background === 7 || background >= 9) ? "light" : "dark"];
|
|
320
|
-
}
|
|
321
|
-
const ThemeContext = createContext(THEMES.dark);
|
|
322
|
-
const useTheme = () => useContext(ThemeContext);
|
|
323
377
|
const segmenter = new Intl.Segmenter();
|
|
324
378
|
const ANSI = /\x1b\[[0-9;:?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
325
379
|
const ZERO = /^[\p{Mn}\p{Me}\p{Cf}\p{Cc}]+$/u;
|
|
@@ -789,6 +843,8 @@ function ItemView({ item, live = false, context }) {
|
|
|
789
843
|
/* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { color: theme.error, children: "✗" }) }),
|
|
790
844
|
/* @__PURE__ */ jsx(Text, { color: theme.error, wrap: "wrap", children: item.text })
|
|
791
845
|
] }) : /* @__PURE__ */ jsx(Box, { width: width2, children: /* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(Text, { color: item.tone === "warn" ? theme.warning : theme.muted, wrap: "wrap", children: item.text }) }) });
|
|
846
|
+
case "context":
|
|
847
|
+
return /* @__PURE__ */ jsx(ContextView, { usage: item.usage, width: width2 });
|
|
792
848
|
case "divider": {
|
|
793
849
|
const label = ` ${item.text} `;
|
|
794
850
|
return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: theme.muted, wrap: "truncate-end", children: [
|
|
@@ -801,7 +857,7 @@ function ItemView({ item, live = false, context }) {
|
|
|
801
857
|
}
|
|
802
858
|
const emptyTranscript = () => ({ flushed: [{ id: 0, type: "banner" }], live: [], next: 1 });
|
|
803
859
|
const STREAMING = /* @__PURE__ */ new Set(["assistant", "thought", "plan"]);
|
|
804
|
-
const FINAL = /* @__PURE__ */ new Set(["banner", "user", "notice", "divider"]);
|
|
860
|
+
const FINAL = /* @__PURE__ */ new Set(["banner", "user", "notice", "divider", "context"]);
|
|
805
861
|
const isDone = (item) => FINAL.has(item.type) || !!item.done;
|
|
806
862
|
const textOf = (content) => {
|
|
807
863
|
const block = content;
|
|
@@ -863,7 +919,7 @@ function tool(state, update) {
|
|
|
863
919
|
function applyUpdate(state, update) {
|
|
864
920
|
switch (update.sessionUpdate) {
|
|
865
921
|
case "agent_message_chunk":
|
|
866
|
-
return stream(state, "assistant", textOf(update.content));
|
|
922
|
+
return update._meta?.oxbit?.context ? append(state, { type: "context", usage: update._meta.oxbit.context }) : stream(state, "assistant", textOf(update.content));
|
|
867
923
|
case "agent_thought_chunk":
|
|
868
924
|
return stream(state, "thought", textOf(update.content));
|
|
869
925
|
case "user_message_chunk": {
|
|
@@ -1217,19 +1273,33 @@ class SessionController {
|
|
|
1217
1273
|
if (this.state.running) return this.notice("Wait for the agent to finish, or press Esc to stop it", "warn");
|
|
1218
1274
|
this.rules.clear();
|
|
1219
1275
|
if (label) this.transcript((transcript) => append(transcript, { type: "divider", text: label }));
|
|
1276
|
+
await this.load(sessionId, true);
|
|
1277
|
+
}
|
|
1278
|
+
/** Loads the open session again after the agent was rebuilt; its replayed history stays out of the transcript. */
|
|
1279
|
+
async reload() {
|
|
1280
|
+
if (this.state.running) return this.notice("Wait for the agent to finish, or press Esc to stop it", "warn");
|
|
1281
|
+
await this.load(this.state.status.sessionId, false);
|
|
1282
|
+
}
|
|
1283
|
+
async load(sessionId, replay) {
|
|
1220
1284
|
this.early = [];
|
|
1221
1285
|
let session;
|
|
1222
1286
|
try {
|
|
1223
1287
|
session = sessionId ? await this.connection.loadSession(sessionId, this.cwd) : await this.connection.newSession(this.cwd);
|
|
1224
1288
|
} catch (error) {
|
|
1225
|
-
|
|
1289
|
+
if (!replay && /Unknown ACP session/.test(message(error))) session = await this.connection.newSession(this.cwd).catch(() => void 0);
|
|
1290
|
+
if (!session) this.notice(message(error), "error");
|
|
1226
1291
|
}
|
|
1227
1292
|
const early = this.early;
|
|
1228
1293
|
this.early = void 0;
|
|
1229
1294
|
if (!session) return;
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
this.
|
|
1295
|
+
const { title, usage, goal, loop, commands } = this.state.status;
|
|
1296
|
+
const kept = replay || session.sessionId !== sessionId ? {} : { title, usage, goal, loop, commands };
|
|
1297
|
+
this.set({ status: { ...fromSession(session), ...kept }, info: this.connection.info() });
|
|
1298
|
+
for (const [id, update] of early) {
|
|
1299
|
+
if (replay) this.onUpdate(id, update);
|
|
1300
|
+
else if (id === session.sessionId) this.set({ status: applyStatus(this.state.status, update) });
|
|
1301
|
+
}
|
|
1302
|
+
if (replay) this.transcript(endTurn);
|
|
1233
1303
|
}
|
|
1234
1304
|
async send(text) {
|
|
1235
1305
|
const sessionId = this.state.status.sessionId;
|
|
@@ -1268,8 +1338,10 @@ class SessionController {
|
|
|
1268
1338
|
this.set({ permission: this.permissions[0] });
|
|
1269
1339
|
}
|
|
1270
1340
|
cycleMode() {
|
|
1271
|
-
const ids = MODES.map((
|
|
1272
|
-
|
|
1341
|
+
const ids = MODES.map((mode) => mode.id);
|
|
1342
|
+
this.setMode(ids[(ids.indexOf(this.state.status.mode) + 1) % ids.length]);
|
|
1343
|
+
}
|
|
1344
|
+
setMode(mode) {
|
|
1273
1345
|
const sessionId = this.state.status.sessionId;
|
|
1274
1346
|
this.set({ status: { ...this.state.status, mode } });
|
|
1275
1347
|
if (sessionId) this.connection.setMode(sessionId, mode).catch((error) => this.notice(message(error), "error"));
|
|
@@ -1332,10 +1404,187 @@ class SessionController {
|
|
|
1332
1404
|
return item?.tool;
|
|
1333
1405
|
}
|
|
1334
1406
|
}
|
|
1407
|
+
const PANELS = ["config", "skills", "hooks", "mcp", "instructions", "providers"];
|
|
1408
|
+
const isPanel = (name) => PANELS.includes(name);
|
|
1409
|
+
function panelRows(panel) {
|
|
1410
|
+
const rows = panel?.rows ?? [];
|
|
1411
|
+
return [...new Set(rows.map((row) => row.section))].flatMap((section) => rows.filter((row) => row.section === section));
|
|
1412
|
+
}
|
|
1413
|
+
class PanelController extends SessionController {
|
|
1414
|
+
panelAbort;
|
|
1415
|
+
panelOverlay() {
|
|
1416
|
+
const overlay = this.state.overlay;
|
|
1417
|
+
return overlay?.kind === "panel" ? overlay : void 0;
|
|
1418
|
+
}
|
|
1419
|
+
patchPanel(patch) {
|
|
1420
|
+
const overlay = this.panelOverlay();
|
|
1421
|
+
if (overlay) this.set({ overlay: { ...overlay, ...patch } });
|
|
1422
|
+
}
|
|
1423
|
+
selectedRow() {
|
|
1424
|
+
const overlay = this.panelOverlay();
|
|
1425
|
+
return overlay && panelRows(overlay.panel)[overlay.index];
|
|
1426
|
+
}
|
|
1427
|
+
openPanel(id) {
|
|
1428
|
+
this.closeOverlay();
|
|
1429
|
+
this.set({ overlay: { kind: "panel", id, index: 0, loading: true } });
|
|
1430
|
+
void this.loadPanel();
|
|
1431
|
+
}
|
|
1432
|
+
/** Closes any overlay and cancels the panel request in flight. */
|
|
1433
|
+
closeOverlay() {
|
|
1434
|
+
this.panelAbort?.abort();
|
|
1435
|
+
this.panelAbort = void 0;
|
|
1436
|
+
if (this.state.overlay) this.set({ overlay: void 0 });
|
|
1437
|
+
}
|
|
1438
|
+
/** Runs one SettingsManager call; undefined when it failed, was cancelled or another call replaced it. */
|
|
1439
|
+
async call(run2) {
|
|
1440
|
+
const overlay = this.panelOverlay();
|
|
1441
|
+
const settings = this.connection.settings;
|
|
1442
|
+
if (!overlay || !settings) return void 0;
|
|
1443
|
+
this.panelAbort?.abort();
|
|
1444
|
+
const abort = this.panelAbort = new AbortController();
|
|
1445
|
+
const current = () => this.panelAbort === abort;
|
|
1446
|
+
const context = {
|
|
1447
|
+
cwd: this.cwd,
|
|
1448
|
+
sessionId: this.state.status.sessionId,
|
|
1449
|
+
signal: abort.signal,
|
|
1450
|
+
progress: (text) => current() && this.patchPanel({ progress: text })
|
|
1451
|
+
};
|
|
1452
|
+
try {
|
|
1453
|
+
const result = await run2(settings, overlay.id, context);
|
|
1454
|
+
return current() ? result : void 0;
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
if (current()) this.patchPanel({ error: message(error), loading: false, busy: false, progress: void 0 });
|
|
1457
|
+
return void 0;
|
|
1458
|
+
} finally {
|
|
1459
|
+
if (current()) this.panelAbort = void 0;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
async loadPanel() {
|
|
1463
|
+
const panel = await this.call((settings, id, context) => settings.panel(id, context));
|
|
1464
|
+
const overlay = this.panelOverlay();
|
|
1465
|
+
if (!panel || !overlay) return;
|
|
1466
|
+
const rows = panelRows(panel);
|
|
1467
|
+
const selected2 = rows.findIndex((row) => row.id === panelRows(overlay.panel)[overlay.index]?.id);
|
|
1468
|
+
const index = selected2 >= 0 ? selected2 : Math.min(overlay.index, Math.max(0, rows.length - 1));
|
|
1469
|
+
this.patchPanel({ panel, index, loading: false, busy: false, progress: void 0 });
|
|
1470
|
+
}
|
|
1471
|
+
async change(run2) {
|
|
1472
|
+
const overlay = this.panelOverlay();
|
|
1473
|
+
if (!overlay?.panel || overlay.busy) return;
|
|
1474
|
+
this.patchPanel({ busy: true, progress: void 0, error: void 0, menu: void 0, input: void 0 });
|
|
1475
|
+
const change = await this.call(run2);
|
|
1476
|
+
if (!change) return;
|
|
1477
|
+
if (change.reloaded) await this.reload();
|
|
1478
|
+
if (change.mode) this.setMode(change.mode);
|
|
1479
|
+
if (change.draft !== void 0) {
|
|
1480
|
+
this.closeOverlay();
|
|
1481
|
+
this.set({ draft: draftOf(change.draft), completion: void 0 });
|
|
1482
|
+
} else await this.loadPanel();
|
|
1483
|
+
if (change.message) this.flash(change.message);
|
|
1484
|
+
}
|
|
1485
|
+
movePanel(step) {
|
|
1486
|
+
const overlay = this.panelOverlay();
|
|
1487
|
+
const row = this.selectedRow();
|
|
1488
|
+
if (!overlay || overlay.busy) return;
|
|
1489
|
+
if (overlay.menu !== void 0 && row?.actions) {
|
|
1490
|
+
const count2 = row.actions.length;
|
|
1491
|
+
return this.patchPanel({ menu: (overlay.menu + step + count2) % count2 });
|
|
1492
|
+
}
|
|
1493
|
+
const count = panelRows(overlay.panel).length;
|
|
1494
|
+
if (count) this.patchPanel({ index: (overlay.index + step + count) % count });
|
|
1495
|
+
}
|
|
1496
|
+
togglePanelRow() {
|
|
1497
|
+
const row = this.selectedRow();
|
|
1498
|
+
if (row?.enabled !== void 0) void this.change((settings, id, context) => settings.toggle(id, row.id, !row.enabled, context));
|
|
1499
|
+
}
|
|
1500
|
+
cyclePanelChoice(step) {
|
|
1501
|
+
const row = this.selectedRow();
|
|
1502
|
+
const options = row?.choice?.options ?? [];
|
|
1503
|
+
if (!row?.choice || !options.length) return;
|
|
1504
|
+
const at = options.findIndex((option2) => option2.value === row.choice.value);
|
|
1505
|
+
const value = options[(at + step + options.length) % options.length].value;
|
|
1506
|
+
void this.change((settings, id, context) => settings.choose(id, row.id, value, context));
|
|
1507
|
+
}
|
|
1508
|
+
/** Enter: runs the row's only action, lists several, or runs the one picked from that list. */
|
|
1509
|
+
enterPanel() {
|
|
1510
|
+
const overlay = this.panelOverlay();
|
|
1511
|
+
const row = this.selectedRow();
|
|
1512
|
+
if (!overlay || !row || overlay.busy) return;
|
|
1513
|
+
const actions = row.actions ?? [];
|
|
1514
|
+
if (overlay.menu !== void 0) return this.startAction(row, actions[overlay.menu]);
|
|
1515
|
+
if (actions.length === 1) return this.startAction(row, actions[0]);
|
|
1516
|
+
if (actions.length) return this.patchPanel({ menu: 0 });
|
|
1517
|
+
if (row.enabled !== void 0) return this.togglePanelRow();
|
|
1518
|
+
this.cyclePanelChoice(1);
|
|
1519
|
+
}
|
|
1520
|
+
startAction(row, action) {
|
|
1521
|
+
if (!action) return;
|
|
1522
|
+
if (action.reloads && this.state.running) {
|
|
1523
|
+
this.patchPanel({ menu: void 0 });
|
|
1524
|
+
return this.flash("Wait for the turn to finish");
|
|
1525
|
+
}
|
|
1526
|
+
const first = action.inputs?.[0];
|
|
1527
|
+
if (first) return this.patchPanel({ menu: void 0, input: { rowId: row.id, action, values: [], draft: draftOf(first.value ?? "") } });
|
|
1528
|
+
void this.change((settings, id, context) => settings.act(id, row.id, action.id, [], context));
|
|
1529
|
+
}
|
|
1530
|
+
editPanelInput(draft) {
|
|
1531
|
+
const input = this.panelOverlay()?.input;
|
|
1532
|
+
if (input) this.patchPanel({ input: { ...input, draft } });
|
|
1533
|
+
}
|
|
1534
|
+
pastePanelInput(text) {
|
|
1535
|
+
const input = this.panelOverlay()?.input;
|
|
1536
|
+
if (input) this.editPanelInput(insert(input.draft, text.replace(/\n+$/, "").replace(/\n/g, " ")));
|
|
1537
|
+
}
|
|
1538
|
+
/** Enter in the input field: asks the next input, or runs the action with every answer. */
|
|
1539
|
+
submitPanelInput() {
|
|
1540
|
+
const input = this.panelOverlay()?.input;
|
|
1541
|
+
if (!input) return;
|
|
1542
|
+
const values = [...input.values, input.draft.text];
|
|
1543
|
+
const next = input.action.inputs?.[values.length];
|
|
1544
|
+
if (next) return this.patchPanel({ input: { ...input, values, draft: draftOf(next.value ?? "") } });
|
|
1545
|
+
if (input.action.reloads && this.state.running) {
|
|
1546
|
+
this.patchPanel({ input: void 0 });
|
|
1547
|
+
return this.flash("Wait for the turn to finish");
|
|
1548
|
+
}
|
|
1549
|
+
void this.change((settings, id, context) => settings.act(id, input.rowId, input.action.id, values, context));
|
|
1550
|
+
}
|
|
1551
|
+
/** Typed `/providers use <id>`: the agent command cannot rebuild the agent mid-turn, so the panel action switches it here. */
|
|
1552
|
+
async useProvider(id) {
|
|
1553
|
+
const settings = this.connection.settings;
|
|
1554
|
+
if (!settings) return;
|
|
1555
|
+
if (this.state.running) return this.flash("Wait for the turn to finish");
|
|
1556
|
+
const context = { cwd: this.cwd, sessionId: this.state.status.sessionId, signal: new AbortController().signal, progress: () => {
|
|
1557
|
+
} };
|
|
1558
|
+
try {
|
|
1559
|
+
const change = await settings.act("providers", id, "use", [], context);
|
|
1560
|
+
if (change.reloaded) await this.reload();
|
|
1561
|
+
if (change.message) this.flash(change.message);
|
|
1562
|
+
} catch (error) {
|
|
1563
|
+
this.notice(message(error), "error");
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
/** Esc: cancels the running call, then leaves the input or action list, then closes the panel. */
|
|
1567
|
+
escapePanel() {
|
|
1568
|
+
const overlay = this.panelOverlay();
|
|
1569
|
+
if (overlay?.busy) {
|
|
1570
|
+
this.panelAbort?.abort();
|
|
1571
|
+
this.panelAbort = void 0;
|
|
1572
|
+
return this.patchPanel({ busy: false, progress: void 0 });
|
|
1573
|
+
}
|
|
1574
|
+
if (overlay?.input || overlay?.menu !== void 0) return this.patchPanel({ input: void 0, menu: void 0 });
|
|
1575
|
+
this.closeOverlay();
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1335
1578
|
const LOCAL_COMMANDS = [
|
|
1336
1579
|
{ name: "model", description: "Choose the model and reasoning effort", hint: "[model]" },
|
|
1337
1580
|
{ name: "new", description: "Start a new session" },
|
|
1338
1581
|
{ name: "resume", description: "Resume a saved session" },
|
|
1582
|
+
{ name: "config", description: "Settings: permission mode, auto-compact, hooks and MCP trust" },
|
|
1583
|
+
{ name: "skills", description: "Turn skills on or off, or start a prompt with one" },
|
|
1584
|
+
{ name: "hooks", description: "Turn hook sources on or off" },
|
|
1585
|
+
{ name: "mcp", description: "Add, remove, trust and sign in to MCP servers" },
|
|
1586
|
+
{ name: "instructions", description: "Choose which AGENTS.md and CLAUDE.md files load" },
|
|
1587
|
+
{ name: "providers", description: "Sign in to providers and set API keys" },
|
|
1339
1588
|
{ name: "help", description: "Show keys and commands" },
|
|
1340
1589
|
{ name: "exit", description: "Quit" }
|
|
1341
1590
|
];
|
|
@@ -1348,7 +1597,7 @@ function overlayItems(state) {
|
|
|
1348
1597
|
return rank(overlay.sessions, (session) => session.title ?? session.sessionId, overlay.filter, 200).map((session) => ({ value: session.sessionId, label: session.title || session.sessionId, detail: session.updatedAt?.slice(0, 16).replace("T", " ") }));
|
|
1349
1598
|
return [];
|
|
1350
1599
|
}
|
|
1351
|
-
class Controller extends
|
|
1600
|
+
class Controller extends PanelController {
|
|
1352
1601
|
constructor(connection, options) {
|
|
1353
1602
|
super(connection, options.cwd);
|
|
1354
1603
|
this.options = options;
|
|
@@ -1362,9 +1611,14 @@ class Controller extends SessionController {
|
|
|
1362
1611
|
fileTimer;
|
|
1363
1612
|
files = /* @__PURE__ */ new Map();
|
|
1364
1613
|
lastInterrupt = 0;
|
|
1614
|
+
/** Server commands, then local ones; a panel command shows the server command's hint, since arguments go to the server. */
|
|
1365
1615
|
commands() {
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1616
|
+
const server = this.state.status.commands;
|
|
1617
|
+
const local = LOCAL_COMMANDS.filter((command) => command.name === "resume" ? this.connection.listSessions : !isPanel(command.name) || this.connection.settings).map((command) => {
|
|
1618
|
+
const hint = isPanel(command.name) ? server.find((item) => item.name === command.name)?.hint : void 0;
|
|
1619
|
+
return hint ? { ...command, hint: hint.startsWith("[") ? hint : `[${hint}]` } : command;
|
|
1620
|
+
});
|
|
1621
|
+
return [...server.filter((command) => !local.some((item) => item.name === command.name)), ...local];
|
|
1368
1622
|
}
|
|
1369
1623
|
submit() {
|
|
1370
1624
|
const text = this.state.draft.text.replace(/\s+$/, "");
|
|
@@ -1396,6 +1650,21 @@ class Controller extends SessionController {
|
|
|
1396
1650
|
if (args) void this.open(args, "Resumed session");
|
|
1397
1651
|
else void this.openSessions();
|
|
1398
1652
|
return true;
|
|
1653
|
+
case "config":
|
|
1654
|
+
case "skills":
|
|
1655
|
+
case "hooks":
|
|
1656
|
+
case "mcp":
|
|
1657
|
+
case "instructions":
|
|
1658
|
+
case "providers": {
|
|
1659
|
+
const use = name === "providers" && this.connection.settings ? /^use\s+(\S+)$/i.exec(args) : null;
|
|
1660
|
+
if (use) {
|
|
1661
|
+
void this.useProvider(use[1]);
|
|
1662
|
+
return true;
|
|
1663
|
+
}
|
|
1664
|
+
if (!this.connection.settings || args) return false;
|
|
1665
|
+
this.openPanel(name);
|
|
1666
|
+
return true;
|
|
1667
|
+
}
|
|
1399
1668
|
case "help":
|
|
1400
1669
|
this.set({ overlay: { kind: "help" } });
|
|
1401
1670
|
return true;
|
|
@@ -1417,6 +1686,7 @@ class Controller extends SessionController {
|
|
|
1417
1686
|
const clean = text.replace(/\r\n?/g, "\n");
|
|
1418
1687
|
const overlay = this.state.overlay;
|
|
1419
1688
|
if (picker(overlay)) return this.filter(picker(overlay).filter + clean.replace(/\n/g, " "));
|
|
1689
|
+
if (overlay?.kind === "panel") return this.pastePanelInput(clean);
|
|
1420
1690
|
if (overlay) return;
|
|
1421
1691
|
this.setDraft(insert(this.state.draft, clean));
|
|
1422
1692
|
}
|
|
@@ -1459,7 +1729,7 @@ class Controller extends SessionController {
|
|
|
1459
1729
|
if (!completion) return;
|
|
1460
1730
|
const item = completion.items[completion.index];
|
|
1461
1731
|
const command = completion.kind === "command" ? this.commands().find((entry) => `/${entry.name}` === item.value) : void 0;
|
|
1462
|
-
if (run2 && command && !command.hint) {
|
|
1732
|
+
if (run2 && command && (!command.hint || isPanel(command.name) && this.connection.settings)) {
|
|
1463
1733
|
this.set({ draft: draftOf(item.value) });
|
|
1464
1734
|
return this.submit();
|
|
1465
1735
|
}
|
|
@@ -1472,7 +1742,9 @@ class Controller extends SessionController {
|
|
|
1472
1742
|
const index = this.historyIndex < 0 ? this.history.length - 1 : this.historyIndex + step;
|
|
1473
1743
|
if (index < 0) return true;
|
|
1474
1744
|
this.historyIndex = index >= this.history.length ? -1 : index;
|
|
1475
|
-
this.
|
|
1745
|
+
const draft = this.historyIndex < 0 ? this.stash : draftOf(this.history[index]);
|
|
1746
|
+
this.dismissed = draft.text;
|
|
1747
|
+
this.setDraft(draft);
|
|
1476
1748
|
return true;
|
|
1477
1749
|
}
|
|
1478
1750
|
filter(text) {
|
|
@@ -1506,7 +1778,7 @@ class Controller extends SessionController {
|
|
|
1506
1778
|
if (now - this.lastInterrupt < 2e3) return this.exit();
|
|
1507
1779
|
this.lastInterrupt = now;
|
|
1508
1780
|
if (this.state.running || this.state.permission) this.cancel();
|
|
1509
|
-
else if (this.state.overlay) this.
|
|
1781
|
+
else if (this.state.overlay) this.closeOverlay();
|
|
1510
1782
|
else if (this.state.draft.text) this.setDraft(emptyDraft);
|
|
1511
1783
|
this.flash("Press Ctrl+C again to exit");
|
|
1512
1784
|
}
|
|
@@ -1611,7 +1883,7 @@ function OverlayView({ state, width: width2 }) {
|
|
|
1611
1883
|
] })
|
|
1612
1884
|
] });
|
|
1613
1885
|
}
|
|
1614
|
-
if (overlay.kind === "details") return null;
|
|
1886
|
+
if (overlay.kind === "details" || overlay.kind === "panel") return null;
|
|
1615
1887
|
const items = overlayItems(state);
|
|
1616
1888
|
const reasoning = overlay.kind === "models" ? state.status.reasoning : void 0;
|
|
1617
1889
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, width: width2, marginTop: 1, children: [
|
|
@@ -1636,6 +1908,20 @@ function OverlayView({ state, width: width2 }) {
|
|
|
1636
1908
|
] })
|
|
1637
1909
|
] });
|
|
1638
1910
|
}
|
|
1911
|
+
function lineEdit(draft, input, key) {
|
|
1912
|
+
if (key.leftArrow) return key.meta || key.ctrl ? wordLeft(draft) : left(draft);
|
|
1913
|
+
if (key.rightArrow) return key.meta || key.ctrl ? wordRight(draft) : right(draft);
|
|
1914
|
+
if (key.home || key.ctrl && input === "a") return home(draft);
|
|
1915
|
+
if (key.end || key.ctrl && input === "e") return end(draft);
|
|
1916
|
+
if (key.backspace) return key.meta || key.ctrl ? deleteWordLeft(draft) : backspace(draft);
|
|
1917
|
+
if (key.delete) return deleteForward(draft);
|
|
1918
|
+
if (key.ctrl && input === "w") return deleteWordLeft(draft);
|
|
1919
|
+
if (key.ctrl && input === "u") return killLineLeft(draft);
|
|
1920
|
+
if (key.ctrl && input === "k") return killLineRight(draft);
|
|
1921
|
+
if (key.meta && input === "b") return wordLeft(draft);
|
|
1922
|
+
if (key.meta && input === "f") return wordRight(draft);
|
|
1923
|
+
if (input && !key.ctrl && !key.meta && !key.tab) return insert(draft, input.replace(/\r\n?/g, "\n"));
|
|
1924
|
+
}
|
|
1639
1925
|
function permissionKey(controller, input, key) {
|
|
1640
1926
|
const pending = controller.getState().permission;
|
|
1641
1927
|
const choose = (index) => controller.decide(DECISIONS[index]);
|
|
@@ -1648,8 +1934,23 @@ function permissionKey(controller, input, key) {
|
|
|
1648
1934
|
const shortcut = { 1: 0, 2: 1, 3: 2, y: 0, a: 1, n: 2 }[input.toLowerCase()];
|
|
1649
1935
|
if (shortcut !== void 0) choose(shortcut);
|
|
1650
1936
|
}
|
|
1937
|
+
function panelKey(controller, input, key, overlay) {
|
|
1938
|
+
if (overlay.input) {
|
|
1939
|
+
if (key.escape) return controller.escapePanel();
|
|
1940
|
+
if (key.return) return controller.submitPanelInput();
|
|
1941
|
+
const next = lineEdit(overlay.input.draft, input.replace(/[\r\n]/g, ""), key);
|
|
1942
|
+
return next && controller.editPanelInput(next);
|
|
1943
|
+
}
|
|
1944
|
+
if (key.escape) return controller.escapePanel();
|
|
1945
|
+
if (key.upArrow || key.downArrow) return controller.movePanel(key.upArrow ? -1 : 1);
|
|
1946
|
+
if (key.return) return controller.enterPanel();
|
|
1947
|
+
if (overlay.menu !== void 0) return;
|
|
1948
|
+
if (input === " ") return controller.togglePanelRow();
|
|
1949
|
+
if (key.leftArrow || key.rightArrow) controller.cyclePanelChoice(key.leftArrow ? -1 : 1);
|
|
1950
|
+
}
|
|
1651
1951
|
function overlayKey(controller, input, key) {
|
|
1652
1952
|
const overlay = controller.getState().overlay;
|
|
1953
|
+
if (overlay.kind === "panel") return panelKey(controller, input, key, overlay);
|
|
1653
1954
|
if (overlay.kind === "details") {
|
|
1654
1955
|
if (key.escape || key.return || input === "q" || key.ctrl && input === "o") controller.set({ overlay: void 0 });
|
|
1655
1956
|
else if (key.leftArrow || key.rightArrow) controller.showDetails(key.leftArrow ? -1 : 1);
|
|
@@ -1699,18 +2000,130 @@ function handleKey(controller, input, key) {
|
|
|
1699
2000
|
if (moved) return edit(moved);
|
|
1700
2001
|
return void controller.recall(step);
|
|
1701
2002
|
}
|
|
1702
|
-
|
|
1703
|
-
if (
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
if (
|
|
1708
|
-
if (
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
2003
|
+
const next = lineEdit(draft, input, key);
|
|
2004
|
+
if (next) edit(next);
|
|
2005
|
+
}
|
|
2006
|
+
const SIZE = 12;
|
|
2007
|
+
function hints(overlay, row) {
|
|
2008
|
+
if (overlay.input) return "enter submit · esc cancel";
|
|
2009
|
+
if (overlay.busy) return "esc cancel";
|
|
2010
|
+
if (overlay.menu !== void 0) return "↑↓ select · enter run · esc back";
|
|
2011
|
+
const actions = row?.actions ?? [];
|
|
2012
|
+
return [
|
|
2013
|
+
"↑↓ move",
|
|
2014
|
+
row?.enabled !== void 0 ? "space toggle" : "",
|
|
2015
|
+
row?.choice ? "←→ change" : "",
|
|
2016
|
+
actions.length === 1 ? `enter ${actions[0].label.toLowerCase()}` : actions.length ? "enter actions" : "",
|
|
2017
|
+
"esc close"
|
|
2018
|
+
].filter(Boolean).join(" · ");
|
|
2019
|
+
}
|
|
2020
|
+
function Busy() {
|
|
2021
|
+
const theme = useTheme();
|
|
2022
|
+
const { frame } = useAnimation({ interval: 100 });
|
|
2023
|
+
return /* @__PURE__ */ jsxs(Text, { color: theme.accent, children: [
|
|
2024
|
+
SPINNER[frame % SPINNER.length],
|
|
2025
|
+
" Working…"
|
|
2026
|
+
] });
|
|
2027
|
+
}
|
|
2028
|
+
function RowLine({ row, selected: selected2 }) {
|
|
2029
|
+
const theme = useTheme();
|
|
2030
|
+
const value = row.choice && (row.choice.options.find((option2) => option2.value === row.choice.value)?.name ?? row.choice.value);
|
|
2031
|
+
return /* @__PURE__ */ jsxs(Text, { wrap: "truncate-end", children: [
|
|
2032
|
+
/* @__PURE__ */ jsxs(Text, { color: selected2 ? theme.accent : void 0, bold: selected2, children: [
|
|
2033
|
+
selected2 ? "❯ " : " ",
|
|
2034
|
+
row.enabled === void 0 ? "" : row.enabled ? "[x] " : "[ ] ",
|
|
2035
|
+
row.label
|
|
2036
|
+
] }),
|
|
2037
|
+
row.choice ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
2038
|
+
/* @__PURE__ */ jsx(Text, { color: theme.muted, children: " ◂ " }),
|
|
2039
|
+
/* @__PURE__ */ jsx(Text, { color: theme.accent, children: value }),
|
|
2040
|
+
/* @__PURE__ */ jsx(Text, { color: theme.muted, children: " ▸" })
|
|
2041
|
+
] }) : null,
|
|
2042
|
+
row.detail ? /* @__PURE__ */ jsxs(Text, { color: theme.muted, children: [
|
|
2043
|
+
" ",
|
|
2044
|
+
row.detail
|
|
2045
|
+
] }) : null
|
|
2046
|
+
] });
|
|
2047
|
+
}
|
|
2048
|
+
function Rows({ overlay }) {
|
|
2049
|
+
const theme = useTheme();
|
|
2050
|
+
const lines = [];
|
|
2051
|
+
panelRows(overlay.panel).forEach((row, at, rows2) => {
|
|
2052
|
+
if (row.section && row.section !== rows2[at - 1]?.section) lines.push({ heading: row.section });
|
|
2053
|
+
lines.push({ row, at });
|
|
2054
|
+
});
|
|
2055
|
+
if (!lines.length) return /* @__PURE__ */ jsx(Text, { color: theme.muted, children: "Nothing to show" });
|
|
2056
|
+
const { start, rows } = windowOf(lines, lines.findIndex((line) => "row" in line && line.at === overlay.index), SIZE);
|
|
2057
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2058
|
+
start > 0 ? /* @__PURE__ */ jsxs(Text, { color: theme.muted, children: [
|
|
2059
|
+
" ↑ ",
|
|
2060
|
+
start,
|
|
2061
|
+
" more"
|
|
2062
|
+
] }) : null,
|
|
2063
|
+
rows.map((line, at) => "heading" in line ? /* @__PURE__ */ jsx(Text, { bold: true, color: theme.muted, wrap: "truncate-end", children: line.heading }, `h${start + at}`) : /* @__PURE__ */ jsx(RowLine, { row: line.row, selected: line.at === overlay.index }, line.row.id)),
|
|
2064
|
+
start + rows.length < lines.length ? /* @__PURE__ */ jsxs(Text, { color: theme.muted, children: [
|
|
2065
|
+
" ↓ ",
|
|
2066
|
+
lines.length - start - rows.length,
|
|
2067
|
+
" more"
|
|
2068
|
+
] }) : null
|
|
2069
|
+
] });
|
|
2070
|
+
}
|
|
2071
|
+
function Menu({ row, index }) {
|
|
2072
|
+
const theme = useTheme();
|
|
2073
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2074
|
+
/* @__PURE__ */ jsx(Text, { bold: true, wrap: "truncate-end", children: row.label }),
|
|
2075
|
+
(row.actions ?? []).map((action, at) => /* @__PURE__ */ jsxs(Text, { color: at === index ? theme.accent : void 0, bold: at === index, wrap: "truncate-end", children: [
|
|
2076
|
+
at === index ? "❯ " : " ",
|
|
2077
|
+
action.label
|
|
2078
|
+
] }, action.id))
|
|
2079
|
+
] });
|
|
2080
|
+
}
|
|
2081
|
+
function Field({ input }) {
|
|
2082
|
+
const theme = useTheme();
|
|
2083
|
+
const inputs = input.action.inputs ?? [];
|
|
2084
|
+
const field = inputs[input.values.length];
|
|
2085
|
+
const mask = (text = "") => field?.secret ? "•".repeat(graphemes(text).length) : text;
|
|
2086
|
+
const line = cursorLines(input.draft)[0];
|
|
2087
|
+
const under = input.draft.cursor < input.draft.text.length ? mask(line.under) : " ";
|
|
2088
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2089
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, wrap: "truncate-end", children: [
|
|
2090
|
+
input.action.label,
|
|
2091
|
+
inputs.length > 1 ? /* @__PURE__ */ jsxs(Text, { color: theme.muted, children: [
|
|
2092
|
+
" (",
|
|
2093
|
+
input.values.length + 1,
|
|
2094
|
+
"/",
|
|
2095
|
+
inputs.length,
|
|
2096
|
+
")"
|
|
2097
|
+
] }) : null
|
|
2098
|
+
] }),
|
|
2099
|
+
/* @__PURE__ */ jsxs(Text, { wrap: "wrap", children: [
|
|
2100
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.accent, children: [
|
|
2101
|
+
field?.label,
|
|
2102
|
+
": "
|
|
2103
|
+
] }),
|
|
2104
|
+
mask(line.before),
|
|
2105
|
+
/* @__PURE__ */ jsx(Text, { inverse: true, children: under }),
|
|
2106
|
+
mask(line.after),
|
|
2107
|
+
input.draft.text ? null : /* @__PURE__ */ jsx(Text, { color: theme.muted, children: field?.placeholder ?? "" })
|
|
2108
|
+
] })
|
|
2109
|
+
] });
|
|
2110
|
+
}
|
|
2111
|
+
function PanelView({ overlay, width: width2 }) {
|
|
2112
|
+
const theme = useTheme();
|
|
2113
|
+
const { panel } = overlay;
|
|
2114
|
+
const row = panelRows(panel)[overlay.index];
|
|
2115
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: width2, children: [
|
|
2116
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, width: width2, marginTop: 1, children: [
|
|
2117
|
+
/* @__PURE__ */ jsx(Text, { bold: true, wrap: "truncate-end", children: panel?.title ?? `/${overlay.id}` }),
|
|
2118
|
+
overlay.loading && !panel ? /* @__PURE__ */ jsx(Text, { color: theme.muted, children: "Loading…" }) : null,
|
|
2119
|
+
!panel ? null : overlay.input ? /* @__PURE__ */ jsx(Field, { input: overlay.input }) : overlay.menu !== void 0 && row ? /* @__PURE__ */ jsx(Menu, { row, index: overlay.menu }) : /* @__PURE__ */ jsx(Rows, { overlay }),
|
|
2120
|
+
overlay.busy ? /* @__PURE__ */ jsx(Busy, {}) : null,
|
|
2121
|
+
overlay.error ? /* @__PURE__ */ jsx(Text, { color: theme.error, wrap: "wrap", children: overlay.error }) : null,
|
|
2122
|
+
panel?.footer ? /* @__PURE__ */ jsx(Text, { color: theme.muted, wrap: "wrap", children: panel.footer }) : null,
|
|
2123
|
+
/* @__PURE__ */ jsx(Text, { color: theme.muted, wrap: "truncate-end", children: hints(overlay, row) })
|
|
2124
|
+
] }),
|
|
2125
|
+
overlay.progress ? /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: overlay.progress }) : null
|
|
2126
|
+
] });
|
|
1714
2127
|
}
|
|
1715
2128
|
const MODE_LABELS = { ask: "◇ ask", "accept-edits": "⏵⏵ accept edits", "full-auto": "⏵⏵⏵ full auto" };
|
|
1716
2129
|
function PromptBox({ state, width: width2 }) {
|
|
@@ -1814,7 +2227,7 @@ function Screen({ controller, version }) {
|
|
|
1814
2227
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: width2, children: [
|
|
1815
2228
|
/* @__PURE__ */ jsx(Static, { items: state.transcript.flushed, children: (item) => /* @__PURE__ */ jsx(Box, { width: width2, flexDirection: "column", children: /* @__PURE__ */ jsx(ItemView, { item, context }) }, item.id) }),
|
|
1816
2229
|
state.transcript.live.map((item) => /* @__PURE__ */ jsx(ItemView, { item, live: true, context }, item.id)),
|
|
1817
|
-
state.permission ? /* @__PURE__ */ jsx(PermissionDialog, { pending: state.permission, controller, width: width2, cwd: state.cwd }) : state.overlay?.kind === "details" ? /* @__PURE__ */ jsx(Details, { id: state.overlay.id, transcript: state.transcript, context }, state.overlay.id) : state.overlay ? /* @__PURE__ */ jsx(OverlayView, { state, width: width2 }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2230
|
+
state.permission ? /* @__PURE__ */ jsx(PermissionDialog, { pending: state.permission, controller, width: width2, cwd: state.cwd }) : state.overlay?.kind === "details" ? /* @__PURE__ */ jsx(Details, { id: state.overlay.id, transcript: state.transcript, context }, state.overlay.id) : state.overlay?.kind === "panel" ? /* @__PURE__ */ jsx(PanelView, { overlay: state.overlay, width: width2 }) : state.overlay ? /* @__PURE__ */ jsx(OverlayView, { state, width: width2 }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1818
2231
|
state.running ? /* @__PURE__ */ jsx(Working, { state }) : null,
|
|
1819
2232
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
1820
2233
|
state.queued.map((text, index) => /* @__PURE__ */ jsxs(Text, { color: theme.muted, wrap: "truncate-end", children: [
|
|
@@ -1831,78 +2244,6 @@ function Screen({ controller, version }) {
|
|
|
1831
2244
|
function App({ controller, theme, version }) {
|
|
1832
2245
|
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx(Screen, { controller, version }) });
|
|
1833
2246
|
}
|
|
1834
|
-
const FILE_LIST_TTL_MS = 1e4;
|
|
1835
|
-
const FILE_MATCHES = 2e3;
|
|
1836
|
-
const HISTORY_LIMIT = 500;
|
|
1837
|
-
const subsequence = (query, path) => {
|
|
1838
|
-
let at = 0;
|
|
1839
|
-
for (const char of query) {
|
|
1840
|
-
at = path.indexOf(char, at) + 1;
|
|
1841
|
-
if (!at) return false;
|
|
1842
|
-
}
|
|
1843
|
-
return true;
|
|
1844
|
-
};
|
|
1845
|
-
function memoryTuiConnection(connection, options = {}) {
|
|
1846
|
-
const listed = /* @__PURE__ */ new Map();
|
|
1847
|
-
const files = (cwd) => {
|
|
1848
|
-
const cached = listed.get(cwd);
|
|
1849
|
-
if (cached && Date.now() - cached.at < FILE_LIST_TTL_MS) return cached.files;
|
|
1850
|
-
const entry = { at: Date.now(), files: walkFiles(cwd).catch(() => []) };
|
|
1851
|
-
listed.set(cwd, entry);
|
|
1852
|
-
return entry.files;
|
|
1853
|
-
};
|
|
1854
|
-
return {
|
|
1855
|
-
info: () => ({ provider: options.provider, usingChatGPTPlan: options.usingChatGPTPlan }),
|
|
1856
|
-
async newSession(cwd) {
|
|
1857
|
-
const state = await connection.newSession({ cwd });
|
|
1858
|
-
if (!options.mode || !state.modes || state.modes.currentModeId === options.mode) return state;
|
|
1859
|
-
await connection.setMode(state.sessionId, options.mode);
|
|
1860
|
-
return { ...state, modes: { ...state.modes, currentModeId: options.mode } };
|
|
1861
|
-
},
|
|
1862
|
-
loadSession: async (sessionId, cwd) => await connection.loadSession(sessionId, { cwd }),
|
|
1863
|
-
listSessions: async (cwd) => (await connection.listSessions(cwd)).sessions,
|
|
1864
|
-
prompt: (sessionId, prompt) => connection.prompt(sessionId, prompt),
|
|
1865
|
-
cancel: (sessionId) => connection.cancel(sessionId),
|
|
1866
|
-
setMode: (sessionId, modeId) => connection.setMode(sessionId, modeId),
|
|
1867
|
-
setModel: async (sessionId, modelId) => await connection.setModel(sessionId, modelId),
|
|
1868
|
-
setConfigOption: async (sessionId, configId, value) => await connection.setConfigOption(sessionId, configId, value),
|
|
1869
|
-
onUpdate: (listener) => connection.onUpdate((sessionId, update) => listener(sessionId, update)),
|
|
1870
|
-
onPermission: (handler) => connection.onPermission(async (request) => (await handler(request)).outcome),
|
|
1871
|
-
async listFiles(query, cwd) {
|
|
1872
|
-
const term = query.toLowerCase();
|
|
1873
|
-
return (await files(cwd)).filter((path) => subsequence(term, path.toLowerCase())).slice(0, FILE_MATCHES);
|
|
1874
|
-
}
|
|
1875
|
-
};
|
|
1876
|
-
}
|
|
1877
|
-
function fileHistory(path) {
|
|
1878
|
-
return {
|
|
1879
|
-
load() {
|
|
1880
|
-
try {
|
|
1881
|
-
return readFileSync(path, "utf8").split("\n").flatMap((line) => {
|
|
1882
|
-
try {
|
|
1883
|
-
const entry = JSON.parse(line);
|
|
1884
|
-
return typeof entry === "string" ? [entry] : [];
|
|
1885
|
-
} catch {
|
|
1886
|
-
return [];
|
|
1887
|
-
}
|
|
1888
|
-
}).slice(-HISTORY_LIMIT);
|
|
1889
|
-
} catch {
|
|
1890
|
-
return [];
|
|
1891
|
-
}
|
|
1892
|
-
},
|
|
1893
|
-
save(entry) {
|
|
1894
|
-
try {
|
|
1895
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
1896
|
-
appendFileSync(path, `${JSON.stringify(entry)}
|
|
1897
|
-
`, { mode: 384 });
|
|
1898
|
-
const lines = readFileSync(path, "utf8").split("\n").filter(Boolean);
|
|
1899
|
-
if (lines.length > HISTORY_LIMIT * 2) writeFileSync(path, `${lines.slice(-HISTORY_LIMIT).join("\n")}
|
|
1900
|
-
`, { mode: 384 });
|
|
1901
|
-
} catch {
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
2247
|
async function run(connection, options) {
|
|
1907
2248
|
const controller = new Controller(connection, { cwd: options.cwd, history: options.history });
|
|
1908
2249
|
const exited = new Promise((resolve) => {
|
|
@@ -1951,4 +2292,4 @@ export {
|
|
|
1951
2292
|
run,
|
|
1952
2293
|
runTui
|
|
1953
2294
|
};
|
|
1954
|
-
//# sourceMappingURL=index-
|
|
2295
|
+
//# sourceMappingURL=index-T_6HaJx-.js.map
|