erosolar-cli 1.7.421 → 1.7.423

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 (36) hide show
  1. package/dist/capabilities/statusCapability.d.ts +4 -0
  2. package/dist/capabilities/statusCapability.d.ts.map +1 -0
  3. package/dist/capabilities/statusCapability.js +92 -0
  4. package/dist/capabilities/statusCapability.js.map +1 -0
  5. package/dist/core/toolRuntime.d.ts +5 -0
  6. package/dist/core/toolRuntime.d.ts.map +1 -1
  7. package/dist/core/toolRuntime.js +25 -1
  8. package/dist/core/toolRuntime.js.map +1 -1
  9. package/dist/shell/interactiveShell.d.ts +7 -8
  10. package/dist/shell/interactiveShell.d.ts.map +1 -1
  11. package/dist/shell/interactiveShell.js +73 -75
  12. package/dist/shell/interactiveShell.js.map +1 -1
  13. package/dist/shell/shellApp.d.ts.map +1 -1
  14. package/dist/shell/shellApp.js +3 -0
  15. package/dist/shell/shellApp.js.map +1 -1
  16. package/dist/shell/terminalInput.d.ts +0 -5
  17. package/dist/shell/terminalInput.d.ts.map +1 -1
  18. package/dist/shell/terminalInput.js +0 -7
  19. package/dist/shell/terminalInput.js.map +1 -1
  20. package/dist/tools/localExplore.d.ts +1 -1
  21. package/dist/tools/localExplore.d.ts.map +1 -1
  22. package/dist/tools/localExplore.js +117 -46
  23. package/dist/tools/localExplore.js.map +1 -1
  24. package/dist/ui/ShellUIAdapter.d.ts +6 -0
  25. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  26. package/dist/ui/ShellUIAdapter.js +78 -1
  27. package/dist/ui/ShellUIAdapter.js.map +1 -1
  28. package/dist/ui/display.d.ts +0 -31
  29. package/dist/ui/display.d.ts.map +1 -1
  30. package/dist/ui/display.js +0 -42
  31. package/dist/ui/display.js.map +1 -1
  32. package/dist/ui/streamingFormatter.d.ts +6 -2
  33. package/dist/ui/streamingFormatter.d.ts.map +1 -1
  34. package/dist/ui/streamingFormatter.js +37 -23
  35. package/dist/ui/streamingFormatter.js.map +1 -1
  36. package/package.json +1 -1
@@ -6,7 +6,6 @@ import { join } from 'node:path';
6
6
  import { display } from '../ui/display.js';
7
7
  import { isPlainOutputMode } from '../ui/outputMode.js';
8
8
  import { theme } from '../ui/theme.js';
9
- import { renderDivider } from '../ui/unified/layout.js';
10
9
  import { StreamingResponseFormatter } from '../ui/streamingFormatter.js';
11
10
  import { getContextWindowTokens } from '../core/contextWindow.js';
12
11
  import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
@@ -33,7 +32,7 @@ import { addToolPattern } from '../core/learningPersistence.js';
33
32
  import { classifyTaskType } from '../core/alphaZeroEngine.js';
34
33
  import { analyzeImprovementOpportunities, runSelfImprovementCycle, getImprovementSummary, runAutonomousImprovement, requestAutoImprovementStop, getAutoImprovementState, emergencyRollback, } from '../core/selfImprovement.js';
35
34
  import { listAvailablePlugins } from '../plugins/index.js';
36
- import { isErosolarRepo, isValidSourceRepo, getRepoName, analyzeSource, runSelfEvolution, stopEvolution, getEvolutionStatus, emergencyEvolutionRollback, learnSourcePatterns, generateFix, } from '../core/selfEvolution.js';
35
+ import { isValidSourceRepo, getRepoName, analyzeSource, runSelfEvolution, stopEvolution, getEvolutionStatus, emergencyEvolutionRollback, learnSourcePatterns, generateFix, } from '../core/selfEvolution.js';
37
36
  import { analyzeTokenUsage, discoverModularTargets, getModularStatusDisplay, generateContextOptimizations, getGuidelines, deleteGuideline, getPendingActions, executeModularAction, } from '../core/alphaZeroModular.js';
38
37
  import { startOffsecRun, resumeOffsecRun, recordOffsecOutcome, getOffsecNextActions, simulateOffsecRollout, formatOffsecStatus, listOffsecRuns, } from '../core/offsecAlphaZero.js';
