@akiojin/gwt 2.11.0 → 2.12.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 (76) hide show
  1. package/dist/claude.d.ts +4 -1
  2. package/dist/claude.d.ts.map +1 -1
  3. package/dist/claude.js +51 -7
  4. package/dist/claude.js.map +1 -1
  5. package/dist/cli/ui/components/App.d.ts +7 -0
  6. package/dist/cli/ui/components/App.d.ts.map +1 -1
  7. package/dist/cli/ui/components/App.js +307 -18
  8. package/dist/cli/ui/components/App.js.map +1 -1
  9. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +21 -0
  10. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -0
  11. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +145 -0
  12. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -0
  13. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -1
  14. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  15. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +4 -2
  16. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  17. package/dist/cli/ui/components/screens/ModelSelectorScreen.js +1 -1
  18. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +10 -2
  19. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts.map +1 -1
  20. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +18 -7
  21. package/dist/cli/ui/components/screens/SessionSelectorScreen.js.map +1 -1
  22. package/dist/cli/ui/types.d.ts +1 -1
  23. package/dist/cli/ui/types.d.ts.map +1 -1
  24. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  25. package/dist/cli/ui/utils/branchFormatter.js +0 -13
  26. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  27. package/dist/cli/ui/utils/continueSession.d.ts +18 -0
  28. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -0
  29. package/dist/cli/ui/utils/continueSession.js +67 -0
  30. package/dist/cli/ui/utils/continueSession.js.map +1 -0
  31. package/dist/codex.d.ts +4 -1
  32. package/dist/codex.d.ts.map +1 -1
  33. package/dist/codex.js +70 -5
  34. package/dist/codex.js.map +1 -1
  35. package/dist/config/index.d.ts +9 -1
  36. package/dist/config/index.d.ts.map +1 -1
  37. package/dist/config/index.js +11 -2
  38. package/dist/config/index.js.map +1 -1
  39. package/dist/gemini.d.ts +4 -1
  40. package/dist/gemini.d.ts.map +1 -1
  41. package/dist/gemini.js +146 -32
  42. package/dist/gemini.js.map +1 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +118 -8
  45. package/dist/index.js.map +1 -1
  46. package/dist/qwen.d.ts +4 -1
  47. package/dist/qwen.d.ts.map +1 -1
  48. package/dist/qwen.js +45 -4
  49. package/dist/qwen.js.map +1 -1
  50. package/dist/utils/session.d.ts +82 -0
  51. package/dist/utils/session.d.ts.map +1 -0
  52. package/dist/utils/session.js +579 -0
  53. package/dist/utils/session.js.map +1 -0
  54. package/package.json +1 -1
  55. package/src/claude.ts +69 -8
  56. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +12 -2
  57. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +2 -2
  58. package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +142 -0
  59. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +14 -0
  60. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +29 -10
  61. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +4 -1
  62. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +0 -1
  63. package/src/cli/ui/components/App.tsx +403 -23
  64. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +237 -0
  65. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +5 -1
  66. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +1 -1
  67. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +34 -6
  68. package/src/cli/ui/types.ts +1 -0
  69. package/src/cli/ui/utils/branchFormatter.ts +0 -13
  70. package/src/cli/ui/utils/continueSession.ts +106 -0
  71. package/src/codex.ts +91 -6
  72. package/src/config/index.ts +22 -2
  73. package/src/gemini.ts +179 -41
  74. package/src/index.ts +144 -16
  75. package/src/qwen.ts +56 -5
  76. package/src/utils/session.ts +704 -0
@@ -14,6 +14,8 @@ import { AIToolSelectorScreen } from "./screens/AIToolSelectorScreen.js";
14
14
  import { SessionSelectorScreen } from "./screens/SessionSelectorScreen.js";
15
15
  import { ExecutionModeSelectorScreen } from "./screens/ExecutionModeSelectorScreen.js";
16
16
  import type { ExecutionMode } from "./screens/ExecutionModeSelectorScreen.js";
