erosolar-cli 1.7.396 → 1.7.398

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 (80) hide show
  1. package/dist/browser/BrowserSessionManager.d.ts +3 -1
  2. package/dist/browser/BrowserSessionManager.d.ts.map +1 -1
  3. package/dist/browser/BrowserSessionManager.js +24 -4
  4. package/dist/browser/BrowserSessionManager.js.map +1 -1
  5. package/dist/contracts/agent-schemas.json +5 -0
  6. package/dist/contracts/unified-schema.json +2 -1
  7. package/dist/core/agent.d.ts +5 -0
  8. package/dist/core/agent.d.ts.map +1 -1
  9. package/dist/core/agent.js +99 -0
  10. package/dist/core/agent.js.map +1 -1
  11. package/dist/core/alphaZeroConfig.d.ts +11 -0
  12. package/dist/core/alphaZeroConfig.d.ts.map +1 -0
  13. package/dist/core/alphaZeroConfig.js +59 -0
  14. package/dist/core/alphaZeroConfig.js.map +1 -0
  15. package/dist/core/alphaZeroEngine.d.ts +8 -0
  16. package/dist/core/alphaZeroEngine.d.ts.map +1 -1
  17. package/dist/core/alphaZeroEngine.js +149 -35
  18. package/dist/core/alphaZeroEngine.js.map +1 -1
  19. package/dist/core/alphaZeroEnhanced.d.ts +125 -0
  20. package/dist/core/alphaZeroEnhanced.d.ts.map +1 -0
  21. package/dist/core/alphaZeroEnhanced.js +386 -0
  22. package/dist/core/alphaZeroEnhanced.js.map +1 -0
  23. package/dist/core/alphaZeroOrchestrator.d.ts +17 -0
  24. package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -1
  25. package/dist/core/alphaZeroOrchestrator.js +95 -8
  26. package/dist/core/alphaZeroOrchestrator.js.map +1 -1
  27. package/dist/core/autonomousVerification.d.ts +103 -0
  28. package/dist/core/autonomousVerification.d.ts.map +1 -0
  29. package/dist/core/autonomousVerification.js +583 -0
  30. package/dist/core/autonomousVerification.js.map +1 -0
  31. package/dist/core/cliTestHarness.d.ts +5 -0
  32. package/dist/core/cliTestHarness.d.ts.map +1 -1
  33. package/dist/core/cliTestHarness.js +14 -3
  34. package/dist/core/cliTestHarness.js.map +1 -1
  35. package/dist/core/contextManager.d.ts +10 -0
  36. package/dist/core/contextManager.d.ts.map +1 -1
  37. package/dist/core/contextManager.js +18 -0
  38. package/dist/core/contextManager.js.map +1 -1
  39. package/dist/core/offsecAlphaZeroEnhanced.d.ts +98 -0
  40. package/dist/core/offsecAlphaZeroEnhanced.d.ts.map +1 -0
  41. package/dist/core/offsecAlphaZeroEnhanced.js +441 -0
  42. package/dist/core/offsecAlphaZeroEnhanced.js.map +1 -0
  43. package/dist/core/parallelAgentOrchestrator.d.ts +171 -0
  44. package/dist/core/parallelAgentOrchestrator.d.ts.map +1 -0
  45. package/dist/core/parallelAgentOrchestrator.js +459 -0
  46. package/dist/core/parallelAgentOrchestrator.js.map +1 -0
  47. package/dist/index.d.ts +5 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +3 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/shell/interactiveShell.d.ts +15 -0
  52. package/dist/shell/interactiveShell.d.ts.map +1 -1
  53. package/dist/shell/interactiveShell.js +344 -210
  54. package/dist/shell/interactiveShell.js.map +1 -1
  55. package/dist/shell/shellApp.d.ts.map +1 -1
  56. package/dist/shell/shellApp.js +15 -1
  57. package/dist/shell/shellApp.js.map +1 -1
  58. package/dist/shell/terminalInput.d.ts +42 -1
  59. package/dist/shell/terminalInput.d.ts.map +1 -1
  60. package/dist/shell/terminalInput.js +276 -6
  61. package/dist/shell/terminalInput.js.map +1 -1
  62. package/dist/shell/terminalInputAdapter.d.ts +14 -2
  63. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  64. package/dist/shell/terminalInputAdapter.js +19 -1
  65. package/dist/shell/terminalInputAdapter.js.map +1 -1
  66. package/dist/ui/assistantBlockRenderer.d.ts +28 -0
  67. package/dist/ui/assistantBlockRenderer.d.ts.map +1 -0
  68. package/dist/ui/assistantBlockRenderer.js +99 -0
  69. package/dist/ui/assistantBlockRenderer.js.map +1 -0
  70. package/dist/ui/display.d.ts +11 -0
  71. package/dist/ui/display.d.ts.map +1 -1
  72. package/dist/ui/display.js +37 -0
  73. package/dist/ui/display.js.map +1 -1
  74. package/dist/ui/theme.d.ts +80 -81
  75. package/dist/ui/theme.d.ts.map +1 -1
  76. package/dist/ui/unified/layout.d.ts +0 -23
  77. package/dist/ui/unified/layout.d.ts.map +1 -1
  78. package/dist/ui/unified/layout.js +11 -114
  79. package/dist/ui/unified/layout.js.map +1 -1
  80. package/package.json +33 -19
