ccmanager 3.5.0 → 3.5.2

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.
Files changed (82) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/components/App.d.ts +1 -0
  3. package/dist/components/App.js +17 -39
  4. package/dist/components/App.test.js +12 -44
  5. package/dist/components/Configuration.js +10 -15
  6. package/dist/components/ConfigureCommand.js +18 -106
  7. package/dist/components/ConfigureCustomCommand.js +2 -17
  8. package/dist/components/ConfigureOther.js +5 -23
  9. package/dist/components/ConfigureOther.test.js +7 -31
  10. package/dist/components/ConfigureShortcuts.js +4 -31
  11. package/dist/components/ConfigureStatusHooks.js +5 -44
  12. package/dist/components/ConfigureStatusHooks.test.js +7 -31
  13. package/dist/components/ConfigureTimeout.js +2 -14
  14. package/dist/components/ConfigureWorktree.js +4 -47
  15. package/dist/components/ConfigureWorktreeHooks.js +5 -37
  16. package/dist/components/ConfigureWorktreeHooks.test.js +8 -33
  17. package/dist/components/Confirmation.js +9 -21
  18. package/dist/components/CustomCommandSummary.js +2 -5
  19. package/dist/components/DeleteConfirmation.js +10 -47
  20. package/dist/components/DeleteWorktree.js +14 -42
  21. package/dist/components/DeleteWorktree.test.js +6 -6
  22. package/dist/components/LoadingSpinner.js +3 -6
  23. package/dist/components/LoadingSpinner.test.js +22 -22
  24. package/dist/components/Menu.d.ts +1 -0
  25. package/dist/components/Menu.js +10 -42
  26. package/dist/components/Menu.recent-projects.test.js +8 -8
  27. package/dist/components/Menu.test.js +10 -10
  28. package/dist/components/MergeWorktree.js +16 -88
  29. package/dist/components/MergeWorktree.test.js +5 -5
  30. package/dist/components/NewWorktree.js +25 -105
  31. package/dist/components/NewWorktree.test.js +8 -8
  32. package/dist/components/PresetSelector.js +3 -9
  33. package/dist/components/ProjectList.js +9 -38
  34. package/dist/components/ProjectList.recent-projects.test.js +7 -7
  35. package/dist/components/ProjectList.test.js +37 -37
  36. package/dist/components/RemoteBranchSelector.js +2 -21
  37. package/dist/components/RemoteBranchSelector.test.js +8 -8
  38. package/dist/components/TextInputWrapper.d.ts +5 -0
  39. package/dist/components/TextInputWrapper.js +138 -11
  40. package/dist/constants/statusIcons.d.ts +2 -1
  41. package/dist/constants/statusIcons.js +13 -4
  42. package/dist/constants/statusIcons.test.js +41 -11
  43. package/dist/contexts/ConfigEditorContext.d.ts +1 -1
  44. package/dist/contexts/ConfigEditorContext.js +3 -2
  45. package/dist/services/autoApprovalVerifier.js +1 -8
  46. package/dist/services/bunTerminal.js +41 -136
  47. package/dist/services/config/configEditor.js +2 -12
  48. package/dist/services/config/globalConfigManager.js +4 -24
  49. package/dist/services/config/projectConfigManager.js +3 -18
  50. package/dist/services/globalSessionOrchestrator.js +3 -12
  51. package/dist/services/globalSessionOrchestrator.test.js +1 -8
  52. package/dist/services/projectManager.js +13 -68
  53. package/dist/services/sessionManager.d.ts +1 -1
  54. package/dist/services/sessionManager.effect.test.js +9 -37
  55. package/dist/services/sessionManager.js +12 -28
  56. package/dist/services/sessionManager.test.js +48 -40
  57. package/dist/services/shortcutManager.js +7 -13
  58. package/dist/services/stateDetector/base.d.ts +1 -1
  59. package/dist/services/stateDetector/claude.d.ts +1 -1
  60. package/dist/services/stateDetector/claude.js +11 -4
  61. package/dist/services/stateDetector/claude.test.js +47 -24
  62. package/dist/services/stateDetector/cline.d.ts +1 -1
  63. package/dist/services/stateDetector/cline.js +1 -1
  64. package/dist/services/stateDetector/codex.d.ts +1 -1
  65. package/dist/services/stateDetector/codex.js +1 -1
  66. package/dist/services/stateDetector/cursor.d.ts +1 -1
  67. package/dist/services/stateDetector/cursor.js +1 -1
  68. package/dist/services/stateDetector/gemini.d.ts +1 -1
  69. package/dist/services/stateDetector/gemini.js +1 -1
  70. package/dist/services/stateDetector/github-copilot.d.ts +1 -1
  71. package/dist/services/stateDetector/github-copilot.js +1 -1
  72. package/dist/services/stateDetector/opencode.d.ts +1 -1
  73. package/dist/services/stateDetector/opencode.js +1 -1
  74. package/dist/services/stateDetector/types.d.ts +1 -1
  75. package/dist/services/worktreeConfigManager.js +3 -8
  76. package/dist/services/worktreeService.js +2 -12
  77. package/dist/types/index.js +4 -12
  78. package/dist/utils/logger.js +12 -33
  79. package/dist/utils/mutex.d.ts +1 -1
  80. package/dist/utils/mutex.js +4 -19
  81. package/dist/utils/worktreeUtils.js +4 -4
  82. package/package.json +12 -12
