erosolar-cli 1.7.253 → 1.7.254

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 (81) hide show
  1. package/README.md +148 -22
  2. package/dist/core/aiFlowOptimizer.d.ts +26 -0
  3. package/dist/core/aiFlowOptimizer.d.ts.map +1 -0
  4. package/dist/core/aiFlowOptimizer.js +31 -0
  5. package/dist/core/aiFlowOptimizer.js.map +1 -0
  6. package/dist/core/aiOptimizationEngine.d.ts +158 -0
  7. package/dist/core/aiOptimizationEngine.d.ts.map +1 -0
  8. package/dist/core/aiOptimizationEngine.js +428 -0
  9. package/dist/core/aiOptimizationEngine.js.map +1 -0
  10. package/dist/core/aiOptimizationIntegration.d.ts +93 -0
  11. package/dist/core/aiOptimizationIntegration.d.ts.map +1 -0
  12. package/dist/core/aiOptimizationIntegration.js +250 -0
  13. package/dist/core/aiOptimizationIntegration.js.map +1 -0
  14. package/dist/core/customCommands.d.ts +0 -1
  15. package/dist/core/customCommands.d.ts.map +1 -1
  16. package/dist/core/customCommands.js +0 -3
  17. package/dist/core/customCommands.js.map +1 -1
  18. package/dist/core/enhancedErrorRecovery.d.ts +100 -0
  19. package/dist/core/enhancedErrorRecovery.d.ts.map +1 -0
  20. package/dist/core/enhancedErrorRecovery.js +345 -0
  21. package/dist/core/enhancedErrorRecovery.js.map +1 -0
  22. package/dist/core/toolPreconditions.d.ts.map +1 -1
  23. package/dist/core/toolPreconditions.js +14 -0
  24. package/dist/core/toolPreconditions.js.map +1 -1
  25. package/dist/core/toolRuntime.d.ts.map +1 -1
  26. package/dist/core/toolRuntime.js +5 -0
  27. package/dist/core/toolRuntime.js.map +1 -1
  28. package/dist/core/toolValidation.d.ts.map +1 -1
  29. package/dist/core/toolValidation.js +3 -14
  30. package/dist/core/toolValidation.js.map +1 -1
  31. package/dist/mcp/sseClient.d.ts.map +1 -1
  32. package/dist/mcp/sseClient.js +18 -9
  33. package/dist/mcp/sseClient.js.map +1 -1
  34. package/dist/plugins/tools/build/buildPlugin.d.ts +6 -0
  35. package/dist/plugins/tools/build/buildPlugin.d.ts.map +1 -1
  36. package/dist/plugins/tools/build/buildPlugin.js +10 -4
  37. package/dist/plugins/tools/build/buildPlugin.js.map +1 -1
  38. package/dist/shell/claudeCodeStreamHandler.d.ts +145 -0
  39. package/dist/shell/claudeCodeStreamHandler.d.ts.map +1 -0
  40. package/dist/shell/claudeCodeStreamHandler.js +322 -0
  41. package/dist/shell/claudeCodeStreamHandler.js.map +1 -0
  42. package/dist/shell/inputQueueManager.d.ts +144 -0
  43. package/dist/shell/inputQueueManager.d.ts.map +1 -0
  44. package/dist/shell/inputQueueManager.js +290 -0
  45. package/dist/shell/inputQueueManager.js.map +1 -0
  46. package/dist/shell/interactiveShell.d.ts +2 -10
  47. package/dist/shell/interactiveShell.d.ts.map +1 -1
  48. package/dist/shell/interactiveShell.js +31 -183
  49. package/dist/shell/interactiveShell.js.map +1 -1
  50. package/dist/shell/streamingOutputManager.d.ts +115 -0
  51. package/dist/shell/streamingOutputManager.d.ts.map +1 -0
  52. package/dist/shell/streamingOutputManager.js +225 -0
  53. package/dist/shell/streamingOutputManager.js.map +1 -0
  54. package/dist/shell/terminalInput.d.ts +131 -54
  55. package/dist/shell/terminalInput.d.ts.map +1 -1
  56. package/dist/shell/terminalInput.js +643 -353
  57. package/dist/shell/terminalInput.js.map +1 -1
  58. package/dist/shell/terminalInputAdapter.d.ts +15 -12
  59. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  60. package/dist/shell/terminalInputAdapter.js +22 -8
  61. package/dist/shell/terminalInputAdapter.js.map +1 -1
  62. package/dist/ui/display.d.ts +0 -19
  63. package/dist/ui/display.d.ts.map +1 -1
  64. package/dist/ui/display.js +2 -133
  65. package/dist/ui/display.js.map +1 -1
  66. package/dist/ui/persistentPrompt.d.ts +50 -0
  67. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  68. package/dist/ui/persistentPrompt.js +92 -0
  69. package/dist/ui/persistentPrompt.js.map +1 -0
  70. package/dist/ui/terminalUISchema.d.ts +195 -0
  71. package/dist/ui/terminalUISchema.d.ts.map +1 -0
  72. package/dist/ui/terminalUISchema.js +113 -0
  73. package/dist/ui/terminalUISchema.js.map +1 -0
  74. package/dist/ui/theme.d.ts.map +1 -1
  75. package/dist/ui/theme.js +8 -6
  76. package/dist/ui/theme.js.map +1 -1
  77. package/dist/ui/unified/layout.d.ts +0 -1
  78. package/dist/ui/unified/layout.d.ts.map +1 -1
  79. package/dist/ui/unified/layout.js +25 -15
  80. package/dist/ui/unified/layout.js.map +1 -1
  81. package/package.json +1 -1
