@akiojin/gwt 2.12.1 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.ja.md +33 -0
  2. package/README.md +31 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +17 -11
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/components/App.d.ts +0 -6
  7. package/dist/cli/ui/components/App.d.ts.map +1 -1
  8. package/dist/cli/ui/components/App.js +93 -85
  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 +3 -1
  13. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  14. package/dist/cli/ui/components/screens/BranchListScreen.js +168 -34
  15. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  16. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
  17. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +3 -3
  18. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
  19. package/dist/cli/ui/hooks/useGitData.d.ts.map +1 -1
  20. package/dist/cli/ui/hooks/useGitData.js +17 -0
  21. package/dist/cli/ui/hooks/useGitData.js.map +1 -1
  22. package/dist/cli/ui/types.d.ts +2 -0
  23. package/dist/cli/ui/types.d.ts.map +1 -1
  24. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  25. package/dist/cli/ui/utils/branchFormatter.js +7 -2
  26. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  27. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  28. package/dist/cli/ui/utils/continueSession.js +1 -1
  29. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  30. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  31. package/dist/cli/ui/utils/modelOptions.js +7 -0
  32. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  33. package/dist/client/assets/index-DPWWHorC.js +72 -0
  34. package/dist/client/assets/index-DsDNCy5f.css +1 -0
  35. package/dist/client/index.html +2 -2
  36. package/dist/codex.d.ts.map +1 -1
  37. package/dist/codex.js +21 -11
  38. package/dist/codex.js.map +1 -1
  39. package/dist/config/builtin-tools.d.ts.map +1 -1
  40. package/dist/config/builtin-tools.js +3 -2
  41. package/dist/config/builtin-tools.js.map +1 -1
  42. package/dist/config/shared-env.d.ts +41 -0
  43. package/dist/config/shared-env.d.ts.map +1 -0
  44. package/dist/config/shared-env.js +114 -0
  45. package/dist/config/shared-env.js.map +1 -0
  46. package/dist/gemini.d.ts.map +1 -1
  47. package/dist/gemini.js +20 -17
  48. package/dist/gemini.js.map +1 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +55 -7
  51. package/dist/index.js.map +1 -1
  52. package/dist/logging/logger.d.ts +24 -0
  53. package/dist/logging/logger.d.ts.map +1 -0
  54. package/dist/logging/logger.js +60 -0
  55. package/dist/logging/logger.js.map +1 -0
  56. package/dist/logging/rotation.d.ts +6 -0
  57. package/dist/logging/rotation.d.ts.map +1 -0
  58. package/dist/logging/rotation.js +26 -0
  59. package/dist/logging/rotation.js.map +1 -0
  60. package/dist/qwen.d.ts.map +1 -1
  61. package/dist/qwen.js +15 -11
  62. package/dist/qwen.js.map +1 -1
  63. package/dist/services/aiToolResolver.d.ts +41 -0
  64. package/dist/services/aiToolResolver.d.ts.map +1 -0
  65. package/dist/services/aiToolResolver.js +194 -0
  66. package/dist/services/aiToolResolver.js.map +1 -0
  67. package/dist/services/customToolResolver.d.ts +10 -0
  68. package/dist/services/customToolResolver.d.ts.map +1 -0
  69. package/dist/services/customToolResolver.js +71 -0
  70. package/dist/services/customToolResolver.js.map +1 -0
  71. package/dist/shared/aiToolConstants.d.ts +9 -0
  72. package/dist/shared/aiToolConstants.d.ts.map +1 -0
  73. package/dist/shared/aiToolConstants.js +29 -0
  74. package/dist/shared/aiToolConstants.js.map +1 -0
  75. package/dist/types/tools.d.ts +12 -0
  76. package/dist/types/tools.d.ts.map +1 -1
  77. package/dist/utils/prompt.d.ts.map +1 -1
  78. package/dist/utils/prompt.js.map +1 -1
  79. package/dist/utils/session.d.ts.map +1 -1
  80. package/dist/utils/session.js +15 -6
  81. package/dist/utils/session.js.map +1 -1
  82. package/dist/utils/terminal.d.ts +12 -3
  83. package/dist/utils/terminal.d.ts.map +1 -1
  84. package/dist/utils/terminal.js +5 -34
  85. package/dist/utils/terminal.js.map +1 -1
  86. package/dist/utils/webui.d.ts +8 -0
  87. package/dist/utils/webui.d.ts.map +1 -0
  88. package/dist/utils/webui.js +35 -0
  89. package/dist/utils/webui.js.map +1 -0
  90. package/dist/web/client/src/components/AIToolLaunchModal.d.ts +9 -0
  91. package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +1 -0
  92. package/dist/web/client/src/components/AIToolLaunchModal.js +363 -0
  93. package/dist/web/client/src/components/AIToolLaunchModal.js.map +1 -0
  94. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  95. package/dist/web/client/src/components/BranchGraph.js +46 -49
  96. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  97. package/dist/web/client/src/components/CustomToolForm.d.ts +23 -0
  98. package/dist/web/client/src/components/CustomToolForm.d.ts.map +1 -0
  99. package/dist/web/client/src/components/CustomToolForm.js +209 -0
  100. package/dist/web/client/src/components/CustomToolForm.js.map +1 -0
  101. package/dist/web/client/src/components/CustomToolList.d.ts +10 -0
  102. package/dist/web/client/src/components/CustomToolList.d.ts.map +1 -0
  103. package/dist/web/client/src/components/CustomToolList.js +57 -0
  104. package/dist/web/client/src/components/CustomToolList.js.map +1 -0
  105. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  106. package/dist/web/client/src/components/EnvEditor.js +33 -26
  107. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  108. package/dist/web/client/src/components/EnvironmentEditor.d.ts +17 -0
  109. package/dist/web/client/src/components/EnvironmentEditor.d.ts.map +1 -0
  110. package/dist/web/client/src/components/EnvironmentEditor.js +22 -0
  111. package/dist/web/client/src/components/EnvironmentEditor.js.map +1 -0
  112. package/dist/web/client/src/components/Terminal.d.ts.map +1 -1
  113. package/dist/web/client/src/components/Terminal.js +10 -3
  114. package/dist/web/client/src/components/Terminal.js.map +1 -1
  115. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts +10 -0
  116. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -0
  117. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +104 -0
  118. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -0
  119. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +22 -0
  120. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -0
  121. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +79 -0
  122. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -0
  123. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts +11 -0
  124. package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts.map +1 -0
  125. package/dist/web/client/src/components/branch-detail/TerminalPanel.js +32 -0
  126. package/dist/web/client/src/components/branch-detail/TerminalPanel.js.map +1 -0
  127. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +40 -0
  128. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -0
  129. package/dist/web/client/src/components/branch-detail/ToolLauncher.js +147 -0
  130. package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -0
  131. package/dist/web/client/src/components/branch-detail/index.d.ts +5 -0
  132. package/dist/web/client/src/components/branch-detail/index.d.ts.map +1 -0
  133. package/dist/web/client/src/components/branch-detail/index.js +5 -0
  134. package/dist/web/client/src/components/branch-detail/index.js.map +1 -0
  135. package/dist/web/client/src/components/common/BranchCard.d.ts +17 -0
  136. package/dist/web/client/src/components/common/BranchCard.d.ts.map +1 -0
  137. package/dist/web/client/src/components/common/BranchCard.js +36 -0
  138. package/dist/web/client/src/components/common/BranchCard.js.map +1 -0
  139. package/dist/web/client/src/components/common/MetricCard.d.ts +10 -0
  140. package/dist/web/client/src/components/common/MetricCard.d.ts.map +1 -0
  141. package/dist/web/client/src/components/common/MetricCard.js +10 -0
  142. package/dist/web/client/src/components/common/MetricCard.js.map +1 -0
  143. package/dist/web/client/src/components/common/PageHeader.d.ts +12 -0
  144. package/dist/web/client/src/components/common/PageHeader.d.ts.map +1 -0
  145. package/dist/web/client/src/components/common/PageHeader.js +14 -0
  146. package/dist/web/client/src/components/common/PageHeader.js.map +1 -0
  147. package/dist/web/client/src/components/common/SearchInput.d.ts +14 -0
  148. package/dist/web/client/src/components/common/SearchInput.d.ts.map +1 -0
  149. package/dist/web/client/src/components/common/SearchInput.js +15 -0
  150. package/dist/web/client/src/components/common/SearchInput.js.map +1 -0
  151. package/dist/web/client/src/components/common/StatusBadge.d.ts +10 -0
  152. package/dist/web/client/src/components/common/StatusBadge.d.ts.map +1 -0
  153. package/dist/web/client/src/components/common/StatusBadge.js +15 -0
  154. package/dist/web/client/src/components/common/StatusBadge.js.map +1 -0
  155. package/dist/web/client/src/components/common/index.d.ts +6 -0
  156. package/dist/web/client/src/components/common/index.d.ts.map +1 -0
  157. package/dist/web/client/src/components/common/index.js +6 -0
  158. package/dist/web/client/src/components/common/index.js.map +1 -0
  159. package/dist/web/client/src/components/ui/alert.d.ts +9 -0
  160. package/dist/web/client/src/components/ui/alert.d.ts.map +1 -0
  161. package/dist/web/client/src/components/ui/alert.js +25 -0
  162. package/dist/web/client/src/components/ui/alert.js.map +1 -0
  163. package/dist/web/client/src/components/ui/badge.d.ts +10 -0
  164. package/dist/web/client/src/components/ui/badge.d.ts.map +1 -0
  165. package/dist/web/client/src/components/ui/badge.js +25 -0
  166. package/dist/web/client/src/components/ui/badge.js.map +1 -0
  167. package/dist/web/client/src/components/ui/button.d.ts +12 -0
  168. package/dist/web/client/src/components/ui/button.d.ts.map +1 -0
  169. package/dist/web/client/src/components/ui/button.js +33 -0
  170. package/dist/web/client/src/components/ui/button.js.map +1 -0
  171. package/dist/web/client/src/components/ui/card.d.ts +9 -0
  172. package/dist/web/client/src/components/ui/card.d.ts.map +1 -0
  173. package/dist/web/client/src/components/ui/card.js +16 -0
  174. package/dist/web/client/src/components/ui/card.js.map +1 -0
  175. package/dist/web/client/src/components/ui/index.d.ts +8 -0
  176. package/dist/web/client/src/components/ui/index.d.ts.map +1 -0
  177. package/dist/web/client/src/components/ui/index.js +8 -0
  178. package/dist/web/client/src/components/ui/index.js.map +1 -0
  179. package/dist/web/client/src/components/ui/input.d.ts +4 -0
  180. package/dist/web/client/src/components/ui/input.d.ts.map +1 -0
  181. package/dist/web/client/src/components/ui/input.js +8 -0
  182. package/dist/web/client/src/components/ui/input.js.map +1 -0
  183. package/dist/web/client/src/components/ui/select.d.ts +14 -0
  184. package/dist/web/client/src/components/ui/select.d.ts.map +1 -0
  185. package/dist/web/client/src/components/ui/select.js +39 -0
  186. package/dist/web/client/src/components/ui/select.js.map +1 -0
  187. package/dist/web/client/src/components/ui/table.d.ts +11 -0
  188. package/dist/web/client/src/components/ui/table.d.ts.map +1 -0
  189. package/dist/web/client/src/components/ui/table.js +21 -0
  190. package/dist/web/client/src/components/ui/table.js.map +1 -0
  191. package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
  192. package/dist/web/client/src/hooks/useSessions.js +6 -1
  193. package/dist/web/client/src/hooks/useSessions.js.map +1 -1
  194. package/dist/web/client/src/lib/utils.d.ts +7 -0
  195. package/dist/web/client/src/lib/utils.d.ts.map +1 -0
  196. package/dist/web/client/src/lib/utils.js +10 -0
  197. package/dist/web/client/src/lib/utils.js.map +1 -0
  198. package/dist/web/client/src/lib/websocket.d.ts +7 -0
  199. package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
  200. package/dist/web/client/src/lib/websocket.js +44 -0
  201. package/dist/web/client/src/lib/websocket.js.map +1 -1
  202. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  203. package/dist/web/client/src/pages/BranchDetailPage.js +113 -361
  204. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  205. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  206. package/dist/web/client/src/pages/BranchListPage.js +89 -127
  207. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  208. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  209. package/dist/web/client/src/pages/ConfigManagementPage.js +46 -41
  210. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  211. package/dist/web/client/src/pages/ConfigPage.d.ts +3 -0
  212. package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -0
  213. package/dist/web/client/src/pages/ConfigPage.js +216 -0
  214. package/dist/web/client/src/pages/ConfigPage.js.map +1 -0
  215. package/dist/web/client/vite.config.d.ts.map +1 -1
  216. package/dist/web/client/vite.config.js +8 -1
  217. package/dist/web/client/vite.config.js.map +1 -1
  218. package/dist/web/server/index.d.ts +24 -2
  219. package/dist/web/server/index.d.ts.map +1 -1
  220. package/dist/web/server/index.js +49 -18
  221. package/dist/web/server/index.js.map +1 -1
  222. package/dist/web/server/pty/manager.d.ts +12 -10
  223. package/dist/web/server/pty/manager.d.ts.map +1 -1
  224. package/dist/web/server/pty/manager.js +76 -43
  225. package/dist/web/server/pty/manager.js.map +1 -1
  226. package/dist/web/server/routes/branches.d.ts +2 -2
  227. package/dist/web/server/routes/branches.d.ts.map +1 -1
  228. package/dist/web/server/routes/branches.js.map +1 -1
  229. package/dist/web/server/routes/config.d.ts +2 -2
  230. package/dist/web/server/routes/config.d.ts.map +1 -1
  231. package/dist/web/server/routes/config.js.map +1 -1
  232. package/dist/web/server/routes/index.d.ts +2 -2
  233. package/dist/web/server/routes/index.d.ts.map +1 -1
  234. package/dist/web/server/routes/index.js.map +1 -1
  235. package/dist/web/server/routes/sessions.d.ts +2 -2
  236. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  237. package/dist/web/server/routes/sessions.js +35 -2
  238. package/dist/web/server/routes/sessions.js.map +1 -1
  239. package/dist/web/server/routes/worktrees.d.ts +2 -2
  240. package/dist/web/server/routes/worktrees.d.ts.map +1 -1
  241. package/dist/web/server/routes/worktrees.js +2 -2
  242. package/dist/web/server/routes/worktrees.js.map +1 -1
  243. package/dist/web/server/services/worktrees.d.ts.map +1 -1
  244. package/dist/web/server/services/worktrees.js +7 -1
  245. package/dist/web/server/services/worktrees.js.map +1 -1
  246. package/dist/web/server/tray.d.ts +24 -0
  247. package/dist/web/server/tray.d.ts.map +1 -0
  248. package/dist/web/server/tray.js +79 -0
  249. package/dist/web/server/tray.js.map +1 -0
  250. package/dist/web/server/types.d.ts +4 -0
  251. package/dist/web/server/types.d.ts.map +1 -0
  252. package/dist/web/server/types.js +2 -0
  253. package/dist/web/server/types.js.map +1 -0
  254. package/dist/web/server/websocket/handler.d.ts +18 -2
  255. package/dist/web/server/websocket/handler.d.ts.map +1 -1
  256. package/dist/web/server/websocket/handler.js +82 -9
  257. package/dist/web/server/websocket/handler.js.map +1 -1
  258. package/dist/worktree.d.ts +1 -0
  259. package/dist/worktree.d.ts.map +1 -1
  260. package/dist/worktree.js.map +1 -1
  261. package/package.json +17 -3
  262. package/src/claude.ts +26 -15
  263. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +13 -13
  264. package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
  265. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +97 -33
  266. package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +4 -4
  267. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +7 -3
  268. package/src/cli/ui/components/App.tsx +111 -125
  269. package/src/cli/ui/components/common/Select.tsx +2 -2
  270. package/src/cli/ui/components/screens/BranchListScreen.tsx +220 -34
  271. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +41 -46
  272. package/src/cli/ui/hooks/useGitData.ts +20 -0
  273. package/src/cli/ui/types.ts +3 -0
  274. package/src/cli/ui/utils/branchFormatter.ts +7 -2
  275. package/src/cli/ui/utils/continueSession.ts +1 -7
  276. package/src/cli/ui/utils/modelOptions.test.ts +14 -0
  277. package/src/cli/ui/utils/modelOptions.ts +7 -0
  278. package/src/codex.ts +31 -22
  279. package/src/config/builtin-tools.ts +6 -2
  280. package/src/config/shared-env.ts +139 -0
  281. package/src/gemini.ts +35 -22
  282. package/src/index.ts +61 -7
  283. package/src/logging/logger.ts +82 -0
  284. package/src/logging/rotation.ts +25 -0
  285. package/src/qwen.ts +28 -19
  286. package/src/services/aiToolResolver.ts +276 -0
  287. package/src/services/customToolResolver.ts +98 -0
  288. package/src/shared/aiToolConstants.ts +30 -0
  289. package/src/trayicon.d.ts +30 -0
  290. package/src/types/tools.ts +15 -0
  291. package/src/utils/prompt.ts +15 -9
  292. package/src/utils/session.ts +80 -26
  293. package/src/utils/terminal.ts +11 -41
  294. package/src/utils/webui.ts +43 -0
  295. package/src/web/client/components.json +21 -0
  296. package/src/web/client/src/components/AIToolLaunchModal.tsx +575 -0
  297. package/src/web/client/src/components/BranchGraph.tsx +95 -75
  298. package/src/web/client/src/components/CustomToolForm.tsx +386 -0
  299. package/src/web/client/src/components/CustomToolList.tsx +119 -0
  300. package/src/web/client/src/components/EnvEditor.tsx +91 -81
  301. package/src/web/client/src/components/EnvironmentEditor.tsx +97 -0
  302. package/src/web/client/src/components/Terminal.tsx +11 -3
  303. package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +179 -0
  304. package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +181 -0
  305. package/src/web/client/src/components/branch-detail/TerminalPanel.tsx +92 -0
  306. package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +327 -0
  307. package/src/web/client/src/components/branch-detail/index.ts +4 -0
  308. package/src/web/client/src/components/common/BranchCard.tsx +117 -0
  309. package/src/web/client/src/components/common/MetricCard.tsx +22 -0
  310. package/src/web/client/src/components/common/PageHeader.tsx +44 -0
  311. package/src/web/client/src/components/common/SearchInput.tsx +40 -0
  312. package/src/web/client/src/components/common/StatusBadge.tsx +37 -0
  313. package/src/web/client/src/components/common/index.ts +5 -0
  314. package/src/web/client/src/components/ui/alert.tsx +63 -0
  315. package/src/web/client/src/components/ui/badge.tsx +44 -0
  316. package/src/web/client/src/components/ui/button.tsx +57 -0
  317. package/src/web/client/src/components/ui/card.tsx +82 -0
  318. package/src/web/client/src/components/ui/index.ts +32 -0
  319. package/src/web/client/src/components/ui/input.tsx +21 -0
  320. package/src/web/client/src/components/ui/select.tsx +156 -0
  321. package/src/web/client/src/components/ui/table.tsx +119 -0
  322. package/src/web/client/src/hooks/useSessions.ts +10 -1
  323. package/src/web/client/src/index.css +46 -816
  324. package/src/web/client/src/lib/utils.ts +10 -0
  325. package/src/web/client/src/lib/websocket.ts +48 -1
  326. package/src/web/client/src/pages/BranchDetailPage.tsx +222 -694
  327. package/src/web/client/src/pages/BranchListPage.tsx +190 -236
  328. package/src/web/client/src/pages/ConfigManagementPage.tsx +94 -76
  329. package/src/web/client/src/pages/ConfigPage.tsx +362 -0
  330. package/src/web/client/vite.config.ts +8 -1
  331. package/src/web/server/index.ts +78 -19
  332. package/src/web/server/pty/manager.ts +128 -55
  333. package/src/web/server/routes/branches.ts +2 -2
  334. package/src/web/server/routes/config.ts +2 -2
  335. package/src/web/server/routes/index.ts +2 -2
  336. package/src/web/server/routes/sessions.ts +61 -9
  337. package/src/web/server/routes/worktrees.ts +5 -5
  338. package/src/web/server/services/worktrees.ts +12 -4
  339. package/src/web/server/tray.ts +93 -0
  340. package/src/web/server/types.ts +14 -0
  341. package/src/web/server/websocket/handler.ts +119 -13
  342. package/src/worktree.ts +1 -0
  343. package/dist/client/assets/index-DeNwPosA.css +0 -1
  344. package/dist/client/assets/index-Dl798X5w.js +0 -32
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useState, useMemo } from "react";
1
+ import React, { useCallback, useState, useMemo, useEffect } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import { Header } from "../parts/Header.js";
4
4
  import { Stats } from "../parts/Stats.js";
