@akiojin/gwt 2.1.1 → 2.3.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 (149) hide show
  1. package/README.ja.md +4 -4
  2. package/README.md +4 -4
  3. package/dist/cli/ui/components/App.d.ts +4 -4
  4. package/dist/cli/ui/components/App.d.ts.map +1 -1
  5. package/dist/cli/ui/components/App.js +144 -105
  6. package/dist/cli/ui/components/App.js.map +1 -1
  7. package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
  8. package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
  9. package/dist/cli/ui/components/common/Confirm.js +7 -7
  10. package/dist/cli/ui/components/common/Confirm.js.map +1 -1
  11. package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
  12. package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
  13. package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
  14. package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
  15. package/dist/cli/ui/components/common/Input.d.ts +7 -2
  16. package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
  17. package/dist/cli/ui/components/common/Input.js +12 -4
  18. package/dist/cli/ui/components/common/Input.js.map +1 -1
  19. package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
  20. package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
  21. package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
  22. package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
  23. package/dist/cli/ui/components/common/Select.d.ts +1 -1
  24. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  25. package/dist/cli/ui/components/common/Select.js +11 -12
  26. package/dist/cli/ui/components/common/Select.js.map +1 -1
  27. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +2 -2
  28. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
  29. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
  30. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
  31. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
  32. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
  33. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
  34. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
  35. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +8 -4
  36. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  37. package/dist/cli/ui/components/screens/BranchListScreen.js +122 -48
  38. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  39. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
  40. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
  42. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  43. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
  44. package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
  45. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
  46. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
  47. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
  48. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
  49. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
  50. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
  51. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
  52. package/dist/cli/ui/types.d.ts.map +1 -1
  53. package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
  54. package/dist/client/index.html +1 -1
  55. package/dist/config/builtin-tools.d.ts +10 -2
  56. package/dist/config/builtin-tools.d.ts.map +1 -1
  57. package/dist/config/builtin-tools.js +40 -4
  58. package/dist/config/builtin-tools.js.map +1 -1
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js.map +1 -1
  61. package/dist/config/tools.d.ts.map +1 -1
  62. package/dist/config/tools.js +4 -3
  63. package/dist/config/tools.js.map +1 -1
  64. package/dist/gemini.d.ts +12 -0
  65. package/dist/gemini.d.ts.map +1 -0
  66. package/dist/gemini.js +154 -0
  67. package/dist/gemini.js.map +1 -0
  68. package/dist/git.d.ts.map +1 -1
  69. package/dist/git.js.map +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +30 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/qwen.d.ts +12 -0
  74. package/dist/qwen.d.ts.map +1 -0
  75. package/dist/qwen.js +154 -0
  76. package/dist/qwen.js.map +1 -0
  77. package/dist/services/git.service.d.ts.map +1 -1
  78. package/dist/services/git.service.js.map +1 -1
  79. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  80. package/dist/web/client/src/components/BranchGraph.js +1 -1
  81. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  82. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  83. package/dist/web/client/src/components/EnvEditor.js +7 -4
  84. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  85. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  86. package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
  87. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  88. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  89. package/dist/web/client/src/pages/BranchListPage.js +10 -4
  90. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  91. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  92. package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
  93. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  94. package/package.json +2 -1
  95. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
  96. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
  97. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
  98. package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
  99. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
  100. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
  101. package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
  102. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +40 -34
  103. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
  104. package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
  105. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
  106. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
  107. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
  108. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
  109. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
  110. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
  111. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +496 -75
  112. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
  113. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
  114. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
  115. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
  116. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
  117. package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
  118. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
  119. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
  120. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
  121. package/src/cli/ui/components/App.tsx +247 -150
  122. package/src/cli/ui/components/common/Confirm.tsx +13 -9
  123. package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
  124. package/src/cli/ui/components/common/Input.tsx +26 -4
  125. package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
  126. package/src/cli/ui/components/common/Select.tsx +28 -17
  127. package/src/cli/ui/components/parts/Header.test.tsx +5 -15
  128. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +19 -13
  129. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
  130. package/src/cli/ui/components/screens/BranchListScreen.tsx +187 -62
  131. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
  132. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
  133. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
  134. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
  135. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
  136. package/src/cli/ui/types.ts +8 -1
  137. package/src/config/builtin-tools.ts +42 -4
  138. package/src/config/index.ts +2 -12
  139. package/src/config/tools.ts +16 -6
  140. package/src/gemini.ts +202 -0
  141. package/src/git.ts +2 -1
  142. package/src/index.ts +30 -0
  143. package/src/qwen.ts +208 -0
  144. package/src/services/git.service.ts +2 -1
  145. package/src/web/client/src/components/BranchGraph.tsx +3 -2
  146. package/src/web/client/src/components/EnvEditor.tsx +44 -11
  147. package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
  148. package/src/web/client/src/pages/BranchListPage.tsx +37 -13
  149. package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
