@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.
- package/bin/gwt.js +1 -1
- package/dist/claude.d.ts +1 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +50 -24
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/App.solid.d.ts.map +1 -1
- package/dist/cli/ui/App.solid.js +247 -49
- package/dist/cli/ui/App.solid.js.map +1 -1
- package/dist/cli/ui/components/solid/QuickStartStep.d.ts.map +1 -1
- package/dist/cli/ui/components/solid/QuickStartStep.js +35 -22
- package/dist/cli/ui/components/solid/QuickStartStep.js.map +1 -1
- package/dist/cli/ui/components/solid/SelectInput.d.ts.map +1 -1
- package/dist/cli/ui/components/solid/SelectInput.js +2 -1
- package/dist/cli/ui/components/solid/SelectInput.js.map +1 -1
- package/dist/cli/ui/components/solid/WizardController.d.ts.map +1 -1
- package/dist/cli/ui/components/solid/WizardController.js +19 -11
- package/dist/cli/ui/components/solid/WizardController.js.map +1 -1
- package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -1
- package/dist/cli/ui/components/solid/WizardSteps.js +26 -69
- package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -1
- package/dist/cli/ui/core/theme.d.ts +9 -0
- package/dist/cli/ui/core/theme.d.ts.map +1 -1
- package/dist/cli/ui/core/theme.js +21 -0
- package/dist/cli/ui/core/theme.js.map +1 -1
- package/dist/cli/ui/screens/solid/BranchListScreen.d.ts +9 -2
- package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/BranchListScreen.js +101 -28
- package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +2 -1
- package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/ConfirmScreen.js +11 -3
- package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -1
- package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/EnvironmentScreen.js +9 -10
- package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -1
- package/dist/cli/ui/screens/solid/LogScreen.d.ts +7 -1
- package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/LogScreen.js +254 -16
- package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -1
- package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +8 -5
- package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -1
- package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/solid/SelectorScreen.js +12 -4
- package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts +1 -0
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts +1 -0
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +29 -7
- package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
- package/dist/cli/ui/utils/continueSession.d.ts +14 -0
- package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
- package/dist/cli/ui/utils/continueSession.js +61 -3
- package/dist/cli/ui/utils/continueSession.js.map +1 -1
- package/dist/cli/ui/utils/versionCache.d.ts +37 -0
- package/dist/cli/ui/utils/versionCache.d.ts.map +1 -0
- package/dist/cli/ui/utils/versionCache.js +70 -0
- package/dist/cli/ui/utils/versionCache.js.map +1 -0
- package/dist/cli/ui/utils/versionFetcher.d.ts +41 -0
- package/dist/cli/ui/utils/versionFetcher.d.ts.map +1 -0
- package/dist/cli/ui/utils/versionFetcher.js +89 -0
- package/dist/cli/ui/utils/versionFetcher.js.map +1 -0
- package/dist/codex.d.ts +1 -0
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +48 -19
- package/dist/codex.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +10 -1
- package/dist/config/index.js.map +1 -1
- package/dist/gemini.d.ts +1 -0
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +36 -3
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -2
- package/dist/index.js.map +1 -1
- package/dist/launcher.d.ts.map +1 -1
- package/dist/launcher.js +43 -8
- package/dist/launcher.js.map +1 -1
- package/dist/logging/agentOutput.d.ts +21 -0
- package/dist/logging/agentOutput.d.ts.map +1 -0
- package/dist/logging/agentOutput.js +164 -0
- package/dist/logging/agentOutput.js.map +1 -0
- package/dist/logging/formatter.d.ts.map +1 -1
- package/dist/logging/formatter.js +18 -4
- package/dist/logging/formatter.js.map +1 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +2 -0
- package/dist/logging/logger.js.map +1 -1
- package/dist/logging/reader.d.ts +21 -0
- package/dist/logging/reader.d.ts.map +1 -1
- package/dist/logging/reader.js +79 -0
- package/dist/logging/reader.js.map +1 -1
- package/dist/opentui/index.solid.js +2306 -653
- package/dist/services/dependency-installer.js +2 -2
- package/dist/services/dependency-installer.js.map +1 -1
- package/dist/utils/session/common.d.ts +8 -0
- package/dist/utils/session/common.d.ts.map +1 -1
- package/dist/utils/session/common.js +22 -0
- package/dist/utils/session/common.js.map +1 -1
- package/dist/utils/session/parsers/claude.d.ts +10 -4
- package/dist/utils/session/parsers/claude.d.ts.map +1 -1
- package/dist/utils/session/parsers/claude.js +64 -18
- package/dist/utils/session/parsers/claude.js.map +1 -1
- package/dist/utils/session/parsers/codex.d.ts.map +1 -1
- package/dist/utils/session/parsers/codex.js +48 -28
- package/dist/utils/session/parsers/codex.js.map +1 -1
- package/dist/utils/session/parsers/gemini.d.ts.map +1 -1
- package/dist/utils/session/parsers/gemini.js +43 -6
- package/dist/utils/session/parsers/gemini.js.map +1 -1
- package/dist/utils/session/parsers/opencode.d.ts.map +1 -1
- package/dist/utils/session/parsers/opencode.js +43 -6
- package/dist/utils/session/parsers/opencode.js.map +1 -1
- package/dist/utils/session/types.d.ts +7 -0
- package/dist/utils/session/types.d.ts.map +1 -1
- package/dist/web/client/src/components/ui/alert.d.ts +1 -1
- package/dist/worktree.d.ts +4 -1
- package/dist/worktree.d.ts.map +1 -1
- package/dist/worktree.js +21 -15
- package/dist/worktree.js.map +1 -1
- package/package.json +2 -1
- package/src/claude.ts +64 -28
- package/src/cli/ui/App.solid.tsx +324 -51
- package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +830 -1
- package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +105 -5
- package/src/cli/ui/__tests__/solid/ConfirmScreen.test.tsx +77 -0
- package/src/cli/ui/__tests__/solid/LogScreen.test.tsx +351 -0
- package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +73 -2
- package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +4 -1
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +72 -45
- package/src/cli/ui/components/solid/QuickStartStep.tsx +35 -23
- package/src/cli/ui/components/solid/SearchInput.tsx +1 -1
- package/src/cli/ui/components/solid/SelectInput.tsx +4 -0
- package/src/cli/ui/components/solid/WizardController.tsx +20 -11
- package/src/cli/ui/components/solid/WizardSteps.tsx +29 -86
- package/src/cli/ui/core/theme.ts +32 -0
- package/src/cli/ui/hooks/solid/useAsyncOperation.ts +8 -6
- package/src/cli/ui/hooks/solid/useGitOperations.ts +6 -5
- package/src/cli/ui/screens/solid/BranchListScreen.tsx +135 -32
- package/src/cli/ui/screens/solid/ConfirmScreen.tsx +20 -8
- package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +22 -20
- package/src/cli/ui/screens/solid/LogScreen.tsx +364 -35
- package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +19 -15
- package/src/cli/ui/screens/solid/SelectorScreen.tsx +25 -14
- package/src/cli/ui/screens/solid/SettingsScreen.tsx +5 -3
- package/src/cli/ui/types.ts +1 -0
- package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +53 -6
- package/src/cli/ui/utils/branchFormatter.ts +35 -7
- package/src/cli/ui/utils/continueSession.ts +90 -3
- package/src/cli/ui/utils/versionCache.ts +93 -0
- package/src/cli/ui/utils/versionFetcher.ts +120 -0
- package/src/codex.ts +62 -20
- package/src/config/__tests__/saveSession.test.ts +2 -2
- package/src/config/index.ts +11 -1
- package/src/gemini.ts +50 -4
- package/src/index.test.ts +16 -10
- package/src/index.ts +38 -1
- package/src/launcher.ts +49 -8
- package/src/logging/agentOutput.ts +216 -0
- package/src/logging/formatter.ts +23 -4
- package/src/logging/logger.ts +2 -0
- package/src/logging/reader.ts +117 -0
- package/src/services/__tests__/BatchMergeService.test.ts +34 -14
- package/src/services/dependency-installer.ts +2 -2
- package/src/utils/session/common.ts +28 -0
- package/src/utils/session/parsers/claude.ts +79 -29
- package/src/utils/session/parsers/codex.ts +50 -26
- package/src/utils/session/parsers/gemini.ts +46 -5
- package/src/utils/session/parsers/opencode.ts +46 -5
- package/src/utils/session/types.ts +4 -0
- package/src/worktree.ts +28 -15
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
1
2
|
import { describe, it, expect } from "bun:test";
|
|
2
3
|
import {
|
|
3
4
|
formatBranchItem,
|
|
@@ -5,6 +6,31 @@ import {
|
|
|
5
6
|
} from "../../utils/branchFormatter.js";
|
|
6
7
|
import type { BranchInfo } from "../../types.js";
|
|
7
8
|
|
|
9
|
+
const LOCAL_DATE_TIME_FORMATTER = new Intl.DateTimeFormat(undefined, {
|
|
10
|
+
year: "numeric",
|
|
11
|
+
month: "2-digit",
|
|
12
|
+
day: "2-digit",
|
|
13
|
+
hour: "2-digit",
|
|
14
|
+
minute: "2-digit",
|
|
15
|
+
hour12: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const formatLocalDateTime = (timestampMs: number): string => {
|
|
19
|
+
const date = new Date(timestampMs);
|
|
20
|
+
const parts = LOCAL_DATE_TIME_FORMATTER.formatToParts(date);
|
|
21
|
+
const get = (type: Intl.DateTimeFormatPartTypes) =>
|
|
22
|
+
parts.find((part) => part.type === type)?.value;
|
|
23
|
+
const year = get("year");
|
|
24
|
+
const month = get("month");
|
|
25
|
+
const day = get("day");
|
|
26
|
+
const hour = get("hour");
|
|
27
|
+
const minute = get("minute");
|
|
28
|
+
if (!year || !month || !day || !hour || !minute) {
|
|
29
|
+
return LOCAL_DATE_TIME_FORMATTER.format(date);
|
|
30
|
+
}
|
|
31
|
+
return `${year}-${month}-${day} ${hour}:${minute}`;
|
|
32
|
+
};
|
|
33
|
+
|
|
8
34
|
describe("branchFormatter", () => {
|
|
9
35
|
describe("formatBranchItem", () => {
|
|
10
36
|
it("should format a branch without icons", () => {
|
|
@@ -46,8 +72,9 @@ describe("branchFormatter", () => {
|
|
|
46
72
|
|
|
47
73
|
const result = formatBranchItem(branchInfo);
|
|
48
74
|
|
|
49
|
-
expect(result.lastToolUsageLabel).
|
|
50
|
-
|
|
75
|
+
expect(result.lastToolUsageLabel).toBe(
|
|
76
|
+
`Codex@latest | ${formatLocalDateTime(branchInfo.lastToolUsage.timestamp)}`,
|
|
77
|
+
);
|
|
51
78
|
});
|
|
52
79
|
|
|
53
80
|
it("should set lastToolUsageLabel to null when no usage exists", () => {
|
|
@@ -156,13 +183,13 @@ describe("branchFormatter", () => {
|
|
|
156
183
|
|
|
157
184
|
expect(results).toHaveLength(3);
|
|
158
185
|
// Current branch (main) should be first
|
|
159
|
-
expect(results[0]
|
|
160
|
-
expect(results[0]
|
|
186
|
+
expect(results[0]!.name).toBe("main");
|
|
187
|
+
expect(results[0]!.isCurrent).toBe(true);
|
|
161
188
|
// origin/main is also main branch, so it comes second
|
|
162
|
-
expect(results[1]
|
|
163
|
-
expect(results[1]
|
|
189
|
+
expect(results[1]!.name).toBe("origin/main");
|
|
190
|
+
expect(results[1]!.type).toBe("remote");
|
|
164
191
|
// feature/test comes last
|
|
165
|
-
expect(results[2]
|
|
192
|
+
expect(results[2]!.name).toBe("feature/test");
|
|
166
193
|
});
|
|
167
194
|
|
|
168
195
|
it("should handle empty array", () => {
|
|
@@ -189,8 +216,8 @@ describe("branchFormatter", () => {
|
|
|
189
216
|
|
|
190
217
|
const results = formatBranchItems(branches);
|
|
191
218
|
|
|
192
|
-
expect(results[0]
|
|
193
|
-
expect(results[1]
|
|
219
|
+
expect(results[0]!.name).toBe("a-branch");
|
|
220
|
+
expect(results[1]!.name).toBe("z-branch");
|
|
194
221
|
});
|
|
195
222
|
});
|
|
196
223
|
|
|
@@ -219,8 +246,8 @@ describe("branchFormatter", () => {
|
|
|
219
246
|
|
|
220
247
|
const results = formatBranchItems(branches);
|
|
221
248
|
|
|
222
|
-
expect(results[0]
|
|
223
|
-
expect(results[0]
|
|
249
|
+
expect(results[0]!.name).toBe("feature/current");
|
|
250
|
+
expect(results[0]!.isCurrent).toBe(true);
|
|
224
251
|
});
|
|
225
252
|
|
|
226
253
|
it("should prioritize main branch as second (after current)", () => {
|
|
@@ -247,8 +274,8 @@ describe("branchFormatter", () => {
|
|
|
247
274
|
|
|
248
275
|
const results = formatBranchItems(branches);
|
|
249
276
|
|
|
250
|
-
expect(results[0]
|
|
251
|
-
expect(results[1]
|
|
277
|
+
expect(results[0]!.name).toBe("main");
|
|
278
|
+
expect(results[1]!.name).toBe("develop");
|
|
252
279
|
});
|
|
253
280
|
|
|
254
281
|
it("should prioritize develop branch after main (when main exists)", () => {
|
|
@@ -275,9 +302,9 @@ describe("branchFormatter", () => {
|
|
|
275
302
|
|
|
276
303
|
const results = formatBranchItems(branches);
|
|
277
304
|
|
|
278
|
-
expect(results[0]
|
|
279
|
-
expect(results[1]
|
|
280
|
-
expect(results[2]
|
|
305
|
+
expect(results[0]!.name).toBe("main");
|
|
306
|
+
expect(results[1]!.name).toBe("develop");
|
|
307
|
+
expect(results[2]!.name).toBe("feature/test");
|
|
281
308
|
});
|
|
282
309
|
|
|
283
310
|
it("should NOT prioritize develop branch when main does not exist", () => {
|
|
@@ -305,9 +332,9 @@ describe("branchFormatter", () => {
|
|
|
305
332
|
const results = formatBranchItems(branches);
|
|
306
333
|
|
|
307
334
|
// develop should be sorted alphabetically, not prioritized
|
|
308
|
-
expect(results[0]
|
|
309
|
-
expect(results[1]
|
|
310
|
-
expect(results[2]
|
|
335
|
+
expect(results[0]!.name).toBe("develop");
|
|
336
|
+
expect(results[1]!.name).toBe("feature/a");
|
|
337
|
+
expect(results[2]!.name).toBe("feature/z");
|
|
311
338
|
});
|
|
312
339
|
|
|
313
340
|
it("should prioritize branches with worktree", () => {
|
|
@@ -346,8 +373,8 @@ describe("branchFormatter", () => {
|
|
|
346
373
|
|
|
347
374
|
const results = formatBranchItems(branches, worktreeMap);
|
|
348
375
|
|
|
349
|
-
expect(results[0]
|
|
350
|
-
expect(results[1]
|
|
376
|
+
expect(results[0]!.name).toBe("feature/with-worktree");
|
|
377
|
+
expect(results[1]!.name).toBe("feature/no-worktree");
|
|
351
378
|
});
|
|
352
379
|
|
|
353
380
|
it("should sort branches with worktree by latest commit timestamp", () => {
|
|
@@ -403,8 +430,8 @@ describe("branchFormatter", () => {
|
|
|
403
430
|
|
|
404
431
|
const results = formatBranchItems(branches, worktreeMap);
|
|
405
432
|
|
|
406
|
-
expect(results[0]
|
|
407
|
-
expect(results[1]
|
|
433
|
+
expect(results[0]!.name).toBe("feature/recent");
|
|
434
|
+
expect(results[1]!.name).toBe("feature/older");
|
|
408
435
|
});
|
|
409
436
|
|
|
410
437
|
it("should prioritize local branches over remote branches", () => {
|
|
@@ -425,10 +452,10 @@ describe("branchFormatter", () => {
|
|
|
425
452
|
|
|
426
453
|
const results = formatBranchItems(branches);
|
|
427
454
|
|
|
428
|
-
expect(results[0]
|
|
429
|
-
expect(results[0]
|
|
430
|
-
expect(results[1]
|
|
431
|
-
expect(results[1]
|
|
455
|
+
expect(results[0]!.name).toBe("feature/local");
|
|
456
|
+
expect(results[0]!.type).toBe("local");
|
|
457
|
+
expect(results[1]!.name).toBe("origin/feature/remote");
|
|
458
|
+
expect(results[1]!.type).toBe("remote");
|
|
432
459
|
});
|
|
433
460
|
|
|
434
461
|
it("should sort by latest commit timestamp when worktree status matches", () => {
|
|
@@ -451,8 +478,8 @@ describe("branchFormatter", () => {
|
|
|
451
478
|
|
|
452
479
|
const results = formatBranchItems(branches);
|
|
453
480
|
|
|
454
|
-
expect(results[0]
|
|
455
|
-
expect(results[1]
|
|
481
|
+
expect(results[0]!.name).toBe("origin/feature/newer");
|
|
482
|
+
expect(results[1]!.name).toBe("feature/local-older");
|
|
456
483
|
});
|
|
457
484
|
|
|
458
485
|
it("should apply all sorting rules in correct priority order", () => {
|
|
@@ -528,13 +555,13 @@ describe("branchFormatter", () => {
|
|
|
528
555
|
// 4. Branches with worktree
|
|
529
556
|
// 5. Local branches (alphabetically)
|
|
530
557
|
// 6. Remote branches
|
|
531
|
-
expect(results[0]
|
|
532
|
-
expect(results[1]
|
|
533
|
-
expect(results[2]
|
|
534
|
-
expect(results[3]
|
|
535
|
-
expect(results[4]
|
|
536
|
-
expect(results[5]
|
|
537
|
-
expect(results[6]
|
|
558
|
+
expect(results[0]!.name).toBe("feature/current");
|
|
559
|
+
expect(results[1]!.name).toBe("main");
|
|
560
|
+
expect(results[2]!.name).toBe("develop");
|
|
561
|
+
expect(results[3]!.name).toBe("feature/with-worktree");
|
|
562
|
+
expect(results[4]!.name).toBe("feature/a-local-no-worktree");
|
|
563
|
+
expect(results[5]!.name).toBe("feature/z-local-no-worktree");
|
|
564
|
+
expect(results[6]!.name).toBe("origin/feature/z-remote");
|
|
538
565
|
});
|
|
539
566
|
|
|
540
567
|
it("should handle release and hotfix branches without special priority", () => {
|
|
@@ -562,9 +589,9 @@ describe("branchFormatter", () => {
|
|
|
562
589
|
const results = formatBranchItems(branches);
|
|
563
590
|
|
|
564
591
|
// Should be sorted alphabetically (no special priority)
|
|
565
|
-
expect(results[0]
|
|
566
|
-
expect(results[1]
|
|
567
|
-
expect(results[2]
|
|
592
|
+
expect(results[0]!.name).toBe("feature/test");
|
|
593
|
+
expect(results[1]!.name).toBe("hotfix/urgent");
|
|
594
|
+
expect(results[2]!.name).toBe("release/v1.0");
|
|
568
595
|
});
|
|
569
596
|
|
|
570
597
|
it("should sort by latest activity time (max of git commit and tool usage)", () => {
|
|
@@ -604,8 +631,8 @@ describe("branchFormatter", () => {
|
|
|
604
631
|
// Both have same latest activity time (1_800_000_000), so alphabetical
|
|
605
632
|
// feature/git-newer: max(1_800_000_000, 1_700_000_000) = 1_800_000_000
|
|
606
633
|
// feature/tool-newer: max(1_700_000_000, 1_800_000_000) = 1_800_000_000
|
|
607
|
-
expect(results[0]
|
|
608
|
-
expect(results[1]
|
|
634
|
+
expect(results[0]!.name).toBe("feature/git-newer");
|
|
635
|
+
expect(results[1]!.name).toBe("feature/tool-newer");
|
|
609
636
|
});
|
|
610
637
|
|
|
611
638
|
it("should prioritize branch with tool usage over branch with only git commit when tool is newer", () => {
|
|
@@ -636,8 +663,8 @@ describe("branchFormatter", () => {
|
|
|
636
663
|
const results = formatBranchItems(branches);
|
|
637
664
|
|
|
638
665
|
// feature/with-tool has newer activity (tool usage at 1_800_000_000)
|
|
639
|
-
expect(results[0]
|
|
640
|
-
expect(results[1]
|
|
666
|
+
expect(results[0]!.name).toBe("feature/with-tool");
|
|
667
|
+
expect(results[1]!.name).toBe("feature/git-only");
|
|
641
668
|
});
|
|
642
669
|
|
|
643
670
|
it("should prioritize branch with newer git commit over branch with older tool usage", () => {
|
|
@@ -668,8 +695,8 @@ describe("branchFormatter", () => {
|
|
|
668
695
|
const results = formatBranchItems(branches);
|
|
669
696
|
|
|
670
697
|
// feature/new-git has newer activity (git commit at 1_800_000_000)
|
|
671
|
-
expect(results[0]
|
|
672
|
-
expect(results[1]
|
|
698
|
+
expect(results[0]!.name).toBe("feature/new-git");
|
|
699
|
+
expect(results[1]!.name).toBe("feature/old-tool");
|
|
673
700
|
});
|
|
674
701
|
});
|
|
675
702
|
});
|
|
@@ -42,40 +42,52 @@ export function QuickStartStep(props: QuickStartStepProps) {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const buildSettingsDescription = (entry: ToolSessionEntry): string => {
|
|
46
|
+
const parts: string[] = [];
|
|
47
|
+
if (entry.toolLabel) {
|
|
48
|
+
parts.push(entry.toolLabel);
|
|
49
|
+
} else if (entry.toolId) {
|
|
50
|
+
parts.push(entry.toolId);
|
|
51
|
+
}
|
|
52
|
+
if (entry.model) {
|
|
53
|
+
parts.push(entry.model);
|
|
54
|
+
}
|
|
55
|
+
if (entry.toolId === "codex-cli" && entry.reasoningLevel) {
|
|
56
|
+
parts.push(entry.reasoningLevel);
|
|
57
|
+
}
|
|
58
|
+
return parts.join(", ");
|
|
59
|
+
};
|
|
48
60
|
|
|
49
61
|
const items = createMemo<QuickStartItem[]>(() => {
|
|
50
62
|
const result: QuickStartItem[] = [];
|
|
51
|
-
const entry = latestEntry();
|
|
52
|
-
|
|
53
|
-
if (entry) {
|
|
54
|
-
const reasoningInfo = entry.reasoningLevel
|
|
55
|
-
? `, ${entry.reasoningLevel}`
|
|
56
|
-
: "";
|
|
57
|
-
const versionInfo = entry.toolVersion ? `@${entry.toolVersion}` : "";
|
|
58
|
-
const settingsDesc = `${entry.toolLabel}${versionInfo}, ${entry.model}${reasoningInfo}`;
|
|
59
|
-
const resumeDesc = entry.sessionId
|
|
60
|
-
? `${settingsDesc}, session ${entry.sessionId}`
|
|
61
|
-
: settingsDesc;
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
props.history.forEach((entry, index) => {
|
|
65
|
+
if (!entry) return;
|
|
66
|
+
const settingsDesc = buildSettingsDescription(entry);
|
|
67
|
+
const sessionId =
|
|
68
|
+
entry.sessionId && entry.sessionId.trim().length > 0
|
|
69
|
+
? entry.sessionId.trim()
|
|
70
|
+
: null;
|
|
71
|
+
const suffix = `${entry.toolId ?? "tool"}-${index}`;
|
|
72
|
+
|
|
73
|
+
if (sessionId) {
|
|
74
|
+
result.push({
|
|
75
|
+
label: "Resume session (previous settings)",
|
|
76
|
+
value: `resume-${suffix}`,
|
|
77
|
+
description: `${settingsDesc} | Session: ${sessionId}`,
|
|
78
|
+
action: "resume",
|
|
79
|
+
entry,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
70
82
|
|
|
71
83
|
result.push({
|
|
72
84
|
label: "Start new (previous settings)",
|
|
73
|
-
value: `start-new-${
|
|
85
|
+
value: `start-new-${suffix}`,
|
|
74
86
|
description: settingsDesc,
|
|
75
87
|
action: "start-new",
|
|
76
88
|
entry,
|
|
77
89
|
});
|
|
78
|
-
}
|
|
90
|
+
});
|
|
79
91
|
|
|
80
92
|
// Add "Choose different settings..." at the end
|
|
81
93
|
result.push({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
import type { SelectOption, SelectRenderable } from "@opentui/core";
|
|
3
3
|
import type { Ref } from "solid-js";
|
|
4
|
+
import { selectionStyle } from "../../core/theme.js";
|
|
4
5
|
|
|
5
6
|
export interface SelectInputItem {
|
|
6
7
|
label: string;
|
|
@@ -69,6 +70,9 @@ export function SelectInput(props: SelectInputProps) {
|
|
|
69
70
|
<select
|
|
70
71
|
options={options()}
|
|
71
72
|
height={computedHeight()}
|
|
73
|
+
selectedBackgroundColor={selectionStyle.bg}
|
|
74
|
+
selectedTextColor={selectionStyle.fg}
|
|
75
|
+
selectedDescriptionColor={selectionStyle.fg}
|
|
72
76
|
{...(props.selectedIndex !== undefined && {
|
|
73
77
|
selectedIndex: props.selectedIndex,
|
|
74
78
|
})}
|
|
@@ -88,8 +88,8 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
88
88
|
const [executionMode, setExecutionMode] =
|
|
89
89
|
createSignal<ExecutionMode>("normal");
|
|
90
90
|
|
|
91
|
-
// キー伝播防止:
|
|
92
|
-
const [
|
|
91
|
+
// キー伝播防止: ステップ遷移直後は focused を無効にする
|
|
92
|
+
const [isTransitioning, setIsTransitioning] = createSignal(true);
|
|
93
93
|
|
|
94
94
|
// Reset state when wizard becomes visible
|
|
95
95
|
function getInitialStep(): WizardStep {
|
|
@@ -115,6 +115,13 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
115
115
|
setExecutionMode("normal");
|
|
116
116
|
};
|
|
117
117
|
|
|
118
|
+
// ステップ遷移時にキー伝搬防止を有効化するヘルパー
|
|
119
|
+
const startTransition = () => {
|
|
120
|
+
setIsTransitioning(true);
|
|
121
|
+
// 50ms後にフォーカスを有効化(キー伝搬を防止しつつ、応答性を維持)
|
|
122
|
+
setTimeout(() => setIsTransitioning(false), 50);
|
|
123
|
+
};
|
|
124
|
+
|
|
118
125
|
// Reset wizard when it becomes visible
|
|
119
126
|
let prevVisible = false;
|
|
120
127
|
createEffect(() => {
|
|
@@ -123,19 +130,17 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
123
130
|
resetWizard();
|
|
124
131
|
// キー伝播防止: 最初の数フレームでは focused を無効にする
|
|
125
132
|
// Enter キーが複数フレームにわたって伝播する可能性があるため、長めに設定
|
|
126
|
-
|
|
127
|
-
// 十分な時間を置いて focused を有効化 (150ms)
|
|
128
|
-
setTimeout(() => setIsInitialFrame(false), 150);
|
|
133
|
+
startTransition();
|
|
129
134
|
}
|
|
130
135
|
prevVisible = visible;
|
|
131
136
|
});
|
|
132
137
|
|
|
133
138
|
// Handle keyboard events for step navigation
|
|
134
|
-
// T412:
|
|
139
|
+
// T412: ステップ遷移直後は Enter キーをブロックして伝播を防ぐ
|
|
135
140
|
useKeyboard((key) => {
|
|
136
141
|
if (!props.visible) return;
|
|
137
|
-
//
|
|
138
|
-
if (
|
|
142
|
+
// ステップ遷移直後は Enter キーを無視(ステップ間のキー伝播防止)
|
|
143
|
+
if (isTransitioning() && key.name === "return") {
|
|
139
144
|
return;
|
|
140
145
|
}
|
|
141
146
|
if (key.name === "escape") {
|
|
@@ -146,6 +151,8 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
146
151
|
const goToStep = (nextStep: WizardStep) => {
|
|
147
152
|
setStepHistory((prev) => [...prev, step()]);
|
|
148
153
|
setStep(nextStep);
|
|
154
|
+
// ステップ遷移時にキー伝搬防止を有効化
|
|
155
|
+
startTransition();
|
|
149
156
|
};
|
|
150
157
|
|
|
151
158
|
const goBack = () => {
|
|
@@ -157,6 +164,8 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
157
164
|
const previousStep = history[history.length - 1] ?? "agent-select";
|
|
158
165
|
setStepHistory(history.slice(0, -1));
|
|
159
166
|
setStep(previousStep);
|
|
167
|
+
// ステップ遷移時にキー伝搬防止を有効化
|
|
168
|
+
startTransition();
|
|
160
169
|
};
|
|
161
170
|
|
|
162
171
|
// Determine if reasoning level step is needed
|
|
@@ -204,8 +213,8 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
204
213
|
};
|
|
205
214
|
|
|
206
215
|
const handleVersionSelect = (version: string) => {
|
|
207
|
-
// "installed"
|
|
208
|
-
setSelectedVersion(version
|
|
216
|
+
// "installed" を明示的に保存し、未指定時は後方互換で "latest" にフォールバックできるようにする
|
|
217
|
+
setSelectedVersion(version);
|
|
209
218
|
goToStep("model-select");
|
|
210
219
|
};
|
|
211
220
|
|
|
@@ -258,7 +267,7 @@ export function WizardController(props: WizardControllerProps) {
|
|
|
258
267
|
|
|
259
268
|
const renderStep = () => {
|
|
260
269
|
const currentStep = step();
|
|
261
|
-
const focused = !
|
|
270
|
+
const focused = !isTransitioning();
|
|
262
271
|
|
|
263
272
|
if (currentStep === "action-select") {
|
|
264
273
|
return (
|
|
@@ -9,12 +9,13 @@ 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import { findCommand } from "../../../../utils/command.js";
|
|
15
|
+
fetchInstalledVersionForAgent,
|
|
16
|
+
versionInfoToSelectItem,
|
|
17
|
+
createInstalledOption,
|
|
18
|
+
} from "../../utils/versionFetcher.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=
|
|
350
|
+
<text bg={selectionStyle.bg} fg={selectionStyle.fg}>
|
|
350
351
|
{"> "}
|
|
351
352
|
{agent.label}
|
|
352
353
|
</text>
|
|
@@ -626,89 +627,31 @@ const LATEST_OPTION: SelectInputItem = {
|
|
|
626
627
|
description: "Always fetch latest version",
|
|
627
628
|
};
|
|
628
629
|
|
|
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
630
|
export function VersionSelectStep(props: VersionSelectStepProps) {
|
|
694
631
|
const [selectedIndex, setSelectedIndex] = createSignal(0);
|
|
695
632
|
const [selectRef, setSelectRef] = createSignal<SelectRenderable | undefined>(
|
|
696
633
|
undefined,
|
|
697
634
|
);
|
|
698
635
|
|
|
699
|
-
// npm
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
636
|
+
// FR-029: Use cached versions from startup prefetch (no npm registry re-access)
|
|
637
|
+
const cachedVersions = () => {
|
|
638
|
+
const cached = getVersionCache(props.agentId);
|
|
639
|
+
if (!cached) {
|
|
640
|
+
return []; // FR-031: Fallback to empty, UI will show "latest" only
|
|
641
|
+
}
|
|
642
|
+
return cached.map(versionInfoToSelectItem);
|
|
643
|
+
};
|
|
704
644
|
|
|
705
|
-
// インストール済み情報を取得
|
|
645
|
+
// インストール済み情報を取得 (still needs async fetch for local command check)
|
|
706
646
|
const [installedOption] = createResource(
|
|
707
647
|
() => props.agentId,
|
|
708
|
-
|
|
648
|
+
async (agentId: string) => {
|
|
649
|
+
const installed = await fetchInstalledVersionForAgent(agentId);
|
|
650
|
+
return installed ? createInstalledOption(installed) : null;
|
|
651
|
+
},
|
|
709
652
|
);
|
|
710
653
|
|
|
711
|
-
// 全オプション(installed + latest +
|
|
654
|
+
// 全オプション(installed + latest + cached versions)
|
|
712
655
|
const allOptions = (): SelectInputItem[] => {
|
|
713
656
|
const options: SelectInputItem[] = [];
|
|
714
657
|
|
|
@@ -721,9 +664,9 @@ export function VersionSelectStep(props: VersionSelectStepProps) {
|
|
|
721
664
|
// latestオプション(常に表示)
|
|
722
665
|
options.push(LATEST_OPTION);
|
|
723
666
|
|
|
724
|
-
// npm
|
|
725
|
-
const
|
|
726
|
-
options.push(...
|
|
667
|
+
// FR-029: Use cached versions (no npm registry re-access)
|
|
668
|
+
const cached = cachedVersions();
|
|
669
|
+
options.push(...cached);
|
|
727
670
|
|
|
728
671
|
return options;
|
|
729
672
|
};
|
|
@@ -761,11 +704,11 @@ export function VersionSelectStep(props: VersionSelectStepProps) {
|
|
|
761
704
|
};
|
|
762
705
|
|
|
763
706
|
const statusText = () => {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
if (
|
|
768
|
-
return "
|
|
707
|
+
// FR-029: No loading state since we use cached versions from startup
|
|
708
|
+
// FR-031: Show message if cache is empty (failed to fetch at startup)
|
|
709
|
+
const cached = cachedVersions();
|
|
710
|
+
if (cached.length === 0) {
|
|
711
|
+
return "Version list unavailable (using latest)";
|
|
769
712
|
}
|
|
770
713
|
return null;
|
|
771
714
|
};
|
package/src/cli/ui/core/theme.ts
CHANGED
|
@@ -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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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);
|