@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.
Files changed (132) hide show
  1. package/README.ja.md +323 -0
  2. package/README.md +347 -0
  3. package/bin/gwt.js +5 -0
  4. package/package.json +125 -0
  5. package/src/claude-history.ts +717 -0
  6. package/src/claude.ts +292 -0
  7. package/src/cli/ui/__tests__/SKIPPED_TESTS.md +119 -0
  8. package/src/cli/ui/__tests__/acceptance/branchList.acceptance.test.tsx.skip +239 -0
  9. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +214 -0
  10. package/src/cli/ui/__tests__/acceptance/realtimeUpdate.acceptance.test.tsx.skip +219 -0
  11. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +183 -0
  12. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +313 -0
  13. package/src/cli/ui/__tests__/components/App.test.tsx +270 -0
  14. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +66 -0
  15. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +103 -0
  16. package/src/cli/ui/__tests__/components/common/Input.test.tsx +92 -0
  17. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +127 -0
  18. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +264 -0
  19. package/src/cli/ui/__tests__/components/common/Select.test.tsx +246 -0
  20. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +62 -0
  21. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +54 -0
  22. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +68 -0
  23. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +135 -0
  24. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +153 -0
  25. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +215 -0
  26. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +293 -0
  27. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +161 -0
  28. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +215 -0
  29. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +99 -0
  30. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +127 -0
  31. package/src/cli/ui/__tests__/hooks/useGitData.test.ts.skip +228 -0
  32. package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +146 -0
  33. package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +98 -0
  34. package/src/cli/ui/__tests__/integration/branchList.test.tsx.skip +253 -0
  35. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +306 -0
  36. package/src/cli/ui/__tests__/integration/navigation.test.tsx +405 -0
  37. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +505 -0
  38. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx.skip +216 -0
  39. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +180 -0
  40. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +237 -0
  41. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +775 -0
  42. package/src/cli/ui/__tests__/utils/statisticsCalculator.test.ts +243 -0
  43. package/src/cli/ui/components/App.tsx +793 -0
  44. package/src/cli/ui/components/common/Confirm.tsx +40 -0
  45. package/src/cli/ui/components/common/ErrorBoundary.tsx +57 -0
  46. package/src/cli/ui/components/common/Input.tsx +36 -0
  47. package/src/cli/ui/components/common/LoadingIndicator.tsx +95 -0
  48. package/src/cli/ui/components/common/Select.tsx +216 -0
  49. package/src/cli/ui/components/parts/Footer.tsx +41 -0
  50. package/src/cli/ui/components/parts/Header.test.tsx +85 -0
  51. package/src/cli/ui/components/parts/Header.tsx +63 -0
  52. package/src/cli/ui/components/parts/MergeStatusList.tsx +75 -0
  53. package/src/cli/ui/components/parts/ProgressBar.tsx +73 -0
  54. package/src/cli/ui/components/parts/ScrollableList.tsx +24 -0
  55. package/src/cli/ui/components/parts/Stats.tsx +67 -0
  56. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +116 -0
  57. package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +70 -0
  58. package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +104 -0
  59. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +213 -0
  60. package/src/cli/ui/components/screens/BranchListScreen.tsx +299 -0
  61. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +149 -0
  62. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +167 -0
  63. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +100 -0
  64. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +117 -0
  65. package/src/cli/ui/hooks/useBatchMerge.ts +96 -0
  66. package/src/cli/ui/hooks/useGitData.ts +157 -0
  67. package/src/cli/ui/hooks/useScreenState.ts +44 -0
  68. package/src/cli/ui/hooks/useTerminalSize.ts +33 -0
  69. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +102 -0
  70. package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +151 -0
  71. package/src/cli/ui/types.ts +295 -0
  72. package/src/cli/ui/utils/baseBranch.ts +34 -0
  73. package/src/cli/ui/utils/branchFormatter.ts +222 -0
  74. package/src/cli/ui/utils/statisticsCalculator.ts +44 -0
  75. package/src/codex.ts +139 -0
  76. package/src/config/builtin-tools.ts +44 -0
  77. package/src/config/constants.ts +100 -0
  78. package/src/config/env-history.ts +45 -0
  79. package/src/config/index.ts +204 -0
  80. package/src/config/tools.ts +293 -0
  81. package/src/git.ts +1102 -0
  82. package/src/github.ts +158 -0
  83. package/src/index.test.ts +87 -0
  84. package/src/index.ts +684 -0
  85. package/src/index.ts.backup +1543 -0
  86. package/src/launcher.ts +142 -0
  87. package/src/repositories/git.repository.ts +129 -0
  88. package/src/repositories/github.repository.ts +83 -0
  89. package/src/repositories/worktree.repository.ts +69 -0
  90. package/src/services/BatchMergeService.ts +251 -0
  91. package/src/services/WorktreeOrchestrator.ts +115 -0
  92. package/src/services/__tests__/BatchMergeService.test.ts +518 -0
  93. package/src/services/__tests__/WorktreeOrchestrator.test.ts +258 -0
  94. package/src/services/dependency-installer.ts +199 -0
  95. package/src/services/git.service.ts +113 -0
  96. package/src/services/github.service.ts +61 -0
  97. package/src/services/worktree.service.ts +66 -0
  98. package/src/types/api.ts +241 -0
  99. package/src/types/tools.ts +235 -0
  100. package/src/utils/spinner.ts +54 -0
  101. package/src/utils/terminal.ts +272 -0
  102. package/src/utils.test.ts +43 -0
  103. package/src/utils.ts +60 -0
  104. package/src/web/client/index.html +12 -0
  105. package/src/web/client/src/components/BranchGraph.tsx +231 -0
  106. package/src/web/client/src/components/EnvEditor.tsx +145 -0
  107. package/src/web/client/src/components/Terminal.tsx +137 -0
  108. package/src/web/client/src/hooks/useBranches.ts +41 -0
  109. package/src/web/client/src/hooks/useConfig.ts +31 -0
  110. package/src/web/client/src/hooks/useSessions.ts +59 -0
  111. package/src/web/client/src/hooks/useWorktrees.ts +47 -0
  112. package/src/web/client/src/index.css +834 -0
  113. package/src/web/client/src/lib/api.ts +184 -0
  114. package/src/web/client/src/lib/websocket.ts +174 -0
  115. package/src/web/client/src/main.tsx +29 -0
  116. package/src/web/client/src/pages/BranchDetailPage.tsx +847 -0
  117. package/src/web/client/src/pages/BranchListPage.tsx +264 -0
  118. package/src/web/client/src/pages/ConfigManagementPage.tsx +203 -0
  119. package/src/web/client/src/router.tsx +27 -0
  120. package/src/web/client/vite.config.ts +21 -0
  121. package/src/web/server/env/importer.ts +54 -0
  122. package/src/web/server/index.ts +74 -0
  123. package/src/web/server/pty/manager.ts +189 -0
  124. package/src/web/server/routes/branches.ts +126 -0
  125. package/src/web/server/routes/config.ts +220 -0
  126. package/src/web/server/routes/index.ts +37 -0
  127. package/src/web/server/routes/sessions.ts +130 -0
  128. package/src/web/server/routes/worktrees.ts +108 -0
  129. package/src/web/server/services/branches.ts +368 -0
  130. package/src/web/server/services/worktrees.ts +85 -0
  131. package/src/web/server/websocket/handler.ts +180 -0
  132. 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
+ }