package/README.ja.md CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  [English](README.md)
4
4
 
5
- Claude Code / Codex CLI 対応の対話型Gitワークツリーマネージャー(グラフィカルなブランチ選択と高度なワークフロー管理機能付き)
5
+ Claude Code / Codex CLI / Gemini CLI 対応の対話型Gitワークツリーマネージャー(グラフィカルなブランチ選択と高度なワークフロー管理機能付き)
6
6
 
7
7
  ## 概要
8
8
 
9
- `@akiojin/gwt`は、直感的なインターフェースを通じてGitワークツリー管理を革新する強力なCLIツールです。Claude Code / Codex CLI の開発ワークフローとシームレスに統合し、インテリジェントなブランチ選択、自動ワークツリー作成、包括的なプロジェクト管理機能を提供します。
9
+ `@akiojin/gwt`は、直感的なインターフェースを通じてGitワークツリー管理を革新する強力なCLIツールです。Claude Code / Codex CLI / Gemini CLI の開発ワークフローとシームレスに統合し、インテリジェントなブランチ選択、自動ワークツリー作成、包括的なプロジェクト管理機能を提供します。
10
10
 
11
11
  ## ✨ 主要機能
12
12
 
13
13
  - 🎯 **対話型ブランチ選択**: ブランチ種別・ワークツリー・変更状態アイコンに加え配置インジケータ枠(左=L, 右=R, リモートのみ=☁)で所在を示し、リモート名の `origin/` を省いたシンプルなリストで判別しやすく、現在の選択は `>` プレフィックスで強調されるため誤操作を防止
14
14
  - 🌟 **スマートブランチ作成**: ガイド付きプロンプトと自動ベースブランチ選択でfeature、bugfix、hotfix、releaseブランチを作成
15
15
  - 🔄 **高度なワークツリー管理**: 作成、クリーンアップ、パス最適化を含む完全なライフサイクル管理
16
- - 🤖 **AIツール選択**: 起動時の対話型ランチャーで Claude Code / Codex CLI を選択
16
+ - 🤖 **AIツール選択**: 起動時の対話型ランチャーで Claude Code / Codex CLI / Gemini CLI を選択
17
17
  - 🚀 **AIツール統合**: 選択したツールをワークツリーで起動(Claude Codeは権限設定・変更処理の統合あり)
18
18
  - 📊 **GitHub PR統合**: マージされたプルリクエストのブランチとワークツリーの自動クリーンアップ
19
19
  - 🛠️ **変更管理**: 開発セッション後のコミット、stash、破棄の内蔵サポート
@@ -129,7 +129,7 @@ gwt -v
129
129
  - **Node.js**(任意): Nodeベースの開発ツール利用時は >= 18.0.0 を推奨
130
130
  - **pnpm**: >= 8.0.0(CI/CD・Docker環境用 - ハードリンクによるnode_modules効率化)
131
131
  - **Git**: ワークツリーサポート付き最新版
132
- - **AIツール**: 少なくともいずれかが必要(Claude Code もしくは Codex CLI)
132
+ - **AIツール**: 少なくともいずれかが必要(Claude Code、Codex CLI、または Gemini CLI)
133
133
  - **GitHub CLI**: PR クリーンアップ機能に必要(オプション)
134
134
  - **Python**: >= 3.11(Spec Kit CLIに必要)
135
135
  - **uv**: Pythonパッケージマネージャー(Spec Kit CLIに必要)
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  [日本語](README.ja.md)
4
4
 
5
- Interactive Git worktree manager with AI tool selection (Claude Code / Codex CLI), graphical branch selection, and advanced workflow management.
5
+ Interactive Git worktree manager with AI tool selection (Claude Code / Codex CLI / Gemini CLI), graphical branch selection, and advanced workflow management.
6
6
 
7
7
  ## Overview
8
8
 
9
- `@akiojin/gwt` is a powerful CLI tool that revolutionizes Git worktree management through an intuitive interface. It seamlessly integrates with Claude Code / Codex CLI workflows, providing intelligent branch selection, automated worktree creation, and comprehensive project management capabilities.
9
+ `@akiojin/gwt` is a powerful CLI tool that revolutionizes Git worktree management through an intuitive interface. It seamlessly integrates with Claude Code / Codex CLI / Gemini CLI workflows, providing intelligent branch selection, automated worktree creation, and comprehensive project management capabilities.
10
10
 
11
11
  ## ✨ Key Features
12
12
 
