@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
@@ -0,0 +1,575 @@
1
+ import React, { useEffect, useMemo, useState } from "react";
2
+ import { useNavigate } from "react-router-dom";
3
+ import { Card, CardHeader, CardContent } from "@/components/ui/card";
4
+ import { Button } from "@/components/ui/button";
5
+ import { Input } from "@/components/ui/input";
6
+ import { Alert, AlertDescription } from "@/components/ui/alert";
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "@/components/ui/select";
14
+ import type { Branch, CustomAITool } from "../../../../types/api.js";
15
+ import {
16
+ CLAUDE_PERMISSION_SKIP_ARGS,
17
+ CODEX_DEFAULT_ARGS,
18
+ } from "../../../../shared/aiToolConstants.js";
19
+ import { useConfig } from "../hooks/useConfig";
20
+ import { useStartSession } from "../hooks/useSessions";
21
+ import { useCreateWorktree } from "../hooks/useWorktrees";
22
+ import { useSyncBranch } from "../hooks/useBranches";
23
+ import { ApiError } from "../lib/api";
24
+
25
+ const BUILTIN_TOOL_SUMMARIES: Record<string, ToolSummary> = {
26
+ "claude-code": {
27
+ command: "claude",
28
+ defaultArgs: [],
29
+ modeArgs: {
30
+ normal: [],
31
+ continue: ["-c"],
32
+ resume: ["-r"],
33
+ },
34
+ permissionSkipArgs: Array.from(CLAUDE_PERMISSION_SKIP_ARGS),
35
+ },
36
+ "codex-cli": {
37
+ command: "codex",
38
+ defaultArgs: Array.from(CODEX_DEFAULT_ARGS),
39
+ modeArgs: {
40
+ normal: [],
41
+ continue: ["resume", "--last"],
42
+ resume: ["resume"],
43
+ },
44
+ },
45
+ };
46
+
47
+ interface ToolSummary {
48
+ command: string;
49
+ defaultArgs?: string[] | null;
50
+ modeArgs?: {
51
+ normal?: string[];
52
+ continue?: string[];
53
+ resume?: string[];
54
+ };
55
+ permissionSkipArgs?: string[] | null;
56
+ }
57
+
58
+ interface AIToolLaunchModalProps {
59
+ branch: Branch;
60
+ onClose: () => void;
61
+ }
62
+
63
+ type ToolMode = "normal" | "continue" | "resume";
64
+
65
+ type SelectableTool =
66
+ | { id: "claude-code"; label: string; target: "claude" }
67
+ | { id: "codex-cli"; label: string; target: "codex" }
68
+ | { id: string; label: string; target: "custom"; definition: CustomAITool };
69
+
70
+ export function AIToolLaunchModal({ branch, onClose }: AIToolLaunchModalProps) {
71
+ const {
72
+ data: config,
73
+ isLoading: isConfigLoading,
74
+ error: configError,
75
+ } = useConfig();
76
+ const startSession = useStartSession();
77
+ const createWorktree = useCreateWorktree();
78
+ const syncBranch = useSyncBranch(branch.name);
79
+ const navigate = useNavigate();
80
+
81
+ const [selectedToolId, setSelectedToolId] = useState<string>("claude-code");
82
+ const [selectedMode, setSelectedMode] = useState<ToolMode>("normal");
83
+ const [skipPermissions, setSkipPermissions] = useState(false);
84
+ const [extraArgsText, setExtraArgsText] = useState("");
85
+ const [banner, setBanner] = useState<{
86
+ type: "success" | "error" | "info";
87
+ message: string;
88
+ } | null>(null);
89
+ const [isStartingSession, setIsStartingSession] = useState(false);
90
+ const [isCreatingWorktree, setIsCreatingWorktree] = useState(false);
91
+
92
+ const customTools = config?.tools ?? [];
93
+ const availableTools: SelectableTool[] = useMemo(
94
+ () => [
95
+ { id: "claude-code", label: "Claude Code", target: "claude" },
96
+ { id: "codex-cli", label: "Codex CLI", target: "codex" },
97
+ ...customTools.map((tool) => ({
98
+ id: tool.id,
99
+ label: tool.displayName,
100
+ target: "custom" as const,
101
+ definition: tool,
102
+ })),
103
+ ],
104
+ [customTools],
105
+ );
106
+
107
+ useEffect(() => {
108
+ if (!availableTools.length) {
109
+ setSelectedToolId("claude-code");
110
+ return;
111
+ }
112
+ if (!availableTools.find((tool) => tool.id === selectedToolId)) {
113
+ const first = availableTools[0];
114
+ if (first) {
115
+ setSelectedToolId(first.id);
116
+ }
117
+ }
118
+ }, [availableTools, selectedToolId]);
119
+
120
+ const selectedTool = availableTools.find(
121
+ (tool) => tool.id === selectedToolId,
122
+ );
123
+
124
+ const selectedToolSummary: ToolSummary | null = useMemo(() => {
125
+ if (!selectedTool) {
126
+ return null;
127
+ }
128
+ if (selectedTool.target === "custom") {
129
+ return {
130
+ command: selectedTool.definition.command,
131
+ defaultArgs: selectedTool.definition.defaultArgs ?? null,
132
+ modeArgs: selectedTool.definition.modeArgs,
133
+ permissionSkipArgs: selectedTool.definition.permissionSkipArgs ?? null,
134
+ };
135
+ }
136
+ return BUILTIN_TOOL_SUMMARIES[selectedTool.id] ?? null;
137
+ }, [selectedTool]);
138
+
139
+ const argsPreview = useMemo(() => {
140
+ if (!selectedToolSummary) {
141
+ return null;
142
+ }
143
+ const args: string[] = [];
144
+ if (selectedToolSummary.defaultArgs?.length) {
145
+ args.push(...selectedToolSummary.defaultArgs);
146
+ }
147
+ const mode = selectedToolSummary.modeArgs?.[selectedMode];
148
+ if (mode?.length) {
149
+ args.push(...mode);
150
+ }
151
+ if (skipPermissions && selectedToolSummary.permissionSkipArgs?.length) {
152
+ args.push(...selectedToolSummary.permissionSkipArgs);
153
+ }
154
+ const extraArgs = parseExtraArgs(extraArgsText);
155
+ if (extraArgs.length) {
156
+ args.push(...extraArgs);
157
+ }
158
+ return { command: selectedToolSummary.command, args };
159
+ }, [selectedToolSummary, selectedMode, skipPermissions, extraArgsText]);
160
+
161
+ const PROTECTED_BRANCHES = ["main", "master", "develop"];
162
+ const isProtectedBranch = PROTECTED_BRANCHES.includes(
163
+ branch.name.replace(/^origin\//, ""),
164
+ );
165
+ const divergenceInfo = branch.divergence ?? null;
166
+ const hasBlockingDivergence = Boolean(
167
+ divergenceInfo && divergenceInfo.ahead > 0 && divergenceInfo.behind > 0,
168
+ );
169
+ const needsRemoteSync = Boolean(
170
+ branch.worktreePath &&
171
+ divergenceInfo &&
172
+ divergenceInfo.behind > 0 &&
173
+ divergenceInfo.ahead === 0 &&
174
+ !hasBlockingDivergence,
175
+ );
176
+
177
+ useEffect(() => {
178
+ const previousOverflow = document.body.style.overflow;
179
+ document.body.style.overflow = "hidden";
180
+ return () => {
181
+ document.body.style.overflow = previousOverflow;
182
+ };
183
+ }, []);
184
+
185
+ const handleClose = () => {
186
+ setBanner(null);
187
+ onClose();
188
+ };
189
+
190
+ const handleCreateWorktree = async () => {
191
+ if (isProtectedBranch) {
192
+ setBanner({
193
+ type: "error",
194
+ message: `Cannot create worktree for protected branch: ${branch.name}. Protected branches (main, develop, master) must remain in the main repository.`,
195
+ });
196
+ return;
197
+ }
198
+ setIsCreatingWorktree(true);
199
+ try {
200
+ await createWorktree.mutateAsync({
201
+ branchName: branch.name,
202
+ createBranch: false,
203
+ });
204
+ setBanner({
205
+ type: "success",
206
+ message: `Worktree created for ${branch.name}. Please sync before launching.`,
207
+ });
208
+ } catch (error) {
209
+ setBanner({
210
+ type: "error",
211
+ message: formatError(error, "Failed to create worktree"),
212
+ });
213
+ } finally {
214
+ setIsCreatingWorktree(false);
215
+ }
216
+ };
217
+
218
+ const handleSyncBranch = async () => {
219
+ if (!branch.worktreePath) {
220
+ setBanner({
221
+ type: "error",
222
+ message: "Cannot sync because worktree is missing.",
223
+ });
224
+ return;
225
+ }
226
+ try {
227
+ const result = await syncBranch.mutateAsync({
228
+ worktreePath: branch.worktreePath,
229
+ });
230
+ if (result.pullStatus === "success") {
231
+ setBanner({
232
+ type: "success",
233
+ message: "Fetched latest changes from remote.",
234
+ });
235
+ } else {
236
+ const warning =
237
+ result.warnings?.join("\n") ?? "fast-forward pull did not complete.";
238
+ setBanner({
239
+ type: "error",
240
+ message: `git pull --ff-only failed.\n${warning}`,
241
+ });
242
+ }
243
+ } catch (error) {
244
+ setBanner({
245
+ type: "error",
246
+ message: formatError(error, "Git sync failed"),
247
+ });
248
+ }
249
+ };
250
+
251
+ const handleStartSession = async () => {
252
+ if (!branch.worktreePath) {
253
+ setBanner({
254
+ type: "error",
255
+ message: "Worktree missing. Create one first.",
256
+ });
257
+ return;
258
+ }
259
+ if (!selectedTool) {
260
+ setBanner({ type: "error", message: "Select an AI tool to launch." });
261
+ return;
262
+ }
263
+ if (needsRemoteSync) {
264
+ setBanner({
265
+ type: "error",
266
+ message: "Cannot launch until remote updates are synced.",
267
+ });
268
+ return;
269
+ }
270
+ if (hasBlockingDivergence) {
271
+ setBanner({
272
+ type: "error",
273
+ message:
274
+ "Both remote and local have diverged. Resolve differences before launching.",
275
+ });
276
+ return;
277
+ }
278
+
279
+ if (
280
+ skipPermissions &&
281
+ !window.confirm("Skip permission checks? This is risky.")
282
+ ) {
283
+ return;
284
+ }
285
+
286
+ setIsStartingSession(true);
287
+ try {
288
+ const toolType =
289
+ selectedTool.target === "codex"
290
+ ? "codex-cli"
291
+ : selectedTool.target === "custom"
292
+ ? "custom"
293
+ : "claude-code";
294
+ const extraArgs = parseExtraArgs(extraArgsText);
295
+ const sessionRequest = {
296
+ toolType,
297
+ toolName: selectedTool.target === "custom" ? selectedTool.id : null,
298
+ ...(selectedTool.target === "custom"
299
+ ? { customToolId: selectedTool.id }
300
+ : {}),
301
+ mode: selectedMode,
302
+ worktreePath: branch.worktreePath,
303
+ skipPermissions,
304
+ ...(selectedTool.target === "codex"
305
+ ? { bypassApprovals: skipPermissions }
306
+ : {}),
307
+ ...(extraArgs.length ? { extraArgs } : {}),
308
+ } as const;
309
+
310
+ const session = await startSession.mutateAsync(sessionRequest);
311
+ handleClose();
312
+ navigate(`/${encodeURIComponent(branch.name)}`, {
313
+ state: { focusSessionId: session.sessionId },
314
+ });
315
+ } catch (error) {
316
+ setBanner({
317
+ type: "error",
318
+ message: formatError(error, "Failed to start session"),
319
+ });
320
+ } finally {
321
+ setIsStartingSession(false);
322
+ }
323
+ };
324
+
325
+ return (
326
+ <div
327
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
328
+ role="dialog"
329
+ aria-modal="true"
330
+ >
331
+ <Card className="mx-4 w-full max-w-2xl" role="document">
332
+ <CardHeader className="pb-4">
333
+ <div className="flex items-start justify-between gap-4">
334
+ <div>
335
+ <p className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
336
+ Launch AI Tool
337
+ </p>
338
+ <h2 className="mt-1 text-lg font-semibold">{branch.name}</h2>
339
+ </div>
340
+ <Button variant="ghost" size="sm" onClick={handleClose}>
341
+ ×
342
+ </Button>
343
+ </div>
344
+ </CardHeader>
345
+
346
+ <CardContent className="space-y-4">
347
+ {banner && (
348
+ <Alert
349
+ variant={
350
+ banner.type === "error"
351
+ ? "destructive"
352
+ : banner.type === "success"
353
+ ? "success"
354
+ : "info"
355
+ }
356
+ >
357
+ <AlertDescription>{banner.message}</AlertDescription>
358
+ </Alert>
359
+ )}
360
+
361
+ {configError && (
362
+ <Alert variant="warning">
363
+ <AlertDescription>
364
+ Failed to load config:{" "}
365
+ {configError instanceof Error ? configError.message : "unknown"}
366
+ </AlertDescription>
367
+ </Alert>
368
+ )}
369
+
370
+ {!branch.worktreePath && (
371
+ <Alert variant={isProtectedBranch ? "destructive" : "warning"}>
372
+ <AlertDescription className="space-y-2">
373
+ {isProtectedBranch ? (
374
+ <p>
375
+ Cannot create worktree for protected branches (main,
376
+ develop, master). Protected branches must remain in the main
377
+ repository.
378
+ </p>
379
+ ) : (
380
+ <>
381
+ <p>
382
+ Worktree is missing. Create it before launching AI tools.
383
+ </p>
384
+ <Button
385
+ variant="secondary"
386
+ size="sm"
387
+ onClick={handleCreateWorktree}
388
+ disabled={isCreatingWorktree}
389
+ >
390
+ {isCreatingWorktree ? "Creating..." : "Create worktree"}
391
+ </Button>
392
+ </>
393
+ )}
394
+ </AlertDescription>
395
+ </Alert>
396
+ )}
397
+
398
+ {needsRemoteSync && (
399
+ <Alert variant="info">
400
+ <AlertDescription>
401
+ Remote has {branch.divergence?.behind ?? 0} commits you need to
402
+ pull before launching.
403
+ </AlertDescription>
404
+ </Alert>
405
+ )}
406
+
407
+ {hasBlockingDivergence && (
408
+ <Alert variant="warning">
409
+ <AlertDescription>
410
+ Both remote and local have unresolved differences. Rebase/merge
411
+ before launching.
412
+ </AlertDescription>
413
+ </Alert>
414
+ )}
415
+
416
+ <div className="grid gap-4 sm:grid-cols-3">
417
+ <div className="space-y-2">
418
+ <label className="text-sm font-medium">AI tool</label>
419
+ <Select
420
+ value={selectedToolId}
421
+ onValueChange={setSelectedToolId}
422
+ disabled={isConfigLoading ?? false}
423
+ >
424
+ <SelectTrigger>
425
+ <SelectValue />
426
+ </SelectTrigger>
427
+ <SelectContent>
428
+ {availableTools.map((tool) => (
429
+ <SelectItem key={tool.id} value={tool.id}>
430
+ {tool.label}
431
+ </SelectItem>
432
+ ))}
433
+ </SelectContent>
434
+ </Select>
435
+ </div>
436
+
437
+ <div className="space-y-2">
438
+ <label className="text-sm font-medium">Launch mode</label>
439
+ <Select
440
+ value={selectedMode}
441
+ onValueChange={(value) => setSelectedMode(value as ToolMode)}
442
+ >
443
+ <SelectTrigger>
444
+ <SelectValue />
445
+ </SelectTrigger>
446
+ <SelectContent>
447
+ <SelectItem value="normal">normal</SelectItem>
448
+ <SelectItem value="continue">continue</SelectItem>
449
+ <SelectItem value="resume">resume</SelectItem>
450
+ </SelectContent>
451
+ </Select>
452
+ </div>
453
+
454
+ <div className="space-y-2">
455
+ <label className="text-sm font-medium">Extra args</label>
456
+ <Input
457
+ type="text"
458
+ value={extraArgsText}
459
+ onChange={(event) => setExtraArgsText(event.target.value)}
460
+ placeholder="--flag value"
461
+ />
462
+ </div>
463
+ </div>
464
+
465
+ <label className="flex items-center gap-2 text-sm">
466
+ <input
467
+ type="checkbox"
468
+ checked={skipPermissions}
469
+ onChange={(event) => setSkipPermissions(event.target.checked)}
470
+ className="h-4 w-4 rounded border-border"
471
+ />
472
+ <span>Skip permission checks (at your own risk)</span>
473
+ </label>
474
+
475
+ <div className="flex flex-wrap gap-2">
476
+ <Button
477
+ onClick={handleStartSession}
478
+ disabled={
479
+ isStartingSession ||
480
+ !selectedTool ||
481
+ hasBlockingDivergence ||
482
+ needsRemoteSync
483
+ }
484
+ >
485
+ {isStartingSession ? "Launching..." : "Launch AI tool"}
486
+ </Button>
487
+ <Button
488
+ variant="secondary"
489
+ onClick={handleSyncBranch}
490
+ disabled={!branch.worktreePath || syncBranch.isPending}
491
+ >
492
+ {syncBranch.isPending ? "Syncing..." : "Sync latest"}
493
+ </Button>
494
+ <Button variant="ghost" onClick={handleClose}>
495
+ Cancel
496
+ </Button>
497
+ </div>
498
+
499
+ {selectedToolSummary && (
500
+ <div className="space-y-2 rounded-lg border bg-muted/30 p-4 text-sm">
501
+ <div className="grid gap-2 sm:grid-cols-3">
502
+ <div>
503
+ <span className="text-muted-foreground">Command:</span>{" "}
504
+ <code className="rounded bg-muted px-1.5 py-0.5 font-mono">
505
+ {selectedToolSummary.command}
506
+ </code>
507
+ </div>
508
+ <div>
509
+ <span className="text-muted-foreground">defaultArgs:</span>{" "}
510
+ <span
511
+ className={
512
+ !selectedToolSummary.defaultArgs?.length
513
+ ? "text-muted-foreground/50"
514
+ : ""
515
+ }
516
+ >
517
+ {renderArgs(selectedToolSummary.defaultArgs)}
518
+ </span>
519
+ </div>
520
+ <div>
521
+ <span className="text-muted-foreground">
522
+ permissionSkipArgs:
523
+ </span>{" "}
524
+ <span
525
+ className={
526
+ !selectedToolSummary.permissionSkipArgs?.length
527
+ ? "text-muted-foreground/50"
528
+ : ""
529
+ }
530
+ >
531
+ {renderArgs(selectedToolSummary.permissionSkipArgs)}
532
+ </span>
533
+ </div>
534
+ </div>
535
+ {argsPreview && (
536
+ <div className="border-t pt-2">
537
+ <span className="text-xs text-muted-foreground">
538
+ Command to run:
539
+ </span>
540
+ <pre className="mt-1 overflow-x-auto rounded bg-background p-2 font-mono text-sm">
541
+ {argsPreview.command} {argsPreview.args.join(" ")}
542
+ </pre>
543
+ </div>
544
+ )}
545
+ </div>
546
+ )}
547
+ </CardContent>
548
+ </Card>
549
+ </div>
550
+ );
551
+ }
552
+
553
+ function parseExtraArgs(value: string): string[] {
554
+ return value
555
+ .split(/\s+/)
556
+ .map((chunk) => chunk.trim())
557
+ .filter(Boolean);
558
+ }
559
+
560
+ function renderArgs(args?: string[] | null) {
561
+ if (!args || args.length === 0) {
562
+ return "未設定";
563
+ }
564
+ return args.join(" ");
565
+ }
566
+
567
+ function formatError(error: unknown, fallback: string) {
568
+ if (error instanceof ApiError) {
569
+ return `${error.message}${error.details ? `\n${error.details}` : ""}`;
570
+ }
571
+ if (error instanceof Error) {
572
+ return error.message;
573
+ }
574
+ return fallback;
575
+ }