erosolar-cli 1.7.429 → 1.7.431

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 (87) 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/preferences.d.ts +3 -1
  18. package/dist/core/preferences.d.ts.map +1 -1
  19. package/dist/core/preferences.js +4 -2
  20. package/dist/core/preferences.js.map +1 -1
  21. package/dist/core/validationRunner.d.ts +1 -1
  22. package/dist/core/validationRunner.js +1 -1
  23. package/dist/shell/interactiveShell.d.ts +5 -1
  24. package/dist/shell/interactiveShell.d.ts.map +1 -1
  25. package/dist/shell/interactiveShell.js +120 -88
  26. package/dist/shell/interactiveShell.js.map +1 -1
  27. package/dist/shell/systemPrompt.d.ts.map +1 -1
  28. package/dist/shell/systemPrompt.js +14 -34
  29. package/dist/shell/systemPrompt.js.map +1 -1
  30. package/dist/shell/terminalInputAdapter.d.ts +77 -85
  31. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  32. package/dist/shell/terminalInputAdapter.js +163 -223
  33. package/dist/shell/terminalInputAdapter.js.map +1 -1
  34. package/dist/shell/vimMode.d.ts +1 -1
  35. package/dist/shell/vimMode.js +1 -1
  36. package/dist/tools/buildTools.d.ts +1 -1
  37. package/dist/tools/buildTools.js +1 -1
  38. package/dist/tools/diffUtils.d.ts +2 -2
  39. package/dist/tools/diffUtils.js +2 -2
  40. package/dist/tools/editTools.js +4 -4
  41. package/dist/tools/editTools.js.map +1 -1
  42. package/dist/tools/localExplore.d.ts +3 -3
  43. package/dist/tools/localExplore.js +3 -3
  44. package/dist/tools/skillTools.js +2 -2
  45. package/dist/tools/skillTools.js.map +1 -1
  46. package/dist/tools/validationTools.js +1 -1
  47. package/dist/ui/DisplayEventQueue.d.ts +99 -0
  48. package/dist/ui/DisplayEventQueue.d.ts.map +1 -0
  49. package/dist/ui/DisplayEventQueue.js +167 -0
  50. package/dist/ui/DisplayEventQueue.js.map +1 -0
  51. package/dist/ui/SequentialRenderer.d.ts +69 -0
  52. package/dist/ui/SequentialRenderer.d.ts.map +1 -0
  53. package/dist/ui/SequentialRenderer.js +137 -0
  54. package/dist/ui/SequentialRenderer.js.map +1 -0
  55. package/dist/ui/ShellUIAdapter.d.ts +18 -6
  56. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  57. package/dist/ui/ShellUIAdapter.js +65 -14
  58. package/dist/ui/ShellUIAdapter.js.map +1 -1
  59. package/dist/ui/UnifiedUIRenderer.d.ts +188 -0
  60. package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -0
  61. package/dist/ui/UnifiedUIRenderer.js +581 -0
  62. package/dist/ui/UnifiedUIRenderer.js.map +1 -0
  63. package/dist/ui/display.d.ts +100 -173
  64. package/dist/ui/display.d.ts.map +1 -1
  65. package/dist/ui/display.js +364 -926
  66. package/dist/ui/display.js.map +1 -1
  67. package/dist/ui/errorFormatter.d.ts +1 -1
  68. package/dist/ui/errorFormatter.js +1 -1
  69. package/dist/ui/shortcutsHelp.d.ts +6 -6
  70. package/dist/ui/shortcutsHelp.js +6 -6
  71. package/dist/ui/streamingFormatter.d.ts +2 -5
  72. package/dist/ui/streamingFormatter.d.ts.map +1 -1
  73. package/dist/ui/streamingFormatter.js +9 -33
  74. package/dist/ui/streamingFormatter.js.map +1 -1
  75. package/dist/ui/textHighlighter.d.ts +8 -8
  76. package/dist/ui/textHighlighter.js +9 -9
  77. package/dist/ui/textHighlighter.js.map +1 -1
  78. package/dist/ui/theme.d.ts +2 -2
  79. package/dist/ui/theme.js +4 -4
  80. package/dist/ui/theme.js.map +1 -1
  81. package/dist/ui/toolDisplay.d.ts +8 -8
  82. package/dist/ui/toolDisplay.js +8 -8
  83. package/package.json +1 -1
  84. package/dist/shell/terminalInput.d.ts +0 -619
  85. package/dist/shell/terminalInput.d.ts.map +0 -1
  86. package/dist/shell/terminalInput.js +0 -2699
  87. 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;
