@akiojin/gwt 2.10.0 → 2.11.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 (96) hide show
  1. package/dist/cli/ui/components/App.d.ts.map +1 -1
  2. package/dist/cli/ui/components/App.js +5 -47
  3. package/dist/cli/ui/components/App.js.map +1 -1
  4. package/dist/cli/ui/components/common/Input.d.ts +1 -1
  5. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +1 -2
  6. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  7. package/dist/cli/ui/components/screens/BranchListScreen.js +19 -14
  8. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  9. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
  10. package/dist/cli/ui/components/screens/ModelSelectorScreen.js +6 -6
  11. package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
  12. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts.map +1 -1
  13. package/dist/cli/ui/components/screens/PRCleanupScreen.js +0 -3
  14. package/dist/cli/ui/components/screens/PRCleanupScreen.js.map +1 -1
  15. package/dist/cli/ui/hooks/useGitData.d.ts.map +1 -1
  16. package/dist/cli/ui/hooks/useGitData.js +43 -2
  17. package/dist/cli/ui/hooks/useGitData.js.map +1 -1
  18. package/dist/cli/ui/types.d.ts +16 -4
  19. package/dist/cli/ui/types.d.ts.map +1 -1
  20. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  21. package/dist/cli/ui/utils/branchFormatter.js +124 -28
  22. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  23. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  24. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  25. package/dist/client/assets/{index-CNWntAlF.js → index-Dl798X5w.js} +1 -1
  26. package/dist/client/index.html +1 -1
  27. package/dist/config/index.d.ts.map +1 -1
  28. package/dist/config/index.js.map +1 -1
  29. package/dist/git.d.ts +6 -0
  30. package/dist/git.d.ts.map +1 -1
  31. package/dist/git.js +40 -5
  32. package/dist/git.js.map +1 -1
  33. package/dist/web/client/src/components/BranchGraph.js +1 -1
  34. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  35. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  36. package/dist/web/client/src/pages/BranchDetailPage.js +8 -3
  37. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  38. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  39. package/dist/web/server/routes/sessions.js +4 -2
  40. package/dist/web/server/routes/sessions.js.map +1 -1
  41. package/dist/worktree.d.ts.map +1 -1
  42. package/dist/worktree.js +31 -44
  43. package/dist/worktree.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +9 -17
  46. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +14 -20
  47. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +14 -44
  48. package/src/cli/ui/__tests__/components/App.test.tsx +8 -15
  49. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +12 -5
  50. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +3 -2
  51. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +3 -2
  52. package/src/cli/ui/__tests__/components/common/Input.test.tsx +3 -2
  53. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +15 -14
  54. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +3 -3
  55. package/src/cli/ui/__tests__/components/common/Select.test.tsx +1 -4
  56. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +3 -2
  57. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +3 -2
  58. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +3 -2
  59. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +3 -2
  60. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +3 -2
  61. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +3 -2
  62. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +33 -43
  63. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +3 -2
  64. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +3 -2
  65. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +3 -2
  66. package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +18 -17
  67. package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +3 -2
  68. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +5 -12
  69. package/src/cli/ui/__tests__/integration/navigation.test.tsx +15 -10
  70. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +3 -2
  71. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +0 -4
  72. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +3 -5
  73. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +24 -23
  74. package/src/cli/ui/components/App.tsx +3 -74
  75. package/src/cli/ui/components/common/Input.tsx +1 -1
  76. package/src/cli/ui/components/screens/BranchListScreen.tsx +32 -22
  77. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +46 -49
  78. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +0 -3
  79. package/src/cli/ui/hooks/useGitData.ts +59 -1
  80. package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +3 -2
  81. package/src/cli/ui/types.ts +24 -5
  82. package/src/cli/ui/utils/branchFormatter.ts +123 -28
  83. package/src/cli/ui/utils/modelOptions.test.ts +4 -6
  84. package/src/cli/ui/utils/modelOptions.ts +1 -2
  85. package/src/config/index.ts +2 -1
  86. package/src/git.ts +56 -16
  87. package/src/web/client/src/components/BranchGraph.tsx +1 -1
  88. package/src/web/client/src/pages/BranchDetailPage.tsx +8 -3
  89. package/src/web/server/routes/sessions.ts +12 -5
  90. package/src/worktree.ts +31 -59
  91. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +0 -20
  92. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts.map +0 -1
  93. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +0 -65
  94. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js.map +0 -1
  95. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +0 -151
  96. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +0 -117
