erosolar-cli 1.7.429 → 1.7.430

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 (83) hide show
  1. package/dist/capabilities/enhancedGitCapability.js +3 -3
  2. package/dist/capabilities/enhancedGitCapability.js.map +1 -1
  3. package/dist/capabilities/learnCapability.d.ts +1 -1
  4. package/dist/capabilities/learnCapability.d.ts.map +1 -1
  5. package/dist/capabilities/learnCapability.js +1 -1
  6. package/dist/capabilities/learnCapability.js.map +1 -1
  7. package/dist/core/checkpoint.d.ts +1 -1
  8. package/dist/core/checkpoint.js +1 -1
  9. package/dist/core/costTracker.d.ts +1 -1
  10. package/dist/core/costTracker.js +1 -1
  11. package/dist/core/hooks.d.ts +1 -1
  12. package/dist/core/hooks.js +1 -1
  13. package/dist/core/memorySystem.d.ts +2 -2
  14. package/dist/core/memorySystem.js +2 -2
  15. package/dist/core/outputStyles.d.ts +2 -2
  16. package/dist/core/outputStyles.js +2 -2
  17. package/dist/core/validationRunner.d.ts +1 -1
  18. package/dist/core/validationRunner.js +1 -1
  19. package/dist/shell/interactiveShell.d.ts +5 -1
  20. package/dist/shell/interactiveShell.d.ts.map +1 -1
  21. package/dist/shell/interactiveShell.js +115 -83
  22. package/dist/shell/interactiveShell.js.map +1 -1
  23. package/dist/shell/systemPrompt.d.ts.map +1 -1
  24. package/dist/shell/systemPrompt.js +13 -34
  25. package/dist/shell/systemPrompt.js.map +1 -1
  26. package/dist/shell/terminalInputAdapter.d.ts +75 -83
  27. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  28. package/dist/shell/terminalInputAdapter.js +159 -220
  29. package/dist/shell/terminalInputAdapter.js.map +1 -1
  30. package/dist/shell/vimMode.d.ts +1 -1
  31. package/dist/shell/vimMode.js +1 -1
  32. package/dist/tools/buildTools.d.ts +1 -1
  33. package/dist/tools/buildTools.js +1 -1
  34. package/dist/tools/diffUtils.d.ts +2 -2
  35. package/dist/tools/diffUtils.js +2 -2
  36. package/dist/tools/editTools.js +4 -4
  37. package/dist/tools/editTools.js.map +1 -1
  38. package/dist/tools/localExplore.d.ts +3 -3
  39. package/dist/tools/localExplore.js +3 -3
  40. package/dist/tools/skillTools.js +2 -2
  41. package/dist/tools/skillTools.js.map +1 -1
  42. package/dist/tools/validationTools.js +1 -1
  43. package/dist/ui/DisplayEventQueue.d.ts +99 -0
  44. package/dist/ui/DisplayEventQueue.d.ts.map +1 -0
  45. package/dist/ui/DisplayEventQueue.js +167 -0
  46. package/dist/ui/DisplayEventQueue.js.map +1 -0
  47. package/dist/ui/SequentialRenderer.d.ts +69 -0
  48. package/dist/ui/SequentialRenderer.d.ts.map +1 -0
  49. package/dist/ui/SequentialRenderer.js +137 -0
  50. package/dist/ui/SequentialRenderer.js.map +1 -0
  51. package/dist/ui/ShellUIAdapter.d.ts +18 -6
  52. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  53. package/dist/ui/ShellUIAdapter.js +65 -14
  54. package/dist/ui/ShellUIAdapter.js.map +1 -1
  55. package/dist/ui/UnifiedUIRenderer.d.ts +184 -0
  56. package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -0
  57. package/dist/ui/UnifiedUIRenderer.js +567 -0
  58. package/dist/ui/UnifiedUIRenderer.js.map +1 -0
  59. package/dist/ui/display.d.ts +100 -173
  60. package/dist/ui/display.d.ts.map +1 -1
  61. package/dist/ui/display.js +359 -927
  62. package/dist/ui/display.js.map +1 -1
  63. package/dist/ui/errorFormatter.d.ts +1 -1
  64. package/dist/ui/errorFormatter.js +1 -1
  65. package/dist/ui/shortcutsHelp.d.ts +6 -6
  66. package/dist/ui/shortcutsHelp.js +6 -6
  67. package/dist/ui/streamingFormatter.d.ts +2 -5
  68. package/dist/ui/streamingFormatter.d.ts.map +1 -1
  69. package/dist/ui/streamingFormatter.js +9 -33
  70. package/dist/ui/streamingFormatter.js.map +1 -1
  71. package/dist/ui/textHighlighter.d.ts +8 -8
  72. package/dist/ui/textHighlighter.js +9 -9
  73. package/dist/ui/textHighlighter.js.map +1 -1
  74. package/dist/ui/theme.d.ts +2 -2
  75. package/dist/ui/theme.js +4 -4
  76. package/dist/ui/theme.js.map +1 -1
  77. package/dist/ui/toolDisplay.d.ts +8 -8
  78. package/dist/ui/toolDisplay.js +8 -8
  79. package/package.json +1 -1
  80. package/dist/shell/terminalInput.d.ts +0 -619
  81. package/dist/shell/terminalInput.d.ts.map +0 -1
  82. package/dist/shell/terminalInput.js +0 -2699
  83. package/dist/shell/terminalInput.js.map +0 -1
