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.
@@ -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,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.2",
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": ">=16"
24
+ "node": ">=22"
25
25
  },
26
26
  "scripts": {
27
27
  "build": "tsc",