agent-sh 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/acp-client.d.ts +24 -0
- package/dist/acp-client.js +155 -33
- package/dist/context-manager.d.ts +5 -3
- package/dist/context-manager.js +62 -31
- package/dist/core.js +10 -0
- package/dist/event-bus.d.ts +26 -0
- package/dist/event-bus.js +10 -0
- package/dist/extension-loader.js +3 -14
- package/dist/extensions/shell-exec.js +27 -22
- package/dist/extensions/tui-renderer.d.ts +1 -1
- package/dist/extensions/tui-renderer.js +369 -126
- package/dist/index.js +184 -37
- package/dist/input-handler.d.ts +10 -0
- package/dist/input-handler.js +169 -10
- package/dist/mcp-server.js +37 -8
- package/dist/settings.d.ts +44 -0
- package/dist/settings.js +61 -0
- package/dist/shell.d.ts +1 -0
- package/dist/shell.js +44 -4
- package/dist/types.d.ts +17 -0
- package/dist/utils/ansi.d.ts +4 -1
- package/dist/utils/ansi.js +60 -2
- package/dist/utils/box-frame.js +2 -1
- package/dist/utils/diff-renderer.js +1 -1
- package/dist/utils/frame-renderer.d.ts +26 -0
- package/dist/utils/frame-renderer.js +76 -0
- package/dist/utils/handler-registry.d.ts +41 -0
- package/dist/utils/handler-registry.js +52 -0
- package/dist/utils/line-editor.d.ts +21 -1
- package/dist/utils/line-editor.js +193 -99
- package/dist/utils/markdown.d.ts +15 -6
- package/dist/utils/markdown.js +106 -67
- package/dist/utils/output-writer.d.ts +22 -0
- package/dist/utils/output-writer.js +29 -0
- package/dist/utils/stream-transform.d.ts +70 -0
- package/dist/utils/stream-transform.js +229 -0
- package/dist/utils/tool-display.d.ts +11 -8
- package/dist/utils/tool-display.js +69 -46
- package/examples/extensions/latex-images.ts +142 -0
- package/examples/pi-agent-sh.ts +166 -0
- package/package.json +10 -2
|
@@ -10,82 +10,190 @@
|
|
|
10
10
|
* silently dropped. Alternative renderers (web UI, logging, minimal)
|
|
11
11
|
* can subscribe to the same events.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { highlight } from "cli-highlight";
|
|
14
|
+
import { MarkdownRenderer, wrapLine } from "../utils/markdown.js";
|
|
15
|
+
import { createFencedBlockTransform } from "../utils/stream-transform.js";
|
|
14
16
|
import { palette as p } from "../utils/palette.js";
|
|
15
|
-
import { renderToolCall,
|
|
17
|
+
import { renderToolCall, createSpinner, renderSpinnerLine, } from "../utils/tool-display.js";
|
|
16
18
|
import { renderDiff } from "../utils/diff-renderer.js";
|
|
17
19
|
import { renderBoxFrame } from "../utils/box-frame.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
import { getSettings } from "../settings.js";
|
|
21
|
+
import { StdoutWriter } from "../utils/output-writer.js";
|
|
22
|
+
/** Encode a PNG buffer as a terminal inline image escape sequence. */
|
|
23
|
+
function encodeImageForTerminal(data) {
|
|
24
|
+
const b64 = data.toString("base64");
|
|
25
|
+
if (process.env.TERM_PROGRAM === "iTerm.app" || process.env.TERM_PROGRAM === "WezTerm") {
|
|
26
|
+
return `\x1b]1337;File=inline=1;size=${data.length};preserveAspectRatio=1:${b64}\x07`;
|
|
27
|
+
}
|
|
28
|
+
if (process.env.KITTY_WINDOW_ID || process.env.TERM_PROGRAM === "ghostty") {
|
|
29
|
+
const chunks = [];
|
|
30
|
+
for (let i = 0; i < b64.length; i += 4096) {
|
|
31
|
+
const chunk = b64.slice(i, i + 4096);
|
|
32
|
+
const isLast = i + 4096 >= b64.length;
|
|
33
|
+
chunks.push(i === 0
|
|
34
|
+
? `\x1b_Gf=100,t=d,a=T,m=${isLast ? 0 : 1};${chunk}\x1b\\`
|
|
35
|
+
: `\x1b_Gm=${isLast ? 0 : 1};${chunk}\x1b\\`);
|
|
36
|
+
}
|
|
37
|
+
return chunks.join("");
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function createRenderState() {
|
|
42
|
+
return {
|
|
43
|
+
renderer: null,
|
|
44
|
+
hadToolCalls: false,
|
|
45
|
+
spinner: null,
|
|
46
|
+
spinnerLabel: "",
|
|
47
|
+
spinnerOpts: {},
|
|
48
|
+
spinnerInterval: null,
|
|
49
|
+
spinnerStartTime: 0,
|
|
50
|
+
lastCommand: "",
|
|
51
|
+
toolLineOpen: false,
|
|
52
|
+
currentToolKind: undefined,
|
|
53
|
+
commandOutputBuffer: "",
|
|
54
|
+
commandOutputLineCount: 0,
|
|
55
|
+
commandOutputOverflow: 0,
|
|
56
|
+
isThinking: false,
|
|
57
|
+
showThinkingText: false,
|
|
58
|
+
thinkingPending: false,
|
|
59
|
+
lastTruncatedDiff: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export default function activate(ctx) {
|
|
63
|
+
const { bus, getAcpClient, define } = ctx;
|
|
64
|
+
const writer = new StdoutWriter();
|
|
65
|
+
const s = createRenderState();
|
|
66
|
+
// ── Register fenced block transform (code blocks → ContentBlock) ──
|
|
67
|
+
// Nobody is special — tui-renderer uses the same primitive as any extension.
|
|
68
|
+
const fencedTransform = createFencedBlockTransform(bus, {
|
|
69
|
+
open: /^```(\w*)\s*$/,
|
|
70
|
+
close: /^```\s*$/,
|
|
71
|
+
transform(match, content) {
|
|
72
|
+
return { type: "code-block", language: match[1] || "", code: content };
|
|
73
|
+
},
|
|
74
|
+
});
|
|
29
75
|
// ── Event subscriptions ─────────────────────────────────────
|
|
30
76
|
bus.on("agent:query", (e) => {
|
|
77
|
+
s.spinnerStartTime = 0;
|
|
31
78
|
showUserQuery(e.query);
|
|
32
79
|
startAgentResponse();
|
|
33
80
|
startThinkingSpinner();
|
|
34
81
|
});
|
|
35
82
|
bus.on("agent:thinking-chunk", (e) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (showThinkingText) {
|
|
40
|
-
|
|
83
|
+
s.thinkingPending = true;
|
|
84
|
+
if (!s.isThinking) {
|
|
85
|
+
s.isThinking = true;
|
|
86
|
+
if (s.showThinkingText) {
|
|
87
|
+
stopCurrentSpinner();
|
|
88
|
+
if (!s.renderer)
|
|
41
89
|
startAgentResponse();
|
|
42
|
-
renderer.writeLine(`${p.dim}
|
|
90
|
+
s.renderer.writeLine(`${p.dim}Thinking (ctrl+t to collapse)${p.reset}`);
|
|
91
|
+
drain();
|
|
43
92
|
}
|
|
44
93
|
else {
|
|
45
|
-
|
|
94
|
+
// Restart spinner with ctrl+t hint now that we know thinking is available
|
|
95
|
+
startThinkingSpinner();
|
|
46
96
|
}
|
|
47
97
|
}
|
|
48
|
-
if (showThinkingText && e.text) {
|
|
49
|
-
|
|
98
|
+
if (s.showThinkingText && e.text) {
|
|
99
|
+
s.thinkingPending = false;
|
|
100
|
+
if (!s.renderer)
|
|
50
101
|
startAgentResponse();
|
|
51
|
-
renderer.push(`${p.dim}${e.text}${p.reset}`);
|
|
52
|
-
|
|
102
|
+
s.renderer.push(`${p.dim}${e.text}${p.reset}`);
|
|
103
|
+
drain();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
bus.on("agent:response-chunk", (e) => {
|
|
107
|
+
if (e.blocks) {
|
|
108
|
+
// Inject spacing: append \n to text blocks that precede non-text blocks
|
|
109
|
+
const blocks = e.blocks;
|
|
110
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
111
|
+
const block = blocks[i];
|
|
112
|
+
const next = blocks[i + 1];
|
|
113
|
+
if (block.type === "text" && next && next.type !== "text") {
|
|
114
|
+
block.text += "\n";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const block of blocks) {
|
|
118
|
+
switch (block.type) {
|
|
119
|
+
case "text":
|
|
120
|
+
if (block.text)
|
|
121
|
+
writeAgentText(block.text);
|
|
122
|
+
break;
|
|
123
|
+
case "code-block":
|
|
124
|
+
writeCodeBlock(block.language, block.code);
|
|
125
|
+
break;
|
|
126
|
+
case "image":
|
|
127
|
+
writeInlineImage(block.data);
|
|
128
|
+
break;
|
|
129
|
+
case "raw":
|
|
130
|
+
flushForRaw();
|
|
131
|
+
writer.write(block.escape);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
writeAgentText(e.text);
|
|
53
138
|
}
|
|
54
139
|
});
|
|
55
|
-
bus.on("agent:response-chunk", (e) => writeAgentText(e.text));
|
|
56
140
|
bus.on("agent:response-done", () => {
|
|
57
|
-
isThinking = false;
|
|
141
|
+
s.isThinking = false;
|
|
58
142
|
endAgentResponse();
|
|
59
143
|
});
|
|
60
144
|
bus.on("agent:tool-call", (e) => {
|
|
61
|
-
lastCommand = e.tool;
|
|
145
|
+
s.lastCommand = e.tool;
|
|
62
146
|
});
|
|
63
147
|
bus.on("agent:tool-started", (e) => {
|
|
148
|
+
fencedTransform.flush();
|
|
64
149
|
stopCurrentSpinner();
|
|
65
|
-
|
|
66
|
-
|
|
150
|
+
s.currentToolKind = e.kind;
|
|
151
|
+
if (e.title === "user_shell") {
|
|
152
|
+
closeToolLine();
|
|
153
|
+
if (!s.renderer)
|
|
154
|
+
startAgentResponse();
|
|
155
|
+
s.renderer.flush();
|
|
156
|
+
const cmd = e.rawInput?.command || "";
|
|
157
|
+
s.renderer.writeLine(`${p.dim}▶ user_shell: ${cmd}${p.reset}`);
|
|
158
|
+
drain();
|
|
159
|
+
s.hadToolCalls = true;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
showToolCall(e.title, s.lastCommand, e);
|
|
163
|
+
}
|
|
164
|
+
s.lastCommand = "";
|
|
165
|
+
});
|
|
166
|
+
bus.on("agent:tool-completed", (e) => {
|
|
167
|
+
showToolComplete(e.exitCode);
|
|
168
|
+
s.currentToolKind = undefined;
|
|
169
|
+
s.spinnerStartTime = 0;
|
|
170
|
+
startThinkingSpinner();
|
|
67
171
|
});
|
|
68
|
-
bus.on("agent:tool-completed", (e) => showToolComplete(e.exitCode));
|
|
69
172
|
bus.on("agent:tool-output-chunk", (e) => writeCommandOutput(e.chunk));
|
|
70
173
|
bus.on("agent:tool-output", () => flushCommandOutput());
|
|
71
174
|
bus.on("agent:cancelled", () => {
|
|
72
|
-
isThinking = false;
|
|
175
|
+
s.isThinking = false;
|
|
73
176
|
stopCurrentSpinner();
|
|
74
177
|
showInfo("(cancelled)");
|
|
75
178
|
endAgentResponse();
|
|
76
179
|
});
|
|
180
|
+
bus.on("agent:processing-done", () => {
|
|
181
|
+
s.isThinking = false;
|
|
182
|
+
stopCurrentSpinner();
|
|
183
|
+
endAgentResponse();
|
|
184
|
+
});
|
|
77
185
|
bus.on("agent:error", (e) => showError(e.message));
|
|
78
|
-
// Flush rendering state and show inline diff for file writes
|
|
79
186
|
bus.on("permission:request", (e) => {
|
|
80
187
|
stopCurrentSpinner();
|
|
81
188
|
flushCommandOutput();
|
|
82
|
-
renderer
|
|
189
|
+
if (s.renderer) {
|
|
190
|
+
s.renderer.flush();
|
|
191
|
+
drain();
|
|
192
|
+
}
|
|
83
193
|
if (e.kind === "file-write" && e.metadata?.diff) {
|
|
84
194
|
showFileDiff(e.title, e.metadata.diff);
|
|
85
195
|
}
|
|
86
196
|
else {
|
|
87
|
-
// Non-file permission (e.g. tool-call) — end response box
|
|
88
|
-
// so interactive extensions can render their own UI
|
|
89
197
|
endAgentResponse();
|
|
90
198
|
}
|
|
91
199
|
});
|
|
@@ -98,38 +206,46 @@ export default function activate({ bus }) {
|
|
|
98
206
|
bus.on("ui:info", (e) => showInfo(e.message));
|
|
99
207
|
bus.on("ui:error", (e) => showError(e.message));
|
|
100
208
|
// ── Rendering functions ─────────────────────────────────────
|
|
101
|
-
function
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
catch { }
|
|
209
|
+
function drain() {
|
|
210
|
+
if (!s.renderer)
|
|
211
|
+
return;
|
|
212
|
+
for (const line of s.renderer.drainLines()) {
|
|
213
|
+
writer.write(line + "\n");
|
|
107
214
|
}
|
|
108
215
|
}
|
|
109
216
|
function startAgentResponse() {
|
|
110
|
-
renderer = new MarkdownRenderer();
|
|
111
|
-
|
|
112
|
-
renderer.printTopBorder();
|
|
217
|
+
s.renderer = new MarkdownRenderer(writer.columns);
|
|
218
|
+
s.hadToolCalls = false;
|
|
219
|
+
s.renderer.printTopBorder();
|
|
220
|
+
drain();
|
|
221
|
+
}
|
|
222
|
+
function showCollapsedThinking() {
|
|
223
|
+
if (s.thinkingPending && !s.showThinkingText) {
|
|
224
|
+
if (!s.renderer)
|
|
225
|
+
startAgentResponse();
|
|
226
|
+
s.renderer.writeLine(`${p.muted}… thinking${p.reset}`);
|
|
227
|
+
s.thinkingPending = false;
|
|
228
|
+
}
|
|
113
229
|
}
|
|
114
230
|
function endAgentResponse() {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
renderer
|
|
231
|
+
closeToolLine();
|
|
232
|
+
stopCurrentSpinner();
|
|
233
|
+
if (s.renderer) {
|
|
234
|
+
s.renderer.flush();
|
|
235
|
+
s.renderer.printBottomBorder();
|
|
236
|
+
drain();
|
|
237
|
+
s.renderer = null;
|
|
119
238
|
}
|
|
120
239
|
}
|
|
121
240
|
function showUserQuery(query) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const contentW = boxW - 4; // inside box padding
|
|
125
|
-
// Wrap long queries to fit within box
|
|
241
|
+
const boxW = Math.min(84, writer.columns);
|
|
242
|
+
const contentW = boxW - 4;
|
|
126
243
|
const lines = [];
|
|
127
244
|
for (const raw of query.split("\n")) {
|
|
128
245
|
if (raw.length <= contentW) {
|
|
129
246
|
lines.push(`${p.accent}${raw}${p.reset}`);
|
|
130
247
|
}
|
|
131
248
|
else {
|
|
132
|
-
// Simple word wrap
|
|
133
249
|
let remaining = raw;
|
|
134
250
|
while (remaining.length > contentW) {
|
|
135
251
|
let breakAt = remaining.lastIndexOf(" ", contentW);
|
|
@@ -148,101 +264,209 @@ export default function activate({ bus }) {
|
|
|
148
264
|
borderColor: p.accent,
|
|
149
265
|
title: `${p.accent}${p.bold}❯${p.reset}`,
|
|
150
266
|
});
|
|
151
|
-
|
|
267
|
+
writer.write("\n");
|
|
152
268
|
for (const line of framed) {
|
|
153
|
-
|
|
269
|
+
writer.write(line + "\n");
|
|
154
270
|
}
|
|
155
271
|
}
|
|
156
272
|
function writeAgentText(text) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
renderer.
|
|
273
|
+
closeToolLine();
|
|
274
|
+
const needsGap = s.hadToolCalls;
|
|
275
|
+
s.hadToolCalls = false;
|
|
276
|
+
if (s.isThinking) {
|
|
277
|
+
s.isThinking = false;
|
|
278
|
+
if (s.showThinkingText && s.renderer) {
|
|
279
|
+
s.renderer.flush();
|
|
280
|
+
const w = Math.min(80, writer.columns);
|
|
281
|
+
s.renderer.writeLine(`${p.dim}${"─".repeat(w)}${p.reset}`);
|
|
282
|
+
drain();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
showCollapsedThinking();
|
|
286
|
+
stopCurrentSpinner();
|
|
287
|
+
if (!s.renderer)
|
|
288
|
+
startAgentResponse();
|
|
289
|
+
if (needsGap)
|
|
290
|
+
writer.write("\n");
|
|
291
|
+
s.renderer.push(text);
|
|
292
|
+
drain();
|
|
293
|
+
}
|
|
294
|
+
define("render:code-block", (language, code, width) => {
|
|
295
|
+
flushForRaw();
|
|
296
|
+
if (language) {
|
|
297
|
+
s.renderer.writeLine(`${p.dim}${language}${p.reset}`);
|
|
298
|
+
}
|
|
299
|
+
let highlighted;
|
|
300
|
+
if (!language) {
|
|
301
|
+
// No language specified — render as plain text to avoid false syntax detection
|
|
302
|
+
highlighted = code;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
try {
|
|
306
|
+
highlighted = highlight(code, { language });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
highlighted = `${p.success}${code}${p.reset}`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const contentWidth = Math.min(90, width - 2);
|
|
313
|
+
for (const line of highlighted.split("\n")) {
|
|
314
|
+
const indented = ` ${line}`;
|
|
315
|
+
const wrapped = wrapLine(indented, contentWidth);
|
|
316
|
+
for (const wl of wrapped) {
|
|
317
|
+
s.renderer.writeLine(wl);
|
|
164
318
|
}
|
|
165
319
|
}
|
|
320
|
+
drain();
|
|
321
|
+
});
|
|
322
|
+
function writeCodeBlock(language, code) {
|
|
323
|
+
ctx.call("render:code-block", language, code, writer.columns);
|
|
324
|
+
}
|
|
325
|
+
function flushForRaw() {
|
|
326
|
+
closeToolLine();
|
|
166
327
|
stopCurrentSpinner();
|
|
167
|
-
if (!renderer)
|
|
328
|
+
if (!s.renderer)
|
|
168
329
|
startAgentResponse();
|
|
169
|
-
renderer.
|
|
170
|
-
|
|
330
|
+
s.renderer.flush();
|
|
331
|
+
drain();
|
|
332
|
+
}
|
|
333
|
+
define("render:image", (data) => {
|
|
334
|
+
flushForRaw();
|
|
335
|
+
const escape = encodeImageForTerminal(data);
|
|
336
|
+
if (escape) {
|
|
337
|
+
writer.write(" " + escape + "\n");
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
function writeInlineImage(data) {
|
|
341
|
+
ctx.call("render:image", data);
|
|
171
342
|
}
|
|
172
343
|
function showToolCall(title, command, extra) {
|
|
344
|
+
closeToolLine();
|
|
173
345
|
stopCurrentSpinner();
|
|
174
|
-
if (!renderer)
|
|
346
|
+
if (!s.renderer)
|
|
175
347
|
startAgentResponse();
|
|
176
|
-
|
|
177
|
-
|
|
348
|
+
showCollapsedThinking();
|
|
349
|
+
s.renderer.flush();
|
|
350
|
+
drain();
|
|
178
351
|
const lines = renderToolCall({
|
|
179
352
|
title,
|
|
180
353
|
command: command || undefined,
|
|
181
354
|
kind: extra?.kind,
|
|
182
355
|
locations: extra?.locations,
|
|
183
356
|
rawInput: extra?.rawInput,
|
|
184
|
-
},
|
|
185
|
-
for (
|
|
186
|
-
renderer.writeLine(
|
|
357
|
+
}, writer.columns);
|
|
358
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
359
|
+
s.renderer.writeLine(lines[i]);
|
|
187
360
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
361
|
+
drain();
|
|
362
|
+
if (lines.length > 0) {
|
|
363
|
+
writer.write(` ${lines[lines.length - 1]}`);
|
|
364
|
+
s.toolLineOpen = true;
|
|
365
|
+
}
|
|
366
|
+
s.hadToolCalls = true;
|
|
367
|
+
s.commandOutputLineCount = 0;
|
|
368
|
+
s.commandOutputOverflow = 0;
|
|
191
369
|
}
|
|
192
370
|
function showToolComplete(exitCode) {
|
|
193
|
-
if (!renderer)
|
|
371
|
+
if (!s.renderer)
|
|
194
372
|
return;
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
373
|
+
const mark = exitCode === null
|
|
374
|
+
? `${p.muted}(timed out)${p.reset}`
|
|
375
|
+
: exitCode === 0
|
|
376
|
+
? `${p.success}✓${p.reset}`
|
|
377
|
+
: `${p.error}✗ exit ${exitCode}${p.reset}`;
|
|
378
|
+
if (s.toolLineOpen && s.commandOutputLineCount === 0) {
|
|
379
|
+
writer.write(` ${mark}\n`);
|
|
380
|
+
s.toolLineOpen = false;
|
|
199
381
|
}
|
|
382
|
+
else {
|
|
383
|
+
closeToolLine();
|
|
384
|
+
flushCommandOutput();
|
|
385
|
+
s.renderer.writeLine(` ${mark}`);
|
|
386
|
+
drain();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function hasThinkingMode() {
|
|
390
|
+
const mode = getAcpClient().getCurrentMode();
|
|
391
|
+
return !mode || mode.id !== "off";
|
|
200
392
|
}
|
|
201
|
-
function startThinkingSpinner(
|
|
393
|
+
function startThinkingSpinner() {
|
|
394
|
+
if (!s.spinnerStartTime)
|
|
395
|
+
s.spinnerStartTime = Date.now();
|
|
202
396
|
stopCurrentSpinner();
|
|
203
|
-
|
|
397
|
+
const thinking = hasThinkingMode();
|
|
398
|
+
s.spinnerLabel = thinking ? "Thinking" : "Working";
|
|
399
|
+
const hint = thinking
|
|
400
|
+
? (s.showThinkingText ? "(ctrl+t to collapse)" : "(ctrl+t to expand)")
|
|
401
|
+
: "";
|
|
402
|
+
s.spinnerOpts = { hint: hint || undefined, startTime: s.spinnerStartTime };
|
|
403
|
+
s.spinner = createSpinner({ startTime: s.spinnerStartTime });
|
|
404
|
+
s.spinnerInterval = setInterval(() => {
|
|
405
|
+
if (s.spinner) {
|
|
406
|
+
const line = renderSpinnerLine(s.spinner, s.spinnerLabel, s.spinnerOpts);
|
|
407
|
+
writer.write(`\r ${line}\x1b[K`);
|
|
408
|
+
}
|
|
409
|
+
}, 80);
|
|
204
410
|
}
|
|
205
411
|
function stopCurrentSpinner() {
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
|
|
412
|
+
if (s.spinnerInterval) {
|
|
413
|
+
clearInterval(s.spinnerInterval);
|
|
414
|
+
s.spinnerInterval = null;
|
|
415
|
+
}
|
|
416
|
+
if (s.spinner) {
|
|
417
|
+
writer.write("\r\x1b[2K");
|
|
418
|
+
s.spinner = null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function closeToolLine() {
|
|
422
|
+
if (s.toolLineOpen) {
|
|
423
|
+
writer.write("\n");
|
|
424
|
+
s.toolLineOpen = false;
|
|
209
425
|
}
|
|
210
426
|
}
|
|
211
427
|
function writeCommandOutput(chunk) {
|
|
212
|
-
if (!renderer)
|
|
428
|
+
if (!s.renderer)
|
|
213
429
|
return;
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
430
|
+
closeToolLine();
|
|
431
|
+
const maxLines = s.currentToolKind === "read"
|
|
432
|
+
? getSettings().readOutputMaxLines
|
|
433
|
+
: getSettings().maxCommandOutputLines;
|
|
434
|
+
s.commandOutputBuffer += chunk;
|
|
435
|
+
const lines = s.commandOutputBuffer.split("\n");
|
|
436
|
+
s.commandOutputBuffer = lines.pop();
|
|
217
437
|
for (const line of lines) {
|
|
218
|
-
if (commandOutputLineCount <
|
|
219
|
-
renderer.writeLine(`${p.dim} ${line}${p.reset}`);
|
|
220
|
-
commandOutputLineCount++;
|
|
438
|
+
if (s.commandOutputLineCount < maxLines) {
|
|
439
|
+
s.renderer.writeLine(`${p.dim} ${line}${p.reset}`);
|
|
440
|
+
s.commandOutputLineCount++;
|
|
221
441
|
}
|
|
222
442
|
else {
|
|
223
|
-
commandOutputOverflow++;
|
|
443
|
+
s.commandOutputOverflow++;
|
|
224
444
|
}
|
|
225
445
|
}
|
|
446
|
+
drain();
|
|
226
447
|
}
|
|
227
448
|
function flushCommandOutput() {
|
|
228
|
-
if (!renderer)
|
|
449
|
+
if (!s.renderer)
|
|
229
450
|
return;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
451
|
+
const maxLines = s.currentToolKind === "read"
|
|
452
|
+
? getSettings().readOutputMaxLines
|
|
453
|
+
: getSettings().maxCommandOutputLines;
|
|
454
|
+
if (s.commandOutputBuffer) {
|
|
455
|
+
if (s.commandOutputLineCount < maxLines) {
|
|
456
|
+
s.renderer.writeLine(`${p.dim} ${s.commandOutputBuffer}${p.reset}`);
|
|
457
|
+
s.commandOutputLineCount++;
|
|
234
458
|
}
|
|
235
459
|
else {
|
|
236
|
-
commandOutputOverflow++;
|
|
460
|
+
s.commandOutputOverflow++;
|
|
237
461
|
}
|
|
238
|
-
commandOutputBuffer = "";
|
|
462
|
+
s.commandOutputBuffer = "";
|
|
239
463
|
}
|
|
240
|
-
if (commandOutputOverflow > 0) {
|
|
241
|
-
renderer.writeLine(`${p.dim} … ${commandOutputOverflow} more lines${p.reset}`);
|
|
242
|
-
commandOutputOverflow = 0;
|
|
464
|
+
if (s.commandOutputOverflow > 0 && maxLines > 0) {
|
|
465
|
+
s.renderer.writeLine(`${p.dim} … ${s.commandOutputOverflow} more lines${p.reset}`);
|
|
243
466
|
}
|
|
467
|
+
s.commandOutputOverflow = 0;
|
|
468
|
+
drain();
|
|
244
469
|
}
|
|
245
|
-
const DIFF_MAX_LINES = 20;
|
|
246
470
|
function diffTitle(filePath, diff) {
|
|
247
471
|
const stats = diff.isNewFile
|
|
248
472
|
? `${p.success}+${diff.added}${p.reset}`
|
|
@@ -252,23 +476,22 @@ export default function activate({ bus }) {
|
|
|
252
476
|
function showFileDiff(filePath, diff) {
|
|
253
477
|
if (diff.isIdentical)
|
|
254
478
|
return;
|
|
255
|
-
const
|
|
256
|
-
const boxW = Math.min(84, termW);
|
|
479
|
+
const boxW = Math.min(84, writer.columns);
|
|
257
480
|
const contentW = boxW - 4;
|
|
258
481
|
const diffLines = renderDiff(diff, {
|
|
259
482
|
width: contentW,
|
|
260
483
|
filePath,
|
|
261
|
-
maxLines:
|
|
484
|
+
maxLines: getSettings().diffMaxLines,
|
|
262
485
|
trueColor: true,
|
|
263
486
|
mode: "unified",
|
|
264
487
|
});
|
|
265
488
|
const lastLine = diffLines[diffLines.length - 1] ?? "";
|
|
266
489
|
const isTruncated = lastLine.includes("… ");
|
|
267
490
|
if (isTruncated) {
|
|
268
|
-
lastTruncatedDiff = { filePath, diff, expanded: false };
|
|
491
|
+
s.lastTruncatedDiff = { filePath, diff, expanded: false };
|
|
269
492
|
}
|
|
270
493
|
else {
|
|
271
|
-
lastTruncatedDiff = null;
|
|
494
|
+
s.lastTruncatedDiff = null;
|
|
272
495
|
}
|
|
273
496
|
const body = diffLines.length > 1 ? ["", ...diffLines.slice(1), ""] : diffLines;
|
|
274
497
|
const footer = isTruncated
|
|
@@ -281,16 +504,17 @@ export default function activate({ bus }) {
|
|
|
281
504
|
title: diffTitle(filePath, diff),
|
|
282
505
|
footer,
|
|
283
506
|
});
|
|
284
|
-
if (!renderer)
|
|
507
|
+
if (!s.renderer)
|
|
285
508
|
startAgentResponse();
|
|
286
509
|
for (const line of framed) {
|
|
287
|
-
renderer.writeLine(line);
|
|
510
|
+
s.renderer.writeLine(line);
|
|
288
511
|
}
|
|
512
|
+
drain();
|
|
289
513
|
}
|
|
290
514
|
function expandLastDiff() {
|
|
291
|
-
if (!lastTruncatedDiff)
|
|
515
|
+
if (!s.lastTruncatedDiff)
|
|
292
516
|
return;
|
|
293
|
-
const entry = lastTruncatedDiff;
|
|
517
|
+
const entry = s.lastTruncatedDiff;
|
|
294
518
|
entry.expanded = !entry.expanded;
|
|
295
519
|
if (!entry.expanded) {
|
|
296
520
|
showFileDiffCached(entry);
|
|
@@ -298,8 +522,7 @@ export default function activate({ bus }) {
|
|
|
298
522
|
}
|
|
299
523
|
if (!entry.expandedLines) {
|
|
300
524
|
const { filePath, diff } = entry;
|
|
301
|
-
const
|
|
302
|
-
const boxW = Math.min(120, termW);
|
|
525
|
+
const boxW = Math.min(120, writer.columns);
|
|
303
526
|
const contentW = boxW - 4;
|
|
304
527
|
const diffLines = renderDiff(diff, {
|
|
305
528
|
width: contentW,
|
|
@@ -316,20 +539,19 @@ export default function activate({ bus }) {
|
|
|
316
539
|
footer: [` ${p.dim}ctrl+o to collapse${p.reset}`],
|
|
317
540
|
});
|
|
318
541
|
}
|
|
319
|
-
|
|
542
|
+
writer.write("\n");
|
|
320
543
|
for (const line of entry.expandedLines) {
|
|
321
|
-
|
|
544
|
+
writer.write(line + "\n");
|
|
322
545
|
}
|
|
323
546
|
}
|
|
324
547
|
function showFileDiffCached(entry) {
|
|
325
548
|
const { filePath, diff } = entry;
|
|
326
|
-
const
|
|
327
|
-
const boxW = Math.min(84, termW);
|
|
549
|
+
const boxW = Math.min(84, writer.columns);
|
|
328
550
|
const contentW = boxW - 4;
|
|
329
551
|
const diffLines = renderDiff(diff, {
|
|
330
552
|
width: contentW,
|
|
331
553
|
filePath,
|
|
332
|
-
maxLines:
|
|
554
|
+
maxLines: getSettings().diffMaxLines,
|
|
333
555
|
trueColor: true,
|
|
334
556
|
mode: "unified",
|
|
335
557
|
});
|
|
@@ -341,20 +563,41 @@ export default function activate({ bus }) {
|
|
|
341
563
|
title: diffTitle(filePath, diff),
|
|
342
564
|
footer: [` ${p.dim}ctrl+o to expand${p.reset}`],
|
|
343
565
|
});
|
|
344
|
-
|
|
566
|
+
writer.write("\n");
|
|
345
567
|
for (const line of framed) {
|
|
346
|
-
|
|
568
|
+
writer.write(line + "\n");
|
|
347
569
|
}
|
|
348
570
|
}
|
|
349
571
|
function toggleThinkingDisplay() {
|
|
350
|
-
showThinkingText = !showThinkingText;
|
|
351
|
-
|
|
352
|
-
|
|
572
|
+
s.showThinkingText = !s.showThinkingText;
|
|
573
|
+
if (s.spinner) {
|
|
574
|
+
stopCurrentSpinner();
|
|
575
|
+
startThinkingSpinner();
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (!s.isThinking)
|
|
579
|
+
return;
|
|
580
|
+
if (s.showThinkingText) {
|
|
581
|
+
stopCurrentSpinner();
|
|
582
|
+
if (!s.renderer)
|
|
583
|
+
startAgentResponse();
|
|
584
|
+
s.renderer.writeLine(`${p.dim}Thinking (ctrl+t to collapse)${p.reset}`);
|
|
585
|
+
drain();
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
if (s.renderer) {
|
|
589
|
+
s.renderer.flush();
|
|
590
|
+
const w = Math.min(80, writer.columns);
|
|
591
|
+
s.renderer.writeLine(`${p.dim}${"─".repeat(w)}${p.reset}`);
|
|
592
|
+
drain();
|
|
593
|
+
}
|
|
594
|
+
startThinkingSpinner();
|
|
595
|
+
}
|
|
353
596
|
}
|
|
354
597
|
function showError(message) {
|
|
355
|
-
|
|
598
|
+
writer.write(`\n${p.error}Error: ${message}${p.reset}\n`);
|
|
356
599
|
}
|
|
357
600
|
function showInfo(message) {
|
|
358
|
-
|
|
601
|
+
writer.write(`${p.muted}${message}${p.reset}\n`);
|
|
359
602
|
}
|
|
360
603
|
}
|