@akiojin/gwt 2.2.0 → 2.4.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 (172) hide show
  1. package/README.ja.md +6 -4
  2. package/README.md +6 -4
  3. package/dist/claude.d.ts +1 -0
  4. package/dist/claude.d.ts.map +1 -1
  5. package/dist/claude.js +6 -3
  6. package/dist/claude.js.map +1 -1
  7. package/dist/cli/ui/components/App.d.ts +6 -4
  8. package/dist/cli/ui/components/App.d.ts.map +1 -1
  9. package/dist/cli/ui/components/App.js +184 -107
  10. package/dist/cli/ui/components/App.js.map +1 -1
  11. package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
  12. package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
  13. package/dist/cli/ui/components/common/Confirm.js +7 -7
  14. package/dist/cli/ui/components/common/Confirm.js.map +1 -1
  15. package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
  16. package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
  17. package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
  18. package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
  19. package/dist/cli/ui/components/common/Input.d.ts +2 -2
  20. package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
  21. package/dist/cli/ui/components/common/Input.js +4 -4
  22. package/dist/cli/ui/components/common/Input.js.map +1 -1
  23. package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
  24. package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
  25. package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
  26. package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
  27. package/dist/cli/ui/components/common/Select.d.ts +1 -1
  28. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  29. package/dist/cli/ui/components/common/Select.js +11 -12
  30. package/dist/cli/ui/components/common/Select.js.map +1 -1
  31. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +3 -3
  32. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
  33. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
  34. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
  35. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
  36. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
  37. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
  38. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
  39. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -3
  40. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/components/screens/BranchListScreen.js +55 -50
  42. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  43. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
  44. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  45. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
  46. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  47. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +18 -0
  48. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -0
  49. package/dist/cli/ui/components/screens/ModelSelectorScreen.js +201 -0
  50. package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -0
  51. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
  52. package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
  53. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
  54. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
  55. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
  56. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
  57. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
  58. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
  59. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
  60. package/dist/cli/ui/types.d.ts +11 -1
  61. package/dist/cli/ui/types.d.ts.map +1 -1
  62. package/dist/cli/ui/utils/modelOptions.d.ts +6 -0
  63. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -0
  64. package/dist/cli/ui/utils/modelOptions.js +111 -0
  65. package/dist/cli/ui/utils/modelOptions.js.map +1 -0
  66. package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
  67. package/dist/client/index.html +1 -1
  68. package/dist/codex.d.ts +6 -0
  69. package/dist/codex.d.ts.map +1 -1
  70. package/dist/codex.js +11 -4
  71. package/dist/codex.js.map +1 -1
  72. package/dist/config/builtin-tools.d.ts +10 -2
  73. package/dist/config/builtin-tools.d.ts.map +1 -1
  74. package/dist/config/builtin-tools.js +40 -4
  75. package/dist/config/builtin-tools.js.map +1 -1
  76. package/dist/config/index.d.ts.map +1 -1
  77. package/dist/config/index.js.map +1 -1
  78. package/dist/config/tools.d.ts.map +1 -1
  79. package/dist/config/tools.js +4 -3
  80. package/dist/config/tools.js.map +1 -1
  81. package/dist/gemini.d.ts +13 -0
  82. package/dist/gemini.d.ts.map +1 -0
  83. package/dist/gemini.js +157 -0
  84. package/dist/gemini.js.map +1 -0
  85. package/dist/git.d.ts.map +1 -1
  86. package/dist/git.js.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +59 -7
  89. package/dist/index.js.map +1 -1
  90. package/dist/qwen.d.ts +13 -0
  91. package/dist/qwen.d.ts.map +1 -0
  92. package/dist/qwen.js +157 -0
  93. package/dist/qwen.js.map +1 -0
  94. package/dist/services/git.service.d.ts.map +1 -1
  95. package/dist/services/git.service.js.map +1 -1
  96. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  97. package/dist/web/client/src/components/BranchGraph.js +1 -1
  98. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  99. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  100. package/dist/web/client/src/components/EnvEditor.js +7 -4
  101. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  102. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  103. package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
  104. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  105. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  106. package/dist/web/client/src/pages/BranchListPage.js +10 -4
  107. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  108. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  109. package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
  110. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  111. package/package.json +2 -1
  112. package/src/claude.ts +8 -3
  113. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
  114. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
  115. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
  116. package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
  117. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +81 -0
  118. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
  119. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
  120. package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
  121. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +63 -43
  122. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
  123. package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
  124. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
  125. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
  126. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
  127. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
  128. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
  129. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
  130. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +261 -153
  131. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
  132. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
  133. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
  134. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
  135. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
  136. package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
  137. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
  138. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
  139. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
  140. package/src/cli/ui/components/App.tsx +317 -150
  141. package/src/cli/ui/components/common/Confirm.tsx +13 -9
  142. package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
  143. package/src/cli/ui/components/common/Input.tsx +12 -4
  144. package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
  145. package/src/cli/ui/components/common/Select.tsx +28 -17
  146. package/src/cli/ui/components/parts/Header.test.tsx +5 -15
  147. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +20 -15
  148. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
  149. package/src/cli/ui/components/screens/BranchListScreen.tsx +92 -75
  150. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
  151. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +320 -0
  152. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
  153. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
  154. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
  155. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
  156. package/src/cli/ui/types.ts +21 -1
  157. package/src/cli/ui/utils/modelOptions.test.ts +36 -0
  158. package/src/cli/ui/utils/modelOptions.ts +122 -0
  159. package/src/codex.ts +23 -4
  160. package/src/config/builtin-tools.ts +42 -4
  161. package/src/config/index.ts +2 -12
  162. package/src/config/tools.ts +16 -6
  163. package/src/gemini.ts +207 -0
  164. package/src/git.ts +2 -1
  165. package/src/index.ts +86 -6
  166. package/src/qwen.ts +213 -0
  167. package/src/services/git.service.ts +2 -1
  168. package/src/web/client/src/components/BranchGraph.tsx +3 -2
  169. package/src/web/client/src/components/EnvEditor.tsx +44 -11
  170. package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
  171. package/src/web/client/src/pages/BranchListPage.tsx +37 -13
  172. package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
