ccmanager 0.0.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.
Files changed (40) hide show
  1. package/README.md +85 -0
  2. package/dist/app.d.ts +6 -0
  3. package/dist/app.js +57 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +24 -0
  6. package/dist/components/App.d.ts +3 -0
  7. package/dist/components/App.js +228 -0
  8. package/dist/components/ConfigureShortcuts.d.ts +6 -0
  9. package/dist/components/ConfigureShortcuts.js +139 -0
  10. package/dist/components/Confirmation.d.ts +12 -0
  11. package/dist/components/Confirmation.js +42 -0
  12. package/dist/components/DeleteWorktree.d.ts +7 -0
  13. package/dist/components/DeleteWorktree.js +116 -0
  14. package/dist/components/Menu.d.ts +9 -0
  15. package/dist/components/Menu.js +154 -0
  16. package/dist/components/MergeWorktree.d.ts +7 -0
  17. package/dist/components/MergeWorktree.js +142 -0
  18. package/dist/components/NewWorktree.d.ts +7 -0
  19. package/dist/components/NewWorktree.js +49 -0
  20. package/dist/components/Session.d.ts +10 -0
  21. package/dist/components/Session.js +121 -0
  22. package/dist/constants/statusIcons.d.ts +18 -0
  23. package/dist/constants/statusIcons.js +27 -0
  24. package/dist/services/sessionManager.d.ts +16 -0
  25. package/dist/services/sessionManager.js +190 -0
  26. package/dist/services/sessionManager.test.d.ts +1 -0
  27. package/dist/services/sessionManager.test.js +99 -0
  28. package/dist/services/shortcutManager.d.ts +17 -0
  29. package/dist/services/shortcutManager.js +167 -0
  30. package/dist/services/worktreeService.d.ts +24 -0
  31. package/dist/services/worktreeService.js +220 -0
  32. package/dist/types/index.d.ts +36 -0
  33. package/dist/types/index.js +4 -0
  34. package/dist/utils/logger.d.ts +14 -0
  35. package/dist/utils/logger.js +21 -0
  36. package/dist/utils/promptDetector.d.ts +1 -0
  37. package/dist/utils/promptDetector.js +20 -0
  38. package/dist/utils/promptDetector.test.d.ts +1 -0
  39. package/dist/utils/promptDetector.test.js +81 -0
  40. package/package.json +70 -0
