erosolar-cli 1.7.410 → 1.7.411

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 (205) hide show
  1. package/README.md +6 -6
  2. package/dist/StringUtils.d.ts +1 -4
  3. package/dist/StringUtils.d.ts.map +1 -1
  4. package/dist/StringUtils.js +2 -8
  5. package/dist/StringUtils.js.map +1 -1
  6. package/dist/browser/BrowserSessionManager.d.ts +1 -3
  7. package/dist/browser/BrowserSessionManager.d.ts.map +1 -1
  8. package/dist/browser/BrowserSessionManager.js +4 -24
  9. package/dist/browser/BrowserSessionManager.js.map +1 -1
  10. package/dist/capabilities/askUserCapability.d.ts.map +1 -1
  11. package/dist/capabilities/askUserCapability.js +64 -10
  12. package/dist/capabilities/askUserCapability.js.map +1 -1
  13. package/dist/capabilities/toolRegistry.d.ts +2 -0
  14. package/dist/capabilities/toolRegistry.d.ts.map +1 -1
  15. package/dist/capabilities/toolRegistry.js +40 -5
  16. package/dist/capabilities/toolRegistry.js.map +1 -1
  17. package/dist/contracts/agent-profiles.schema.json +5 -5
  18. package/dist/contracts/agent-schemas.json +6 -16
  19. package/dist/contracts/schemas/agent.schema.json +1 -5
  20. package/dist/contracts/schemas/tool-selection.schema.json +1 -7
  21. package/dist/contracts/tools.schema.json +80 -207
  22. package/dist/contracts/unified-schema.json +4 -5
  23. package/dist/contracts/v1/agent.d.ts +0 -3
  24. package/dist/contracts/v1/agent.d.ts.map +1 -1
  25. package/dist/contracts/v1/provider.d.ts +1 -2
  26. package/dist/contracts/v1/provider.d.ts.map +1 -1
  27. package/dist/contracts/v1/toolAccess.d.ts +1 -1
  28. package/dist/contracts/v1/toolAccess.d.ts.map +1 -1
  29. package/dist/core/agent.d.ts +1 -7
  30. package/dist/core/agent.d.ts.map +1 -1
  31. package/dist/core/agent.js +2 -131
  32. package/dist/core/agent.js.map +1 -1
  33. package/dist/core/alphaZeroEngine.d.ts +0 -8
  34. package/dist/core/alphaZeroEngine.d.ts.map +1 -1
  35. package/dist/core/alphaZeroEngine.js +35 -149
  36. package/dist/core/alphaZeroEngine.js.map +1 -1
  37. package/dist/core/alphaZeroOrchestrator.d.ts +0 -17
  38. package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -1
  39. package/dist/core/alphaZeroOrchestrator.js +8 -95
  40. package/dist/core/alphaZeroOrchestrator.js.map +1 -1
  41. package/dist/core/claudeCodeFeatures.d.ts +2 -1
  42. package/dist/core/claudeCodeFeatures.d.ts.map +1 -1
  43. package/dist/core/claudeCodeFeatures.js +2 -1
  44. package/dist/core/claudeCodeFeatures.js.map +1 -1
  45. package/dist/core/cliTestHarness.d.ts +0 -5
  46. package/dist/core/cliTestHarness.d.ts.map +1 -1
  47. package/dist/core/cliTestHarness.js +3 -14
  48. package/dist/core/cliTestHarness.js.map +1 -1
  49. package/dist/core/contextManager.d.ts +0 -30
  50. package/dist/core/contextManager.d.ts.map +1 -1
  51. package/dist/core/contextManager.js +5 -87
  52. package/dist/core/contextManager.js.map +1 -1
  53. package/dist/core/contextWindow.d.ts +4 -4
  54. package/dist/core/contextWindow.js +9 -9
  55. package/dist/core/contextWindow.js.map +1 -1
  56. package/dist/core/modelDiscovery.js +3 -3
  57. package/dist/core/modelDiscovery.js.map +1 -1
  58. package/dist/core/preferences.d.ts +2 -3
  59. package/dist/core/preferences.d.ts.map +1 -1
  60. package/dist/core/preferences.js +11 -18
  61. package/dist/core/preferences.js.map +1 -1
  62. package/dist/core/secretStore.d.ts.map +1 -1
  63. package/dist/core/secretStore.js +31 -0
  64. package/dist/core/secretStore.js.map +1 -1
  65. package/dist/core/toolPreconditions.d.ts.map +1 -1
  66. package/dist/core/toolPreconditions.js +0 -60
  67. package/dist/core/toolPreconditions.js.map +1 -1
  68. package/dist/core/toolRuntime.d.ts.map +1 -1
  69. package/dist/core/toolRuntime.js +0 -17
  70. package/dist/core/toolRuntime.js.map +1 -1
  71. package/dist/core/types.d.ts +1 -1
  72. package/dist/core/types.d.ts.map +1 -1
  73. package/dist/headless/headlessApp.d.ts.map +1 -1
  74. package/dist/headless/headlessApp.js +6 -22
  75. package/dist/headless/headlessApp.js.map +1 -1
  76. package/dist/plugins/providers/google/index.js +3 -2
  77. package/dist/plugins/providers/google/index.js.map +1 -1
  78. package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
  79. package/dist/providers/openaiChatCompletionsProvider.js +6 -60
  80. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
  81. package/dist/runtime/agentController.d.ts.map +1 -1
  82. package/dist/runtime/agentController.js +6 -27
  83. package/dist/runtime/agentController.js.map +1 -1
  84. package/dist/shell/interactiveShell.d.ts +30 -79
  85. package/dist/shell/interactiveShell.d.ts.map +1 -1
  86. package/dist/shell/interactiveShell.js +726 -1511
  87. package/dist/shell/interactiveShell.js.map +1 -1
  88. package/dist/shell/shellApp.d.ts.map +1 -1
  89. package/dist/shell/shellApp.js +41 -15
  90. package/dist/shell/shellApp.js.map +1 -1
  91. package/dist/shell/systemPrompt.d.ts.map +1 -1
  92. package/dist/shell/systemPrompt.js +0 -1
  93. package/dist/shell/systemPrompt.js.map +1 -1
  94. package/dist/shell/terminalInput.d.ts +21 -85
  95. package/dist/shell/terminalInput.d.ts.map +1 -1
  96. package/dist/shell/terminalInput.js +60 -517
  97. package/dist/shell/terminalInput.js.map +1 -1
  98. package/dist/shell/terminalInputAdapter.d.ts +16 -37
  99. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  100. package/dist/shell/terminalInputAdapter.js +22 -44
  101. package/dist/shell/terminalInputAdapter.js.map +1 -1
  102. package/dist/shell/updateManager.d.ts.map +1 -1
  103. package/dist/shell/updateManager.js +17 -1
  104. package/dist/shell/updateManager.js.map +1 -1
  105. package/dist/subagents/parallelAgentManager.d.ts.map +1 -1
  106. package/dist/subagents/parallelAgentManager.js +2 -1
  107. package/dist/subagents/parallelAgentManager.js.map +1 -1
  108. package/dist/tools/buildTools.d.ts.map +1 -1
  109. package/dist/tools/buildTools.js +76 -19
  110. package/dist/tools/buildTools.js.map +1 -1
  111. package/dist/tools/editTools.js +1 -1
  112. package/dist/tools/editTools.js.map +1 -1
  113. package/dist/tools/enhancedCodeIntelligenceTools.js +2 -1
  114. package/dist/tools/enhancedCodeIntelligenceTools.js.map +1 -1
  115. package/dist/tools/fileTools.js +0 -3
  116. package/dist/tools/fileTools.js.map +1 -1
  117. package/dist/tools/frontendTestingTools.js +1 -1
  118. package/dist/tools/frontendTestingTools.js.map +1 -1
  119. package/dist/tools/interactionTools.d.ts.map +1 -1
  120. package/dist/tools/interactionTools.js +82 -15
  121. package/dist/tools/interactionTools.js.map +1 -1
  122. package/dist/tools/learnTools.d.ts +0 -2
  123. package/dist/tools/learnTools.d.ts.map +1 -1
  124. package/dist/tools/learnTools.js +81 -29
  125. package/dist/tools/learnTools.js.map +1 -1
  126. package/dist/tools/localExplore.d.ts.map +1 -1
  127. package/dist/tools/localExplore.js +1 -0
  128. package/dist/tools/localExplore.js.map +1 -1
  129. package/dist/tools/notebookEditTools.js.map +1 -1
  130. package/dist/tools/repoChecksTools.js +3 -4
  131. package/dist/tools/repoChecksTools.js.map +1 -1
  132. package/dist/tools/searchTools.js +0 -4
  133. package/dist/tools/searchTools.js.map +1 -1
  134. package/dist/tools/softwareEngineeringTools.d.ts.map +1 -1
  135. package/dist/tools/softwareEngineeringTools.js +0 -1
  136. package/dist/tools/softwareEngineeringTools.js.map +1 -1
  137. package/dist/tools/webTools.d.ts.map +1 -1
  138. package/dist/tools/webTools.js.map +1 -1
  139. package/dist/ui/ShellUIAdapter.d.ts +17 -54
  140. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  141. package/dist/ui/ShellUIAdapter.js +90 -378
  142. package/dist/ui/ShellUIAdapter.js.map +1 -1
  143. package/dist/ui/display.d.ts +38 -19
  144. package/dist/ui/display.d.ts.map +1 -1
  145. package/dist/ui/display.js +311 -96
  146. package/dist/ui/display.js.map +1 -1
  147. package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
  148. package/dist/ui/orchestration/UIUpdateCoordinator.js +3 -5
  149. package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
  150. package/dist/ui/shortcutsHelp.d.ts +11 -1
  151. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  152. package/dist/ui/shortcutsHelp.js +54 -8
  153. package/dist/ui/shortcutsHelp.js.map +1 -1
  154. package/dist/ui/streamingFormatter.d.ts +16 -0
  155. package/dist/ui/streamingFormatter.d.ts.map +1 -0
  156. package/dist/ui/streamingFormatter.js +62 -0
  157. package/dist/ui/streamingFormatter.js.map +1 -0
  158. package/dist/ui/theme.d.ts +100 -100
  159. package/dist/ui/theme.d.ts.map +1 -1
  160. package/dist/ui/theme.js.map +1 -1
  161. package/dist/ui/toolDisplay.d.ts +3 -3
  162. package/dist/ui/toolDisplay.d.ts.map +1 -1
  163. package/dist/ui/toolDisplay.js +8 -7
  164. package/dist/ui/toolDisplay.js.map +1 -1
  165. package/dist/ui/unified/index.d.ts +3 -2
  166. package/dist/ui/unified/index.d.ts.map +1 -1
  167. package/dist/ui/unified/index.js +1 -0
  168. package/dist/ui/unified/index.js.map +1 -1
  169. package/dist/ui/unified/layout.d.ts +23 -0
  170. package/dist/ui/unified/layout.d.ts.map +1 -1
  171. package/dist/ui/unified/layout.js +113 -11
  172. package/dist/ui/unified/layout.js.map +1 -1
  173. package/package.json +24 -37
  174. package/dist/core/alphaZeroConfig.d.ts +0 -11
  175. package/dist/core/alphaZeroConfig.d.ts.map +0 -1
  176. package/dist/core/alphaZeroConfig.js +0 -59
  177. package/dist/core/alphaZeroConfig.js.map +0 -1
  178. package/dist/core/alphaZeroEnhanced.d.ts +0 -125
  179. package/dist/core/alphaZeroEnhanced.d.ts.map +0 -1
  180. package/dist/core/alphaZeroEnhanced.js +0 -386
  181. package/dist/core/alphaZeroEnhanced.js.map +0 -1
  182. package/dist/core/autonomousVerification.d.ts +0 -103
  183. package/dist/core/autonomousVerification.d.ts.map +0 -1
  184. package/dist/core/autonomousVerification.js +0 -583
  185. package/dist/core/autonomousVerification.js.map +0 -1
  186. package/dist/core/offsecAlphaZeroEnhanced.d.ts +0 -98
  187. package/dist/core/offsecAlphaZeroEnhanced.d.ts.map +0 -1
  188. package/dist/core/offsecAlphaZeroEnhanced.js +0 -441
  189. package/dist/core/offsecAlphaZeroEnhanced.js.map +0 -1
  190. package/dist/core/parallelAgentOrchestrator.d.ts +0 -171
  191. package/dist/core/parallelAgentOrchestrator.d.ts.map +0 -1
  192. package/dist/core/parallelAgentOrchestrator.js +0 -459
  193. package/dist/core/parallelAgentOrchestrator.js.map +0 -1
  194. package/dist/index.d.ts +0 -5
  195. package/dist/index.d.ts.map +0 -1
  196. package/dist/index.js +0 -3
  197. package/dist/index.js.map +0 -1
  198. package/dist/tools/detectCommands.d.ts +0 -8
  199. package/dist/tools/detectCommands.d.ts.map +0 -1
  200. package/dist/tools/detectCommands.js +0 -183
  201. package/dist/tools/detectCommands.js.map +0 -1
  202. package/dist/ui/assistantBlockRenderer.d.ts +0 -30
  203. package/dist/ui/assistantBlockRenderer.d.ts.map +0 -1
  204. package/dist/ui/assistantBlockRenderer.js +0 -121
  205. package/dist/ui/assistantBlockRenderer.js.map +0 -1
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Advanced UI Features:
6
6
  * - Compact same-line tool displays