@@ -1,22 +1,37 @@
1
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import { useApp } from 'ink';
3
- import { ErrorBoundary } from './common/ErrorBoundary.js';
4
- import { BranchListScreen } from './screens/BranchListScreen.js';
5
- import { WorktreeManagerScreen } from './screens/WorktreeManagerScreen.js';
6
- import { BranchCreatorScreen } from './screens/BranchCreatorScreen.js';
7
- import { BranchActionSelectorScreen } from '../screens/BranchActionSelectorScreen.js';
8
- import { AIToolSelectorScreen } from './screens/AIToolSelectorScreen.js';
9
- import { SessionSelectorScreen } from './screens/SessionSelectorScreen.js';
10
- import { ExecutionModeSelectorScreen } from './screens/ExecutionModeSelectorScreen.js';
11
- import type { AITool } from './screens/AIToolSelectorScreen.js';
12
- import type { ExecutionMode } from './screens/ExecutionModeSelectorScreen.js';
13
- import type { WorktreeItem } from './screens/WorktreeManagerScreen.js';
14
- import { useGitData } from '../hooks/useGitData.js';
15
- import { useScreenState } from '../hooks/useScreenState.js';
16
- import { formatBranchItems } from '../utils/branchFormatter.js';
17
- import { calculateStatistics } from '../utils/statisticsCalculator.js';
18
- import type { BranchInfo, BranchItem, SelectedBranchState } from '../types.js';
19
- import { getRepositoryRoot, deleteBranch } from '../../../git.js';
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from "react";
8
+ import { useApp } from "ink";
9
+ import { ErrorBoundary } from "./common/ErrorBoundary.js";
10
+ import { BranchListScreen } from "./screens/BranchListScreen.js";
11
+ import { WorktreeManagerScreen } from "./screens/WorktreeManagerScreen.js";
12
+ import { BranchCreatorScreen } from "./screens/BranchCreatorScreen.js";
13
+ import { BranchActionSelectorScreen } from "../screens/BranchActionSelectorScreen.js";
14
+ import { AIToolSelectorScreen } from "./screens/AIToolSelectorScreen.js";
15
+ import { SessionSelectorScreen } from "./screens/SessionSelectorScreen.js";
16
+ import { ExecutionModeSelectorScreen } from "./screens/ExecutionModeSelectorScreen.js";
17
+ import type { ExecutionMode } from "./screens/ExecutionModeSelectorScreen.js";
18
+ import {
19
+ ModelSelectorScreen,
20
+ type ModelSelectionResult,
21
+ } from "./screens/ModelSelectorScreen.js";
22
+ import type { WorktreeItem } from "./screens/WorktreeManagerScreen.js";
23
+ import { useGitData } from "../hooks/useGitData.js";
24
+ import { useScreenState } from "../hooks/useScreenState.js";
25
+ import { formatBranchItems } from "../utils/branchFormatter.js";
26
+ import { calculateStatistics } from "../utils/statisticsCalculator.js";
27
+ import type {
28
+ AITool,
29
+ BranchInfo,
30
+ BranchItem,
31
+ InferenceLevel,
32
+ SelectedBranchState,
33
+ } from "../types.js";
34
+ import { getRepositoryRoot, deleteBranch } from "../../../git.js";
20
35
  import {
21
36
  createWorktree,
22
37
  generateWorktreePath,
@@ -24,34 +39,40 @@ import {
24
39
  isProtectedBranchName,
25
40
  removeWorktree,
26
41
  switchToProtectedBranch,
27
- } from '../../../worktree.js';
28
- import { getPackageVersion } from '../../../utils.js';
42
+ } from "../../../worktree.js";
43
+ import { getPackageVersion } from "../../../utils.js";
29
44
  import {
30
45
  resolveBaseBranchLabel,
31
46
  resolveBaseBranchRef,
32
- } from '../utils/baseBranch.js';
47
+ } from "../utils/baseBranch.js";
48
+ import {
49
+ getDefaultInferenceForModel,
50
+ getDefaultModelOption,
51
+ } from "../utils/modelOptions.js";
33
52
 
34
- const SPINNER_FRAMES = ['', '', '', '', '', '', '', ''];
53
+ const SPINNER_FRAMES = ["", "", "", "", "", "", "", ""];
35
54
  const COMPLETION_HOLD_DURATION_MS = 3000;
36
55
  const PROTECTED_BRANCH_WARNING =
37
- 'Root branches operate directly in the repository root. Create a new branch if you need a dedicated worktree.';
56
+ "Root branches operate directly in the repository root. Create a new branch if you need a dedicated worktree.";
38
57
 
