erosolar-cli 1.7.14 → 1.7.16
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/dist/core/responseVerifier.d.ts +79 -0
- package/dist/core/responseVerifier.d.ts.map +1 -0
- package/dist/core/responseVerifier.js +443 -0
- package/dist/core/responseVerifier.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +10 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +80 -0
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +3 -0
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +4 -10
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +4 -0
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +10 -11
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/package.json +1 -1
- package/dist/bin/core/agent.js +0 -362
- package/dist/bin/core/agentProfileManifest.js +0 -187
- package/dist/bin/core/agentProfiles.js +0 -34
- package/dist/bin/core/agentRulebook.js +0 -135
- package/dist/bin/core/agentSchemaLoader.js +0 -233
- package/dist/bin/core/contextManager.js +0 -412
- package/dist/bin/core/contextWindow.js +0 -122
- package/dist/bin/core/customCommands.js +0 -80
- package/dist/bin/core/errors/apiKeyErrors.js +0 -114
- package/dist/bin/core/errors/errorTypes.js +0 -340
- package/dist/bin/core/errors/safetyValidator.js +0 -304
- package/dist/bin/core/errors.js +0 -32
- package/dist/bin/core/modelDiscovery.js +0 -755
- package/dist/bin/core/preferences.js +0 -224
- package/dist/bin/core/schemaValidator.js +0 -92
- package/dist/bin/core/secretStore.js +0 -199
- package/dist/bin/core/sessionStore.js +0 -187
- package/dist/bin/core/toolRuntime.js +0 -290
- package/dist/bin/core/types.js +0 -1
- package/dist/bin/shell/bracketedPasteManager.js +0 -350
- package/dist/bin/shell/fileChangeTracker.js +0 -65
- package/dist/bin/shell/interactiveShell.js +0 -2908
- package/dist/bin/shell/liveStatus.js +0 -78
- package/dist/bin/shell/shellApp.js +0 -290
- package/dist/bin/shell/systemPrompt.js +0 -60
- package/dist/bin/shell/updateManager.js +0 -108
- package/dist/bin/ui/ShellUIAdapter.js +0 -459
- package/dist/bin/ui/UnifiedUIController.js +0 -183
- package/dist/bin/ui/animation/AnimationScheduler.js +0 -430
- package/dist/bin/ui/codeHighlighter.js +0 -854
- package/dist/bin/ui/designSystem.js +0 -121
- package/dist/bin/ui/display.js +0 -1222
- package/dist/bin/ui/interrupts/InterruptManager.js +0 -437
- package/dist/bin/ui/layout.js +0 -139
- package/dist/bin/ui/orchestration/StatusOrchestrator.js +0 -403
- package/dist/bin/ui/outputMode.js +0 -38
- package/dist/bin/ui/persistentPrompt.js +0 -183
- package/dist/bin/ui/richText.js +0 -338
- package/dist/bin/ui/shortcutsHelp.js +0 -87
- package/dist/bin/ui/telemetry/UITelemetry.js +0 -443
- package/dist/bin/ui/textHighlighter.js +0 -210
- package/dist/bin/ui/theme.js +0 -116
- package/dist/bin/ui/toolDisplay.js +0 -423
- package/dist/bin/ui/toolDisplayAdapter.js +0 -357
|
@@ -1,459 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ShellUIAdapter - Bridges the UnifiedUIController with the existing shell infrastructure
|
|
3
|
-
* Provides compatibility layer and migration path from old to new UI system
|
|
4
|
-
*/
|
|
5
|
-
import { UnifiedUIController } from './UnifiedUIController.js';
|
|
6
|
-
import { InterruptPriority } from './interrupts/InterruptManager.js';
|
|
7
|
-
import { getTerminalColumns } from './layout.js';
|
|
8
|
-
import { ToolDisplayAdapter } from './toolDisplayAdapter.js';
|
|
9
|
-
export class ShellUIAdapter {
|
|
10
|
-
constructor(writeStream, display, config = {}) {
|
|
11
|
-
this.isProcessing = false;
|
|
12
|
-
this.contextUsage = 0;
|
|
13
|
-
this.display = display;
|
|
14
|
-
this.toolDisplay = new ToolDisplayAdapter(display);
|
|
15
|
-
this.config = {
|
|
16
|
-
useUnifiedUI: true,
|
|
17
|
-
preserveCompatibility: true,
|
|
18
|
-
enableTelemetry: true,
|
|
19
|
-
debugMode: false,
|
|
20
|
-
...config,
|
|
21
|
-
};
|
|
22
|
-
// Initialize unified UI controller
|
|
23
|
-
this.uiController = new UnifiedUIController(writeStream, {
|
|
24
|
-
enableOverlay: true,
|
|
25
|
-
enableAnimations: true,
|
|
26
|
-
enableTelemetry: this.config.enableTelemetry,
|
|
27
|
-
adaptivePerformance: true,
|
|
28
|
-
debugMode: this.config.debugMode,
|
|
29
|
-
});
|
|
30
|
-
// Legacy components are optional and should be passed in if needed
|
|
31
|
-
// They require a readline.Interface which we don't have here
|
|
32
|
-
this.setupDisplayIntegration();
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Setup display integration
|
|
36
|
-
*/
|
|
37
|
-
setupDisplayIntegration() {
|
|
38
|
-
// Register output interceptor to coordinate with overlay
|
|
39
|
-
this.display.registerOutputInterceptor({
|
|
40
|
-
beforeWrite: () => {
|
|
41
|
-
if (this.config.useUnifiedUI) {
|
|
42
|
-
this.uiController.beginOutput();
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
afterWrite: () => {
|
|
46
|
-
if (this.config.useUnifiedUI) {
|
|
47
|
-
this.uiController.endOutput();
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Create a tool observer for the agent
|
|
54
|
-
*/
|
|
55
|
-
createToolObserver() {
|
|
56
|
-
return {
|
|
57
|
-
onToolStart: (call) => {
|
|
58
|
-
// Use safe write to coordinate with spinner output
|
|
59
|
-
this.display.safeWrite(() => {
|
|
60
|
-
this.toolDisplay.showToolStart(call);
|
|
61
|
-
const actionDesc = this.getToolActionDescription(call);
|
|
62
|
-
if (actionDesc) {
|
|
63
|
-
this.updateSpinnerMessage(actionDesc);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
if (this.config.useUnifiedUI) {
|
|
67
|
-
this.uiController.onToolStart(call);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
onToolProgress: (call, progress) => {
|
|
71
|
-
if (this.config.useUnifiedUI) {
|
|
72
|
-
this.uiController.onToolProgress(call.id, {
|
|
73
|
-
current: progress.current,
|
|
74
|
-
total: progress.total ?? progress.current,
|
|
75
|
-
message: progress.message,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
this.display.safeWrite(() => {
|
|
79
|
-
const description = this.getToolActionDescription(call, progress);
|
|
80
|
-
if (description) {
|
|
81
|
-
this.updateSpinnerMessage(description);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
},
|
|
85
|
-
onToolResult: (call, output) => {
|
|
86
|
-
// Use safe write to coordinate with spinner output
|
|
87
|
-
this.display.safeWrite(() => {
|
|
88
|
-
// Temporarily stop spinner to show result
|
|
89
|
-
const wasSpinning = this.display.isSpinnerActive();
|
|
90
|
-
if (wasSpinning) {
|
|
91
|
-
this.display.stopThinking(false);
|
|
92
|
-
}
|
|
93
|
-
// Track file changes for Edit/Write tools
|
|
94
|
-
if (this.fileChangeCallback && (call.name === 'Edit' || call.name === 'Write')) {
|
|
95
|
-
const fileChange = this.parseFileChange(call, output);
|
|
96
|
-
if (fileChange) {
|
|
97
|
-
this.fileChangeCallback(fileChange.path, fileChange.type, fileChange.additions, fileChange.removals);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
this.toolDisplay.showToolResult(call.id, output, 'success');
|
|
101
|
-
// Update spinner for next action (reuse existing if it was running)
|
|
102
|
-
if (wasSpinning) {
|
|
103
|
-
this.display.showThinking('Analyzing results...');
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
if (this.config.useUnifiedUI) {
|
|
107
|
-
this.uiController.onToolComplete(call.id, output);
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
onToolError: (call, message) => {
|
|
111
|
-
// Use safe write to coordinate with spinner output
|
|
112
|
-
this.display.safeWrite(() => {
|
|
113
|
-
// Temporarily stop spinner to show error
|
|
114
|
-
const wasSpinning = this.display.isSpinnerActive();
|
|
115
|
-
if (wasSpinning) {
|
|
116
|
-
this.display.stopThinking(false);
|
|
117
|
-
}
|
|
118
|
-
this.toolDisplay.showToolResult(call.id, message, 'error');
|
|
119
|
-
// Update spinner (reuse existing if it was running)
|
|
120
|
-
if (wasSpinning) {
|
|
121
|
-
this.display.showThinking('Handling error...');
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
if (this.config.useUnifiedUI) {
|
|
125
|
-
this.uiController.onToolError(call.id, { message });
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
onCacheHit: (call) => {
|
|
129
|
-
if (this.config.useUnifiedUI) {
|
|
130
|
-
this.uiController.onToolStart(call);
|
|
131
|
-
this.uiController.onToolComplete(call.id, 'cache-hit');
|
|
132
|
-
}
|
|
133
|
-
this.display.safeWrite(() => {
|
|
134
|
-
this.display.showSubAction(`Cache hit: ${call.name}`, 'info');
|
|
135
|
-
});
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Start processing a request
|
|
141
|
-
*/
|
|
142
|
-
startProcessing(message = 'Working on your request') {
|
|
143
|
-
this.isProcessing = true;
|
|
144
|
-
if (this.config.useUnifiedUI) {
|
|
145
|
-
this.uiController.startProcessing();
|
|
146
|
-
this.uiController.setBaseStatus(message, 'info');
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
if (this.legacyStatusTracker) {
|
|
150
|
-
this.legacyStatusTracker.setBase(message, { tone: 'info' });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* End processing
|
|
156
|
-
*/
|
|
157
|
-
endProcessing(message = 'Ready for prompts') {
|
|
158
|
-
this.isProcessing = false;
|
|
159
|
-
if (this.config.useUnifiedUI) {
|
|
160
|
-
this.uiController.endProcessing();
|
|
161
|
-
this.uiController.setBaseStatus(message, 'success');
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
if (this.legacyStatusTracker) {
|
|
165
|
-
this.legacyStatusTracker.setBase(message, { tone: 'success' });
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Update context usage
|
|
171
|
-
*/
|
|
172
|
-
updateContextUsage(percentage) {
|
|
173
|
-
this.contextUsage = percentage;
|
|
174
|
-
if (this.config.useUnifiedUI) {
|
|
175
|
-
// Determine tone based on usage
|
|
176
|
-
let tone = 'info';
|
|
177
|
-
if (percentage > 90)
|
|
178
|
-
tone = 'danger';
|
|
179
|
-
else if (percentage > 70)
|
|
180
|
-
tone = 'warning';
|
|
181
|
-
const message = `Context ${percentage}% used`;
|
|
182
|
-
this.uiController.pushStatusOverride('context', message, undefined, tone);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Show a user interrupt
|
|
187
|
-
*/
|
|
188
|
-
showInterrupt(message, type = 'alert', handler) {
|
|
189
|
-
if (this.config.useUnifiedUI) {
|
|
190
|
-
const priority = type === 'alert'
|
|
191
|
-
? InterruptPriority.HIGH
|
|
192
|
-
: InterruptPriority.NORMAL;
|
|
193
|
-
return this.uiController.queueInterrupt(type, message, priority, handler ? async () => handler() : undefined);
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
// For legacy, just show the message immediately
|
|
197
|
-
this.display.showWarning(message);
|
|
198
|
-
if (handler) {
|
|
199
|
-
setTimeout(() => handler(), 0);
|
|
200
|
-
}
|
|
201
|
-
return 'legacy-interrupt';
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Complete an interrupt
|
|
206
|
-
*/
|
|
207
|
-
completeInterrupt(id) {
|
|
208
|
-
if (this.config.useUnifiedUI) {
|
|
209
|
-
this.uiController.completeInterrupt(id);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Show slash command preview with dynamic overlay
|
|
214
|
-
*/
|
|
215
|
-
showSlashCommandPreview(commands) {
|
|
216
|
-
if (this.config.useUnifiedUI) {
|
|
217
|
-
// Build a Claude Code-style command menu without duplicate separators
|
|
218
|
-
// Build command lines with proper indentation
|
|
219
|
-
const commandLines = commands.map(cmd => {
|
|
220
|
-
return ` ${cmd.command}\n ${cmd.description}`;
|
|
221
|
-
}).join('\n');
|
|
222
|
-
// Create command content
|
|
223
|
-
const content = commandLines;
|
|
224
|
-
// Use the unified UI overlay system
|
|
225
|
-
this.uiController.updateCommandsOverlay(content);
|
|
226
|
-
}
|
|
227
|
-
else if (this.legacyStatusTracker) {
|
|
228
|
-
// Legacy fallback - just show command names
|
|
229
|
-
const preview = commands.map(c => c.command).join(' | ');
|
|
230
|
-
this.legacyStatusTracker.pushOverride('slash-preview', `Commands: ${preview}`, {
|
|
231
|
-
tone: 'info',
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Hide slash command preview
|
|
237
|
-
*/
|
|
238
|
-
hideSlashCommandPreview() {
|
|
239
|
-
if (this.config.useUnifiedUI) {
|
|
240
|
-
this.uiController.hideCommandsOverlay();
|
|
241
|
-
}
|
|
242
|
-
else if (this.legacyStatusTracker) {
|
|
243
|
-
this.legacyStatusTracker.clearOverride('slash-preview');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Show profile switcher (Shift+Tab)
|
|
248
|
-
*/
|
|
249
|
-
showProfileSwitcher(profiles, currentProfile) {
|
|
250
|
-
if (this.config.useUnifiedUI) {
|
|
251
|
-
// Build profile switcher overlay
|
|
252
|
-
const terminalWidth = getTerminalColumns();
|
|
253
|
-
const separator = '─'.repeat(terminalWidth - 1);
|
|
254
|
-
// Build profile lines
|
|
255
|
-
const profileLines = profiles.map(profile => {
|
|
256
|
-
return ` ${profile.command}\n ${profile.description}`;
|
|
257
|
-
}).join('\n');
|
|
258
|
-
// Create overlay content
|
|
259
|
-
const header = `Switch Profile (Shift+Tab)`;
|
|
260
|
-
const content = `${separator}\n${header}\n${separator}\n${profileLines}\n\nPress number to switch or Esc to cancel`;
|
|
261
|
-
// Use the unified UI overlay system
|
|
262
|
-
this.uiController.updateCommandsOverlay(content);
|
|
263
|
-
// Auto-hide after 5 seconds (track timeout to allow cleanup)
|
|
264
|
-
if (this.profileSwitcherTimeout) {
|
|
265
|
-
clearTimeout(this.profileSwitcherTimeout);
|
|
266
|
-
}
|
|
267
|
-
this.profileSwitcherTimeout = setTimeout(() => {
|
|
268
|
-
this.uiController.hideCommandsOverlay();
|
|
269
|
-
this.profileSwitcherTimeout = undefined;
|
|
270
|
-
}, 5000);
|
|
271
|
-
}
|
|
272
|
-
else if (this.legacyStatusTracker) {
|
|
273
|
-
// Legacy fallback
|
|
274
|
-
const preview = `Current: ${currentProfile} | Press Shift+Tab to switch`;
|
|
275
|
-
this.legacyStatusTracker.pushOverride('profile-switch', preview, {
|
|
276
|
-
tone: 'info',
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Enable or disable overlay
|
|
282
|
-
*/
|
|
283
|
-
setOverlayEnabled(enabled) {
|
|
284
|
-
if (this.config.useUnifiedUI) {
|
|
285
|
-
this.uiController.updateConfig({ enableOverlay: enabled });
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Get telemetry data (only available in unified mode)
|
|
290
|
-
*/
|
|
291
|
-
getTelemetry() {
|
|
292
|
-
if (this.config.useUnifiedUI) {
|
|
293
|
-
return {
|
|
294
|
-
snapshot: this.uiController.getTelemetrySnapshot(),
|
|
295
|
-
performance: this.uiController.getPerformanceSummary(),
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Switch between UI modes
|
|
302
|
-
*/
|
|
303
|
-
switchUIMode(useUnified) {
|
|
304
|
-
this.config.useUnifiedUI = useUnified;
|
|
305
|
-
if (useUnified) {
|
|
306
|
-
// Clear legacy status tracker if it exists
|
|
307
|
-
if (this.legacyStatusTracker) {
|
|
308
|
-
this.legacyStatusTracker.reset();
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
// Hide unified UI overlays
|
|
313
|
-
this.uiController.updateConfig({ enableOverlay: false });
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Helper methods
|
|
318
|
-
*/
|
|
319
|
-
updateSpinnerMessage(message) {
|
|
320
|
-
if (!message?.trim()) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (this.display.isSpinnerActive()) {
|
|
324
|
-
this.display.updateThinking(message);
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
this.display.showThinking(message);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Get action description for tool execution (Claude Code style)
|
|
332
|
-
* Returns a human-readable description of what the tool is doing
|
|
333
|
-
*/
|
|
334
|
-
getToolActionDescription(call, progress) {
|
|
335
|
-
let description;
|
|
336
|
-
switch (call.name) {
|
|
337
|
-
case 'Read':
|
|
338
|
-
description = `Reading file...`;
|
|
339
|
-
break;
|
|
340
|
-
case 'Write':
|
|
341
|
-
description = `Writing file...`;
|
|
342
|
-
break;
|
|
343
|
-
case 'Edit':
|
|
344
|
-
description = `Editing file...`;
|
|
345
|
-
break;
|
|
346
|
-
case 'Bash':
|
|
347
|
-
description = `Executing command...`;
|
|
348
|
-
break;
|
|
349
|
-
case 'Grep':
|
|
350
|
-
description = `Searching code...`;
|
|
351
|
-
break;
|
|
352
|
-
case 'Glob':
|
|
353
|
-
description = `Finding files...`;
|
|
354
|
-
break;
|
|
355
|
-
case 'WebFetch':
|
|
356
|
-
description = `Fetching web content...`;
|
|
357
|
-
break;
|
|
358
|
-
case 'WebSearch':
|
|
359
|
-
description = `Searching web...`;
|
|
360
|
-
break;
|
|
361
|
-
case 'Task':
|
|
362
|
-
description = `Running task...`;
|
|
363
|
-
break;
|
|
364
|
-
default:
|
|
365
|
-
description = `Executing ${call.name}...`;
|
|
366
|
-
}
|
|
367
|
-
if (progress) {
|
|
368
|
-
const details = [];
|
|
369
|
-
if (progress.message?.trim()) {
|
|
370
|
-
details.push(progress.message.trim());
|
|
371
|
-
}
|
|
372
|
-
if (typeof progress.total === 'number' && progress.total > 0) {
|
|
373
|
-
const pct = Math.max(0, Math.min(100, Math.round((progress.current / progress.total) * 100)));
|
|
374
|
-
details.push(`${pct}%`);
|
|
375
|
-
}
|
|
376
|
-
if (details.length) {
|
|
377
|
-
return `${description} (${details.join(' ')})`;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return description;
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Set file change tracking callback
|
|
384
|
-
*/
|
|
385
|
-
setFileChangeCallback(callback) {
|
|
386
|
-
this.fileChangeCallback = callback;
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Parse file changes from Edit/Write tool output
|
|
390
|
-
*/
|
|
391
|
-
parseFileChange(call, output) {
|
|
392
|
-
const args = call.arguments;
|
|
393
|
-
const path = args.file_path || args.path;
|
|
394
|
-
if (!path)
|
|
395
|
-
return null;
|
|
396
|
-
// Parse additions and removals from output
|
|
397
|
-
let additions = 0;
|
|
398
|
-
let removals = 0;
|
|
399
|
-
// For Edit tool: parse "Lines changed: +X / -Y" or "+X addition -Y removal"
|
|
400
|
-
const editMatch = output.match(/\+(\d+).*-(\d+)/);
|
|
401
|
-
if (editMatch) {
|
|
402
|
-
additions = parseInt(editMatch[1] || '0', 10);
|
|
403
|
-
removals = parseInt(editMatch[2] || '0', 10);
|
|
404
|
-
}
|
|
405
|
-
// Alternative format: "X addition" and "Y removal"
|
|
406
|
-
const addMatch = output.match(/(\d+)\s+addition/);
|
|
407
|
-
const remMatch = output.match(/(\d+)\s+removal/);
|
|
408
|
-
if (addMatch)
|
|
409
|
-
additions = parseInt(addMatch[1] || '0', 10);
|
|
410
|
-
if (remMatch)
|
|
411
|
-
removals = parseInt(remMatch[1] || '0', 10);
|
|
412
|
-
// For Write tool: estimate as all additions (new file or full rewrite)
|
|
413
|
-
if (call.name === 'Write' && additions === 0 && removals === 0) {
|
|
414
|
-
const lines = (args.content || '').split('\n').length;
|
|
415
|
-
additions = lines;
|
|
416
|
-
}
|
|
417
|
-
return {
|
|
418
|
-
path,
|
|
419
|
-
type: call.name === 'Edit' ? 'edit' : 'write',
|
|
420
|
-
additions,
|
|
421
|
-
removals
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Get current UI mode
|
|
426
|
-
*/
|
|
427
|
-
getCurrentMode() {
|
|
428
|
-
return this.config.useUnifiedUI ? 'unified' : 'legacy';
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Get UI state
|
|
432
|
-
*/
|
|
433
|
-
getState() {
|
|
434
|
-
if (this.config.useUnifiedUI) {
|
|
435
|
-
return this.uiController.getState();
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
return {
|
|
439
|
-
isProcessing: this.isProcessing,
|
|
440
|
-
contextUsage: this.contextUsage,
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Dispose of resources
|
|
446
|
-
*/
|
|
447
|
-
dispose() {
|
|
448
|
-
// Clear any pending timeouts to prevent process hang
|
|
449
|
-
if (this.profileSwitcherTimeout) {
|
|
450
|
-
clearTimeout(this.profileSwitcherTimeout);
|
|
451
|
-
this.profileSwitcherTimeout = undefined;
|
|
452
|
-
}
|
|
453
|
-
this.uiController.dispose();
|
|
454
|
-
if (this.legacyStatusTracker) {
|
|
455
|
-
// Clear all status
|
|
456
|
-
this.legacyStatusTracker.reset();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UnifiedUIController - Minimal stub for unified UI system
|
|
3
|
-
*
|
|
4
|
-
* The overlay-based implementation has been removed in favor of simple,
|
|
5
|
-
* stable terminal output that doesn't fight with readline.
|
|
6
|
-
*/
|
|
7
|
-
import { EventEmitter } from 'events';
|
|
8
|
-
import { StatusOrchestrator } from './orchestration/StatusOrchestrator.js';
|
|
9
|
-
import { AnimationScheduler } from './animation/AnimationScheduler.js';
|
|
10
|
-
import { InterruptManager, InterruptPriority } from './interrupts/InterruptManager.js';
|
|
11
|
-
import { UITelemetry } from './telemetry/UITelemetry.js';
|
|
12
|
-
export class UnifiedUIController extends EventEmitter {
|
|
13
|
-
constructor(_writeStream, config = {}) {
|
|
14
|
-
super();
|
|
15
|
-
this.activeSpinners = new Map();
|
|
16
|
-
this.activeElapsed = new Map();
|
|
17
|
-
this.config = {
|
|
18
|
-
enableOverlay: false, // Disabled - no complex overlay
|
|
19
|
-
enableAnimations: true,
|
|
20
|
-
enableTelemetry: true,
|
|
21
|
-
adaptivePerformance: false,
|
|
22
|
-
debugMode: false,
|
|
23
|
-
...config,
|
|
24
|
-
};
|
|
25
|
-
this.state = {
|
|
26
|
-
isProcessing: false,
|
|
27
|
-
hasActiveTools: false,
|
|
28
|
-
hasActiveInterrupts: false,
|
|
29
|
-
overlayVisible: false,
|
|
30
|
-
currentStatus: null,
|
|
31
|
-
activeAnimations: 0,
|
|
32
|
-
performanceMode: 'balanced',
|
|
33
|
-
};
|
|
34
|
-
// Initialize components (no overlay manager)
|
|
35
|
-
this.statusOrchestrator = new StatusOrchestrator();
|
|
36
|
-
this.animationScheduler = new AnimationScheduler(30);
|
|
37
|
-
this.interruptManager = new InterruptManager();
|
|
38
|
-
this.telemetry = new UITelemetry({ enabled: config.enableTelemetry });
|
|
39
|
-
this.setupEventHandlers();
|
|
40
|
-
this.emit('ui.initialized');
|
|
41
|
-
}
|
|
42
|
-
setupEventHandlers() {
|
|
43
|
-
this.statusOrchestrator.subscribe((event) => {
|
|
44
|
-
this.handleStatusEvent(event);
|
|
45
|
-
});
|
|
46
|
-
this.interruptManager.on('interrupt:activated', () => {
|
|
47
|
-
this.state.hasActiveInterrupts = true;
|
|
48
|
-
this.emit('ui.state.changed', { hasActiveInterrupts: true });
|
|
49
|
-
});
|
|
50
|
-
this.interruptManager.on('interrupt:completed', () => {
|
|
51
|
-
const activeCount = this.interruptManager.getInterruptsByStatus('active').length;
|
|
52
|
-
this.state.hasActiveInterrupts = activeCount > 0;
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
handleStatusEvent(event) {
|
|
56
|
-
this.telemetry.recordEvent(event.type, event.data);
|
|
57
|
-
switch (event.type) {
|
|
58
|
-
case 'tool.start':
|
|
59
|
-
this.handleToolStart(event.data);
|
|
60
|
-
break;
|
|
61
|
-
case 'tool.complete':
|
|
62
|
-
this.handleToolComplete(event.data);
|
|
63
|
-
break;
|
|
64
|
-
case 'tool.error':
|
|
65
|
-
this.handleToolError(event.data);
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
handleToolStart(data) {
|
|
70
|
-
const { toolCall, toolStatus } = data;
|
|
71
|
-
this.telemetry.markStart(`tool.${toolCall.name}`);
|
|
72
|
-
if (this.config.enableAnimations) {
|
|
73
|
-
const spinner = this.animationScheduler.createSpinner(`tool-${toolCall.id}`, toolStatus.description);
|
|
74
|
-
this.activeSpinners.set(toolCall.id, spinner);
|
|
75
|
-
}
|
|
76
|
-
const elapsed = this.animationScheduler.createElapsed(`elapsed-${toolCall.id}`, toolStatus.startedAt);
|
|
77
|
-
this.activeElapsed.set(toolCall.id, elapsed);
|
|
78
|
-
this.state.hasActiveTools = true;
|
|
79
|
-
}
|
|
80
|
-
handleToolComplete(data) {
|
|
81
|
-
this.activeSpinners.delete(data.toolId);
|
|
82
|
-
this.activeElapsed.delete(data.toolId);
|
|
83
|
-
this.animationScheduler.unregister(`tool-${data.toolId}`);
|
|
84
|
-
this.animationScheduler.unregister(`elapsed-${data.toolId}`);
|
|
85
|
-
if (this.statusOrchestrator.getContext().tools.size === 0) {
|
|
86
|
-
this.state.hasActiveTools = false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
handleToolError(data) {
|
|
90
|
-
this.telemetry.recordError(data.error, { toolId: data.toolId });
|
|
91
|
-
this.activeSpinners.delete(data.toolId);
|
|
92
|
-
this.activeElapsed.delete(data.toolId);
|
|
93
|
-
this.animationScheduler.unregister(`tool-${data.toolId}`);
|
|
94
|
-
this.animationScheduler.unregister(`elapsed-${data.toolId}`);
|
|
95
|
-
}
|
|
96
|
-
// Public API
|
|
97
|
-
startProcessing() {
|
|
98
|
-
this.state.isProcessing = true;
|
|
99
|
-
this.emit('ui.state.changed', { isProcessing: true });
|
|
100
|
-
}
|
|
101
|
-
endProcessing() {
|
|
102
|
-
this.state.isProcessing = false;
|
|
103
|
-
this.emit('ui.state.changed', { isProcessing: false });
|
|
104
|
-
}
|
|
105
|
-
setBaseStatus(text, tone) {
|
|
106
|
-
this.statusOrchestrator.setBaseStatus({
|
|
107
|
-
text,
|
|
108
|
-
tone,
|
|
109
|
-
startedAt: Date.now(),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
pushStatusOverride(id, text, detail, tone) {
|
|
113
|
-
this.statusOrchestrator.pushOverride(id, {
|
|
114
|
-
text,
|
|
115
|
-
detail,
|
|
116
|
-
tone,
|
|
117
|
-
startedAt: Date.now(),
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
clearStatusOverride(id) {
|
|
121
|
-
this.statusOrchestrator.clearOverride(id);
|
|
122
|
-
}
|
|
123
|
-
updateCommandsOverlay(_content) {
|
|
124
|
-
// No-op - overlay removed
|
|
125
|
-
}
|
|
126
|
-
hideCommandsOverlay() {
|
|
127
|
-
// No-op - overlay removed
|
|
128
|
-
}
|
|
129
|
-
onToolStart(toolCall) {
|
|
130
|
-
this.statusOrchestrator.onToolStart(toolCall);
|
|
131
|
-
}
|
|
132
|
-
onToolProgress(toolId, progress) {
|
|
133
|
-
this.statusOrchestrator.onToolProgress(toolId, progress);
|
|
134
|
-
}
|
|
135
|
-
onToolComplete(toolId, result) {
|
|
136
|
-
this.statusOrchestrator.onToolComplete(toolId, result);
|
|
137
|
-
}
|
|
138
|
-
onToolError(toolId, error) {
|
|
139
|
-
this.statusOrchestrator.onToolError(toolId, error);
|
|
140
|
-
}
|
|
141
|
-
queueInterrupt(type, message, priority = InterruptPriority.NORMAL, handler) {
|
|
142
|
-
return this.interruptManager.queue({
|
|
143
|
-
type,
|
|
144
|
-
priority,
|
|
145
|
-
message,
|
|
146
|
-
handler,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
completeInterrupt(id) {
|
|
150
|
-
this.interruptManager.completeInterrupt(id);
|
|
151
|
-
}
|
|
152
|
-
beginOutput() {
|
|
153
|
-
// No-op - overlay removed
|
|
154
|
-
}
|
|
155
|
-
endOutput() {
|
|
156
|
-
// No-op - overlay removed
|
|
157
|
-
}
|
|
158
|
-
getState() {
|
|
159
|
-
return { ...this.state };
|
|
160
|
-
}
|
|
161
|
-
getTelemetrySnapshot() {
|
|
162
|
-
return this.telemetry.getSnapshot();
|
|
163
|
-
}
|
|
164
|
-
getPerformanceSummary() {
|
|
165
|
-
return this.telemetry.getPerformanceSummary();
|
|
166
|
-
}
|
|
167
|
-
updateConfig(config) {
|
|
168
|
-
this.config = { ...this.config, ...config };
|
|
169
|
-
this.telemetry.setEnabled(this.config.enableTelemetry);
|
|
170
|
-
if (!this.config.enableAnimations) {
|
|
171
|
-
this.animationScheduler.clearAll();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
dispose() {
|
|
175
|
-
this.statusOrchestrator.reset();
|
|
176
|
-
this.animationScheduler.dispose();
|
|
177
|
-
this.interruptManager.dispose();
|
|
178
|
-
this.telemetry.dispose();
|
|
179
|
-
this.activeSpinners.clear();
|
|
180
|
-
this.activeElapsed.clear();
|
|
181
|
-
this.removeAllListeners();
|
|
182
|
-
}
|
|
183
|
-
}
|