17
+ import { BranchQuickStartScreen } from "./screens/BranchQuickStartScreen.js";
18
+ import type { QuickStartAction } from "./screens/BranchQuickStartScreen.js";
17
19
  import {
18
20
  ModelSelectorScreen,
19
21
  type ModelSelectionResult,
@@ -30,6 +32,7 @@ import type {
30
32
  SelectedBranchState,
31
33
  } from "../types.js";
32
34
  import { getRepositoryRoot, deleteBranch } from "../../../git.js";
35
+ import { loadSession } from "../../../config/index.js";
33
36
  import {
34
37
  createWorktree,
35
38
  generateWorktreePath,
@@ -47,6 +50,17 @@ import {
47
50
  getDefaultInferenceForModel,
48
51
  getDefaultModelOption,
49
52
  } from "../utils/modelOptions.js";
53
+ import {
54
+ resolveContinueSessionId,
55
+ findLatestBranchSessionsByTool,
56
+ } from "../utils/continueSession.js";
57
+ import {
58
+ findLatestCodexSession,
59
+ findLatestCodexSessionId,
60
+ findLatestClaudeSession,
61
+ findLatestGeminiSession,
62
+ } from "../../../utils/session.js";
63
+ import type { ToolSessionEntry } from "../../../config/index.js";
50
64
 
51
65
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"];
52
66
  const COMPLETION_HOLD_DURATION_MS = 3000;
@@ -71,6 +85,7 @@ export interface SelectionResult {
71
85
  skipPermissions: boolean;
72
86
  model?: string | null;
73
87
  inferenceLevel?: InferenceLevel;
88
+ sessionId?: string | null;
74
89
  }
75
90
 
76
91
  export interface AppProps {
@@ -78,6 +93,13 @@ export interface AppProps {
78
93
  loadingIndicatorDelay?: number;
79
94
  }
80
95
 
96
+ export interface SessionOption {
97
+ sessionId: string;
98
+ toolLabel: string;
99
+ branch: string;
100
+ timestamp: number;
101
+ }
102
+
81
103
  /**
82
104
  * App - Top-level component for Ink.js UI
83
105
  * Integrates ErrorBoundary, data fetching, screen navigation, and all screens
@@ -96,6 +118,30 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
96
118
 
97
119
  // Version state
98
120
  const [version, setVersion] = useState<string | null>(null);
121
+ const [repoRoot, setRepoRoot] = useState<string | null>(null);
122
+ const [sessionOptions, setSessionOptions] = useState<SessionOption[]>([]);
123
+ const [sessionLoading, setSessionLoading] = useState(false);
124
+ const [sessionError, setSessionError] = useState<string | null>(null);
125
+ const [pendingExecution, setPendingExecution] = useState<{
126
+ mode: ExecutionMode;
127
+ skipPermissions: boolean;
128
+ } | null>(null);
129
+ const [continueSessionId, setContinueSessionId] = useState<string | null>(
130
+ null,
131
+ );
132
+ const [branchQuickStart, setBranchQuickStart] = useState<
133
+ {
134
+ toolId: AITool;
135
+ toolLabel: string;
136
+ model?: string | null;
137
+ sessionId?: string | null;
138
+ inferenceLevel?: InferenceLevel | null;
139
+ skipPermissions?: boolean | null;
140
+ timestamp?: number | null;
141
+ }[]
142
+ >([]);
143
+ const [branchQuickStartLoading, setBranchQuickStartLoading] =
144
+ useState(false);
99
145
 
100
146
  // Selection state (for branch → tool → mode flow)
101
147
  const [selectedBranch, setSelectedBranch] =
@@ -137,6 +183,13 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
137
183
  .catch(() => setVersion(null));
138
184
  }, []);
139
185
 
186
+ // Fetch repository root once for session lookups
187
+ useEffect(() => {
188
+ getRepositoryRoot()
189
+ .then(setRepoRoot)
190
+ .catch(() => setRepoRoot(null));
191
+ }, []);
192
+
140
193
  useEffect(() => {
141
194
  if (!cleanupInputLocked) {
142
195
  spinnerFrameIndexRef.current = 0;
@@ -199,6 +252,225 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
199
252
  }
200
253
  }, [branches, hiddenBranches]);
201
254
 
255
+ // Load available sessions when entering session selector
256
+ useEffect(() => {
257
+ if (currentScreen !== "session-selector") {
258
+ return;
259
+ }
260
+ if (!selectedTool || !selectedBranch) {
261
+ setSessionOptions([]);
262
+ return;
263
+ }
264
+
265
+ setSessionLoading(true);
266
+ setSessionError(null);
267
+
268
+ (async () => {
269
+ try {
270
+ const root = repoRoot ?? (await getRepositoryRoot());
271
+ if (!repoRoot && root) {
272
+ setRepoRoot(root);
273
+ }
274
+ const sessionData = root ? await loadSession(root) : null;
275
+ const history = sessionData?.history ?? [];
276
+
277
+ const filtered = history
278
+ .filter(
279
+ (entry) =>
280
+ entry.sessionId &&
281
+ entry.toolId === selectedTool &&
282
+ entry.branch === selectedBranch.name,
283
+ )
284
+ .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0))
285
+ .map((entry) => ({
286
+ sessionId: entry.sessionId as string,
287
+ toolLabel: entry.toolLabel,
288
+ branch: entry.branch,
289
+ timestamp: entry.timestamp,
290
+ }));
291
+
292
+ setSessionOptions(filtered);
293
+ } catch (_err) {
294
+ setSessionOptions([]);
295
+ setSessionError("セッション一覧の取得に失敗しました");
296
+ } finally {
297
+ setSessionLoading(false);
298
+ }
299
+ })();
300
+ }, [currentScreen, selectedTool, selectedBranch, repoRoot]);
301
+
302
+ // Load quick start options for selected branch (latest per tool)
303
+ useEffect(() => {
304
+ if (!selectedBranch) {
305
+ setBranchQuickStart([]);
306
+ return;
307
+ }
308
+ let cancelled = false;
309
+ setBranchQuickStartLoading(true);
310
+ (async () => {
311
+ try {
312
+ const root = repoRoot ?? (await getRepositoryRoot());
313
+ if (!repoRoot && root) {
314
+ setRepoRoot(root);
315
+ }
316
+ if (!root) {
317
+ if (!cancelled) setBranchQuickStart([]);
318
+ return;
319
+ }
320
+ const sessionData = await loadSession(root);
321
+ const history = sessionData?.history ?? [];
322
+
323
+ const combinedHistory = [...history];
324
+ if (
325
+ sessionData?.lastSessionId &&
326
+ sessionData.lastBranch === selectedBranch.name &&
327
+ sessionData.lastUsedTool
328
+ ) {
329
+ const synthetic: ToolSessionEntry = {
330
+ branch: sessionData.lastBranch,
331
+ worktreePath: sessionData.lastWorktreePath ?? null,
332
+ toolId: sessionData.lastUsedTool,
333
+ toolLabel: sessionData.toolLabel ?? sessionData.lastUsedTool,
334
+ sessionId: sessionData.lastSessionId,
335
+ mode: sessionData.mode ?? null,
336
+ model: sessionData.model ?? null,
337
+ reasoningLevel: sessionData.reasoningLevel ?? null,
338
+ skipPermissions: sessionData.skipPermissions ?? null,
339
+ timestamp: sessionData.timestamp ?? Date.now(),
340
+ };
341
+ combinedHistory.push(synthetic);
342
+ }
343
+ const latestPerTool = findLatestBranchSessionsByTool(
344
+ combinedHistory,
345
+ selectedBranch.name,
346
+ selectedWorktreePath,
347
+ );
348
+
349
+ const mapped = await Promise.all(
350
+ latestPerTool.map(async (entry) => {
351
+ let sessionId = entry.sessionId ?? null;
352
+
353
+ // For Codex, prefer a newer filesystem session over stale history
354
+ if (entry.toolId === "codex-cli") {
355
+ try {
356
+ const latestCodex = await findLatestCodexSession();
357
+ const historyTs = entry.timestamp ?? 0;
358
+ if (
359
+ latestCodex &&
360
+ (!sessionId || latestCodex.mtime > historyTs)
361
+ ) {
362
+ sessionId = latestCodex.id;
363
+ }
364
+ // Fallback when filesystem unavailable and history missing
365
+ if (!sessionId) {
366
+ const latestId = await findLatestCodexSessionId();
367
+ if (latestId) sessionId = latestId;
368
+ }
369
+ } catch {
370
+ // ignore lookup failure
371
+ }
372
+ }
373
+
374
+ // For Claude Code, prefer the newest session file in the worktree even if history is stale.
375
+ if (entry.toolId === "claude-code") {
376
+ try {
377
+ const worktree =
378
+ selectedWorktreePath ??
379
+ selectedBranch?.displayName ??
380
+ workingDirectory;
381
+
382
+ // Always resolve freshest on-disk session for this worktree (no window restriction)
383
+ const latestAny = await findLatestClaudeSession(worktree);
384
+ sessionId = latestAny?.id ?? sessionId ?? null;
385
+
386
+ } catch {
387
+ // ignore lookup failure
388
+ }
389
+ }
390
+
391
+ // For Gemini, prefer newest session file (Gemini keeps per-project chats)
392
+ if (entry.toolId === "gemini-cli") {
393
+ try {
394
+ const gemSession = await findLatestGeminiSession(
395
+ selectedWorktreePath ??
396
+ selectedBranch?.displayName ??
397
+ workingDirectory,
398
+ {
399
+ ...(entry.timestamp !== null && entry.timestamp !== undefined
400
+ ? { since: entry.timestamp - 60_000, preferClosestTo: entry.timestamp }
401
+ : {}),
402
+ windowMs: 60 * 60 * 1000,
403
+ },
404
+ );
405
+ if (gemSession?.id) sessionId = gemSession.id;
406
+ } catch {
407
+ // ignore
408
+ }
409
+ }
410
+
411
+ return {
412
+ toolId: entry.toolId as AITool,
413
+ toolLabel: entry.toolLabel,
414
+ model: entry.model ?? null,
415
+ inferenceLevel: (entry.reasoningLevel ??
416
+ sessionData?.reasoningLevel ??
417
+ null) as InferenceLevel | null,
418
+ sessionId,
419
+ skipPermissions:
420
+ entry.skipPermissions ?? sessionData?.skipPermissions ?? null,
421
+ timestamp: entry.timestamp ?? null,
422
+ };
423
+ }),
424
+ );
425
+
426
+ if (!cancelled) {
427
+ setBranchQuickStart(mapped);
428
+ }
429
+ } catch {
430
+ if (!cancelled) setBranchQuickStart([]);
431
+ } finally {
432
+ if (!cancelled) setBranchQuickStartLoading(false);
433
+ }
434
+ })();
435
+
436
+ return () => {
437
+ cancelled = true;
438
+ };
439
+ }, [selectedBranch, repoRoot]);
440
+
441
+ // Load last session ID for "Continue" label when entering execution mode selector
442
+ useEffect(() => {
443
+ if (currentScreen !== "execution-mode-selector") {
444
+ return;
445
+ }
446
+ if (!selectedTool || !selectedBranch) {
447
+ setContinueSessionId(null);
448
+ return;
449
+ }
450
+ (async () => {
451
+ try {
452
+ const root = repoRoot ?? (await getRepositoryRoot());
453
+ if (!repoRoot && root) {
454
+ setRepoRoot(root);
455
+ }
456
+ const sessionData = root ? await loadSession(root) : null;
457
+ const history = sessionData?.history ?? [];
458
+
459
+ const found = await resolveContinueSessionId({
460
+ history,
461
+ sessionData,
462
+ branch: selectedBranch.name,
463
+ toolId: selectedTool,
464
+ repoRoot: root,
465
+ });
466
+
467
+ setContinueSessionId(found ?? null);
468
+ } catch {
469
+ setContinueSessionId(null);
470
+ }
471
+ })();
472
+ }, [currentScreen, selectedTool, selectedBranch, repoRoot]);
473
+
202
474
  // Update preferred tool when branch or data changes
203
475
  useEffect(() => {
204
476
  if (!selectedBranch) return;
@@ -227,6 +499,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
227
499
  [branches, hiddenBranches],
228
500
  );
229
501
 
502
+ const selectedWorktreePath = useMemo(() => {
503
+ if (!selectedBranch) return null;
504
+ const wt = worktrees.find((w) => w.branch === selectedBranch.name);
505
+ return wt?.path ?? null;
506
+ }, [selectedBranch, worktrees]);
507
+
230
508
  // Helper function to create content-based hash for branches
231
509
  const branchHash = useMemo(
232
510
  () =>
@@ -445,7 +723,11 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
445
723
  color: "green",
446
724
  });
447
725
  refresh();
448
- navigateTo("ai-tool-selector");
726
+ const nextScreen =
727
+ branchQuickStart.length || branchQuickStartLoading
728
+ ? "branch-quick-start"
729
+ : "ai-tool-selector";
730
+ navigateTo(nextScreen);
449
731
  } catch (error) {
450
732
  const message = error instanceof Error ? error.message : String(error);
451
733
  setCleanupFooterMessage({
@@ -454,18 +736,30 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
454
736
  });
455
737
  console.error("Failed to switch protected branch:", error);
456
738
  }
457
- }, [navigateTo, refresh, selectedBranch, setCleanupFooterMessage]);
739
+ }, [
740
+ branchQuickStart,
741
+ branchQuickStartLoading,
742
+ navigateTo,
743
+ refresh,
744
+ selectedBranch,
745
+ setCleanupFooterMessage,
746
+ ]);
458
747
 
459
748
  const handleUseExistingBranch = useCallback(() => {
460
749
  if (selectedBranch && isProtectedSelection(selectedBranch)) {
461
750
  void handleProtectedBranchSwitch();
462
751
  return;
463
752
  }
464
- navigateTo("ai-tool-selector");
753
+ if (branchQuickStart.length) {
754
+ navigateTo("branch-quick-start");
755
+ } else {
756
+ navigateTo("ai-tool-selector");
757
+ }
465
758
  }, [
466
759
  handleProtectedBranchSwitch,
467
760
  isProtectedSelection,
468
761
  navigateTo,
762
+ branchQuickStart.length,
469
763
  selectedBranch,
470
764
  ]);
471
765
 
@@ -742,20 +1036,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
742
1036
  [navigateTo, selectedTool],
743
1037
  );
744
1038
 
745
- // Handle session selection
746
- const handleSessionSelect = useCallback(
747
- (_session: string) => {
748
- // TODO: Load selected session and navigate to next screen
749
- // For now, just go back to branch list
750
- goBack();
751
- },
752
- [goBack],
753
- );
754
-
755
- // Handle execution mode and skipPermissions selection
756
- const handleModeSelect = useCallback(
757
- (result: { mode: ExecutionMode; skipPermissions: boolean }) => {
758
- // All selections complete - exit with result
1039
+ const completeSelection = useCallback(
1040
+ (
1041
+ executionMode: ExecutionMode,
1042
+ skip: boolean,
1043
+ sessionId?: string | null,
1044
+ ) => {
759
1045
  if (selectedBranch && selectedTool) {
760
1046
  const defaultModel = getDefaultModelOption(selectedTool);
761
1047
  const resolvedModel = selectedModel?.model ?? defaultModel?.id ?? null;
@@ -763,22 +1049,21 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
763
1049
  selectedModel?.inferenceLevel ??
764
1050
  getDefaultInferenceForModel(defaultModel ?? undefined);
765
1051
 
766
- const modelForPayload = resolvedModel;
767
-
768
1052
  const payload: SelectionResult = {
769
1053
  branch: selectedBranch.name,
770
1054
  displayName: selectedBranch.displayName,
771
1055
  branchType: selectedBranch.branchType,
772
1056
  tool: selectedTool,
773
- mode: result.mode,
774
- skipPermissions: result.skipPermissions,
775
- ...(modelForPayload !== undefined ? { model: modelForPayload } : {}),
1057
+ mode: executionMode,
1058
+ skipPermissions: skip,
1059
+ ...(resolvedModel !== undefined ? { model: resolvedModel } : {}),
776
1060
  ...(resolvedInference !== undefined
777
1061
  ? { inferenceLevel: resolvedInference }
778
1062
  : {}),
779
1063
  ...(selectedBranch.remoteBranch
780
1064
  ? { remoteBranch: selectedBranch.remoteBranch }
781
1065
  : {}),
1066
+ ...(sessionId ? { sessionId } : {}),
782
1067
  };
783
1068
 
784
1069
  onExit(payload);
@@ -796,6 +1081,79 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
796
1081
  ],
797
1082
  );
798
1083
 
1084
+ // Handle session selection
1085
+ const handleSessionSelect = useCallback(
1086
+ (session: string) => {
1087
+ const execution = pendingExecution ?? {
1088
+ mode: "resume" as ExecutionMode,
1089
+ skipPermissions: false,
1090
+ };
1091
+ completeSelection(execution.mode, execution.skipPermissions, session);
1092
+ },
1093
+ [pendingExecution, completeSelection],
1094
+ );
1095
+
1096
+ const handleQuickStartSelect = useCallback(
1097
+ (action: QuickStartAction, toolId?: AITool | null) => {
1098
+ if (action === "manual" || !branchQuickStart.length) {
1099
+ navigateTo("ai-tool-selector");
1100
+ return;
1101
+ }
1102
+
1103
+ const selected =
1104
+ branchQuickStart.find((opt) => opt.toolId === toolId) ??
1105
+ branchQuickStart[0];
1106
+ if (!selected) {
1107
+ navigateTo("ai-tool-selector");
1108
+ return;
1109
+ }
1110
+
1111
+ setSelectedTool(selected.toolId);
1112
+ setPreferredToolId(selected.toolId);
1113
+ setSelectedModel(
1114
+ selected.model
1115
+ ? ({
1116
+ model: selected.model,
1117
+ inferenceLevel: selected.inferenceLevel ?? undefined,
1118
+ } as ModelSelectionResult)
1119
+ : null,
1120
+ );
1121
+
1122
+ const skip = selected.skipPermissions ?? false;
1123
+
1124
+ if (action === "reuse-continue") {
1125
+ const hasSession = Boolean(selected.sessionId);
1126
+ const mode: ExecutionMode = hasSession ? "resume" : "continue";
1127
+ completeSelection(mode, skip, selected.sessionId ?? null);
1128
+ return;
1129
+ }
1130
+
1131
+ // "Start new with previous settings" skips the execution mode screen and launches immediately
1132
+ completeSelection("normal", skip, null);
1133
+ },
1134
+ [
1135
+ branchQuickStart,
1136
+ navigateTo,
1137
+ setPreferredToolId,
1138
+ setSelectedModel,
1139
+ setSelectedTool,
1140
+ completeSelection,
1141
+ ],
1142
+ );
1143
+
1144
+ // Handle execution mode and skipPermissions selection
1145
+ const handleModeSelect = useCallback(
1146
+ (result: { mode: ExecutionMode; skipPermissions: boolean }) => {
1147
+ if (result.mode === "resume") {
1148
+ setPendingExecution(result);
1149
+ navigateTo("session-selector");
1150
+ return;
1151
+ }
1152
+ completeSelection(result.mode, result.skipPermissions, null);
1153
+ },
1154
+ [completeSelection, navigateTo],
1155
+ );
1156
+
799
1157
  // Render screen based on currentScreen
800
1158
  const renderScreen = () => {
801
1159
  switch (currentScreen) {
@@ -857,6 +1215,25 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
857
1215
  return <BranchActionSelectorScreen {...baseProps} />;
858
1216
  }
859
1217
 
1218
+ case "branch-quick-start":
1219
+ return (
1220
+ <BranchQuickStartScreen
1221
+ branchName={selectedBranch?.displayName ?? ""}
1222
+ previousOptions={branchQuickStart.map((opt) => ({
1223
+ toolId: opt.toolId,
1224
+ toolLabel: opt.toolLabel,
1225
+ model: opt.model ?? null,
1226
+ inferenceLevel: opt.inferenceLevel ?? null,
1227
+ skipPermissions: opt.skipPermissions ?? null,
1228
+ sessionId: opt.sessionId ?? null,
1229
+ }))}
1230
+ loading={branchQuickStartLoading}
1231
+ onBack={goBack}
1232
+ onSelect={handleQuickStartSelect}
1233
+ version={version}
1234
+ />
1235
+ );
1236
+
860
1237
  case "ai-tool-selector":
861
1238
  return (
862
1239
  <AIToolSelectorScreen
@@ -886,7 +1263,9 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
886
1263
  // TODO: Implement session data fetching
887
1264
  return (
888
1265
  <SessionSelectorScreen
889
- sessions={[]}
1266
+ sessions={sessionOptions}
1267
+ loading={sessionLoading}
1268
+ errorMessage={sessionError}
890
1269
  onBack={goBack}
891
1270
  onSelect={handleSessionSelect}
892
1271
  version={version}
@@ -899,6 +1278,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
899
1278
  onBack={goBack}
900
1279
  onSelect={handleModeSelect}
901
1280
  version={version}
1281
+ continueSessionId={continueSessionId}
902
1282
  />
903
1283
  );
904
1284