erosolar-cli 2.1.170 → 2.1.172

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.
Files changed (172) hide show
  1. package/README.md +1 -1
  2. package/agents/erosolar-code.rules.json +2 -2
  3. package/agents/general.rules.json +3 -21
  4. package/dist/StringUtils.d.ts +8 -0
  5. package/dist/StringUtils.d.ts.map +1 -0
  6. package/dist/StringUtils.js +11 -0
  7. package/dist/StringUtils.js.map +1 -0
  8. package/dist/capabilities/statusCapability.js +2 -2
  9. package/dist/capabilities/statusCapability.js.map +1 -1
  10. package/dist/contracts/agent-schemas.json +5 -5
  11. package/dist/core/agent.d.ts +24 -83
  12. package/dist/core/agent.d.ts.map +1 -1
  13. package/dist/core/agent.js +248 -499
  14. package/dist/core/agent.js.map +1 -1
  15. package/dist/core/aiFlowSupervisor.d.ts +44 -0
  16. package/dist/core/aiFlowSupervisor.d.ts.map +1 -0
  17. package/dist/core/aiFlowSupervisor.js +299 -0
  18. package/dist/core/aiFlowSupervisor.js.map +1 -0
  19. package/dist/core/cliTestHarness.d.ts +200 -0
  20. package/dist/core/cliTestHarness.d.ts.map +1 -0
  21. package/dist/core/cliTestHarness.js +549 -0
  22. package/dist/core/cliTestHarness.js.map +1 -0
  23. package/dist/core/preferences.d.ts +0 -1
  24. package/dist/core/preferences.d.ts.map +1 -1
  25. package/dist/core/preferences.js +1 -8
  26. package/dist/core/preferences.js.map +1 -1
  27. package/dist/core/schemaValidator.js +3 -3
  28. package/dist/core/schemaValidator.js.map +1 -1
  29. package/dist/core/testUtils.d.ts +121 -0
  30. package/dist/core/testUtils.d.ts.map +1 -0
  31. package/dist/core/testUtils.js +235 -0
  32. package/dist/core/testUtils.js.map +1 -0
  33. package/dist/core/toolPreconditions.d.ts +11 -0
  34. package/dist/core/toolPreconditions.d.ts.map +1 -1
  35. package/dist/core/toolPreconditions.js +164 -33
  36. package/dist/core/toolPreconditions.js.map +1 -1
  37. package/dist/core/toolRuntime.d.ts.map +1 -1
  38. package/dist/core/toolRuntime.js +114 -9
  39. package/dist/core/toolRuntime.js.map +1 -1
  40. package/dist/core/toolValidation.d.ts +116 -0
  41. package/dist/core/toolValidation.d.ts.map +1 -0
  42. package/dist/core/toolValidation.js +282 -0
  43. package/dist/core/toolValidation.js.map +1 -0
  44. package/dist/core/updateChecker.d.ts +1 -61
  45. package/dist/core/updateChecker.d.ts.map +1 -1
  46. package/dist/core/updateChecker.js +3 -147
  47. package/dist/core/updateChecker.js.map +1 -1
  48. package/dist/headless/headlessApp.d.ts.map +1 -1
  49. package/dist/headless/headlessApp.js +39 -0
  50. package/dist/headless/headlessApp.js.map +1 -1
  51. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  52. package/dist/plugins/tools/nodeDefaults.js +2 -0
  53. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  54. package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
  55. package/dist/providers/openaiResponsesProvider.js +74 -79
  56. package/dist/providers/openaiResponsesProvider.js.map +1 -1
  57. package/dist/runtime/agentController.d.ts.map +1 -1
  58. package/dist/runtime/agentController.js +3 -6
  59. package/dist/runtime/agentController.js.map +1 -1
  60. package/dist/runtime/agentSession.d.ts +2 -0
  61. package/dist/runtime/agentSession.d.ts.map +1 -1
  62. package/dist/runtime/agentSession.js +2 -2
  63. package/dist/runtime/agentSession.js.map +1 -1
  64. package/dist/shell/interactiveShell.d.ts +18 -11
  65. package/dist/shell/interactiveShell.d.ts.map +1 -1
  66. package/dist/shell/interactiveShell.js +291 -273
  67. package/dist/shell/interactiveShell.js.map +1 -1
  68. package/dist/shell/shellApp.d.ts.map +1 -1
  69. package/dist/shell/shellApp.js +1 -7
  70. package/dist/shell/shellApp.js.map +1 -1
  71. package/dist/shell/systemPrompt.d.ts.map +1 -1
  72. package/dist/shell/systemPrompt.js +15 -4
  73. package/dist/shell/systemPrompt.js.map +1 -1
  74. package/dist/subagents/taskRunner.js +1 -2
  75. package/dist/subagents/taskRunner.js.map +1 -1
  76. package/dist/tools/bashTools.d.ts.map +1 -1
  77. package/dist/tools/bashTools.js +8 -101
  78. package/dist/tools/bashTools.js.map +1 -1
  79. package/dist/tools/diffUtils.d.ts +2 -8
  80. package/dist/tools/diffUtils.d.ts.map +1 -1
  81. package/dist/tools/diffUtils.js +13 -72
  82. package/dist/tools/diffUtils.js.map +1 -1
  83. package/dist/tools/grepTools.d.ts.map +1 -1
  84. package/dist/tools/grepTools.js +2 -10
  85. package/dist/tools/grepTools.js.map +1 -1
  86. package/dist/tools/planningTools.d.ts +10 -0
  87. package/dist/tools/planningTools.d.ts.map +1 -1
  88. package/dist/tools/planningTools.js +16 -0
  89. package/dist/tools/planningTools.js.map +1 -1
  90. package/dist/tools/searchTools.d.ts.map +1 -1
  91. package/dist/tools/searchTools.js +2 -4
  92. package/dist/tools/searchTools.js.map +1 -1
  93. package/dist/ui/PromptController.d.ts +4 -1
  94. package/dist/ui/PromptController.d.ts.map +1 -1
  95. package/dist/ui/PromptController.js +7 -1
  96. package/dist/ui/PromptController.js.map +1 -1
  97. package/dist/ui/ShellUIAdapter.d.ts +28 -292
  98. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  99. package/dist/ui/ShellUIAdapter.js +121 -1513
  100. package/dist/ui/ShellUIAdapter.js.map +1 -1
  101. package/dist/ui/UnifiedUIRenderer.d.ts +30 -133
  102. package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
  103. package/dist/ui/UnifiedUIRenderer.js +370 -939
  104. package/dist/ui/UnifiedUIRenderer.js.map +1 -1
  105. package/dist/ui/animatedStatus.d.ts +6 -128
  106. package/dist/ui/animatedStatus.d.ts.map +1 -1
  107. package/dist/ui/animatedStatus.js +50 -383
  108. package/dist/ui/animatedStatus.js.map +1 -1
  109. package/dist/ui/display.d.ts +26 -182
  110. package/dist/ui/display.d.ts.map +1 -1
  111. package/dist/ui/display.js +97 -678
  112. package/dist/ui/display.js.map +1 -1
  113. package/dist/ui/layout.d.ts +1 -0
  114. package/dist/ui/layout.d.ts.map +1 -1
  115. package/dist/ui/layout.js +12 -0
  116. package/dist/ui/layout.js.map +1 -1
  117. package/dist/ui/orchestration/UIUpdateCoordinator.d.ts +7 -61
  118. package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
  119. package/dist/ui/orchestration/UIUpdateCoordinator.js +20 -232
  120. package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
  121. package/dist/ui/planOverlay.d.ts +28 -0
  122. package/dist/ui/planOverlay.d.ts.map +1 -0
  123. package/dist/ui/planOverlay.js +156 -0
  124. package/dist/ui/planOverlay.js.map +1 -0
  125. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  126. package/dist/ui/shortcutsHelp.js +1 -0
  127. package/dist/ui/shortcutsHelp.js.map +1 -1
  128. package/dist/ui/streamingFormatter.d.ts +30 -0
  129. package/dist/ui/streamingFormatter.d.ts.map +1 -0
  130. package/dist/ui/streamingFormatter.js +91 -0
  131. package/dist/ui/streamingFormatter.js.map +1 -0
  132. package/dist/ui/unified/index.d.ts +1 -30
  133. package/dist/ui/unified/index.d.ts.map +1 -1
  134. package/dist/ui/unified/index.js +2 -45
  135. package/dist/ui/unified/index.js.map +1 -1
  136. package/dist/utils/errorUtils.d.ts +16 -0
  137. package/dist/utils/errorUtils.d.ts.map +1 -0
  138. package/dist/utils/errorUtils.js +66 -0
  139. package/dist/utils/errorUtils.js.map +1 -0
  140. package/package.json +2 -1
  141. package/dist/core/reliabilityPrompt.d.ts +0 -9
  142. package/dist/core/reliabilityPrompt.d.ts.map +0 -1
  143. package/dist/core/reliabilityPrompt.js +0 -31
  144. package/dist/core/reliabilityPrompt.js.map +0 -1
  145. package/dist/ui/UnifiedUIController.d.ts +0 -81
  146. package/dist/ui/UnifiedUIController.d.ts.map +0 -1
  147. package/dist/ui/UnifiedUIController.js +0 -212
  148. package/dist/ui/UnifiedUIController.js.map +0 -1
  149. package/dist/ui/animation/AnimationScheduler.d.ts +0 -192
  150. package/dist/ui/animation/AnimationScheduler.d.ts.map +0 -1
  151. package/dist/ui/animation/AnimationScheduler.js +0 -432
  152. package/dist/ui/animation/AnimationScheduler.js.map +0 -1
  153. package/dist/ui/inPlaceUpdater.d.ts +0 -181
  154. package/dist/ui/inPlaceUpdater.d.ts.map +0 -1
  155. package/dist/ui/inPlaceUpdater.js +0 -515
  156. package/dist/ui/inPlaceUpdater.js.map +0 -1
  157. package/dist/ui/interrupts/InterruptManager.d.ts +0 -142
  158. package/dist/ui/interrupts/InterruptManager.d.ts.map +0 -1
  159. package/dist/ui/interrupts/InterruptManager.js +0 -439
  160. package/dist/ui/interrupts/InterruptManager.js.map +0 -1
  161. package/dist/ui/telemetry/ResponseTracker.d.ts +0 -22
  162. package/dist/ui/telemetry/ResponseTracker.d.ts.map +0 -1
  163. package/dist/ui/telemetry/ResponseTracker.js +0 -60
  164. package/dist/ui/telemetry/ResponseTracker.js.map +0 -1
  165. package/dist/ui/telemetry/UITelemetry.d.ts +0 -181
  166. package/dist/ui/telemetry/UITelemetry.d.ts.map +0 -1
  167. package/dist/ui/telemetry/UITelemetry.js +0 -446
  168. package/dist/ui/telemetry/UITelemetry.js.map +0 -1
  169. package/dist/ui/unified/layout.d.ts +0 -12
  170. package/dist/ui/unified/layout.d.ts.map +0 -1
  171. package/dist/ui/unified/layout.js +0 -96
  172. package/dist/ui/unified/layout.js.map +0 -1
