erosolar-cli 1.7.420 → 1.7.422

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 +2 -9
  10. package/dist/shell/interactiveShell.d.ts.map +1 -1
  11. package/dist/shell/interactiveShell.js +60 -83
  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;
@@ -169,14 +166,13 @@ export class InteractiveShell {
169
166
  aiRuntimeTotalMs = 0;
170
167
  streamingFormatter = null;
171
168
  streamingThoughtBuffer = '';
172
- streamingThoughtRecorded = false;
173
169
  statusLineState = null;
174
170
  statusMessageOverride = null;
175
171
  promptRefreshTimer = null;
176
172
  launchPaletteShown = false;
177
- launchBannerText = null;
178
173
  version;
179
174
  alternateScreenEnabled;
175
+ welcomeShown = false;
180
176
  constructor(config) {
181
177
  this.profile = config.profile;
182
178
  this.profileLabel = config.profileLabel;
@@ -380,29 +376,25 @@ export class InteractiveShell {
380
376
  display.showInfo(this.sessionResumeNotice);
381
377
  this.sessionResumeNotice = null;
382
378
  }
383
- renderWelcomeBanner(force = false) {
384
- const header = `${theme.fields.model(this.sessionState.model)} ${theme.ui.muted('@')} ${theme.fields.agent(this.providerLabel(this.sessionState.provider))}`;
379
+ showWelcomeBanner(force = false) {
380
+ if (this.welcomeShown && !force) {
381
+ return;
382
+ }
383
+ const header = theme.gradient.primary('Erosolar CLI');
384
+ const modelLine = `${theme.fields.model(this.sessionState.model)} ${theme.ui.muted('@')} ${theme.fields.agent(this.providerLabel(this.sessionState.provider))}`;
385
385
  const profileLine = `${theme.fields.profile(this.profileLabel)} ${theme.ui.muted(`(${this.profile})`)}`;
386
386
  const workspaceLine = theme.fields.workspace(this.workingDir);
387
387
  const versionLine = this.version ? theme.ui.muted(`v${this.version} · support@ero.solar`) : null;
388
388
  const hintLine = theme.ui.muted('/help for commands · /model to switch · /secrets for keys');
389
- const width = output.columns ?? 80;
390
- const banner = [header, profileLine, workspaceLine, versionLine, hintLine, renderDivider(width)]
389
+ const message = [header, modelLine, profileLine, workspaceLine, versionLine, hintLine]
391
390
  .filter(Boolean)
392
391
  .join('\n');
393
- if (this.bannerShown && !force) {
394
- return;
395
- }
396
- if (this.launchBannerText === banner) {
397
- return;
398
- }
399
- display.stream(`${banner}\n`);
400
- this.bannerShown = true;
401
- this.launchBannerText = banner;
392
+ display.stream(`\n${message}\n\n`);
393
+ this.welcomeShown = true;
402
394
  this.requestPromptRefresh(true);
403
395
  }
404
396
  async start(initialPrompt) {
405
- this.renderWelcomeBanner();
397
+ this.showWelcomeBanner();
406
398
  if (initialPrompt) {
407
399
  await this.processInputBlock(initialPrompt);
408
400
  return;
@@ -580,11 +572,11 @@ export class InteractiveShell {
580
572
  if (!changed && source === 'shortcut') {
581
573
  return;
582
574
  }
583
- display.showInfo(`Auto-continue ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
584
- (this.autoContinueEnabled
585
- ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
586
- : 'The model will not be auto-prompted to continue.') +
587
- ' Toggle with Ctrl+Shift+C.');
575
+ const modeLabel = this.autoContinueEnabled ? 'enabled' : 'disabled';
576
+ const behavior = this.autoContinueEnabled
577
+ ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
578
+ : 'The model will not be auto-prompted to continue.';
579
+ display.showInfo(`Auto-continue ${modeLabel}. ${behavior} Toggle with Ctrl+Shift+C.`);
588
580
  }
589
581
  /**
590
582
  * Cycle through thinking modes (Tab shortcut).
@@ -1385,7 +1377,10 @@ export class InteractiveShell {
1385
1377
  // Emit a streaming note for stop/quit so the status stays inside the stream
1386
1378
  if (reason === 'stop' || reason === 'quit') {
1387
1379
  const note = reason === 'quit' ? 'Session closed.' : 'Stream stopped.';
1388
- this.finishStreamingFormatter(note, { refreshPrompt: false });
1380
+ this.finishStreamingFormatter(note, {
1381
+ refreshPrompt: false,
1382
+ mode: reason,
1383
+ });
1389
1384
  }
1390
1385
  // Force refresh to update the input area now that streaming has ended
1391
1386
  if (!quiet) {
@@ -1415,15 +1410,17 @@ export class InteractiveShell {
1415
1410
  if (!this.streamingFormatter) {
1416
1411
  return;
1417
1412
  }
1418
- const closing = this.streamingFormatter.finish(note);
1413
+ const closing = this.streamingFormatter.finish({
1414
+ note,
1415
+ mode: options?.mode ?? 'complete',
1416
+ });
1419
1417
  if (closing) {
1420
1418
  this.terminalInput.streamContent(closing);
1421
1419
  }
1422
- if (!this.streamingThoughtRecorded && this.streamingThoughtBuffer.trim()) {
1420
+ if (this.streamingThoughtBuffer.trim()) {
1423
1421
  this.ui.controller.recordAssistantThought(this.streamingThoughtBuffer.trim());
1424
1422
  }
1425
1423
  this.streamingThoughtBuffer = '';
1426
- this.streamingThoughtRecorded = false;
1427
1424
  this.streamingFormatter = null;
1428
1425
  if (options?.refreshPrompt ?? true) {
1429
1426
  this.requestPromptRefresh(true);
@@ -1436,18 +1433,14 @@ export class InteractiveShell {
1436
1433
  return `${prefix} ${label}`.trim();
1437
1434
  }
1438
1435
  captureStreamingThought(chunk) {
1439
- if (this.streamingThoughtRecorded) {
1440
- return;
1441
- }
1442
1436
  const normalized = chunk.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
1443
1437
  for (const char of normalized) {
1444
1438
  if (char === '\n') {
1445
1439
  const thought = this.streamingThoughtBuffer.trim();
1446
1440
  if (thought) {
1447
1441
  this.ui.controller.recordAssistantThought(thought);
1448
- this.streamingThoughtRecorded = true;
1449
1442
  this.streamingThoughtBuffer = '';
1450
- return;
1443
+ continue;
1451
1444
  }
1452
1445
  this.streamingThoughtBuffer = '';
1453
1446
  continue;
@@ -1455,9 +1448,7 @@ export class InteractiveShell {
1455
1448
  this.streamingThoughtBuffer += char;
1456
1449
  if (this.streamingThoughtBuffer.length > 240) {
1457
1450
  this.ui.controller.recordAssistantThought(this.streamingThoughtBuffer.trim());
1458
- this.streamingThoughtRecorded = true;
1459
1451
  this.streamingThoughtBuffer = '';
1460
- return;
1461
1452
  }
1462
1453
  }
1463
1454
  }
@@ -1718,7 +1709,6 @@ export class InteractiveShell {
1718
1709
  this.showAlphaZeroMetrics();
1719
1710
  break;
1720
1711
  case '/suggestions':
1721
- case '/improve':
1722
1712
  this.showImprovementSuggestions();
1723
1713
  break;
1724
1714
  case '/plugins':
@@ -2145,7 +2135,7 @@ export class InteractiveShell {
2145
2135
  lines.push(theme.bold('Session File Changes'));
2146
2136
  lines.push('');
2147
2137
  lines.push(`${theme.info('•')} ${summary.files} file${summary.files === 1 ? '' : 's'} modified`);
2148
- lines.push(`${theme.info('•')} ${theme.success('+' + summary.additions)} ${theme.error('-' + summary.removals)} lines`);
2138
+ lines.push(`${theme.info('•')} ${theme.success(`+${summary.additions}`)} ${theme.error(`-${summary.removals}`)} lines`);
2149
2139
  lines.push('');
2150
2140
  // Group changes by file
2151
2141
  const fileMap = new Map();
@@ -2169,7 +2159,7 @@ export class InteractiveShell {
2169
2159
  if (stats.writes > 0)
2170
2160
  operations.push(`${stats.writes} write${stats.writes === 1 ? '' : 's'}`);
2171
2161
  const opsText = operations.join(', ');
2172
- const diffText = `${theme.success('+' + stats.additions)} ${theme.error('-' + stats.removals)}`;
2162
+ const diffText = `${theme.success(`+${stats.additions}`)} ${theme.error(`-${stats.removals}`)}`;
2173
2163
  lines.push(` ${theme.dim(path)}`);
2174
2164
  lines.push(` ${opsText} • ${diffText}`);
2175
2165
  }
@@ -2626,7 +2616,7 @@ export class InteractiveShell {
2626
2616
  if (!isValidSourceRepo(this.workingDir)) {
2627
2617
  display.showWarning('Self-evolution requires a git repository with source code.');
2628
2618
  display.showSystemMessage('');
2629
- display.showSystemMessage('Current directory: ' + this.workingDir);
2619
+ display.showSystemMessage(`Current directory: ${this.workingDir}`);
2630
2620
  display.showSystemMessage('');
2631
2621
  display.showSystemMessage('Requirements:');
2632
2622
  display.showSystemMessage(' • Must be a git repository (.git folder)');
@@ -2636,7 +2626,6 @@ export class InteractiveShell {
2636
2626
  return;
2637
2627
  }
2638
2628
  const repoName = getRepoName(this.workingDir);
2639
- const isErosolar = isErosolarRepo(this.workingDir);
2640
2629
  if (subcommand === 'analyze') {
2641
2630
  display.showSystemMessage(theme.gradient.primary(`🔍 Analyzing ${repoName} Source Code...`));
2642
2631
  display.showSystemMessage('');
@@ -3008,13 +2997,6 @@ export class InteractiveShell {
3008
2997
  ' /offsec win|fail|detect <actionId> [note]\n' +
3009
2998
  ' /offsec resume <runId>\n' +
3010
2999
  ' /offsec runs';
3011
- const loadRun = (explicitId) => {
3012
- const run = resumeOffsecRun(explicitId ?? this.offsecRunId);
3013
- if (!run) {
3014
- display.showWarning('No offsec run found. Start one with /offsec start "<objective>".');
3015
- }
3016
- return run;
3017
- };
3018
3000
  if (sub === 'start') {
3019
3001
  const rest = args.slice(1);
3020
3002
  const scope = [];
@@ -3453,8 +3435,7 @@ export class InteractiveShell {
3453
3435
  const value = tokens[0]?.toLowerCase();
3454
3436
  if (!value) {
3455
3437
  // Show current status
3456
- display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
3457
- `Use /autocontinue on|off or Ctrl+Shift+C to toggle.`);
3438
+ display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. Use /autocontinue on|off or Ctrl+Shift+C to toggle.`);
3458
3439
  return;
3459
3440
  }
3460
3441
  if (value !== 'on' && value !== 'off') {
@@ -3464,7 +3445,7 @@ export class InteractiveShell {
3464
3445
  this.setAutoContinueMode(value === 'on', 'command');
3465
3446
  }
3466
3447
  // ==================== Claude Code Style Commands ====================
3467
- async handleRewindCommand(input) {
3448
+ async handleRewindCommand(_input) {
3468
3449
  const lines = [];
3469
3450
  lines.push(theme.bold('Rewind / Checkpoint System'));
3470
3451
  lines.push('');
@@ -3480,7 +3461,7 @@ export class InteractiveShell {
3480
3461
  lines.push(theme.ui.muted('Press Esc+Esc for quick access to the rewind menu'));
3481
3462
  display.showSystemMessage(lines.join('\n'));
3482
3463
  }
3483
- handleMemoryCommand(input) {
3464
+ handleMemoryCommand(_input) {
3484
3465
  const lines = [];
3485
3466
  lines.push(theme.bold('Memory System (EROSOLAR.md)'));
3486
3467
  lines.push('');
@@ -3521,7 +3502,7 @@ export class InteractiveShell {
3521
3502
  lines.push(theme.ui.muted('Vim mode is experimental. Toggle with /vim'));
3522
3503
  display.showSystemMessage(lines.join('\n'));
3523
3504
  }
3524
- handleOutputStyleCommand(input) {
3505
+ handleOutputStyleCommand(_input) {
3525
3506
  const lines = [];
3526
3507
  lines.push(theme.bold('Output Styles'));
3527
3508
  lines.push('');
@@ -3642,7 +3623,7 @@ export class InteractiveShell {
3642
3623
  }
3643
3624
  }
3644
3625
  }
3645
- handleExportCommand(input) {
3626
+ handleExportCommand(_input) {
3646
3627
  const lines = [];
3647
3628
  lines.push(theme.bold('Export Conversation'));
3648
3629
  lines.push('');
@@ -4235,7 +4216,7 @@ export class InteractiveShell {
4235
4216
  const parts = tool.name.split('__');
4236
4217
  const serverName = parts[1] || '';
4237
4218
  const toolName = parts.slice(2).join('__') || tool.name;
4238
- lines.push(` ${theme.info('•')} ${theme.ui.muted(serverName + ':')} ${toolName}`);
4219
+ lines.push(` ${theme.info('•')} ${theme.ui.muted(`${serverName}:`)} ${toolName}`);
4239
4220
  }
4240
4221
  if (mcpTools.length > 15) {
4241
4222
  lines.push(` ${theme.ui.muted(`... and ${mcpTools.length - 15} more`)}`);
@@ -4696,7 +4677,7 @@ export class InteractiveShell {
4696
4677
  // Start streaming - no header needed, the input area already provides context
4697
4678
  this.startStreamingHeartbeat('Streaming response');
4698
4679
  responseText = await agent.send(request, true);
4699
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4680
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4700
4681
  await this.awaitPendingCleanup();
4701
4682
  this.captureHistorySnapshot();
4702
4683
  this.autosaveIfEnabled();
@@ -4752,7 +4733,7 @@ export class InteractiveShell {
4752
4733
  }
4753
4734
  }
4754
4735
  finally {
4755
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4736
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4756
4737
  display.stopThinking(false);
4757
4738
  this.uiUpdates.setMode('processing');
4758
4739
  this.stopStreamingHeartbeat('complete', { quiet: true });
@@ -4761,7 +4742,6 @@ export class InteractiveShell {
4761
4742
  this.terminalInput.setStreaming(false);
4762
4743
  this.uiAdapter.endProcessing('Ready for prompts');
4763
4744
  this.setIdleStatus();
4764
- display.newLine();
4765
4745
  this.updateStatusMessage(null);
4766
4746
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
4767
4747
  // CRITICAL: Ensure readline prompt is active for user input
@@ -4845,7 +4825,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
4845
4825
  display.showThinking('Responding...');
4846
4826
  this.refreshStatusLine(true);
4847
4827
  const response = await agent.send(currentPrompt, true);
4848
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4828
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4849
4829
  await this.awaitPendingCleanup();
4850
4830
  this.captureHistorySnapshot();
4851
4831
  this.autosaveIfEnabled();
@@ -4978,7 +4958,7 @@ What's the next action?`;
4978
4958
  }
4979
4959
  }
4980
4960
  finally {
4981
- this.finishStreamingFormatter(undefined, { refreshPrompt: false });
4961
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
4982
4962
  const totalElapsed = Date.now() - overallStartTime;
4983
4963
  const minutes = Math.floor(totalElapsed / 60000);
4984
4964
  const seconds = Math.floor((totalElapsed % 60000) / 1000);
@@ -4993,10 +4973,6 @@ What's the next action?`;
4993
4973
  this.uiAdapter.endProcessing('Ready for prompts');
4994
4974
  this.setIdleStatus();
4995
4975
  this.updateStatusMessage(null);
4996
- display.newLine();
4997
- // Claude Code style: Show unified status bar before prompt
4998
- // This creates consistent UI between startup and post-streaming
4999
- this.showUnifiedStatusBar();
5000
4976
  queueMicrotask(() => this.uiUpdates.setMode('idle'));
5001
4977
  // CRITICAL: Ensure readline prompt is active for user input
5002
4978
  // Claude Code style: New prompt naturally appears at bottom
@@ -5190,7 +5166,14 @@ What's the next action?`;
5190
5166
  return latest;
5191
5167
  }
5192
5168
  formatCommandError(error) {
5193
- const parts = [error?.stdout, error?.stderr, error?.message].filter((part) => typeof part === 'string' && part.trim());
5169
+ const candidates = [];
5170
+ if (error && typeof error === 'object') {
5171
+ const stdout = error.stdout;
5172
+ const stderr = error.stderr;
5173
+ const message = error.message;
5174
+ candidates.push(stdout, stderr, message);
5175
+ }
5176
+ const parts = candidates.filter((part) => typeof part === 'string' && part.trim().length > 0);
5194
5177
  return parts.join('\n').trim();
5195
5178
  }
5196
5179
  runAutoQualityChecks(trigger, assistantResponse, verificationContext) {
@@ -5523,7 +5506,6 @@ Return ONLY JSON array:
5523
5506
  display.showThinking('Analyzing build errors');
5524
5507
  this.refreshStatusLine(true);
5525
5508
  const response = await this.agent.send(prompt, true);
5526
- this.finishStreamingFormatter();
5527
5509
  display.stopThinking();
5528
5510
  this.refreshStatusLine(true);
5529
5511
  if (response) {
@@ -5536,7 +5518,7 @@ Return ONLY JSON array:
5536
5518
  display.showWarning('Agent could not automatically fix build errors. Please review manually.');
5537
5519
  }
5538
5520
  finally {
5539
- this.finishStreamingFormatter();
5521
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
5540
5522
  }
5541
5523
  }
5542
5524
  rebuildAgent() {
@@ -5707,9 +5689,9 @@ Return ONLY JSON array:
5707
5689
  * Ensures the input area is visible and ready for input, just like on startup.
5708
5690
  */
5709
5691
  resetChatBoxAfterModelSwap() {
5710
- this.renderWelcomeBanner(true);
5711
5692
  this.updateStatusMessage(null);
5712
5693
  this.terminalInput.setStreaming(false);
5694
+ this.requestPromptRefresh(true);
5713
5695
  this.ensureReadlineReady();
5714
5696
  }
5715
5697
  /**
@@ -6262,7 +6244,7 @@ Return ONLY JSON array:
6262
6244
  const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
6263
6245
  const partialNote = info.partialResponse ? ' Received partial stream before failure.' : '';
6264
6246
  display.showWarning(`Streaming failed${reason}, retrying without streaming.${detail}${partialNote}`);
6265
- this.finishStreamingFormatter('Stream interrupted - retrying without streaming');
6247
+ this.finishStreamingFormatter('Stream interrupted - retrying without streaming', { mode: 'update' });
6266
6248
  this.startStreamingHeartbeat('Fallback in progress');
6267
6249
  this.requestPromptRefresh(true);
6268
6250
  }
@@ -6363,7 +6345,7 @@ Return ONLY JSON array:
6363
6345
  // Truncate to reasonable length
6364
6346
  const maxLength = 50;
6365
6347
  return cleaned.length > maxLength
6366
- ? cleaned.slice(0, maxLength - 3) + '...'
6348
+ ? `${cleaned.slice(0, maxLength - 3)}...`
6367
6349
  : cleaned;
6368
6350
  }
6369
6351
  splitThinkingResponse(content) {
@@ -6528,13 +6510,15 @@ Return ONLY JSON array:
6528
6510
  await this.scanLocalProviders();
6529
6511
  break;
6530
6512
  case 'use':
6531
- const provider = tokens[1]?.toLowerCase();
6532
- if (!provider) {
6533
- display.showWarning('Usage: /local use <provider>');
6534
- display.showInfo('Example: /local use ollama');
6535
- return;
6513
+ {
6514
+ const provider = tokens[1]?.toLowerCase();
6515
+ if (!provider) {
6516
+ display.showWarning('Usage: /local use <provider>');
6517
+ display.showInfo('Example: /local use ollama');
6518
+ return;
6519
+ }
6520
+ await this.handleProviderCommand(`/provider ${provider}`);
6536
6521
  }
6537
- await this.handleProviderCommand(`/provider ${provider}`);
6538
6522
  break;
6539
6523
  default:
6540
6524
  this.showLocalHelp();
@@ -6641,13 +6625,6 @@ Return ONLY JSON array:
6641
6625
  setProviderStatus(providers) {
6642
6626
  this.cachedProviderStatus = providers;
6643
6627
  }
6644
- /**
6645
- * Show the unified status bar (Claude Code style).
6646
- * Displays provider indicators and ready hints before the prompt.
6647
- */
6648
- showUnifiedStatusBar() {
6649
- display.showUnifiedStatusBar(this.cachedProviderStatus);
6650
- }
6651
6628
  }
6652
6629
  function setsEqual(first, second) {
6653
6630
  if (first.size !== second.size) {