@@ -257,7 +259,12 @@ export class InteractiveShell {
257
259
  for (const definition of createSkillTools({ repository: this.skillRepository })) {
258
260
  this.skillToolHandlers.set(definition.name, definition.handler);
259
261
  }
260
- // Initialize unified terminal input handler
262
+ // Create unified UI renderer first (single instance shared by all components)
263
+ const canUseRenderer = output.isTTY;
264
+ this.uiRenderer = canUseRenderer ? new UnifiedUIRenderer(output, input) : null;
265
+ this.useUnifiedRenderer = !!this.uiRenderer;
266
+ display.setRenderer(this.uiRenderer);
267
+ // Initialize unified terminal input handler with shared renderer
261
268
  this.terminalInput = new TerminalInputAdapter(input, output, {
262
269
  onSubmit: (text) => this.processInput(text),
263
270
  onQueue: (text) => this.handleQueuedInput(text),
@@ -269,7 +276,7 @@ export class InteractiveShell {
269
276
  onToggleAutoContinue: () => this.toggleAutoContinueMode(),
270
277
  onToggleThinking: () => this.cycleThinkingMode(),
271
278
  onClearContext: () => this.handleClearContext(),
272
- });
279
+ }, this.uiRenderer ?? undefined);
273
280
  // Initialize Alpha Zero 2 metrics tracking
274
281
  this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
275
282
  this.setupStatusTracking();
@@ -292,7 +299,8 @@ export class InteractiveShell {
292
299
  }
293
300
  // Render chat box immediately using the streaming UI lifecycle
294
301
  this.refreshControlBar();
295
- this.terminalInput.forceRender();
302
+ this.syncRendererInput();
303
+ this.uiRenderer?.render();
296
304
  this.rebuildAgent();
297
305
  this.setupHandlers();
298
306
  this.refreshSessionContext();
