reasonix 0.24.1 → 0.25.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.
@@ -1,944 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- CharPool,
4
- HyperlinkPool,
5
- StylePool,
6
- ink_compat_exports,
7
- mount,
8
- useKeystroke
9
- } from "./chunk-V3DLUJY6.js";
10
- import "./chunk-2H7UOFLK.js";
11
-
12
- // src/cli/commands/card-demo.tsx
13
- import React, { useEffect, useRef, useState } from "react";
14
- var BRAND = "#79c0ff";
15
- var FAINT = "#6e7681";
16
- var META = "#8b949e";
17
- var ACCENT = "#d2a8ff";
18
- var VIOLET = "#b395f5";
19
- var OK = "#7ee787";
20
- var WARN = "#f0b07d";
21
- var ERR = "#ff8b81";
22
- var PEND = "#484f58";
23
- var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
24
- var TOOL_GLYPH = {
25
- read: "\u25A4",
26
- write: "\u25A5",
27
- bash: "\u25B6",
28
- search: "\u2299",
29
- fetch: "\u232C",
30
- mcp: "\u2295",
31
- patch: "\u270E"
32
- };
33
- var TOOL_COLOR = {
34
- read: BRAND,
35
- write: WARN,
36
- bash: ACCENT,
37
- search: BRAND,
38
- fetch: VIOLET,
39
- mcp: VIOLET,
40
- patch: WARN
41
- };
42
- var TOOL_TAIL_LEN = {
43
- read: 2,
44
- write: 2,
45
- bash: 5,
46
- search: 2,
47
- fetch: 2,
48
- mcp: 5,
49
- patch: 2
50
- };
51
- function StaticRow({ item }) {
52
- switch (item.kind) {
53
- case "user":
54
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT }, "\u203A"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, item.text)));
55
- case "reasoning":
56
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: item.aborted ? ERR : ACCENT }, "\u25C6"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: item.aborted ? ERR : ACCENT, bold: true }, item.aborted ? "reasoning (aborted)" : "reasoning"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${item.paragraphs}\xB6 \xB7 ${item.tokens} tok \xB7 ${item.seconds.toFixed(1)}s`)), item.tail.map((line, i) => (
57
- // biome-ignore lint/suspicious/noArrayIndexKey: tail preview, positional
58
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: `r-${i}`, paddingLeft: 2 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true }, line))
59
- )));
60
- case "tool":
61
- return /* @__PURE__ */ React.createElement(ToolStaticRow, { item });
62
- case "plan":
63
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT }, "\u229E"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT, bold: true }, "plan"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${item.steps.length} steps \xB7 ${item.seconds.toFixed(1)}s`)), item.steps.map((step) => /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: step.label, paddingLeft: 2, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: planColor(step.status) }, planGlyph(step.status)), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: step.status === "skipped" }, step.label), step.note ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `\xB7 ${step.note}`) : null)));
64
- case "response":
65
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: item.aborted ? ERR : OK }, "\u2039"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: item.aborted ? ERR : OK, bold: true }, item.aborted ? "response (truncated by esc)" : "response")), item.lines.map((line, i) => (
66
- // biome-ignore lint/suspicious/noArrayIndexKey: response body lines positional
67
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: `resp-${i}`, paddingLeft: 2 }, line.kind === "code" ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND }, line.text || " ") : line.kind === "header" ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { bold: true }, line.text || " ") : line.kind === "list" ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, ` \u2022 ${line.text}`) : /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, line.text || " "))
68
- )));
69
- case "diff":
70
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: WARN }, "\xB1"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { bold: true }, item.file), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: OK }, `+${item.added}`), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ERR }, `-${item.removed}`)), item.preview.map((row, i) => (
71
- // biome-ignore lint/suspicious/noArrayIndexKey: diff preview lines positional
72
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: `d-${i}`, paddingLeft: 2, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: row.kind === "+" ? OK : row.kind === "-" ? ERR : FAINT }, row.kind), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: row.kind === " " }, row.text))
73
- )));
74
- case "subagent":
75
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: item.ok ? VIOLET : ERR }, item.ok ? "\u232C" : "\u2716"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: VIOLET, bold: true }, "subagent"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, item.task), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `\xB7 ${item.seconds.toFixed(1)}s`)), item.children.map((c, i) => (
76
- // biome-ignore lint/suspicious/noArrayIndexKey: subagent children positional
77
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: `sub-${i}`, paddingLeft: 2, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: VIOLET }, "\u258E"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: subChildColor(c.kind) }, subChildGlyph(c.kind)), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true }, c.summary))
78
- )));
79
- case "usage":
80
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 2, marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND }, "\u03A3"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: META }, `in ${item.inputTokens} \xB7 out ${item.outputTokens} \xB7 $${item.totalCost.toFixed(4)}`));
81
- case "error":
82
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ERR }, "\u2716"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ERR }, item.message));
83
- case "warn":
84
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: WARN }, "\u26A0"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: WARN }, item.message));
85
- }
86
- }
87
- function ToolStaticRow({ item }) {
88
- const glyph = item.status === "ok" ? "\u2713" : item.status === "rejected" ? "\u2717" : "\u2716";
89
- const headerColor = item.status === "ok" ? TOOL_COLOR[item.tone] : item.status === "rejected" ? FAINT : ERR;
90
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: headerColor }, glyph), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: headerColor }, TOOL_GLYPH[item.tone]), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: headerColor, bold: true }, item.name), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, item.args), item.status === "rejected" ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ERR, bold: true }, "rejected") : null, item.status === "retry" && item.retryInfo ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: WARN }, `R ${item.retryInfo}`) : null, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${item.seconds.toFixed(1)}s`)), item.status === "rejected" ? null : /* @__PURE__ */ React.createElement(React.Fragment, null, item.hidden > 0 ? /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `: ${item.hidden} earlier lines`)) : null, item.output.map((line, i) => (
91
- // biome-ignore lint/suspicious/noArrayIndexKey: tool tail output lines positional
92
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: `tool-${i}`, paddingLeft: 2 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true, color: item.status === "error" ? ERR : void 0 }, line || " "))
93
- ))));
94
- }
95
- function planGlyph(status) {
96
- switch (status) {
97
- case "todo":
98
- return "\u25CB";
99
- case "running":
100
- return "\u25B6";
101
- case "done":
102
- return "\u2713";
103
- case "skipped":
104
- return "s";
105
- case "failed":
106
- return "\u2717";
107
- case "blocked":
108
- return "!";
109
- }
110
- }
111
- function planColor(status) {
112
- switch (status) {
113
- case "todo":
114
- return PEND;
115
- case "running":
116
- return BRAND;
117
- case "done":
118
- return OK;
119
- case "skipped":
120
- return FAINT;
121
- case "failed":
122
- return ERR;
123
- case "blocked":
124
- return WARN;
125
- }
126
- }
127
- function subChildGlyph(kind) {
128
- switch (kind) {
129
- case "reasoning":
130
- return "\u25C6";
131
- case "tool":
132
- return "\u25B6";
133
- case "diff":
134
- return "\xB1";
135
- case "error":
136
- return "\u2716";
137
- }
138
- }
139
- function subChildColor(kind) {
140
- switch (kind) {
141
- case "reasoning":
142
- return ACCENT;
143
- case "tool":
144
- return BRAND;
145
- case "diff":
146
- return WARN;
147
- case "error":
148
- return ERR;
149
- }
150
- }
151
- function ReasoningCard({ card }) {
152
- const spin = SPINNER[card.frame % SPINNER.length] ?? "\xB7";
153
- return /* @__PURE__ */ React.createElement(
154
- ink_compat_exports.Box,
155
- {
156
- flexDirection: "column",
157
- borderStyle: "round",
158
- borderColor: ACCENT,
159
- paddingX: 1,
160
- marginTop: 1
161
- },
162
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT }, spin), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT, bold: true }, "reasoning"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `\xB7 ${card.tokens} tok`)),
163
- card.tail.map((line, i) => (
164
- // biome-ignore lint/suspicious/noArrayIndexKey: tail preview rotates by content, positional
165
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { key: `rl-${i}`, dimColor: true }, line)
166
- ))
167
- );
168
- }
169
- function ToolActiveCard({ card }) {
170
- const spin = SPINNER[card.frame % SPINNER.length] ?? "\xB7";
171
- const tailLen = TOOL_TAIL_LEN[card.tone];
172
- const tail = card.outputLines.slice(-tailLen);
173
- const hidden = Math.max(0, card.outputLines.length - tail.length);
174
- const seconds = (card.elapsedMs / 1e3).toFixed(1);
175
- const c = TOOL_COLOR[card.tone];
176
- return /* @__PURE__ */ React.createElement(
177
- ink_compat_exports.Box,
178
- {
179
- flexDirection: "column",
180
- borderStyle: "round",
181
- borderColor: c,
182
- paddingX: 1,
183
- marginTop: 1
184
- },
185
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: c }, spin), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: c }, TOOL_GLYPH[card.tone]), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: c, bold: true }, card.name), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, card.args), card.retry ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: WARN }, `R ${card.retry.attempt}/${card.retry.max}`) : null, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `\xB7 ${seconds}s`)),
186
- hidden > 0 ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `: ${hidden} earlier lines`) : null,
187
- tail.map((line, i) => (
188
- // biome-ignore lint/suspicious/noArrayIndexKey: tool active tail positional
189
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { key: `ta-${i}`, dimColor: true }, line || " ")
190
- ))
191
- );
192
- }
193
- function PlanActiveCard({ card }) {
194
- const spin = SPINNER[card.frame % SPINNER.length] ?? "\xB7";
195
- const done = card.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
196
- return /* @__PURE__ */ React.createElement(
197
- ink_compat_exports.Box,
198
- {
199
- flexDirection: "column",
200
- borderStyle: "round",
201
- borderColor: ACCENT,
202
- paddingX: 1,
203
- marginTop: 1
204
- },
205
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT }, "\u229E"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT, bold: true }, card.title), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${done} of ${card.steps.length} done`)),
206
- card.steps.map((step, i) => {
207
- const running = i === card.inProgressIdx;
208
- const glyph = running ? spin : planGlyph(step.status);
209
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: step.label, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: planColor(step.status) }, glyph), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { bold: running, dimColor: step.status === "todo" && !running }, step.label), step.note ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `\xB7 ${step.note}`) : null, running ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, "\u2190 in progress") : null);
210
- })
211
- );
212
- }
213
- function SubAgentActiveCard({ card }) {
214
- const spin = SPINNER[card.frame % SPINNER.length] ?? "\xB7";
215
- const runningCount = card.children.filter((c) => c.status === "running").length;
216
- return /* @__PURE__ */ React.createElement(
217
- ink_compat_exports.Box,
218
- {
219
- flexDirection: "column",
220
- borderStyle: "round",
221
- borderColor: VIOLET,
222
- paddingX: 1,
223
- marginTop: 1
224
- },
225
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: VIOLET }, spin), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: VIOLET, bold: true }, "subagent"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, card.task), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${runningCount} running`)),
226
- card.children.map((c, i) => {
227
- const cglyph = c.status === "running" ? spin : "\u2713";
228
- const ccolor = c.status === "running" ? BRAND : OK;
229
- return /* @__PURE__ */ React.createElement(
230
- ink_compat_exports.Box,
231
- {
232
- key: `sc-${i}`,
233
- flexDirection: "row",
234
- gap: 1
235
- },
236
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: VIOLET }, "\u258E"),
237
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ccolor }, cglyph),
238
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: subChildColor(c.kind) }, subChildGlyph(c.kind)),
239
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: c.status === "done" }, c.summary)
240
- );
241
- })
242
- );
243
- }
244
- function ResponseActiveCard({ card }) {
245
- const spin = SPINNER[card.frame % SPINNER.length] ?? "\xB7";
246
- return /* @__PURE__ */ React.createElement(
247
- ink_compat_exports.Box,
248
- {
249
- flexDirection: "column",
250
- borderStyle: "round",
251
- borderColor: OK,
252
- paddingX: 1,
253
- marginTop: 1
254
- },
255
- /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: OK }, spin), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: OK, bold: true }, "writing\u2026")),
256
- card.tail.map((line, i) => (
257
- // biome-ignore lint/suspicious/noArrayIndexKey: response tail positional
258
- /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { key: `rsp-${i}` }, line || " ")
259
- ))
260
- );
261
- }
262
- function ActiveCardView({ card }) {
263
- switch (card.kind) {
264
- case "reasoning":
265
- return /* @__PURE__ */ React.createElement(ReasoningCard, { card });
266
- case "tool":
267
- return /* @__PURE__ */ React.createElement(ToolActiveCard, { card });
268
- case "plan":
269
- return /* @__PURE__ */ React.createElement(PlanActiveCard, { card });
270
- case "subagent":
271
- return /* @__PURE__ */ React.createElement(SubAgentActiveCard, { card });
272
- case "response":
273
- return /* @__PURE__ */ React.createElement(ResponseActiveCard, { card });
274
- }
275
- }
276
- function StatusRow({ elapsedMs, cost }) {
277
- const seconds = (elapsedMs / 1e3).toFixed(1);
278
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 2 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND, bold: true }, "\u25C8 Reasonix"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: META }, "deepseek-r1"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${seconds}s`), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `$${cost.toFixed(4)}`));
279
- }
280
- function PromptInput() {
281
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND, bold: true }, "\u203A"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true }, "type your question\u2026"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, "\u258F"));
282
- }
283
- function HintBar() {
284
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true }, "card lifecycle reference \xB7 auto-replays \xB7 Esc exit"));
285
- }
286
- function CardDemoShell({ onExit }) {
287
- const [history, setHistory] = useState([]);
288
- const [active, setActive] = useState([]);
289
- const [elapsed, setElapsed] = useState(0);
290
- const [cost, setCost] = useState(0);
291
- const startedRef = useRef(Date.now());
292
- useKeystroke((k) => {
293
- if (k.escape) onExit();
294
- });
295
- useEffect(() => {
296
- const id = setInterval(() => {
297
- const now = Date.now() - startedRef.current;
298
- setElapsed(now);
299
- setCost(now / 1e3 * 8e-4);
300
- }, 100);
301
- return () => clearInterval(id);
302
- }, []);
303
- useEffect(() => {
304
- let cancelled = false;
305
- const api = {
306
- setActive: (updater) => {
307
- if (cancelled) return;
308
- setActive((prev) => updater(prev));
309
- },
310
- push: (item) => {
311
- if (cancelled) return;
312
- setHistory((prev) => [...prev, item]);
313
- },
314
- reset: () => {
315
- if (cancelled) return;
316
- setActive([]);
317
- setHistory([]);
318
- startedRef.current = Date.now();
319
- },
320
- cancelled: () => cancelled,
321
- sleep: (ms) => new Promise((r) => setTimeout(r, ms))
322
- };
323
- void runScript(api);
324
- return () => {
325
- cancelled = true;
326
- };
327
- }, []);
328
- return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(StatusRow, { elapsedMs: elapsed, cost }), /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column" }, history.map((item, i) => (
329
- // biome-ignore lint/suspicious/noArrayIndexKey: append-only history
330
- /* @__PURE__ */ React.createElement(StaticRow, { key: `h-${i}`, item })
331
- ))), active.map((card, i) => (
332
- // biome-ignore lint/suspicious/noArrayIndexKey: live region cards positional
333
- /* @__PURE__ */ React.createElement(ActiveCardView, { key: `a-${i}-${card.id}`, card })
334
- )), active.length === 0 ? /* @__PURE__ */ React.createElement(PromptInput, null) : null, /* @__PURE__ */ React.createElement(HintBar, null));
335
- }
336
- function replaceById(cards, id, next) {
337
- if (next === null) return cards.filter((c) => c.id !== id);
338
- return cards.map((c) => c.id === id ? next : c);
339
- }
340
- function appendCard(cards, card) {
341
- return [...cards, card];
342
- }
343
- async function runScript(api) {
344
- while (!api.cancelled()) {
345
- await sceneFixFailingTest(api);
346
- if (api.cancelled()) return;
347
- await api.sleep(1e3);
348
- if (api.cancelled()) return;
349
- await sceneSubagent(api);
350
- if (api.cancelled()) return;
351
- await api.sleep(800);
352
- if (api.cancelled()) return;
353
- await sceneEdgeCases(api);
354
- if (api.cancelled()) return;
355
- await api.sleep(1500);
356
- api.reset();
357
- await api.sleep(500);
358
- }
359
- }
360
- async function streamReasoning(api, fullText, durationMs, abortAtMs) {
361
- const id = "reason";
362
- const start = Date.now();
363
- let frame = 0;
364
- let revealed = "";
365
- let aborted = false;
366
- api.setActive((prev) => appendCard(prev, { id, kind: "reasoning", tail: [], tokens: 0, frame }));
367
- while (Date.now() - start < durationMs && !api.cancelled()) {
368
- if (abortAtMs !== void 0 && Date.now() - start >= abortAtMs) {
369
- aborted = true;
370
- break;
371
- }
372
- await api.sleep(60);
373
- frame++;
374
- revealed = fullText.slice(0, Math.min(fullText.length, revealed.length + 6));
375
- const lines2 = wrapText(revealed, 70);
376
- const tail = lines2.slice(-4);
377
- const tokens = Math.floor(revealed.length / 3);
378
- const aborting = abortAtMs !== void 0 && Date.now() - start >= abortAtMs - 100;
379
- api.setActive(
380
- (prev) => replaceById(prev, id, {
381
- id,
382
- kind: "reasoning",
383
- tail,
384
- tokens,
385
- frame,
386
- aborted: aborting
387
- })
388
- );
389
- }
390
- api.setActive((prev) => replaceById(prev, id, null));
391
- const lines = wrapText(revealed, 70);
392
- return { tokens: Math.floor(revealed.length / 3), lines, aborted };
393
- }
394
- async function streamTool(api, config) {
395
- const id = `tool-${Math.random().toString(36).slice(2)}`;
396
- const start = Date.now();
397
- let frame = 0;
398
- const out = [];
399
- api.setActive(
400
- (prev) => appendCard(prev, {
401
- id,
402
- kind: "tool",
403
- tone: config.tone,
404
- name: config.name,
405
- args: config.args,
406
- outputLines: out,
407
- elapsedMs: 0,
408
- frame,
409
- retry: config.retry
410
- })
411
- );
412
- const lineRate = config.durationMs / Math.max(1, config.output.length);
413
- let lineIdx = 0;
414
- while (Date.now() - start < config.durationMs && !api.cancelled()) {
415
- await api.sleep(80);
416
- frame++;
417
- const elapsed = Date.now() - start;
418
- while (lineIdx < config.output.length && elapsed > lineIdx * lineRate) {
419
- out.push(config.output[lineIdx] ?? "");
420
- lineIdx++;
421
- }
422
- api.setActive(
423
- (prev) => replaceById(prev, id, {
424
- id,
425
- kind: "tool",
426
- tone: config.tone,
427
- name: config.name,
428
- args: config.args,
429
- outputLines: out.slice(),
430
- elapsedMs: elapsed,
431
- frame,
432
- retry: config.retry
433
- })
434
- );
435
- }
436
- api.setActive((prev) => replaceById(prev, id, null));
437
- }
438
- async function sceneFixFailingTest(api) {
439
- api.push({ kind: "user", text: "fix the failing test in src/loop.test.ts" });
440
- await api.sleep(400);
441
- if (api.cancelled()) return;
442
- const reasoningText = "Looking at recent test failures in src/loop.test.ts. The assertion shape changed -- expects a stripped trailing marker. The new tokenizer in src/parser.ts keeps it. Two paths: patch tokenizer's strip step, or update the test expectation.";
443
- const reasonStart = Date.now();
444
- const r = await streamReasoning(api, reasoningText, 2200);
445
- if (api.cancelled()) return;
446
- api.push({
447
- kind: "reasoning",
448
- tail: r.lines.slice(-2),
449
- paragraphs: 3,
450
- tokens: r.tokens,
451
- seconds: (Date.now() - reasonStart) / 1e3
452
- });
453
- await api.sleep(200);
454
- const planId = "plan-main";
455
- const planStart = Date.now();
456
- let frame = 0;
457
- const steps = [
458
- { status: "todo", label: "locate failing assertion" },
459
- { status: "todo", label: "patch tokenizer.strip()" },
460
- { status: "todo", label: "verify with npm test" },
461
- { status: "todo", label: "publish patch" }
462
- ];
463
- const renderPlan = (inProgressIdx) => {
464
- api.setActive(
465
- (prev) => replaceById(
466
- prev.find((c) => c.id === planId) ? prev : appendCard(prev, makePlan()),
467
- planId,
468
- makePlan()
469
- )
470
- );
471
- function makePlan() {
472
- return {
473
- id: planId,
474
- kind: "plan",
475
- title: "fix loop test",
476
- steps: steps.slice(),
477
- inProgressIdx,
478
- frame
479
- };
480
- }
481
- };
482
- api.setActive(
483
- (prev) => appendCard(prev, {
484
- id: planId,
485
- kind: "plan",
486
- title: "fix loop test",
487
- steps: steps.slice(),
488
- inProgressIdx: 0,
489
- frame
490
- })
491
- );
492
- const planTick = setInterval(() => {
493
- frame++;
494
- api.setActive((prev) => {
495
- const cur = prev.find((c) => c.id === planId);
496
- if (!cur || cur.kind !== "plan") return prev;
497
- return replaceById(prev, planId, { ...cur, frame });
498
- });
499
- }, 80);
500
- try {
501
- steps[0] = { ...steps[0], status: "running" };
502
- renderPlan(0);
503
- await streamTool(api, {
504
- tone: "search",
505
- name: "grep",
506
- args: "TRAILER src/parser.ts",
507
- output: [
508
- "src/parser.ts:42:const TRAILER = '<|/tool|>';",
509
- "src/parser.ts:67: if (s.endsWith(TRAILER))"
510
- ],
511
- durationMs: 800
512
- });
513
- if (api.cancelled()) return;
514
- api.push({
515
- kind: "tool",
516
- tone: "search",
517
- name: "grep",
518
- args: "TRAILER src/parser.ts",
519
- output: ["src/parser.ts:67: if (s.endsWith(TRAILER))"],
520
- hidden: 1,
521
- seconds: 0.8,
522
- status: "ok"
523
- });
524
- steps[0] = { ...steps[0], status: "done" };
525
- renderPlan(1);
526
- await api.sleep(300);
527
- if (api.cancelled()) return;
528
- steps[1] = { ...steps[1], status: "running" };
529
- renderPlan(1);
530
- await streamTool(api, {
531
- tone: "write",
532
- name: "write_file",
533
- args: "src/parser.ts (full rewrite)",
534
- output: [],
535
- durationMs: 600
536
- });
537
- if (api.cancelled()) return;
538
- api.push({
539
- kind: "tool",
540
- tone: "write",
541
- name: "write_file",
542
- args: "src/parser.ts (full rewrite)",
543
- output: [],
544
- hidden: 0,
545
- seconds: 0.6,
546
- status: "rejected"
547
- });
548
- steps[1] = { ...steps[1], status: "blocked", note: "rejected; trying patch instead" };
549
- renderPlan(null);
550
- await api.sleep(800);
551
- if (api.cancelled()) return;
552
- steps[1] = { ...steps[1], status: "running", note: void 0 };
553
- renderPlan(1);
554
- await streamTool(api, {
555
- tone: "patch",
556
- name: "edit_file",
557
- args: "src/parser.ts -3+12",
558
- output: [
559
- "applying hunk 1/1",
560
- "--- src/parser.ts",
561
- "+++ src/parser.ts",
562
- "@@ -42,3 +42,12 @@",
563
- "+function strip(token: string) {...}"
564
- ],
565
- durationMs: 1100
566
- });
567
- if (api.cancelled()) return;
568
- api.push({
569
- kind: "tool",
570
- tone: "patch",
571
- name: "edit_file",
572
- args: "src/parser.ts -3+12",
573
- output: ["+function strip(token: string) {...}"],
574
- hidden: 4,
575
- seconds: 1.1,
576
- status: "ok"
577
- });
578
- steps[1] = { ...steps[1], status: "done" };
579
- renderPlan(2);
580
- await api.sleep(300);
581
- if (api.cancelled()) return;
582
- steps[2] = { ...steps[2], status: "running" };
583
- renderPlan(2);
584
- const bashOutput = [
585
- " RUNS src/loop.test.ts",
586
- " FAIL src/loop.test.ts",
587
- " expect(received).toBe(expected)",
588
- " - expected: '<final>'",
589
- " + received: '<final><|/tool|>'"
590
- ];
591
- await streamTool(api, {
592
- tone: "bash",
593
- name: "bash",
594
- args: "npm test",
595
- output: bashOutput,
596
- durationMs: 900,
597
- retry: { attempt: 1, max: 3 }
598
- });
599
- if (api.cancelled()) return;
600
- api.push({
601
- kind: "tool",
602
- tone: "bash",
603
- name: "bash",
604
- args: "npm test",
605
- output: bashOutput.slice(-5),
606
- hidden: 0,
607
- seconds: 0.9,
608
- status: "retry",
609
- retryInfo: "1/3"
610
- });
611
- await api.sleep(200);
612
- const bashOk = [
613
- " PASS src/loop.test.ts",
614
- " PASS src/parser.test.ts",
615
- " PASS src/diff/cell.test.ts",
616
- " PASS src/diff/screen.test.ts",
617
- " PASS src/renderer/layout.test.ts",
618
- "Tests: 142 passed"
619
- ];
620
- await streamTool(api, {
621
- tone: "bash",
622
- name: "bash",
623
- args: "npm test",
624
- output: bashOk,
625
- durationMs: 1300,
626
- retry: { attempt: 2, max: 3 }
627
- });
628
- if (api.cancelled()) return;
629
- api.push({
630
- kind: "tool",
631
- tone: "bash",
632
- name: "bash",
633
- args: "npm test",
634
- output: bashOk.slice(-5),
635
- hidden: 1,
636
- seconds: 1.3,
637
- status: "ok"
638
- });
639
- steps[2] = { ...steps[2], status: "done" };
640
- renderPlan(3);
641
- await api.sleep(300);
642
- if (api.cancelled()) return;
643
- steps[3] = { ...steps[3], status: "running" };
644
- renderPlan(3);
645
- await streamTool(api, {
646
- tone: "bash",
647
- name: "bash",
648
- args: "npm run build && npm publish",
649
- output: [
650
- " > tsup",
651
- " ESM Build start",
652
- " CJS Build start",
653
- " DTS Build start",
654
- " ESM done",
655
- " + reasonix@0.24.0"
656
- ],
657
- durationMs: 1500
658
- });
659
- if (api.cancelled()) return;
660
- api.push({
661
- kind: "tool",
662
- tone: "bash",
663
- name: "bash",
664
- args: "npm run build && npm publish",
665
- output: [" + reasonix@0.24.0"],
666
- hidden: 5,
667
- seconds: 1.5,
668
- status: "ok"
669
- });
670
- steps[3] = { ...steps[3], status: "done" };
671
- renderPlan(null);
672
- } finally {
673
- clearInterval(planTick);
674
- }
675
- await api.sleep(400);
676
- api.setActive((prev) => replaceById(prev, planId, null));
677
- api.push({ kind: "plan", steps: steps.slice(), seconds: (Date.now() - planStart) / 1e3 });
678
- await api.sleep(300);
679
- if (api.cancelled()) return;
680
- api.push({
681
- kind: "diff",
682
- file: "src/parser.ts",
683
- added: 12,
684
- removed: 3,
685
- preview: [
686
- { kind: " ", text: "function strip(token: string) {" },
687
- { kind: "-", text: " return token.trimEnd();" },
688
- { kind: "+", text: " if (token.endsWith(TRAILER)) {" },
689
- { kind: "+", text: " return token.slice(0, -TRAILER.length);" },
690
- { kind: "+", text: " }" },
691
- { kind: "+", text: " return token;" },
692
- { kind: " ", text: "}" }
693
- ]
694
- });
695
- await api.sleep(400);
696
- if (api.cancelled()) return;
697
- await streamMarkdownResponse(api);
698
- }
699
- async function streamMarkdownResponse(api) {
700
- const lines = [
701
- { kind: "header", text: "## Patch landed" },
702
- { kind: "text", text: "Tests pass on second retry. The patch:" },
703
- { kind: "text", text: "" },
704
- { kind: "code", text: "function strip(token: string) {" },
705
- { kind: "code", text: " if (token.endsWith(TRAILER))" },
706
- { kind: "code", text: " return token.slice(0, -TRAILER.length);" },
707
- { kind: "code", text: " return token;" },
708
- { kind: "code", text: "}" },
709
- { kind: "text", text: "" },
710
- { kind: "list", text: "checks the suffix before slicing \u2014 no spurious slices on plain tokens" },
711
- { kind: "list", text: "released as 0.24.0 to npm" }
712
- ];
713
- const id = "resp";
714
- let frame = 0;
715
- let revealed = 0;
716
- api.setActive((prev) => appendCard(prev, { id, kind: "response", tail: [], frame }));
717
- while (revealed < lines.length && !api.cancelled()) {
718
- await api.sleep(160);
719
- frame++;
720
- revealed = Math.min(lines.length, revealed + 1);
721
- const tail = lines.slice(0, revealed).slice(-4).map(
722
- (l) => l.kind === "code" ? ` ${l.text}` : l.kind === "list" ? ` - ${l.text}` : l.text
723
- );
724
- api.setActive((prev) => replaceById(prev, id, { id, kind: "response", tail, frame }));
725
- }
726
- api.setActive((prev) => replaceById(prev, id, null));
727
- api.push({ kind: "response", lines });
728
- await api.sleep(200);
729
- if (api.cancelled()) return;
730
- api.push({ kind: "usage", inputTokens: 1842, outputTokens: 421, totalCost: 94e-4 });
731
- }
732
- async function sceneSubagent(api) {
733
- api.push({ kind: "user", text: "investigate the auth flow regressions in 0.24" });
734
- await api.sleep(400);
735
- if (api.cancelled()) return;
736
- const id = "sub";
737
- let frame = 0;
738
- const start = Date.now();
739
- const children = [
740
- { status: "running", kind: "reasoning", summary: "scanning auth fixtures" },
741
- { status: "running", kind: "tool", summary: "grep 'authToken' src/", tone: "search" },
742
- { status: "running", kind: "tool", summary: "read src/auth/session.ts", tone: "read" }
743
- ];
744
- api.setActive(
745
- (prev) => appendCard(prev, {
746
- id,
747
- kind: "subagent",
748
- task: "auth regressions",
749
- children: children.slice(),
750
- frame
751
- })
752
- );
753
- const tick = setInterval(() => {
754
- frame++;
755
- api.setActive((prev) => {
756
- const cur = prev.find((c) => c.id === id);
757
- if (!cur || cur.kind !== "subagent") return prev;
758
- return replaceById(prev, id, { ...cur, frame, children: children.slice() });
759
- });
760
- }, 80);
761
- try {
762
- await api.sleep(1200);
763
- if (api.cancelled()) return;
764
- children[0] = { ...children[0], status: "done" };
765
- await api.sleep(900);
766
- if (api.cancelled()) return;
767
- children[1] = { ...children[1], status: "done" };
768
- await api.sleep(1100);
769
- if (api.cancelled()) return;
770
- children[2] = { ...children[2], status: "done" };
771
- children.push({ status: "running", kind: "diff", summary: "patching session.ts" });
772
- await api.sleep(1e3);
773
- if (api.cancelled()) return;
774
- children[3] = { ...children[3], status: "done" };
775
- await api.sleep(400);
776
- } finally {
777
- clearInterval(tick);
778
- }
779
- api.setActive((prev) => replaceById(prev, id, null));
780
- api.push({
781
- kind: "subagent",
782
- task: "auth regressions",
783
- children: [
784
- { kind: "reasoning", summary: "scanned 14 fixtures, found 2 stale" },
785
- { kind: "tool", summary: "grep 'authToken' src/" },
786
- { kind: "tool", summary: "read src/auth/session.ts" },
787
- { kind: "diff", summary: "src/auth/session.ts +5 -2" }
788
- ],
789
- seconds: (Date.now() - start) / 1e3,
790
- ok: true
791
- });
792
- await api.sleep(400);
793
- if (api.cancelled()) return;
794
- const id2 = "resp-cjk";
795
- const text = "\u6211\u5DF2\u7ECF\u5B9A\u4F4D\u5230\u95EE\u9898\uFF1Asession.ts \u7684 token \u7EED\u671F\u903B\u8F91\u5728 0.24 \u6539\u4E86\u987A\u5E8F\uFF0C\u5BFC\u81F4\u65E7 token \u5728 refresh \u4E4B\u524D\u5C31\u88AB\u6E05\u6389\u3002\u4FEE\u4E86\uFF0C\u518D\u8DD1\u6D4B\u8BD5\u90FD\u8FC7\u4E86\u3002";
796
- const lines = [
797
- { kind: "header", text: "\u7ED3\u8BBA" },
798
- { kind: "text", text },
799
- { kind: "list", text: "\u5DF2\u4FEE\u590D\uFF0C\u63D0\u4EA4\u5728 src/auth/session.ts" }
800
- ];
801
- let frame2 = 0;
802
- let revealed2 = 0;
803
- api.setActive((prev) => appendCard(prev, { id: id2, kind: "response", tail: [], frame: frame2 }));
804
- while (revealed2 < lines.length && !api.cancelled()) {
805
- await api.sleep(180);
806
- frame2++;
807
- revealed2++;
808
- const tail = lines.slice(0, revealed2).slice(-4).map((l) => l.kind === "list" ? ` - ${l.text}` : l.text);
809
- api.setActive(
810
- (prev) => replaceById(prev, id2, { id: id2, kind: "response", tail, frame: frame2 })
811
- );
812
- }
813
- api.setActive((prev) => replaceById(prev, id2, null));
814
- api.push({ kind: "response", lines });
815
- }
816
- async function sceneEdgeCases(api) {
817
- api.push({ kind: "user", text: "summarize the deploy log" });
818
- await api.sleep(400);
819
- if (api.cancelled()) return;
820
- const reasoningText = "Pulling deploy logs from the last hour. Looking for warnings and errors. The database connection retried 3 times before stabilizing...";
821
- const reasonStart = Date.now();
822
- const r = await streamReasoning(api, reasoningText, 1200, 800);
823
- if (api.cancelled()) return;
824
- api.push({
825
- kind: "reasoning",
826
- tail: r.lines.slice(-2),
827
- paragraphs: 1,
828
- tokens: r.tokens,
829
- seconds: (Date.now() - reasonStart) / 1e3,
830
- aborted: true
831
- });
832
- await api.sleep(300);
833
- if (api.cancelled()) return;
834
- await streamTool(api, {
835
- tone: "mcp",
836
- name: "mcp.linear.search_issues",
837
- args: '{"query":"deploy error","limit":5}',
838
- output: [
839
- "INC-2871 \u2014 db pool exhausted on retry",
840
- "INC-2872 \u2014 auth refresh race",
841
- "INC-2873 \u2014 log shipper timeout"
842
- ],
843
- durationMs: 1100
844
- });
845
- if (api.cancelled()) return;
846
- api.push({
847
- kind: "tool",
848
- tone: "mcp",
849
- name: "mcp.linear.search_issues",
850
- args: '{"query":"deploy error","limit":5}',
851
- output: ["INC-2873 \u2014 log shipper timeout"],
852
- hidden: 2,
853
- seconds: 1.1,
854
- status: "ok"
855
- });
856
- await api.sleep(300);
857
- if (api.cancelled()) return;
858
- await streamTool(api, {
859
- tone: "fetch",
860
- name: "web_fetch",
861
- args: "https://status.deepseek.com/api/incidents",
862
- output: ["status: 200", '{"incidents":[]}'],
863
- durationMs: 700
864
- });
865
- if (api.cancelled()) return;
866
- api.push({
867
- kind: "tool",
868
- tone: "fetch",
869
- name: "web_fetch",
870
- args: "https://status.deepseek.com/api/incidents",
871
- output: ["status: 200", '{"incidents":[]}'],
872
- hidden: 0,
873
- seconds: 0.7,
874
- status: "ok"
875
- });
876
- await api.sleep(300);
877
- if (api.cancelled()) return;
878
- api.push({ kind: "warn", message: "context budget at 73% \xB7 /compact suggested" });
879
- await api.sleep(400);
880
- api.push({ kind: "error", message: "rate-limit hit \xB7 backing off 8s" });
881
- await api.sleep(500);
882
- api.push({ kind: "usage", inputTokens: 487, outputTokens: 96, totalCost: 21e-4 });
883
- }
884
- function wrapText(text, width) {
885
- const lines = [];
886
- for (const para of text.split("\n")) {
887
- if (para.length <= width) {
888
- lines.push(para);
889
- continue;
890
- }
891
- const words = para.split(" ");
892
- let cur = "";
893
- for (const w of words) {
894
- if (cur.length + w.length + 1 > width) {
895
- if (cur) lines.push(cur);
896
- cur = w;
897
- } else {
898
- cur = cur ? `${cur} ${w}` : w;
899
- }
900
- }
901
- if (cur) lines.push(cur);
902
- }
903
- return lines;
904
- }
905
- async function runCardDemo(opts = {}) {
906
- const stdout = opts.stdout ?? process.stdout;
907
- const stdin = opts.stdin ?? process.stdin;
908
- if (!stdin.isTTY || !stdout.isTTY) {
909
- console.error("card-demo requires an interactive TTY.");
910
- process.exit(1);
911
- }
912
- const pools = {
913
- char: new CharPool(),
914
- style: new StylePool(),
915
- hyperlink: new HyperlinkPool()
916
- };
917
- let resolveExit = () => {
918
- };
919
- const exited = new Promise((resolve) => {
920
- resolveExit = resolve;
921
- });
922
- const handle = mount(/* @__PURE__ */ React.createElement(CardDemoShell, { onExit: () => resolveExit() }), {
923
- viewportWidth: stdout.columns ?? 80,
924
- viewportHeight: stdout.rows ?? 30,
925
- pools,
926
- write: (bytes) => stdout.write(bytes),
927
- stdin,
928
- onExit: () => resolveExit()
929
- });
930
- const onResize = () => handle.resize(stdout.columns ?? 80, stdout.rows ?? 30);
931
- stdout.on("resize", onResize);
932
- try {
933
- await exited;
934
- } finally {
935
- stdout.off("resize", onResize);
936
- handle.destroy();
937
- stdin.pause();
938
- }
939
- }
940
- export {
941
- CardDemoShell,
942
- runCardDemo
943
- };
944
- //# sourceMappingURL=card-demo-OEFBRAJP.js.map