@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,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";
@@ -22,17 +23,23 @@ import { createLogger } from "../../logging/logger.js";
22
23
  const logger = createLogger({ category: "app" });
23
24
  import { getDefaultInferenceForModel, getDefaultModelOption, normalizeModelId, } from "./utils/modelOptions.js";
24
25
  import { resolveBaseBranchLabel, resolveBaseBranchRef, } from "./utils/baseBranch.js";
25
- import { getAllBranches, getCurrentBranch, getLocalBranches, getRepositoryRoot, deleteBranch, } from "../../git.js";
26
+ import { getAllBranches, getCurrentBranch, getLocalBranches, getRepositoryRoot, deleteBranch, fetchAllRemotes, } 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, selectLogTargetByRecency, } 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 { prefetchInstalledVersions } from "./utils/installedVersionCache.js";
40
+ import { getBunxAgentIds } from "./utils/versionFetcher.js";
41
+ const UNSAFE_SELECTION_MESSAGE = "Unsafe branch selected. Select anyway?";
42
+ const SAFETY_PENDING_MESSAGE = "Safety check in progress. Select anyway?";
36
43
  const DEFAULT_SCREEN = "branch-list";
37
44
  const buildStats = (branches) => calculateStatistics(branches);
38
45
  const applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
@@ -58,6 +65,23 @@ const applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
58
65
  };
59
66
  return worktree ? { ...base, worktree } : base;
60
67
  });