@@ -9,8 +9,12 @@ import { LoadingIndicator } from "../common/LoadingIndicator.js";
9
9
  import { useTerminalSize } from "../../hooks/useTerminalSize.js";
10
10
  import type { BranchItem, Statistics } from "../../types.js";
11
11
  import stringWidth from "string-width";
12
+ import stripAnsi from "strip-ansi";
12
13
  import chalk from "chalk";
14
+ import { resolveWebUiPort } from "../../../../utils/webui.js";
13
15
 
16
+ // Emoji 幅は端末によって 1 または 2 になることがあるため、最小幅を上書きして
17
+ // 実測より小さくならないようにする(過小評価=折り返しの原因を防ぐ)
14
18
  const WIDTH_OVERRIDES: Record<string, number> = {
15
19
  // Remote icon
16
20
  "☁": 1,
@@ -22,19 +26,24 @@ const WIDTH_OVERRIDES: Record<string, number> = {
22
26
  "🚀": 1,
23
27
  "📌": 1,
24
28
  // Worktree status icons
25
- "🟢": 1,
29
+ "🟢": 2,
30
+ "⚪": 2,
26
31
  "🟠": 1,
27
32
  // Change status icons
28
33
  "👉": 1,
29
34
  "💾": 1,
30
35
  "📤": 1,
31
36
  "🔃": 1,
32
- "✅": 1,
33
- "⚠️": 1,
37
+ "✅": 2,
38
+ "": 2,
39
+ "⚠️": 2,
40
+ "🛡": 2,
34
41
  // Remote markers
35
- "🔗": 1,
36
- "💻": 1,
37
- "☁️": 1,
42
+ "🔗": 2,
43
+ "💻": 2,
44
+ "☁️": 2,
45
+ "☑": 2,
46
+ "☐": 2,
38
47
  };
39
48
 
40
49
  const getCharWidth = (char: string): number => {
@@ -88,6 +97,8 @@ export interface BranchListScreenProps {
88
97
  testOnFilterModeChange?: (mode: boolean) => void;
89
98
  testFilterQuery?: string;
90
99
  testOnFilterQueryChange?: (query: string) => void;
100
+ selectedBranches?: string[];
101
+ onToggleSelect?: (branchName: string) => void;
91
102
  }
92
103
 
93
104
  /**
@@ -111,11 +122,15 @@ export function BranchListScreen({
111
122
  testOnFilterModeChange,
112
123
  testFilterQuery,
113
124
  testOnFilterQueryChange,
125
+ selectedBranches = [],
126
+ onToggleSelect,
114
127
  }: BranchListScreenProps) {
115
128
  const { rows } = useTerminalSize();
116
- const COLUMN_WIDTH = 2;
117
- const SYNC_COLUMN_WIDTH = 6;
118
- const headerText = ` ${"Ty".padEnd(COLUMN_WIDTH)}${"Wt".padEnd(COLUMN_WIDTH)}${"St".padEnd(COLUMN_WIDTH)}${"Rm".padEnd(COLUMN_WIDTH)}${"Sync".padEnd(SYNC_COLUMN_WIDTH)}Branch`;
129
+ const headerText = " Legend: [ ]/[ * ] select 🟢/⚪ worktree 🛡/⚠ safe";
130
+ const selectedSet = useMemo(
131
+ () => new Set(selectedBranches),
132
+ [selectedBranches],
133
+ );
119
134
 
120
135
  // Filter state - allow test control via props
121
136
  const [internalFilterQuery, setInternalFilterQuery] = useState("");
@@ -142,6 +157,9 @@ export function BranchListScreen({
142
157
  [testOnFilterModeChange],
143
158
  );
144
159
 
160
+ // Cursor position for Select (controlled to enable space toggle)
161
+ const [selectedIndex, setSelectedIndex] = useState(0);
162
+
145
163
  // Handle keyboard input
146
164
  // Note: Input component blocks specific keys (c/r/f) using blockKeys prop
147
165
  // This prevents shortcuts from triggering while typing in the filter
@@ -170,6 +188,15 @@ export function BranchListScreen({
170
188
  return;
171
189
  }
172
190
 
191
+ // Toggle selection with space (only in branch selection mode)
192
+ if (input === " " && !filterMode) {
193
+ const target = filteredBranches[selectedIndex];
194
+ if (target) {
195
+ onToggleSelect?.(target.name);
196
+ }
197
+ return;
198
+ }
199
+
173
200
  // Disable global shortcuts while in filter mode
174
201
  if (filterMode) {
175
202
  return;
@@ -205,20 +232,36 @@ export function BranchListScreen({
205
232
  });
206
233
  }, [branches, filterQuery]);
207
234
 
235
+ useEffect(() => {
236
+ setSelectedIndex((prev) => {
237
+ if (filteredBranches.length === 0) {
238
+ return 0;
239
+ }
240
+ return Math.min(prev, filteredBranches.length - 1);
241
+ });
242
+ }, [filteredBranches.length]);
243
+
208
244
  // Calculate available space for branch list
209
245
  // Header: 2 lines (title + divider)
210
246
  // Filter input: 1 line
211
247
  // Stats: 1 line
212
248
  // Empty line: 1 line
249
+ // Web UI URL: 1 line
213
250
  // Footer: 1 line
214
- // Total fixed: 6 lines
251
+ // Total fixed: 7 lines
215
252
  const headerLines = 2;
216
253
  const filterLines = 1;
217
254
  const statsLines = 1;
218
255
  const emptyLine = 1;
256
+ const webUiLines = 1;
219
257
  const footerLines = 1;
220
258
  const fixedLines =
221
- headerLines + filterLines + statsLines + emptyLine + footerLines;
259
+ headerLines +
260
+ filterLines +
261
+ statsLines +
262
+ emptyLine +
263
+ webUiLines +
264
+ footerLines;
222
265
  const contentHeight = rows - fixedLines;
223
266
  const limit = Math.max(5, contentHeight); // Minimum 5 items visible
224
267
 
@@ -275,18 +318,70 @@ export function BranchListScreen({
275
318
  return result + ellipsis;
276
319
  }, []);
277
320
 
321
+ const colorToolLabel = useCallback(
322
+ (label: string, toolId?: string | null) => {
323
+ switch (toolId) {
324
+ case "claude-code":
325
+ return chalk.hex("#ffaf00")(label); // orange-ish
326
+ case "codex-cli":
327
+ return chalk.cyan(label);
328
+ case "gemini-cli":
329
+ return chalk.magenta(label);
330
+ case "qwen-cli":
331
+ return chalk.green(label);
332
+ default: {
333
+ const trimmed = label.trim().toLowerCase();
334
+ if (!toolId || trimmed === "unknown") {
335
+ return chalk.gray(label);
336
+ }
337
+ return chalk.white(label);
338
+ }
339
+ }
340
+ },
341
+ [],
342
+ );
343
+
278
344
  const renderBranchRow = useCallback(
279
345
  (item: BranchItem, isSelected: boolean, context: { columns: number }) => {
280
- // Use a small safety margin to avoid terminal-dependent wrapping
346
+ // 端末幅ピッタリでの自動折返しを避けるため、1桁だけ余白を取る
281
347
  const columns = Math.max(20, context.columns - 1);
348
+ const visibleWidth = (value: string) =>
349
+ measureDisplayWidth(stripAnsi(value));
282
350
  const arrow = isSelected ? ">" : " ";
283
- const commitText = formatLatestCommit(item.latestCommitTimestamp);
284
- const infoText =
285
- item.lastToolUsage && item.lastToolUsageLabel
286
- ? item.lastToolUsageLabel
287
- : `${chalk.gray("Unknown")}${commitText !== "---" ? ` | ${commitText}` : ""}`;
288
- const timestampText = infoText;
289
- const timestampWidth = stringWidth(timestampText);
351
+ let commitText = "---";
352
+ if (item.latestCommitTimestamp) {
353
+ commitText = formatLatestCommit(item.latestCommitTimestamp);
354
+ } else if (item.lastToolUsage?.timestamp) {
355
+ const seconds = Math.floor(item.lastToolUsage.timestamp / 1000);
356
+ commitText = formatLatestCommit(seconds);
357
+ }
358
+ const toolLabelRaw =
359
+ item.lastToolUsageLabel?.split("|")?.[0]?.trim() ??
360
+ item.lastToolUsage?.toolId ??
361
+ "Unknown";
362
+
363
+ const formatFixedWidth = (value: string, targetWidth: number) => {
364
+ let v = value;
365
+ if (measureDisplayWidth(v) > targetWidth) {
366
+ v = truncateToWidth(v, targetWidth);
367
+ }
368
+ const padding = Math.max(0, targetWidth - measureDisplayWidth(v));
369
+ return v + " ".repeat(padding);
370
+ };
371
+
372
+ const TOOL_WIDTH = 7;
373
+ const DATE_WIDTH = 16; // "YYYY-MM-DD HH:mm"
374
+ const paddedTool = formatFixedWidth(toolLabelRaw, TOOL_WIDTH);
375
+ const paddedDate =
376
+ commitText === "---"
377
+ ? " ".repeat(DATE_WIDTH)
378
+ : commitText.padStart(DATE_WIDTH, " ");
379
+ const timestampText = `${paddedTool} | ${paddedDate}`;
380
+ const displayTimestampText = `${colorToolLabel(
381
+ paddedTool,
382
+ item.lastToolUsage?.toolId,
383
+ )} | ${paddedDate}`;
384
+ const timestampWidth = measureDisplayWidth(timestampText);
290
385
 
291
386
  const indicatorInfo = cleanupUI?.indicators?.[item.name];
292
387
  let indicatorIcon = indicatorInfo?.icon ?? "";
@@ -309,30 +404,114 @@ export function BranchListScreen({
309
404
  }
310
405
  }
311
406
  const indicatorPrefix = indicatorIcon ? `${indicatorIcon} ` : "";
312
- const staticPrefix = `${arrow} ${indicatorPrefix}`;
313
- const staticPrefixWidth = measureDisplayWidth(staticPrefix);
407
+
408
+ const isChecked = selectedSet.has(item.name);
409
+ const selectionIcon = isChecked ? "[*]" : "[ ]";
410
+ const hasWorktree =
411
+ item.worktreeStatus === "active" ||
412
+ item.worktreeStatus === "inaccessible";
413
+ const worktreeIcon = hasWorktree ? chalk.green("🟢") : chalk.gray("⚪");
414
+ const safeIcon =
415
+ item.safeToCleanup === true ? chalk.green("🛡") : chalk.yellow("⚠");
416
+ const stateCluster = `${selectionIcon} ${worktreeIcon} ${safeIcon}`;
417
+
418
+ const staticPrefix = `${arrow} ${indicatorPrefix}${stateCluster} `;
419
+ const staticPrefixWidth = visibleWidth(staticPrefix);
314
420
  const maxLeftDisplayWidth = Math.max(0, columns - timestampWidth - 1);
315
421
  const maxLabelWidth = Math.max(
316
422
  0,
317
423
  maxLeftDisplayWidth - staticPrefixWidth,
318
424
  );
319
- const truncatedLabel = truncateToWidth(item.label, maxLabelWidth);
320
- const leftText = `${staticPrefix}${truncatedLabel}`;
425
+ const displayLabel =
426
+ item.type === "remote" && item.remoteName ? item.remoteName : item.name;
427
+ let truncatedLabel = truncateToWidth(displayLabel, maxLabelWidth);
428
+ let leftText = `${staticPrefix}${truncatedLabel}`;
429
+
430
+ let leftDisplayWidth = visibleWidth(leftText);
431
+ // Gap between labelとツール/日時。右端に寄せるため必要分だけ確保。
432
+ let gapWidth = Math.max(1, columns - leftDisplayWidth - timestampWidth);
433
+
434
+ // もしまだオーバーする場合、隙間→ラベルの順で削って収める
435
+ let totalWidth = leftDisplayWidth + gapWidth + timestampWidth;
436
+ if (totalWidth > columns) {
437
+ const overflow = totalWidth - columns;
438
+ const reducedGap = Math.max(1, gapWidth - overflow);
439
+ gapWidth = reducedGap;
440
+ totalWidth = leftDisplayWidth + gapWidth + timestampWidth;
441
+ }
442
+ if (leftDisplayWidth + gapWidth + timestampWidth > columns) {
443
+ const extra = leftDisplayWidth + gapWidth + timestampWidth - columns;
444
+ const newLabelWidth = Math.max(
445
+ 0,
446
+ measureDisplayWidth(truncatedLabel) - extra,
447
+ );
448
+ truncatedLabel = truncateToWidth(displayLabel, newLabelWidth);
449
+ leftText = `${staticPrefix}${truncatedLabel}`;
450
+ leftDisplayWidth = visibleWidth(leftText);
451
+ gapWidth = Math.max(1, columns - leftDisplayWidth - timestampWidth);
452
+ }
321
453
 
322
- const leftDisplayWidth = measureDisplayWidth(leftText);
323
- const gapWidth = Math.max(1, columns - leftDisplayWidth - timestampWidth);
454
+ const buildLine = () =>
455
+ `${leftText}${" ".repeat(gapWidth)}${timestampText}`;
324
456
 
325
- let line = `${leftText}${" ".repeat(gapWidth)}${timestampText}`;
326
- const totalDisplayWidth = leftDisplayWidth + gapWidth + timestampWidth;
327
- const paddingWidth = Math.max(0, columns - totalDisplayWidth);
328
- if (paddingWidth > 0) {
329
- line += " ".repeat(paddingWidth);
330
- }
457
+ let line = buildLine();
458
+ // Replace timestamp with colorized tool name (keep alignment from width calc)
459
+ let lineWithColoredTimestamp = line.replace(
460
+ timestampText,
461
+ displayTimestampText,
462
+ );
331
463
 
332
- const output = isSelected ? `\u001b[46m\u001b[30m${line}\u001b[0m` : line;
464
+ // 端末幅を超えた場合は隙間→ラベルの順で詰めて収める
465
+ const clampToWidth = () => {
466
+ const finalWidth = measureDisplayWidth(
467
+ stripAnsi(lineWithColoredTimestamp),
468
+ );
469
+ if (finalWidth <= columns) {
470
+ return;
471
+ }
472
+ const overflow = finalWidth - columns;
473
+ const reducedGap = Math.max(1, gapWidth - overflow);
474
+ gapWidth = reducedGap;
475
+ line = buildLine();
476
+ lineWithColoredTimestamp = line.replace(
477
+ timestampText,
478
+ displayTimestampText,
479
+ );
480
+ const widthAfterGap = measureDisplayWidth(
481
+ stripAnsi(lineWithColoredTimestamp),
482
+ );
483
+ if (widthAfterGap > columns) {
484
+ const extra = widthAfterGap - columns;
485
+ const newLabelWidth = Math.max(
486
+ 0,
487
+ measureDisplayWidth(truncatedLabel) - extra,
488
+ );
489
+ truncatedLabel = truncateToWidth(displayLabel, newLabelWidth);
490
+ leftText = `${staticPrefix}${truncatedLabel}`;
491
+ leftDisplayWidth = visibleWidth(leftText);
492
+ gapWidth = Math.max(1, columns - leftDisplayWidth - timestampWidth);
493
+ line = buildLine();
494
+ lineWithColoredTimestamp = line.replace(
495
+ timestampText,
496
+ displayTimestampText,
497
+ );
498
+ }
499
+ };
500
+
501
+ clampToWidth();
502
+
503
+ const output = isSelected
504
+ ? `\u001b[46m\u001b[30m${lineWithColoredTimestamp}\u001b[0m`
505
+ : lineWithColoredTimestamp;
333
506
  return <Text>{output}</Text>;
334
507
  },
335
- [cleanupUI, formatLatestCommit, truncateToWidth],
508
+ [
509
+ cleanupUI,
510
+ formatLatestCommit,
511
+ truncateToWidth,
512
+ selectedSet,
513
+ colorToolLabel,
514
+ ],
336
515
  );
337
516
 
338
517
  return (
@@ -425,6 +604,8 @@ export function BranchListScreen({
425
604
  disabled={Boolean(cleanupUI?.inputLocked)}
426
605
  renderIndicator={() => null}
427
606
  renderItem={renderBranchRow}
607
+ selectedIndex={selectedIndex}
608
+ onSelectedIndexChange={setSelectedIndex}
428
609
  />
429
610
  </>
430
611
  )}
@@ -442,6 +623,11 @@ export function BranchListScreen({
442
623
  </Box>
443
624
  )}
444
625
 
626
+ {/* Web UI URL */}
627
+ <Box>
628
+ <Text dimColor>Web UI: http://localhost:{resolveWebUiPort()}</Text>
629
+ </Box>
630
+
445
631
  {/* Footer */}
446
632
  <Footer actions={footerActions} />
447
633
  </Box>
@@ -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
  )}
