@akiojin/gwt 4.11.6 → 4.12.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 (172) hide show
  1. package/bin/gwt.js +1 -1
  2. package/dist/claude.d.ts +1 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +50 -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 +247 -49
  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 +19 -11
  17. package/dist/cli/ui/components/solid/WizardController.js.map +1 -1
  18. package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -1
  19. package/dist/cli/ui/components/solid/WizardSteps.js +26 -69
  20. package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -1
  21. package/dist/cli/ui/core/theme.d.ts +9 -0
  22. package/dist/cli/ui/core/theme.d.ts.map +1 -1
  23. package/dist/cli/ui/core/theme.js +21 -0
  24. package/dist/cli/ui/core/theme.js.map +1 -1
  25. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts +9 -2
  26. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -1
  27. package/dist/cli/ui/screens/solid/BranchListScreen.js +101 -28
  28. package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -1
  29. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +2 -1
  30. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -1
  31. package/dist/cli/ui/screens/solid/ConfirmScreen.js +11 -3
  32. package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -1
  33. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -1
  34. package/dist/cli/ui/screens/solid/EnvironmentScreen.js +9 -10
  35. package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -1
  36. package/dist/cli/ui/screens/solid/LogScreen.d.ts +7 -1
  37. package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -1
  38. package/dist/cli/ui/screens/solid/LogScreen.js +254 -16
  39. package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -1
  40. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +8 -5
  42. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -1
  43. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -1
  44. package/dist/cli/ui/screens/solid/SelectorScreen.js +12 -4
  45. package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -1
  46. package/dist/cli/ui/types.d.ts +1 -0
  47. package/dist/cli/ui/types.d.ts.map +1 -1
  48. package/dist/cli/ui/utils/branchFormatter.d.ts +1 -0
  49. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  50. package/dist/cli/ui/utils/branchFormatter.js +29 -7
  51. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  52. package/dist/cli/ui/utils/continueSession.d.ts +14 -0
  53. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  54. package/dist/cli/ui/utils/continueSession.js +61 -3
  55. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  56. package/dist/cli/ui/utils/versionCache.d.ts +37 -0
  57. package/dist/cli/ui/utils/versionCache.d.ts.map +1 -0
  58. package/dist/cli/ui/utils/versionCache.js +70 -0
  59. package/dist/cli/ui/utils/versionCache.js.map +1 -0
  60. package/dist/cli/ui/utils/versionFetcher.d.ts +41 -0
  61. package/dist/cli/ui/utils/versionFetcher.d.ts.map +1 -0
  62. package/dist/cli/ui/utils/versionFetcher.js +89 -0
  63. package/dist/cli/ui/utils/versionFetcher.js.map +1 -0
  64. package/dist/codex.d.ts +1 -0
  65. package/dist/codex.d.ts.map +1 -1
  66. package/dist/codex.js +48 -19
  67. package/dist/codex.js.map +1 -1
  68. package/dist/config/index.d.ts.map +1 -1
  69. package/dist/config/index.js +10 -1
  70. package/dist/config/index.js.map +1 -1
  71. package/dist/gemini.d.ts +1 -0
  72. package/dist/gemini.d.ts.map +1 -1
  73. package/dist/gemini.js +36 -3
  74. package/dist/gemini.js.map +1 -1
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +32 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/launcher.d.ts.map +1 -1
  79. package/dist/launcher.js +43 -8
  80. package/dist/launcher.js.map +1 -1
  81. package/dist/logging/agentOutput.d.ts +21 -0
  82. package/dist/logging/agentOutput.d.ts.map +1 -0
  83. package/dist/logging/agentOutput.js +164 -0
  84. package/dist/logging/agentOutput.js.map +1 -0
  85. package/dist/logging/formatter.d.ts.map +1 -1
  86. package/dist/logging/formatter.js +18 -4
  87. package/dist/logging/formatter.js.map +1 -1
  88. package/dist/logging/logger.d.ts.map +1 -1
  89. package/dist/logging/logger.js +2 -0
  90. package/dist/logging/logger.js.map +1 -1
  91. package/dist/logging/reader.d.ts +21 -0
  92. package/dist/logging/reader.d.ts.map +1 -1
  93. package/dist/logging/reader.js +79 -0
  94. package/dist/logging/reader.js.map +1 -1
  95. package/dist/opentui/index.solid.js +2306 -653
  96. package/dist/services/dependency-installer.js +2 -2
  97. package/dist/services/dependency-installer.js.map +1 -1
  98. package/dist/utils/session/common.d.ts +8 -0
  99. package/dist/utils/session/common.d.ts.map +1 -1
  100. package/dist/utils/session/common.js +22 -0
  101. package/dist/utils/session/common.js.map +1 -1
  102. package/dist/utils/session/parsers/claude.d.ts +10 -4
  103. package/dist/utils/session/parsers/claude.d.ts.map +1 -1
  104. package/dist/utils/session/parsers/claude.js +64 -18
  105. package/dist/utils/session/parsers/claude.js.map +1 -1
  106. package/dist/utils/session/parsers/codex.d.ts.map +1 -1
  107. package/dist/utils/session/parsers/codex.js +48 -28
  108. package/dist/utils/session/parsers/codex.js.map +1 -1
  109. package/dist/utils/session/parsers/gemini.d.ts.map +1 -1
  110. package/dist/utils/session/parsers/gemini.js +43 -6
  111. package/dist/utils/session/parsers/gemini.js.map +1 -1
  112. package/dist/utils/session/parsers/opencode.d.ts.map +1 -1
  113. package/dist/utils/session/parsers/opencode.js +43 -6
  114. package/dist/utils/session/parsers/opencode.js.map +1 -1
  115. package/dist/utils/session/types.d.ts +7 -0
  116. package/dist/utils/session/types.d.ts.map +1 -1
  117. package/dist/web/client/src/components/ui/alert.d.ts +1 -1
  118. package/dist/worktree.d.ts +4 -1
  119. package/dist/worktree.d.ts.map +1 -1
  120. package/dist/worktree.js +21 -15
  121. package/dist/worktree.js.map +1 -1
  122. package/package.json +2 -1
  123. package/src/claude.ts +64 -28
  124. package/src/cli/ui/App.solid.tsx +324 -51
  125. package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +830 -1
  126. package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +105 -5
  127. package/src/cli/ui/__tests__/solid/ConfirmScreen.test.tsx +77 -0
  128. package/src/cli/ui/__tests__/solid/LogScreen.test.tsx +351 -0
  129. package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +73 -2
  130. package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +4 -1
  131. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +72 -45
  132. package/src/cli/ui/components/solid/QuickStartStep.tsx +35 -23
  133. package/src/cli/ui/components/solid/SearchInput.tsx +1 -1
  134. package/src/cli/ui/components/solid/SelectInput.tsx +4 -0
  135. package/src/cli/ui/components/solid/WizardController.tsx +20 -11
  136. package/src/cli/ui/components/solid/WizardSteps.tsx +29 -86
  137. package/src/cli/ui/core/theme.ts +32 -0
  138. package/src/cli/ui/hooks/solid/useAsyncOperation.ts +8 -6
  139. package/src/cli/ui/hooks/solid/useGitOperations.ts +6 -5
  140. package/src/cli/ui/screens/solid/BranchListScreen.tsx +135 -32
  141. package/src/cli/ui/screens/solid/ConfirmScreen.tsx +20 -8
  142. package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +22 -20
  143. package/src/cli/ui/screens/solid/LogScreen.tsx +364 -35
  144. package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +19 -15
  145. package/src/cli/ui/screens/solid/SelectorScreen.tsx +25 -14
  146. package/src/cli/ui/screens/solid/SettingsScreen.tsx +5 -3
  147. package/src/cli/ui/types.ts +1 -0
  148. package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +53 -6
  149. package/src/cli/ui/utils/branchFormatter.ts +35 -7
  150. package/src/cli/ui/utils/continueSession.ts +90 -3
  151. package/src/cli/ui/utils/versionCache.ts +93 -0
  152. package/src/cli/ui/utils/versionFetcher.ts +120 -0
  153. package/src/codex.ts +62 -20
  154. package/src/config/__tests__/saveSession.test.ts +2 -2
  155. package/src/config/index.ts +11 -1
  156. package/src/gemini.ts +50 -4
  157. package/src/index.test.ts +16 -10
  158. package/src/index.ts +38 -1
  159. package/src/launcher.ts +49 -8
  160. package/src/logging/agentOutput.ts +216 -0
  161. package/src/logging/formatter.ts +23 -4
  162. package/src/logging/logger.ts +2 -0
  163. package/src/logging/reader.ts +117 -0
  164. package/src/services/__tests__/BatchMergeService.test.ts +34 -14
  165. package/src/services/dependency-installer.ts +2 -2
  166. package/src/utils/session/common.ts +28 -0
  167. package/src/utils/session/parsers/claude.ts +79 -29
  168. package/src/utils/session/parsers/codex.ts +50 -26
  169. package/src/utils/session/parsers/gemini.ts +46 -5
  170. package/src/utils/session/parsers/opencode.ts +46 -5
  171. package/src/utils/session/types.ts +4 -0
  172. 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";
