erosolar-cli 1.7.155 → 1.7.156
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.
- package/README.md +7 -17
- package/agents/erosolar-code.rules.json +1 -111
- package/agents/general.rules.json +0 -6
- package/dist/bin/erosolar.js +0 -22
- package/dist/bin/erosolar.js.map +1 -1
- package/dist/bin/selfTest.js +2 -190
- package/dist/bin/selfTest.js.map +1 -1
- package/dist/core/agent.d.ts +0 -12
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +12 -75
- package/dist/core/agent.js.map +1 -1
- package/dist/core/contextManager.d.ts +1 -2
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +2 -11
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/multilinePasteHandler.d.ts +4 -8
- package/dist/core/multilinePasteHandler.d.ts.map +1 -1
- package/dist/core/multilinePasteHandler.js +17 -67
- package/dist/core/multilinePasteHandler.js.map +1 -1
- package/dist/core/preferences.js +2 -8
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/resultVerification.d.ts +1 -1
- package/dist/core/resultVerification.d.ts.map +1 -1
- package/dist/core/resultVerification.js +10 -11
- package/dist/core/resultVerification.js.map +1 -1
- package/dist/core/schemaValidator.d.ts.map +1 -1
- package/dist/core/schemaValidator.js +1 -36
- package/dist/core/schemaValidator.js.map +1 -1
- package/dist/core/toolRuntime.d.ts +0 -61
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +1 -303
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/unified/schema.d.ts.map +1 -1
- package/dist/core/unified/schema.js +1 -34
- package/dist/core/unified/schema.js.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +0 -3
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropicProvider.js +1 -4
- package/dist/providers/anthropicProvider.js.map +1 -1
- package/dist/shell/bracketedPasteManager.d.ts.map +1 -1
- package/dist/shell/bracketedPasteManager.js +3 -5
- package/dist/shell/bracketedPasteManager.js.map +1 -1
- package/dist/shell/composableMessage.js +2 -2
- package/dist/shell/composableMessage.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +1 -6
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +95 -421
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +0 -7
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.js +5 -5
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/tools/bashTools.d.ts +0 -8
- package/dist/tools/bashTools.d.ts.map +1 -1
- package/dist/tools/bashTools.js +5 -80
- package/dist/tools/bashTools.js.map +1 -1
- package/dist/tools/diffUtils.d.ts.map +1 -1
- package/dist/tools/diffUtils.js +8 -12
- package/dist/tools/diffUtils.js.map +1 -1
- package/dist/tools/editTools.d.ts.map +1 -1
- package/dist/tools/editTools.js +36 -386
- package/dist/tools/editTools.js.map +1 -1
- package/dist/tools/fileTools.d.ts.map +1 -1
- package/dist/tools/fileTools.js +130 -25
- package/dist/tools/fileTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +0 -5
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +0 -21
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +10 -83
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +117 -522
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/dist/ui/richText.js +4 -4
- package/dist/ui/richText.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +13 -31
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/package.json +1 -4
- package/dist/ui/EnhancedPinnedChatBox.d.ts +0 -93
- package/dist/ui/EnhancedPinnedChatBox.d.ts.map +0 -1
- package/dist/ui/EnhancedPinnedChatBox.js +0 -309
- package/dist/ui/EnhancedPinnedChatBox.js.map +0 -1
- package/dist/ui/PinnedChatBoxEnhancer.d.ts +0 -88
- package/dist/ui/PinnedChatBoxEnhancer.d.ts.map +0 -1
- package/dist/ui/PinnedChatBoxEnhancer.js +0 -205
- package/dist/ui/PinnedChatBoxEnhancer.js.map +0 -1
|
@@ -84,7 +84,6 @@ export class InteractiveShell {
|
|
|
84
84
|
cleanupInProgress = false;
|
|
85
85
|
slashPreviewVisible = false;
|
|
86
86
|
keypressHandler = null;
|
|
87
|
-
sigintHandler = null;
|
|
88
87
|
rawDataHandler = null;
|
|
89
88
|
skillRepository;
|
|
90
89
|
skillToolHandlers = new Map();
|
|
@@ -114,6 +113,7 @@ export class InteractiveShell {
|
|
|
114
113
|
_enabledPlugins;
|
|
115
114
|
pinnedChatBox;
|
|
116
115
|
readlineOutputSuppressed = false;
|
|
116
|
+
originalStdoutWrite = null;
|
|
117
117
|
constructor(config) {
|
|
118
118
|
this.profile = config.profile;
|
|
119
119
|
this.profileLabel = config.profileLabel;
|
|
@@ -416,30 +416,6 @@ export class InteractiveShell {
|
|
|
416
416
|
display.showInfo(`${this.agentMenuLabel(profileName)} will load the next time you start the CLI. Restart to switch now.`);
|
|
417
417
|
}
|
|
418
418
|
setupHandlers() {
|
|
419
|
-
// Set up SIGINT handler to clear input instead of exiting when there's content
|
|
420
|
-
// This is a backup in case the keypress handler doesn't catch Ctrl+C
|
|
421
|
-
this.sigintHandler = () => {
|
|
422
|
-
const currentLine = this.rl.line || '';
|
|
423
|
-
const hasComposedContent = this.composableMessage.hasContent();
|
|
424
|
-
const hasAnyContent = currentLine.length > 0 || hasComposedContent;
|
|
425
|
-
if (hasAnyContent) {
|
|
426
|
-
// Clear all input buffers instead of exiting
|
|
427
|
-
this.rl.line = '';
|
|
428
|
-
this.rl.cursor = 0;
|
|
429
|
-
this.composableMessage.clear();
|
|
430
|
-
this.bracketedPaste.reset();
|
|
431
|
-
this.persistentPrompt.updateInput('', 0);
|
|
432
|
-
this.pinnedChatBox.setInput('');
|
|
433
|
-
this.pinnedChatBox.clearPastedBlockState();
|
|
434
|
-
output.write('\r\x1b[K^C\n');
|
|
435
|
-
this.rl.prompt();
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
// No content - allow exit
|
|
439
|
-
output.write('\n👋 Goodbye!\n');
|
|
440
|
-
process.exit(0);
|
|
441
|
-
};
|
|
442
|
-
process.on('SIGINT', this.sigintHandler);
|
|
443
419
|
// Set up raw data interception for bracketed paste
|
|
444
420
|
this.setupRawPasteHandler();
|
|
445
421
|
this.rl.on('line', (line) => {
|
|
@@ -448,10 +424,7 @@ export class InteractiveShell {
|
|
|
448
424
|
if (this.bracketedPaste.isCapturingRaw()) {
|
|
449
425
|
this.resetBufferedInputLines();
|
|
450
426
|
// Clear the line that readline just echoed - move up and clear
|
|
451
|
-
|
|
452
|
-
if (!this.isProcessing) {
|
|
453
|
-
output.write('\x1b[A\r\x1b[K');
|
|
454
|
-
}
|
|
427
|
+
output.write('\x1b[A\r\x1b[K');
|
|
455
428
|
// Show paste progress (this will update in place)
|
|
456
429
|
this.showMultiLinePastePreview(this.bracketedPaste.getRawBufferLineCount(), this.bracketedPaste.getRawBufferPreview());
|
|
457
430
|
return;
|
|
@@ -461,10 +434,7 @@ export class InteractiveShell {
|
|
|
461
434
|
if (this.bracketedPaste.shouldIgnoreLineEvent()) {
|
|
462
435
|
this.resetBufferedInputLines();
|
|
463
436
|
// Clear the echoed line that readline wrote
|
|
464
|
-
|
|
465
|
-
if (!this.isProcessing) {
|
|
466
|
-
output.write('\x1b[A\r\x1b[K');
|
|
467
|
-
}
|
|
437
|
+
output.write('\x1b[A\r\x1b[K');
|
|
468
438
|
return;
|
|
469
439
|
}
|
|
470
440
|
const normalized = this.bracketedPaste.process(line);
|
|
@@ -474,21 +444,15 @@ export class InteractiveShell {
|
|
|
474
444
|
// If still accumulating multi-line paste, show preview
|
|
475
445
|
if (normalized.isPending) {
|
|
476
446
|
// Clear the line that readline just echoed - move up and clear
|
|
477
|
-
|
|
478
|
-
if (!this.isProcessing) {
|
|
479
|
-
output.write('\x1b[A\r\x1b[K');
|
|
480
|
-
}
|
|
447
|
+
output.write('\x1b[A\r\x1b[K');
|
|
481
448
|
this.showMultiLinePastePreview(normalized.lineCount || 0, normalized.preview);
|
|
482
449
|
return;
|
|
483
450
|
}
|
|
484
|
-
// Paste complete
|
|
485
|
-
// This is critical during streaming - pastes should be queued, not submitted
|
|
451
|
+
// Paste complete, store or submit the full content
|
|
486
452
|
if (typeof normalized.result === 'string') {
|
|
487
453
|
this.clearMultiLinePastePreview();
|
|
488
454
|
const lineCount = normalized.lineCount ?? normalized.result.split('\n').length;
|
|
489
|
-
//
|
|
490
|
-
// Not streaming: capture paste and wait for user to press Enter to submit
|
|
491
|
-
// Either way, we NEVER auto-submit pasted content
|
|
455
|
+
// All pastes (single or multi-line) are captured for confirmation before submit
|
|
492
456
|
this.capturePaste(normalized.result, lineCount);
|
|
493
457
|
return;
|
|
494
458
|
}
|
|
@@ -514,11 +478,6 @@ export class InteractiveShell {
|
|
|
514
478
|
this.rawDataHandler(); // This restores the original emit function
|
|
515
479
|
this.rawDataHandler = null;
|
|
516
480
|
}
|
|
517
|
-
// Remove SIGINT handler
|
|
518
|
-
if (this.sigintHandler) {
|
|
519
|
-
process.off('SIGINT', this.sigintHandler);
|
|
520
|
-
this.sigintHandler = null;
|
|
521
|
-
}
|
|
522
481
|
// Clear any pending cleanup to prevent hanging
|
|
523
482
|
this.pendingCleanup = null;
|
|
524
483
|
// Dispose persistent prompt
|
|
@@ -605,114 +564,15 @@ export class InteractiveShell {
|
|
|
605
564
|
if (inputStream.setRawMode && !inputStream.isRaw) {
|
|
606
565
|
inputStream.setRawMode(true);
|
|
607
566
|
}
|
|
608
|
-
// Track last escape time for double-escape detection
|
|
609
|
-
let lastEscapeTime = 0;
|
|
610
567
|
this.keypressHandler = (_str, key) => {
|
|
611
568
|
// Handle special keys
|
|
612
569
|
if (key) {
|
|
613
|
-
// Shift+Enter or Option+Enter: Insert newline for multi-line input
|
|
614
|
-
if ((key.name === 'enter' || key.name === 'return') && (key.shift || key.meta)) {
|
|
615
|
-
const currentLine = this.rl.line || '';
|
|
616
|
-
const cursorPos = this.rl.cursor || 0;
|
|
617
|
-
// Insert newline at cursor position
|
|
618
|
-
const newLine = currentLine.slice(0, cursorPos) + '\n' + currentLine.slice(cursorPos);
|
|
619
|
-
this.rl.line = newLine;
|
|
620
|
-
this.rl.cursor = cursorPos + 1;
|
|
621
|
-
this.persistentPrompt.updateInput(newLine, cursorPos + 1);
|
|
622
|
-
this.pinnedChatBox.setInput(newLine, cursorPos + 1);
|
|
623
|
-
if (this.isProcessing) {
|
|
624
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
625
|
-
}
|
|
626
|
-
return; // Prevent readline from submitting
|
|
627
|
-
}
|
|
628
|
-
// Backslash+Enter: Insert newline (escape sequence for multi-line)
|
|
629
|
-
if (key.name === 'enter' || key.name === 'return') {
|
|
630
|
-
const currentLine = this.rl.line || '';
|
|
631
|
-
const cursorPos = this.rl.cursor || 0;
|
|
632
|
-
// Check if last char before cursor is backslash
|
|
633
|
-
if (cursorPos > 0 && currentLine[cursorPos - 1] === '\\') {
|
|
634
|
-
// Replace backslash with newline
|
|
635
|
-
const newLine = currentLine.slice(0, cursorPos - 1) + '\n' + currentLine.slice(cursorPos);
|
|
636
|
-
this.rl.line = newLine;
|
|
637
|
-
this.rl.cursor = cursorPos; // Cursor stays after newline
|
|
638
|
-
this.persistentPrompt.updateInput(newLine, cursorPos);
|
|
639
|
-
this.pinnedChatBox.setInput(newLine, cursorPos);
|
|
640
|
-
if (this.isProcessing) {
|
|
641
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
642
|
-
}
|
|
643
|
-
return; // Prevent readline from submitting
|
|
644
|
-
}
|
|
645
|
-
// During AI streaming, use pinned chat box for input submission
|
|
646
|
-
if (this.isProcessing) {
|
|
647
|
-
// Check if there's content BEFORE calling handleSubmit
|
|
648
|
-
const hadContent = this.pinnedChatBox.getInput().length > 0 || this.pinnedChatBox.hasPastedBlock();
|
|
649
|
-
const hadComposedContent = this.composableMessage.hasContent();
|
|
650
|
-
const submittedInput = this.pinnedChatBox.handleSubmit();
|
|
651
|
-
// If there was content (now queued), clear readline buffer and return
|
|
652
|
-
// This prevents double-queueing via readline's 'line' event
|
|
653
|
-
if (hadContent || hadComposedContent || submittedInput !== null) {
|
|
654
|
-
// Clear readline's buffer to prevent 'line' event from re-processing
|
|
655
|
-
this.rl.line = '';
|
|
656
|
-
this.rl.cursor = 0;
|
|
657
|
-
// Also clear composableMessage to prevent duplication since
|
|
658
|
-
// pinnedChatBox already queued the paste content
|
|
659
|
-
if (hadComposedContent) {
|
|
660
|
-
this.composableMessage.clear();
|
|
661
|
-
}
|
|
662
|
-
// Input was handled by pinned chat box (queued)
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
// Otherwise, let readline handle the enter key normally
|
|
667
|
-
}
|
|
668
|
-
// Ctrl+L: Clear screen (like Claude Code)
|
|
669
|
-
if (key.ctrl && key.name === 'l') {
|
|
670
|
-
display.clear();
|
|
671
|
-
this.rl.prompt();
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
// ? alone: Show keyboard shortcuts help
|
|
675
|
-
if (_str === '?' && !key.ctrl && !key.meta && !key.shift) {
|
|
676
|
-
const currentLine = this.rl.line || '';
|
|
677
|
-
// Only show help if input is empty (typing ? normally otherwise)
|
|
678
|
-
if (currentLine.length === 0 && !this.isProcessing) {
|
|
679
|
-
this.handleShortcutsCommand();
|
|
680
|
-
this.rl.prompt();
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
570
|
// Shift+Tab for profile switching
|
|
685
571
|
if (key.name === 'tab' && key.shift && this.agentMenu) {
|
|
686
572
|
this.showProfileSwitcher();
|
|
687
573
|
}
|
|
688
|
-
// Escape: Cancel current operation
|
|
574
|
+
// Escape: Cancel current operation if agent is running
|
|
689
575
|
if (key.name === 'escape') {
|
|
690
|
-
const now = Date.now();
|
|
691
|
-
const timeSinceLastEscape = now - lastEscapeTime;
|
|
692
|
-
lastEscapeTime = now;
|
|
693
|
-
// Double Escape (within 500ms): Clear input and queue
|
|
694
|
-
if (timeSinceLastEscape < 500 && !this.isProcessing) {
|
|
695
|
-
const currentLine = this.rl.line || '';
|
|
696
|
-
const hasQueue = this.followUpQueue.length > 0;
|
|
697
|
-
if (currentLine.length > 0 || hasQueue) {
|
|
698
|
-
// Clear everything
|
|
699
|
-
this.rl.line = '';
|
|
700
|
-
this.rl.cursor = 0;
|
|
701
|
-
this.composableMessage.clear();
|
|
702
|
-
this.bracketedPaste.reset();
|
|
703
|
-
this.persistentPrompt.updateInput('', 0);
|
|
704
|
-
this.pinnedChatBox.setInput('');
|
|
705
|
-
this.pinnedChatBox.clearPastedBlockState();
|
|
706
|
-
if (hasQueue) {
|
|
707
|
-
this.followUpQueue.length = 0;
|
|
708
|
-
this.refreshQueueIndicators();
|
|
709
|
-
}
|
|
710
|
-
output.write('\r\x1b[K');
|
|
711
|
-
display.showInfo('Input and queue cleared.');
|
|
712
|
-
this.rl.prompt();
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
576
|
if (this.isProcessing && this.agent) {
|
|
717
577
|
this.agent.requestCancellation();
|
|
718
578
|
output.write('\n');
|
|
@@ -740,15 +600,11 @@ export class InteractiveShell {
|
|
|
740
600
|
return;
|
|
741
601
|
}
|
|
742
602
|
const currentLine = this.rl.line || '';
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (hasAnyContent) {
|
|
747
|
-
// Clear all input buffers before exiting
|
|
603
|
+
if (currentLine.length > 0) {
|
|
604
|
+
// Clear the input buffer instead of exiting
|
|
605
|
+
// Write Ctrl+U to clear the line in readline
|
|
748
606
|
this.rl.line = '';
|
|
749
607
|
this.rl.cursor = 0;
|
|
750
|
-
this.composableMessage.clear();
|
|
751
|
-
this.bracketedPaste.reset();
|
|
752
608
|
this.persistentPrompt.updateInput('', 0);
|
|
753
609
|
this.pinnedChatBox.setInput('');
|
|
754
610
|
this.pinnedChatBox.clearPastedBlockState();
|
|
@@ -760,79 +616,6 @@ export class InteractiveShell {
|
|
|
760
616
|
}
|
|
761
617
|
// If no text in buffer, let default Ctrl+C behavior exit
|
|
762
618
|
}
|
|
763
|
-
// Backspace: Handle chip-aware deletion
|
|
764
|
-
if (key.name === 'backspace') {
|
|
765
|
-
const currentLine = this.rl.line || '';
|
|
766
|
-
const cursorPos = this.rl.cursor || 0;
|
|
767
|
-
// Check if we're at the end of a paste chip (character before cursor is ']')
|
|
768
|
-
if (cursorPos > 0 && currentLine[cursorPos - 1] === ']') {
|
|
769
|
-
const beforeCursor = currentLine.slice(0, cursorPos);
|
|
770
|
-
const chipStart = beforeCursor.lastIndexOf('[');
|
|
771
|
-
if (chipStart !== -1) {
|
|
772
|
-
// Verify this looks like a paste chip
|
|
773
|
-
const potentialChip = beforeCursor.slice(chipStart);
|
|
774
|
-
if (potentialChip.match(/^\[(?:📋|📝|📊|📄)[^\]]*\]$/)) {
|
|
775
|
-
// Delete the entire chip by updating readline directly
|
|
776
|
-
const newLine = currentLine.slice(0, chipStart) + currentLine.slice(cursorPos);
|
|
777
|
-
this.rl.line = newLine;
|
|
778
|
-
this.rl.cursor = chipStart;
|
|
779
|
-
// Sync to display components
|
|
780
|
-
this.persistentPrompt.updateInput(newLine, chipStart);
|
|
781
|
-
this.pinnedChatBox.setInput(newLine, chipStart);
|
|
782
|
-
// Check if this was the last chip - clear composable message
|
|
783
|
-
const hasChipRemaining = /\[(?:📋|📝|📊|📄)[^\]]*\]/.test(newLine);
|
|
784
|
-
if (!hasChipRemaining && this.composableMessage.hasContent()) {
|
|
785
|
-
this.composableMessage.clear();
|
|
786
|
-
this.updateComposeStatusSummary();
|
|
787
|
-
}
|
|
788
|
-
// During processing, update the persistent display
|
|
789
|
-
if (this.isProcessing) {
|
|
790
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
791
|
-
}
|
|
792
|
-
else {
|
|
793
|
-
// Refresh readline display
|
|
794
|
-
this.rl.prompt(true);
|
|
795
|
-
}
|
|
796
|
-
// Prevent readline from processing this backspace
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
// Delete key: Handle chip-aware deletion
|
|
803
|
-
if (key.name === 'delete') {
|
|
804
|
-
const currentLine = this.rl.line || '';
|
|
805
|
-
const cursorPos = this.rl.cursor || 0;
|
|
806
|
-
// Check if we're at the start of a paste chip
|
|
807
|
-
if (cursorPos < currentLine.length && currentLine[cursorPos] === '[') {
|
|
808
|
-
const afterCursor = currentLine.slice(cursorPos);
|
|
809
|
-
const chipMatch = afterCursor.match(/^\[(?:📋|📝|📊|📄)[^\]]*\]/);
|
|
810
|
-
if (chipMatch) {
|
|
811
|
-
// Delete the entire chip
|
|
812
|
-
const newLine = currentLine.slice(0, cursorPos) + currentLine.slice(cursorPos + chipMatch[0].length);
|
|
813
|
-
this.rl.line = newLine;
|
|
814
|
-
// Cursor stays in same position
|
|
815
|
-
// Sync to display components
|
|
816
|
-
this.persistentPrompt.updateInput(newLine, cursorPos);
|
|
817
|
-
this.pinnedChatBox.setInput(newLine, cursorPos);
|
|
818
|
-
// Check if this was the last chip
|
|
819
|
-
const hasChipRemaining = /\[(?:📋|📝|📊|📄)[^\]]*\]/.test(newLine);
|
|
820
|
-
if (!hasChipRemaining && this.composableMessage.hasContent()) {
|
|
821
|
-
this.composableMessage.clear();
|
|
822
|
-
this.updateComposeStatusSummary();
|
|
823
|
-
}
|
|
824
|
-
// During processing, update the persistent display
|
|
825
|
-
if (this.isProcessing) {
|
|
826
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
this.rl.prompt(true);
|
|
830
|
-
}
|
|
831
|
-
// Prevent readline from processing this delete
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
619
|
}
|
|
837
620
|
// Readline handles all keyboard input natively (history, shortcuts, etc.)
|
|
838
621
|
// We just sync the current state to our display components
|
|
@@ -841,25 +624,6 @@ export class InteractiveShell {
|
|
|
841
624
|
const currentLine = this.rl.line || '';
|
|
842
625
|
const cursorPos = this.rl.cursor || 0;
|
|
843
626
|
this.persistentPrompt.updateInput(currentLine, cursorPos);
|
|
844
|
-
// During AI streaming, use handleInput for character-by-character input
|
|
845
|
-
// to enable the persistent chat box functionality
|
|
846
|
-
if (this.isProcessing && _str && key && key.name && !key.ctrl && !key.meta) {
|
|
847
|
-
// Handle regular character input during streaming
|
|
848
|
-
if (_str.length === 1 && !['enter', 'return', 'backspace', 'delete', 'escape', 'tab'].includes(key.name)) {
|
|
849
|
-
this.pinnedChatBox.handleInput(_str);
|
|
850
|
-
return; // Skip the normal sync since handleInput already updated the display
|
|
851
|
-
}
|
|
852
|
-
// Handle backspace during streaming
|
|
853
|
-
if (key.name === 'backspace') {
|
|
854
|
-
this.pinnedChatBox.handleBackspace();
|
|
855
|
-
return; // Skip the normal sync since handleBackspace already updated the display
|
|
856
|
-
}
|
|
857
|
-
// Handle delete during streaming
|
|
858
|
-
if (key.name === 'delete') {
|
|
859
|
-
this.pinnedChatBox.handleDelete();
|
|
860
|
-
return; // Skip the normal sync since handleDelete already updated the display
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
627
|
// Sync to pinned chat box for display only
|
|
864
628
|
this.pinnedChatBox.setInput(currentLine, cursorPos);
|
|
865
629
|
// During processing, update the persistent input display so user can see what they're typing
|
|
@@ -867,18 +631,8 @@ export class InteractiveShell {
|
|
|
867
631
|
this.pinnedChatBox.updatePersistentInput();
|
|
868
632
|
}
|
|
869
633
|
if (this.composableMessage.hasContent()) {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
const hasChipInInput = /\[(?:📋|📝|📊|📄)[^\]]*\]/.test(currentLine);
|
|
873
|
-
if (!hasChipInInput) {
|
|
874
|
-
// User deleted all chips - clear the composable message
|
|
875
|
-
this.composableMessage.clear();
|
|
876
|
-
this.updateComposeStatusSummary();
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
this.composableMessage.setDraft(currentLine);
|
|
880
|
-
this.updateComposeStatusSummary();
|
|
881
|
-
}
|
|
634
|
+
this.composableMessage.setDraft(currentLine);
|
|
635
|
+
this.updateComposeStatusSummary();
|
|
882
636
|
}
|
|
883
637
|
this.handleSlashCommandPreviewChange();
|
|
884
638
|
});
|
|
@@ -906,11 +660,7 @@ export class InteractiveShell {
|
|
|
906
660
|
});
|
|
907
661
|
}
|
|
908
662
|
setProcessingStatus(detail) {
|
|
909
|
-
|
|
910
|
-
const model = this.sessionState.model?.toLowerCase() || '';
|
|
911
|
-
const isReasoningModel = model.includes('reasoner') || model.startsWith('o1') || model.includes('-reasoning');
|
|
912
|
-
const baseMessage = isReasoningModel ? '🧠 Reasoning...' : 'Working on your request';
|
|
913
|
-
this.statusTracker.setBase(baseMessage, {
|
|
663
|
+
this.statusTracker.setBase('Working on your request', {
|
|
914
664
|
detail: this.describeStatusDetail(detail),
|
|
915
665
|
tone: 'info',
|
|
916
666
|
});
|
|
@@ -1042,47 +792,71 @@ export class InteractiveShell {
|
|
|
1042
792
|
* Suppress readline's character echo during streaming.
|
|
1043
793
|
* Characters typed will be captured but not echoed to the main output.
|
|
1044
794
|
* Instead, they appear only in the persistent input box at the bottom.
|
|
1045
|
-
*
|
|
1046
|
-
* Strategy: Completely redirect readline's output to a null stream.
|
|
1047
|
-
* AI streaming writes directly to process.stdout, bypassing readline,
|
|
1048
|
-
* so it will still appear. Only readline's echo is suppressed.
|
|
1049
795
|
*/
|
|
1050
796
|
suppressReadlineOutput() {
|
|
1051
797
|
if (this.readlineOutputSuppressed || !output.isTTY) {
|
|
1052
798
|
return;
|
|
1053
799
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
//
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
800
|
+
this.originalStdoutWrite = output.write.bind(output);
|
|
801
|
+
const self = this;
|
|
802
|
+
// Replace stdout.write to filter readline echo
|
|
803
|
+
// Readline writes single characters for echo - we filter those out
|
|
804
|
+
// but allow multi-character writes (actual output) through
|
|
805
|
+
output.write = function (chunk, encodingOrCallback, callback) {
|
|
806
|
+
if (!self.originalStdoutWrite) {
|
|
807
|
+
return true;
|
|
808
|
+
}
|
|
809
|
+
const str = typeof chunk === 'string' ? chunk : chunk.toString();
|
|
810
|
+
// Filter out readline echo patterns:
|
|
811
|
+
// - Single printable characters (user typing)
|
|
812
|
+
// - Cursor movement sequences for single chars
|
|
813
|
+
// - Backspace sequences
|
|
814
|
+
// - Prompt redraws
|
|
815
|
+
// But allow through:
|
|
816
|
+
// - Multi-character content (actual AI output)
|
|
817
|
+
// - Newlines and control sequences for formatting
|
|
818
|
+
// If it's a single printable char, suppress it (user typing)
|
|
819
|
+
if (str.length === 1 && str.charCodeAt(0) >= 32 && str.charCodeAt(0) < 127) {
|
|
820
|
+
return true;
|
|
821
|
+
}
|
|
822
|
+
// Suppress backspace sequences (readline's delete char)
|
|
823
|
+
if (str === '\b \b' || str === '\x1b[D \x1b[D' || str === '\b' || str === '\x7f') {
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
// Suppress cursor movement sequences (readline cursor positioning)
|
|
827
|
+
if (/^\x1b\[\d*[ABCD]$/.test(str)) {
|
|
828
|
+
return true;
|
|
829
|
+
}
|
|
830
|
+
// Suppress readline prompt redraw patterns (starts with \r or cursor home)
|
|
831
|
+
if (/^\r/.test(str) && str.length < 20 && /^[\r\x1b\[\dGK> ]+$/.test(str)) {
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
// Suppress clear line + prompt patterns from readline
|
|
835
|
+
if (/^\x1b\[\d*[GK]/.test(str) && str.length < 15) {
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
// Suppress short sequences that look like readline control (not AI content)
|
|
839
|
+
// AI content is typically longer or contains actual text
|
|
840
|
+
if (str.length <= 3 && /^[\x1b\[\]\d;GKJ]+$/.test(str)) {
|
|
841
|
+
return true;
|
|
842
|
+
}
|
|
843
|
+
// Allow everything else through (actual AI output)
|
|
844
|
+
if (typeof encodingOrCallback === 'function') {
|
|
845
|
+
return self.originalStdoutWrite(chunk, encodingOrCallback);
|
|
846
|
+
}
|
|
847
|
+
return self.originalStdoutWrite(chunk, encodingOrCallback, callback);
|
|
1067
848
|
};
|
|
1068
|
-
// Redirect readline's output to null - this completely stops echo
|
|
1069
|
-
this.rl.output = nullStream;
|
|
1070
849
|
this.readlineOutputSuppressed = true;
|
|
1071
850
|
}
|
|
1072
|
-
// Store original readline output for restoration
|
|
1073
|
-
originalReadlineOutput = null;
|
|
1074
851
|
/**
|
|
1075
852
|
* Restore normal readline output after streaming completes.
|
|
1076
853
|
*/
|
|
1077
854
|
restoreReadlineOutput() {
|
|
1078
|
-
if (!this.readlineOutputSuppressed) {
|
|
855
|
+
if (!this.readlineOutputSuppressed || !this.originalStdoutWrite) {
|
|
1079
856
|
return;
|
|
1080
857
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
this.rl.output = this.originalReadlineOutput;
|
|
1084
|
-
this.originalReadlineOutput = null;
|
|
1085
|
-
}
|
|
858
|
+
output.write = this.originalStdoutWrite;
|
|
859
|
+
this.originalStdoutWrite = null;
|
|
1086
860
|
this.readlineOutputSuppressed = false;
|
|
1087
861
|
}
|
|
1088
862
|
enqueueUserInput(line, flushImmediately = false) {
|
|
@@ -1135,20 +909,12 @@ export class InteractiveShell {
|
|
|
1135
909
|
return;
|
|
1136
910
|
}
|
|
1137
911
|
this.lastPastePreviewLineCount = lineCount;
|
|
912
|
+
// Clear current line and write preview (the line handler already moved cursor up)
|
|
913
|
+
output.write('\r\x1b[K');
|
|
1138
914
|
const statusText = preview
|
|
1139
|
-
?
|
|
1140
|
-
: `📋 Pasting ${lineCount} line${lineCount !== 1 ? 's' : ''}
|
|
1141
|
-
|
|
1142
|
-
// This prevents it from appearing in the streaming output area
|
|
1143
|
-
if (this.isProcessing) {
|
|
1144
|
-
this.pinnedChatBox.setInput(statusText, statusText.length);
|
|
1145
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
1146
|
-
}
|
|
1147
|
-
else {
|
|
1148
|
-
// Not processing - write to output normally
|
|
1149
|
-
output.write('\r\x1b[K');
|
|
1150
|
-
output.write(theme.ui.muted(statusText));
|
|
1151
|
-
}
|
|
915
|
+
? `${theme.ui.muted('📋 Pasting:')} ${theme.ui.muted(preview.slice(0, 50))}${preview.length > 50 ? '...' : ''}`
|
|
916
|
+
: `${theme.ui.muted(`📋 Pasting ${lineCount} line${lineCount !== 1 ? 's' : ''}...`)}`;
|
|
917
|
+
output.write(statusText);
|
|
1152
918
|
}
|
|
1153
919
|
/**
|
|
1154
920
|
* Clear the multi-line paste preview
|
|
@@ -1157,18 +923,11 @@ export class InteractiveShell {
|
|
|
1157
923
|
if (!this.inPasteCapture) {
|
|
1158
924
|
return;
|
|
1159
925
|
}
|
|
926
|
+
// Clear current line
|
|
927
|
+
output.write('\r\x1b[K');
|
|
1160
928
|
// Reset tracking state
|
|
1161
929
|
this.lastPastePreviewLineCount = 0;
|
|
1162
930
|
this.inPasteCapture = false;
|
|
1163
|
-
// During processing, clear the persistent chat box preview
|
|
1164
|
-
if (this.isProcessing) {
|
|
1165
|
-
this.pinnedChatBox.setInput('', 0);
|
|
1166
|
-
this.pinnedChatBox.updatePersistentInput();
|
|
1167
|
-
}
|
|
1168
|
-
else {
|
|
1169
|
-
// Not processing - clear output normally
|
|
1170
|
-
output.write('\r\x1b[K');
|
|
1171
|
-
}
|
|
1172
931
|
}
|
|
1173
932
|
/**
|
|
1174
933
|
* Capture any paste (single or multi-line) without immediately submitting it.
|
|
@@ -1187,15 +946,7 @@ export class InteractiveShell {
|
|
|
1187
946
|
const displayContent = lineCount === 1
|
|
1188
947
|
? content
|
|
1189
948
|
: content.replace(/\n/g, ' ↵ '); // Visual newline indicator for 2-line pastes
|
|
1190
|
-
//
|
|
1191
|
-
// This prevents paste from leaking into the streaming area
|
|
1192
|
-
if (this.isProcessing) {
|
|
1193
|
-
// handleInput triggers handleMultilinePaste which handles rendering
|
|
1194
|
-
// NO redundant updatePersistentInput call - it causes double renders
|
|
1195
|
-
this.pinnedChatBox.handleInput(content);
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
// Clear any echoed content first (only when not processing)
|
|
949
|
+
// Clear any echoed content first
|
|
1199
950
|
output.write('\r\x1b[K');
|
|
1200
951
|
// Get current readline content and append paste
|
|
1201
952
|
const currentLine = this.rl.line || '';
|
|
@@ -1205,55 +956,42 @@ export class InteractiveShell {
|
|
|
1205
956
|
const after = currentLine.slice(cursorPos);
|
|
1206
957
|
const newLine = before + displayContent + after;
|
|
1207
958
|
const newCursor = cursorPos + displayContent.length;
|
|
1208
|
-
// Update readline buffer
|
|
1209
|
-
|
|
1210
|
-
this.rl.
|
|
959
|
+
// Update readline buffer - write directly without storing in composableMessage
|
|
960
|
+
// This allows short pastes to flow through as normal typed text
|
|
961
|
+
this.rl.write(null, { ctrl: true, name: 'u' }); // Clear line
|
|
962
|
+
this.rl.write(newLine); // Write new content
|
|
1211
963
|
// Update persistent prompt display
|
|
1212
964
|
this.persistentPrompt.updateInput(newLine, newCursor);
|
|
1213
|
-
//
|
|
1214
|
-
//
|
|
1215
|
-
//
|
|
1216
|
-
|
|
1217
|
-
// Not processing - render pinned chat box and sync readline
|
|
1218
|
-
this.pinnedChatBox.forceRender();
|
|
965
|
+
// NOTE: Don't clear pasteJustCaptured here - the counter-based logic in shouldIgnoreLineEvent()
|
|
966
|
+
// will decrement for each readline line event and auto-clear when all are processed.
|
|
967
|
+
// Clearing prematurely causes the remaining readline-echoed lines to pass through.
|
|
968
|
+
// Re-prompt to show the inline content
|
|
1219
969
|
this.rl.prompt(true);
|
|
1220
970
|
return;
|
|
1221
971
|
}
|
|
1222
972
|
// For longer pastes (3+ lines), store as a composable block
|
|
1223
973
|
this.composableMessage.addPaste(content);
|
|
1224
|
-
//
|
|
1225
|
-
// This prevents paste from leaking into the streaming area
|
|
1226
|
-
if (this.isProcessing) {
|
|
1227
|
-
// handleInput triggers handleMultilinePaste which handles rendering
|
|
1228
|
-
// NO redundant updatePersistentInput call - it causes double renders
|
|
1229
|
-
this.pinnedChatBox.handleInput(content);
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
// Clear remaining echoed lines from terminal (only when not processing)
|
|
974
|
+
// Clear remaining echoed lines from terminal
|
|
1233
975
|
output.write('\r\x1b[K');
|
|
1234
976
|
// Build the paste chips to show inline with prompt
|
|
1235
977
|
// Format: [Pasted text #1 +104 lines] [Pasted text #2 +50 lines]
|
|
1236
978
|
const pasteChips = this.composableMessage.formatPasteChips();
|
|
1237
|
-
const displayText = `${pasteChips} `;
|
|
1238
|
-
const cursorPos = displayText.length;
|
|
1239
979
|
// Update status bar - minimal hint, no confirmation required
|
|
1240
980
|
this.persistentPrompt.updateStatusBar({
|
|
1241
981
|
message: 'Press Enter to send',
|
|
1242
982
|
});
|
|
1243
|
-
//
|
|
1244
|
-
|
|
983
|
+
// Set the prompt to show paste chips, then position cursor after them
|
|
984
|
+
// The user can type additional text after the chips
|
|
985
|
+
this.persistentPrompt.updateInput(pasteChips + ' ', pasteChips.length + 1);
|
|
1245
986
|
// Update readline's line buffer to include the chips as prefix
|
|
1246
987
|
// This ensures typed text appears after the chips
|
|
1247
988
|
if (this.rl.line !== undefined) {
|
|
1248
|
-
this.rl.line =
|
|
1249
|
-
this.rl.cursor =
|
|
1250
|
-
}
|
|
1251
|
-
//
|
|
1252
|
-
//
|
|
1253
|
-
//
|
|
1254
|
-
this.pinnedChatBox.handleInput(content);
|
|
1255
|
-
// Not processing - render pinned chat box and sync readline
|
|
1256
|
-
this.pinnedChatBox.forceRender();
|
|
989
|
+
this.rl.line = pasteChips + ' ';
|
|
990
|
+
this.rl.cursor = pasteChips.length + 1;
|
|
991
|
+
}
|
|
992
|
+
// NOTE: Don't clear pasteJustCaptured here - the counter-based logic in shouldIgnoreLineEvent()
|
|
993
|
+
// will decrement for each readline line event (one per pasted line) and auto-clear when done.
|
|
994
|
+
// Clearing prematurely causes remaining readline-echoed lines to pass through and get displayed.
|
|
1257
995
|
this.rl.prompt(true); // preserveCursor=true to keep position after chips
|
|
1258
996
|
}
|
|
1259
997
|
/**
|
|
@@ -1279,13 +1017,6 @@ export class InteractiveShell {
|
|
|
1279
1017
|
const combined = this.bufferedInputLines.join('\n');
|
|
1280
1018
|
this.bufferedInputLines = [];
|
|
1281
1019
|
this.bufferedInputTimer = null;
|
|
1282
|
-
// CRITICAL: Long multi-line content (3+ lines) should NEVER be auto-submitted
|
|
1283
|
-
// This catches rapid input that bypasses bracketed paste detection
|
|
1284
|
-
// Instead, capture it like a paste and require explicit Enter to submit
|
|
1285
|
-
if (lineCount >= 3) {
|
|
1286
|
-
this.capturePaste(combined, lineCount);
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
1020
|
try {
|
|
1290
1021
|
await this.processInputBlock(combined, lineCount > 1);
|
|
1291
1022
|
}
|
|
@@ -1379,14 +1110,6 @@ export class InteractiveShell {
|
|
|
1379
1110
|
this.slashPreviewVisible = false;
|
|
1380
1111
|
this.uiAdapter.hideSlashCommandPreview();
|
|
1381
1112
|
const trimmed = line.trim();
|
|
1382
|
-
// CRITICAL SAFETY: Long multi-line content (3+ lines) should NEVER be auto-submitted
|
|
1383
|
-
// Capture it for explicit user confirmation, regardless of how it arrived
|
|
1384
|
-
const lineCount = line.split('\n').length;
|
|
1385
|
-
if (lineCount >= 3 && !this.composableMessage.hasContent()) {
|
|
1386
|
-
// Don't re-capture if we already have composed content (user is submitting a paste)
|
|
1387
|
-
this.capturePaste(line, lineCount);
|
|
1388
|
-
return;
|
|
1389
|
-
}
|
|
1390
1113
|
if (await this.handlePendingInteraction(trimmed)) {
|
|
1391
1114
|
return;
|
|
1392
1115
|
}
|
|
@@ -1443,14 +1166,6 @@ export class InteractiveShell {
|
|
|
1443
1166
|
this.rl.prompt();
|
|
1444
1167
|
return;
|
|
1445
1168
|
}
|
|
1446
|
-
// CRITICAL: If we're processing, queue the assembled content as a follow-up
|
|
1447
|
-
// This ensures multi-line pastes submitted during streaming don't interrupt
|
|
1448
|
-
if (this.isProcessing) {
|
|
1449
|
-
this.enqueueFollowUpAction({ type: 'request', text: assembled });
|
|
1450
|
-
// Clear input display after queueing
|
|
1451
|
-
this.pinnedChatBox.clearInput();
|
|
1452
|
-
return;
|
|
1453
|
-
}
|
|
1454
1169
|
// Check if assembled content is a continuous command
|
|
1455
1170
|
if (this.isContinuousCommand(assembled)) {
|
|
1456
1171
|
await this.processContinuousRequest(assembled);
|
|
@@ -1654,7 +1369,6 @@ export class InteractiveShell {
|
|
|
1654
1369
|
this.buildSlashCommandList('Available Commands:'),
|
|
1655
1370
|
'',
|
|
1656
1371
|
'Type your request in natural language and press Enter.',
|
|
1657
|
-
'Press ? or type /shortcuts for keyboard shortcuts.',
|
|
1658
1372
|
];
|
|
1659
1373
|
display.showSystemMessage(info.join('\n'));
|
|
1660
1374
|
}
|
|
@@ -1937,7 +1651,7 @@ export class InteractiveShell {
|
|
|
1937
1651
|
lines.push(theme.bold('Session File Changes'));
|
|
1938
1652
|
lines.push('');
|
|
1939
1653
|
lines.push(`${theme.info('•')} ${summary.files} file${summary.files === 1 ? '' : 's'} modified`);
|
|
1940
|
-
lines.push(`${theme.info('•')} ${theme.success(
|
|
1654
|
+
lines.push(`${theme.info('•')} ${theme.success('+' + summary.additions)} ${theme.error('-' + summary.removals)} lines`);
|
|
1941
1655
|
lines.push('');
|
|
1942
1656
|
// Group changes by file
|
|
1943
1657
|
const fileMap = new Map();
|
|
@@ -1961,7 +1675,7 @@ export class InteractiveShell {
|
|
|
1961
1675
|
if (stats.writes > 0)
|
|
1962
1676
|
operations.push(`${stats.writes} write${stats.writes === 1 ? '' : 's'}`);
|
|
1963
1677
|
const opsText = operations.join(', ');
|
|
1964
|
-
const diffText = `${theme.success(
|
|
1678
|
+
const diffText = `${theme.success('+' + stats.additions)} ${theme.error('-' + stats.removals)}`;
|
|
1965
1679
|
lines.push(` ${theme.dim(path)}`);
|
|
1966
1680
|
lines.push(` ${opsText} • ${diffText}`);
|
|
1967
1681
|
}
|
|
@@ -2687,14 +2401,6 @@ export class InteractiveShell {
|
|
|
2687
2401
|
this.rl.prompt();
|
|
2688
2402
|
}
|
|
2689
2403
|
async processRequest(request) {
|
|
2690
|
-
// CRITICAL SAFETY: Long multi-line content should never be auto-queued
|
|
2691
|
-
// This is a last-line defense - content should have been captured earlier
|
|
2692
|
-
const lineCount = request.split('\n').length;
|
|
2693
|
-
if (lineCount >= 3 && !this.composableMessage.hasContent()) {
|
|
2694
|
-
// Capture instead of processing/queueing - user must explicitly confirm
|
|
2695
|
-
this.capturePaste(request, lineCount);
|
|
2696
|
-
return;
|
|
2697
|
-
}
|
|
2698
2404
|
if (this.isProcessing) {
|
|
2699
2405
|
this.enqueueFollowUpAction({ type: 'request', text: request });
|
|
2700
2406
|
return;
|
|
@@ -2709,17 +2415,10 @@ export class InteractiveShell {
|
|
|
2709
2415
|
}
|
|
2710
2416
|
this.isProcessing = true;
|
|
2711
2417
|
const requestStartTime = Date.now(); // Alpha Zero 2 timing
|
|
2712
|
-
// Detect reasoning models (o1, deepseek-reasoner) that don't stream text
|
|
2713
|
-
const model = this.sessionState.model?.toLowerCase() || '';
|
|
2714
|
-
const isReasoningModel = model.includes('reasoner') || model.startsWith('o1') || model.includes('-reasoning');
|
|
2715
2418
|
// Keep persistent prompt visible during processing so users can type follow-up requests
|
|
2716
2419
|
// The prompt will show a "processing" indicator but remain interactive
|
|
2717
|
-
|
|
2718
|
-
? '🧠 Reasoning... (type to queue follow-up)'
|
|
2719
|
-
: '⏳ Processing... (type to queue follow-up)';
|
|
2720
|
-
this.persistentPrompt.updateStatusBar({ message: statusMessage });
|
|
2420
|
+
this.persistentPrompt.updateStatusBar({ message: '⏳ Processing... (type to queue follow-up)' });
|
|
2721
2421
|
// Update pinned chat box to show processing state
|
|
2722
|
-
this.pinnedChatBox.setReasoningModel(isReasoningModel);
|
|
2723
2422
|
// Clear the input display since the request was already submitted
|
|
2724
2423
|
// Note: Don't set statusMessage here - the isProcessing flag already shows "⏳ Processing..."
|
|
2725
2424
|
this.pinnedChatBox.setProcessing(true);
|
|
@@ -2727,7 +2426,6 @@ export class InteractiveShell {
|
|
|
2727
2426
|
this.pinnedChatBox.clearInput();
|
|
2728
2427
|
this.uiAdapter.startProcessing('Working on your request');
|
|
2729
2428
|
this.setProcessingStatus();
|
|
2730
|
-
let renderInterval = null;
|
|
2731
2429
|
try {
|
|
2732
2430
|
// Add visual separator between user prompt and AI response
|
|
2733
2431
|
display.newLine();
|
|
@@ -2736,12 +2434,6 @@ export class InteractiveShell {
|
|
|
2736
2434
|
this.pinnedChatBox.enableScrollRegion();
|
|
2737
2435
|
// Suppress readline echo so typed characters only appear in persistent input box
|
|
2738
2436
|
this.suppressReadlineOutput();
|
|
2739
|
-
// Set up periodic render timer to ensure pinned chat box stays visible during streaming
|
|
2740
|
-
renderInterval = setInterval(() => {
|
|
2741
|
-
if (this.isProcessing && this.pinnedChatBox.isActive()) {
|
|
2742
|
-
this.pinnedChatBox.forceRender();
|
|
2743
|
-
}
|
|
2744
|
-
}, 2000); // Render every 2 seconds during streaming
|
|
2745
2437
|
// Enable streaming for real-time text output (Claude Code style)
|
|
2746
2438
|
await agent.send(request, true);
|
|
2747
2439
|
await this.awaitPendingCleanup();
|
|
@@ -2781,11 +2473,6 @@ export class InteractiveShell {
|
|
|
2781
2473
|
this.rl.prompt();
|
|
2782
2474
|
this.scheduleQueueProcessing();
|
|
2783
2475
|
this.refreshQueueIndicators();
|
|
2784
|
-
// Ensure any periodic render timer is cleaned up
|
|
2785
|
-
if (renderInterval) {
|
|
2786
|
-
clearInterval(renderInterval);
|
|
2787
|
-
renderInterval = null;
|
|
2788
|
-
}
|
|
2789
2476
|
}
|
|
2790
2477
|
}
|
|
2791
2478
|
/**
|
|
@@ -2834,8 +2521,6 @@ export class InteractiveShell {
|
|
|
2834
2521
|
this.pinnedChatBox.enableScrollRegion();
|
|
2835
2522
|
// Suppress readline echo so typed characters only appear in persistent input box
|
|
2836
2523
|
this.suppressReadlineOutput();
|
|
2837
|
-
// Periodically refresh the pinned chat box while streaming
|
|
2838
|
-
let renderInterval = null;
|
|
2839
2524
|
let iteration = 0;
|
|
2840
2525
|
let lastResponse = '';
|
|
2841
2526
|
let consecutiveNoProgress = 0;
|
|
@@ -2858,11 +2543,6 @@ IMPORTANT: You have full git access. After making improvements:
|
|
|
2858
2543
|
Commit frequently with descriptive messages. Push when ready.
|
|
2859
2544
|
When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
|
|
2860
2545
|
}
|
|
2861
|
-
renderInterval = setInterval(() => {
|
|
2862
|
-
if (this.isProcessing && this.pinnedChatBox.isActive()) {
|
|
2863
|
-
this.pinnedChatBox.forceRender();
|
|
2864
|
-
}
|
|
2865
|
-
}, 2000);
|
|
2866
2546
|
while (iteration < MAX_ITERATIONS) {
|
|
2867
2547
|
iteration++;
|
|
2868
2548
|
display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
|
|
@@ -3000,10 +2680,6 @@ What's the next action?`;
|
|
|
3000
2680
|
const minutes = Math.floor(totalElapsed / 60000);
|
|
3001
2681
|
const seconds = Math.floor((totalElapsed % 60000) / 1000);
|
|
3002
2682
|
display.showSystemMessage(`\n🏁 Continuous execution completed: ${iteration} iterations, ${minutes}m ${seconds}s total`);
|
|
3003
|
-
if (renderInterval) {
|
|
3004
|
-
clearInterval(renderInterval);
|
|
3005
|
-
renderInterval = null;
|
|
3006
|
-
}
|
|
3007
2683
|
// Reset completion detector for next task
|
|
3008
2684
|
resetTaskCompletionDetector();
|
|
3009
2685
|
// Restore readline echo before other cleanup
|
|
@@ -3248,9 +2924,7 @@ What's the next action?`;
|
|
|
3248
2924
|
display.showNarrative(content.trim());
|
|
3249
2925
|
}
|
|
3250
2926
|
// The isProcessing flag already shows "⏳ Processing..." - no need for duplicate status
|
|
3251
|
-
|
|
3252
|
-
// re-rendering the persistent input. Calling forceRender() immediately can cause
|
|
3253
|
-
// cursor position issues when RESTORE_CURSOR doesn't work correctly in some terminals.
|
|
2927
|
+
this.pinnedChatBox.forceRender();
|
|
3254
2928
|
return;
|
|
3255
2929
|
}
|
|
3256
2930
|
const cleanup = this.handleContextTelemetry(metadata, enriched);
|
|
@@ -3719,7 +3393,7 @@ What's the next action?`;
|
|
|
3719
3393
|
// Truncate to reasonable length
|
|
3720
3394
|
const maxLength = 50;
|
|
3721
3395
|
return cleaned.length > maxLength
|
|
3722
|
-
?
|
|
3396
|
+
? cleaned.slice(0, maxLength - 3) + '...'
|
|
3723
3397
|
: cleaned;
|
|
3724
3398
|
}
|
|
3725
3399
|
splitThinkingResponse(content) {
|