@@ -84,7 +84,6 @@ describe("BranchListScreen Performance", () => {
84
84
  branches={branches}
85
85
  stats={stats}
86
86
  onSelect={() => {}}
87
- onNavigate={() => {}}
88
87
  onQuit={() => {}}
89
88
  />,
90
89
  );
@@ -123,7 +122,6 @@ describe("BranchListScreen Performance", () => {
123
122
  branches={branches}
124
123
  stats={stats}
125
124
  onSelect={() => {}}
126
- onNavigate={() => {}}
127
125
  onQuit={() => {}}
128
126
  lastUpdated={new Date()}
129
127
  />,
@@ -137,7 +135,6 @@ describe("BranchListScreen Performance", () => {
137
135
  branches={branches}
138
136
  stats={{ ...stats, total: stats.total + 1 }}
139
137
  onSelect={() => {}}
140
- onNavigate={() => {}}
141
138
  onQuit={() => {}}
142
139
  lastUpdated={new Date()}
143
140
  />,
@@ -174,7 +171,6 @@ describe("BranchListScreen Performance", () => {
174
171
  branches={branches}
175
172
  stats={stats}
176
173
  onSelect={() => {}}
177
- onNavigate={() => {}}
178
174
  onQuit={() => {}}
179
175
  />,
180
176
  );
@@ -18,9 +18,6 @@ vi.mock("../../hooks/useGitData.js", () => ({
18
18
  useGitData: vi.fn(),
19
19
  }));
20
20
 
21
- import { useGitData } from "../../hooks/useGitData.js";
22
- // const mockUseGitData = useGitData as ReturnType<typeof vi.fn>;
23
-
24
21
  // Helper function to create a stable hash of branch data
25
22
  function createBranchHash(branches: BranchInfo[]): string {
26
23
  return branches.map((b) => `${b.name}-${b.type}-${b.isCurrent}`).join(",");
@@ -63,8 +60,9 @@ function TestComponent({
63
60
  describe("useMemo Optimization (T082-1)", () => {
64
61
  beforeEach(() => {
65
62
  const window = new Window();
66
- globalThis.window = window as any;
67
- globalThis.document = window.document as any;
63
+ globalThis.window = window as unknown as typeof globalThis.window;
64
+ globalThis.document =
65
+ window.document as unknown as typeof globalThis.document;
68
66
  vi.clearAllMocks();
69
67
  });
70
68
 
@@ -23,7 +23,7 @@ describe("branchFormatter", () => {
23
23
  expect(result.branchType).toBe("main");
24
24
  expect(result.isCurrent).toBe(true);
25
25
  expect(result.icons).toContain("⚡"); // main icon
26
- expect(result.icons).toContain(""); // current icon
26
+ expect(result.icons).toContain("👉"); // current icon
27
27
  expect(result.label).toContain("main");
28
28
  expect(result.value).toBe("main");
29
29
  expect(result.hasChanges).toBe(false);
@@ -40,7 +40,7 @@ describe("branchFormatter", () => {
40
40
  const result = formatBranchItem(branchInfo);
41
41
 
42
42
  expect(result.icons).toContain("✨"); // feature icon
43
- expect(result.icons).not.toContain(""); // not current
43
+ expect(result.icons).not.toContain("👉"); // not current
44
44
  expect(result.label).toContain("feature/new-ui");
45
45
  expect(result.value).toBe("feature/new-ui");
46
46
  });
@@ -65,7 +65,6 @@ describe("branchFormatter", () => {
65
65
  const result = formatBranchItem(branchInfo);
66
66
 
67
67
  expect(result.lastToolUsageLabel).toContain("Codex");
68
- expect(result.lastToolUsageLabel).toContain("New");
69
68
  expect(result.lastToolUsageLabel).toContain("2025-11-26");
70
69
  });
71
70
 
@@ -157,12 +156,12 @@ describe("branchFormatter", () => {
157
156
  const localResult = formatBranchItem(localBranch);
158
157
  const remoteResult = formatBranchItem(remoteBranch);
159
158
 
160
- const localNameIndex = localResult.label.indexOf(localResult.name);
161
- const remoteNameIndex = remoteResult.label.indexOf(remoteResult.name);
159
+ // Both should have the branch name in the label
160
+ expect(localResult.label).toContain("feature/foo");
161
+ expect(remoteResult.label).toContain("origin/feature/foo");
162
162
 
163
- expect(localNameIndex).toBeGreaterThan(0);
164
- expect(localNameIndex).toBe(remoteNameIndex);
165
- expect(remoteResult.label).toMatch(/☁(?:️|︎)?\s+origin/);
163
+ // Remote branch should have ☁️ marker for remote-only status
164
+ expect(remoteResult.label).toMatch(/☁️/);
166
165
  });
167
166
 
168
167
  it("should keep icon columns fixed-width when using wide emoji icons", () => {
@@ -182,11 +181,13 @@ describe("branchFormatter", () => {
182
181
 
183
182
  const result = formatBranchItem(branchInfo, { hasChanges: true });
184
183
 
185
- // Four icon columns should occupy exactly 8 columns (4 * COLUMN_WIDTH)
184
+ // Icon columns: [Type][Worktree][Changes][Remote] = 4 * 2 = 8
185
+ // Sync column: 6 (fixed width for icon + up to 4 digits + space)
186
+ // Total: 14
186
187
  const iconBlockWidth =
187
188
  stringWidth(result.label) - stringWidth(branchInfo.name);
188
189
 
189
- expect(iconBlockWidth).toBe(8);
190
+ expect(iconBlockWidth).toBe(14);
190
191
  });
191
192
 
192
193
  it("should include worktree status icon when provided", () => {
@@ -218,7 +219,7 @@ describe("branchFormatter", () => {
218
219
 
219
220
  const result = formatBranchItem(branchInfo, { hasChanges: true });
220
221
 
221
- expect(result.icons).toContain("✏️"); // changes icon
222
+ expect(result.icons).toContain("💾"); // changes icon
222
223
  expect(result.hasChanges).toBe(true);
223
224
  });
224
225
 
@@ -233,8 +234,8 @@ describe("branchFormatter", () => {
233
234
 
234
235
  const result = formatBranchItem(branchInfo);
235
236
 
236
- expect(result.icons).toContain("⬆️"); // unpushed icon
237
- expect(result.label).toContain("⬆️");
237
+ expect(result.icons).toContain("📤"); // unpushed icon
238
+ expect(result.label).toContain("📤");
238
239
  });
239
240
 
240
241
  it("should show open PR icon", () => {
@@ -248,8 +249,8 @@ describe("branchFormatter", () => {
248
249
 
249
250
  const result = formatBranchItem(branchInfo);
250
251
 
251
- expect(result.icons).toContain("🔀"); // open PR icon
252
- expect(result.label).toContain("🔀");
252
+ expect(result.icons).toContain("🔃"); // open PR icon
253
+ expect(result.label).toContain("🔃");
253
254
  });
254
255
 
255
256
  it("should show merged PR icon", () => {
@@ -300,12 +301,12 @@ describe("branchFormatter", () => {
300
301
  const resultWithChanges = formatBranchItem(branchInfo, {
301
302
  hasChanges: true,
302
303
  });
303
- expect(resultWithChanges.icons).toContain("✏️");
304
- expect(resultWithChanges.icons).not.toContain("⬆️");
304
+ expect(resultWithChanges.icons).toContain("💾");
305
+ expect(resultWithChanges.icons).not.toContain("📤");
305
306
 
306
307
  const resultWithoutChanges = formatBranchItem(branchInfo);
307
- expect(resultWithoutChanges.icons).toContain("⬆️");
308
- expect(resultWithoutChanges.icons).not.toContain("✏️");
308
+ expect(resultWithoutChanges.icons).toContain("📤");
309
+ expect(resultWithoutChanges.icons).not.toContain("💾");
309
310
  });
310
311
 
311
312
  it("should prioritize unpushed over open PR", () => {
@@ -320,8 +321,8 @@ describe("branchFormatter", () => {
320
321
 
321
322
  const result = formatBranchItem(branchInfo);
322
323
 
323
- expect(result.icons).toContain("⬆️");
324
- expect(result.icons).not.toContain("🔀");
324
+ expect(result.icons).toContain("📤");
325
+ expect(result.icons).not.toContain("🔃");
325
326
  });
326
327
 
327
328
  it("should prioritize open PR over merged PR", () => {
@@ -336,7 +337,7 @@ describe("branchFormatter", () => {
336
337
 
337
338
  const result = formatBranchItem(branchInfo);
338
339
 
339
- expect(result.icons).toContain("🔀");
340
+ expect(result.icons).toContain("🔃");
340
341
  expect(result.icons).not.toContain("✅");
341
342
  });
342
343
 
@@ -378,7 +379,7 @@ describe("branchFormatter", () => {
378
379
  const result = formatBranchItem(branchInfo);
379
380
 
380
381
  expect(result.icons).toContain("⚠️");
381
- expect(result.icons).not.toContain("");
382
+ expect(result.icons).not.toContain("👉");
382
383
  });
383
384
 
384
385
  it("should handle develop branch", () => {
@@ -8,7 +8,6 @@ import React, {
8
8
  import { useApp } from "ink";
9
9
  import { ErrorBoundary } from "./common/ErrorBoundary.js";
10
10
  import { BranchListScreen } from "./screens/BranchListScreen.js";
11
- import { WorktreeManagerScreen } from "./screens/WorktreeManagerScreen.js";
12
11
  import { BranchCreatorScreen } from "./screens/BranchCreatorScreen.js";
13
12
  import { BranchActionSelectorScreen } from "../screens/BranchActionSelectorScreen.js";
14
13
  import { AIToolSelectorScreen } from "./screens/AIToolSelectorScreen.js";
@@ -19,7 +18,6 @@ import {
19
18
  ModelSelectorScreen,
20
19
  type ModelSelectionResult,
21
20
  } from "./screens/ModelSelectorScreen.js";
22
- import type { WorktreeItem } from "./screens/WorktreeManagerScreen.js";
23
21
  import { useGitData } from "../hooks/useGitData.js";
24
22
  import { useScreenState } from "../hooks/useScreenState.js";
25
23
  import { formatBranchItems } from "../utils/branchFormatter.js";
@@ -31,11 +29,7 @@ import type {
31
29
  InferenceLevel,
32
30
  SelectedBranchState,
33
31
  } from "../types.js";
34
- import {
35
- getRepositoryRoot,
36
- deleteBranch,
37
- deleteRemoteBranch,
38
- } from "../../../git.js";
32
+ import { getRepositoryRoot, deleteBranch } from "../../../git.js";
39
33
  import {
40
34
  createWorktree,
41
35
  generateWorktreePath,
@@ -269,19 +263,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
269
263
  [visibleBranches],
270
264
  );
271
265
 
272
- // Format worktrees to WorktreeItems
273
- const worktreeItems: WorktreeItem[] = useMemo(
274
- () =>
275
- worktrees.map(
276
- (wt): WorktreeItem => ({
277
- branch: wt.branch,
278
- path: wt.path,
279
- isAccessible: wt.isAccessible ?? true,
280
- }),
281
- ),
282
- [worktrees],
283
- );
284
-
285
266
  const resolveBaseBranch = useCallback(() => {
286
267
  const localMain = branches.find(
287
268
  (branch) =>
@@ -428,40 +409,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
428
409
  ],
429
410
  );
430
411
 
431
- // Handle navigation
432
- const handleNavigate = useCallback(
433
- (screen: string) => {
434
- navigateTo(screen as any);
435
- },
436
- [navigateTo],
437
- );
438
-
439
- const handleWorktreeSelect = useCallback(
440
- (worktree: WorktreeItem) => {
441
- const lastTool = branches.find((b) => b.name === worktree.branch)
442
- ?.lastToolUsage?.toolId;
443
- setSelectedBranch({
444
- name: worktree.branch,
445
- displayName: worktree.branch,
446
- branchType: "local",
447
- branchCategory: inferBranchCategory(worktree.branch),
448
- });
449
- setSelectedTool(null);
450
- setSelectedModel(null);
451
- setCreationSourceBranch(null);
452
- setPreferredToolId(lastTool ?? null);
453
- setCleanupFooterMessage(null);
454
- navigateTo("ai-tool-selector");
455
- },
456
- [
457
- inferBranchCategory,
458
- navigateTo,
459
- setCleanupFooterMessage,
460
- setCreationSourceBranch,
461
- branches,
462
- ],
463
- );
464
-
465
412
  // Handle branch action selection
466
413
  const handleProtectedBranchSwitch = useCallback(async () => {
467
414
  if (!selectedBranch) {
@@ -733,14 +680,8 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
733
680
 
734
681
  await deleteBranch(target.branch, true);
735
682
 
736
- // マージ済みの場合のみリモートブランチも削除
737
- if (target.hasRemoteBranch && target.reasons?.includes("merged-pr")) {
738
- try {
739
- await deleteRemoteBranch(target.branch);
740
- } catch {
741
- // リモート削除失敗はログのみ、処理は続行
742
- }
743
- }
683
+ // 自動クリーンアップではリモートブランチは削除しない
684
+ // リモートブランチはユーザーが明示的に削除する必要がある
744
685
 
745
686
  succeededBranches.push(target.branch);
746
687
  setCleanupIndicators((prev) => ({
@@ -864,7 +805,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
864
805
  branches={branchItems}
865
806
  stats={stats}
866
807
  onSelect={handleSelect}
867
- onNavigate={handleNavigate}
868
808
  onQuit={handleQuit}
869
809
  onCleanupCommand={handleCleanupCommand}
870
810
  onRefresh={refresh}
@@ -882,16 +822,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
882
822
  />
883
823
  );
884
824
 
885
- case "worktree-manager":
886
- return (
887
- <WorktreeManagerScreen
888
- worktrees={worktreeItems}
889
- onBack={goBack}
890
- onSelect={handleWorktreeSelect}
891
- version={version}
892
- />
893
- );
894
-
895
825
  case "branch-creator":
896
826
  return (
897
827
  <BranchCreatorScreen
@@ -978,7 +908,6 @@ export function App({ onExit, loadingIndicatorDelay = 300 }: AppProps) {
978
908
  branches={branchItems}
979
909
  stats={stats}
980
910
  onSelect={handleSelect}
981
- onNavigate={handleNavigate}
982
911
  onQuit={handleQuit}
983
912
  onRefresh={refresh}
984
913
  loading={loading}
@@ -11,7 +11,7 @@ export interface InputProps {
11
11
  mask?: string;
12
12
  /**
13
13
  * Block specific key bindings to prevent parent handlers from processing them
14
- * Useful for blocking shortcuts like 'c', 'r', 'm' while typing
14
+ * Useful for blocking shortcuts like 'c', 'r' while typing
15
15
  */
16
16
  blockKeys?: string[];
17
17
  }
@@ -14,8 +14,6 @@ import chalk from "chalk";
14
14
  const WIDTH_OVERRIDES: Record<string, number> = {
15
15
  // Remote icon
16
16
  "☁": 1,
17
- // Unpushed icon
18
- "⬆": 1,
19
17
  // Branch type icons
20
18
  "⚡": 1,
21
19
  "✨": 1,
@@ -27,11 +25,16 @@ const WIDTH_OVERRIDES: Record<string, number> = {
27
25
  "🟢": 1,
28
26
  "🟠": 1,
29
27
  // Change status icons
30
- "": 1,
31
- "✏️": 1,
32
- "🔀": 1,
28
+ "👉": 1,
29
+ "💾": 1,
30
+ "📤": 1,
31
+ "🔃": 1,
33
32
  "✅": 1,
34
33
  "⚠️": 1,
34
+ // Remote markers
35
+ "🔗": 1,
36
+ "💻": 1,
37
+ "☁️": 1,
35
38
  };
36
39
 
37
40
  const getCharWidth = (char: string): number => {
@@ -70,7 +73,6 @@ export interface BranchListScreenProps {
70
73
  branches: BranchItem[];
71
74
  stats: Statistics;
72
75
  onSelect: (branch: BranchItem) => void;
73
- onNavigate?: (screen: string) => void;
74
76
  onQuit?: () => void;
75
77
  onCleanupCommand?: () => void;
76
78
  onRefresh?: () => void;
@@ -96,7 +98,6 @@ export function BranchListScreen({
96
98
  branches,
97
99
  stats,
98
100
  onSelect,
99
- onNavigate,
100
101
  onCleanupCommand,
101
102
  onRefresh,
102
103
  loading = false,
@@ -112,6 +113,9 @@ export function BranchListScreen({
112
113
  testOnFilterQueryChange,
113
114
  }: BranchListScreenProps) {
114
115
  const { rows } = useTerminalSize();
116
+ const COLUMN_WIDTH = 2;
117
+ const SYNC_COLUMN_WIDTH = 6;
118
+ const headerText = ` ${"Ty".padEnd(COLUMN_WIDTH)}${"Wt".padEnd(COLUMN_WIDTH)}${"St".padEnd(COLUMN_WIDTH)}${"Rm".padEnd(COLUMN_WIDTH)}${"Sync".padEnd(SYNC_COLUMN_WIDTH)}Branch`;
115
119
 
116
120
  // Filter state - allow test control via props
117
121
  const [internalFilterQuery, setInternalFilterQuery] = useState("");
@@ -139,7 +143,7 @@ export function BranchListScreen({
139
143
  );
140
144
 
141
145
  // Handle keyboard input
142
- // Note: Input component blocks specific keys (c/r/m/f) using blockKeys prop
146
+ // Note: Input component blocks specific keys (c/r/f) using blockKeys prop
143
147
  // This prevents shortcuts from triggering while typing in the filter
144
148
  useInput((input, key) => {
145
149
  if (cleanupUI?.inputLocked) {
@@ -172,9 +176,7 @@ export function BranchListScreen({
172
176
  }
173
177
 
174
178
  // Global shortcuts (blocked by Input component when typing in filter mode)
175
- if (input === "m" && onNavigate) {
176
- onNavigate("worktree-manager");
177
- } else if (input === "c") {
179
+ if (input === "c") {
178
180
  onCleanupCommand?.();
179
181
  } else if (input === "r" && onRefresh) {
180
182
  onRefresh();
@@ -225,7 +227,6 @@ export function BranchListScreen({
225
227
  { key: "enter", description: "Select" },
226
228
  { key: "f", description: "Filter" },
227
229
  { key: "r", description: "Refresh" },
228
- { key: "m", description: "Manage worktrees" },
229
230
  { key: "c", description: "Cleanup branches" },
230
231
  ];
231
232
 
@@ -311,7 +312,10 @@ export function BranchListScreen({
311
312
  const staticPrefix = `${arrow} ${indicatorPrefix}`;
312
313
  const staticPrefixWidth = measureDisplayWidth(staticPrefix);
313
314
  const maxLeftDisplayWidth = Math.max(0, columns - timestampWidth - 1);
314
- const maxLabelWidth = Math.max(0, maxLeftDisplayWidth - staticPrefixWidth);
315
+ const maxLabelWidth = Math.max(
316
+ 0,
317
+ maxLeftDisplayWidth - staticPrefixWidth,
318
+ );
315
319
  const truncatedLabel = truncateToWidth(item.label, maxLabelWidth);
316
320
  const leftText = `${staticPrefix}${truncatedLabel}`;
317
321
 
@@ -350,7 +354,7 @@ export function BranchListScreen({
350
354
  onChange={setFilterQuery}
351
355
  onSubmit={() => {}} // No-op: filter is applied in real-time
352
356
  placeholder="Type to search..."
353
- blockKeys={["c", "r", "m", "f"]} // Block shortcuts while typing
357
+ blockKeys={["c", "r", "f"]} // Block shortcuts while typing
354
358
  />
355
359
  ) : (
356
360
  <Text dimColor>{filterQuery || "(press f to filter)"}</Text>
@@ -409,14 +413,20 @@ export function BranchListScreen({
409
413
  !error &&
410
414
  branches.length > 0 &&
411
415
  filteredBranches.length > 0 && (
412
- <Select
413
- items={filteredBranches}
414
- onSelect={onSelect}
415
- limit={limit}
416
- disabled={Boolean(cleanupUI?.inputLocked)}
417
- renderIndicator={() => null}
418
- renderItem={renderBranchRow}
419
- />
416
+ <>
417
+ {/* Column labels */}
418
+ <Box>
419
+ <Text dimColor>{headerText}</Text>
420
+ </Box>
421
+ <Select
422
+ items={filteredBranches}
423
+ onSelect={onSelect}
424
+ limit={limit}
425
+ disabled={Boolean(cleanupUI?.inputLocked)}
426
+ renderIndicator={() => null}
427
+ renderItem={renderBranchRow}
428
+ />
429
+ </>
420
430
  )}
421
431
  </Box>
422
432
 
@@ -106,56 +106,54 @@ export function ModelSelectorScreen({
106
106
  [selectedModel],
107
107
  );
108
108
 
109
- const inferenceItems: InferenceSelectItem[] = useMemo(
110
- () => {
111
- return inferenceOptions.map((level) => {
112
- if (selectedModel?.id === "gpt-5.1-codex-max") {
113
- if (level === "low") {
114
- return {
115
- label: "Low",
116
- value: level,
117
- hint: "Fast responses with lighter reasoning",
118
- };
119
- }
120
- if (level === "medium") {
121
- return {
122
- label: "Medium (default)",
123
- value: level,
124
- hint: "Balances speed and reasoning depth for everyday tasks",
125
- };
126
- }
127
- if (level === "high") {
128
- return {
129
- label: "High",
130
- value: level,
131
- hint: "Maximizes reasoning depth for complex problems",
132
- };
133
- }
134
- if (level === "xhigh") {
135
- return {
136
- label: "Extra high",
137
- value: level,
138
- hint:
139
- "Extra high reasoning depth; may quickly consume Plus plan rate limits.",
140
- };
141
- }
109
+ const inferenceItems: InferenceSelectItem[] = useMemo(() => {
110
+ return inferenceOptions.map((level) => {
111
+ if (selectedModel?.id === "gpt-5.1-codex-max") {
112
+ if (level === "low") {
113
+ return {
114
+ label: "Low",
115
+ value: level,
116
+ hint: "Fast responses with lighter reasoning",
117
+ };
142
118
  }
119
+ if (level === "medium") {
120
+ return {
121
+ label: "Medium (default)",
122
+ value: level,
123
+ hint: "Balances speed and reasoning depth for everyday tasks",
124
+ };
125
+ }
126
+ if (level === "high") {
127
+ return {
128
+ label: "High",
129
+ value: level,
130
+ hint: "Maximizes reasoning depth for complex problems",
131
+ };
132
+ }
133
+ if (level === "xhigh") {
134
+ return {
135
+ label: "Extra high",
136
+ value: level,
137
+ hint: "Extra high reasoning depth; may quickly consume Plus plan rate limits.",
138
+ };
139
+ }
140
+ }
143
141
 
144
- return {
145
- label: INFERENCE_LABELS[level],
146
- value: level,
147
- };
148
- });
149
- },
150
- [inferenceOptions, selectedModel?.id],
151
- );
142
+ return {
143
+ label: INFERENCE_LABELS[level],
144
+ value: level,
145
+ };
146
+ });
147
+ }, [inferenceOptions, selectedModel?.id]);
152
148
 
153
149
  const defaultInferenceIndex = useMemo(() => {
154
150
  const initialLevel = initialSelection?.inferenceLevel;
155
151
  if (initialLevel && inferenceOptions.includes(initialLevel)) {
156
152
  return inferenceOptions.findIndex((lvl) => lvl === initialLevel);
157
153
  }
158
- const defaultLevel = getDefaultInferenceForModel(selectedModel ?? undefined);
154
+ const defaultLevel = getDefaultInferenceForModel(
155
+ selectedModel ?? undefined,
156
+ );
159
157
  if (!defaultLevel) return 0;
160
158
  const index = inferenceOptions.findIndex((lvl) => lvl === defaultLevel);
161
159
  return index >= 0 ? index : 0;
@@ -223,11 +221,9 @@ export function ModelSelectorScreen({
223
221
  {isSelected ? (
224
222
  <Text color="cyan">➤ {item.label}</Text>
225
223
  ) : (
226
- <Text> {item.label}</Text>
224
+ <Text> {item.label}</Text>
227
225
  )}
228
- {item.description ? (
229
- <Text color="gray"> {item.description}</Text>
230
- ) : null}
226
+ {item.description ? <Text color="gray"> {item.description}</Text> : null}
231
227
  </Box>
232
228
  );
233
229
 
@@ -271,7 +267,8 @@ export function ModelSelectorScreen({
271
267
  <Select
272
268
  items={[
273
269
  {
274
- label: "No model selection required. Press Enter to continue.",
270
+ label:
271
+ "No model selection required. Press Enter to continue.",
275
272
  value: "__continue__",
276
273
  },
277
274
  ]}
@@ -302,10 +299,10 @@ export function ModelSelectorScreen({
302
299
  {isSelected ? (
303
300
  <Text color="cyan">➤ {item.label}</Text>
304
301
  ) : (
305
- <Text> {item.label}</Text>
302
+ <Text> {item.label}</Text>
306
303
  )}
307
304
  {"hint" in item && item.hint ? (
308
- <Text color="gray"> {item.hint}</Text>
305
+ <Text color="gray"> {item.hint}</Text>
309
306
  ) : null}
310
307
  </Box>
311
308
  )}
@@ -58,9 +58,6 @@ export function PRCleanupScreen({
58
58
  } else {
59
59
  flags.push("branch");
60
60
  }
61
- if (target.reasons?.includes("merged-pr")) {
62
- flags.push("merged");
63
- }
64
61
  if (target.reasons?.includes("no-diff-with-base")) {
65
62
  flags.push("base");
66
63
  }