@akiojin/gwt 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.ja.md +33 -0
  2. package/README.md +31 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +17 -11
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/components/App.d.ts +0 -6
  7. package/dist/cli/ui/components/App.d.ts.map +1 -1
  8. package/dist/cli/ui/components/App.js +27 -88
  9. package/dist/cli/ui/components/App.js.map +1 -1
  10. package/dist/cli/ui/components/common/Select.js +2 -2
  11. package/dist/cli/ui/components/common/Select.js.map +1 -1
  12. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  13. package/dist/cli/ui/components/screens/BranchListScreen.js +14 -2
  14. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  15. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
  16. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +3 -3
  17. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
  18. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  19. package/dist/cli/ui/utils/continueSession.js +1 -1
  20. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  21. package/dist/client/assets/index-DPWWHorC.js +72 -0
  22. package/dist/client/assets/index-DsDNCy5f.css +1 -0
  23. package/dist/client/index.html +2 -2
  24. package/dist/codex.d.ts.map +1 -1
  25. package/dist/codex.js +21 -11
  26. package/dist/codex.js.map +1 -1
  27. package/dist/config/builtin-tools.d.ts.map +1 -1
  28. package/dist/config/builtin-tools.js +3 -2
  29. package/dist/config/builtin-tools.js.map +1 -1
  30. package/dist/config/shared-env.d.ts +41 -0
  31. package/dist/config/shared-env.d.ts.map +1 -0
  32. package/dist/config/shared-env.js +114 -0
  33. package/dist/config/shared-env.js.map +1 -0
  34. package/dist/gemini.d.ts.map +1 -1
  35. package/dist/gemini.js +20 -17
  36. package/dist/gemini.js.map +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +49 -7
  39. package/dist/index.js.map +1 -1
  40. package/dist/logging/logger.d.ts.map +1 -1
  41. package/dist/logging/logger.js +4 -1
  42. package/dist/logging/logger.js.map +1 -1
  43. package/dist/qwen.d.ts.map +1 -1
  44. package/dist/qwen.js +15 -11
  45. package/dist/qwen.js.map +1 -1
  46. package/dist/services/aiToolResolver.d.ts +41 -0
  47. package/dist/services/aiToolResolver.d.ts.map +1 -0
  48. package/dist/services/aiToolResolver.js +194 -0
  49. package/dist/services/aiToolResolver.js.map +1 -0
  50. package/dist/services/customToolResolver.d.ts +10 -0
  51. package/dist/services/customToolResolver.d.ts.map +1 -0
  52. package/dist/services/customToolResolver.js +71 -0
  53. package/dist/services/customToolResolver.js.map +1 -0
  54. package/dist/shared/aiToolConstants.d.ts +9 -0
  55. package/dist/shared/aiToolConstants.d.ts.map +1 -0
  56. package/dist/shared/aiToolConstants.js +29 -0
  57. package/dist/shared/aiToolConstants.js.map +1 -0
  58. package/dist/types/tools.d.ts +12 -0
  59. package/dist/types/tools.d.ts.map +1 -1
  60. package/dist/utils/prompt.d.ts.map +1 -1
  61. package/dist/utils/prompt.js.map +1 -1
  62. package/dist/utils/session.d.ts.map +1 -1
  63. package/dist/utils/session.js +15 -6
  64. package/dist/utils/session.js.map +1 -1
  65. package/dist/utils/terminal.d.ts +12 -3
  66. package/dist/utils/terminal.d.ts.map +1 -1
  67. package/dist/utils/terminal.js +5 -34
  68. package/dist/utils/terminal.js.map +1 -1
  69. package/dist/utils/webui.d.ts +8 -0
  70. package/dist/utils/webui.d.ts.map +1 -0
  71. package/dist/utils/webui.js +35 -0
  72. package/dist/utils/webui.js.map +1 -0
  73. package/dist/web/client/src/components/AIToolLaunchModal.d.ts +9 -0
  74. package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +1 -0
  75. package/dist/web/client/src/components/AIToolLaunchModal.js +363 -0
  76. package/dist/web/client/src/components/AIToolLaunchModal.js.map +1 -0
  77. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  78. package/dist/web/client/src/components/BranchGraph.js +46 -49
  79. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  80. package/dist/web/client/src/components/CustomToolForm.d.ts +23 -0
  81. package/dist/web/client/src/components/CustomToolForm.d.ts.map +1 -0
  82. package/dist/web/client/src/components/CustomToolForm.js +209 -0
  83. package/dist/web/client/src/components/CustomToolForm.js.map +1 -0
  84. package/dist/web/client/src/components/CustomToolList.d.ts +10 -0
  85. package/dist/web/client/src/components/CustomToolList.d.ts.map +1 -0
  86. package/dist/web/client/src/components/CustomToolList.js +57 -0
  87. package/dist/web/client/src/components/CustomToolList.js.map +1 -0
  88. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  89. package/dist/web/client/src/components/EnvEditor.js +33 -26
  90. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  91. package/dist/web/client/src/components/EnvironmentEditor.d.ts +17 -0
  92. package/dist/web/client/src/components/EnvironmentEditor.d.ts.map +1 -0
  93. package/dist/web/client/src/components/EnvironmentEditor.js +22 -0
  94. package/dist/web/client/src/components/EnvironmentEditor.js.map +1 -0
  95. package/dist/web/client/src/components/Terminal.d.ts.map +1 -1
  96. package/dist/web/client/src/components/Terminal.js +10 -3
  97. package/dist/web/client/src/components/Terminal.js.map +1 -1
  98. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts +10 -0
  99. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -0
  100. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +104 -0
  101. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -0
  102. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +22 -0
  103. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -0
  104. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +79 -0
  105. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -0
  106. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts +11 -0
  107. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts.map +1 -0
  108. package/dist/web/client/src/components/branch-detail/TerminalPanel.js +32 -0
  109. package/dist/web/client/src/components/branch-detail/TerminalPanel.js.map +1 -0
  110. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +40 -0
  111. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -0
  112. package/dist/web/client/src/components/branch-detail/ToolLauncher.js +147 -0
  113. package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -0
  114. package/dist/web/client/src/components/branch-detail/index.d.ts +5 -0
  115. package/dist/web/client/src/components/branch-detail/index.d.ts.map +1 -0
  116. package/dist/web/client/src/components/branch-detail/index.js +5 -0
  117. package/dist/web/client/src/components/branch-detail/index.js.map +1 -0
  118. package/dist/web/client/src/components/common/BranchCard.d.ts +17 -0
  119. package/dist/web/client/src/components/common/BranchCard.d.ts.map +1 -0
  120. package/dist/web/client/src/components/common/BranchCard.js +36 -0
  121. package/dist/web/client/src/components/common/BranchCard.js.map +1 -0
  122. package/dist/web/client/src/components/common/MetricCard.d.ts +10 -0
  123. package/dist/web/client/src/components/common/MetricCard.d.ts.map +1 -0
  124. package/dist/web/client/src/components/common/MetricCard.js +10 -0
  125. package/dist/web/client/src/components/common/MetricCard.js.map +1 -0
  126. package/dist/web/client/src/components/common/PageHeader.d.ts +12 -0
  127. package/dist/web/client/src/components/common/PageHeader.d.ts.map +1 -0
  128. package/dist/web/client/src/components/common/PageHeader.js +14 -0
  129. package/dist/web/client/src/components/common/PageHeader.js.map +1 -0
  130. package/dist/web/client/src/components/common/SearchInput.d.ts +14 -0
  131. package/dist/web/client/src/components/common/SearchInput.d.ts.map +1 -0
  132. package/dist/web/client/src/components/common/SearchInput.js +15 -0
  133. package/dist/web/client/src/components/common/SearchInput.js.map +1 -0
  134. package/dist/web/client/src/components/common/StatusBadge.d.ts +10 -0
  135. package/dist/web/client/src/components/common/StatusBadge.d.ts.map +1 -0
  136. package/dist/web/client/src/components/common/StatusBadge.js +15 -0
  137. package/dist/web/client/src/components/common/StatusBadge.js.map +1 -0
  138. package/dist/web/client/src/components/common/index.d.ts +6 -0
  139. package/dist/web/client/src/components/common/index.d.ts.map +1 -0
  140. package/dist/web/client/src/components/common/index.js +6 -0
  141. package/dist/web/client/src/components/common/index.js.map +1 -0
  142. package/dist/web/client/src/components/ui/alert.d.ts +9 -0
  143. package/dist/web/client/src/components/ui/alert.d.ts.map +1 -0
  144. package/dist/web/client/src/components/ui/alert.js +25 -0
  145. package/dist/web/client/src/components/ui/alert.js.map +1 -0
  146. package/dist/web/client/src/components/ui/badge.d.ts +10 -0
  147. package/dist/web/client/src/components/ui/badge.d.ts.map +1 -0
  148. package/dist/web/client/src/components/ui/badge.js +25 -0
  149. package/dist/web/client/src/components/ui/badge.js.map +1 -0
  150. package/dist/web/client/src/components/ui/button.d.ts +12 -0
  151. package/dist/web/client/src/components/ui/button.d.ts.map +1 -0
  152. package/dist/web/client/src/components/ui/button.js +33 -0
  153. package/dist/web/client/src/components/ui/button.js.map +1 -0
  154. package/dist/web/client/src/components/ui/card.d.ts +9 -0
  155. package/dist/web/client/src/components/ui/card.d.ts.map +1 -0
  156. package/dist/web/client/src/components/ui/card.js +16 -0
  157. package/dist/web/client/src/components/ui/card.js.map +1 -0
  158. package/dist/web/client/src/components/ui/index.d.ts +8 -0
  159. package/dist/web/client/src/components/ui/index.d.ts.map +1 -0
  160. package/dist/web/client/src/components/ui/index.js +8 -0
  161. package/dist/web/client/src/components/ui/index.js.map +1 -0
  162. package/dist/web/client/src/components/ui/input.d.ts +4 -0
  163. package/dist/web/client/src/components/ui/input.d.ts.map +1 -0
  164. package/dist/web/client/src/components/ui/input.js +8 -0
  165. package/dist/web/client/src/components/ui/input.js.map +1 -0
  166. package/dist/web/client/src/components/ui/select.d.ts +14 -0
  167. package/dist/web/client/src/components/ui/select.d.ts.map +1 -0
  168. package/dist/web/client/src/components/ui/select.js +39 -0
  169. package/dist/web/client/src/components/ui/select.js.map +1 -0
  170. package/dist/web/client/src/components/ui/table.d.ts +11 -0
  171. package/dist/web/client/src/components/ui/table.d.ts.map +1 -0
  172. package/dist/web/client/src/components/ui/table.js +21 -0
  173. package/dist/web/client/src/components/ui/table.js.map +1 -0
  174. package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
  175. package/dist/web/client/src/hooks/useSessions.js +6 -1
  176. package/dist/web/client/src/hooks/useSessions.js.map +1 -1
  177. package/dist/web/client/src/lib/utils.d.ts +7 -0
  178. package/dist/web/client/src/lib/utils.d.ts.map +1 -0
  179. package/dist/web/client/src/lib/utils.js +10 -0
  180. package/dist/web/client/src/lib/utils.js.map +1 -0
  181. package/dist/web/client/src/lib/websocket.d.ts +7 -0
  182. package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
  183. package/dist/web/client/src/lib/websocket.js +44 -0
  184. package/dist/web/client/src/lib/websocket.js.map +1 -1
  185. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  186. package/dist/web/client/src/pages/BranchDetailPage.js +113 -361
  187. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  188. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  189. package/dist/web/client/src/pages/BranchListPage.js +89 -127
  190. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  191. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  192. package/dist/web/client/src/pages/ConfigManagementPage.js +46 -41
  193. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  194. package/dist/web/client/src/pages/ConfigPage.d.ts +3 -0
  195. package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -0
  196. package/dist/web/client/src/pages/ConfigPage.js +216 -0
  197. package/dist/web/client/src/pages/ConfigPage.js.map +1 -0
  198. package/dist/web/client/vite.config.d.ts.map +1 -1
  199. package/dist/web/client/vite.config.js +8 -1
  200. package/dist/web/client/vite.config.js.map +1 -1
  201. package/dist/web/server/index.d.ts +24 -2
  202. package/dist/web/server/index.d.ts.map +1 -1
  203. package/dist/web/server/index.js +46 -15
  204. package/dist/web/server/index.js.map +1 -1
  205. package/dist/web/server/pty/manager.d.ts +12 -10
  206. package/dist/web/server/pty/manager.d.ts.map +1 -1
  207. package/dist/web/server/pty/manager.js +76 -43
  208. package/dist/web/server/pty/manager.js.map +1 -1
  209. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  210. package/dist/web/server/routes/sessions.js +35 -2
  211. package/dist/web/server/routes/sessions.js.map +1 -1
  212. package/dist/web/server/routes/worktrees.js +2 -2
  213. package/dist/web/server/routes/worktrees.js.map +1 -1
  214. package/dist/web/server/services/worktrees.d.ts.map +1 -1
  215. package/dist/web/server/services/worktrees.js +7 -1
  216. package/dist/web/server/services/worktrees.js.map +1 -1
  217. package/dist/web/server/tray.d.ts +24 -0
  218. package/dist/web/server/tray.d.ts.map +1 -0
  219. package/dist/web/server/tray.js +79 -0
  220. package/dist/web/server/tray.js.map +1 -0
  221. package/dist/web/server/websocket/handler.d.ts +18 -2
  222. package/dist/web/server/websocket/handler.d.ts.map +1 -1
  223. package/dist/web/server/websocket/handler.js +82 -9
  224. package/dist/web/server/websocket/handler.js.map +1 -1
  225. package/package.json +15 -2
  226. package/src/claude.ts +26 -15
  227. package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
  228. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +17 -1
  229. package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +4 -4
  230. package/src/cli/ui/components/App.tsx +33 -133
  231. package/src/cli/ui/components/common/Select.tsx +2 -2
  232. package/src/cli/ui/components/screens/BranchListScreen.tsx +43 -23
  233. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +41 -46
  234. package/src/cli/ui/utils/continueSession.ts +1 -7
  235. package/src/codex.ts +31 -22
  236. package/src/config/builtin-tools.ts +6 -2
  237. package/src/config/shared-env.ts +139 -0
  238. package/src/gemini.ts +35 -22
  239. package/src/index.ts +54 -7
  240. package/src/logging/logger.ts +5 -2
  241. package/src/qwen.ts +28 -19
  242. package/src/services/aiToolResolver.ts +276 -0
  243. package/src/services/customToolResolver.ts +98 -0
  244. package/src/shared/aiToolConstants.ts +30 -0
  245. package/src/trayicon.d.ts +30 -0
  246. package/src/types/tools.ts +15 -0
  247. package/src/utils/prompt.ts +15 -9
  248. package/src/utils/session.ts +80 -26
  249. package/src/utils/terminal.ts +11 -41
  250. package/src/utils/webui.ts +43 -0
  251. package/src/web/client/components.json +21 -0
  252. package/src/web/client/src/components/AIToolLaunchModal.tsx +575 -0
  253. package/src/web/client/src/components/BranchGraph.tsx +95 -75
  254. package/src/web/client/src/components/CustomToolForm.tsx +386 -0
  255. package/src/web/client/src/components/CustomToolList.tsx +119 -0
  256. package/src/web/client/src/components/EnvEditor.tsx +91 -81
  257. package/src/web/client/src/components/EnvironmentEditor.tsx +97 -0
  258. package/src/web/client/src/components/Terminal.tsx +11 -3
  259. package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +179 -0
  260. package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +181 -0
  261. package/src/web/client/src/components/branch-detail/TerminalPanel.tsx +92 -0
  262. package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +327 -0
  263. package/src/web/client/src/components/branch-detail/index.ts +4 -0
  264. package/src/web/client/src/components/common/BranchCard.tsx +117 -0
  265. package/src/web/client/src/components/common/MetricCard.tsx +22 -0
  266. package/src/web/client/src/components/common/PageHeader.tsx +44 -0
  267. package/src/web/client/src/components/common/SearchInput.tsx +40 -0
  268. package/src/web/client/src/components/common/StatusBadge.tsx +37 -0
  269. package/src/web/client/src/components/common/index.ts +5 -0
  270. package/src/web/client/src/components/ui/alert.tsx +63 -0
  271. package/src/web/client/src/components/ui/badge.tsx +44 -0
  272. package/src/web/client/src/components/ui/button.tsx +57 -0
  273. package/src/web/client/src/components/ui/card.tsx +82 -0
  274. package/src/web/client/src/components/ui/index.ts +32 -0
  275. package/src/web/client/src/components/ui/input.tsx +21 -0
  276. package/src/web/client/src/components/ui/select.tsx +156 -0
  277. package/src/web/client/src/components/ui/table.tsx +119 -0
  278. package/src/web/client/src/hooks/useSessions.ts +10 -1
  279. package/src/web/client/src/index.css +46 -816
  280. package/src/web/client/src/lib/utils.ts +10 -0
  281. package/src/web/client/src/lib/websocket.ts +48 -1
  282. package/src/web/client/src/pages/BranchDetailPage.tsx +222 -694
  283. package/src/web/client/src/pages/BranchListPage.tsx +190 -236
  284. package/src/web/client/src/pages/ConfigManagementPage.tsx +94 -76
  285. package/src/web/client/src/pages/ConfigPage.tsx +362 -0
  286. package/src/web/client/vite.config.ts +8 -1
  287. package/src/web/server/index.ts +72 -15
  288. package/src/web/server/pty/manager.ts +128 -55
  289. package/src/web/server/routes/sessions.ts +59 -7
  290. package/src/web/server/routes/worktrees.ts +3 -3
  291. package/src/web/server/services/worktrees.ts +12 -4
  292. package/src/web/server/tray.ts +93 -0
  293. package/src/web/server/websocket/handler.ts +119 -13
  294. package/dist/client/assets/index-DeNwPosA.css +0 -1
  295. package/dist/client/assets/index-Dl798X5w.js +0 -32
