@akiojin/gwt 2.13.0 → 3.0.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 +41 -0
  2. package/README.md +38 -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.map +1 -1
  14. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
  15. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +3 -3
  16. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
  17. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  18. package/dist/cli/ui/utils/continueSession.js +1 -1
  19. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  20. package/dist/client/assets/index-DsDNCy5f.css +1 -0
  21. package/dist/client/assets/index-f5D2XwDh.js +72 -0
  22. package/dist/client/index.html +2 -2
  23. package/dist/codex.d.ts.map +1 -1
  24. package/dist/codex.js +21 -11
  25. package/dist/codex.js.map +1 -1
  26. package/dist/config/builtin-tools.d.ts.map +1 -1
  27. package/dist/config/builtin-tools.js +3 -2
  28. package/dist/config/builtin-tools.js.map +1 -1
  29. package/dist/config/shared-env.d.ts +41 -0
  30. package/dist/config/shared-env.d.ts.map +1 -0
  31. package/dist/config/shared-env.js +114 -0
  32. package/dist/config/shared-env.js.map +1 -0
  33. package/dist/gemini.d.ts.map +1 -1
  34. package/dist/gemini.js +20 -17
  35. package/dist/gemini.js.map +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +13 -8
  38. package/dist/index.js.map +1 -1
  39. package/dist/logging/logger.d.ts.map +1 -1
  40. package/dist/logging/logger.js +4 -1
  41. package/dist/logging/logger.js.map +1 -1
  42. package/dist/qwen.d.ts.map +1 -1
  43. package/dist/qwen.js +15 -11
  44. package/dist/qwen.js.map +1 -1
  45. package/dist/services/aiToolResolver.d.ts +41 -0
  46. package/dist/services/aiToolResolver.d.ts.map +1 -0
  47. package/dist/services/aiToolResolver.js +194 -0
  48. package/dist/services/aiToolResolver.js.map +1 -0
  49. package/dist/services/customToolResolver.d.ts +10 -0
  50. package/dist/services/customToolResolver.d.ts.map +1 -0
  51. package/dist/services/customToolResolver.js +71 -0
  52. package/dist/services/customToolResolver.js.map +1 -0
  53. package/dist/shared/aiToolConstants.d.ts +9 -0
  54. package/dist/shared/aiToolConstants.d.ts.map +1 -0
  55. package/dist/shared/aiToolConstants.js +29 -0
  56. package/dist/shared/aiToolConstants.js.map +1 -0
  57. package/dist/types/tools.d.ts +12 -0
  58. package/dist/types/tools.d.ts.map +1 -1
  59. package/dist/utils/prompt.d.ts.map +1 -1
  60. package/dist/utils/prompt.js.map +1 -1
  61. package/dist/utils/session.d.ts.map +1 -1
  62. package/dist/utils/session.js +15 -6
  63. package/dist/utils/session.js.map +1 -1
  64. package/dist/utils/terminal.d.ts +12 -3
  65. package/dist/utils/terminal.d.ts.map +1 -1
  66. package/dist/utils/terminal.js +5 -34
  67. package/dist/utils/terminal.js.map +1 -1
  68. package/dist/utils/webui.d.ts +8 -0
  69. package/dist/utils/webui.d.ts.map +1 -0
  70. package/dist/utils/webui.js +35 -0
  71. package/dist/utils/webui.js.map +1 -0
  72. package/dist/web/client/src/components/AIToolLaunchModal.d.ts +9 -0
  73. package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +1 -0
  74. package/dist/web/client/src/components/AIToolLaunchModal.js +363 -0
  75. package/dist/web/client/src/components/AIToolLaunchModal.js.map +1 -0
  76. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  77. package/dist/web/client/src/components/BranchGraph.js +46 -49
  78. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  79. package/dist/web/client/src/components/CustomToolForm.d.ts +23 -0
  80. package/dist/web/client/src/components/CustomToolForm.d.ts.map +1 -0
  81. package/dist/web/client/src/components/CustomToolForm.js +209 -0
  82. package/dist/web/client/src/components/CustomToolForm.js.map +1 -0
  83. package/dist/web/client/src/components/CustomToolList.d.ts +10 -0
  84. package/dist/web/client/src/components/CustomToolList.d.ts.map +1 -0
  85. package/dist/web/client/src/components/CustomToolList.js +57 -0
  86. package/dist/web/client/src/components/CustomToolList.js.map +1 -0
  87. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  88. package/dist/web/client/src/components/EnvEditor.js +33 -26
  89. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  90. package/dist/web/client/src/components/EnvironmentEditor.d.ts +17 -0
  91. package/dist/web/client/src/components/EnvironmentEditor.d.ts.map +1 -0
  92. package/dist/web/client/src/components/EnvironmentEditor.js +22 -0
  93. package/dist/web/client/src/components/EnvironmentEditor.js.map +1 -0
  94. package/dist/web/client/src/components/Terminal.d.ts.map +1 -1
  95. package/dist/web/client/src/components/Terminal.js +10 -3
  96. package/dist/web/client/src/components/Terminal.js.map +1 -1
  97. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts +10 -0
  98. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -0
  99. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +104 -0
  100. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -0
  101. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +22 -0
  102. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -0
  103. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +79 -0
  104. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -0
  105. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts +11 -0
  106. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts.map +1 -0
  107. package/dist/web/client/src/components/branch-detail/TerminalPanel.js +32 -0
  108. package/dist/web/client/src/components/branch-detail/TerminalPanel.js.map +1 -0
  109. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +40 -0
  110. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -0
  111. package/dist/web/client/src/components/branch-detail/ToolLauncher.js +147 -0
  112. package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -0
  113. package/dist/web/client/src/components/branch-detail/index.d.ts +5 -0
  114. package/dist/web/client/src/components/branch-detail/index.d.ts.map +1 -0
  115. package/dist/web/client/src/components/branch-detail/index.js +5 -0
  116. package/dist/web/client/src/components/branch-detail/index.js.map +1 -0
  117. package/dist/web/client/src/components/common/BranchCard.d.ts +17 -0
  118. package/dist/web/client/src/components/common/BranchCard.d.ts.map +1 -0
  119. package/dist/web/client/src/components/common/BranchCard.js +36 -0
  120. package/dist/web/client/src/components/common/BranchCard.js.map +1 -0
  121. package/dist/web/client/src/components/common/MetricCard.d.ts +10 -0
  122. package/dist/web/client/src/components/common/MetricCard.d.ts.map +1 -0
  123. package/dist/web/client/src/components/common/MetricCard.js +10 -0
  124. package/dist/web/client/src/components/common/MetricCard.js.map +1 -0
  125. package/dist/web/client/src/components/common/PageHeader.d.ts +12 -0
  126. package/dist/web/client/src/components/common/PageHeader.d.ts.map +1 -0
  127. package/dist/web/client/src/components/common/PageHeader.js +14 -0
  128. package/dist/web/client/src/components/common/PageHeader.js.map +1 -0
  129. package/dist/web/client/src/components/common/SearchInput.d.ts +14 -0
  130. package/dist/web/client/src/components/common/SearchInput.d.ts.map +1 -0
  131. package/dist/web/client/src/components/common/SearchInput.js +15 -0
  132. package/dist/web/client/src/components/common/SearchInput.js.map +1 -0
  133. package/dist/web/client/src/components/common/StatusBadge.d.ts +10 -0
  134. package/dist/web/client/src/components/common/StatusBadge.d.ts.map +1 -0
  135. package/dist/web/client/src/components/common/StatusBadge.js +15 -0
  136. package/dist/web/client/src/components/common/StatusBadge.js.map +1 -0
  137. package/dist/web/client/src/components/common/index.d.ts +6 -0
  138. package/dist/web/client/src/components/common/index.d.ts.map +1 -0
  139. package/dist/web/client/src/components/common/index.js +6 -0
  140. package/dist/web/client/src/components/common/index.js.map +1 -0
  141. package/dist/web/client/src/components/ui/alert.d.ts +9 -0
  142. package/dist/web/client/src/components/ui/alert.d.ts.map +1 -0
  143. package/dist/web/client/src/components/ui/alert.js +25 -0
  144. package/dist/web/client/src/components/ui/alert.js.map +1 -0
  145. package/dist/web/client/src/components/ui/badge.d.ts +10 -0
  146. package/dist/web/client/src/components/ui/badge.d.ts.map +1 -0
  147. package/dist/web/client/src/components/ui/badge.js +25 -0
  148. package/dist/web/client/src/components/ui/badge.js.map +1 -0
  149. package/dist/web/client/src/components/ui/button.d.ts +12 -0
  150. package/dist/web/client/src/components/ui/button.d.ts.map +1 -0
  151. package/dist/web/client/src/components/ui/button.js +33 -0
  152. package/dist/web/client/src/components/ui/button.js.map +1 -0
  153. package/dist/web/client/src/components/ui/card.d.ts +9 -0
  154. package/dist/web/client/src/components/ui/card.d.ts.map +1 -0
  155. package/dist/web/client/src/components/ui/card.js +16 -0
  156. package/dist/web/client/src/components/ui/card.js.map +1 -0
  157. package/dist/web/client/src/components/ui/index.d.ts +8 -0
  158. package/dist/web/client/src/components/ui/index.d.ts.map +1 -0
  159. package/dist/web/client/src/components/ui/index.js +8 -0
  160. package/dist/web/client/src/components/ui/index.js.map +1 -0
  161. package/dist/web/client/src/components/ui/input.d.ts +4 -0
  162. package/dist/web/client/src/components/ui/input.d.ts.map +1 -0
  163. package/dist/web/client/src/components/ui/input.js +8 -0
  164. package/dist/web/client/src/components/ui/input.js.map +1 -0
  165. package/dist/web/client/src/components/ui/select.d.ts +14 -0
  166. package/dist/web/client/src/components/ui/select.d.ts.map +1 -0
  167. package/dist/web/client/src/components/ui/select.js +39 -0
  168. package/dist/web/client/src/components/ui/select.js.map +1 -0
  169. package/dist/web/client/src/components/ui/table.d.ts +11 -0
  170. package/dist/web/client/src/components/ui/table.d.ts.map +1 -0
  171. package/dist/web/client/src/components/ui/table.js +21 -0
  172. package/dist/web/client/src/components/ui/table.js.map +1 -0
  173. package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
  174. package/dist/web/client/src/hooks/useSessions.js +6 -1
  175. package/dist/web/client/src/hooks/useSessions.js.map +1 -1
  176. package/dist/web/client/src/lib/utils.d.ts +7 -0
  177. package/dist/web/client/src/lib/utils.d.ts.map +1 -0
  178. package/dist/web/client/src/lib/utils.js +10 -0
  179. package/dist/web/client/src/lib/utils.js.map +1 -0
  180. package/dist/web/client/src/lib/websocket.d.ts +7 -0
  181. package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
  182. package/dist/web/client/src/lib/websocket.js +44 -0
  183. package/dist/web/client/src/lib/websocket.js.map +1 -1
  184. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  185. package/dist/web/client/src/pages/BranchDetailPage.js +125 -376
  186. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  187. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  188. package/dist/web/client/src/pages/BranchListPage.js +89 -127
  189. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  190. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  191. package/dist/web/client/src/pages/ConfigManagementPage.js +46 -41
  192. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  193. package/dist/web/client/src/pages/ConfigPage.d.ts +3 -0
  194. package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -0
  195. package/dist/web/client/src/pages/ConfigPage.js +216 -0
  196. package/dist/web/client/src/pages/ConfigPage.js.map +1 -0
  197. package/dist/web/client/vite.config.d.ts.map +1 -1
  198. package/dist/web/client/vite.config.js +8 -1
  199. package/dist/web/client/vite.config.js.map +1 -1
  200. package/dist/web/server/index.d.ts +24 -2
  201. package/dist/web/server/index.d.ts.map +1 -1
  202. package/dist/web/server/index.js +46 -15
  203. package/dist/web/server/index.js.map +1 -1
  204. package/dist/web/server/pty/manager.d.ts +12 -10
  205. package/dist/web/server/pty/manager.d.ts.map +1 -1
  206. package/dist/web/server/pty/manager.js +76 -43
  207. package/dist/web/server/pty/manager.js.map +1 -1
  208. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  209. package/dist/web/server/routes/sessions.js +35 -2
  210. package/dist/web/server/routes/sessions.js.map +1 -1
  211. package/dist/web/server/routes/worktrees.js +2 -2
  212. package/dist/web/server/routes/worktrees.js.map +1 -1
  213. package/dist/web/server/services/worktrees.d.ts.map +1 -1
  214. package/dist/web/server/services/worktrees.js +7 -1
  215. package/dist/web/server/services/worktrees.js.map +1 -1
  216. package/dist/web/server/tray.d.ts +25 -0
  217. package/dist/web/server/tray.d.ts.map +1 -0
  218. package/dist/web/server/tray.js +98 -0
  219. package/dist/web/server/tray.js.map +1 -0
  220. package/dist/web/server/websocket/handler.d.ts +18 -2
  221. package/dist/web/server/websocket/handler.d.ts.map +1 -1
  222. package/dist/web/server/websocket/handler.js +82 -9
  223. package/dist/web/server/websocket/handler.js.map +1 -1
  224. package/package.json +15 -2
  225. package/src/claude.ts +26 -15
  226. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +1 -1
  227. package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
  228. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +3 -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 +28 -21
  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 +13 -8
  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 +247 -723
  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 +120 -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(
@@ -203,7 +203,7 @@ describe("App protected branch handling", () => {
203
203
  remoteRef: null,
204
204
  });
205
205
 
206
- expect(navigateToMock).toHaveBeenCalledWith("branch-quick-start");
206
+ expect(navigateToMock).toHaveBeenCalledWith("branch-action-selector");
207
207
  expect(branchQuickStartProps).not.toHaveLength(0);
208
208
  });