@@ -2,6 +2,7 @@ import { stdin as input, stdout as output, exit } from 'node:process';
2
2
  import { exec } from 'node:child_process';
3
3
  import { promisify } from 'node:util';
4
4
  import { createInterface } from 'node:readline/promises';
5
+ import { AssistantBlockRenderer } from '../ui/assistantBlockRenderer.js';
5
6
  import { display } from '../ui/display.js';
6
7
  import { theme } from '../ui/theme.js';
7
8
  import { getContextWindowTokens } from '../core/contextWindow.js';
@@ -114,6 +115,8 @@ export class InteractiveShell {
114
115
  ui;
115
116
  uiAdapter;
116
117
  uiUpdates;
118
+ assistantBlocksEnabled;
119
+ assistantBlockRenderer = null;
117
120
  _fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
118
121
  alphaZeroMetrics; // Alpha Zero 2 performance tracking
119
122
  statusSubscription = null;
@@ -168,15 +171,19 @@ export class InteractiveShell {
168
171
  streamingHeartbeatStart = null;
169
172
  streamingHeartbeatFrame = 0;
170
173
  streamingStatusLabel = null;
174
+ streamingStatusBase = null;
175
+ streamingStatusDetail = null;
171
176
  lastStreamingElapsedSeconds = null; // Preserve final elapsed time
172
177
  statusLineState = null;
173
178
  statusMessageOverride = null;
179
+ latestThoughtSummary = null;
174
180
  hasShownThoughtProcess = false;
175
181
  promptRefreshTimer = null;
176
182
  launchPaletteShown = false;
177
183
  version;
178
184
  alternateScreenEnabled;
179
185
  inputInitialized = false;
186
+ assistantStreamBuffer = '';
180
187
  constructor(config) {
181
188
  this.profile = config.profile;
182
189
  this.profileLabel = config.profileLabel;
@@ -192,6 +199,7 @@ export class InteractiveShell {
192
199
  this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
193
200
  this._enabledPlugins = config.enabledPlugins ?? [];
194
201
  this.version = config.version ?? '0.0.0';
202
+ this.assistantBlocksEnabled = Boolean(config.assistantBlocksEnabled);
195
203
  // Alternate screen disabled - use terminal-native mode for proper scrollback and text selection
196
204
  this.alternateScreenEnabled = false;
197
205
  this.initializeSessionHistory();
@@ -287,6 +295,12 @@ export class InteractiveShell {
287
295
  onToggleAlphaZero: () => this.toggleAlphaZeroMode('shortcut'),
288
296
  onClearContext: () => this.handleClearContext(),
289
297
  });
298
+ if (this.assistantBlocksEnabled) {
299
+ this.assistantBlockRenderer = new AssistantBlockRenderer({
300
+ write: (text) => this.terminalInput.streamContent(text),
301
+ updates: this.uiUpdates,
302
+ });
303
+ }
290
304
  // Initialize Alpha Zero 2 metrics tracking
291
305
  this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
292
306
  this.setupStatusTracking();
@@ -538,7 +552,8 @@ export class InteractiveShell {
538
552
  // Keep adapter queue trimmed so hints stay accurate
539
553
  this.terminalInput.dequeue();
540
554
  this.followUpQueue.push({ type: 'request', text });
541
- display.showInfo(`Queued: "${text}"`);
555
+ // Record the queued action in the pinned recent strip instead of the scroll log
556
+ this.terminalInput.recordRecentAction(`queued: ${text}`);
542
557
  this.refreshQueueIndicators();
543
558
  this.scheduleQueueProcessing();
544
559
  this.handleInputChange('');
@@ -599,15 +614,30 @@ export class InteractiveShell {
599
614
  * Execute a command immediately during streaming.
600
615
  */
601
616
  async executeImmediateCommand(text) {
602
- // Pause streaming display briefly to show command output
603
- display.showInfo(`Running command during stream: ${text}`);
604
- await this.processSlashCommand(text);
617
+ const label = text.trim() || 'command';
618
+ // Keep the action visible in the pinned input area instead of the scroll log
619
+ this.terminalInput.recordRecentAction(label);
620
+ // Surface a lightweight inline status while the command runs
621
+ const previousOverride = this.statusMessageOverride;
622
+ this.statusMessageOverride = `Running ${label}`;
623
+ this.refreshStatusLine(true);
624
+ try {
625
+ await this.processSlashCommand(text);
626
+ }
627
+ finally {
628
+ this.statusMessageOverride = previousOverride;
629
+ this.refreshStatusLine(true);
630
+ }
605
631
  }
606
632
  /**
607
633
  * TerminalInputAdapter change handler
608
634
  */
609
635
  handleInputChange(text) {
636
+ const previous = this.currentInput;
610
637
  this.currentInput = text;
638
+ if (previous !== text) {
639
+ this.terminalInput.clearInlineCommandPanel();
640
+ }
611
641
  if (text.length > 0) {
612
642
  this.resetCtrlCSequence();
613
643
  }
@@ -1298,9 +1328,13 @@ export class InteractiveShell {
1298
1328
  alphaZeroHotkey: 'ctrl+shift+a',
1299
1329
  alphaZeroLabel: 'AlphaZero RL',
1300
1330
  });
1331
+ this.refreshFeatureStatusDisplay();
1301
1332
  this.refreshStatusLine();
1302
1333
  this.renderPromptArea();
1303
1334
  }
1335
+ refreshFeatureStatusDisplay() {
1336
+ this.terminalInput.setFeatureStatus(this.buildFeatureStatusSnapshot());
1337
+ }
1304
1338
  writeLocked(content) {
1305
1339
  if (!content) {
1306
1340
  return;
@@ -1308,6 +1342,29 @@ export class InteractiveShell {
1308
1342
  // Route through display stream so scroll regions and streaming locks stay in sync
1309
1343
  display.stream(content);
1310
1344
  }
1345
+ resetAssistantStreamBuffer() {
1346
+ this.assistantStreamBuffer = '';
1347
+ }
1348
+ appendAssistantStreamChunk(chunk) {
1349
+ if (!this.assistantBlocksEnabled || !chunk) {
1350
+ return;
1351
+ }
1352
+ this.assistantStreamBuffer += chunk;
1353
+ }
1354
+ consumeAssistantStreamBuffer(fallback) {
1355
+ const buffered = this.assistantStreamBuffer;
1356
+ this.assistantStreamBuffer = '';
1357
+ if (buffered.trim()) {
1358
+ return buffered;
1359
+ }
1360
+ return fallback ?? '';
1361
+ }
1362
+ renderAssistantBlock(type, content, metadata) {
1363
+ if (!this.assistantBlockRenderer) {
1364
+ return;
1365
+ }
1366
+ this.assistantBlockRenderer.renderBlock(type, content, metadata);
1367
+ }
1311
1368
  isStreamingUiActive() {
1312
1369
  return this.streamingHeartbeatStart !== null;
1313
1370
  }
@@ -1356,19 +1413,23 @@ export class InteractiveShell {
1356
1413
  // Surface meta header (elapsed + context usage) above the divider
1357
1414
  // Use streaming elapsed time if available, otherwise fall back to status line state
1358
1415
  let elapsedSeconds = null;
1359
- if (this.streamingHeartbeatStart) {
1416
+ const shouldShowElapsed = this.streamingHeartbeatStart !== null || this.isProcessing;
1417
+ if (this.streamingHeartbeatStart && shouldShowElapsed) {
1360
1418
  // Actively streaming - compute live elapsed
1361
1419
  elapsedSeconds = Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000));
1362
1420
  }
1363
- else if (this.lastStreamingElapsedSeconds !== null) {
1421
+ else if (shouldShowElapsed && this.lastStreamingElapsedSeconds !== null) {
1364
1422
  // Just finished streaming - use preserved final time
1365
1423
  elapsedSeconds = this.lastStreamingElapsedSeconds;
1366
1424
  }
1367
- else if (this.statusLineState) {
1425
+ else if (shouldShowElapsed && this.statusLineState) {
1368
1426
  // Fallback to status line state elapsed
1369
1427
  elapsedSeconds = Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000));
1370
1428
  }
1371
- const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
1429
+ const hasThoughtSummary = !!this.latestThoughtSummary;
1430
+ const thinkingMs = hasThoughtSummary && display.isSpinnerActive()
1431
+ ? display.getThinkingElapsedMs()
1432
+ : null;
1372
1433
  const tokensUsed = this.latestTokenUsage.used;
1373
1434
  const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
1374
1435
  this.terminalInput.setMetaStatus({
@@ -1376,7 +1437,7 @@ export class InteractiveShell {
1376
1437
  tokensUsed,
1377
1438
  tokenLimit,
1378
1439
  thinkingMs,
1379
- thinkingHasContent: display.isSpinnerActive(),
1440
+ thinkingHasContent: hasThoughtSummary,
1380
1441
  });
1381
1442
  // Keep model/provider visible in the controls bar
1382
1443
  this.terminalInput.setModelContext({
@@ -1483,14 +1544,17 @@ export class InteractiveShell {
1483
1544
  this.stopStreamingHeartbeat();
1484
1545
  // Enter global streaming mode - blocks all non-streaming UI output
1485
1546
  enterStreamingMode();
1547
+ this.resetAssistantStreamBuffer();
1548
+ this.streamingStatusBase = label;
1549
+ this.streamingStatusDetail = null;
1550
+ this.latestThoughtSummary = null;
1551
+ this.lastStreamingElapsedSeconds = null;
1486
1552
  // Set up scroll region for streaming content
1487
1553
  this.terminalInput.enterStreamingScrollRegion();
1488
1554
  this.uiUpdates.setMode('streaming');
1489
1555
  this.streamingHeartbeatStart = Date.now();
1490
1556
  this.streamingHeartbeatFrame = 0;
1491
- const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
1492
- this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
1493
- display.updateStreamingStatus(this.streamingStatusLabel);
1557
+ this.rebuildStreamingStatusLabel();
1494
1558
  this.refreshStatusLine(true);
1495
1559
  // Periodically refresh the pinned input/status region while streaming so
1496
1560
  // elapsed time remains visible without interrupting the scroll region.
@@ -1500,14 +1564,9 @@ export class InteractiveShell {
1500
1564
  mode: ['streaming', 'processing'],
1501
1565
  coalesceKey: 'streaming:heartbeat',
1502
1566
  run: () => {
1503
- const elapsedSeconds = this.streamingHeartbeatStart
1504
- ? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
1505
- : 0;
1506
1567
  this.streamingHeartbeatFrame =
1507
1568
  (this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
1508
- const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
1509
- this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
1510
- display.updateStreamingStatus(this.streamingStatusLabel);
1569
+ this.rebuildStreamingStatusLabel();
1511
1570
  // Update parallel agent display during streaming
1512
1571
  const manager = getParallelAgentManager();
1513
1572
  if (manager.isRunning()) {
@@ -1531,6 +1590,9 @@ export class InteractiveShell {
1531
1590
  this.streamingHeartbeatStart = null;
1532
1591
  this.streamingHeartbeatFrame = 0;
1533
1592
  this.streamingStatusLabel = null;
1593
+ this.streamingStatusBase = null;
1594
+ this.streamingStatusDetail = null;
1595
+ this.latestThoughtSummary = null;
1534
1596
  // Clear streaming label specifically (keeps override and main status if set)
1535
1597
  this.terminalInput.setStreamingLabel(null);
1536
1598
  // Clear streaming status from display
@@ -1540,9 +1602,25 @@ export class InteractiveShell {
1540
1602
  }
1541
1603
  buildStreamingStatus(label, _elapsedSeconds) {
1542
1604
  // Model + elapsed time already live in the pinned meta header; keep the streaming
1543
- // status focused on the activity to avoid duplicate info.
1605
+ // status focused on the activity and most recent thought summary.
1544
1606
  const prefix = theme.info('⏺');
1545
- return `${prefix} ${label}`.trim();
1607
+ const parts = [label.trim()];
1608
+ if (this.streamingStatusDetail) {
1609
+ const detail = this.streamingStatusDetail.length > 52
1610
+ ? `${this.streamingStatusDetail.slice(0, 51)}…`
1611
+ : this.streamingStatusDetail;
1612
+ parts.push(theme.ui.muted(detail));
1613
+ }
1614
+ return `${prefix} ${parts.join(' · ')}`.trim();
1615
+ }
1616
+ rebuildStreamingStatusLabel() {
1617
+ if (this.streamingHeartbeatStart === null) {
1618
+ return;
1619
+ }
1620
+ const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
1621
+ const base = this.streamingStatusBase ?? 'Streaming';
1622
+ this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${base}`);
1623
+ display.updateStreamingStatus(this.streamingStatusLabel);
1546
1624
  }
1547
1625
  formatElapsedShort(seconds) {
1548
1626
  if (seconds < 60) {
@@ -1886,170 +1964,197 @@ export class InteractiveShell {
1886
1964
  this.renderPromptArea();
1887
1965
  return;
1888
1966
  }
1889
- switch (command) {
1890
- case '/help':
1891
- case '/?':
1892
- this.showHelp();
1893
- break;
1894
- case '/features':
1895
- this.showFeaturesMenu(input);
1896
- break;
1897
- case '/learn':
1898
- this.showLearningStatus(input);
1899
- break;
1900
- case '/improve':
1901
- void this.handleImprovementCommand(input);
1902
- break;
1903
- case '/model':
1904
- this.showModelMenu();
1905
- break;
1906
- case '/exit':
1907
- case '/quit':
1908
- case '/q':
1909
- this.shutdown();
1910
- break;
1911
- case '/secrets':
1912
- this.showSecretsMenu();
1913
- break;
1914
- case '/tools':
1915
- this.showToolsMenu();
1916
- break;
1917
- case '/mcp':
1918
- await this.showMcpStatus();
1919
- break;
1920
- case '/doctor':
1921
- this.runDoctor();
1922
- break;
1923
- case '/checks':
1924
- await this.runRepoChecksCommand();
1925
- break;
1926
- case '/context':
1927
- await this.refreshWorkspaceContextCommand(input);
1928
- break;
1929
- case '/agents':
1930
- this.showAgentsMenu();
1931
- break;
1932
- case '/sessions':
1933
- await this.handleSessionCommand(input);
1934
- break;
1935
- case '/skills':
1936
- await this.handleSkillsCommand(input);
1937
- break;
1938
- case '/thinking':
1939
- this.handleThinkingCommand(input);
1940
- break;
1941
- case '/autocontinue':
1942
- this.handleAutoContinueCommand(input);
1943
- break;
1944
- case '/alphazero':
1945
- this.handleAlphaZeroCommand(input);
1946
- break;
1947
- case '/shortcuts':
1948
- case '/keys':
1949
- this.handleShortcutsCommand();
1950
- break;
1951
- case '/changes':
1952
- case '/summary':
1953
- this.showFileChangeSummary();
1954
- break;
1955
- case '/metrics':
1956
- case '/stats':
1957
- case '/perf':
1958
- this.showAlphaZeroMetrics();
1959
- break;
1960
- case '/suggestions':
1961
- case '/improve':
1962
- this.showImprovementSuggestions();
1963
- break;
1964
- case '/plugins':
1965
- this.showPluginStatus();
1966
- break;
1967
- case '/evolve':
1968
- void this.handleEvolveCommand(input);
1969
- break;
1970
- case '/modular':
1971
- case '/a0':
1972
- void this.handleModularCommand(input);
1973
- break;
1974
- case '/offsec':
1975
- void this.handleOffsecCommand(input);
1976
- break;
1977
- case '/test':
1978
- case '/tests':
1979
- void this.handleTestCommand(input);
1980
- break;
1981
- case '/provider':
1982
- await this.handleProviderCommand(input);
1983
- break;
1984
- case '/providers':
1985
- this.showConfiguredProviders();
1986
- break;
1987
- case '/local':
1988
- await this.handleLocalCommand(input);
1989
- break;
1990
- case '/discover':
1991
- await this.discoverModelsCommand();
1992
- break;
1993
- // Claude Code style commands
1994
- case '/rewind':
1995
- await this.handleRewindCommand(input);
1996
- break;
1997
- case '/memory':
1998
- this.handleMemoryCommand(input);
1999
- break;
2000
- case '/vim':
2001
- this.handleVimCommand();
2002
- break;
2003
- case '/output-style':
2004
- this.handleOutputStyleCommand(input);
2005
- break;
2006
- case '/cost':
2007
- this.handleCostCommand();
2008
- break;
2009
- case '/usage':
2010
- this.handleUsageCommand();
2011
- break;
2012
- case '/update':
2013
- await this.handleUpdateCommand();
2014
- break;
2015
- case '/clear':
2016
- this.handleClearCommand();
2017
- break;
2018
- case '/resume':
2019
- await this.handleResumeCommand(input);
2020
- break;
2021
- case '/export':
2022
- this.handleExportCommand(input);
2023
- break;
2024
- case '/review':
2025
- await this.handleReviewCommand();
2026
- break;
2027
- case '/security-review':
2028
- await this.handleSecurityReviewCommand();
2029
- break;
2030
- case '/bug':
2031
- this.handleBugCommand();
2032
- break;
2033
- case '/terminal-setup':
2034
- this.handleTerminalSetupCommand();
2035
- break;
2036
- case '/permissions':
2037
- this.handlePermissionsCommand();
2038
- break;
2039
- case '/init':
2040
- this.handleInitCommand();
2041
- break;
2042
- case '/compact':
2043
- await this.handleCompactCommand();
2044
- break;
2045
- default:
2046
- if (!(await this.tryCustomSlashCommand(command, input))) {
2047
- display.showWarning(`Unknown command "${command}".`);
2048
- }
2049
- break;
1967
+ // Keep the slash action visible in the pinned recent strip
1968
+ this.terminalInput.recordRecentAction(command);
1969
+ const runCommand = async () => {
1970
+ switch (command) {
1971
+ case '/help':
1972
+ case '/?':
1973
+ this.showHelp();
1974
+ break;
1975
+ case '/features':
1976
+ this.showFeaturesMenu(input);
1977
+ break;
1978
+ case '/learn':
1979
+ this.showLearningStatus(input);
1980
+ break;
1981
+ case '/improve':
1982
+ void this.handleImprovementCommand(input);
1983
+ break;
1984
+ case '/model':
1985
+ this.showModelMenu();
1986
+ break;
1987
+ case '/exit':
1988
+ case '/quit':
1989
+ case '/q':
1990
+ this.shutdown();
1991
+ break;
1992
+ case '/secrets':
1993
+ this.showSecretsMenu();
1994
+ break;
1995
+ case '/tools':
1996
+ this.showToolsMenu();
1997
+ break;
1998
+ case '/mcp':
1999
+ await this.showMcpStatus();
2000
+ break;
2001
+ case '/doctor':
2002
+ this.runDoctor();
2003
+ break;
2004
+ case '/checks':
2005
+ await this.runRepoChecksCommand();
2006
+ break;
2007
+ case '/context':
2008
+ await this.refreshWorkspaceContextCommand(input);
2009
+ break;
2010
+ case '/agents':
2011
+ this.showAgentsMenu();
2012
+ break;
2013
+ case '/sessions':
2014
+ await this.handleSessionCommand(input);
2015
+ break;
2016
+ case '/skills':
2017
+ await this.handleSkillsCommand(input);
2018
+ break;
2019
+ case '/thinking':
2020
+ this.handleThinkingCommand(input);
2021
+ break;
2022
+ case '/autocontinue':
2023
+ this.handleAutoContinueCommand(input);
2024
+ break;
2025
+ case '/alphazero':
2026
+ this.handleAlphaZeroCommand(input);
2027
+ break;
2028
+ case '/shortcuts':
2029
+ case '/keys':
2030
+ this.handleShortcutsCommand();
2031
+ break;
2032
+ case '/changes':
2033
+ case '/summary':
2034
+ this.showFileChangeSummary();
2035
+ break;
2036
+ case '/metrics':
2037
+ case '/stats':
2038
+ case '/perf':
2039
+ this.showAlphaZeroMetrics();
2040
+ break;
2041
+ case '/suggestions':
2042
+ case '/improve':
2043
+ this.showImprovementSuggestions();
2044
+ break;
2045
+ case '/plugins':
2046
+ this.showPluginStatus();
2047
+ break;
2048
+ case '/evolve':
2049
+ void this.handleEvolveCommand(input);
2050
+ break;
2051
+ case '/modular':
2052
+ case '/a0':
2053
+ void this.handleModularCommand(input);
2054
+ break;
2055
+ case '/offsec':
2056
+ void this.handleOffsecCommand(input);
2057
+ break;
2058
+ case '/test':
2059
+ case '/tests':
2060
+ void this.handleTestCommand(input);
2061
+ break;
2062
+ case '/provider':
2063
+ await this.handleProviderCommand(input);
2064
+ break;
2065
+ case '/providers':
2066
+ this.showConfiguredProviders();
2067
+ break;
2068
+ case '/local':
2069
+ await this.handleLocalCommand(input);
2070
+ break;
2071
+ case '/discover':
2072
+ await this.discoverModelsCommand();
2073
+ break;
2074
+ // Claude Code style commands
2075
+ case '/rewind':
2076
+ await this.handleRewindCommand(input);
2077
+ break;
2078
+ case '/memory':
2079
+ this.handleMemoryCommand(input);
2080
+ break;
2081
+ case '/vim':
2082
+ this.handleVimCommand();
2083
+ break;
2084
+ case '/output-style':
2085
+ this.handleOutputStyleCommand(input);
2086
+ break;
2087
+ case '/cost':
2088
+ this.handleCostCommand();
2089
+ break;
2090
+ case '/usage':
2091
+ this.handleUsageCommand();
2092
+ break;
2093
+ case '/update':
2094
+ await this.handleUpdateCommand();
2095
+ break;
2096
+ case '/clear':
2097
+ this.handleClearCommand();
2098
+ break;
2099
+ case '/resume':
2100
+ await this.handleResumeCommand(input);
2101
+ break;
2102
+ case '/export':
2103
+ this.handleExportCommand(input);
2104
+ break;
2105
+ case '/review':
2106
+ await this.handleReviewCommand();
2107
+ break;
2108
+ case '/security-review':
2109
+ await this.handleSecurityReviewCommand();
2110
+ break;
2111
+ case '/bug':
2112
+ this.handleBugCommand();
2113
+ break;
2114
+ case '/terminal-setup':
2115
+ this.handleTerminalSetupCommand();
2116
+ break;
2117
+ case '/permissions':
2118
+ this.handlePermissionsCommand();
2119
+ break;
2120
+ case '/init':
2121
+ this.handleInitCommand();
2122
+ break;
2123
+ case '/compact':
2124
+ await this.handleCompactCommand();
2125
+ break;
2126
+ default:
2127
+ if (!(await this.tryCustomSlashCommand(command, input))) {
2128
+ display.showWarning(`Unknown command "${command}".`);
2129
+ }
2130
+ break;
2131
+ }
2132
+ };
2133
+ let capturedOutput = '';
2134
+ try {
2135
+ const { output: outputBuffer } = await display.captureOutput(runCommand);
2136
+ capturedOutput = outputBuffer;
2050
2137
  }
2138
+ catch (error) {
2139
+ capturedOutput = error?.capturedOutput ?? capturedOutput;
2140
+ display.showError(error instanceof Error ? error.message : String(error), error);
2141
+ }
2142
+ const panelContent = this.buildInlineCommandPanel(command, capturedOutput);
2143
+ this.terminalInput.setInlineCommandPanel(panelContent);
2051
2144
  this.renderPromptArea();
2052
2145
  }
2146
+ buildInlineCommandPanel(command, output) {
2147
+ const normalized = output ? output.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trimEnd() : '';
2148
+ if (!normalized) {
2149
+ return null;
2150
+ }
2151
+ const header = theme.ui.muted(command);
2152
+ const body = normalized.split('\n');
2153
+ if (body.length === 1) {
2154
+ return [`${header} ${body[0] ?? ''}`.trimEnd()];
2155
+ }
2156
+ return [header, ...body];
2157
+ }
2053
2158
  async tryCustomSlashCommand(command, fullInput) {
2054
2159
  const custom = this.customCommandMap.get(command);
2055
2160
  if (!custom) {
@@ -2476,6 +2581,7 @@ export class InteractiveShell {
2476
2581
  const updated = toggleFeatureFlag(matchedKey, newValue);
2477
2582
  const status = updated[matchedKey] ? theme.success('enabled') : theme.ui.muted('disabled');
2478
2583
  display.showInfo(`Feature "${FEATURE_FLAG_INFO[matchedKey].label}" is now ${status}.`);
2584
+ this.refreshFeatureStatusDisplay();
2479
2585
  display.showInfo('Changes will take effect on next launch or after /features refresh.');
2480
2586
  return;
2481
2587
  }
@@ -2488,6 +2594,7 @@ export class InteractiveShell {
2488
2594
  }
2489
2595
  saveFeatureFlags(updated);
2490
2596
  display.showInfo(`All features ${newValue ? theme.success('enabled') : theme.ui.muted('disabled')}.`);
2597
+ this.refreshFeatureStatusDisplay();
2491
2598
  return;
2492
2599
  }
2493
2600
  else {
@@ -3660,9 +3767,7 @@ export class InteractiveShell {
3660
3767
  }
3661
3768
  display.showInfo(`Deleted session "${summary.title}".`);
3662
3769
  if (this.activeSessionId === summary.id) {
3663
- this.activeSessionId = null;
3664
- this.activeSessionTitle = null;
3665
- saveSessionPreferences({ lastSessionId: null });
3770
+ this.updateActiveSession(null, true);
3666
3771
  }
3667
3772
  }
3668
3773
  newSessionCommand(title) {
@@ -3682,6 +3787,7 @@ export class InteractiveShell {
3682
3787
  clearAutosaveSnapshot(this.profile);
3683
3788
  display.showInfo('Started a new empty session.');
3684
3789
  this.refreshContextGauge();
3790
+ this.refreshFeatureStatusDisplay();
3685
3791
  }
3686
3792
  toggleAutosaveCommand(value) {
3687
3793
  if (!value) {
@@ -3999,6 +4105,7 @@ export class InteractiveShell {
3999
4105
  if (remember) {
4000
4106
  saveSessionPreferences({ lastSessionId: summary?.id ?? null });
4001
4107
  }
4108
+ this.refreshFeatureStatusDisplay();
4002
4109
  }
4003
4110
  resolveSessionBySelector(selector) {
4004
4111
  const sessions = listSessions(this.profile);
@@ -5670,20 +5777,38 @@ What's the next action?`;
5670
5777
  };
5671
5778
  this.agent = this.runtimeSession.createAgent(selection, {
5672
5779
  onStreamChunk: (chunk) => {
5780
+ if (this.assistantBlocksEnabled) {
5781
+ this.appendAssistantStreamChunk(chunk);
5782
+ return;
5783
+ }
5673
5784
  // Stream output using clean streamContent() - chat box floats below
5674
5785
  this.terminalInput.streamContent(chunk);
5675
5786
  },
5676
5787
  onStreamFallback: (info) => this.handleStreamingFallback(info),
5677
5788
  onAssistantMessage: (content, metadata) => {
5678
5789
  const enriched = this.buildDisplayMetadata(metadata);
5679
- const parsed = this.splitThinkingResponse(content);
5790
+ const usingBlocks = this.assistantBlocksEnabled && this.assistantBlockRenderer !== null;
5791
+ const sourceContent = usingBlocks && metadata.wasStreamed ? this.consumeAssistantStreamBuffer(content) : content;
5792
+ const parsed = this.splitThinkingResponse(sourceContent);
5680
5793
  const thinking = parsed?.thinking ?? null;
5681
- const responseContent = parsed ? parsed.response?.trim() ?? '' : content.trim();
5682
- // Update spinner based on message type
5794
+ const shouldRenderThoughtBlock = Boolean(thinking) && !this.hasShownThoughtProcess;
5795
+ const responseContent = parsed ? parsed.response?.trim() ?? '' : sourceContent.trim();
5796
+ const narrativeContent = parsed?.response ?? sourceContent;
5683
5797
  if (metadata.isFinal) {
5684
- this.presentThoughtProcess(thinking, enriched, { wasStreamed: metadata.wasStreamed });
5685
- // Skip display if content was already streamed to avoid double-display
5686
- if (!metadata.wasStreamed && responseContent) {
5798
+ if (thinking) {
5799
+ // Update status + summary, but skip the legacy UI output when blocks are enabled
5800
+ this.presentThoughtProcess(thinking, enriched, {
5801
+ wasStreamed: usingBlocks || metadata.wasStreamed,
5802
+ });
5803
+ if (usingBlocks && shouldRenderThoughtBlock) {
5804
+ this.renderAssistantBlock('thought', thinking, enriched);
5805
+ }
5806
+ }
5807
+ if (usingBlocks) {
5808
+ const body = responseContent || sourceContent;
5809
+ this.renderAssistantBlock('response', body, enriched);
5810
+ }
5811
+ else if (!metadata.wasStreamed && responseContent) {
5687
5812
  display.showAssistantMessage(responseContent, enriched);
5688
5813
  }
5689
5814
  // Status shown in mode controls bar - no separate status line needed
@@ -5702,13 +5827,18 @@ What's the next action?`;
5702
5827
  }
5703
5828
  else {
5704
5829
  if (thinking) {
5705
- this.presentThoughtProcess(thinking, enriched, { wasStreamed: metadata.wasStreamed });
5830
+ this.presentThoughtProcess(thinking, enriched, {
5831
+ wasStreamed: usingBlocks || metadata.wasStreamed,
5832
+ });
5706
5833
  }
5707
5834
  // Non-final message = narrative text before tool calls (Claude Code style)
5708
5835
  // Stop spinner and show the narrative text directly
5709
5836
  display.stopThinking();
5710
- // Skip display if content was already streamed to avoid double-display
5711
- if (!metadata.wasStreamed) {
5837
+ if (usingBlocks) {
5838
+ const body = metadata.wasStreamed ? responseContent || sourceContent : narrativeContent;
5839
+ this.renderAssistantBlock('thought', body, enriched);
5840
+ }
5841
+ else if (!metadata.wasStreamed) {
5712
5842
  const narrative = parsed?.response ?? content;
5713
5843
  display.showNarrative(narrative.trim());
5714
5844
  }
@@ -5900,6 +6030,11 @@ What's the next action?`;
5900
6030
  const summary = this.extractThoughtSummary(thinking);
5901
6031
  if (summary) {
5902
6032
  display.updateThinking(`💭 ${summary}`);
6033
+ this.latestThoughtSummary = summary;
6034
+ this.streamingStatusDetail = summary;
6035
+ this.terminalInput.recordRecentAction(`💭 ${summary}`);
6036
+ this.rebuildStreamingStatusLabel();
6037
+ this.refreshStatusLine(true);
5903
6038
  }
5904
6039
  if (!options?.wasStreamed) {
5905
6040
  display.showAssistantMessage(thinking, { ...metadata, isFinal: false });
@@ -6281,6 +6416,7 @@ What's the next action?`;
6281
6416
  const detail = detailText ? ` Error: ${detailText}` : '';
6282
6417
  const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
6283
6418
  const partialNote = info.partialResponse ? ' Received partial stream before failure.' : '';
6419
+ this.resetAssistantStreamBuffer();
6284
6420
  display.showWarning(`Streaming failed${reason}, retrying without streaming.${detail}${partialNote}`);
6285
6421
  this.startStreamingHeartbeat('Fallback in progress');
6286
6422
  this.requestPromptRefresh(true);
@@ -6360,10 +6496,6 @@ What's the next action?`;
6360
6496
  buildBanner() {
6361
6497
  const terminalWidth = output.columns ?? 100;
6362
6498
  const width = Math.min(terminalWidth - 4, 110);
6363
- // Collect tool categories for display
6364
- const toolCategories = this.collectToolCategories();
6365
- // Load feature flags for banner display
6366
- const featureFlags = loadFeatureFlags();
6367
6499
  return renderSessionFrame({
6368
6500
  profileLabel: this.profileLabel,
6369
6501
  profileName: this.profile,
@@ -6372,19 +6504,6 @@ What's the next action?`;
6372
6504
  workspace: this.workingDir,
6373
6505
  version: this.version,
6374
6506
  width,
6375
- features: {
6376
- verification: this.verificationEnabled,
6377
- autoContinue: this.autoContinueEnabled,
6378
- thinkingMode: this.thinkingMode,
6379
- plugins: this._enabledPlugins,
6380
- tools: toolCategories,
6381
- sessionId: this.activeSessionId ?? undefined,
6382
- // Include feature flags
6383
- alphaZeroDual: featureFlags.alphaZeroDual,
6384
- autoCompact: featureFlags.autoCompact,
6385
- mcpEnabled: featureFlags.mcpEnabled,
6386
- metrics: featureFlags.metrics,
6387
- },
6388
6507
  });
6389
6508
  }
6390
6509
  /**
@@ -6415,6 +6534,21 @@ What's the next action?`;
6415
6534
  }
6416
6535
  return categories;
6417
6536
  }
6537
+ buildFeatureStatusSnapshot() {
6538
+ const featureFlags = loadFeatureFlags();
6539
+ const toolCategories = this.collectToolCategories();
6540
+ const toolCount = toolCategories.reduce((sum, cat) => sum + cat.count, 0);
6541
+ const pluginCount = this._enabledPlugins.length;
6542
+ return {
6543
+ pluginCount: pluginCount > 0 ? pluginCount : undefined,
6544
+ toolCount: toolCount > 0 ? toolCount : undefined,
6545
+ sessionId: this.activeSessionId,
6546
+ mcpEnabled: featureFlags.mcpEnabled,
6547
+ metricsEnabled: featureFlags.metrics,
6548
+ autoCompact: featureFlags.autoCompact,
6549
+ dualMode: featureFlags.alphaZeroDual,
6550
+ };
6551
+ }
6418
6552
  /**
6419
6553
  * Extract category from tool name.
6420
6554
  */