@aayambansal/squint 0.4.6 → 0.4.7

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.
@@ -15,7 +15,7 @@ function sandboxExists(cwd) {
15
15
  }
16
16
  function openSandbox(cwd) {
17
17
  const dir = sandboxDir(cwd);
18
- void import("./state-QOS7WCZO.js").then(({ ensureSquintIgnore }) => ensureSquintIgnore(cwd)).catch(() => {
18
+ void import("./state-PLY7YAD2.js").then(({ ensureSquintIgnore }) => ensureSquintIgnore(cwd)).catch(() => {
19
19
  });
20
20
  if (sandboxExists(cwd)) return { dir, reused: true };
21
21
  fs.rmSync(dir, { recursive: true, force: true });
@@ -445,11 +445,17 @@ var WEBMCP_SHIM = `(() => {
445
445
  if (t && t.name) window.__squintWebMcp.push(t.name + (t.description ? ' \u2014 ' + t.description : ''));
446
446
  }
447
447
  };
448
- const target = navigator.modelContext || (navigator.modelContext = {});
449
- const provide = target.provideContext && target.provideContext.bind(target);
450
- target.provideContext = (params) => { record(params && params.tools); return provide ? provide(params) : undefined; };
451
- const register = target.registerTool && target.registerTool.bind(target);
452
- target.registerTool = (tool) => { record([tool]); return register ? register(tool) : undefined; };
448
+ const wrap = (target) => {
449
+ const provide = target.provideContext && target.provideContext.bind(target);
450
+ target.provideContext = (params) => { record(params && params.tools); return provide ? provide(params) : undefined; };
451
+ const register = target.registerTool && target.registerTool.bind(target);
452
+ target.registerTool = (tool) => { record([tool]); return register ? register(tool) : undefined; };
453
+ return target;
454
+ };
455
+ // The spec moved the API to document.modelContext (Chrome 150 drops
456
+ // the navigator location); shim both so either registration is seen.
457
+ wrap(document.modelContext || (document.modelContext = {}));
458
+ wrap(navigator.modelContext || (navigator.modelContext = {}));
453
459
  })()`;
454
460
  async function cdpCapture(chromePath, url, outDir, viewports, settleMs = 2500, audit = false, checks = []) {
455
461
  const { child, wsUrl, profileDir } = await launchChrome(chromePath);
@@ -0,0 +1,587 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ completeCommand
4
+ } from "./chunk-43NQNIJY.js";
5
+ import {
6
+ Session
7
+ } from "./chunk-7MOKOZOR.js";
8
+
9
+ // src/tui/App.tsx
10
+ import { Box as Box3, Static, Text as Text3, useApp, useInput } from "ink";
11
+ import { InkPictureProvider } from "ink-picture";
12
+ import path from "path";
13
+ import { useMemo, useRef, useState as useState2, useSyncExternalStore } from "react";
14
+
15
+ // src/tui/lineEditor.ts
16
+ var emptyLine = { text: "", cursor: 0 };
17
+ function fromText(text) {
18
+ return { text, cursor: text.length };
19
+ }
20
+ function insert(line, str) {
21
+ const clean = str.replace(/\r/g, "").replace(/\n+/g, " ");
22
+ return {
23
+ text: line.text.slice(0, line.cursor) + clean + line.text.slice(line.cursor),
24
+ cursor: line.cursor + clean.length
25
+ };
26
+ }
27
+ function backspace(line) {
28
+ if (line.cursor === 0) return line;
29
+ return {
30
+ text: line.text.slice(0, line.cursor - 1) + line.text.slice(line.cursor),
31
+ cursor: line.cursor - 1
32
+ };
33
+ }
34
+ function left(line) {
35
+ return { ...line, cursor: Math.max(0, line.cursor - 1) };
36
+ }
37
+ function right(line) {
38
+ return { ...line, cursor: Math.min(line.text.length, line.cursor + 1) };
39
+ }
40
+ function home(line) {
41
+ return { ...line, cursor: 0 };
42
+ }
43
+ function end(line) {
44
+ return { ...line, cursor: line.text.length };
45
+ }
46
+ var isWordChar = (ch) => /[\p{L}\p{N}_/@.-]/u.test(ch);
47
+ function wordLeft(line) {
48
+ let i = line.cursor;
49
+ while (i > 0 && !isWordChar(line.text[i - 1])) i--;
50
+ while (i > 0 && isWordChar(line.text[i - 1])) i--;
51
+ return { ...line, cursor: i };
52
+ }
53
+ function wordRight(line) {
54
+ let i = line.cursor;
55
+ const n = line.text.length;
56
+ while (i < n && !isWordChar(line.text[i])) i++;
57
+ while (i < n && isWordChar(line.text[i])) i++;
58
+ return { ...line, cursor: i };
59
+ }
60
+ function killToEnd(line) {
61
+ return { text: line.text.slice(0, line.cursor), cursor: line.cursor };
62
+ }
63
+ function killToStart(line) {
64
+ return { text: line.text.slice(line.cursor), cursor: 0 };
65
+ }
66
+ function killWordBack(line) {
67
+ const target = wordLeft(line).cursor;
68
+ return {
69
+ text: line.text.slice(0, target) + line.text.slice(line.cursor),
70
+ cursor: target
71
+ };
72
+ }
73
+
74
+ // src/tui/markdown.tsx
75
+ import { Box, Text } from "ink";
76
+
77
+ // src/tui/themeContext.tsx
78
+ import { createContext, useContext } from "react";
79
+
80
+ // src/tui/theme.ts
81
+ var THEMES = {
82
+ amber: {
83
+ name: "amber",
84
+ accent: "#e8a33d",
85
+ dim: "gray",
86
+ user: "#7aa2f7",
87
+ error: "#f7768e",
88
+ success: "#9ece6a",
89
+ tool: "#7dcfff"
90
+ },
91
+ ocean: {
92
+ name: "ocean",
93
+ accent: "#56b6c2",
94
+ dim: "gray",
95
+ user: "#61afef",
96
+ error: "#e06c75",
97
+ success: "#98c379",
98
+ tool: "#c678dd"
99
+ },
100
+ moss: {
101
+ name: "moss",
102
+ accent: "#a7c080",
103
+ dim: "gray",
104
+ user: "#7fbbb3",
105
+ error: "#e67e80",
106
+ success: "#83c092",
107
+ tool: "#d699b6"
108
+ },
109
+ rose: {
110
+ name: "rose",
111
+ accent: "#ebbcba",
112
+ dim: "gray",
113
+ user: "#9ccfd8",
114
+ error: "#eb6f92",
115
+ success: "#31748f",
116
+ tool: "#c4a7e7"
117
+ },
118
+ light: {
119
+ name: "light",
120
+ accent: "#9a6b1f",
121
+ dim: "#6b6f76",
122
+ user: "#2a5db0",
123
+ error: "#c4322e",
124
+ success: "#3d7a37",
125
+ tool: "#0f7b8a"
126
+ },
127
+ mono: {
128
+ name: "mono",
129
+ accent: "white",
130
+ dim: "gray",
131
+ user: "white",
132
+ error: "white",
133
+ success: "white",
134
+ tool: "gray"
135
+ }
136
+ };
137
+ var DEFAULT_THEME = "amber";
138
+ function resolveTheme(name, env = process.env) {
139
+ if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return THEMES.mono;
140
+ return THEMES[name ?? DEFAULT_THEME] ?? THEMES[DEFAULT_THEME];
141
+ }
142
+ var theme = THEMES[DEFAULT_THEME];
143
+
144
+ // src/tui/themeContext.tsx
145
+ var ThemeContext = createContext(theme);
146
+ var ThemeProvider = ThemeContext.Provider;
147
+ function useTheme() {
148
+ return useContext(ThemeContext);
149
+ }
150
+
151
+ // src/tui/markdown.tsx
152
+ import { jsx, jsxs } from "react/jsx-runtime";
153
+ var INLINE_RE = /(\*\*[^*]+\*\*|\*[^*\s][^*]*\*|`[^`]+`)/g;
154
+ function parseInline(text) {
155
+ const segments = [];
156
+ let last = 0;
157
+ for (const match of text.matchAll(INLINE_RE)) {
158
+ if (match.index > last) segments.push({ text: text.slice(last, match.index) });
159
+ const token = match[0];
160
+ if (token.startsWith("**")) segments.push({ text: token.slice(2, -2), bold: true });
161
+ else if (token.startsWith("`")) segments.push({ text: token.slice(1, -1), code: true });
162
+ else segments.push({ text: token.slice(1, -1), italic: true });
163
+ last = match.index + token.length;
164
+ }
165
+ if (last < text.length) segments.push({ text: text.slice(last) });
166
+ return segments;
167
+ }
168
+ function parseBlocks(text) {
169
+ const blocks = [];
170
+ let code = null;
171
+ for (const rawLine of text.split("\n")) {
172
+ const line = rawLine.replace(/\s+$/, "");
173
+ if (code) {
174
+ if (/^\s*```/.test(line)) {
175
+ blocks.push({ type: "code", ...code });
176
+ code = null;
177
+ } else {
178
+ code.lines.push(rawLine);
179
+ }
180
+ continue;
181
+ }
182
+ const fence = /^\s*```(\S*)/.exec(line);
183
+ if (fence) {
184
+ code = { lang: fence[1] ?? "", lines: [] };
185
+ continue;
186
+ }
187
+ const heading = /^(#{1,4})\s+(.*)$/.exec(line);
188
+ if (heading) {
189
+ blocks.push({ type: "heading", level: heading[1].length, text: heading[2] });
190
+ continue;
191
+ }
192
+ if (/^\s*([-*_])\s*\1\s*\1[\s\-*_]*$/.test(line) && line.trim().length >= 3) {
193
+ blocks.push({ type: "hr" });
194
+ continue;
195
+ }
196
+ const item = /^(\s*)[-*+]\s+(.*)$/.exec(line);
197
+ if (item) {
198
+ blocks.push({ type: "item", text: item[2], indent: Math.floor(item[1].length / 2) });
199
+ continue;
200
+ }
201
+ const ordered = /^(\s*)(\d+)[.)]\s+(.*)$/.exec(line);
202
+ if (ordered) {
203
+ blocks.push({
204
+ type: "item",
205
+ text: ordered[3],
206
+ indent: Math.floor(ordered[1].length / 2),
207
+ ordered: ordered[2]
208
+ });
209
+ continue;
210
+ }
211
+ const quote = /^\s*>\s?(.*)$/.exec(line);
212
+ if (quote) {
213
+ blocks.push({ type: "quote", text: quote[1] });
214
+ continue;
215
+ }
216
+ blocks.push({ type: "para", text: line });
217
+ }
218
+ if (code) blocks.push({ type: "code", ...code });
219
+ return blocks;
220
+ }
221
+ function Inline({ text, dim }) {
222
+ const theme2 = useTheme();
223
+ const segments = parseInline(text);
224
+ return /* @__PURE__ */ jsx(Text, { wrap: "wrap", dimColor: dim, children: segments.map(
225
+ (segment, index) => segment.code ? /* @__PURE__ */ jsx(Text, { color: theme2.tool, children: segment.text }, index) : /* @__PURE__ */ jsx(Text, { bold: segment.bold, italic: segment.italic, children: segment.text }, index)
226
+ ) });
227
+ }
228
+ function Markdown({ text }) {
229
+ const theme2 = useTheme();
230
+ const blocks = parseBlocks(text);
231
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: blocks.map((block, index) => {
232
+ switch (block.type) {
233
+ case "heading":
234
+ return /* @__PURE__ */ jsx(Text, { bold: true, color: block.level <= 2 ? theme2.accent : void 0, wrap: "wrap", children: block.text }, index);
235
+ case "item":
236
+ return /* @__PURE__ */ jsxs(Box, { paddingLeft: 1 + block.indent * 2, children: [
237
+ /* @__PURE__ */ jsxs(Text, { color: theme2.accent, children: [
238
+ block.ordered ? `${block.ordered}.` : "\u2022",
239
+ " "
240
+ ] }),
241
+ /* @__PURE__ */ jsx(Inline, { text: block.text })
242
+ ] }, index);
243
+ case "quote":
244
+ return /* @__PURE__ */ jsxs(Box, { paddingLeft: 1, children: [
245
+ /* @__PURE__ */ jsx(Text, { color: theme2.dim, children: "\u2502 " }),
246
+ /* @__PURE__ */ jsx(Inline, { text: block.text, dim: true })
247
+ ] }, index);
248
+ case "code":
249
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: 1, children: block.lines.map((line, lineIndex) => /* @__PURE__ */ jsxs(Text, { children: [
250
+ /* @__PURE__ */ jsx(Text, { color: theme2.dim, children: "\u258F " }),
251
+ /* @__PURE__ */ jsx(Text, { color: theme2.tool, children: line.length > 0 ? line : " " })
252
+ ] }, lineIndex)) }, index);
253
+ case "hr":
254
+ return /* @__PURE__ */ jsx(Text, { color: theme2.dim, children: "\u2500".repeat(32) }, index);
255
+ case "para":
256
+ return block.text.length === 0 ? /* @__PURE__ */ jsx(Text, { children: " " }, index) : /* @__PURE__ */ jsx(Inline, { text: block.text }, index);
257
+ }
258
+ }) });
259
+ }
260
+
261
+ // src/tui/messages.tsx
262
+ import { Box as Box2, Text as Text2 } from "ink";
263
+ import Image from "ink-picture";
264
+ import { useEffect, useState } from "react";
265
+
266
+ // src/tui/termImage.ts
267
+ function supportsInlineImages(env = process.env) {
268
+ if (env.TMUX) return false;
269
+ if (env.TERM === "xterm-kitty" || env.KITTY_WINDOW_ID) return true;
270
+ if (env.TERM === "xterm-ghostty" || env.GHOSTTY_RESOURCES_DIR) return true;
271
+ if (env.TERM_PROGRAM === "WezTerm") return true;
272
+ if (env.TERM_PROGRAM === "iTerm.app") return true;
273
+ return false;
274
+ }
275
+
276
+ // src/tui/messages.tsx
277
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
278
+ var INLINE_IMAGES = supportsInlineImages();
279
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
280
+ var PHRASES = ["working", "thinking", "squinting", "crafting", "still at it"];
281
+ function WorkingLine({ startedAt }) {
282
+ const theme2 = useTheme();
283
+ const [frame, setFrame] = useState(0);
284
+ const [elapsed, setElapsed] = useState(0);
285
+ useEffect(() => {
286
+ const timer = setInterval(() => {
287
+ setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
288
+ setElapsed(Math.floor((Date.now() - startedAt) / 1e3));
289
+ }, 80);
290
+ return () => clearInterval(timer);
291
+ }, [startedAt]);
292
+ const phrase = PHRASES[Math.min(Math.floor(elapsed / 8), PHRASES.length - 1)];
293
+ return /* @__PURE__ */ jsxs2(Text2, { children: [
294
+ /* @__PURE__ */ jsx2(Text2, { color: theme2.accent, children: SPINNER_FRAMES[frame] }),
295
+ /* @__PURE__ */ jsxs2(Text2, { color: theme2.dim, children: [
296
+ " ",
297
+ phrase,
298
+ "\u2026 ",
299
+ elapsed,
300
+ "s \xB7 esc to interrupt"
301
+ ] })
302
+ ] });
303
+ }
304
+ function toolGlyph(text) {
305
+ const name = text.split(/[\s·]/, 1)[0]?.toLowerCase() ?? "";
306
+ if (name.includes("todo")) return "\u2630";
307
+ if (name.includes("read") || name.includes("view") || name.includes("cat")) return "\u2299";
308
+ if (name.includes("edit") || name.includes("write") || name.includes("patch") || name.includes("apply")) return "\u270E";
309
+ if (name.includes("bash") || name.includes("shell") || name.includes("exec") || name.includes("command")) return "$";
310
+ if (name.includes("grep") || name.includes("glob") || name.includes("search") || name.includes("find")) return "\u2315";
311
+ if (name.includes("web") || name.includes("fetch") || name.includes("http")) return "\u21E3";
312
+ if (name.includes("task") || name.includes("agent")) return "\u25C7";
313
+ return "\u2699";
314
+ }
315
+ function MessageLine({ message }) {
316
+ const theme2 = useTheme();
317
+ switch (message.role) {
318
+ case "user":
319
+ return /* @__PURE__ */ jsxs2(Text2, { color: theme2.user, wrap: "wrap", children: [
320
+ "\u276F ",
321
+ message.text
322
+ ] });
323
+ case "assistant":
324
+ return /* @__PURE__ */ jsx2(Markdown, { text: message.text });
325
+ case "status":
326
+ return /* @__PURE__ */ jsxs2(Text2, { color: theme2.dim, wrap: "wrap", children: [
327
+ "\xB7 ",
328
+ message.text
329
+ ] });
330
+ case "tool":
331
+ return /* @__PURE__ */ jsxs2(Text2, { color: theme2.tool, wrap: "wrap", children: [
332
+ toolGlyph(message.text),
333
+ " ",
334
+ message.text
335
+ ] });
336
+ case "thinking":
337
+ return /* @__PURE__ */ jsx2(Text2, { color: theme2.dim, italic: true, wrap: "wrap", children: message.text });
338
+ case "error":
339
+ return /* @__PURE__ */ jsxs2(Text2, { color: theme2.error, wrap: "wrap", children: [
340
+ "\u2717 ",
341
+ message.text
342
+ ] });
343
+ case "image":
344
+ if (!INLINE_IMAGES) {
345
+ return /* @__PURE__ */ jsxs2(Text2, { color: theme2.dim, wrap: "wrap", children: [
346
+ "\u25A3 ",
347
+ message.text
348
+ ] });
349
+ }
350
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
351
+ /* @__PURE__ */ jsx2(Image, { src: message.text, width: 48, height: 14, alt: "screenshot" }),
352
+ /* @__PURE__ */ jsxs2(Text2, { color: theme2.dim, children: [
353
+ "\u25A3 ",
354
+ message.text
355
+ ] })
356
+ ] });
357
+ }
358
+ }
359
+
360
+ // src/tui/App.tsx
361
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
362
+ function App({
363
+ cwd,
364
+ initialEngine,
365
+ initialModel,
366
+ autoDev,
367
+ autoFix,
368
+ autoProbe,
369
+ autoCheck,
370
+ autoReview,
371
+ fixModel,
372
+ bell,
373
+ budgetUsd,
374
+ initialTheme,
375
+ attachTo
376
+ }) {
377
+ const { exit } = useApp();
378
+ const [themeName, setThemeName] = useState2(() => resolveTheme(initialTheme).name);
379
+ const theme2 = resolveTheme(themeName);
380
+ const sessionRef = useRef(null);
381
+ if (!sessionRef.current && attachTo) sessionRef.current = attachTo;
382
+ if (!sessionRef.current) {
383
+ sessionRef.current = new Session({
384
+ cwd,
385
+ engineId: initialEngine,
386
+ model: initialModel,
387
+ autoDev,
388
+ autoFix,
389
+ autoProbe,
390
+ autoCheck,
391
+ autoReview,
392
+ fixModel,
393
+ budgetUsd,
394
+ // Delay lets the goodbye summary land in the Static scrollback.
395
+ onQuit: () => setTimeout(() => exit(), 60)
396
+ });
397
+ }
398
+ const session = sessionRef.current;
399
+ const state = useSyncExternalStore(
400
+ useMemo(() => (listener) => session.subscribe(listener), [session]),
401
+ () => session.getState()
402
+ );
403
+ const [line, setLine] = useState2(emptyLine);
404
+ const historyRef = useRef([]);
405
+ const historyIndexRef = useRef(-1);
406
+ const ctrlCArmedAtRef = useRef(0);
407
+ const wasRunningRef = useRef(false);
408
+ if (wasRunningRef.current && !state.running && bell !== false) {
409
+ process.stdout.write("\x07");
410
+ }
411
+ wasRunningRef.current = state.running;
412
+ useInput((char, key) => {
413
+ if (key.ctrl && char === "c") {
414
+ const now = Date.now();
415
+ if (now - ctrlCArmedAtRef.current < 2e3) {
416
+ session.note(session.summary());
417
+ session.dispose();
418
+ setTimeout(() => exit(), 60);
419
+ } else {
420
+ ctrlCArmedAtRef.current = now;
421
+ session.note("press ctrl+c again to exit");
422
+ }
423
+ return;
424
+ }
425
+ if (key.escape && state.running) {
426
+ session.interrupt();
427
+ return;
428
+ }
429
+ if (key.tab && key.shift) {
430
+ session.cycleMode();
431
+ return;
432
+ }
433
+ if (key.tab && line.text.startsWith("/") && !line.text.includes(" ")) {
434
+ const matches = completeCommand(line.text.slice(1));
435
+ if (matches.length > 0) {
436
+ setLine(fromText(`/${matches[0].name}${matches[0].args ? " " : ""}`));
437
+ }
438
+ return;
439
+ }
440
+ if (key.return) {
441
+ const value = line.text.trim();
442
+ setLine(emptyLine);
443
+ historyIndexRef.current = -1;
444
+ if (!value) return;
445
+ historyRef.current.push(value);
446
+ if (value === "/theme" || value.startsWith("/theme ")) {
447
+ const requested = value.slice("/theme".length).trim();
448
+ if (!requested) {
449
+ session.note(`themes: ${Object.keys(THEMES).join(", ")} \u2014 /theme <name>`);
450
+ } else if (THEMES[requested]) {
451
+ setThemeName(requested);
452
+ session.note(`theme \u2192 ${requested}`);
453
+ } else {
454
+ session.note(`unknown theme "${requested}" \u2014 themes: ${Object.keys(THEMES).join(", ")}`);
455
+ }
456
+ return;
457
+ }
458
+ session.input(value);
459
+ return;
460
+ }
461
+ if (key.upArrow) {
462
+ const history = historyRef.current;
463
+ if (history.length === 0) return;
464
+ const next = historyIndexRef.current === -1 ? history.length - 1 : Math.max(historyIndexRef.current - 1, 0);
465
+ historyIndexRef.current = next;
466
+ setLine(fromText(history[next] ?? ""));
467
+ return;
468
+ }
469
+ if (key.downArrow) {
470
+ const history = historyRef.current;
471
+ if (historyIndexRef.current === -1) return;
472
+ const next = historyIndexRef.current + 1;
473
+ if (next >= history.length) {
474
+ historyIndexRef.current = -1;
475
+ setLine(emptyLine);
476
+ } else {
477
+ historyIndexRef.current = next;
478
+ setLine(fromText(history[next] ?? ""));
479
+ }
480
+ return;
481
+ }
482
+ if (key.leftArrow) {
483
+ setLine((prev) => key.meta ? wordLeft(prev) : left(prev));
484
+ return;
485
+ }
486
+ if (key.rightArrow) {
487
+ setLine((prev) => key.meta ? wordRight(prev) : right(prev));
488
+ return;
489
+ }
490
+ if (key.backspace || key.delete) {
491
+ setLine((prev) => key.meta ? killWordBack(prev) : backspace(prev));
492
+ return;
493
+ }
494
+ if (key.ctrl) {
495
+ switch (char) {
496
+ case "a":
497
+ setLine(home);
498
+ return;
499
+ case "e":
500
+ setLine(end);
501
+ return;
502
+ case "k":
503
+ setLine(killToEnd);
504
+ return;
505
+ case "u":
506
+ setLine(killToStart);
507
+ return;
508
+ case "w":
509
+ setLine(killWordBack);
510
+ return;
511
+ }
512
+ return;
513
+ }
514
+ if (char && !key.meta) {
515
+ setLine((prev) => insert(prev, char));
516
+ }
517
+ });
518
+ const devBadge = state.devState === "running" ? ` \xB7 ${state.devUrl ?? "dev running"}` : state.devState === "starting" ? " \xB7 dev starting\u2026" : state.devState === "crashed" ? " \xB7 dev crashed" : "";
519
+ const totalsBadge = state.totals.turns > 0 ? ` \xB7 ${state.totals.turns} turn${state.totals.turns === 1 ? "" : "s"}${state.totals.costUsd > 0 ? ` \xB7 $${state.totals.costUsd.toFixed(2)}` : ""}` : "";
520
+ return /* @__PURE__ */ jsx3(ThemeProvider, { value: theme2, children: /* @__PURE__ */ jsx3(InkPictureProvider, { children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
521
+ /* @__PURE__ */ jsx3(Static, { items: state.items, children: (item) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(MessageLine, { message: item }) }, item.id) }),
522
+ state.liveText.length > 0 && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Markdown, { text: state.liveText }) }),
523
+ state.items.length === 0 && state.liveText.length === 0 && !state.running && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
524
+ /* @__PURE__ */ jsx3(Text3, { color: theme2.dim, children: "describe what to build \u2014 or try:" }),
525
+ /* @__PURE__ */ jsx3(Text3, { color: theme2.dim, children: " /dev start the preview server \xB7 /review after a change \xB7 /variants 3 explore wide" }),
526
+ /* @__PURE__ */ jsx3(Text3, { color: theme2.dim, children: " shift+tab cycles plan/safe/yolo \xB7 type while the agent works to queue asks" })
527
+ ] }),
528
+ state.running && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(WorkingLine, { startedAt: state.runStartedAt }) }),
529
+ state.queue.map((queued, index) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: theme2.dim, children: [
530
+ "\u22EF ",
531
+ index + 1,
532
+ ". ",
533
+ queued,
534
+ index === state.queue.length - 1 ? " (/queue drop <n> removes)" : ""
535
+ ] }) }, index)),
536
+ line.text.startsWith("/") && !line.text.includes(" ") && /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: completeCommand(line.text.slice(1)).slice(0, 5).map((command, index) => /* @__PURE__ */ jsxs3(Text3, { color: index === 0 ? theme2.accent : theme2.dim, children: [
537
+ "/",
538
+ command.name,
539
+ command.args ? ` ${command.args}` : "",
540
+ " ",
541
+ /* @__PURE__ */ jsxs3(Text3, { color: theme2.dim, children: [
542
+ "\u2014 ",
543
+ command.description
544
+ ] })
545
+ ] }, command.name)) }),
546
+ /* @__PURE__ */ jsx3(Box3, { marginTop: state.running ? 0 : 1, children: /* @__PURE__ */ jsxs3(Text3, { children: [
547
+ /* @__PURE__ */ jsx3(Text3, { color: theme2.accent, children: "\u276F " }),
548
+ line.text.slice(0, line.cursor),
549
+ /* @__PURE__ */ jsx3(Text3, { inverse: true, children: line.text[line.cursor] ?? " " }),
550
+ line.text.slice(line.cursor + 1)
551
+ ] }) }),
552
+ /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: theme2.dim, children: [
553
+ /* @__PURE__ */ jsxs3(
554
+ Text3,
555
+ {
556
+ color: state.mode === "yolo" ? theme2.error : state.mode === "plan" ? theme2.user : theme2.dim,
557
+ bold: state.mode !== "safe",
558
+ children: [
559
+ "[",
560
+ state.mode,
561
+ "]"
562
+ ]
563
+ }
564
+ ),
565
+ state.sandbox && /* @__PURE__ */ jsx3(Text3, { color: theme2.accent, bold: true, children: " [sandbox]" }),
566
+ " ",
567
+ state.engineId,
568
+ state.model ? ` \xB7 ${state.model}` : "",
569
+ " \xB7 ",
570
+ path.basename(cwd),
571
+ devBadge,
572
+ totalsBadge,
573
+ state.problems.length > 0 && /* @__PURE__ */ jsxs3(Text3, { color: theme2.error, children: [
574
+ " \xB7 ",
575
+ state.problems.length,
576
+ " problem",
577
+ state.problems.length === 1 ? "" : "s"
578
+ ] }),
579
+ " ",
580
+ "\xB7 shift+tab mode \xB7 /help"
581
+ ] }) })
582
+ ] }) }) });
583
+ }
584
+
585
+ export {
586
+ App
587
+ };