ccmanager 3.2.4 → 3.2.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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ https://github.com/user-attachments/assets/15914a88-e288-4ac9-94d5-8127f2e19dbf
20
20
  - Configurable state detection strategies for different CLI tools
21
21
  - Status change hooks for automation and notifications
22
22
  - Devcontainer integration
23
+ - **Auto Approval (experimental)**: Automatically approve safe prompts using AI verification
23
24
 
24
25
  ## Why CCManager over Claude Squad?
25
26
 
@@ -249,6 +250,25 @@ CCManager can automatically generate worktree directory paths based on branch na
249
250
 
250
251
  For detailed configuration and examples, see [docs/worktree-auto-directory.md](docs/worktree-auto-directory.md).
251
252
 
253
+ ## Auto Approval (Experimental)
254
+
255
+ CCManager can automatically approve Claude Code prompts that don't require user permission, reducing manual intervention while maintaining safety for sensitive operations.
256
+
257
+ ### Features
258
+
259
+ - **Automatic decision**: Uses Claude (Haiku) to analyze prompts and determine if they need manual approval
260
+ - **Custom commands**: Replace the default verifier with your own script or different AI model
261
+ - **Safe fallback**: Always defaults to manual approval on errors or timeouts
262
+ - **Interruptible**: Press any key to cancel auto-approval and review manually
263
+
264
+ ### Quick Start
265
+
266
+ 1. Navigate to **Configuration** → **Other & Experimental**
267
+ 2. Enable **Auto Approval (experimental)**
268
+ 3. (Optional) Configure a custom command for verification
269
+
270
+ For detailed configuration and usage, see [docs/auto-approval.md](docs/auto-approval.md).
271
+
252
272
  ## Devcontainer Integration
253
273
 
254
274
  CCManager supports running AI assistant sessions inside devcontainers while keeping the manager itself on the host machine. This enables sandboxed development environments with restricted network access while maintaining host-level notifications and automation.
@@ -54,6 +54,28 @@ describe('CodexStateDetector', () => {
54
54
  // Assert
55
55
  expect(state).toBe('waiting_input');
56
56
  });
