@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,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@opentui/solid/jsx-runtime";
2
2
  /** @jsxImportSource @opentui/solid */
3
3
  import { useKeyboard, useRenderer } from "@opentui/solid";
4
- import { createEffect, createMemo, createSignal, onCleanup, onMount, } from "solid-js";
4
+ import { batch, createEffect, createMemo, createSignal, onCleanup, onMount, } from "solid-js";
5
5
  import { BranchListScreen } from "./screens/solid/BranchListScreen.js";
6
6
  import { HelpOverlay } from "./components/solid/HelpOverlay.js";
7
7
  import { WizardController, } from "./components/solid/WizardController.js";
@@ -13,6 +13,7 @@ import { LoadingIndicatorScreen } from "./screens/solid/LoadingIndicator.js";
13
13
  import { WorktreeCreateScreen } from "./screens/solid/WorktreeCreateScreen.js";
14
14
  import { InputScreen } from "./screens/solid/InputScreen.js";
15
15
  import { ConfirmScreen } from "./screens/solid/ConfirmScreen.js";
16
+ import { useTerminalSize } from "./hooks/solid/useTerminalSize.js";
16
17
  import { EnvironmentScreen } from "./screens/solid/EnvironmentScreen.js";
17
18
  import { ProfileScreen } from "./screens/solid/ProfileScreen.js";
18
19
  import { ProfileEnvScreen } from "./screens/solid/ProfileEnvScreen.js";
@@ -25,14 +26,18 @@ import { resolveBaseBranchLabel, resolveBaseBranchRef, } from "./utils/baseBranc
25
26
  import { getAllBranches, getCurrentBranch, getLocalBranches, getRepositoryRoot, deleteBranch, } from "../../git.js";
26
27
  import { isProtectedBranchName, getCleanupStatus, listAdditionalWorktrees, removeWorktree, repairWorktrees, } from "../../worktree.js";
27
28
  import { getConfig, getLastToolUsageMap, loadSession, } from "../../config/index.js";
29
+ import { findLatestBranchSessionsByTool, refreshQuickStartEntries, } from "./utils/continueSession.js";
28
30
  import { getAllCodingAgents } from "../../config/tools.js";
29
- import { buildLogFilePath, getTodayLogDate, readLogFileLines, resolveLogDir, } from "../../logging/reader.js";
31
+ import { clearLogFiles, getTodayLogDate, readLogLinesForDate, resolveLogTarget, } from "../../logging/reader.js";
30
32
  import { parseLogLines } from "../../logging/formatter.js";
31
33
  import { copyToClipboard } from "./utils/clipboard.js";
32
34
  import { getPackageVersion } from "../../utils.js";
33
35
  import { createProfile, deleteProfile, loadProfiles, setActiveProfile, updateProfile, } from "../../config/profiles.js";
34
36
  import { isValidProfileName, } from "../../types/profiles.js";
35
37
  import { BRANCH_PREFIXES } from "../../config/constants.js";
38
+ import { prefetchAgentVersions } from "./utils/versionCache.js";
39
+ import { getBunxAgentIds } from "./utils/versionFetcher.js";
40
+ const UNSAFE_SELECTION_MESSAGE = "Unsafe branch selected. Select anyway?";
36
41
  const DEFAULT_SCREEN = "branch-list";
37
42
  const buildStats = (branches) => calculateStatistics(branches);
38
43
  const applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
@@ -58,6 +63,23 @@ const applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
58
63
  };
59
64
  return worktree ? { ...base, worktree } : base;
60
65
  });