7
- * - Streaming progress updates (no redraw)
7
+ * - In-place progress updates
8
8
  * - Grouped operation summaries
9
9
  * - Dynamic status badges
10
10
  */
@@ -14,6 +14,8 @@ import { getTerminalColumns } from './layout.js';
14
14
  import { theme, icons } from './theme.js';
15
15
  import { UIUpdateCoordinator } from './orchestration/UIUpdateCoordinator.js';
16
16
  import { compactRenderer } from './compactRenderer.js';
17
+ import { inPlaceUpdater } from './inPlaceUpdater.js';
18
+ import { formatTip } from './shortcutsHelp.js';
17
19
  export class ShellUIAdapter {
18
20
  uiController;
19
21
  display;
@@ -30,8 +32,9 @@ export class ShellUIAdapter {
30
32
  maxCompletedOps = 8;
31
33
  compactDisplayMode = true; // Enable compact same-line displays
32
34
  toolWarnings = new Map();
33
- recentWarnings = [];
34
- toolProgressState = new Map();
35
+ // Tips display tracking - show tips periodically (Claude Code style)
36
+ toolResultCount = 0;
37
+ tipInterval = 5; // Show tip every N tool results
35
38
  constructor(writeStream, display, config = {}, updateCoordinator) {
36
39
  this.display = display;
37
40
  this.config = {
@@ -106,7 +109,7 @@ export class ShellUIAdapter {
106
109
  // Update status bar to show active tool
107
110
  if (this._toolStatusCallback) {
108
111
  const actionDesc = this.getToolActionDescription(call);
109
- this._toolStatusCallback({ text: actionDesc, stage: 'start' });
112
+ this._toolStatusCallback(actionDesc);
110
113
  }
111
114
  this.uiController.onToolStart(call);
112
115
  },
@@ -121,34 +124,27 @@ export class ShellUIAdapter {
121
124
  total: progress.total ?? progress.current,
122
125
  message: progress.message,
123
126
  });
124
- // Update status bar with progress
127
+ // Update status bar with progress - use in-place update for supported terminals
125
128
  if (this._toolStatusCallback) {
126
129
  const description = this.getToolActionDescription(call, progress);
127
130
  if (description) {
128
- this._toolStatusCallback({ text: description, stage: 'progress' });
131
+ this._toolStatusCallback(description);
129
132
  }
130
133
  }
131
- // Stream progress updates instead of in-place redraws
132
- if (progress.total && progress.total > 1) {
133
- const progressLine = this.formatStreamingProgress(call, progress);
134
- this.streamProgressUpdate(call.id, progressLine);
135
- }
136
- else if (progress.message) {
137
- this.streamProgressUpdate(call.id, `${theme.info(icons.action)} ${theme.tool(call.name)} ${progress.message}`);
134
+ // Show progress bar for file operations and long-running tools
135
+ if (inPlaceUpdater.hasTTY && progress.total && progress.total > 1) {
136
+ inPlaceUpdater.showToolProgress(call.name, progress.message || '', { current: progress.current, total: progress.total });
138
137
  }
139
138
  },
140
139
  onToolWarning: (call, warning) => {
141
140
  const toolName = theme.tool(call.name);
142
141
  const warningText = this.formatToolWarning(warning);
143
142
  const line = `${theme.warning(icons.warning)} ${toolName} ${theme.ui.muted('preflight')} ${warningText}`;
144
- this.recordToolWarning(call.id, call.name, warningText);
143
+ this.recordToolWarning(call.id, warningText);
145
144
  // Use display.stream() to work with scroll regions
146
145
  this.display.stream(`\n${line}\n`);
147
146
  if (this._toolStatusCallback) {
148
- this._toolStatusCallback({
149
- text: `Warning: ${this.getToolActionDescription(call)}`,
150
- stage: 'progress',
151
- });
147
+ this._toolStatusCallback(`Warning: ${this.getToolActionDescription(call)}`);
152
148
  }
153
149
  },
154
150
  onToolResult: (call, output) => {
@@ -172,21 +168,26 @@ export class ShellUIAdapter {
172
168
  this.fileChangeCallback(fileChange.path, fileChange.type, fileChange.additions, fileChange.removals);
173
169
  }
174
170
  }
175
- // Render concise tool result + preview instead of verbose blocks
176
- this.displayToolResultSummary(call, output, true);
171
+ // Use compact display for quick single-result tools
172
+ if (this.compactDisplayMode && this.isCompactTool(call.name)) {
173
+ this.displayCompactResult(call, output, true);
174
+ }
175
+ else {
176
+ // Claude Code style: Display tool result with ⎿ prefix
177
+ this.displayToolResultSummary(call, output, true);
178
+ }
177
179
  // Surface any captured preflight warnings in a structured block
178
180
  this.flushToolWarnings(call.id, call.name);
181
+ // Show tip periodically (Claude Code style)
182
+ this.toolResultCount++;
183
+ if (this.toolResultCount % this.tipInterval === 0) {
184
+ this.display.stream(`${formatTip()}\n`);
185
+ }
179
186
  // Clear status bar after showing result
180
187
  if (this._toolStatusCallback) {
181
- const completed = this.getToolActionDescription(call);
182
- this._toolStatusCallback({
183
- text: completed ? `Completed ${completed}` : 'Tool complete',
184
- stage: 'result',
185
- });
186
- this._toolStatusCallback({ text: null, stage: 'complete' });
188
+ this._toolStatusCallback(null);
187
189
  }
188
190
  this.uiController.onToolComplete(call.id, output);
189
- this.toolProgressState.delete(call.id);
190
191
  },
191
192
  onToolError: (call, message) => {
192
193
  // Complete the operation tracking with error
@@ -198,20 +199,20 @@ export class ShellUIAdapter {
198
199
  this.pendingOperations.delete(call.id);
199
200
  this.completedOperations.push(op);
200
201
  }
201
- // Render concise error summary + preview instead of verbose blocks
202
- this.displayToolResultSummary(call, message, false);
202
+ // Use compact display for errors too
203
+ if (this.compactDisplayMode && this.isCompactTool(call.name)) {
204
+ this.displayCompactResult(call, message, false);
205
+ }
206
+ else {
207
+ // Claude Code style: Display error with ⎿ prefix (error color)
208
+ this.displayToolResultSummary(call, message, false);
209
+ }
203
210
  this.flushToolWarnings(call.id, call.name);
204
211
  // Clear status bar after showing error
205
212
  if (this._toolStatusCallback) {
206
- const failed = this.getToolActionDescription(call);
207
- this._toolStatusCallback({
208
- text: failed ? `Failed ${failed}` : 'Tool failed',
209
- stage: 'error',
210
- });
211
- this._toolStatusCallback({ text: null, stage: 'complete' });
213
+ this._toolStatusCallback(null);
212
214
  }
213
215
  this.uiController.onToolError(call.id, { message });
214
- this.toolProgressState.delete(call.id);
215
216
  },
216
217
  onCacheHit: (call) => {
217
218
  // Track as cached operation
@@ -230,21 +231,24 @@ export class ShellUIAdapter {
230
231
  const badge = compactRenderer.formatToolBadge(operation);
231
232
  this.display.stream(`${badge}\n`);
232
233
  }
233
- if (this._toolStatusCallback) {
234
- const cached = this.getToolActionDescription(call);
235
- this._toolStatusCallback({
236
- text: cached ? `Cached ${cached}` : 'Tool cache hit',
237
- stage: 'result',
238
- });
239
- this._toolStatusCallback({ text: null, stage: 'complete' });
240
- }
241
234
  this.flushToolWarnings(call.id, call.name);
242
235
  this.uiController.onToolStart(call);
243
236
  this.uiController.onToolComplete(call.id, 'cache-hit');
244
- this.toolProgressState.delete(call.id);
245
237
  },
246
238
  };
247
239
  }