@@ -1,223 +1,53 @@
1
- /**
2
- * Display - Simplified UI facade that routes all output through UnifiedUIRenderer
3
- *
4
- * This class now serves as a compatibility layer, providing the same API
5
- * but delegating all actual rendering to UnifiedUIRenderer.
6
- */
7
1
  import readline from 'node:readline';
8
- import { theme, icons } from './theme.js';
9
- import { renderMessagePanel, renderMessageBody } from './richText.js';
10
- import { getTerminalColumns } from './layout.js';
11
- import { highlightError } from './textHighlighter.js';
12
- import { renderSectionHeading } from './designSystem.js';
13
- import { isPlainOutputMode } from './outputMode.js';
14
- // Display configuration constants
15
- const DISPLAY_CONSTANTS = {
16
- MIN_BANNER_WIDTH: 32,
17
- MAX_BANNER_WIDTH: 120,
18
- BANNER_PADDING: 4,
19
- MIN_MESSAGE_WIDTH: 42,
20
- MAX_MESSAGE_WIDTH: 110,
21
- MESSAGE_PADDING: 4,
22
- MIN_ACTION_WIDTH: 40,
23
- MAX_ACTION_WIDTH: 90,
24
- MIN_THOUGHT_WIDTH: 48,
25
- MAX_THOUGHT_WIDTH: 96,
26
- MIN_CONTENT_WIDTH: 10,
27
- MIN_WRAP_WIDTH: 12,
28
- };
29
- /**
30
- * Display class - now a thin wrapper around UnifiedUIRenderer
31
- *
32
- * Provides backward-compatible API while routing all output through the renderer.
33
- */
2
+ import { theme } from './theme.js';
34
3
  export class Display {
35
- outputStream;
36
- errorStream;
37
4
  renderer = null;
38
- outputInterceptors = new Set();
5
+ outputStream;
39
6
  inlinePanelHandler = null;
40
- constructor(stream = process.stdout, errorStream) {
7
+ thinkingStart = null;
8
+ lastToolResult = null;
9
+ activeSpinner = null;
10
+ constructor(stream = process.stdout, _errorStream) {
41
11
  this.outputStream = stream;
42
- this.errorStream = errorStream ?? stream;
43
12
  }
44
13
  setRenderer(renderer) {
45
14
  this.renderer = renderer;
46
15
  }
47
- hasRenderer() {
48
- return Boolean(this.renderer);
49
- }
50
16
  setInlinePanelHandler(handler) {
51
17
  this.inlinePanelHandler = handler;
52
18
  }
53
- maybeHandleInlinePanel(content) {
54
- if (!this.inlinePanelHandler) {
55
- return false;
56
- }
57
- try {
58
- return this.inlinePanelHandler(content) === true;
59
- }
60
- catch {
61
- return false;
62
- }
63
- }
64
- enqueueEvent(type, content) {
65
- if (!this.renderer || !content) {
66
- return false;
67
- }
68
- this.renderer.addEvent(type, content);
69
- return true;
70
- }
71
- registerOutputInterceptor(interceptor) {
72
- if (!interceptor) {
73
- return () => { };
74
- }
75
- this.outputInterceptors.add(interceptor);
76
- return () => {
77
- this.outputInterceptors.delete(interceptor);
78
- };
79
- }
80
- notifyBeforeOutput() {
81
- for (const interceptor of this.outputInterceptors) {
82
- interceptor.beforeWrite?.();
83
- }
84
- }
85
- notifyAfterOutput(content) {
86
- const interceptors = Array.from(this.outputInterceptors);
87
- for (let index = interceptors.length - 1; index >= 0; index -= 1) {
88
- interceptors[index]?.afterWrite?.(content);
89
- }
90
- }
91
- /**
92
- * Write raw content directly
93
- */
94
- writeRaw(content) {
19
+ emit(type, content, options) {
95
20
  if (!content)
96
21
  return;
97
- this.notifyBeforeOutput();
98
- if (this.enqueueEvent('raw', content)) {
99
- this.notifyAfterOutput(content);
22
+ if (this.renderer) {
23
+ this.renderer.addEvent(type, content, options);
100
24
  return;
101
25
  }
102
- // Fallback if no renderer
103
26
  this.outputStream.write(content);
104
- this.notifyAfterOutput(content);
105
27
  }
106
- /**
107
- * Stream chunk (for streaming responses)
108
- */
109
28
  stream(chunk) {
110
29
  if (!chunk)
111
30
  return;
112
- this.notifyBeforeOutput();
113
- if (this.enqueueEvent('streaming', chunk)) {
114
- this.notifyAfterOutput(chunk);
115
- return;
116
- }
117
- // Fallback
118
- this.outputStream.write(chunk);
119
- this.notifyAfterOutput(chunk);
120
- }
121
- /**
122
- * Backward-compatible alias
123
- */
124
- writeStreamChunk(chunk) {
125
- this.stream(chunk);
126
- }
127
- /**
128
- * Get the output stream for direct access
129
- */
130
- getOutputStream() {
131
- return this.outputStream;
132
- }
133
- /**
134
- * Show thinking indicator
135
- * NO-OP: Don't emit events during SSE streaming.
136
- * The animated spinner in the status line shows thinking state.
137
- */
138
- showThinking(_message = 'Thinking…') {
139
- // NO-OP: Spinner animation handles thinking state visually.
140
- // No events emitted - final message handler renders complete content.
141
- }
142
- /**
143
- * Update thinking indicator (status line only, not scrollback)
144
- * This is called frequently during streaming to show progress snippets.
145
- * The actual content will be rendered as a complete block later.
146
- */
147
- updateThinking(_message) {
148
- // NO-OP: Don't emit events for transient thinking updates.
149
- // Thinking snippets are shown only in the status indicator (animated spinner).
150
- // The complete thought/response will be rendered as a block when ready.
31
+ this.emit('streaming', chunk);
151
32
  }
152
- /**
153
- * Stop thinking (no-op - renderer handles state)
154
- */
155
- stopThinking(_addNewLine = true) {
156
- // No-op - renderer handles thinking state
33
+ parallelAgentStatus(content) {
34
+ if (!content)
35
+ return;
36
+ const payload = content.endsWith('\n') ? content : `${content}\n`;
37
+ this.emit('streaming', payload);
157
38
  }
158
- /**
159
- * Show assistant message
160
- */
161
39
  showAssistantMessage(content, metadata) {
162
40
  const normalized = content.trim();
163
41
  if (!normalized)
164
42
  return;
43
+ this.clearActiveSpinner();
165
44
  const isThought = metadata?.isFinal === false;
166
- const useRichBlock = !isThought && metadata?.wasStreamed !== true;
167
- const wrapped = useRichBlock ? this.buildChatBox(normalized, metadata) : this.wrapForRenderer(normalized);
168
- const telemetry = !useRichBlock && metadata?.isFinal !== false ? this.formatTelemetryLine(metadata) : '';
169
- const payload = telemetry ? `${wrapped}\n${telemetry}` : wrapped;
170
- const eventType = isThought ? 'thought' : useRichBlock ? 'banner' : 'response';
171
- this.notifyBeforeOutput();
172
- if (!this.enqueueEvent(eventType, payload)) {
173
- this.outputStream.write(`${payload}\n`);
174
- }
175
- this.notifyAfterOutput(payload);
45
+ const eventType = isThought ? 'thought' : 'response';
46
+ this.emit(eventType, normalized);
176
47
  }
177
- /**
178
- * Show narrative (thought)
179
- * NO-OP: Don't emit events during SSE streaming.
180
- * Narratives are buffered and rendered as part of the final response.
181
- */
182
- showNarrative(_content) {
183
- // NO-OP: Don't emit intermediate narrative events.
184
- // Final message handler renders complete thought + response.
48
+ showNarrative(content) {
49
+ this.showAssistantMessage(content, { isFinal: false });
185
50
  }
186
- /**
187
- * Show action
188
- */
189
- showAction(text, status = 'info') {
190
- if (!text.trim())
191
- return;
192
- const icon = this.formatActionIcon(status);
193
- const rendered = this.wrapWithPrefix(text, `${icon} `);
194
- this.enqueueEvent('raw', `${rendered}\n`);
195
- }
196
- /**
197
- * Show sub-action
198
- */
199
- showSubAction(text, status = 'info') {
200
- if (!text.trim())
201
- return;
202
- const lines = this.buildWrappedSubActionLines(text, status);
203
- if (!lines.length)
204
- return;
205
- this.enqueueEvent('raw', `${lines.join('\n')}\n\n`);
206
- }
207
- /**
208
- * Show message
209
- */
210
- showMessage(content, role = 'assistant') {
211
- if (role === 'system') {
212
- this.showSystemMessage(content);
213
- }
214
- else {
215
- this.showAssistantMessage(content);
216
- }
217
- }
218
- /**
219
- * Show system message
220
- */
221
51
  showSystemMessage(content) {
222
52
  const normalized = content.trim();
223
53
  if (!normalized)
@@ -225,29 +55,69 @@ export class Display {
225
55
  if (this.maybeHandleInlinePanel(normalized)) {
226
56
  return;
227
57
  }
228
- const output = `${normalized}\n\n`;
229
- this.notifyBeforeOutput();
230
- if (!this.enqueueEvent('response', output)) {
231
- this.outputStream.write(output);
58
+ this.emit('response', normalized);
59
+ }
60
+ showError(message, error) {
61
+ const parts = [`${theme.error('✗')} ${message}`];
62
+ if (error) {
63
+ const detail = error instanceof Error ? error.message : String(error);
64
+ parts.push(theme.ui.muted(detail));
232
65
  }
233
- this.notifyAfterOutput(output);
66
+ this.emit('response', parts.join('\n'));
67
+ }
68
+ showWarning(message) {
69
+ this.emit('response', `${theme.warning('!')} ${message}`);
70
+ }
71
+ showInfo(message) {
72
+ this.emit('response', `${theme.info('•')} ${message}`);
73
+ }
74
+ showSuccess(message) {
75
+ this.emit('response', `${theme.success('✓')} ${message}`);
234
76
  }
235
- /**
236
- * Show test event (renders in unified UI "test" lane)
237
- */
238
77
  showTestEvent(content) {
239
78
  const normalized = content.trim();
240
79
  if (!normalized)
241
80
  return;
242
- this.notifyBeforeOutput();
243
- if (!this.enqueueEvent('test', normalized)) {
244
- this.outputStream.write(`${normalized}\n\n`);
81
+ this.emit('test', normalized);
82
+ }
83
+ showThinking(message = 'Thinking…') {
84
+ this.thinkingStart = Date.now();
85
+ this.emit('thought', message);
86
+ }
87
+ updateThinking(message) {
88
+ this.emit('thought', message);
89
+ }
90
+ stopThinking(addNewline = true) {
91
+ this.clearActiveSpinner(!addNewline);
92
+ this.thinkingStart = null;
93
+ }
94
+ clearActiveSpinner(useStop = false) {
95
+ if (!this.activeSpinner) {
96
+ return;
97
+ }
98
+ const spinner = this.activeSpinner;
99
+ this.activeSpinner = null;
100
+ try {
101
+ if (!useStop && typeof spinner.clear === 'function') {
102
+ spinner.clear();
103
+ }
104
+ else if (typeof spinner.stop === 'function') {
105
+ spinner.stop();
106
+ }
107
+ }
108
+ catch {
109
+ // ignore spinner errors
245
110
  }
246
- this.notifyAfterOutput(normalized);
247
111
  }
248
- /**
249
- * Show an inline panel pinned below the prompt (overlay only, no scrollback).
250
- */
112
+ isSpinnerActive() {
113
+ return false;
114
+ }
115
+ getThinkingElapsedMs() {
116
+ if (!this.thinkingStart) {
117
+ return null;
118
+ }
119
+ return Date.now() - this.thinkingStart;
120
+ }
251
121
  showInlinePanel(content) {
252
122
  const lines = Array.isArray(content) ? content : content.split('\n');
253
123
  const normalized = lines.map(line => line.trimEnd()).filter(line => line.trim().length > 0);
@@ -259,22 +129,15 @@ export class Display {
259
129
  this.renderer.setInlinePanel(normalized);
260
130
  return;
261
131
  }
262
- // Fallback for plain mode: emit directly without routing through the inline handler
263
132
  const output = `${normalized.join('\n')}\n`;
264
- this.notifyBeforeOutput();
265
133
  this.outputStream.write(output);
266
- this.notifyAfterOutput(output);
267
134
  }
268
135
  clearInlinePanel() {
269
136
  if (this.renderer && typeof this.renderer.clearInlinePanel === 'function') {
270
137
  this.renderer.clearInlinePanel();
271
138
  }
272
139
  }
273
- /**
274
- * Capture a single line of user input without logging it to scrollback.
275
- * Uses the unified renderer when available, with a readline fallback.
276
- */
277
- async captureUserInput(options = {}) {
140
+ captureUserInput(options = {}) {
278
141
  if (this.renderer && typeof this.renderer.captureInput === 'function') {
279
142
  return this.renderer.captureInput(options);
280
143
  }
@@ -295,180 +158,46 @@ export class Display {
295
158
  });
296
159
  });
297
160
  }
298
- /**
299
- * Show error
300
- */
301
- showError(message, error) {
302
- const details = this.formatErrorDetails(error);
303
- const parts = [`${theme.error('✗')} ${message}`];
304
- if (details) {
305
- parts.push(` ${details}`);
306
- }
307
- const output = `${parts.join('\n')}\n`;
308
- this.notifyBeforeOutput();
309
- if (!this.enqueueEvent('response', output)) {
310
- this.outputStream.write(output);
311
- }
312
- this.notifyAfterOutput(output);
313
- }
314
- /**
315
- * Show warning
316
- */
317
- showWarning(message) {
318
- const output = `${theme.warning('!')} ${message}\n`;
319
- this.notifyBeforeOutput();
320
- if (!this.enqueueEvent('response', output)) {
321
- this.outputStream.write(output);
322
- }
323
- this.notifyAfterOutput(output);
324
- }
325
- /**
326
- * Show info
327
- */
328
- showInfo(message) {
329
- const output = `${theme.info('ℹ')} ${message}\n`;
330
- this.notifyBeforeOutput();
331
- if (!this.enqueueEvent('response', output)) {
332
- this.outputStream.write(output);
333
- }
334
- this.notifyAfterOutput(output);
161
+ rememberToolResult(content, summary) {
162
+ const normalized = content.trim();
163
+ if (!normalized)
164
+ return;
165
+ this.lastToolResult = normalized;
166
+ this.renderer?.rememberToolResult(normalized, summary);
335
167
  }
336
- /**
337
- * Show success
338
- */
339
- showSuccess(message) {
340
- const output = `${theme.success('✓')} ${message}\n`;
341
- this.notifyBeforeOutput();
342
- if (!this.enqueueEvent('response', output)) {
343
- this.outputStream.write(output);
168
+ expandLastToolResult() {
169
+ if (this.renderer?.expandLastToolResult?.()) {
170
+ return;
344
171
  }
345
- this.notifyAfterOutput(output);
346
- }
347
- /**
348
- * Show progress badge
349
- */
350
- showProgressBadge(label, current, total) {
351
- const percentage = Math.round((current / total) * 100);
352
- const barWidth = 20;
353
- const filled = Math.round((current / total) * barWidth);
354
- const empty = barWidth - filled;
355
- const progressBar = `${'█'.repeat(filled)}${'░'.repeat(empty)}`;
356
- const badge = `[${label}] ${progressBar} ${percentage}%`;
357
- this.stream(`\r${theme.info(badge)}`);
358
- if (current >= total) {
359
- this.stream('\n');
172
+ if (this.lastToolResult) {
173
+ this.emit('tool-result', this.lastToolResult);
360
174
  }
361
175
  }
362
- /**
363
- * Show status line
364
- */
365
- showStatusLine(status, elapsedMs, _context) {
176
+ showStatusLine(status, elapsedMs) {
366
177
  const normalized = status?.trim();
367
178
  if (!normalized)
368
179
  return;
369
180
  const elapsed = this.formatElapsed(elapsedMs);
370
- const parts = [`• ${normalized}`];
181
+ const parts = [normalized];
371
182
  if (elapsed) {
372
183
  parts.push(elapsed);
373
184
  }
374
- if (process.stdout.isTTY) {
375
- parts.push('esc to interrupt');
376
- }
377
- // Status is kept internal; avoid forcing re-render of the overlay.
378
185
  this.renderer?.updateStatusBundle({ main: parts.join(' • ') }, { render: false });
379
186
  }
380
- /**
381
- * Show command palette
382
- */
383
- showCommandPalette(commands, options) {
384
- if (!commands || commands.length === 0)
385
- return;
386
- const panel = this.buildCommandPalette(commands, options);
387
- if (!panel.trim())
388
- return;
389
- const output = `\n${panel}\n\n`;
390
- this.notifyBeforeOutput();
391
- if (!this.enqueueEvent('response', output)) {
392
- this.outputStream.write(output);
393
- }
394
- this.notifyAfterOutput(output);
395
- }
396
- /**
397
- * Show planning step
398
- */
399
- showPlanningStep(step, index, total) {
400
- if (!step?.trim() || index < 1 || total < 1 || index > total)
401
- return;
402
- const width = Math.max(DISPLAY_CONSTANTS.MIN_THOUGHT_WIDTH, Math.min(this.getColumnWidth(), DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
403
- const heading = renderSectionHeading(`Plan ${index}/${total}`, {
404
- subtitle: step,
405
- icon: icons.arrow,
406
- tone: 'info',
407
- width,
408
- });
409
- this.enqueueEvent('raw', `${heading}\n`);
410
- }
411
- /**
412
- * Show thinking block
413
- */
414
- showThinkingBlock(content, durationMs) {
415
- const block = this.buildClaudeStyleThought(content, durationMs);
416
- this.enqueueEvent('raw', `\n${block}\n\n`);
417
- }
418
- /**
419
- * Clear screen
420
- */
421
187
  clear() {
422
- // Renderer handles this
423
- }
424
- /**
425
- * Update streaming status (routes to renderer)
426
- */
427
- updateStreamingStatus(status) {
428
- this.renderer?.updateStatus(status);
429
- }
430
- /**
431
- * Clear streaming status
432
- */
433
- clearStreamingStatus() {
434
- this.renderer?.updateStatus(null);
435
- }
436
- /**
437
- * Check if streaming status is visible
438
- */
439
- isStreamingStatusVisible() {
440
- return false; // Renderer manages this
188
+ // Renderer manages overlay cleanup; nothing to do here.
441
189
  }
442
- parallelAgentStatus(content) {
443
- if (!content)
444
- return;
445
- this.enqueueEvent('streaming', `${content}\n`);
446
- }
447
- // ==================== Private Helper Methods ====================
448
- getColumnWidth() {
449
- if (typeof this.outputStream.columns === 'number' &&
450
- Number.isFinite(this.outputStream.columns) &&
451
- this.outputStream.columns > 0) {
452
- return this.outputStream.columns;
453
- }
454
- return getTerminalColumns();
190
+ registerOutputInterceptor(_interceptor) {
191
+ return () => { };
455
192
  }
456
- formatErrorDetails(error) {
457
- if (!error)
458
- return null;
459
- if (error instanceof Error) {
460
- if (error.stack)
461
- return highlightError(error.stack);
462
- return highlightError(error.message);
463
- }
464
- if (typeof error === 'string') {
465
- return highlightError(error);
466
- }
193
+ maybeHandleInlinePanel(content) {
194
+ if (!this.inlinePanelHandler)
195
+ return false;
467
196
  try {
468
- return highlightError(JSON.stringify(error, null, 2));
197
+ return this.inlinePanelHandler(content) === true;
469
198
  }
470
199
  catch {
471
- return null;
200
+ return false;
472
201
  }
473
202
  }
474
203
  formatElapsed(elapsedMs) {
@@ -483,316 +212,6 @@ export class Display {
483
212
  }
484
213
  return `${seconds}s`;
485
214
  }
486
- buildChatBox(content, metadata) {
487
- const normalized = content.trim();
488
- if (!normalized)
489
- return '';
490
- if (isPlainOutputMode()) {
491
- const body = renderMessageBody(normalized, this.resolveMessageWidth());
492
- const telemetry = this.formatTelemetryLine(metadata);
493
- return telemetry ? `${body}\n${telemetry}` : body;
494
- }
495
- const width = this.resolveMessageWidth();
496
- const panel = renderMessagePanel(normalized, {
497
- width,
498
- title: 'Assistant',
499
- icon: icons.assistant,
500
- accentColor: theme.assistant ?? theme.primary,
501
- borderColor: theme.ui.border,
502
- });
503
- const telemetry = this.formatTelemetryLine(metadata);
504
- return telemetry ? `${panel}\n${telemetry}` : panel;
505
- }
506
- resolveMessageWidth() {
507
- const columns = this.getColumnWidth();
508
- return Math.max(DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, Math.min(columns - DISPLAY_CONSTANTS.MESSAGE_PADDING, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
509
- }
510
- formatTelemetryLine(metadata) {
511
- if (!metadata)
512
- return '';
513
- const parts = [];
514
- const elapsed = this.formatElapsed(metadata.elapsedMs);
515
- if (elapsed) {
516
- const elapsedLabel = theme.metrics?.elapsedLabel ?? theme.accent;
517
- const elapsedValue = theme.metrics?.elapsedValue ?? theme.secondary;
518
- parts.push(`${elapsedLabel('elapsed')} ${elapsedValue(elapsed)}`);
519
- }
520
- if (!parts.length)
521
- return '';
522
- const separator = theme.ui.muted(' • ');
523
- return ` ${parts.join(separator)}`;
524
- }
525
- buildClaudeStyleThought(content, durationMs) {
526
- const thinkingStyle = theme.thinking || {
527
- icon: theme.info,
528
- text: theme.ui.muted,
529
- border: theme.ui.border,
530
- label: theme.info,
531
- };
532
- const width = Math.min(this.getColumnWidth() - 4, 70);
533
- const lines = [];
534
- // Header
535
- if (durationMs !== undefined) {
536
- const elapsed = this.formatElapsedTime(Math.floor(durationMs / 1000));
537
- lines.push(`${theme.info('∴')} Thought for ${elapsed}`);
538
- }
539
- else {
540
- lines.push(`${theme.info('✻')} ${thinkingStyle.label('Thinking…')}`);
541
- }
542
- // Content
543
- const contentLines = content.split('\n');
544
- const hasContent = contentLines.some(line => line.trim().length > 0);
545
- if (hasContent) {
546
- lines.push('');
547
- }
548
- for (const line of contentLines) {
549
- const trimmed = line.replace(/\s+$/, '');
550
- if (!trimmed.trim()) {
551
- lines.push('');
552
- continue;
553
- }
554
- const wrapped = this.wrapLine(trimmed, width - 4);
555
- for (const wrappedLine of wrapped) {
556
- lines.push(` ${thinkingStyle.text(wrappedLine)}`);
557
- }
558
- }
559
- return lines.join('\n');
560
- }
561
- formatElapsedTime(seconds) {
562
- if (seconds < 60) {
563
- return `${seconds}s`;
564
- }
565
- const mins = Math.floor(seconds / 60);
566
- const secs = seconds % 60;
567
- return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
568
- }
569
- buildCommandPalette(commands, options) {
570
- if (!commands.length)
571
- return '';
572
- const width = Math.max(DISPLAY_CONSTANTS.MIN_MESSAGE_WIDTH, Math.min(this.getColumnWidth() - 2, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
573
- const indent = ' ';
574
- const grouped = this.groupPaletteCommands(commands);
575
- const commandWidth = this.computeCommandColumnWidth(commands, width, indent.length);
576
- const descWidth = Math.max(DISPLAY_CONSTANTS.MIN_WRAP_WIDTH, width - indent.length - commandWidth - 1);
577
- const title = options?.title ?? 'Slash commands';
578
- const intro = options?.intro ?? 'Describe a task or pick a command below:';
579
- const lines = [];
580
- lines.push(theme.gradient.primary(title));
581
- const introLines = this.wrapLine(intro, width);
582
- for (const line of introLines) {
583
- lines.push(theme.ui.muted(line));
584
- }
585
- lines.push('');
586
- grouped.forEach(({ category, items }, index) => {
587
- const label = this.formatPaletteCategory(category);
588
- if (label) {
589
- lines.push(theme.bold(label));
590
- }
591
- for (const item of items) {
592
- const wrappedDesc = this.wrapLine(item.description, descWidth);
593
- const paddedCommand = item.command.padEnd(commandWidth);
594
- const commandLabel = theme.primary(paddedCommand);
595
- const firstLine = wrappedDesc[0] ?? '';
596
- lines.push(`${indent}${commandLabel} ${this.colorizePaletteText(firstLine, item.tone)}`);
597
- for (const extra of wrappedDesc.slice(1)) {
598
- lines.push(`${indent}${' '.repeat(commandWidth)} ${this.colorizePaletteText(extra, item.tone)}`);
599
- }
600
- }
601
- if (index < grouped.length - 1) {
602
- lines.push('');
603
- }
604
- });
605
- return lines.join('\n').trimEnd();
606
- }
607
- groupPaletteCommands(commands) {
608
- const FALLBACK = 'other';
609
- const groups = new Map();
610
- for (const item of commands) {
611
- const key = (item.category ?? FALLBACK).toLowerCase();
612
- const bucket = groups.get(key) ?? [];
613
- bucket.push(item);
614
- groups.set(key, bucket);
615
- }
616
- const order = ['configuration', 'workspace', 'diagnostics', 'other'];
617
- const orderedKeys = [
618
- ...order.filter((key) => groups.has(key)),
619
- ...Array.from(groups.keys()).filter((key) => !order.includes(key)),
620
- ];
621
- return orderedKeys.map((category) => ({ category, items: groups.get(category) ?? [] }));
622
- }
623
- computeCommandColumnWidth(commands, totalWidth, indentWidth) {
624
- const longest = commands.reduce((max, item) => Math.max(max, this.visibleLength(item.command)), 0);
625
- const maxAllowed = Math.max(12, Math.min(longest + 2, Math.floor(totalWidth * 0.35)));
626
- const budget = Math.max(10, totalWidth - indentWidth - DISPLAY_CONSTANTS.MIN_WRAP_WIDTH);
627
- return Math.min(maxAllowed, budget);
628
- }
629
- formatPaletteCategory(category) {
630
- if (!category)
631
- return 'Other';
632
- switch (category.toLowerCase()) {
633
- case 'configuration': return 'Configuration';
634
- case 'workspace': return 'Workspace';
635
- case 'diagnostics': return 'Diagnostics';
636
- case 'other': return 'Other';
637
- default: return category[0]?.toUpperCase() + category.slice(1);
638
- }
639
- }
640
- colorizePaletteText(text, tone) {
641
- switch (tone) {
642
- case 'warn': return theme.warning(text);
643
- case 'success': return theme.success(text);
644
- case 'info': return theme.info(text);
645
- case 'muted':
646
- default: return theme.ui.muted(text);
647
- }
648
- }
649
- wrapWithPrefix(text, prefix, options) {
650
- if (!text) {
651
- return prefix.trimEnd();
652
- }
653
- const width = Math.max(DISPLAY_CONSTANTS.MIN_ACTION_WIDTH, Math.min(this.getColumnWidth(), DISPLAY_CONSTANTS.MAX_ACTION_WIDTH));
654
- const prefixWidth = this.visibleLength(prefix);
655
- const available = Math.max(DISPLAY_CONSTANTS.MIN_CONTENT_WIDTH, width - prefixWidth);
656
- const indent = typeof options?.continuationPrefix === 'string'
657
- ? options.continuationPrefix
658
- : ' '.repeat(Math.max(0, prefixWidth));
659
- const segments = text.split('\n');
660
- const lines = [];
661
- let usedPrefix = false;
662
- for (const segment of segments) {
663
- if (!segment.trim()) {
664
- if (usedPrefix) {
665
- lines.push(indent);
666
- }
667
- else {
668
- lines.push(prefix.trimEnd());
669
- usedPrefix = true;
670
- }
671
- continue;
672
- }
673
- const wrapped = this.wrapLine(segment.trim(), available);
674
- for (const line of wrapped) {
675
- lines.push(!usedPrefix ? `${prefix}${line}` : `${indent}${line}`);
676
- usedPrefix = true;
677
- }
678
- }
679
- return lines.join('\n');
680
- }
681
- buildWrappedSubActionLines(text, status) {
682
- const lines = text.split('\n').map((line) => line.trimEnd());
683
- while (lines.length && !lines[lines.length - 1]?.trim()) {
684
- lines.pop();
685
- }
686
- if (!lines.length)
687
- return [];
688
- const rendered = [];
689
- for (let index = 0; index < lines.length; index += 1) {
690
- const segment = lines[index] ?? '';
691
- const isLast = index === lines.length - 1;
692
- const { prefix, continuation } = this.buildSubActionPrefixes(status, isLast);
693
- rendered.push(this.wrapWithPrefix(segment, prefix, { continuationPrefix: continuation }));
694
- }
695
- return rendered;
696
- }
697
- buildSubActionPrefixes(status, isLast) {
698
- if (isLast) {
699
- const colorize = this.resolveStatusColor(status);
700
- return {
701
- prefix: ` ${colorize(icons.subaction)} `,
702
- continuation: ' ',
703
- };
704
- }
705
- const branch = theme.ui.muted('│');
706
- return {
707
- prefix: ` ${branch} `,
708
- continuation: ` ${branch} `,
709
- };
710
- }
711
- resolveStatusColor(status) {
712
- switch (status) {
713
- case 'success': return theme.success;
714
- case 'error': return theme.error;
715
- case 'warning': return theme.warning;
716
- case 'pending': return theme.info;
717
- default: return theme.secondary;
718
- }
719
- }
720
- formatActionIcon(status) {
721
- const colorize = this.resolveStatusColor(status);
722
- return colorize(`${icons.action}`);
723
- }
724
- wrapForRenderer(text) {
725
- const width = Math.max(DISPLAY_CONSTANTS.MIN_WRAP_WIDTH, Math.min(this.getColumnWidth() - 4, DISPLAY_CONSTANTS.MAX_MESSAGE_WIDTH));
726
- const lines = [];
727
- for (const line of text.split('\n')) {
728
- const trimmed = line.trimEnd();
729
- const wrapped = this.wrapLine(trimmed, width);
730
- lines.push(...wrapped);
731
- }
732
- return lines.join('\n').trimEnd();
733
- }
734
- wrapLine(text, width) {
735
- if (width <= 0)
736
- return [text];
737
- if (!text)
738
- return [''];
739
- if (text.length <= width)
740
- return [text];
741
- const words = text.split(/\s+/).filter(Boolean);
742
- if (!words.length)
743
- return this.chunkWord(text, width);
744
- const lines = [];
745
- let current = '';
746
- for (const word of words) {
747
- if (!current) {
748
- if (word.length <= width) {
749
- current = word;
750
- }
751
- else {
752
- const chunks = this.chunkWord(word, width);
753
- lines.push(...chunks.slice(0, -1));
754
- current = chunks[chunks.length - 1] ?? '';
755
- }
756
- }
757
- else if (current.length + 1 + word.length <= width) {
758
- current = `${current} ${word}`;
759
- }
760
- else {
761
- lines.push(current);
762
- if (word.length <= width) {
763
- current = word;
764
- }
765
- else {
766
- const chunks = this.chunkWord(word, width);
767
- lines.push(...chunks.slice(0, -1));
768
- current = chunks[chunks.length - 1] ?? '';
769
- }
770
- }
771
- }
772
- if (current) {
773
- lines.push(current);
774
- }
775
- return lines.length ? lines : [''];
776
- }
777
- chunkWord(word, width) {
778
- if (width <= 0 || !word)
779
- return word ? [word] : [''];
780
- const chunks = [];
781
- for (let i = 0; i < word.length; i += width) {
782
- chunks.push(word.slice(i, i + width));
783
- }
784
- return chunks.length > 0 ? chunks : [''];
785
- }
786
- visibleLength(value) {
787
- if (!value)
788
- return 0;
789
- return this.stripAnsi(value).length;
790
- }
791
- stripAnsi(value) {
792
- if (!value)
793
- return '';
794
- return value.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, '');
795
- }
796
215
  }
797
216
  export const display = new Display();
798
217
  //# sourceMappingURL=display.js.map