@@ -14,7 +14,7 @@ Interactive Git worktree manager with AI tool selection (Claude Code / Codex CLI
14
14
  - 🖼️ **Full-screen Layout**: Persistent header with statistics, scrollable branch list, and always-visible footer with keyboard shortcuts
15
15
  - 🌟 **Smart Branch Creation**: Create feature, bugfix, hotfix, or release branches with guided prompts and automatic base branch selection
16
16
  - 🔄 **Advanced Worktree Management**: Complete lifecycle management including creation, cleanup, and path optimization
17
- - 🤖 **AI Tool Selection**: Choose between Claude Code / Codex CLI through the interactive launcher
17
+ - 🤖 **AI Tool Selection**: Choose between Claude Code / Codex CLI / Gemini CLI through the interactive launcher
18
18
  - 🚀 **AI Tool Integration**: Launch the selected tool in the worktree (Claude Code includes permission handling and post-change flow)
19
19
  - 🔒 **Worktree Command Restriction**: PreToolUse hooks enforce worktree boundaries, blocking directory navigation, branch switching, and file operations outside the worktree
20
20
  - 📊 **GitHub PR Integration**: Automatic cleanup of merged pull request branches and worktrees
@@ -154,7 +154,7 @@ For technical details, see [specs/SPEC-cff08403/](specs/SPEC-cff08403/).
154
154
  - **Node.js** (optional): Recommended >= 18.0.0 when working with Node-based tooling
155
155
  - **pnpm**: >= 8.0.0 (for CI/CD and Docker environments - uses hardlinked node_modules)
156
156
  - **Git**: Latest version with worktree support
157
- - **AI Tool**: At least one of Claude Code or Codex CLI should be available
157
+ - **AI Tool**: At least one of Claude Code, Codex CLI, or Gemini CLI should be available
158
158
  - **GitHub CLI**: Required for PR cleanup features (optional)
159
159
  - **Python**: >= 3.11 (for Spec Kit CLI)
160
160
  - **uv**: Python package manager (for Spec Kit CLI)
@@ -1,10 +1,10 @@
1
- import React from 'react';
2
- import type { AITool } from './screens/AIToolSelectorScreen.js';
3
- import type { ExecutionMode } from './screens/ExecutionModeSelectorScreen.js';
1
+ import React from "react";
2
+ import type { AITool } from "./screens/AIToolSelectorScreen.js";
3
+ import type { ExecutionMode } from "./screens/ExecutionModeSelectorScreen.js";
4
4
  export interface SelectionResult {
5
5
  branch: string;
6
6
  displayName: string;
7
- branchType: 'local' | 'remote';
7
+ branchType: "local" | "remote";
8
8
  remoteBranch?: string;
9
9
  tool: AITool;
10
10
  mode: ExecutionMode;
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/cli/ui/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAUjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAmC9E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,qBAA2B,EAAE,EAAE,QAAQ,qBAutBpE"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/cli/ui/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAUf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAmC9E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,qBAA2B,EAAE,EAAE,QAAQ,qBAkzBpE"}
@@ -1,30 +1,30 @@
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 { useGitData } from '../hooks/useGitData.js';
12
- import { useScreenState } from '../hooks/useScreenState.js';
13
- import { formatBranchItems } from '../utils/branchFormatter.js';
14
- import { calculateStatistics } from '../utils/statisticsCalculator.js';
15
- import { getRepositoryRoot, deleteBranch } from '../../../git.js';
16
- import { createWorktree, generateWorktreePath, getMergedPRWorktrees, isProtectedBranchName, removeWorktree, switchToProtectedBranch, } from '../../../worktree.js';
17
- import { getPackageVersion } from '../../../utils.js';
18
- import { resolveBaseBranchLabel, resolveBaseBranchRef, } from '../utils/baseBranch.js';
19
- const SPINNER_FRAMES = ['', '', '', '', '', '', '', ''];
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 { useGitData } from "../hooks/useGitData.js";
12
+ import { useScreenState } from "../hooks/useScreenState.js";
13
+ import { formatBranchItems } from "../utils/branchFormatter.js";
14
+ import { calculateStatistics } from "../utils/statisticsCalculator.js";
15
+ import { getRepositoryRoot, deleteBranch } from "../../../git.js";
16
+ import { createWorktree, generateWorktreePath, getMergedPRWorktrees, isProtectedBranchName, removeWorktree, switchToProtectedBranch, } from "../../../worktree.js";
17
+ import { getPackageVersion } from "../../../utils.js";
18
+ import { resolveBaseBranchLabel, resolveBaseBranchRef, } from "../utils/baseBranch.js";
19
+ const SPINNER_FRAMES = ["", "", "", "", "", "", "", ""];
20
20
  const COMPLETION_HOLD_DURATION_MS = 3000;
21
- const PROTECTED_BRANCH_WARNING = 'Root branches operate directly in the repository root. Create a new branch if you need a dedicated worktree.';
21
+ const PROTECTED_BRANCH_WARNING = "Root branches operate directly in the repository root. Create a new branch if you need a dedicated worktree.";
22
22
  const getSpinnerFrame = (index) => {
23
23
  const frame = SPINNER_FRAMES[index];
24
- if (typeof frame === 'string') {
24
+ if (typeof frame === "string") {
25
25
  return frame;
26
26
  }
27
- return SPINNER_FRAMES[0] ?? '';
27
+ return SPINNER_FRAMES[0] ?? "";
28
28
  };
29
29
  /**
30
30
  * App - Top-level component for Ink.js UI
@@ -66,7 +66,8 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
66
66
  return undefined;
67
67
  }
68
68
  const interval = setInterval(() => {
69
- spinnerFrameIndexRef.current = (spinnerFrameIndexRef.current + 1) % SPINNER_FRAMES.length;
69
+ spinnerFrameIndexRef.current =
70
+ (spinnerFrameIndexRef.current + 1) % SPINNER_FRAMES.length;
70
71
  setSpinnerFrameIndex(spinnerFrameIndexRef.current);
71
72
  }, 120);
72
73
  return () => {
@@ -83,17 +84,17 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
83
84
  if (cleanupProcessingBranch) {
84
85
  setCleanupIndicators((prev) => {
85
86
  const current = prev[cleanupProcessingBranch];
86
- if (current && current.icon === frame && current.color === 'cyan') {
87
+ if (current && current.icon === frame && current.color === "cyan") {
87
88
  return prev;
88
89
  }
89
90
  const next = {
90
91
  ...prev,
91
- [cleanupProcessingBranch]: { icon: frame, color: 'cyan' },
92
+ [cleanupProcessingBranch]: { icon: frame, color: "cyan" },
92
93
  };
93
94
  return next;
94
95
  });
95
96
  }
96
- setCleanupFooterMessage({ text: `Processing... ${frame}`, color: 'cyan' });
97
+ setCleanupFooterMessage({ text: `Processing... ${frame}`, color: "cyan" });
97
98
  }, [cleanupInputLocked, cleanupProcessingBranch, spinnerFrameIndex]);
98
99
  useEffect(() => {
99
100
  if (!hiddenBranches.length) {
@@ -113,9 +114,11 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
113
114
  }, []);
114
115
  const visibleBranches = useMemo(() => branches.filter((branch) => !hiddenBranches.includes(branch.name)), [branches, hiddenBranches]);
115
116
  // Helper function to create content-based hash for branches
116
- const branchHash = useMemo(() => visibleBranches.map((b) => `${b.name}-${b.type}-${b.isCurrent}`).join(','), [visibleBranches]);
117
+ const branchHash = useMemo(() => visibleBranches
118
+ .map((b) => `${b.name}-${b.type}-${b.isCurrent}`)
119
+ .join(","), [visibleBranches]);
117
120
  // Helper function to create content-based hash for worktrees
118
- const worktreeHash = useMemo(() => worktrees.map((w) => `${w.branch}-${w.path}`).join(','), [worktrees]);
121
+ const worktreeHash = useMemo(() => worktrees.map((w) => `${w.branch}-${w.path}`).join(","), [worktrees]);
119
122
  // Format branches to BranchItems (memoized for performance with content-based dependencies)
120
123
  const branchItems = useMemo(() => {
121
124
  // Build worktreeMap for sorting
@@ -139,46 +142,48 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
139
142
  isAccessible: wt.isAccessible ?? true,
140
143
  })), [worktrees]);
141
144
  const resolveBaseBranch = useCallback(() => {
142
- const localMain = branches.find((branch) => branch.type === 'local' && (branch.name === 'main' || branch.name === 'master'));
145
+ const localMain = branches.find((branch) => branch.type === "local" &&
146
+ (branch.name === "main" || branch.name === "master"));
143
147
  if (localMain) {
144
148
  return localMain.name;
145
149
  }
146
- const develop = branches.find((branch) => branch.type === 'local' && (branch.name === 'develop' || branch.name === 'dev'));
150
+ const develop = branches.find((branch) => branch.type === "local" &&
151
+ (branch.name === "develop" || branch.name === "dev"));
147
152
  if (develop) {
148
153
  return develop.name;
149
154
  }
150
- return 'main';
155
+ return "main";
151
156
  }, [branches]);
152
157
  const baseBranchLabel = useMemo(() => resolveBaseBranchLabel(creationSourceBranch, selectedBranch, resolveBaseBranch), [creationSourceBranch, resolveBaseBranch, selectedBranch]);
153
158
  // Handle branch selection
154
159
  const toLocalBranchName = useCallback((remoteName) => {
155
- const segments = remoteName.split('/');
160
+ const segments = remoteName.split("/");
156
161
  if (segments.length <= 1) {
157
162
  return remoteName;
158
163
  }
159
- return segments.slice(1).join('/');
164
+ return segments.slice(1).join("/");
160
165
  }, []);
161
166
  const inferBranchCategory = useCallback((branchName) => {
162
167
  const matched = branches.find((branch) => branch.name === branchName);
163
168
  if (matched) {
164
169
  return matched.branchType;
165
170
  }
166
- if (branchName === 'main' || branchName === 'master') {
167
- return 'main';
171
+ if (branchName === "main" || branchName === "master") {
172
+ return "main";
168
173
  }
169
- if (branchName === 'develop' || branchName === 'dev') {
170
- return 'develop';
174
+ if (branchName === "develop" || branchName === "dev") {
175
+ return "develop";
171
176
  }
172
- if (branchName.startsWith('feature/')) {
173
- return 'feature';
177
+ if (branchName.startsWith("feature/")) {
178
+ return "feature";
174
179
  }
175
- if (branchName.startsWith('hotfix/')) {
176
- return 'hotfix';
180
+ if (branchName.startsWith("hotfix/")) {
181
+ return "hotfix";
177
182
  }
178
- if (branchName.startsWith('release/')) {
179
- return 'release';
183
+ if (branchName.startsWith("release/")) {
184
+ return "release";
180
185
  }
181
- return 'other';
186
+ return "other";
182
187
  }, [branches]);
183
188
  const isProtectedSelection = useCallback((branch) => {
184
189
  if (!branch) {
@@ -186,9 +191,11 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
186
191
  }
187
192
  return (isProtectedBranchName(branch.name) ||
188
193
  isProtectedBranchName(branch.displayName) ||
189
- (branch.remoteBranch ? isProtectedBranchName(branch.remoteBranch) : false) ||
190
- branch.branchCategory === 'main' ||
191
- branch.branchCategory === 'develop');
194
+ (branch.remoteBranch
195
+ ? isProtectedBranchName(branch.remoteBranch)
196
+ : false) ||
197
+ branch.branchCategory === "main" ||
198
+ branch.branchCategory === "develop");
192
199
  }, [isProtectedBranchName]);
193
200
  const protectedBranchInfo = useMemo(() => {
194
201
  if (!selectedBranch) {
@@ -204,18 +211,18 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
204
211
  };
205
212
  }, [selectedBranch, isProtectedSelection]);
206
213
  const handleSelect = useCallback((item) => {
207
- const selection = item.type === 'remote'
214
+ const selection = item.type === "remote"
208
215
  ? {
209
216
  name: toLocalBranchName(item.name),
210
217
  displayName: item.name,
211
- branchType: 'remote',
218
+ branchType: "remote",
212
219
  branchCategory: item.branchType,
213
220
  remoteBranch: item.name,
214
221
  }
215
222
  : {
216
223
  name: item.name,
217
224
  displayName: item.name,
218
- branchType: 'local',
225
+ branchType: "local",
219
226
  branchCategory: item.branchType,
220
227
  };
221
228
  const protectedSelected = isProtectedSelection(selection);
@@ -225,14 +232,21 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
225
232
  if (protectedSelected) {
226
233
  setCleanupFooterMessage({
227
234
  text: PROTECTED_BRANCH_WARNING,
228
- color: 'yellow',
235
+ color: "yellow",
229
236
  });
230
237
  }
231
238
  else {
232
239
  setCleanupFooterMessage(null);
233
240
  }
234
- navigateTo('branch-action-selector');
235
- }, [isProtectedSelection, navigateTo, setCleanupFooterMessage, setCreationSourceBranch, setSelectedTool, toLocalBranchName]);
241
+ navigateTo("branch-action-selector");
242
+ }, [
243
+ isProtectedSelection,
244
+ navigateTo,
245
+ setCleanupFooterMessage,
246
+ setCreationSourceBranch,
247
+ setSelectedTool,
248
+ toLocalBranchName,
249
+ ]);
236
250
  // Handle navigation
237
251
  const handleNavigate = useCallback((screen) => {
238
252
  navigateTo(screen);
@@ -241,14 +255,19 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
241
255
  setSelectedBranch({
242
256
  name: worktree.branch,
243
257
  displayName: worktree.branch,
244
- branchType: 'local',
258
+ branchType: "local",
245
259
  branchCategory: inferBranchCategory(worktree.branch),
246
260
  });
247
261
  setSelectedTool(null);
248
262
  setCreationSourceBranch(null);
249
263
  setCleanupFooterMessage(null);
250
- navigateTo('ai-tool-selector');
251
- }, [inferBranchCategory, navigateTo, setCleanupFooterMessage, setCreationSourceBranch]);
264
+ navigateTo("ai-tool-selector");
265
+ }, [
266
+ inferBranchCategory,
267
+ navigateTo,
268
+ setCleanupFooterMessage,
269
+ setCreationSourceBranch,
270
+ ]);
252
271
  // Handle branch action selection
253
272
  const handleProtectedBranchSwitch = useCallback(async () => {
254
273
  if (!selectedBranch) {
@@ -257,12 +276,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
257
276
  try {
258
277
  setCleanupFooterMessage({
259
278
  text: `Preparing root branch '${selectedBranch.displayName ?? selectedBranch.name}'...`,
260
- color: 'cyan',
279
+ color: "cyan",
261
280
  });
262
281
  const repoRoot = await getRepositoryRoot();
263
282
  const remoteRef = selectedBranch.remoteBranch ??
264
- (selectedBranch.branchType === 'remote'
265
- ? selectedBranch.displayName ?? selectedBranch.name
283
+ (selectedBranch.branchType === "remote"
284
+ ? (selectedBranch.displayName ?? selectedBranch.name)
266
285
  : null);
267
286
  const result = await switchToProtectedBranch({
268
287
  branchName: selectedBranch.name,
@@ -270,43 +289,43 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
270
289
  remoteRef: remoteRef ?? null,
271
290
  });
272
291
  let successMessage = `'${selectedBranch.displayName ?? selectedBranch.name}' will use the repository root.`;
273
- if (result === 'remote') {
292
+ if (result === "remote") {
274
293
  successMessage = `Created a local tracking branch for '${selectedBranch.displayName ?? selectedBranch.name}' and switched to the protected branch.`;
275
294
  }
276
- else if (result === 'local') {
295
+ else if (result === "local") {
277
296
  successMessage = `Checked out '${selectedBranch.displayName ?? selectedBranch.name}' in the repository root.`;
278
297
  }
279
298
  setCleanupFooterMessage({
280
299
  text: successMessage,
281
- color: 'green',
300
+ color: "green",
282
301
  });
283
302
  refresh();
284
- navigateTo('ai-tool-selector');
303
+ navigateTo("ai-tool-selector");
285
304
  }
286
305
  catch (error) {
287
306
  const message = error instanceof Error ? error.message : String(error);
288
307
  setCleanupFooterMessage({
289
308
  text: `Failed to switch root branch: ${message}`,
290
- color: 'red',
309
+ color: "red",
291
310
  });
292
- console.error('Failed to switch protected branch:', error);
311
+ console.error("Failed to switch protected branch:", error);
293
312
  }
294
- }, [
295
- navigateTo,
296
- refresh,
297
- selectedBranch,
298
- setCleanupFooterMessage,
299
- ]);
313
+ }, [navigateTo, refresh, selectedBranch, setCleanupFooterMessage]);
300
314
  const handleUseExistingBranch = useCallback(() => {
301
315
  if (selectedBranch && isProtectedSelection(selectedBranch)) {
302
316
  void handleProtectedBranchSwitch();
303
317
  return;
304
318
  }
305
- navigateTo('ai-tool-selector');
306
- }, [handleProtectedBranchSwitch, isProtectedSelection, navigateTo, selectedBranch]);
319
+ navigateTo("ai-tool-selector");
320
+ }, [
321
+ handleProtectedBranchSwitch,
322
+ isProtectedSelection,
323
+ navigateTo,
324
+ selectedBranch,
325
+ ]);
307
326
  const handleCreateNewBranch = useCallback(() => {
308
327
  setCreationSourceBranch(selectedBranch);
309
- navigateTo('branch-creator');
328
+ navigateTo("branch-creator");
310
329
  }, [navigateTo, selectedBranch]);
311
330
  // Handle quit
312
331
  const handleQuit = useCallback(() => {
@@ -332,16 +351,16 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
332
351
  setSelectedBranch({
333
352
  name: branchName,
334
353
  displayName: branchName,
335
- branchType: 'local',
354
+ branchType: "local",
336
355
  branchCategory: inferBranchCategory(branchName),
337
356
  });
338
357
  setSelectedTool(null);
339
358
  setCleanupFooterMessage(null);
340
- navigateTo('ai-tool-selector');
359
+ navigateTo("ai-tool-selector");
341
360
  }
342
361
  catch (error) {
343
362
  // On error, go back to branch list
344
- console.error('Failed to create branch:', error);
363
+ console.error("Failed to create branch:", error);
345
364
  goBack();
346
365
  refresh();
347
366
  }
@@ -382,7 +401,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
382
401
  setCleanupInputLocked(true);
383
402
  setCleanupIndicators({});
384
403
  const initialFrame = getSpinnerFrame(0);
385
- setCleanupFooterMessage({ text: `Processing... ${initialFrame}`, color: 'cyan' });
404
+ setCleanupFooterMessage({
405
+ text: `Processing... ${initialFrame}`,
406
+ color: "cyan",
407
+ });
386
408
  setCleanupProcessingBranch(null);
387
409
  spinnerFrameIndexRef.current = 0;
388
410
  setSpinnerFrameIndex(0);
@@ -393,7 +415,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
393
415
  catch (error) {
394
416
  const message = error instanceof Error ? error.message : String(error);
395
417
  setCleanupIndicators({});
396
- setCleanupFooterMessage({ text: `❌ ${message}`, color: 'red' });
418
+ setCleanupFooterMessage({ text: `❌ ${message}`, color: "red" });
397
419
  setCleanupInputLocked(false);
398
420
  completionTimerRef.current = setTimeout(() => {
399
421
  setCleanupFooterMessage(null);
@@ -403,7 +425,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
403
425
  }
404
426
  if (targets.length === 0) {
405
427
  setCleanupIndicators({});
406
- setCleanupFooterMessage({ text: '✅ Nothing to clean up.', color: 'green' });
428
+ setCleanupFooterMessage({
429
+ text: "✅ Nothing to clean up.",
430
+ color: "green",
431
+ });
407
432
  setCleanupInputLocked(false);
408
433
  completionTimerRef.current = setTimeout(() => {
409
434
  setCleanupFooterMessage(null);
@@ -414,8 +439,8 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
414
439
  // Reset hidden branches that may already be gone
415
440
  setHiddenBranches((prev) => prev.filter((name) => targets.find((t) => t.branch === name) === undefined));
416
441
  const initialIndicators = targets.reduce((acc, target, index) => {
417
- const icon = index === 0 ? getSpinnerFrame(0) : '';
418
- const color = index === 0 ? 'cyan' : 'yellow';
442
+ const icon = index === 0 ? getSpinnerFrame(0) : "";
443
+ const color = index === 0 ? "cyan" : "yellow";
419
444
  acc[target.branch] = { icon, color };
420
445
  return acc;
421
446
  }, {});
@@ -424,7 +449,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
424
449
  setCleanupProcessingBranch(firstTarget ? firstTarget.branch : null);
425
450
  spinnerFrameIndexRef.current = 0;
426
451
  setSpinnerFrameIndex(0);
427
- setCleanupFooterMessage({ text: `Processing... ${getSpinnerFrame(0)}`, color: 'cyan' });
452
+ setCleanupFooterMessage({
453
+ text: `Processing... ${getSpinnerFrame(0)}`,
454
+ color: "cyan",
455
+ });
428
456
  for (let index = 0; index < targets.length; index += 1) {
429
457
  const currentTarget = targets[index];
430
458
  if (!currentTarget) {
@@ -436,58 +464,69 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
436
464
  setSpinnerFrameIndex(0);
437
465
  setCleanupIndicators((prev) => {
438
466
  const updated = { ...prev };
439
- updated[target.branch] = { icon: getSpinnerFrame(0), color: 'cyan' };
467
+ updated[target.branch] = { icon: getSpinnerFrame(0), color: "cyan" };
440
468
  for (const pending of targets.slice(index + 1)) {
441
469
  const current = updated[pending.branch];
442
- if (!current || current.icon !== '') {
443
- updated[pending.branch] = { icon: '', color: 'yellow' };
470
+ if (!current || current.icon !== "") {
471
+ updated[pending.branch] = { icon: "", color: "yellow" };
444
472
  }
445
473
  }
446
474
  return updated;
447
475
  });
448
476
  const shouldSkip = target.hasUncommittedChanges ||
449
477
  target.hasUnpushedCommits ||
450
- (target.cleanupType === 'worktree-and-branch' && (!target.worktreePath || target.isAccessible === false));
478
+ (target.cleanupType === "worktree-and-branch" &&
479
+ (!target.worktreePath || target.isAccessible === false));
451
480
  if (shouldSkip) {
452
481
  setCleanupIndicators((prev) => ({
453
482
  ...prev,
454
- [target.branch]: { icon: '⏭️', color: 'yellow' },
483
+ [target.branch]: { icon: "⏭️", color: "yellow" },
455
484
  }));
456
485
  setCleanupProcessingBranch(null);
457
486
  continue;
458
487
  }
459
488
  try {
460
- if (target.cleanupType === 'worktree-and-branch' && target.worktreePath) {
489
+ if (target.cleanupType === "worktree-and-branch" &&
490
+ target.worktreePath) {
461
491
  await removeWorktree(target.worktreePath, true);
462
492
  }
463
493
  await deleteBranch(target.branch, true);
464
494
  succeededBranches.push(target.branch);
465
495
  setCleanupIndicators((prev) => ({
466
496
  ...prev,
467
- [target.branch]: { icon: '', color: 'green' },
497
+ [target.branch]: { icon: "", color: "green" },
468
498
  }));
469
499
  }
470
500
  catch {
471
- const icon = '';
501
+ const icon = "";
472
502
  setCleanupIndicators((prev) => ({
473
503
  ...prev,
474
- [target.branch]: { icon, color: 'red' },
504
+ [target.branch]: { icon, color: "red" },
475
505
  }));
476
506
  }
477
507
  setCleanupProcessingBranch(null);
478
508
  }
479
509
  setCleanupProcessingBranch(null);
480
510
  setCleanupInputLocked(false);
481
- setCleanupFooterMessage({ text: 'Cleanup completed. Finalizing...', color: 'green' });
482
- const holdDuration = typeof process !== 'undefined' && process.env?.NODE_ENV === 'test'
511
+ setCleanupFooterMessage({
512
+ text: "Cleanup completed. Finalizing...",
513
+ color: "green",
514
+ });
515
+ const holdDuration = typeof process !== "undefined" && process.env?.NODE_ENV === "test"
483
516
  ? 0
484
517
  : COMPLETION_HOLD_DURATION_MS;
485
518
  completionTimerRef.current = setTimeout(resetAfterWait, holdDuration);
486
- }, [cleanupInputLocked, deleteBranch, getMergedPRWorktrees, refresh, removeWorktree]);
519
+ }, [
520
+ cleanupInputLocked,
521
+ deleteBranch,
522
+ getMergedPRWorktrees,
523
+ refresh,
524
+ removeWorktree,
525
+ ]);
487
526
  // Handle AI tool selection
488
527
  const handleToolSelect = useCallback((tool) => {
489
528
  setSelectedTool(tool);
490
- navigateTo('execution-mode-selector');
529
+ navigateTo("execution-mode-selector");
491
530
  }, [navigateTo]);
492
531
  // Handle session selection
493
532
  const handleSessionSelect = useCallback((_session) => {
@@ -517,20 +556,20 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
517
556
  // Render screen based on currentScreen
518
557
  const renderScreen = () => {
519
558
  switch (currentScreen) {
520
- case 'branch-list':
559
+ case "branch-list":
521
560
  return (React.createElement(BranchListScreen, { branches: branchItems, stats: stats, onSelect: handleSelect, onNavigate: handleNavigate, onQuit: handleQuit, onCleanupCommand: handleCleanupCommand, onRefresh: refresh, loading: loading, error: error, lastUpdated: lastUpdated, loadingIndicatorDelay: loadingIndicatorDelay, cleanupUI: {
522
561
  indicators: cleanupIndicators,
523
562
  footerMessage: cleanupFooterMessage,
524
563
  inputLocked: cleanupInputLocked,
525
564
  }, version: version, workingDirectory: workingDirectory }));
526
- case 'worktree-manager':
565
+ case "worktree-manager":
527
566
  return (React.createElement(WorktreeManagerScreen, { worktrees: worktreeItems, onBack: goBack, onSelect: handleWorktreeSelect, version: version }));
528
- case 'branch-creator':
567
+ case "branch-creator":
529
568
  return (React.createElement(BranchCreatorScreen, { onBack: goBack, onCreate: handleCreate, baseBranch: baseBranchLabel, version: version }));
530
- case 'branch-action-selector': {
569
+ case "branch-action-selector": {
531
570
  const isProtected = Boolean(protectedBranchInfo);
532
571
  const baseProps = {
533
- selectedBranch: selectedBranch?.displayName ?? '',
572
+ selectedBranch: selectedBranch?.displayName ?? "",
534
573
  onUseExisting: handleUseExistingBranch,
535
574
  onCreateNew: handleCreateNewBranch,
536
575
  onBack: goBack,
@@ -541,12 +580,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
541
580
  }
542
581
  return React.createElement(BranchActionSelectorScreen, { ...baseProps });
543
582
  }
544
- case 'ai-tool-selector':
545
- return React.createElement(AIToolSelectorScreen, { onBack: goBack, onSelect: handleToolSelect, version: version });
546
- case 'session-selector':
583
+ case "ai-tool-selector":
584
+ return (React.createElement(AIToolSelectorScreen, { onBack: goBack, onSelect: handleToolSelect, version: version }));
585
+ case "session-selector":
547
586
  // TODO: Implement session data fetching
548
587
  return (React.createElement(SessionSelectorScreen, { sessions: [], onBack: goBack, onSelect: handleSessionSelect, version: version }));
549
- case 'execution-mode-selector':
588
+ case "execution-mode-selector":
550
589
  return (React.createElement(ExecutionModeSelectorScreen, { onBack: goBack, onSelect: handleModeSelect, version: version }));
551
590
  default:
552
591
  return (React.createElement(BranchListScreen, { branches: branchItems, stats: stats, onSelect: handleSelect, onNavigate: handleNavigate, onQuit: handleQuit, onRefresh: refresh, loading: loading, error: error, lastUpdated: lastUpdated, loadingIndicatorDelay: loadingIndicatorDelay, version: version, workingDirectory: workingDirectory }));