@@ -63,43 +63,15 @@ vi.mock('./worktreeService.js', () => ({
63
63
  }));
64
64
  // Create a mock IPty class
65
65
  class MockPty extends EventEmitter {
66
- constructor() {
67
- super(...arguments);
68
- Object.defineProperty(this, "kill", {
69
- enumerable: true,
70
- configurable: true,
71
- writable: true,
72
- value: vi.fn()
73
- });
74
- Object.defineProperty(this, "resize", {
75
- enumerable: true,
76
- configurable: true,
77
- writable: true,
78
- value: vi.fn()
79
- });
80
- Object.defineProperty(this, "write", {
81
- enumerable: true,
82
- configurable: true,
83
- writable: true,
84
- value: vi.fn()
85
- });
86
- Object.defineProperty(this, "onData", {
87
- enumerable: true,
88
- configurable: true,
89
- writable: true,
90
- value: vi.fn((callback) => {
91
- this.on('data', callback);
92
- })
93
- });
94
- Object.defineProperty(this, "onExit", {
95
- enumerable: true,
96
- configurable: true,
97
- writable: true,
98
- value: vi.fn((callback) => {
99
- this.on('exit', callback);
100
- })
101
- });
102
- }
66
+ kill = vi.fn();
67
+ resize = vi.fn();
68
+ write = vi.fn();
69
+ onData = vi.fn((callback) => {
70
+ this.on('data', callback);
71
+ });
72
+ onExit = vi.fn((callback) => {
73
+ this.on('exit', callback);
74
+ });
103
75
  }
