archymedes-cli 2.0.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.
@@ -0,0 +1,826 @@
1
+ import {
2
+ rgbTo256
3
+ } from "./archymedes-jecyxv5c.mjs";
4
+
5
+ // packages/archymedes-cli/src/glyphs.ts
6
+ var UNICODE_GLYPHS = {
7
+ starGlyphs: [".", "·", "✧", "✦", "✶"],
8
+ spinnerFrames: [" ◜ ", " ◠ ", " ◝ ", " ◞ ", " ◡ ", " ◟ "],
9
+ star: "✦",
10
+ starDim: "✧",
11
+ check: "✓",
12
+ cross: "✗",
13
+ pending: "⋯",
14
+ prompt: "❯",
15
+ caret: "›",
16
+ bullet: "•",
17
+ ellipsis: "…",
18
+ middot: "·",
19
+ elbow: "⎿",
20
+ boxTopLeft: "╭",
21
+ boxTopRight: "╮",
22
+ boxBottomLeft: "╰",
23
+ boxBottomRight: "╯",
24
+ boxHorizontal: "─",
25
+ boxVertical: "│",
26
+ boxTeeLeft: "├",
27
+ boxTeeRight: "┤",
28
+ boxCross: "┼",
29
+ circleEmpty: "○",
30
+ circleHalf: "◐",
31
+ circleFull: "●",
32
+ checkbox: "☐",
33
+ checkboxDone: "☑",
34
+ paused: "⏸",
35
+ cancelled: "⊘",
36
+ treeBranch: "├─",
37
+ treeLast: "╰─",
38
+ treeTrunk: "│ ",
39
+ collapsed: "▸",
40
+ expanded: "▾",
41
+ arrowUp: "↑",
42
+ arrowLeft: "←",
43
+ arrowRight: "→",
44
+ arrowDown: "↓",
45
+ plus: "+",
46
+ minus: "-",
47
+ warning: "!",
48
+ effectWorkspace: "✎",
49
+ effectExternal: "◎",
50
+ sparkLevels: ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
51
+ };
52
+ var ASCII_GLYPHS = {
53
+ starGlyphs: [".", ".", "+", "*", "*"],
54
+ spinnerFrames: ["-", "\\", "|", "/"],
55
+ star: "*",
56
+ starDim: "+",
57
+ check: "+",
58
+ cross: "x",
59
+ pending: "~",
60
+ prompt: ">",
61
+ caret: ">",
62
+ bullet: "-",
63
+ ellipsis: "...",
64
+ middot: "-",
65
+ elbow: "\\",
66
+ boxTopLeft: "+",
67
+ boxTopRight: "+",
68
+ boxBottomLeft: "+",
69
+ boxBottomRight: "+",
70
+ boxHorizontal: "-",
71
+ boxVertical: "|",
72
+ boxTeeLeft: "+",
73
+ boxTeeRight: "+",
74
+ boxCross: "+",
75
+ circleEmpty: "o",
76
+ circleHalf: "*",
77
+ circleFull: "*",
78
+ checkbox: "[ ]",
79
+ checkboxDone: "[x]",
80
+ paused: "=",
81
+ cancelled: "x",
82
+ treeBranch: "|-",
83
+ treeLast: "`-",
84
+ treeTrunk: "| ",
85
+ collapsed: ">",
86
+ expanded: "v",
87
+ arrowUp: "^",
88
+ arrowLeft: "<-",
89
+ arrowRight: "->",
90
+ arrowDown: "v",
91
+ plus: "+",
92
+ minus: "-",
93
+ warning: "!",
94
+ effectWorkspace: "w",
95
+ effectExternal: "e",
96
+ sparkLevels: [" ", ".", ":", "-", "=", "+", "*", "#"]
97
+ };
98
+ var ROUND_BORDER = { topLeft: "╭", topRight: "╮", bottomLeft: "╰", bottomRight: "╯", horizontal: "─", vertical: "│", teeLeft: "├", teeRight: "┤", cross: "┼" };
99
+ var SINGLE_BORDER = { topLeft: "┌", topRight: "┐", bottomLeft: "└", bottomRight: "┘", horizontal: "─", vertical: "│", teeLeft: "├", teeRight: "┤", cross: "┼" };
100
+ var DOUBLE_BORDER = { topLeft: "╔", topRight: "╗", bottomLeft: "╚", bottomRight: "╝", horizontal: "═", vertical: "║", teeLeft: "╠", teeRight: "╣", cross: "╬" };
101
+ var ASCII_BORDER = { topLeft: "+", topRight: "+", bottomLeft: "+", bottomRight: "+", horizontal: "-", vertical: "|", teeLeft: "+", teeRight: "+", cross: "+" };
102
+ function borderGlyphsFor(style, glyphs) {
103
+ if (glyphs === ASCII_GLYPHS)
104
+ return ASCII_BORDER;
105
+ if (style === "single")
106
+ return SINGLE_BORDER;
107
+ if (style === "double")
108
+ return DOUBLE_BORDER;
109
+ return ROUND_BORDER;
110
+ }
111
+
112
+ // packages/archymedes-cli/src/markdown.ts
113
+ var RESET = "\x1B[0m";
114
+ var BOLD = "\x1B[1m";
115
+ var DIM = "\x1B[2m";
116
+ var ITALIC = "\x1B[3m";
117
+ var STRIKE = "\x1B[9m";
118
+ var CYAN = "\x1B[36m";
119
+ var BLUE = "\x1B[34m";
120
+ var GREEN = "\x1B[32m";
121
+ var YELLOW = "\x1B[33m";
122
+ var ANSI = /\x1b\[[0-9;]*m/g;
123
+ function visibleWidth(text) {
124
+ let width = 0;
125
+ for (const character of text.replace(ANSI, "")) {
126
+ const code = character.codePointAt(0) ?? 0;
127
+ if (code >= 768 && code <= 879)
128
+ continue;
129
+ width += isWide(code) ? 2 : 1;
130
+ }
131
+ return width;
132
+ }
133
+ function isWide(code) {
134
+ return code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 127744 && code <= 129535;
135
+ }
136
+ var INLINE_PATTERN = /(?<!\\)(`[^`\n]+`)|(?<!\\)(\*\*[^*\n]+\*\*)|(?<!\\)(__[^_\n]+__)|(?<!\\)(~~[^~\n]+~~)|(?<!\\)(\*[^*\n]+\*)|(?<!\\)(_[^_\n]+_)|(!?\[([^\]\n]+)\]\((https?:\/\/[^\s)\n]+)\))|(<(https?:\/\/[^>\s]+)>)/g;
137
+ var unescapeMarkdown = (text) => text.replace(/\\([\\`*_[\]{}()#+.!~>-])/g, "$1");
138
+ function parseInline(line) {
139
+ const tokens = [];
140
+ let lastIndex = 0;
141
+ INLINE_PATTERN.lastIndex = 0;
142
+ for (let match = INLINE_PATTERN.exec(line);match !== null; match = INLINE_PATTERN.exec(line)) {
143
+ if (match.index > lastIndex)
144
+ tokens.push({ text: unescapeMarkdown(line.slice(lastIndex, match.index)), code: "" });
145
+ const [whole, code, boldStars, boldUnderscores, strike, italicStars, italicUnderscores, link, linkLabel, linkUrl, autolink, autolinkUrl] = match;
146
+ if (code !== undefined)
147
+ tokens.push({ text: code.slice(1, -1), code: CYAN });
148
+ else if (boldStars !== undefined)
149
+ tokens.push({ text: boldStars.slice(2, -2), code: BOLD });
150
+ else if (boldUnderscores !== undefined)
151
+ tokens.push({ text: boldUnderscores.slice(2, -2), code: BOLD });
152
+ else if (strike !== undefined)
153
+ tokens.push({ text: strike.slice(2, -2), code: STRIKE });
154
+ else if (italicStars !== undefined)
155
+ tokens.push({ text: italicStars.slice(1, -1), code: ITALIC });
156
+ else if (italicUnderscores !== undefined)
157
+ tokens.push({ text: italicUnderscores.slice(1, -1), code: ITALIC });
158
+ else if (link !== undefined)
159
+ tokens.push({ text: `${link.startsWith("!") ? `image: ${linkLabel}` : linkLabel} (${linkUrl})`, code: CYAN });
160
+ else if (autolink !== undefined)
161
+ tokens.push({ text: autolinkUrl, code: CYAN });
162
+ lastIndex = match.index + whole.length;
163
+ }
164
+ if (lastIndex < line.length)
165
+ tokens.push({ text: unescapeMarkdown(line.slice(lastIndex)), code: "" });
166
+ return tokens.filter((token) => token.text.length > 0);
167
+ }
168
+ function paint(text, code, depth) {
169
+ return depth === "none" || code === "" ? text : `${code}${text}${RESET}`;
170
+ }
171
+ function wrapTokens(tokens, width, options) {
172
+ const firstPrefix = options.firstPrefix ?? "";
173
+ const continuationPrefix = options.continuationPrefix ?? "";
174
+ const lines = [];
175
+ let current = "";
176
+ let currentWidth = 0;
177
+ let prefix = firstPrefix;
178
+ let prefixWidth = visibleWidth(firstPrefix);
179
+ const limit = Math.max(1, width);
180
+ const flush = () => {
181
+ lines.push(`${prefix}${current}`.replace(/[ \t]+$/, ""));
182
+ current = "";
183
+ currentWidth = 0;
184
+ prefix = continuationPrefix;
185
+ prefixWidth = visibleWidth(continuationPrefix);
186
+ };
187
+ for (const token of tokens) {
188
+ for (const word of token.text.split(/(\s+)/)) {
189
+ if (word === "")
190
+ continue;
191
+ const wordWidth = visibleWidth(word);
192
+ if (/^\s+$/.test(word)) {
193
+ if (currentWidth > 0 && prefixWidth + currentWidth + wordWidth <= limit) {
194
+ current += word;
195
+ currentWidth += wordWidth;
196
+ }
197
+ continue;
198
+ }
199
+ if (currentWidth > 0 && prefixWidth + currentWidth + wordWidth > limit)
200
+ flush();
201
+ if (prefixWidth + wordWidth > limit) {
202
+ let remaining = word;
203
+ while (remaining.length > 0) {
204
+ const available = Math.max(1, limit - prefixWidth - currentWidth);
205
+ if (visibleWidth(remaining) <= available)
206
+ break;
207
+ const head = [...remaining].slice(0, available).join("");
208
+ current += paint(head, token.code, options.depth);
209
+ remaining = [...remaining].slice(available).join("");
210
+ flush();
211
+ }
212
+ if (remaining.length > 0) {
213
+ current += paint(remaining, token.code, options.depth);
214
+ currentWidth += visibleWidth(remaining);
215
+ }
216
+ continue;
217
+ }
218
+ current += paint(word, token.code, options.depth);
219
+ currentWidth += wordWidth;
220
+ }
221
+ }
222
+ if (current !== "" || lines.length === 0)
223
+ lines.push(`${prefix}${current}`.replace(/[ \t]+$/, ""));
224
+ return lines;
225
+ }
226
+ function newMarkdownState() {
227
+ return { inFence: false, fenceLanguage: "" };
228
+ }
229
+ var HEADING = /^(#{1,6})\s+(.*)$/;
230
+ var BULLET = /^(\s*)([-*+])\s+(.*)$/;
231
+ var TASK = /^(\s*)[-*+]\s+\[([ xX])\]\s+(.*)$/;
232
+ var NUMBERED = /^(\s*)(\d+[.)])\s+(.*)$/;
233
+ var BLOCKQUOTE = /^>\s?(.*)$/;
234
+ var RULE = /^\s*([-*_])\s*(?:\1\s*){2,}$/;
235
+ var FENCE = /^\s*```(.*)$/;
236
+ var TABLE_SEPARATOR_CELL = /^:?-{3,}:?$/;
237
+ function tableCells(line) {
238
+ if (!line.includes("|"))
239
+ return null;
240
+ const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
241
+ const cells = trimmed.split("|").map((cell) => cell.trim());
242
+ return cells.length >= 2 ? cells : null;
243
+ }
244
+ function fitCell(text, width, glyphs = UNICODE_GLYPHS) {
245
+ let result = "";
246
+ for (const character of text) {
247
+ if (visibleWidth(result + character) > width) {
248
+ while (visibleWidth(result) > Math.max(0, width - visibleWidth(glyphs.ellipsis)))
249
+ result = [...result].slice(0, -1).join("");
250
+ result += glyphs.ellipsis;
251
+ return result + " ".repeat(Math.max(0, width - visibleWidth(result)));
252
+ }
253
+ result += character;
254
+ }
255
+ return result + " ".repeat(Math.max(0, width - visibleWidth(result)));
256
+ }
257
+ function renderTableLine(cells, separator, width, depth, glyphs) {
258
+ const cellWidth = Math.max(3, Math.floor((Math.max(width, 12) - cells.length - 1) / cells.length));
259
+ if (separator)
260
+ return paint(`${glyphs.boxTeeLeft}${cells.map(() => glyphs.boxHorizontal.repeat(cellWidth)).join(glyphs.boxCross)}${glyphs.boxTeeRight}`, DIM, depth);
261
+ const contentWidth = Math.max(1, cellWidth - 2);
262
+ const rendered = cells.map((cell) => fitCell(parseInline(cell).map((token) => token.text).join(""), contentWidth, glyphs));
263
+ return `${glyphs.boxVertical}${rendered.map((cell) => ` ${cell} `).join(glyphs.boxVertical)}${glyphs.boxVertical}`;
264
+ }
265
+ function renderMarkdownLine(line, state, options) {
266
+ const { width, depth } = options;
267
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
268
+ const fence = FENCE.exec(line);
269
+ if (fence) {
270
+ const ruleWidth = Math.max(4, Math.min(width - 4, 40));
271
+ if (state.inFence) {
272
+ state.inFence = false;
273
+ state.fenceLanguage = "";
274
+ return [paint(` ${glyphs.boxBottomLeft}${glyphs.boxHorizontal.repeat(ruleWidth)}`, DIM, depth)];
275
+ }
276
+ state.inFence = true;
277
+ state.fenceLanguage = fence[1].trim();
278
+ const label = state.fenceLanguage ? ` ${state.fenceLanguage} ` : "";
279
+ const drawn = 1 + visibleWidth(label);
280
+ return [paint(` ${glyphs.boxTopLeft}${glyphs.boxHorizontal}${label}${glyphs.boxHorizontal.repeat(Math.max(0, ruleWidth - drawn))}`, DIM, depth)];
281
+ }
282
+ if (state.inFence) {
283
+ return [`${paint(` ${glyphs.boxVertical} `, DIM, depth)}${paint(line, GREEN, depth)}`];
284
+ }
285
+ const table = tableCells(line);
286
+ if (table) {
287
+ const separator = table.every((cell) => TABLE_SEPARATOR_CELL.test(cell));
288
+ return [renderTableLine(table, separator, width, depth, glyphs)];
289
+ }
290
+ if (RULE.test(line) && line.trim().length > 0) {
291
+ return [paint(" " + glyphs.boxHorizontal.repeat(Math.max(4, Math.min(width - 4, 40))), DIM, depth)];
292
+ }
293
+ const heading = HEADING.exec(line);
294
+ if (heading) {
295
+ const level = heading[1].length;
296
+ const code = level <= 2 ? `${BOLD}${CYAN}` : BOLD;
297
+ const tokens = parseInline(heading[2]).map((token) => ({ text: token.text, code }));
298
+ return wrapTokens(tokens, width, { depth });
299
+ }
300
+ const task = TASK.exec(line);
301
+ if (task) {
302
+ return wrapTokens(parseInline(task[3]), width, {
303
+ firstPrefix: `${task[1]}${paint(task[2].trim() ? glyphs.checkboxDone : glyphs.checkbox, task[2].trim() ? GREEN : BLUE, depth)} `,
304
+ continuationPrefix: `${task[1]} `,
305
+ depth
306
+ });
307
+ }
308
+ const bullet = BULLET.exec(line);
309
+ if (bullet) {
310
+ const indent = bullet[1];
311
+ return wrapTokens(parseInline(bullet[3]), width, {
312
+ firstPrefix: `${indent}${paint(glyphs.bullet, BLUE, depth)} `,
313
+ continuationPrefix: `${indent} `,
314
+ depth
315
+ });
316
+ }
317
+ const numbered = NUMBERED.exec(line);
318
+ if (numbered) {
319
+ const indent = numbered[1];
320
+ const marker = numbered[2];
321
+ return wrapTokens(parseInline(numbered[3]), width, {
322
+ firstPrefix: `${indent}${paint(marker, BLUE, depth)} `,
323
+ continuationPrefix: `${indent}${" ".repeat(marker.length + 1)}`,
324
+ depth
325
+ });
326
+ }
327
+ const quote = BLOCKQUOTE.exec(line);
328
+ if (quote) {
329
+ return wrapTokens([{ text: quote[1], code: DIM }], width, {
330
+ firstPrefix: paint(`${glyphs.boxVertical} `, YELLOW, depth),
331
+ continuationPrefix: paint(`${glyphs.boxVertical} `, YELLOW, depth),
332
+ depth
333
+ });
334
+ }
335
+ if (line.trim() === "")
336
+ return [""];
337
+ return wrapTokens(parseInline(line), width, { depth });
338
+ }
339
+ function renderMarkdown(text, options) {
340
+ const state = newMarkdownState();
341
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
342
+ const rendered = text.split(`
343
+ `).flatMap((line) => renderMarkdownLine(line, state, options));
344
+ if (state.inFence)
345
+ rendered.push(paint(` ${glyphs.boxBottomLeft}${glyphs.boxHorizontal.repeat(4)}`, DIM, options.depth));
346
+ return rendered.join(`
347
+ `);
348
+ }
349
+
350
+ // packages/archymedes-cli/src/output.ts
351
+ var terminalStream = {
352
+ write(text) {
353
+ process.stdout.write(text);
354
+ },
355
+ get columns() {
356
+ return process.stdout.columns;
357
+ },
358
+ get rows() {
359
+ return process.stdout.rows;
360
+ }
361
+ };
362
+
363
+ // packages/archymedes-cli/src/tui.ts
364
+ var RESET2 = "\x1B[0m";
365
+ var DIM2 = "\x1B[2m";
366
+ var CYAN2 = "\x1B[36m";
367
+ var GREEN2 = "\x1B[32m";
368
+ var YELLOW2 = "\x1B[33m";
369
+ var RED = "\x1B[31m";
370
+ function paint2(text, code, depth) {
371
+ return depth === "none" ? text : `${code}${text}${RESET2}`;
372
+ }
373
+ function archymedesSpinnerFrame(index, glyphs = UNICODE_GLYPHS) {
374
+ const frames = glyphs.spinnerFrames;
375
+ const normalized = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
376
+ return frames[normalized % frames.length];
377
+ }
378
+ class Spring {
379
+ angularFrequency;
380
+ dampingRatio;
381
+ constructor(angularFrequency, dampingRatio) {
382
+ this.angularFrequency = angularFrequency;
383
+ this.dampingRatio = dampingRatio;
384
+ }
385
+ update(position, velocity, target, dt) {
386
+ const omega = this.angularFrequency;
387
+ const zeta = this.dampingRatio;
388
+ const f = 1 + 2 * dt * zeta * omega;
389
+ const oo = omega * omega;
390
+ const hoo = dt * oo;
391
+ const hhoo = dt * hoo;
392
+ const detInv = 1 / (f + hhoo);
393
+ const detX = f * position + dt * velocity + hhoo * target;
394
+ const detV = velocity + hoo * (target - position);
395
+ return [detX * detInv, detV * detInv];
396
+ }
397
+ }
398
+
399
+ class SpringAnimator {
400
+ onTick;
401
+ spring;
402
+ position;
403
+ velocity = 0;
404
+ target;
405
+ timer;
406
+ intervalMs;
407
+ epsilon;
408
+ constructor(initial, onTick, options = {}) {
409
+ this.onTick = onTick;
410
+ this.position = initial;
411
+ this.target = initial;
412
+ this.spring = new Spring(options.angularFrequency ?? 18, options.dampingRatio ?? 0.86);
413
+ this.intervalMs = options.intervalMs ?? 40;
414
+ this.epsilon = options.epsilon ?? 0.01;
415
+ }
416
+ get value() {
417
+ return this.position;
418
+ }
419
+ get settled() {
420
+ return !this.timer;
421
+ }
422
+ retarget(target) {
423
+ this.target = target;
424
+ if (Math.abs(this.target - this.position) < this.epsilon && Math.abs(this.velocity) < this.epsilon) {
425
+ this.position = target;
426
+ this.onTick(target);
427
+ return;
428
+ }
429
+ this.ensureRunning();
430
+ }
431
+ snapTo(target) {
432
+ this.stop();
433
+ this.position = target;
434
+ this.velocity = 0;
435
+ this.target = target;
436
+ this.onTick(target);
437
+ }
438
+ ensureRunning() {
439
+ if (this.timer)
440
+ return;
441
+ this.timer = setInterval(() => {
442
+ const dt = this.intervalMs / 1000;
443
+ const [pos, vel] = this.spring.update(this.position, this.velocity, this.target, dt);
444
+ this.position = pos;
445
+ this.velocity = vel;
446
+ if (Math.abs(this.target - pos) < this.epsilon && Math.abs(vel) < this.epsilon) {
447
+ this.position = this.target;
448
+ this.stop();
449
+ this.onTick(this.target);
450
+ return;
451
+ }
452
+ this.onTick(pos);
453
+ }, this.intervalMs);
454
+ }
455
+ stop() {
456
+ if (!this.timer)
457
+ return;
458
+ clearInterval(this.timer);
459
+ this.timer = undefined;
460
+ }
461
+ }
462
+ function formatElapsed(ms) {
463
+ const seconds = ms / 1000;
464
+ return seconds < 60 ? `${seconds.toFixed(1)}s` : `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
465
+ }
466
+ function formatTokens(tokens) {
467
+ if (tokens < 1000)
468
+ return `${tokens} tokens`;
469
+ return `${(tokens / 1000).toFixed(1)}k tokens`;
470
+ }
471
+ function sparkline(values, glyphs = UNICODE_GLYPHS) {
472
+ const levels = glyphs.sparkLevels;
473
+ if (values.length === 0)
474
+ return "";
475
+ const max = Math.max(...values);
476
+ if (max <= 0)
477
+ return levels[0].repeat(values.length);
478
+ return values.map((value) => levels[Math.min(levels.length - 1, Math.floor(Math.max(0, value) / max * (levels.length - 1)))]).join("");
479
+ }
480
+ var PARTIAL_BLOCKS = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
481
+ var FULL_BLOCK = "█";
482
+ var TRACK_BLOCK = "░";
483
+ var DEFAULT_GRADIENT_FROM = { r: 124, g: 197, b: 255 };
484
+ var DEFAULT_GRADIENT_TO = { r: 255, g: 207, b: 107 };
485
+ function lerp(from, to, t) {
486
+ return { r: from.r + (to.r - from.r) * t, g: from.g + (to.g - from.g) * t, b: from.b + (to.b - from.b) * t };
487
+ }
488
+ function paintRgb(text, rgb, depth) {
489
+ if (depth === "none")
490
+ return text;
491
+ const r = Math.round(rgb.r);
492
+ const g = Math.round(rgb.g);
493
+ const b = Math.round(rgb.b);
494
+ const code = depth === "truecolor" ? `\x1B[38;2;${r};${g};${b}m` : `\x1B[38;5;${rgbTo256({ r, g, b })}m`;
495
+ return `${code}${text}\x1B[0m`;
496
+ }
497
+ function progressBar(fraction, width, options) {
498
+ const clamped = Math.max(0, Math.min(1, Number.isFinite(fraction) ? fraction : 0));
499
+ const cells = Math.max(1, Math.floor(width));
500
+ const from = options.from ?? DEFAULT_GRADIENT_FROM;
501
+ const to = options.to ?? DEFAULT_GRADIENT_TO;
502
+ const ascii = options.glyphs === ASCII_GLYPHS;
503
+ const eighths = ascii ? cells : cells * 8;
504
+ const filledEighths = Math.round(clamped * eighths);
505
+ let bar = "";
506
+ for (let index = 0;index < cells; index += 1) {
507
+ const t = cells <= 1 ? 0 : index / (cells - 1);
508
+ const rgb = lerp(from, to, t);
509
+ if (ascii) {
510
+ bar += index < filledEighths ? paintRgb("#", rgb, options.depth) : "-";
511
+ continue;
512
+ }
513
+ const cellStart = index * 8;
514
+ if (filledEighths >= cellStart + 8)
515
+ bar += paintRgb(FULL_BLOCK, rgb, options.depth);
516
+ else if (filledEighths > cellStart)
517
+ bar += paintRgb(PARTIAL_BLOCKS[filledEighths - cellStart], rgb, options.depth);
518
+ else
519
+ bar += options.depth === "none" ? TRACK_BLOCK : `\x1B[2m${TRACK_BLOCK}\x1B[0m`;
520
+ }
521
+ return bar;
522
+ }
523
+ function table(columns, rows, options) {
524
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
525
+ const border = borderGlyphsFor(options.borderStyle ?? "round", glyphs);
526
+ const terminalWidth = Math.max(1, options.width ?? process.stdout.columns ?? 80);
527
+ const count = columns.length;
528
+ const widths = columns.map((header, index) => Math.max(visibleWidth(header), ...rows.map((row2) => visibleWidth(row2[index] ?? ""))));
529
+ const budget = terminalWidth - (count + 1) - count * 2;
530
+ while (widths.reduce((sum, width) => sum + width, 0) > Math.max(count, budget)) {
531
+ const widest = widths.indexOf(Math.max(...widths));
532
+ if (widths[widest] <= 3)
533
+ break;
534
+ widths[widest] -= 1;
535
+ }
536
+ const cell = (text, width) => {
537
+ const clipped = visibleWidth(text) > width ? `${sliceToWidth(text, Math.max(0, width - visibleWidth(glyphs.ellipsis)))}${glyphs.ellipsis}` : text;
538
+ return `${clipped}${" ".repeat(Math.max(0, width - visibleWidth(clipped)))}`;
539
+ };
540
+ const rule = (left, mid, right) => `${left}${widths.map((width) => border.horizontal.repeat(width + 2)).join(mid)}${right}`;
541
+ const row = (cells) => `${border.vertical} ${widths.map((width, index) => cell(cells[index] ?? "", width)).join(` ${border.vertical} `)} ${border.vertical}`;
542
+ return [
543
+ rule(border.topLeft, border.horizontal, border.topRight),
544
+ row(columns.map((header) => paint2(header, DIM2, options.depth))),
545
+ rule(border.teeLeft, border.cross, border.teeRight),
546
+ ...rows.map((line) => row(line)),
547
+ rule(border.bottomLeft, border.horizontal, border.bottomRight)
548
+ ].join(`
549
+ `);
550
+ }
551
+ function scrollPercent(offsetFromTop, contentLines, viewportHeight) {
552
+ const maxOffset = Math.max(0, contentLines - viewportHeight);
553
+ if (maxOffset === 0)
554
+ return 1;
555
+ return Math.max(0, Math.min(1, offsetFromTop / maxOffset));
556
+ }
557
+ function scrollIndicator(fraction) {
558
+ if (fraction >= 1)
559
+ return "Bot";
560
+ if (fraction <= 0)
561
+ return "Top";
562
+ return `${Math.round(fraction * 100)}%`;
563
+ }
564
+ function paginator(current, total, options = {}) {
565
+ const count = Math.max(1, Math.floor(total));
566
+ const index = Math.max(0, Math.min(Math.floor(current), count - 1));
567
+ if (options.style === "dots") {
568
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
569
+ return Array.from({ length: count }, (_, position) => position === index ? glyphs.bullet : glyphs.middot).join(" ");
570
+ }
571
+ return `${index + 1}/${count}`;
572
+ }
573
+ var VERBS = ["Thinking", "Reasoning", "Tracing", "Considering"];
574
+ function thinkingVerb(elapsedMs) {
575
+ return VERBS[Math.floor(Math.max(0, elapsedMs) / 6000) % VERBS.length];
576
+ }
577
+ function activityLabel(phase, operation, elapsedMs) {
578
+ if (phase === "thinking")
579
+ return thinkingVerb(elapsedMs);
580
+ switch (operation) {
581
+ case "read_file":
582
+ return "Reading file";
583
+ case "list_files":
584
+ case "glob_files":
585
+ case "grep_files":
586
+ return "Searching workspace";
587
+ case "write_file":
588
+ case "edit_file":
589
+ return "Editing workspace";
590
+ case "run_command":
591
+ return "Running command";
592
+ case "web_search":
593
+ return "Searching the web";
594
+ case "deep_research":
595
+ return "Researching in depth";
596
+ case "todo_write":
597
+ return "Updating plan";
598
+ default:
599
+ return "Running operation";
600
+ }
601
+ }
602
+ function stepProgress(done, total, options) {
603
+ const safeTotal = Math.max(0, Math.floor(Number.isFinite(total) ? total : 0));
604
+ const safeDone = Math.max(0, Math.min(safeTotal, Math.floor(Number.isFinite(done) ? done : 0)));
605
+ const counted = `${safeDone}/${safeTotal}`;
606
+ const label = options.label ? `${options.label} ` : "";
607
+ if (safeTotal === 0)
608
+ return `${label}${counted}`;
609
+ const width = Math.floor(options.width ?? 0);
610
+ if (width <= 0)
611
+ return `${label}${counted}`;
612
+ const bar = progressBar(safeDone / safeTotal, width, {
613
+ depth: options.depth,
614
+ glyphs: options.glyphs ?? UNICODE_GLYPHS,
615
+ ...options.from ? { from: options.from } : {},
616
+ ...options.to ? { to: options.to } : {}
617
+ });
618
+ return `${label}${counted} ${bar}`;
619
+ }
620
+ function formatStatusLine(fields, width, depth, glyphs = UNICODE_GLYPHS, accent = CYAN2) {
621
+ const label = activityLabel(fields.phase ?? "thinking", fields.operation, fields.elapsedMs);
622
+ const ellipsis = glyphs.ellipsis;
623
+ const separator = ` ${glyphs.middot} `;
624
+ const left = `${fields.spinnerGlyph} ${label}${ellipsis}`;
625
+ const paintedLeft = `${paint2(fields.spinnerGlyph, accent, depth)} ${paint2(`${label}${ellipsis}`, DIM2, depth)}`;
626
+ const optional = [
627
+ fields.cost,
628
+ fields.tokens > 0 ? formatTokens(fields.tokens) : "",
629
+ fields.toolCalls > 0 ? `${fields.toolCalls} tool${fields.toolCalls === 1 ? "" : "s"}` : "",
630
+ fields.badge ?? "",
631
+ fields.steps && fields.steps.total > 0 ? stepProgress(fields.steps.done, fields.steps.total, { label: fields.steps.label ?? "steps", depth: "none" }) : "",
632
+ fields.mode,
633
+ fields.balance ?? ""
634
+ ].filter((segment) => segment !== "");
635
+ let kept = [...optional].reverse();
636
+ for (;; ) {
637
+ const right = [...kept, formatElapsed(fields.elapsedMs)].join(`${separator}`);
638
+ const total = visibleWidth(left) + 1 + right.length;
639
+ if (total <= width || kept.length === 0) {
640
+ if (total > width) {
641
+ if (visibleWidth(left) <= width)
642
+ return paintedLeft;
643
+ const compact = `${fields.spinnerGlyph} ${ellipsis}`;
644
+ if (visibleWidth(compact) <= width)
645
+ return `${paint2(fields.spinnerGlyph, accent, depth)} ${paint2(ellipsis, DIM2, depth)}`;
646
+ return width > 0 ? paint2(glyphs.star, accent, depth) : "";
647
+ }
648
+ const gap = Math.max(1, width - visibleWidth(left) - right.length);
649
+ return `${paintedLeft}${" ".repeat(gap)}${paint2(right, DIM2, depth)}`;
650
+ }
651
+ kept = kept.slice(0, -1);
652
+ }
653
+ }
654
+ function rowsOccupied(text, columns) {
655
+ const width = visibleWidth(text);
656
+ if (width === 0)
657
+ return 1;
658
+ return Math.ceil(width / Math.max(1, columns));
659
+ }
660
+ class MarkdownStream {
661
+ stream;
662
+ depth;
663
+ columns;
664
+ live;
665
+ glyphs;
666
+ pending = "";
667
+ atLineStart = true;
668
+ state = newMarkdownState();
669
+ constructor(stream = terminalStream, depth = "none", columns = () => process.stdout.columns ?? 80, live = true, glyphs = UNICODE_GLYPHS) {
670
+ this.stream = stream;
671
+ this.depth = depth;
672
+ this.columns = columns;
673
+ this.live = live;
674
+ this.glyphs = glyphs;
675
+ }
676
+ get active() {
677
+ return this.live ? this.pending !== "" : !this.atLineStart;
678
+ }
679
+ push(text) {
680
+ if (!this.live) {
681
+ if (text === "")
682
+ return;
683
+ this.stream.write(text);
684
+ this.atLineStart = text.endsWith(`
685
+ `);
686
+ return;
687
+ }
688
+ const parts = text.split(`
689
+ `);
690
+ for (let index = 0;index < parts.length; index += 1) {
691
+ if (index > 0)
692
+ this.finalizeLine();
693
+ if (parts[index] !== "") {
694
+ this.pending += parts[index];
695
+ this.stream.write(parts[index]);
696
+ }
697
+ }
698
+ }
699
+ end() {
700
+ if (!this.live) {
701
+ if (!this.atLineStart) {
702
+ this.stream.write(`
703
+ `);
704
+ this.atLineStart = true;
705
+ }
706
+ return;
707
+ }
708
+ if (this.pending !== "")
709
+ this.finalizeLine();
710
+ if (this.state.inFence) {
711
+ const rendered = renderMarkdownLine("```", this.state, { width: this.columns(), depth: this.depth, glyphs: this.glyphs });
712
+ this.stream.write(`${rendered.join(`
713
+ `)}
714
+ `);
715
+ }
716
+ }
717
+ reset() {
718
+ this.pending = "";
719
+ this.atLineStart = true;
720
+ this.state = newMarkdownState();
721
+ }
722
+ finalizeLine() {
723
+ this.erasePending();
724
+ const rendered = renderMarkdownLine(this.pending, this.state, { width: this.columns(), depth: this.depth, glyphs: this.glyphs });
725
+ this.stream.write(`${rendered.join(`
726
+ `)}
727
+ `);
728
+ this.pending = "";
729
+ }
730
+ erasePending() {
731
+ if (this.pending === "")
732
+ return;
733
+ const rows = rowsOccupied(this.pending, this.columns());
734
+ this.stream.write("\r\x1B[2K");
735
+ for (let index = 1;index < rows; index += 1)
736
+ this.stream.write("\x1B[1A\x1B[2K");
737
+ }
738
+ }
739
+ function box(lines, options) {
740
+ const terminalWidth = options.width ?? process.stdout.columns ?? 80;
741
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
742
+ const border = borderGlyphsFor(options.borderStyle ?? "round", glyphs);
743
+ const titleWidth = options.title ? visibleWidth(options.title) : 0;
744
+ const titlePaint = TONE_CODES[options.titleColor ?? "cyan"];
745
+ const edge = (text) => options.borderColor === undefined ? text : paint2(text, TONE_CODES[options.borderColor], options.depth);
746
+ const contentWidth = Math.min(Math.max(titleWidth, ...lines.map((line) => visibleWidth(line)), 1), Math.max(1, terminalWidth - 4));
747
+ const horizontal = border.horizontal.repeat(contentWidth + 2);
748
+ const top = options.title ? `${edge(`${border.topLeft}${border.horizontal}`)} ${paint2(options.title, titlePaint, options.depth)} ${edge(`${border.horizontal.repeat(Math.max(0, contentWidth - titleWidth - 1))}${border.topRight}`)}` : edge(`${border.topLeft}${horizontal}${border.topRight}`);
749
+ const bottom = edge(`${border.bottomLeft}${horizontal}${border.bottomRight}`);
750
+ const body = lines.map((line) => {
751
+ const clipped = visibleWidth(line) > contentWidth ? `${sliceToWidth(line, contentWidth - visibleWidth(glyphs.ellipsis))}${glyphs.ellipsis}` : line;
752
+ return `${edge(border.vertical)} ${clipped}${" ".repeat(Math.max(0, contentWidth - visibleWidth(clipped)))} ${edge(border.vertical)}`;
753
+ });
754
+ return [top, ...body, bottom].join(`
755
+ `);
756
+ }
757
+ function sliceToWidth(text, width) {
758
+ let taken = "";
759
+ let used = 0;
760
+ for (const token of text.match(/\x1b\[[0-9;]*m|[\s\S]/gu) ?? []) {
761
+ if (token.startsWith("\x1B")) {
762
+ taken += token;
763
+ continue;
764
+ }
765
+ const next = visibleWidth(token);
766
+ if (used + next > width)
767
+ break;
768
+ taken += token;
769
+ used += next;
770
+ }
771
+ return taken;
772
+ }
773
+ function padToWidth(text, width, align = "left") {
774
+ const size = visibleWidth(text);
775
+ if (size >= width)
776
+ return sliceToWidth(text, width);
777
+ const slack = width - size;
778
+ if (align === "right")
779
+ return " ".repeat(slack) + text;
780
+ if (align === "center")
781
+ return " ".repeat(Math.floor(slack / 2)) + text + " ".repeat(Math.ceil(slack / 2));
782
+ return text + " ".repeat(slack);
783
+ }
784
+ function joinHorizontal(left, right, options) {
785
+ const separator = options.separator ?? " ";
786
+ return `${padToWidth(left, options.leftWidth, options.leftAlign)}${separator}${padToWidth(right, options.rightWidth, options.rightAlign)}`;
787
+ }
788
+ var TONE_CODES = { cyan: CYAN2, green: GREEN2, yellow: YELLOW2, red: RED };
789
+ var MODE_COLORS = { plan: YELLOW2, auto: GREEN2, build: CYAN2, defender: RED };
790
+ var PROMPT_CHROME_COLUMNS = 6;
791
+ function renderPromptBox(options) {
792
+ const { mode, workspace, depth } = options;
793
+ const glyphs = options.glyphs ?? UNICODE_GLYPHS;
794
+ const border = borderGlyphsFor(options.palette?.borderStyle ?? options.borderStyle ?? "round", glyphs);
795
+ const width = Math.max(12, options.width);
796
+ const primary = options.palette?.primary ?? CYAN2;
797
+ const modeColor = options.palette ? { plan: options.palette.warning, auto: options.palette.success, build: primary, defender: options.palette.error }[mode] ?? primary : MODE_COLORS[mode] ?? CYAN2;
798
+ const horizontal = (count) => paint2(border.horizontal.repeat(Math.max(0, count)), primary, depth);
799
+ const CHROME = PROMPT_CHROME_COLUMNS;
800
+ const status = options.status ?? "";
801
+ const separator = ` ${glyphs.middot} `;
802
+ const full = `${paint2(glyphs.star, primary, depth)} ${paint2("archymedes", primary, depth)}${separator}${paint2(mode, modeColor, depth)}`;
803
+ const compact = `${paint2(glyphs.star, primary, depth)} ${paint2(mode, modeColor, depth)}`;
804
+ const statusRoom = status === "" ? 0 : visibleWidth(status) + 2;
805
+ const showStatus = status !== "" && width - CHROME - 1 - statusRoom - visibleWidth(compact) >= 0;
806
+ const room = width - CHROME - 1 - (showStatus ? statusRoom : 0);
807
+ const workspaceRoom = room - visibleWidth(full) - visibleWidth(separator);
808
+ let title;
809
+ if (workspaceRoom >= 4) {
810
+ const shown = visibleWidth(workspace) <= workspaceRoom ? workspace : `${sliceToWidth(workspace, Math.max(0, workspaceRoom - visibleWidth(glyphs.ellipsis)))}${glyphs.ellipsis}`;
811
+ title = `${full}${separator}${paint2(shown, DIM2, depth)}`;
812
+ } else if (visibleWidth(full) <= room)
813
+ title = full;
814
+ else if (visibleWidth(compact) <= room)
815
+ title = compact;
816
+ else
817
+ title = paint2(glyphs.star, primary, depth);
818
+ const tail = showStatus ? ` ${status} ` : "";
819
+ const filler = Math.max(1, width - visibleWidth(title) - visibleWidth(tail) - CHROME);
820
+ const top = `${paint2(`${border.topLeft}${border.horizontal}`, primary, depth)} ${title} ${horizontal(filler)}${tail}${horizontal(1)}${paint2(border.topRight, primary, depth)}`;
821
+ const prefix = `${paint2(border.vertical, primary, depth)} ${paint2(glyphs.caret, modeColor, depth)} `;
822
+ const bottom = `${paint2(border.bottomLeft, primary, depth)}${horizontal(width - 2)}${paint2(border.bottomRight, primary, depth)}`;
823
+ return { top, prefix, bottom };
824
+ }
825
+
826
+ export { UNICODE_GLYPHS, ASCII_GLYPHS, borderGlyphsFor, visibleWidth, newMarkdownState, renderMarkdownLine, renderMarkdown, terminalStream, archymedesSpinnerFrame, SpringAnimator, sparkline, progressBar, table, scrollPercent, scrollIndicator, paginator, stepProgress, formatStatusLine, rowsOccupied, box, sliceToWidth, padToWidth, joinHorizontal, renderPromptBox };