ccmanager 3.6.11 → 3.7.0

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.
@@ -21,6 +21,7 @@ const createStrategyItems = () => {
21
21
  },
22
22
  cline: { label: 'Cline', value: 'cline' },
23
23
  opencode: { label: 'OpenCode', value: 'opencode' },
24
+ kimi: { label: 'Kimi', value: 'kimi' },
24
25
  };
25
26
  return Object.values(strategies);
26
27
  };
@@ -35,6 +36,7 @@ const DEFAULT_COMMANDS = {
35
36
  'github-copilot': 'copilot',
36
37
  cline: 'cline',
37
38
  opencode: 'opencode',
39
+ kimi: 'kimi',
38
40
  };
39
41
  const formatDetectionStrategy = (strategy) => {
40
42
  const value = strategy || 'claude';
@@ -51,6 +53,8 @@ const formatDetectionStrategy = (strategy) => {
51
53
  return 'Cline';
52
54
  case 'opencode':
53
55
  return 'OpenCode';
56
+ case 'kimi':
57
+ return 'Kimi';
54
58
  default:
55
59
  return 'Claude';
56
60
  }
@@ -1,16 +1,18 @@
1
1
  import { BaseStateDetector } from './base.js';
2
2
  export class ClaudeStateDetector extends BaseStateDetector {
3
3
  detectState(terminal, currentState) {
4
- const content = this.getTerminalContent(terminal);
4
+ // Check for search prompt (⌕ Search…) within 200 lines - always idle
5
+ const extendedContent = this.getTerminalContent(terminal, 200);
6
+ if (extendedContent.includes('⌕ Search…')) {
7
+ return 'idle';
8
+ }
9
+ // Existing logic with 30 lines
10
+ const content = this.getTerminalContent(terminal, 30);
5
11
  const lowerContent = content.toLowerCase();
6
12
  // Check for ctrl+r toggle prompt - maintain current state
7
13
  if (lowerContent.includes('ctrl+r to toggle')) {
8
14
  return currentState;
9
15
  }
10
- // Check for search prompt (⌕ Search…) - always idle
11
- if (content.includes('⌕ Search…')) {
12
- return 'idle';
13
- }
14
16
  // Check for "Do you want" or "Would you like" pattern with options
15
17
  // Handles both simple ("Do you want...\nYes") and complex (numbered options) formats
16
18
  if (/(?:do you want|would you like).+\n+[\s\S]*?(?:yes|❯)/.test(lowerContent)) {
@@ -2,7 +2,7 @@ import { BaseStateDetector } from './base.js';
2
2
  // https://github.com/cline/cline/blob/580db36476b6b52def03c8aeda325aae1c817cde/cli/pkg/cli/task/input_handler.go
3
3
  export class ClineStateDetector extends BaseStateDetector {
4
4
  detectState(terminal, _currentState) {
5
- const content = this.getTerminalContent(terminal);
5
+ const content = this.getTerminalContent(terminal, 30);
6
6
  const lowerContent = content.toLowerCase();
7
7
  // Check for waiting prompts with tool permission - Priority 1
8
8
  // Pattern: [\[act|plan\] mode].*?\n.*yes (when mode indicator present)
@@ -1,7 +1,7 @@
1
1
  import { BaseStateDetector } from './base.js';
2
2
  export class CodexStateDetector extends BaseStateDetector {
3
3
  detectState(terminal, _currentState) {
4
- const content = this.getTerminalContent(terminal);
4
+ const content = this.getTerminalContent(terminal, 30);
5
5
  const lowerContent = content.toLowerCase();
6
6
  // Check for confirmation prompt patterns - highest priority
7
7
  if (lowerContent.includes('press enter to confirm or esc to cancel') ||
@@ -1,7 +1,7 @@
1
1
  import { BaseStateDetector } from './base.js';
2
2
  export class CursorStateDetector extends BaseStateDetector {
3
3
  detectState(terminal, _currentState) {
4
- const content = this.getTerminalContent(terminal);
4
+ const content = this.getTerminalContent(terminal, 30);
5
5
  const lowerContent = content.toLowerCase();
6
6
  // Check for waiting prompts - Priority 1
7
7
  if (lowerContent.includes('(y) (enter)') ||
@@ -2,7 +2,7 @@ import { BaseStateDetector } from './base.js';
2
2
  // https://github.com/google-gemini/gemini-cli/blob/main/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
3
3
  export class GeminiStateDetector extends BaseStateDetector {
4
4
  detectState(terminal, _currentState) {
5
- const content = this.getTerminalContent(terminal);
5
+ const content = this.getTerminalContent(terminal, 30);
6
6
  const lowerContent = content.toLowerCase();
7
7
  // Check for explicit user confirmation message - highest priority
8
8
  if (lowerContent.includes('waiting for user confirmation')) {
@@ -1,7 +1,7 @@
1
1
  import { BaseStateDetector } from './base.js';
2
2
  export class GitHubCopilotStateDetector extends BaseStateDetector {
3
3
  detectState(terminal, _currentState) {
4
- const content = this.getTerminalContent(terminal);
4
+ const content = this.getTerminalContent(terminal, 30);
5
5
  const lowerContent = content.toLowerCase();
6
6
  // Check for confirmation prompt pattern - highest priority
7
7
  if (/confirm with .+ enter/i.test(content)) {
@@ -5,6 +5,7 @@ import { CursorStateDetector } from './cursor.js';
5
5
  import { GitHubCopilotStateDetector } from './github-copilot.js';
6
6
  import { ClineStateDetector } from './cline.js';
7
7
  import { OpenCodeStateDetector } from './opencode.js';
8
+ import { KimiStateDetector } from './kimi.js';
8
9
  export function createStateDetector(strategy = 'claude') {
9
10
  switch (strategy) {
10
11
  case 'claude':
@@ -21,6 +22,8 @@ export function createStateDetector(strategy = 'claude') {
21
22
  return new ClineStateDetector();
22
23
  case 'opencode':
23
24
  return new OpenCodeStateDetector();
25
+ case 'kimi':
26
+ return new KimiStateDetector();
24
27
  default:
25
28
  return new ClaudeStateDetector();
26
29
  }
@@ -0,0 +1,12 @@
1
+ import { SessionState, Terminal } from '../../types/index.js';
2
+ import { BaseStateDetector } from './base.js';
3
+ /**
4
+ * State detector for Kimi CLI (kimi-cli).
5
+ * Kimi CLI is an AI coding assistant by Moonshot AI.
6
+ * Command: kimi
7
+ * Installation: uv tool install --python 3.13 kimi-cli
8
+ */
9
+ export declare class KimiStateDetector extends BaseStateDetector {
10
+ detectState(terminal: Terminal, _currentState: SessionState): SessionState;
11
+ detectBackgroundTask(_terminal: Terminal): number;
12
+ }
@@ -0,0 +1,44 @@
1
+ import { BaseStateDetector } from './base.js';
2
+ /**
3
+ * State detector for Kimi CLI (kimi-cli).
4
+ * Kimi CLI is an AI coding assistant by Moonshot AI.
5
+ * Command: kimi
6
+ * Installation: uv tool install --python 3.13 kimi-cli
7
+ */
8
+ export class KimiStateDetector extends BaseStateDetector {
9
+ detectState(terminal, _currentState) {
10
+ const content = this.getTerminalContent(terminal);
11
+ const lowerContent = content.toLowerCase();
12
+ // Check for permission/confirmation prompts - waiting_input state
13
+ // Kimi CLI uses prompts like "Allow?", "Confirm?", "Yes/No" patterns
14
+ if (lowerContent.includes('allow?') ||
15
+ lowerContent.includes('confirm?') ||
16
+ lowerContent.includes('approve?') ||
17
+ lowerContent.includes('proceed?')) {
18
+ return 'waiting_input';
19
+ }
20
+ // Check for Yes/No option patterns
21
+ if (/\[y\/n\]/.test(lowerContent) || /\(y\/n\)/.test(lowerContent)) {
22
+ return 'waiting_input';
23
+ }
24
+ // Check for busy state - processing indicators
25
+ if (lowerContent.includes('thinking') ||
26
+ lowerContent.includes('processing') ||
27
+ lowerContent.includes('generating') ||
28
+ lowerContent.includes('waiting for response')) {
29
+ return 'busy';
30
+ }
31
+ // Check for common interrupt patterns
32
+ if (lowerContent.includes('ctrl+c to cancel') ||
33
+ lowerContent.includes('ctrl-c to cancel') ||
34
+ lowerContent.includes('press ctrl+c')) {
35
+ return 'busy';
36
+ }
37
+ // Otherwise idle
38
+ return 'idle';
39
+ }
40
+ detectBackgroundTask(_terminal) {
41
+ // Kimi CLI does not currently support background tasks
42
+ return 0;
43
+ }
44
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,195 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { KimiStateDetector } from './kimi.js';
3
+ import { createMockTerminal } from './testUtils.js';
4
+ describe('KimiStateDetector', () => {
5
+ let detector;
6
+ let terminal;
7
+ beforeEach(() => {
8
+ detector = new KimiStateDetector();
9
+ });
10
+ describe('detectState', () => {
11
+ it('should detect idle when no specific patterns are found', () => {
12
+ // Arrange
13
+ terminal = createMockTerminal([
14
+ 'Command completed successfully',
15
+ 'Ready for next command',
16
+ '> ',
17
+ ]);
18
+ // Act
19
+ const state = detector.detectState(terminal, 'idle');
20
+ // Assert
21
+ expect(state).toBe('idle');
22
+ });
23
+ it('should handle empty terminal', () => {
24
+ // Arrange
25
+ terminal = createMockTerminal([]);
26
+ // Act
27
+ const state = detector.detectState(terminal, 'idle');
28
+ // Assert
29
+ expect(state).toBe('idle');
30
+ });
31
+ it('should detect waiting_input when "Allow?" prompt is present', () => {
32
+ // Arrange
33
+ terminal = createMockTerminal([
34
+ 'Kimi wants to execute a command',
35
+ 'Allow?',
36
+ ]);
37
+ // Act
38
+ const state = detector.detectState(terminal, 'idle');
39
+ // Assert
40
+ expect(state).toBe('waiting_input');
41
+ });
42
+ it('should detect waiting_input when "Confirm?" prompt is present', () => {
43
+ // Arrange
44
+ terminal = createMockTerminal([
45
+ 'About to make changes to file.ts',
46
+ 'Confirm?',
47
+ ]);
48
+ // Act
49
+ const state = detector.detectState(terminal, 'idle');
50
+ // Assert
51
+ expect(state).toBe('waiting_input');
52
+ });
53
+ it('should detect waiting_input when "Approve?" prompt is present', () => {
54
+ // Arrange
55
+ terminal = createMockTerminal([
56
+ 'Requesting permission to modify config',
57
+ 'Approve?',
58
+ ]);
59
+ // Act
60
+ const state = detector.detectState(terminal, 'idle');
61
+ // Assert
62
+ expect(state).toBe('waiting_input');
63
+ });
64
+ it('should detect waiting_input when "Proceed?" prompt is present', () => {
65
+ // Arrange
66
+ terminal = createMockTerminal(['Ready to execute action', 'Proceed?']);
67
+ // Act
68
+ const state = detector.detectState(terminal, 'idle');
69
+ // Assert
70
+ expect(state).toBe('waiting_input');
71
+ });
72
+ it('should detect waiting_input when "[y/n]" pattern is present', () => {
73
+ // Arrange
74
+ terminal = createMockTerminal(['Do you want to continue? [y/n]', '> ']);
75
+ // Act
76
+ const state = detector.detectState(terminal, 'idle');
77
+ // Assert
78
+ expect(state).toBe('waiting_input');
79
+ });
80
+ it('should detect waiting_input when "(y/n)" pattern is present', () => {
81
+ // Arrange
82
+ terminal = createMockTerminal(['Apply changes? (y/n)', '> ']);
83
+ // Act
84
+ const state = detector.detectState(terminal, 'idle');
85
+ // Assert
86
+ expect(state).toBe('waiting_input');
87
+ });
88
+ it('should detect busy when "thinking" is present', () => {
89
+ // Arrange
90
+ terminal = createMockTerminal(['Processing your request...', 'thinking']);
91
+ // Act
92
+ const state = detector.detectState(terminal, 'idle');
93
+ // Assert
94
+ expect(state).toBe('busy');
95
+ });
96
+ it('should detect busy when "processing" is present', () => {
97
+ // Arrange
98
+ terminal = createMockTerminal(['Analyzing code...', 'processing']);
99
+ // Act
100
+ const state = detector.detectState(terminal, 'idle');
101
+ // Assert
102
+ expect(state).toBe('busy');
103
+ });
104
+ it('should detect busy when "generating" is present', () => {
105
+ // Arrange
106
+ terminal = createMockTerminal(['Working on solution...', 'generating']);
107
+ // Act
108
+ const state = detector.detectState(terminal, 'idle');
109
+ // Assert
110
+ expect(state).toBe('busy');
111
+ });
112
+ it('should detect busy when "waiting for response" is present', () => {
113
+ // Arrange
114
+ terminal = createMockTerminal([
115
+ 'Request sent',
116
+ 'waiting for response from API',
117
+ ]);
118
+ // Act
119
+ const state = detector.detectState(terminal, 'idle');
120
+ // Assert
121
+ expect(state).toBe('busy');
122
+ });
123
+ it('should detect busy when "ctrl+c to cancel" is present', () => {
124
+ // Arrange
125
+ terminal = createMockTerminal([
126
+ 'Running task...',
127
+ 'Press ctrl+c to cancel',
128
+ ]);
129
+ // Act
130
+ const state = detector.detectState(terminal, 'idle');
131
+ // Assert
132
+ expect(state).toBe('busy');
133
+ });
134
+ it('should detect busy when "ctrl-c to cancel" is present', () => {
135
+ // Arrange
136
+ terminal = createMockTerminal([
137
+ 'Executing command...',
138
+ 'ctrl-c to cancel',
139
+ ]);
140
+ // Act
141
+ const state = detector.detectState(terminal, 'idle');
142
+ // Assert
143
+ expect(state).toBe('busy');
144
+ });
145
+ it('should detect busy when "press ctrl+c" is present', () => {
146
+ // Arrange
147
+ terminal = createMockTerminal([
148
+ 'Working...',
149
+ 'Press Ctrl+C to stop the operation',
150
+ ]);
151
+ // Act
152
+ const state = detector.detectState(terminal, 'idle');
153
+ // Assert
154
+ expect(state).toBe('busy');
155
+ });
156
+ it('should detect patterns case-insensitively', () => {
157
+ // Arrange
158
+ terminal = createMockTerminal(['THINKING about the problem...']);
159
+ // Act
160
+ const state = detector.detectState(terminal, 'idle');
161
+ // Assert
162
+ expect(state).toBe('busy');
163
+ });
164
+ it('should prioritize waiting_input over busy when both patterns present', () => {
165
+ // Arrange
166
+ terminal = createMockTerminal(['Processing...', 'thinking', 'Allow?']);
167
+ // Act
168
+ const state = detector.detectState(terminal, 'idle');
169
+ // Assert
170
+ expect(state).toBe('waiting_input');
171
+ });
172
+ });
173
+ describe('detectBackgroundTask', () => {
174
+ it('should return 0 as Kimi CLI does not support background tasks', () => {
175
+ // Arrange
176
+ terminal = createMockTerminal([
177
+ 'Some output',
178
+ 'More output',
179
+ 'Status bar',
180
+ ]);
181
+ // Act
182
+ const count = detector.detectBackgroundTask(terminal);
183
+ // Assert
184
+ expect(count).toBe(0);
185
+ });
186
+ it('should return 0 for empty terminal', () => {
187
+ // Arrange
188
+ terminal = createMockTerminal([]);
189
+ // Act
190
+ const count = detector.detectBackgroundTask(terminal);
191
+ // Assert
192
+ expect(count).toBe(0);
193
+ });
194
+ });
195
+ });
@@ -1,7 +1,7 @@
1
1
  import { BaseStateDetector } from './base.js';
2
2
  export class OpenCodeStateDetector extends BaseStateDetector {
3
3
  detectState(terminal, _currentState) {
4
- const content = this.getTerminalContent(terminal);
4
+ const content = this.getTerminalContent(terminal, 30);
5
5
  // Check for waiting input state - permission required prompt
6
6
  // The triangle symbol (△) indicates permission is required
7
7
  if (content.includes('△ Permission required')) {
@@ -5,7 +5,7 @@ import { Mutex, SessionStateData } from '../utils/mutex.js';
5
5
  import type { StateDetector } from '../services/stateDetector/types.js';
6
6
  export type Terminal = InstanceType<typeof pkg.Terminal>;
7
7
  export type SessionState = 'idle' | 'busy' | 'waiting_input' | 'pending_auto_approval';
8
- export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor' | 'github-copilot' | 'cline' | 'opencode';
8
+ export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor' | 'github-copilot' | 'cline' | 'opencode' | 'kimi';
9
9
  export interface Worktree {
10
10
  path: string;
11
11
  branch?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "3.6.11",
3
+ "version": "3.7.0",
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.6.11",
45
- "@kodaikabasawa/ccmanager-darwin-x64": "3.6.11",
46
- "@kodaikabasawa/ccmanager-linux-arm64": "3.6.11",
47
- "@kodaikabasawa/ccmanager-linux-x64": "3.6.11",
48
- "@kodaikabasawa/ccmanager-win32-x64": "3.6.11"
44
+ "@kodaikabasawa/ccmanager-darwin-arm64": "3.7.0",
45
+ "@kodaikabasawa/ccmanager-darwin-x64": "3.7.0",
46
+ "@kodaikabasawa/ccmanager-linux-arm64": "3.7.0",
47
+ "@kodaikabasawa/ccmanager-linux-x64": "3.7.0",
48
+ "@kodaikabasawa/ccmanager-win32-x64": "3.7.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/js": "^9.28.0",