@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
@@ -246,10 +246,930 @@ describe("AppSolid cleanup command", () => {
246
246
 
247
247
  try {
248
248
  const frame = testSetup.captureCharFrame();
249
- expect(frame).toMatch(/\[ \] w {2,}feature\/safe/);
249
+ expect(frame).toMatch(/\[ \] w o feature\/safe/);
250
250
  expect(frame).toContain("[ ] w ! feature/unsafe");
251
251
  } finally {
252
252
  testSetup.renderer.destroy();
253
253
  }
254
254
  });
255
+
256
+ it("updates safety icons as each branch check completes", async () => {
257
+ let releaseSecond: (() => void) | null = null;
258
+ const progressGate = new Promise<void>((resolve) => {
259
+ releaseSecond = resolve;
260
+ });
261
+ const getCleanupStatusMock = mock(
262
+ async ({
263
+ onProgress,
264
+ }: { onProgress?: (status: { branch: string }) => void } = {}) => {
265
+ const firstStatus = {
266
+ worktreePath: "/tmp/first",
267
+ branch: "feature/first",
268
+ hasUncommittedChanges: false,
269
+ hasUnpushedCommits: false,
270
+ cleanupType: "worktree-and-branch",
271
+ hasRemoteBranch: true,
272
+ hasUniqueCommits: false,
273
+ hasUpstream: true,
274
+ upstream: "origin/feature/first",
275
+ isAccessible: true,
276
+ reasons: ["no-diff-with-base"],
277
+ };
278
+ const secondStatus = {
279
+ worktreePath: "/tmp/second",
280
+ branch: "feature/second",
281
+ hasUncommittedChanges: false,
282
+ hasUnpushedCommits: false,
283
+ cleanupType: "worktree-and-branch",
284
+ hasRemoteBranch: true,
285
+ hasUniqueCommits: true,
286
+ hasUpstream: true,
287
+ upstream: "origin/feature/second",
288
+ isAccessible: true,
289
+ reasons: ["remote-synced"],
290
+ };
291
+
292
+ onProgress?.(firstStatus);
293
+ await progressGate;
294
+ onProgress?.(secondStatus);
295
+ return [firstStatus, secondStatus];
296
+ },
297
+ );
298
+
299
+ mock.module?.("../../../../worktree.js", () => ({
300
+ listAdditionalWorktrees: mock(async () => []),
301
+ repairWorktrees: mock(async () => ({
302
+ repairedCount: 0,
303
+ failedCount: 0,
304
+ failures: [],
305
+ })),
306
+ removeWorktree: mock(async () => {}),
307
+ getCleanupStatus: getCleanupStatusMock,
308
+ isProtectedBranchName: mock(() => false),
309
+ }));
310
+
311
+ mock.module?.("../../../../git.js", () => ({
312
+ getRepositoryRoot: mock(async () => "/repo"),
313
+ getAllBranches: mock(async () => []),
314
+ getLocalBranches: mock(async () => []),
315
+ getCurrentBranch: mock(async () => "main"),
316
+ deleteBranch: mock(async () => {}),
317
+ }));
318
+
319
+ mock.module?.("../../../../config/index.js", () => ({
320
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
321
+ getLastToolUsageMap: mock(async () => new Map()),
322
+ loadSession: mock(async () => null),
323
+ }));
324
+
325
+ mock.module?.("../../../../config/tools.js", () => ({
326
+ getAllCodingAgents: mock(async () => [
327
+ { id: "codex-cli", displayName: "Codex CLI" },
328
+ ]),
329
+ }));
330
+
331
+ mock.module?.("../../../../config/profiles.js", () => ({
332
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
333
+ createProfile: mock(async () => {}),
334
+ updateProfile: mock(async () => {}),
335
+ deleteProfile: mock(async () => {}),
336
+ setActiveProfile: mock(async () => {}),
337
+ }));
338
+
339
+ const { AppSolid } = await import("../../App.solid.js");
340
+
341
+ const firstBranch = createBranch({
342
+ name: "feature/first",
343
+ label: "feature/first",
344
+ value: "feature/first",
345
+ worktree: { path: "/tmp/first", locked: false, prunable: false },
346
+ });
347
+ const secondBranch = createBranch({
348
+ name: "feature/second",
349
+ label: "feature/second",
350
+ value: "feature/second",
351
+ worktree: { path: "/tmp/second", locked: false, prunable: false },
352
+ });
353
+
354
+ const testSetup = await testRender(
355
+ () => (
356
+ <AppSolid
357
+ branches={[firstBranch, secondBranch]}
358
+ stats={makeStats({ localCount: 2, worktreeCount: 2 })}
359
+ version={null}
360
+ toolStatuses={[]}
361
+ />
362
+ ),
363
+ { width: 80, height: 24 },
364
+ );
365
+ await testSetup.renderOnce();
366
+ await new Promise((resolve) => setTimeout(resolve, 0));
367
+ await testSetup.renderOnce();
368
+
369
+ try {
370
+ let frame = testSetup.captureCharFrame();
371
+ expect(frame).toMatch(/\[ \] w o feature\/first/);
372
+ expect(frame).toMatch(/\[ \] w [-\\|/] feature\/second/);
373
+
374
+ releaseSecond?.();
375
+ await new Promise((resolve) => setTimeout(resolve, 0));
376
+ await testSetup.renderOnce();
377
+
378
+ frame = testSetup.captureCharFrame();
379
+ expect(frame).toMatch(/\[ \] w \* feature\/second/);
380
+ } finally {
381
+ testSetup.renderer.destroy();
382
+ }
383
+ });
384
+ });
385
+
386
+ describe("AppSolid unsafe selection confirm", () => {
387
+ it("shows confirm and cancels selection on Cancel", async () => {
388
+ const getCleanupStatusMock = mock(async () => [
389
+ {
390
+ worktreePath: "/tmp/worktree",
391
+ branch: "feature/unsafe",
392
+ hasUncommittedChanges: false,
393
+ hasUnpushedCommits: true,
394
+ cleanupType: "worktree-and-branch",
395
+ hasRemoteBranch: true,
396
+ hasUniqueCommits: false,
397
+ hasUpstream: true,
398
+ upstream: "origin/feature/unsafe",
399
+ isAccessible: true,
400
+ reasons: ["no-diff-with-base"],
401
+ },
402
+ ]);
403
+
404
+ mock.module?.("../../../../worktree.js", () => ({
405
+ listAdditionalWorktrees: mock(async () => []),
406
+ repairWorktrees: mock(async () => ({
407
+ repairedCount: 0,
408
+ failedCount: 0,
409
+ failures: [],
410
+ })),
411
+ removeWorktree: mock(async () => {}),
412
+ getCleanupStatus: getCleanupStatusMock,
413
+ isProtectedBranchName: mock(() => false),
414
+ }));
415
+
416
+ mock.module?.("../../../../git.js", () => ({
417
+ getRepositoryRoot: mock(async () => "/repo"),
418
+ getAllBranches: mock(async () => []),
419
+ getLocalBranches: mock(async () => []),
420
+ getCurrentBranch: mock(async () => "main"),
421
+ deleteBranch: mock(async () => {}),
422
+ }));
423
+
424
+ mock.module?.("../../../../config/index.js", () => ({
425
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
426
+ getLastToolUsageMap: mock(async () => new Map()),
427
+ loadSession: mock(async () => null),
428
+ }));
429
+
430
+ mock.module?.("../../../../config/tools.js", () => ({
431
+ getAllCodingAgents: mock(async () => [
432
+ { id: "codex-cli", displayName: "Codex CLI" },
433
+ ]),
434
+ }));
435
+
436
+ mock.module?.("../../../../config/profiles.js", () => ({
437
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
438
+ createProfile: mock(async () => {}),
439
+ updateProfile: mock(async () => {}),
440
+ deleteProfile: mock(async () => {}),
441
+ setActiveProfile: mock(async () => {}),
442
+ }));
443
+
444
+ const { AppSolid } = await import("../../App.solid.js");
445
+
446
+ const branch = createBranch({
447
+ name: "feature/unsafe",
448
+ label: "feature/unsafe",
449
+ value: "feature/unsafe",
450
+ });
451
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
452
+
453
+ const testSetup = await testRender(
454
+ () => (
455
+ <AppSolid
456
+ branches={[branch]}
457
+ stats={stats}
458
+ version={null}
459
+ toolStatuses={[]}
460
+ />
461
+ ),
462
+ { width: 80, height: 24 },
463
+ );
464
+ await testSetup.renderOnce();
465
+ await new Promise((resolve) => setTimeout(resolve, 0));
466
+ await testSetup.renderOnce();
467
+
468
+ try {
469
+ await testSetup.mockInput.typeText(" ");
470
+ await testSetup.renderOnce();
471
+
472
+ let frame = testSetup.captureCharFrame();
473
+ expect(frame).toContain("Unsafe branch selected. Select anyway?");
474
+ expect(frame).toContain("OK");
475
+ expect(frame).toContain("Cancel");
476
+
477
+ await testSetup.mockInput.typeText("n");
478
+ await testSetup.renderOnce();
479
+
480
+ frame = testSetup.captureCharFrame();
481
+ expect(frame).toContain("[ ] w");
482
+ expect(frame).toContain("feature/unsafe");
483
+ expect(frame).not.toContain("Unsafe branch selected. Select anyway?");
484
+ } finally {
485
+ testSetup.renderer.destroy();
486
+ }
487
+ });
488
+
489
+ it("shows confirm when safety check is pending", async () => {
490
+ let resolveStatus: ((value: unknown[]) => void) | null = null;
491
+ const pendingPromise = new Promise<unknown[]>((resolve) => {
492
+ resolveStatus = resolve;
493
+ });
494
+ const getCleanupStatusMock = mock(async () => pendingPromise);
495
+
496
+ mock.module?.("../../../../worktree.js", () => ({
497
+ listAdditionalWorktrees: mock(async () => []),
498
+ repairWorktrees: mock(async () => ({
499
+ repairedCount: 0,
500
+ failedCount: 0,
501
+ failures: [],
502
+ })),
503
+ removeWorktree: mock(async () => {}),
504
+ getCleanupStatus: getCleanupStatusMock,
505
+ isProtectedBranchName: mock(() => false),
506
+ }));
507
+
508
+ mock.module?.("../../../../git.js", () => ({
509
+ getRepositoryRoot: mock(async () => "/repo"),
510
+ getAllBranches: mock(async () => []),
511
+ getLocalBranches: mock(async () => []),
512
+ getCurrentBranch: mock(async () => "main"),
513
+ deleteBranch: mock(async () => {}),
514
+ }));
515
+
516
+ mock.module?.("../../../../config/index.js", () => ({
517
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
518
+ getLastToolUsageMap: mock(async () => new Map()),
519
+ loadSession: mock(async () => null),
520
+ }));
521
+
522
+ mock.module?.("../../../../config/tools.js", () => ({
523
+ getAllCodingAgents: mock(async () => [
524
+ { id: "codex-cli", displayName: "Codex CLI" },
525
+ ]),
526
+ }));
527
+
528
+ mock.module?.("../../../../config/profiles.js", () => ({
529
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
530
+ createProfile: mock(async () => {}),
531
+ updateProfile: mock(async () => {}),
532
+ deleteProfile: mock(async () => {}),
533
+ setActiveProfile: mock(async () => {}),
534
+ }));
535
+
536
+ const { AppSolid } = await import("../../App.solid.js");
537
+
538
+ const branch = createBranch({
539
+ name: "feature/pending",
540
+ label: "feature/pending",
541
+ value: "feature/pending",
542
+ });
543
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
544
+
545
+ const testSetup = await testRender(
546
+ () => (
547
+ <AppSolid
548
+ branches={[branch]}
549
+ stats={stats}
550
+ version={null}
551
+ toolStatuses={[]}
552
+ />
553
+ ),
554
+ { width: 80, height: 24 },
555
+ );
556
+ await testSetup.renderOnce();
557
+ await new Promise((resolve) => setTimeout(resolve, 0));
558
+ await testSetup.renderOnce();
559
+
560
+ try {
561
+ await testSetup.mockInput.typeText(" ");
562
+ await testSetup.renderOnce();
563
+
564
+ let frame = testSetup.captureCharFrame();
565
+ expect(frame).toContain("Safety check in progress. Select anyway?");
566
+
567
+ await testSetup.mockInput.typeText("n");
568
+ await testSetup.renderOnce();
569
+
570
+ frame = testSetup.captureCharFrame();
571
+ expect(frame).toContain("[ ] w");
572
+ expect(frame).toContain("feature/pending");
573
+ expect(frame).not.toContain("Safety check in progress. Select anyway?");
574
+ } finally {
575
+ resolveStatus?.([]);
576
+ testSetup.renderer.destroy();
577
+ }
578
+ });
579
+
580
+ it("does not propagate Enter from confirm to branch selection", async () => {
581
+ const getCleanupStatusMock = mock(async () => [
582
+ {
583
+ worktreePath: "/tmp/worktree",
584
+ branch: "feature/unsafe-enter",
585
+ hasUncommittedChanges: false,
586
+ hasUnpushedCommits: true,
587
+ cleanupType: "worktree-and-branch",
588
+ hasRemoteBranch: true,
589
+ hasUniqueCommits: false,
590
+ hasUpstream: true,
591
+ upstream: "origin/feature/unsafe-enter",
592
+ isAccessible: true,
593
+ reasons: ["no-diff-with-base"],
594
+ },
595
+ ]);
596
+
597
+ mock.module?.("../../../../worktree.js", () => ({
598
+ listAdditionalWorktrees: mock(async () => []),
599
+ repairWorktrees: mock(async () => ({
600
+ repairedCount: 0,
601
+ failedCount: 0,
602
+ failures: [],
603
+ })),
604
+ removeWorktree: mock(async () => {}),
605
+ getCleanupStatus: getCleanupStatusMock,
606
+ isProtectedBranchName: mock(() => false),
607
+ }));
608
+
609
+ mock.module?.("../../../../git.js", () => ({
610
+ getRepositoryRoot: mock(async () => "/repo"),
611
+ getAllBranches: mock(async () => []),
612
+ getLocalBranches: mock(async () => []),
613
+ getCurrentBranch: mock(async () => "main"),
614
+ deleteBranch: mock(async () => {}),
615
+ }));
616
+
617
+ mock.module?.("../../../../config/index.js", () => ({
618
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
619
+ getLastToolUsageMap: mock(async () => new Map()),
620
+ loadSession: mock(async () => null),
621
+ }));
622
+
623
+ mock.module?.("../../../../config/tools.js", () => ({
624
+ getAllCodingAgents: mock(async () => [
625
+ { id: "codex-cli", displayName: "Codex CLI" },
626
+ ]),
627
+ }));
628
+
629
+ mock.module?.("../../../../config/profiles.js", () => ({
630
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
631
+ createProfile: mock(async () => {}),
632
+ updateProfile: mock(async () => {}),
633
+ deleteProfile: mock(async () => {}),
634
+ setActiveProfile: mock(async () => {}),
635
+ }));
636
+
637
+ const { AppSolid } = await import("../../App.solid.js");
638
+
639
+ const branch = createBranch({
640
+ name: "feature/unsafe-enter",
641
+ label: "feature/unsafe-enter",
642
+ value: "feature/unsafe-enter",
643
+ });
644
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
645
+
646
+ const testSetup = await testRender(
647
+ () => (
648
+ <AppSolid
649
+ branches={[branch]}
650
+ stats={stats}
651
+ version={null}
652
+ toolStatuses={[]}
653
+ />
654
+ ),
655
+ { width: 80, height: 24 },
656
+ );
657
+ await testSetup.renderOnce();
658
+ await new Promise((resolve) => setTimeout(resolve, 0));
659
+ await testSetup.renderOnce();
660
+
661
+ try {
662
+ await testSetup.mockInput.typeText(" ");
663
+ await testSetup.renderOnce();
664
+
665
+ testSetup.mockInput.pressEnter();
666
+ await testSetup.renderOnce();
667
+
668
+ const frame = testSetup.captureCharFrame();
669
+ expect(frame).not.toContain("Unsafe branch selected. Select anyway?");
670
+ expect(frame).toContain("[ ] w");
671
+ expect(frame).not.toContain("Open existing worktree");
672
+ } finally {
673
+ testSetup.renderer.destroy();
674
+ }
675
+ });
676
+
677
+ it("selects unsafe branch on OK", async () => {
678
+ const getCleanupStatusMock = mock(async () => [
679
+ {
680
+ worktreePath: "/tmp/worktree",
681
+ branch: "feature/unsafe-ok",
682
+ hasUncommittedChanges: false,
683
+ hasUnpushedCommits: true,
684
+ cleanupType: "worktree-and-branch",
685
+ hasRemoteBranch: true,
686
+ hasUniqueCommits: false,
687
+ hasUpstream: true,
688
+ upstream: "origin/feature/unsafe-ok",
689
+ isAccessible: true,
690
+ reasons: ["no-diff-with-base"],
691
+ },
692
+ ]);
693
+
694
+ mock.module?.("../../../../worktree.js", () => ({
695
+ listAdditionalWorktrees: mock(async () => []),
696
+ repairWorktrees: mock(async () => ({
697
+ repairedCount: 0,
698
+ failedCount: 0,
699
+ failures: [],
700
+ })),
701
+ removeWorktree: mock(async () => {}),
702
+ getCleanupStatus: getCleanupStatusMock,
703
+ isProtectedBranchName: mock(() => false),
704
+ }));
705
+
706
+ mock.module?.("../../../../git.js", () => ({
707
+ getRepositoryRoot: mock(async () => "/repo"),
708
+ getAllBranches: mock(async () => []),
709
+ getLocalBranches: mock(async () => []),
710
+ getCurrentBranch: mock(async () => "main"),
711
+ deleteBranch: mock(async () => {}),
712
+ }));
713
+
714
+ mock.module?.("../../../../config/index.js", () => ({
715
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
716
+ getLastToolUsageMap: mock(async () => new Map()),
717
+ loadSession: mock(async () => null),
718
+ }));
719
+
720
+ mock.module?.("../../../../config/tools.js", () => ({
721
+ getAllCodingAgents: mock(async () => [
722
+ { id: "codex-cli", displayName: "Codex CLI" },
723
+ ]),
724
+ }));
725
+
726
+ mock.module?.("../../../../config/profiles.js", () => ({
727
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
728
+ createProfile: mock(async () => {}),
729
+ updateProfile: mock(async () => {}),
730
+ deleteProfile: mock(async () => {}),
731
+ setActiveProfile: mock(async () => {}),
732
+ }));
733
+
734
+ const { AppSolid } = await import("../../App.solid.js");
735
+
736
+ const branch = createBranch({
737
+ name: "feature/unsafe-ok",
738
+ label: "feature/unsafe-ok",
739
+ value: "feature/unsafe-ok",
740
+ });
741
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
742
+
743
+ const testSetup = await testRender(
744
+ () => (
745
+ <AppSolid
746
+ branches={[branch]}
747
+ stats={stats}
748
+ version={null}
749
+ toolStatuses={[]}
750
+ />
751
+ ),
752
+ { width: 80, height: 24 },
753
+ );
754
+ await testSetup.renderOnce();
755
+ await new Promise((resolve) => setTimeout(resolve, 0));
756
+ await testSetup.renderOnce();
757
+
758
+ try {
759
+ await testSetup.mockInput.typeText(" ");
760
+ await testSetup.renderOnce();
761
+
762
+ await testSetup.mockInput.typeText("y");
763
+ await testSetup.renderOnce();
764
+
765
+ const frame = testSetup.captureCharFrame();
766
+ expect(frame).toContain("[*]");
767
+ expect(frame).toContain("feature/unsafe-ok");
768
+ expect(frame).not.toContain("Unsafe branch selected. Select anyway?");
769
+ } finally {
770
+ testSetup.renderer.destroy();
771
+ }
772
+ });
773
+ });
774
+
775
+ describe("AppSolid selected cleanup targets", () => {
776
+ it("cleans unsafe branch when confirmed and selected", async () => {
777
+ const deleteBranchMock = mock(async () => {});
778
+ const removeWorktreeMock = mock(async () => {});
779
+ const getCleanupStatusMock = mock(async () => [
780
+ {
781
+ worktreePath: "/tmp/worktree",
782
+ branch: "feature/unsafe-clean",
783
+ hasUncommittedChanges: false,
784
+ hasUnpushedCommits: true,
785
+ cleanupType: "worktree-and-branch",
786
+ hasRemoteBranch: true,
787
+ hasUniqueCommits: false,
788
+ hasUpstream: true,
789
+ upstream: "origin/feature/unsafe-clean",
790
+ isAccessible: true,
791
+ reasons: ["no-diff-with-base"],
792
+ },
793
+ ]);
794
+
795
+ mock.module?.("../../../../worktree.js", () => ({
796
+ listAdditionalWorktrees: mock(async () => []),
797
+ repairWorktrees: mock(async () => ({
798
+ repairedCount: 0,
799
+ failedCount: 0,
800
+ failures: [],
801
+ })),
802
+ removeWorktree: removeWorktreeMock,
803
+ getCleanupStatus: getCleanupStatusMock,
804
+ isProtectedBranchName: mock(() => false),
805
+ }));
806
+
807
+ mock.module?.("../../../../git.js", () => ({
808
+ getRepositoryRoot: mock(async () => "/repo"),
809
+ getAllBranches: mock(async () => []),
810
+ getLocalBranches: mock(async () => []),
811
+ getCurrentBranch: mock(async () => "main"),
812
+ deleteBranch: deleteBranchMock,
813
+ }));
814
+
815
+ mock.module?.("../../../../config/index.js", () => ({
816
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
817
+ getLastToolUsageMap: mock(async () => new Map()),
818
+ loadSession: mock(async () => null),
819
+ }));
820
+
821
+ mock.module?.("../../../../config/tools.js", () => ({
822
+ getAllCodingAgents: mock(async () => [
823
+ { id: "codex-cli", displayName: "Codex CLI" },
824
+ ]),
825
+ }));
826
+
827
+ mock.module?.("../../../../config/profiles.js", () => ({
828
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
829
+ createProfile: mock(async () => {}),
830
+ updateProfile: mock(async () => {}),
831
+ deleteProfile: mock(async () => {}),
832
+ setActiveProfile: mock(async () => {}),
833
+ }));
834
+
835
+ const { AppSolid } = await import("../../App.solid.js");
836
+
837
+ const branch = createBranch({
838
+ name: "feature/unsafe-clean",
839
+ label: "feature/unsafe-clean",
840
+ value: "feature/unsafe-clean",
841
+ });
842
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
843
+
844
+ const testSetup = await testRender(
845
+ () => (
846
+ <AppSolid
847
+ branches={[branch]}
848
+ stats={stats}
849
+ version={null}
850
+ toolStatuses={[]}
851
+ />
852
+ ),
853
+ { width: 80, height: 24 },
854
+ );
855
+ await testSetup.renderOnce();
856
+ await new Promise((resolve) => setTimeout(resolve, 0));
857
+ await testSetup.renderOnce();
858
+
859
+ try {
860
+ await testSetup.mockInput.typeText(" ");
861
+ await testSetup.renderOnce();
862
+
863
+ await testSetup.mockInput.typeText("y");
864
+ await testSetup.renderOnce();
865
+
866
+ await testSetup.mockInput.typeText("c");
867
+ await testSetup.renderOnce();
868
+ await new Promise((resolve) => setTimeout(resolve, 0));
869
+
870
+ expect(removeWorktreeMock).toHaveBeenCalledWith("/tmp/worktree", false);
871
+ expect(deleteBranchMock).toHaveBeenCalledWith(
872
+ "feature/unsafe-clean",
873
+ true,
874
+ );
875
+ } finally {
876
+ testSetup.renderer.destroy();
877
+ }
878
+ });
879
+
880
+ it("includes protected branch when selected", async () => {
881
+ const deleteBranchMock = mock(async () => {});
882
+ const removeWorktreeMock = mock(async () => {});
883
+ const getCleanupStatusMock = mock(async () => [
884
+ {
885
+ worktreePath: "/tmp/worktree",
886
+ branch: "develop",
887
+ hasUncommittedChanges: false,
888
+ hasUnpushedCommits: false,
889
+ cleanupType: "worktree-and-branch",
890
+ hasRemoteBranch: true,
891
+ hasUniqueCommits: false,
892
+ hasUpstream: true,
893
+ upstream: "origin/develop",
894
+ isAccessible: true,
895
+ reasons: ["no-diff-with-base"],
896
+ },
897
+ ]);
898
+
899
+ mock.module?.("../../../../worktree.js", () => ({
900
+ listAdditionalWorktrees: mock(async () => []),
901
+ repairWorktrees: mock(async () => ({
902
+ repairedCount: 0,
903
+ failedCount: 0,
904
+ failures: [],
905
+ })),
906
+ removeWorktree: removeWorktreeMock,
907
+ getCleanupStatus: getCleanupStatusMock,
908
+ isProtectedBranchName: mock(() => true),
909
+ }));
910
+
911
+ mock.module?.("../../../../git.js", () => ({
912
+ getRepositoryRoot: mock(async () => "/repo"),
913
+ getAllBranches: mock(async () => []),
914
+ getLocalBranches: mock(async () => []),
915
+ getCurrentBranch: mock(async () => "main"),
916
+ deleteBranch: deleteBranchMock,
917
+ }));
918
+
919
+ mock.module?.("../../../../config/index.js", () => ({
920
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
921
+ getLastToolUsageMap: mock(async () => new Map()),
922
+ loadSession: mock(async () => null),
923
+ }));
924
+
925
+ mock.module?.("../../../../config/tools.js", () => ({
926
+ getAllCodingAgents: mock(async () => [
927
+ { id: "codex-cli", displayName: "Codex CLI" },
928
+ ]),
929
+ }));
930
+
931
+ mock.module?.("../../../../config/profiles.js", () => ({
932
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
933
+ createProfile: mock(async () => {}),
934
+ updateProfile: mock(async () => {}),
935
+ deleteProfile: mock(async () => {}),
936
+ setActiveProfile: mock(async () => {}),
937
+ }));
938
+
939
+ const { AppSolid } = await import("../../App.solid.js");
940
+
941
+ const branch = createBranch({
942
+ name: "develop",
943
+ label: "develop",
944
+ value: "develop",
945
+ });
946
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
947
+
948
+ const testSetup = await testRender(
949
+ () => (
950
+ <AppSolid
951
+ branches={[branch]}
952
+ stats={stats}
953
+ version={null}
954
+ toolStatuses={[]}
955
+ />
956
+ ),
957
+ { width: 80, height: 24 },
958
+ );
959
+ await testSetup.renderOnce();
960
+ await new Promise((resolve) => setTimeout(resolve, 0));
961
+ await testSetup.renderOnce();
962
+
963
+ try {
964
+ await testSetup.mockInput.typeText(" ");
965
+ await testSetup.renderOnce();
966
+
967
+ await testSetup.mockInput.typeText("c");
968
+ await testSetup.renderOnce();
969
+ await new Promise((resolve) => setTimeout(resolve, 0));
970
+
971
+ expect(deleteBranchMock).toHaveBeenCalledWith("develop", true);
972
+ expect(removeWorktreeMock).toHaveBeenCalledWith("/tmp/worktree", false);
973
+ } finally {
974
+ testSetup.renderer.destroy();
975
+ }
976
+ });
977
+
978
+ it("excludes current branch even when selected", async () => {
979
+ const deleteBranchMock = mock(async () => {});
980
+ const removeWorktreeMock = mock(async () => {});
981
+ const getCleanupStatusMock = mock(async () => [
982
+ {
983
+ worktreePath: "/tmp/worktree",
984
+ branch: "main",
985
+ hasUncommittedChanges: false,
986
+ hasUnpushedCommits: false,
987
+ cleanupType: "worktree-and-branch",
988
+ hasRemoteBranch: true,
989
+ hasUniqueCommits: false,
990
+ hasUpstream: true,
991
+ upstream: "origin/main",
992
+ isAccessible: true,
993
+ reasons: ["no-diff-with-base"],
994
+ },
995
+ ]);
996
+
997
+ mock.module?.("../../../../worktree.js", () => ({
998
+ listAdditionalWorktrees: mock(async () => []),
999
+ repairWorktrees: mock(async () => ({
1000
+ repairedCount: 0,
1001
+ failedCount: 0,
1002
+ failures: [],
1003
+ })),
1004
+ removeWorktree: removeWorktreeMock,
1005
+ getCleanupStatus: getCleanupStatusMock,
1006
+ isProtectedBranchName: mock(() => true),
1007
+ }));
1008
+
1009
+ mock.module?.("../../../../git.js", () => ({
1010
+ getRepositoryRoot: mock(async () => "/repo"),
1011
+ getAllBranches: mock(async () => []),
1012
+ getLocalBranches: mock(async () => []),
1013
+ getCurrentBranch: mock(async () => "main"),
1014
+ deleteBranch: deleteBranchMock,
1015
+ }));
1016
+
1017
+ mock.module?.("../../../../config/index.js", () => ({
1018
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
1019
+ getLastToolUsageMap: mock(async () => new Map()),
1020
+ loadSession: mock(async () => null),
1021
+ }));
1022
+
1023
+ mock.module?.("../../../../config/tools.js", () => ({
1024
+ getAllCodingAgents: mock(async () => [
1025
+ { id: "codex-cli", displayName: "Codex CLI" },
1026
+ ]),
1027
+ }));
1028
+
1029
+ mock.module?.("../../../../config/profiles.js", () => ({
1030
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
1031
+ createProfile: mock(async () => {}),
1032
+ updateProfile: mock(async () => {}),
1033
+ deleteProfile: mock(async () => {}),
1034
+ setActiveProfile: mock(async () => {}),
1035
+ }));
1036
+
1037
+ const { AppSolid } = await import("../../App.solid.js");
1038
+
1039
+ const branch = createBranch({
1040
+ name: "main",
1041
+ label: "main",
1042
+ value: "main",
1043
+ isCurrent: true,
1044
+ });
1045
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
1046
+
1047
+ const testSetup = await testRender(
1048
+ () => (
1049
+ <AppSolid
1050
+ branches={[branch]}
1051
+ stats={stats}
1052
+ version={null}
1053
+ toolStatuses={[]}
1054
+ />
1055
+ ),
1056
+ { width: 80, height: 24 },
1057
+ );
1058
+ await testSetup.renderOnce();
1059
+ await new Promise((resolve) => setTimeout(resolve, 0));
1060
+ await testSetup.renderOnce();
1061
+
1062
+ try {
1063
+ await testSetup.mockInput.typeText(" ");
1064
+ await testSetup.renderOnce();
1065
+
1066
+ await testSetup.mockInput.typeText("c");
1067
+ await testSetup.renderOnce();
1068
+ await new Promise((resolve) => setTimeout(resolve, 0));
1069
+
1070
+ expect(deleteBranchMock).not.toHaveBeenCalled();
1071
+ expect(removeWorktreeMock).not.toHaveBeenCalled();
1072
+ } finally {
1073
+ testSetup.renderer.destroy();
1074
+ }
1075
+ });
1076
+
1077
+ it("repairs selected branch even when worktree is accessible", async () => {
1078
+ const repairWorktreesMock = mock(async () => ({
1079
+ repairedCount: 1,
1080
+ failedCount: 0,
1081
+ failures: [],
1082
+ }));
1083
+ const getCleanupStatusMock = mock(async () => [
1084
+ {
1085
+ worktreePath: "/tmp/worktree",
1086
+ branch: "feature/repair-target",
1087
+ hasUncommittedChanges: false,
1088
+ hasUnpushedCommits: false,
1089
+ cleanupType: "worktree-and-branch",
1090
+ hasRemoteBranch: true,
1091
+ hasUniqueCommits: false,
1092
+ hasUpstream: true,
1093
+ upstream: "origin/feature/repair-target",
1094
+ isAccessible: true,
1095
+ reasons: ["no-diff-with-base"],
1096
+ },
1097
+ ]);
1098
+
1099
+ mock.module?.("../../../../worktree.js", () => ({
1100
+ listAdditionalWorktrees: mock(async () => []),
1101
+ repairWorktrees: repairWorktreesMock,
1102
+ removeWorktree: mock(async () => {}),
1103
+ getCleanupStatus: getCleanupStatusMock,
1104
+ isProtectedBranchName: mock(() => false),
1105
+ }));
1106
+
1107
+ mock.module?.("../../../../git.js", () => ({
1108
+ getRepositoryRoot: mock(async () => "/repo"),
1109
+ getAllBranches: mock(async () => []),
1110
+ getLocalBranches: mock(async () => []),
1111
+ getCurrentBranch: mock(async () => "main"),
1112
+ deleteBranch: mock(async () => {}),
1113
+ }));
1114
+
1115
+ mock.module?.("../../../../config/index.js", () => ({
1116
+ getConfig: mock(async () => ({ defaultBaseBranch: "main" })),
1117
+ getLastToolUsageMap: mock(async () => new Map()),
1118
+ loadSession: mock(async () => null),
1119
+ }));
1120
+
1121
+ mock.module?.("../../../../config/tools.js", () => ({
1122
+ getAllCodingAgents: mock(async () => [
1123
+ { id: "codex-cli", displayName: "Codex CLI" },
1124
+ ]),
1125
+ }));
1126
+
1127
+ mock.module?.("../../../../config/profiles.js", () => ({
1128
+ loadProfiles: mock(async () => ({ profiles: {}, activeProfile: null })),
1129
+ createProfile: mock(async () => {}),
1130
+ updateProfile: mock(async () => {}),
1131
+ deleteProfile: mock(async () => {}),
1132
+ setActiveProfile: mock(async () => {}),
1133
+ }));
1134
+
1135
+ const { AppSolid } = await import("../../App.solid.js");
1136
+
1137
+ const branch = createBranch({
1138
+ name: "feature/repair-target",
1139
+ label: "feature/repair-target",
1140
+ value: "feature/repair-target",
1141
+ worktreeStatus: "active",
1142
+ });
1143
+ const stats = makeStats({ localCount: 1, worktreeCount: 1 });
1144
+
1145
+ const testSetup = await testRender(
1146
+ () => (
1147
+ <AppSolid
1148
+ branches={[branch]}
1149
+ stats={stats}
1150
+ version={null}
1151
+ toolStatuses={[]}
1152
+ />
1153
+ ),
1154
+ { width: 80, height: 24 },
1155
+ );
1156
+ await testSetup.renderOnce();
1157
+ await new Promise((resolve) => setTimeout(resolve, 0));
1158
+ await testSetup.renderOnce();
1159
+
1160
+ try {
1161
+ await testSetup.mockInput.typeText(" ");
1162
+ await testSetup.renderOnce();
1163
+
1164
+ await testSetup.mockInput.typeText("x");
1165
+ await testSetup.renderOnce();
1166
+ await new Promise((resolve) => setTimeout(resolve, 0));
1167
+
1168
+ expect(repairWorktreesMock).toHaveBeenCalledWith([
1169
+ "feature/repair-target",
1170
+ ]);
1171
+ } finally {
1172
+ testSetup.renderer.destroy();
1173
+ }
1174
+ });
255
1175
  });