erosolar-cli 1.7.199 → 1.7.200

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 (38) hide show
  1. package/dist/core/agent.d.ts +3 -0
  2. package/dist/core/agent.d.ts.map +1 -1
  3. package/dist/core/agent.js +109 -18
  4. package/dist/core/agent.js.map +1 -1
  5. package/dist/core/toolPreconditions.js +1 -1
  6. package/dist/core/toolPreconditions.js.map +1 -1
  7. package/dist/core/toolRuntime.js +1 -1
  8. package/dist/core/toolRuntime.js.map +1 -1
  9. package/dist/core/toolValidation.js +1 -1
  10. package/dist/core/toolValidation.js.map +1 -1
  11. package/dist/shell/interactiveShell.d.ts +18 -0
  12. package/dist/shell/interactiveShell.d.ts.map +1 -1
  13. package/dist/shell/interactiveShell.js +100 -23
  14. package/dist/shell/interactiveShell.js.map +1 -1
  15. package/dist/shell/terminalInput.d.ts +16 -1
  16. package/dist/shell/terminalInput.d.ts.map +1 -1
  17. package/dist/shell/terminalInput.js +86 -31
  18. package/dist/shell/terminalInput.js.map +1 -1
  19. package/dist/shell/terminalInputAdapter.d.ts +12 -0
  20. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  21. package/dist/shell/terminalInputAdapter.js +14 -0
  22. package/dist/shell/terminalInputAdapter.js.map +1 -1
  23. package/dist/tools/fileTools.d.ts.map +1 -1
  24. package/dist/tools/fileTools.js +102 -39
  25. package/dist/tools/fileTools.js.map +1 -1
  26. package/dist/ui/display.d.ts +1 -0
  27. package/dist/ui/display.d.ts.map +1 -1
  28. package/dist/ui/display.js +9 -8
  29. package/dist/ui/display.js.map +1 -1
  30. package/dist/ui/keyboardShortcuts.js +1 -1
  31. package/dist/ui/keyboardShortcuts.js.map +1 -1
  32. package/dist/ui/persistentPrompt.d.ts +50 -0
  33. package/dist/ui/persistentPrompt.d.ts.map +1 -0
  34. package/dist/ui/persistentPrompt.js +92 -0
  35. package/dist/ui/persistentPrompt.js.map +1 -0
  36. package/dist/ui/shortcutsHelp.js +2 -2
  37. package/dist/ui/shortcutsHelp.js.map +1 -1
  38. package/package.json +1 -1