39
58
  const getSpinnerFrame = (index: number): string => {
40
59
  const frame = SPINNER_FRAMES[index];
41
- if (typeof frame === 'string') {
60
+ if (typeof frame === "string") {
42
61
  return frame;
43
62
  }
44
- return SPINNER_FRAMES[0] ?? '';
63
+ return SPINNER_FRAMES[0] ?? "";
45
64
  };
46
65
 
47
66
  export interface SelectionResult {
48
67
  branch: string; // Local branch name (without remote prefix)
49
68
  displayName: string; // Name that was selected in the UI (may include remote prefix)
50
- branchType: 'local' | 'remote';
69
+ branchType: "local" | "remote";
51
70
  remoteBranch?: string; // Full remote ref when branchType === 'remote'
52
71
  tool: AITool;
53
72
  mode: ExecutionMode;
54
73
  skipPermissions: boolean;
74
+ model?: string | null;
75
+ inferenceLevel?: InferenceLevel;
55
76
  }
56
77
 
57
78
  export interface AppProps {
@@ -69,24 +90,42 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
69
90
  // 起動ディレクトリの取得
70
91
  const workingDirectory = process.cwd();
71
92
 
72
- const { branches, worktrees, loading, error, refresh, lastUpdated } = useGitData({
73
- enableAutoRefresh: false, // Manual refresh with 'r' key
74
- });
93
+ const { branches, worktrees, loading, error, refresh, lastUpdated } =
94
+ useGitData({
95
+ enableAutoRefresh: false, // Manual refresh with 'r' key
96
+ });
75
97
  const { currentScreen, navigateTo, goBack } = useScreenState();
76
98
 
77
99
  // Version state
78
100
  const [version, setVersion] = useState<string | null>(null);
79
101
 
80
102
  // Selection state (for branch → tool → mode flow)
81
- const [selectedBranch, setSelectedBranch] = useState<SelectedBranchState | null>(null);
82
- const [creationSourceBranch, setCreationSourceBranch] = useState<SelectedBranchState | null>(null);
103
+ const [selectedBranch, setSelectedBranch] =
104
+ useState<SelectedBranchState | null>(null);
105
+ const [creationSourceBranch, setCreationSourceBranch] =
106
+ useState<SelectedBranchState | null>(null);
83
107
  const [selectedTool, setSelectedTool] = useState<AITool | null>(null);
108
+ const [selectedModel, setSelectedModel] =
109
+ useState<ModelSelectionResult | null>(null);
110
+ const [lastModelByTool, setLastModelByTool] = useState<
111
+ Record<AITool, ModelSelectionResult | undefined>
112
+ >({});
84
113
 
85
114
  // PR cleanup feedback
86
- const [cleanupIndicators, setCleanupIndicators] = useState<Record<string, { icon: string; color?: 'cyan' | 'green' | 'yellow' | 'red' }>>({});
87
- const [cleanupProcessingBranch, setCleanupProcessingBranch] = useState<string | null>(null);
115
+ const [cleanupIndicators, setCleanupIndicators] = useState<
116
+ Record<
117
+ string,
118
+ { icon: string; color?: "cyan" | "green" | "yellow" | "red" }
119
+ >
120
+ >({});
121
+ const [cleanupProcessingBranch, setCleanupProcessingBranch] = useState<
122
+ string | null
123
+ >(null);
88
124
  const [cleanupInputLocked, setCleanupInputLocked] = useState(false);
89
- const [cleanupFooterMessage, setCleanupFooterMessage] = useState<{ text: string; color?: 'cyan' | 'green' | 'yellow' | 'red' } | null>(null);
125
+ const [cleanupFooterMessage, setCleanupFooterMessage] = useState<{
126
+ text: string;
127
+ color?: "cyan" | "green" | "yellow" | "red";
128
+ } | null>(null);
90
129
  const [hiddenBranches, setHiddenBranches] = useState<string[]>([]);
91
130
  const spinnerFrameIndexRef = useRef(0);
92
131
  const [spinnerFrameIndex, setSpinnerFrameIndex] = useState(0);
@@ -107,7 +146,8 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
107
146
  }
108
147
 
109
148
  const interval = setInterval(() => {
110
- spinnerFrameIndexRef.current = (spinnerFrameIndexRef.current + 1) % SPINNER_FRAMES.length;
149
+ spinnerFrameIndexRef.current =
150
+ (spinnerFrameIndexRef.current + 1) % SPINNER_FRAMES.length;
111
151
  setSpinnerFrameIndex(spinnerFrameIndexRef.current);
112
152
  }, 120);
113
153
 
@@ -128,20 +168,23 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
128
168
  if (cleanupProcessingBranch) {
129
169
  setCleanupIndicators((prev) => {
130
170
  const current = prev[cleanupProcessingBranch];
131
- if (current && current.icon === frame && current.color === 'cyan') {
171
+ if (current && current.icon === frame && current.color === "cyan") {
132
172
  return prev;
133
173
  }
134
174
 
135
- const next: Record<string, { icon: string; color?: 'cyan' | 'green' | 'yellow' | 'red' }> = {
175
+ const next: Record<
176
+ string,
177
+ { icon: string; color?: "cyan" | "green" | "yellow" | "red" }
178
+ > = {
136
179
  ...prev,
137
- [cleanupProcessingBranch]: { icon: frame, color: 'cyan' },
180
+ [cleanupProcessingBranch]: { icon: frame, color: "cyan" },
138
181
  };
139
182
 
140
183
  return next;
141
184
  });
142
185
  }
143
186
 
144
- setCleanupFooterMessage({ text: `Processing... ${frame}`, color: 'cyan' });
187
+ setCleanupFooterMessage({ text: `Processing... ${frame}`, color: "cyan" });
145
188
  }, [cleanupInputLocked, cleanupProcessingBranch, spinnerFrameIndex]);
146
189
 
147
190
  useEffect(() => {
@@ -157,28 +200,34 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
157
200
  }
158
201
  }, [branches, hiddenBranches]);