@@ -393,7 +401,7 @@ export class InteractiveShell {
393
401
  const model = `${this.sessionState.model} @ ${this.providerLabel(this.sessionState.provider)}`;
394
402
  const workspace = this.workingDir;
395
403
  const lines = [
396
- 'Claude Code',
404
+ 'Erosolar-CLI',
397
405
  welcome,
398
406
  model,
399
407
  workspace,
@@ -401,6 +409,20 @@ export class InteractiveShell {
401
409
  ];
402
410
  return lines.join('\n');
403
411
  }
412
+ syncRendererInput() {
413
+ // No-op: UnifiedUIRenderer handles its own input state now
414
+ // The new architecture doesn't require separate input syncing
415
+ }
416
+ pushUiEvent(type, content) {
417
+ if (!content) {
418
+ return;
419
+ }
420
+ if (this.useUnifiedRenderer && this.uiRenderer) {
421
+ this.uiRenderer.addEvent(type, content);
422
+ return;
423
+ }
424
+ display.stream(content);
425
+ }
404
426
  /**
405
427
  * Stream a content block as a single event, isolating it from adjacent output.
406
428
  */
@@ -409,6 +431,10 @@ export class InteractiveShell {
409
431
  if (!normalized) {
410
432
  return;
411
433
  }
434
+ if (this.useUnifiedRenderer && this.uiRenderer) {
435
+ this.uiRenderer.addEvent('banner', normalized);
436
+ return;
437
+ }
412
438
  const bullet = `${theme.info('⏺')} `;
413
439
  const indent = ' ';
414
440
  const lines = normalized.split('\n').map(line => line.trimEnd());
@@ -416,11 +442,18 @@ export class InteractiveShell {
416
442
  .map((line, index) => (index === 0 ? `${bullet}${line}` : `${indent}${line}`))
417
443
  .join('\n');
418
444
  const block = `\n${formatted}\n`;
419
- this.terminalInput.streamContent(block);
445
+ this.pushUiEvent('raw', block);
420
446
  }
421
447
  async start(initialPrompt) {
422
448
  // Always use the scroll-region streaming layout from the outset
423
- this.terminalInput.enterStreamingScrollRegion({ statusMessage: 'Ready for prompts' });
449
+ if (this.useUnifiedRenderer && this.uiRenderer) {
450
+ this.uiRenderer.initialize();
451
+ }
452
+ else {
453
+ this.terminalInput.clearScreen();
454
+ this.terminalInput.resetContentPosition();
455
+ }
456
+ this.syncRendererInput();
424
457
  this.showWelcomeBanner();
425
458
  if (initialPrompt) {
426
459
  await this.processInputBlock(initialPrompt);
@@ -428,7 +461,7 @@ export class InteractiveShell {
428
461
  }
429
462
  this.showLaunchCommandPalette();
430
463
  // Ensure the terminal input is visible
431
- this.terminalInput.render();
464
+ this.syncRendererInput();
432
465
  }
433
466
  showLaunchCommandPalette() {
434
467
  // Disabled: Quick commands palette takes up too much space
@@ -547,6 +580,7 @@ export class InteractiveShell {
547
580
  */
548
581
  handleInputChange(text) {
549
582
  this.currentInput = text;
583
+ this.syncRendererInput();
550
584
  if (text.length > 0) {
551
585
  this.resetCtrlCSequence();
552
586
  }
@@ -568,7 +602,7 @@ export class InteractiveShell {
568
602
  display.showSystemMessage('✏️ Display edits mode enabled.');
569
603
  }
570
604
  }
571
- this.terminalInput.render();
605
+ this.syncRendererInput();
572
606
  }
573
607
  toggleVerificationMode() {
574
608
  this.setVerificationMode(!this.verificationEnabled, 'shortcut');
@@ -689,7 +723,7 @@ export class InteractiveShell {
689
723
  if (['n', 'no', 'cancel', '/cancel'].includes(lower)) {
690
724
  this.pendingPermissionInput = null;
691
725
  display.showInfo('Request cancelled.');
692
- this.terminalInput.render();
726
+ this.syncRendererInput();
693
727
  return null;
694
728
  }
695
729
  // Treat any other input as a replacement request that also needs confirmation
@@ -707,7 +741,7 @@ export class InteractiveShell {
707
741
  ]
708
742
  .filter(Boolean)
709
743
  .join('\n'));
710
- this.terminalInput.render();
744
+ this.syncRendererInput();
711
745
  }
712
746
  /**
713
747
  * Handle Ctrl+C presses in three stages:
@@ -772,6 +806,7 @@ export class InteractiveShell {
772
806
  this.stopStreamingHeartbeat('quit', { quiet: true });
773
807
  this.endAiRuntime();
774
808
  this.uiUpdates.dispose();
809
+ this.uiRenderer?.cleanup();
775
810
  this.clearPromptRefreshTimer();
776
811
  this.teardownStatusTracking();
777
812
  // Clear global AI enhancer (explore tool will work offline after this)
@@ -831,26 +866,26 @@ export class InteractiveShell {
831
866
  const trimmed = input.trim();
832
867
  if (!trimmed) {
833
868
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
834
- this.terminalInput.render();
869
+ this.syncRendererInput();
835
870
  return;
836
871
  }
837
872
  const normalized = trimmed.toLowerCase();
838
873
  if (normalized === 'cancel') {
839
874
  this.pendingInteraction = null;
840
875
  display.showInfo('Tool selection cancelled.');
841
- this.terminalInput.render();
876
+ this.syncRendererInput();
842
877
  return;
843
878
  }
844
879
  if (normalized === 'defaults') {
845
880
  pending.selection = buildEnabledToolSet(null);
846
881
  this.renderToolMenu(pending);
847
- this.terminalInput.render();
882
+ this.syncRendererInput();
848
883
  return;
849
884
  }
850
885
  if (normalized === 'save') {
851
886
  await this.persistToolSelection(pending);
852
887
  this.pendingInteraction = null;
853
- this.terminalInput.render();
888
+ this.syncRendererInput();
854
889
  return;
855
890
  }
856
891
  const choice = Number.parseInt(trimmed, 10);
@@ -873,11 +908,11 @@ export class InteractiveShell {
873
908
  }
874
909
  this.renderToolMenu(pending);
875
910
  }
876
- this.terminalInput.render();
911
+ this.syncRendererInput();
877
912
  return;
878
913
  }
879
914
  display.showWarning('Enter a number, "save", "defaults", or "cancel".');
880
- this.terminalInput.render();
915
+ this.syncRendererInput();
881
916
  }
882
917
  async persistToolSelection(interaction) {
883
918
  if (setsEqual(interaction.selection, interaction.initialSelection)) {
@@ -905,36 +940,36 @@ export class InteractiveShell {
905
940
  if (!this.agentMenu) {
906
941
  this.pendingInteraction = null;
907
942
  display.showWarning('Agent selection is unavailable in this CLI.');
908
- this.terminalInput.render();
943
+ this.syncRendererInput();
909
944
  return;
910
945
  }
911
946
  const trimmed = input.trim();
912
947
  if (!trimmed) {
913
948
  display.showWarning('Enter a number or type "cancel".');
914
- this.terminalInput.render();
949
+ this.syncRendererInput();
915
950
  return;
916
951
  }
917
952
  if (trimmed.toLowerCase() === 'cancel') {
918
953
  this.pendingInteraction = null;
919
954
  display.showInfo('Agent selection cancelled.');
920
- this.terminalInput.render();
955
+ this.syncRendererInput();
921
956
  return;
922
957
  }
923
958
  const choice = Number.parseInt(trimmed, 10);
924
959
  if (!Number.isFinite(choice)) {
925
960
  display.showWarning('Please enter a valid number.');
926
- this.terminalInput.render();
961
+ this.syncRendererInput();
927
962
  return;
928
963
  }
929
964
  const option = pending.options[choice - 1];
930
965
  if (!option) {
931
966
  display.showWarning('That option is not available.');
932
- this.terminalInput.render();
967
+ this.syncRendererInput();
933
968
  return;
934
969
  }
935
970
  await this.persistAgentSelection(option.name);
936
971
  this.pendingInteraction = null;
937
- this.terminalInput.render();
972
+ this.syncRendererInput();
938
973
  }
939
974
  async persistAgentSelection(profileName) {
940
975
  if (!this.agentMenu) {
@@ -1007,7 +1042,7 @@ export class InteractiveShell {
1007
1042
  lines.push(` ${theme.primary('[text]')} Submit your own solution instead`);
1008
1043
  lines.push('');
1009
1044
  display.showSystemMessage(lines.join('\n'));
1010
- this.terminalInput.render();
1045
+ this.syncRendererInput();
1011
1046
  }
1012
1047
  async handlePlanApprovalInput(input) {
1013
1048
  const pending = this.pendingInteraction;
@@ -1016,7 +1051,7 @@ export class InteractiveShell {
1016
1051
  const trimmed = input.trim();
1017
1052
  if (!trimmed) {
1018
1053
  display.showWarning('Enter a command or your own solution.');
1019
- this.terminalInput.render();
1054
+ this.syncRendererInput();
1020
1055
  return;
1021
1056
  }
1022
1057
  const lower = trimmed.toLowerCase();
@@ -1024,7 +1059,7 @@ export class InteractiveShell {
1024
1059
  if (lower === 'cancel' || lower === 'c') {
1025
1060
  this.pendingInteraction = null;
1026
1061
  display.showInfo('Plan cancelled. You can continue with a different approach.');
1027
- this.terminalInput.render();
1062
+ this.syncRendererInput();
1028
1063
  return;
1029
1064
  }
1030
1065
  // Select all
@@ -1044,7 +1079,7 @@ export class InteractiveShell {
1044
1079
  const selectedSteps = pending.steps.filter(s => pending.selectedSteps.has(s.id));
1045
1080
  if (selectedSteps.length === 0) {
1046
1081
  display.showWarning('No steps selected. Select steps or enter your own solution.');
1047
- this.terminalInput.render();
1082
+ this.syncRendererInput();
1048
1083
  return;
1049
1084
  }
1050
1085
  this.pendingInteraction = null;
@@ -1077,7 +1112,7 @@ export class InteractiveShell {
1077
1112
  return;
1078
1113
  }
1079
1114
  display.showWarning('Invalid input. Enter a step number, command (go/cancel/all/none), or your own solution.');
1080
- this.terminalInput.render();
1115
+ this.syncRendererInput();
1081
1116
  }
1082
1117
  setupHandlers() {
1083
1118
  // Handle terminal resize
@@ -1085,7 +1120,7 @@ export class InteractiveShell {
1085
1120
  this.terminalInput.handleResize();
1086
1121
  });
1087
1122
  // Show initial input UI
1088
- this.terminalInput.render();
1123
+ this.syncRendererInput();
1089
1124
  }
1090
1125
  /**
1091
1126
  * Set up command autocomplete with all available slash commands.
@@ -1198,7 +1233,7 @@ export class InteractiveShell {
1198
1233
  thinkingHotkey: 'tab',
1199
1234
  });
1200
1235
  this.refreshStatusLine();
1201
- this.terminalInput.render();
1236
+ this.syncRendererInput();
1202
1237
  }
1203
1238
  writeLocked(content) {
1204
1239
  if (!content) {
@@ -1236,7 +1271,7 @@ export class InteractiveShell {
1236
1271
  /**
1237
1272
  * Refresh the status line in the persistent input area.
1238
1273
  * Uses combined status display: streaming label + override + main status.
1239
- * All three can be shown simultaneously (Claude Code style).
1274
+ * All three can be shown simultaneously (Erosolar-CLI style).
1240
1275
  */
1241
1276
  refreshStatusLine(forceRender = false) {
1242
1277
  // Set streaming label (spinner + label) - shows during streaming
@@ -1263,8 +1298,10 @@ export class InteractiveShell {
1263
1298
  model: this.sessionState.model,
1264
1299
  provider: this.providerLabel(this.sessionState.provider),
1265
1300
  });
1301
+ this.syncRendererInput();
1266
1302
  if (forceRender) {
1267
- this.terminalInput.render();
1303
+ this.syncRendererInput();
1304
+ this.uiRenderer?.render();
1268
1305
  }
1269
1306
  }
1270
1307
  /**
@@ -1317,13 +1354,13 @@ export class InteractiveShell {
1317
1354
  }
1318
1355
  this.lastLoggedPrompt = normalized;
1319
1356
  this.lastLoggedPromptAt = now;
1320
- // Format with user prompt prefix and stream as its own event
1321
- const formatted = `${theme.user('>')} ${normalized}`;
1322
- this.streamEventBlock(formatted);
1357
+ // Note: User prompt display is now handled by UnifiedUIRenderer.displayUserPrompt()
1358
+ // This method just tracks the last prompt for deduplication purposes
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