@@ -74,6 +74,8 @@ export class InteractiveShell {
74
74
  pendingSecretRetry = null;
75
75
  terminalInput;
76
76
  currentInput = '';
77
+ ctrlCPressCount = 0;
78
+ ctrlCHandledThisPress = false;
77
79
  pendingCleanup = null;
78
80
  cleanupInProgress = false;
79
81
  slashPreviewVisible = false;
@@ -94,6 +96,7 @@ export class InteractiveShell {
94
96
  sessionPreferences;
95
97
  autosaveEnabled;
96
98
  autoContinueEnabled;
99
+ verificationEnabled = true;
97
100
  editGuardMode = 'display-edits';
98
101
  pendingPermissionInput = null;
99
102
  pendingHistoryLoad = null;
@@ -184,9 +187,12 @@ export class InteractiveShell {
184
187
  this.terminalInput = new TerminalInputAdapter(input, output, {
185
188
  onSubmit: (text) => this.processInput(text),
186
189
  onQueue: (text) => this.handleQueuedInput(text),
190
+ onCtrlC: ({ hadBuffer }) => this.handleCtrlCPress(hadBuffer),
187
191
  onInterrupt: () => this.handleInterrupt(),
188
192
  onChange: (text) => this.handleInputChange(text),
189
193
  onEditModeChange: (mode) => this.handleEditModeChange(mode),
194
+ onToggleVerify: () => this.toggleVerificationMode(),
195
+ onToggleAutoContinue: () => this.toggleAutoContinueMode(),
190
196
  });
191
197
  // Register output interceptor for cursor positioning during streaming
192
198
  this.terminalInput.registerOutputInterceptor(display);
@@ -195,6 +201,7 @@ export class InteractiveShell {
195
201
  this.setupStatusTracking();
196
202
  this.refreshContextGauge();
197
203
  this.terminalInput.start();
204
+ this.refreshControlBar();
198
205
  this.rebuildAgent();
199
206
  this.setupHandlers();
200
207
  this.refreshBannerSessionInfo();
@@ -281,6 +288,9 @@ export class InteractiveShell {
281
288
  */
282
289
  handleInputChange(text) {
283
290
  this.currentInput = text;
291
+ if (text.length > 0) {
292
+ this.resetCtrlCSequence();
293
+ }
284
294
  this.handleSlashCommandPreviewChange();
285
295
  }
286
296
  /**
@@ -297,6 +307,41 @@ export class InteractiveShell {
297
307
  }
298
308
  this.terminalInput.render();
299
309
  }
310
+ toggleVerificationMode() {
311
+ this.setVerificationMode(!this.verificationEnabled, 'shortcut');
312
+ }
313
+ setVerificationMode(enabled, source) {
314
+ const changed = this.verificationEnabled !== enabled;
315
+ this.verificationEnabled = enabled;
316
+ this.refreshControlBar();
317
+ if (!changed && source === 'shortcut') {
318
+ return;
319
+ }
320
+ const message = enabled
321
+ ? '✅ Verification on. Auto-tests will run after edits. (alt+v to toggle)'
322
+ : '⏭️ Verification off. Skipping auto-tests until re-enabled. (alt+v to toggle)';
323
+ display.showSystemMessage(message);
324
+ }
325
+ toggleAutoContinueMode() {
326
+ this.setAutoContinueMode(!this.autoContinueEnabled, 'shortcut');
327
+ }
328
+ setAutoContinueMode(enabled, source) {
329
+ const changed = this.autoContinueEnabled !== enabled;
330
+ this.autoContinueEnabled = enabled;
331
+ saveSessionPreferences({ autoContinue: this.autoContinueEnabled });
332
+ if (this.agent) {
333
+ this.agent.setAutoContinue(this.autoContinueEnabled);
334
+ }
335
+ this.refreshControlBar();
336
+ if (!changed && source === 'shortcut') {
337
+ return;
338
+ }
339
+ display.showInfo(`Auto-continue ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
340
+ (this.autoContinueEnabled
341
+ ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
342
+ : 'The model will not be auto-prompted to continue.') +
343
+ ' Toggle with alt+c.');
344
+ }
300
345
  /**
301
346
  * Gate submissions when edit permission mode is active.
302
347
  * Returns the text to send when approved, or null when waiting for confirmation.
@@ -342,15 +387,51 @@ export class InteractiveShell {
342
387
  this.terminalInput.render();
343
388
  }
344
389
  /**
345
- * TerminalInputAdapter interrupt handler (Ctrl+C with empty buffer)
390
+ * Handle Ctrl+C presses in three stages:
391
+ * 1) Clear chat box text
392
+ * 2) Pause/interrupt AI execution (even while streaming)
393
+ * 3) Quit the program
346
394
  */
347
- handleInterrupt() {
395
+ handleCtrlCPress(hadBuffer) {
396
+ this.ctrlCHandledThisPress = true;
397
+ this.ctrlCPressCount = Math.min(this.ctrlCPressCount + 1, 3);
398
+ if (this.ctrlCPressCount === 1) {
399
+ this.clearChatInput();
400
+ const prefix = hadBuffer ? 'Input cleared.' : 'Nothing to clear.';
401
+ display.showSystemMessage(`${prefix} Press Ctrl+C again to pause the AI; a third time quits.`);
402
+ this.terminalInput.render();
403
+ return;
404
+ }
405
+ if (this.ctrlCPressCount === 2) {
406
+ this.pauseAiExecution();
407
+ return;
408
+ }
409
+ this.shutdown();
410
+ }
411
+ pauseAiExecution() {
348
412
  if (this.isProcessing && this.agent) {
349
413
  this.agent.requestCancellation();
350
- display.showWarning('Cancelling current operation... (Ctrl+C again to force quit)');
414
+ display.showWarning('Pausing AI execution... (Press Ctrl+C again to quit)');
351
415
  return;
352
416
  }
353
- this.shutdown();
417
+ display.showInfo('No active AI execution to pause. Press Ctrl+C again to quit.');
418
+ }
419
+ resetCtrlCSequence() {
420
+ this.ctrlCPressCount = 0;
421
+ this.ctrlCHandledThisPress = false;
422
+ }
423
+ clearChatInput() {
424
+ this.pendingPermissionInput = null;
425
+ this.handleInputChange('');
426
+ }
427
+ /**
428
+ * TerminalInputAdapter interrupt handler (Ctrl+C with empty buffer)
429
+ */
430
+ handleInterrupt() {
431
+ if (!this.ctrlCHandledThisPress) {
432
+ this.handleCtrlCPress(false);
433
+ }
434
+ this.ctrlCHandledThisPress = false;
354
435
  }
355
436
  /**
356
437
  * Gracefully tear down the shell and exit
@@ -570,6 +651,10 @@ export class InteractiveShell {
570
651
  this.uiAdapter.updateContextUsage(percentage);
571
652
  this.terminalInput.setContextUsage(percentage);
572
653
  }
654
+ refreshControlBar() {
655
+ // Status indicators are now shown inline in the prompt
656
+ this.terminalInput.render();
657
+ }
573
658
  handleSlashCommandPreviewChange() {
574
659
  if (this.pendingInteraction) {
575
660
  this.slashPreviewVisible = false;
@@ -631,8 +716,6 @@ export class InteractiveShell {
631
716
  }
632
717
  startStreamingHeartbeat(label = 'Streaming response') {
633
718
  this.stopStreamingHeartbeat();
634
- // Set up scroll region so status stays fixed at bottom while content streams above
635
- display.setupScrollRegion();
636
719
  this.streamingHeartbeatStart = Date.now();
637
720
  this.streamingHeartbeatFrame = 0;
638
721
  const frames = ['●', '◐', '◉', '◐'];
@@ -643,10 +726,10 @@ export class InteractiveShell {
643
726
  ? Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000)
644
727
  : 0;
645
728
  const suffix = elapsed > 0 ? ` • ${elapsed}s` : '';
646
- display.updateStreamingStatus(`${theme.info(frame)} ${label}${suffix}`);
729
+ this.terminalInput.setStatusMessage(`${frame} ${label}${suffix}`);
647
730
  };
648
731
  tick();
649
- this.streamingHeartbeat = setInterval(tick, 500); // Faster animation with scroll region
732
+ this.streamingHeartbeat = setInterval(tick, 500);
650
733
  }
651
734
  stopStreamingHeartbeat() {
652
735
  if (this.streamingHeartbeat) {
@@ -654,9 +737,7 @@ export class InteractiveShell {
654
737
  this.streamingHeartbeat = null;
655
738
  }
656
739
  this.streamingHeartbeatStart = null;
657
- // Tear down scroll region and clear status
658
- display.teardownScrollRegion();
659
- display.clearStreamingStatus();
740
+ this.terminalInput.setStatusMessage(null);
660
741
  }
661
742
  refreshQueueIndicators() {
662
743
  if (this.isProcessing) {
@@ -1441,23 +1522,14 @@ export class InteractiveShell {
1441
1522
  if (!value) {
1442
1523
  // Show current status
1443
1524
  display.showInfo(`Auto-continue is ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
1444
- `Use /autocontinue on|off to toggle.`);
1525
+ `Use /autocontinue on|off or alt+c to toggle.`);
1445
1526
  return;
1446
1527
  }
1447
1528
  if (value !== 'on' && value !== 'off') {
1448
1529
  display.showWarning('Usage: /autocontinue on|off');
1449
1530
  return;
1450
1531
  }
1451
- this.autoContinueEnabled = value === 'on';
1452
- saveSessionPreferences({ autoContinue: this.autoContinueEnabled });
1453
- // Update the current agent if it exists
1454
- if (this.agent) {
1455
- this.agent.setAutoContinue(this.autoContinueEnabled);
1456
- }
1457
- display.showInfo(`Auto-continue ${this.autoContinueEnabled ? 'enabled' : 'disabled'}. ` +
1458
- (this.autoContinueEnabled
1459
- ? 'The model will be auto-prompted to continue when it expresses intent but does not use tools.'
1460
- : 'The model will not be auto-prompted to continue.'));
1532
+ this.setAutoContinueMode(value === 'on', 'command');
1461
1533
  }
1462
1534
  updateActiveSession(summary, remember = false) {
1463
1535
  this.activeSessionId = summary?.id ?? null;
@@ -2464,6 +2536,9 @@ What's the next action?`;
2464
2536
  if (this.autoTestInFlight) {
2465
2537
  return;
2466
2538
  }
2539
+ if (!this.verificationEnabled) {
2540
+ return;
2541
+ }
2467
2542
  const latestChange = this.getLatestFileChangeTimestamp();
2468
2543
  if (!latestChange) {
2469
2544
  return;
@@ -2930,7 +3005,9 @@ What's the next action?`;
2930
3005
  handleStreamingFallback(info) {
2931
3006
  const detailText = info.message.trim();
2932
3007
  const detail = detailText ? ` Error: ${detailText}` : '';
2933
- display.showWarning(`Streaming failed, retrying without streaming.${detail}`);
3008
+ const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
3009
+ const partialNote = info.partialResponse ? ' Received partial stream before failure.' : '';
3010
+ display.showWarning(`Streaming failed${reason}, retrying without streaming.${detail}${partialNote}`);
2934
3011
  this.startStreamingHeartbeat('Fallback in progress');
2935
3012
  this.requestPromptRefresh(true);
2936
3013
  }