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.
- package/dist/cli.js +3 -2
- package/dist/components/App.d.ts +1 -0
- package/dist/components/App.js +17 -39
- package/dist/components/App.test.js +12 -44
- package/dist/components/Configuration.js +10 -15
- package/dist/components/ConfigureCommand.js +18 -106
- package/dist/components/ConfigureCustomCommand.js +2 -17
- package/dist/components/ConfigureOther.js +5 -23
- package/dist/components/ConfigureOther.test.js +7 -31
- package/dist/components/ConfigureShortcuts.js +4 -31
- package/dist/components/ConfigureStatusHooks.js +5 -44
- package/dist/components/ConfigureStatusHooks.test.js +7 -31
- package/dist/components/ConfigureTimeout.js +2 -14
- package/dist/components/ConfigureWorktree.js +4 -47
- package/dist/components/ConfigureWorktreeHooks.js +5 -37
- package/dist/components/ConfigureWorktreeHooks.test.js +8 -33
- package/dist/components/Confirmation.js +9 -21
- package/dist/components/CustomCommandSummary.js +2 -5
- package/dist/components/DeleteConfirmation.js +10 -47
- package/dist/components/DeleteWorktree.js +14 -42
- package/dist/components/DeleteWorktree.test.js +6 -6
- package/dist/components/LoadingSpinner.js +3 -6
- package/dist/components/LoadingSpinner.test.js +22 -22
- package/dist/components/Menu.d.ts +1 -0
- package/dist/components/Menu.js +10 -42
- package/dist/components/Menu.recent-projects.test.js +8 -8
- package/dist/components/Menu.test.js +10 -10
- package/dist/components/MergeWorktree.js +16 -88
- package/dist/components/MergeWorktree.test.js +5 -5
- package/dist/components/NewWorktree.js +25 -105
- package/dist/components/NewWorktree.test.js +8 -8
- package/dist/components/PresetSelector.js +3 -9
- package/dist/components/ProjectList.js +9 -38
- package/dist/components/ProjectList.recent-projects.test.js +7 -7
- package/dist/components/ProjectList.test.js +37 -37
- package/dist/components/RemoteBranchSelector.js +2 -21
- package/dist/components/RemoteBranchSelector.test.js +8 -8
- package/dist/components/TextInputWrapper.d.ts +5 -0
- package/dist/components/TextInputWrapper.js +138 -11
- package/dist/constants/statusIcons.d.ts +2 -1
- package/dist/constants/statusIcons.js +13 -4
- package/dist/constants/statusIcons.test.js +41 -11
- package/dist/contexts/ConfigEditorContext.d.ts +1 -1
- package/dist/contexts/ConfigEditorContext.js +3 -2
- package/dist/services/autoApprovalVerifier.js +1 -8
- package/dist/services/bunTerminal.js +41 -136
- package/dist/services/config/configEditor.js +2 -12
- package/dist/services/config/globalConfigManager.js +4 -24
- package/dist/services/config/projectConfigManager.js +3 -18
- package/dist/services/globalSessionOrchestrator.js +3 -12
- package/dist/services/globalSessionOrchestrator.test.js +1 -8
- package/dist/services/projectManager.js +13 -68
- package/dist/services/sessionManager.d.ts +1 -1
- package/dist/services/sessionManager.effect.test.js +9 -37
- package/dist/services/sessionManager.js +12 -28
- package/dist/services/sessionManager.test.js +48 -40
- package/dist/services/shortcutManager.js +7 -13
- package/dist/services/stateDetector/base.d.ts +1 -1
- package/dist/services/stateDetector/claude.d.ts +1 -1
- package/dist/services/stateDetector/claude.js +11 -4
- package/dist/services/stateDetector/claude.test.js +47 -24
- package/dist/services/stateDetector/cline.d.ts +1 -1
- package/dist/services/stateDetector/cline.js +1 -1
- package/dist/services/stateDetector/codex.d.ts +1 -1
- package/dist/services/stateDetector/codex.js +1 -1
- package/dist/services/stateDetector/cursor.d.ts +1 -1
- package/dist/services/stateDetector/cursor.js +1 -1
- package/dist/services/stateDetector/gemini.d.ts +1 -1
- package/dist/services/stateDetector/gemini.js +1 -1
- package/dist/services/stateDetector/github-copilot.d.ts +1 -1
- package/dist/services/stateDetector/github-copilot.js +1 -1
- package/dist/services/stateDetector/opencode.d.ts +1 -1
- package/dist/services/stateDetector/opencode.js +1 -1
- package/dist/services/stateDetector/types.d.ts +1 -1
- package/dist/services/worktreeConfigManager.js +3 -8
- package/dist/services/worktreeService.js +2 -12
- package/dist/types/index.js +4 -12
- package/dist/utils/logger.js +12 -33
- package/dist/utils/mutex.d.ts +1 -1
- package/dist/utils/mutex.js +4 -19
- package/dist/utils/worktreeUtils.js +4 -4
- 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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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,
|
|
700
|
+
const createMockSession = (id, state, backgroundTaskCount = 0) => ({
|
|
729
701
|
id,
|
|
730
702
|
stateMutex: {
|
|
731
|
-
getSnapshot: () => ({ state,
|
|
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
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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):
|
|
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):
|
|
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
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
|
235
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
236
236
|
// Assert
|
|
237
|
-
expect(
|
|
237
|
+
expect(count).toBe(1);
|
|
238
238
|
});
|
|
239
|
-
it('should
|
|
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
|
|
247
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
248
248
|
// Assert
|
|
249
|
-
expect(
|
|
249
|
+
expect(count).toBe(2);
|
|
250
250
|
});
|
|
251
|
-
it('should
|
|
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
|
|
271
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
260
272
|
// Assert
|
|
261
|
-
expect(
|
|
273
|
+
expect(count).toBe(1);
|
|
262
274
|
});
|
|
263
|
-
it('should return
|
|
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
|
|
283
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
272
284
|
// Assert
|
|
273
|
-
expect(
|
|
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
|
|
298
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
287
299
|
// Assert - should only check last 3 lines, not the conversation content
|
|
288
|
-
expect(
|
|
300
|
+
expect(count).toBe(0);
|
|
289
301
|
});
|
|
290
|
-
it('should
|
|
302
|
+
it('should return 0 for empty terminal', () => {
|
|
291
303
|
// Arrange
|
|
292
304
|
terminal = createMockTerminal([]);
|
|
293
305
|
// Act
|
|
294
|
-
const
|
|
306
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
295
307
|
// Assert
|
|
296
|
-
expect(
|
|
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
|
|
314
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
303
315
|
// Assert
|
|
304
|
-
expect(
|
|
316
|
+
expect(count).toBe(1);
|
|
305
317
|
});
|
|
306
|
-
it('should
|
|
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
|
|
326
|
+
const count = detector.detectBackgroundTask(terminal);
|
|
315
327
|
// Assert
|
|
316
|
-
expect(
|
|
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
|
|
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(
|
|
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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
5
|
+
detectBackgroundTask(_terminal: Terminal): number;
|
|
6
6
|
}
|
|
@@ -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):
|
|
4
|
+
detectBackgroundTask(terminal: Terminal): number;
|
|
5
5
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { isWorktreeConfigEnabled } from '../utils/worktreeConfig.js';
|
|
2
2
|
class WorktreeConfigManager {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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();
|
package/dist/types/index.js
CHANGED
|
@@ -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
|
}
|
package/dist/utils/logger.js
CHANGED
|
@@ -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
|
|
6
|
+
* Log level constants for structured logging
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,
|
package/dist/utils/mutex.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface SessionStateData {
|
|
|
47
47
|
autoApprovalFailed: boolean;
|
|
48
48
|
autoApprovalReason: string | undefined;
|
|
49
49
|
autoApprovalAbortController: AbortController | undefined;
|
|
50
|
-
|
|
50
|
+
backgroundTaskCount: number;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Create initial session state data with default values.
|
package/dist/utils/mutex.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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()
|
|
46
|
+
.toLowerCase(); // Convert to lowercase for consistency
|
|
47
47
|
return sanitizedBranch;
|
|
48
48
|
case 'project':
|
|
49
|
-
projectName
|
|
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.
|
|
76
|
+
? ` [${getStatusDisplay(stateData.state, stateData.backgroundTaskCount)}]`
|
|
77
77
|
: '';
|
|
78
78
|
const fullBranchName = wt.branch
|
|
79
79
|
? wt.branch.replace('refs/heads/', '')
|