240
+ /**
241
+ * Check if a tool should use compact (single-line) display
242
+ */
243
+ isCompactTool(toolName) {
244
+ const compactTools = new Set([
245
+ 'Read', 'read_file',
246
+ 'Glob', 'glob',
247
+ 'TodoWrite', 'todo_write',
248
+ 'WebFetch', 'web_fetch',
249
+ ]);
250
+ return compactTools.has(toolName);
251
+ }
248
252
  /**
249
253
  * Normalize preflight warnings for consistent display.
250
254
  */
@@ -259,15 +263,10 @@ export class ShellUIAdapter {
259
263
  /**
260
264
  * Track warnings for a tool call so we can show them after the result too.
261
265
  */
262
- recordToolWarning(callId, toolName, warningText) {
266
+ recordToolWarning(callId, warningText) {
263
267
  const existing = this.toolWarnings.get(callId) ?? [];
264
268
  existing.push(warningText);
265
269
  this.toolWarnings.set(callId, existing);
266
- this.recentWarnings.push({ tool: toolName, text: warningText, time: Date.now() });
267
- // Keep recent warnings list compact
268
- if (this.recentWarnings.length > 20) {
269
- this.recentWarnings.shift();
270
- }
271
270
  }
272
271
  /**
273
272
  * Render any captured warnings for the given tool call as a structured block.
@@ -283,42 +282,6 @@ export class ShellUIAdapter {
283
282
  const body = warnings.map(text => ` ${bullet} ${text}`).join('\n');
284
283
  this.display.stream(`\n${header}\n${body}\n`);
285
284
  }
286
- /**
287
- * Render progress updates as streaming lines rather than in-place redraws.
288
- */
289
- formatStreamingProgress(call, progress) {
290
- const pct = progress.total ? Math.round((progress.current / progress.total) * 100) : null;
291
- const parts = [];
292
- parts.push(theme.info(icons.action));
293
- parts.push(theme.tool(call.name));
294
- if (pct !== null && Number.isFinite(pct)) {
295
- parts.push(theme.progress.percentage(`${pct}%`));
296
- parts.push(theme.ui.muted(`(${progress.current}/${progress.total})`));
297
- }
298
- if (progress.message) {
299
- parts.push(progress.message);
300
- }
301
- return parts.join(' ');
302
- }
303
- /**
304
- * Only stream distinct progress updates to avoid noisy duplicates.
305
- */
306
- streamProgressUpdate(callId, line) {
307
- const previous = this.toolProgressState.get(callId);
308
- if (previous === line) {
309
- return;
310
- }
311
- this.toolProgressState.set(callId, line);
312
- this.display.stream(`${line}\n`);
313
- }
314
- /**
315
- * Return preflight warnings captured since a timestamp.
316
- */
317
- getRecentWarnings(since) {
318
- return this.recentWarnings
319
- .filter((entry) => entry.time >= since)
320
- .map((entry) => ({ tool: entry.tool, text: entry.text }));
321
- }
322
285
  /**
323
286
  * Get a short summary for tool operation
324
287
  */
@@ -418,6 +381,21 @@ export class ShellUIAdapter {
418
381
  return 'done';
419
382
  }
420
383
  }
384
+ /**
385
+ * Display compact single-line result
386
+ */
387
+ displayCompactResult(call, output, success) {
388
+ const op = {
389
+ id: call.id,
390
+ name: call.name,
391
+ status: success ? 'success' : 'error',
392
+ summary: this.extractResultSummary(call, output),
393
+ startedAt: Date.now(),
394
+ };
395
+ const badge = compactRenderer.formatToolBadge(op);
396
+ // Add newline after result for visual separation
397
+ this.display.stream(`${badge}\n\n`);
398
+ }
421
399
  /**
422
400
  * Determine if a tool should show initial ⏺ display
423
401
  * Returns true for tools with streaming/progressive output
@@ -852,19 +830,6 @@ export class ShellUIAdapter {
852
830
  setToolStatusCallback(callback) {
853
831
  this._toolStatusCallback = callback;
854
832
  }
855
- /**
856
- * Expose a snapshot of tool operations for external summary displays.
857
- * Returns both completed and in-flight operations, optionally filtered by start time.
858
- */
859
- getToolOperationsSince(sinceMs) {
860
- const cutoff = sinceMs ?? 0;
861
- const completed = this.completedOperations.map(op => ({ ...op }));
862
- const pending = Array.from(this.pendingOperations.values()).map(op => ({ ...op }));
863
- const all = [...completed, ...pending];
864
- return all
865
- .filter(op => op.startedAt >= cutoff)
866
- .sort((a, b) => (a.startedAt || 0) - (b.startedAt || 0));
867
- }
868
833
  /**
869
834
  * Parse file changes from Edit/Write/NotebookEdit tool output
870
835
  */
@@ -909,283 +874,29 @@ export class ShellUIAdapter {
909
874
  };
910
875
  }