57
+ it('should detect waiting_input state for "Press enter to confirm or esc to cancel" pattern', () => {
58
+ // Arrange
59
+ terminal = createMockTerminal([
60
+ 'Some output',
61
+ 'Press enter to confirm or esc to cancel',
62
+ ]);
63
+ // Act
64
+ const state = detector.detectState(terminal, 'idle');
65
+ // Assert
66
+ expect(state).toBe('waiting_input');
67
+ });
68
+ it('should prioritize "Press enter to confirm" over busy state with esc interrupt', () => {
69
+ // Arrange
70
+ terminal = createMockTerminal([
71
+ 'esc to interrupt',
72
+ 'Press enter to confirm or esc to cancel',
73
+ ]);
74
+ // Act
75
+ const state = detector.detectState(terminal, 'idle');
76
+ // Assert
77
+ expect(state).toBe('waiting_input');
78
+ });
57
79
  it('should detect busy state for Esc to interrupt pattern', () => {
58
80
  // Arrange
59
81
  terminal = createMockTerminal([
@@ -94,4 +116,31 @@ describe('CodexStateDetector', () => {
94
116
  // Assert
95
117
  expect(state).toBe('waiting_input');
96
118
  });
119
+ it('should detect waiting_input state for "Confirm with ... Enter" pattern', () => {
120
+ // Arrange
121
+ terminal = createMockTerminal(['Some output', 'Confirm with Y Enter']);
122
+ // Act
123
+ const state = detector.detectState(terminal, 'idle');
124
+ // Assert
125
+ expect(state).toBe('waiting_input');
126
+ });
127
+ it('should detect waiting_input for "Confirm with" pattern with longer text', () => {
128
+ // Arrange
129
+ terminal = createMockTerminal([
130
+ 'Some output',
131
+ 'Confirm with Shift + Y Enter',
132
+ ]);
133
+ // Act
134
+ const state = detector.detectState(terminal, 'idle');
135
+ // Assert
136
+ expect(state).toBe('waiting_input');
137
+ });
138
+ it('should prioritize "Confirm with ... Enter" over busy state', () => {
139
+ // Arrange
140
+ terminal = createMockTerminal(['Esc to interrupt', 'Confirm with Y Enter']);
141
+ // Act
142
+ const state = detector.detectState(terminal, 'idle');
143
+ // Assert
144
+ expect(state).toBe('waiting_input');
145
+ });
97
146
  });
@@ -20,6 +20,18 @@ describe('GeminiStateDetector', () => {
20
20
  // Assert
21
21
  expect(state).toBe('waiting_input');
22
22
  });
23
+ it('should detect waiting_input when "Apply this change" prompt is present (without ?)', () => {
24
+ // Arrange
25
+ terminal = createMockTerminal([
26
+ 'Some output from Gemini',
27
+ '│ Apply this change',
28
+ '│ > ',
29
+ ]);
30
+ // Act
31
+ const state = detector.detectState(terminal, 'idle');
32
+ // Assert
33
+ expect(state).toBe('waiting_input');
34
+ });
23
35
  it('should detect waiting_input when "Allow execution?" prompt is present', () => {
24
36
  // Arrange
25
37
  terminal = createMockTerminal([
@@ -32,6 +44,18 @@ describe('GeminiStateDetector', () => {
32
44
  // Assert
33
45
  expect(state).toBe('waiting_input');
34
46
  });
47
+ it('should detect waiting_input when "Allow execution" prompt is present (without ?)', () => {
48
+ // Arrange
49
+ terminal = createMockTerminal([
50
+ 'Command found: npm install',
51
+ '│ Allow execution',
52
+ '│ > ',
53
+ ]);
54
+ // Act
55
+ const state = detector.detectState(terminal, 'idle');
56
+ // Assert
57
+ expect(state).toBe('waiting_input');
58
+ });
35
59
  it('should detect waiting_input when "Do you want to proceed?" prompt is present', () => {
36
60
  // Arrange
37
61
  terminal = createMockTerminal([
@@ -44,6 +68,40 @@ describe('GeminiStateDetector', () => {
44
68
  // Assert
45
69
  expect(state).toBe('waiting_input');
46
70
  });
71
+ it('should detect waiting_input when "Do you want to proceed" prompt is present (without ?)', () => {
72
+ // Arrange
73
+ terminal = createMockTerminal([
74
+ 'Changes detected',
75
+ '│ Do you want to proceed',
76
+ '│ > ',
77
+ ]);
78
+ // Act
79
+ const state = detector.detectState(terminal, 'idle');
80
+ // Assert
81
+ expect(state).toBe('waiting_input');
82
+ });
83
+ it('should detect waiting_input when "Waiting for user confirmation..." is present', () => {
84
+ // Arrange
85
+ terminal = createMockTerminal([
86
+ 'Processing...',
87
+ 'Waiting for user confirmation...',
88
+ ]);
89
+ // Act
90
+ const state = detector.detectState(terminal, 'idle');
91
+ // Assert
92
+ expect(state).toBe('waiting_input');
93
+ });
94
+ it('should prioritize "Waiting for user confirmation" over busy state', () => {
95
+ // Arrange
96
+ terminal = createMockTerminal([
97
+ 'Press ESC to cancel',
98
+ 'Waiting for user confirmation...',
99
+ ]);
100
+ // Act
101
+ const state = detector.detectState(terminal, 'idle');
102
+ // Assert
103
+ expect(state).toBe('waiting_input');
104
+ });
47
105
  it('should detect waiting_input for multiline confirmation ending with "yes"', () => {
48
106
  // Arrange
49
107
  terminal = createMockTerminal([
@@ -19,6 +19,36 @@ describe('GitHubCopilotStateDetector', () => {
19
19
  // Assert
20
20
  expect(state).toBe('waiting_input');
21
21
  });
22
+ it('detects waiting_input when "Confirm with ... Enter" pattern is present', () => {
23
+ // Arrange
24
+ terminal = createMockTerminal(['Some output', 'Confirm with Y Enter']);
25
+ // Act
26
+ const state = detector.detectState(terminal, 'idle');
27
+ // Assert
28
+ expect(state).toBe('waiting_input');
29
+ });
30
+ it('detects waiting_input for "Confirm with" pattern with longer text', () => {
31
+ // Arrange
32
+ terminal = createMockTerminal([
33
+ 'Some output',
34
+ 'Confirm with Shift + Y Enter',
35
+ ]);
36
+ // Act
37
+ const state = detector.detectState(terminal, 'idle');
38
+ // Assert
39
+ expect(state).toBe('waiting_input');
40
+ });
41
+ it('prioritizes "Confirm with ... Enter" over busy state', () => {
42
+ // Arrange
43
+ terminal = createMockTerminal([
44
+ 'Press Esc to cancel',
45
+ 'Confirm with Y Enter',
46
+ ]);
47
+ // Act
48
+ const state = detector.detectState(terminal, 'idle');
49
+ // Assert
50
+ expect(state).toBe('waiting_input');
51
+ });
22
52
  it('detects busy when "Esc to cancel" is present', () => {
23
53
  // Arrange
24
54
  terminal = createMockTerminal([
@@ -63,10 +63,14 @@ export class GeminiStateDetector extends BaseStateDetector {
63
63
  detectState(terminal, _currentState) {
64
64
  const content = this.getTerminalContent(terminal);
65
65
  const lowerContent = content.toLowerCase();
66
+ // Check for explicit user confirmation message - highest priority
67
+ if (lowerContent.includes('waiting for user confirmation')) {
68
+ return 'waiting_input';
69
+ }
66
70
  // Check for waiting prompts with box character
67
- if (content.includes('│ Apply this change?') ||
68
- content.includes('│ Allow execution?') ||
69
- content.includes('│ Do you want to proceed?')) {
71
+ if (content.includes('│ Apply this change') ||
72
+ content.includes('│ Allow execution') ||
73
+ content.includes('│ Do you want to proceed')) {
70
74
  return 'waiting_input';
71
75
  }
72
76
  // Check for multiline confirmation prompts ending with "yes"
@@ -85,6 +89,11 @@ export class CodexStateDetector extends BaseStateDetector {
85
89
  detectState(terminal, _currentState) {
86
90
  const content = this.getTerminalContent(terminal);
87
91
  const lowerContent = content.toLowerCase();
92
+ // Check for confirmation prompt patterns - highest priority
93
+ if (lowerContent.includes('press enter to confirm or esc to cancel') ||
94
+ /confirm with .+ enter/i.test(content)) {
95
+ return 'waiting_input';
96
+ }
88
97
  // Check for waiting prompts
89
98
  if (lowerContent.includes('allow command?') ||
90
99
  lowerContent.includes('[y/n]') ||
@@ -124,15 +133,19 @@ export class GitHubCopilotStateDetector extends BaseStateDetector {
124
133
  detectState(terminal, _currentState) {
125
134
  const content = this.getTerminalContent(terminal);
126
135
  const lowerContent = content.toLowerCase();
127
- // Waiting prompt has priority 1
136
+ // Check for confirmation prompt pattern - highest priority
137
+ if (/confirm with .+ enter/i.test(content)) {
138
+ return 'waiting_input';
139
+ }
140
+ // Waiting prompt has priority 2
128
141
  if (lowerContent.includes('│ do you want')) {
129
142
  return 'waiting_input';
130
143
  }
131
- // Busy state detection has priority 2
144
+ // Busy state detection has priority 3
132
145
  if (lowerContent.includes('esc to cancel')) {
133
146
  return 'busy';
134
147
  }
135
- // Otherwise idle as priority 3
148
+ // Otherwise idle as priority 4
136
149
  return 'idle';
137
150
  }
138
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "3.2.4",
3
+ "version": "3.2.6",
4
4
  "description": "TUI application for managing multiple Claude Code sessions across Git worktrees",
5
5
  "license": "MIT",
6
6
  "author": "Kodai Kabasawa",
@@ -41,11 +41,11 @@
41
41
  "bin"
42
42
  ],
43
43
  "optionalDependencies": {
44
- "@kodaikabasawa/ccmanager-darwin-arm64": "3.2.4",
45
- "@kodaikabasawa/ccmanager-darwin-x64": "3.2.4",
46
- "@kodaikabasawa/ccmanager-linux-arm64": "3.2.4",
47
- "@kodaikabasawa/ccmanager-linux-x64": "3.2.4",
48
- "@kodaikabasawa/ccmanager-win32-x64": "3.2.4"
44
+ "@kodaikabasawa/ccmanager-darwin-arm64": "3.2.6",
45
+ "@kodaikabasawa/ccmanager-darwin-x64": "3.2.6",
46
+ "@kodaikabasawa/ccmanager-linux-arm64": "3.2.6",
47
+ "@kodaikabasawa/ccmanager-linux-x64": "3.2.6",
48
+ "@kodaikabasawa/ccmanager-win32-x64": "3.2.6"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/js": "^9.28.0",