104
76
  describe('SessionManager', () => {
105
77
  let sessionManager;
@@ -725,10 +697,10 @@ describe('SessionManager', () => {
725
697
  describe('static methods', () => {
726
698
  describe('getSessionCounts', () => {
727
699
  // Helper to create mock session with stateMutex
728
- const createMockSession = (id, state, hasBackgroundTask = false) => ({
700
+ const createMockSession = (id, state, backgroundTaskCount = 0) => ({
729
701
  id,
730
702
  stateMutex: {
731
- getSnapshot: () => ({ state, hasBackgroundTask }),
703
+ getSnapshot: () => ({ state, backgroundTaskCount }),
732
704
  },
733
705
  });
734
706
  it('should count sessions by state', () => {
@@ -764,6 +736,16 @@ describe('SessionManager', () => {
764
736
  expect(counts.waiting_input).toBe(0);
765
737
  expect(counts.total).toBe(3);
766
738
  });
739
+ it('should sum background task counts across sessions', () => {
740
+ const sessions = [
741
+ createMockSession('1', 'idle', 0),
742
+ createMockSession('2', 'busy', 2),
743
+ createMockSession('3', 'busy', 3),
744
+ createMockSession('4', 'waiting_input', 1),
745
+ ];
746
+ const counts = SessionManager.getSessionCounts(sessions);
747
+ expect(counts.backgroundTasks).toBe(6);
748
+ });
767
749
  });
768
750
  describe('formatSessionCounts', () => {
769
751
  it('should format counts with all states', () => {
@@ -814,7 +796,7 @@ describe('SessionManager', () => {
814
796
  const formatted = SessionManager.formatSessionCounts(counts);
815
797
  expect(formatted).toBe('');
816
798
  });
817
- it('should append [BG] tag when background tasks exist', () => {
799
+ it('should append [BG] tag when backgroundTasks is 1', () => {
818
800
  const counts = {
819
801
  idle: 1,
820
802
  busy: 1,
@@ -827,6 +809,32 @@ describe('SessionManager', () => {
827
809
  expect(formatted).toContain('[BG]');
828
810
  expect(formatted).toBe(' (1 Idle / 1 Busy \x1b[2m[BG]\x1b[0m)');
829
811
  });
812
+ it('should append [BG:N] tag when backgroundTasks is 2+', () => {
813
+ const counts = {
814
+ idle: 1,
815
+ busy: 1,
816
+ waiting_input: 0,
817
+ pending_auto_approval: 0,
818
+ total: 2,
819
+ backgroundTasks: 5,
820
+ };
821
+ const formatted = SessionManager.formatSessionCounts(counts);
822
+ expect(formatted).toContain('[BG:5]');
823
+ expect(formatted).toBe(' (1 Idle / 1 Busy \x1b[2m[BG:5]\x1b[0m)');
824
+ });
825
+ it('should not append BG tag when backgroundTasks is 0', () => {
826
+ const counts = {
827
+ idle: 1,
828
+ busy: 1,
829
+ waiting_input: 0,
830
+ pending_auto_approval: 0,
831
+ total: 2,
832
+ backgroundTasks: 0,
833
+ };
834
+ const formatted = SessionManager.formatSessionCounts(counts);
835
+ expect(formatted).not.toContain('[BG');
836
+ expect(formatted).toBe(' (1 Idle / 1 Busy)');
837
+ });
830
838
  });
831
839
  });
832
840
  });
@@ -1,18 +1,12 @@
1
1
  import { configReader } from './config/configReader.js';
2
2
  export class ShortcutManager {
3
- constructor() {
4
- Object.defineProperty(this, "reservedKeys", {
5
- enumerable: true,
6
- configurable: true,
7
- writable: true,
8
- value: [
9
- { ctrl: true, key: 'c' },
10
- { ctrl: true, key: 'd' },
11
- { key: 'escape' }, // Ctrl+[ is equivalent to Escape
12
- { ctrl: true, key: '[' },
13
- ]
14
- });
15
- }
3
+ reservedKeys = [
4
+ { ctrl: true, key: 'c' },
5
+ { ctrl: true, key: 'd' },
6
+ { key: 'escape' }, // Ctrl+[ is equivalent to Escape
7
+ { ctrl: true, key: '[' },
8
+ ];
9
+ constructor() { }
16
10
  validateShortcut(shortcut) {
17
11
  if (!shortcut || typeof shortcut !== 'object') {
18
12
  return null;
@@ -4,5 +4,5 @@ export declare abstract class BaseStateDetector implements StateDetector {
4
4
  abstract detectState(terminal: Terminal, currentState: SessionState): SessionState;
5
5
  protected getTerminalLines(terminal: Terminal, maxLines?: number): string[];
6
6
  protected getTerminalContent(terminal: Terminal, maxLines?: number): string;
7
- abstract detectBackgroundTask(terminal: Terminal): boolean;
7
+ abstract detectBackgroundTask(terminal: Terminal): number;
8
8
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class ClaudeStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, currentState: SessionState): SessionState;
5
- detectBackgroundTask(terminal: Terminal): boolean;
5
+ detectBackgroundTask(terminal: Terminal): number;
6
6
  }
@@ -27,9 +27,16 @@ export class ClaudeStateDetector extends BaseStateDetector {
27
27
  detectBackgroundTask(terminal) {
28
28
  const lines = this.getTerminalLines(terminal, 3);
29
29
  const content = lines.join('\n').toLowerCase();
30
- // Detect background task patterns:
31
- // - "N background task(s)" in status bar
32
- // - "(running)" in status bar for active background commands
33
- return content.includes('background task') || content.includes('(running)');
30
+ // Check for "N background task(s)" pattern first (content already lowercased)
31
+ const countMatch = content.match(/(\d+)\s+background\s+task/);
32
+ if (countMatch?.[1]) {
33
+ return parseInt(countMatch[1], 10);
34
+ }
35
+ // Check for "(running)" pattern - indicates at least 1 background task
36
+ if (content.includes('(running)')) {
37
+ return 1;
38
+ }
39
+ // No background task detected
40
+ return 0;
34
41
  }
35
42
  }
@@ -223,7 +223,7 @@ describe('ClaudeStateDetector', () => {
223
223
  });
224
224
  });
225
225
  describe('detectBackgroundTask', () => {
226
- it('should detect background task when pattern is in last 3 lines (status bar)', () => {
226
+ it('should return count 1 when "1 background task" is in status bar', () => {
227
227
  // Arrange
228
228
  terminal = createMockTerminal([
229
229
  'Previous conversation content',
@@ -232,11 +232,11 @@ describe('ClaudeStateDetector', () => {
232
232
  '1 background task | api-call',
233
233
  ]);
234
234
  // Act
235
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
235
+ const count = detector.detectBackgroundTask(terminal);
236
236
  // Assert
237
- expect(hasBackgroundTask).toBe(true);
237
+ expect(count).toBe(1);
238
238
  });
239
- it('should detect background task with plural "background tasks"', () => {
239
+ it('should return count 2 when "2 background tasks" is in status bar', () => {
240
240
  // Arrange
241
241
  terminal = createMockTerminal([
242
242
  'Some output',
@@ -244,11 +244,23 @@ describe('ClaudeStateDetector', () => {
244
244
  '2 background tasks running',
245
245
  ]);
246
246
  // Act
247
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
247
+ const count = detector.detectBackgroundTask(terminal);
248
248
  // Assert
249
- expect(hasBackgroundTask).toBe(true);
249
+ expect(count).toBe(2);
250
250
  });
251
- it('should detect background task case-insensitively', () => {
251
+ it('should return count 3 when "3 background tasks" is in status bar', () => {
252
+ // Arrange
253
+ terminal = createMockTerminal([
254
+ 'Some output',
255
+ 'More output',
256
+ '3 background tasks | build, test, lint',
257
+ ]);
258
+ // Act
259
+ const count = detector.detectBackgroundTask(terminal);
260
+ // Assert
261
+ expect(count).toBe(3);
262
+ });
263
+ it('should detect background task count case-insensitively', () => {
252
264
  // Arrange
253
265
  terminal = createMockTerminal([
254
266
  'Output line 1',
@@ -256,11 +268,11 @@ describe('ClaudeStateDetector', () => {
256
268
  '1 BACKGROUND TASK running',
257
269
  ]);
258
270
  // Act
259
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
271
+ const count = detector.detectBackgroundTask(terminal);
260
272
  // Assert
261
- expect(hasBackgroundTask).toBe(true);
273
+ expect(count).toBe(1);
262
274
  });
263
- it('should return false when no background task pattern in last 3 lines', () => {
275
+ it('should return 0 when no background task pattern in last 3 lines', () => {
264
276
  // Arrange
265
277
  terminal = createMockTerminal([
266
278
  'Command completed successfully',
@@ -268,9 +280,9 @@ describe('ClaudeStateDetector', () => {
268
280
  '> ',
269
281
  ]);
270
282
  // Act
271
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
283
+ const count = detector.detectBackgroundTask(terminal);
272
284
  // Assert
273
- expect(hasBackgroundTask).toBe(false);
285
+ expect(count).toBe(0);
274
286
  });
275
287
  it('should not detect background task when pattern is in conversation content (not status bar)', () => {
276
288
  // Arrange - "background task" mentioned earlier in conversation, but not in last 3 lines
@@ -283,27 +295,27 @@ describe('ClaudeStateDetector', () => {
283
295
  'Ready',
284
296
  ]);
285
297
  // Act
286
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
298
+ const count = detector.detectBackgroundTask(terminal);
287
299
  // Assert - should only check last 3 lines, not the conversation content
288
- expect(hasBackgroundTask).toBe(false);
300
+ expect(count).toBe(0);
289
301
  });
290
- it('should handle empty terminal', () => {
302
+ it('should return 0 for empty terminal', () => {
291
303
  // Arrange
292
304
  terminal = createMockTerminal([]);
293
305
  // Act
294
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
306
+ const count = detector.detectBackgroundTask(terminal);
295
307
  // Assert
296
- expect(hasBackgroundTask).toBe(false);
308
+ expect(count).toBe(0);
297
309
  });
298
310
  it('should handle terminal with fewer than 3 lines', () => {
299
311
  // Arrange
300
312
  terminal = createMockTerminal(['1 background task']);
301
313
  // Act
302
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
314
+ const count = detector.detectBackgroundTask(terminal);
303
315
  // Assert
304
- expect(hasBackgroundTask).toBe(true);
316
+ expect(count).toBe(1);
305
317
  });
306
- it('should detect "(running)" status bar indicator', () => {
318
+ it('should return 1 when "(running)" status bar indicator is present', () => {
307
319
  // Arrange
308
320
  terminal = createMockTerminal([
309
321
  'Some conversation output',
@@ -311,17 +323,28 @@ describe('ClaudeStateDetector', () => {
311
323
  'bypass permissions on - uv run pytest tests/integration/e2e/tes... (running)',
312
324
  ]);
313
325
  // Act
314
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
326
+ const count = detector.detectBackgroundTask(terminal);
315
327
  // Assert
316
- expect(hasBackgroundTask).toBe(true);
328
+ expect(count).toBe(1);
317
329
  });
318
330
  it('should detect "(running)" case-insensitively', () => {
319
331
  // Arrange
320
332
  terminal = createMockTerminal(['Some output', 'command name (RUNNING)']);
321
333
  // Act
322
- const hasBackgroundTask = detector.detectBackgroundTask(terminal);
334
+ const count = detector.detectBackgroundTask(terminal);
335
+ // Assert
336
+ expect(count).toBe(1);
337
+ });
338
+ it('should prioritize count from "N background task" over "(running)"', () => {
339
+ // Arrange - both patterns present, count should be from explicit pattern
340
+ terminal = createMockTerminal([
341
+ 'Some output',
342
+ '3 background tasks | task1, task2 (running)',
343
+ ]);
344
+ // Act
345
+ const count = detector.detectBackgroundTask(terminal);
323
346
  // Assert
324
- expect(hasBackgroundTask).toBe(true);
347
+ expect(count).toBe(3);
325
348
  });
326
349
  });
327
350
  });
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class ClineStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -22,6 +22,6 @@ export class ClineStateDetector extends BaseStateDetector {
22
22
  return 'busy';
23
23
  }
24
24
  detectBackgroundTask(_terminal) {
25
- return false;
25
+ return 0;
26
26
  }
27
27
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class CodexStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -25,6 +25,6 @@ export class CodexStateDetector extends BaseStateDetector {
25
25
  return 'idle';
26
26
  }
27
27
  detectBackgroundTask(_terminal) {
28
- return false;
28
+ return 0;
29
29
  }
30
30
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class CursorStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -17,6 +17,6 @@ export class CursorStateDetector extends BaseStateDetector {
17
17
  return 'idle';
18
18
  }
19
19
  detectBackgroundTask(_terminal) {
20
- return false;
20
+ return 0;
21
21
  }
22
22
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class GeminiStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -26,6 +26,6 @@ export class GeminiStateDetector extends BaseStateDetector {
26
26
  return 'idle';
27
27
  }
28
28
  detectBackgroundTask(_terminal) {
29
- return false;
29
+ return 0;
30
30
  }
31
31
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class GitHubCopilotStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -19,6 +19,6 @@ export class GitHubCopilotStateDetector extends BaseStateDetector {
19
19
  return 'idle';
20
20
  }
21
21
  detectBackgroundTask(_terminal) {
22
- return false;
22
+ return 0;
23
23
  }
24
24
  }
@@ -2,5 +2,5 @@ import { SessionState, Terminal } from '../../types/index.js';
2
2
  import { BaseStateDetector } from './base.js';
3
3
  export declare class OpenCodeStateDetector extends BaseStateDetector {
4
4
  detectState(terminal: Terminal, _currentState: SessionState): SessionState;
5
- detectBackgroundTask(_terminal: Terminal): boolean;
5
+ detectBackgroundTask(_terminal: Terminal): number;
6
6
  }
@@ -15,6 +15,6 @@ export class OpenCodeStateDetector extends BaseStateDetector {
15
15
  return 'idle';
16
16
  }
17
17
  detectBackgroundTask(_terminal) {
18
- return false;
18
+ return 0;
19
19
  }
20
20
  }
@@ -1,5 +1,5 @@
1
1
  import { SessionState, Terminal } from '../../types/index.js';
2
2
  export interface StateDetector {
3
3
  detectState(terminal: Terminal, currentState: SessionState): SessionState;
4
- detectBackgroundTask(terminal: Terminal): boolean;
4
+ detectBackgroundTask(terminal: Terminal): number;
5
5
  }
@@ -1,13 +1,8 @@
1
1
  import { isWorktreeConfigEnabled } from '../utils/worktreeConfig.js';
2
2
  class WorktreeConfigManager {
3
- constructor() {
4
- Object.defineProperty(this, "isExtensionAvailable", {
5
- enumerable: true,
6
- configurable: true,
7
- writable: true,
8
- value: null
9
- });
10
- }
3
+ static instance;
4
+ isExtensionAvailable = null;
5
+ constructor() { }
11
6
  static getInstance() {
12
7
  if (!WorktreeConfigManager.instance) {
13
8
  WorktreeConfigManager.instance = new WorktreeConfigManager();
@@ -54,19 +54,9 @@ export function getWorktreeLastOpenedTime(worktreePath) {
54
54
  * ```
55
55
  */
56
56
  export class WorktreeService {
57
+ rootPath;
58
+ gitRootPath;
57
59
  constructor(rootPath) {
58
- Object.defineProperty(this, "rootPath", {
59
- enumerable: true,
60
- configurable: true,
61
- writable: true,
62
- value: void 0
63
- });
64
- Object.defineProperty(this, "gitRootPath", {
65
- enumerable: true,
66
- configurable: true,
67
- writable: true,
68
- value: void 0
69
- });
70
60
  this.rootPath = path.resolve(rootPath || process.cwd());
71
61
  // Get the actual git repository root for worktree operations
72
62
  this.gitRootPath = this.getGitRepositoryRoot();
@@ -3,22 +3,14 @@ export const DEFAULT_SHORTCUTS = {
3
3
  cancel: { key: 'escape' },
4
4
  };
5
5
  export class AmbiguousBranchError extends Error {
6
+ branchName;
7
+ matches;
6
8
  constructor(branchName, matches) {
7
9
  super(`Ambiguous branch '${branchName}' found in multiple remotes: ${matches
8
10
  .map(m => m.fullRef)
9
11
  .join(', ')}. Please specify which remote to use.`);
10
- Object.defineProperty(this, "branchName", {
11
- enumerable: true,
12
- configurable: true,
13
- writable: true,
14
- value: branchName
15
- });
16
- Object.defineProperty(this, "matches", {
17
- enumerable: true,
18
- configurable: true,
19
- writable: true,
20
- value: matches
21
- });
22
12
  this.name = 'AmbiguousBranchError';
13
+ this.branchName = branchName;
14
+ this.matches = matches;
23
15
  }
24
16
  }
@@ -3,16 +3,15 @@ import * as path from 'path';
3
3
  import { format } from 'util';
4
4
  import os from 'os';
5
5
  /**
6
- * Log level enum for structured logging
6
+ * Log level constants for structured logging
7
7
  */
8
- var LogLevel;
9
- (function (LogLevel) {
10
- LogLevel["DEBUG"] = "DEBUG";
11
- LogLevel["INFO"] = "INFO";
12
- LogLevel["WARN"] = "WARN";
13
- LogLevel["ERROR"] = "ERROR";
14
- LogLevel["LOG"] = "LOG";
15
- })(LogLevel || (LogLevel = {}));
8
+ const LogLevel = {
9
+ DEBUG: 'DEBUG',
10
+ INFO: 'INFO',
11
+ WARN: 'WARN',
12
+ ERROR: 'ERROR',
13
+ LOG: 'LOG',
14
+ };
16
15
  /**
17
16
  * CLI-optimized logger with size management and rotation
18
17
  *
@@ -25,31 +24,11 @@ var LogLevel;
25
24
  * - Sensitive information filtering on console output
26
25
  */
27
26
  class Logger {
27
+ logFile;
28
+ config;
29
+ writeQueue = [];
30
+ isWriting = false;
28
31
  constructor(config = {}) {
29
- Object.defineProperty(this, "logFile", {
30
- enumerable: true,
31
- configurable: true,
32
- writable: true,
33
- value: void 0
34
- });
35
- Object.defineProperty(this, "config", {
36
- enumerable: true,
37
- configurable: true,
38
- writable: true,
39
- value: void 0
40
- });
41
- Object.defineProperty(this, "writeQueue", {
42
- enumerable: true,
43
- configurable: true,
44
- writable: true,
45
- value: []
46
- });
47
- Object.defineProperty(this, "isWriting", {
48
- enumerable: true,
49
- configurable: true,
50
- writable: true,
51
- value: false
52
- });
53
32
  this.config = {
54
33
  maxSizeBytes: config.maxSizeBytes ?? 5 * 1024 * 1024, // 5MB default
55
34
  maxRotatedFiles: config.maxRotatedFiles ?? 3,
@@ -47,7 +47,7 @@ export interface SessionStateData {
47
47
  autoApprovalFailed: boolean;
48
48
  autoApprovalReason: string | undefined;
49
49
  autoApprovalAbortController: AbortController | undefined;
50
- hasBackgroundTask: boolean;
50
+ backgroundTaskCount: number;
51
51
  }
52
52
  /**
53
53
  * Create initial session state data with default values.
@@ -3,25 +3,10 @@
3
3
  * Provides exclusive access to wrapped data through async locking.
4
4
  */
5
5
  export class Mutex {
6
+ data;
7
+ locked = false;
8
+ waitQueue = [];
6
9
  constructor(initialData) {
7
- Object.defineProperty(this, "data", {
8
- enumerable: true,
9
- configurable: true,
10
- writable: true,
11
- value: void 0
12
- });
13
- Object.defineProperty(this, "locked", {
14
- enumerable: true,
15
- configurable: true,
16
- writable: true,
17
- value: false
18
- });
19
- Object.defineProperty(this, "waitQueue", {
20
- enumerable: true,
21
- configurable: true,
22
- writable: true,
23
- value: []
24
- });
25
10
  this.data = initialData;
26
11
  }
27
12
  /**
@@ -100,6 +85,6 @@ export function createInitialSessionStateData() {
100
85
  autoApprovalFailed: false,
101
86
  autoApprovalReason: undefined,
102
87
  autoApprovalAbortController: undefined,
103
- hasBackgroundTask: false,
88
+ backgroundTaskCount: 0,
104
89
  };
105
90
  }
@@ -39,14 +39,14 @@ export function generateWorktreeDirectory(projectPath, branchName, pattern) {
39
39
  case 'branch':
40
40
  case 'branch-name':
41
41
  // Sanitize branch name for filesystem
42
- sanitizedBranch ?? (sanitizedBranch = branchName
42
+ sanitizedBranch ??= branchName
43
43
  .replace(/\//g, '-') // Replace forward slashes with dashes
44
44
  .replace(/[^a-zA-Z0-9-_.]+/g, '') // Remove special characters except dash, dot, underscore
45
45
  .replace(/^-+|-+$/g, '') // Remove leading/trailing dashes
46
- .toLowerCase()); // Convert to lowercase for consistency
46
+ .toLowerCase(); // Convert to lowercase for consistency
47
47
  return sanitizedBranch;
48
48
  case 'project':
49
- projectName ?? (projectName = getGitRepositoryName(projectPath));
49
+ projectName ??= getGitRepositoryName(projectPath);
50
50
  return projectName;
51
51
  default:
52
52
  return placeholder;
@@ -73,7 +73,7 @@ export function prepareWorktreeItems(worktrees, sessions) {
73
73
  const session = sessions.find(s => s.worktreePath === wt.path);
74
74
  const stateData = session?.stateMutex.getSnapshot();
75
75
  const status = stateData
76
- ? ` [${getStatusDisplay(stateData.state, stateData.hasBackgroundTask)}]`
76
+ ? ` [${getStatusDisplay(stateData.state, stateData.backgroundTaskCount)}]`
77
77
  : '';
78
78
  const fullBranchName = wt.branch
79
79
  ? wt.branch.replace('refs/heads/', '')