@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,5 +1,11 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { createSignal, createEffect, createMemo, Show } from "solid-js";
2
+ import {
3
+ createSignal,
4
+ createEffect,
5
+ createMemo,
6
+ onCleanup,
7
+ Show,
8
+ } from "solid-js";
3
9
  import { useKeyboard } from "@opentui/solid";
4
10
  import type { ToolSessionEntry } from "../../../../config/index.js";
5
11
  import type { CodingAgentId, InferenceLevel } from "../../types.js";
@@ -13,6 +19,7 @@ import {
13
19
  AgentSelectStep,
14
20
  VersionSelectStep,
15
21
  ModelSelectStep,
22
+ ModelInputStep,
16
23
  ReasoningLevelStep,
17
24
  ExecutionModeStep,
18
25
  SkipPermissionsStep,
@@ -53,6 +60,7 @@ type WizardStep =
53
60
  | "agent-select"
54
61
  | "version-select"
55
62
  | "model-select"
63
+ | "model-input"
56
64
  | "reasoning-level"
57
65
  | "execution-mode"
58
66
  | "skip-permissions";
@@ -87,9 +95,11 @@ export function WizardController(props: WizardControllerProps) {
87
95
  >(undefined);
88
96
  const [executionMode, setExecutionMode] =
89
97
  createSignal<ExecutionMode>("normal");
98
+ const [versionSelectionReady, setVersionSelectionReady] = createSignal(false);
90
99
 
91
- // キー伝播防止: 最初のフレームでは focused を無効にする
92
- const [isInitialFrame, setIsInitialFrame] = createSignal(true);
100
+ // キー伝播防止: ステップ遷移直後は focused を無効にする
101
+ const [isTransitioning, setIsTransitioning] = createSignal(true);
102
+ let versionSelectionTimer: ReturnType<typeof setTimeout> | null = null;
93
103
 
94
104
  // Reset state when wizard becomes visible
95
105
  function getInitialStep(): WizardStep {
@@ -115,6 +125,13 @@ export function WizardController(props: WizardControllerProps) {
115
125
  setExecutionMode("normal");
116
126
  };
117
127
 
128
+ // ステップ遷移時にキー伝搬防止を有効化するヘルパー
129
+ const startTransition = () => {
130
+ setIsTransitioning(true);
131
+ // 50ms後にフォーカスを有効化(キー伝搬を防止しつつ、応答性を維持)
132
+ setTimeout(() => setIsTransitioning(false), 50);
133
+ };
134
+
118
135
  // Reset wizard when it becomes visible
119
136
  let prevVisible = false;
120
137
  createEffect(() => {
@@ -123,19 +140,17 @@ export function WizardController(props: WizardControllerProps) {
123
140
  resetWizard();
124
141
  // キー伝播防止: 最初の数フレームでは focused を無効にする
125
142
  // Enter キーが複数フレームにわたって伝播する可能性があるため、長めに設定
126
- setIsInitialFrame(true);
127
- // 十分な時間を置いて focused を有効化 (150ms)
128
- setTimeout(() => setIsInitialFrame(false), 150);
143
+ startTransition();
129
144
  }
130
145
  prevVisible = visible;
131
146
  });
132
147
 
133
148
  // Handle keyboard events for step navigation
134
- // T412: 最初のフレームでは Enter キーをブロックして伝播を防ぐ
149
+ // T412: ステップ遷移直後は Enter キーをブロックして伝播を防ぐ
135
150
  useKeyboard((key) => {
136
151
  if (!props.visible) return;
137
- // 最初のフレームでは Enter キーを無視(ブランチ選択からの伝播防止)
138
- if (isInitialFrame() && key.name === "return") {
152
+ // ステップ遷移直後は Enter キーを無視(ステップ間のキー伝播防止)
153
+ if (isTransitioning() && key.name === "return") {
139
154
  return;
140
155
  }
141
156
  if (key.name === "escape") {
@@ -146,6 +161,8 @@ export function WizardController(props: WizardControllerProps) {
146
161
  const goToStep = (nextStep: WizardStep) => {
147
162
  setStepHistory((prev) => [...prev, step()]);
148
163
  setStep(nextStep);
164
+ // ステップ遷移時にキー伝搬防止を有効化
165
+ startTransition();
149
166
  };
150
167
 
151
168
  const goBack = () => {
@@ -157,8 +174,32 @@ export function WizardController(props: WizardControllerProps) {
157
174
  const previousStep = history[history.length - 1] ?? "agent-select";
158
175
  setStepHistory(history.slice(0, -1));
159
176
  setStep(previousStep);
177
+ // ステップ遷移時にキー伝搬防止を有効化
178
+ startTransition();
160
179
  };
161
180
 
181
+ createEffect(() => {
182
+ const currentStep = step();
183
+ if (versionSelectionTimer) {
184
+ clearTimeout(versionSelectionTimer);
185
+ versionSelectionTimer = null;
186
+ }
187
+ if (currentStep === "version-select") {
188
+ setVersionSelectionReady(false);
189
+ versionSelectionTimer = setTimeout(() => {
190
+ setVersionSelectionReady(true);
191
+ }, 50);
192
+ return;
193
+ }
194
+ setVersionSelectionReady(false);
195
+ });
196
+
197
+ onCleanup(() => {
198
+ if (versionSelectionTimer) {
199
+ clearTimeout(versionSelectionTimer);
200
+ }
201
+ });
202
+
162
203
  // Determine if reasoning level step is needed
163
204
  const needsReasoningLevel = createMemo(() => {
164
205
  return selectedAgent() === "codex-cli";
@@ -204,12 +245,20 @@ export function WizardController(props: WizardControllerProps) {
204
245
  };
205
246
 
206
247
  const handleVersionSelect = (version: string) => {
207
- // "installed" null として保存(bunx のデフォルト動作)
208
- setSelectedVersion(version === "installed" ? null : version);
248
+ if (!versionSelectionReady() || step() !== "version-select") {
249
+ return;
250
+ }
251
+ // "installed" を明示的に保存し、未指定時は後方互換で "latest" にフォールバックできるようにする
252
+ setSelectedVersion(version);
209
253
  goToStep("model-select");
210
254
  };
211
255
 
212
256
  const handleModelSelect = (model: string) => {
257
+ const agent = selectedAgent();
258
+ if (agent === "opencode" && model === "__custom__") {
259
+ goToStep("model-input");
260
+ return;
261
+ }
213
262
  setSelectedModel(model);
214
263
  if (needsReasoningLevel()) {
215
264
  goToStep("reasoning-level");
@@ -218,6 +267,19 @@ export function WizardController(props: WizardControllerProps) {
218
267
  }
219
268
  };
220
269
 
270
+ const handleModelInputSubmit = (value: string) => {
271
+ const trimmed = value.trim();
272
+ if (!trimmed) {
273
+ return;
274
+ }
275
+ setSelectedModel(trimmed);
276
+ if (needsReasoningLevel()) {
277
+ goToStep("reasoning-level");
278
+ } else {
279
+ goToStep("execution-mode");
280
+ }
281
+ };
282
+
221
283
  const handleReasoningLevelSelect = (level: string) => {
222
284
  setReasoningLevel(level as InferenceLevel);
223
285
  goToStep("execution-mode");
@@ -258,7 +320,7 @@ export function WizardController(props: WizardControllerProps) {
258
320
 
259
321
  const renderStep = () => {
260
322
  const currentStep = step();
261
- const focused = !isInitialFrame();
323
+ const focused = !isTransitioning();
262
324
 
263
325
  if (currentStep === "action-select") {
264
326
  return (
@@ -337,6 +399,17 @@ export function WizardController(props: WizardControllerProps) {
337
399
  );
338
400
  }
339
401
 
402
+ if (currentStep === "model-input") {
403
+ return (
404
+ <ModelInputStep
405
+ agentId={selectedAgent() ?? "claude-code"}
406
+ onSubmit={handleModelInputSubmit}
407
+ onBack={goBack}
408
+ focused={focused}
409
+ />
410
+ );
411
+ }
412
+
340
413
  if (currentStep === "reasoning-level") {
341
414
  return (
342
415
  <ReasoningLevelStep
@@ -2,19 +2,20 @@
2
2
  import { TextAttributes } from "@opentui/core";
3
3
  import type { SelectRenderable } from "@opentui/core";
4
4
  import { useKeyboard } from "@opentui/solid";
5
- import { createEffect, createResource, createSignal } from "solid-js";
5
+ import { createEffect, createSignal } from "solid-js";
6
6
  import { SelectInput, type SelectInputItem } from "./SelectInput.js";
7
7
  import { TextInput } from "./TextInput.js";
8
8
  import { getModelOptions } from "../../utils/modelOptions.js";
9
9
  import type { CodingAgentId } from "../../types.js";
10
10
  import { useWizardScroll } from "./WizardPopup.js";
11
11
  import { getAgentTerminalColor } from "../../../../utils/coding-agent-colors.js";
12
+ import { getVersionCache } from "../../utils/versionCache.js";
13
+ import { selectionStyle } from "../../core/theme.js";
12
14
  import {
13
- fetchPackageVersions,
14
- parsePackageCommand,
15
- } from "../../../../utils/npmRegistry.js";
16
- import { BUILTIN_CODING_AGENTS } from "../../../../config/builtin-coding-agents.js";
17
- import { findCommand } from "../../../../utils/command.js";
15
+ versionInfoToSelectItem,
16
+ createInstalledOption,
17
+ } from "../../utils/versionFetcher.js";
18
+ import { getInstalledVersionCache } from "../../utils/installedVersionCache.js";
18
19
 
19
20
  /**
20
21
  * WizardSteps - ウィザードの各ステップコンポーネント
@@ -346,7 +347,7 @@ export function AgentSelectStep(props: AgentSelectStepProps) {
346
347
  const isSelected = () => selectedIndex() === index;
347
348
  const agentColor = getAgentTerminalColor(agent.value);
348
349
  return isSelected() ? (
349
- <text bg="cyan" fg="black">
350
+ <text bg={selectionStyle.bg} fg={selectionStyle.fg}>
350
351
  {"> "}
351
352
  {agent.label}
352
353
  </text>
@@ -440,6 +441,54 @@ export function ModelSelectStep(props: ModelSelectStepProps) {
440
441
  );
441
442
  }
442
443
 
444
+ // T408b: カスタムモデル入力ステップ
445
+ export interface ModelInputStepProps extends StepProps {
446
+ agentId: CodingAgentId;
447
+ onSubmit: (value: string) => void;
448
+ }
449
+
450
+ export function ModelInputStep(props: ModelInputStepProps) {
451
+ const [value, setValue] = createSignal("");
452
+ const scroll = useWizardScroll();
453
+ const placeholder = props.agentId === "opencode" ? "provider/model" : "model";
454
+
455
+ createEffect(() => {
456
+ if (props.focused === false) {
457
+ return;
458
+ }
459
+ if (!scroll) {
460
+ return;
461
+ }
462
+ scroll.ensureLineVisible(2);
463
+ });
464
+
465
+ const handleSubmit = (next: string) => {
466
+ const trimmed = next.trim();
467
+ if (!trimmed) {
468
+ return;
469
+ }
470
+ props.onSubmit(trimmed);
471
+ };
472
+
473
+ return (
474
+ <box flexDirection="column">
475
+ <text fg="cyan" attributes={TextAttributes.BOLD}>
476
+ Enter custom model:
477
+ </text>
478
+ <text> </text>
479
+ <TextInput
480
+ value={value()}
481
+ onChange={setValue}
482
+ onSubmit={handleSubmit}
483
+ placeholder={placeholder}
484
+ focused={props.focused ?? true}
485
+ />
486
+ <text> </text>
487
+ <text attributes={TextAttributes.DIM}>[Enter] Submit [Esc] Back</text>
488
+ </box>
489
+ );
490
+ }
491
+
443
492
  // T409: 推論レベル選択ステップ(Codexのみ)
444
493
  export interface ReasoningLevelStepProps extends StepProps {
445
494
  onSelect: (level: string) => void;
@@ -626,89 +675,28 @@ const LATEST_OPTION: SelectInputItem = {
626
675
  description: "Always fetch latest version",
627
676
  };
628
677
 
629
- // エージェントIDからパッケージ名を取得
630
- function getPackageNameForAgent(agentId: string): string | null {
631
- const agent = BUILTIN_CODING_AGENTS.find((a) => a.id === agentId);
632
- if (!agent || agent.type !== "bunx") {
633
- return null;
634
- }
635
- const { packageName } = parsePackageCommand(agent.command);
636
- return packageName;
637
- }
638
-
639
- // バージョン一覧を取得
640
- async function fetchVersionOptions(
641
- agentId: string,
642
- ): Promise<SelectInputItem[]> {
643
- const packageName = getPackageNameForAgent(agentId);
644
- if (!packageName) {
645
- return [];
646
- }
647
-
648
- const versions = await fetchPackageVersions(packageName);
649
- return versions.map((v) => {
650
- const item: SelectInputItem = {
651
- label: v.isPrerelease ? `${v.version} (pre)` : v.version,
652
- value: v.version,
653
- };
654
- if (v.publishedAt) {
655
- item.description = new Date(v.publishedAt).toLocaleDateString();
656
- }
657
- return item;
658
- });
659
- }
660
-
661
- // エージェントIDからコマンド名へのマッピング
662
- const AGENT_COMMAND_MAP: Record<string, string> = {
663
- "claude-code": "claude",
664
- "codex-cli": "codex",
665
- "gemini-cli": "gemini",
666
- opencode: "opencode",
667
- };
668
-
669
- // インストール済みコマンド情報を取得(findCommandと統一)
670
- async function fetchInstalledOption(
671
- agentId: string,
672
- ): Promise<SelectInputItem | null> {
673
- const commandName = AGENT_COMMAND_MAP[agentId];
674
- if (!commandName) {
675
- return null;
676
- }
677
-
678
- const result = await findCommand(commandName);
679
- if (result.source !== "installed" || !result.path) {
680
- return null;
681
- }
682
-
683
- // バージョン表示(v1.0.3 → 1.0.3 形式に)
684
- const version = result.version?.replace(/^v/, "") ?? "unknown";
685
-
686
- return {
687
- label: `installed@${version}`,
688
- value: "installed",
689
- description: result.path,
690
- };
691
- }
692
-
693
678
  export function VersionSelectStep(props: VersionSelectStepProps) {
694
679
  const [selectedIndex, setSelectedIndex] = createSignal(0);
695
680
  const [selectRef, setSelectRef] = createSignal<SelectRenderable | undefined>(
696
681
  undefined,
697
682
  );
698
683
 
699
- // npmレジストリからバージョン取得
700
- const [versionOptions] = createResource(
701
- () => props.agentId,
702
- fetchVersionOptions,
703
- );
684
+ // FR-029: Use cached versions from startup prefetch (no npm registry re-access)
685
+ const cachedVersions = () => {
686
+ const cached = getVersionCache(props.agentId);
687
+ if (!cached) {
688
+ return []; // FR-031: Fallback to empty, UI will show "latest" only
689
+ }
690
+ return cached.map(versionInfoToSelectItem);
691
+ };
704
692
 
705
- // インストール済み情報を取得
706
- const [installedOption] = createResource(
707
- () => props.agentId,
708
- fetchInstalledOption,
709
- );
693
+ // インストール済み情報は起動時にキャッシュ済み(FR-017)
694
+ const installedOption = () => {
695
+ const installed = getInstalledVersionCache(props.agentId);
696
+ return installed ? createInstalledOption(installed) : null;
697
+ };
710
698
 
711
- // 全オプション(installed + latest + 動的)
699
+ // 全オプション(installed + latest + cached versions)
712
700
  const allOptions = (): SelectInputItem[] => {
713
701
  const options: SelectInputItem[] = [];
714
702
 
@@ -721,9 +709,9 @@ export function VersionSelectStep(props: VersionSelectStepProps) {
721
709
  // latestオプション(常に表示)
722
710
  options.push(LATEST_OPTION);
723
711
 
724
- // npmレジストリから取得したバージョン
725
- const dynamic = versionOptions() ?? [];
726
- options.push(...dynamic);
712
+ // FR-029: Use cached versions (no npm registry re-access)
713
+ const cached = cachedVersions();
714
+ options.push(...cached);
727
715
 
728
716
  return options;
729
717
  };
@@ -761,11 +749,11 @@ export function VersionSelectStep(props: VersionSelectStepProps) {
761
749
  };
762
750
 
763
751
  const statusText = () => {
764
- if (versionOptions.loading) {
765
- return "Fetching versions...";
766
- }
767
- if (versionOptions.error) {
768
- return "Could not fetch versions";
752
+ // FR-029: No loading state since we use cached versions from startup
753
+ // FR-031: Show message if cache is empty (failed to fetch at startup)
754
+ const cached = cachedVersions();
755
+ if (cached.length === 0) {
756
+ return "Version list unavailable (using latest)";
769
757
  }
770
758
  return null;
771
759
  };
@@ -33,6 +33,8 @@ export const colors = {
33
33
  selected: "cyan",
34
34
  highlighted: "cyan",
35
35
  disabled: "gray",
36
+ selectionBackground: "cyan",
37
+ selectionText: "black",
36
38
 
37
39
  // Branch type colors
38
40
  branchFeature: "green",
@@ -64,6 +66,36 @@ export const colors = {
64
66
  export type ColorName = keyof typeof colors;
65
67
  export type ColorValue = (typeof colors)[ColorName];
66
68
 
69
+ export type LogLevelLabel =
70
+ | "TRACE"
71
+ | "DEBUG"
72
+ | "INFO"
73
+ | "WARN"
74
+ | "ERROR"
75
+ | "FATAL";
76
+
77
+ export const logLevelColors: Record<LogLevelLabel, ColorValue> = {
78
+ TRACE: colors.textDim,
79
+ DEBUG: colors.info,
80
+ INFO: colors.success,
81
+ WARN: colors.warning,
82
+ ERROR: colors.error,
83
+ FATAL: colors.error,
84
+ };
85
+
86
+ export const selectionStyle = {
87
+ fg: colors.selectionText,
88
+ bg: colors.selectionBackground,
89
+ } as const;
90
+
91
+ export const getLogLevelColor = (label?: string | null): ColorValue => {
92
+ if (!label) {
93
+ return colors.text;
94
+ }
95
+ const normalized = label.toUpperCase() as LogLevelLabel;
96
+ return logLevelColors[normalized] ?? colors.text;
97
+ };
98
+
67
99
  // ========================================
68
100
  // Icon Definitions
69
101
  // ========================================
@@ -32,12 +32,14 @@ export function useAsyncOperation<T, Args extends unknown[]>(
32
32
  );
33
33
 
34
34
  const isLoading = createMemo(() => state().status === "loading");
35
- const error = createMemo(() =>
36
- state().status === "error" ? state().error : null,
37
- );
38
- const data = createMemo(() =>
39
- state().status === "success" ? state().data : null,
40
- );
35
+ const error = createMemo(() => {
36
+ const current = state();
37
+ return current.status === "error" ? current.error : null;
38
+ });
39
+ const data = createMemo(() => {
40
+ const current = state();
41
+ return current.status === "success" ? current.data : null;
42
+ });
41
43
 
42
44
  const setState = (next: AsyncState<T>) => {
43
45
  setStateInternal(next);
@@ -5,8 +5,8 @@ import {
5
5
  createWorktree,
6
6
  generateWorktreePath,
7
7
  removeWorktree,
8
- } from "../../../worktree.js";
9
- import { deleteBranch, getRepositoryRoot } from "../../../git.js";
8
+ } from "../../../../worktree.js";
9
+ import { deleteBranch, getRepositoryRoot } from "../../../../git.js";
10
10
 
11
11
  export interface UseGitOperationsResult {
12
12
  state: Accessor<AsyncState<unknown>>;
@@ -36,9 +36,10 @@ export function useGitOperations(): UseGitOperationsResult {
36
36
  });
37
37
 
38
38
  const isLoading = createMemo(() => state().status === "loading");
39
- const error = createMemo(() =>
40
- state().status === "error" ? state().error : null,
41
- );
39
+ const error = createMemo(() => {
40
+ const current = state();
41
+ return current.status === "error" ? current.error : null;
42
+ });
42
43
 
43
44
  const run = async <T>(operation: () => Promise<T>): Promise<T> => {
44
45
  setState({ status: "loading" });