@akiojin/gwt 2.0.0
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/README.ja.md +323 -0
- package/README.md +347 -0
- package/bin/gwt.js +5 -0
- package/package.json +125 -0
- package/src/claude-history.ts +717 -0
- package/src/claude.ts +292 -0
- package/src/cli/ui/__tests__/SKIPPED_TESTS.md +119 -0
- package/src/cli/ui/__tests__/acceptance/branchList.acceptance.test.tsx.skip +239 -0
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +214 -0
- package/src/cli/ui/__tests__/acceptance/realtimeUpdate.acceptance.test.tsx.skip +219 -0
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +183 -0
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +313 -0
- package/src/cli/ui/__tests__/components/App.test.tsx +270 -0
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +66 -0
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +103 -0
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +92 -0
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +127 -0
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +264 -0
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +246 -0
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +62 -0
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +54 -0
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +68 -0
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +135 -0
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +153 -0
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +215 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +293 -0
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +161 -0
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +215 -0
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +99 -0
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +127 -0
- package/src/cli/ui/__tests__/hooks/useGitData.test.ts.skip +228 -0
- package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +146 -0
- package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +98 -0
- package/src/cli/ui/__tests__/integration/branchList.test.tsx.skip +253 -0
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +306 -0
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +405 -0
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +505 -0
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx.skip +216 -0
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +180 -0
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +237 -0
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +775 -0
- package/src/cli/ui/__tests__/utils/statisticsCalculator.test.ts +243 -0
- package/src/cli/ui/components/App.tsx +793 -0
- package/src/cli/ui/components/common/Confirm.tsx +40 -0
- package/src/cli/ui/components/common/ErrorBoundary.tsx +57 -0
- package/src/cli/ui/components/common/Input.tsx +36 -0
- package/src/cli/ui/components/common/LoadingIndicator.tsx +95 -0
- package/src/cli/ui/components/common/Select.tsx +216 -0
- package/src/cli/ui/components/parts/Footer.tsx +41 -0
- package/src/cli/ui/components/parts/Header.test.tsx +85 -0
- package/src/cli/ui/components/parts/Header.tsx +63 -0
- package/src/cli/ui/components/parts/MergeStatusList.tsx +75 -0
- package/src/cli/ui/components/parts/ProgressBar.tsx +73 -0
- package/src/cli/ui/components/parts/ScrollableList.tsx +24 -0
- package/src/cli/ui/components/parts/Stats.tsx +67 -0
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +116 -0
- package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +70 -0
- package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +104 -0
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +213 -0
- package/src/cli/ui/components/screens/BranchListScreen.tsx +299 -0
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +149 -0
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +167 -0
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +100 -0
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +117 -0
- package/src/cli/ui/hooks/useBatchMerge.ts +96 -0
- package/src/cli/ui/hooks/useGitData.ts +157 -0
- package/src/cli/ui/hooks/useScreenState.ts +44 -0
- package/src/cli/ui/hooks/useTerminalSize.ts +33 -0
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +102 -0
- package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +151 -0
- package/src/cli/ui/types.ts +295 -0
- package/src/cli/ui/utils/baseBranch.ts +34 -0
- package/src/cli/ui/utils/branchFormatter.ts +222 -0
- package/src/cli/ui/utils/statisticsCalculator.ts +44 -0
- package/src/codex.ts +139 -0
- package/src/config/builtin-tools.ts +44 -0
- package/src/config/constants.ts +100 -0
- package/src/config/env-history.ts +45 -0
- package/src/config/index.ts +204 -0
- package/src/config/tools.ts +293 -0
- package/src/git.ts +1102 -0
- package/src/github.ts +158 -0
- package/src/index.test.ts +87 -0
- package/src/index.ts +684 -0
- package/src/index.ts.backup +1543 -0
- package/src/launcher.ts +142 -0
- package/src/repositories/git.repository.ts +129 -0
- package/src/repositories/github.repository.ts +83 -0
- package/src/repositories/worktree.repository.ts +69 -0
- package/src/services/BatchMergeService.ts +251 -0
- package/src/services/WorktreeOrchestrator.ts +115 -0
- package/src/services/__tests__/BatchMergeService.test.ts +518 -0
- package/src/services/__tests__/WorktreeOrchestrator.test.ts +258 -0
- package/src/services/dependency-installer.ts +199 -0
- package/src/services/git.service.ts +113 -0
- package/src/services/github.service.ts +61 -0
- package/src/services/worktree.service.ts +66 -0
- package/src/types/api.ts +241 -0
- package/src/types/tools.ts +235 -0
- package/src/utils/spinner.ts +54 -0
- package/src/utils/terminal.ts +272 -0
- package/src/utils.test.ts +43 -0
- package/src/utils.ts +60 -0
- package/src/web/client/index.html +12 -0
- package/src/web/client/src/components/BranchGraph.tsx +231 -0
- package/src/web/client/src/components/EnvEditor.tsx +145 -0
- package/src/web/client/src/components/Terminal.tsx +137 -0
- package/src/web/client/src/hooks/useBranches.ts +41 -0
- package/src/web/client/src/hooks/useConfig.ts +31 -0
- package/src/web/client/src/hooks/useSessions.ts +59 -0
- package/src/web/client/src/hooks/useWorktrees.ts +47 -0
- package/src/web/client/src/index.css +834 -0
- package/src/web/client/src/lib/api.ts +184 -0
- package/src/web/client/src/lib/websocket.ts +174 -0
- package/src/web/client/src/main.tsx +29 -0
- package/src/web/client/src/pages/BranchDetailPage.tsx +847 -0
- package/src/web/client/src/pages/BranchListPage.tsx +264 -0
- package/src/web/client/src/pages/ConfigManagementPage.tsx +203 -0
- package/src/web/client/src/router.tsx +27 -0
- package/src/web/client/vite.config.ts +21 -0
- package/src/web/server/env/importer.ts +54 -0
- package/src/web/server/index.ts +74 -0
- package/src/web/server/pty/manager.ts +189 -0
- package/src/web/server/routes/branches.ts +126 -0
- package/src/web/server/routes/config.ts +220 -0
- package/src/web/server/routes/index.ts +37 -0
- package/src/web/server/routes/sessions.ts +130 -0
- package/src/web/server/routes/worktrees.ts +108 -0
- package/src/web/server/services/branches.ts +368 -0
- package/src/web/server/services/worktrees.ts +85 -0
- package/src/web/server/websocket/handler.ts +180 -0
- package/src/worktree.ts +703 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export interface TerminalSize {
|
|
4
|
+
rows: number;
|
|
5
|
+
columns: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook to get current terminal size and listen for resize events
|
|
10
|
+
*/
|
|
11
|
+
export function useTerminalSize(): TerminalSize {
|
|
12
|
+
const [size, setSize] = useState<TerminalSize>(() => ({
|
|
13
|
+
rows: process.stdout.rows || 24,
|
|
14
|
+
columns: process.stdout.columns || 80,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handleResize = () => {
|
|
19
|
+
setSize({
|
|
20
|
+
rows: process.stdout.rows || 24,
|
|
21
|
+
columns: process.stdout.columns || 80,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
process.stdout.on("resize", handleResize);
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
process.stdout.removeListener("resize", handleResize);
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
return size;
|
|
33
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { Select, type SelectItem } from "../components/common/Select.js";
|
|
4
|
+
import { Footer } from "../components/parts/Footer.js";
|
|
5
|
+
import type { BranchAction } from "../types.js";
|
|
6
|
+
|
|
7
|
+
export interface BranchActionSelectorScreenProps {
|
|
8
|
+
selectedBranch: string;
|
|
9
|
+
onUseExisting: () => void;
|
|
10
|
+
onCreateNew: () => void;
|
|
11
|
+
onBack: () => void;
|
|
12
|
+
canCreateNew?: boolean;
|
|
13
|
+
mode?: "default" | "protected";
|
|
14
|
+
infoMessage?: string | null;
|
|
15
|
+
primaryLabel?: string;
|
|
16
|
+
secondaryLabel?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* BranchActionSelectorScreen - Screen for selecting action after branch selection
|
|
21
|
+
*
|
|
22
|
+
* Allows user to choose between:
|
|
23
|
+
* - Using existing branch (continue to AI tool selection)
|
|
24
|
+
* - Creating new branch from selected branch (go to branch creator)
|
|
25
|
+
*/
|
|
26
|
+
export function BranchActionSelectorScreen({
|
|
27
|
+
selectedBranch,
|
|
28
|
+
onUseExisting,
|
|
29
|
+
onCreateNew,
|
|
30
|
+
onBack,
|
|
31
|
+
canCreateNew = true,
|
|
32
|
+
mode = "default",
|
|
33
|
+
infoMessage,
|
|
34
|
+
primaryLabel,
|
|
35
|
+
secondaryLabel,
|
|
36
|
+
}: BranchActionSelectorScreenProps) {
|
|
37
|
+
// Handle keyboard input for back navigation
|
|
38
|
+
useInput((input, key) => {
|
|
39
|
+
if (key.escape) {
|
|
40
|
+
onBack();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const primaryActionLabel =
|
|
45
|
+
primaryLabel ??
|
|
46
|
+
(mode === "protected" ? "Switch to root branch" : "Use existing branch");
|
|
47
|
+
const secondaryActionLabel =
|
|
48
|
+
secondaryLabel ??
|
|
49
|
+
(mode === "protected" ? "Create new branch from this branch" : "Create new branch");
|
|
50
|
+
|
|
51
|
+
const items: SelectItem[] = [
|
|
52
|
+
{
|
|
53
|
+
label: primaryActionLabel,
|
|
54
|
+
value: "use-existing",
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
if (canCreateNew) {
|
|
59
|
+
items.push({
|
|
60
|
+
label: secondaryActionLabel,
|
|
61
|
+
value: "create-new",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const handleSelect = (item: SelectItem) => {
|
|
66
|
+
const action = item.value as BranchAction;
|
|
67
|
+
|
|
68
|
+
if (action === "use-existing") {
|
|
69
|
+
onUseExisting();
|
|
70
|
+
} else if (action === "create-new") {
|
|
71
|
+
onCreateNew();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Footer actions
|
|
76
|
+
const footerActions = [
|
|
77
|
+
{ key: 'enter', description: 'Select' },
|
|
78
|
+
{ key: 'esc', description: 'Back' },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Box flexDirection="column">
|
|
83
|
+
<Box marginBottom={1}>
|
|
84
|
+
<Text>
|
|
85
|
+
Selected branch: <Text bold color="cyan">{selectedBranch}</Text>
|
|
86
|
+
</Text>
|
|
87
|
+
</Box>
|
|
88
|
+
{infoMessage ? (
|
|
89
|
+
<Box marginBottom={1}>
|
|
90
|
+
<Text color="yellow">{infoMessage}</Text>
|
|
91
|
+
</Box>
|
|
92
|
+
) : null}
|
|
93
|
+
<Box marginBottom={1}>
|
|
94
|
+
<Text color="gray">Choose an action:</Text>
|
|
95
|
+
</Box>
|
|
96
|
+
<Select items={items} onSelect={handleSelect} />
|
|
97
|
+
|
|
98
|
+
{/* Footer */}
|
|
99
|
+
<Footer actions={footerActions} />
|
|
100
|
+
</Box>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment happy-dom
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
5
|
+
import { render } from "@testing-library/react";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { BranchActionSelectorScreen } from "../BranchActionSelectorScreen.js";
|
|
8
|
+
import { Window } from "happy-dom";
|
|
9
|
+
|
|
10
|
+
describe("BranchActionSelectorScreen", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
// Setup happy-dom
|
|
13
|
+
const window = new Window();
|
|
14
|
+
globalThis.window = window as any;
|
|
15
|
+
globalThis.document = window.document as any;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render the screen", () => {
|
|
19
|
+
const onUseExisting = vi.fn();
|
|
20
|
+
const onCreateNew = vi.fn();
|
|
21
|
+
const onBack = vi.fn();
|
|
22
|
+
const { container } = render(
|
|
23
|
+
<BranchActionSelectorScreen
|
|
24
|
+
selectedBranch="feature-test"
|
|
25
|
+
onUseExisting={onUseExisting}
|
|
26
|
+
onCreateNew={onCreateNew}
|
|
27
|
+
onBack={onBack}
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(container).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should display the message with selected branch name", () => {
|
|
35
|
+
const onUseExisting = vi.fn();
|
|
36
|
+
const onCreateNew = vi.fn();
|
|
37
|
+
const onBack = vi.fn();
|
|
38
|
+
const { getByText } = render(
|
|
39
|
+
<BranchActionSelectorScreen
|
|
40
|
+
selectedBranch="feature-test"
|
|
41
|
+
onUseExisting={onUseExisting}
|
|
42
|
+
onCreateNew={onCreateNew}
|
|
43
|
+
onBack={onBack}
|
|
44
|
+
/>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Should show message about selecting action for the branch
|
|
48
|
+
const messageElement = getByText(/feature-test/);
|
|
49
|
+
expect(messageElement).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should display two action options", () => {
|
|
53
|
+
const onUseExisting = vi.fn();
|
|
54
|
+
const onCreateNew = vi.fn();
|
|
55
|
+
const onBack = vi.fn();
|
|
56
|
+
const { getByText } = render(
|
|
57
|
+
<BranchActionSelectorScreen
|
|
58
|
+
selectedBranch="feature-test"
|
|
59
|
+
onUseExisting={onUseExisting}
|
|
60
|
+
onCreateNew={onCreateNew}
|
|
61
|
+
onBack={onBack}
|
|
62
|
+
/>,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Should show "Use existing branch" option
|
|
66
|
+
expect(getByText(/Use existing branch/)).toBeDefined();
|
|
67
|
+
|
|
68
|
+
// Should show "Create new branch" option
|
|
69
|
+
expect(getByText(/Create new branch/)).toBeDefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should render protected mode labels and info message", () => {
|
|
73
|
+
const onUseExisting = vi.fn();
|
|
74
|
+
const onCreateNew = vi.fn();
|
|
75
|
+
const onBack = vi.fn();
|
|
76
|
+
const { getByText } = render(
|
|
77
|
+
<BranchActionSelectorScreen
|
|
78
|
+
selectedBranch="main"
|
|
79
|
+
onUseExisting={onUseExisting}
|
|
80
|
+
onCreateNew={onCreateNew}
|
|
81
|
+
onBack={onBack}
|
|
82
|
+
mode="protected"
|
|
83
|
+
infoMessage="Root branches are handled in the repository root."
|
|
84
|
+
primaryLabel="Use root branch"
|
|
85
|
+
secondaryLabel="Create new branch from root"
|
|
86
|
+
/>,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(getByText(/Use root branch/)).toBeDefined();
|
|
90
|
+
expect(getByText(/Create new branch from root/)).toBeDefined();
|
|
91
|
+
expect(
|
|
92
|
+
getByText(/Root branches are handled in the repository root./),
|
|
93
|
+
).toBeDefined();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should hide create option when canCreateNew is false", () => {
|
|
97
|
+
const onUseExisting = vi.fn();
|
|
98
|
+
const onCreateNew = vi.fn();
|
|
99
|
+
const onBack = vi.fn();
|
|
100
|
+
const { getByText, queryByText } = render(
|
|
101
|
+
<BranchActionSelectorScreen
|
|
102
|
+
selectedBranch="main"
|
|
103
|
+
onUseExisting={onUseExisting}
|
|
104
|
+
onCreateNew={onCreateNew}
|
|
105
|
+
onBack={onBack}
|
|
106
|
+
canCreateNew={false}
|
|
107
|
+
/>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(getByText(/Use existing branch/)).toBeDefined();
|
|
111
|
+
expect(queryByText(/Create new branch/)).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should call onUseExisting when existing branch option is selected", () => {
|
|
115
|
+
const onUseExisting = vi.fn();
|
|
116
|
+
const onCreateNew = vi.fn();
|
|
117
|
+
const onBack = vi.fn();
|
|
118
|
+
|
|
119
|
+
render(
|
|
120
|
+
<BranchActionSelectorScreen
|
|
121
|
+
selectedBranch="feature-test"
|
|
122
|
+
onUseExisting={onUseExisting}
|
|
123
|
+
onCreateNew={onCreateNew}
|
|
124
|
+
onBack={onBack}
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Note: Simulating selection requires ink-testing-library
|
|
129
|
+
// For now, we verify the component structure and callbacks are set up
|
|
130
|
+
expect(onUseExisting).not.toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should call onCreateNew when create new branch option is selected", () => {
|
|
134
|
+
const onUseExisting = vi.fn();
|
|
135
|
+
const onCreateNew = vi.fn();
|
|
136
|
+
const onBack = vi.fn();
|
|
137
|
+
|
|
138
|
+
render(
|
|
139
|
+
<BranchActionSelectorScreen
|
|
140
|
+
selectedBranch="feature-test"
|
|
141
|
+
onUseExisting={onUseExisting}
|
|
142
|
+
onCreateNew={onCreateNew}
|
|
143
|
+
onBack={onBack}
|
|
144
|
+
/>,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Note: Simulating selection requires ink-testing-library
|
|
148
|
+
// For now, we verify the component structure and callbacks are set up
|
|
149
|
+
expect(onCreateNew).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
export interface WorktreeInfo {
|
|
2
|
+
path: string;
|
|
3
|
+
locked: boolean;
|
|
4
|
+
prunable: boolean;
|
|
5
|
+
isAccessible?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface BranchInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
type: "local" | "remote";
|
|
11
|
+
branchType: "feature" | "hotfix" | "release" | "main" | "develop" | "other";
|
|
12
|
+
isCurrent: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
worktree?: WorktreeInfo;
|
|
15
|
+
hasUnpushedCommits?: boolean;
|
|
16
|
+
openPR?: { number: number; title: string };
|
|
17
|
+
mergedPR?: { number: number; mergedAt: string };
|
|
18
|
+
latestCommitTimestamp?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface BranchChoice {
|
|
22
|
+
name: string;
|
|
23
|
+
value: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
disabled?: boolean | string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface EnhancedBranchChoice extends BranchChoice {
|
|
29
|
+
hasWorktree: boolean;
|
|
30
|
+
worktreePath?: string;
|
|
31
|
+
branchType: BranchInfo["branchType"];
|
|
32
|
+
branchDataType: "local" | "remote";
|
|
33
|
+
isCurrent: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type BranchType =
|
|
37
|
+
| "feature"
|
|
38
|
+
| "hotfix"
|
|
39
|
+
| "release"
|
|
40
|
+
| "main"
|
|
41
|
+
| "develop"
|
|
42
|
+
| "other";
|
|
43
|
+
|
|
44
|
+
export interface NewBranchConfig {
|
|
45
|
+
type: BranchType;
|
|
46
|
+
taskName: string;
|
|
47
|
+
branchName: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface WorktreeConfig {
|
|
51
|
+
branchName: string;
|
|
52
|
+
worktreePath: string;
|
|
53
|
+
repoRoot: string;
|
|
54
|
+
isNewBranch: boolean;
|
|
55
|
+
baseBranch: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CleanupResult {
|
|
59
|
+
hasChanges: boolean;
|
|
60
|
+
committed: boolean;
|
|
61
|
+
pushed: boolean;
|
|
62
|
+
worktreeRemoved: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface BranchGroup {
|
|
66
|
+
title: string;
|
|
67
|
+
branches: EnhancedBranchChoice[];
|
|
68
|
+
priority: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SelectedBranchState {
|
|
72
|
+
name: string;
|
|
73
|
+
displayName: string;
|
|
74
|
+
branchType: "local" | "remote";
|
|
75
|
+
branchCategory: BranchInfo["branchType"];
|
|
76
|
+
remoteBranch?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface UIFilter {
|
|
80
|
+
showWithWorktree: boolean;
|
|
81
|
+
showWithoutWorktree: boolean;
|
|
82
|
+
branchTypes: BranchInfo["branchType"][];
|
|
83
|
+
showLocal: boolean;
|
|
84
|
+
showRemote: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface PullRequest {
|
|
88
|
+
number: number;
|
|
89
|
+
title: string;
|
|
90
|
+
state: "OPEN" | "CLOSED" | "MERGED";
|
|
91
|
+
branch: string;
|
|
92
|
+
mergedAt: string | null;
|
|
93
|
+
author: string;
|
|
94
|
+
baseRefName?: string | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface MergedPullRequest {
|
|
98
|
+
number: number;
|
|
99
|
+
title: string;
|
|
100
|
+
branch: string;
|
|
101
|
+
mergedAt: string;
|
|
102
|
+
author: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface WorktreeWithPR {
|
|
106
|
+
worktreePath: string;
|
|
107
|
+
branch: string;
|
|
108
|
+
pullRequest: PullRequest | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type CleanupReason = "merged-pr" | "no-diff-with-base";
|
|
112
|
+
|
|
113
|
+
export interface CleanupTarget {
|
|
114
|
+
worktreePath: string | null; // null for local branch only cleanup
|
|
115
|
+
branch: string;
|
|
116
|
+
pullRequest: MergedPullRequest | null;
|
|
117
|
+
hasUncommittedChanges: boolean;
|
|
118
|
+
hasUnpushedCommits: boolean;
|
|
119
|
+
cleanupType: "worktree-and-branch" | "branch-only";
|
|
120
|
+
hasRemoteBranch?: boolean;
|
|
121
|
+
isAccessible?: boolean;
|
|
122
|
+
invalidReason?: string;
|
|
123
|
+
reasons?: CleanupReason[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface GitHubPRAuthor {
|
|
127
|
+
id?: string;
|
|
128
|
+
is_bot?: boolean;
|
|
129
|
+
login?: string;
|
|
130
|
+
name?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface GitHubPRResponse {
|
|
134
|
+
number: number;
|
|
135
|
+
title: string;
|
|
136
|
+
state: string;
|
|
137
|
+
headRefName: string;
|
|
138
|
+
mergedAt: string | null;
|
|
139
|
+
author: GitHubPRAuthor | null;
|
|
140
|
+
baseRefName?: string | null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ========================================
|
|
144
|
+
// Ink.js UI Types (Phase 2+)
|
|
145
|
+
// ========================================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Screen types for Ink.js UI
|
|
149
|
+
*/
|
|
150
|
+
export type ScreenType =
|
|
151
|
+
| "branch-list"
|
|
152
|
+
| "worktree-manager"
|
|
153
|
+
| "branch-creator"
|
|
154
|
+
| "branch-action-selector"
|
|
155
|
+
| "ai-tool-selector"
|
|
156
|
+
| "session-selector"
|
|
157
|
+
| "execution-mode-selector"
|
|
158
|
+
| "batch-merge-progress"
|
|
159
|
+
| "batch-merge-result";
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Branch action types for action selector screen
|
|
163
|
+
*/
|
|
164
|
+
export type BranchAction = "use-existing" | "create-new";
|
|
165
|
+
|
|
166
|
+
export type ScreenState = "active" | "hidden";
|
|
167
|
+
|
|
168
|
+
export interface Screen {
|
|
169
|
+
type: ScreenType;
|
|
170
|
+
state: ScreenState;
|
|
171
|
+
data?: unknown;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* BranchItem - Extended BranchInfo for display purposes
|
|
176
|
+
*/
|
|
177
|
+
export type WorktreeStatus = "active" | "inaccessible" | undefined;
|
|
178
|
+
|
|
179
|
+
export interface BranchItem extends BranchInfo {
|
|
180
|
+
// Display properties
|
|
181
|
+
icons: string[];
|
|
182
|
+
worktreeStatus?: WorktreeStatus;
|
|
183
|
+
hasChanges: boolean;
|
|
184
|
+
label: string;
|
|
185
|
+
value: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Statistics - Real-time statistics
|
|
190
|
+
*/
|
|
191
|
+
export interface Statistics {
|
|
192
|
+
localCount: number;
|
|
193
|
+
remoteCount: number;
|
|
194
|
+
worktreeCount: number;
|
|
195
|
+
changesCount: number;
|
|
196
|
+
lastUpdated: Date;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Layout - Dynamic layout information
|
|
201
|
+
*/
|
|
202
|
+
export interface Layout {
|
|
203
|
+
terminalHeight: number;
|
|
204
|
+
terminalWidth: number;
|
|
205
|
+
headerLines: number;
|
|
206
|
+
footerLines: number;
|
|
207
|
+
contentHeight: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ========================================
|
|
211
|
+
// Batch Merge Types (SPEC-ee33ca26)
|
|
212
|
+
// ========================================
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* BatchMergeConfig - Configuration for batch merge execution
|
|
216
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
217
|
+
*/
|
|
218
|
+
export interface BatchMergeConfig {
|
|
219
|
+
sourceBranch: string;
|
|
220
|
+
targetBranches: string[];
|
|
221
|
+
dryRun: boolean;
|
|
222
|
+
autoPush: boolean;
|
|
223
|
+
remote?: string; // default: "origin"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* MergePhase - Current phase of merge operation
|
|
228
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
229
|
+
*/
|
|
230
|
+
export type MergePhase = "fetch" | "worktree" | "merge" | "push" | "cleanup";
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* BatchMergeProgress - Real-time progress information
|
|
234
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
235
|
+
*/
|
|
236
|
+
export interface BatchMergeProgress {
|
|
237
|
+
currentBranch: string;
|
|
238
|
+
currentIndex: number;
|
|
239
|
+
totalBranches: number;
|
|
240
|
+
percentage: number; // 0-100
|
|
241
|
+
elapsedSeconds: number;
|
|
242
|
+
estimatedRemainingSeconds?: number;
|
|
243
|
+
currentPhase: MergePhase;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* MergeStatus - Status of individual branch merge
|
|
248
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
249
|
+
*/
|
|
250
|
+
export type MergeStatus = "success" | "skipped" | "failed";
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* PushStatus - Status of push operation
|
|
254
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
255
|
+
*/
|
|
256
|
+
export type PushStatus = "success" | "failed" | "not_executed";
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* BranchMergeStatus - Individual branch merge result
|
|
260
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
261
|
+
*/
|
|
262
|
+
export interface BranchMergeStatus {
|
|
263
|
+
branchName: string;
|
|
264
|
+
status: MergeStatus;
|
|
265
|
+
error?: string;
|
|
266
|
+
conflictFiles?: string[];
|
|
267
|
+
pushStatus?: PushStatus;
|
|
268
|
+
worktreeCreated: boolean;
|
|
269
|
+
durationSeconds: number;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* BatchMergeSummary - Summary statistics
|
|
274
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
275
|
+
*/
|
|
276
|
+
export interface BatchMergeSummary {
|
|
277
|
+
totalCount: number;
|
|
278
|
+
successCount: number;
|
|
279
|
+
skippedCount: number;
|
|
280
|
+
failedCount: number;
|
|
281
|
+
pushedCount: number;
|
|
282
|
+
pushFailedCount: number;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* BatchMergeResult - Final batch merge result
|
|
287
|
+
* @see specs/SPEC-ee33ca26/data-model.md
|
|
288
|
+
*/
|
|
289
|
+
export interface BatchMergeResult {
|
|
290
|
+
statuses: BranchMergeStatus[];
|
|
291
|
+
summary: BatchMergeSummary;
|
|
292
|
+
totalDurationSeconds: number;
|
|
293
|
+
cancelled: boolean;
|
|
294
|
+
config: BatchMergeConfig;
|
|
295
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SelectedBranchState } from "../types.js";
|
|
2
|
+
|
|
3
|
+
function getBranchRef(
|
|
4
|
+
branch: SelectedBranchState | null | undefined,
|
|
5
|
+
): string | null {
|
|
6
|
+
if (!branch) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
return branch.remoteBranch ?? branch.name;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveBaseBranchRef(
|
|
13
|
+
creationSource: SelectedBranchState | null,
|
|
14
|
+
selectedBranch: SelectedBranchState | null,
|
|
15
|
+
resolveDefault: () => string,
|
|
16
|
+
): string {
|
|
17
|
+
return (
|
|
18
|
+
getBranchRef(creationSource) ??
|
|
19
|
+
getBranchRef(selectedBranch) ??
|
|
20
|
+
resolveDefault()
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveBaseBranchLabel(
|
|
25
|
+
creationSource: SelectedBranchState | null,
|
|
26
|
+
selectedBranch: SelectedBranchState | null,
|
|
27
|
+
resolveDefault: () => string,
|
|
28
|
+
): string {
|
|
29
|
+
return (
|
|
30
|
+
creationSource?.displayName ??
|
|
31
|
+
selectedBranch?.displayName ??
|
|
32
|
+
resolveDefault()
|
|
33
|
+
);
|
|
34
|
+
}
|