ccmanager 4.1.14 → 4.1.17
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/components/ConfigureMerge.js +2 -3
- package/dist/components/Dashboard.js +6 -1
- package/dist/components/Menu.js +11 -9
- package/dist/components/PresetSelector.js +3 -10
- package/dist/components/PresetSelector.test.js +4 -15
- package/dist/services/autoApprovalVerifier.js +1 -1
- package/dist/services/projectManager.js +10 -4
- package/dist/services/sessionManager.js +4 -3
- package/dist/services/worktreeService.js +20 -8
- package/dist/services/worktreeService.test.js +69 -4
- package/package.json +6 -6
|
@@ -37,9 +37,8 @@ const ConfigureMerge = ({ onComplete }) => {
|
|
|
37
37
|
onComplete();
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
switch (field) {
|
|
40
|
+
setEditField(item.value);
|
|
41
|
+
switch (item.value) {
|
|
43
42
|
case 'mergeArgs':
|
|
44
43
|
setInputValue(getMergeArgs().join(' '));
|
|
45
44
|
break;
|
|
@@ -442,7 +442,12 @@ const Dashboard = ({ projectsDir, onSelectSession, onSelectProject, error, onDis
|
|
|
442
442
|
});
|
|
443
443
|
}
|
|
444
444
|
};
|
|
445
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, flexDirection: "column", children: _jsxs(Text, { bold: true, color: "green", children: ["CCManager - Dashboard v", version] }) }), loading ? (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "Discovering projects..." }) })) : projects.length === 0 && !displayError ? (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: ["No git repositories found in ", projectsDir] }) })) : (_jsx(SearchableList, { isSearchMode: isSearchMode, searchQuery: searchQuery, onSearchQueryChange: setSearchQuery, selectedIndex: selectedIndex, items: items, limit: limit, placeholder: "Type to filter...", noMatchMessage: "No matches found", children: _jsx(SelectInput, { items: items, onSelect:
|
|
445
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, flexDirection: "column", children: _jsxs(Text, { bold: true, color: "green", children: ["CCManager - Dashboard v", version] }) }), loading ? (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: "Discovering projects..." }) })) : projects.length === 0 && !displayError ? (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: ["No git repositories found in ", projectsDir] }) })) : (_jsx(SearchableList, { isSearchMode: isSearchMode, searchQuery: searchQuery, onSearchQueryChange: setSearchQuery, selectedIndex: selectedIndex, items: items, limit: limit, placeholder: "Type to filter...", noMatchMessage: "No matches found", children: _jsx(SelectInput, { items: items, onSelect: raw => {
|
|
446
|
+
const item = items.find(i => i.value === raw?.value);
|
|
447
|
+
if (!item)
|
|
448
|
+
return;
|
|
449
|
+
handleSelect(item);
|
|
450
|
+
}, isFocused: !displayError, limit: limit, initialIndex: selectedIndex }) })), displayError && (_jsx(Box, { marginTop: 1, paddingX: 1, borderStyle: "round", borderColor: "red", children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", bold: true, children: ["Error: ", displayError] }), _jsx(Text, { color: "gray", dimColor: true, children: "Press any key to dismiss" })] }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Status: ", STATUS_ICONS.BUSY, " ", STATUS_LABELS.BUSY, ' ', STATUS_ICONS.WAITING, " ", STATUS_LABELS.WAITING, " ", STATUS_ICONS.IDLE, ' ', STATUS_LABELS.IDLE] }), _jsx(Text, { dimColor: true, children: isSearchMode
|
|
446
451
|
? 'Search Mode: Type to filter, Enter to exit search, ESC to exit search'
|
|
447
452
|
: searchQuery
|
|
448
453
|
? `Filtered: "${searchQuery}" | ↑↓ Navigate Enter Select | /-Search ESC-Clear 0-9 Quick Select R-Refresh Q-Quit`
|
package/dist/components/Menu.js
CHANGED
|
@@ -430,16 +430,18 @@ const Menu = ({ sessionManager, worktreeService, onMenuAction, onSelectRecentPro
|
|
|
430
430
|
});
|
|
431
431
|
}
|
|
432
432
|
};
|
|
433
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "green", children: ["CCManager - Claude Code Worktree Manager v", version] }), projectName && (_jsx(Text, { bold: true, color: "green", children: projectName }))] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Select a worktree to start or resume a Claude Code session:" }) }), _jsx(SearchableList, { isSearchMode: isSearchMode, searchQuery: searchQuery, onSearchQueryChange: setSearchQuery, selectedIndex: selectedIndex, items: items, limit: limit, placeholder: "Type to filter worktrees...", noMatchMessage: "No worktrees match your search", children: _jsx(SelectInput, { items: items, onSelect:
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (!item) {
|
|
433
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "green", children: ["CCManager - Claude Code Worktree Manager v", version] }), projectName && (_jsx(Text, { bold: true, color: "green", children: projectName }))] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Select a worktree to start or resume a Claude Code session:" }) }), _jsx(SearchableList, { isSearchMode: isSearchMode, searchQuery: searchQuery, onSearchQueryChange: setSearchQuery, selectedIndex: selectedIndex, items: items, limit: limit, placeholder: "Type to filter worktrees...", noMatchMessage: "No worktrees match your search", children: _jsx(SelectInput, { items: items, onSelect: raw => {
|
|
434
|
+
const item = items.find(i => i.value === raw?.value);
|
|
435
|
+
if (!item)
|
|
437
436
|
return;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
437
|
+
handleSelect(item);
|
|
438
|
+
}, onHighlight: raw => {
|
|
439
|
+
const item = items.find(i => i.value === raw?.value);
|
|
440
|
+
if (!item)
|
|
441
|
+
return;
|
|
442
|
+
if (item.type === 'worktree') {
|
|
443
|
+
setHighlightedWorktreePath(item.worktree.path);
|
|
444
|
+
setHighlightedSession(item.session);
|
|
443
445
|
}
|
|
444
446
|
}, isFocused: !error, initialIndex: selectedIndex, limit: limit }) }), (error || loadError) && (_jsx(Box, { marginTop: 1, paddingX: 1, borderStyle: "round", borderColor: "red", children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", bold: true, children: ["Error: ", error || loadError] }), _jsx(Text, { color: "gray", dimColor: true, children: "Press any key to dismiss" })] }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Status: ", STATUS_ICONS.BUSY, " ", STATUS_LABELS.BUSY, ' ', STATUS_ICONS.WAITING, " ", STATUS_LABELS.WAITING, " ", STATUS_ICONS.IDLE, ' ', STATUS_LABELS.IDLE, configReader.isAutoApprovalEnabled() && (_jsxs(_Fragment, { children: [' | ', _jsx(Text, { color: "green", children: "Auto Approval Enabled" })] }))] }), _jsx(Text, { dimColor: true, children: isSearchMode
|
|
445
447
|
? 'Search Mode: Type to filter, Enter to exit search, ESC to exit search'
|
|
@@ -37,18 +37,11 @@ const PresetSelector = ({ onSelect, onCancel, }) => {
|
|
|
37
37
|
};
|
|
38
38
|
// Find initial index based on default preset
|
|
39
39
|
const initialIndex = selectItems.findIndex(item => item.value === defaultPresetId);
|
|
40
|
-
|
|
40
|
+
// ink-select-input v6+ handles number keys 1-9 natively, so only handle ESC here
|
|
41
|
+
// to avoid double-firing onSelect (which would create two sessions for one worktree).
|
|
42
|
+
useInput((_input, key) => {
|
|
41
43
|
if (key.escape) {
|
|
42
44
|
onCancel();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
// Number keys 1-9: immediate launch
|
|
46
|
-
if (/^[1-9]$/.test(input)) {
|
|
47
|
-
const idx = parseInt(input) - 1;
|
|
48
|
-
if (idx < presets.length && presets[idx]) {
|
|
49
|
-
onSelect(presets[idx].id);
|
|
50
|
-
}
|
|
51
|
-
return;
|
|
52
45
|
}
|
|
53
46
|
});
|
|
54
47
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "green", children: "Select Command Preset" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Choose a preset to start the session with" }) }), _jsx(SelectInput, { items: selectItems, onSelect: handleSelectItem, initialIndex: initialIndex >= 0 ? initialIndex : 0 }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 Navigate 1-9 Quick Select Enter Select ESC Cancel" }) })] }));
|
|
@@ -86,26 +86,15 @@ describe('PresetSelector component', () => {
|
|
|
86
86
|
expect(output).toContain('(default)');
|
|
87
87
|
expect(output).toContain('← Cancel');
|
|
88
88
|
});
|
|
89
|
-
|
|
89
|
+
// Number key selection is handled by ink-select-input v6+ natively.
|
|
90
|
+
// PresetSelector's useInput only handles ESC to avoid double-firing onSelect
|
|
91
|
+
// (which would create two sessions for one worktree).
|
|
92
|
+
it('pressing number keys via useInput does NOT trigger onSelect', () => {
|
|
90
93
|
render(_jsx(PresetSelector, { onSelect: onSelect, onCancel: onCancel }));
|
|
91
94
|
expect(capturedHandlers.inputHandler).not.toBeNull();
|
|
92
95
|
capturedHandlers.inputHandler('1', makeKey());
|
|
93
|
-
expect(onSelect).toHaveBeenCalledWith('preset-1');
|
|
94
|
-
expect(onCancel).not.toHaveBeenCalled();
|
|
95
|
-
});
|
|
96
|
-
it('pressing 2 calls onSelect with second preset id immediately', () => {
|
|
97
|
-
render(_jsx(PresetSelector, { onSelect: onSelect, onCancel: onCancel }));
|
|
98
96
|
capturedHandlers.inputHandler('2', makeKey());
|
|
99
|
-
expect(onSelect).toHaveBeenCalledWith('preset-2');
|
|
100
|
-
});
|
|
101
|
-
it('pressing 3 calls onSelect with third preset id immediately', () => {
|
|
102
|
-
render(_jsx(PresetSelector, { onSelect: onSelect, onCancel: onCancel }));
|
|
103
97
|
capturedHandlers.inputHandler('3', makeKey());
|
|
104
|
-
expect(onSelect).toHaveBeenCalledWith('preset-3');
|
|
105
|
-
});
|
|
106
|
-
it('pressing a number beyond preset count does nothing', () => {
|
|
107
|
-
render(_jsx(PresetSelector, { onSelect: onSelect, onCancel: onCancel }));
|
|
108
|
-
capturedHandlers.inputHandler('9', makeKey());
|
|
109
98
|
expect(onSelect).not.toHaveBeenCalled();
|
|
110
99
|
expect(onCancel).not.toHaveBeenCalled();
|
|
111
100
|
});
|
|
@@ -566,7 +566,7 @@ export class AutoApprovalVerifier {
|
|
|
566
566
|
: await this.runClaudePrompt(prompt, jsonSchema, signal);
|
|
567
567
|
return JSON.parse(responseText);
|
|
568
568
|
},
|
|
569
|
-
catch: (error) => error,
|
|
569
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error)),
|
|
570
570
|
});
|
|
571
571
|
return Effect.catchAll(attemptVerification, (error) => {
|
|
572
572
|
if (error.name === 'AbortError') {
|
|
@@ -183,7 +183,10 @@ export class ProjectManager {
|
|
|
183
183
|
}
|
|
184
184
|
catch (error) {
|
|
185
185
|
// Silently skip directories we can't read
|
|
186
|
-
if (error
|
|
186
|
+
if (!(typeof error === 'object' &&
|
|
187
|
+
error !== null &&
|
|
188
|
+
'code' in error &&
|
|
189
|
+
error.code === 'EACCES')) {
|
|
187
190
|
console.error(`Error scanning directory ${dir}:`, error);
|
|
188
191
|
}
|
|
189
192
|
}
|
|
@@ -267,7 +270,7 @@ export class ProjectManager {
|
|
|
267
270
|
}
|
|
268
271
|
}
|
|
269
272
|
catch (error) {
|
|
270
|
-
result.error = `Failed to process: ${error.message}`;
|
|
273
|
+
result.error = `Failed to process: ${error instanceof Error ? error.message : String(error)}`;
|
|
271
274
|
}
|
|
272
275
|
return result;
|
|
273
276
|
}
|
|
@@ -363,8 +366,11 @@ export class ProjectManager {
|
|
|
363
366
|
if (error instanceof FileSystemError) {
|
|
364
367
|
return error;
|
|
365
368
|
}
|
|
366
|
-
const
|
|
367
|
-
|
|
369
|
+
const isEnoent = typeof error === 'object' &&
|
|
370
|
+
error !== null &&
|
|
371
|
+
'code' in error &&
|
|
372
|
+
error.code === 'ENOENT';
|
|
373
|
+
const cause = isEnoent
|
|
368
374
|
? `Projects directory does not exist: ${projectsDir}`
|
|
369
375
|
: String(error);
|
|
370
376
|
return new FileSystemError({
|
|
@@ -144,7 +144,7 @@ export class SessionManager extends EventEmitter {
|
|
|
144
144
|
})
|
|
145
145
|
.catch(async (error) => {
|
|
146
146
|
if (abortController.signal.aborted) {
|
|
147
|
-
logger.debug(`[${session.id}] Auto-approval verification aborted (${error
|
|
147
|
+
logger.debug(`[${session.id}] Auto-approval verification aborted (${error instanceof Error ? error.message : 'aborted'})`);
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
// On failure, fall back to requiring explicit permission
|
|
@@ -153,8 +153,9 @@ export class SessionManager extends EventEmitter {
|
|
|
153
153
|
if (currentState === 'pending_auto_approval') {
|
|
154
154
|
await this.updateSessionState(session, 'waiting_input', {
|
|
155
155
|
autoApprovalFailed: true,
|
|
156
|
-
autoApprovalReason: error
|
|
157
|
-
|
|
156
|
+
autoApprovalReason: error instanceof Error
|
|
157
|
+
? error.message
|
|
158
|
+
: 'Auto-approval verification failed',
|
|
158
159
|
});
|
|
159
160
|
}
|
|
160
161
|
})
|
|
@@ -775,10 +775,14 @@ export class WorktreeService {
|
|
|
775
775
|
copySessionData,
|
|
776
776
|
copyClaudeDirectory,
|
|
777
777
|
});
|
|
778
|
-
// Check if branch exists
|
|
779
|
-
|
|
778
|
+
// Check if a LOCAL branch exists (refs/heads/ only).
|
|
779
|
+
// Using `git rev-parse --verify` without qualifying the ref would also
|
|
780
|
+
// match remote-tracking refs (e.g. refs/remotes/origin/<branch>), which
|
|
781
|
+
// causes `git worktree add` to create the worktree in detached-HEAD
|
|
782
|
+
// state instead of on a proper local branch.
|
|
783
|
+
const localBranchExists = yield* Effect.catchAll(Effect.try({
|
|
780
784
|
try: () => {
|
|
781
|
-
execSync(`git
|
|
785
|
+
execSync(`git show-ref --verify --quiet refs/heads/${branch}`, {
|
|
782
786
|
cwd: self.rootPath,
|
|
783
787
|
encoding: 'utf8',
|
|
784
788
|
});
|
|
@@ -798,15 +802,23 @@ export class WorktreeService {
|
|
|
798
802
|
worktreeHooksConfig.pre_creation?.command) {
|
|
799
803
|
yield* executeWorktreePreCreationHook(worktreeHooksConfig.pre_creation.command, resolvedPath, branch, absoluteGitRoot, baseBranch);
|
|
800
804
|
}
|
|
801
|
-
// Create the worktree command
|
|
805
|
+
// Create the worktree command.
|
|
806
|
+
// Three cases:
|
|
807
|
+
// 1. Local branch exists → attach worktree directly
|
|
808
|
+
// 2. No local branch, but remote-tracking branch exists → create
|
|
809
|
+
// local branch from the remote ref (avoids detached HEAD)
|
|
810
|
+
// 3. Branch is brand-new → create from baseBranch
|
|
802
811
|
let command;
|
|
803
|
-
if (
|
|
812
|
+
if (localBranchExists) {
|
|
804
813
|
command = `git worktree add "${resolvedPath}" "${branch}"`;
|
|
805
814
|
}
|
|
806
815
|
else {
|
|
807
|
-
|
|
808
|
-
const
|
|
809
|
-
|
|
816
|
+
const resolvedRef = self.resolveBranchReference(branch);
|
|
817
|
+
const isRemoteBranch = resolvedRef !== branch;
|
|
818
|
+
const startPoint = isRemoteBranch
|
|
819
|
+
? resolvedRef
|
|
820
|
+
: self.resolveBranchReference(baseBranch);
|
|
821
|
+
command = `git worktree add -b "${branch}" "${resolvedPath}" "${startPoint}"`;
|
|
810
822
|
}
|
|
811
823
|
// Execute the worktree creation command
|
|
812
824
|
yield* Effect.try({
|
|
@@ -631,9 +631,15 @@ branch refs/heads/feature
|
|
|
631
631
|
if (cmd === 'git rev-parse --git-common-dir') {
|
|
632
632
|
return '/fake/path/.git\n';
|
|
633
633
|
}
|
|
634
|
-
if (cmd.includes('
|
|
634
|
+
if (cmd.includes('show-ref --verify --quiet refs/heads/')) {
|
|
635
635
|
throw new Error('Branch not found');
|
|
636
636
|
}
|
|
637
|
+
if (cmd === 'git remote') {
|
|
638
|
+
return 'origin\n';
|
|
639
|
+
}
|
|
640
|
+
if (cmd.includes('show-ref --verify --quiet refs/remotes/')) {
|
|
641
|
+
throw new Error('Remote branch not found');
|
|
642
|
+
}
|
|
637
643
|
if (cmd.includes('git worktree add')) {
|
|
638
644
|
return '';
|
|
639
645
|
}
|
|
@@ -648,15 +654,62 @@ branch refs/heads/feature
|
|
|
648
654
|
isMainWorktree: false,
|
|
649
655
|
});
|
|
650
656
|
});
|
|
657
|
+
it('should create local branch from remote ref when only remote branch exists', async () => {
|
|
658
|
+
const executedCommands = [];
|
|
659
|
+
mockedExecSync.mockImplementation((cmd, _options) => {
|
|
660
|
+
if (typeof cmd === 'string') {
|
|
661
|
+
executedCommands.push(cmd);
|
|
662
|
+
if (cmd === 'git rev-parse --git-common-dir') {
|
|
663
|
+
return '/fake/path/.git\n';
|
|
664
|
+
}
|
|
665
|
+
// No local branch
|
|
666
|
+
if (cmd.includes('show-ref --verify --quiet refs/heads/')) {
|
|
667
|
+
throw new Error('Branch not found');
|
|
668
|
+
}
|
|
669
|
+
if (cmd === 'git remote') {
|
|
670
|
+
return 'origin\n';
|
|
671
|
+
}
|
|
672
|
+
// Remote branch exists
|
|
673
|
+
if (cmd ===
|
|
674
|
+
'git show-ref --verify --quiet refs/remotes/origin/feature/remote-only') {
|
|
675
|
+
return '';
|
|
676
|
+
}
|
|
677
|
+
if (cmd.includes('show-ref --verify --quiet refs/remotes/')) {
|
|
678
|
+
throw new Error('Remote branch not found');
|
|
679
|
+
}
|
|
680
|
+
if (cmd.includes('git worktree add')) {
|
|
681
|
+
return '';
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return '';
|
|
685
|
+
});
|
|
686
|
+
const effect = service.createWorktreeEffect('/path/to/worktree', 'feature/remote-only', 'main');
|
|
687
|
+
const result = await Effect.runPromise(effect);
|
|
688
|
+
expect(result.worktree).toMatchObject({
|
|
689
|
+
path: '/path/to/worktree',
|
|
690
|
+
branch: 'feature/remote-only',
|
|
691
|
+
isMainWorktree: false,
|
|
692
|
+
});
|
|
693
|
+
// Should use -b with the remote ref as start point, not baseBranch
|
|
694
|
+
const worktreeAddCmd = executedCommands.find(c => c.includes('git worktree add'));
|
|
695
|
+
expect(worktreeAddCmd).toContain('-b "feature/remote-only"');
|
|
696
|
+
expect(worktreeAddCmd).toContain('"origin/feature/remote-only"');
|
|
697
|
+
});
|
|
651
698
|
it('should return Effect that fails with GitError on git command failure', async () => {
|
|
652
699
|
mockedExecSync.mockImplementation((cmd, _options) => {
|
|
653
700
|
if (typeof cmd === 'string') {
|
|
654
701
|
if (cmd === 'git rev-parse --git-common-dir') {
|
|
655
702
|
return '/fake/path/.git\n';
|
|
656
703
|
}
|
|
657
|
-
if (cmd.includes('
|
|
704
|
+
if (cmd.includes('show-ref --verify --quiet refs/heads/')) {
|
|
658
705
|
throw new Error('Branch not found');
|
|
659
706
|
}
|
|
707
|
+
if (cmd === 'git remote') {
|
|
708
|
+
return 'origin\n';
|
|
709
|
+
}
|
|
710
|
+
if (cmd.includes('show-ref --verify --quiet refs/remotes/')) {
|
|
711
|
+
throw new Error('Remote branch not found');
|
|
712
|
+
}
|
|
660
713
|
if (cmd.includes('git worktree add')) {
|
|
661
714
|
const error = new Error('fatal: invalid reference: main');
|
|
662
715
|
error.status = 128;
|
|
@@ -696,9 +749,15 @@ branch refs/heads/feature
|
|
|
696
749
|
if (cmd === 'git rev-parse --git-common-dir') {
|
|
697
750
|
return '/fake/path/.git\n';
|
|
698
751
|
}
|
|
699
|
-
if (cmd.includes('
|
|
752
|
+
if (cmd.includes('show-ref --verify --quiet refs/heads/')) {
|
|
700
753
|
throw new Error('Branch not found');
|
|
701
754
|
}
|
|
755
|
+
if (cmd === 'git remote') {
|
|
756
|
+
return 'origin\n';
|
|
757
|
+
}
|
|
758
|
+
if (cmd.includes('show-ref --verify --quiet refs/remotes/')) {
|
|
759
|
+
throw new Error('Remote branch not found');
|
|
760
|
+
}
|
|
702
761
|
if (cmd.includes('git worktree add')) {
|
|
703
762
|
throw new Error('git worktree add should not be called');
|
|
704
763
|
}
|
|
@@ -732,9 +791,15 @@ branch refs/heads/feature
|
|
|
732
791
|
if (cmd === 'git rev-parse --git-common-dir') {
|
|
733
792
|
return '/fake/path/.git\n';
|
|
734
793
|
}
|
|
735
|
-
if (cmd.includes('
|
|
794
|
+
if (cmd.includes('show-ref --verify --quiet refs/heads/')) {
|
|
736
795
|
throw new Error('Branch not found');
|
|
737
796
|
}
|
|
797
|
+
if (cmd === 'git remote') {
|
|
798
|
+
return 'origin\n';
|
|
799
|
+
}
|
|
800
|
+
if (cmd.includes('show-ref --verify --quiet refs/remotes/')) {
|
|
801
|
+
throw new Error('Remote branch not found');
|
|
802
|
+
}
|
|
738
803
|
if (cmd.includes('git worktree add')) {
|
|
739
804
|
return '';
|
|
740
805
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.17",
|
|
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": "4.1.
|
|
45
|
-
"@kodaikabasawa/ccmanager-darwin-x64": "4.1.
|
|
46
|
-
"@kodaikabasawa/ccmanager-linux-arm64": "4.1.
|
|
47
|
-
"@kodaikabasawa/ccmanager-linux-x64": "4.1.
|
|
48
|
-
"@kodaikabasawa/ccmanager-win32-x64": "4.1.
|
|
44
|
+
"@kodaikabasawa/ccmanager-darwin-arm64": "4.1.17",
|
|
45
|
+
"@kodaikabasawa/ccmanager-darwin-x64": "4.1.17",
|
|
46
|
+
"@kodaikabasawa/ccmanager-linux-arm64": "4.1.17",
|
|
47
|
+
"@kodaikabasawa/ccmanager-linux-x64": "4.1.17",
|
|
48
|
+
"@kodaikabasawa/ccmanager-win32-x64": "4.1.17"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.28.0",
|