ccmanager 4.1.9 → 4.1.10
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/App.js
CHANGED
|
@@ -566,7 +566,7 @@ const App = ({ devcontainerConfig, multiProject, version, }) => {
|
|
|
566
566
|
return (_jsx(Box, { flexDirection: "column", children: _jsx(LoadingSpinner, { message: message, color: "cyan" }) }));
|
|
567
567
|
}
|
|
568
568
|
if (view === 'merge-worktree') {
|
|
569
|
-
return (_jsxs(Box, { flexDirection: "column", children: [error && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) })), _jsx(MergeWorktree, { onComplete: handleReturnToMenu, onCancel: handleReturnToMenu })] }));
|
|
569
|
+
return (_jsxs(Box, { flexDirection: "column", children: [error && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) })), _jsx(MergeWorktree, { projectPath: selectedProject?.path, onComplete: handleReturnToMenu, onCancel: handleReturnToMenu })] }));
|
|
570
570
|
}
|
|
571
571
|
if (view === 'configuration') {
|
|
572
572
|
return (_jsx(Configuration, { scope: configScope, onComplete: handleReturnToMenu }));
|
|
@@ -9,7 +9,7 @@ import Confirmation, { SimpleConfirmation } from './Confirmation.js';
|
|
|
9
9
|
import { shortcutManager } from '../services/shortcutManager.js';
|
|
10
10
|
import { GitError } from '../types/errors.js';
|
|
11
11
|
import { hasUncommittedChanges } from '../utils/gitUtils.js';
|
|
12
|
-
const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
12
|
+
const MergeWorktree = ({ projectPath, onComplete, onCancel, }) => {
|
|
13
13
|
const [step, setStep] = useState('select-source');
|
|
14
14
|
const [sourceBranch, setSourceBranch] = useState('');
|
|
15
15
|
const [targetBranch, setTargetBranch] = useState('');
|
|
@@ -17,7 +17,6 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
17
17
|
const [originalBranchItems, setOriginalBranchItems] = useState([]);
|
|
18
18
|
const [operation, setOperation] = useState('merge');
|
|
19
19
|
const [mergeError, setMergeError] = useState(null);
|
|
20
|
-
const [worktreeService] = useState(() => new WorktreeService());
|
|
21
20
|
const [mergeConfig] = useState(() => configReader.getMergeConfig());
|
|
22
21
|
const [isLoading, setIsLoading] = useState(true);
|
|
23
22
|
const [loadError, setLoadError] = useState(null);
|
|
@@ -25,6 +24,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
25
24
|
let cancelled = false;
|
|
26
25
|
const loadWorktrees = async () => {
|
|
27
26
|
try {
|
|
27
|
+
const worktreeService = new WorktreeService(projectPath);
|
|
28
28
|
const loadedWorktrees = await Effect.runPromise(worktreeService.getWorktreesEffect());
|
|
29
29
|
if (!cancelled) {
|
|
30
30
|
// Create branch items for selection
|
|
@@ -56,7 +56,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
56
56
|
return () => {
|
|
57
57
|
cancelled = true;
|
|
58
58
|
};
|
|
59
|
-
}, [
|
|
59
|
+
}, [projectPath]);
|
|
60
60
|
useInput((input, key) => {
|
|
61
61
|
if (shortcutManager.matchesShortcut('cancel', input, key)) {
|
|
62
62
|
onCancel();
|
|
@@ -85,6 +85,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
85
85
|
return;
|
|
86
86
|
const performMerge = async () => {
|
|
87
87
|
try {
|
|
88
|
+
const worktreeService = new WorktreeService(projectPath);
|
|
88
89
|
await Effect.runPromise(worktreeService.mergeWorktreeEffect(sourceBranch, targetBranch, operation, mergeConfig));
|
|
89
90
|
// Merge successful, check for uncommitted changes before asking about deletion
|
|
90
91
|
setStep('check-uncommitted');
|
|
@@ -101,14 +102,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
101
102
|
}
|
|
102
103
|
};
|
|
103
104
|
performMerge();
|
|
104
|
-
}, [
|
|
105
|
-
step,
|
|
106
|
-
sourceBranch,
|
|
107
|
-
targetBranch,
|
|
108
|
-
operation,
|
|
109
|
-
mergeConfig,
|
|
110
|
-
worktreeService,
|
|
111
|
-
]);
|
|
105
|
+
}, [step, sourceBranch, targetBranch, operation, mergeConfig, projectPath]);
|
|
112
106
|
// Check for uncommitted changes in source worktree when entering check-uncommitted step
|
|
113
107
|
useEffect(() => {
|
|
114
108
|
if (step !== 'check-uncommitted')
|
|
@@ -116,6 +110,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
116
110
|
const checkUncommitted = async () => {
|
|
117
111
|
try {
|
|
118
112
|
// Find the worktree path for the source branch
|
|
113
|
+
const worktreeService = new WorktreeService(projectPath);
|
|
119
114
|
const worktrees = await Effect.runPromise(worktreeService.getWorktreesEffect());
|
|
120
115
|
const sourceWorktree = worktrees.find(wt => wt.branch && wt.branch.replace('refs/heads/', '') === sourceBranch);
|
|
121
116
|
if (sourceWorktree && hasUncommittedChanges(sourceWorktree.path)) {
|
|
@@ -131,7 +126,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
131
126
|
}
|
|
132
127
|
};
|
|
133
128
|
checkUncommitted();
|
|
134
|
-
}, [step, sourceBranch,
|
|
129
|
+
}, [step, sourceBranch, projectPath]);
|
|
135
130
|
if (isLoading) {
|
|
136
131
|
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { color: "cyan", children: "Loading worktrees..." }) }));
|
|
137
132
|
}
|
|
@@ -197,6 +192,7 @@ const MergeWorktree = ({ onComplete, onCancel, }) => {
|
|
|
197
192
|
return (_jsx(SimpleConfirmation, { message: deleteMessage, onConfirm: async () => {
|
|
198
193
|
try {
|
|
199
194
|
// Find the worktree path for the source branch
|
|
195
|
+
const worktreeService = new WorktreeService(projectPath);
|
|
200
196
|
const worktrees = await Effect.runPromise(worktreeService.getWorktreesEffect());
|
|
201
197
|
const sourceWorktree = worktrees.find(wt => wt.branch &&
|
|
202
198
|
wt.branch.replace('refs/heads/', '') === sourceBranch);
|
|
@@ -40,6 +40,61 @@ describe('MergeWorktree - Effect Integration', () => {
|
|
|
40
40
|
beforeEach(() => {
|
|
41
41
|
vi.clearAllMocks();
|
|
42
42
|
});
|
|
43
|
+
it('should pass projectPath to WorktreeService when provided', async () => {
|
|
44
|
+
const projectPath = '/test/project';
|
|
45
|
+
const mockWorktrees = [
|
|
46
|
+
{
|
|
47
|
+
path: '/test/project/main',
|
|
48
|
+
branch: 'main',
|
|
49
|
+
isMainWorktree: true,
|
|
50
|
+
hasSession: false,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
path: '/test/project/feature',
|
|
54
|
+
branch: 'feature-1',
|
|
55
|
+
isMainWorktree: false,
|
|
56
|
+
hasSession: false,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const mockEffect = Effect.succeed(mockWorktrees);
|
|
60
|
+
vi.mocked(WorktreeService).mockImplementation(function () {
|
|
61
|
+
return {
|
|
62
|
+
getWorktreesEffect: vi.fn(() => mockEffect),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
const onComplete = vi.fn();
|
|
66
|
+
const onCancel = vi.fn();
|
|
67
|
+
render(_jsx(MergeWorktree, { projectPath: projectPath, onComplete: onComplete, onCancel: onCancel }));
|
|
68
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
69
|
+
expect(WorktreeService).toHaveBeenCalledWith(projectPath);
|
|
70
|
+
});
|
|
71
|
+
it('should use undefined when projectPath not provided', async () => {
|
|
72
|
+
const mockWorktrees = [
|
|
73
|
+
{
|
|
74
|
+
path: '/test/main',
|
|
75
|
+
branch: 'main',
|
|
76
|
+
isMainWorktree: true,
|
|
77
|
+
hasSession: false,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
path: '/test/feature',
|
|
81
|
+
branch: 'feature-1',
|
|
82
|
+
isMainWorktree: false,
|
|
83
|
+
hasSession: false,
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
const mockEffect = Effect.succeed(mockWorktrees);
|
|
87
|
+
vi.mocked(WorktreeService).mockImplementation(function () {
|
|
88
|
+
return {
|
|
89
|
+
getWorktreesEffect: vi.fn(() => mockEffect),
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
const onComplete = vi.fn();
|
|
93
|
+
const onCancel = vi.fn();
|
|
94
|
+
render(_jsx(MergeWorktree, { onComplete: onComplete, onCancel: onCancel }));
|
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
96
|
+
expect(WorktreeService).toHaveBeenCalledWith(undefined);
|
|
97
|
+
});
|
|
43
98
|
it('should load worktrees using Effect-based method', async () => {
|
|
44
99
|
// GIVEN: Mock worktrees returned by Effect
|
|
45
100
|
const mockWorktrees = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccmanager",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.10",
|
|
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.10",
|
|
45
|
+
"@kodaikabasawa/ccmanager-darwin-x64": "4.1.10",
|
|
46
|
+
"@kodaikabasawa/ccmanager-linux-arm64": "4.1.10",
|
|
47
|
+
"@kodaikabasawa/ccmanager-linux-x64": "4.1.10",
|
|
48
|
+
"@kodaikabasawa/ccmanager-win32-x64": "4.1.10"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.28.0",
|