@@ -2,7 +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 { display } from '../ui/display.js';
5
- import { theme } from '../ui/theme.js';
5
+ import { theme, formatUserPrompt } from '../ui/theme.js';
6
6
  import { getContextWindowTokens } from '../core/contextWindow.js';
7
7
  import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
8
8
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, } from '../core/preferences.js';
@@ -34,7 +34,6 @@ const DROPDOWN_COLORS = [
34
34
  theme.success,
35
35
  theme.warning,
36
36
  ];
37
- const STREAMING_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
38
37
  // Load MODEL_PRESETS from centralized schema
39
38
  const MODEL_PRESETS = getModels().map((model) => ({
40
39
  id: model.id,
@@ -49,13 +48,11 @@ const MODEL_PRESETS = getModels().map((model) => ({
49
48
  const BASE_SLASH_COMMANDS = getSlashCommands().map((cmd) => ({
50
49
  command: cmd.command,
51
50
  description: cmd.description,
52
- category: cmd.category,
53
51
  }));
54
52
  // Load PROVIDER_LABELS from centralized schema
55
53
  const PROVIDER_LABELS = Object.fromEntries(getProviders().map((provider) => [provider.id, provider.label]));
56
54
  // Allow enough time for paste detection to kick in before flushing buffered lines
57
55
  const CONTEXT_USAGE_THRESHOLD = 0.9;
58
- const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
59
56
  const CONTEXT_RECENT_MESSAGE_COUNT = 12;
60
57
  const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
61
58
  const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
@@ -101,7 +98,6 @@ export class InteractiveShell {
101
98
  followUpQueue = [];
102
99
  isDrainingQueue = false;
103
100
  activeContextWindowTokens = null;
104
- latestTokenUsage = { used: null, limit: null };
105
101
  sessionPreferences;
106
102
  autosaveEnabled;
107
103
  autoContinueEnabled;
@@ -132,7 +128,6 @@ export class InteractiveShell {
132
128
  statusLineState = null;
133
129
  statusMessageOverride = null;
134
130
  promptRefreshTimer = null;
135
- launchPaletteShown = false;
136
131
  constructor(config) {
137
132
  this.profile = config.profile;
138
133
  this.profileLabel = config.profileLabel;
@@ -166,7 +161,6 @@ export class InteractiveShell {
166
161
  this.slashCommands.push({
167
162
  command: '/agents',
168
163
  description: 'Select the default agent profile (applies on next launch)',
169
- category: 'configuration',
170
164
  });
171
165
  }
172
166
  this.customCommands = loadCustomSlashCommands();
@@ -175,21 +169,18 @@ export class InteractiveShell {
175
169
  this.slashCommands.push({
176
170
  command: custom.command,
177
171
  description: `${custom.description} (custom)`,
178
- category: custom.category ?? 'other',
179
172
  });
180
173
  }
181
174
  if (!this.slashCommands.some((cmd) => cmd.command === '/exit')) {
182
175
  this.slashCommands.push({
183
176
  command: '/exit',
184
177
  description: 'Quit the CLI immediately',
185
- category: 'other',
186
178
  });
187
179
  }
188
180
  // Add /plugins command
189
181
  this.slashCommands.push({
190
182
  command: '/plugins',
191
183
  description: 'Show available and loaded plugins',
192
- category: 'configuration',
193
184
  });
194
185
  this.statusTracker = config.statusTracker;
195
186
  this.ui = config.ui;
@@ -224,6 +215,9 @@ export class InteractiveShell {
224
215
  });
225
216
  // Register output interceptor for cursor positioning during streaming
226
217
  this.terminalInput.registerOutputInterceptor(display);
218
+ // Use flow mode: input renders inline after content for a unified layout
219
+ // This eliminates the blank space between banner and input area
220
+ this.terminalInput.setFlowMode(true);
227
221
  // Initialize Alpha Zero 2 metrics tracking
228
222
  this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
229
223
  this.setupStatusTracking();
@@ -281,24 +275,9 @@ export class InteractiveShell {
281
275
  await this.processInputBlock(initialPrompt);
282
276
  return;
283
277
  }
284
- this.showLaunchCommandPalette();
285
278
  // Ensure the terminal input is visible
286
279
  this.terminalInput.render();
287
280
  }
288
- showLaunchCommandPalette() {
289
- if (this.launchPaletteShown) {
290
- return;
291
- }
292
- const palette = this.buildLaunchCommandPalette();
293
- if (!palette.length) {
294
- return;
295
- }
296
- display.showCommandPalette(palette, {
297
- title: 'Quick commands',
298
- intro: 'Describe a task or run a slash command to get started:',
299
- });
300
- this.launchPaletteShown = true;
301
- }
302
281
  /**
303
282
  * TerminalInputAdapter submit handler
304
283
  */
@@ -312,8 +291,9 @@ export class InteractiveShell {
312
291
  this.handleInputChange('');
313
292
  return;
314
293
  }
315
- // DON'T clear the input here - keep it visible while streaming.
316
- // The input will be cleared after streaming completes in the finally block.
294
+ // Enter streaming mode BEFORE logging prompt - this positions cursor correctly
295
+ // so content appears right after the banner, not at bottom with blank space above
296
+ this.terminalInput.setStreaming(true);
317
297
  this.logUserPrompt(approved);
318
298
  void this.processInputBlock(approved).catch((err) => {
319
299
  display.showError(err instanceof Error ? err.message : String(err), err);
@@ -515,9 +495,10 @@ export class InteractiveShell {
515
495
  // Dispose unified UI adapter
516
496
  this.uiAdapter.dispose();
517
497
  display.newLine();
518
- console.log(theme.ui.muted('━'.repeat(44)));
519
- console.log(theme.ui.muted(' Goodbye! · support@ero.solar'));
520
- console.log(theme.ui.muted(''.repeat(44)));
498
+ console.log(theme.gradient.warm('━'.repeat(50)));
499
+ console.log(` ${theme.gradient.cool('Goodbye!')} ${theme.ui.muted('·')} ${theme.info('support@ero.solar')}`);
500
+ console.log(` ${theme.ui.muted('Read:')} ${theme.accent('anthropic.com/news/disrupting-AI-espionage')}`);
501
+ console.log(theme.gradient.warm('━'.repeat(50)));
521
502
  exit(0);
522
503
  }
523
504
  /**
@@ -687,7 +668,6 @@ export class InteractiveShell {
687
668
  });
688
669
  }
689
670
  setProcessingStatus(detail) {
690
- this.latestTokenUsage = { used: null, limit: this.latestTokenUsage.limit };
691
671
  this.statusTracker.setBase('Working on your request', {
692
672
  detail: this.describeStatusDetail(detail),
693
673
  tone: 'info',
@@ -707,18 +687,12 @@ export class InteractiveShell {
707
687
  }
708
688
  refreshContextGauge() {
709
689
  const tokens = getContextWindowTokens(this.sessionState.model);
710
- const normalizedTokens = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
711
- this.activeContextWindowTokens = normalizedTokens;
712
- if (normalizedTokens !== null) {
713
- this.latestTokenUsage = {
714
- used: this.latestTokenUsage.used,
715
- limit: normalizedTokens,
716
- };
717
- }
690
+ this.activeContextWindowTokens =
691
+ typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : null;
718
692
  }
719
693
  updateContextUsage(percentage) {
720
694
  this.uiAdapter.updateContextUsage(percentage);
721
- this.terminalInput.setContextUsage(percentage, CONTEXT_AUTOCOMPACT_PERCENT);
695
+ this.terminalInput.setContextUsage(percentage);
722
696
  }
723
697
  refreshControlBar() {
724
698
  this.terminalInput.setModeToggles({
@@ -726,7 +700,6 @@ export class InteractiveShell {
726
700
  autoContinueEnabled: this.autoContinueEnabled,
727
701
  verificationHotkey: 'alt+v',
728
702
  autoContinueHotkey: 'alt+c',
729
- thinkingModeLabel: this.thinkingMode,
730
703
  });
731
704
  this.refreshStatusLine();
732
705
  this.terminalInput.render();
@@ -758,20 +731,6 @@ export class InteractiveShell {
758
731
  // Set main status (tool execution, etc.) - shown when not overridden
759
732
  const statusText = this.formatStatusLine(this.statusLineState);
760
733
  this.terminalInput.setStatusMessage(statusText);
761
- // Surface meta header (elapsed + context usage) above the divider
762
- const elapsedSeconds = this.statusLineState
763
- ? Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000))
764
- : null;
765
- const thinkingMs = display.isSpinnerActive() ? display.getThinkingElapsedMs() : null;
766
- const tokensUsed = this.latestTokenUsage.used;
767
- const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
768
- this.terminalInput.setMetaStatus({
769
- elapsedSeconds,
770
- tokensUsed,
771
- tokenLimit,
772
- thinkingMs,
773
- thinkingHasContent: display.isSpinnerActive(),
774
- });
775
734
  if (forceRender) {
776
735
  this.terminalInput.render();
777
736
  }
@@ -831,11 +790,13 @@ export class InteractiveShell {
831
790
  this.terminalInput.render();
832
791
  }
833
792
  /**
834
- * Keep submissions out of the transcript to preserve the persistent input area.
835
- * The chat box already holds the user's prompt, so avoid echoing it into output.
793
+ * Log the user's prompt as a visible message in the conversation.
794
+ * This creates a persistent log entry that remains visible during and after streaming.
836
795
  */
837
- logUserPrompt(_text) {
838
- // Intentionally no-op to keep the input area persistent and uncluttered.
796
+ logUserPrompt(text) {
797
+ // Display the user's prompt with the standard prefix
798
+ const prefix = formatUserPrompt();
799
+ display.stream(`\n${prefix}${text}\n\n`);
839
800
  }
840
801
  requestPromptRefresh(force = false) {
841
802
  if (force) {
@@ -863,29 +824,9 @@ export class InteractiveShell {
863
824
  this.uiUpdates.setMode('streaming');
864
825
  this.streamingHeartbeatStart = Date.now();
865
826
  this.streamingHeartbeatFrame = 0;
866
- const initialFrame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
867
- this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
868
- display.updateStreamingStatus(this.streamingStatusLabel);
869
- this.refreshStatusLine(true);
870
- // Periodically refresh the pinned input/status region while streaming so
871
- // elapsed time remains visible without interrupting the scroll region.
872
- this.uiUpdates.startHeartbeat('streaming', {
873
- intervalMs: 1000,
874
- lane: 'heartbeat',
875
- mode: ['streaming', 'processing'],
876
- coalesceKey: 'streaming:heartbeat',
877
- run: () => {
878
- const elapsedSeconds = this.streamingHeartbeatStart
879
- ? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
880
- : 0;
881
- this.streamingHeartbeatFrame =
882
- (this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
883
- const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
884
- this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
885
- display.updateStreamingStatus(this.streamingStatusLabel);
886
- this.refreshStatusLine(true);
887
- },
888
- });
827
+ // Note: We don't start a heartbeat during streaming anymore
828
+ // because the UI shouldn't be rendering during streaming.
829
+ // The streaming status is shown in the streaming header instead.
889
830
  }
890
831
  stopStreamingHeartbeat() {
891
832
  // Exit global streaming mode - allows UI to render again
@@ -901,28 +842,10 @@ export class InteractiveShell {
901
842
  // Force refresh to update the input area now that streaming has ended
902
843
  this.refreshStatusLine(true);
903
844
  }
904
- buildStreamingStatus(label, elapsedSeconds) {
845
+ buildStreamingStatus(label) {
905
846
  const detail = this.describeModelDetail();
906
- const elapsedLabel = typeof elapsedSeconds === 'number' && elapsedSeconds >= 0
907
- ? theme.ui.muted(this.formatElapsedShort(elapsedSeconds))
908
- : null;
909
- const prefix = theme.info('⏺');
910
- const parts = [label];
911
- if (detail) {
912
- parts.push(theme.ui.muted('·'), detail);
913
- }
914
- if (elapsedLabel) {
915
- parts.push(theme.ui.muted('·'), elapsedLabel);
916
- }
917
- return `${prefix} ${parts.join(' ')}`.trim();
918
- }
919
- formatElapsedShort(seconds) {
920
- if (seconds < 60) {
921
- return `${seconds}s`;
922
- }
923
- const minutes = Math.floor(seconds / 60);
924
- const remaining = seconds % 60;
925
- return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
847
+ const prefix = theme.info('');
848
+ return detail ? `${prefix} ${label} ${theme.ui.muted('·')} ${detail}` : `${prefix} ${label}`;
926
849
  }
927
850
  refreshQueueIndicators() {
928
851
  if (this.isProcessing) {
@@ -1816,75 +1739,6 @@ export class InteractiveShell {
1816
1739
  }
1817
1740
  return `${warning.label}: ${warning.reason}.`;
1818
1741
  }
1819
- buildLaunchCommandPalette() {
1820
- const entries = [];
1821
- const secretsSummary = this.summarizeSecretsForPalette();
1822
- const toolSummary = this.getToolSelectionSummary();
1823
- const autosaveLabel = this.autosaveEnabled ? 'on' : 'off';
1824
- for (const command of this.slashCommands) {
1825
- const entry = {
1826
- command: command.command,
1827
- description: command.description,
1828
- category: command.category ?? 'other',
1829
- };
1830
- switch (command.command) {
1831
- case '/secrets':
1832
- if (secretsSummary.text) {
1833
- entry.description = `${command.description} (${secretsSummary.text})`;
1834
- entry.tone = secretsSummary.tone;
1835
- }
1836
- break;
1837
- case '/tools':
1838
- if (toolSummary) {
1839
- entry.description = `${command.description} (${toolSummary})`;
1840
- }
1841
- break;
1842
- case '/sessions':
1843
- entry.description = `${command.description} (autosave ${autosaveLabel})`;
1844
- break;
1845
- case '/model':
1846
- entry.description = `${command.description} (current: ${this.sessionState.model})`;
1847
- break;
1848
- case '/provider':
1849
- entry.description = `${command.description} (current: ${this.providerLabel(this.sessionState.provider)})`;
1850
- break;
1851
- default:
1852
- break;
1853
- }
1854
- entries.push(entry);
1855
- }
1856
- return entries;
1857
- }
1858
- summarizeSecretsForPalette() {
1859
- const definitions = listSecretDefinitions();
1860
- if (!definitions.length) {
1861
- return { text: null };
1862
- }
1863
- const missing = definitions.filter((definition) => !getSecretValue(definition.id));
1864
- if (missing.length === 0) {
1865
- return { text: 'all configured', tone: 'success' };
1866
- }
1867
- const labels = missing.map((definition) => definition.label ?? definition.id);
1868
- return { text: `missing ${this.formatList(labels)}`, tone: 'warn' };
1869
- }
1870
- getToolSelectionSummary() {
1871
- const toolSettings = loadToolSettings();
1872
- const selection = buildEnabledToolSet(toolSettings);
1873
- const options = getToolToggleOptions();
1874
- if (!options.length) {
1875
- return null;
1876
- }
1877
- const enabledCount = options.filter((option) => selection.has(option.id)).length;
1878
- return `${enabledCount}/${options.length} enabled`;
1879
- }
1880
- formatList(values, maxItems = 3) {
1881
- if (!values.length) {
1882
- return '';
1883
- }
1884
- const shown = values.slice(0, maxItems);
1885
- const suffix = values.length > maxItems ? ', …' : '';
1886
- return `${shown.join(', ')}${suffix}`;
1887
- }
1888
1742
  buildSlashCommandList(header) {
1889
1743
  const lines = [theme.gradient.primary(header), ''];
1890
1744
  for (const command of this.slashCommands) {
@@ -2454,7 +2308,6 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
2454
2308
  try {
2455
2309
  // Send the request and capture the response (streaming disabled)
2456
2310
  display.showThinking('Responding...');
2457
- this.refreshStatusLine(true);
2458
2311
  const response = await agent.send(currentPrompt, true);
2459
2312
  await this.awaitPendingCleanup();
2460
2313
  this.captureHistorySnapshot();
@@ -2954,10 +2807,8 @@ What's the next action?`;
2954
2807
  try {
2955
2808
  // Send the error to the agent for fixing
2956
2809
  display.showThinking('Analyzing build errors');
2957
- this.refreshStatusLine(true);
2958
2810
  const response = await this.agent.send(prompt, true);
2959
2811
  display.stopThinking();
2960
- this.refreshStatusLine(true);
2961
2812
  if (response) {
2962
2813
  display.showAssistantMessage(response, { isFinal: true });
2963
2814
  }
@@ -3007,16 +2858,18 @@ What's the next action?`;
3007
2858
  display.showAssistantMessage(finalContent, enriched);
3008
2859
  }
3009
2860
  }
3010
- // Status shown in mode controls bar - no separate status line needed
2861
+ // Show status line at end (Claude Code style: "• Context X% used • Ready for prompts (2s)")
3011
2862
  display.stopThinking();
3012
- // Update context usage for mode controls display
2863
+ // Calculate context usage
2864
+ let contextInfo;
3013
2865
  if (enriched.contextWindowTokens && metadata.usage) {
3014
2866
  const total = this.totalTokens(metadata.usage);
3015
2867
  if (total && total > 0) {
3016
2868
  const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
3017
- this.updateContextUsage(percentage);
2869
+ contextInfo = { percentage, tokens: total };
3018
2870
  }
3019
2871
  }
2872
+ display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
3020
2873
  // Auto-verify changes: build first (catches type errors), then tests
3021
2874
  void this.enforceAutoBuild('final-response');
3022
2875
  void this.enforceAutoTests('final-response');
@@ -3186,14 +3039,9 @@ What's the next action?`;
3186
3039
  return null;
3187
3040
  }
3188
3041
  const usageRatio = total / windowTokens;
3189
- this.latestTokenUsage = {
3190
- used: total,
3191
- limit: windowTokens,
3192
- };
3193
3042
  // Always update context usage in the UI
3194
3043
  const percentUsed = Math.round(usageRatio * 100);
3195
3044
  this.updateContextUsage(percentUsed);
3196
- this.refreshStatusLine(true);
3197
3045
  if (usageRatio < CONTEXT_USAGE_THRESHOLD) {
3198
3046
  return null;
3199
3047
  }