911
876
  /**
912
- * Display tool result summary in a compact, badge-driven line with an optional preview block.
913
- * Routes through display module to work with scroll regions while keeping output minimal.
877
+ * Display tool result summary using Claude Code style.
878
+ * Format: ⎿ Summary text (ctrl+o to expand)
879
+ * Routes through display module to work with scroll regions
914
880
  */
915
881
  displayToolResultSummary(call, output, success) {
916
- const summaryLine = this.formatCompactToolLine(call, output, success);
917
- const preview = this.formatToolPreview(call, output, success);
918
- if (!summaryLine && !preview) {
919
- return;
920
- }
921
- const blocks = [];
922
- if (summaryLine)
923
- blocks.push(summaryLine);
924
- if (preview)
925
- blocks.push(preview);
926
- // Add newline for separation while keeping output compact
927
- this.display.stream(`\n${blocks.join('\n')}\n\n`);
928
- }
929
- /**
930
- * Display full tool output directly under the summary block.
931
- * Now uses the same compact preview system to keep verbose tools readable.
932
- */
933
- displayFullToolOutput(call, output, success) {
934
- const normalized = typeof output === 'string' ? output.trimEnd() : '';
935
- if (!normalized) {
936
- return;
937
- }
938
- // Use the same preview builder to keep verbose output compact
939
- const preview = this.formatToolPreview(call, normalized, success, true);
940
- const summary = this.formatCompactToolLine(call, normalized, success);
941
- const parts = [];
942
- if (summary)
943
- parts.push(summary);
944
- if (preview)
945
- parts.push(preview);
946
- if (parts.length) {
947
- this.display.stream(`\n${parts.join('\n')}\n\n`);
948
- }
949
- }
950
- /**
951
- * Format tool result summary in Claude Code style
952
- * Returns compact summary like "Found 4 files" or "Read 50 lines"
953
- */
954
- formatCompactToolLine(call, output, success) {
955
- const { main, detail } = this.buildToolSummaryParts(call, output, success);
956
- const summary = main || this.getToolSummary(call) || call.name;
957
- return compactRenderer.formatInlineResult(call.name, success ? 'success' : 'error', summary, detail);
958
- }
959
- /**
960
- * Build compact summary + detail parts for a tool result line.
961
- * Keeps the main text meaningful (path/query) and pushes counts into detail.
962
- */
963
- buildToolSummaryParts(call, output, success) {
964
882
  const args = call.arguments || {};
965
- const lineCount = this.countOutputLines(output);
966
- switch (call.name) {
967
- case 'Read':
968
- case 'read_file': {
969
- const path = this.extractPath(args, ['file_path', 'path']);
970
- return {
971
- main: path ? theme.info(this.truncatePath(path, 60)) : 'Read',
972
- detail: `${lineCount} line${lineCount === 1 ? '' : 's'}`,
973
- };
974
- }
975
- case 'Write':
976
- case 'write_file': {
977
- const path = this.extractPath(args, ['file_path', 'path']);
978
- const content = args['content'] || output;
979
- const lines = this.countOutputLines(content);
980
- return {
981
- main: path ? theme.info(this.truncatePath(path, 60)) : 'Write',
982
- detail: `${lines} line${lines === 1 ? '' : 's'}`,
983
- };
984
- }
985
- case 'Edit':
986
- case 'edit_file':
987
- case 'NotebookEdit': {
988
- const change = this.parseFileChange(call, output);
989
- const path = change?.path || this.extractPath(args, ['file_path', 'path', 'notebook_path']);
990
- const detailParts = [];
991
- if (change) {
992
- detailParts.push(`+${change.additions}/-${change.removals}`);
993
- }
994
- else if (lineCount > 0) {
995
- detailParts.push(`${lineCount} line${lineCount === 1 ? '' : 's'}`);
996
- }
997
- const action = call.name === 'NotebookEdit' ? 'Notebook' : 'Edit';
998
- return {
999
- main: path ? `${action} ${theme.info(this.truncatePath(path, 60))}` : action,
1000
- detail: detailParts.join(' · ') || undefined,
1001
- };
1002
- }
1003
- case 'Grep':
1004
- case 'grep': {
1005
- const pattern = args['pattern'] || '';
1006
- const path = this.extractPath(args, ['path', 'directory']);
1007
- const matches = output.trim() ? output.trim().split('\n').filter(l => l.trim()).length : 0;
1008
- const detailParts = [`${matches} match${matches === 1 ? '' : 'es'}`];
1009
- if (path && path !== '.') {
1010
- detailParts.push(`in ${this.truncatePath(path, 40)}`);
1011
- }
1012
- return {
1013
- main: pattern ? `"${pattern.length > 40 ? `${pattern.slice(0, 37)}...` : pattern}"` : 'Grep',
1014
- detail: detailParts.join(' · '),
1015
- };
1016
- }
1017
- case 'Glob':
1018
- case 'glob': {
1019
- const pattern = args['pattern'] || '*';
1020
- const files = output.trim() ? output.trim().split('\n').filter(f => f.trim()).length : 0;
1021
- const path = this.extractPath(args, ['path', 'directory']);
1022
- const detailParts = [`${files} file${files === 1 ? '' : 's'}`];
1023
- if (path && path !== '.') {
1024
- detailParts.push(`in ${this.truncatePath(path, 40)}`);
1025
- }
1026
- return {
1027
- main: pattern ? theme.info(pattern) : 'Glob',
1028
- detail: detailParts.join(' · '),
1029
- };
1030
- }
1031
- case 'Bash':
1032
- case 'bash': {
1033
- const command = args['command'] || '';
1034
- const shortCmd = command.split('\n')[0]?.trim() || command;
1035
- const main = shortCmd
1036
- ? `${theme.ui.muted('$')} ${shortCmd.length > 70 ? `${shortCmd.slice(0, 67)}...` : shortCmd}`
1037
- : 'Shell';
1038
- const detail = lineCount === 0 ? 'no output' : `${lineCount} line${lineCount === 1 ? '' : 's'}`;
1039
- return { main, detail };
1040
- }
1041
- case 'WebFetch':
1042
- case 'web_fetch': {
1043
- const url = args['url'] || '';
1044
- let host = url;
1045
- try {
1046
- host = new URL(url).hostname;
1047
- }
1048
- catch {
1049
- // Keep original
1050
- }
1051
- const sizeStr = output.length > 1024 ? `${(output.length / 1024).toFixed(1)}KB` : `${output.length}B`;
1052
- return { main: host ? `Fetch ${theme.info(host)}` : 'Fetch', detail: sizeStr };
1053
- }
1054
- case 'WebSearch':
1055
- case 'web_search': {
1056
- const query = args['query'] || '';
1057
- const queryDisplay = query.length > 50 ? `${query.slice(0, 47)}...` : query;
1058
- const resultMatches = output.match(/^\d+\./gm);
1059
- const count = resultMatches ? resultMatches.length : this.countOutputLines(output);
1060
- return { main: queryDisplay ? `Search "${queryDisplay}"` : 'Search', detail: `${count} result${count === 1 ? '' : 's'}` };
1061
- }
1062
- case 'Task':
1063
- case 'task': {
1064
- const desc = args['description'] || '';
1065
- const trimmed = desc.length > 60 ? `${desc.slice(0, 57)}...` : desc;
1066
- return { main: trimmed || 'Task', detail: success ? 'done' : 'incomplete' };
1067
- }
1068
- case 'TodoWrite':
1069
- case 'todo_write': {
1070
- const todos = args['todos'];
1071
- const total = todos?.length || 0;
1072
- return { main: 'Todos', detail: total ? `${total} item${total === 1 ? '' : 's'}` : undefined };
1073
- }
1074
- case 'run_tests': {
1075
- const pattern = (args['pattern'] || args['filter']);
1076
- const counts = this.parseTestCounts(output);
1077
- const detailParts = [];
1078
- if (counts.passed > 0)
1079
- detailParts.push(`${counts.passed} pass`);
1080
- if (counts.failed > 0)
1081
- detailParts.push(`${counts.failed} fail`);
1082
- if (counts.skipped > 0)
1083
- detailParts.push(`${counts.skipped} skip`);
1084
- if (detailParts.length === 0 && lineCount > 0) {
1085
- detailParts.push(`${lineCount} line${lineCount === 1 ? '' : 's'}`);
1086
- }
1087
- return {
1088
- main: pattern ? `Tests "${pattern}"` : 'Tests',
1089
- detail: detailParts.join(' · ') || (success ? 'all passed' : 'failed'),
1090
- };
1091
- }
1092
- case 'run_build': {
1093
- const target = (args['target'] || args['script']);
1094
- const errors = (output.match(/error/gi) || []).length;
1095
- const detail = errors > 0
1096
- ? `${errors} error${errors === 1 ? '' : 's'}`
1097
- : `${lineCount} line${lineCount === 1 ? '' : 's'}`;
1098
- return { main: target ? `Build ${target}` : 'Build', detail };
1099
- }
1100
- default: {
1101
- const summary = this.extractResultSummary(call, output);
1102
- if (summary) {
1103
- return { main: summary };
1104
- }
1105
- const claudeSummary = this.formatClaudeCodeResultSummary(call.name, args, output, success);
1106
- if (claudeSummary) {
1107
- return { main: claudeSummary };
1108
- }
1109
- return { main: call.name, detail: lineCount ? `${lineCount} line${lineCount === 1 ? '' : 's'}` : undefined };
1110
- }
883
+ // Get compact summary for the result
884
+ const summary = this.formatClaudeCodeResultSummary(call.name, args, output, success);
885
+ if (summary) {
886
+ // Claude Code style: ⎿ Summary (indented under tool call)
887
+ const prefix = success
888
+ ? theme.success(icons.subaction)
889
+ : theme.error(icons.subaction);
890
+ // Add newline after result for visual separation
891
+ this.display.stream(` ${prefix} ${summary}\n\n`);
1111
892
  }
1112
893
  }
1113
894
  /**
1114
- * Render a compact preview of tool output (first N lines) with truncation hints.
895
+ * Format tool result summary in Claude Code style
896
+ * Returns compact summary like "Found 4 files (ctrl+o to expand)" or "Read 50 lines"
1115
897
  */
1116
- formatToolPreview(call, output, success, force = false) {
1117
- const previewLines = this.buildPreviewLines(call, output, success, force);
1118
- if (!previewLines.length) {
1119
- return null;
1120
- }
1121
- const gutter = theme.ui.muted('│');
1122
- return previewLines.map(line => ` ${gutter} ${line}`).join('\n');
1123
- }
1124
- buildPreviewLines(call, output, success, force) {
1125
- const normalized = (output || '').trim();
1126
- const allLines = normalized ? normalized.split('\n').filter(l => l.trim()) : [];
1127
- const limit = this.getPreviewLineLimit(call.name, success);
1128
- if (limit <= 0 && !force) {
1129
- return [];
1130
- }
1131
- const maxLines = limit > 0 ? limit : force ? Math.min(1, allLines.length) : 0;
1132
- if (maxLines === 0 || allLines.length === 0) {
1133
- return [];
1134
- }
1135
- const preview = allLines.slice(0, maxLines).map(line => this.truncatePreviewLine(line));
1136
- const remaining = allLines.length - maxLines;
1137
- if (remaining > 0) {
1138
- preview.push(theme.ui.muted(`… +${remaining} more`));
1139
- }
1140
- return preview;
1141
- }
1142
- getPreviewLineLimit(toolName, success) {
1143
- switch (toolName) {
1144
- case 'Read':
1145
- case 'read_file':
1146
- return success ? 0 : 1;
1147
- case 'Glob':
1148
- case 'glob':
1149
- return 3;
1150
- case 'Grep':
1151
- case 'grep':
1152
- return 4;
1153
- case 'Bash':
1154
- case 'bash':
1155
- return success ? 3 : 4;
1156
- case 'run_tests':
1157
- return success ? 2 : 4;
1158
- case 'Edit':
1159
- case 'edit_file':
1160
- case 'NotebookEdit':
1161
- return success ? 30 : 4; // Show more diff context for successful edits
1162
- default:
1163
- return success ? 2 : 3;
1164
- }
1165
- }
1166
- truncatePreviewLine(line, maxLen = 100) {
1167
- const trimmed = line.trim();
1168
- if (trimmed.length <= maxLen) {
1169
- return theme.dim(trimmed);
1170
- }
1171
- return theme.dim(`${trimmed.slice(0, maxLen - 3)}...`);
1172
- }
1173
- countOutputLines(output) {
1174
- if (!output || !output.trim())
1175
- return 0;
1176
- return output.trimEnd().split('\n').length;
1177
- }
1178
- parseTestCounts(output) {
1179
- const passMatch = output.match(/(\d+)\s*(?:passing|passed|✓)/i);
1180
- const failMatch = output.match(/(\d+)\s*(?:failing|failed|✗)/i);
1181
- const skipMatch = output.match(/(\d+)\s*(?:skipped|pending)/i);
1182
- return {
1183
- passed: passMatch ? parseInt(passMatch[1] || '0', 10) : 0,
1184
- failed: failMatch ? parseInt(failMatch[1] || '0', 10) : 0,
1185
- skipped: skipMatch ? parseInt(skipMatch[1] || '0', 10) : 0,
1186
- };
1187
- }
1188
898
  formatClaudeCodeResultSummary(toolName, args, output, success) {
899
+ const expandHint = theme.ui.muted('(ctrl+o to expand)');
1189
900
  if (!success) {
1190
901
  // Error case - show error message
1191
902
  const errorMsg = output.length > 60 ? `${output.slice(0, 57)}...` : output;
@@ -1227,9 +938,9 @@ export class ShellUIAdapter {
1227
938
  return theme.ui.muted('No matches found');
1228
939
  }
1229
940
  if (outputMode === 'content') {
1230
- return `Found ${theme.info(String(count))} lines`;
941
+ return `Found ${theme.info(String(count))} lines ${expandHint}`;
1231
942
  }
1232
- return `Found ${theme.info(String(count))} files`;
943
+ return `Found ${theme.info(String(count))} files ${expandHint}`;
1233
944
  }
1234
945
  case 'Glob':
1235
946
  case 'glob': {
@@ -1238,7 +949,7 @@ export class ShellUIAdapter {
1238
949
  if (count === 0) {
1239
950
  return theme.ui.muted('No files found');
1240
951
  }
1241
- return `Found ${theme.info(String(count))} file${count === 1 ? '' : 's'}`;
952
+ return `Found ${theme.info(String(count))} file${count === 1 ? '' : 's'} ${expandHint}`;
1242
953
  }
1243
954
  case 'Bash':
1244
955
  case 'bash': {
@@ -1246,7 +957,7 @@ export class ShellUIAdapter {
1246
957
  if (lines === 0) {
1247
958
  return theme.ui.muted('Completed (no output)');
1248
959
  }
1249
- return `Output: ${theme.info(String(lines))} lines`;
960
+ return `Output: ${theme.info(String(lines))} lines ${expandHint}`;
1250
961
  }
1251
962
  case 'WebFetch':
1252
963
  case 'web_fetch': {
@@ -1258,7 +969,7 @@ export class ShellUIAdapter {
1258
969
  case 'web_search': {
1259
970
  const resultMatches = output.match(/^\d+\./gm);
1260
971
  const count = resultMatches ? resultMatches.length : 'multiple';
1261
- return `Found ${theme.info(String(count))} results`;
972
+ return `Found ${theme.info(String(count))} results ${expandHint}`;
1262
973
  }
1263
974
  case 'Task':
1264
975
  case 'task': {
@@ -1292,7 +1003,7 @@ export class ShellUIAdapter {
1292
1003
  if (lines <= 3) {
1293
1004
  return 'Completed';
1294
1005
  }
1295
- return `${theme.info(String(lines))} lines`;
1006
+ return `${theme.info(String(lines))} lines ${expandHint}`;
1296
1007
  }
1297
1008
  }
1298
1009
  }
@@ -1301,10 +1012,11 @@ export class ShellUIAdapter {
1301
1012
  * Example output:
1302
1013
  * Read 100 lines
1303
1014
  * Found 4 lines
1304
- * +31 more tool uses
1015
+ * +31 more tool uses (ctrl+o to expand)
1305
1016
  */
1306
1017
  formatAgentResultSummary(output) {
1307
1018
  const lines = [];
1019
+ const expandHint = theme.ui.muted('(ctrl+o to expand)');
1308
1020
  // Parse output for tool use summaries
1309
1021
  // Look for patterns like "Read X lines", "Found X files", "Output: X lines", etc.
1310
1022
  const toolPatterns = [
@@ -1337,13 +1049,13 @@ export class ShellUIAdapter {
1337
1049
  lines.push(use);
1338
1050
  }
1339
1051
  if (remaining > 0) {
1340
- lines.push(`+${remaining} more tool uses`);
1052
+ lines.push(`+${remaining} more tool uses ${expandHint}`);
1341
1053
  }
1342
1054
  else if (lines.length === 0) {
1343
1055
  // Fallback if no tool uses found
1344
1056
  const outputLines = output.trim().split('\n').filter(l => l.trim()).length;
1345
1057
  if (outputLines > 0) {
1346
- return `${theme.info(String(outputLines))} lines`;
1058
+ return `${theme.info(String(outputLines))} lines ${expandHint}`;
1347
1059
  }
1348
1060
  return 'Completed';
1349
1061
  }