39
38
  import { generateTestFlows, detectBugs, detectUIUpdates, saveTestFlows, saveBugReports, saveUIUpdates, getTestFlowStatus, } from '../core/intelligentTestFlows.js';
@@ -77,7 +76,6 @@ const CONTEXT_AUTOCOMPACT_PERCENT = Math.round(CONTEXT_USAGE_THRESHOLD * 100);
77
76
  const CONTEXT_AUTOCOMPACT_FLOOR = 0.6;
78
77
  const MIN_COMPACTION_TOKEN_SAVINGS = 200;
79
78
  const MIN_COMPACTION_PERCENT_SAVINGS = 0.5;
80
- const CONTEXT_RECENT_MESSAGE_COUNT = 12;
81
79
  const CONTEXT_CLEANUP_CHARS_PER_CHUNK = 6000;
82
80
  const CONTEXT_CLEANUP_MAX_OUTPUT_TOKENS = 800;
83
81
  const CONTEXT_CLEANUP_SYSTEM_PROMPT = `Summarize earlier IDE collaboration so the agent can keep working.
@@ -125,7 +123,6 @@ export class InteractiveShell {
125
123
  planApprovalBridgeRegistered = false;
126
124
  contextCompactionInFlight = false;
127
125
  contextCompactionLog = [];
128
- bannerShown = false;
129
126
  lastContextWarningLevel = null;
130
127
  sessionPreferences;
131
128
  autosaveEnabled;
@@ -173,9 +170,10 @@ export class InteractiveShell {
173
170
  statusMessageOverride = null;
174
171
  promptRefreshTimer = null;
175
172
  launchPaletteShown = false;
176
- launchBannerText = null;
177
173
  version;
178
174
  alternateScreenEnabled;
175
+ welcomeShown = false;
176
+ scrollbackRestored = false;
179
177
  constructor(config) {
180
178
  this.profile = config.profile;
181
179
  this.profileLabel = config.profileLabel;
@@ -348,6 +346,7 @@ export class InteractiveShell {
348
346
  // Restore scrollback buffer if available
349
347
  if (stored.scrollbackBuffer && Array.isArray(stored.scrollbackBuffer)) {
350
348
  this.terminalInput.loadScrollbackBuffer(stored.scrollbackBuffer);
349
+ this.scrollbackRestored = true;
351
350
  }
352
351
  return;
353
352
  }
@@ -365,6 +364,7 @@ export class InteractiveShell {
365
364
  // Restore scrollback buffer if available
366
365
  if (autosave.scrollbackBuffer && Array.isArray(autosave.scrollbackBuffer)) {
367
366
  this.terminalInput.loadScrollbackBuffer(autosave.scrollbackBuffer);
367
+ this.scrollbackRestored = true;
368
368
  }
369
369
  return;
370
370
  }
@@ -379,29 +379,35 @@ export class InteractiveShell {
379
379
  display.showInfo(this.sessionResumeNotice);
380
380
  this.sessionResumeNotice = null;
381
381
  }
382
- renderWelcomeBanner(force = false) {
383
- const header = `${theme.fields.model(this.sessionState.model)} ${theme.ui.muted('@')} ${theme.fields.agent(this.providerLabel(this.sessionState.provider))}`;
382
+ showWelcomeBanner(force = false) {
383
+ if ((this.welcomeShown || this.scrollbackRestored) && !force) {
384
+ return;
385
+ }
386
+ const header = theme.gradient.primary('Erosolar CLI');
387
+ const modelLine = `${theme.fields.model(this.sessionState.model)} ${theme.ui.muted('@')} ${theme.fields.agent(this.providerLabel(this.sessionState.provider))}`;
384
388
  const profileLine = `${theme.fields.profile(this.profileLabel)} ${theme.ui.muted(`(${this.profile})`)}`;
385
389
  const workspaceLine = theme.fields.workspace(this.workingDir);
386
390
  const versionLine = this.version ? theme.ui.muted(`v${this.version} · support@ero.solar`) : null;
387
391
  const hintLine = theme.ui.muted('/help for commands · /model to switch · /secrets for keys');
388
- const width = output.columns ?? 80;
389
- const banner = [header, profileLine, workspaceLine, versionLine, hintLine, renderDivider(width)]
392
+ const message = [header, modelLine, profileLine, workspaceLine, versionLine, hintLine]
390
393
  .filter(Boolean)
391
394
  .join('\n');
392
- if (this.bannerShown && !force) {
393
- return;
394
- }
395
- if (this.launchBannerText === banner) {
395
+ this.streamEventBlock(message);
396
+ this.welcomeShown = true;
397
+ this.requestPromptRefresh(true);
398
+ }
399
+ /**
400
+ * Stream a content block as a single event, isolating it from adjacent output.
401
+ */
402
+ streamEventBlock(content) {
403
+ const normalized = content.replace(/\s+$/u, '');
404
+ if (!normalized) {
396
405
  return;
397
406
  }
398
- display.stream(`${banner}\n`);
399
- this.bannerShown = true;
400
- this.launchBannerText = banner;
401
- this.requestPromptRefresh(true);
407
+ this.terminalInput.streamContent(`\n${normalized}\n\n`);
402
408
  }
403
409
  async start(initialPrompt) {
404
- this.renderWelcomeBanner();
410
+ this.showWelcomeBanner();
405
411
  if (initialPrompt) {
406
412
  await this.processInputBlock(initialPrompt);
407
413
  return;
@@ -579,11 +585,11 @@ export class InteractiveShell {
579
585
  if (!changed && source === 'shortcut') {
580
586
  return;
581
587
  }
582
- display.showInfo(`Auto-continue ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
583
- (this.autoContinueEnabled
584
- ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
585
- : 'The model will not be auto-prompted to continue.') +
586
- ' Toggle with Ctrl+Shift+C.');
588
+ const modeLabel = this.autoContinueEnabled ? 'enabled' : 'disabled';
589
+ const behavior = this.autoContinueEnabled
590
+ ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
591
+ : 'The model will not be auto-prompted to continue.';
592
+ display.showInfo(`Auto-continue ${modeLabel}. ${behavior} Toggle with Ctrl+Shift+C.`);
587
593
  }