package/src/claude.ts CHANGED
@@ -51,6 +51,9 @@ export async function launchClaudeCode(
51
51
  options.sessionId && options.sessionId.trim().length > 0
52
52
  ? options.sessionId.trim()
53
53
  : null;
54
+ const usedExplicitSessionId =
55
+ Boolean(resumeSessionId) &&
56
+ (options.mode === "continue" || options.mode === "resume");
54
57
 
55
58
  // Handle execution mode
56
59
  switch (options.mode) {
@@ -173,14 +176,16 @@ export async function launchClaudeCode(
173
176
 
174
177
  terminal.exitRawMode();
175
178
 
176
- const baseEnv = {
177
- ...process.env,
178
- ...(options.envOverrides ?? {}),
179
- };
180
- const launchEnv =
179
+ const baseEnv = { ...process.env, ...(options.envOverrides ?? {}) };
180
+ const launchEnvSource =
181
181
  options.skipPermissions && !baseEnv.IS_SANDBOX
182
182
  ? { ...baseEnv, IS_SANDBOX: "1" }
183
183
  : baseEnv;
184
+ const launchEnv = Object.fromEntries(
185
+ Object.entries(launchEnvSource).filter(
186
+ (entry): entry is [string, string] => typeof entry[1] === "string",
187
+ ),
188
+ );
184
189
 
185
190
  const childStdio = createChildStdio();
186
191
 
@@ -199,7 +204,7 @@ export async function launchClaudeCode(
199
204
  stdout: childStdio.stdout,
200
205
  stderr: childStdio.stderr,
201
206
  env: launchEnv,
202
- } as any);
207
+ });
203
208
  } else {
204
209
  console.log(
205
210
  chalk.cyan(
@@ -233,7 +238,7 @@ export async function launchClaudeCode(
233
238
  stdout: childStdio.stdout,
234
239
  stderr: childStdio.stderr,
235
240
  env: launchEnv,
236
- } as any);
241
+ });
237
242
  }