@@ -12,6 +12,7 @@ import { getPullRequestByBranch } from "../../../github.js";
12
12
  import type { BranchInfo, WorktreeInfo } from "../types.js";
13
13
  import type { WorktreeInfo as GitWorktreeInfo } from "../../../worktree.js";
14
14
  import { getLastToolUsageMap } from "../../../config/index.js";
15
+ import { hasUncommittedChanges } from "../../../git.js";
15
16
 
16
17
  export interface UseGitDataOptions {
17
18
  enableAutoRefresh?: boolean;
@@ -65,6 +66,22 @@ export function useGitData(options?: UseGitDataOptions): UseGitDataResult {
65
66
  }
66
67
  worktreesData = [];
67
68
  }
69
+
70
+ // enrich worktrees with uncommitted status (only for accessible paths)
71
+ worktreesData = await Promise.all(
72
+ worktreesData.map(async (wt) => {
73
+ if (wt.isAccessible === false) {
74
+ return wt;
75
+ }
76
+ try {
77
+ const hasUncommitted = await hasUncommittedChanges(wt.path);
78
+ return { ...wt, hasUncommittedChanges: hasUncommitted };
79
+ } catch {
80
+ return wt;
81
+ }
82
+ }),
83
+ );
84
+
68
85
  const lastToolUsageMap = await getLastToolUsageMap(repoRoot);