159
202
 
160
- useEffect(() => () => {
161
- if (completionTimerRef.current) {
162
- clearTimeout(completionTimerRef.current);
163
- completionTimerRef.current = null;
164
- }
165
- }, []);
203
+ useEffect(
204
+ () => () => {
205
+ if (completionTimerRef.current) {
206
+ clearTimeout(completionTimerRef.current);
207
+ completionTimerRef.current = null;
208
+ }
209
+ },
210
+ [],
211
+ );
166
212
 
167
213
  const visibleBranches = useMemo(
168
214
  () => branches.filter((branch) => !hiddenBranches.includes(branch.name)),
169
- [branches, hiddenBranches]
215
+ [branches, hiddenBranches],
170
216
  );
171
217
 
172
218
  // Helper function to create content-based hash for branches
173
219
  const branchHash = useMemo(
174
- () => visibleBranches.map((b) => `${b.name}-${b.type}-${b.isCurrent}`).join(','),
175
- [visibleBranches]
220
+ () =>
221
+ visibleBranches
222
+ .map((b) => `${b.name}-${b.type}-${b.isCurrent}`)
223
+ .join(","),
224
+ [visibleBranches],
176
225
  );
177
226
 
178
227
  // Helper function to create content-based hash for worktrees
179
228
  const worktreeHash = useMemo(
180
- () => worktrees.map((w) => `${w.branch}-${w.path}`).join(','),
181
- [worktrees]
229
+ () => worktrees.map((w) => `${w.branch}-${w.path}`).join(","),
230
+ [worktrees],
182
231
  );
183
232
 
184
233
  // Format branches to BranchItems (memoized for performance with content-based dependencies)
@@ -197,76 +246,89 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
197
246
  }, [branchHash, worktreeHash, visibleBranches, worktrees]);
198
247
 
199
248
  // Calculate statistics (memoized for performance)
200
- const stats = useMemo(() => calculateStatistics(visibleBranches), [visibleBranches]);
249
+ const stats = useMemo(
250
+ () => calculateStatistics(visibleBranches),
251
+ [visibleBranches],
252
+ );
201
253
 
202
254
  // Format worktrees to WorktreeItems
203
255
  const worktreeItems: WorktreeItem[] = useMemo(
204
256
  () =>
205
- worktrees.map((wt): WorktreeItem => ({
206
- branch: wt.branch,
207
- path: wt.path,
208
- isAccessible: wt.isAccessible ?? true,
209
- })),
210
- [worktrees]
257
+ worktrees.map(
258
+ (wt): WorktreeItem => ({
259
+ branch: wt.branch,
260
+ path: wt.path,
261
+ isAccessible: wt.isAccessible ?? true,
262
+ }),
263
+ ),
264
+ [worktrees],
211
265
  );
212
266
 
213
267
  const resolveBaseBranch = useCallback(() => {
214
268
  const localMain = branches.find(
215
269
  (branch) =>
216
- branch.type === 'local' && (branch.name === 'main' || branch.name === 'master')
270
+ branch.type === "local" &&
271
+ (branch.name === "main" || branch.name === "master"),
217
272
  );
218
273
  if (localMain) {
219
274
  return localMain.name;
220
275
  }
221
276
 
222
277
  const develop = branches.find(
223
- (branch) => branch.type === 'local' && (branch.name === 'develop' || branch.name === 'dev')
278
+ (branch) =>
279
+ branch.type === "local" &&
280
+ (branch.name === "develop" || branch.name === "dev"),
224
281
  );
225
282
  if (develop) {
226
283
  return develop.name;
227
284
  }
228
285
 
229
- return 'main';
286
+ return "main";
230
287
  }, [branches]);
231
288
 
232
289
  const baseBranchLabel = useMemo(
233
- () => resolveBaseBranchLabel(creationSourceBranch, selectedBranch, resolveBaseBranch),
234
- [creationSourceBranch, resolveBaseBranch, selectedBranch]
290
+ () =>
291
+ resolveBaseBranchLabel(
292
+ creationSourceBranch,
293
+ selectedBranch,
294
+ resolveBaseBranch,
295
+ ),
296
+ [creationSourceBranch, resolveBaseBranch, selectedBranch],
235
297
  );
236
298
 
237
299
  // Handle branch selection
238
300
  const toLocalBranchName = useCallback((remoteName: string) => {
239
- const segments = remoteName.split('/');
301
+ const segments = remoteName.split("/");
240
302
  if (segments.length <= 1) {
241
303
  return remoteName;
242
304
  }
243
- return segments.slice(1).join('/');
305
+ return segments.slice(1).join("/");
244
306
  }, []);
245
307
 