209
209
  });
@@ -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} />);
@@ -431,7 +431,9 @@ describe("BranchListScreen", () => {
431
431
  stdin.write(" ");
432
432
  });
433
433
 
434
- const frame = stripControlSequences(stripAnsi(renderResult.lastFrame() ?? ""));
434
+ const frame = stripControlSequences(
435
+ stripAnsi(renderResult.lastFrame() ?? ""),
436
+ );
435
437
  expect(frame).toContain("[*] 🟢 🛡");
436
438
  expect(frame).toContain("feature/login");
437
439
  });
@@ -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);
@@ -125,9 +125,11 @@ export function BranchListScreen({
125
125
  onToggleSelect,
126
126
  }: BranchListScreenProps) {
127
127
  const { rows } = useTerminalSize();
128
- const headerText =
129
- " Legend: [ ]/[ * ] select 🟢/⚪ worktree 🛡/⚠ safe";
130
- const selectedSet = useMemo(() => new Set(selectedBranches), [selectedBranches]);
128
+ const headerText = " Legend: [ ]/[ * ] select 🟢/⚪ worktree 🛡/⚠ safe";
129
+ const selectedSet = useMemo(
130
+ () => new Set(selectedBranches),
131
+ [selectedBranches],
132
+ );
131
133
 
132
134
  // Filter state - allow test control via props
133
135
  const [internalFilterQuery, setInternalFilterQuery] = useState("");
@@ -308,25 +310,28 @@ export function BranchListScreen({
308
310
  return result + ellipsis;
309
311
  }, []);
310
312
 
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);
313
+ const colorToolLabel = useCallback(
314
+ (label: string, toolId?: string | null) => {
315
+ switch (toolId) {
316
+ case "claude-code":
317
+ return chalk.hex("#ffaf00")(label); // orange-ish
318
+ case "codex-cli":
319
+ return chalk.cyan(label);
320
+ case "gemini-cli":
321
+ return chalk.magenta(label);
322
+ case "qwen-cli":
323
+ return chalk.green(label);
324
+ default: {
325
+ const trimmed = label.trim().toLowerCase();
326
+ if (!toolId || trimmed === "unknown") {
327
+ return chalk.gray(label);
328
+ }
329
+ return chalk.white(label);
325
330
  }
326
- return chalk.white(label);
327
331
  }
328
- }
329
- }, []);
332
+ },
333
+ [],
334
+ );
330
335
 