588
594
  /**
589
595
  * Cycle through thinking modes (Tab shortcut).
@@ -1297,9 +1303,9 @@ export class InteractiveShell {
1297
1303
  }
1298
1304
  this.lastLoggedPrompt = normalized;
1299
1305
  this.lastLoggedPromptAt = now;
1300
- // Format with user prompt prefix and write to scroll region
1301
- const formatted = `${theme.user('>')} ${normalized}\n`;
1302
- this.terminalInput.writeToScrollRegion(formatted);
1306
+ // Format with user prompt prefix and stream as its own event
1307
+ const formatted = `${theme.user('>')} ${normalized}`;
1308
+ this.streamEventBlock(formatted);
1303
1309
  }
1304
1310
  requestPromptRefresh(force = false) {
1305
1311
  if (force) {
@@ -1384,7 +1390,10 @@ export class InteractiveShell {
1384
1390
  // Emit a streaming note for stop/quit so the status stays inside the stream
1385
1391
  if (reason === 'stop' || reason === 'quit') {
1386
1392
  const note = reason === 'quit' ? 'Session closed.' : 'Stream stopped.';
1387
- this.finishStreamingFormatter(note, { refreshPrompt: false });
1393
+ this.finishStreamingFormatter(note, {
1394
+ refreshPrompt: false,
1395
+ mode: reason,
1396
+ });
1388
1397
  }
1389
1398
  // Force refresh to update the input area now that streaming has ended
1390
1399
  if (!quiet) {
@@ -1414,7 +1423,10 @@ export class InteractiveShell {
1414
1423
  if (!this.streamingFormatter) {
1415
1424
  return;
1416
1425
  }
1417
- const closing = this.streamingFormatter.finish(note);
1426
+ const closing = this.streamingFormatter.finish({
1427
+ note,
1428
+ mode: options?.mode ?? 'complete',
1429
+ });
1418
1430
  if (closing) {
1419
1431
  this.terminalInput.streamContent(closing);
1420
1432
  }
@@ -1710,7 +1722,6 @@ export class InteractiveShell {
1710
1722
  this.showAlphaZeroMetrics();
1711
1723
  break;
1712
1724
  case '/suggestions':
1713
- case '/improve':
1714
1725
  this.showImprovementSuggestions();
1715
1726
  break;
1716
1727
  case '/plugins':
@@ -2137,7 +2148,7 @@ export class InteractiveShell {
2137
2148
  lines.push(theme.bold('Session File Changes'));
2138
2149
  lines.push('');
2139
2150
  lines.push(`${theme.info('•')} ${summary.files} file${summary.files === 1 ? '' : 's'} modified`);
2140
- lines.push(`${theme.info('•')} ${theme.success('+' + summary.additions)} ${theme.error('-' + summary.removals)} lines`);
2151
+ lines.push(`${theme.info('•')} ${theme.success(`+${summary.additions}`)} ${theme.error(`-${summary.removals}`)} lines`);
2141
2152
  lines.push('');
2142
2153
  // Group changes by file
2143
2154
  const fileMap = new Map();
@@ -2161,7 +2172,7 @@ export class InteractiveShell {
2161
2172
  if (stats.writes > 0)
2162
2173
  operations.push(`${stats.writes} write${stats.writes === 1 ? '' : 's'}`);
2163
2174
  const opsText = operations.join(', ');
2164
- const diffText = `${theme.success('+' + stats.additions)} ${theme.error('-' + stats.removals)}`;
2175
+ const diffText = `${theme.success(`+${stats.additions}`)} ${theme.error(`-${stats.removals}`)}`;
2165
2176
  lines.push(` ${theme.dim(path)}`);
2166
2177
  lines.push(` ${opsText} • ${diffText}`);
2167
2178
  }
@@ -2618,7 +2629,7 @@ export class InteractiveShell {
2618
2629
  if (!isValidSourceRepo(this.workingDir)) {
2619
2630
  display.showWarning('Self-evolution requires a git repository with source code.');
2620
2631
  display.showSystemMessage('');
2621
- display.showSystemMessage('Current directory: ' + this.workingDir);
2632
+ display.showSystemMessage(`Current directory: ${this.workingDir}`);
2622
2633
  display.showSystemMessage('');
2623
2634
  display.showSystemMessage('Requirements:');
2624
2635
  display.showSystemMessage(' • Must be a git repository (.git folder)');
@@ -2628,7 +2639,6 @@ export class InteractiveShell {
2628
2639
  return;
2629
2640
  }
2630
2641
  const repoName = getRepoName(this.workingDir);
2631
- const isErosolar = isErosolarRepo(this.workingDir);
2632
2642
  if (subcommand === 'analyze') {
2633
2643
  display.showSystemMessage(theme.gradient.primary(`🔍 Analyzing ${repoName} Source Code...`));
2634
2644
  display.showSystemMessage('');
@@ -3000,13 +3010,6 @@ export class InteractiveShell {
3000
3010
  ' /offsec win|fail|detect <actionId> [note]\n' +
3001
3011
  ' /offsec resume <runId>\n' +
3002
3012
  ' /offsec runs';
3003
- const loadRun = (explicitId) => {
3004
- const run = resumeOffsecRun(explicitId ?? this.offsecRunId);
3005
- if (!run) {
3006
- display.showWarning('No offsec run found. Start one with /offsec start "<objective>".');
3007
- }
3008
- return run;
3009
- };
3010
3013
  if (sub === 'start') {
3011
3014
  const rest = args.slice(1);
3012
3015
  const scope = [];
@@ -3445,8 +3448,7 @@ export class InteractiveShell {
3445
3448
  const value = tokens[0]?.toLowerCase();
3446
3449
  if (!value) {
3447
3450
  // Show current status
3448
- display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
3449
- `Use /autocontinue on|off or Ctrl+Shift+C to toggle.`);
3451
+ display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. Use /autocontinue on|off or Ctrl+Shift+C to toggle.`);
3450
3452
  return;
3451
3453
  }
3452
3454
  if (value !== 'on' && value !== 'off') {
@@ -3456,7 +3458,7 @@ export class InteractiveShell {
3456
3458
  this.setAutoContinueMode(value === 'on', 'command');
3457
3459
  }
3458
3460
  // ==================== Claude Code Style Commands ====================
3459
- async handleRewindCommand(input) {
3461
+ async handleRewindCommand(_input) {
3460
3462
  const lines = [];
3461
3463
  lines.push(theme.bold('Rewind / Checkpoint System'));
3462
3464
  lines.push('');
@@ -3472,7 +3474,7 @@ export class InteractiveShell {
3472
3474
  lines.push(theme.ui.muted('Press Esc+Esc for quick access to the rewind menu'));
3473
3475
  display.showSystemMessage(lines.join('\n'));
3474
3476
  }
3475
- handleMemoryCommand(input) {
3477
+ handleMemoryCommand(_input) {
3476
3478
  const lines = [];
3477
3479
  lines.push(theme.bold('Memory System (EROSOLAR.md)'));
3478
3480
  lines.push('');
@@ -3513,7 +3515,7 @@ export class InteractiveShell {
3513
3515
  lines.push(theme.ui.muted('Vim mode is experimental. Toggle with /vim'));
3514
3516
  display.showSystemMessage(lines.join('\n'));
3515
3517
  }
3516
- handleOutputStyleCommand(input) {
3518
+ handleOutputStyleCommand(_input) {
3517
3519
  const lines = [];
3518
3520
  lines.push(theme.bold('Output Styles'));
3519
3521
  lines.push('');
@@ -3634,7 +3636,7 @@ export class InteractiveShell {
3634
3636
  }
3635
3637
  }
3636
3638
  }
3637
- handleExportCommand(input) {
3639
+ handleExportCommand(_input) {
3638
3640
  const lines = [];
3639
3641
  lines.push(theme.bold('Export Conversation'));
3640
3642
  lines.push('');
@@ -4227,7 +4229,7 @@ export class InteractiveShell {
4227
4229
  const parts = tool.name.split('__');
4228
4230
  const serverName = parts[1] || '';
4229
4231
  const toolName = parts.slice(2).join('__') || tool.name;
4230
- lines.push(` ${theme.info('•')} ${theme.ui.muted(serverName + ':')} ${toolName}`);
4232
+ lines.push(` ${theme.info('•')} ${theme.ui.muted(`${serverName}:`)} ${toolName}`);
4231
4233
  }
4232
4234
  if (mcpTools.length > 15) {
4233
4235
  lines.push(` ${theme.ui.muted(`... and ${mcpTools.length - 15} more`)}`);
@@ -4688,7 +4690,7 @@ export class InteractiveShell {
4688
4690
  // Start streaming - no header needed, the input area already provides context
4689
4691
  this.startStreamingHeartbeat('Streaming response');
4690
4692
  responseText = await agent.send(request, true);
4691
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4693
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4692
4694
  await this.awaitPendingCleanup();
4693
4695
  this.captureHistorySnapshot();
4694
4696
  this.autosaveIfEnabled();
@@ -4744,7 +4746,7 @@ export class InteractiveShell {
4744
4746
  }
4745
4747
  }
4746
4748
  finally {
4747
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4749
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4748
4750
  display.stopThinking(false);
4749
4751
  this.uiUpdates.setMode('processing');
4750
4752
  this.stopStreamingHeartbeat('complete', { quiet: true });
@@ -4753,7 +4755,6 @@ export class InteractiveShell {
4753
4755
  this.terminalInput.setStreaming(false);
4754
4756
  this.uiAdapter.endProcessing('Ready for prompts');
4755
4757
  this.setIdleStatus();
4756
- display.newLine();
4757
4758
  this.updateStatusMessage(null);
4758
4759
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
4759
4760
  // CRITICAL: Ensure readline prompt is active for user input
@@ -4837,7 +4838,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
4837
4838
  display.showThinking('Responding...');
4838
4839
  this.refreshStatusLine(true);
4839
4840
  const response = await agent.send(currentPrompt, true);
4840
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4841
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4841
4842
  await this.awaitPendingCleanup();
4842
4843
  this.captureHistorySnapshot();
4843
4844
  this.autosaveIfEnabled();
@@ -4970,7 +4971,7 @@ What's the next action?`;
4970
4971
  }
4971
4972
  }
4972
4973
  finally {
4973
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4974
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4974
4975
  const totalElapsed = Date.now() - overallStartTime;
4975
4976
  const minutes = Math.floor(totalElapsed / 60000);
4976
4977
  const seconds = Math.floor((totalElapsed % 60000) / 1000);
@@ -4985,10 +4986,6 @@ What's the next action?`;
4985
4986
  this.uiAdapter.endProcessing('Ready for prompts');
4986
4987
  this.setIdleStatus();
4987
4988
  this.updateStatusMessage(null);
4988
- display.newLine();
4989
- // Claude Code style: Show unified status bar before prompt
4990
- // This creates consistent UI between startup and post-streaming
4991
- this.showUnifiedStatusBar();
4992
4989
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
4993
4990
  // CRITICAL: Ensure readline prompt is active for user input
4994
4991
  // Claude Code style: New prompt naturally appears at bottom
@@ -5182,7 +5179,14 @@ What's the next action?`;
5182
5179
  return latest;
5183
5180
  }
5184
5181
  formatCommandError(error) {
5185
- const parts = [error?.stdout, error?.stderr, error?.message].filter((part) => typeof part === 'string' && part.trim());
5182
+ const candidates = [];
5183
+ if (error && typeof error === 'object') {
5184
+ const stdout = error.stdout;
5185
+ const stderr = error.stderr;
5186
+ const message = error.message;
5187
+ candidates.push(stdout, stderr, message);
5188
+ }
5189
+ const parts = candidates.filter((part) => typeof part === 'string' && part.trim().length > 0);
5186
5190
  return parts.join('\n').trim();
5187
5191
  }
5188
5192
  runAutoQualityChecks(trigger, assistantResponse, verificationContext) {
@@ -5515,7 +5519,6 @@ Return ONLY JSON array:
5515
5519
  display.showThinking('Analyzing build errors');
5516
5520
  this.refreshStatusLine(true);
5517
5521
  const response = await this.agent.send(prompt, true);
5518
- this.finishStreamingFormatter();
5519
5522
  display.stopThinking();
5520
5523
  this.refreshStatusLine(true);
5521
5524
  if (response) {
@@ -5528,7 +5531,7 @@ Return ONLY JSON array:
5528
5531
  display.showWarning('Agent could not automatically fix build errors. Please review manually.');
5529
5532
  }
5530
5533
  finally {
5531
- this.finishStreamingFormatter();
5534
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
5532
5535
  }
5533
5536
  }
5534
5537
  rebuildAgent() {
@@ -5699,9 +5702,9 @@ Return ONLY JSON array:
5699
5702
  * Ensures the input area is visible and ready for input, just like on startup.
5700
5703
  */
