ccmanager 3.2.3 → 3.2.5

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.
@@ -68,6 +68,21 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
68
68
  // These sequences leak as literal text when replaying buffered output
69
69
  return input.replace(/\x1B\](?:10|11);[^\x07\x1B]*(?:\x07|\x1B\\)/g, '');
70
70
  };
71
+ const normalizeLineEndings = (input) => {
72
+ // Ensure LF moves to column 0 to prevent cursor drift when ONLCR is disabled.
73
+ let normalized = '';
74
+ for (let i = 0; i < input.length; i++) {
75
+ const char = input[i];
76
+ if (char === '\n') {
77
+ const prev = i > 0 ? input[i - 1] : '';
78
+ if (prev !== '\r') {
79
+ normalized += '\r';
80
+ }
81
+ }
82
+ normalized += char;
83
+ }
84
+ return normalized;
85
+ };
71
86
  useEffect(() => {
72
87
  if (!stdout)
73
88
  return;
@@ -79,6 +94,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
79
94
  stdout.write('\x1b[>4m'); // Disable xterm modifyOtherKeys extensions
80
95
  stdout.write('\x1b[?1004l'); // Disable focus reporting
81
96
  stdout.write('\x1b[?2004l'); // Disable bracketed paste (can interfere with shortcuts)
97
+ stdout.write('\x1b[?7h'); // Re-enable auto-wrap
82
98
  };
83
99
  const sanitizeReplayBuffer = (input) => {
84
100
  // Remove terminal mode toggles emitted by Codex so replay doesn't re-enable them
@@ -91,6 +107,8 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
91
107
  };
92
108
  // Reset modes immediately on entry in case a previous session left them on
93
109
  resetTerminalInputModes();
110
+ // Prevent line wrapping from drifting redraws in TUIs that rely on cursor-up clears.
111
+ stdout.write('\x1b[?7l');
94
112
  // Clear screen when entering session
95
113
  stdout.write('\x1B[2J\x1B[H');
96
114
  // Handle session restoration
@@ -101,7 +119,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
101
119
  const buffer = restoredSession.outputHistory[i];
102
120
  if (!buffer)
103
121
  continue;
104
- const str = sanitizeReplayBuffer(buffer.toString('utf8'));
122
+ const str = normalizeLineEndings(sanitizeReplayBuffer(buffer.toString('utf8')));
105
123
  // Skip clear screen sequences at the beginning
106
124
  if (i === 0 && (str.includes('\x1B[2J') || str.includes('\x1B[H'))) {
107
125
  // Skip this buffer or remove the clear sequence
@@ -109,7 +127,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
109
127
  .replace(/\x1B\[2J/g, '')
110
128
  .replace(/\x1B\[H/g, '');
111
129
  if (cleaned.length > 0) {
112
- stdout.write(cleaned);
130
+ stdout.write(normalizeLineEndings(cleaned));
113
131
  }
114
132
  }
115
133
  else {
@@ -144,7 +162,7 @@ const Session = ({ session, sessionManager, onReturnToMenu, }) => {
144
162
  const handleSessionData = (activeSession, data) => {
145
163
  // Only handle data for our session
146
164
  if (activeSession.id === session.id && !isExiting) {
147
- stdout.write(data);
165
+ stdout.write(normalizeLineEndings(data));
148
166
  }
149
167
  };
150
168
  const handleSessionExit = (exitedSession) => {
@@ -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([
@@ -58,6 +58,37 @@ class BunTerminal {
58
58
  writable: true,
59
59
  value: null
60
60
  });
61
+ Object.defineProperty(this, "_terminal", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: null
66
+ });
67
+ Object.defineProperty(this, "_decoder", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: new globalThis.TextDecoder('utf-8')
72
+ });
73
+ // Buffering to combine fragmented data chunks from the same event loop
74
+ Object.defineProperty(this, "_dataBuffer", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: ''
79
+ });
80
+ Object.defineProperty(this, "_flushTimer", {
81
+ enumerable: true,
82
+ configurable: true,
83
+ writable: true,
84
+ value: null
85
+ });
86
+ Object.defineProperty(this, "_syncOutputMode", {
87
+ enumerable: true,
88
+ configurable: true,
89
+ writable: true,
90
+ value: false
91
+ });
61
92
  Object.defineProperty(this, "onData", {
62
93
  enumerable: true,
63
94
  configurable: true,
@@ -93,29 +124,61 @@ class BunTerminal {
93
124
  this._cols = options.cols ?? 80;
94
125
  this._rows = options.rows ?? 24;
95
126
  this._process = file;
96
- // Spawn the process with Bun's built-in terminal support
127
+ // Build environment with TERM variable (like node-pty does with 'name' option)
128
+ const env = {};
129
+ if (options.env) {
130
+ for (const [key, value] of Object.entries(options.env)) {
131
+ if (value !== undefined) {
132
+ env[key] = value;
133
+ }
134
+ }
135
+ }
136
+ // Set TERM from the 'name' option (like node-pty does)
137
+ env['TERM'] = options.name || 'xterm-256color';
138
+ // Create a standalone Bun.Terminal instance for better control over termios settings
139
+ this._terminal = new Bun.Terminal({
140
+ cols: this._cols,
141
+ rows: this._rows,
142
+ data: (_terminal, data) => {
143
+ if (this._closed)
144
+ return;
145
+ const str = typeof data === 'string'
146
+ ? data
147
+ : this._decoder.decode(data, { stream: true });
148
+ this._dataBuffer += str;
149
+ this._processBuffer();
150
+ },
151
+ });
152
+ // Match node-pty behavior by starting in raw mode (no canonical input/echo),
153
+ // while keeping Bun's output processing defaults intact.
154
+ this._terminal.setRawMode(true);
155
+ // Disable ONLCR in the PTY output flags to avoid double CRLF translation
156
+ // when forwarding PTY output to the real stdout TTY.
157
+ const ONLCR_FLAG = 0x0002;
158
+ this._terminal.outputFlags = this._terminal.outputFlags & ~ONLCR_FLAG;
159
+ // Keep Bun defaults for other termios flags.
160
+ // Spawn the process with the pre-configured terminal
97
161
  this._subprocess = Bun.spawn([file, ...args], {
98
162
  cwd: options.cwd ?? process.cwd(),
99
- env: options.env,
100
- terminal: {
101
- cols: this._cols,
102
- rows: this._rows,
103
- data: (_terminal, data) => {
104
- if (!this._closed) {
105
- const str = typeof data === 'string'
106
- ? data
107
- : Buffer.from(data).toString('utf8');
108
- for (const listener of this._dataListeners) {
109
- listener(str);
110
- }
111
- }
112
- },
113
- },
163
+ env,
164
+ terminal: this._terminal,
114
165
  });
115
166
  this._pid = this._subprocess.pid;
116
167
  // Handle process exit
117
168
  this._subprocess.exited.then(exitCode => {
118
169
  if (!this._closed) {
170
+ this._closed = true;
171
+ // Clear any pending flush timer
172
+ if (this._flushTimer) {
173
+ clearTimeout(this._flushTimer);
174
+ this._flushTimer = null;
175
+ }
176
+ // Flush any remaining buffered data before exit
177
+ this._finalizeDecoder();
178
+ this._syncOutputMode = false;
179
+ // Temporarily unset _closed to allow final flush
180
+ this._closed = false;
181
+ this._flushBuffer();
119
182
  this._closed = true;
120
183
  for (const listener of this._exitListeners) {
121
184
  listener({ exitCode });
@@ -123,6 +186,86 @@ class BunTerminal {
123
186
  }
124
187
  });
125
188
  }
189
+ _emitData(payload) {
190
+ if (payload.length === 0 || this._closed) {
191
+ return;
192
+ }
193
+ for (const listener of this._dataListeners) {
194
+ listener(payload);
195
+ }
196
+ }
197
+ _flushBuffer() {
198
+ if (this._dataBuffer.length === 0 || this._closed) {
199
+ return;
200
+ }
201
+ const bufferedData = this._dataBuffer;
202
+ this._dataBuffer = '';
203
+ this._emitData(bufferedData);
204
+ }
205
+ _finalizeDecoder() {
206
+ const remaining = this._decoder.decode(new Uint8Array(), { stream: false });
207
+ if (remaining.length > 0) {
208
+ this._dataBuffer += remaining;
209
+ }
210
+ }
211
+ _processBuffer() {
212
+ if (this._closed) {
213
+ return;
214
+ }
215
+ let madeProgress = true;
216
+ while (madeProgress) {
217
+ madeProgress = false;
218
+ if (this._syncOutputMode) {
219
+ const endIndex = this._dataBuffer.indexOf(BunTerminal.SYNC_OUTPUT_END);
220
+ if (endIndex !== -1) {
221
+ const endOffset = endIndex + BunTerminal.SYNC_OUTPUT_END.length;
222
+ const frame = this._dataBuffer.slice(0, endOffset);
223
+ this._dataBuffer = this._dataBuffer.slice(endOffset);
224
+ this._syncOutputMode = false;
225
+ if (this._flushTimer) {
226
+ clearTimeout(this._flushTimer);
227
+ this._flushTimer = null;
228
+ }
229
+ this._emitData(frame);
230
+ madeProgress = true;
231
+ continue;
232
+ }
233
+ if (this._flushTimer) {
234
+ clearTimeout(this._flushTimer);
235
+ }
236
+ this._flushTimer = setTimeout(() => {
237
+ this._flushTimer = null;
238
+ this._syncOutputMode = false;
239
+ this._flushBuffer();
240
+ }, BunTerminal.SYNC_TIMEOUT_MS);
241
+ return;
242
+ }
243
+ const startIndex = this._dataBuffer.indexOf(BunTerminal.SYNC_OUTPUT_START);
244
+ if (startIndex !== -1) {
245
+ if (startIndex > 0) {
246
+ const leading = this._dataBuffer.slice(0, startIndex);
247
+ this._dataBuffer = this._dataBuffer.slice(startIndex);
248
+ this._emitData(leading);
249
+ madeProgress = true;
250
+ continue;
251
+ }
252
+ this._syncOutputMode = true;
253
+ if (this._flushTimer) {
254
+ clearTimeout(this._flushTimer);
255
+ this._flushTimer = null;
256
+ }
257
+ madeProgress = true;
258
+ continue;
259
+ }
260
+ if (this._flushTimer) {
261
+ clearTimeout(this._flushTimer);
262
+ }
263
+ this._flushTimer = setTimeout(() => {
264
+ this._flushTimer = null;
265
+ this._flushBuffer();
266
+ }, BunTerminal.FLUSH_DELAY_MS);
267
+ }
268
+ }
126
269
  get pid() {
127
270
  return this._pid;
128
271
  }
@@ -136,32 +279,69 @@ class BunTerminal {
136
279
  return this._process;
137
280
  }
138
281
  write(data) {
139
- if (this._closed || !this._subprocess?.terminal) {
282
+ if (this._closed || !this._terminal) {
140
283
  return;
141
284
  }
142
- this._subprocess.terminal.write(data);
285
+ this._terminal.write(data);
143
286
  }
144
287
  resize(columns, rows) {
145
- if (this._closed || !this._subprocess?.terminal) {
288
+ if (this._closed || !this._terminal) {
146
289
  return;
147
290
  }
148
291
  this._cols = columns;
149
292
  this._rows = rows;
150
- this._subprocess.terminal.resize(columns, rows);
293
+ this._terminal.resize(columns, rows);
151
294
  }
152
295
  kill(_signal) {
153
296
  if (this._closed) {
154
297
  return;
155
298
  }
156
299
  this._closed = true;
157
- if (this._subprocess?.terminal) {
158
- this._subprocess.terminal.close();
300
+ // Clear any pending flush timer
301
+ if (this._flushTimer) {
302
+ clearTimeout(this._flushTimer);
303
+ this._flushTimer = null;
304
+ }
305
+ // Flush any remaining buffered data
306
+ this._finalizeDecoder();
307
+ this._syncOutputMode = false;
308
+ // Temporarily unset _closed to allow final flush
309
+ this._closed = false;
310
+ this._flushBuffer();
311
+ this._closed = true;
312
+ if (this._terminal) {
313
+ this._terminal.close();
159
314
  }
160
315
  if (this._subprocess) {
161
316
  this._subprocess.kill();
162
317
  }
163
318
  }
164
319
  }
320
+ // Synchronized output escape sequences (used by Ink and other TUI frameworks)
321
+ Object.defineProperty(BunTerminal, "SYNC_OUTPUT_START", {
322
+ enumerable: true,
323
+ configurable: true,
324
+ writable: true,
325
+ value: '\x1b[?2026h'
326
+ });
327
+ Object.defineProperty(BunTerminal, "SYNC_OUTPUT_END", {
328
+ enumerable: true,
329
+ configurable: true,
330
+ writable: true,
331
+ value: '\x1b[?2026l'
332
+ });
333
+ Object.defineProperty(BunTerminal, "FLUSH_DELAY_MS", {
334
+ enumerable: true,
335
+ configurable: true,
336
+ writable: true,
337
+ value: 8
338
+ }); // ~2 frames at 60fps for batching
339
+ Object.defineProperty(BunTerminal, "SYNC_TIMEOUT_MS", {
340
+ enumerable: true,
341
+ configurable: true,
342
+ writable: true,
343
+ value: 100
344
+ }); // Timeout for sync mode
165
345
  /**
166
346
  * Spawn a new PTY process using Bun's built-in Terminal API.
167
347
  *
@@ -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.3",
3
+ "version": "3.2.5",
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.3",
45
- "@kodaikabasawa/ccmanager-darwin-x64": "3.2.3",
46
- "@kodaikabasawa/ccmanager-linux-arm64": "3.2.3",
47
- "@kodaikabasawa/ccmanager-linux-x64": "3.2.3",
48
- "@kodaikabasawa/ccmanager-win32-x64": "3.2.3"
44
+ "@kodaikabasawa/ccmanager-darwin-arm64": "3.2.5",
45
+ "@kodaikabasawa/ccmanager-darwin-x64": "3.2.5",
46
+ "@kodaikabasawa/ccmanager-linux-arm64": "3.2.5",
47
+ "@kodaikabasawa/ccmanager-linux-x64": "3.2.5",
48
+ "@kodaikabasawa/ccmanager-win32-x64": "3.2.5"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/js": "^9.28.0",