ccmanager 3.2.10 → 3.3.1

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
@@ -112,23 +112,24 @@ Note: Shortcuts from `shortcuts.json` will be automatically migrated to `config.
112
112
 
113
113
  ## Supported AI Assistants
114
114
 
115
- CCManager now supports multiple AI coding assistants with tailored state detection:
116
-
117
- ### Claude Code (Default)
118
- - Command: `claude`
119
- - State detection: Built-in patterns for Claude's prompts and status messages
120
-
121
- ### Gemini CLI
122
- - Command: `gemini`
123
- - State detection: Custom patterns for Gemini's confirmation prompts
124
- - Installation: [google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli)
115
+ CCManager supports multiple AI coding assistants with tailored state detection for each:
116
+
117
+ | Assistant | Command | Installation |
118
+ |-----------|---------|--------------|
119
+ | Claude Code (Default) | `claude` | [code.claude.com](https://code.claude.com/docs/en/setup) |
120
+ | Gemini CLI | `gemini` | [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) |
121
+ | Codex CLI | `codex` | [github.com/openai/codex](https://github.com/openai/codex) |
122
+ | Cursor Agent | `cursor-agent` | [cursor.com/cli](https://cursor.com/docs/cli/overview) |
123
+ | Copilot CLI | `copilot` | [github.com/github/copilot-cli](https://github.com/github/copilot-cli) |
124
+ | Cline CLI | `cline` | [github.com/cline/cline](https://github.com/cline/cline) |
125
+ | OpenCode | `opencode` | [opencode.ai/docs](https://opencode.ai/docs) |
125
126
 
126
127
  Each assistant has its own state detection strategy to properly track:
127
128
  - **Idle**: Ready for new input
128
129
  - **Busy**: Processing a request
129
130
  - **Waiting**: Awaiting user confirmation
130
131
 
131
- See [Gemini Support Documentation](docs/gemini-support.md) for detailed configuration instructions.
132
+ See [Gemini Support Documentation](docs/gemini-support.md) for CCManager-specific configuration.
132
133
 
133
134
 
134
135
  ## Command Configuration
@@ -19,11 +19,22 @@ const createStrategyItems = () => {
19
19
  value: 'github-copilot',
20
20
  },
21
21
  cline: { label: 'Cline', value: 'cline' },
22
+ opencode: { label: 'OpenCode', value: 'opencode' },
22
23
  };
23
24
  return Object.values(strategies);
24
25
  };
25
26
  // Type-safe strategy items that ensures all StateDetectionStrategy values are included
26
27
  const ALL_STRATEGY_ITEMS = createStrategyItems();
28
+ // Default command mapping for each strategy
29
+ const DEFAULT_COMMANDS = {
30
+ claude: 'claude',
31
+ gemini: 'gemini',
32
+ codex: 'codex',
33
+ cursor: 'cursor',
34
+ 'github-copilot': 'copilot',
35
+ cline: 'cline',
36
+ opencode: 'opencode',
37
+ };
27
38
  const formatDetectionStrategy = (strategy) => {
28
39
  const value = strategy || 'claude';
29
40
  switch (value) {
@@ -35,6 +46,10 @@ const formatDetectionStrategy = (strategy) => {
35
46
  return 'Cursor';
36
47
  case 'github-copilot':
37
48
  return 'GitHub Copilot CLI';
49
+ case 'cline':
50
+ return 'Cline';
51
+ case 'opencode':
52
+ return 'OpenCode';
38
53
  default:
39
54
  return 'Claude';
40
55
  }
@@ -52,7 +67,7 @@ const ConfigureCommand = ({ onComplete }) => {
52
67
  const [isSelectingStrategy, setIsSelectingStrategy] = useState(false);
53
68
  const [isSelectingStrategyInAdd, setIsSelectingStrategyInAdd] = useState(false);
54
69
  const [newPreset, setNewPreset] = useState({});
55
- const [addStep, setAddStep] = useState('name');
70
+ const [addStep, setAddStep] = useState('detectionStrategy');
56
71
  const [errorMessage, setErrorMessage] = useState(null);
57
72
  // Remove handleListNavigation as SelectInput handles navigation internally
58
73
  // Remove handleListSelection as we now use handleSelectItem
@@ -137,19 +152,8 @@ const ConfigureCommand = ({ onComplete }) => {
137
152
  };
138
153
  const handleAddPresetInput = (value) => {
139
154
  switch (addStep) {
140
- case 'name':
141
- // Prevent using "Default" as a name to avoid confusion
142
- if (value.trim().toLowerCase() === 'default') {
143
- setErrorMessage('Cannot use "Default" as a preset name. Please choose a different name.');
144
- return;
145
- }
146
- setNewPreset({ ...newPreset, name: value });
147
- setAddStep('command');
148
- setInputValue('');
149
- setErrorMessage(null);
150
- break;
151
155
  case 'command':
152
- setNewPreset({ ...newPreset, command: value || 'claude' });
156
+ setNewPreset({ ...newPreset, command: value });
153
157
  setAddStep('args');
154
158
  setInputValue('');
155
159
  break;
@@ -165,8 +169,38 @@ const ConfigureCommand = ({ onComplete }) => {
165
169
  ? value.trim().split(/\s+/)
166
170
  : undefined;
167
171
  setNewPreset({ ...newPreset, fallbackArgs });
172
+ setAddStep('name');
173
+ setInputValue('');
174
+ break;
175
+ }
176
+ case 'name': {
177
+ if (!value.trim()) {
178
+ setErrorMessage('Preset name cannot be empty. Please enter a name.');
179
+ return;
180
+ }
181
+ if (value.trim().toLowerCase() === 'default') {
182
+ setErrorMessage('Cannot use "Default" as a preset name. Please choose a different name.');
183
+ return;
184
+ }
185
+ const id = Date.now().toString();
186
+ const completePreset = {
187
+ id,
188
+ name: value,
189
+ command: newPreset.command || 'claude',
190
+ args: newPreset.args,
191
+ fallbackArgs: newPreset.fallbackArgs,
192
+ detectionStrategy: newPreset.detectionStrategy || 'claude',
193
+ };
194
+ const updatedPresets = [...presets, completePreset];
195
+ setPresets(updatedPresets);
196
+ configurationManager.addPreset(completePreset);
197
+ setViewMode('list');
198
+ setSelectedIndex(updatedPresets.length - 1);
199
+ setNewPreset({});
168
200
  setAddStep('detectionStrategy');
169
- setIsSelectingStrategyInAdd(true);
201
+ setInputValue('');
202
+ setIsSelectingStrategyInAdd(false);
203
+ setErrorMessage(null);
170
204
  break;
171
205
  }
172
206
  }
@@ -183,25 +217,16 @@ const ConfigureCommand = ({ onComplete }) => {
183
217
  setIsSelectingStrategy(false);
184
218
  };
185
219
  const handleAddStrategySelect = (item) => {
186
- const id = Date.now().toString();
187
- const completePreset = {
188
- id,
189
- name: newPreset.name || 'New Preset',
190
- command: newPreset.command || 'claude',
191
- args: newPreset.args,
192
- fallbackArgs: newPreset.fallbackArgs,
193
- detectionStrategy: item.value,
194
- };
195
- const updatedPresets = [...presets, completePreset];
196
- setPresets(updatedPresets);
197
- configurationManager.addPreset(completePreset);
198
- setViewMode('list');
199
- setSelectedIndex(updatedPresets.length - 1);
200
- setNewPreset({});
201
- setAddStep('name');
202
- setInputValue('');
220
+ const strategy = item.value;
221
+ const defaultCommand = DEFAULT_COMMANDS[strategy];
222
+ setNewPreset({
223
+ ...newPreset,
224
+ detectionStrategy: strategy,
225
+ command: defaultCommand,
226
+ });
227
+ setAddStep('command');
228
+ setInputValue(defaultCommand);
203
229
  setIsSelectingStrategyInAdd(false);
204
- setErrorMessage(null);
205
230
  };
206
231
  const handleDeleteConfirm = () => {
207
232
  if (selectedIndex === 0) {
@@ -234,7 +259,7 @@ const ConfigureCommand = ({ onComplete }) => {
234
259
  else if (isSelectingStrategyInAdd) {
235
260
  setIsSelectingStrategyInAdd(false);
236
261
  setViewMode('list');
237
- setAddStep('name');
262
+ setAddStep('detectionStrategy');
238
263
  setNewPreset({});
239
264
  }
240
265
  else if (editField) {
@@ -333,6 +358,8 @@ const ConfigureCommand = ({ onComplete }) => {
333
358
  React.createElement(Text, { bold: true, color: "green" }, "Add New Preset - Detection Strategy")),
334
359
  React.createElement(Box, { marginBottom: 1 },
335
360
  React.createElement(Text, null, "Choose the state detection strategy for this preset:")),
361
+ React.createElement(Box, { marginBottom: 1 },
362
+ React.createElement(Text, { dimColor: true }, "The command will be auto-set based on the strategy (can be changed later)")),
336
363
  React.createElement(SelectInput, { items: strategyItems, onSelect: handleAddStrategySelect, initialIndex: 0 }),
337
364
  React.createElement(Box, { marginTop: 1 },
338
365
  React.createElement(Text, { dimColor: true },
@@ -342,23 +369,26 @@ const ConfigureCommand = ({ onComplete }) => {
342
369
  " to cancel"))));
343
370
  }
344
371
  const titles = {
345
- name: 'Enter preset name:',
346
- command: 'Enter command (e.g., claude):',
372
+ detectionStrategy: 'Select detection strategy:',
373
+ command: 'Enter command (default set by strategy, can be modified):',
347
374
  args: 'Enter command arguments (space-separated):',
348
375
  fallbackArgs: 'Enter fallback arguments (space-separated):',
376
+ name: 'Enter preset name (freely customizable):',
349
377
  };
350
378
  return (React.createElement(Box, { flexDirection: "column" },
351
379
  React.createElement(Box, { marginBottom: 1 },
352
380
  React.createElement(Text, { bold: true, color: "green" }, "Add New Preset")),
353
381
  React.createElement(Box, { marginBottom: 1 },
354
382
  React.createElement(Text, null, titles[addStep])),
383
+ addStep === 'command' && (React.createElement(Box, { marginBottom: 1 },
384
+ React.createElement(Text, { dimColor: true }, "Auto-filled from your strategy selection. You can change this if needed."))),
355
385
  errorMessage && (React.createElement(Box, { marginBottom: 1 },
356
386
  React.createElement(Text, { color: "red" }, errorMessage))),
357
387
  React.createElement(Box, null,
358
- React.createElement(TextInputWrapper, { value: inputValue, onChange: setInputValue, onSubmit: handleAddPresetInput, placeholder: addStep === 'args' || addStep === 'fallbackArgs'
359
- ? 'e.g., --resume or leave empty'
360
- : addStep === 'name'
361
- ? 'e.g., Development'
388
+ React.createElement(TextInputWrapper, { value: inputValue, onChange: setInputValue, onSubmit: handleAddPresetInput, placeholder: addStep === 'name'
389
+ ? 'e.g., Development'
390
+ : addStep === 'args' || addStep === 'fallbackArgs'
391
+ ? 'e.g., --resume or leave empty'
362
392
  : '' })),
363
393
  React.createElement(Box, { marginTop: 1 },
364
394
  React.createElement(Text, { dimColor: true },
@@ -489,7 +519,8 @@ const ConfigureCommand = ({ onComplete }) => {
489
519
  // Add New Preset
490
520
  setViewMode('add');
491
521
  setNewPreset({});
492
- setAddStep('name');
522
+ setAddStep('detectionStrategy');
523
+ setIsSelectingStrategyInAdd(true);
493
524
  setInputValue('');
494
525
  }
495
526
  else if (item.value === 'exit') {
@@ -12,6 +12,7 @@ import { ProcessError, ConfigError } from '../types/errors.js';
12
12
  import { autoApprovalVerifier } from './autoApprovalVerifier.js';
13
13
  import { logger } from '../utils/logger.js';
14
14
  import { Mutex, createInitialSessionStateData } from '../utils/mutex.js';
15
+ import { getTerminalScreenContent } from '../utils/screenCapture.js';
15
16
  const { Terminal } = pkg;
16
17
  const execAsync = promisify(exec);
17
18
  const TERMINAL_CONTENT_MAX_LINES = 300;
@@ -41,20 +42,9 @@ export class SessionManager extends EventEmitter {
41
42
  return detectedState;
42
43
  }
43
44
  getTerminalContent(session) {
44
- const buffer = session.terminal.buffer.active;
45
- const lines = [];
46
- // Start from the bottom and work our way up
47
- for (let i = buffer.length - 1; i >= 0 && lines.length < TERMINAL_CONTENT_MAX_LINES; i--) {
48
- const line = buffer.getLine(i);
49
- if (line) {
50
- const text = line.translateToString(true);
51
- // Skip empty lines at the bottom
52
- if (lines.length > 0 || text.trim() !== '') {
53
- lines.unshift(text);
54
- }
55
- }
56
- }
57
- return lines.join('\n');
45
+ // Use the new screen capture utility that correctly handles
46
+ // both normal and alternate screen buffers
47
+ return getTerminalScreenContent(session.terminal, TERMINAL_CONTENT_MAX_LINES);
58
48
  }
59
49
  handleAutoApproval(session) {
60
50
  // Cancel any existing verification before starting a new one
@@ -4,6 +4,7 @@ import { CodexStateDetector } from './codex.js';
4
4
  import { CursorStateDetector } from './cursor.js';
5
5
  import { GitHubCopilotStateDetector } from './github-copilot.js';
6
6
  import { ClineStateDetector } from './cline.js';
7
+ import { OpenCodeStateDetector } from './opencode.js';
7
8
  export function createStateDetector(strategy = 'claude') {
8
9
  switch (strategy) {
9
10
  case 'claude':
@@ -18,6 +19,8 @@ export function createStateDetector(strategy = 'claude') {
18
19
  return new GitHubCopilotStateDetector();
19
20
  case 'cline':
20
21
  return new ClineStateDetector();
22
+ case 'opencode':
23
+ return new OpenCodeStateDetector();
21
24
  default:
22
25
  return new ClaudeStateDetector();
23
26
  }
@@ -0,0 +1,5 @@
1
+ import { SessionState, Terminal } from '../../types/index.js';
2
+ import { BaseStateDetector } from './base.js';
3
+ export declare class OpenCodeStateDetector extends BaseStateDetector {
4
+ detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
+ }
@@ -0,0 +1,17 @@
1
+ import { BaseStateDetector } from './base.js';
2
+ export class OpenCodeStateDetector extends BaseStateDetector {
3
+ detectState(terminal, _currentState) {
4
+ const content = this.getTerminalContent(terminal);
5
+ // Check for waiting input state - permission required prompt
6
+ // The triangle symbol (△) indicates permission is required
7
+ if (content.includes('△ Permission required')) {
8
+ return 'waiting_input';
9
+ }
10
+ // Check for busy state - "esc interrupt" pattern indicates active processing
11
+ if (/esc.*interrupt/i.test(content)) {
12
+ return 'busy';
13
+ }
14
+ // Otherwise idle
15
+ return 'idle';
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { OpenCodeStateDetector } from './opencode.js';
3
+ import { createMockTerminal } from './testUtils.js';
4
+ describe('OpenCodeStateDetector', () => {
5
+ let detector;
6
+ let terminal;
7
+ beforeEach(() => {
8
+ detector = new OpenCodeStateDetector();
9
+ });
10
+ it('should detect waiting_input state for "△ Permission required" pattern', () => {
11
+ // Arrange
12
+ terminal = createMockTerminal([
13
+ 'Some output',
14
+ '△ Permission required',
15
+ 'Press Enter to allow',
16
+ ]);
17
+ // Act
18
+ const state = detector.detectState(terminal, 'idle');
19
+ // Assert
20
+ expect(state).toBe('waiting_input');
21
+ });
22
+ it('should detect busy state for "esc interrupt" pattern', () => {
23
+ // Arrange
24
+ terminal = createMockTerminal([
25
+ 'Processing...',
26
+ 'Press esc to interrupt',
27
+ 'Working...',
28
+ ]);
29
+ // Act
30
+ const state = detector.detectState(terminal, 'idle');
31
+ // Assert
32
+ expect(state).toBe('busy');
33
+ });
34
+ it('should detect busy state for "ESC INTERRUPT" (uppercase)', () => {
35
+ // Arrange
36
+ terminal = createMockTerminal([
37
+ 'Processing...',
38
+ 'PRESS ESC TO INTERRUPT',
39
+ 'Working...',
40
+ ]);
41
+ // Act
42
+ const state = detector.detectState(terminal, 'idle');
43
+ // Assert
44
+ expect(state).toBe('busy');
45
+ });
46
+ it('should detect busy state for "Esc to interrupt" pattern', () => {
47
+ // Arrange
48
+ terminal = createMockTerminal(['Processing...', 'Esc to interrupt']);
49
+ // Act
50
+ const state = detector.detectState(terminal, 'idle');
51
+ // Assert
52
+ expect(state).toBe('busy');
53
+ });
54
+ it('should detect idle state when no patterns match', () => {
55
+ // Arrange
56
+ terminal = createMockTerminal(['Normal output', 'Some message', 'Ready']);
57
+ // Act
58
+ const state = detector.detectState(terminal, 'idle');
59
+ // Assert
60
+ expect(state).toBe('idle');
61
+ });
62
+ it('should prioritize waiting_input over busy when both patterns present', () => {
63
+ // Arrange
64
+ terminal = createMockTerminal([
65
+ 'esc to interrupt',
66
+ '△ Permission required',
67
+ ]);
68
+ // Act
69
+ const state = detector.detectState(terminal, 'idle');
70
+ // Assert
71
+ expect(state).toBe('waiting_input');
72
+ });
73
+ it('should detect waiting_input with full permission prompt', () => {
74
+ // Arrange
75
+ terminal = createMockTerminal([
76
+ 'opencode v0.1.0',
77
+ '',
78
+ '△ Permission required',
79
+ 'The AI wants to execute a shell command',
80
+ '',
81
+ 'Press Enter to allow, Esc to deny',
82
+ ]);
83
+ // Act
84
+ const state = detector.detectState(terminal, 'idle');
85
+ // Assert
86
+ expect(state).toBe('waiting_input');
87
+ });
88
+ });
@@ -4,7 +4,7 @@ import { GitStatus } from '../utils/gitStatus.js';
4
4
  import { Mutex, SessionStateData } from '../utils/mutex.js';
5
5
  export type Terminal = InstanceType<typeof pkg.Terminal>;
6
6
  export type SessionState = 'idle' | 'busy' | 'waiting_input' | 'pending_auto_approval';
7
- export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor' | 'github-copilot' | 'cline';
7
+ export type StateDetectionStrategy = 'claude' | 'gemini' | 'codex' | 'cursor' | 'github-copilot' | 'cline' | 'opencode';
8
8
  export interface Worktree {
9
9
  path: string;
10
10
  branch?: string;
@@ -0,0 +1,36 @@
1
+ import type { Terminal } from '@xterm/headless';
2
+ export interface ScreenState {
3
+ timestamp: string;
4
+ bufferType: 'normal' | 'alternate';
5
+ cursorX: number;
6
+ cursorY: number;
7
+ cols: number;
8
+ rows: number;
9
+ lines: string[];
10
+ }
11
+ /**
12
+ * Captures the current screen state of the terminal.
13
+ * This correctly handles both normal and alternate screen buffers,
14
+ * capturing only the visible screen content (not scrollback history).
15
+ *
16
+ * @param terminal - The xterm terminal instance
17
+ * @returns The current screen state including all visible lines
18
+ */
19
+ export declare function captureScreen(terminal: Terminal): ScreenState;
20
+ /**
21
+ * Formats the screen state into a human-readable string.
22
+ *
23
+ * @param state - The screen state to format
24
+ * @returns Formatted string representation of the screen state
25
+ */
26
+ export declare function formatScreenState(state: ScreenState): string;
27
+ /**
28
+ * Gets the terminal content as a single string.
29
+ * This is a convenience function that captures the screen and returns
30
+ * just the lines joined together.
31
+ *
32
+ * @param terminal - The xterm terminal instance
33
+ * @param maxLines - Optional maximum number of lines to return (from the bottom)
34
+ * @returns The terminal content as a string
35
+ */
36
+ export declare function getTerminalScreenContent(terminal: Terminal, maxLines?: number): string;
@@ -0,0 +1,70 @@
1
+ function lineToString(line, cols) {
2
+ if (!line) {
3
+ return '';
4
+ }
5
+ return line.translateToString(true, 0, cols);
6
+ }
7
+ /**
8
+ * Captures the current screen state of the terminal.
9
+ * This correctly handles both normal and alternate screen buffers,
10
+ * capturing only the visible screen content (not scrollback history).
11
+ *
12
+ * @param terminal - The xterm terminal instance
13
+ * @returns The current screen state including all visible lines
14
+ */
15
+ export function captureScreen(terminal) {
16
+ const buffer = terminal.buffer.active;
17
+ const lines = [];
18
+ // Capture only the visible screen area (terminal.rows lines)
19
+ // This works correctly for both normal and alternate screen buffers
20
+ for (let y = 0; y < terminal.rows; y++) {
21
+ const line = buffer.getLine(y);
22
+ lines.push(lineToString(line, terminal.cols));
23
+ }
24
+ return {
25
+ timestamp: new Date().toISOString(),
26
+ bufferType: buffer.type,
27
+ cursorX: buffer.cursorX,
28
+ cursorY: buffer.cursorY,
29
+ cols: terminal.cols,
30
+ rows: terminal.rows,
31
+ lines,
32
+ };
33
+ }
34
+ /**
35
+ * Formats the screen state into a human-readable string.
36
+ *
37
+ * @param state - The screen state to format
38
+ * @returns Formatted string representation of the screen state
39
+ */
40
+ export function formatScreenState(state) {
41
+ const separator = '-'.repeat(state.cols);
42
+ let output = `\n[${state.timestamp}] Buffer: ${state.bufferType} | Cursor: (${state.cursorX}, ${state.cursorY}) | Size: ${state.cols}x${state.rows}\n${separator}\n`;
43
+ for (const line of state.lines) {
44
+ output += line + '\n';
45
+ }
46
+ output += `${separator}\n\n`;
47
+ return output;
48
+ }
49
+ /**
50
+ * Gets the terminal content as a single string.
51
+ * This is a convenience function that captures the screen and returns
52
+ * just the lines joined together.
53
+ *
54
+ * @param terminal - The xterm terminal instance
55
+ * @param maxLines - Optional maximum number of lines to return (from the bottom)
56
+ * @returns The terminal content as a string
57
+ */
58
+ export function getTerminalScreenContent(terminal, maxLines) {
59
+ const state = captureScreen(terminal);
60
+ let lines = state.lines;
61
+ // Trim empty lines from the bottom
62
+ while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') {
63
+ lines.pop();
64
+ }
65
+ // If maxLines is specified, take only the last maxLines
66
+ if (maxLines !== undefined && lines.length > maxLines) {
67
+ lines = lines.slice(-maxLines);
68
+ }
69
+ return lines.join('\n');
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccmanager",
3
- "version": "3.2.10",
3
+ "version": "3.3.1",
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.10",
45
- "@kodaikabasawa/ccmanager-darwin-x64": "3.2.10",
46
- "@kodaikabasawa/ccmanager-linux-arm64": "3.2.10",
47
- "@kodaikabasawa/ccmanager-linux-x64": "3.2.10",
48
- "@kodaikabasawa/ccmanager-win32-x64": "3.2.10"
44
+ "@kodaikabasawa/ccmanager-darwin-arm64": "3.3.1",
45
+ "@kodaikabasawa/ccmanager-darwin-x64": "3.3.1",
46
+ "@kodaikabasawa/ccmanager-linux-arm64": "3.3.1",
47
+ "@kodaikabasawa/ccmanager-linux-x64": "3.3.1",
48
+ "@kodaikabasawa/ccmanager-win32-x64": "3.3.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/js": "^9.28.0",
@@ -68,14 +68,14 @@
68
68
  },
69
69
  "prettier": "@vdemedes/prettier-config",
70
70
  "dependencies": {
71
- "@xterm/headless": "^5.5.0",
71
+ "@xterm/headless": "^6.0.0",
72
72
  "effect": "^3.18.2",
73
73
  "ink": "5.2.1",
74
74
  "ink-select-input": "^6.0.0",
75
75
  "ink-text-input": "^6.0.0",
76
76
  "meow": "^11.0.0",
77
77
  "react": "18.3.1",
78
- "react-devtools-core": "^4.19.1",
78
+ "react-devtools-core": "^7.0.1",
79
79
  "react-dom": "18.3.1",
80
80
  "strip-ansi": "^7.1.0"
81
81
  }