@pentoshi/clai 0.5.5 → 0.5.8

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,16 +1,25 @@
1
1
  import chalk from "chalk";
2
- // Lightweight, dependency-free spinner used while we wait on the model
3
- // (especially during long thinking phases when no visible tokens stream).
4
- // Renders on a single line and clears itself on stop, so output below it
5
- // stays clean.
2
+ // Live, dependency-free thinking indicator. Renders below the prompt as
3
+ // up to two dim lines:
4
+ // thinking 12.3s · 240 reasoning tokens
5
+ // tail of the most recent reasoning text…
6
+ // On stop() it erases its own lines so subsequent output starts clean.
6
7
  const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
7
8
  const FRAME_INTERVAL_MS = 80;
9
+ const PREVIEW_TAIL_CHARS = 220;
10
+ function stripAnsi(text) {
11
+ // biome-ignore lint: ANSI escape pattern is intentional.
12
+ return text.replace(/\x1b\[[0-9;]*m/g, "");
13
+ }
14
+ function clamp(n, lo, hi) {
15
+ return Math.max(lo, Math.min(hi, n));
16
+ }
8
17
  export function startThinkingSpinner(initialLabel = "thinking", signal) {
9
18
  if (!process.stdout.isTTY) {
10
- // No-op for piped output so logs stay grep-friendly.
11
19
  return {
12
20
  setLabel: () => { },
13
21
  bumpReasoning: () => { },
22
+ pushPreview: () => { },
14
23
  stop: () => { },
15
24
  };
16
25
  }
@@ -19,31 +28,61 @@ export function startThinkingSpinner(initialLabel = "thinking", signal) {
19
28
  let frame = 0;
20
29
  const start = Date.now();
21
30
  let stopped = false;
22
- let lastWidth = 0;
31
+ let renderedLines = 0;
32
+ let preview = "";
33
+ const erase = () => {
34
+ if (renderedLines === 0)
35
+ return;
36
+ // Cursor is currently after the last rendered line. Move back up,
37
+ // clearing each line as we go.
38
+ for (let i = 0; i < renderedLines; i += 1) {
39
+ process.stdout.write("\r\x1b[2K"); // clear current line
40
+ if (i < renderedLines - 1)
41
+ process.stdout.write("\x1b[1A"); // move up
42
+ }
43
+ renderedLines = 0;
44
+ };
45
+ const truncateForWidth = (text, width) => {
46
+ const visibleLen = stripAnsi(text).length;
47
+ if (visibleLen <= width)
48
+ return text;
49
+ // For preview text that has no ANSI codes, simple slice works.
50
+ return text.slice(0, Math.max(0, width - 1)) + "…";
51
+ };
23
52
  const render = () => {
24
53
  if (stopped)
25
54
  return;
55
+ erase();
26
56
  const elapsed = ((Date.now() - start) / 1000).toFixed(1);
27
57
  const tokenSuffix = reasoningTokens > 0
28
58
  ? chalk.dim(` · ${reasoningTokens.toLocaleString()} reasoning tokens`)
29
59
  : "";
30
- const line = chalk.magenta(FRAMES[frame % FRAMES.length]) +
60
+ const headLine = chalk.magenta(FRAMES[frame % FRAMES.length]) +
31
61
  " " +
32
62
  chalk.dim(`${label} ${elapsed}s`) +
33
63
  tokenSuffix;
34
- process.stdout.write(`\r${line}`);
35
- // Pad to clear leftover characters when the line shrinks.
36
- const visibleLen = stripAnsi(line).length;
37
- if (visibleLen < lastWidth) {
38
- process.stdout.write(" ".repeat(lastWidth - visibleLen));
39
- process.stdout.write(`\r${line}`);
64
+ const cols = clamp(process.stdout.columns ?? 80, 40, 200);
65
+ let lines = [headLine];
66
+ if (preview) {
67
+ // Take last N chars, drop ANSI/control noise, collapse whitespace,
68
+ // and indent so the preview lines up under the spinner.
69
+ const tail = preview
70
+ .slice(-PREVIEW_TAIL_CHARS)
71
+ // biome-ignore lint: control char strip
72
+ .replace(/[\x00-\x09\x0b-\x1f\x7f]/g, "")
73
+ .replace(/\s+/g, " ")
74
+ .trim();
75
+ if (tail) {
76
+ const previewLine = chalk.dim.italic(` ${tail}`);
77
+ lines.push(truncateForWidth(previewLine, cols));
78
+ }
40
79
  }
41
- lastWidth = visibleLen;
80
+ process.stdout.write(lines.join("\n"));
81
+ renderedLines = lines.length;
42
82
  frame += 1;
43
83
  };
44
84
  render();
45
85
  const timer = setInterval(render, FRAME_INTERVAL_MS);
46
- // Don't keep the process alive just for the spinner.
47
86
  if (typeof timer.unref === "function")
48
87
  timer.unref();
49
88
  const stop = () => {
@@ -51,11 +90,8 @@ export function startThinkingSpinner(initialLabel = "thinking", signal) {
51
90
  return;
52
91
  stopped = true;
53
92
  clearInterval(timer);
54
- // Erase the spinner line entirely so subsequent output starts clean.
55
- process.stdout.write(`\r${" ".repeat(Math.max(lastWidth, 1))}\r`);
93
+ erase();
56
94
  };
57
- // Stop the moment the user aborts so the spinner can never linger past
58
- // an ESC, even if upstream cleanup paths are skipped by an exception.
59
95
  if (signal) {
60
96
  if (signal.aborted)
61
97
  stop();
@@ -69,10 +105,16 @@ export function startThinkingSpinner(initialLabel = "thinking", signal) {
69
105
  bumpReasoning(tokens) {
70
106
  reasoningTokens += tokens;
71
107
  },
108
+ pushPreview(text) {
109
+ if (!text)
110
+ return;
111
+ preview += text;
112
+ // Only keep what we'll actually show, plus a small overflow buffer.
113
+ if (preview.length > PREVIEW_TAIL_CHARS * 4) {
114
+ preview = preview.slice(-PREVIEW_TAIL_CHARS * 2);
115
+ }
116
+ },
72
117
  stop,
73
118
  };
74
119
  }
75
- function stripAnsi(text) {
76
- return text.replace(/\x1b\[[0-9;]*m/g, "");
77
- }
78
120
  //# sourceMappingURL=spinner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/ui/spinner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/ui/spinner.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pentoshi/clai",
3
- "version": "0.5.5",
3
+ "version": "0.5.8",
4
4
  "description": "A fast, cross-platform AI CLI assistant with ask and agent modes for shell tasks, file operations, and cybersecurity workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,8 +53,7 @@
53
53
  "zod": "^3.24.1"
54
54
  },
55
55
  "optionalDependencies": {
56
- "better-sqlite3": "^11.7.0",
57
- "keytar": "^7.9.0"
56
+ "@napi-rs/keyring": "^1.3.0"
58
57
  },
59
58
  "devDependencies": {
60
59
  "@types/node": "^25.6.0",