@akiojin/gwt 4.9.1 → 4.11.6

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 (605) hide show
  1. package/README.ja.md +4 -4
  2. package/README.md +4 -4
  3. package/dist/claude.d.ts +1 -0
  4. package/dist/claude.d.ts.map +1 -1
  5. package/dist/claude.js +52 -49
  6. package/dist/claude.js.map +1 -1
  7. package/dist/cli/ui/App.solid.d.ts +29 -0
  8. package/dist/cli/ui/App.solid.d.ts.map +1 -0
  9. package/dist/cli/ui/App.solid.js +1197 -0
  10. package/dist/cli/ui/App.solid.js.map +1 -0
  11. package/dist/cli/ui/components/solid/Footer.d.ts +10 -0
  12. package/dist/cli/ui/components/solid/Footer.d.ts.map +1 -0
  13. package/dist/cli/ui/components/solid/Footer.js +10 -0
  14. package/dist/cli/ui/components/solid/Footer.js.map +1 -0
  15. package/dist/cli/ui/components/{parts → solid}/Header.d.ts +1 -6
  16. package/dist/cli/ui/components/solid/Header.d.ts.map +1 -0
  17. package/dist/cli/ui/components/solid/Header.js +13 -0
  18. package/dist/cli/ui/components/solid/Header.js.map +1 -0
  19. package/dist/cli/ui/components/solid/HelpOverlay.d.ts +8 -0
  20. package/dist/cli/ui/components/solid/HelpOverlay.d.ts.map +1 -0
  21. package/dist/cli/ui/components/solid/HelpOverlay.js +118 -0
  22. package/dist/cli/ui/components/solid/HelpOverlay.js.map +1 -0
  23. package/dist/cli/ui/components/solid/QuickStartStep.d.ts +17 -0
  24. package/dist/cli/ui/components/solid/QuickStartStep.d.ts.map +1 -0
  25. package/dist/cli/ui/components/solid/QuickStartStep.js +139 -0
  26. package/dist/cli/ui/components/solid/QuickStartStep.js.map +1 -0
  27. package/dist/cli/ui/components/solid/SelectInput.d.ts +22 -0
  28. package/dist/cli/ui/components/solid/SelectInput.d.ts.map +1 -0
  29. package/dist/cli/ui/components/solid/SelectInput.js +44 -0
  30. package/dist/cli/ui/components/solid/SelectInput.js.map +1 -0
  31. package/dist/cli/ui/components/solid/TextInput.d.ts +12 -0
  32. package/dist/cli/ui/components/solid/TextInput.d.ts.map +1 -0
  33. package/dist/cli/ui/components/solid/TextInput.js +9 -0
  34. package/dist/cli/ui/components/solid/TextInput.js.map +1 -0
  35. package/dist/cli/ui/components/solid/WizardController.d.ts +34 -0
  36. package/dist/cli/ui/components/solid/WizardController.d.ts.map +1 -0
  37. package/dist/cli/ui/components/solid/WizardController.js +215 -0
  38. package/dist/cli/ui/components/solid/WizardController.js.map +1 -0
  39. package/dist/cli/ui/components/solid/WizardPopup.d.ts +26 -0
  40. package/dist/cli/ui/components/solid/WizardPopup.d.ts.map +1 -0
  41. package/dist/cli/ui/components/solid/WizardPopup.js +68 -0
  42. package/dist/cli/ui/components/solid/WizardPopup.js.map +1 -0
  43. package/dist/cli/ui/components/solid/WizardSteps.d.ts +52 -0
  44. package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -0
  45. package/dist/cli/ui/components/solid/WizardSteps.js +462 -0
  46. package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -0
  47. package/dist/cli/ui/core/index.d.ts +12 -0
  48. package/dist/cli/ui/core/index.d.ts.map +1 -0
  49. package/dist/cli/ui/core/index.js +15 -0
  50. package/dist/cli/ui/core/index.js.map +1 -0
  51. package/dist/cli/ui/core/keybindings.d.ts +106 -0
  52. package/dist/cli/ui/core/keybindings.d.ts.map +1 -0
  53. package/dist/cli/ui/core/keybindings.js +270 -0
  54. package/dist/cli/ui/core/keybindings.js.map +1 -0
  55. package/dist/cli/ui/core/theme.d.ts +114 -0
  56. package/dist/cli/ui/core/theme.d.ts.map +1 -0
  57. package/dist/cli/ui/core/theme.js +170 -0
  58. package/dist/cli/ui/core/theme.js.map +1 -0
  59. package/dist/cli/ui/core/types.d.ts +156 -0
  60. package/dist/cli/ui/core/types.d.ts.map +1 -0
  61. package/dist/cli/ui/core/types.js +10 -0
  62. package/dist/cli/ui/core/types.js.map +1 -0
  63. package/dist/cli/ui/hooks/solid/useScrollableList.d.ts +26 -0
  64. package/dist/cli/ui/hooks/solid/useScrollableList.d.ts.map +1 -0
  65. package/dist/cli/ui/hooks/solid/useScrollableList.js +89 -0
  66. package/dist/cli/ui/hooks/solid/useScrollableList.js.map +1 -0
  67. package/dist/cli/ui/hooks/solid/useTerminalSize.d.ts +10 -0
  68. package/dist/cli/ui/hooks/solid/useTerminalSize.d.ts.map +1 -0
  69. package/dist/cli/ui/hooks/solid/useTerminalSize.js +16 -0
  70. package/dist/cli/ui/hooks/solid/useTerminalSize.js.map +1 -0
  71. package/dist/cli/ui/index.solid.d.ts +5 -0
  72. package/dist/cli/ui/index.solid.d.ts.map +1 -0
  73. package/dist/cli/ui/index.solid.js +21 -0
  74. package/dist/cli/ui/index.solid.js.map +1 -0
  75. package/dist/cli/ui/{components/screens → screens/solid}/BranchListScreen.d.ts +8 -22
  76. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -0
  77. package/dist/cli/ui/screens/solid/BranchListScreen.js +756 -0
  78. package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -0
  79. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +10 -0
  80. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -0
  81. package/dist/cli/ui/screens/solid/ConfirmScreen.js +37 -0
  82. package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -0
  83. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts +14 -0
  84. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -0
  85. package/dist/cli/ui/screens/solid/EnvironmentScreen.js +91 -0
  86. package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -0
  87. package/dist/cli/ui/screens/solid/ErrorScreen.d.ts +8 -0
  88. package/dist/cli/ui/screens/solid/ErrorScreen.d.ts.map +1 -0
  89. package/dist/cli/ui/screens/solid/ErrorScreen.js +18 -0
  90. package/dist/cli/ui/screens/solid/ErrorScreen.js.map +1 -0
  91. package/dist/cli/ui/screens/solid/InputScreen.d.ts +13 -0
  92. package/dist/cli/ui/screens/solid/InputScreen.d.ts.map +1 -0
  93. package/dist/cli/ui/screens/solid/InputScreen.js +17 -0
  94. package/dist/cli/ui/screens/solid/InputScreen.js.map +1 -0
  95. package/dist/cli/ui/screens/solid/LoadingIndicator.d.ts +9 -0
  96. package/dist/cli/ui/screens/solid/LoadingIndicator.d.ts.map +1 -0
  97. package/dist/cli/ui/screens/solid/LoadingIndicator.js +43 -0
  98. package/dist/cli/ui/screens/solid/LoadingIndicator.js.map +1 -0
  99. package/dist/cli/ui/{components/screens → screens/solid}/LogDetailScreen.d.ts +2 -2
  100. package/dist/cli/ui/screens/solid/LogDetailScreen.d.ts.map +1 -0
  101. package/dist/cli/ui/screens/solid/LogDetailScreen.js +34 -0
  102. package/dist/cli/ui/screens/solid/LogDetailScreen.js.map +1 -0
  103. package/dist/cli/ui/{components/screens/LogListScreen.d.ts → screens/solid/LogScreen.d.ts} +4 -4
  104. package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -0
  105. package/dist/cli/ui/screens/solid/LogScreen.js +95 -0
  106. package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -0
  107. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts +17 -0
  108. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -0
  109. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +112 -0
  110. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -0
  111. package/dist/cli/ui/screens/solid/ProfileScreen.d.ts +17 -0
  112. package/dist/cli/ui/screens/solid/ProfileScreen.d.ts.map +1 -0
  113. package/dist/cli/ui/screens/solid/ProfileScreen.js +50 -0
  114. package/dist/cli/ui/screens/solid/ProfileScreen.js.map +1 -0
  115. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts +20 -0
  116. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -0
  117. package/dist/cli/ui/screens/solid/SelectorScreen.js +90 -0
  118. package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -0
  119. package/dist/cli/ui/screens/solid/WorktreeCreateScreen.d.ts +13 -0
  120. package/dist/cli/ui/screens/solid/WorktreeCreateScreen.d.ts.map +1 -0
  121. package/dist/cli/ui/screens/solid/WorktreeCreateScreen.js +59 -0
  122. package/dist/cli/ui/screens/solid/WorktreeCreateScreen.js.map +1 -0
  123. package/dist/cli/ui/stores/appStore.d.ts +143 -0
  124. package/dist/cli/ui/stores/appStore.d.ts.map +1 -0
  125. package/dist/cli/ui/stores/appStore.js +158 -0
  126. package/dist/cli/ui/stores/appStore.js.map +1 -0
  127. package/dist/cli/ui/stores/branchStore.d.ts +159 -0
  128. package/dist/cli/ui/stores/branchStore.d.ts.map +1 -0
  129. package/dist/cli/ui/stores/branchStore.js +275 -0
  130. package/dist/cli/ui/stores/branchStore.js.map +1 -0
  131. package/dist/cli/ui/stores/index.d.ts +11 -0
  132. package/dist/cli/ui/stores/index.d.ts.map +1 -0
  133. package/dist/cli/ui/stores/index.js +14 -0
  134. package/dist/cli/ui/stores/index.js.map +1 -0
  135. package/dist/cli/ui/stores/uiStore.d.ts +146 -0
  136. package/dist/cli/ui/stores/uiStore.d.ts.map +1 -0
  137. package/dist/cli/ui/stores/uiStore.js +166 -0
  138. package/dist/cli/ui/stores/uiStore.js.map +1 -0
  139. package/dist/cli/ui/types.d.ts +16 -1
  140. package/dist/cli/ui/types.d.ts.map +1 -1
  141. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  142. package/dist/cli/ui/utils/branchFormatter.js +7 -210
  143. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  144. package/dist/cli/ui/utils/continueSession.d.ts +4 -0
  145. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  146. package/dist/cli/ui/utils/continueSession.js +30 -0
  147. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  148. package/dist/client/assets/{index-ChHC-Puh.css → index-BbfV7Wuj.css} +1 -1
  149. package/dist/client/assets/index-CoAyq5x1.js +78 -0
  150. package/dist/client/index.html +2 -2
  151. package/dist/codex.d.ts +1 -0
  152. package/dist/codex.d.ts.map +1 -1
  153. package/dist/codex.js +86 -45
  154. package/dist/codex.js.map +1 -1
  155. package/dist/config/builtin-coding-agents.d.ts +4 -0
  156. package/dist/config/builtin-coding-agents.d.ts.map +1 -1
  157. package/dist/config/builtin-coding-agents.js +18 -3
  158. package/dist/config/builtin-coding-agents.js.map +1 -1
  159. package/dist/config/index.d.ts +2 -0
  160. package/dist/config/index.d.ts.map +1 -1
  161. package/dist/config/index.js +2 -0
  162. package/dist/config/index.js.map +1 -1
  163. package/dist/config/tools.d.ts.map +1 -1
  164. package/dist/config/tools.js +11 -0
  165. package/dist/config/tools.js.map +1 -1
  166. package/dist/gemini.d.ts +1 -0
  167. package/dist/gemini.d.ts.map +1 -1
  168. package/dist/gemini.js +42 -37
  169. package/dist/gemini.js.map +1 -1
  170. package/dist/index.d.ts +1 -1
  171. package/dist/index.d.ts.map +1 -1
  172. package/dist/index.js +122 -102
  173. package/dist/index.js.map +1 -1
  174. package/dist/launcher.d.ts.map +1 -1
  175. package/dist/launcher.js +18 -3
  176. package/dist/launcher.js.map +1 -1
  177. package/dist/logging/logger.d.ts.map +1 -1
  178. package/dist/logging/logger.js +26 -9
  179. package/dist/logging/logger.js.map +1 -1
  180. package/dist/opentui/highlights-eq9cgrbb.scm +604 -0
  181. package/dist/opentui/highlights-ghv9g403.scm +205 -0
  182. package/dist/opentui/highlights-hk7bwhj4.scm +284 -0
  183. package/dist/opentui/highlights-r812a2qc.scm +150 -0
  184. package/dist/opentui/highlights-x6tmsnaa.scm +115 -0
  185. package/dist/opentui/index.solid.d.ts +2 -0
  186. package/dist/opentui/index.solid.d.ts.map +1 -0
  187. package/dist/opentui/index.solid.js +52034 -0
  188. package/dist/opentui/index.solid.js.map +1 -0
  189. package/dist/opentui/injections-73j83es3.scm +27 -0
  190. package/dist/opentui/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  191. package/dist/opentui/tree-sitter-markdown-411r6y9b.wasm +0 -0
  192. package/dist/opentui/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  193. package/dist/opentui/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  194. package/dist/opentui/tree-sitter-zig-e78zbjpm.wasm +0 -0
  195. package/dist/repositories/worktree.repository.d.ts +1 -0
  196. package/dist/repositories/worktree.repository.d.ts.map +1 -1
  197. package/dist/repositories/worktree.repository.js +7 -0
  198. package/dist/repositories/worktree.repository.js.map +1 -1
  199. package/dist/services/codingAgentResolver.d.ts +2 -0
  200. package/dist/services/codingAgentResolver.d.ts.map +1 -1
  201. package/dist/services/codingAgentResolver.js +30 -4
  202. package/dist/services/codingAgentResolver.js.map +1 -1
  203. package/dist/services/dependency-installer.d.ts.map +1 -1
  204. package/dist/services/dependency-installer.js +0 -5
  205. package/dist/services/dependency-installer.js.map +1 -1
  206. package/dist/types/api.d.ts +3 -0
  207. package/dist/types/api.d.ts.map +1 -1
  208. package/dist/types/coding-agent.d.ts +62 -0
  209. package/dist/types/coding-agent.d.ts.map +1 -0
  210. package/dist/types/coding-agent.js +29 -0
  211. package/dist/types/coding-agent.js.map +1 -0
  212. package/dist/types/tools.d.ts +17 -0
  213. package/dist/types/tools.d.ts.map +1 -1
  214. package/dist/utils/coding-agent-colors.d.ts +88 -0
  215. package/dist/utils/coding-agent-colors.d.ts.map +1 -0
  216. package/dist/utils/coding-agent-colors.js +137 -0
  217. package/dist/utils/coding-agent-colors.js.map +1 -0
  218. package/dist/utils/command.d.ts +1 -1
  219. package/dist/utils/command.d.ts.map +1 -1
  220. package/dist/utils/command.js +10 -1
  221. package/dist/utils/command.js.map +1 -1
  222. package/dist/utils/error-utils.d.ts +27 -0
  223. package/dist/utils/error-utils.d.ts.map +1 -0
  224. package/dist/utils/error-utils.js +98 -0
  225. package/dist/utils/error-utils.js.map +1 -0
  226. package/dist/utils/npmRegistry.d.ts +61 -0
  227. package/dist/utils/npmRegistry.d.ts.map +1 -0
  228. package/dist/utils/npmRegistry.js +180 -0
  229. package/dist/utils/npmRegistry.js.map +1 -0
  230. package/dist/utils/prompt.d.ts +1 -1
  231. package/dist/utils/prompt.js +1 -1
  232. package/dist/utils/session/index.d.ts +5 -3
  233. package/dist/utils/session/index.d.ts.map +1 -1
  234. package/dist/utils/session/index.js +5 -2
  235. package/dist/utils/session/index.js.map +1 -1
  236. package/dist/utils/session/parsers/codex.d.ts.map +1 -1
  237. package/dist/utils/session/parsers/codex.js +8 -1
  238. package/dist/utils/session/parsers/codex.js.map +1 -1
  239. package/dist/utils/session/parsers/index.d.ts +1 -0
  240. package/dist/utils/session/parsers/index.d.ts.map +1 -1
  241. package/dist/utils/session/parsers/index.js +2 -0
  242. package/dist/utils/session/parsers/index.js.map +1 -1
  243. package/dist/utils/session/parsers/opencode.d.ts +23 -0
  244. package/dist/utils/session/parsers/opencode.d.ts.map +1 -0
  245. package/dist/utils/session/parsers/opencode.js +89 -0
  246. package/dist/utils/session/parsers/opencode.js.map +1 -0
  247. package/dist/utils/session/types.d.ts +4 -0
  248. package/dist/utils/session/types.d.ts.map +1 -1
  249. package/dist/utils/terminal.d.ts +1 -0
  250. package/dist/utils/terminal.d.ts.map +1 -1
  251. package/dist/utils/terminal.js +20 -0
  252. package/dist/utils/terminal.js.map +1 -1
  253. package/dist/utils.d.ts +9 -0
  254. package/dist/utils.d.ts.map +1 -1
  255. package/dist/utils.js +33 -2
  256. package/dist/utils.js.map +1 -1
  257. package/dist/web/client/src/components/CodingAgentLaunchModal.d.ts.map +1 -1
  258. package/dist/web/client/src/components/CodingAgentLaunchModal.js +7 -16
  259. package/dist/web/client/src/components/CodingAgentLaunchModal.js.map +1 -1
  260. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -1
  261. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +7 -2
  262. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -1
  263. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -1
  264. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +2 -1
  265. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -1
  266. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +2 -2
  267. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -1
  268. package/dist/web/client/src/components/branch-detail/ToolLauncher.js +5 -5
  269. package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -1
  270. package/dist/web/client/src/lib/coding-agent-colors.d.ts +86 -0
  271. package/dist/web/client/src/lib/coding-agent-colors.d.ts.map +1 -0
  272. package/dist/web/client/src/lib/coding-agent-colors.js +135 -0
  273. package/dist/web/client/src/lib/coding-agent-colors.js.map +1 -0
  274. package/dist/web/client/src/pages/BranchDetailPage.js +10 -10
  275. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  276. package/dist/web/server/pty/manager.d.ts +2 -0
  277. package/dist/web/server/pty/manager.d.ts.map +1 -1
  278. package/dist/web/server/pty/manager.js +104 -0
  279. package/dist/web/server/pty/manager.js.map +1 -1
  280. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  281. package/dist/web/server/routes/sessions.js +5 -1
  282. package/dist/web/server/routes/sessions.js.map +1 -1
  283. package/dist/web/server/services/branches.d.ts.map +1 -1
  284. package/dist/web/server/services/branches.js +10 -8
  285. package/dist/web/server/services/branches.js.map +1 -1
  286. package/dist/web/server/services/worktrees.js +2 -2
  287. package/dist/web/server/services/worktrees.js.map +1 -1
  288. package/dist/worktree.d.ts +47 -1
  289. package/dist/worktree.d.ts.map +1 -1
  290. package/dist/worktree.js +280 -94
  291. package/dist/worktree.js.map +1 -1
  292. package/package.json +12 -14
  293. package/src/claude.ts +68 -70
  294. package/src/cli/ui/App.solid.tsx +1823 -0
  295. package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +255 -0
  296. package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +243 -0
  297. package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +237 -0
  298. package/src/cli/ui/__tests__/solid/components/WizardPopup.test.tsx +231 -0
  299. package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +238 -0
  300. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +7 -289
  301. package/src/cli/ui/__tests__/utils/clipboard.test.ts +3 -3
  302. package/src/cli/ui/__tests__/utils/statisticsCalculator.test.ts +1 -1
  303. package/src/cli/ui/components/solid/Footer.tsx +36 -0
  304. package/src/cli/ui/components/{parts → solid}/Header.tsx +17 -28
  305. package/src/cli/ui/components/solid/HelpOverlay.tsx +194 -0
  306. package/src/cli/ui/components/solid/QuickStartStep.tsx +197 -0
  307. package/src/cli/ui/components/{parts → solid}/ScrollableList.tsx +7 -8
  308. package/src/cli/ui/components/solid/SearchInput.tsx +42 -0
  309. package/src/cli/ui/components/solid/SelectInput.tsx +84 -0
  310. package/src/cli/ui/components/solid/Stats.tsx +92 -0
  311. package/src/cli/ui/components/solid/TextInput.tsx +49 -0
  312. package/src/cli/ui/components/solid/WizardController.tsx +384 -0
  313. package/src/cli/ui/components/solid/WizardPopup.tsx +135 -0
  314. package/src/cli/ui/components/solid/WizardSteps.tsx +793 -0
  315. package/src/cli/ui/core/index.ts +17 -0
  316. package/src/cli/ui/core/keybindings.ts +367 -0
  317. package/src/cli/ui/core/theme.ts +234 -0
  318. package/src/cli/ui/core/types.ts +235 -0
  319. package/src/cli/ui/hooks/solid/useAsyncOperation.ts +76 -0
  320. package/src/cli/ui/hooks/solid/useFilter.ts +86 -0
  321. package/src/cli/ui/hooks/solid/useGitOperations.ts +80 -0
  322. package/src/cli/ui/hooks/solid/useKeyHandler.ts +103 -0
  323. package/src/cli/ui/hooks/solid/useScrollableList.ts +149 -0
  324. package/src/cli/ui/hooks/solid/useSelection.ts +77 -0
  325. package/src/cli/ui/hooks/solid/useTerminalSize.ts +22 -0
  326. package/src/cli/ui/index.solid.ts +28 -0
  327. package/src/cli/ui/screens/solid/BranchListScreen.tsx +1050 -0
  328. package/src/cli/ui/screens/solid/ConfirmScreen.tsx +74 -0
  329. package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +159 -0
  330. package/src/cli/ui/screens/solid/ErrorScreen.tsx +42 -0
  331. package/src/cli/ui/screens/solid/InputScreen.tsx +55 -0
  332. package/src/cli/ui/screens/solid/LoadingIndicator.tsx +77 -0
  333. package/src/cli/ui/screens/solid/LogDetailScreen.tsx +75 -0
  334. package/src/cli/ui/screens/solid/LogScreen.tsx +175 -0
  335. package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +192 -0
  336. package/src/cli/ui/screens/solid/ProfileScreen.tsx +98 -0
  337. package/src/cli/ui/screens/solid/SelectorScreen.tsx +170 -0
  338. package/src/cli/ui/screens/solid/SettingsScreen.tsx +50 -0
  339. package/src/cli/ui/screens/solid/WorktreeCreateScreen.tsx +136 -0
  340. package/src/cli/ui/screens/solid/WorktreeDeleteScreen.tsx +40 -0
  341. package/src/cli/ui/stores/appStore.ts +208 -0
  342. package/src/cli/ui/stores/branchStore.ts +357 -0
  343. package/src/cli/ui/stores/index.ts +31 -0
  344. package/src/cli/ui/stores/uiStore.ts +226 -0
  345. package/src/cli/ui/types.ts +20 -3
  346. package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +180 -0
  347. package/src/cli/ui/utils/branchFormatter.ts +8 -215
  348. package/src/cli/ui/utils/continueSession.ts +44 -0
  349. package/src/cli/ui/utils/modelOptions.test.ts +1 -1
  350. package/src/codex.ts +100 -43
  351. package/src/config/__tests__/saveSession.test.ts +143 -0
  352. package/src/config/builtin-coding-agents.ts +19 -3
  353. package/src/config/index.ts +4 -0
  354. package/src/config/tools.ts +16 -0
  355. package/src/gemini.ts +58 -43
  356. package/src/index.test.ts +12 -12
  357. package/src/index.ts +164 -142
  358. package/src/launcher.ts +22 -3
  359. package/src/logging/logger.ts +32 -10
  360. package/src/opentui/index.solid.ts +1 -0
  361. package/src/repositories/worktree.repository.ts +8 -0
  362. package/src/services/__tests__/BatchMergeService.test.ts +62 -66
  363. package/src/services/__tests__/WorktreeOrchestrator.test.ts +8 -7
  364. package/src/services/codingAgentResolver.ts +30 -4
  365. package/src/services/dependency-installer.ts +0 -7
  366. package/src/types/api.ts +3 -0
  367. package/src/types/coding-agent.ts +85 -0
  368. package/src/types/tools.ts +19 -0
  369. package/src/utils/__tests__/npmRegistry.test.ts +250 -0
  370. package/src/utils/__tests__/prompt.test.ts +4 -5
  371. package/src/utils/coding-agent-colors.ts +165 -0
  372. package/src/utils/command.ts +10 -1
  373. package/src/utils/error-utils.ts +133 -0
  374. package/src/utils/npmRegistry.ts +249 -0
  375. package/src/utils/prompt.ts +1 -1
  376. package/src/utils/session/index.ts +10 -2
  377. package/src/utils/session/parsers/codex.ts +9 -1
  378. package/src/utils/session/parsers/index.ts +6 -0
  379. package/src/utils/session/parsers/opencode.ts +110 -0
  380. package/src/utils/session/types.ts +5 -0
  381. package/src/utils/terminal.ts +24 -0
  382. package/src/utils.test.ts +1 -1
  383. package/src/utils.ts +37 -4
  384. package/src/web/client/src/components/CodingAgentLaunchModal.tsx +12 -21
  385. package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +16 -1
  386. package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +7 -1
  387. package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +11 -6
  388. package/src/web/client/src/lib/coding-agent-colors.ts +149 -0
  389. package/src/web/client/src/pages/BranchDetailPage.tsx +11 -11
  390. package/src/web/server/pty/manager.ts +139 -0
  391. package/src/web/server/routes/sessions.ts +6 -0
  392. package/src/web/server/services/branches.ts +11 -8
  393. package/src/web/server/services/worktrees.ts +2 -2
  394. package/src/worktree.ts +366 -107
  395. package/dist/cli/ui/components/App.d.ts +0 -25
  396. package/dist/cli/ui/components/App.d.ts.map +0 -1
  397. package/dist/cli/ui/components/App.js +0 -988
  398. package/dist/cli/ui/components/App.js.map +0 -1
  399. package/dist/cli/ui/components/common/Confirm.d.ts +0 -13
  400. package/dist/cli/ui/components/common/Confirm.d.ts.map +0 -1
  401. package/dist/cli/ui/components/common/Confirm.js +0 -20
  402. package/dist/cli/ui/components/common/Confirm.js.map +0 -1
  403. package/dist/cli/ui/components/common/ErrorBoundary.d.ts +0 -23
  404. package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +0 -1
  405. package/dist/cli/ui/components/common/ErrorBoundary.js +0 -37
  406. package/dist/cli/ui/components/common/ErrorBoundary.js.map +0 -1
  407. package/dist/cli/ui/components/common/Input.d.ts +0 -19
  408. package/dist/cli/ui/components/common/Input.d.ts.map +0 -1
  409. package/dist/cli/ui/components/common/Input.js +0 -22
  410. package/dist/cli/ui/components/common/Input.js.map +0 -1
  411. package/dist/cli/ui/components/common/LoadingIndicator.d.ts +0 -19
  412. package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +0 -1
  413. package/dist/cli/ui/components/common/LoadingIndicator.js +0 -61
  414. package/dist/cli/ui/components/common/LoadingIndicator.js.map +0 -1
  415. package/dist/cli/ui/components/common/Select.d.ts +0 -38
  416. package/dist/cli/ui/components/common/Select.d.ts.map +0 -1
  417. package/dist/cli/ui/components/common/Select.js +0 -151
  418. package/dist/cli/ui/components/common/Select.js.map +0 -1
  419. package/dist/cli/ui/components/common/SpinnerIcon.d.ts +0 -20
  420. package/dist/cli/ui/components/common/SpinnerIcon.d.ts.map +0 -1
  421. package/dist/cli/ui/components/common/SpinnerIcon.js +0 -61
  422. package/dist/cli/ui/components/common/SpinnerIcon.js.map +0 -1
  423. package/dist/cli/ui/components/parts/Footer.d.ts +0 -15
  424. package/dist/cli/ui/components/parts/Footer.d.ts.map +0 -1
  425. package/dist/cli/ui/components/parts/Footer.js +0 -20
  426. package/dist/cli/ui/components/parts/Footer.js.map +0 -1
  427. package/dist/cli/ui/components/parts/Header.d.ts.map +0 -1
  428. package/dist/cli/ui/components/parts/Header.js +0 -24
  429. package/dist/cli/ui/components/parts/Header.js.map +0 -1
  430. package/dist/cli/ui/components/parts/MergeStatusList.d.ts +0 -13
  431. package/dist/cli/ui/components/parts/MergeStatusList.d.ts.map +0 -1
  432. package/dist/cli/ui/components/parts/MergeStatusList.js +0 -52
  433. package/dist/cli/ui/components/parts/MergeStatusList.js.map +0 -1
  434. package/dist/cli/ui/components/parts/ProgressBar.d.ts +0 -13
  435. package/dist/cli/ui/components/parts/ProgressBar.d.ts.map +0 -1
  436. package/dist/cli/ui/components/parts/ProgressBar.js +0 -53
  437. package/dist/cli/ui/components/parts/ProgressBar.js.map +0 -1
  438. package/dist/cli/ui/components/parts/ScrollableList.d.ts +0 -12
  439. package/dist/cli/ui/components/parts/ScrollableList.d.ts.map +0 -1
  440. package/dist/cli/ui/components/parts/ScrollableList.js +0 -11
  441. package/dist/cli/ui/components/parts/ScrollableList.js.map +0 -1
  442. package/dist/cli/ui/components/parts/Stats.d.ts +0 -10
  443. package/dist/cli/ui/components/parts/Stats.d.ts.map +0 -1
  444. package/dist/cli/ui/components/parts/Stats.js +0 -55
  445. package/dist/cli/ui/components/parts/Stats.js.map +0 -1
  446. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts +0 -17
  447. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts.map +0 -1
  448. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js +0 -42
  449. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js.map +0 -1
  450. package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts +0 -17
  451. package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts.map +0 -1
  452. package/dist/cli/ui/components/screens/BatchMergeResultScreen.js +0 -72
  453. package/dist/cli/ui/components/screens/BatchMergeResultScreen.js.map +0 -1
  454. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +0 -18
  455. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +0 -1
  456. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +0 -151
  457. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +0 -1
  458. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +0 -1
  459. package/dist/cli/ui/components/screens/BranchListScreen.js +0 -476
  460. package/dist/cli/ui/components/screens/BranchListScreen.js.map +0 -1
  461. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +0 -30
  462. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +0 -1
  463. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +0 -145
  464. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +0 -1
  465. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.d.ts +0 -27
  466. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.d.ts.map +0 -1
  467. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.js +0 -93
  468. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.js.map +0 -1
  469. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts +0 -19
  470. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts.map +0 -1
  471. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js +0 -577
  472. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js.map +0 -1
  473. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +0 -45
  474. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +0 -1
  475. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +0 -95
  476. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +0 -1
  477. package/dist/cli/ui/components/screens/LogDatePickerScreen.d.ts +0 -10
  478. package/dist/cli/ui/components/screens/LogDatePickerScreen.d.ts.map +0 -1
  479. package/dist/cli/ui/components/screens/LogDatePickerScreen.js +0 -44
  480. package/dist/cli/ui/components/screens/LogDatePickerScreen.js.map +0 -1
  481. package/dist/cli/ui/components/screens/LogDetailScreen.d.ts.map +0 -1
  482. package/dist/cli/ui/components/screens/LogDetailScreen.js +0 -34
  483. package/dist/cli/ui/components/screens/LogDetailScreen.js.map +0 -1
  484. package/dist/cli/ui/components/screens/LogListScreen.d.ts.map +0 -1
  485. package/dist/cli/ui/components/screens/LogListScreen.js +0 -107
  486. package/dist/cli/ui/components/screens/LogListScreen.js.map +0 -1
  487. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +0 -24
  488. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +0 -1
  489. package/dist/cli/ui/components/screens/ModelSelectorScreen.js +0 -197
  490. package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +0 -1
  491. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +0 -29
  492. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts.map +0 -1
  493. package/dist/cli/ui/components/screens/PRCleanupScreen.js +0 -92
  494. package/dist/cli/ui/components/screens/PRCleanupScreen.js.map +0 -1
  495. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +0 -31
  496. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts.map +0 -1
  497. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +0 -67
  498. package/dist/cli/ui/components/screens/SessionSelectorScreen.js.map +0 -1
  499. package/dist/cli/ui/hooks/useAppInput.d.ts +0 -21
  500. package/dist/cli/ui/hooks/useAppInput.d.ts.map +0 -1
  501. package/dist/cli/ui/hooks/useAppInput.js +0 -138
  502. package/dist/cli/ui/hooks/useAppInput.js.map +0 -1
  503. package/dist/cli/ui/hooks/useBatchMerge.d.ts +0 -17
  504. package/dist/cli/ui/hooks/useBatchMerge.d.ts.map +0 -1
  505. package/dist/cli/ui/hooks/useBatchMerge.js +0 -77
  506. package/dist/cli/ui/hooks/useBatchMerge.js.map +0 -1
  507. package/dist/cli/ui/hooks/useGitData.d.ts +0 -21
  508. package/dist/cli/ui/hooks/useGitData.d.ts.map +0 -1
  509. package/dist/cli/ui/hooks/useGitData.js +0 -229
  510. package/dist/cli/ui/hooks/useGitData.js.map +0 -1
  511. package/dist/cli/ui/hooks/useProfiles.d.ts +0 -41
  512. package/dist/cli/ui/hooks/useProfiles.d.ts.map +0 -1
  513. package/dist/cli/ui/hooks/useProfiles.js +0 -136
  514. package/dist/cli/ui/hooks/useProfiles.js.map +0 -1
  515. package/dist/cli/ui/hooks/useScreenState.d.ts +0 -12
  516. package/dist/cli/ui/hooks/useScreenState.d.ts.map +0 -1
  517. package/dist/cli/ui/hooks/useScreenState.js +0 -30
  518. package/dist/cli/ui/hooks/useScreenState.js.map +0 -1
  519. package/dist/cli/ui/hooks/useTerminalSize.d.ts +0 -9
  520. package/dist/cli/ui/hooks/useTerminalSize.d.ts.map +0 -1
  521. package/dist/cli/ui/hooks/useTerminalSize.js +0 -24
  522. package/dist/cli/ui/hooks/useTerminalSize.js.map +0 -1
  523. package/dist/cli/ui/hooks/useToolStatus.d.ts +0 -30
  524. package/dist/cli/ui/hooks/useToolStatus.d.ts.map +0 -1
  525. package/dist/cli/ui/hooks/useToolStatus.js +0 -49
  526. package/dist/cli/ui/hooks/useToolStatus.js.map +0 -1
  527. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts +0 -24
  528. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +0 -1
  529. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +0 -65
  530. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +0 -1
  531. package/dist/client/assets/index-LNPtOrn3.js +0 -78
  532. package/src/cli/ui/__tests__/SKIPPED_TESTS.md +0 -119
  533. package/src/cli/ui/__tests__/acceptance/branchList.acceptance.test.tsx.skip +0 -239
  534. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +0 -225
  535. package/src/cli/ui/__tests__/acceptance/realtimeUpdate.acceptance.test.tsx.skip +0 -219
  536. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +0 -212
  537. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +0 -440
  538. package/src/cli/ui/__tests__/components/App.test.tsx +0 -365
  539. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +0 -91
  540. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +0 -80
  541. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +0 -104
  542. package/src/cli/ui/__tests__/components/common/Input.test.tsx +0 -100
  543. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +0 -148
  544. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +0 -255
  545. package/src/cli/ui/__tests__/components/common/Select.test.tsx +0 -335
  546. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +0 -65
  547. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +0 -55
  548. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +0 -69
  549. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +0 -148
  550. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +0 -253
  551. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +0 -1070
  552. package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +0 -142
  553. package/src/cli/ui/__tests__/components/screens/CodingAgentSelectorScreen.test.tsx +0 -174
  554. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +0 -182
  555. package/src/cli/ui/__tests__/components/screens/LogDetailScreen.test.tsx +0 -57
  556. package/src/cli/ui/__tests__/components/screens/LogListScreen.test.tsx +0 -102
  557. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +0 -216
  558. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +0 -147
  559. package/src/cli/ui/__tests__/hooks/useGitData.nonblocking.test.tsx +0 -206
  560. package/src/cli/ui/__tests__/hooks/useGitData.test.ts +0 -197
  561. package/src/cli/ui/__tests__/hooks/useGitData.test.ts.skip +0 -228
  562. package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +0 -147
  563. package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +0 -99
  564. package/src/cli/ui/__tests__/integration/branchList.test.tsx.skip +0 -253
  565. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +0 -436
  566. package/src/cli/ui/__tests__/integration/navigation.test.tsx +0 -514
  567. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +0 -509
  568. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx.skip +0 -216
  569. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +0 -193
  570. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +0 -234
  571. package/src/cli/ui/components/App.tsx +0 -1456
  572. package/src/cli/ui/components/common/Confirm.tsx +0 -44
  573. package/src/cli/ui/components/common/ErrorBoundary.tsx +0 -60
  574. package/src/cli/ui/components/common/Input.tsx +0 -58
  575. package/src/cli/ui/components/common/LoadingIndicator.tsx +0 -98
  576. package/src/cli/ui/components/common/Select.tsx +0 -247
  577. package/src/cli/ui/components/common/SpinnerIcon.tsx +0 -86
  578. package/src/cli/ui/components/parts/Footer.tsx +0 -41
  579. package/src/cli/ui/components/parts/Header.test.tsx +0 -75
  580. package/src/cli/ui/components/parts/MergeStatusList.tsx +0 -75
  581. package/src/cli/ui/components/parts/ProgressBar.tsx +0 -73
  582. package/src/cli/ui/components/parts/Stats.tsx +0 -88
  583. package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +0 -74
  584. package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +0 -108
  585. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +0 -242
  586. package/src/cli/ui/components/screens/BranchListScreen.tsx +0 -744
  587. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +0 -241
  588. package/src/cli/ui/components/screens/CodingAgentSelectorScreen.tsx +0 -159
  589. package/src/cli/ui/components/screens/EnvironmentProfileScreen.tsx +0 -928
  590. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +0 -176
  591. package/src/cli/ui/components/screens/LogDatePickerScreen.tsx +0 -83
  592. package/src/cli/ui/components/screens/LogDetailScreen.tsx +0 -67
  593. package/src/cli/ui/components/screens/LogListScreen.tsx +0 -192
  594. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +0 -320
  595. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +0 -171
  596. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +0 -135
  597. package/src/cli/ui/hooks/useAppInput.ts +0 -172
  598. package/src/cli/ui/hooks/useBatchMerge.ts +0 -96
  599. package/src/cli/ui/hooks/useGitData.ts +0 -347
  600. package/src/cli/ui/hooks/useProfiles.ts +0 -211
  601. package/src/cli/ui/hooks/useScreenState.ts +0 -44
  602. package/src/cli/ui/hooks/useTerminalSize.ts +0 -33
  603. package/src/cli/ui/hooks/useToolStatus.ts +0 -68
  604. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +0 -111
  605. package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +0 -264