246
308
  const inferBranchCategory = useCallback(
247
- (branchName: string): BranchInfo['branchType'] => {
309
+ (branchName: string): BranchInfo["branchType"] => {
248
310
  const matched = branches.find((branch) => branch.name === branchName);
249
311
  if (matched) {
250
312
  return matched.branchType;
251
313
  }
252
- if (branchName === 'main' || branchName === 'master') {
253
- return 'main';
314
+ if (branchName === "main" || branchName === "master") {
315
+ return "main";
254
316
  }
255
- if (branchName === 'develop' || branchName === 'dev') {
256
- return 'develop';
317
+ if (branchName === "develop" || branchName === "dev") {
318
+ return "develop";
257
319
  }
258
- if (branchName.startsWith('feature/')) {
259
- return 'feature';
320
+ if (branchName.startsWith("feature/")) {
321
+ return "feature";
260
322
  }
261
- if (branchName.startsWith('hotfix/')) {
262
- return 'hotfix';
323
+ if (branchName.startsWith("hotfix/")) {
324
+ return "hotfix";
263
325
  }
264
- if (branchName.startsWith('release/')) {
265
- return 'release';
326
+ if (branchName.startsWith("release/")) {
327
+ return "release";
266
328
  }
267
- return 'other';
329
+ return "other";
268
330
  },
269
- [branches]
331
+ [branches],
270
332
  );
271
333
 
272
334
  const isProtectedSelection = useCallback(
@@ -277,12 +339,14 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
277
339
  return (
278
340
  isProtectedBranchName(branch.name) ||
279
341
  isProtectedBranchName(branch.displayName) ||
280
- (branch.remoteBranch ? isProtectedBranchName(branch.remoteBranch) : false) ||
281
- branch.branchCategory === 'main' ||
282
- branch.branchCategory === 'develop'
342
+ (branch.remoteBranch
343
+ ? isProtectedBranchName(branch.remoteBranch)
344
+ : false) ||
345
+ branch.branchCategory === "main" ||
346
+ branch.branchCategory === "develop"
283
347
  );
284
348
  },
285
- [isProtectedBranchName]
349
+ [isProtectedBranchName],
286
350
  );
287
351
 
288
352
  const protectedBranchInfo = useMemo(() => {
@@ -302,18 +366,18 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
302
366
  const handleSelect = useCallback(
303
367
  (item: BranchItem) => {
304
368
  const selection: SelectedBranchState =
305
- item.type === 'remote'
369
+ item.type === "remote"
306
370
  ? {
307
371
  name: toLocalBranchName(item.name),
308
372
  displayName: item.name,
309
- branchType: 'remote',
373
+ branchType: "remote",
310
374
  branchCategory: item.branchType,
311
375
  remoteBranch: item.name,
312
376
  }
313
377
  : {
314
378
  name: item.name,
315
379
  displayName: item.name,
316
- branchType: 'local',
380
+ branchType: "local",
317
381
  branchCategory: item.branchType,
318
382
  };
319
383
 
@@ -321,20 +385,28 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
321
385
 
322
386
  setSelectedBranch(selection);
323
387
  setSelectedTool(null);
388
+ setSelectedModel(null);
324
389
  setCreationSourceBranch(null);
325
390
 
326
391
  if (protectedSelected) {
327
392
  setCleanupFooterMessage({
328
393
  text: PROTECTED_BRANCH_WARNING,
329
- color: 'yellow',
394
+ color: "yellow",
330
395
  });
331
396
  } else {
332
397
  setCleanupFooterMessage(null);
333
398
  }
334
399
 
335
- navigateTo('branch-action-selector');
400
+ navigateTo("branch-action-selector");
336
401
  },
337
- [isProtectedSelection, navigateTo, setCleanupFooterMessage, setCreationSourceBranch, setSelectedTool, toLocalBranchName]
402
+ [
403
+ isProtectedSelection,
404
+ navigateTo,
405
+ setCleanupFooterMessage,
406
+ setCreationSourceBranch,
407
+ setSelectedTool,
408
+ toLocalBranchName,
409
+ ],
338
410
  );
339
411
 
340
412
  // Handle navigation
@@ -342,7 +414,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
342
414
  (screen: string) => {
343
415
  navigateTo(screen as any);
344
416
  },
345
- [navigateTo]
417
+ [navigateTo],
346
418
  );
347
419
 
348
420
  const handleWorktreeSelect = useCallback(
@@ -350,15 +422,21 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
350
422
  setSelectedBranch({
351
423
  name: worktree.branch,
352
424
  displayName: worktree.branch,
353
- branchType: 'local',
425
+ branchType: "local",
354
426
  branchCategory: inferBranchCategory(worktree.branch),
355
427
  });
356
428
  setSelectedTool(null);
429
+ setSelectedModel(null);
357
430
  setCreationSourceBranch(null);
358
431
  setCleanupFooterMessage(null);
359
- navigateTo('ai-tool-selector');
432
+ navigateTo("ai-tool-selector");
360
433
  },
361
- [inferBranchCategory, navigateTo, setCleanupFooterMessage, setCreationSourceBranch]
434
+ [
435
+ inferBranchCategory,
436
+ navigateTo,
437
+ setCleanupFooterMessage,
438
+ setCreationSourceBranch,
439
+ ],
362
440
  );
363
441
 
364
442
  // Handle branch action selection
@@ -370,13 +448,13 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
370
448
  try {
371
449
  setCleanupFooterMessage({
372
450
  text: `Preparing root branch '${selectedBranch.displayName ?? selectedBranch.name}'...`,
373
- color: 'cyan',
451
+ color: "cyan",
374
452
  });
375
453
  const repoRoot = await getRepositoryRoot();
376
454
  const remoteRef =
377
455
  selectedBranch.remoteBranch ??
378
- (selectedBranch.branchType === 'remote'
379
- ? selectedBranch.displayName ?? selectedBranch.name
456
+ (selectedBranch.branchType === "remote"
457
+ ? (selectedBranch.displayName ?? selectedBranch.name)
380
458
  : null);
381
459
 
382
460
  const result = await switchToProtectedBranch({
@@ -386,45 +464,44 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
386
464
  });
387
465
 
388
466
  let successMessage = `'${selectedBranch.displayName ?? selectedBranch.name}' will use the repository root.`;
389
- if (result === 'remote') {
467
+ if (result === "remote") {
390
468
  successMessage = `Created a local tracking branch for '${selectedBranch.displayName ?? selectedBranch.name}' and switched to the protected branch.`;
391
- } else if (result === 'local') {
469
+ } else if (result === "local") {
392
470
  successMessage = `Checked out '${selectedBranch.displayName ?? selectedBranch.name}' in the repository root.`;
393
471
  }
394
472
 
395
473
  setCleanupFooterMessage({
396
474
  text: successMessage,
397
- color: 'green',
475
+ color: "green",
398
476
  });
399
477
  refresh();
400
- navigateTo('ai-tool-selector');
478
+ navigateTo("ai-tool-selector");
401
479
  } catch (error) {
402
- const message =
403
- error instanceof Error ? error.message : String(error);
480
+ const message = error instanceof Error ? error.message : String(error);
404
481
  setCleanupFooterMessage({
405
482
  text: `Failed to switch root branch: ${message}`,
406
- color: 'red',
483
+ color: "red",
407
484
  });
408
- console.error('Failed to switch protected branch:', error);
485
+ console.error("Failed to switch protected branch:", error);
409
486
  }
410
- }, [
411
- navigateTo,
412
- refresh,
413
- selectedBranch,
414
- setCleanupFooterMessage,
415
- ]);
487
+ }, [navigateTo, refresh, selectedBranch, setCleanupFooterMessage]);
416
488
 
417
489
  const handleUseExistingBranch = useCallback(() => {
418
490
  if (selectedBranch && isProtectedSelection(selectedBranch)) {
419
491
  void handleProtectedBranchSwitch();
420
492
  return;
421
493
  }
422
- navigateTo('ai-tool-selector');
423
- }, [handleProtectedBranchSwitch, isProtectedSelection, navigateTo, selectedBranch]);
494
+ navigateTo("ai-tool-selector");
495
+ }, [
496
+ handleProtectedBranchSwitch,
497
+ isProtectedSelection,
498
+ navigateTo,
499
+ selectedBranch,
500
+ ]);
424
501
 
425
502
  const handleCreateNewBranch = useCallback(() => {
426
503
  setCreationSourceBranch(selectedBranch);
427
- navigateTo('branch-creator');
504
+ navigateTo("branch-creator");
428
505
  }, [navigateTo, selectedBranch]);
429
506
 
430
507
  // Handle quit
@@ -459,16 +536,17 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
459
536
  setSelectedBranch({
460
537
  name: branchName,
461
538
  displayName: branchName,
462
- branchType: 'local',
539
+ branchType: "local",
463
540
  branchCategory: inferBranchCategory(branchName),
464
541
  });
465
542
  setSelectedTool(null);
543
+ setSelectedModel(null);
466
544
  setCleanupFooterMessage(null);
467
545
 
468
- navigateTo('ai-tool-selector');
546
+ navigateTo("ai-tool-selector");
469
547
  } catch (error) {
470
548
  // On error, go back to branch list
471
- console.error('Failed to create branch:', error);
549
+ console.error("Failed to create branch:", error);
472
550
  goBack();
473
551
  refresh();
474
552
  }
