@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,276 @@
1
+ import { execa } from "execa";
2
+ import { platform } from "os";
3
+ import { getToolById } from "../config/tools.js";
4
+ import {
5
+ CODEX_DEFAULT_ARGS,
6
+ CLAUDE_PERMISSION_SKIP_ARGS,
7
+ } from "../shared/aiToolConstants.js";
8
+ import { prepareCustomToolExecution } from "./customToolResolver.js";
9
+ import type { LaunchOptions } from "../types/tools.js";
10
+
11
+ const DETECTION_COMMAND = platform() === "win32" ? "where" : "which";
12
+ const MIN_BUN_MAJOR = 1;
13
+
14
+ export const CLAUDE_CLI_PACKAGE = "@anthropic-ai/claude-code@latest";
15
+ export const CODEX_CLI_PACKAGE = "@openai/codex@latest";
16
+
17
+ export type ResolverErrorCode =
18
+ | "COMMAND_NOT_FOUND"
19
+ | "BUNX_NOT_FOUND"
20
+ | "BUN_TOO_OLD"
21
+ | "CUSTOM_TOOL_NOT_FOUND";
22
+
23
+ export interface ResolvedCommand {
24
+ command: string;
25
+ args: string[];
26
+ usesFallback: boolean;
27
+ env?: NodeJS.ProcessEnv;
28
+ }
29
+
30
+ export class AIToolResolutionError extends Error {
31
+ constructor(
32
+ public code: ResolverErrorCode,
33
+ message: string,
34
+ public hints?: string[],
35
+ ) {
36
+ super(message);
37
+ this.name = "AIToolResolutionError";
38
+ }
39
+ }
40
+
41
+ async function commandExists(command: string): Promise<boolean> {
42
+ try {
43
+ await execa(DETECTION_COMMAND, [command], { shell: true });
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ let bunxCheckPromise: Promise<void> | null = null;
51
+
52
+ async function ensureBunxAvailable(): Promise<void> {
53
+ if (!bunxCheckPromise) {
54
+ bunxCheckPromise = (async () => {
55
+ const bunxExists = await commandExists("bunx");
56
+ if (!bunxExists) {
57
+ throw new AIToolResolutionError(
58
+ "BUNX_NOT_FOUND",
59
+ "bunx command not found. Install Bun 1.0+ so bunx is available on PATH.",
60
+ [
61
+ "Install Bun: https://bun.sh/docs/installation",
62
+ "After installation, restart your terminal so bunx is on PATH.",
63
+ ],
64
+ );
65
+ }
66
+
67
+ try {
68
+ const { stdout } = await execa("bun", ["--version"]);
69
+ const version = stdout.trim();
70
+ const major = parseInt(version.split(".")[0] ?? "0", 10);
71
+ if (!Number.isFinite(major) || major < MIN_BUN_MAJOR) {
72
+ throw new AIToolResolutionError(
73
+ "BUN_TOO_OLD",
74
+ `Detected Bun ${version}. Bun ${MIN_BUN_MAJOR}.0+ is required for bunx fallback execution.`,
75
+ [
76
+ "Upgrade Bun: curl -fsSL https://bun.sh/install | bash",
77
+ "Verify with 'bun --version' (needs >= 1.0)",
78
+ ],
79
+ );
80
+ }
81
+ } catch (error: unknown) {
82
+ if (error instanceof AIToolResolutionError) {
83
+ throw error;
84
+ }
85
+ const err = error as NodeJS.ErrnoException;
86
+ if (err?.code === "ENOENT") {
87
+ throw new AIToolResolutionError(
88
+ "BUNX_NOT_FOUND",
89
+ "bun command not found while verifying bunx. Install Bun 1.0+ and ensure it is on PATH.",
90
+ [
91
+ "Install Bun: https://bun.sh/docs/installation",
92
+ "After installation, run 'bun --version' to confirm.",
93
+ ],
94
+ );
95
+ }
96
+ throw new AIToolResolutionError(
97
+ "BUN_TOO_OLD",
98
+ `Failed to verify Bun version: ${err?.message ?? "unknown error"}`,
99
+ );
100
+ }
101
+ })();
102
+ }
103
+
104
+ try {
105
+ await bunxCheckPromise;
106
+ } catch (error) {
107
+ bunxCheckPromise = null;
108
+ throw error;
109
+ }
110
+ }
111
+
112
+ export interface ClaudeCommandOptions {
113
+ mode?: "normal" | "continue" | "resume";
114
+ skipPermissions?: boolean;
115
+ extraArgs?: string[];
116
+ }
117
+
118
+ export function buildClaudeArgs(options: ClaudeCommandOptions = {}): string[] {
119
+ const args: string[] = [];
120
+
121
+ switch (options.mode) {
122
+ case "continue":
123
+ args.push("-c");
124
+ break;
125
+ case "resume":
126
+ args.push("-r");
127
+ break;
128
+ default:
129
+ break;
130
+ }
131
+
132
+ if (options.skipPermissions) {
133
+ args.push(...CLAUDE_PERMISSION_SKIP_ARGS);
134
+ }
135
+
136
+ if (options.extraArgs?.length) {
137
+ args.push(...options.extraArgs);
138
+ }
139
+
140
+ return args;
141
+ }
142
+
143
+ export async function resolveClaudeCommand(
144
+ options: ClaudeCommandOptions = {},
145
+ ): Promise<ResolvedCommand> {
146
+ const args = buildClaudeArgs(options);
147
+
148
+ if (await commandExists("claude")) {
149
+ return {
150
+ command: "claude",
151
+ args,
152
+ usesFallback: false,
153
+ };
154
+ }
155
+
156
+ await ensureBunxAvailable();
157
+ return {
158
+ command: "bunx",
159
+ args: [CLAUDE_CLI_PACKAGE, ...args],
160
+ usesFallback: true,
161
+ };
162
+ }
163
+
164
+ export interface CodexCommandOptions {
165
+ mode?: "normal" | "continue" | "resume";
166
+ bypassApprovals?: boolean;
167
+ extraArgs?: string[];
168
+ }
169
+
170
+ export function buildCodexArgs(options: CodexCommandOptions = {}): string[] {
171
+ const args: string[] = [];
172
+
173
+ switch (options.mode) {
174
+ case "continue":
175
+ args.push("resume", "--last");
176
+ break;
177
+ case "resume":
178
+ args.push("resume");
179
+ break;
180
+ default:
181
+ break;
182
+ }
183
+
184
+ if (options.bypassApprovals) {
185
+ args.push("--yolo");
186
+ }
187
+
188
+ if (options.extraArgs?.length) {
189
+ args.push(...options.extraArgs);
190
+ }
191
+
192
+ args.push(...CODEX_DEFAULT_ARGS);
193
+ return args;
194
+ }
195
+
196
+ export async function resolveCodexCommand(
197
+ options: CodexCommandOptions = {},
198
+ ): Promise<ResolvedCommand> {
199
+ const args = buildCodexArgs(options);
200
+
201
+ if (await commandExists("codex")) {
202
+ return {
203
+ command: "codex",
204
+ args,
205
+ usesFallback: false,
206
+ };
207
+ }
208
+
209
+ await ensureBunxAvailable();
210
+ return {
211
+ command: "bunx",
212
+ args: [CODEX_CLI_PACKAGE, ...args],
213
+ usesFallback: true,
214
+ };
215
+ }
216
+
217
+ export interface CustomToolCommandOptions extends LaunchOptions {
218
+ toolId: string;
219
+ }
220
+
221
+ export async function resolveCustomToolCommand(
222
+ options: CustomToolCommandOptions,
223
+ ): Promise<ResolvedCommand> {
224
+ const tool = await getToolById(options.toolId);
225
+ if (!tool) {
226
+ throw new AIToolResolutionError(
227
+ "CUSTOM_TOOL_NOT_FOUND",
228
+ `Custom tool not found: ${options.toolId}`,
229
+ [
230
+ "Update ~/.gwt/tools.json to include this ID",
231
+ "Reload the Web UI after editing the tools list",
232
+ ],
233
+ );
234
+ }
235
+
236
+ const execution = await prepareCustomToolExecution(tool, options);
237
+
238
+ return {
239
+ command: execution.command,
240
+ args: execution.args,
241
+ usesFallback: tool.type === "bunx",
242
+ ...(execution.env ? { env: execution.env } : {}),
243
+ };
244
+ }
245
+
246
+ export async function isClaudeCodeAvailable(): Promise<boolean> {
247
+ try {
248
+ await resolveClaudeCommand();
249
+ return true;
250
+ } catch (error) {
251
+ if (error instanceof AIToolResolutionError) {
252
+ return false;
253
+ }
254
+ return false;
255
+ }
256
+ }
257
+
258
+ export async function isCodexAvailable(): Promise<boolean> {
259
+ try {
260
+ await resolveCodexCommand();
261
+ return true;
262
+ } catch (error) {
263
+ if (error instanceof AIToolResolutionError) {
264
+ return false;
265
+ }
266
+ return false;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Test-helper: resets cached bunx availability check.
272
+ * Not exported in type definitions to avoid production usage.
273
+ */
274
+ export function __resetBunxCacheForTests(): void {
275
+ bunxCheckPromise = null;
276
+ }
@@ -0,0 +1,98 @@
1
+ import { execa } from "execa";
2
+ import type { CustomAITool, LaunchOptions } from "../types/tools.js";
3
+
4
+ export interface CustomToolExecutionPlan {
5
+ command: string;
6
+ args: string[];
7
+ env?: NodeJS.ProcessEnv;
8
+ }
9
+
10
+ const WHICH_COMMAND = process.platform === "win32" ? "where" : "which";
11
+
12
+ export async function resolveCommandPath(commandName: string): Promise<string> {
13
+ try {
14
+ const { stdout } = await execa(WHICH_COMMAND, [commandName]);
15
+ const resolvedPath = (stdout.split("\n")[0] ?? "").trim();
16
+
17
+ if (!resolvedPath) {
18
+ throw new Error(
19
+ `Command "${commandName}" not found in PATH.\n` +
20
+ "Please ensure it is installed and available in your PATH.",
21
+ );
22
+ }
23
+
24
+ return resolvedPath;
25
+ } catch (error) {
26
+ const reason = error instanceof Error ? error.message : String(error);
27
+ throw new Error(
28
+ `Failed to resolve command "${commandName}".\n${reason}\n` +
29
+ "Please ensure the command is installed and available in your PATH.",
30
+ );
31
+ }
32
+ }
33
+
34
+ export function buildCustomToolArgs(
35
+ tool: CustomAITool,
36
+ options: LaunchOptions = {},
37
+ ): string[] {
38
+ const args: string[] = [];
39
+
40
+ if (tool.defaultArgs?.length) {
41
+ args.push(...tool.defaultArgs);
42
+ }
43
+
44
+ const mode = options.mode ?? "normal";
45
+ const modeArgs = tool.modeArgs?.[mode];
46
+ if (modeArgs?.length) {
47
+ args.push(...modeArgs);
48
+ }
49
+
50
+ if (options.skipPermissions && tool.permissionSkipArgs?.length) {
51
+ args.push(...tool.permissionSkipArgs);
52
+ }
53
+
54
+ if (options.extraArgs?.length) {
55
+ args.push(...options.extraArgs);
56
+ }
57
+
58
+ return args;
59
+ }
60
+
61
+ export async function prepareCustomToolExecution(
62
+ tool: CustomAITool,
63
+ options: LaunchOptions = {},
64
+ ): Promise<CustomToolExecutionPlan> {
65
+ const args = buildCustomToolArgs(tool, options);
66
+ const envOverrides: NodeJS.ProcessEnv | undefined = tool.env
67
+ ? ({ ...tool.env } as NodeJS.ProcessEnv)
68
+ : undefined;
69
+
70
+ switch (tool.type) {
71
+ case "path": {
72
+ return {
73
+ command: tool.command,
74
+ args,
75
+ ...(envOverrides ? { env: envOverrides } : {}),
76
+ };
77
+ }
78
+ case "bunx": {
79
+ return {
80
+ command: "bunx",
81
+ args: [tool.command, ...args],
82
+ ...(envOverrides ? { env: envOverrides } : {}),
83
+ };
84
+ }
85
+ case "command": {
86
+ const resolved = await resolveCommandPath(tool.command);
87
+ return {
88
+ command: resolved,
89
+ args,
90
+ ...(envOverrides ? { env: envOverrides } : {}),
91
+ };
92
+ }
93
+ default: {
94
+ const exhaustive: never = tool.type;
95
+ throw new Error(`Unknown custom tool type: ${exhaustive as string}`);
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared constants for AI tool integrations.
3
+ *
4
+ * These values are consumed by both the CLI (Node) runtime and the Web UI so
5
+ * that command previews, permission flags, and default arguments stay in sync.
6
+ */
7
+
8
+ export const CLAUDE_PERMISSION_SKIP_ARGS = [
9
+ "--dangerously-skip-permissions",
10
+ ] as const;
11
+
12
+ export const CODEX_DEFAULT_ARGS = [
13
+ "--enable",
14
+ "web_search_request",
15
+ "--model=gpt-5-codex",
16
+ "--sandbox",
17
+ "workspace-write",
18
+ "-c",
19
+ "model_reasoning_effort=high",
20
+ "-c",
21
+ "model_reasoning_summaries=detailed",
22
+ "-c",
23
+ "sandbox_workspace_write.network_access=true",
24
+ "-c",
25
+ "shell_environment_policy.inherit=all",
26
+ "-c",
27
+ "shell_environment_policy.ignore_default_excludes=true",
28
+ "-c",
29
+ "shell_environment_policy.experimental_use_profile=true",
30
+ ] as const;
@@ -0,0 +1,30 @@
1
+ declare module "trayicon" {
2
+ export interface TrayIconMenuItem {
3
+ text: string;
4
+ action?: () => void | Promise<void>;
5
+ disabled?: boolean;
6
+ checked?: boolean;
7
+ }
8
+
9
+ export interface TrayIconOptions {
10
+ icon: Buffer;
11
+ title?: string;
12
+ tooltip?: string;
13
+ action?: () => void | Promise<void>;
14
+ menu?: TrayIconMenuItem[];
15
+ }
16
+
17
+ export interface TrayIconInstance {
18
+ setIcon?: (icon: Buffer) => void;
19
+ setTitle?: (title: string) => void;
20
+ dispose?: () => void;
21
+ }
22
+
23
+ export function create(options: TrayIconOptions): TrayIconInstance;
24
+
25
+ const trayicon: {
26
+ create: typeof create;
27
+ };
28
+
29
+ export default trayicon;
30
+ }
@@ -69,6 +69,11 @@ export interface CustomAITool {
69
69
  */
70
70
  icon?: string;
71
71
 
72
+ /**
73
+ * 説明文(オプション)
74
+ */
75
+ description?: string;
76
+
72
77
  /**
73
78
  * 実行方式
74
79
  *
@@ -118,6 +123,16 @@ export interface CustomAITool {
118
123
  * APIキーや設定ファイルパスなどを指定。
119
124
  */
120
125
  env?: Record<string, string>;
126
+
127
+ /**
128
+ * 作成日時(ISO8601)。tools.jsonのメタデータとして使用。
129
+ */
130
+ createdAt?: string;
131
+
132
+ /**
133
+ * 更新日時(ISO8601)。tools.jsonのメタデータとして使用。
134
+ */
135
+ updatedAt?: string;
121
136
  }
122
137
 
123
138
  /**
@@ -22,7 +22,9 @@ export async function waitForEnter(promptMessage: string): Promise<void> {
22
22
 
23
23
  if ((stdin as NodeJS.ReadStream & { isRaw?: boolean }).isRaw) {
24
24
  try {
25
- (stdin as NodeJS.ReadStream & { setRawMode?: (flag: boolean) => void }).setRawMode?.(false);
25
+ (
26
+ stdin as NodeJS.ReadStream & { setRawMode?: (flag: boolean) => void }
27
+ ).setRawMode?.(false);
26
28
  } catch {
27
29
  // Ignore raw mode errors
28
30
  }
@@ -35,17 +37,21 @@ export async function waitForEnter(promptMessage: string): Promise<void> {
35
37
  rl.removeAllListeners();
36
38
  rl.close();
37
39
  const remover = (method: "off" | "removeListener") =>
38
- (stdin as unknown as Record<string, (event: string, fn: () => void) => void>)[method]?.(
39
- "end",
40
- onEnd,
41
- );
40
+ (
41
+ stdin as unknown as Record<
42
+ string,
43
+ (event: string, fn: () => void) => void
44
+ >
45
+ )[method]?.("end", onEnd);
42
46
  remover("off");
43
47
  remover("removeListener");
44
48
  const removerErr = (method: "off" | "removeListener") =>
45
- (stdin as unknown as Record<string, (event: string, fn: () => void) => void>)[method]?.(
46
- "error",
47
- onEnd,
48
- );
49
+ (
50
+ stdin as unknown as Record<
51
+ string,
52
+ (event: string, fn: () => void) => void
53
+ >
54
+ )[method]?.("error", onEnd);
49
55
  removerErr("off");
50
56
  removerErr("removeListener");
51
57
  if (typeof stdin.pause === "function") {
@@ -11,7 +11,9 @@ const UUID_REGEX =
11
11
  * @returns true if the string is a valid UUID format
12
12
  */
13
13
  export function isValidUuidSessionId(id: string): boolean {
14
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
14
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
15
+ id,
16
+ );
15
17
  }
16
18
 
17
19
  function pickSessionIdFromObject(obj: unknown): string | null {
@@ -34,7 +36,13 @@ function pickSessionIdFromObject(obj: unknown): string | null {
34
36
  function pickCwdFromObject(obj: unknown): string | null {
35
37
  if (!obj || typeof obj !== "object") return null;
36
38
  const candidate = obj as Record<string, unknown>;
37
- const keys = ["cwd", "workingDirectory", "workdir", "directory", "projectPath"];
39
+ const keys = [
40
+ "cwd",
41
+ "workingDirectory",
42
+ "workdir",
43
+ "directory",
44
+ "projectPath",
45
+ ];
38
46
  for (const key of keys) {
39
47
  const value = candidate[key];
40
48
  if (typeof value === "string" && value.trim().length > 0) {
@@ -118,7 +126,12 @@ async function findLatestFile(
118
126
  async function findNewestSessionIdFromDir(
119
127
  dir: string,
120
128
  recursive: boolean,
121
- options: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number } = {},
129
+ options: {
130
+ since?: number;
131
+ until?: number;
132
+ preferClosestTo?: number;
133
+ windowMs?: number;
134
+ } = {},
122
135
  ): Promise<{ id: string; mtime: number } | null> {
123
136
  try {
124
137
  const files: { fullPath: string; mtime: number }[] = [];
@@ -423,7 +436,12 @@ function generateClaudeProjectPathCandidates(cwd: string): string[] {
423
436
 
424
437
  export async function findLatestClaudeSessionId(
425
438
  cwd: string,
426
- options: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number } = {},
439
+ options: {
440
+ since?: number;
441
+ until?: number;
442
+ preferClosestTo?: number;
443
+ windowMs?: number;
444
+ } = {},
427
445
  ): Promise<string | null> {
428
446
  const found = await findLatestClaudeSession(cwd, options);
429
447
  return found?.id ?? null;
@@ -436,7 +454,12 @@ export interface ClaudeSessionInfo {
436
454
 
437
455
  export async function findLatestClaudeSession(
438
456
  cwd: string,
439
- options: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number } = {},
457
+ options: {
458
+ since?: number;
459
+ until?: number;
460
+ preferClosestTo?: number;
461
+ windowMs?: number;
462
+ } = {},
440
463
  ): Promise<ClaudeSessionInfo | null> {
441
464
  const rootCandidates: string[] = [];
442
465
  if (process.env.CLAUDE_CONFIG_DIR) {
@@ -481,9 +504,15 @@ export async function findLatestClaudeSession(
481
504
  try {
482
505
  const line = lines[i] ?? "";
483
506
  const parsed = JSON.parse(line) as Record<string, unknown>;
484
- const project = typeof parsed.project === "string" ? parsed.project : null;
485
- const sessionId = typeof parsed.sessionId === "string" ? parsed.sessionId : null;
486
- if (project && sessionId && (project === cwd || cwd.startsWith(project))) {
507
+ const project =
508
+ typeof parsed.project === "string" ? parsed.project : null;
509
+ const sessionId =
510
+ typeof parsed.sessionId === "string" ? parsed.sessionId : null;
511
+ if (
512
+ project &&
513
+ sessionId &&
514
+ (project === cwd || cwd.startsWith(project))
515
+ ) {
487
516
  return { id: sessionId, mtime: Date.now() };
488
517
  }
489
518
  } catch {
@@ -513,10 +542,16 @@ export async function waitForClaudeSessionId(
513
542
  const deadline = Date.now() + timeoutMs;
514
543
 
515
544
  while (Date.now() < deadline) {
516
- const opt: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number } = {};
545
+ const opt: {
546
+ since?: number;
547
+ until?: number;
548
+ preferClosestTo?: number;
549
+ windowMs?: number;
550
+ } = {};
517
551
  if (options.since !== undefined) opt.since = options.since;
518
552
  if (options.until !== undefined) opt.until = options.until;
519
- if (options.preferClosestTo !== undefined) opt.preferClosestTo = options.preferClosestTo;
553
+ if (options.preferClosestTo !== undefined)
554
+ opt.preferClosestTo = options.preferClosestTo;
520
555
  if (options.windowMs !== undefined) opt.windowMs = options.windowMs;
521
556
 
522
557
  const found = await findLatestClaudeSession(cwd, opt);
@@ -560,7 +595,13 @@ async function findLatestNestedSessionFile(
560
595
 
561
596
  export async function findLatestGeminiSession(
562
597
  _cwd: string,
563
- options: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number; cwd?: string | null } = {},
598
+ options: {
599
+ since?: number;
600
+ until?: number;
601
+ preferClosestTo?: number;
602
+ windowMs?: number;
603
+ cwd?: string | null;
604
+ } = {},
564
605
  ): Promise<GeminiSessionInfo | null> {
565
606
  // Gemini stores sessions/logs under ~/.gemini/tmp/<project_hash>/(chats|logs).json
566
607
  const baseDir = path.join(homedir(), ".gemini", "tmp");
@@ -588,17 +629,15 @@ export async function findLatestGeminiSession(
588
629
 
589
630
  const ref = options.preferClosestTo;
590
631
  const window = options.windowMs ?? 30 * 60 * 1000;
591
- pool = pool
592
- .slice()
593
- .sort((a, b) => {
594
- if (typeof ref === "number") {
595
- const da = Math.abs(a.mtime - ref);
596
- const db = Math.abs(b.mtime - ref);
597
- if (da === db) return b.mtime - a.mtime;
598
- if (da <= window || db <= window) return da - db;
599
- }
600
- return b.mtime - a.mtime;
601
- });
632
+ pool = pool.slice().sort((a, b) => {
633
+ if (typeof ref === "number") {
634
+ const da = Math.abs(a.mtime - ref);
635
+ const db = Math.abs(b.mtime - ref);
636
+ if (da === db) return b.mtime - a.mtime;
637
+ if (da <= window || db <= window) return da - db;
638
+ }
639
+ return b.mtime - a.mtime;
640
+ });
602
641
 
603
642
  for (const file of pool) {
604
643
  const info = await readSessionInfoFromFile(file.fullPath);
@@ -620,16 +659,31 @@ export async function findLatestGeminiSession(
620
659
 
621
660
  export async function findLatestGeminiSessionId(
622
661
  cwd: string,
623
- options: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number; cwd?: string | null } = {},
662
+ options: {
663
+ since?: number;
664
+ until?: number;
665
+ preferClosestTo?: number;
666
+ windowMs?: number;
667
+ cwd?: string | null;
668
+ } = {},
624
669
  ): Promise<string | null> {
625
- const normalized: { since?: number; until?: number; preferClosestTo?: number; windowMs?: number } = {};
670
+ const normalized: {
671
+ since?: number;
672
+ until?: number;
673
+ preferClosestTo?: number;
674
+ windowMs?: number;
675
+ } = {};
626
676
  if (options.since !== undefined) normalized.since = options.since as number;
627
677
  if (options.until !== undefined) normalized.until = options.until as number;
628
678
  if (options.preferClosestTo !== undefined)
629
679
  normalized.preferClosestTo = options.preferClosestTo as number;
630
- if (options.windowMs !== undefined) normalized.windowMs = options.windowMs as number;
680
+ if (options.windowMs !== undefined)
681
+ normalized.windowMs = options.windowMs as number;
631
682
 
632
- const found = await findLatestGeminiSession(cwd, { ...normalized, cwd: options.cwd ?? cwd });
683
+ const found = await findLatestGeminiSession(cwd, {
684
+ ...normalized,
685
+ cwd: options.cwd ?? cwd,
686
+ });
633
687
  return found?.id ?? null;
634
688
  }
635
689