@@ -4,9 +4,9 @@ import { promisify } from 'node:util';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
6
  import { display } from '../ui/display.js';
7
- import { isPlainOutputMode } from '../ui/outputMode.js';
8
7
  import { theme } from '../ui/theme.js';
9
8
  import { StreamingResponseFormatter } from '../ui/streamingFormatter.js';
9
+ import { UnifiedUIRenderer } from '../ui/UnifiedUIRenderer.js';
10
10
  import { getContextWindowTokens } from '../core/contextWindow.js';
11
11
  import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
12
12
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
@@ -174,6 +174,8 @@ export class InteractiveShell {
174
174
  alternateScreenEnabled;
175
175
  welcomeShown = false;
176
176
  scrollbackRestored = false;
177
+ uiRenderer;
178
+ useUnifiedRenderer;
177
179
  constructor(config) {
178
180
  this.profile = config.profile;
179
181
  this.profileLabel = config.profileLabel;
@@ -270,6 +272,10 @@ export class InteractiveShell {
270
272
  onToggleThinking: () => this.cycleThinkingMode(),
271
273
  onClearContext: () => this.handleClearContext(),
272
274
  });
275
+ const canUseRenderer = output.isTTY;
276
+ this.uiRenderer = canUseRenderer ? new UnifiedUIRenderer(output, input) : null;
277
+ this.useUnifiedRenderer = !!this.uiRenderer;
278
+ display.setRenderer(this.uiRenderer);
273
279
  // Initialize Alpha Zero 2 metrics tracking
274
280
  this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
275
281
  this.setupStatusTracking();
@@ -292,7 +298,8 @@ export class InteractiveShell {
292
298
  }
293
299
  // Render chat box immediately using the streaming UI lifecycle
294
300
  this.refreshControlBar();
295
- this.terminalInput.forceRender();
301
+ this.syncRendererInput();
302
+ this.uiRenderer?.render();
296
303
  this.rebuildAgent();
297
304
  this.setupHandlers();
298
305
  this.refreshSessionContext();
@@ -393,7 +400,7 @@ export class InteractiveShell {
393
400
  const model = `${this.sessionState.model} @ ${this.providerLabel(this.sessionState.provider)}`;
394
401
  const workspace = this.workingDir;
395
402
  const lines = [
396
- 'Claude Code',
403
+ 'Erosolar-CLI',
397
404
  welcome,
398
405
  model,
399
406
  workspace,
@@ -401,6 +408,20 @@ export class InteractiveShell {
401
408
  ];
402
409
  return lines.join('\n');
403
410
  }
411
+ syncRendererInput() {
412
+ // No-op: UnifiedUIRenderer handles its own input state now
413
+ // The new architecture doesn't require separate input syncing
414
+ }
415
+ pushUiEvent(type, content) {
416
+ if (!content) {
417
+ return;
418
+ }
419
+ if (this.useUnifiedRenderer && this.uiRenderer) {
420
+ this.uiRenderer.addEvent(type, content);
421
+ return;
422
+ }
423
+ display.stream(content);
424
+ }
404
425
  /**
405
426
  * Stream a content block as a single event, isolating it from adjacent output.
406
427
  */
@@ -409,6 +430,10 @@ export class InteractiveShell {
409
430
  if (!normalized) {
410
431
  return;
411
432
  }
433
+ if (this.useUnifiedRenderer && this.uiRenderer) {
434
+ this.uiRenderer.addEvent('banner', normalized);
435
+ return;
436
+ }
412
437
  const bullet = `${theme.info('⏺')} `;
413
438
  const indent = ' ';
414
439
  const lines = normalized.split('\n').map(line => line.trimEnd());
@@ -416,11 +441,18 @@ export class InteractiveShell {
416
441
  .map((line, index) => (index === 0 ? `${bullet}${line}` : `${indent}${line}`))
417
442
  .join('\n');
418
443
  const block = `\n${formatted}\n`;
419
- this.terminalInput.streamContent(block);
444
+ this.pushUiEvent('raw', block);
420
445
  }
421
446
  async start(initialPrompt) {
422
447
  // Always use the scroll-region streaming layout from the outset
423
- this.terminalInput.enterStreamingScrollRegion({ statusMessage: 'Ready for prompts' });
448
+ if (this.useUnifiedRenderer && this.uiRenderer) {
449
+ this.uiRenderer.initialize();
450
+ }
451
+ else {
452
+ this.terminalInput.clearScreen();
453
+ this.terminalInput.resetContentPosition();
454
+ }
455
+ this.syncRendererInput();
424
456
  this.showWelcomeBanner();
425
457
  if (initialPrompt) {
426
458
  await this.processInputBlock(initialPrompt);
@@ -428,7 +460,7 @@ export class InteractiveShell {
428
460
  }
429
461
  this.showLaunchCommandPalette();
430
462
  // Ensure the terminal input is visible
431
- this.terminalInput.render();
463
+ this.syncRendererInput();
432
464
  }
433
465
  showLaunchCommandPalette() {
434
466
  // Disabled: Quick commands palette takes up too much space
@@ -547,6 +579,7 @@ export class InteractiveShell {
547
579
  */
548
580
  handleInputChange(text) {
549
581
  this.currentInput = text;
582
+ this.syncRendererInput();
550
583
  if (text.length > 0) {
551
584
  this.resetCtrlCSequence();
552
585
  }
@@ -568,7 +601,7 @@ export class InteractiveShell {
568
601
  display.showSystemMessage('✏️ Display edits mode enabled.');
569
602
  }
570
603
  }
571
- this.terminalInput.render();
604
+ this.syncRendererInput();
572
605
  }
573
606
  toggleVerificationMode() {
574
607
  this.setVerificationMode(!this.verificationEnabled, 'shortcut');
@@ -689,7 +722,7 @@ export class InteractiveShell {
689
722
  if (['n', 'no', 'cancel', '/cancel'].includes(lower)) {
690
723
  this.pendingPermissionInput = null;
691
724
  display.showInfo('Request cancelled.');
692
- this.terminalInput.render();
725
+ this.syncRendererInput();
693
726
  return null;
694
727
  }
695
728
  // Treat any other input as a replacement request that also needs confirmation
@@ -707,7 +740,7 @@ export class InteractiveShell {
707
740
  ]
708
741
  .filter(Boolean)
709
742
  .join('\n'));
710
- this.terminalInput.render();
743
+ this.syncRendererInput();
711
744
  }
712
745
  /**
713
746
  * Handle Ctrl+C presses in three stages:
@@ -772,6 +805,7 @@ export class InteractiveShell {
772
805
  this.stopStreamingHeartbeat('quit', { quiet: true });
773
806
  this.endAiRuntime();
774
807
  this.uiUpdates.dispose();
808
+ this.uiRenderer?.cleanup();
775
809
  this.clearPromptRefreshTimer();
776
810
  this.teardownStatusTracking();
777
811
  // Clear global AI enhancer (explore tool will work offline after this)
@@ -831,26 +865,26 @@ export class InteractiveShell {
831
865
  const trimmed = input.trim();
832
866
  if (!trimmed) {
833
867
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
834
- this.terminalInput.render();
868
+ this.syncRendererInput();
835
869
  return;
836
870
  }
837
871
  const normalized = trimmed.toLowerCase();
838
872
  if (normalized === 'cancel') {
839
873
  this.pendingInteraction = null;
840
874
  display.showInfo('Tool selection cancelled.');
841
- this.terminalInput.render();
875
+ this.syncRendererInput();
842
876
  return;
843
877
  }
844
878
  if (normalized === 'defaults') {
845
879
  pending.selection = buildEnabledToolSet(null);
846
880
  this.renderToolMenu(pending);
847
- this.terminalInput.render();
881
+ this.syncRendererInput();
848
882
  return;
849
883
  }
850
884
  if (normalized === 'save') {
851
885
  await this.persistToolSelection(pending);
852
886
  this.pendingInteraction = null;
853
- this.terminalInput.render();
887
+ this.syncRendererInput();
854
888
  return;
855
889
  }
856
890
  const choice = Number.parseInt(trimmed, 10);
@@ -873,11 +907,11 @@ export class InteractiveShell {
873
907
  }
874
908
  this.renderToolMenu(pending);
875
909
  }
876
- this.terminalInput.render();
910
+ this.syncRendererInput();
877
911
  return;
878
912
  }
879
913
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
880
- this.terminalInput.render();
914
+ this.syncRendererInput();
881
915
  }
882
916
  async persistToolSelection(interaction) {
883
917
  if (setsEqual(interaction.selection, interaction.initialSelection)) {
@@ -905,36 +939,36 @@ export class InteractiveShell {
905
939
  if (!this.agentMenu) {
906
940
  this.pendingInteraction = null;
907
941
  display.showWarning('Agent selection is unavailable in this CLI.');
908
- this.terminalInput.render();
942
+ this.syncRendererInput();
909
943
  return;
910
944
  }
911
945
  const trimmed = input.trim();
912
946
  if (!trimmed) {
913
947
  display.showWarning('Enter a number or type "cancel".');
914
- this.terminalInput.render();
948
+ this.syncRendererInput();
915
949
  return;
916
950
  }
917
951
  if (trimmed.toLowerCase() === 'cancel') {
918
952
  this.pendingInteraction = null;
919
953
  display.showInfo('Agent selection cancelled.');
920
- this.terminalInput.render();
954
+ this.syncRendererInput();
921
955
  return;
922
956
  }
923
957
  const choice = Number.parseInt(trimmed, 10);
924
958
  if (!Number.isFinite(choice)) {
925
959
  display.showWarning('Please enter a valid number.');
926
- this.terminalInput.render();
960
+ this.syncRendererInput();
927
961
  return;
928
962
  }
929
963
  const option = pending.options[choice - 1];
930
964
  if (!option) {
931
965
  display.showWarning('That option is not available.');
932
- this.terminalInput.render();
966
+ this.syncRendererInput();
933
967
  return;
934
968
  }
935
969
  await this.persistAgentSelection(option.name);
936
970
  this.pendingInteraction = null;
937
- this.terminalInput.render();
971
+ this.syncRendererInput();
938
972
  }
939
973
  async persistAgentSelection(profileName) {
940
974
  if (!this.agentMenu) {
@@ -1007,7 +1041,7 @@ export class InteractiveShell {
1007
1041
  lines.push(` ${theme.primary('[text]')} Submit your own solution instead`);
1008
1042
  lines.push('');
1009
1043
  display.showSystemMessage(lines.join('\n'));
1010
- this.terminalInput.render();
1044
+ this.syncRendererInput();
1011
1045
  }
1012
1046
  async handlePlanApprovalInput(input) {
1013
1047
  const pending = this.pendingInteraction;
@@ -1016,7 +1050,7 @@ export class InteractiveShell {
1016
1050
  const trimmed = input.trim();
1017
1051
  if (!trimmed) {
1018
1052
  display.showWarning('Enter a command or your own solution.');
1019
- this.terminalInput.render();
1053
+ this.syncRendererInput();
1020
1054
  return;
1021
1055
  }
1022
1056
  const lower = trimmed.toLowerCase();
@@ -1024,7 +1058,7 @@ export class InteractiveShell {
1024
1058
  if (lower === 'cancel' || lower === 'c') {
1025
1059
  this.pendingInteraction = null;
1026
1060
  display.showInfo('Plan cancelled. You can continue with a different approach.');
1027
- this.terminalInput.render();
1061
+ this.syncRendererInput();
1028
1062
  return;
1029
1063
  }
1030
1064
  // Select all
@@ -1044,7 +1078,7 @@ export class InteractiveShell {
1044
1078
  const selectedSteps = pending.steps.filter(s => pending.selectedSteps.has(s.id));
1045
1079
  if (selectedSteps.length === 0) {
1046
1080
  display.showWarning('No steps selected. Select steps or enter your own solution.');
1047
- this.terminalInput.render();
1081
+ this.syncRendererInput();
1048
1082
  return;
1049
1083
  }
1050
1084
  this.pendingInteraction = null;
@@ -1077,7 +1111,7 @@ export class InteractiveShell {
1077
1111
  return;
1078
1112
  }
1079
1113
  display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
1080
- this.terminalInput.render();
1114
+ this.syncRendererInput();
1081
1115
  }
1082
1116
  setupHandlers() {
1083
1117
  // Handle terminal resize
@@ -1085,7 +1119,7 @@ export class InteractiveShell {
1085
1119
  this.terminalInput.handleResize();
1086
1120
  });
1087
1121
  // Show initial input UI
1088
- this.terminalInput.render();
1122
+ this.syncRendererInput();
1089
1123
  }
1090
1124
  /**
1091
1125
  * Set up command autocomplete with all available slash commands.
@@ -1198,7 +1232,7 @@ export class InteractiveShell {
1198
1232
  thinkingHotkey: 'tab',
1199
1233
  });
1200
1234
  this.refreshStatusLine();
1201
- this.terminalInput.render();
1235
+ this.syncRendererInput();
1202
1236
  }
1203
1237
  writeLocked(content) {
1204
1238
  if (!content) {
@@ -1236,7 +1270,7 @@ export class InteractiveShell {
1236
1270
  /**
1237
1271
  * Refresh the status line in the persistent input area.
1238
1272
  * Uses combined status display: streaming label + override + main status.
1239
- * All three can be shown simultaneously (Claude Code style).
1273
+ * All three can be shown simultaneously (Erosolar-CLI style).
1240
1274
  */
1241
1275
  refreshStatusLine(forceRender = false) {
1242
1276
  // Set streaming label (spinner + label) - shows during streaming
@@ -1263,8 +1297,10 @@ export class InteractiveShell {
1263
1297
  model: this.sessionState.model,
1264
1298
  provider: this.providerLabel(this.sessionState.provider),
1265
1299
  });
1300
+ this.syncRendererInput();
1266
1301
  if (forceRender) {
1267
- this.terminalInput.render();
1302
+ this.syncRendererInput();
1303
+ this.uiRenderer?.render();
1268
1304
  }
1269
1305
  }
1270
1306
  /**
@@ -1323,7 +1359,8 @@ export class InteractiveShell {
1323
1359
  }
1324
1360
  requestPromptRefresh(force = false) {
1325
1361
  if (force) {
1326
- this.terminalInput.forceRender();
1362
+ this.syncRendererInput();
1363
+ this.uiRenderer?.render();
1327
1364
  return;
1328
1365
  }
1329
1366
  if (this.promptRefreshTimer) {
@@ -1331,7 +1368,7 @@ export class InteractiveShell {
1331
1368
  }
1332
1369
  this.promptRefreshTimer = setTimeout(() => {
1333
1370
  this.promptRefreshTimer = null;
1334
- this.terminalInput.render();
1371
+ this.syncRendererInput();
1335
1372
  }, 48);
1336
1373
  }
1337
1374
  clearPromptRefreshTimer() {
@@ -1345,7 +1382,6 @@ export class InteractiveShell {
1345
1382
  // Enter global streaming mode - blocks all non-streaming UI output
1346
1383
  enterStreamingMode();
1347
1384
  // Set up scroll region for streaming content
1348
- this.terminalInput.enterStreamingScrollRegion();
1349
1385
  this.uiUpdates.setMode('streaming');
1350
1386
  this.streamingHeartbeatStart = Date.now();
1351
1387
  this.streamingHeartbeatFrame = 0;
@@ -1412,19 +1448,14 @@ export class InteractiveShell {
1412
1448
  if (!chunk) {
1413
1449
  return;
1414
1450
  }
1415
- // Preserve raw output in plain/CI modes or non-TTY environments
1416
- if (isPlainOutputMode() || !output.isTTY) {
1417
- this.terminalInput.streamContent(chunk);
1418
- return;
1419
- }
1420
1451
  if (!this.streamingFormatter) {
1421
1452
  this.streamingFormatter = new StreamingResponseFormatter(output.columns ?? undefined);
1422
- this.terminalInput.streamContent(this.streamingFormatter.header());
1453
+ this.pushUiEvent('streaming', this.streamingFormatter.header());
1423
1454
  }
1424
1455
  const formatted = this.streamingFormatter.formatChunk(chunk);
1425
1456
  this.captureStreamingThought(chunk);
1426
1457
  if (formatted) {
1427
- this.terminalInput.streamContent(formatted);
1458
+ this.pushUiEvent('streaming', formatted);
1428
1459
  }
1429
1460
  }
1430
1461
  finishStreamingFormatter(note, options) {
@@ -1436,7 +1467,7 @@ export class InteractiveShell {
1436
1467
  mode: options?.mode ?? 'complete',
1437
1468
  });
1438
1469
  if (closing) {
1439
- this.terminalInput.streamContent(closing);
1470
+ this.pushUiEvent('streaming', closing);
1440
1471
  }
1441
1472
  if (this.streamingThoughtBuffer.trim()) {
1442
1473
  this.ui.controller.recordAssistantThought(this.streamingThoughtBuffer.trim());
@@ -1488,7 +1519,7 @@ export class InteractiveShell {
1488
1519
  else {
1489
1520
  this.setIdleStatus();
1490
1521
  }
1491
- this.terminalInput.render();
1522
+ this.syncRendererInput();
1492
1523
  }
1493
1524
  enqueueFollowUpAction(action) {
1494
1525
  this.followUpQueue.push(action);
@@ -1507,7 +1538,7 @@ export class InteractiveShell {
1507
1538
  this.refreshQueueIndicators();
1508
1539
  this.scheduleQueueProcessing();
1509
1540
  // Re-show the prompt so user can continue typing more follow-ups
1510
- this.terminalInput.render();
1541
+ this.syncRendererInput();
1511
1542
  }
1512
1543
  scheduleQueueProcessing() {
1513
1544
  if (!this.followUpQueue.length) {
@@ -1562,12 +1593,12 @@ export class InteractiveShell {
1562
1593
  }
1563
1594
  if (lower === 'clear') {
1564
1595
  display.clear();
1565
- this.terminalInput.render();
1596
+ this.syncRendererInput();
1566
1597
  return;
1567
1598
  }
1568
1599
  if (lower === 'help') {
1569
1600
  this.showHelp();
1570
- this.terminalInput.render();
1601
+ this.syncRendererInput();
1571
1602
  return;
1572
1603
  }
1573
1604
  if (trimmed.startsWith('/')) {
@@ -1577,12 +1608,12 @@ export class InteractiveShell {
1577
1608
  // Check for continuous/infinite loop commands
1578
1609
  if (this.isContinuousCommand(trimmed)) {
1579
1610
  await this.processContinuousRequest(trimmed);
1580
- this.terminalInput.render();
1611
+ this.syncRendererInput();
1581
1612
  return;
1582
1613
  }
1583
1614
  // Direct execution for all inputs, including multi-line pastes
1584
1615
  await this.processRequest(trimmed);
1585
- this.terminalInput.render();
1616
+ this.syncRendererInput();
1586
1617
  }
1587
1618
  /**
1588
1619
  * Check if the command is a continuous/infinite loop command
@@ -1627,7 +1658,7 @@ export class InteractiveShell {
1627
1658
  switch (this.pendingInteraction.type) {
1628
1659
  case 'model-loading':
1629
1660
  display.showInfo('Still fetching model options. Please wait a moment.');
1630
- this.terminalInput.render();
1661
+ this.syncRendererInput();
1631
1662
  return true;
1632
1663
  case 'model-provider':
1633
1664
  await this.handleModelProviderSelection(input);
@@ -1658,7 +1689,7 @@ export class InteractiveShell {
1658
1689
  const [command] = input.split(/\s+/);
1659
1690
  if (!command) {
1660
1691
  display.showWarning('Enter a slash command.');
1661
- this.terminalInput.render();
1692
+ this.syncRendererInput();
1662
1693
  return;
1663
1694
  }
1664
1695
  switch (command) {
@@ -1761,7 +1792,7 @@ export class InteractiveShell {
1761
1792
  case '/discover':
1762
1793
  await this.discoverModelsCommand();
1763
1794
  break;
1764
- // Claude Code style commands
1795
+ // Erosolar-CLI style commands
1765
1796
  case '/rewind':
1766
1797
  await this.handleRewindCommand(input);
1767
1798
  break;
@@ -1816,7 +1847,7 @@ export class InteractiveShell {
1816
1847
  }
1817
1848
  break;
1818
1849
  }
1819
- this.terminalInput.render();
1850
+ this.syncRendererInput();
1820
1851
  }
1821
1852
  async tryCustomSlashCommand(command, fullInput) {
1822
1853
  const custom = this.customCommandMap.get(command);
@@ -2142,7 +2173,7 @@ export class InteractiveShell {
2142
2173
  display.showSystemMessage(`${headline}\n${theme.ui.muted(descriptions[this.thinkingMode])}`);
2143
2174
  }
2144
2175
  handleShortcutsCommand() {
2145
- // Display keyboard shortcuts help (Claude Code style)
2176
+ // Display keyboard shortcuts help (Erosolar-CLI style)
2146
2177
  display.showSystemMessage(formatShortcutsHelp());
2147
2178
  }
2148
2179
  showFileChangeSummary() {
@@ -3465,7 +3496,7 @@ export class InteractiveShell {
3465
3496
  }
3466
3497
  this.setAutoContinueMode(value === 'on', 'command');
3467
3498
  }
3468
- // ==================== Claude Code Style Commands ====================
3499
+ // ==================== Erosolar-CLI Style Commands ====================
3469
3500
  async handleRewindCommand(_input) {
3470
3501
  const lines = [];
3471
3502
  lines.push(theme.bold('Rewind / Checkpoint System'));
@@ -3624,7 +3655,7 @@ export class InteractiveShell {
3624
3655
  display.clear();
3625
3656
  clearAutosaveSnapshot(this.profile);
3626
3657
  display.showInfo('Conversation cleared. Starting fresh.');
3627
- this.terminalInput.render();
3658
+ this.syncRendererInput();
3628
3659
  }
3629
3660
  async handleResumeCommand(input) {
3630
3661
  const tokens = input.split(/\s+/).slice(1);
@@ -3752,7 +3783,7 @@ export class InteractiveShell {
3752
3783
  display.showInfo('Compacting conversation context...');
3753
3784
  await this.performContextCompaction('Manual /compact request');
3754
3785
  }
3755
- // ==================== End Claude Code Style Commands ====================
3786
+ // ==================== End Erosolar-CLI Style Commands ====================
3756
3787
  updateActiveSession(summary, remember = false) {
3757
3788
  this.activeSessionId = summary?.id ?? null;
3758
3789
  this.activeSessionTitle = summary?.title ?? null;
@@ -3927,7 +3958,7 @@ export class InteractiveShell {
3927
3958
  if (!providerOptions.length) {
3928
3959
  display.showWarning('No providers are available.');
3929
3960
  this.pendingInteraction = null;
3930
- this.terminalInput.render();
3961
+ this.syncRendererInput();
3931
3962
  return;
3932
3963
  }
3933
3964
  const lines = [
@@ -3948,7 +3979,7 @@ export class InteractiveShell {
3948
3979
  catch (error) {
3949
3980
  display.showError('Failed to load model list. Try again in a moment.', error);
3950
3981
  this.pendingInteraction = null;
3951
- this.terminalInput.render();
3982
+ this.syncRendererInput();
3952
3983
  }
3953
3984
  }
3954
3985
  buildProviderOptions() {
@@ -4503,29 +4534,29 @@ export class InteractiveShell {
4503
4534
  const trimmed = input.trim();
4504
4535
  if (!trimmed) {
4505
4536
  display.showWarning('Enter a number or type cancel.');
4506
- this.terminalInput.render();
4537
+ this.syncRendererInput();
4507
4538
  return;
4508
4539
  }
4509
4540
  if (trimmed.toLowerCase() === 'cancel') {
4510
4541
  this.pendingInteraction = null;
4511
4542
  display.showInfo('Model selection cancelled.');
4512
- this.terminalInput.render();
4543
+ this.syncRendererInput();
4513
4544
  return;
4514
4545
  }
4515
4546
  const choice = Number.parseInt(trimmed, 10);
4516
4547
  if (!Number.isFinite(choice)) {
4517
4548
  display.showWarning('Please enter a valid number.');
4518
- this.terminalInput.render();
4549
+ this.syncRendererInput();
4519
4550
  return;
4520
4551
  }
4521
4552
  const option = pending.options[choice - 1];
4522
4553
  if (!option) {
4523
4554
  display.showWarning('That option is not available.');
4524
- this.terminalInput.render();
4555
+ this.syncRendererInput();
4525
4556
  return;
4526
4557
  }
4527
4558
  this.showProviderModels(option);
4528
- this.terminalInput.render();
4559
+ this.syncRendererInput();
4529
4560
  }
4530
4561
  async handleModelSelection(input) {
4531
4562
  const pending = this.pendingInteraction;
@@ -4535,35 +4566,35 @@ export class InteractiveShell {
4535
4566
  const trimmed = input.trim();
4536
4567
  if (!trimmed) {
4537
4568
  display.showWarning('Enter a number, type "back", or type "cancel".');
4538
- this.terminalInput.render();
4569
+ this.syncRendererInput();
4539
4570
  return;
4540
4571
  }
4541
4572
  if (trimmed.toLowerCase() === 'back') {
4542
4573
  this.showModelMenu();
4543
- this.terminalInput.render();
4574
+ this.syncRendererInput();
4544
4575
  return;
4545
4576
  }
4546
4577
  if (trimmed.toLowerCase() === 'cancel') {
4547
4578
  this.pendingInteraction = null;
4548
4579
  display.showInfo('Model selection cancelled.');
4549
- this.terminalInput.render();
4580
+ this.syncRendererInput();
4550
4581
  return;
4551
4582
  }
4552
4583
  const choice = Number.parseInt(trimmed, 10);
4553
4584
  if (!Number.isFinite(choice)) {
4554
4585
  display.showWarning('Please enter a valid number.');
4555
- this.terminalInput.render();
4586
+ this.syncRendererInput();
4556
4587
  return;
4557
4588
  }
4558
4589
  const preset = pending.options[choice - 1];
4559
4590
  if (!preset) {
4560
4591
  display.showWarning('That option is not available.');
4561
- this.terminalInput.render();
4592
+ this.syncRendererInput();
4562
4593
  return;
4563
4594
  }
4564
4595
  this.pendingInteraction = null;
4565
4596
  await this.applyModelPreset(preset);
4566
- this.terminalInput.render();
4597
+ this.syncRendererInput();
4567
4598
  }
4568
4599
  async applyModelPreset(preset) {
4569
4600
  try {
@@ -4596,30 +4627,30 @@ export class InteractiveShell {
4596
4627
  const trimmed = input.trim();
4597
4628
  if (!trimmed) {
4598
4629
  display.showWarning('Enter a number or type cancel.');
4599
- this.terminalInput.render();
4630
+ this.syncRendererInput();
4600
4631
  return;
4601
4632
  }
4602
4633
  if (trimmed.toLowerCase() === 'cancel') {
4603
4634
  this.pendingInteraction = null;
4604
4635
  display.showInfo('Secret management cancelled.');
4605
- this.terminalInput.render();
4636
+ this.syncRendererInput();
4606
4637
  return;
4607
4638
  }
4608
4639
  const choice = Number.parseInt(trimmed, 10);
4609
4640
  if (!Number.isFinite(choice)) {
4610
4641
  display.showWarning('Please enter a valid number.');
4611
- this.terminalInput.render();
4642
+ this.syncRendererInput();
4612
4643
  return;
4613
4644
  }
4614
4645
  const secret = pending.options[choice - 1];
4615
4646
  if (!secret) {
4616
4647
  display.showWarning('That option is not available.');
4617
- this.terminalInput.render();
4648
+ this.syncRendererInput();
4618
4649
  return;
4619
4650
  }
4620
4651
  display.showSystemMessage(`Enter a new value for ${secret.label} or type "cancel".`);
4621
4652
  this.pendingInteraction = { type: 'secret-input', secret };
4622
- this.terminalInput.render();
4653
+ this.syncRendererInput();
4623
4654
  }
4624
4655
  async handleSecretInput(input) {
4625
4656
  const pending = this.pendingInteraction;
@@ -4629,14 +4660,14 @@ export class InteractiveShell {
4629
4660
  const trimmed = input.trim();
4630
4661
  if (!trimmed) {
4631
4662
  display.showWarning('Enter a value or type cancel.');
4632
- this.terminalInput.render();
4663
+ this.syncRendererInput();
4633
4664
  return;
4634
4665
  }
4635
4666
  if (trimmed.toLowerCase() === 'cancel') {
4636
4667
  this.pendingInteraction = null;
4637
4668
  this.pendingSecretRetry = null;
4638
4669
  display.showInfo('Secret unchanged.');
4639
- this.terminalInput.render();
4670
+ this.syncRendererInput();
4640
4671
  return;
4641
4672
  }
4642
4673
  try {
@@ -4660,7 +4691,7 @@ export class InteractiveShell {
4660
4691
  this.pendingInteraction = null;
4661
4692
  this.pendingSecretRetry = null;
4662
4693
  }
4663
- this.terminalInput.render();
4694
+ this.syncRendererInput();
4664
4695
  }
4665
4696
  async processRequest(request) {
4666
4697
  if (this.isProcessing) {
@@ -4680,7 +4711,8 @@ export class InteractiveShell {
4680
4711
  this.uiUpdates.setMode('processing');
4681
4712
  this.terminalInput.setStreaming(true);
4682
4713
  // Keep the persistent input/control bar active as we transition into streaming.
4683
- this.terminalInput.forceRender();
4714
+ this.syncRendererInput();
4715
+ this.uiRenderer?.render();
4684
4716
  const requestStartTime = Date.now(); // Alpha Zero 2 timing
4685
4717
  // Clear previous parallel agents and start fresh for new request
4686
4718
  const parallelManager = getParallelAgentManager();
@@ -4766,7 +4798,7 @@ export class InteractiveShell {
4766
4798
  this.updateStatusMessage(null);
4767
4799
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
4768
4800
  // CRITICAL: Ensure readline prompt is active for user input
4769
- // Claude Code style: New prompt naturally appears at bottom
4801
+ // Erosolar-CLI style: New prompt naturally appears at bottom
4770
4802
  this.ensureReadlineReady();
4771
4803
  this.scheduleQueueProcessing();
4772
4804
  this.refreshQueueIndicators();
@@ -4996,7 +5028,7 @@ What's the next action?`;
4996
5028
  this.updateStatusMessage(null);
4997
5029
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
4998
5030
  // CRITICAL: Ensure readline prompt is active for user input
4999
- // Claude Code style: New prompt naturally appears at bottom
5031
+ // Erosolar-CLI style: New prompt naturally appears at bottom
5000
5032
  this.ensureReadlineReady();
5001
5033
  this.scheduleQueueProcessing();
5002
5034
  this.refreshQueueIndicators();
@@ -5607,7 +5639,7 @@ Return ONLY JSON array:
5607
5639
  void this.runAutoQualityChecks('final-response', finalContent);
5608
5640
  }
5609
5641
  else {
5610
- // Non-final message = narrative text before tool calls (Claude Code style)
5642
+ // Non-final message = narrative text before tool calls (Erosolar-CLI style)
5611
5643
  // Stop spinner and show the narrative text directly
5612
5644
  display.stopThinking();
5613
5645
  // Skip display if content was already streamed to avoid double-display
@@ -5654,19 +5686,19 @@ Return ONLY JSON array:
5654
5686
  this.updateContextUsage(percentage);
5655
5687
  }
5656
5688
  // Ensure prompt remains visible at bottom after context messages
5657
- this.terminalInput.render();
5689
+ this.syncRendererInput();
5658
5690
  },
5659
5691
  onContinueAfterRecovery: () => {
5660
5692
  // Update UI to show we're continuing after context recovery
5661
5693
  display.showSystemMessage(`🔄 Continuing after context recovery...`);
5662
5694
  this.updateStatusMessage('Retrying with reduced context...');
5663
- this.terminalInput.render();
5695
+ this.syncRendererInput();
5664
5696
  },
5665
5697
  onAutoContinue: (attempt, maxAttempts, _message) => {
5666
5698
  // Show auto-continue progress in UI
5667
5699
  display.showSystemMessage(`🔄 Auto-continue (${attempt}/${maxAttempts}): Model expressed intent, prompting to act...`);
5668
5700
  this.updateStatusMessage('Auto-continuing...');
5669
- this.terminalInput.render();
5701
+ this.syncRendererInput();
5670
5702
  },
5671
5703
  onCancelled: () => {
5672
5704
  // Update UI to show operation was cancelled