68
+ const buildCleanupSafetyPending = (items) => {
69
+ const pending = new Set();
70
+ for (const branch of items) {
71
+ if (branch.type === "remote") {
72
+ continue;
73
+ }
74
+ if (branch.worktree) {
75
+ pending.add(branch.name);
76
+ continue;
77
+ }
78
+ if (isProtectedBranchName(branch.name)) {
79
+ continue;
80
+ }
81
+ pending.add(branch.name);
82
+ }
83
+ return pending;
84
+ };
61
85
  const toLocalBranchName = (name) => {
62
86
  const segments = name.split("/");
63
87
  if (segments.length <= 1) {
@@ -73,11 +97,13 @@ const toSelectedBranchState = (branch) => {
73
97
  displayName: branch.name,
74
98
  branchType: branch.type,
75
99
  branchCategory: branch.branchType,
100
+ worktreePath: branch.worktree?.path ?? null,
76
101
  ...(isRemote ? { remoteBranch: branch.name } : {}),
77
102
  };
78
103
  };
79
104
  export function AppSolid(props) {
80
105
  const renderer = useRenderer();
106
+ const terminal = useTerminalSize();
81
107
  let hasExited = false;
82
108
  const exitApp = (result) => {
83
109
  if (hasExited)
@@ -94,6 +120,8 @@ export function AppSolid(props) {
94
120
  const [stats, setStats] = createSignal(props.stats ?? buildStats(props.branches ?? []));
95
121
  const [loading, setLoading] = createSignal(!props.branches);
96
122
  const [error, setError] = createSignal(null);
123
+ // ブランチ一覧のカーソル位置(グローバル管理で再マウント時もリセットされない)
124
+ const [branchCursorPosition, setBranchCursorPosition] = createSignal(0);
97
125
  const [toolItems, setToolItems] = createSignal([]);
98
126
  const [toolError, setToolError] = createSignal(null);
99
127
  const [version, setVersion] = createSignal(props.version ?? null);
@@ -102,11 +130,16 @@ export function AppSolid(props) {
102
130
  const [selectedTool, setSelectedTool] = createSignal(null);
103
131
  const [selectedMode, setSelectedMode] = createSignal("normal");
104
132
  const [selectedBranches, setSelectedBranches] = createSignal([]);
133
+ const [unsafeSelectionConfirmVisible, setUnsafeSelectionConfirmVisible] = createSignal(false);
134
+ const [unsafeConfirmInputLocked, setUnsafeConfirmInputLocked] = createSignal(false);
135
+ const [unsafeSelectionTarget, setUnsafeSelectionTarget] = createSignal(null);
136
+ const [unsafeSelectionMessage, setUnsafeSelectionMessage] = createSignal(UNSAFE_SELECTION_MESSAGE);
105
137
  const [branchFooterMessage, setBranchFooterMessage] = createSignal(null);
106
138
  const [branchInputLocked, setBranchInputLocked] = createSignal(false);
107
139
  const [cleanupIndicators, setCleanupIndicators] = createSignal({});
108
140
  const [cleanupStatusByBranch, setCleanupStatusByBranch] = createSignal(new Map());
109
141
  const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
142
+ const [cleanupSafetyPending, setCleanupSafetyPending] = createSignal(new Set());
110
143
  const [isNewBranch, setIsNewBranch] = createSignal(false);
111
144
  const [newBranchBaseRef, setNewBranchBaseRef] = createSignal(null);
112
145
  const [creationSource, setCreationSource] = createSignal(null);
@@ -115,25 +148,61 @@ export function AppSolid(props) {
115
148
  });
116
149
  const [suppressCreateKey, setSuppressCreateKey] = createSignal(null);
117
150
  const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
151
+ const suppressBranchInputOnce = () => {
152
+ setUnsafeConfirmInputLocked(true);
153
+ queueMicrotask(() => {
154
+ setUnsafeConfirmInputLocked(false);
155
+ });
156
+ };
157
+ const unsafeConfirmBoxWidth = createMemo(() => {
158
+ const columns = terminal().columns || 80;
159
+ return Math.max(1, Math.floor(columns * 0.6));
160
+ });
161
+ const unsafeConfirmContentWidth = createMemo(() => Math.max(0, unsafeConfirmBoxWidth() - 4));
118
162
  // セッション履歴(最終使用エージェントなど)
119
163
  const [sessionHistory, setSessionHistory] = createSignal([]);
164
+ const [quickStartHistory, setQuickStartHistory] = createSignal([]);
120
165
  // 選択中ブランチの履歴をフィルタリング
121
166
  const historyForBranch = createMemo(() => {
122
167
  const history = sessionHistory();
123
168
  const branch = selectedBranch();
124
169
  if (!branch)
125
170
  return [];
126
- // 選択中ブランチにマッチする履歴エントリを新しい順で返す
127
- return history
128
- .filter((entry) => entry.branch === branch.name)
129
- .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
171
+ return findLatestBranchSessionsByTool(history, branch.name, branch.worktreePath ?? null);
172
+ });
173
+ createEffect(() => {
174
+ setQuickStartHistory(historyForBranch());
175
+ });
176
+ createEffect(() => {
177
+ const branch = selectedBranch();
178
+ const baseHistory = historyForBranch();
179
+ if (!wizardVisible() || !branch || baseHistory.length === 0) {
180
+ return;
181
+ }
182
+ const worktreePath = branch.worktreePath ?? null;
183
+ if (!worktreePath) {
184
+ return;
185
+ }
186
+ const branchName = branch.name;
187
+ void (async () => {
188
+ const refreshed = await refreshQuickStartEntries(baseHistory, {
189
+ branch: branchName,
190
+ worktreePath,
191
+ });
192
+ if (selectedBranch()?.name !== branchName) {
193
+ return;
194
+ }
195
+ setQuickStartHistory(refreshed);
196
+ })();
130
197
  });
131
198
  const [logEntries, setLogEntries] = createSignal([]);
132
199
  const [logLoading, setLogLoading] = createSignal(false);
133
200
  const [logError, setLogError] = createSignal(null);
134
201
  const [logSelectedEntry, setLogSelectedEntry] = createSignal(null);
135
- const [logSelectedDate, _setLogSelectedDate] = createSignal(getTodayLogDate());
202
+ const [logSelectedDate, setLogSelectedDate] = createSignal(getTodayLogDate());
136
203
  const [logNotification, setLogNotification] = createSignal(null);
204
+ const [logTailEnabled, setLogTailEnabled] = createSignal(false);
205
+ const [logTargetBranch, setLogTargetBranch] = createSignal(null);
137
206
  const [profileItems, setProfileItems] = createSignal([]);
138
207
  const [activeProfile, setActiveProfileName] = createSignal(null);
139
208
  const [profileError, setProfileError] = createSignal(null);
@@ -148,7 +217,32 @@ export function AppSolid(props) {
148
217
  const [profileInputSuppressKey, setProfileInputSuppressKey] = createSignal(null);
149
218
  const [profileEnvKey, setProfileEnvKey] = createSignal(null);
150
219
  const [profileConfirmMode, setProfileConfirmMode] = createSignal("delete-profile");
151
- const logDir = createMemo(() => resolveLogDir(workingDirectory()));
220
+ const [logEffectiveTarget, setLogEffectiveTarget] = createSignal(null);
221
+ const logPrimaryTarget = createMemo(() => resolveLogTarget(logTargetBranch(), workingDirectory()));
222
+ const logFallbackTarget = createMemo(() => resolveLogTarget(null, workingDirectory()));
223
+ const logActiveTarget = createMemo(() => logEffectiveTarget() ?? logPrimaryTarget());
224
+ const logBranchLabel = createMemo(() => logTargetBranch()?.label ?? null);
225
+ const logSourceLabel = createMemo(() => {
226
+ const target = logActiveTarget();
227
+ if (!target.sourcePath) {
228
+ return "(none)";
229
+ }
230
+ if (target.reason === "current-working-directory" ||
231
+ target.reason === "working-directory") {
232
+ return `${target.sourcePath} (cwd)`;
233
+ }
234
+ if (target.reason === "working-directory-fallback") {
235
+ return `${target.sourcePath} (cwd fallback)`;
236
+ }
237
+ if (target.reason === "worktree-inaccessible") {
238
+ return `${target.sourcePath} (inaccessible)`;
239
+ }
240
+ return target.sourcePath;
241
+ });
242
+ createEffect(() => {
243
+ logPrimaryTarget();
244
+ setLogEffectiveTarget(null);
245
+ });
152
246
  const selectedProfileConfig = createMemo(() => {
153
247
  const name = selectedProfileName();
154
248
  const config = profilesConfig();
@@ -173,15 +267,42 @@ export function AppSolid(props) {
173
267
  let cleanupSafetyRequestId = 0;
174
268
  const refreshCleanupSafety = async () => {
175
269
  const requestId = ++cleanupSafetyRequestId;
176
- setCleanupSafetyLoading(true);
270
+ const pendingBranches = buildCleanupSafetyPending(branchItems());
271
+ setCleanupSafetyPending(pendingBranches);
272
+ setCleanupSafetyLoading(pendingBranches.size > 0);
273
+ const statusByBranch = new Map();
274
+ const applyProgress = (status) => {
275
+ if (requestId !== cleanupSafetyRequestId) {
276
+ return;
277
+ }
278
+ statusByBranch.set(status.branch, status);
279
+ batch(() => {
280
+ setCleanupStatusByBranch(new Map(statusByBranch));
281
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
282
+ setCleanupSafetyPending((prev) => {
283
+ if (!prev.has(status.branch)) {
284
+ return prev;
285
+ }
286
+ const next = new Set(prev);
287
+ next.delete(status.branch);
288
+ return next;
289
+ });
290
+ });
291
+ };
177
292
  try {
178
- const cleanupStatuses = await getCleanupStatus();
293
+ const cleanupStatuses = await getCleanupStatus({
294
+ onProgress: applyProgress,
295
+ });
179
296
  if (requestId !== cleanupSafetyRequestId) {
180
297
  return;
181
298
  }
182
- const statusByBranch = new Map(cleanupStatuses.map((status) => [status.branch, status]));
183
- setCleanupStatusByBranch(statusByBranch);
184
- setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
299
+ if (cleanupStatuses.length > statusByBranch.size) {
300
+ cleanupStatuses.forEach((status) => {
301
+ if (!statusByBranch.has(status.branch)) {
302
+ applyProgress(status);
303
+ }
304
+ });
305
+ }
185
306
  }
186
307
  catch (err) {
187
308
  if (requestId !== cleanupSafetyRequestId) {
@@ -189,16 +310,20 @@ export function AppSolid(props) {
189
310
  }
190
311
  logger.warn({ err }, "Failed to refresh cleanup safety indicators");
191
312
  const empty = new Map();
192
- setCleanupStatusByBranch(empty);
193
- setBranchItems((items) => applyCleanupStatus(items, empty));
313
+ batch(() => {
314
+ setCleanupStatusByBranch(empty);
315
+ setBranchItems((items) => applyCleanupStatus(items, empty));
316
+ });
194
317
  }
195
318
  finally {
196
319
  if (requestId === cleanupSafetyRequestId) {
197
320
  setCleanupSafetyLoading(false);
321
+ setCleanupSafetyPending(new Set());
198
322
  }
199
323
  }
200
324
  };
201
325
  let logNotificationTimer = null;
326
+ let logTailTimer = null;
202
327
  let branchFooterTimer = null;
203
328
  const BRANCH_LOAD_TIMEOUT_MS = 3000;
204
329
  const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
@@ -263,9 +388,23 @@ export function AppSolid(props) {
263
388
  setLogLoading(true);
264
389
  setLogError(null);
265
390
  try {
266
- const filePath = buildLogFilePath(logDir(), targetDate);
267
- const lines = await readLogFileLines(filePath);
268
- const parsed = parseLogLines(lines, { limit: 100 });
391
+ const primaryTarget = logPrimaryTarget();
392
+ const fallbackTarget = logFallbackTarget();
393
+ const target = await selectLogTargetByRecency(primaryTarget, fallbackTarget);
394
+ setLogEffectiveTarget(target);
395
+ if (!target.logDir) {
396
+ setLogEntries([]);
397
+ setLogSelectedDate(targetDate);
398
+ return;
399
+ }
400
+ const result = await readLogLinesForDate(target.logDir, targetDate);
401
+ if (!result) {
402
+ setLogEntries([]);
403
+ setLogSelectedDate(targetDate);
404
+ return;
405
+ }
406
+ setLogSelectedDate(result.date);
407
+ const parsed = parseLogLines(result.lines, { limit: 100 });
269
408
  setLogEntries(parsed);
270
409
  }
271
410
  catch (err) {
@@ -276,10 +415,39 @@ export function AppSolid(props) {
276
415
  setLogLoading(false);
277
416
  }
278
417
  };
418
+ const clearLogTailTimer = () => {
419
+ if (logTailTimer) {
420
+ clearInterval(logTailTimer);
421
+ logTailTimer = null;
422
+ }
423
+ };
424
+ const toggleLogTail = () => {
425
+ setLogTailEnabled((prev) => !prev);
426
+ };
427
+ const resetLogFiles = async () => {
428
+ const target = logActiveTarget();
429
+ if (!target.logDir) {
430
+ showLogNotification("No logs available.", "error");
431
+ return;
432
+ }
433
+ try {
434
+ const cleared = await clearLogFiles(target.logDir);
435
+ if (cleared === 0) {
436
+ showLogNotification("No logs to reset.", "error");
437
+ }
438
+ else {
439
+ showLogNotification("Logs cleared.", "success");
440
+ }
441
+ await loadLogEntries(logSelectedDate());
442
+ }
443
+ catch (err) {
444
+ logger.warn({ err }, "Failed to clear log files");
445
+ showLogNotification("Failed to reset logs.", "error");
446
+ }
447
+ };
279
448
  const refreshBranches = async () => {
280
449
  setLoading(true);
281
450
  setError(null);
282
- void refreshCleanupSafety();
283
451
  try {
284
452
  const repoRoot = await getRepositoryRoot();
285
453
  const worktreesPromise = listAdditionalWorktrees();
@@ -303,7 +471,12 @@ export function AppSolid(props) {
303
471
  const initialItems = applyCleanupStatus(initial.items, cleanupStatusByBranch());
304
472
  setBranchItems(initialItems);
305
473
  setStats(buildStats(initialItems));
474
+ void refreshCleanupSafety();
306
475
  void (async () => {
476
+ // Fetch remote updates with --prune to remove stale tracking refs
477
+ await withTimeout(fetchAllRemotes({ cwd: repoRoot }), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => {
478
+ // Ignore fetch errors (e.g., network issues)
479
+ });
307
480
  const [branches, latestWorktrees] = await Promise.all([
308
481
  withTimeout(getAllBranches(repoRoot), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => localBranches),
309
482
  withTimeout(listAdditionalWorktrees(), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => worktrees),
@@ -400,15 +573,38 @@ export function AppSolid(props) {
400
573
  setToolError(err instanceof Error ? err : new Error(String(err)));
401
574
  });
402
575
  });
576
+ // FR-028: Prefetch npm versions for all bunx-type agents at startup (background)
577
+ onMount(() => {
578
+ const bunxAgentIds = getBunxAgentIds();
579
+ void prefetchAgentVersions(bunxAgentIds).catch(() => {
580
+ // Silently handle errors - cache will return null and UI will show "latest" only
581
+ });
582
+ // FR-017: Prefetch installed versions for all builtin agents at startup
583
+ void prefetchInstalledVersions(bunxAgentIds).catch(() => {
584
+ // Silently handle errors - cache will return null and UI won't show "installed"
585
+ });
586
+ });
403
587
  createEffect(() => {
404
588
  if (currentScreen() === "log-list") {
589
+ logPrimaryTarget();
405
590
  void loadLogEntries(logSelectedDate());
406
591
  }
407
592
  });
593
+ createEffect(() => {
594
+ if (currentScreen() !== "log-list" || !logTailEnabled()) {
595
+ clearLogTailTimer();
596
+ return;
597
+ }
598
+ clearLogTailTimer();
599
+ logTailTimer = setInterval(() => {
600
+ void loadLogEntries(logSelectedDate());
601
+ }, 1500);
602
+ });
408
603
  onCleanup(() => {
409
604
  if (logNotificationTimer) {
410
605
  clearTimeout(logNotificationTimer);
411
606
  }
607
+ clearLogTailTimer();
412
608
  if (branchFooterTimer) {
413
609
  clearTimeout(branchFooterTimer);
414
610
  }
@@ -507,6 +703,10 @@ export function AppSolid(props) {
507
703
  };
508
704
  // FR-010: クイックスタートからのResume(前回設定で続きから)
509
705
  const handleWizardResume = (entry) => {
706
+ if (!entry.sessionId) {
707
+ handleWizardStartNew(entry);
708
+ return;
709
+ }
510
710
  setWizardVisible(false);
511
711
  const branch = selectedBranch();
512
712
  if (!branch) {
@@ -605,24 +805,10 @@ export function AppSolid(props) {
605
805
  skipCounts.remote += 1;
606
806
  continue;
607
807
  }
608
- if (isProtectedBranchName(branch.name)) {
609
- skipCounts.protected += 1;
610
- continue;
611
- }
612
808
  if (branch.isCurrent) {
613
809
  skipCounts.current += 1;
614
810
  continue;
615
811
  }
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
812
  const worktreePath = branch.worktree?.path ?? null;
627
813
  const cleanupType = worktreePath
628
814
  ? "worktree-and-branch"
@@ -717,11 +903,12 @@ export function AppSolid(props) {
717
903
  showBranchFooterMessage("No branches selected.", "yellow");
718
904
  return;
719
905
  }
720
- // 選択されたブランチのうち、inaccessibleなものを修復対象とする
906
+ // 選択されたローカルブランチのうち、Worktreeを持つものを修復対象とする
721
907
  const selectionSet = new Set(selection);
722
908
  const targets = branchItems()
723
909
  .filter((branch) => selectionSet.has(branch.name) &&
724
- branch.worktreeStatus === "inaccessible")
910
+ branch.type !== "remote" &&
911
+ branch.worktreeStatus !== undefined)
725
912
  .map((branch) => branch.name);
726
913
  if (targets.length === 0) {
727
914
  showBranchFooterMessage("No worktrees to repair.", "yellow");
@@ -924,16 +1111,52 @@ export function AppSolid(props) {
924
1111
  }
925
1112
  };
926
1113
  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
- });
1114
+ if (unsafeSelectionConfirmVisible()) {
1115
+ return;
1116
+ }
1117
+ const currentSelection = new Set(selectedBranches());
1118
+ if (currentSelection.has(branchName)) {
1119
+ currentSelection.delete(branchName);
1120
+ setSelectedBranches(Array.from(currentSelection));
1121
+ return;
1122
+ }
1123
+ const branch = branchItems().find((item) => item.name === branchName);
1124
+ const pending = cleanupSafetyPending();
1125
+ const hasSafetyPending = pending.has(branchName);
1126
+ const hasUncommitted = branch?.worktree?.hasUncommittedChanges === true;
1127
+ const hasUnpushed = branch?.hasUnpushedCommits === true;
1128
+ const isUnmerged = branch?.isUnmerged === true;
1129
+ const safeToCleanup = branch?.safeToCleanup === true;
1130
+ const isRemoteBranch = branch?.type === "remote";
1131
+ const isUnsafe = Boolean(branch) &&
1132
+ !isRemoteBranch &&
1133
+ !hasSafetyPending &&
1134
+ (hasUncommitted || hasUnpushed || isUnmerged || !safeToCleanup);
1135
+ if (branch && hasSafetyPending) {
1136
+ setUnsafeSelectionTarget(branch.name);
1137
+ setUnsafeSelectionMessage(SAFETY_PENDING_MESSAGE);
1138
+ setUnsafeSelectionConfirmVisible(true);
1139
+ return;
1140
+ }
1141
+ if (branch && isUnsafe) {
1142
+ setUnsafeSelectionTarget(branch.name);
1143
+ setUnsafeSelectionMessage(UNSAFE_SELECTION_MESSAGE);
1144
+ setUnsafeSelectionConfirmVisible(true);
1145
+ return;
1146
+ }
1147
+ currentSelection.add(branchName);
1148
+ setSelectedBranches(Array.from(currentSelection));
1149
+ };
1150
+ const confirmUnsafeSelection = (confirmed) => {
1151
+ suppressBranchInputOnce();
1152
+ const target = unsafeSelectionTarget();
1153
+ setUnsafeSelectionConfirmVisible(false);
1154
+ setUnsafeSelectionTarget(null);
1155
+ setUnsafeSelectionMessage(UNSAFE_SELECTION_MESSAGE);
1156
+ if (!confirmed || !target) {
1157
+ return;
1158
+ }
1159
+ setSelectedBranches((prev) => prev.includes(target) ? prev : [...prev, target]);
937
1160
  };
938
1161
  const handleToolSelect = (item) => {
939
1162
  setSelectedTool(item.value);
@@ -980,10 +1203,17 @@ export function AppSolid(props) {
980
1203
  const cleanupUI = {
981
1204
  indicators: cleanupIndicators(),
982
1205
  footerMessage: branchFooterMessage(),
983
- inputLocked: branchInputLocked(),
1206
+ inputLocked: branchInputLocked() || unsafeConfirmInputLocked(),
984
1207
  safetyLoading: cleanupSafetyLoading(),
1208
+ safetyPendingBranches: cleanupSafetyPending(),
985
1209
  };
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() }));
1210
+ 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) => {
1211
+ setLogTargetBranch(branch);
1212
+ setLogSelectedEntry(null);
1213
+ setLogSelectedDate(getTodayLogDate());
1214
+ setLogTailEnabled(false);
1215
+ navigateTo("log-list");
1216
+ }, onOpenProfiles: () => navigateTo("profile"), selectedBranches: selectedBranches(), onToggleSelect: toggleSelectedBranch, onCreateBranch: handleQuickCreate, cleanupUI: cleanupUI, helpVisible: helpVisible(), wizardVisible: wizardVisible(), confirmVisible: unsafeSelectionConfirmVisible(), cursorPosition: branchCursorPosition(), onCursorPositionChange: setBranchCursorPosition }));
987
1217
  }
988
1218
  if (screen === "tool-select") {
989
1219
  if (toolError()) {
@@ -1016,7 +1246,7 @@ export function AppSolid(props) {
1016
1246
  catch {
1017
1247
  showLogNotification("Failed to copy to clipboard.", "error");
1018
1248
  }
1019
- }, notification: logNotification(), version: version(), selectedDate: logSelectedDate(), helpVisible: helpVisible() }));
1249
+ }, onReload: () => void loadLogEntries(logSelectedDate()), onToggleTail: toggleLogTail, onReset: () => void resetLogFiles(), notification: logNotification(), version: version(), selectedDate: logSelectedDate(), branchLabel: logBranchLabel(), sourceLabel: logSourceLabel(), tailing: logTailEnabled(), helpVisible: helpVisible() }));
1020
1250
  }
1021
1251
  if (screen === "log-detail") {
1022
1252
  return (_jsx(LogDetailScreen, { entry: logSelectedEntry(), onBack: goBack, onCopy: async (entry) => {
@@ -1143,7 +1373,7 @@ export function AppSolid(props) {
1143
1373
  }
1144
1374
  return (_jsx(ErrorScreen, { error: `Unknown screen: ${screen}`, onBack: goBack, helpVisible: helpVisible() }));
1145
1375
  };
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 })] }));
1376
+ 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: unsafeSelectionMessage(), 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
1377
  }
1148
1378
  const withTimeout = async (promise, timeoutMs) => new Promise((resolve, reject) => {
1149
1379
  const timer = setTimeout(() => {