@@ -482,7 +560,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
482
560
  creationSourceBranch,
483
561
  inferBranchCategory,
484
562
  setCleanupFooterMessage,
485
- ]
563
+ ],
486
564
  );
487
565
 
488
566
  const handleCleanupCommand = useCallback(async () => {
@@ -516,7 +594,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
516
594
  setCleanupInputLocked(true);
517
595
  setCleanupIndicators({});
518
596
  const initialFrame = getSpinnerFrame(0);
519
- setCleanupFooterMessage({ text: `Processing... ${initialFrame}`, color: 'cyan' });
597
+ setCleanupFooterMessage({
598
+ text: `Processing... ${initialFrame}`,
599
+ color: "cyan",
600
+ });
520
601
  setCleanupProcessingBranch(null);
521
602
  spinnerFrameIndexRef.current = 0;
522
603
  setSpinnerFrameIndex(0);
@@ -527,7 +608,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
527
608
  } catch (error) {
528
609
  const message = error instanceof Error ? error.message : String(error);
529
610
  setCleanupIndicators({});
530
- setCleanupFooterMessage({ text: `❌ ${message}`, color: 'red' });
611
+ setCleanupFooterMessage({ text: `❌ ${message}`, color: "red" });
531
612
  setCleanupInputLocked(false);
532
613
  completionTimerRef.current = setTimeout(() => {
533
614
  setCleanupFooterMessage(null);
@@ -538,7 +619,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
538
619
 
539
620
  if (targets.length === 0) {
540
621
  setCleanupIndicators({});
541
- setCleanupFooterMessage({ text: '✅ Nothing to clean up.', color: 'green' });
622
+ setCleanupFooterMessage({
623
+ text: "✅ Nothing to clean up.",
624
+ color: "green",
625
+ });
542
626
  setCleanupInputLocked(false);
543
627
  completionTimerRef.current = setTimeout(() => {
544
628
  setCleanupFooterMessage(null);
@@ -548,11 +632,21 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
548
632
  }
549
633
 
550
634
  // Reset hidden branches that may already be gone
551
- setHiddenBranches((prev) => prev.filter((name) => targets.find((t) => t.branch === name) === undefined));
635
+ setHiddenBranches((prev) =>
636
+ prev.filter(
637
+ (name) => targets.find((t) => t.branch === name) === undefined,
638
+ ),
639
+ );
552
640
 
553
- const initialIndicators = targets.reduce<Record<string, { icon: string; color?: 'cyan' | 'green' | 'yellow' | 'red' }>>((acc, target, index) => {
554
- const icon = index === 0 ? getSpinnerFrame(0) : '⏳';
555
- const color: 'cyan' | 'green' | 'yellow' | 'red' = index === 0 ? 'cyan' : 'yellow';
641
+ const initialIndicators = targets.reduce<
642
+ Record<
643
+ string,
644
+ { icon: string; color?: "cyan" | "green" | "yellow" | "red" }
645
+ >
646
+ >((acc, target, index) => {
647
+ const icon = index === 0 ? getSpinnerFrame(0) : "⏳";
648
+ const color: "cyan" | "green" | "yellow" | "red" =
649
+ index === 0 ? "cyan" : "yellow";
556
650
  acc[target.branch] = { icon, color };
557
651
  return acc;
558
652
  }, {});
@@ -562,7 +656,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
562
656
  setCleanupProcessingBranch(firstTarget ? firstTarget.branch : null);
563
657
  spinnerFrameIndexRef.current = 0;
564
658
  setSpinnerFrameIndex(0);
565
- setCleanupFooterMessage({ text: `Processing... ${getSpinnerFrame(0)}`, color: 'cyan' });
659
+ setCleanupFooterMessage({
660
+ text: `Processing... ${getSpinnerFrame(0)}`,
661
+ color: "cyan",
662
+ });
566
663
 
567
664
  for (let index = 0; index < targets.length; index += 1) {
568
665
  const currentTarget = targets[index];
@@ -577,11 +674,11 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
577
674
 
578
675
  setCleanupIndicators((prev) => {
579
676
  const updated = { ...prev };
580
- updated[target.branch] = { icon: getSpinnerFrame(0), color: 'cyan' };
677
+ updated[target.branch] = { icon: getSpinnerFrame(0), color: "cyan" };
581
678
  for (const pending of targets.slice(index + 1)) {
582
679
  const current = updated[pending.branch];
583
- if (!current || current.icon !== '') {
584
- updated[pending.branch] = { icon: '', color: 'yellow' };
680
+ if (!current || current.icon !== "") {
681
+ updated[pending.branch] = { icon: "", color: "yellow" };
585
682
  }
586
683
  }
587
684
  return updated;
@@ -590,19 +687,23 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
590
687
  const shouldSkip =
591
688
  target.hasUncommittedChanges ||
592
689
  target.hasUnpushedCommits ||
593
- (target.cleanupType === 'worktree-and-branch' && (!target.worktreePath || target.isAccessible === false));
690
+ (target.cleanupType === "worktree-and-branch" &&
691
+ (!target.worktreePath || target.isAccessible === false));
594
692
 
595
693
  if (shouldSkip) {
596
694
  setCleanupIndicators((prev) => ({
597
695
  ...prev,
598
- [target.branch]: { icon: '⏭️', color: 'yellow' },
696
+ [target.branch]: { icon: "⏭️", color: "yellow" },
599
697
  }));
600
698
  setCleanupProcessingBranch(null);
601
699
  continue;
602
700
  }
603
701
 
604
702
  try {
605
- if (target.cleanupType === 'worktree-and-branch' && target.worktreePath) {
703
+ if (
704
+ target.cleanupType === "worktree-and-branch" &&
705
+ target.worktreePath
706
+ ) {
606
707
  await removeWorktree(target.worktreePath, true);
607
708
  }
608
709
 
@@ -610,13 +711,13 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
610
711
  succeededBranches.push(target.branch);
611
712
  setCleanupIndicators((prev) => ({
612
713
  ...prev,
613
- [target.branch]: { icon: '', color: 'green' },
714
+ [target.branch]: { icon: "", color: "green" },
614
715
  }));
615
716
  } catch {
616
- const icon = '';
717
+ const icon = "";
617
718
  setCleanupIndicators((prev) => ({
618
719
  ...prev,
619
- [target.branch]: { icon, color: 'red' },
720
+ [target.branch]: { icon, color: "red" },
620
721
  }));
621
722
  }
622
723
 
@@ -625,23 +726,45 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
625
726
 
626
727
  setCleanupProcessingBranch(null);
627
728
  setCleanupInputLocked(false);
628
- setCleanupFooterMessage({ text: 'Cleanup completed. Finalizing...', color: 'green' });
729
+ setCleanupFooterMessage({
730
+ text: "Cleanup completed. Finalizing...",
731
+ color: "green",
732
+ });
629
733
 
630
734
  const holdDuration =
631
- typeof process !== 'undefined' && process.env?.NODE_ENV === 'test'
735
+ typeof process !== "undefined" && process.env?.NODE_ENV === "test"
632
736
  ? 0
633
737
  : COMPLETION_HOLD_DURATION_MS;
634
738
 
635
739
  completionTimerRef.current = setTimeout(resetAfterWait, holdDuration);
636
- }, [cleanupInputLocked, deleteBranch, getMergedPRWorktrees, refresh, removeWorktree]);
740
+ }, [
741
+ cleanupInputLocked,
742
+ deleteBranch,
743
+ getMergedPRWorktrees,
744
+ refresh,
745
+ removeWorktree,
746
+ ]);
637
747
 
638
748
  // Handle AI tool selection
639
749
  const handleToolSelect = useCallback(
640
750
  (tool: AITool) => {
641
751
  setSelectedTool(tool);
642
- navigateTo('execution-mode-selector');
752
+ setSelectedModel(lastModelByTool[tool] ?? null);
753
+ navigateTo("model-selector");
754
+ },
755
+ [lastModelByTool, navigateTo],
756
+ );
757
+
758
+ const handleModelSelect = useCallback(
759
+ (selection: ModelSelectionResult) => {
760
+ setSelectedModel(selection);
761
+ setLastModelByTool((prev) => ({
762
+ ...prev,
763
+ ...(selectedTool ? { [selectedTool]: selection } : {}),
764
+ }));
765
+ navigateTo("execution-mode-selector");
643
766
  },
644
- [navigateTo]
767
+ [navigateTo, selectedTool],
645
768
  );
646
769
 
647
770
  // Handle session selection
@@ -651,7 +774,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
651
774
  // For now, just go back to branch list
652
775
  goBack();
653
776
  },
654
- [goBack]
777
+ [goBack],
655
778
  );
656
779
 
657
780
  // Handle execution mode and skipPermissions selection
@@ -659,6 +782,13 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
659
782
  (result: { mode: ExecutionMode; skipPermissions: boolean }) => {
660
783
  // All selections complete - exit with result
661
784
  if (selectedBranch && selectedTool) {
785
+ const defaultModel = getDefaultModelOption(selectedTool);
786
+ const resolvedModel =
787
+ selectedModel?.model ?? defaultModel?.id ?? null;
788
+ const resolvedInference =
789
+ selectedModel?.inferenceLevel ??
790
+ getDefaultInferenceForModel(defaultModel ?? undefined);
791
+
662
792
  const payload: SelectionResult = {
663
793
  branch: selectedBranch.name,
664
794
  displayName: selectedBranch.displayName,
@@ -666,6 +796,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
666
796
  tool: selectedTool,
667
797
  mode: result.mode,
668
798
  skipPermissions: result.skipPermissions,
799
+ ...(resolvedModel !== undefined ? { model: resolvedModel } : {}),
800
+ ...(resolvedInference !== undefined
801
+ ? { inferenceLevel: resolvedInference }
802
+ : {}),
669
803
  ...(selectedBranch.remoteBranch
670
804
  ? { remoteBranch: selectedBranch.remoteBranch }
671
805
  : {}),
@@ -675,13 +809,21 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
675
809
  exit();
676
810
  }
677
811
  },
678
- [selectedBranch, selectedTool, onExit, exit]
812
+ [
813
+ selectedBranch,
814
+ selectedTool,
815
+ selectedModel,
816
+ onExit,
817
+ exit,
818
+ getDefaultModelOption,
819
+ getDefaultInferenceForModel,
820
+ ],
679
821
  );
680
822
 
681
823
  // Render screen based on currentScreen
682
824
  const renderScreen = () => {
683
825
  switch (currentScreen) {
684
- case 'branch-list':
826
+ case "branch-list":
685
827
  return (
686
828
  <BranchListScreen
687
829
  branches={branchItems}
@@ -705,7 +847,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
705
847
  />
706
848
  );
707
849
 
708
- case 'worktree-manager':
850
+ case "worktree-manager":
709
851
  return (
710
852
  <WorktreeManagerScreen
711
853
  worktrees={worktreeItems}
@@ -715,7 +857,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
715
857
  />
716
858
  );
717
859
 
718
- case 'branch-creator':
860
+ case "branch-creator":
719
861
  return (
720
862
  <BranchCreatorScreen
721
863
  onBack={goBack}
@@ -725,10 +867,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
725
867
  />
726
868
  );
727
869
 
728
- case 'branch-action-selector': {
870
+ case "branch-action-selector": {
729
871
  const isProtected = Boolean(protectedBranchInfo);
730
872
  const baseProps = {
731
- selectedBranch: selectedBranch?.displayName ?? '',
873
+ selectedBranch: selectedBranch?.displayName ?? "",
732
874
  onUseExisting: handleUseExistingBranch,
733
875
  onCreateNew: handleCreateNewBranch,
734
876
  onBack: goBack,
@@ -750,10 +892,31 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
750
892
  return <BranchActionSelectorScreen {...baseProps} />;
751
893
  }
752
894
 
753
- case 'ai-tool-selector':
754
- return <AIToolSelectorScreen onBack={goBack} onSelect={handleToolSelect} version={version} />;
895
+ case "ai-tool-selector":
896
+ return (
897
+ <AIToolSelectorScreen
898
+ onBack={goBack}
899
+ onSelect={handleToolSelect}
900
+ version={version}
901
+ />
902
+ );
755
903
 
756
- case 'session-selector':
904
+ case "model-selector":
905
+ if (!selectedTool) {
906
+ goBack();
907
+ return null;
908
+ }
909
+ return (
910
+ <ModelSelectorScreen
911
+ tool={selectedTool}
912
+ onBack={goBack}
913
+ onSelect={handleModelSelect}
914
+ version={version}
915
+ initialSelection={selectedModel}
916
+ />
917
+ );
918
+
919
+ case "session-selector":
757
920
  // TODO: Implement session data fetching
758
921
  return (
759
922
  <SessionSelectorScreen
@@ -764,9 +927,13 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
764
927
  />
765
928
  );
766
929
 
767
- case 'execution-mode-selector':
930
+ case "execution-mode-selector":
768
931
  return (
769
- <ExecutionModeSelectorScreen onBack={goBack} onSelect={handleModeSelect} version={version} />
932
+ <ExecutionModeSelectorScreen
933
+ onBack={goBack}
934
+ onSelect={handleModeSelect}
935
+ version={version}
936
+ />
770
937
  );
771
938
 
772
939
  default: