@akiojin/gwt 4.11.6 → 4.12.1

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 (199) hide show
  1. package/bin/gwt.js +36 -10
  2. package/dist/claude.d.ts +1 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +81 -24
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/App.solid.d.ts.map +1 -1
  7. package/dist/cli/ui/App.solid.js +280 -50
  8. package/dist/cli/ui/App.solid.js.map +1 -1
  9. package/dist/cli/ui/components/solid/QuickStartStep.d.ts.map +1 -1
  10. package/dist/cli/ui/components/solid/QuickStartStep.js +35 -22
  11. package/dist/cli/ui/components/solid/QuickStartStep.js.map +1 -1
  12. package/dist/cli/ui/components/solid/SelectInput.d.ts.map +1 -1
  13. package/dist/cli/ui/components/solid/SelectInput.js +2 -1
  14. package/dist/cli/ui/components/solid/SelectInput.js.map +1 -1
  15. package/dist/cli/ui/components/solid/WizardController.d.ts.map +1 -1
  16. package/dist/cli/ui/components/solid/WizardController.js +67 -13
  17. package/dist/cli/ui/components/solid/WizardController.js.map +1 -1
  18. package/dist/cli/ui/components/solid/WizardSteps.d.ts +5 -0
  19. package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -1
  20. package/dist/cli/ui/components/solid/WizardSteps.js +50 -70
  21. package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -1
  22. package/dist/cli/ui/core/theme.d.ts +9 -0
  23. package/dist/cli/ui/core/theme.d.ts.map +1 -1
  24. package/dist/cli/ui/core/theme.js +21 -0
  25. package/dist/cli/ui/core/theme.js.map +1 -1
  26. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts +9 -2
  27. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -1
  28. package/dist/cli/ui/screens/solid/BranchListScreen.js +101 -28
  29. package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -1
  30. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +2 -1
  31. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -1
  32. package/dist/cli/ui/screens/solid/ConfirmScreen.js +11 -3
  33. package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -1
  34. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -1
  35. package/dist/cli/ui/screens/solid/EnvironmentScreen.js +9 -10
  36. package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -1
  37. package/dist/cli/ui/screens/solid/LogScreen.d.ts +7 -1
  38. package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -1
  39. package/dist/cli/ui/screens/solid/LogScreen.js +254 -16
  40. package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -1
  41. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -1
  42. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +8 -5
  43. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -1
  44. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -1
  45. package/dist/cli/ui/screens/solid/SelectorScreen.js +12 -4
  46. package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -1
  47. package/dist/cli/ui/types.d.ts +1 -0
  48. package/dist/cli/ui/types.d.ts.map +1 -1
  49. package/dist/cli/ui/utils/branchFormatter.d.ts +1 -0
  50. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  51. package/dist/cli/ui/utils/branchFormatter.js +29 -7
  52. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  53. package/dist/cli/ui/utils/continueSession.d.ts +14 -0
  54. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  55. package/dist/cli/ui/utils/continueSession.js +61 -3
  56. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  57. package/dist/cli/ui/utils/installedVersionCache.d.ts +33 -0
  58. package/dist/cli/ui/utils/installedVersionCache.d.ts.map +1 -0
  59. package/dist/cli/ui/utils/installedVersionCache.js +59 -0
  60. package/dist/cli/ui/utils/installedVersionCache.js.map +1 -0
  61. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  62. package/dist/cli/ui/utils/modelOptions.js +16 -0
  63. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  64. package/dist/cli/ui/utils/versionCache.d.ts +37 -0
  65. package/dist/cli/ui/utils/versionCache.d.ts.map +1 -0
  66. package/dist/cli/ui/utils/versionCache.js +70 -0
  67. package/dist/cli/ui/utils/versionCache.js.map +1 -0
  68. package/dist/cli/ui/utils/versionFetcher.d.ts +41 -0
  69. package/dist/cli/ui/utils/versionFetcher.d.ts.map +1 -0
  70. package/dist/cli/ui/utils/versionFetcher.js +89 -0
  71. package/dist/cli/ui/utils/versionFetcher.js.map +1 -0
  72. package/dist/codex.d.ts +1 -0
  73. package/dist/codex.d.ts.map +1 -1
  74. package/dist/codex.js +95 -25
  75. package/dist/codex.js.map +1 -1
  76. package/dist/config/index.d.ts.map +1 -1
  77. package/dist/config/index.js +10 -1
  78. package/dist/config/index.js.map +1 -1
  79. package/dist/gemini.d.ts +1 -0
  80. package/dist/gemini.d.ts.map +1 -1
  81. package/dist/gemini.js +36 -3
  82. package/dist/gemini.js.map +1 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +35 -2
  85. package/dist/index.js.map +1 -1
  86. package/dist/launcher.d.ts.map +1 -1
  87. package/dist/launcher.js +43 -8
  88. package/dist/launcher.js.map +1 -1
  89. package/dist/logging/agentOutput.d.ts +21 -0
  90. package/dist/logging/agentOutput.d.ts.map +1 -0
  91. package/dist/logging/agentOutput.js +164 -0
  92. package/dist/logging/agentOutput.js.map +1 -0
  93. package/dist/logging/formatter.d.ts.map +1 -1
  94. package/dist/logging/formatter.js +18 -4
  95. package/dist/logging/formatter.js.map +1 -1
  96. package/dist/logging/logger.d.ts.map +1 -1
  97. package/dist/logging/logger.js +2 -0
  98. package/dist/logging/logger.js.map +1 -1
  99. package/dist/logging/reader.d.ts +22 -0
  100. package/dist/logging/reader.d.ts.map +1 -1
  101. package/dist/logging/reader.js +116 -1
  102. package/dist/logging/reader.js.map +1 -1
  103. package/dist/opentui/index.solid.js +2575 -888
  104. package/dist/services/codingAgentResolver.d.ts.map +1 -1
  105. package/dist/services/codingAgentResolver.js +8 -6
  106. package/dist/services/codingAgentResolver.js.map +1 -1
  107. package/dist/services/dependency-installer.js +2 -2
  108. package/dist/services/dependency-installer.js.map +1 -1
  109. package/dist/shared/codingAgentConstants.d.ts +3 -0
  110. package/dist/shared/codingAgentConstants.d.ts.map +1 -1
  111. package/dist/shared/codingAgentConstants.js +66 -0
  112. package/dist/shared/codingAgentConstants.js.map +1 -1
  113. package/dist/utils/bun-runtime.d.ts +12 -0
  114. package/dist/utils/bun-runtime.d.ts.map +1 -0
  115. package/dist/utils/bun-runtime.js +13 -0
  116. package/dist/utils/bun-runtime.js.map +1 -0
  117. package/dist/utils/session/common.d.ts +8 -0
  118. package/dist/utils/session/common.d.ts.map +1 -1
  119. package/dist/utils/session/common.js +22 -0
  120. package/dist/utils/session/common.js.map +1 -1
  121. package/dist/utils/session/parsers/claude.d.ts +10 -4
  122. package/dist/utils/session/parsers/claude.d.ts.map +1 -1
  123. package/dist/utils/session/parsers/claude.js +64 -18
  124. package/dist/utils/session/parsers/claude.js.map +1 -1
  125. package/dist/utils/session/parsers/codex.d.ts.map +1 -1
  126. package/dist/utils/session/parsers/codex.js +47 -28
  127. package/dist/utils/session/parsers/codex.js.map +1 -1
  128. package/dist/utils/session/parsers/gemini.d.ts.map +1 -1
  129. package/dist/utils/session/parsers/gemini.js +43 -6
  130. package/dist/utils/session/parsers/gemini.js.map +1 -1
  131. package/dist/utils/session/parsers/opencode.d.ts.map +1 -1
  132. package/dist/utils/session/parsers/opencode.js +43 -6
  133. package/dist/utils/session/parsers/opencode.js.map +1 -1
  134. package/dist/utils/session/types.d.ts +7 -0
  135. package/dist/utils/session/types.d.ts.map +1 -1
  136. package/dist/web/client/src/components/ui/alert.d.ts +1 -1
  137. package/dist/worktree.d.ts +4 -1
  138. package/dist/worktree.d.ts.map +1 -1
  139. package/dist/worktree.js +21 -15
  140. package/dist/worktree.js.map +1 -1
  141. package/package.json +2 -1
  142. package/src/claude.ts +99 -28
  143. package/src/cli/ui/App.solid.tsx +373 -51
  144. package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +921 -1
  145. package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +105 -5
  146. package/src/cli/ui/__tests__/solid/ConfirmScreen.test.tsx +77 -0
  147. package/src/cli/ui/__tests__/solid/LogScreen.test.tsx +351 -0
  148. package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +73 -2
  149. package/src/cli/ui/__tests__/solid/components/WizardController.test.tsx +71 -0
  150. package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +95 -2
  151. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +72 -45
  152. package/src/cli/ui/components/solid/QuickStartStep.tsx +35 -23
  153. package/src/cli/ui/components/solid/SearchInput.tsx +1 -1
  154. package/src/cli/ui/components/solid/SelectInput.tsx +4 -0
  155. package/src/cli/ui/components/solid/WizardController.tsx +85 -12
  156. package/src/cli/ui/components/solid/WizardSteps.tsx +78 -90
  157. package/src/cli/ui/core/theme.ts +32 -0
  158. package/src/cli/ui/hooks/solid/useAsyncOperation.ts +8 -6
  159. package/src/cli/ui/hooks/solid/useGitOperations.ts +6 -5
  160. package/src/cli/ui/screens/solid/BranchListScreen.tsx +135 -32
  161. package/src/cli/ui/screens/solid/ConfirmScreen.tsx +20 -8
  162. package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +22 -20
  163. package/src/cli/ui/screens/solid/LogScreen.tsx +364 -35
  164. package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +19 -15
  165. package/src/cli/ui/screens/solid/SelectorScreen.tsx +25 -14
  166. package/src/cli/ui/screens/solid/SettingsScreen.tsx +5 -3
  167. package/src/cli/ui/types.ts +1 -0
  168. package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +53 -6
  169. package/src/cli/ui/utils/__tests__/installedVersionCache.test.ts +46 -0
  170. package/src/cli/ui/utils/branchFormatter.ts +35 -7
  171. package/src/cli/ui/utils/continueSession.ts +90 -3
  172. package/src/cli/ui/utils/installedVersionCache.ts +84 -0
  173. package/src/cli/ui/utils/modelOptions.test.ts +6 -0
  174. package/src/cli/ui/utils/modelOptions.ts +16 -0
  175. package/src/cli/ui/utils/versionCache.ts +93 -0
  176. package/src/cli/ui/utils/versionFetcher.ts +120 -0
  177. package/src/codex.ts +124 -26
  178. package/src/config/__tests__/saveSession.test.ts +2 -2
  179. package/src/config/index.ts +11 -1
  180. package/src/gemini.ts +50 -4
  181. package/src/index.test.ts +16 -10
  182. package/src/index.ts +41 -1
  183. package/src/launcher.ts +49 -8
  184. package/src/logging/agentOutput.ts +216 -0
  185. package/src/logging/formatter.ts +23 -4
  186. package/src/logging/logger.ts +2 -0
  187. package/src/logging/reader.ts +165 -1
  188. package/src/services/__tests__/BatchMergeService.test.ts +34 -14
  189. package/src/services/codingAgentResolver.ts +12 -5
  190. package/src/services/dependency-installer.ts +2 -2
  191. package/src/shared/codingAgentConstants.ts +73 -0
  192. package/src/utils/bun-runtime.ts +29 -0
  193. package/src/utils/session/common.ts +28 -0
  194. package/src/utils/session/parsers/claude.ts +79 -29
  195. package/src/utils/session/parsers/codex.ts +49 -26
  196. package/src/utils/session/parsers/gemini.ts +46 -5
  197. package/src/utils/session/parsers/opencode.ts +46 -5
  198. package/src/utils/session/types.ts +4 -0
  199. package/src/worktree.ts +28 -15
