ccmanager 2.11.4 → 2.11.6
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.
|
@@ -12,6 +12,26 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
|
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
if (!stdout)
|
|
14
14
|
return;
|
|
15
|
+
const resetTerminalInputModes = () => {
|
|
16
|
+
// Reset terminal modes that interactive tools like Codex enable (kitty keyboard
|
|
17
|
+
// protocol / modifyOtherKeys / focus tracking) so they don't leak into other
|
|
18
|
+
// sessions after we detach.
|
|
19
|
+
stdout.write('\x1b[>0u'); // Disable kitty keyboard protocol (CSI u sequences)
|
|
20
|
+
stdout.write('\x1b[>4m'); // Disable xterm modifyOtherKeys extensions
|
|
21
|
+
stdout.write('\x1b[?1004l'); // Disable focus reporting
|
|
22
|
+
stdout.write('\x1b[?2004l'); // Disable bracketed paste (can interfere with shortcuts)
|
|
23
|
+
};
|
|
24
|
+
const sanitizeReplayBuffer = (input) => {
|
|
25
|
+
// Remove terminal mode toggles emitted by Codex so replay doesn't re-enable them
|
|
26
|
+
// on our own TTY when restoring the session view.
|
|
27
|
+
return stripOscColorSequences(input)
|
|
28
|
+
.replace(/\x1B\[>4;?\d*m/g, '') // modifyOtherKeys set/reset
|
|
29
|
+
.replace(/\x1B\[>[0-9;]*u/g, '') // kitty keyboard protocol enables
|
|
30
|
+
.replace(/\x1B\[\?1004[hl]/g, '') // focus tracking
|
|
31
|
+
.replace(/\x1B\[\?2004[hl]/g, ''); // bracketed paste
|
|
32
|
+
};
|
|
33
|
+
// Reset modes immediately on entry in case a previous session left them on
|
|
34
|
+
resetTerminalInputModes();
|
|
15
35
|
// Clear screen when entering session
|
|
16
36
|
stdout.write('\x1B[2J\x1B[H');
|
|
17
37
|
// Handle session restoration
|
|
@@ -22,7 +42,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
|
|
|
22
42
|
const buffer = restoredSession.outputHistory[i];
|
|
23
43
|
if (!buffer)
|
|
24
44
|
continue;
|
|
25
|
-
const str =
|
|
45
|
+
const str = sanitizeReplayBuffer(buffer.toString('utf8'));
|
|
26
46
|
// Skip clear screen sequences at the beginning
|
|
27
47
|
if (i === 0 && (str.includes('\x1B[2J') || str.includes('\x1B[H'))) {
|
|
28
48
|
// Skip this buffer or remove the clear sequence
|
|
@@ -101,9 +121,9 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
|
|
|
101
121
|
return;
|
|
102
122
|
// Check for return to menu shortcut
|
|
103
123
|
if (shortcutManager.matchesRawInput('returnToMenu', data)) {
|
|
104
|
-
// Disable
|
|
124
|
+
// Disable any extended input modes that might have been enabled by the PTY
|
|
105
125
|
if (stdout) {
|
|
106
|
-
|
|
126
|
+
resetTerminalInputModes();
|
|
107
127
|
}
|
|
108
128
|
// Restore stdin state before returning to menu
|
|
109
129
|
stdin.removeListener('data', handleStdinData);
|
|
@@ -121,7 +141,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
|
|
|
121
141
|
stdin.removeListener('data', handleStdinData);
|
|
122
142
|
// Disable focus reporting mode that might have been enabled by the PTY
|
|
123
143
|
if (stdout) {
|
|
124
|
-
|
|
144
|
+
resetTerminalInputModes();
|
|
125
145
|
}
|
|
126
146
|
// Restore stdin to its original state
|
|
127
147
|
if (stdin.isTTY) {
|
|
@@ -34,6 +34,26 @@ describe('CodexStateDetector', () => {
|
|
|
34
34
|
// Assert
|
|
35
35
|
expect(state).toBe('waiting_input');
|
|
36
36
|
});
|
|
37
|
+
it('should detect waiting_input state for multiline do you want prompt with yes', () => {
|
|
38
|
+
// Arrange
|
|
39
|
+
terminal = createMockTerminal([
|
|
40
|
+
'Would you like to run the following command?',
|
|
41
|
+
'',
|
|
42
|
+
'Reason: Need to write to .git/worktrees metadata to stage changes for the requested commi',
|
|
43
|
+
'',
|
|
44
|
+
'$ git add test.ts',
|
|
45
|
+
'',
|
|
46
|
+
'› 1. Yes, proceed (y)',
|
|
47
|
+
" 2. Yes, and don't ask again for this command (a)",
|
|
48
|
+
' 3. No, and tell Codex what to do differently (esc)',
|
|
49
|
+
'',
|
|
50
|
+
'Press enter to confirm or esc to cancel',
|
|
51
|
+
]);
|
|
52
|
+
// Act
|
|
53
|
+
const state = detector.detectState(terminal, 'idle');
|
|
54
|
+
// Assert
|
|
55
|
+
expect(state).toBe('waiting_input');
|
|
56
|
+
});
|
|
37
57
|
it('should detect busy state for Esc to interrupt pattern', () => {
|
|
38
58
|
// Arrange
|
|
39
59
|
terminal = createMockTerminal([
|
|
@@ -44,6 +44,18 @@ describe('GeminiStateDetector', () => {
|
|
|
44
44
|
// Assert
|
|
45
45
|
expect(state).toBe('waiting_input');
|
|
46
46
|
});
|
|
47
|
+
it('should detect waiting_input for multiline confirmation ending with "yes"', () => {
|
|
48
|
+
// Arrange
|
|
49
|
+
terminal = createMockTerminal([
|
|
50
|
+
'Apply this change to the workspace?',
|
|
51
|
+
'The operation will modify several files.',
|
|
52
|
+
' yes',
|
|
53
|
+
]);
|
|
54
|
+
// Act
|
|
55
|
+
const state = detector.detectState(terminal, 'idle');
|
|
56
|
+
// Assert
|
|
57
|
+
expect(state).toBe('waiting_input');
|
|
58
|
+
});
|
|
47
59
|
it('should detect busy when "esc to cancel" is present', () => {
|
|
48
60
|
// Arrange
|
|
49
61
|
terminal = createMockTerminal([
|
|
@@ -83,6 +83,10 @@ export class GeminiStateDetector extends BaseStateDetector {
|
|
|
83
83
|
content.includes('│ Do you want to proceed?')) {
|
|
84
84
|
return 'waiting_input';
|
|
85
85
|
}
|
|
86
|
+
// Check for multiline confirmation prompts ending with "yes"
|
|
87
|
+
if (/(allow execution|do you want to|apply this change)[\s\S]*?\n+[\s\S]*?\byes\b/.test(lowerContent)) {
|
|
88
|
+
return 'waiting_input';
|
|
89
|
+
}
|
|
86
90
|
// Check for busy state
|
|
87
91
|
if (lowerContent.includes('esc to cancel')) {
|
|
88
92
|
return 'busy';
|
|
@@ -101,6 +105,9 @@ export class CodexStateDetector extends BaseStateDetector {
|
|
|
101
105
|
lowerContent.includes('yes (y)')) {
|
|
102
106
|
return 'waiting_input';
|
|
103
107
|
}
|
|
108
|
+
if (/(do you want|would you like)[\s\S]*?\n+[\s\S]*?\byes\b/.test(lowerContent)) {
|
|
109
|
+
return 'waiting_input';
|
|
110
|
+
}
|
|
104
111
|
// Check for busy state
|
|
105
112
|
if (/esc.*interrupt/i.test(lowerContent)) {
|
|
106
113
|
return 'busy';
|