@@ -0,0 +1,1823 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+ import { useKeyboard, useRenderer } from "@opentui/solid";
3
+ import {
4
+ createEffect,
5
+ createMemo,
6
+ createSignal,
7
+ onCleanup,
8
+ onMount,
9
+ } from "solid-js";
10
+ import type {
11
+ BranchItem,
12
+ BranchInfo,
13
+ CodingAgentId,
14
+ CleanupStatus,
15
+ InferenceLevel,
16
+ SelectedBranchState,
17
+ Statistics,
18
+ WorktreeInfo as UIWorktreeInfo,
19
+ } from "./types.js";
20
+ import type { FormattedLogEntry } from "../../logging/formatter.js";
21
+ import { BranchListScreen } from "./screens/solid/BranchListScreen.js";
22
+ import { HelpOverlay } from "./components/solid/HelpOverlay.js";
23
+ import {
24
+ WizardController,
25
+ type WizardResult,
26
+ } from "./components/solid/WizardController.js";
27
+ import {
28
+ SelectorScreen,
29
+ type SelectorItem,
30
+ } from "./screens/solid/SelectorScreen.js";
31
+ import { LogScreen } from "./screens/solid/LogScreen.js";
32
+ import { LogDetailScreen } from "./screens/solid/LogDetailScreen.js";
33
+ import { ErrorScreen } from "./screens/solid/ErrorScreen.js";
34
+ import { LoadingIndicatorScreen } from "./screens/solid/LoadingIndicator.js";
35
+ import { WorktreeCreateScreen } from "./screens/solid/WorktreeCreateScreen.js";
36
+ import { InputScreen } from "./screens/solid/InputScreen.js";
37
+ import { ConfirmScreen } from "./screens/solid/ConfirmScreen.js";
38
+ import { EnvironmentScreen } from "./screens/solid/EnvironmentScreen.js";
39
+ import { ProfileScreen } from "./screens/solid/ProfileScreen.js";
40
+ import { ProfileEnvScreen } from "./screens/solid/ProfileEnvScreen.js";
41
+ import { calculateStatistics } from "./utils/statisticsCalculator.js";
42
+ import { formatBranchItems } from "./utils/branchFormatter.js";
43
+ import { createLogger } from "../../logging/logger.js";
44
+
45
+ const logger = createLogger({ category: "app" });
46
+ import {
47
+ getDefaultInferenceForModel,
48
+ getDefaultModelOption,
49
+ normalizeModelId,
50
+ } from "./utils/modelOptions.js";
51
+ import {
52
+ resolveBaseBranchLabel,
53
+ resolveBaseBranchRef,
54
+ } from "./utils/baseBranch.js";
55
+ import {
56
+ getAllBranches,
57
+ getCurrentBranch,
58
+ getLocalBranches,
59
+ getRepositoryRoot,
60
+ deleteBranch,
61
+ } from "../../git.js";
62
+ import {
63
+ isProtectedBranchName,
64
+ getCleanupStatus,
65
+ listAdditionalWorktrees,
66
+ removeWorktree,
67
+ repairWorktrees,
68
+ type WorktreeInfo as WorktreeEntry,
69
+ } from "../../worktree.js";
70
+ import {
71
+ getConfig,
72
+ getLastToolUsageMap,
73
+ loadSession,
74
+ type ToolSessionEntry,
75
+ } from "../../config/index.js";
76
+ import { getAllCodingAgents } from "../../config/tools.js";
77
+ import {
78
+ buildLogFilePath,
79
+ getTodayLogDate,
80
+ readLogFileLines,
81
+ resolveLogDir,
82
+ } from "../../logging/reader.js";
83
+ import { parseLogLines } from "../../logging/formatter.js";
84
+ import { copyToClipboard } from "./utils/clipboard.js";
85
+ import { getPackageVersion } from "../../utils.js";
86
+ import {
87
+ createProfile,
88
+ deleteProfile,
89
+ loadProfiles,
90
+ setActiveProfile,
91
+ updateProfile,
92
+ } from "../../config/profiles.js";
93
+ import {
94
+ isValidProfileName,
95
+ type ProfilesConfig,
96
+ } from "../../types/profiles.js";
97
+ import { BRANCH_PREFIXES } from "../../config/constants.js";
98
+
99
+ export type ExecutionMode = "normal" | "continue" | "resume";
100
+
101
+ export interface SelectionResult {
102
+ branch: string;
103
+ displayName: string;
104
+ branchType: "local" | "remote";
105
+ remoteBranch?: string;
106
+ baseBranch?: string;
107
+ isNewBranch?: boolean;
108
+ tool: CodingAgentId;
109
+ mode: ExecutionMode;
110
+ skipPermissions: boolean;
111
+ model?: string | null;
112
+ inferenceLevel?: InferenceLevel;
113
+ sessionId?: string | null;
114
+ toolVersion?: string | null;
115
+ }
116
+
117
+ export type AppScreen =
118
+ | "branch-list"
119
+ | "tool-select"
120
+ | "mode-select"
121
+ | "skip-permissions"
122
+ | "log-list"
123
+ | "log-detail"
124
+ | "profile"
125
+ | "profile-env"
126
+ | "profile-input"
127
+ | "profile-confirm"
128
+ | "profile-os-env"
129
+ | "profile-error"
130
+ | "worktree-create"
131
+ | "loading"
132
+ | "error";
133
+
134
+ type ProfileInputMode = "create-profile" | "add-env" | "edit-env";
135
+ type ProfileConfirmMode = "delete-profile" | "delete-env";
136
+ type StatusColor = "cyan" | "green" | "yellow" | "red";
137
+
138
+ interface CleanupIndicator {
139
+ icon: string;
140
+ isSpinning?: boolean;
141
+ color?: StatusColor;
142
+ }
143
+
144
+ interface CleanupTask {
145
+ branch: string;
146
+ worktreePath: string | null;
147
+ cleanupType: "worktree-and-branch" | "branch-only";
148
+ isAccessible?: boolean;
149
+ }
150
+
151
+ export interface AppSolidProps {
152
+ onExit?: (result?: SelectionResult) => void;
153
+ loadingIndicatorDelay?: number;
154
+ initialScreen?: AppScreen;
155
+ version?: string | null;
156
+ workingDirectory?: string;
157
+ branches?: BranchItem[];
158
+ stats?: Statistics;
159
+ }
160
+
161
+ const DEFAULT_SCREEN: AppScreen = "branch-list";
162
+
163
+ const buildStats = (branches: BranchItem[]): Statistics =>
164
+ calculateStatistics(branches);
165
+
166
+ const applyCleanupStatus = (
167
+ items: BranchItem[],
168
+ statusByBranch: Map<string, CleanupStatus>,
169
+ ): BranchItem[] =>
170
+ items.map((branch) => {
171
+ const status = statusByBranch.get(branch.name);
172
+ if (!status) {
173
+ return { ...branch, safeToCleanup: false, isUnmerged: false };
174
+ }
175
+ const safeToCleanup =
176
+ status.hasUpstream && status.reasons.includes("no-diff-with-base");
177
+ const isUnmerged = status.hasUpstream && status.hasUniqueCommits;
178
+ const worktree = branch.worktree
179
+ ? {
180
+ ...branch.worktree,
181
+ ...(status.hasUncommittedChanges !== undefined
182
+ ? { hasUncommittedChanges: status.hasUncommittedChanges }
183
+ : {}),
184
+ }
185
+ : undefined;
186
+ const base: BranchItem = {
187
+ ...branch,
188
+ safeToCleanup,
189
+ isUnmerged,
190
+ hasUnpushedCommits: status.hasUnpushedCommits,
191
+ };
192
+ return worktree ? { ...base, worktree } : base;
193
+ });
194
+
195
+ const toLocalBranchName = (name: string): string => {
196
+ const segments = name.split("/");
197
+ if (segments.length <= 1) {
198
+ return name;
199
+ }
200
+ return segments.slice(1).join("/");
201
+ };
202
+
203
+ const toSelectedBranchState = (branch: BranchItem): SelectedBranchState => {
204
+ const isRemote = branch.type === "remote";
205
+ const baseName = isRemote ? toLocalBranchName(branch.name) : branch.name;
206
+ return {
207
+ name: baseName,
208
+ displayName: branch.name,
209
+ branchType: branch.type,
210
+ branchCategory: branch.branchType,
211
+ ...(isRemote ? { remoteBranch: branch.name } : {}),
212
+ };
213
+ };
214
+
215
+ export function AppSolid(props: AppSolidProps) {
216
+ const renderer = useRenderer();
217
+ let hasExited = false;
218
+
219
+ const exitApp = (result?: SelectionResult) => {
220
+ if (hasExited) return;
221
+ hasExited = true;
222
+ props.onExit?.(result);
223
+ renderer.destroy();
224
+ };
225
+
226
+ const [currentScreen, setCurrentScreen] = createSignal<AppScreen>(
227
+ props.initialScreen ?? DEFAULT_SCREEN,
228
+ );
229
+ const [screenStack, setScreenStack] = createSignal<AppScreen[]>([]);
230
+ const [helpVisible, setHelpVisible] = createSignal(false);
231
+ const [wizardVisible, setWizardVisible] = createSignal(false);
232
+
233
+ const [branchItems, setBranchItems] = createSignal<BranchItem[]>(
234
+ props.branches ?? [],
235
+ );
236
+ const [stats, setStats] = createSignal<Statistics>(
237
+ props.stats ?? buildStats(props.branches ?? []),
238
+ );
239
+ const [loading, setLoading] = createSignal(!props.branches);
240
+ const [error, setError] = createSignal<Error | null>(null);
241
+
242
+ const [toolItems, setToolItems] = createSignal<SelectorItem[]>([]);
243
+ const [toolError, setToolError] = createSignal<Error | null>(null);
244
+
245
+ const [version, setVersion] = createSignal<string | null>(
246
+ props.version ?? null,
247
+ );
248
+
249
+ const workingDirectory = createMemo(
250
+ () => props.workingDirectory ?? process.cwd(),
251
+ );
252
+
253
+ const [selectedBranch, setSelectedBranch] =
254
+ createSignal<SelectedBranchState | null>(null);
255
+ const [selectedTool, setSelectedTool] = createSignal<CodingAgentId | null>(
256
+ null,
257
+ );
258
+ const [selectedMode, setSelectedMode] = createSignal<ExecutionMode>("normal");
259
+ const [selectedBranches, setSelectedBranches] = createSignal<string[]>([]);
260
+ const [branchFooterMessage, setBranchFooterMessage] = createSignal<{
261
+ text: string;
262
+ isSpinning?: boolean;
263
+ color?: StatusColor;
264
+ } | null>(null);
265
+ const [branchInputLocked, setBranchInputLocked] = createSignal(false);
266
+ const [cleanupIndicators, setCleanupIndicators] = createSignal<
267
+ Record<string, CleanupIndicator>
268
+ >({});
269
+ const [cleanupStatusByBranch, setCleanupStatusByBranch] = createSignal<
270
+ Map<string, CleanupStatus>
271
+ >(new Map());
272
+ const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
273
+ const [isNewBranch, setIsNewBranch] = createSignal(false);
274
+ const [newBranchBaseRef, setNewBranchBaseRef] = createSignal<string | null>(
275
+ null,
276
+ );
277
+ const [creationSource, setCreationSource] =
278
+ createSignal<SelectedBranchState | null>(null);
279
+ const [createBranchName, setCreateBranchName] = createSignal("", {
280
+ equals: false,
281
+ });
282
+ const [suppressCreateKey, setSuppressCreateKey] = createSignal<string | null>(
283
+ null,
284
+ );
285
+ const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
286
+
287
+ // セッション履歴(最終使用エージェントなど)
288
+ const [sessionHistory, setSessionHistory] = createSignal<ToolSessionEntry[]>(
289
+ [],
290
+ );
291
+
292
+ // 選択中ブランチの履歴をフィルタリング
293
+ const historyForBranch = createMemo(() => {
294
+ const history = sessionHistory();
295
+ const branch = selectedBranch();
296
+ if (!branch) return [];
297
+ // 選択中ブランチにマッチする履歴エントリを新しい順で返す
298
+ return history
299
+ .filter((entry) => entry.branch === branch.name)
300
+ .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
301
+ });
302
+
303
+ const [logEntries, setLogEntries] = createSignal<FormattedLogEntry[]>([]);
304
+ const [logLoading, setLogLoading] = createSignal(false);
305
+ const [logError, setLogError] = createSignal<string | null>(null);
306
+ const [logSelectedEntry, setLogSelectedEntry] =
307
+ createSignal<FormattedLogEntry | null>(null);
308
+ const [logSelectedDate, _setLogSelectedDate] = createSignal<string | null>(
309
+ getTodayLogDate(),
310
+ );
311
+ const [logNotification, setLogNotification] = createSignal<{
312
+ message: string;
313
+ tone: "success" | "error";
314
+ } | null>(null);
315
+
316
+ const [profileItems, setProfileItems] = createSignal<
317
+ { name: string; displayName?: string; isActive?: boolean }[]
318
+ >([]);
319
+ const [activeProfile, setActiveProfileName] = createSignal<string | null>(
320
+ null,
321
+ );
322
+ const [profileError, setProfileError] = createSignal<Error | null>(null);
323
+ const [profilesConfig, setProfilesConfig] =
324
+ createSignal<ProfilesConfig | null>(null);
325
+ const [profileActionError, setProfileActionError] =
326
+ createSignal<Error | null>(null);
327
+ const [profileActionHint, setProfileActionHint] = createSignal<string | null>(
328
+ null,
329
+ );
330
+ const [selectedProfileName, setSelectedProfileName] = createSignal<
331
+ string | null
332
+ >(null);
333
+ const [profileInputValue, setProfileInputValue] = createSignal("", {
334
+ equals: false,
335
+ });
336
+ const [profileInputMode, setProfileInputMode] =
337
+ createSignal<ProfileInputMode>("create-profile");
338
+ const [profileInputSuppressKey, setProfileInputSuppressKey] = createSignal<
339
+ string | null
340
+ >(null);
341
+ const [profileEnvKey, setProfileEnvKey] = createSignal<string | null>(null);
342
+ const [profileConfirmMode, setProfileConfirmMode] =
343
+ createSignal<ProfileConfirmMode>("delete-profile");
344
+
345
+ const logDir = createMemo(() => resolveLogDir(workingDirectory()));
346
+ const selectedProfileConfig = createMemo(() => {
347
+ const name = selectedProfileName();
348
+ const config = profilesConfig();
349
+ if (!name || !config?.profiles?.[name]) {
350
+ return null;
351
+ }
352
+ return { name, profile: config.profiles[name] };
353
+ });
354
+ const profileEnvVariables = createMemo(() => {
355
+ const entry = selectedProfileConfig();
356
+ if (!entry) {
357
+ return [];
358
+ }
359
+ return Object.entries(entry.profile.env ?? {})
360
+ .map(([key, value]) => ({ key, value }))
361
+ .sort((a, b) => a.key.localeCompare(b.key));
362
+ });
363
+ const osEnvVariables = createMemo(() =>
364
+ Object.entries(process.env)
365
+ .filter(([, value]) => typeof value === "string")
366
+ .map(([key, value]) => ({ key, value: String(value) }))
367
+ .sort((a, b) => a.key.localeCompare(b.key)),
368
+ );
369
+
370
+ let cleanupSafetyRequestId = 0;
371
+ const refreshCleanupSafety = async () => {
372
+ const requestId = ++cleanupSafetyRequestId;
373
+ setCleanupSafetyLoading(true);
374
+ try {
375
+ const cleanupStatuses = await getCleanupStatus();
376
+ if (requestId !== cleanupSafetyRequestId) {
377
+ return;
378
+ }
379
+ const statusByBranch = new Map(
380
+ cleanupStatuses.map((status) => [status.branch, status]),
381
+ );
382
+ setCleanupStatusByBranch(statusByBranch);
383
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
384
+ } catch (err) {
385
+ if (requestId !== cleanupSafetyRequestId) {
386
+ return;
387
+ }
388
+ logger.warn({ err }, "Failed to refresh cleanup safety indicators");
389
+ const empty = new Map<string, CleanupStatus>();
390
+ setCleanupStatusByBranch(empty);
391
+ setBranchItems((items) => applyCleanupStatus(items, empty));
392
+ } finally {
393
+ if (requestId === cleanupSafetyRequestId) {
394
+ setCleanupSafetyLoading(false);
395
+ }
396
+ }
397
+ };
398
+
399
+ let logNotificationTimer: ReturnType<typeof setTimeout> | null = null;
400
+ let branchFooterTimer: ReturnType<typeof setTimeout> | null = null;
401
+ const BRANCH_LOAD_TIMEOUT_MS = 3000;
402
+ const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
403
+
404
+ const isHelpKey = (key: {
405
+ name: string;
406
+ sequence: string;
407
+ ctrl: boolean;
408
+ meta: boolean;
409
+ super?: boolean;
410
+ hyper?: boolean;
411
+ option?: boolean;
412
+ }) => {
413
+ if (key.ctrl || key.meta || key.super || key.hyper || key.option) {
414
+ return false;
415
+ }
416
+ return key.name === "h" || key.sequence === "h" || key.sequence === "?";
417
+ };
418
+
419
+ useKeyboard((key) => {
420
+ if (key.repeated) {
421
+ return;
422
+ }
423
+
424
+ if (helpVisible()) {
425
+ if (key.name === "escape" || isHelpKey(key)) {
426
+ setHelpVisible(false);
427
+ }
428
+ return;
429
+ }
430
+
431
+ if (isHelpKey(key)) {
432
+ setHelpVisible(true);
433
+ }
434
+ });
435
+
436
+ const navigateTo = (screen: AppScreen) => {
437
+ setScreenStack((prev) => [...prev, currentScreen()]);
438
+ setCurrentScreen(screen);
439
+ };
440
+
441
+ const goBack = () => {
442
+ const stack = screenStack();
443
+ if (stack.length === 0) {
444
+ return;
445
+ }
446
+ const previous = stack[stack.length - 1] ?? DEFAULT_SCREEN;
447
+ setScreenStack(stack.slice(0, -1));
448
+
449
+ // branch-list に戻る場合は選択状態をリセット
450
+ if (previous === "branch-list") {
451
+ setSelectedBranch(null);
452
+ setSelectedTool(null);
453
+ setSelectedMode("normal");
454
+ setIsNewBranch(false);
455
+ setNewBranchBaseRef(null);
456
+ setCreationSource(null);
457
+ }
458
+
459
+ setCurrentScreen(previous);
460
+ };
461
+
462
+ const openProfileError = (err: unknown, hint: string) => {
463
+ setProfileActionError(err instanceof Error ? err : new Error(String(err)));
464
+ setProfileActionHint(hint);
465
+ navigateTo("profile-error");
466
+ };
467
+
468
+ const showLogNotification = (message: string, tone: "success" | "error") => {
469
+ setLogNotification({ message, tone });
470
+ if (logNotificationTimer) {
471
+ clearTimeout(logNotificationTimer);
472
+ }
473
+ logNotificationTimer = setTimeout(() => {
474
+ setLogNotification(null);
475
+ }, 2000);
476
+ };
477
+
478
+ const loadLogEntries = async (date: string | null) => {
479
+ const targetDate = date ?? getTodayLogDate();
480
+ setLogLoading(true);
481
+ setLogError(null);
482
+ try {
483
+ const filePath = buildLogFilePath(logDir(), targetDate);
484
+ const lines = await readLogFileLines(filePath);
485
+ const parsed = parseLogLines(lines, { limit: 100 });
486
+ setLogEntries(parsed);
487
+ } catch (err) {
488
+ setLogEntries([]);
489
+ setLogError(err instanceof Error ? err.message : "Failed to load logs");
490
+ } finally {
491
+ setLogLoading(false);
492
+ }
493
+ };
494
+
495
+ const refreshBranches = async () => {
496
+ setLoading(true);
497
+ setError(null);
498
+ void refreshCleanupSafety();
499
+ try {
500
+ const repoRoot = await getRepositoryRoot();
501
+ const worktreesPromise = listAdditionalWorktrees();
502
+ const localBranchesPromise = getLocalBranches(repoRoot);
503
+ const currentBranchPromise = getCurrentBranch(repoRoot);
504
+ const lastToolUsagePromise = getLastToolUsageMap(repoRoot);
505
+
506
+ const [localBranches, currentBranch, worktrees, lastToolUsageMap] =
507
+ await Promise.all([
508
+ withTimeout(localBranchesPromise, BRANCH_LOAD_TIMEOUT_MS).catch(
509
+ () => [],
510
+ ),
511
+ withTimeout(currentBranchPromise, BRANCH_LOAD_TIMEOUT_MS).catch(
512
+ () => null,
513
+ ),
514
+ withTimeout(worktreesPromise, BRANCH_LOAD_TIMEOUT_MS).catch(() => []),
515
+ withTimeout(lastToolUsagePromise, BRANCH_LOAD_TIMEOUT_MS).catch(
516
+ () => new Map<string, ToolSessionEntry>(),
517
+ ),
518
+ ]);
519
+
520
+ if (currentBranch) {
521
+ localBranches.forEach((branch) => {
522
+ if (branch.name === currentBranch) {
523
+ branch.isCurrent = true;
524
+ }
525
+ });
526
+ }
527
+
528
+ const initial = buildBranchList(
529
+ localBranches,
530
+ worktrees,
531
+ lastToolUsageMap,
532
+ );
533
+ const initialItems = applyCleanupStatus(
534
+ initial.items,
535
+ cleanupStatusByBranch(),
536
+ );
537
+ setBranchItems(initialItems);
538
+ setStats(buildStats(initialItems));
539
+
540
+ void (async () => {
541
+ const [branches, latestWorktrees] = await Promise.all([
542
+ withTimeout(
543
+ getAllBranches(repoRoot),
544
+ BRANCH_FULL_LOAD_TIMEOUT_MS,
545
+ ).catch(() => localBranches),
546
+ withTimeout(
547
+ listAdditionalWorktrees(),
548
+ BRANCH_FULL_LOAD_TIMEOUT_MS,
549
+ ).catch(() => worktrees),
550
+ ]);
551
+
552
+ const full = buildBranchList(
553
+ branches,
554
+ latestWorktrees,
555
+ lastToolUsageMap,
556
+ );
557
+ const fullItems = applyCleanupStatus(
558
+ full.items,
559
+ cleanupStatusByBranch(),
560
+ );
561
+ setBranchItems(fullItems);
562
+ setStats(buildStats(fullItems));
563
+ })();
564
+ } catch (err) {
565
+ setError(err instanceof Error ? err : new Error(String(err)));
566
+ } finally {
567
+ setLoading(false);
568
+ }
569
+ };
570
+
571
+ const refreshProfiles = async () => {
572
+ try {
573
+ const config = await loadProfiles();
574
+ setProfilesConfig(config);
575
+ const items = Object.entries(config.profiles ?? {}).map(
576
+ ([name, profile]) => ({
577
+ name,
578
+ displayName: profile.displayName,
579
+ isActive: config.activeProfile === name,
580
+ }),
581
+ );
582
+ items.sort((a, b) => a.name.localeCompare(b.name));
583
+ setProfileItems(items);
584
+ setActiveProfileName(config.activeProfile ?? null);
585
+ setProfileError(null);
586
+ } catch (err) {
587
+ setProfilesConfig(null);
588
+ setProfileItems([]);
589
+ setActiveProfileName(null);
590
+ setProfileError(err instanceof Error ? err : new Error(String(err)));
591
+ }
592
+ };
593
+
594
+ createEffect(() => {
595
+ if (props.branches) {
596
+ const statusByBranch = cleanupStatusByBranch();
597
+ const nextItems = applyCleanupStatus(props.branches, statusByBranch);
598
+ setBranchItems(nextItems);
599
+ setStats(props.stats ?? buildStats(nextItems));
600
+ setLoading(false);
601
+ }
602
+ });
603
+
604
+ onMount(() => {
605
+ if (!props.branches) {
606
+ void refreshBranches();
607
+ }
608
+ });
609
+
610
+ onMount(() => {
611
+ if (props.branches) {
612
+ void refreshCleanupSafety();
613
+ }
614
+ });
615
+
616
+ onMount(() => {
617
+ if (props.version === undefined) {
618
+ void getPackageVersion()
619
+ .then((value) => setVersion(value ?? null))
620
+ .catch(() => setVersion(null));
621
+ }
622
+ });
623
+
624
+ onMount(() => {
625
+ void refreshProfiles();
626
+ });
627
+
628
+ onMount(() => {
629
+ void getConfig()
630
+ .then((config) => setDefaultBaseBranch(config.defaultBaseBranch))
631
+ .catch(() => setDefaultBaseBranch("main"));
632
+ });
633
+
634
+ // セッション履歴をロード(最終使用エージェントなど)
635
+ onMount(() => {
636
+ void getRepositoryRoot()
637
+ .then((repoRoot) => loadSession(repoRoot))
638
+ .then((session) => {
639
+ if (session?.history) {
640
+ setSessionHistory(session.history);
641
+ }
642
+ })
643
+ .catch(() => setSessionHistory([]));
644
+ });
645
+
646
+ onMount(() => {
647
+ void getAllCodingAgents()
648
+ .then((agents) => {
649
+ setToolItems(
650
+ agents.map((agent) => ({
651
+ label: agent.displayName,
652
+ value: agent.id,
653
+ })),
654
+ );
655
+ })
656
+ .catch((err) => {
657
+ setToolItems([]);
658
+ setToolError(err instanceof Error ? err : new Error(String(err)));
659
+ });
660
+ });
661
+
662
+ createEffect(() => {
663
+ if (currentScreen() === "log-list") {
664
+ void loadLogEntries(logSelectedDate());
665
+ }
666
+ });
667
+
668
+ onCleanup(() => {
669
+ if (logNotificationTimer) {
670
+ clearTimeout(logNotificationTimer);
671
+ }
672
+ if (branchFooterTimer) {
673
+ clearTimeout(branchFooterTimer);
674
+ }
675
+ });
676
+
677
+ const showBranchFooterMessage = (
678
+ text: string,
679
+ color: StatusColor,
680
+ options?: { spinning?: boolean; timeoutMs?: number },
681
+ ) => {
682
+ if (branchFooterTimer) {
683
+ clearTimeout(branchFooterTimer);
684
+ branchFooterTimer = null;
685
+ }
686
+ setBranchFooterMessage({
687
+ text,
688
+ color,
689
+ ...(options?.spinning ? { isSpinning: true } : {}),
690
+ });
691
+ const timeout = options?.timeoutMs ?? 2000;
692
+ if (timeout > 0) {
693
+ branchFooterTimer = setTimeout(() => {
694
+ setBranchFooterMessage(null);
695
+ }, timeout);
696
+ }
697
+ };
698
+
699
+ const setCleanupIndicator = (
700
+ branchName: string,
701
+ indicator: CleanupIndicator | null,
702
+ ) => {
703
+ setCleanupIndicators((prev) => {
704
+ const next = { ...prev };
705
+ if (indicator) {
706
+ next[branchName] = indicator;
707
+ } else {
708
+ delete next[branchName];
709
+ }
710
+ return next;
711
+ });
712
+ };
713
+
714
+ const handleBranchSelect = (branch: BranchItem) => {
715
+ setSelectedBranch(toSelectedBranchState(branch));
716
+ setIsNewBranch(false);
717
+ setNewBranchBaseRef(null);
718
+ setCreationSource(null);
719
+ setSelectedTool(null);
720
+ setSelectedMode("normal");
721
+ // FR-044: ウィザードポップアップをレイヤー表示
722
+ setWizardVisible(true);
723
+ };
724
+
725
+ const handleQuickCreate = (branch: BranchItem | null) => {
726
+ // 選択中のブランチをベースにウィザードを開始
727
+ if (branch) {
728
+ setSelectedBranch(toSelectedBranchState(branch));
729
+ }
730
+ setCreationSource(branch ? toSelectedBranchState(branch) : null);
731
+ setCreateBranchName("");
732
+ setSuppressCreateKey("n");
733
+ // FR-044: ウィザードポップアップをレイヤー表示(アクション選択から開始)
734
+ setWizardVisible(true);
735
+ };
736
+
737
+ // FR-049: Escapeキーでウィザードをキャンセル
738
+ const handleWizardClose = () => {
739
+ setWizardVisible(false);
740
+ };
741
+
742
+ // ウィザード完了時の処理
743
+ const handleWizardComplete = (result: WizardResult) => {
744
+ setWizardVisible(false);
745
+
746
+ const branch = selectedBranch();
747
+ if (!branch) {
748
+ return;
749
+ }
750
+
751
+ // 新規ブランチ作成の場合、ブランチ名を設定
752
+ const isCreatingNew = result.isNewBranch ?? false;
753
+ const finalBranchName = result.branchName
754
+ ? `${result.branchType ?? ""}${result.branchName}`
755
+ : branch.name;
756
+
757
+ // 新規作成時は選択中のブランチがベースとなる
758
+ const baseBranchRef = isCreatingNew ? branch.name : null;
759
+ const normalizedModel = normalizeModelId(result.tool, result.model);
760
+
761
+ exitApp({
762
+ branch: finalBranchName,
763
+ displayName: branch.displayName,
764
+ branchType: branch.branchType,
765
+ ...(branch.remoteBranch ? { remoteBranch: branch.remoteBranch } : {}),
766
+ ...(isCreatingNew
767
+ ? {
768
+ isNewBranch: true,
769
+ ...(baseBranchRef ? { baseBranch: baseBranchRef } : {}),
770
+ }
771
+ : {}),
772
+ tool: result.tool,
773
+ mode: result.mode,
774
+ skipPermissions: result.skipPermissions,
775
+ ...(normalizedModel !== undefined ? { model: normalizedModel } : {}),
776
+ ...(result.reasoningLevel !== undefined
777
+ ? { inferenceLevel: result.reasoningLevel }
778
+ : {}),
779
+ ...(result.toolVersion !== undefined
780
+ ? { toolVersion: result.toolVersion }
781
+ : {}),
782
+ });
783
+ };
784
+
785
+ // FR-010: クイックスタートからのResume(前回設定で続きから)
786
+ const handleWizardResume = (entry: ToolSessionEntry) => {
787
+ setWizardVisible(false);
788
+
789
+ const branch = selectedBranch();
790
+ if (!branch) {
791
+ return;
792
+ }
793
+
794
+ const normalizedModel = normalizeModelId(
795
+ entry.toolId as CodingAgentId,
796
+ entry.model ?? undefined,
797
+ );
798
+
799
+ exitApp({
800
+ branch: branch.name,
801
+ displayName: branch.displayName,
802
+ branchType: branch.branchType,
803
+ ...(branch.remoteBranch ? { remoteBranch: branch.remoteBranch } : {}),
804
+ tool: entry.toolId as CodingAgentId,
805
+ mode: "continue",
806
+ skipPermissions: entry.skipPermissions ?? false,
807
+ ...(normalizedModel !== undefined ? { model: normalizedModel } : {}),
808
+ ...(entry.reasoningLevel
809
+ ? { inferenceLevel: entry.reasoningLevel as InferenceLevel }
810
+ : {}),
811
+ ...(entry.sessionId ? { sessionId: entry.sessionId } : {}),
812
+ ...(entry.toolVersion !== undefined
813
+ ? { toolVersion: entry.toolVersion }
814
+ : {}),
815
+ });
816
+ };
817
+
818
+ // FR-010: クイックスタートからのStartNew(前回設定で新規)
819
+ const handleWizardStartNew = (entry: ToolSessionEntry) => {
820
+ setWizardVisible(false);
821
+
822
+ const branch = selectedBranch();
823
+ if (!branch) {
824
+ return;
825
+ }
826
+
827
+ const normalizedModel = normalizeModelId(
828
+ entry.toolId as CodingAgentId,
829
+ entry.model ?? undefined,
830
+ );
831
+
832
+ exitApp({
833
+ branch: branch.name,
834
+ displayName: branch.displayName,
835
+ branchType: branch.branchType,
836
+ ...(branch.remoteBranch ? { remoteBranch: branch.remoteBranch } : {}),
837
+ tool: entry.toolId as CodingAgentId,
838
+ mode: "normal",
839
+ skipPermissions: entry.skipPermissions ?? false,
840
+ ...(normalizedModel !== undefined ? { model: normalizedModel } : {}),
841
+ ...(entry.reasoningLevel
842
+ ? { inferenceLevel: entry.reasoningLevel as InferenceLevel }
843
+ : {}),
844
+ ...(entry.toolVersion !== undefined
845
+ ? { toolVersion: entry.toolVersion }
846
+ : {}),
847
+ });
848
+ };
849
+
850
+ const buildSkipNotice = (
851
+ skip: {
852
+ unsafe: number;
853
+ protected: number;
854
+ remote: number;
855
+ current: number;
856
+ },
857
+ unsafeLabel: string,
858
+ ): string | null => {
859
+ const parts: string[] = [];
860
+ if (skip.unsafe > 0) {
861
+ parts.push(`${skip.unsafe} ${unsafeLabel}`);
862
+ }
863
+ if (skip.protected > 0) {
864
+ parts.push(`${skip.protected} protected`);
865
+ }
866
+ if (skip.remote > 0) {
867
+ parts.push(`${skip.remote} remote-only`);
868
+ }
869
+ if (skip.current > 0) {
870
+ parts.push(`${skip.current} current`);
871
+ }
872
+ if (parts.length === 0) {
873
+ return null;
874
+ }
875
+ return `Skipped branches: ${parts.join(", ")}.`;
876
+ };
877
+
878
+ const handleCleanupCommand = async () => {
879
+ if (branchInputLocked()) {
880
+ return;
881
+ }
882
+
883
+ const selection = selectedBranches();
884
+ const hasSelection = selection.length > 0;
885
+ const skipCounts = {
886
+ unsafe: 0,
887
+ protected: 0,
888
+ remote: 0,
889
+ current: 0,
890
+ };
891
+
892
+ setBranchInputLocked(true);
893
+ setCleanupIndicators({});
894
+ showBranchFooterMessage(
895
+ hasSelection
896
+ ? "Preparing cleanup..."
897
+ : "Scanning for cleanup candidates...",
898
+ "yellow",
899
+ { spinning: true, timeoutMs: 0 },
900
+ );
901
+
902
+ try {
903
+ const tasks: CleanupTask[] = [];
904
+
905
+ if (hasSelection) {
906
+ const branchMap = new Map(
907
+ branchItems().map((branch) => [branch.name, branch]),
908
+ );
909
+ for (const branchName of selection) {
910
+ const branch = branchMap.get(branchName);
911
+ if (!branch) {
912
+ continue;
913
+ }
914
+ if (branch.type === "remote") {
915
+ skipCounts.remote += 1;
916
+ continue;
917
+ }
918
+ if (isProtectedBranchName(branch.name)) {
919
+ skipCounts.protected += 1;
920
+ continue;
921
+ }
922
+ if (branch.isCurrent) {
923
+ skipCounts.current += 1;
924
+ continue;
925
+ }
926
+ const hasUncommitted =
927
+ branch.worktree?.hasUncommittedChanges === true;
928
+ const hasUnpushed = branch.hasUnpushedCommits === true;
929
+ if (hasUncommitted || hasUnpushed) {
930
+ skipCounts.unsafe += 1;
931
+ continue;
932
+ }
933
+ if (branch.safeToCleanup !== true) {
934
+ skipCounts.unsafe += 1;
935
+ continue;
936
+ }
937
+ const worktreePath = branch.worktree?.path ?? null;
938
+ const cleanupType: CleanupTask["cleanupType"] = worktreePath
939
+ ? "worktree-and-branch"
940
+ : "branch-only";
941
+ const baseTask = {
942
+ branch: branch.name,
943
+ worktreePath,
944
+ cleanupType,
945
+ };
946
+ const isAccessible = branch.worktree?.isAccessible;
947
+ tasks.push(
948
+ isAccessible === undefined
949
+ ? baseTask
950
+ : { ...baseTask, isAccessible },
951
+ );
952
+ }
953
+ } else {
954
+ // FR-028: 選択が0件の場合は警告を表示して処理をスキップ
955
+ setBranchInputLocked(false);
956
+ showBranchFooterMessage("No branches selected.", "yellow");
957
+ return;
958
+ }
959
+
960
+ const skipNotice = buildSkipNotice(
961
+ skipCounts,
962
+ hasSelection
963
+ ? "unsafe (uncommitted/unpushed, unmerged, or missing upstream)"
964
+ : "with uncommitted or unpushed changes",
965
+ );
966
+
967
+ if (tasks.length === 0) {
968
+ const baseMessage = hasSelection
969
+ ? "No eligible branches selected for cleanup."
970
+ : "No cleanup candidates found.";
971
+ showBranchFooterMessage(
972
+ skipNotice ? `${baseMessage} ${skipNotice}` : baseMessage,
973
+ "yellow",
974
+ );
975
+ return;
976
+ }
977
+
978
+ setCleanupIndicators(() => {
979
+ const next: Record<string, CleanupIndicator> = {};
980
+ tasks.forEach((task) => {
981
+ next[task.branch] = {
982
+ icon: "-",
983
+ isSpinning: true,
984
+ color: "yellow",
985
+ };
986
+ });
987
+ return next;
988
+ });
989
+
990
+ showBranchFooterMessage(
991
+ `Cleaning up ${tasks.length} branch(es)...`,
992
+ "yellow",
993
+ { spinning: true, timeoutMs: 0 },
994
+ );
995
+
996
+ let successCount = 0;
997
+ let failedCount = 0;
998
+
999
+ for (const task of tasks) {
1000
+ try {
1001
+ if (task.cleanupType === "worktree-and-branch" && task.worktreePath) {
1002
+ await removeWorktree(
1003
+ task.worktreePath,
1004
+ task.isAccessible === false,
1005
+ );
1006
+ }
1007
+ await deleteBranch(task.branch, true);
1008
+ setCleanupIndicator(task.branch, { icon: "v", color: "green" });
1009
+ successCount += 1;
1010
+ } catch {
1011
+ setCleanupIndicator(task.branch, { icon: "x", color: "red" });
1012
+ failedCount += 1;
1013
+ }
1014
+ }
1015
+
1016
+ setSelectedBranches([]);
1017
+ await refreshBranches();
1018
+
1019
+ const skippedTotal =
1020
+ skipCounts.unsafe +
1021
+ skipCounts.protected +
1022
+ skipCounts.remote +
1023
+ skipCounts.current;
1024
+ const summaryParts = [`${successCount} cleaned`];
1025
+ if (failedCount > 0) {
1026
+ summaryParts.push(`${failedCount} failed`);
1027
+ }
1028
+ if (skippedTotal > 0) {
1029
+ summaryParts.push(`${skippedTotal} skipped`);
1030
+ }
1031
+ const summary = `Cleanup finished: ${summaryParts.join(", ")}.`;
1032
+ const message = skipNotice ? `${summary} ${skipNotice}` : summary;
1033
+ showBranchFooterMessage(message, failedCount > 0 ? "red" : "green");
1034
+ } catch (err) {
1035
+ const errorMessage = err instanceof Error ? err.message : String(err);
1036
+ showBranchFooterMessage(`Cleanup failed: ${errorMessage}`, "red");
1037
+ } finally {
1038
+ setBranchInputLocked(false);
1039
+ }
1040
+ };
1041
+
1042
+ const handleRepairWorktrees = async () => {
1043
+ if (branchInputLocked()) {
1044
+ return;
1045
+ }
1046
+
1047
+ // FR-002/FR-007: 選択済みブランチのみを対象とする
1048
+ const selection = selectedBranches();
1049
+ if (selection.length === 0) {
1050
+ showBranchFooterMessage("No branches selected.", "yellow");
1051
+ return;
1052
+ }
1053
+
1054
+ // 選択されたブランチのうち、inaccessibleなものを修復対象とする
1055
+ const selectionSet = new Set(selection);
1056
+ const targets = branchItems()
1057
+ .filter(
1058
+ (branch) =>
1059
+ selectionSet.has(branch.name) &&
1060
+ branch.worktreeStatus === "inaccessible",
1061
+ )
1062
+ .map((branch) => branch.name);
1063
+
1064
+ if (targets.length === 0) {
1065
+ showBranchFooterMessage("No worktrees to repair.", "yellow");
1066
+ return;
1067
+ }
1068
+
1069
+ setBranchInputLocked(true);
1070
+ showBranchFooterMessage("Repairing worktrees...", "yellow", {
1071
+ spinning: true,
1072
+ timeoutMs: 0,
1073
+ });
1074
+ try {
1075
+ const result = await repairWorktrees(targets);
1076
+ await refreshBranches();
1077
+
1078
+ // エラー詳細をログに出力
1079
+ if (result.failedCount > 0) {
1080
+ logger.error(
1081
+ { failures: result.failures, targets },
1082
+ "Worktree repair failed for some branches",
1083
+ );
1084
+ }
1085
+
1086
+ const message =
1087
+ result.failedCount > 0
1088
+ ? `Repair finished: ${result.repairedCount} repaired, ${result.failedCount} failed.`
1089
+ : result.repairedCount > 0
1090
+ ? `Repaired ${result.repairedCount} worktree(s).`
1091
+ : "No worktrees repaired.";
1092
+ showBranchFooterMessage(
1093
+ message,
1094
+ result.failedCount > 0 ? "red" : "green",
1095
+ );
1096
+ } catch (err) {
1097
+ const errorMessage = err instanceof Error ? err.message : String(err);
1098
+ logger.error({ error: err, targets }, "Worktree repair threw an error");
1099
+ showBranchFooterMessage(`Repair failed: ${errorMessage}`, "red");
1100
+ } finally {
1101
+ setBranchInputLocked(false);
1102
+ }
1103
+ };
1104
+
1105
+ const openProfileCreate = () => {
1106
+ setProfileInputMode("create-profile");
1107
+ setProfileInputValue("");
1108
+ setProfileEnvKey(null);
1109
+ setProfileInputSuppressKey("n");
1110
+ navigateTo("profile-input");
1111
+ };
1112
+
1113
+ const openProfileDelete = (profile: { name: string }) => {
1114
+ setSelectedProfileName(profile.name);
1115
+ setProfileConfirmMode("delete-profile");
1116
+ navigateTo("profile-confirm");
1117
+ };
1118
+
1119
+ const openProfileEnv = (profile: { name: string }) => {
1120
+ setSelectedProfileName(profile.name);
1121
+ navigateTo("profile-env");
1122
+ };
1123
+
1124
+ const openProfileEnvAdd = () => {
1125
+ setProfileInputMode("add-env");
1126
+ setProfileInputValue("");
1127
+ setProfileEnvKey(null);
1128
+ setProfileInputSuppressKey("a");
1129
+ navigateTo("profile-input");
1130
+ };
1131
+
1132
+ const openProfileEnvEdit = (variable: { key: string; value: string }) => {
1133
+ setProfileInputMode("edit-env");
1134
+ setProfileEnvKey(variable.key);
1135
+ setProfileInputValue(variable.value);
1136
+ setProfileInputSuppressKey("e");
1137
+ navigateTo("profile-input");
1138
+ };
1139
+
1140
+ const openProfileEnvDelete = (variable: { key: string }) => {
1141
+ setProfileConfirmMode("delete-env");
1142
+ setProfileEnvKey(variable.key);
1143
+ navigateTo("profile-confirm");
1144
+ };
1145
+
1146
+ const handleProfileInputChange = (value: string) => {
1147
+ const suppressKey = profileInputSuppressKey();
1148
+ if (suppressKey && profileInputValue() === "" && value === suppressKey) {
1149
+ setProfileInputSuppressKey(null);
1150
+ setProfileInputValue("");
1151
+ return;
1152
+ }
1153
+ setProfileInputSuppressKey(null);
1154
+ setProfileInputValue(value);
1155
+ };
1156
+
1157
+ const submitProfileInput = async (value: string) => {
1158
+ const mode = profileInputMode();
1159
+ const trimmed = value.trim();
1160
+
1161
+ if (mode === "create-profile") {
1162
+ if (!trimmed) {
1163
+ openProfileError(
1164
+ new Error("Profile name is required."),
1165
+ "Invalid name.",
1166
+ );
1167
+ return;
1168
+ }
1169
+ if (!isValidProfileName(trimmed)) {
1170
+ openProfileError(
1171
+ new Error(`Invalid profile name: "${trimmed}".`),
1172
+ "Profile names must use lowercase letters, numbers, and hyphens.",
1173
+ );
1174
+ return;
1175
+ }
1176
+ try {
1177
+ await createProfile(trimmed, { displayName: trimmed, env: {} });
1178
+ await refreshProfiles();
1179
+ setSelectedProfileName(trimmed);
1180
+ setProfileInputValue("");
1181
+ setProfileInputSuppressKey(null);
1182
+ goBack();
1183
+ } catch (err) {
1184
+ openProfileError(err, "Unable to create profile.");
1185
+ }
1186
+ return;
1187
+ }
1188
+
1189
+ const entry = selectedProfileConfig();
1190
+ if (!entry) {
1191
+ openProfileError(
1192
+ new Error("Profile not selected."),
1193
+ "Profile not found.",
1194
+ );
1195
+ return;
1196
+ }
1197
+
1198
+ if (mode === "add-env") {
1199
+ const separatorIndex = trimmed.indexOf("=");
1200
+ if (separatorIndex <= 0) {
1201
+ openProfileError(
1202
+ new Error("Environment variable must be in KEY=VALUE format."),
1203
+ "Invalid environment variable.",
1204
+ );
1205
+ return;
1206
+ }
1207
+ const key = trimmed.slice(0, separatorIndex).trim();
1208
+ const valuePart = trimmed.slice(separatorIndex + 1);
1209
+ if (!key) {
1210
+ openProfileError(
1211
+ new Error("Environment variable key is required."),
1212
+ "Invalid environment variable.",
1213
+ );
1214
+ return;
1215
+ }
1216
+ const nextEnv = { ...(entry.profile.env ?? {}) };
1217
+ nextEnv[key] = valuePart;
1218
+ try {
1219
+ await updateProfile(entry.name, { env: nextEnv });
1220
+ await refreshProfiles();
1221
+ setProfileInputValue("");
1222
+ setProfileInputSuppressKey(null);
1223
+ goBack();
1224
+ } catch (err) {
1225
+ openProfileError(err, "Unable to update profile.");
1226
+ }
1227
+ return;
1228
+ }
1229
+
1230
+ if (mode === "edit-env") {
1231
+ const envKey = profileEnvKey();
1232
+ if (!envKey) {
1233
+ openProfileError(
1234
+ new Error("Environment variable not selected."),
1235
+ "Select a variable to edit.",
1236
+ );
1237
+ return;
1238
+ }
1239
+ const nextEnv = { ...(entry.profile.env ?? {}) };
1240
+ nextEnv[envKey] = value;
1241
+ try {
1242
+ await updateProfile(entry.name, { env: nextEnv });
1243
+ await refreshProfiles();
1244
+ setProfileInputValue("");
1245
+ setProfileInputSuppressKey(null);
1246
+ goBack();
1247
+ } catch (err) {
1248
+ openProfileError(err, "Unable to update profile.");
1249
+ }
1250
+ }
1251
+ };
1252
+
1253
+ const confirmProfileAction = async (confirmed: boolean) => {
1254
+ if (!confirmed) {
1255
+ goBack();
1256
+ return;
1257
+ }
1258
+
1259
+ const mode = profileConfirmMode();
1260
+ const entry = selectedProfileConfig();
1261
+
1262
+ if (mode === "delete-profile") {
1263
+ const name = selectedProfileName();
1264
+ if (!name) {
1265
+ openProfileError(
1266
+ new Error("Profile not selected."),
1267
+ "Profile not found.",
1268
+ );
1269
+ return;
1270
+ }
1271
+ try {
1272
+ await deleteProfile(name);
1273
+ await refreshProfiles();
1274
+ setProfileEnvKey(null);
1275
+ goBack();
1276
+ } catch (err) {
1277
+ openProfileError(err, "Unable to delete profile.");
1278
+ }
1279
+ return;
1280
+ }
1281
+
1282
+ if (mode === "delete-env") {
1283
+ if (!entry) {
1284
+ openProfileError(
1285
+ new Error("Profile not selected."),
1286
+ "Profile not found.",
1287
+ );
1288
+ return;
1289
+ }
1290
+ const envKey = profileEnvKey();
1291
+ if (!envKey) {
1292
+ openProfileError(
1293
+ new Error("Environment variable not selected."),
1294
+ "Select a variable to delete.",
1295
+ );
1296
+ return;
1297
+ }
1298
+ const nextEnv = { ...(entry.profile.env ?? {}) };
1299
+ delete nextEnv[envKey];
1300
+ try {
1301
+ await updateProfile(entry.name, { env: nextEnv });
1302
+ await refreshProfiles();
1303
+ setProfileEnvKey(null);
1304
+ goBack();
1305
+ } catch (err) {
1306
+ openProfileError(err, "Unable to update profile.");
1307
+ }
1308
+ }
1309
+ };
1310
+
1311
+ const toggleSelectedBranch = (branchName: string) => {
1312
+ setSelectedBranches((prev) => {
1313
+ const next = new Set(prev);
1314
+ if (next.has(branchName)) {
1315
+ next.delete(branchName);
1316
+ } else {
1317
+ next.add(branchName);
1318
+ }
1319
+ return Array.from(next);
1320
+ });
1321
+ };
1322
+
1323
+ const handleToolSelect = (item: SelectorItem) => {
1324
+ setSelectedTool(item.value as CodingAgentId);
1325
+ navigateTo("mode-select");
1326
+ };
1327
+
1328
+ const handleModeSelect = (item: SelectorItem) => {
1329
+ setSelectedMode(item.value as ExecutionMode);
1330
+ navigateTo("skip-permissions");
1331
+ };
1332
+
1333
+ const finalizeSelection = (skipPermissions: boolean) => {
1334
+ const branch = selectedBranch();
1335
+ const tool = selectedTool();
1336
+ if (!branch || !tool) {
1337
+ return;
1338
+ }
1339
+
1340
+ const defaultModel = getDefaultModelOption(tool);
1341
+ const resolvedModel = defaultModel?.id ?? null;
1342
+ const normalizedModel = normalizeModelId(tool, resolvedModel);
1343
+ const resolvedInference = getDefaultInferenceForModel(defaultModel);
1344
+ const baseRef = newBranchBaseRef();
1345
+
1346
+ exitApp({
1347
+ branch: branch.name,
1348
+ displayName: branch.displayName,
1349
+ branchType: branch.branchType,
1350
+ ...(branch.remoteBranch ? { remoteBranch: branch.remoteBranch } : {}),
1351
+ ...(isNewBranch()
1352
+ ? {
1353
+ isNewBranch: true,
1354
+ ...(baseRef ? { baseBranch: baseRef } : {}),
1355
+ }
1356
+ : {}),
1357
+ tool,
1358
+ mode: selectedMode(),
1359
+ skipPermissions,
1360
+ ...(normalizedModel !== undefined ? { model: normalizedModel } : {}),
1361
+ ...(resolvedInference !== undefined
1362
+ ? { inferenceLevel: resolvedInference }
1363
+ : {}),
1364
+ });
1365
+ };
1366
+
1367
+ const renderCurrentScreen = () => {
1368
+ const screen = currentScreen();
1369
+
1370
+ if (screen === "branch-list") {
1371
+ const cleanupUI = {
1372
+ indicators: cleanupIndicators(),
1373
+ footerMessage: branchFooterMessage(),
1374
+ inputLocked: branchInputLocked(),
1375
+ safetyLoading: cleanupSafetyLoading(),
1376
+ };
1377
+ return (
1378
+ <BranchListScreen
1379
+ branches={branchItems()}
1380
+ stats={stats()}
1381
+ onSelect={handleBranchSelect}
1382
+ onQuit={() => exitApp(undefined)}
1383
+ onCleanupCommand={handleCleanupCommand}
1384
+ onRefresh={refreshBranches}
1385
+ onRepairWorktrees={handleRepairWorktrees}
1386
+ loading={loading()}
1387
+ error={error()}
1388
+ loadingIndicatorDelay={props.loadingIndicatorDelay ?? 0}
1389
+ lastUpdated={stats().lastUpdated}
1390
+ version={version()}
1391
+ workingDirectory={workingDirectory()}
1392
+ activeProfile={activeProfile()}
1393
+ onOpenLogs={() => navigateTo("log-list")}
1394
+ onOpenProfiles={() => navigateTo("profile")}
1395
+ selectedBranches={selectedBranches()}
1396
+ onToggleSelect={toggleSelectedBranch}
1397
+ onCreateBranch={handleQuickCreate}
1398
+ cleanupUI={cleanupUI}
1399
+ helpVisible={helpVisible()}
1400
+ wizardVisible={wizardVisible()}
1401
+ />
1402
+ );
1403
+ }
1404
+
1405
+ if (screen === "tool-select") {
1406
+ if (toolError()) {
1407
+ return (
1408
+ <ErrorScreen
1409
+ error={toolError() as Error}
1410
+ onBack={goBack}
1411
+ hint="Unable to load available tools."
1412
+ />
1413
+ );
1414
+ }
1415
+ return (
1416
+ <SelectorScreen
1417
+ title="Select tool"
1418
+ items={toolItems()}
1419
+ onSelect={handleToolSelect}
1420
+ onBack={goBack}
1421
+ helpVisible={helpVisible()}
1422
+ />
1423
+ );
1424
+ }
1425
+
1426
+ if (screen === "mode-select") {
1427
+ return (
1428
+ <SelectorScreen
1429
+ title="Execution mode"
1430
+ items={[
1431
+ { label: "Normal", value: "normal" },
1432
+ { label: "Continue", value: "continue" },
1433
+ { label: "Resume", value: "resume" },
1434
+ ]}
1435
+ onSelect={handleModeSelect}
1436
+ onBack={goBack}
1437
+ helpVisible={helpVisible()}
1438
+ />
1439
+ );
1440
+ }
1441
+
1442
+ if (screen === "skip-permissions") {
1443
+ return (
1444
+ <SelectorScreen
1445
+ title="Skip permission prompts?"
1446
+ items={[
1447
+ { label: "Yes", value: "true" },
1448
+ { label: "No", value: "false" },
1449
+ ]}
1450
+ onSelect={(item) => finalizeSelection(item.value === "true")}
1451
+ onBack={goBack}
1452
+ helpVisible={helpVisible()}
1453
+ />
1454
+ );
1455
+ }
1456
+
1457
+ if (screen === "log-list") {
1458
+ return (
1459
+ <LogScreen
1460
+ entries={logEntries()}
1461
+ loading={logLoading()}
1462
+ error={logError()}
1463
+ onBack={goBack}
1464
+ onSelect={(entry) => {
1465
+ setLogSelectedEntry(entry);
1466
+ navigateTo("log-detail");
1467
+ }}
1468
+ onCopy={async (entry) => {
1469
+ try {
1470
+ await copyToClipboard(entry.json);
1471
+ showLogNotification("Copied to clipboard.", "success");
1472
+ } catch {
1473
+ showLogNotification("Failed to copy to clipboard.", "error");
1474
+ }
1475
+ }}
1476
+ notification={logNotification()}
1477
+ version={version()}
1478
+ selectedDate={logSelectedDate()}
1479
+ helpVisible={helpVisible()}
1480
+ />
1481
+ );
1482
+ }
1483
+
1484
+ if (screen === "log-detail") {
1485
+ return (
1486
+ <LogDetailScreen
1487
+ entry={logSelectedEntry()}
1488
+ onBack={goBack}
1489
+ onCopy={async (entry) => {
1490
+ try {
1491
+ await copyToClipboard(entry.json);
1492
+ showLogNotification("Copied to clipboard.", "success");
1493
+ } catch {
1494
+ showLogNotification("Failed to copy to clipboard.", "error");
1495
+ }
1496
+ }}
1497
+ notification={logNotification()}
1498
+ version={version()}
1499
+ helpVisible={helpVisible()}
1500
+ />
1501
+ );
1502
+ }
1503
+
1504
+ if (screen === "profile") {
1505
+ if (profileError()) {
1506
+ return (
1507
+ <ErrorScreen
1508
+ error={profileError() as Error}
1509
+ onBack={goBack}
1510
+ hint="Unable to load profiles."
1511
+ />
1512
+ );
1513
+ }
1514
+ return (
1515
+ <ProfileScreen
1516
+ profiles={profileItems()}
1517
+ version={version()}
1518
+ helpVisible={helpVisible()}
1519
+ onCreate={openProfileCreate}
1520
+ onDelete={openProfileDelete}
1521
+ onEdit={openProfileEnv}
1522
+ onSelect={(profile) => {
1523
+ void setActiveProfile(profile.name)
1524
+ .then(() => {
1525
+ void refreshProfiles();
1526
+ })
1527
+ .catch((err) => {
1528
+ openProfileError(err, "Unable to set active profile.");
1529
+ });
1530
+ }}
1531
+ onBack={goBack}
1532
+ />
1533
+ );
1534
+ }
1535
+
1536
+ if (screen === "profile-env") {
1537
+ const entry = selectedProfileConfig();
1538
+ if (!entry) {
1539
+ return (
1540
+ <ErrorScreen
1541
+ error="Profile not found."
1542
+ onBack={goBack}
1543
+ hint="Select a profile before editing."
1544
+ />
1545
+ );
1546
+ }
1547
+ return (
1548
+ <ProfileEnvScreen
1549
+ profileName={entry.name}
1550
+ variables={profileEnvVariables()}
1551
+ onAdd={openProfileEnvAdd}
1552
+ onEdit={openProfileEnvEdit}
1553
+ onDelete={openProfileEnvDelete}
1554
+ onViewOsEnv={() => navigateTo("profile-os-env")}
1555
+ onBack={goBack}
1556
+ version={version()}
1557
+ helpVisible={helpVisible()}
1558
+ />
1559
+ );
1560
+ }
1561
+
1562
+ if (screen === "profile-input") {
1563
+ const mode = profileInputMode();
1564
+ const envKey = profileEnvKey();
1565
+ const message =
1566
+ mode === "create-profile"
1567
+ ? "New profile name"
1568
+ : mode === "add-env"
1569
+ ? "Add environment variable"
1570
+ : `Edit value for ${envKey ?? "(unknown)"}`;
1571
+ const label =
1572
+ mode === "create-profile"
1573
+ ? "Profile name"
1574
+ : mode === "add-env"
1575
+ ? "KEY=VALUE"
1576
+ : "Value";
1577
+ const placeholder =
1578
+ mode === "create-profile"
1579
+ ? "development"
1580
+ : mode === "add-env"
1581
+ ? "MY_VAR=value"
1582
+ : undefined;
1583
+
1584
+ return (
1585
+ <InputScreen
1586
+ message={message}
1587
+ value={profileInputValue()}
1588
+ onChange={handleProfileInputChange}
1589
+ onSubmit={(value) => void submitProfileInput(value)}
1590
+ onCancel={() => {
1591
+ setProfileInputSuppressKey(null);
1592
+ goBack();
1593
+ }}
1594
+ label={label}
1595
+ {...(placeholder !== undefined ? { placeholder } : {})}
1596
+ width={32}
1597
+ helpVisible={helpVisible()}
1598
+ />
1599
+ );
1600
+ }
1601
+
1602
+ if (screen === "profile-confirm") {
1603
+ const mode = profileConfirmMode();
1604
+ const profileName = selectedProfileName();
1605
+ const envKey = profileEnvKey();
1606
+ const message =
1607
+ mode === "delete-profile"
1608
+ ? `Delete profile ${profileName ?? "(unknown)"}?`
1609
+ : `Delete ${envKey ?? "(unknown)"}?`;
1610
+
1611
+ return (
1612
+ <ConfirmScreen
1613
+ message={message}
1614
+ onConfirm={(confirmed) => void confirmProfileAction(confirmed)}
1615
+ defaultNo
1616
+ helpVisible={helpVisible()}
1617
+ />
1618
+ );
1619
+ }
1620
+
1621
+ if (screen === "profile-os-env") {
1622
+ const highlightKeys = profileEnvVariables().map(
1623
+ (variable) => variable.key,
1624
+ );
1625
+ return (
1626
+ <EnvironmentScreen
1627
+ variables={osEnvVariables()}
1628
+ highlightKeys={highlightKeys}
1629
+ onBack={goBack}
1630
+ version={version()}
1631
+ helpVisible={helpVisible()}
1632
+ />
1633
+ );
1634
+ }
1635
+
1636
+ if (screen === "profile-error") {
1637
+ return (
1638
+ <ErrorScreen
1639
+ error={profileActionError() ?? "Profile error"}
1640
+ onBack={() => {
1641
+ setProfileActionError(null);
1642
+ setProfileActionHint(null);
1643
+ goBack();
1644
+ }}
1645
+ {...(profileActionHint()
1646
+ ? { hint: profileActionHint() as string }
1647
+ : {})}
1648
+ helpVisible={helpVisible()}
1649
+ />
1650
+ );
1651
+ }
1652
+
1653
+ if (screen === "worktree-create") {
1654
+ const baseBranchRef = resolveBaseBranchRef(creationSource(), null, () =>
1655
+ defaultBaseBranch(),
1656
+ );
1657
+ const baseBranchLabel = resolveBaseBranchLabel(
1658
+ creationSource(),
1659
+ null,
1660
+ () => defaultBaseBranch(),
1661
+ );
1662
+
1663
+ return (
1664
+ <WorktreeCreateScreen
1665
+ branchName={createBranchName()}
1666
+ baseBranch={baseBranchLabel}
1667
+ version={version()}
1668
+ helpVisible={helpVisible()}
1669
+ onChange={(value) => {
1670
+ const suppressKey = suppressCreateKey();
1671
+ if (
1672
+ suppressKey &&
1673
+ createBranchName() === "" &&
1674
+ value === suppressKey
1675
+ ) {
1676
+ setSuppressCreateKey(null);
1677
+ setCreateBranchName("");
1678
+ return;
1679
+ }
1680
+ setSuppressCreateKey(null);
1681
+ setCreateBranchName(value);
1682
+ }}
1683
+ onSubmit={(value, branchType) => {
1684
+ const trimmed = value.trim();
1685
+ if (!trimmed) {
1686
+ return;
1687
+ }
1688
+ // Add prefix based on selected branch type
1689
+ const prefixKey =
1690
+ branchType.toUpperCase() as keyof typeof BRANCH_PREFIXES;
1691
+ const prefix = BRANCH_PREFIXES[prefixKey] ?? "";
1692
+ const fullBranchName = `${prefix}${trimmed}`;
1693
+
1694
+ setSelectedBranch({
1695
+ name: fullBranchName,
1696
+ displayName: fullBranchName,
1697
+ branchType: "local",
1698
+ branchCategory: branchType,
1699
+ });
1700
+ setIsNewBranch(true);
1701
+ setNewBranchBaseRef(baseBranchRef);
1702
+ setSelectedTool(null);
1703
+ setSelectedMode("normal");
1704
+ setSuppressCreateKey(null);
1705
+ navigateTo("tool-select");
1706
+ }}
1707
+ onCancel={() => {
1708
+ setSuppressCreateKey(null);
1709
+ goBack();
1710
+ }}
1711
+ />
1712
+ );
1713
+ }
1714
+
1715
+ if (screen === "loading") {
1716
+ return (
1717
+ <LoadingIndicatorScreen
1718
+ message="Loading..."
1719
+ delay={props.loadingIndicatorDelay ?? 0}
1720
+ />
1721
+ );
1722
+ }
1723
+
1724
+ if (screen === "error") {
1725
+ return (
1726
+ <ErrorScreen
1727
+ error={error() ?? "Unknown error"}
1728
+ helpVisible={helpVisible()}
1729
+ />
1730
+ );
1731
+ }
1732
+
1733
+ return (
1734
+ <ErrorScreen
1735
+ error={`Unknown screen: ${screen}`}
1736
+ onBack={goBack}
1737
+ helpVisible={helpVisible()}
1738
+ />
1739
+ );
1740
+ };
1741
+
1742
+ return (
1743
+ <>
1744
+ {renderCurrentScreen()}
1745
+ <HelpOverlay visible={helpVisible()} context={currentScreen()} />
1746
+ {/* FR-044: ウィザードポップアップをレイヤー表示 */}
1747
+ <WizardController
1748
+ visible={wizardVisible()}
1749
+ selectedBranchName={selectedBranch()?.name ?? ""}
1750
+ history={historyForBranch()}
1751
+ onClose={handleWizardClose}
1752
+ onComplete={handleWizardComplete}
1753
+ onResume={handleWizardResume}
1754
+ onStartNew={handleWizardStartNew}
1755
+ />
1756
+ </>
1757
+ );
1758
+ }
1759
+ const withTimeout = async <T,>(
1760
+ promise: Promise<T>,
1761
+ timeoutMs: number,
1762
+ ): Promise<T> =>
1763
+ new Promise<T>((resolve, reject) => {
1764
+ const timer = setTimeout(() => {
1765
+ reject(new Error("timeout"));
1766
+ }, timeoutMs);
1767
+
1768
+ promise
1769
+ .then((value) => {
1770
+ clearTimeout(timer);
1771
+ resolve(value);
1772
+ })
1773
+ .catch((err) => {
1774
+ clearTimeout(timer);
1775
+ reject(err);
1776
+ });
1777
+ });
1778
+
1779
+ const buildBranchList = (
1780
+ branches: BranchInfo[],
1781
+ worktrees: WorktreeEntry[],
1782
+ lastToolUsageMap?: Map<string, ToolSessionEntry>,
1783
+ ) => {
1784
+ const localBranchNames = new Set(
1785
+ branches.filter((branch) => branch.type === "local").map((b) => b.name),
1786
+ );
1787
+
1788
+ const filtered = branches.filter((branch) => {
1789
+ if (branch.type === "remote") {
1790
+ const remoteName = branch.name.replace(/^origin\//, "");
1791
+ return !localBranchNames.has(remoteName);
1792
+ }
1793
+ return true;
1794
+ });
1795
+
1796
+ const worktreeMap = new Map<string, UIWorktreeInfo>();
1797
+ for (const worktree of worktrees) {
1798
+ worktreeMap.set(worktree.branch, {
1799
+ path: worktree.path,
1800
+ locked: worktree.locked ?? false,
1801
+ prunable: worktree.prunable ?? false,
1802
+ isAccessible: worktree.isAccessible ?? true,
1803
+ ...(worktree.hasUncommittedChanges !== undefined
1804
+ ? { hasUncommittedChanges: worktree.hasUncommittedChanges }
1805
+ : {}),
1806
+ });
1807
+ }
1808
+
1809
+ const enriched = filtered.map((branch) => {
1810
+ const lastToolUsage = lastToolUsageMap?.get(branch.name);
1811
+ const baseBranch = lastToolUsage ? { ...branch, lastToolUsage } : branch;
1812
+ if (branch.type === "local") {
1813
+ const worktree = worktreeMap.get(branch.name);
1814
+ if (worktree) {
1815
+ return { ...baseBranch, worktree };
1816
+ }
1817
+ }
1818
+ return baseBranch;
1819
+ });
1820
+
1821
+ const items = formatBranchItems(enriched, worktreeMap);
1822
+ return { items, worktreeMap };
1823
+ };