66
+ const buildCleanupSafetyPending = (items) => {
67
+ const pending = new Set();
68
+ for (const branch of items) {
69
+ if (branch.type === "remote") {
70
+ continue;
71
+ }
72
+ if (branch.worktree) {
73
+ pending.add(branch.name);
74
+ continue;
75
+ }
76
+ if (isProtectedBranchName(branch.name)) {
77
+ continue;
78
+ }
79
+ pending.add(branch.name);
80
+ }
81
+ return pending;
82
+ };
61
83
  const toLocalBranchName = (name) => {
62
84
  const segments = name.split("/");
63
85
  if (segments.length <= 1) {
@@ -73,11 +95,13 @@ const toSelectedBranchState = (branch) => {
73
95
  displayName: branch.name,
74
96
  branchType: branch.type,
75
97
  branchCategory: branch.branchType,
98
+ worktreePath: branch.worktree?.path ?? null,
76
99
  ...(isRemote ? { remoteBranch: branch.name } : {}),
77
100
  };
78
101
  };
79
102
  export function AppSolid(props) {
80
103
  const renderer = useRenderer();
104
+ const terminal = useTerminalSize();
81
105
  let hasExited = false;
82
106
  const exitApp = (result) => {
83
107
  if (hasExited)
@@ -94,6 +118,8 @@ export function AppSolid(props) {
94
118
  const [stats, setStats] = createSignal(props.stats ?? buildStats(props.branches ?? []));
95
119
  const [loading, setLoading] = createSignal(!props.branches);
96
120
  const [error, setError] = createSignal(null);
121
+ // ブランチ一覧のカーソル位置(グローバル管理で再マウント時もリセットされない)
122
+ const [branchCursorPosition, setBranchCursorPosition] = createSignal(0);
97
123
  const [toolItems, setToolItems] = createSignal([]);
98
124
  const [toolError, setToolError] = createSignal(null);
99
125
  const [version, setVersion] = createSignal(props.version ?? null);
@@ -102,11 +128,15 @@ export function AppSolid(props) {
102
128
  const [selectedTool, setSelectedTool] = createSignal(null);
103
129
  const [selectedMode, setSelectedMode] = createSignal("normal");
104
130
  const [selectedBranches, setSelectedBranches] = createSignal([]);
131
+ const [unsafeSelectionConfirmVisible, setUnsafeSelectionConfirmVisible] = createSignal(false);
132
+ const [unsafeConfirmInputLocked, setUnsafeConfirmInputLocked] = createSignal(false);
133
+ const [unsafeSelectionTarget, setUnsafeSelectionTarget] = createSignal(null);
105
134
  const [branchFooterMessage, setBranchFooterMessage] = createSignal(null);
106
135
  const [branchInputLocked, setBranchInputLocked] = createSignal(false);
107
136
  const [cleanupIndicators, setCleanupIndicators] = createSignal({});
108
137
  const [cleanupStatusByBranch, setCleanupStatusByBranch] = createSignal(new Map());
109
138
  const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
139
+ const [cleanupSafetyPending, setCleanupSafetyPending] = createSignal(new Set());
110
140
  const [isNewBranch, setIsNewBranch] = createSignal(false);
111
141
  const [newBranchBaseRef, setNewBranchBaseRef] = createSignal(null);
112
142
  const [creationSource, setCreationSource] = createSignal(null);
@@ -115,25 +145,61 @@ export function AppSolid(props) {
115
145
  });
116
146
  const [suppressCreateKey, setSuppressCreateKey] = createSignal(null);
117
147
  const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
148
+ const suppressBranchInputOnce = () => {
149
+ setUnsafeConfirmInputLocked(true);
150
+ queueMicrotask(() => {
151
+ setUnsafeConfirmInputLocked(false);
152
+ });
153
+ };
154
+ const unsafeConfirmBoxWidth = createMemo(() => {
155
+ const columns = terminal().columns || 80;
156
+ return Math.max(1, Math.floor(columns * 0.6));
157
+ });
158
+ const unsafeConfirmContentWidth = createMemo(() => Math.max(0, unsafeConfirmBoxWidth() - 4));
118
159
  // セッション履歴(最終使用エージェントなど)
119
160
  const [sessionHistory, setSessionHistory] = createSignal([]);
161
+ const [quickStartHistory, setQuickStartHistory] = createSignal([]);
120
162
  // 選択中ブランチの履歴をフィルタリング
121
163
  const historyForBranch = createMemo(() => {
122
164
  const history = sessionHistory();
123
165
  const branch = selectedBranch();
124
166
  if (!branch)
125
167
  return [];
126
- // 選択中ブランチにマッチする履歴エントリを新しい順で返す
127
- return history
128
- .filter((entry) => entry.branch === branch.name)
129
- .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
168
+ return findLatestBranchSessionsByTool(history, branch.name, branch.worktreePath ?? null);
169
+ });
170
+ createEffect(() => {
171
+ setQuickStartHistory(historyForBranch());
172
+ });
173
+ createEffect(() => {
174
+ const branch = selectedBranch();
175
+ const baseHistory = historyForBranch();
176
+ if (!wizardVisible() || !branch || baseHistory.length === 0) {
177
+ return;
178
+ }
179
+ const worktreePath = branch.worktreePath ?? null;
180
+ if (!worktreePath) {
181
+ return;
182
+ }
183
+ const branchName = branch.name;
184
+ void (async () => {
185
+ const refreshed = await refreshQuickStartEntries(baseHistory, {
186
+ branch: branchName,
187
+ worktreePath,
188
+ });
189
+ if (selectedBranch()?.name !== branchName) {
190
+ return;
191
+ }
192
+ setQuickStartHistory(refreshed);
193
+ })();
130
194
  });
131
195
  const [logEntries, setLogEntries] = createSignal([]);
132
196
  const [logLoading, setLogLoading] = createSignal(false);
133
197
  const [logError, setLogError] = createSignal(null);
134
198
  const [logSelectedEntry, setLogSelectedEntry] = createSignal(null);
135
- const [logSelectedDate, _setLogSelectedDate] = createSignal(getTodayLogDate());
199
+ const [logSelectedDate, setLogSelectedDate] = createSignal(getTodayLogDate());
136
200
  const [logNotification, setLogNotification] = createSignal(null);
201
+ const [logTailEnabled, setLogTailEnabled] = createSignal(false);
202
+ const [logTargetBranch, setLogTargetBranch] = createSignal(null);
137
203
  const [profileItems, setProfileItems] = createSignal([]);
138
204
  const [activeProfile, setActiveProfileName] = createSignal(null);
139
205
  const [profileError, setProfileError] = createSignal(null);
@@ -148,7 +214,22 @@ export function AppSolid(props) {
148
214
  const [profileInputSuppressKey, setProfileInputSuppressKey] = createSignal(null);
149
215
  const [profileEnvKey, setProfileEnvKey] = createSignal(null);
150
216
  const [profileConfirmMode, setProfileConfirmMode] = createSignal("delete-profile");
151
- const logDir = createMemo(() => resolveLogDir(workingDirectory()));
217
+ const logTarget = createMemo(() => resolveLogTarget(logTargetBranch(), workingDirectory()));
218
+ const logBranchLabel = createMemo(() => logTargetBranch()?.label ?? null);
219
+ const logSourceLabel = createMemo(() => {
220
+ const target = logTarget();
221
+ if (!target.sourcePath) {
222
+ return "(none)";
223
+ }
224
+ if (target.reason === "current-working-directory" ||
225
+ target.reason === "working-directory") {
226
+ return `${target.sourcePath} (cwd)`;
227
+ }
228
+ if (target.reason === "worktree-inaccessible") {
229
+ return `${target.sourcePath} (inaccessible)`;
230
+ }
231
+ return target.sourcePath;
232
+ });
152
233
  const selectedProfileConfig = createMemo(() => {
153
234
  const name = selectedProfileName();
154
235
  const config = profilesConfig();
@@ -173,15 +254,42 @@ export function AppSolid(props) {
173
254
  let cleanupSafetyRequestId = 0;
174
255
  const refreshCleanupSafety = async () => {
175
256
  const requestId = ++cleanupSafetyRequestId;
176
- setCleanupSafetyLoading(true);
257
+ const pendingBranches = buildCleanupSafetyPending(branchItems());
258
+ setCleanupSafetyPending(pendingBranches);
259
+ setCleanupSafetyLoading(pendingBranches.size > 0);
260
+ const statusByBranch = new Map();
261
+ const applyProgress = (status) => {
262
+ if (requestId !== cleanupSafetyRequestId) {
263
+ return;
264
+ }
265
+ statusByBranch.set(status.branch, status);
266
+ batch(() => {
267
+ setCleanupStatusByBranch(new Map(statusByBranch));
268
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
269
+ setCleanupSafetyPending((prev) => {
270
+ if (!prev.has(status.branch)) {
271
+ return prev;
272
+ }
273
+ const next = new Set(prev);
274
+ next.delete(status.branch);
275
+ return next;
276
+ });
277
+ });
278
+ };
177
279
  try {
178
- const cleanupStatuses = await getCleanupStatus();
280
+ const cleanupStatuses = await getCleanupStatus({
281
+ onProgress: applyProgress,
282
+ });
179
283
  if (requestId !== cleanupSafetyRequestId) {
180
284
  return;
181
285
  }
182
- const statusByBranch = new Map(cleanupStatuses.map((status) => [status.branch, status]));
183
- setCleanupStatusByBranch(statusByBranch);
184
- setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
286
+ if (cleanupStatuses.length > statusByBranch.size) {
287
+ cleanupStatuses.forEach((status) => {
288
+ if (!statusByBranch.has(status.branch)) {
289
+ applyProgress(status);
290
+ }
291
+ });
292
+ }
185
293
  }
186
294
  catch (err) {
187
295
  if (requestId !== cleanupSafetyRequestId) {
@@ -189,16 +297,20 @@ export function AppSolid(props) {
189
297
  }
190
298
  logger.warn({ err }, "Failed to refresh cleanup safety indicators");
191
299
  const empty = new Map();
192
- setCleanupStatusByBranch(empty);
193
- setBranchItems((items) => applyCleanupStatus(items, empty));
300
+ batch(() => {
301
+ setCleanupStatusByBranch(empty);
302
+ setBranchItems((items) => applyCleanupStatus(items, empty));
303
+ });
194
304
  }
195
305
  finally {
196
306
  if (requestId === cleanupSafetyRequestId) {
197
307
  setCleanupSafetyLoading(false);
308
+ setCleanupSafetyPending(new Set());
198
309
  }
199
310
  }
200
311
  };
201
312
  let logNotificationTimer = null;
313
+ let logTailTimer = null;
202
314
  let branchFooterTimer = null;
203
315
  const BRANCH_LOAD_TIMEOUT_MS = 3000;
204
316
  const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
@@ -263,9 +375,20 @@ export function AppSolid(props) {
263
375
  setLogLoading(true);
264
376
  setLogError(null);
265
377
  try {
266
- const filePath = buildLogFilePath(logDir(), targetDate);
267
- const lines = await readLogFileLines(filePath);
268
- const parsed = parseLogLines(lines, { limit: 100 });
378
+ const target = logTarget();
379
+ if (!target.logDir) {
380
+ setLogEntries([]);
381
+ setLogSelectedDate(targetDate);
382
+ return;
383
+ }
384
+ const result = await readLogLinesForDate(target.logDir, targetDate);
385
+ if (!result) {
386
+ setLogEntries([]);
387
+ setLogSelectedDate(targetDate);
388
+ return;
389
+ }
390
+ setLogSelectedDate(result.date);
391
+ const parsed = parseLogLines(result.lines, { limit: 100 });
269
392
  setLogEntries(parsed);
270
393
  }
271
394
  catch (err) {
@@ -276,10 +399,39 @@ export function AppSolid(props) {
276
399
  setLogLoading(false);
277
400
  }
278
401
  };
402
+ const clearLogTailTimer = () => {
403
+ if (logTailTimer) {
404
+ clearInterval(logTailTimer);
405
+ logTailTimer = null;
406
+ }
407
+ };
408
+ const toggleLogTail = () => {
409
+ setLogTailEnabled((prev) => !prev);
410
+ };
411
+ const resetLogFiles = async () => {
412
+ const target = logTarget();
413
+ if (!target.logDir) {
414
+ showLogNotification("No logs available.", "error");
415
+ return;
416
+ }
417
+ try {
418
+ const cleared = await clearLogFiles(target.logDir);
419
+ if (cleared === 0) {
420
+ showLogNotification("No logs to reset.", "error");
421
+ }
422
+ else {
423
+ showLogNotification("Logs cleared.", "success");
424
+ }
425
+ await loadLogEntries(logSelectedDate());
426
+ }
427
+ catch (err) {
428
+ logger.warn({ err }, "Failed to clear log files");
429
+ showLogNotification("Failed to reset logs.", "error");
430
+ }
431
+ };
279
432
  const refreshBranches = async () => {
280
433
  setLoading(true);
281
434
  setError(null);
282
- void refreshCleanupSafety();
283
435
  try {
284
436
  const repoRoot = await getRepositoryRoot();
285
437
  const worktreesPromise = listAdditionalWorktrees();
@@ -303,6 +455,7 @@ export function AppSolid(props) {
303
455
  const initialItems = applyCleanupStatus(initial.items, cleanupStatusByBranch());
304
456
  setBranchItems(initialItems);
305
457
  setStats(buildStats(initialItems));
458
+ void refreshCleanupSafety();
306
459
  void (async () => {
307
460
  const [branches, latestWorktrees] = await Promise.all([
308
461
  withTimeout(getAllBranches(repoRoot), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => localBranches),
@@ -400,15 +553,34 @@ export function AppSolid(props) {
400
553
  setToolError(err instanceof Error ? err : new Error(String(err)));
401
554
  });
402
555
  });
556
+ // FR-028: Prefetch npm versions for all bunx-type agents at startup (background)
557
+ onMount(() => {
558
+ const bunxAgentIds = getBunxAgentIds();
559
+ void prefetchAgentVersions(bunxAgentIds).catch(() => {
560
+ // Silently handle errors - cache will return null and UI will show "latest" only
561
+ });
562
+ });
403
563
  createEffect(() => {
404
564
  if (currentScreen() === "log-list") {
565
+ logTarget();
405
566
  void loadLogEntries(logSelectedDate());
406
567
  }
407
568
  });
569
+ createEffect(() => {
570
+ if (currentScreen() !== "log-list" || !logTailEnabled()) {
571
+ clearLogTailTimer();
572
+ return;
573
+ }
574
+ clearLogTailTimer();
575
+ logTailTimer = setInterval(() => {
576
+ void loadLogEntries(logSelectedDate());
577
+ }, 1500);
578
+ });
408
579
  onCleanup(() => {
409
580
  if (logNotificationTimer) {
410
581
  clearTimeout(logNotificationTimer);
411
582
  }
583
+ clearLogTailTimer();
412
584
  if (branchFooterTimer) {
413
585
  clearTimeout(branchFooterTimer);
414
586
  }
@@ -507,6 +679,10 @@ export function AppSolid(props) {
507
679
  };
508
680
  // FR-010: クイックスタートからのResume(前回設定で続きから)
509
681
  const handleWizardResume = (entry) => {
682
+ if (!entry.sessionId) {
683
+ handleWizardStartNew(entry);
684
+ return;
685
+ }
510
686
  setWizardVisible(false);
511
687
  const branch = selectedBranch();
512
688
  if (!branch) {
@@ -605,24 +781,10 @@ export function AppSolid(props) {
605
781
  skipCounts.remote += 1;
606
782
  continue;
607
783
  }
608
- if (isProtectedBranchName(branch.name)) {
609
- skipCounts.protected += 1;
610
- continue;
611
- }
612
784
  if (branch.isCurrent) {
613
785
  skipCounts.current += 1;
614
786
  continue;
615
787
  }
616
- const hasUncommitted = branch.worktree?.hasUncommittedChanges === true;
617
- const hasUnpushed = branch.hasUnpushedCommits === true;
618
- if (hasUncommitted || hasUnpushed) {
619
- skipCounts.unsafe += 1;
620
- continue;
621
- }
622
- if (branch.safeToCleanup !== true) {
623
- skipCounts.unsafe += 1;
624
- continue;
625
- }
626
788
  const worktreePath = branch.worktree?.path ?? null;
627
789
  const cleanupType = worktreePath
628
790
  ? "worktree-and-branch"
@@ -717,11 +879,12 @@ export function AppSolid(props) {
717
879
  showBranchFooterMessage("No branches selected.", "yellow");
718
880
  return;
719
881
  }
720
- // 選択されたブランチのうち、inaccessibleなものを修復対象とする
882
+ // 選択されたローカルブランチのうち、Worktreeを持つものを修復対象とする
721
883
  const selectionSet = new Set(selection);
722
884
  const targets = branchItems()
723
885
  .filter((branch) => selectionSet.has(branch.name) &&
724
- branch.worktreeStatus === "inaccessible")
886
+ branch.type !== "remote" &&
887
+ branch.worktreeStatus !== undefined)
725
888
  .map((branch) => branch.name);
726
889
  if (targets.length === 0) {
727
890
  showBranchFooterMessage("No worktrees to repair.", "yellow");
@@ -924,16 +1087,44 @@ export function AppSolid(props) {
924
1087
  }
925
1088
  };
926
1089
  const toggleSelectedBranch = (branchName) => {
927
- setSelectedBranches((prev) => {
928
- const next = new Set(prev);
929
- if (next.has(branchName)) {
930
- next.delete(branchName);
931
- }
932
- else {
933
- next.add(branchName);
934
- }
935
- return Array.from(next);
936
- });
1090
+ if (unsafeSelectionConfirmVisible()) {
1091
+ return;
1092
+ }
1093
+ const currentSelection = new Set(selectedBranches());
1094
+ if (currentSelection.has(branchName)) {
1095
+ currentSelection.delete(branchName);
1096
+ setSelectedBranches(Array.from(currentSelection));
1097
+ return;
1098
+ }
1099
+ const branch = branchItems().find((item) => item.name === branchName);
1100
+ const pending = cleanupSafetyPending();
1101
+ const hasSafetyPending = pending.has(branchName);
1102
+ const hasUncommitted = branch?.worktree?.hasUncommittedChanges === true;
1103
+ const hasUnpushed = branch?.hasUnpushedCommits === true;
1104
+ const isUnmerged = branch?.isUnmerged === true;
1105
+ const safeToCleanup = branch?.safeToCleanup === true;
1106
+ const isRemoteBranch = branch?.type === "remote";
1107
+ const isUnsafe = Boolean(branch) &&
1108
+ !isRemoteBranch &&
1109
+ !hasSafetyPending &&
1110
+ (hasUncommitted || hasUnpushed || isUnmerged || !safeToCleanup);
1111
+ if (branch && isUnsafe) {
1112
+ setUnsafeSelectionTarget(branch.name);
1113
+ setUnsafeSelectionConfirmVisible(true);
1114
+ return;
1115
+ }
1116
+ currentSelection.add(branchName);
1117
+ setSelectedBranches(Array.from(currentSelection));
1118
+ };
1119
+ const confirmUnsafeSelection = (confirmed) => {
1120
+ suppressBranchInputOnce();
1121
+ const target = unsafeSelectionTarget();
1122
+ setUnsafeSelectionConfirmVisible(false);
1123
+ setUnsafeSelectionTarget(null);
1124
+ if (!confirmed || !target) {
1125
+ return;
1126
+ }
1127
+ setSelectedBranches((prev) => prev.includes(target) ? prev : [...prev, target]);
937
1128
  };
938
1129
  const handleToolSelect = (item) => {
939
1130
  setSelectedTool(item.value);
@@ -980,10 +1171,17 @@ export function AppSolid(props) {
980
1171
  const cleanupUI = {
981
1172
  indicators: cleanupIndicators(),
982
1173
  footerMessage: branchFooterMessage(),
983
- inputLocked: branchInputLocked(),
1174
+ inputLocked: branchInputLocked() || unsafeConfirmInputLocked(),
984
1175
  safetyLoading: cleanupSafetyLoading(),
1176
+ safetyPendingBranches: cleanupSafetyPending(),
985
1177
  };
986
- return (_jsx(BranchListScreen, { branches: branchItems(), stats: stats(), onSelect: handleBranchSelect, onQuit: () => exitApp(undefined), onCleanupCommand: handleCleanupCommand, onRefresh: refreshBranches, onRepairWorktrees: handleRepairWorktrees, loading: loading(), error: error(), loadingIndicatorDelay: props.loadingIndicatorDelay ?? 0, lastUpdated: stats().lastUpdated, version: version(), workingDirectory: workingDirectory(), activeProfile: activeProfile(), onOpenLogs: () => navigateTo("log-list"), onOpenProfiles: () => navigateTo("profile"), selectedBranches: selectedBranches(), onToggleSelect: toggleSelectedBranch, onCreateBranch: handleQuickCreate, cleanupUI: cleanupUI, helpVisible: helpVisible(), wizardVisible: wizardVisible() }));
1178
+ return (_jsx(BranchListScreen, { branches: branchItems(), stats: stats(), onSelect: handleBranchSelect, onQuit: () => exitApp(undefined), onCleanupCommand: handleCleanupCommand, onRefresh: refreshBranches, onRepairWorktrees: handleRepairWorktrees, loading: loading(), error: error(), loadingIndicatorDelay: props.loadingIndicatorDelay ?? 0, lastUpdated: stats().lastUpdated, version: version(), workingDirectory: workingDirectory(), activeProfile: activeProfile(), onOpenLogs: (branch) => {
1179
+ setLogTargetBranch(branch);
1180
+ setLogSelectedEntry(null);
1181
+ setLogSelectedDate(getTodayLogDate());
1182
+ setLogTailEnabled(false);
1183
+ navigateTo("log-list");
1184
+ }, onOpenProfiles: () => navigateTo("profile"), selectedBranches: selectedBranches(), onToggleSelect: toggleSelectedBranch, onCreateBranch: handleQuickCreate, cleanupUI: cleanupUI, helpVisible: helpVisible(), wizardVisible: wizardVisible(), confirmVisible: unsafeSelectionConfirmVisible(), cursorPosition: branchCursorPosition(), onCursorPositionChange: setBranchCursorPosition }));
987
1185
  }
988
1186
  if (screen === "tool-select") {
989
1187
  if (toolError()) {
@@ -1016,7 +1214,7 @@ export function AppSolid(props) {
1016
1214
  catch {
1017
1215
  showLogNotification("Failed to copy to clipboard.", "error");
1018
1216
  }
1019
- }, notification: logNotification(), version: version(), selectedDate: logSelectedDate(), helpVisible: helpVisible() }));
1217
+ }, onReload: () => void loadLogEntries(logSelectedDate()), onToggleTail: toggleLogTail, onReset: () => void resetLogFiles(), notification: logNotification(), version: version(), selectedDate: logSelectedDate(), branchLabel: logBranchLabel(), sourceLabel: logSourceLabel(), tailing: logTailEnabled(), helpVisible: helpVisible() }));
1020
1218
  }
1021
1219
  if (screen === "log-detail") {
1022
1220
  return (_jsx(LogDetailScreen, { entry: logSelectedEntry(), onBack: goBack, onCopy: async (entry) => {
@@ -1143,7 +1341,7 @@ export function AppSolid(props) {
1143
1341
  }
1144
1342
  return (_jsx(ErrorScreen, { error: `Unknown screen: ${screen}`, onBack: goBack, helpVisible: helpVisible() }));
1145
1343
  };
1146
- return (_jsxs(_Fragment, { children: [renderCurrentScreen(), _jsx(HelpOverlay, { visible: helpVisible(), context: currentScreen() }), _jsx(WizardController, { visible: wizardVisible(), selectedBranchName: selectedBranch()?.name ?? "", history: historyForBranch(), onClose: handleWizardClose, onComplete: handleWizardComplete, onResume: handleWizardResume, onStartNew: handleWizardStartNew })] }));
1344
+ return (_jsxs(_Fragment, { children: [renderCurrentScreen(), unsafeSelectionConfirmVisible() && (_jsx("box", { position: "absolute", top: "30%", left: "20%", width: unsafeConfirmBoxWidth(), zIndex: 110, border: true, borderStyle: "single", borderColor: "yellow", backgroundColor: "black", padding: 1, children: _jsx(ConfirmScreen, { message: UNSAFE_SELECTION_MESSAGE, onConfirm: confirmUnsafeSelection, yesLabel: "OK", noLabel: "Cancel", defaultNo: true, helpVisible: helpVisible(), width: unsafeConfirmContentWidth() }) })), _jsx(HelpOverlay, { visible: helpVisible(), context: currentScreen() }), _jsx(WizardController, { visible: wizardVisible(), selectedBranchName: selectedBranch()?.name ?? "", history: quickStartHistory(), onClose: handleWizardClose, onComplete: handleWizardComplete, onResume: handleWizardResume, onStartNew: handleWizardStartNew })] }));
1147
1345
  }
1148
1346
  const withTimeout = async (promise, timeoutMs) => new Promise((resolve, reject) => {
1149
1347
  const timer = setTimeout(() => {