@@ -73,12 +75,16 @@ import {
73
75
  loadSession,
74
76
  type ToolSessionEntry,
75
77
  } from "../../config/index.js";
78
+ import {
79
+ findLatestBranchSessionsByTool,
80
+ refreshQuickStartEntries,
81
+ } from "./utils/continueSession.js";
76
82
  import { getAllCodingAgents } from "../../config/tools.js";
77
83
  import {
78
- buildLogFilePath,
84
+ clearLogFiles,
79
85
  getTodayLogDate,
80
- readLogFileLines,
81
- resolveLogDir,
86
+ readLogLinesForDate,
87
+ resolveLogTarget,
82
88
  } from "../../logging/reader.js";
83
89
  import { parseLogLines } from "../../logging/formatter.js";
84
90
  import { copyToClipboard } from "./utils/clipboard.js";
@@ -95,9 +101,13 @@ import {
95
101
  type ProfilesConfig,
96
102
  } from "../../types/profiles.js";
97
103
  import { BRANCH_PREFIXES } from "../../config/constants.js";
104
+ import { prefetchAgentVersions } from "./utils/versionCache.js";
105
+ import { getBunxAgentIds } from "./utils/versionFetcher.js";
98
106
 
99
107
  export type ExecutionMode = "normal" | "continue" | "resume";