69
86
 
70
87
  // upstream情報とdivergence情報を取得
@@ -109,6 +126,9 @@ export function useGitData(options?: UseGitDataOptions): UseGitDataResult {
109
126
  locked: false, // worktree.ts doesn't expose locked status
110
127
  prunable: worktree.isAccessible === false,
111
128
  isAccessible: worktree.isAccessible ?? true, // Default to true if undefined
129
+ ...(worktree.hasUncommittedChanges !== undefined
130
+ ? { hasUncommittedChanges: worktree.hasUncommittedChanges }
131
+ : {}),
112
132
  };
113
133
  worktreeMap.set(worktree.branch, uiWorktreeInfo);
114
134
  }
@@ -6,6 +6,7 @@ export interface WorktreeInfo {
6
6
  locked: boolean;
7
7
  prunable: boolean;
8
8
  isAccessible?: boolean;
9
+ hasUncommittedChanges?: boolean;
9
10
  }
10
11
 
11
12
  export type AITool = string;
@@ -229,6 +230,8 @@ export interface BranchItem extends BranchInfo {
229
230
  syncStatus?: SyncStatus;
230
231
  syncInfo?: string | undefined;
231
232
  remoteName?: string | undefined;
233
+ // クリーンアップ判定で「未コミット/未プッシュなし」と評価された場合に true
234
+ safeToCleanup?: boolean;
232
235
  }