5701
5704
  resetChatBoxAfterModelSwap() {
5702
- this.renderWelcomeBanner(true);
5703
5705
  this.updateStatusMessage(null);
5704
5706
  this.terminalInput.setStreaming(false);
5707
+ this.requestPromptRefresh(true);
5705
5708
  this.ensureReadlineReady();
5706
5709
  }
5707
5710
  /**
@@ -6254,7 +6257,7 @@ Return ONLY JSON array:
6254
6257
  const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
6255
6258
  const partialNote = info.partialResponse ? ' Received partial stream before failure.' : '';
6256
6259
  display.showWarning(`Streaming failed${reason}, retrying without streaming.${detail}${partialNote}`);
6257
- this.finishStreamingFormatter('Stream interrupted - retrying without streaming');
6260
+ this.finishStreamingFormatter('Stream interrupted - retrying without streaming', { mode: 'update' });
6258
6261
  this.startStreamingHeartbeat('Fallback in progress');
6259
6262
  this.requestPromptRefresh(true);
6260
6263
  }
@@ -6355,7 +6358,7 @@ Return ONLY JSON array:
6355
6358
  // Truncate to reasonable length
6356
6359
  const maxLength = 50;
6357
6360
  return cleaned.length > maxLength
6358
- ? cleaned.slice(0, maxLength - 3) + '...'
6361
+ ? `${cleaned.slice(0, maxLength - 3)}...`
6359
6362
  : cleaned;
6360
6363
  }
6361
6364
  splitThinkingResponse(content) {
@@ -6520,13 +6523,15 @@ Return ONLY JSON array:
6520
6523
  await this.scanLocalProviders();
6521
6524
  break;
6522
6525
  case 'use':
6523
- const provider = tokens[1]?.toLowerCase();
6524
- if (!provider) {
6525
- display.showWarning('Usage: /local use <provider>');
6526
- display.showInfo('Example: /local use ollama');
6527
- return;
6526
+ {
6527
+ const provider = tokens[1]?.toLowerCase();
6528
+ if (!provider) {
6529
+ display.showWarning('Usage: /local use <provider>');
6530
+ display.showInfo('Example: /local use ollama');
6531
+ return;
6532
+ }
6533
+ await this.handleProviderCommand(`/provider ${provider}`);
6528
6534
  }
6529
- await this.handleProviderCommand(`/provider ${provider}`);
6530
6535
  break;
6531
6536
  default:
6532
6537
  this.showLocalHelp();
@@ -6633,13 +6638,6 @@ Return ONLY JSON array:
6633
6638
  setProviderStatus(providers) {
6634
6639
  this.cachedProviderStatus = providers;
6635
6640
  }
6636
- /**
6637
- * Show the unified status bar (Claude Code style).
6638
- * Displays provider indicators and ready hints before the prompt.
6639
- */
6640
- showUnifiedStatusBar() {
6641
- display.showUnifiedStatusBar(this.cachedProviderStatus);
6642
- }
6643
6641
  }
6644
6642
  function setsEqual(first, second) {
6645
6643
  if (first.size !== second.size) {