238
243
  } finally {
239
244
  childStdio.cleanup();
@@ -250,10 +255,13 @@ export async function launchClaudeCode(
250
255
  preferClosestTo: finishedAt,
251
256
  windowMs: 10 * 60 * 1000,
252
257
  });
253
- // Priority: latest on disk > resumeSessionId
254
- capturedSessionId = latest?.id ?? resumeSessionId ?? null;
258
+ const detectedSessionId = latest?.id ?? null;
259
+ // When we explicitly resumed a specific session, keep that ID as the source of truth.
260
+ capturedSessionId = usedExplicitSessionId
261
+ ? resumeSessionId
262
+ : detectedSessionId;
255
263
  } catch {
256
- capturedSessionId = resumeSessionId ?? null;
264
+ capturedSessionId = usedExplicitSessionId ? resumeSessionId : null;
257
265
  }
258
266
 
259
267
  if (capturedSessionId) {
@@ -270,11 +278,12 @@ export async function launchClaudeCode(
270
278
  }
271
279
 
272
280
  return capturedSessionId ? { sessionId: capturedSessionId } : {};
273
- } catch (error: any) {
281
+ } catch (error: unknown) {
274
282
  const hasLocalClaude = await isClaudeCommandAvailable();
275
283
  let errorMessage: string;
284
+ const err = error as NodeJS.ErrnoException;
276
285
 
277
- if (error.code === "ENOENT") {
286
+ if (err.code === "ENOENT") {
278
287
  if (hasLocalClaude) {
279
288
  errorMessage =
280
289
  "claude command not found. Please ensure Claude Code is properly installed.";
@@ -283,7 +292,8 @@ export async function launchClaudeCode(
283
292
  "bunx command not found. Please ensure Bun is installed so Claude Code can run via bunx.";
284
293
  }
285
294
  } else {
286
- errorMessage = `Failed to launch Claude Code: ${error.message || "Unknown error"}`;
295
+ const details = error instanceof Error ? error.message : String(error);
296
+ errorMessage = `Failed to launch Claude Code: ${details || "Unknown error"}`;
287
297
  }
288
298
 
289
299
  if (process.platform === "win32") {
@@ -344,8 +354,9 @@ export async function isClaudeCodeAvailable(): Promise<boolean> {
344
354
  try {
345
355
  await execa("bunx", [CLAUDE_CLI_PACKAGE, "--version"], { shell: true });
346
356
  return true;
347
- } catch (error: any) {
348
- if (error.code === "ENOENT") {
357
+ } catch (error: unknown) {
358
+ const err = error as NodeJS.ErrnoException;
359
+ if (err.code === "ENOENT") {
349
360
  console.error(chalk.yellow("\n⚠️ bunx command not found"));
350
361
  console.error(
351
362
  chalk.gray(
@@ -183,6 +183,17 @@ describe("Select", () => {
183
183
  expect(onSelect).toHaveBeenCalled();
184
184
  });
185
185
 
186
+ it("should call onSelect when linefeed is pressed (cooked mode Enter)", () => {
187
+ const onSelect = vi.fn();
188
+ const { stdin } = render(
189
+ <Select items={mockItems} onSelect={onSelect} />,
190
+ );
191
+
192
+ stdin.write("\n"); // Linefeed key
193
+
194
+ expect(onSelect).toHaveBeenCalled();
195
+ });
196
+
186
197
  it("should pass selected item to onSelect callback", () => {
187
198
  const onSelect = vi.fn();
188
199
  render(<Select items={mockItems} onSelect={onSelect} initialIndex={2} />);
@@ -123,6 +123,20 @@ describe("BranchListScreen", () => {
123
123
  expect(getAllByText(/enter/i).length).toBeGreaterThan(0);
124
124
  });
125
125
 
126
+ it("should display Web UI URL line", () => {
127
+ const onSelect = vi.fn();
128
+ const { container } = render(
129
+ <BranchListScreen
130
+ branches={mockBranches}
131
+ stats={mockStats}
132
+ onSelect={onSelect}
133
+ />,
134
+ );
135
+
136
+ expect(container.textContent).toContain("Web UI:");
137
+ expect(container.textContent).toContain("http://localhost:3000");
138
+ });
139
+
126
140
  it("should handle empty branch list", () => {
127
141
  const onSelect = vi.fn();
128
142
  const emptyStats: Statistics = {
@@ -431,7 +445,9 @@ describe("BranchListScreen", () => {
431
445
  stdin.write(" ");
432
446
  });
433
447
 
434
- const frame = stripControlSequences(stripAnsi(renderResult.lastFrame() ?? ""));
448
+ const frame = stripControlSequences(
449
+ stripAnsi(renderResult.lastFrame() ?? ""),
450
+ );
435
451
  expect(frame).toContain("[*] 🟢 🛡");
436
452
  expect(frame).toContain("feature/login");
437
453
  });
@@ -66,9 +66,7 @@ describe("BranchQuickStartScreen", () => {
66
66
  />,
67
67
  );
68
68
 
69
- expect(
70
- getByText(/Model: opus \/ Skip: No \/ ID: abc-123/),
71
- ).toBeDefined();
69
+ expect(getByText(/Model: opus \/ Skip: No \/ ID: abc-123/)).toBeDefined();
72
70
  });
73
71
 
74
72
  it("disables previous options when no history", () => {
@@ -132,7 +130,9 @@ describe("BranchQuickStartScreen", () => {
132
130
 
133
131
  expect(getAllByText(/\[Codex\]/i)).toHaveLength(2);
134
132
  expect(
135
- getByText(/Model: gpt-5.1-codex \/ Reasoning: High \/ Skip: Yes \/ ID: codex-123/),
133
+ getByText(
134
+ /Model: gpt-5.1-codex \/ Reasoning: High \/ Skip: Yes \/ ID: codex-123/,
135
+ ),
136
136
  ).toBeDefined();
137
137
  expect(getAllByText(/\[Claude\]/i)).toHaveLength(2);
138
138
  expect(
@@ -11,7 +11,6 @@ import { BranchListScreen } from "./screens/BranchListScreen.js";
11
11
  import { BranchCreatorScreen } from "./screens/BranchCreatorScreen.js";
12
12
  import { BranchActionSelectorScreen } from "../screens/BranchActionSelectorScreen.js";
13
13
  import { AIToolSelectorScreen } from "./screens/AIToolSelectorScreen.js";
14
- import { SessionSelectorScreen } from "./screens/SessionSelectorScreen.js";
15
14
  import { ExecutionModeSelectorScreen } from "./screens/ExecutionModeSelectorScreen.js";
16
15
  import type { ExecutionMode } from "./screens/ExecutionModeSelectorScreen.js";
17
16
  import { BranchQuickStartScreen } from "./screens/BranchQuickStartScreen.js";
@@ -93,13 +92,6 @@ export interface AppProps {
93
92
  loadingIndicatorDelay?: number;
94
93
  }
95
94
 
96
- export interface SessionOption {
97
- sessionId: string;
98
- toolLabel: string;
99
- branch: string;
100
- timestamp: number;
101
- }
102
-
103
95
  /**
104
96
  * App - Top-level component for Ink.js UI
105
97
  * Integrates ErrorBoundary, data fetching, screen navigation, and all screens
@@ -119,13 +111,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
119
111
  // Version state
120
112
  const [version, setVersion] = useState<string | null>(null);
121
113
  const [repoRoot, setRepoRoot] = useState<string | null>(null);
122
- const [sessionOptions, setSessionOptions] = useState<SessionOption[]>([]);
123
- const [sessionLoading, setSessionLoading] = useState(false);
124
- const [sessionError, setSessionError] = useState<string | null>(null);
125
- const [pendingExecution, setPendingExecution] = useState<{
126
- mode: ExecutionMode;
127
- skipPermissions: boolean;
128
- } | null>(null);
129
114
  const [continueSessionId, setContinueSessionId] = useState<string | null>(
130
115
  null,
131
116
  );
@@ -140,8 +125,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
140
125
  timestamp?: number | null;
141
126
  }[]
142
127
  >([]);
143
- const [branchQuickStartLoading, setBranchQuickStartLoading] =
144
- useState(false);
128
+ const [branchQuickStartLoading, setBranchQuickStartLoading] = useState(false);
145
129
 
146
130
  // Selection state (for branch → tool → mode flow)
147
131
  const [selectedBranch, setSelectedBranch] =
@@ -274,9 +258,7 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
274
258
  if (cancelled) return;
275
259
  const safe = new Set(
276
260
  targets
277
- .filter(
278
- (t) => !t.hasUncommittedChanges && !t.hasUnpushedCommits,
279
- )
261
+ .filter((t) => !t.hasUncommittedChanges && !t.hasUnpushedCommits)
280
262
  .map((t) => t.branch),
281
263
  );
282
264
  setSafeBranches(safe);
@@ -290,53 +272,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
290
272
  };
291
273
  }, [branches, worktrees]);
292
274
 
293
- // Load available sessions when entering session selector
294
- useEffect(() => {
295
- if (currentScreen !== "session-selector") {
296
- return;
297
- }
298
- if (!selectedTool || !selectedBranch) {
299
- setSessionOptions([]);
300
- return;
301
- }
302
-
303
- setSessionLoading(true);
304
- setSessionError(null);
305
-
306
- (async () => {
307
- try {
308
- const root = repoRoot ?? (await getRepositoryRoot());
309
- if (!repoRoot && root) {
310
- setRepoRoot(root);
311
- }
312
- const sessionData = root ? await loadSession(root) : null;
313
- const history = sessionData?.history ?? [];
314
-
315
- const filtered = history
316
- .filter(
317
- (entry) =>
318
- entry.sessionId &&
319
- entry.toolId === selectedTool &&
320
- entry.branch === selectedBranch.name,
321
- )
322
- .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0))
323
- .map((entry) => ({
324
- sessionId: entry.sessionId as string,
325
- toolLabel: entry.toolLabel,
326
- branch: entry.branch,
327
- timestamp: entry.timestamp,
328
- }));
329
-
330
- setSessionOptions(filtered);
331
- } catch (_err) {
332
- setSessionOptions([]);
333
- setSessionError("セッション一覧の取得に失敗しました");
334
- } finally {
335
- setSessionLoading(false);
336
- }
337
- })();
338
- }, [currentScreen, selectedTool, selectedBranch, repoRoot]);
339
-
340
275
  // Load quick start options for selected branch (latest per tool)
341
276
  useEffect(() => {
342
277
  if (!selectedBranch) {
@@ -387,60 +322,55 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
387
322
  const mapped = await Promise.all(
388
323
  latestPerTool.map(async (entry) => {
389
324
  let sessionId = entry.sessionId ?? null;
325
+ const worktree = selectedWorktreePath ?? workingDirectory;
390
326
 
391
327
  // For Codex, prefer a newer filesystem session over stale history
392
- if (entry.toolId === "codex-cli") {
328
+ if (!sessionId && entry.toolId === "codex-cli") {
393
329
  try {
394
- const latestCodex = await findLatestCodexSession();
395
- const historyTs = entry.timestamp ?? 0;
396
- if (
397
- latestCodex &&
398
- (!sessionId || latestCodex.mtime > historyTs)
399
- ) {
400
- sessionId = latestCodex.id;
401
- }
402
- // Fallback when filesystem unavailable and history missing
403
- if (!sessionId) {
404
- const latestId = await findLatestCodexSessionId();
405
- if (latestId) sessionId = latestId;
406
- }
330
+ const historyTs = entry.timestamp ?? null;
331
+ const latestCodex = await findLatestCodexSession({
332
+ ...(historyTs
333
+ ? {
334
+ since: historyTs - 60_000,
335
+ preferClosestTo: historyTs,
336
+ windowMs: 60 * 60 * 1000,
337
+ }
338
+ : {}),
339
+ cwd: worktree,
340
+ });
341
+ sessionId =
342
+ latestCodex?.id ??
343
+ (await findLatestCodexSessionId({ cwd: worktree })) ??
344
+ null;
407
345
  } catch {
408
346
  // ignore lookup failure
409
347
  }
410
348
  }
411
349
 
412
350
  // For Claude Code, prefer the newest session file in the worktree even if history is stale.
413
- if (entry.toolId === "claude-code") {
351
+ if (!sessionId && entry.toolId === "claude-code") {
414
352
  try {
415
- const worktree =
416
- selectedWorktreePath ??
417
- selectedBranch?.displayName ??
418
- workingDirectory;
419
-
420
353
  // Always resolve freshest on-disk session for this worktree (no window restriction)
421
354
  const latestAny = await findLatestClaudeSession(worktree);
422
- sessionId = latestAny?.id ?? sessionId ?? null;
423
-
355
+ sessionId = latestAny?.id ?? null;
424
356
  } catch {
425
357
  // ignore lookup failure
426
358
  }
427
359
  }
428
360
 
429
361
  // For Gemini, prefer newest session file (Gemini keeps per-project chats)
430
- if (entry.toolId === "gemini-cli") {
362
+ if (!sessionId && entry.toolId === "gemini-cli") {
431
363
  try {
432
- const gemSession = await findLatestGeminiSession(
433
- selectedWorktreePath ??
434
- selectedBranch?.displayName ??
435
- workingDirectory,
436
- {
437
- ...(entry.timestamp !== null && entry.timestamp !== undefined
438
- ? { since: entry.timestamp - 60_000, preferClosestTo: entry.timestamp }
439
- : {}),
440
- windowMs: 60 * 60 * 1000,
441
- },
442
- );
443
- if (gemSession?.id) sessionId = gemSession.id;
364
+ const gemSession = await findLatestGeminiSession(worktree, {
365
+ ...(entry.timestamp !== null && entry.timestamp !== undefined
366
+ ? {
367
+ since: entry.timestamp - 60_000,
368
+ preferClosestTo: entry.timestamp,
369
+ }
370
+ : {}),
371
+ windowMs: 60 * 60 * 1000,
372
+ });
373
+ sessionId = gemSession?.id ?? null;
444
374
  } catch {
445
375
  // ignore
446
376
  }
@@ -1165,18 +1095,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
1165
1095
  ],
1166
1096
  );
1167
1097
 
1168
- // Handle session selection
1169
- const handleSessionSelect = useCallback(
1170
- (session: string) => {
1171
- const execution = pendingExecution ?? {
1172
- mode: "resume" as ExecutionMode,
1173
- skipPermissions: false,
1174
- };
1175
- completeSelection(execution.mode, execution.skipPermissions, session);
1176
- },
1177
- [pendingExecution, completeSelection],
1178
- );
1179
-
1180
1098
  const handleQuickStartSelect = useCallback(
1181
1099
  (action: QuickStartAction, toolId?: AITool | null) => {
1182
1100
  if (action === "manual" || !branchQuickStart.length) {
@@ -1228,14 +1146,9 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
1228
1146
  // Handle execution mode and skipPermissions selection
1229
1147
  const handleModeSelect = useCallback(
1230
1148
  (result: { mode: ExecutionMode; skipPermissions: boolean }) => {
1231
- if (result.mode === "resume") {
1232
- setPendingExecution(result);
1233
- navigateTo("session-selector");
1234
- return;
1235
- }
1236
1149
  completeSelection(result.mode, result.skipPermissions, null);
1237
1150
  },
1238
- [completeSelection, navigateTo],
1151
+ [completeSelection],
1239
1152
  );
1240
1153
 
1241
1154
  // Render screen based on currentScreen
@@ -1345,19 +1258,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
1345
1258
  />
1346
1259
  );
1347
1260
 
1348
- case "session-selector":
1349
- // TODO: Implement session data fetching
1350
- return (
1351
- <SessionSelectorScreen
1352
- sessions={sessionOptions}
1353
- loading={sessionLoading}
1354
- errorMessage={sessionError}
1355
- onBack={goBack}
1356
- onSelect={handleSessionSelect}
1357
- version={version}
1358
- />
1359
- );
1360
-
1361
1261
  case "execution-mode-selector":
1362
1262
  return (
1363
1263
  <ExecutionModeSelectorScreen
@@ -164,8 +164,8 @@ const SelectComponent = <T extends SelectItem = SelectItem>({
164
164
 
165
165
  return newIndex;
166
166
  });
167
- } else if (key.return) {
168
- // Select current item
167
+ } else if (key.return || input === "\n") {
168
+ // Select current item (handle both CR in raw mode and LF in cooked mode)
169
169
  const selectedItem = items[selectedIndex];
170
170
  if (selectedItem && !disabled) {
171
171
  onSelect(selectedItem);
@@ -11,6 +11,7 @@ import type { BranchItem, Statistics } from "../../types.js";
11
11
  import stringWidth from "string-width";
12
12
  import stripAnsi from "strip-ansi";
13
13
  import chalk from "chalk";
14
+ import { resolveWebUiPort } from "../../../../utils/webui.js";
14
15
 
15
16
  // Emoji 幅は端末によって 1 または 2 になることがあるため、最小幅を上書きして
16
17
  // 実測より小さくならないようにする(過小評価=折り返しの原因を防ぐ)
@@ -125,9 +126,11 @@ export function BranchListScreen({
125
126
  onToggleSelect,
126
127
  }: BranchListScreenProps) {
127
128
  const { rows } = useTerminalSize();
128
- const headerText =
129
- " Legend: [ ]/[ * ] select 🟢/⚪ worktree 🛡/⚠ safe";
130
- const selectedSet = useMemo(() => new Set(selectedBranches), [selectedBranches]);
129
+ const headerText = " Legend: [ ]/[ * ] select 🟢/⚪ worktree 🛡/⚠ safe";
130
+ const selectedSet = useMemo(
131
+ () => new Set(selectedBranches),
132
+ [selectedBranches],
133
+ );
131
134
 
132
135
  // Filter state - allow test control via props
133
136
  const [internalFilterQuery, setInternalFilterQuery] = useState("");
@@ -243,15 +246,22 @@ export function BranchListScreen({
243
246
  // Filter input: 1 line
244
247
  // Stats: 1 line
245
248
  // Empty line: 1 line
249
+ // Web UI URL: 1 line
246
250
  // Footer: 1 line
247
- // Total fixed: 6 lines
251
+ // Total fixed: 7 lines
248
252
  const headerLines = 2;
249
253
  const filterLines = 1;
250
254
  const statsLines = 1;
251
255
  const emptyLine = 1;
256
+ const webUiLines = 1;
252
257
  const footerLines = 1;
253
258
  const fixedLines =
254
- headerLines + filterLines + statsLines + emptyLine + footerLines;
259
+ headerLines +
260
+ filterLines +
261
+ statsLines +
262
+ emptyLine +
263
+ webUiLines +
264
+ footerLines;
255
265
  const contentHeight = rows - fixedLines;
256
266
  const limit = Math.max(5, contentHeight); // Minimum 5 items visible
257
267
 
@@ -308,25 +318,28 @@ export function BranchListScreen({
308
318
  return result + ellipsis;
309
319
  }, []);
310
320
 
311
- const colorToolLabel = useCallback((label: string, toolId?: string | null) => {
312
- switch (toolId) {
313
- case "claude-code":
314
- return chalk.hex("#ffaf00")(label); // orange-ish
315
- case "codex-cli":
316
- return chalk.cyan(label);
317
- case "gemini-cli":
318
- return chalk.magenta(label);
319
- case "qwen-cli":
320
- return chalk.green(label);
321
- default: {
322
- const trimmed = label.trim().toLowerCase();
323
- if (!toolId || trimmed === "unknown") {
324
- return chalk.gray(label);
321
+ const colorToolLabel = useCallback(
322
+ (label: string, toolId?: string | null) => {
323
+ switch (toolId) {
324
+ case "claude-code":
325
+ return chalk.hex("#ffaf00")(label); // orange-ish
326
+ case "codex-cli":
327
+ return chalk.cyan(label);
328
+ case "gemini-cli":
329
+ return chalk.magenta(label);
330
+ case "qwen-cli":
331
+ return chalk.green(label);
332
+ default: {
333
+ const trimmed = label.trim().toLowerCase();
334
+ if (!toolId || trimmed === "unknown") {
335
+ return chalk.gray(label);
336
+ }
337
+ return chalk.white(label);
325
338
  }
326
- return chalk.white(label);
327
339
  }
328
- }
329
- }, []);
340
+ },
341
+ [],
342
+ );
330
343
 
331
344
  const renderBranchRow = useCallback(
332
345
  (item: BranchItem, isSelected: boolean, context: { columns: number }) => {
@@ -450,7 +463,9 @@ export function BranchListScreen({
450
463
 
451
464
  // 端末幅を超えた場合は隙間→ラベルの順で詰めて収める
452
465
  const clampToWidth = () => {
453
- const finalWidth = measureDisplayWidth(stripAnsi(lineWithColoredTimestamp));
466
+ const finalWidth = measureDisplayWidth(
467
+ stripAnsi(lineWithColoredTimestamp),
468
+ );
454
469
  if (finalWidth <= columns) {
455
470
  return;
456
471
  }
@@ -608,6 +623,11 @@ export function BranchListScreen({
608
623
  </Box>
609
624
  )}
610
625
 
626
+ {/* Web UI URL */}
627
+ <Box>
628
+ <Text dimColor>Web UI: http://localhost:{resolveWebUiPort()}</Text>
629
+ </Box>
630
+
611
631
  {/* Footer */}
612
632
  <Footer actions={footerActions} />
613
633
  </Box>