@@ -1,6 +1,7 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
  import { useKeyboard, useRenderer } from "@opentui/solid";
3
3
  import {
4
+ batch,
4
5
  createEffect,
5
6
  createMemo,
6
7
  createSignal,
@@ -35,6 +36,7 @@ import { LoadingIndicatorScreen } from "./screens/solid/LoadingIndicator.js";
35
36
  import { WorktreeCreateScreen } from "./screens/solid/WorktreeCreateScreen.js";
36
37
  import { InputScreen } from "./screens/solid/InputScreen.js";
37
38
  import { ConfirmScreen } from "./screens/solid/ConfirmScreen.js";
39
+ import { useTerminalSize } from "./hooks/solid/useTerminalSize.js";
38
40
  import { EnvironmentScreen } from "./screens/solid/EnvironmentScreen.js";
39
41
  import { ProfileScreen } from "./screens/solid/ProfileScreen.js";
40
42
  import { ProfileEnvScreen } from "./screens/solid/ProfileEnvScreen.js";
@@ -58,6 +60,7 @@ import {
58
60
  getLocalBranches,
59
61
  getRepositoryRoot,
60
62
  deleteBranch,
63
+ fetchAllRemotes,
61
64
  } from "../../git.js";
62
65
  import {
63
66
  isProtectedBranchName,
@@ -73,12 +76,18 @@ import {
73
76
  loadSession,
74
77
  type ToolSessionEntry,
75
78
  } from "../../config/index.js";
79
+ import {
80
+ findLatestBranchSessionsByTool,
81
+ refreshQuickStartEntries,
82
+ } from "./utils/continueSession.js";
76
83
  import { getAllCodingAgents } from "../../config/tools.js";
77
84
  import {
78
- buildLogFilePath,
85
+ clearLogFiles,
79
86
  getTodayLogDate,
80
- readLogFileLines,
81
- resolveLogDir,
87
+ readLogLinesForDate,
88
+ resolveLogTarget,
89
+ selectLogTargetByRecency,
90
+ type LogTargetResolution,
82
91
  } from "../../logging/reader.js";
83
92
  import { parseLogLines } from "../../logging/formatter.js";
84
93
  import { copyToClipboard } from "./utils/clipboard.js";
@@ -95,9 +104,15 @@ import {
95
104
  type ProfilesConfig,
96
105
  } from "../../types/profiles.js";
97
106
  import { BRANCH_PREFIXES } from "../../config/constants.js";
107
+ import { prefetchAgentVersions } from "./utils/versionCache.js";
108
+ import { prefetchInstalledVersions } from "./utils/installedVersionCache.js";
109
+ import { getBunxAgentIds } from "./utils/versionFetcher.js";
98
110
 
99
111
  export type ExecutionMode = "normal" | "continue" | "resume";
100
112
 
113
+ const UNSAFE_SELECTION_MESSAGE = "Unsafe branch selected. Select anyway?";
114
+ const SAFETY_PENDING_MESSAGE = "Safety check in progress. Select anyway?";
115
+
101
116
  export interface SelectionResult {
102
117
  branch: string;
103
118
  displayName: string;
@@ -192,6 +207,24 @@ const applyCleanupStatus = (
192
207
  return worktree ? { ...base, worktree } : base;
193
208
  });
194
209
 
210
+ const buildCleanupSafetyPending = (items: BranchItem[]): Set<string> => {
211
+ const pending = new Set<string>();
212
+ for (const branch of items) {
213
+ if (branch.type === "remote") {
214
+ continue;
215
+ }
216
+ if (branch.worktree) {
217
+ pending.add(branch.name);
218
+ continue;
219
+ }
220
+ if (isProtectedBranchName(branch.name)) {
221
+ continue;
222
+ }
223
+ pending.add(branch.name);
224
+ }
225
+ return pending;
226
+ };
227
+
195
228
  const toLocalBranchName = (name: string): string => {
196
229
  const segments = name.split("/");
197
230
  if (segments.length <= 1) {
@@ -208,12 +241,14 @@ const toSelectedBranchState = (branch: BranchItem): SelectedBranchState => {
208
241
  displayName: branch.name,
209
242
  branchType: branch.type,
210
243
  branchCategory: branch.branchType,
244
+ worktreePath: branch.worktree?.path ?? null,
211
245
  ...(isRemote ? { remoteBranch: branch.name } : {}),
212
246
  };
213
247
  };
214
248
 
215
249
  export function AppSolid(props: AppSolidProps) {
216
250
  const renderer = useRenderer();
251
+ const terminal = useTerminalSize();
217
252
  let hasExited = false;
218
253
 
219
254
  const exitApp = (result?: SelectionResult) => {
@@ -239,6 +274,9 @@ export function AppSolid(props: AppSolidProps) {
239
274
  const [loading, setLoading] = createSignal(!props.branches);
240
275
  const [error, setError] = createSignal<Error | null>(null);
241
276
 
277
+ // ブランチ一覧のカーソル位置(グローバル管理で再マウント時もリセットされない)
278
+ const [branchCursorPosition, setBranchCursorPosition] = createSignal(0);
279
+
242
280
  const [toolItems, setToolItems] = createSignal<SelectorItem[]>([]);
243
281
  const [toolError, setToolError] = createSignal<Error | null>(null);
244
282
 
@@ -257,6 +295,16 @@ export function AppSolid(props: AppSolidProps) {
257
295
  );
258
296
  const [selectedMode, setSelectedMode] = createSignal<ExecutionMode>("normal");
259
297
  const [selectedBranches, setSelectedBranches] = createSignal<string[]>([]);
298
+ const [unsafeSelectionConfirmVisible, setUnsafeSelectionConfirmVisible] =
299
+ createSignal(false);
300
+ const [unsafeConfirmInputLocked, setUnsafeConfirmInputLocked] =
301
+ createSignal(false);
302
+ const [unsafeSelectionTarget, setUnsafeSelectionTarget] = createSignal<
303
+ string | null
304
+ >(null);
305
+ const [unsafeSelectionMessage, setUnsafeSelectionMessage] = createSignal(
306
+ UNSAFE_SELECTION_MESSAGE,
307
+ );
260
308
  const [branchFooterMessage, setBranchFooterMessage] = createSignal<{
261
309
  text: string;
262
310
  isSpinning?: boolean;
@@ -270,6 +318,9 @@ export function AppSolid(props: AppSolidProps) {
270
318
  Map<string, CleanupStatus>
271
319
  >(new Map());
272
320
  const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
321
+ const [cleanupSafetyPending, setCleanupSafetyPending] = createSignal<
322
+ Set<string>
323
+ >(new Set());
273
324
  const [isNewBranch, setIsNewBranch] = createSignal(false);
274
325
  const [newBranchBaseRef, setNewBranchBaseRef] = createSignal<string | null>(
275
326
  null,
@@ -284,20 +335,68 @@ export function AppSolid(props: AppSolidProps) {
284
335
  );
285
336
  const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
286
337
 
338
+ const suppressBranchInputOnce = () => {
339
+ setUnsafeConfirmInputLocked(true);
340
+ queueMicrotask(() => {
341
+ setUnsafeConfirmInputLocked(false);
342
+ });
343
+ };
344
+
345
+ const unsafeConfirmBoxWidth = createMemo(() => {
346
+ const columns = terminal().columns || 80;
347
+ return Math.max(1, Math.floor(columns * 0.6));
348
+ });
349
+ const unsafeConfirmContentWidth = createMemo(() =>
350
+ Math.max(0, unsafeConfirmBoxWidth() - 4),
351
+ );
352
+
287
353
  // セッション履歴(最終使用エージェントなど)
288
354
  const [sessionHistory, setSessionHistory] = createSignal<ToolSessionEntry[]>(
289
355
  [],
290
356
  );
357
+ const [quickStartHistory, setQuickStartHistory] = createSignal<
358
+ ToolSessionEntry[]
359
+ >([]);
291
360
 
292
361
  // 選択中ブランチの履歴をフィルタリング
293
362
  const historyForBranch = createMemo(() => {
294
363
  const history = sessionHistory();
295
364
  const branch = selectedBranch();
296
365
  if (!branch) return [];
297
- // 選択中ブランチにマッチする履歴エントリを新しい順で返す
298
- return history
299
- .filter((entry) => entry.branch === branch.name)
300
- .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
366
+ return findLatestBranchSessionsByTool(
367
+ history,
368
+ branch.name,
369
+ branch.worktreePath ?? null,
370
+ );
371
+ });
372
+
373
+ createEffect(() => {
374
+ setQuickStartHistory(historyForBranch());
375
+ });
376
+
377
+ createEffect(() => {
378
+ const branch = selectedBranch();
379
+ const baseHistory = historyForBranch();
380
+ if (!wizardVisible() || !branch || baseHistory.length === 0) {
381
+ return;
382
+ }
383
+
384
+ const worktreePath = branch.worktreePath ?? null;
385
+ if (!worktreePath) {
386
+ return;
387
+ }
388
+
389
+ const branchName = branch.name;
390
+ void (async () => {
391
+ const refreshed = await refreshQuickStartEntries(baseHistory, {
392
+ branch: branchName,
393
+ worktreePath,
394
+ });
395
+ if (selectedBranch()?.name !== branchName) {
396
+ return;
397
+ }
398
+ setQuickStartHistory(refreshed);
399
+ })();
301
400
  });
302
401
 
303
402
  const [logEntries, setLogEntries] = createSignal<FormattedLogEntry[]>([]);
@@ -305,13 +404,17 @@ export function AppSolid(props: AppSolidProps) {
305
404
  const [logError, setLogError] = createSignal<string | null>(null);
306
405
  const [logSelectedEntry, setLogSelectedEntry] =
307
406
  createSignal<FormattedLogEntry | null>(null);
308
- const [logSelectedDate, _setLogSelectedDate] = createSignal<string | null>(
407
+ const [logSelectedDate, setLogSelectedDate] = createSignal<string | null>(
309
408
  getTodayLogDate(),
310
409
  );
311
410
  const [logNotification, setLogNotification] = createSignal<{
312
411
  message: string;
313
412
  tone: "success" | "error";
314
413
  } | null>(null);
414
+ const [logTailEnabled, setLogTailEnabled] = createSignal(false);
415
+ const [logTargetBranch, setLogTargetBranch] = createSignal<BranchItem | null>(
416
+ null,
417
+ );
315
418
 
316
419
  const [profileItems, setProfileItems] = createSignal<
317
420
  { name: string; displayName?: string; isActive?: boolean }[]
@@ -342,7 +445,41 @@ export function AppSolid(props: AppSolidProps) {
342
445
  const [profileConfirmMode, setProfileConfirmMode] =
343
446
  createSignal<ProfileConfirmMode>("delete-profile");
344
447
 
345
- const logDir = createMemo(() => resolveLogDir(workingDirectory()));
448
+ const [logEffectiveTarget, setLogEffectiveTarget] =
449
+ createSignal<LogTargetResolution | null>(null);
450
+ const logPrimaryTarget = createMemo(() =>
451
+ resolveLogTarget(logTargetBranch(), workingDirectory()),
452
+ );
453
+ const logFallbackTarget = createMemo(() =>
454
+ resolveLogTarget(null, workingDirectory()),
455
+ );
456
+ const logActiveTarget = createMemo(
457
+ () => logEffectiveTarget() ?? logPrimaryTarget(),
458
+ );
459
+ const logBranchLabel = createMemo(() => logTargetBranch()?.label ?? null);
460
+ const logSourceLabel = createMemo(() => {
461
+ const target = logActiveTarget();
462
+ if (!target.sourcePath) {
463
+ return "(none)";
464
+ }
465
+ if (
466
+ target.reason === "current-working-directory" ||
467
+ target.reason === "working-directory"
468
+ ) {
469
+ return `${target.sourcePath} (cwd)`;
470
+ }
471
+ if (target.reason === "working-directory-fallback") {
472
+ return `${target.sourcePath} (cwd fallback)`;
473
+ }
474
+ if (target.reason === "worktree-inaccessible") {
475
+ return `${target.sourcePath} (inaccessible)`;
476
+ }
477
+ return target.sourcePath;
478
+ });
479
+ createEffect(() => {
480
+ logPrimaryTarget();
481
+ setLogEffectiveTarget(null);
482
+ });
346
483
  const selectedProfileConfig = createMemo(() => {
347
484
  const name = selectedProfileName();
348
485
  const config = profilesConfig();
@@ -370,33 +507,62 @@ export function AppSolid(props: AppSolidProps) {
370
507
  let cleanupSafetyRequestId = 0;
371
508
  const refreshCleanupSafety = async () => {
372
509
  const requestId = ++cleanupSafetyRequestId;
373
- setCleanupSafetyLoading(true);
510
+ const pendingBranches = buildCleanupSafetyPending(branchItems());
511
+ setCleanupSafetyPending(pendingBranches);
512
+ setCleanupSafetyLoading(pendingBranches.size > 0);
513
+ const statusByBranch = new Map<string, CleanupStatus>();
514
+ const applyProgress = (status: CleanupStatus) => {
515
+ if (requestId !== cleanupSafetyRequestId) {
516
+ return;
517
+ }
518
+ statusByBranch.set(status.branch, status);
519
+ batch(() => {
520
+ setCleanupStatusByBranch(new Map(statusByBranch));
521
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
522
+ setCleanupSafetyPending((prev) => {
523
+ if (!prev.has(status.branch)) {
524
+ return prev;
525
+ }
526
+ const next = new Set(prev);
527
+ next.delete(status.branch);
528
+ return next;
529
+ });
530
+ });
531
+ };
374
532
  try {
375
- const cleanupStatuses = await getCleanupStatus();
533
+ const cleanupStatuses = await getCleanupStatus({
534
+ onProgress: applyProgress,
535
+ });
376
536
  if (requestId !== cleanupSafetyRequestId) {
377
537
  return;
378
538
  }
379
- const statusByBranch = new Map(
380
- cleanupStatuses.map((status) => [status.branch, status]),
381
- );
382
- setCleanupStatusByBranch(statusByBranch);
383
- setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
539
+ if (cleanupStatuses.length > statusByBranch.size) {
540
+ cleanupStatuses.forEach((status) => {
541
+ if (!statusByBranch.has(status.branch)) {
542
+ applyProgress(status);
543
+ }
544
+ });
545
+ }
384
546
  } catch (err) {
385
547
  if (requestId !== cleanupSafetyRequestId) {
386
548
  return;
387
549
  }
388
550
  logger.warn({ err }, "Failed to refresh cleanup safety indicators");
389
551
  const empty = new Map<string, CleanupStatus>();
390
- setCleanupStatusByBranch(empty);
391
- setBranchItems((items) => applyCleanupStatus(items, empty));
552
+ batch(() => {
553
+ setCleanupStatusByBranch(empty);
554
+ setBranchItems((items) => applyCleanupStatus(items, empty));
555
+ });
392
556
  } finally {
393
557
  if (requestId === cleanupSafetyRequestId) {
394
558
  setCleanupSafetyLoading(false);
559
+ setCleanupSafetyPending(new Set<string>());
395
560
  }
396
561
  }
397
562
  };
398
563
 
399
564
  let logNotificationTimer: ReturnType<typeof setTimeout> | null = null;
565
+ let logTailTimer: ReturnType<typeof setInterval> | null = null;
400
566
  let branchFooterTimer: ReturnType<typeof setTimeout> | null = null;
401
567
  const BRANCH_LOAD_TIMEOUT_MS = 3000;
402
568
  const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
@@ -480,9 +646,26 @@ export function AppSolid(props: AppSolidProps) {
480
646
  setLogLoading(true);
481
647
  setLogError(null);
482
648
  try {
483
- const filePath = buildLogFilePath(logDir(), targetDate);
484
- const lines = await readLogFileLines(filePath);
485
- const parsed = parseLogLines(lines, { limit: 100 });
649
+ const primaryTarget = logPrimaryTarget();
650
+ const fallbackTarget = logFallbackTarget();
651
+ const target = await selectLogTargetByRecency(
652
+ primaryTarget,
653
+ fallbackTarget,
654
+ );
655
+ setLogEffectiveTarget(target);
656
+ if (!target.logDir) {
657
+ setLogEntries([]);
658
+ setLogSelectedDate(targetDate);
659
+ return;
660
+ }
661
+ const result = await readLogLinesForDate(target.logDir, targetDate);
662
+ if (!result) {
663
+ setLogEntries([]);
664
+ setLogSelectedDate(targetDate);
665
+ return;
666
+ }
667
+ setLogSelectedDate(result.date);
668
+ const parsed = parseLogLines(result.lines, { limit: 100 });
486
669
  setLogEntries(parsed);
487
670
  } catch (err) {
488
671
  setLogEntries([]);
@@ -492,10 +675,40 @@ export function AppSolid(props: AppSolidProps) {
492
675
  }
493
676
  };
494
677
 
678
+ const clearLogTailTimer = () => {
679
+ if (logTailTimer) {
680
+ clearInterval(logTailTimer);
681
+ logTailTimer = null;
682
+ }
683
+ };
684
+
685
+ const toggleLogTail = () => {
686
+ setLogTailEnabled((prev) => !prev);
687
+ };
688
+
689
+ const resetLogFiles = async () => {
690
+ const target = logActiveTarget();
691
+ if (!target.logDir) {
692
+ showLogNotification("No logs available.", "error");
693
+ return;
694
+ }
695
+ try {
696
+ const cleared = await clearLogFiles(target.logDir);
697
+ if (cleared === 0) {
698
+ showLogNotification("No logs to reset.", "error");
699
+ } else {
700
+ showLogNotification("Logs cleared.", "success");
701
+ }
702
+ await loadLogEntries(logSelectedDate());
703
+ } catch (err) {
704
+ logger.warn({ err }, "Failed to clear log files");
705
+ showLogNotification("Failed to reset logs.", "error");
706
+ }
707
+ };
708
+
495
709
  const refreshBranches = async () => {
496
710
  setLoading(true);
497
711
  setError(null);
498
- void refreshCleanupSafety();
499
712
  try {
500
713
  const repoRoot = await getRepositoryRoot();
501
714
  const worktreesPromise = listAdditionalWorktrees();
@@ -536,8 +749,17 @@ export function AppSolid(props: AppSolidProps) {
536
749
  );
537
750
  setBranchItems(initialItems);
538
751
  setStats(buildStats(initialItems));
752
+ void refreshCleanupSafety();
539
753
 
540
754
  void (async () => {
755
+ // Fetch remote updates with --prune to remove stale tracking refs
756
+ await withTimeout(
757
+ fetchAllRemotes({ cwd: repoRoot }),
758
+ BRANCH_FULL_LOAD_TIMEOUT_MS,
759
+ ).catch(() => {
760
+ // Ignore fetch errors (e.g., network issues)
761
+ });
762
+
541
763
  const [branches, latestWorktrees] = await Promise.all([
542
764
  withTimeout(
543
765
  getAllBranches(repoRoot),
@@ -659,16 +881,41 @@ export function AppSolid(props: AppSolidProps) {
659
881
  });
660
882
  });
661
883
 
884
+ // FR-028: Prefetch npm versions for all bunx-type agents at startup (background)
885
+ onMount(() => {
886
+ const bunxAgentIds = getBunxAgentIds();
887
+ void prefetchAgentVersions(bunxAgentIds).catch(() => {
888
+ // Silently handle errors - cache will return null and UI will show "latest" only
889
+ });
890
+ // FR-017: Prefetch installed versions for all builtin agents at startup
891
+ void prefetchInstalledVersions(bunxAgentIds).catch(() => {
892
+ // Silently handle errors - cache will return null and UI won't show "installed"
893
+ });
894
+ });
895
+
662
896
  createEffect(() => {
663
897
  if (currentScreen() === "log-list") {
898
+ logPrimaryTarget();
664
899
  void loadLogEntries(logSelectedDate());
665
900
  }
666
901
  });
667
902
 
903
+ createEffect(() => {
904
+ if (currentScreen() !== "log-list" || !logTailEnabled()) {
905
+ clearLogTailTimer();
906
+ return;
907
+ }
908
+ clearLogTailTimer();
909
+ logTailTimer = setInterval(() => {
910
+ void loadLogEntries(logSelectedDate());
911
+ }, 1500);
912
+ });
913
+
668
914
  onCleanup(() => {
669
915
  if (logNotificationTimer) {
670
916
  clearTimeout(logNotificationTimer);
671
917
  }
918
+ clearLogTailTimer();
672
919
  if (branchFooterTimer) {
673
920
  clearTimeout(branchFooterTimer);
674
921
  }
@@ -784,6 +1031,11 @@ export function AppSolid(props: AppSolidProps) {
784
1031
 
785
1032
  // FR-010: クイックスタートからのResume(前回設定で続きから)
786
1033
  const handleWizardResume = (entry: ToolSessionEntry) => {
1034
+ if (!entry.sessionId) {
1035
+ handleWizardStartNew(entry);
1036
+ return;
1037
+ }
1038
+
787
1039
  setWizardVisible(false);
788
1040
 
789
1041
  const branch = selectedBranch();
@@ -915,25 +1167,10 @@ export function AppSolid(props: AppSolidProps) {
915
1167
  skipCounts.remote += 1;
916
1168
  continue;
917
1169
  }
918
- if (isProtectedBranchName(branch.name)) {
919
- skipCounts.protected += 1;
920
- continue;
921
- }
922
1170
  if (branch.isCurrent) {
923
1171
  skipCounts.current += 1;
924
1172
  continue;
925
1173
  }
926
- const hasUncommitted =
927
- branch.worktree?.hasUncommittedChanges === true;
928
- const hasUnpushed = branch.hasUnpushedCommits === true;
929
- if (hasUncommitted || hasUnpushed) {
930
- skipCounts.unsafe += 1;
931
- continue;
932
- }
933
- if (branch.safeToCleanup !== true) {
934
- skipCounts.unsafe += 1;
935
- continue;
936
- }
937
1174
  const worktreePath = branch.worktree?.path ?? null;
938
1175
  const cleanupType: CleanupTask["cleanupType"] = worktreePath
939
1176
  ? "worktree-and-branch"
@@ -1051,13 +1288,14 @@ export function AppSolid(props: AppSolidProps) {
1051
1288
  return;
1052
1289
  }
1053
1290
 
1054
- // 選択されたブランチのうち、inaccessibleなものを修復対象とする
1291
+ // 選択されたローカルブランチのうち、Worktreeを持つものを修復対象とする
1055
1292
  const selectionSet = new Set(selection);
1056
1293
  const targets = branchItems()
1057
1294
  .filter(
1058
1295
  (branch) =>
1059
1296
  selectionSet.has(branch.name) &&
1060
- branch.worktreeStatus === "inaccessible",
1297
+ branch.type !== "remote" &&
1298
+ branch.worktreeStatus !== undefined,
1061
1299
  )
1062
1300
  .map((branch) => branch.name);
1063
1301
 
@@ -1309,15 +1547,59 @@ export function AppSolid(props: AppSolidProps) {
1309
1547
  };
1310
1548
 
1311
1549
  const toggleSelectedBranch = (branchName: string) => {
1312
- setSelectedBranches((prev) => {
1313
- const next = new Set(prev);
1314
- if (next.has(branchName)) {
1315
- next.delete(branchName);
1316
- } else {
1317
- next.add(branchName);
1318
- }
1319
- return Array.from(next);
1320
- });
1550
+ if (unsafeSelectionConfirmVisible()) {
1551
+ return;
1552
+ }
1553
+ const currentSelection = new Set(selectedBranches());
1554
+ if (currentSelection.has(branchName)) {
1555
+ currentSelection.delete(branchName);
1556
+ setSelectedBranches(Array.from(currentSelection));
1557
+ return;
1558
+ }
1559
+
1560
+ const branch = branchItems().find((item) => item.name === branchName);
1561
+ const pending = cleanupSafetyPending();
1562
+ const hasSafetyPending = pending.has(branchName);
1563
+ const hasUncommitted = branch?.worktree?.hasUncommittedChanges === true;
1564
+ const hasUnpushed = branch?.hasUnpushedCommits === true;
1565
+ const isUnmerged = branch?.isUnmerged === true;
1566
+ const safeToCleanup = branch?.safeToCleanup === true;
1567
+ const isRemoteBranch = branch?.type === "remote";
1568
+ const isUnsafe =
1569
+ Boolean(branch) &&
1570
+ !isRemoteBranch &&
1571
+ !hasSafetyPending &&
1572
+ (hasUncommitted || hasUnpushed || isUnmerged || !safeToCleanup);
1573
+
1574
+ if (branch && hasSafetyPending) {
1575
+ setUnsafeSelectionTarget(branch.name);
1576
+ setUnsafeSelectionMessage(SAFETY_PENDING_MESSAGE);
1577
+ setUnsafeSelectionConfirmVisible(true);
1578
+ return;
1579
+ }
1580
+ if (branch && isUnsafe) {
1581
+ setUnsafeSelectionTarget(branch.name);
1582
+ setUnsafeSelectionMessage(UNSAFE_SELECTION_MESSAGE);
1583
+ setUnsafeSelectionConfirmVisible(true);
1584
+ return;
1585
+ }
1586
+
1587
+ currentSelection.add(branchName);
1588
+ setSelectedBranches(Array.from(currentSelection));
1589
+ };
1590
+
1591
+ const confirmUnsafeSelection = (confirmed: boolean) => {
1592
+ suppressBranchInputOnce();
1593
+ const target = unsafeSelectionTarget();
1594
+ setUnsafeSelectionConfirmVisible(false);
1595
+ setUnsafeSelectionTarget(null);
1596
+ setUnsafeSelectionMessage(UNSAFE_SELECTION_MESSAGE);
1597
+ if (!confirmed || !target) {
1598
+ return;
1599
+ }
1600
+ setSelectedBranches((prev) =>
1601
+ prev.includes(target) ? prev : [...prev, target],
1602
+ );
1321
1603
  };
1322
1604
 
1323
1605
  const handleToolSelect = (item: SelectorItem) => {
@@ -1371,8 +1653,9 @@ export function AppSolid(props: AppSolidProps) {
1371
1653
  const cleanupUI = {
1372
1654
  indicators: cleanupIndicators(),
1373
1655
  footerMessage: branchFooterMessage(),
1374
- inputLocked: branchInputLocked(),
1656
+ inputLocked: branchInputLocked() || unsafeConfirmInputLocked(),
1375
1657
  safetyLoading: cleanupSafetyLoading(),
1658
+ safetyPendingBranches: cleanupSafetyPending(),
1376
1659
  };
1377
1660
  return (
1378
1661
  <BranchListScreen
@@ -1390,7 +1673,13 @@ export function AppSolid(props: AppSolidProps) {
1390
1673
  version={version()}
1391
1674
  workingDirectory={workingDirectory()}
1392
1675
  activeProfile={activeProfile()}
1393
- onOpenLogs={() => navigateTo("log-list")}
1676
+ onOpenLogs={(branch) => {
1677
+ setLogTargetBranch(branch);
1678
+ setLogSelectedEntry(null);
1679
+ setLogSelectedDate(getTodayLogDate());
1680
+ setLogTailEnabled(false);
1681
+ navigateTo("log-list");
1682
+ }}
1394
1683
  onOpenProfiles={() => navigateTo("profile")}
1395
1684
  selectedBranches={selectedBranches()}
1396
1685
  onToggleSelect={toggleSelectedBranch}
@@ -1398,6 +1687,9 @@ export function AppSolid(props: AppSolidProps) {
1398
1687
  cleanupUI={cleanupUI}
1399
1688
  helpVisible={helpVisible()}
1400
1689
  wizardVisible={wizardVisible()}
1690
+ confirmVisible={unsafeSelectionConfirmVisible()}
1691
+ cursorPosition={branchCursorPosition()}
1692
+ onCursorPositionChange={setBranchCursorPosition}
1401
1693
  />
1402
1694
  );
1403
1695
  }
@@ -1473,9 +1765,15 @@ export function AppSolid(props: AppSolidProps) {
1473
1765
  showLogNotification("Failed to copy to clipboard.", "error");
1474
1766
  }
1475
1767
  }}
1768
+ onReload={() => void loadLogEntries(logSelectedDate())}
1769
+ onToggleTail={toggleLogTail}
1770
+ onReset={() => void resetLogFiles()}
1476
1771
  notification={logNotification()}
1477
1772
  version={version()}
1478
1773
  selectedDate={logSelectedDate()}
1774
+ branchLabel={logBranchLabel()}
1775
+ sourceLabel={logSourceLabel()}
1776
+ tailing={logTailEnabled()}
1479
1777
  helpVisible={helpVisible()}
1480
1778
  />
1481
1779
  );
@@ -1742,12 +2040,36 @@ export function AppSolid(props: AppSolidProps) {
1742
2040
  return (
1743
2041
  <>
1744
2042
  {renderCurrentScreen()}
2043
+ {unsafeSelectionConfirmVisible() && (
2044
+ <box
2045
+ position="absolute"
2046
+ top="30%"
2047
+ left="20%"
2048
+ width={unsafeConfirmBoxWidth()}
2049
+ zIndex={110}
2050
+ border
2051
+ borderStyle="single"
2052
+ borderColor="yellow"
2053
+ backgroundColor="black"
2054
+ padding={1}
2055
+ >
2056
+ <ConfirmScreen
2057
+ message={unsafeSelectionMessage()}
2058
+ onConfirm={confirmUnsafeSelection}
2059
+ yesLabel="OK"
2060
+ noLabel="Cancel"
2061
+ defaultNo
2062
+ helpVisible={helpVisible()}
2063
+ width={unsafeConfirmContentWidth()}
2064
+ />
2065
+ </box>
2066
+ )}
1745
2067
  <HelpOverlay visible={helpVisible()} context={currentScreen()} />
1746
2068
  {/* FR-044: ウィザードポップアップをレイヤー表示 */}
1747
2069
  <WizardController
1748
2070
  visible={wizardVisible()}
1749
2071
  selectedBranchName={selectedBranch()?.name ?? ""}
1750
- history={historyForBranch()}
2072
+ history={quickStartHistory()}
1751
2073
  onClose={handleWizardClose}
1752
2074
  onComplete={handleWizardComplete}
1753
2075
  onResume={handleWizardResume}