100
108
 
109
+ const UNSAFE_SELECTION_MESSAGE = "Unsafe branch selected. Select anyway?";
110
+
101
111
  export interface SelectionResult {
102
112
  branch: string;
103
113
  displayName: string;
@@ -192,6 +202,24 @@ const applyCleanupStatus = (
192
202
  return worktree ? { ...base, worktree } : base;
193
203
  });
194
204
 
205
+ const buildCleanupSafetyPending = (items: BranchItem[]): Set<string> => {
206
+ const pending = new Set<string>();
207
+ for (const branch of items) {
208
+ if (branch.type === "remote") {
209
+ continue;
210
+ }
211
+ if (branch.worktree) {
212
+ pending.add(branch.name);
213
+ continue;
214
+ }
215
+ if (isProtectedBranchName(branch.name)) {
216
+ continue;
217
+ }
218
+ pending.add(branch.name);
219
+ }
220
+ return pending;
221
+ };
222
+
195
223
  const toLocalBranchName = (name: string): string => {
196
224
  const segments = name.split("/");
197
225
  if (segments.length <= 1) {
@@ -208,12 +236,14 @@ const toSelectedBranchState = (branch: BranchItem): SelectedBranchState => {
208
236
  displayName: branch.name,
209
237
  branchType: branch.type,
210
238
  branchCategory: branch.branchType,
239
+ worktreePath: branch.worktree?.path ?? null,
211
240
  ...(isRemote ? { remoteBranch: branch.name } : {}),
212
241
  };
213
242
  };
214
243
 
215
244
  export function AppSolid(props: AppSolidProps) {
216
245
  const renderer = useRenderer();
246
+ const terminal = useTerminalSize();
217
247
  let hasExited = false;
218
248
 
219
249
  const exitApp = (result?: SelectionResult) => {
@@ -239,6 +269,9 @@ export function AppSolid(props: AppSolidProps) {
239
269
  const [loading, setLoading] = createSignal(!props.branches);
240
270
  const [error, setError] = createSignal<Error | null>(null);
241
271
 
272
+ // ブランチ一覧のカーソル位置(グローバル管理で再マウント時もリセットされない)
273
+ const [branchCursorPosition, setBranchCursorPosition] = createSignal(0);
274
+
242
275
  const [toolItems, setToolItems] = createSignal<SelectorItem[]>([]);
243
276
  const [toolError, setToolError] = createSignal<Error | null>(null);
244
277
 
@@ -257,6 +290,13 @@ export function AppSolid(props: AppSolidProps) {
257
290
  );
258
291
  const [selectedMode, setSelectedMode] = createSignal<ExecutionMode>("normal");
259
292
  const [selectedBranches, setSelectedBranches] = createSignal<string[]>([]);
293
+ const [unsafeSelectionConfirmVisible, setUnsafeSelectionConfirmVisible] =
294
+ createSignal(false);
295
+ const [unsafeConfirmInputLocked, setUnsafeConfirmInputLocked] =
296
+ createSignal(false);
297
+ const [unsafeSelectionTarget, setUnsafeSelectionTarget] = createSignal<
298
+ string | null
299
+ >(null);
260
300
  const [branchFooterMessage, setBranchFooterMessage] = createSignal<{
261
301
  text: string;
262
302
  isSpinning?: boolean;
@@ -270,6 +310,9 @@ export function AppSolid(props: AppSolidProps) {
270
310
  Map<string, CleanupStatus>
271
311
  >(new Map());
272
312
  const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
313
+ const [cleanupSafetyPending, setCleanupSafetyPending] = createSignal<
314
+ Set<string>
315
+ >(new Set());
273
316
  const [isNewBranch, setIsNewBranch] = createSignal(false);
274
317
  const [newBranchBaseRef, setNewBranchBaseRef] = createSignal<string | null>(
275
318
  null,
@@ -284,20 +327,68 @@ export function AppSolid(props: AppSolidProps) {
284
327
  );
285
328
  const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
286
329
 
330
+ const suppressBranchInputOnce = () => {
331
+ setUnsafeConfirmInputLocked(true);
332
+ queueMicrotask(() => {
333
+ setUnsafeConfirmInputLocked(false);
334
+ });
335
+ };
336
+
337
+ const unsafeConfirmBoxWidth = createMemo(() => {
338
+ const columns = terminal().columns || 80;
339
+ return Math.max(1, Math.floor(columns * 0.6));
340
+ });
341
+ const unsafeConfirmContentWidth = createMemo(() =>
342
+ Math.max(0, unsafeConfirmBoxWidth() - 4),
343
+ );
344
+
287
345
  // セッション履歴(最終使用エージェントなど)
288
346
  const [sessionHistory, setSessionHistory] = createSignal<ToolSessionEntry[]>(
289
347
  [],
290
348
  );
349
+ const [quickStartHistory, setQuickStartHistory] = createSignal<
350
+ ToolSessionEntry[]
351
+ >([]);
291
352
 
292
353
  // 選択中ブランチの履歴をフィルタリング
293
354
  const historyForBranch = createMemo(() => {
294
355
  const history = sessionHistory();
295
356
  const branch = selectedBranch();
296
357
  if (!branch) return [];
297
- // 選択中ブランチにマッチする履歴エントリを新しい順で返す
298
- return history
299
- .filter((entry) => entry.branch === branch.name)
300
- .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
358
+ return findLatestBranchSessionsByTool(
359
+ history,
360
+ branch.name,
361
+ branch.worktreePath ?? null,
362
+ );
363
+ });
364
+
365
+ createEffect(() => {
366
+ setQuickStartHistory(historyForBranch());
367
+ });
368
+
369
+ createEffect(() => {
370
+ const branch = selectedBranch();
371
+ const baseHistory = historyForBranch();
372
+ if (!wizardVisible() || !branch || baseHistory.length === 0) {
373
+ return;
374
+ }
375
+
376
+ const worktreePath = branch.worktreePath ?? null;
377
+ if (!worktreePath) {
378
+ return;
379
+ }
380
+
381
+ const branchName = branch.name;
382
+ void (async () => {
383
+ const refreshed = await refreshQuickStartEntries(baseHistory, {
384
+ branch: branchName,
385
+ worktreePath,
386
+ });
387
+ if (selectedBranch()?.name !== branchName) {
388
+ return;
389
+ }
390
+ setQuickStartHistory(refreshed);
391
+ })();
301
392
  });
302
393
 
303
394
  const [logEntries, setLogEntries] = createSignal<FormattedLogEntry[]>([]);
@@ -305,13 +396,17 @@ export function AppSolid(props: AppSolidProps) {
305
396
  const [logError, setLogError] = createSignal<string | null>(null);
306
397
  const [logSelectedEntry, setLogSelectedEntry] =
307
398
  createSignal<FormattedLogEntry | null>(null);
308
- const [logSelectedDate, _setLogSelectedDate] = createSignal<string | null>(
399
+ const [logSelectedDate, setLogSelectedDate] = createSignal<string | null>(
309
400
  getTodayLogDate(),
310
401
  );
311
402
  const [logNotification, setLogNotification] = createSignal<{
312
403
  message: string;
313
404
  tone: "success" | "error";
314
405
  } | null>(null);
406
+ const [logTailEnabled, setLogTailEnabled] = createSignal(false);
407
+ const [logTargetBranch, setLogTargetBranch] = createSignal<BranchItem | null>(
408
+ null,
409
+ );
315
410
 
316
411
  const [profileItems, setProfileItems] = createSignal<
317
412
  { name: string; displayName?: string; isActive?: boolean }[]
@@ -342,7 +437,26 @@ export function AppSolid(props: AppSolidProps) {
342
437
  const [profileConfirmMode, setProfileConfirmMode] =
343
438
  createSignal<ProfileConfirmMode>("delete-profile");
344
439
 
345
- const logDir = createMemo(() => resolveLogDir(workingDirectory()));
440
+ const logTarget = createMemo(() =>
441
+ resolveLogTarget(logTargetBranch(), workingDirectory()),
442
+ );
443
+ const logBranchLabel = createMemo(() => logTargetBranch()?.label ?? null);
444
+ const logSourceLabel = createMemo(() => {
445
+ const target = logTarget();
446
+ if (!target.sourcePath) {
447
+ return "(none)";
448
+ }
449
+ if (
450
+ target.reason === "current-working-directory" ||
451
+ target.reason === "working-directory"
452
+ ) {
453
+ return `${target.sourcePath} (cwd)`;
454
+ }
455
+ if (target.reason === "worktree-inaccessible") {
456
+ return `${target.sourcePath} (inaccessible)`;
457
+ }
458
+ return target.sourcePath;
459
+ });
346
460
  const selectedProfileConfig = createMemo(() => {
347
461
  const name = selectedProfileName();
348
462
  const config = profilesConfig();
@@ -370,33 +484,62 @@ export function AppSolid(props: AppSolidProps) {
370
484
  let cleanupSafetyRequestId = 0;
371
485
  const refreshCleanupSafety = async () => {
372
486
  const requestId = ++cleanupSafetyRequestId;
373
- setCleanupSafetyLoading(true);
487
+ const pendingBranches = buildCleanupSafetyPending(branchItems());
488
+ setCleanupSafetyPending(pendingBranches);
489
+ setCleanupSafetyLoading(pendingBranches.size > 0);
490
+ const statusByBranch = new Map<string, CleanupStatus>();
491
+ const applyProgress = (status: CleanupStatus) => {
492
+ if (requestId !== cleanupSafetyRequestId) {
493
+ return;
494
+ }
495
+ statusByBranch.set(status.branch, status);
496
+ batch(() => {
497
+ setCleanupStatusByBranch(new Map(statusByBranch));
498
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
499
+ setCleanupSafetyPending((prev) => {
500
+ if (!prev.has(status.branch)) {
501
+ return prev;
502
+ }
503
+ const next = new Set(prev);
504
+ next.delete(status.branch);
505
+ return next;
506
+ });
507
+ });
508
+ };
374
509
  try {
375
- const cleanupStatuses = await getCleanupStatus();
510
+ const cleanupStatuses = await getCleanupStatus({
511
+ onProgress: applyProgress,
512
+ });
376
513
  if (requestId !== cleanupSafetyRequestId) {
377
514
  return;
378
515
  }
379
- const statusByBranch = new Map(
380
- cleanupStatuses.map((status) => [status.branch, status]),
381
- );
382
- setCleanupStatusByBranch(statusByBranch);
383
- setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
516
+ if (cleanupStatuses.length > statusByBranch.size) {
517
+ cleanupStatuses.forEach((status) => {
518
+ if (!statusByBranch.has(status.branch)) {
519
+ applyProgress(status);
520
+ }
521
+ });
522
+ }
384
523
  } catch (err) {
385
524
  if (requestId !== cleanupSafetyRequestId) {
386
525
  return;
387
526
  }
388
527
  logger.warn({ err }, "Failed to refresh cleanup safety indicators");
389
528
  const empty = new Map<string, CleanupStatus>();
390
- setCleanupStatusByBranch(empty);
391
- setBranchItems((items) => applyCleanupStatus(items, empty));
529
+ batch(() => {
530
+ setCleanupStatusByBranch(empty);
531
+ setBranchItems((items) => applyCleanupStatus(items, empty));
532
+ });
392
533
  } finally {
393
534
  if (requestId === cleanupSafetyRequestId) {
394
535
  setCleanupSafetyLoading(false);
536
+ setCleanupSafetyPending(new Set<string>());
395
537
  }
396
538
  }
397
539
  };
398
540
 
399
541
  let logNotificationTimer: ReturnType<typeof setTimeout> | null = null;
542
+ let logTailTimer: ReturnType<typeof setInterval> | null = null;
400
543
  let branchFooterTimer: ReturnType<typeof setTimeout> | null = null;
401
544
  const BRANCH_LOAD_TIMEOUT_MS = 3000;
402
545
  const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
@@ -480,9 +623,20 @@ export function AppSolid(props: AppSolidProps) {
480
623
  setLogLoading(true);
481
624
  setLogError(null);
482
625
  try {
483
- const filePath = buildLogFilePath(logDir(), targetDate);
484
- const lines = await readLogFileLines(filePath);
485
- const parsed = parseLogLines(lines, { limit: 100 });
626
+ const target = logTarget();
627
+ if (!target.logDir) {
628
+ setLogEntries([]);
629
+ setLogSelectedDate(targetDate);
630
+ return;
631
+ }
632
+ const result = await readLogLinesForDate(target.logDir, targetDate);
633
+ if (!result) {
634
+ setLogEntries([]);
635
+ setLogSelectedDate(targetDate);
636
+ return;
637
+ }
638
+ setLogSelectedDate(result.date);
639
+ const parsed = parseLogLines(result.lines, { limit: 100 });
486
640
  setLogEntries(parsed);
487
641
  } catch (err) {
488
642
  setLogEntries([]);
@@ -492,10 +646,40 @@ export function AppSolid(props: AppSolidProps) {
492
646
  }
493
647
  };
494
648
 
649
+ const clearLogTailTimer = () => {
650
+ if (logTailTimer) {
651
+ clearInterval(logTailTimer);
652
+ logTailTimer = null;
653
+ }
654
+ };
655
+
656
+ const toggleLogTail = () => {
657
+ setLogTailEnabled((prev) => !prev);
658
+ };
659
+
660
+ const resetLogFiles = async () => {
661
+ const target = logTarget();
662
+ if (!target.logDir) {
663
+ showLogNotification("No logs available.", "error");
664
+ return;
665
+ }
666
+ try {
667
+ const cleared = await clearLogFiles(target.logDir);
668
+ if (cleared === 0) {
669
+ showLogNotification("No logs to reset.", "error");
670
+ } else {
671
+ showLogNotification("Logs cleared.", "success");
672
+ }
673
+ await loadLogEntries(logSelectedDate());
674
+ } catch (err) {
675
+ logger.warn({ err }, "Failed to clear log files");
676
+ showLogNotification("Failed to reset logs.", "error");
677
+ }
678
+ };
679
+
495
680
  const refreshBranches = async () => {
496
681
  setLoading(true);
497
682
  setError(null);
498
- void refreshCleanupSafety();
499
683
  try {
500
684
  const repoRoot = await getRepositoryRoot();
501
685
  const worktreesPromise = listAdditionalWorktrees();
@@ -536,6 +720,7 @@ export function AppSolid(props: AppSolidProps) {
536
720
  );
537
721
  setBranchItems(initialItems);
538
722
  setStats(buildStats(initialItems));
723
+ void refreshCleanupSafety();
539
724
 
540
725
  void (async () => {
541
726
  const [branches, latestWorktrees] = await Promise.all([
@@ -659,16 +844,37 @@ export function AppSolid(props: AppSolidProps) {
659
844
  });
660
845
  });
661
846
 
847
+ // FR-028: Prefetch npm versions for all bunx-type agents at startup (background)
848
+ onMount(() => {
849
+ const bunxAgentIds = getBunxAgentIds();
850
+ void prefetchAgentVersions(bunxAgentIds).catch(() => {
851
+ // Silently handle errors - cache will return null and UI will show "latest" only
852
+ });
853
+ });
854
+
662
855
  createEffect(() => {
663
856
  if (currentScreen() === "log-list") {
857
+ logTarget();
664
858
  void loadLogEntries(logSelectedDate());
665
859
  }
666
860
  });
667
861
 
862
+ createEffect(() => {
863
+ if (currentScreen() !== "log-list" || !logTailEnabled()) {
864
+ clearLogTailTimer();
865
+ return;
866
+ }
867
+ clearLogTailTimer();
868
+ logTailTimer = setInterval(() => {
869
+ void loadLogEntries(logSelectedDate());
870
+ }, 1500);
871
+ });
872
+
668
873
  onCleanup(() => {
669
874
  if (logNotificationTimer) {
670
875
  clearTimeout(logNotificationTimer);
671
876
  }
877
+ clearLogTailTimer();
672
878
  if (branchFooterTimer) {
673
879
  clearTimeout(branchFooterTimer);
674
880
  }
@@ -784,6 +990,11 @@ export function AppSolid(props: AppSolidProps) {
784
990
 
785
991
  // FR-010: クイックスタートからのResume(前回設定で続きから)
786
992
  const handleWizardResume = (entry: ToolSessionEntry) => {
993
+ if (!entry.sessionId) {
994
+ handleWizardStartNew(entry);
995
+ return;
996
+ }
997
+
787
998
  setWizardVisible(false);
788
999
 
789
1000
  const branch = selectedBranch();
@@ -915,25 +1126,10 @@ export function AppSolid(props: AppSolidProps) {
915
1126
  skipCounts.remote += 1;
916
1127
  continue;
917
1128
  }
918
- if (isProtectedBranchName(branch.name)) {
919
- skipCounts.protected += 1;
920
- continue;
921
- }
922
1129
  if (branch.isCurrent) {
923
1130
  skipCounts.current += 1;
924
1131
  continue;
925
1132
  }
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
1133
  const worktreePath = branch.worktree?.path ?? null;
938
1134
  const cleanupType: CleanupTask["cleanupType"] = worktreePath
939
1135
  ? "worktree-and-branch"
@@ -1051,13 +1247,14 @@ export function AppSolid(props: AppSolidProps) {
1051
1247
  return;
1052
1248
  }
1053
1249
 
1054
- // 選択されたブランチのうち、inaccessibleなものを修復対象とする
1250
+ // 選択されたローカルブランチのうち、Worktreeを持つものを修復対象とする
1055
1251
  const selectionSet = new Set(selection);
1056
1252
  const targets = branchItems()
1057
1253
  .filter(
1058
1254
  (branch) =>
1059
1255
  selectionSet.has(branch.name) &&
1060
- branch.worktreeStatus === "inaccessible",
1256
+ branch.type !== "remote" &&
1257
+ branch.worktreeStatus !== undefined,
1061
1258
  )
1062
1259
  .map((branch) => branch.name);
1063
1260
 
@@ -1309,15 +1506,51 @@ export function AppSolid(props: AppSolidProps) {
1309
1506
  };
1310
1507
 
1311
1508
  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
- });
1509
+ if (unsafeSelectionConfirmVisible()) {
1510
+ return;
1511
+ }
1512
+ const currentSelection = new Set(selectedBranches());
1513
+ if (currentSelection.has(branchName)) {
1514
+ currentSelection.delete(branchName);
1515
+ setSelectedBranches(Array.from(currentSelection));
1516
+ return;
1517
+ }
1518
+
1519
+ const branch = branchItems().find((item) => item.name === branchName);
1520
+ const pending = cleanupSafetyPending();
1521
+ const hasSafetyPending = pending.has(branchName);
1522
+ const hasUncommitted = branch?.worktree?.hasUncommittedChanges === true;
1523
+ const hasUnpushed = branch?.hasUnpushedCommits === true;
1524
+ const isUnmerged = branch?.isUnmerged === true;
1525
+ const safeToCleanup = branch?.safeToCleanup === true;
1526
+ const isRemoteBranch = branch?.type === "remote";
1527
+ const isUnsafe =
1528
+ Boolean(branch) &&
1529
+ !isRemoteBranch &&
1530
+ !hasSafetyPending &&
1531
+ (hasUncommitted || hasUnpushed || isUnmerged || !safeToCleanup);
1532
+
1533
+ if (branch && isUnsafe) {
1534
+ setUnsafeSelectionTarget(branch.name);
1535
+ setUnsafeSelectionConfirmVisible(true);
1536
+ return;
1537
+ }
1538
+
1539
+ currentSelection.add(branchName);
1540
+ setSelectedBranches(Array.from(currentSelection));
1541
+ };
1542
+
1543
+ const confirmUnsafeSelection = (confirmed: boolean) => {
1544
+ suppressBranchInputOnce();
1545
+ const target = unsafeSelectionTarget();
1546
+ setUnsafeSelectionConfirmVisible(false);
1547
+ setUnsafeSelectionTarget(null);
1548
+ if (!confirmed || !target) {
1549
+ return;
1550
+ }
1551
+ setSelectedBranches((prev) =>
1552
+ prev.includes(target) ? prev : [...prev, target],
1553
+ );
1321
1554
  };
1322
1555
 
1323
1556
  const handleToolSelect = (item: SelectorItem) => {
@@ -1371,8 +1604,9 @@ export function AppSolid(props: AppSolidProps) {
1371
1604
  const cleanupUI = {
1372
1605
  indicators: cleanupIndicators(),
1373
1606
  footerMessage: branchFooterMessage(),
1374
- inputLocked: branchInputLocked(),
1607
+ inputLocked: branchInputLocked() || unsafeConfirmInputLocked(),
1375
1608
  safetyLoading: cleanupSafetyLoading(),
1609
+ safetyPendingBranches: cleanupSafetyPending(),
1376
1610
  };
1377
1611
  return (
1378
1612
  <BranchListScreen
@@ -1390,7 +1624,13 @@ export function AppSolid(props: AppSolidProps) {
1390
1624
  version={version()}
1391
1625
  workingDirectory={workingDirectory()}
1392
1626
  activeProfile={activeProfile()}
1393
- onOpenLogs={() => navigateTo("log-list")}
1627
+ onOpenLogs={(branch) => {
1628
+ setLogTargetBranch(branch);
1629
+ setLogSelectedEntry(null);
1630
+ setLogSelectedDate(getTodayLogDate());
1631
+ setLogTailEnabled(false);
1632
+ navigateTo("log-list");
1633
+ }}
1394
1634
  onOpenProfiles={() => navigateTo("profile")}
1395
1635
  selectedBranches={selectedBranches()}
1396
1636
  onToggleSelect={toggleSelectedBranch}
@@ -1398,6 +1638,9 @@ export function AppSolid(props: AppSolidProps) {
1398
1638
  cleanupUI={cleanupUI}
1399
1639
  helpVisible={helpVisible()}
1400
1640
  wizardVisible={wizardVisible()}
1641
+ confirmVisible={unsafeSelectionConfirmVisible()}
1642
+ cursorPosition={branchCursorPosition()}
1643
+ onCursorPositionChange={setBranchCursorPosition}
1401
1644
  />
1402
1645
  );
1403
1646
  }
@@ -1473,9 +1716,15 @@ export function AppSolid(props: AppSolidProps) {
1473
1716
  showLogNotification("Failed to copy to clipboard.", "error");
1474
1717
  }
1475
1718
  }}
1719
+ onReload={() => void loadLogEntries(logSelectedDate())}
1720
+ onToggleTail={toggleLogTail}
1721
+ onReset={() => void resetLogFiles()}
1476
1722
  notification={logNotification()}
1477
1723
  version={version()}
1478
1724
  selectedDate={logSelectedDate()}
1725
+ branchLabel={logBranchLabel()}
1726
+ sourceLabel={logSourceLabel()}
1727
+ tailing={logTailEnabled()}
1479
1728
  helpVisible={helpVisible()}
1480
1729
  />
1481
1730
  );
@@ -1742,12 +1991,36 @@ export function AppSolid(props: AppSolidProps) {
1742
1991
  return (
1743
1992
  <>
1744
1993
  {renderCurrentScreen()}
1994
+ {unsafeSelectionConfirmVisible() && (
1995
+ <box
1996
+ position="absolute"
1997
+ top="30%"
1998
+ left="20%"
1999
+ width={unsafeConfirmBoxWidth()}
2000
+ zIndex={110}
2001
+ border
2002
+ borderStyle="single"
2003
+ borderColor="yellow"
2004
+ backgroundColor="black"
2005
+ padding={1}
2006
+ >
2007
+ <ConfirmScreen
2008
+ message={UNSAFE_SELECTION_MESSAGE}
2009
+ onConfirm={confirmUnsafeSelection}
2010
+ yesLabel="OK"
2011
+ noLabel="Cancel"
2012
+ defaultNo
2013
+ helpVisible={helpVisible()}
2014
+ width={unsafeConfirmContentWidth()}
2015
+ />
2016
+ </box>
2017
+ )}
1745
2018
  <HelpOverlay visible={helpVisible()} context={currentScreen()} />
1746
2019
  {/* FR-044: ウィザードポップアップをレイヤー表示 */}
1747
2020
  <WizardController
1748
2021
  visible={wizardVisible()}
1749
2022
  selectedBranchName={selectedBranch()?.name ?? ""}
1750
- history={historyForBranch()}
2023
+ history={quickStartHistory()}
1751
2024
  onClose={handleWizardClose}
1752
2025
  onComplete={handleWizardComplete}
1753
2026
  onResume={handleWizardResume}