331
336
  const renderBranchRow = useCallback(
332
337
  (item: BranchItem, isSelected: boolean, context: { columns: number }) => {
@@ -450,7 +455,9 @@ export function BranchListScreen({
450
455
 
451
456
  // 端末幅を超えた場合は隙間→ラベルの順で詰めて収める
452
457
  const clampToWidth = () => {
453
- const finalWidth = measureDisplayWidth(stripAnsi(lineWithColoredTimestamp));
458
+ const finalWidth = measureDisplayWidth(
459
+ stripAnsi(lineWithColoredTimestamp),
460
+ );
454
461
  if (finalWidth <= columns) {
455
462
  return;
456
463
  }
@@ -25,13 +25,12 @@ const REASONING_LABELS: Record<string, string> = {
25
25
  };
26
26
 
27
27
  const formatReasoning = (level?: string | null) =>
28
- level ? REASONING_LABELS[level] ?? level : "Default";
28
+ level ? (REASONING_LABELS[level] ?? level) : "Default";
29
29
 
30
30
  const formatSkip = (skip?: boolean | null) =>
31
31
  skip === true ? "Yes" : skip === false ? "No" : "No";
32
32
 
33
- const supportsReasoning = (toolId?: string | null) =>
34
- toolId === "codex-cli";
33
+ const supportsReasoning = (toolId?: string | null) => toolId === "codex-cli";
35
34
 
36
35
  const describe = (opt: BranchQuickStartOption, includeSessionId = true) => {
37
36
  const parts = [`Model: ${opt.model ?? "default"}`];
@@ -103,45 +102,45 @@ export function BranchQuickStartScreen({
103
102
  const items: QuickStartItem[] = previousOptions.length
104
103
  ? (() => {
105
104
  const order = ["Claude", "Codex", "Gemini", "Qwen", "Other"];
106
- const sorted = [...previousOptions].sort((a, b) => {
107
- const ca = resolveCategory(a.toolId).label;
108
- const cb = resolveCategory(b.toolId).label;
109
- return order.indexOf(ca) - order.indexOf(cb);
110
- });
105
+ const sorted = [...previousOptions].sort((a, b) => {
106
+ const ca = resolveCategory(a.toolId).label;
107
+ const cb = resolveCategory(b.toolId).label;
108
+ return order.indexOf(ca) - order.indexOf(cb);
109
+ });
111
110
 
112
- const flat: QuickStartItem[] = [];
111
+ const flat: QuickStartItem[] = [];
113
112
  sorted.forEach((opt, idx) => {
114
113
  const cat = resolveCategory(opt.toolId);
115
114
  const prevCat =
116
115
  idx > 0 ? resolveCategory(sorted[idx - 1]?.toolId).label : null;
117
116
  const isNewCategory = prevCat !== cat.label;
118
117
 
119
- flat.push(
120
- {
121
- label: "Resume",
122
- value: `reuse-continue:${opt.toolId ?? "unknown"}:${idx}`,
123
- action: "reuse-continue",
124
- toolId: opt.toolId ?? null,
125
- description: describe(opt, true),
126
- groupStart: isNewCategory && flat.length > 0,
127
- category: cat.label,
128
- categoryColor: cat.color,
129
- },
130
- {
131
- label: "New",
132
- value: `reuse-new:${opt.toolId ?? "unknown"}:${idx}`,
133
- action: "reuse-new",
134
- toolId: opt.toolId ?? null,
135
- description: describe(opt, false),
136
- groupStart: false,
137
- category: cat.label,
138
- categoryColor: cat.color,
139
- },
140
- );
141
- });
142
-
143
- return flat;
144
- })()
118
+ flat.push(
119
+ {
120
+ label: "Resume",
121
+ value: `reuse-continue:${opt.toolId ?? "unknown"}:${idx}`,
122
+ action: "reuse-continue",
123
+ toolId: opt.toolId ?? null,
124
+ description: describe(opt, true),
125
+ groupStart: isNewCategory && flat.length > 0,
126
+ category: cat.label,
127
+ categoryColor: cat.color,
128
+ },
129
+ {
130
+ label: "New",
131
+ value: `reuse-new:${opt.toolId ?? "unknown"}:${idx}`,
132
+ action: "reuse-new",
133
+ toolId: opt.toolId ?? null,
134
+ description: describe(opt, false),
135
+ groupStart: false,
136
+ category: cat.label,
137
+ categoryColor: cat.color,
138
+ },
139
+ );
140
+ });
141
+
142
+ return flat;
143
+ })()
145
144
  : [
146
145
  {
147
146
  label: "Resume with previous settings",
@@ -180,11 +179,7 @@ export function BranchQuickStartScreen({
180
179
 
181
180
  return (
182
181
  <Box flexDirection="column" height={containerHeight}>
183
- <Header
184
- title="Quick Start"
185
- titleColor="cyan"
186
- version={version}
187
- />
182
+ <Header title="Quick Start" titleColor="cyan" version={version} />
188
183
 
189
184
  <Box flexDirection="column" flexGrow={1} marginTop={1}>
190
185
  <Box marginBottom={1} flexDirection="column">
@@ -197,6 +192,7 @@ export function BranchQuickStartScreen({
197
192
  </Box>
198
193
  <Select
199
194
  items={items}
195
+ disabled={loading}
200
196
  onSelect={(item: QuickStartItem) => {
201
197
  if (item.disabled) return;
202
198
  onSelect(item.action, item.toolId ?? null);
@@ -204,13 +200,12 @@ export function BranchQuickStartScreen({
204
200
  renderItem={(item: QuickStartItem, isSelected) => (
205
201
  <Box
206
202
  flexDirection="column"
207
- marginTop={item.groupStart ? 1 : item.category === "Other" ? 1 : 0}
203
+ marginTop={
204
+ item.groupStart ? 1 : item.category === "Other" ? 1 : 0
205
+ }
208
206
  >
209
207
  <Text>
210
- <Text
211
- color={item.categoryColor}
212
- inverse={isSelected}
213
- >
208
+ <Text color={item.categoryColor} inverse={isSelected}>
214
209
  {`[${item.category}] `}
215
210
  </Text>
216
211
  <Text inverse={isSelected}>
@@ -219,7 +214,7 @@ export function BranchQuickStartScreen({
219
214
  </Text>
220
215
  </Text>
221
216
  {item.description && (
222
- <Text color="gray"> {item.description}</Text>
217
+ <Text color="gray"> {item.description}</Text>
223
218
  )}
224
219
  </Box>
225
220
  )}
@@ -17,13 +17,7 @@ export interface ContinueSessionContext {
17
17
  export async function resolveContinueSessionId(
18
18
  context: ContinueSessionContext,
19
19
  ): Promise<string | null> {
20
- const {
21
- history,
22
- sessionData,
23
- branch,
24
- toolId,
25
- repoRoot,
26
- } = context;
20
+ const { history, sessionData, branch, toolId, repoRoot: _repoRoot } = context;
27
21
 
28
22
  // 1) 履歴から最新マッチを探す(末尾から遡る)
29
23
  for (let i = history.length - 1; i >= 0; i -= 1) {