ccmanager 2.11.2 → 2.11.4
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/dist/services/__tests__/stateDetector.claude.test.js +312 -0
- package/dist/services/__tests__/stateDetector.cline.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.cline.test.js +115 -0
- package/dist/services/__tests__/stateDetector.codex.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.codex.test.js +77 -0
- package/dist/services/__tests__/stateDetector.cursor.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.cursor.test.js +130 -0
- package/dist/services/__tests__/stateDetector.gemini.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.gemini.test.js +101 -0
- package/dist/services/__tests__/stateDetector.github-copilot.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.github-copilot.test.js +55 -0
- package/dist/services/__tests__/testUtils.d.ts +7 -0
- package/dist/services/__tests__/testUtils.js +22 -0
- package/dist/services/stateDetector.js +9 -0
- package/package.json +2 -2
- package/dist/services/stateDetector.test.js +0 -705
- /package/dist/services/{stateDetector.test.d.ts → __tests__/stateDetector.claude.test.d.ts} +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { GeminiStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('GeminiStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new GeminiStateDetector();
|
|
9
|
+
});
|
|
10
|
+
describe('detectState', () => {
|
|
11
|
+
it('should detect waiting_input when "Apply this change?" prompt is present', () => {
|
|
12
|
+
// Arrange
|
|
13
|
+
terminal = createMockTerminal([
|
|
14
|
+
'Some output from Gemini',
|
|
15
|
+
'│ Apply this change?',
|
|
16
|
+
'│ > ',
|
|
17
|
+
]);
|
|
18
|
+
// Act
|
|
19
|
+
const state = detector.detectState(terminal, 'idle');
|
|
20
|
+
// Assert
|
|
21
|
+
expect(state).toBe('waiting_input');
|
|
22
|
+
});
|
|
23
|
+
it('should detect waiting_input when "Allow execution?" prompt is present', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
terminal = createMockTerminal([
|
|
26
|
+
'Command found: npm install',
|
|
27
|
+
'│ Allow execution?',
|
|
28
|
+
'│ > ',
|
|
29
|
+
]);
|
|
30
|
+
// Act
|
|
31
|
+
const state = detector.detectState(terminal, 'idle');
|
|
32
|
+
// Assert
|
|
33
|
+
expect(state).toBe('waiting_input');
|
|
34
|
+
});
|
|
35
|
+
it('should detect waiting_input when "Do you want to proceed?" prompt is present', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
terminal = createMockTerminal([
|
|
38
|
+
'Changes detected',
|
|
39
|
+
'│ Do you want to proceed?',
|
|
40
|
+
'│ > ',
|
|
41
|
+
]);
|
|
42
|
+
// Act
|
|
43
|
+
const state = detector.detectState(terminal, 'idle');
|
|
44
|
+
// Assert
|
|
45
|
+
expect(state).toBe('waiting_input');
|
|
46
|
+
});
|
|
47
|
+
it('should detect busy when "esc to cancel" is present', () => {
|
|
48
|
+
// Arrange
|
|
49
|
+
terminal = createMockTerminal([
|
|
50
|
+
'Processing your request...',
|
|
51
|
+
'Press ESC to cancel',
|
|
52
|
+
]);
|
|
53
|
+
// Act
|
|
54
|
+
const state = detector.detectState(terminal, 'idle');
|
|
55
|
+
// Assert
|
|
56
|
+
expect(state).toBe('busy');
|
|
57
|
+
});
|
|
58
|
+
it('should detect busy when "ESC to cancel" is present (case insensitive)', () => {
|
|
59
|
+
// Arrange
|
|
60
|
+
terminal = createMockTerminal([
|
|
61
|
+
'Running command...',
|
|
62
|
+
'Press Esc to cancel the operation',
|
|
63
|
+
]);
|
|
64
|
+
// Act
|
|
65
|
+
const state = detector.detectState(terminal, 'idle');
|
|
66
|
+
// Assert
|
|
67
|
+
expect(state).toBe('busy');
|
|
68
|
+
});
|
|
69
|
+
it('should detect idle when no specific patterns are found', () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
terminal = createMockTerminal([
|
|
72
|
+
'Welcome to Gemini CLI',
|
|
73
|
+
'Type your message below',
|
|
74
|
+
]);
|
|
75
|
+
// Act
|
|
76
|
+
const state = detector.detectState(terminal, 'idle');
|
|
77
|
+
// Assert
|
|
78
|
+
expect(state).toBe('idle');
|
|
79
|
+
});
|
|
80
|
+
it('should handle empty terminal', () => {
|
|
81
|
+
// Arrange
|
|
82
|
+
terminal = createMockTerminal([]);
|
|
83
|
+
// Act
|
|
84
|
+
const state = detector.detectState(terminal, 'idle');
|
|
85
|
+
// Assert
|
|
86
|
+
expect(state).toBe('idle');
|
|
87
|
+
});
|
|
88
|
+
it('should prioritize waiting_input over busy state', () => {
|
|
89
|
+
// Arrange
|
|
90
|
+
terminal = createMockTerminal([
|
|
91
|
+
'Press ESC to cancel',
|
|
92
|
+
'│ Apply this change?',
|
|
93
|
+
'│ > ',
|
|
94
|
+
]);
|
|
95
|
+
// Act
|
|
96
|
+
const state = detector.detectState(terminal, 'idle');
|
|
97
|
+
// Assert
|
|
98
|
+
expect(state).toBe('waiting_input'); // waiting_input should take precedence
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { GitHubCopilotStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('GitHubCopilotStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new GitHubCopilotStateDetector();
|
|
9
|
+
});
|
|
10
|
+
it('detects waiting_input when prompt asks "Do you want" (case insensitive)', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
terminal = createMockTerminal([
|
|
13
|
+
'Running GitHub Copilot CLI...',
|
|
14
|
+
'│ DO YOU WANT to run this command?',
|
|
15
|
+
'│ > ',
|
|
16
|
+
]);
|
|
17
|
+
// Act
|
|
18
|
+
const state = detector.detectState(terminal, 'idle');
|
|
19
|
+
// Assert
|
|
20
|
+
expect(state).toBe('waiting_input');
|
|
21
|
+
});
|
|
22
|
+
it('detects busy when "Esc to cancel" is present', () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
terminal = createMockTerminal([
|
|
25
|
+
'Executing request...',
|
|
26
|
+
'Press Esc to cancel',
|
|
27
|
+
]);
|
|
28
|
+
// Act
|
|
29
|
+
const state = detector.detectState(terminal, 'idle');
|
|
30
|
+
// Assert
|
|
31
|
+
expect(state).toBe('busy');
|
|
32
|
+
});
|
|
33
|
+
it('prioritizes waiting_input over busy when both patterns exist', () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
terminal = createMockTerminal([
|
|
36
|
+
'Press Esc to cancel',
|
|
37
|
+
'│ Do you want to continue?',
|
|
38
|
+
]);
|
|
39
|
+
// Act
|
|
40
|
+
const state = detector.detectState(terminal, 'idle');
|
|
41
|
+
// Assert
|
|
42
|
+
expect(state).toBe('waiting_input');
|
|
43
|
+
});
|
|
44
|
+
it('returns idle when no patterns match', () => {
|
|
45
|
+
// Arrange
|
|
46
|
+
terminal = createMockTerminal([
|
|
47
|
+
'GitHub Copilot CLI ready.',
|
|
48
|
+
'Type a command to begin.',
|
|
49
|
+
]);
|
|
50
|
+
// Act
|
|
51
|
+
const state = detector.detectState(terminal, 'idle');
|
|
52
|
+
// Assert
|
|
53
|
+
expect(state).toBe('idle');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Terminal } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a mock Terminal object for testing state detectors.
|
|
4
|
+
* @param lines - Array of strings representing terminal output lines
|
|
5
|
+
* @returns Mock Terminal object with buffer interface
|
|
6
|
+
*/
|
|
7
|
+
export declare const createMockTerminal: (lines: string[]) => Terminal;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a mock Terminal object for testing state detectors.
|
|
3
|
+
* @param lines - Array of strings representing terminal output lines
|
|
4
|
+
* @returns Mock Terminal object with buffer interface
|
|
5
|
+
*/
|
|
6
|
+
export const createMockTerminal = (lines) => {
|
|
7
|
+
const buffer = {
|
|
8
|
+
length: lines.length,
|
|
9
|
+
active: {
|
|
10
|
+
length: lines.length,
|
|
11
|
+
getLine: (index) => {
|
|
12
|
+
if (index >= 0 && index < lines.length) {
|
|
13
|
+
return {
|
|
14
|
+
translateToString: () => lines[index],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
return { buffer };
|
|
22
|
+
};
|
|
@@ -45,6 +45,15 @@ export class ClaudeStateDetector extends BaseStateDetector {
|
|
|
45
45
|
if (lowerContent.includes('ctrl+r to toggle')) {
|
|
46
46
|
return currentState;
|
|
47
47
|
}
|
|
48
|
+
// Check for interactive selection interface patterns
|
|
49
|
+
// These patterns indicate Claude is waiting for user interaction with navigation/selection UI
|
|
50
|
+
const hasInteractivePattern = lowerContent.includes('enter to select') ||
|
|
51
|
+
lowerContent.includes('tab/arrow keys to navigate') ||
|
|
52
|
+
lowerContent.includes('esc to cancel') ||
|
|
53
|
+
lowerContent.includes('ready to submit your answers?');
|
|
54
|
+
if (hasInteractivePattern) {
|
|
55
|
+
return 'waiting_input';
|
|
56
|
+
}
|
|
48
57
|
// Check for waiting prompts with box character
|
|
49
58
|
if (content.includes('│ Do you want') ||
|
|
50
59
|
content.includes('│ Would you like')) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.4",
|
|
4
4
|
"description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Kodai Kabasawa",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
24
|
+
"node": ">=22"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc",
|