@@ -0,0 +1,16 @@
1
+ import { Session, SessionManager as ISessionManager, SessionState } from '../types/index.js';
2
+ import { EventEmitter } from 'events';
3
+ export declare class SessionManager extends EventEmitter implements ISessionManager {
4
+ sessions: Map<string, Session>;
5
+ private waitingWithBottomBorder;
6
+ private stripAnsi;
7
+ detectSessionState(cleanData: string, currentState: SessionState, sessionId: string): SessionState;
8
+ constructor();
9
+ createSession(worktreePath: string): Session;
10
+ private setupBackgroundHandler;
11
+ getSession(worktreePath: string): Session | undefined;
12
+ setSessionActive(worktreePath: string, active: boolean): void;
13
+ destroySession(worktreePath: string): void;
14
+ getAllSessions(): Session[];
15
+ destroy(): void;
16
+ }
@@ -0,0 +1,190 @@
1
+ import { spawn } from 'node-pty';
2
+ import { EventEmitter } from 'events';
3
+ import { includesPromptBoxBottomBorder } from '../utils/promptDetector.js';
4
+ export class SessionManager extends EventEmitter {
5
+ stripAnsi(str) {
6
+ // Remove all ANSI escape sequences including cursor movement, color codes, etc.
7
+ return str
8
+ .replace(/\x1b\[[0-9;]*m/g, '') // Color codes (including 24-bit)
9
+ .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '') // CSI sequences
10
+ .replace(/\x1b\][^\x07]*\x07/g, '') // OSC sequences
11
+ .replace(/\x1b[PX^_].*?\x1b\\/g, '') // DCS/PM/APC/SOS sequences
12
+ .replace(/\x1b\[\?[0-9;]*[hl]/g, '') // Private mode sequences
13
+ .replace(/\x1b[>=]/g, '') // Other escape sequences
14
+ .replace(/[\x00-\x09\x0B-\x1F\x7F]/g, '') // Control characters except newline (\x0A)
15
+ .replace(/\r/g, '') // Carriage returns
16
+ .replace(/^[0-9;]+m/gm, '') // Orphaned color codes at line start
17
+ .replace(/[0-9]+;[0-9]+;[0-9;]+m/g, ''); // Orphaned 24-bit color codes
18
+ }
19
+ detectSessionState(cleanData, currentState, sessionId) {
20
+ const hasBottomBorder = includesPromptBoxBottomBorder(cleanData);
21
+ const hasWaitingPrompt = cleanData.includes('│ Do you want');
22
+ const wasWaitingWithBottomBorder = this.waitingWithBottomBorder.get(sessionId) || false;
23
+ let newState = currentState;
24
+ // Check if current state is waiting and this is just a prompt box bottom border
25
+ if (hasWaitingPrompt) {
26
+ newState = 'waiting_input';
27
+ // Check if this same data also contains the bottom border
28
+ if (hasBottomBorder) {
29
+ this.waitingWithBottomBorder.set(sessionId, true);
30
+ }
31
+ else {
32
+ this.waitingWithBottomBorder.set(sessionId, false);
33
+ }
34
+ }
35
+ else if (currentState === 'waiting_input' &&
36
+ hasBottomBorder &&
37
+ !hasWaitingPrompt &&
38
+ !wasWaitingWithBottomBorder) {
39
+ // Keep the waiting state and mark that we've seen the bottom border
40
+ newState = 'waiting_input';
41
+ this.waitingWithBottomBorder.set(sessionId, true);
42
+ }
43
+ else if (cleanData.toLowerCase().includes('esc to interrupt')) {
44
+ newState = 'busy';
45
+ this.waitingWithBottomBorder.set(sessionId, false);
46
+ }
47
+ else {
48
+ newState = 'idle';
49
+ this.waitingWithBottomBorder.set(sessionId, false);
50
+ }
51
+ return newState;
52
+ }
53
+ constructor() {
54
+ super();
55
+ Object.defineProperty(this, "sessions", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: void 0
60
+ });
61
+ Object.defineProperty(this, "waitingWithBottomBorder", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: new Map()
66
+ });
67
+ this.sessions = new Map();
68
+ }
69
+ createSession(worktreePath) {
70
+ // Check if session already exists
71
+ const existing = this.sessions.get(worktreePath);
72
+ if (existing) {
73
+ return existing;
74
+ }
75
+ const id = `session-${Date.now()}-${Math.random()
76
+ .toString(36)
77
+ .substr(2, 9)}`;
78
+ const ptyProcess = spawn('claude', [], {
79
+ name: 'xterm-color',
80
+ cols: process.stdout.columns || 80,
81
+ rows: process.stdout.rows || 24,
82
+ cwd: worktreePath,
83
+ env: process.env,
84
+ });
85
+ const session = {
86
+ id,
87
+ worktreePath,
88
+ process: ptyProcess,
89
+ state: 'busy', // Session starts as busy when created
90
+ output: [],
91
+ outputHistory: [],
92
+ lastActivity: new Date(),
93
+ isActive: false,
94
+ };
95
+ // Set up persistent background data handler for state detection
96
+ this.setupBackgroundHandler(session);
97
+ this.sessions.set(worktreePath, session);
98
+ this.emit('sessionCreated', session);
99
+ return session;
100
+ }
101
+ setupBackgroundHandler(session) {
102
+ // This handler always runs for all data
103
+ session.process.onData((data) => {
104
+ // Store in output history as Buffer
105
+ const buffer = Buffer.from(data, 'utf8');
106
+ session.outputHistory.push(buffer);
107
+ // Limit memory usage - keep max 10MB of output history
108
+ const MAX_HISTORY_SIZE = 10 * 1024 * 1024; // 10MB
109
+ let totalSize = session.outputHistory.reduce((sum, buf) => sum + buf.length, 0);
110
+ while (totalSize > MAX_HISTORY_SIZE && session.outputHistory.length > 0) {
111
+ const removed = session.outputHistory.shift();
112
+ if (removed) {
113
+ totalSize -= removed.length;
114
+ }
115
+ }
116
+ // Also store for state detection
117
+ session.output.push(data);
118
+ // Keep only last 100 chunks for state detection
119
+ if (session.output.length > 100) {
120
+ session.output.shift();
121
+ }
122
+ session.lastActivity = new Date();
123
+ // Strip ANSI codes for pattern matching
124
+ const cleanData = this.stripAnsi(data);
125
+ // Skip state monitoring if cleanData is empty
126
+ if (!cleanData.trim()) {
127
+ // Only emit data events when session is active
128
+ if (session.isActive) {
129
+ this.emit('sessionData', session, data);
130
+ }
131
+ return;
132
+ }
133
+ // Detect state based on the new data
134
+ const oldState = session.state;
135
+ const newState = this.detectSessionState(cleanData, oldState, session.id);
136
+ // Update state if changed
137
+ if (newState !== oldState) {
138
+ session.state = newState;
139
+ this.emit('sessionStateChanged', session);
140
+ }
141
+ // Only emit data events when session is active
142
+ if (session.isActive) {
143
+ this.emit('sessionData', session, data);
144
+ }
145
+ });
146
+ session.process.onExit(() => {
147
+ // Update state to idle before destroying
148
+ session.state = 'idle';
149
+ this.emit('sessionStateChanged', session);
150
+ this.destroySession(session.worktreePath);
151
+ this.emit('sessionExit', session);
152
+ });
153
+ }
154
+ getSession(worktreePath) {
155
+ return this.sessions.get(worktreePath);
156
+ }
157
+ setSessionActive(worktreePath, active) {
158
+ const session = this.sessions.get(worktreePath);
159
+ if (session) {
160
+ session.isActive = active;
161
+ // If becoming active, emit a restore event with the output history
162
+ if (active && session.outputHistory.length > 0) {
163
+ this.emit('sessionRestore', session);
164
+ }
165
+ }
166
+ }
167
+ destroySession(worktreePath) {
168
+ const session = this.sessions.get(worktreePath);
169
+ if (session) {
170
+ try {
171
+ session.process.kill();
172
+ }
173
+ catch (_error) {
174
+ // Process might already be dead
175
+ }
176
+ this.sessions.delete(worktreePath);
177
+ this.waitingWithBottomBorder.delete(session.id);
178
+ this.emit('sessionDestroyed', session);
179
+ }
180
+ }
181
+ getAllSessions() {
182
+ return Array.from(this.sessions.values());
183
+ }
184
+ destroy() {
185
+ // Clean up all sessions
186
+ for (const worktreePath of this.sessions.keys()) {
187
+ this.destroySession(worktreePath);
188
+ }
189
+ }
190
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { SessionManager } from './sessionManager.js';
3
+ // Mock the promptDetector module
4
+ vi.mock('../utils/promptDetector.js', () => ({
5
+ includesPromptBoxBottomBorder: vi.fn(),
6
+ }));
7
+ import { includesPromptBoxBottomBorder } from '../utils/promptDetector.js';
8
+ describe('SessionManager', () => {
9
+ let sessionManager;
10
+ const mockSessionId = 'test-session-123';
11
+ beforeEach(() => {
12
+ sessionManager = new SessionManager();
13
+ vi.clearAllMocks();
14
+ });
15
+ describe('detectSessionState', () => {
16
+ it('should detect waiting_input state when "Do you want" prompt is present', () => {
17
+ const cleanData = '│ Do you want to continue?';
18
+ const currentState = 'idle';
19
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
20
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
21
+ expect(newState).toBe('waiting_input');
22
+ });
23
+ it('should set waitingWithBottomBorder when waiting prompt and bottom border are both present', () => {
24
+ const cleanData = '│ Do you want to continue?\n└───────────────────────┘';
25
+ const currentState = 'idle';
26
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
27
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
28
+ expect(newState).toBe('waiting_input');
29
+ // The internal map should have been set to true
30
+ });
31
+ it('should maintain waiting_input state when bottom border appears after waiting prompt', () => {
32
+ const cleanData = '└───────────────────────┘';
33
+ const currentState = 'waiting_input';
34
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
35
+ // First call to set up the waiting state without bottom border
36
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
37
+ sessionManager.detectSessionState('│ Do you want to continue?', 'idle', mockSessionId);
38
+ // Now test the bottom border appearing
39
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
40
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
41
+ expect(newState).toBe('waiting_input');
42
+ });
43
+ it('should detect busy state when "esc to interrupt" is present', () => {
44
+ const cleanData = 'Processing... Press ESC to interrupt';
45
+ const currentState = 'idle';
46
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
47
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
48
+ expect(newState).toBe('busy');
49
+ });
50
+ it('should detect idle state when no specific patterns are found', () => {
51
+ const cleanData = 'Some regular output text';
52
+ const currentState = 'busy';
53
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
54
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
55
+ expect(newState).toBe('idle');
56
+ });
57
+ it('should handle case-insensitive "esc to interrupt" detection', () => {
58
+ const cleanData = 'Running task... PRESS ESC TO INTERRUPT';
59
+ const currentState = 'idle';
60
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
61
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
62
+ expect(newState).toBe('busy');
63
+ });
64
+ it('should not change from waiting_input when bottom border was already seen', () => {
65
+ const cleanData = '└───────────────────────┘';
66
+ const currentState = 'waiting_input';
67
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
68
+ // First, simulate seeing waiting prompt with bottom border
69
+ sessionManager.detectSessionState('│ Do you want to continue?\n└───────────────────────┘', 'idle', mockSessionId);
70
+ // Now another bottom border appears
71
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
72
+ expect(newState).toBe('idle'); // Should change to idle since we already saw the bottom border
73
+ });
74
+ it('should clear waitingWithBottomBorder flag when transitioning to busy', () => {
75
+ const cleanData = 'Processing... Press ESC to interrupt';
76
+ const currentState = 'waiting_input';
77
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
78
+ // First set up waiting state with bottom border
79
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
80
+ sessionManager.detectSessionState('│ Do you want to continue?\n└───────────────────────┘', 'idle', mockSessionId);
81
+ // Now transition to busy
82
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
83
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
84
+ expect(newState).toBe('busy');
85
+ });
86
+ it('should clear waitingWithBottomBorder flag when transitioning to idle', () => {
87
+ const cleanData = 'Task completed successfully';
88
+ const currentState = 'waiting_input';
89
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
90
+ // First set up waiting state with bottom border
91
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(true);
92
+ sessionManager.detectSessionState('│ Do you want to continue?\n└───────────────────────┘', 'idle', mockSessionId);
93
+ // Now transition to idle
94
+ vi.mocked(includesPromptBoxBottomBorder).mockReturnValue(false);
95
+ const newState = sessionManager.detectSessionState(cleanData, currentState, mockSessionId);
96
+ expect(newState).toBe('idle');
97
+ });
98
+ });
99
+ });
@@ -0,0 +1,17 @@
1
+ import { ShortcutKey, ShortcutConfig } from '../types/index.js';
2
+ import { Key } from 'ink';
3
+ export declare class ShortcutManager {
4
+ private shortcuts;
5
+ private configPath;
6
+ private reservedKeys;
7
+ constructor();
8
+ private loadShortcuts;
9
+ private validateShortcut;
10
+ private isReservedKey;
11
+ saveShortcuts(shortcuts: ShortcutConfig): boolean;
12
+ getShortcuts(): ShortcutConfig;
13
+ matchesShortcut(shortcutName: keyof ShortcutConfig, input: string, key: Key): boolean;
14
+ getShortcutDisplay(shortcutName: keyof ShortcutConfig): string;
15
+ getShortcutCode(shortcut: ShortcutKey): string | null;
16
+ }
17
+ export declare const shortcutManager: ShortcutManager;
@@ -0,0 +1,167 @@
1
+ import { DEFAULT_SHORTCUTS, } from '../types/index.js';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+ export class ShortcutManager {
6
+ constructor() {
7
+ Object.defineProperty(this, "shortcuts", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: void 0
12
+ });
13
+ Object.defineProperty(this, "configPath", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: void 0
18
+ });
19
+ Object.defineProperty(this, "reservedKeys", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: [
24
+ { ctrl: true, key: 'c' },
25
+ { ctrl: true, key: 'd' },
26
+ { key: 'escape' }, // Ctrl+[ is equivalent to Escape
27
+ { ctrl: true, key: '[' },
28
+ ]
29
+ });
30
+ // Use platform-specific config directory
31
+ const configDir = process.platform === 'win32'
32
+ ? path.join(process.env['APPDATA'] || os.homedir(), 'ccmanager')
33
+ : path.join(os.homedir(), '.config', 'ccmanager');
34
+ this.configPath = path.join(configDir, 'shortcuts.json');
35
+ this.shortcuts = this.loadShortcuts();
36
+ }
37
+ loadShortcuts() {
38
+ try {
39
+ if (fs.existsSync(this.configPath)) {
40
+ const data = fs.readFileSync(this.configPath, 'utf8');
41
+ const loaded = JSON.parse(data);
42
+ // Validate loaded shortcuts
43
+ const validated = {
44
+ returnToMenu: this.validateShortcut(loaded.returnToMenu) ||
45
+ DEFAULT_SHORTCUTS.returnToMenu,
46
+ cancel: this.validateShortcut(loaded.cancel) || DEFAULT_SHORTCUTS.cancel,
47
+ };
48
+ return validated;
49
+ }
50
+ }
51
+ catch (error) {
52
+ console.error('Failed to load shortcuts:', error);
53
+ }
54
+ return { ...DEFAULT_SHORTCUTS };
55
+ }
56
+ validateShortcut(shortcut) {
57
+ if (!shortcut || typeof shortcut !== 'object') {
58
+ return null;
59
+ }
60
+ const s = shortcut;
61
+ if (!s['key'] || typeof s['key'] !== 'string') {
62
+ return null;
63
+ }
64
+ const validShortcut = {
65
+ key: s['key'],
66
+ ctrl: !!s['ctrl'],
67
+ alt: !!s['alt'],
68
+ shift: !!s['shift'],
69
+ };
70
+ // Check if it's a reserved key
71
+ if (this.isReservedKey(validShortcut)) {
72
+ return null;
73
+ }
74
+ // Ensure at least one modifier key is used (except for special keys like escape)
75
+ if (validShortcut.key !== 'escape' &&
76
+ !validShortcut.ctrl &&
77
+ !validShortcut.alt &&
78
+ !validShortcut.shift) {
79
+ return null;
80
+ }
81
+ return validShortcut;
82
+ }
83
+ isReservedKey(shortcut) {
84
+ return this.reservedKeys.some(reserved => reserved.key === shortcut.key &&
85
+ reserved.ctrl === shortcut.ctrl &&
86
+ reserved.alt === shortcut.alt &&
87
+ reserved.shift === shortcut.shift);
88
+ }
89
+ saveShortcuts(shortcuts) {
90
+ // Validate all shortcuts
91
+ const validated = {
92
+ returnToMenu: this.validateShortcut(shortcuts.returnToMenu) ||
93
+ this.shortcuts.returnToMenu,
94
+ cancel: this.validateShortcut(shortcuts.cancel) || this.shortcuts.cancel,
95
+ };
96
+ try {
97
+ const dir = path.dirname(this.configPath);
98
+ if (!fs.existsSync(dir)) {
99
+ fs.mkdirSync(dir, { recursive: true });
100
+ }
101
+ fs.writeFileSync(this.configPath, JSON.stringify(validated, null, 2));
102
+ this.shortcuts = validated;
103
+ return true;
104
+ }
105
+ catch (error) {
106
+ console.error('Failed to save shortcuts:', error);
107
+ return false;
108
+ }
109
+ }
110
+ getShortcuts() {
111
+ return { ...this.shortcuts };
112
+ }
113
+ matchesShortcut(shortcutName, input, key) {
114
+ const shortcut = this.shortcuts[shortcutName];
115
+ if (!shortcut)
116
+ return false;
117
+ // Handle escape key specially
118
+ if (shortcut.key === 'escape') {
119
+ return key.escape === true;
120
+ }
121
+ // Check modifiers
122
+ if (shortcut.ctrl !== key.ctrl)
123
+ return false;
124
+ // Note: ink's Key type doesn't support alt or shift modifiers
125
+ // so we can't check them here. For now, we'll only support ctrl modifier
126
+ if (shortcut.alt || shortcut.shift)
127
+ return false;
128
+ // Check key
129
+ return input.toLowerCase() === shortcut.key.toLowerCase();
130
+ }
131
+ getShortcutDisplay(shortcutName) {
132
+ const shortcut = this.shortcuts[shortcutName];
133
+ if (!shortcut)
134
+ return '';
135
+ const parts = [];
136
+ if (shortcut.ctrl)
137
+ parts.push('Ctrl');
138
+ if (shortcut.alt)
139
+ parts.push('Alt');
140
+ if (shortcut.shift)
141
+ parts.push('Shift');
142
+ // Format special keys
143
+ let keyDisplay = shortcut.key;
144
+ if (keyDisplay === 'escape')
145
+ keyDisplay = 'Esc';
146
+ else if (keyDisplay.length === 1)
147
+ keyDisplay = keyDisplay.toUpperCase();
148
+ parts.push(keyDisplay);
149
+ return parts.join('+');
150
+ }
151
+ getShortcutCode(shortcut) {
152
+ // Convert shortcut to terminal code for raw stdin handling
153
+ if (!shortcut.ctrl || shortcut.alt || shortcut.shift) {
154
+ return null; // Only support Ctrl+key for raw codes
155
+ }
156
+ const key = shortcut.key.toLowerCase();
157
+ if (key.length !== 1)
158
+ return null;
159
+ // Convert Ctrl+letter to ASCII control code
160
+ const code = key.charCodeAt(0) - 96; // 'a' = 1, 'b' = 2, etc.
161
+ if (code >= 1 && code <= 26) {
162
+ return String.fromCharCode(code);
163
+ }
164
+ return null;
165
+ }
166
+ }
167
+ export const shortcutManager = new ShortcutManager();
@@ -0,0 +1,24 @@
1
+ import { Worktree } from '../types/index.js';
2
+ export declare class WorktreeService {
3
+ private rootPath;
4
+ constructor(rootPath?: string);
5
+ getWorktrees(): Worktree[];
6
+ private getCurrentBranch;
7
+ isGitRepository(): boolean;
8
+ createWorktree(worktreePath: string, branch: string): {
9
+ success: boolean;
10
+ error?: string;
11
+ };
12
+ deleteWorktree(worktreePath: string): {
13
+ success: boolean;
14
+ error?: string;
15
+ };
16
+ mergeWorktree(sourceBranch: string, targetBranch: string, useRebase?: boolean): {
17
+ success: boolean;
18
+ error?: string;
19
+ };
20
+ deleteWorktreeByBranch(branch: string): {
21
+ success: boolean;
22
+ error?: string;
23
+ };
24
+ }