erosolar-cli 2.1.167 → 2.1.168
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/agents/erosolar-code.rules.json +2 -2
- package/agents/general.rules.json +3 -21
- package/dist/StringUtils.d.ts +8 -0
- package/dist/StringUtils.d.ts.map +1 -0
- package/dist/StringUtils.js +11 -0
- package/dist/StringUtils.js.map +1 -0
- package/dist/capabilities/statusCapability.js +2 -2
- package/dist/capabilities/statusCapability.js.map +1 -1
- package/dist/contracts/agent-schemas.json +0 -5
- package/dist/core/agent.d.ts +11 -72
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +182 -869
- package/dist/core/agent.js.map +1 -1
- package/dist/core/aiFlowSupervisor.d.ts +44 -0
- package/dist/core/aiFlowSupervisor.d.ts.map +1 -0
- package/dist/core/aiFlowSupervisor.js +299 -0
- package/dist/core/aiFlowSupervisor.js.map +1 -0
- package/dist/core/cliTestHarness.d.ts +200 -0
- package/dist/core/cliTestHarness.d.ts.map +1 -0
- package/dist/core/cliTestHarness.js +549 -0
- package/dist/core/cliTestHarness.js.map +1 -0
- package/dist/core/preferences.d.ts +0 -1
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +2 -9
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/schemaValidator.js +3 -3
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/testUtils.d.ts +121 -0
- package/dist/core/testUtils.d.ts.map +1 -0
- package/dist/core/testUtils.js +235 -0
- package/dist/core/testUtils.js.map +1 -0
- package/dist/core/toolPreconditions.d.ts +11 -0
- package/dist/core/toolPreconditions.d.ts.map +1 -1
- package/dist/core/toolPreconditions.js +164 -33
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +114 -9
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/toolValidation.d.ts +116 -0
- package/dist/core/toolValidation.d.ts.map +1 -0
- package/dist/core/toolValidation.js +282 -0
- package/dist/core/toolValidation.js.map +1 -0
- package/dist/core/updateChecker.d.ts +1 -61
- package/dist/core/updateChecker.d.ts.map +1 -1
- package/dist/core/updateChecker.js +3 -147
- package/dist/core/updateChecker.js.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +39 -0
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
- package/dist/plugins/tools/nodeDefaults.js +2 -0
- package/dist/plugins/tools/nodeDefaults.js.map +1 -1
- package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
- package/dist/providers/openaiResponsesProvider.js +74 -79
- package/dist/providers/openaiResponsesProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +0 -6
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/runtime/agentSession.d.ts.map +1 -1
- package/dist/runtime/agentSession.js +2 -3
- package/dist/runtime/agentSession.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +8 -16
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +159 -388
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +15 -4
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/subagents/taskRunner.js +1 -2
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +8 -101
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/diffUtils.d.ts +2 -8
- package/dist/tools/diffUtils.d.ts.map +1 -1
- package/dist/tools/diffUtils.js +13 -72
- package/dist/tools/diffUtils.js.map +1 -1
- package/dist/tools/grepTools.d.ts.map +1 -1
- package/dist/tools/grepTools.js +2 -10
- package/dist/tools/grepTools.js.map +1 -1
- package/dist/tools/searchTools.d.ts.map +1 -1
- package/dist/tools/searchTools.js +2 -4
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/ui/PromptController.d.ts +0 -2
- package/dist/ui/PromptController.d.ts.map +1 -1
- package/dist/ui/PromptController.js +0 -2
- package/dist/ui/PromptController.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +18 -71
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +139 -237
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/UnifiedUIController.d.ts +1 -0
- package/dist/ui/UnifiedUIController.d.ts.map +1 -1
- package/dist/ui/UnifiedUIController.js +1 -0
- package/dist/ui/UnifiedUIController.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +5 -122
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +125 -830
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/compactRenderer.d.ts +139 -0
- package/dist/ui/compactRenderer.d.ts.map +1 -0
- package/dist/ui/compactRenderer.js +398 -0
- package/dist/ui/compactRenderer.js.map +1 -0
- package/dist/ui/display.d.ts +48 -13
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +105 -22
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/streamingFormatter.d.ts +30 -0
- package/dist/ui/streamingFormatter.d.ts.map +1 -0
- package/dist/ui/streamingFormatter.js +91 -0
- package/dist/ui/streamingFormatter.js.map +1 -0
- package/dist/ui/unified/index.d.ts +1 -1
- package/dist/ui/unified/index.d.ts.map +1 -1
- package/dist/ui/unified/index.js +2 -0
- package/dist/ui/unified/index.js.map +1 -1
- package/dist/utils/errorUtils.d.ts +16 -0
- package/dist/utils/errorUtils.d.ts.map +1 -0
- package/dist/utils/errorUtils.js +66 -0
- package/dist/utils/errorUtils.js.map +1 -0
- package/package.json +2 -1
- package/dist/core/reliabilityPrompt.d.ts +0 -9
- package/dist/core/reliabilityPrompt.d.ts.map +0 -1
- package/dist/core/reliabilityPrompt.js +0 -31
- package/dist/core/reliabilityPrompt.js.map +0 -1
- package/dist/ui/animatedStatus.d.ts +0 -129
- package/dist/ui/animatedStatus.d.ts.map +0 -1
- package/dist/ui/animatedStatus.js +0 -384
- package/dist/ui/animatedStatus.js.map +0 -1
|
@@ -13,13 +13,15 @@ import { InterruptPriority } from './interrupts/InterruptManager.js';
|
|
|
13
13
|
import { getTerminalColumns } from './layout.js';
|
|
14
14
|
import { theme, icons } from './theme.js';
|
|
15
15
|
import { UIUpdateCoordinator } from './orchestration/UIUpdateCoordinator.js';
|
|
16
|
+
import { formatRichContent } from './richText.js';
|
|
16
17
|
export class ShellUIAdapter {
|
|
17
18
|
uiController;
|
|
18
19
|
display;
|
|
19
20
|
config;
|
|
21
|
+
isProcessing = false;
|
|
22
|
+
contextUsage = 0;
|
|
20
23
|
fileChangeCallback;
|
|
21
24
|
_toolStatusCallback;
|
|
22
|
-
_activityCallback;
|
|
23
25
|
profileSwitcherTimeout;
|
|
24
26
|
updateCoordinator;
|
|
25
27
|
// Track tool operations for compact rendering
|
|
@@ -36,16 +38,6 @@ export class ShellUIAdapter {
|
|
|
36
38
|
activeToolStatus = null;
|
|
37
39
|
activeToolHeartbeatId = null;
|
|
38
40
|
statusSpinnerIndex = 0;
|
|
39
|
-
// Store last tool result for expansion with ctrl+o
|
|
40
|
-
lastToolResult = null;
|
|
41
|
-
// Scrolling output buffer - shows only the last N lines of tool output in-place
|
|
42
|
-
maxScrollingLines = 3;
|
|
43
|
-
scrollingOutputBuffer = new Map();
|
|
44
|
-
scrollingPanelOwner = null;
|
|
45
|
-
// Scrolling tool call display - shows only the last N tool calls, scrolling older ones up
|
|
46
|
-
maxVisibleToolCalls = 3;
|
|
47
|
-
recentToolCalls = [];
|
|
48
|
-
toolCallDisplayedCount = 0;
|
|
49
41
|
constructor(writeStream, display, config = {}, updateCoordinator) {
|
|
50
42
|
this.display = display;
|
|
51
43
|
this.config = {
|
|
@@ -146,12 +138,6 @@ export class ShellUIAdapter {
|
|
|
146
138
|
else {
|
|
147
139
|
this.logToolProgress(call, progress);
|
|
148
140
|
}
|
|
149
|
-
// Update activity line with streaming output from bash commands
|
|
150
|
-
if (this._activityCallback && progress.message) {
|
|
151
|
-
// Show the streaming line as the activity (e.g., npm install progress)
|
|
152
|
-
const activityText = progress.message.slice(0, 60);
|
|
153
|
-
this._activityCallback(activityText);
|
|
154
|
-
}
|
|
155
141
|
// Update status bar with progress - use in-place update for supported terminals
|
|
156
142
|
if (this._toolStatusCallback) {
|
|
157
143
|
const description = this.getToolActionDescription(call, progress);
|
|
@@ -181,10 +167,12 @@ export class ShellUIAdapter {
|
|
|
181
167
|
const now = Date.now();
|
|
182
168
|
// Complete the operation tracking
|
|
183
169
|
const op = this.pendingOperations.get(call.id);
|
|
170
|
+
let durationMs;
|
|
184
171
|
if (op) {
|
|
185
172
|
op.status = 'success';
|
|
186
173
|
op.completedAt = now;
|
|
187
174
|
op.summary = this.extractResultSummary(call, output);
|
|
175
|
+
durationMs = op.startedAt ? Math.max(0, now - op.startedAt) : undefined;
|
|
188
176
|
this.pendingOperations.delete(call.id);
|
|
189
177
|
this.completedOperations.push(op);
|
|
190
178
|
// Keep only recent completed operations
|
|
@@ -203,22 +191,19 @@ export class ShellUIAdapter {
|
|
|
203
191
|
this.fileChangeCallback(fileChange.path, fileChange.type, fileChange.additions, fileChange.removals);
|
|
204
192
|
}
|
|
205
193
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
else {
|
|
220
|
-
// Display tool result with ⎿ prefix
|
|
221
|
-
this.displayToolResultSummary(call, output, true);
|
|
194
|
+
const renderedRich = this.maybeRenderRichToolResult(call, output, true, durationMs);
|
|
195
|
+
// Use compact display for quick single-result tools and structured edit output
|
|
196
|
+
if (!renderedRich) {
|
|
197
|
+
if (call.name === 'Edit' || call.name === 'edit_file') {
|
|
198
|
+
this.displayEditResult(call, output, true);
|
|
199
|
+
}
|
|
200
|
+
else if (this.compactDisplayMode && this.isCompactTool(call.name)) {
|
|
201
|
+
this.displayCompactResult(call, output, true);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Erosolar-CLI style: Display tool result with ⎿ prefix
|
|
205
|
+
this.displayToolResultSummary(call, output, true);
|
|
206
|
+
}
|
|
222
207
|
}
|
|
223
208
|
// Surface any captured preflight warnings in a structured block
|
|
224
209
|
this.flushToolWarnings(call.id, call.name);
|
|
@@ -232,10 +217,12 @@ export class ShellUIAdapter {
|
|
|
232
217
|
onToolError: (call, message) => {
|
|
233
218
|
// Complete the operation tracking with error
|
|
234
219
|
const op = this.pendingOperations.get(call.id);
|
|
220
|
+
let durationMs;
|
|
235
221
|
if (op) {
|
|
236
222
|
op.status = 'error';
|
|
237
223
|
op.completedAt = Date.now();
|
|
238
224
|
op.summary = message;
|
|
225
|
+
durationMs = op.startedAt ? Math.max(0, (op.completedAt ?? Date.now()) - op.startedAt) : undefined;
|
|
239
226
|
this.pendingOperations.delete(call.id);
|
|
240
227
|
this.completedOperations.push(op);
|
|
241
228
|
}
|
|
@@ -243,16 +230,19 @@ export class ShellUIAdapter {
|
|
|
243
230
|
this.logExploreCompletion(call.id, false);
|
|
244
231
|
}
|
|
245
232
|
this.clearToolState(call.id);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
233
|
+
const renderedRich = this.maybeRenderRichToolResult(call, message, false, durationMs);
|
|
234
|
+
// Use compact display for errors too
|
|
235
|
+
if (!renderedRich) {
|
|
236
|
+
if (call.name === 'Edit' || call.name === 'edit_file') {
|
|
237
|
+
this.displayEditResult(call, message, false);
|
|
238
|
+
}
|
|
239
|
+
else if (this.compactDisplayMode && this.isCompactTool(call.name)) {
|
|
240
|
+
this.displayCompactResult(call, message, false);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// Erosolar-CLI style: Display error with ⎿ prefix (error color)
|
|
244
|
+
this.displayToolResultSummary(call, message, false);
|
|
245
|
+
}
|
|
256
246
|
}
|
|
257
247
|
this.flushToolWarnings(call.id, call.name);
|
|
258
248
|
// Clear status bar after showing error
|
|
@@ -320,27 +310,17 @@ export class ShellUIAdapter {
|
|
|
320
310
|
* Shows the top two tools and how many more were used.
|
|
321
311
|
*/
|
|
322
312
|
showToolUsageSummary() {
|
|
323
|
-
// Compute summary for metadata but don't display it
|
|
324
|
-
// The tool calls are already visible in the conversation flow
|
|
325
313
|
this.lastToolUsageSummary = this.formatToolUsageSummary();
|
|
326
314
|
this.lastToolUsageSummaryPlain = this.formatToolUsageSummary({ plain: true });
|
|
327
|
-
|
|
315
|
+
const summary = this.lastToolUsageSummary;
|
|
316
|
+
if (!summary) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.display.stream(`\n${summary}\n`);
|
|
328
320
|
}
|
|
329
321
|
getToolUsageSummary(options = {}) {
|
|
330
322
|
return options.plain ? this.lastToolUsageSummaryPlain : this.lastToolUsageSummary;
|
|
331
323
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Get the last tool result for expansion with ctrl+o
|
|
334
|
-
*/
|
|
335
|
-
getLastToolResult() {
|
|
336
|
-
return this.lastToolResult;
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Clear the stored tool result (e.g., after expansion)
|
|
340
|
-
*/
|
|
341
|
-
clearLastToolResult() {
|
|
342
|
-
this.lastToolResult = null;
|
|
343
|
-
}
|
|
344
324
|
formatToolUsageSummary(options = {}) {
|
|
345
325
|
const plain = options.plain === true;
|
|
346
326
|
if (this.toolUsageCounts.size === 0) {
|
|
@@ -459,6 +439,7 @@ export class ShellUIAdapter {
|
|
|
459
439
|
case 'Write':
|
|
460
440
|
case 'write_file': {
|
|
461
441
|
const path = this.extractPath(args, ['file_path', 'path']);
|
|
442
|
+
const lines = this.countLines(output);
|
|
462
443
|
return path ? `${this.truncatePath(path, 40)} written` : 'file written';
|
|
463
444
|
}
|
|
464
445
|
case 'Edit':
|
|
@@ -523,8 +504,8 @@ export class ShellUIAdapter {
|
|
|
523
504
|
return;
|
|
524
505
|
}
|
|
525
506
|
const prefix = success ? theme.success(icons.subaction) : theme.error(icons.subaction);
|
|
526
|
-
|
|
527
|
-
this.display.stream(` ${prefix} ${summary}\n`);
|
|
507
|
+
const toolLabel = theme.tool(call.name);
|
|
508
|
+
this.display.stream(` ${prefix} ${toolLabel} ${summary}\n\n`);
|
|
528
509
|
}
|
|
529
510
|
/**
|
|
530
511
|
* Display edit results with full colored diff output.
|
|
@@ -542,10 +523,12 @@ export class ShellUIAdapter {
|
|
|
542
523
|
if (success) {
|
|
543
524
|
const diffSection = this.extractEditDiffSection(output);
|
|
544
525
|
if (diffSection) {
|
|
545
|
-
|
|
546
|
-
|
|
526
|
+
this.display.stream(`${diffSection}\n\n`);
|
|
527
|
+
return;
|
|
547
528
|
}
|
|
548
529
|
}
|
|
530
|
+
// Add spacing when no diff section is available
|
|
531
|
+
this.display.stream('\n');
|
|
549
532
|
}
|
|
550
533
|
logToolProgress(call, progress) {
|
|
551
534
|
const description = this.getToolActionDescription(call, progress);
|
|
@@ -568,97 +551,12 @@ export class ShellUIAdapter {
|
|
|
568
551
|
}
|
|
569
552
|
const separator = theme.ui.muted(' — ');
|
|
570
553
|
const line = parts.filter(Boolean).join(separator);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
this.appendScrollingOutput(call.id, line);
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
// Non-TTY: original streaming behavior (for log files, pipes, etc.)
|
|
580
|
-
const last = this.toolProgressSnapshots.get(call.id);
|
|
581
|
-
if (line !== last) {
|
|
582
|
-
this.display.stream(`${line}\n`);
|
|
583
|
-
this.toolProgressSnapshots.set(call.id, line);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
/**
|
|
588
|
-
* Append a line to scrolling output buffer and update display in-place
|
|
589
|
-
* Shows only the last maxScrollingLines, replacing previous output
|
|
590
|
-
*/
|
|
591
|
-
appendScrollingOutput(callId, line) {
|
|
592
|
-
let buffer = this.scrollingOutputBuffer.get(callId);
|
|
593
|
-
if (!buffer) {
|
|
594
|
-
buffer = { lines: [], displayedLineCount: 0 };
|
|
595
|
-
this.scrollingOutputBuffer.set(callId, buffer);
|
|
554
|
+
const last = this.toolProgressSnapshots.get(call.id);
|
|
555
|
+
if (line && line !== last) {
|
|
556
|
+
// Single trailing newline - no leading newline to avoid gaps
|
|
557
|
+
this.display.stream(`${line}\n`);
|
|
558
|
+
this.toolProgressSnapshots.set(call.id, line);
|
|
596
559
|
}
|
|
597
|
-
// Add line to buffer
|
|
598
|
-
buffer.lines.push(line);
|
|
599
|
-
// Keep only the last maxScrollingLines
|
|
600
|
-
while (buffer.lines.length > this.maxScrollingLines) {
|
|
601
|
-
buffer.lines.shift();
|
|
602
|
-
}
|
|
603
|
-
// Prefer inline panel (scroll box) when unified renderer is active
|
|
604
|
-
if (this.shouldUseInlineScrollBox()) {
|
|
605
|
-
this.scrollingPanelOwner = callId;
|
|
606
|
-
const panelLines = this.buildScrollingPanelLines(buffer.lines);
|
|
607
|
-
this.display.showInlinePanel(panelLines);
|
|
608
|
-
buffer.displayedLineCount = 0;
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
// Move cursor up to overwrite previous lines (if any were displayed)
|
|
612
|
-
if (buffer.displayedLineCount > 0) {
|
|
613
|
-
process.stdout.write(`\x1b[${buffer.displayedLineCount}A`);
|
|
614
|
-
}
|
|
615
|
-
// Clear and rewrite the scrolling region
|
|
616
|
-
const linesToShow = buffer.lines;
|
|
617
|
-
for (let i = 0; i < linesToShow.length; i++) {
|
|
618
|
-
process.stdout.write(`\r\x1b[K${linesToShow[i]}\n`);
|
|
619
|
-
}
|
|
620
|
-
// Track how many lines we displayed for next update
|
|
621
|
-
buffer.displayedLineCount = linesToShow.length;
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Build the pinned scroll-box contents for live tool output.
|
|
625
|
-
*/
|
|
626
|
-
buildScrollingPanelLines(lines) {
|
|
627
|
-
if (!lines.length) {
|
|
628
|
-
return [];
|
|
629
|
-
}
|
|
630
|
-
const header = theme.ui.muted(`${icons.action} live output (latest ${Math.min(lines.length, this.maxScrollingLines)})`);
|
|
631
|
-
const indented = lines.map(text => ` ${text}`);
|
|
632
|
-
return [header, ...indented];
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Use inline scroll box only when an interactive renderer is active.
|
|
636
|
-
*/
|
|
637
|
-
shouldUseInlineScrollBox() {
|
|
638
|
-
return this.display.hasRenderer() && process.stdout.isTTY && !process.env['CI'];
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Finalize scrolling output - called when tool completes
|
|
642
|
-
* Clears the scrolling progress lines so only the final result is shown
|
|
643
|
-
*/
|
|
644
|
-
finalizeScrollingOutput(callId) {
|
|
645
|
-
if (this.scrollingPanelOwner === callId && this.shouldUseInlineScrollBox()) {
|
|
646
|
-
this.display.clearInlinePanel();
|
|
647
|
-
this.scrollingPanelOwner = null;
|
|
648
|
-
this.scrollingOutputBuffer.delete(callId);
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
const buffer = this.scrollingOutputBuffer.get(callId);
|
|
652
|
-
if (buffer && buffer.displayedLineCount > 0 && process.stdout.isTTY) {
|
|
653
|
-
// Move cursor up and clear all the progress lines
|
|
654
|
-
process.stdout.write(`\x1b[${buffer.displayedLineCount}A`);
|
|
655
|
-
for (let i = 0; i < buffer.displayedLineCount; i++) {
|
|
656
|
-
process.stdout.write(`\r\x1b[K\n`);
|
|
657
|
-
}
|
|
658
|
-
// Move cursor back up to where progress started
|
|
659
|
-
process.stdout.write(`\x1b[${buffer.displayedLineCount}A`);
|
|
660
|
-
}
|
|
661
|
-
this.scrollingOutputBuffer.delete(callId);
|
|
662
560
|
}
|
|
663
561
|
logExploreProgress(call, progress) {
|
|
664
562
|
const previous = this.exploreProgressState.get(call.id) ?? { lastCurrent: 0 };
|
|
@@ -672,14 +570,8 @@ export class ShellUIAdapter {
|
|
|
672
570
|
}
|
|
673
571
|
const line = parts.join(theme.ui.muted(' • '));
|
|
674
572
|
if (line && line !== previous.lastLine) {
|
|
675
|
-
//
|
|
676
|
-
|
|
677
|
-
// Move cursor up one line and clear it, then write new line
|
|
678
|
-
process.stdout.write(`\x1b[1A\r\x1b[K${line}\n`);
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
this.display.stream(`${line}\n`);
|
|
682
|
-
}
|
|
573
|
+
// Single trailing newline - no leading newline to avoid gaps
|
|
574
|
+
this.display.stream(`${line}\n`);
|
|
683
575
|
}
|
|
684
576
|
this.exploreProgressState.set(call.id, {
|
|
685
577
|
total,
|
|
@@ -693,10 +585,6 @@ export class ShellUIAdapter {
|
|
|
693
585
|
const summary = success
|
|
694
586
|
? `${theme.success('✓')} Indexed ${total} file${total === 1 ? '' : 's'}`
|
|
695
587
|
: `${theme.error('✗')} Explore interrupted`;
|
|
696
|
-
// Clear the progress line before showing summary (if TTY and had progress)
|
|
697
|
-
if (process.stdout.isTTY && state?.lastLine) {
|
|
698
|
-
process.stdout.write(`\x1b[1A\r\x1b[K`);
|
|
699
|
-
}
|
|
700
588
|
this.display.stream(`${summary}\n`);
|
|
701
589
|
}
|
|
702
590
|
/**
|
|
@@ -706,13 +594,12 @@ export class ShellUIAdapter {
|
|
|
706
594
|
clearToolState(callId) {
|
|
707
595
|
this.toolProgressSnapshots.delete(callId);
|
|
708
596
|
this.exploreProgressState.delete(callId);
|
|
709
|
-
this.finalizeScrollingOutput(callId);
|
|
710
597
|
}
|
|
711
598
|
/**
|
|
712
599
|
* Display tool call start with Erosolar-CLI style prefix
|
|
713
600
|
* - ✢ for Task/agent tools (active task indicator)
|
|
714
601
|
* - ⏺ for other tools
|
|
715
|
-
*
|
|
602
|
+
* Routes through display module to work with scroll regions
|
|
716
603
|
*/
|
|
717
604
|
displayToolCallStart(call, isCacheHit = false) {
|
|
718
605
|
const args = call.arguments || {};
|
|
@@ -735,41 +622,8 @@ export class ShellUIAdapter {
|
|
|
735
622
|
}
|
|
736
623
|
// Erosolar-CLI style: bullet + optional task marker + ToolName(args)
|
|
737
624
|
const line = `${bullet}${taskMarker ? taskMarker : ''} ${displayName}${argsStr}${cacheHint}`;
|
|
738
|
-
//
|
|
739
|
-
this.
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Append a tool call to the scrolling display, keeping only the last N visible.
|
|
743
|
-
* Older tool calls scroll up and disappear, showing only recent activity.
|
|
744
|
-
*/
|
|
745
|
-
appendToolCallDisplay(callId, line) {
|
|
746
|
-
// Add to recent tool calls
|
|
747
|
-
this.recentToolCalls.push({ line, id: callId });
|
|
748
|
-
// Keep only the last maxVisibleToolCalls
|
|
749
|
-
while (this.recentToolCalls.length > this.maxVisibleToolCalls) {
|
|
750
|
-
this.recentToolCalls.shift();
|
|
751
|
-
}
|
|
752
|
-
// On TTY, use in-place updating to show only recent tool calls
|
|
753
|
-
if (process.stdout.isTTY && this.toolCallDisplayedCount > 0) {
|
|
754
|
-
// Move cursor up to overwrite previous tool call lines
|
|
755
|
-
const moveUp = `\x1b[${this.toolCallDisplayedCount}A`;
|
|
756
|
-
// Clear from cursor to end of screen
|
|
757
|
-
const clearDown = '\x1b[J';
|
|
758
|
-
process.stdout.write(moveUp + clearDown);
|
|
759
|
-
}
|
|
760
|
-
// Display all recent tool calls
|
|
761
|
-
for (const toolCall of this.recentToolCalls) {
|
|
762
|
-
this.display.stream(`${toolCall.line}\n`);
|
|
763
|
-
}
|
|
764
|
-
// Track how many lines we displayed
|
|
765
|
-
this.toolCallDisplayedCount = this.recentToolCalls.length;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Clear tool call display state when processing completes
|
|
769
|
-
*/
|
|
770
|
-
clearToolCallDisplay() {
|
|
771
|
-
this.recentToolCalls = [];
|
|
772
|
-
this.toolCallDisplayedCount = 0;
|
|
625
|
+
// Stream with spacing to keep it as a distinct event
|
|
626
|
+
this.display.stream(`\n${line}\n`);
|
|
773
627
|
}
|
|
774
628
|
/**
|
|
775
629
|
* Format tool arguments inline for display (Erosolar-CLI style)
|
|
@@ -918,8 +772,8 @@ export class ShellUIAdapter {
|
|
|
918
772
|
* Start processing a request
|
|
919
773
|
*/
|
|
920
774
|
startProcessing(message = 'Working on your request') {
|
|
775
|
+
this.isProcessing = true;
|
|
921
776
|
this.resetToolUsageSummary();
|
|
922
|
-
this.clearToolCallDisplay(); // Clear previous tool call display
|
|
923
777
|
this.uiController.startProcessing();
|
|
924
778
|
this.uiController.setBaseStatus(message, 'info');
|
|
925
779
|
}
|
|
@@ -927,17 +781,20 @@ export class ShellUIAdapter {
|
|
|
927
781
|
* End processing
|
|
928
782
|
*/
|
|
929
783
|
endProcessing(message = 'Ready for prompts') {
|
|
784
|
+
this.isProcessing = false;
|
|
930
785
|
this.uiController.endProcessing();
|
|
931
786
|
this.showToolUsageSummary();
|
|
932
787
|
this.uiController.setBaseStatus(message, 'success');
|
|
933
|
-
// Clear tool call scrolling state for next request
|
|
934
|
-
this.clearToolCallDisplay();
|
|
935
788
|
}
|
|
936
789
|
/**
|
|
937
|
-
* Update context usage
|
|
790
|
+
* Update context usage
|
|
791
|
+
* NOTE: This only tracks the value internally. The actual context display
|
|
792
|
+
* is handled by display.showStatusLine() which shows "Session Xm • Context Y% used • Ready"
|
|
793
|
+
* We don't push a status override here to avoid duplicate context lines.
|
|
938
794
|
*/
|
|
939
|
-
updateContextUsage(
|
|
940
|
-
|
|
795
|
+
updateContextUsage(percentage) {
|
|
796
|
+
this.contextUsage = percentage;
|
|
797
|
+
// Status updates are internal; avoid forcing overlay rerender
|
|
941
798
|
}
|
|
942
799
|
/**
|
|
943
800
|
* Show a user interrupt
|
|
@@ -957,7 +814,7 @@ export class ShellUIAdapter {
|
|
|
957
814
|
/**
|
|
958
815
|
* Show profile switcher (Shift+Tab)
|
|
959
816
|
*/
|
|
960
|
-
showProfileSwitcher(profiles,
|
|
817
|
+
showProfileSwitcher(profiles, currentProfile) {
|
|
961
818
|
this.updateCoordinator.enqueue({
|
|
962
819
|
lane: 'overlay',
|
|
963
820
|
description: 'profile-switcher',
|
|
@@ -1169,12 +1026,6 @@ export class ShellUIAdapter {
|
|
|
1169
1026
|
callback(this.buildRunningStatusLine(this.activeToolStatus.description, this.activeToolStatus.startedAt));
|
|
1170
1027
|
}
|
|
1171
1028
|
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Set activity callback - called during tool progress to update activity line with streaming output
|
|
1174
|
-
*/
|
|
1175
|
-
setActivityCallback(callback) {
|
|
1176
|
-
this._activityCallback = callback;
|
|
1177
|
-
}
|
|
1178
1029
|
/**
|
|
1179
1030
|
* Keep a live status line ticking while an SSE tool or long-running tool is active.
|
|
1180
1031
|
* Uses a heartbeat to animate the spinner and update elapsed time without spamming writes.
|
|
@@ -1295,10 +1146,79 @@ export class ShellUIAdapter {
|
|
|
1295
1146
|
const prefix = success
|
|
1296
1147
|
? theme.success(icons.subaction)
|
|
1297
1148
|
: theme.error(icons.subaction);
|
|
1298
|
-
//
|
|
1299
|
-
this.display.stream(` ${prefix} ${summary}\n`);
|
|
1149
|
+
// Add newline after result for visual separation
|
|
1150
|
+
this.display.stream(` ${prefix} ${summary}\n\n`);
|
|
1300
1151
|
}
|
|
1301
1152
|
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Render a rich block for non-streamed SSE-style tool results so they feel like a "completed event".
|
|
1155
|
+
* Returns true if a block was rendered and downstream compact formatting should be skipped.
|
|
1156
|
+
*/
|
|
1157
|
+
maybeRenderRichToolResult(call, output, success, durationMs) {
|
|
1158
|
+
if (!this.shouldRenderRichToolBlock(call, output)) {
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
const block = this.renderRichToolBlock(call, output, success, durationMs);
|
|
1162
|
+
if (!block.trim()) {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
this.display.stream(`\n${block}\n\n`);
|
|
1166
|
+
return true;
|
|
1167
|
+
}
|
|
1168
|
+
shouldRenderRichToolBlock(call, output) {
|
|
1169
|
+
const trimmed = output?.trim() ?? '';
|
|
1170
|
+
if (!trimmed)
|
|
1171
|
+
return false;
|
|
1172
|
+
// Skip tools that already have bespoke rendering
|
|
1173
|
+
if (call.name === 'Edit' || call.name === 'edit_file' || call.name === 'list_files') {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
// Read operations should stay compact (single-line summary), not boxed
|
|
1177
|
+
if (call.name === 'Read' || call.name === 'read_file') {
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
const isMcpTool = call.name.startsWith('mcp__');
|
|
1181
|
+
const isMultiline = trimmed.includes('\n');
|
|
1182
|
+
const isLong = trimmed.length > 160;
|
|
1183
|
+
return isMcpTool || isMultiline || isLong;
|
|
1184
|
+
}
|
|
1185
|
+
renderRichToolBlock(call, output, success, durationMs) {
|
|
1186
|
+
const columns = Math.max(60, Math.min(getTerminalColumns(), 120));
|
|
1187
|
+
const innerWidth = Math.max(40, columns - 4);
|
|
1188
|
+
const statusIcon = success ? theme.success(icons.success) : theme.error(icons.error);
|
|
1189
|
+
const labels = [
|
|
1190
|
+
`${statusIcon} ${theme.tool(call.name)}`,
|
|
1191
|
+
call.name.startsWith('mcp__') ? theme.ui.muted('SSE result') : theme.ui.muted('Tool result'),
|
|
1192
|
+
];
|
|
1193
|
+
const elapsed = this.formatElapsedMs(durationMs);
|
|
1194
|
+
if (elapsed) {
|
|
1195
|
+
labels.push(`${theme.metrics.elapsedLabel('elapsed')} ${theme.metrics.elapsedValue(elapsed)}`);
|
|
1196
|
+
}
|
|
1197
|
+
const title = labels.filter(Boolean).join(theme.ui.muted(' • '));
|
|
1198
|
+
const titlePadding = Math.max(0, innerWidth - this.visibleLength(title) - 2);
|
|
1199
|
+
const top = theme.ui.muted(`╭─ ${title} ${'─'.repeat(titlePadding)}╮`);
|
|
1200
|
+
const bottom = theme.ui.muted(`╰${'─'.repeat(innerWidth + 2)}╯`);
|
|
1201
|
+
const bodyLines = formatRichContent(output.trim(), innerWidth);
|
|
1202
|
+
const boxed = bodyLines.length ? bodyLines : [''];
|
|
1203
|
+
const content = boxed.map(line => {
|
|
1204
|
+
const truncated = this.visibleLength(line) > innerWidth
|
|
1205
|
+
? `${line.slice(0, Math.max(0, innerWidth - 1))}…`
|
|
1206
|
+
: line;
|
|
1207
|
+
const pad = Math.max(0, innerWidth - this.visibleLength(truncated));
|
|
1208
|
+
return `${theme.ui.muted('│')} ${truncated}${' '.repeat(pad)} ${theme.ui.muted('│')}`;
|
|
1209
|
+
});
|
|
1210
|
+
return [top, ...content, bottom].join('\n');
|
|
1211
|
+
}
|
|
1212
|
+
visibleLength(value) {
|
|
1213
|
+
if (!value)
|
|
1214
|
+
return 0;
|
|
1215
|
+
return this.stripAnsi(value).length;
|
|
1216
|
+
}
|
|
1217
|
+
stripAnsi(value) {
|
|
1218
|
+
if (!value)
|
|
1219
|
+
return '';
|
|
1220
|
+
return value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '');
|
|
1221
|
+
}
|
|
1302
1222
|
/**
|
|
1303
1223
|
* Format tool result summary in Erosolar-CLI style
|
|
1304
1224
|
* Returns compact summary like "Found 4 files" or "Read 50 lines"
|
|
@@ -1411,32 +1331,14 @@ export class ShellUIAdapter {
|
|
|
1411
1331
|
}
|
|
1412
1332
|
return 'Todos updated';
|
|
1413
1333
|
}
|
|
1414
|
-
case 'grep_search':
|
|
1415
|
-
case 'search_files':
|
|
1416
|
-
case 'Grep': {
|
|
1417
|
-
// Count matches in output - look for file paths or line numbers
|
|
1418
|
-
const lines = output.trim().split('\n').filter(l => l.trim());
|
|
1419
|
-
if (lines.length === 0 || output.includes('No matches found') || output.includes('no matches')) {
|
|
1420
|
-
return theme.ui.muted('No matches');
|
|
1421
|
-
}
|
|
1422
|
-
// Count unique files or match lines
|
|
1423
|
-
const fileMatches = output.match(/^[^\n:]+:\d+:/gm);
|
|
1424
|
-
if (fileMatches) {
|
|
1425
|
-
const uniqueFiles = new Set(fileMatches.map(m => m.split(':')[0])).size;
|
|
1426
|
-
return `${theme.info(String(fileMatches.length))} matches in ${uniqueFiles} file${uniqueFiles === 1 ? '' : 's'}`;
|
|
1427
|
-
}
|
|
1428
|
-
return `${theme.info(String(lines.length))} matches`;
|
|
1429
|
-
}
|
|
1430
1334
|
default: {
|
|
1431
|
-
// Generic result
|
|
1335
|
+
// Generic result
|
|
1432
1336
|
const lines = output.trim().split('\n').filter(l => l).length;
|
|
1433
1337
|
if (lines === 0) {
|
|
1434
|
-
return
|
|
1338
|
+
return 'Completed';
|
|
1435
1339
|
}
|
|
1436
1340
|
if (lines <= 3) {
|
|
1437
|
-
|
|
1438
|
-
const shortOutput = output.trim().substring(0, 60);
|
|
1439
|
-
return shortOutput.length < output.trim().length ? `${shortOutput}…` : shortOutput;
|
|
1341
|
+
return 'Completed';
|
|
1440
1342
|
}
|
|
1441
1343
|
return `${theme.info(String(lines))} lines`;
|
|
1442
1344
|
}
|