ccmanager 2.6.0 → 2.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.
package/README.md CHANGED
@@ -146,7 +146,7 @@ CCManager supports configuring the command and arguments used to run Claude Code
146
146
 
147
147
  ### Quick Start
148
148
 
149
- 1. Navigate to **Configuration** → **Configure Command**
149
+ 1. Navigate to **Configuration** → **Configure Command Presets**
150
150
  2. Set your desired arguments (e.g., `--resume` for resuming sessions)
151
151
  3. Optionally set fallback arguments
152
152
  4. Save changes
@@ -27,8 +27,8 @@ const Configuration = ({ onComplete }) => {
27
27
  value: 'worktree',
28
28
  },
29
29
  {
30
- label: 'C 🚀 Configure Command',
31
- value: 'command',
30
+ label: 'C 🚀 Configure Command Presets',
31
+ value: 'presets',
32
32
  },
33
33
  {
34
34
  label: 'B ← Back to Main Menu',
@@ -51,8 +51,8 @@ const Configuration = ({ onComplete }) => {
51
51
  else if (item.value === 'worktree') {
52
52
  setView('worktree');
53
53
  }
54
- else if (item.value === 'command') {
55
- setView('command');
54
+ else if (item.value === 'presets') {
55
+ setView('presets');
56
56
  }
57
57
  };
58
58
  const handleSubMenuComplete = () => {
@@ -77,7 +77,7 @@ const Configuration = ({ onComplete }) => {
77
77
  setView('worktree');
78
78
  break;
79
79
  case 'c':
80
- setView('command');
80
+ setView('presets');
81
81
  break;
82
82
  case 'b':
83
83
  onComplete();
@@ -100,7 +100,7 @@ const Configuration = ({ onComplete }) => {
100
100
  if (view === 'worktree') {
101
101
  return React.createElement(ConfigureWorktree, { onComplete: handleSubMenuComplete });
102
102
  }
103
- if (view === 'command') {
103
+ if (view === 'presets') {
104
104
  return React.createElement(ConfigureCommand, { onComplete: handleSubMenuComplete });
105
105
  }
106
106
  return (React.createElement(Box, { flexDirection: "column" },
@@ -14,6 +14,10 @@ const createStrategyItems = () => {
14
14
  gemini: { label: 'Gemini', value: 'gemini' },
15
15
  codex: { label: 'Codex', value: 'codex' },
16
16
  cursor: { label: 'Cursor Agent', value: 'cursor' },
17
+ 'github-copilot': {
18
+ label: 'GitHub Copilot CLI',
19
+ value: 'github-copilot',
20
+ },
17
21
  };
18
22
  return Object.values(strategies);
19
23
  };
@@ -28,6 +32,8 @@ const formatDetectionStrategy = (strategy) => {
28
32
  return 'Codex';
29
33
  case 'cursor':
30
34
  return 'Cursor';
35
+ case 'github-copilot':
36
+ return 'GitHub Copilot CLI';
31
37
  default:
32
38
  return 'Claude';
33
39
  }
@@ -511,7 +517,7 @@ const ConfigureCommand = ({ onComplete }) => {
511
517
  };
512
518
  return (React.createElement(Box, { flexDirection: "column" },
513
519
  React.createElement(Box, { marginBottom: 1 },
514
- React.createElement(Text, { bold: true, color: "green" }, "Command Presets")),
520
+ React.createElement(Text, { bold: true, color: "green" }, "Command Command Presets")),
515
521
  React.createElement(Box, { marginBottom: 1 },
516
522
  React.createElement(Text, { dimColor: true }, "Configure command presets for running code sessions")),
517
523
  React.createElement(SelectInput, { items: selectItems, onSelect: handleSelectItem, initialIndex: selectedIndex }),
@@ -20,3 +20,6 @@ export declare class CodexStateDetector extends BaseStateDetector {
20
20
  export declare class CursorStateDetector extends BaseStateDetector {
21
21
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
22
22
  }
23
+ export declare class GitHubCopilotStateDetector extends BaseStateDetector {
24
+ detectState(terminal: Terminal, _currentState: SessionState): SessionState;
25
+ }
@@ -8,6 +8,8 @@ export function createStateDetector(strategy = 'claude') {
8
8
  return new CodexStateDetector();
9
9
  case 'cursor':
10
10
  return new CursorStateDetector();
11
+ case 'github-copilot':
12
+ return new GitHubCopilotStateDetector();
11
13
  default:
12
14
  return new ClaudeStateDetector();
13
15
  }
@@ -109,3 +111,19 @@ export class CursorStateDetector extends BaseStateDetector {
109
111
  return 'idle';
110
112
  }
111
113
  }
114
+ export class GitHubCopilotStateDetector extends BaseStateDetector {
115
+ detectState(terminal, _currentState) {
116
+ const content = this.getTerminalContent(terminal);
117
+ const lowerContent = content.toLowerCase();
118
+ // Waiting prompt has priority 1
119
+ if (lowerContent.includes('│ do you want')) {
120
+ return 'waiting_input';
121
+ }
122
+ // Busy state detection has priority 2
123
+ if (lowerContent.includes('esc to cancel')) {
124
+ return 'busy';
125
+ }
126
+ // Otherwise idle as priority 3
127
+ return 'idle';
128
+ }
129
+ }
@@ -1,10 +1,9 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
- import { ClaudeStateDetector, GeminiStateDetector, CodexStateDetector, CursorStateDetector, } from './stateDetector.js';
3
- describe('ClaudeStateDetector', () => {
4
- let detector;
5
- let terminal;
6
- const createMockTerminal = (lines) => {
7
- const buffer = {
2
+ import { ClaudeStateDetector, GeminiStateDetector, CodexStateDetector, CursorStateDetector, GitHubCopilotStateDetector, } from './stateDetector.js';
3
+ const createMockTerminal = (lines) => {
4
+ const buffer = {
5
+ length: lines.length,
6
+ active: {
8
7
  length: lines.length,
9
8
  getLine: (index) => {
10
9
  if (index >= 0 && index < lines.length) {
@@ -14,13 +13,13 @@ describe('ClaudeStateDetector', () => {
14
13
  }
15
14
  return null;
16
15
  },
17
- };
18
- return {
19
- buffer: {
20
- active: buffer,
21
- },
22
- };
16
+ },
23
17
  };
18
+ return { buffer };
19
+ };
20
+ describe('ClaudeStateDetector', () => {
21
+ let detector;
22
+ let terminal;
24
23
  beforeEach(() => {
25
24
  detector = new ClaudeStateDetector();
26
25
  });
@@ -159,24 +158,6 @@ describe('ClaudeStateDetector', () => {
159
158
  describe('GeminiStateDetector', () => {
160
159
  let detector;
161
160
  let terminal;
162
- const createMockTerminal = (lines) => {
163
- const buffer = {
164
- length: lines.length,
165
- getLine: (index) => {
166
- if (index >= 0 && index < lines.length) {
167
- return {
168
- translateToString: () => lines[index],
169
- };
170
- }
171
- return null;
172
- },
173
- };
174
- return {
175
- buffer: {
176
- active: buffer,
177
- },
178
- };
179
- };
180
161
  beforeEach(() => {
181
162
  detector = new GeminiStateDetector();
182
163
  });
@@ -275,23 +256,6 @@ describe('GeminiStateDetector', () => {
275
256
  describe('CodexStateDetector', () => {
276
257
  let detector;
277
258
  let terminal;
278
- const createMockTerminal = (lines) => {
279
- const buffer = {
280
- length: lines.length,
281
- active: {
282
- length: lines.length,
283
- getLine: (index) => {
284
- if (index >= 0 && index < lines.length) {
285
- return {
286
- translateToString: () => lines[index],
287
- };
288
- }
289
- return null;
290
- },
291
- },
292
- };
293
- return { buffer };
294
- };
295
259
  beforeEach(() => {
296
260
  detector = new CodexStateDetector();
297
261
  });
@@ -366,23 +330,6 @@ describe('CodexStateDetector', () => {
366
330
  describe('CursorStateDetector', () => {
367
331
  let detector;
368
332
  let terminal;
369
- const createMockTerminal = (lines) => {
370
- const buffer = {
371
- length: lines.length,
372
- active: {
373
- length: lines.length,
374
- getLine: (index) => {
375
- if (index >= 0 && index < lines.length) {
376
- return {
377
- translateToString: () => lines[index],
378
- };
379
- }
380
- return null;
381
- },
382
- },
383
- };
384
- return { buffer };
385
- };
386
333
  beforeEach(() => {
387
334
  detector = new CursorStateDetector();
388
335
  });
@@ -507,3 +454,55 @@ describe('CursorStateDetector', () => {
507
454
  expect(state).toBe('idle');
508
455
  });
509
456
  });
457
+ describe('GitHubCopilotStateDetector', () => {
458
+ let detector;
459
+ let terminal;
460
+ beforeEach(() => {
461
+ detector = new GitHubCopilotStateDetector();
462
+ });
463
+ it('detects waiting_input when prompt asks "Do you want" (case insensitive)', () => {
464
+ // Arrange
465
+ terminal = createMockTerminal([
466
+ 'Running GitHub Copilot CLI...',
467
+ '│ DO YOU WANT to run this command?',
468
+ '│ > ',
469
+ ]);
470
+ // Act
471
+ const state = detector.detectState(terminal, 'idle');
472
+ // Assert
473
+ expect(state).toBe('waiting_input');
474
+ });
475
+ it('detects busy when "Esc to cancel" is present', () => {
476
+ // Arrange
477
+ terminal = createMockTerminal([
478
+ 'Executing request...',
479
+ 'Press Esc to cancel',
480
+ ]);
481
+ // Act
482
+ const state = detector.detectState(terminal, 'idle');
483
+ // Assert
484
+ expect(state).toBe('busy');
485
+ });
486
+ it('prioritizes waiting_input over busy when both patterns exist', () => {
487
+ // Arrange
488
+ terminal = createMockTerminal([
489
+ 'Press Esc to cancel',
490
+ '│ Do you want to continue?',
491
+ ]);
492
+ // Act
493
+ const state = detector.detectState(terminal, 'idle');
494
+ // Assert
495
+ expect(state).toBe('waiting_input');
496
+ });
497
+ it('returns idle when no patterns match', () => {
498
+ // Arrange
499
+ terminal = createMockTerminal([
500
+ 'GitHub Copilot CLI ready.',
501
+ 'Type a command to begin.',
502
+ ]);
503
+ // Act
504
+ const state = detector.detectState(terminal, 'idle');
505
+ // Assert
506
+ expect(state).toBe('idle');
507
+ });
508
+ });
@@ -3,7 +3,7 @@ import type pkg from '@xterm/headless';
3
3
  import { GitStatus } from '../utils/gitStatus.js';
4
4
  export type Terminal = InstanceType<typeof pkg.Terminal>;
5
5
  export type SessionState = 'idle' | 'busy' | 'waiting_input';
6
- export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor';
6
+ export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor' | 'github-copilot';
7
7
  export interface Worktree {
8
8
  path: string;
9
9
  branch?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "2.6.0",
3
+ "version": "2.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",