233
236
 
234
237
  /**
@@ -65,7 +65,8 @@ const iconWidthOverrides: Record<string, number> = {
65
65
  "🚀": 1,
66
66
  "📌": 1,
67
67
  // Worktree status icons
68
- "🟢": 1,
68
+ "🟢": 2,
69
+ "⚪": 2,
69
70
  "🟠": 1,
70
71
  // Change status icons
71
72
  "👉": 1,
@@ -73,7 +74,11 @@ const iconWidthOverrides: Record<string, number> = {
73
74
  "📤": 1,
74
75
  "🔃": 1,
75
76
  "✅": 1,
76
- "⚠️": 1,
77
+ "⚠️": 2,
78
+ "⚠": 1,
79
+ "🛡": 2,
80
+ "☑": 2,
81
+ "☐": 2,
77
82
  // Remote markers
78
83
  "🔗": 1,
79
84
  "💻": 1,
@@ -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) {
@@ -23,6 +23,7 @@ describe("modelOptions", () => {
23
23
  expect(unique.size).toBe(ids.length);
24
24
  expect(ids).toEqual([
25
25
  "gpt-5.1-codex",
26
+ "gpt-5.2",
26
27
  "gpt-5.1-codex-max",
27
28
  "gpt-5.1-codex-mini",
28
29
  "gpt-5.1",
@@ -36,6 +37,19 @@ describe("modelOptions", () => {
36
37
  expect(getDefaultInferenceForModel(codexMax)).toBe("medium");
37
38
  });
38
39
 
40
+ it("exposes gpt-5.2 with xhigh reasoning and medium default", () => {
41
+ const codex52 = getModelOptions("codex-cli").find(
42
+ (m) => m.id === "gpt-5.2",
43
+ );
44
+ expect(codex52?.inferenceLevels).toEqual([
45
+ "xhigh",
46
+ "high",
47
+ "medium",
48
+ "low",
49
+ ]);
50
+ expect(getDefaultInferenceForModel(codex52)).toBe("medium");
51
+ });
52
+
39
53
  it("lists expected Gemini models", () => {
40
54
  expect(byId("gemini-cli")).toEqual([
41
55
  "gemini-3-pro-preview",
@@ -33,6 +33,13 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
33
33
  defaultInference: "high",
34
34
  isDefault: true,
35
35
  },
36
+ {
37
+ id: "gpt-5.2",
38
+ label: "gpt-5.2",
39
+ description: "Latest frontier model with extra high reasoning",
40
+ inferenceLevels: CODEX_MAX_LEVELS,
41
+ defaultInference: "medium",
42
+ },
36
43
  {
37
44
  id: "gpt-5.1-codex-max",
38
45
  label: "gpt-5.1-codex-max",