@camaradesuk/git-worktree-tools 1.10.0 → 1.11.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.
Files changed (426) hide show
  1. package/README.md +4 -4
  2. package/package.json +12 -3
  3. package/dist/api/list.test.d.ts +0 -5
  4. package/dist/api/list.test.d.ts.map +0 -1
  5. package/dist/api/list.test.js +0 -390
  6. package/dist/api/list.test.js.map +0 -1
  7. package/dist/cli/cleanpr.test.d.ts +0 -2
  8. package/dist/cli/cleanpr.test.d.ts.map +0 -1
  9. package/dist/cli/cleanpr.test.js +0 -954
  10. package/dist/cli/cleanpr.test.js.map +0 -1
  11. package/dist/cli/lswt.test.d.ts +0 -2
  12. package/dist/cli/lswt.test.d.ts.map +0 -1
  13. package/dist/cli/lswt.test.js +0 -376
  14. package/dist/cli/lswt.test.js.map +0 -1
  15. package/dist/cli/newpr.test.d.ts +0 -2
  16. package/dist/cli/newpr.test.d.ts.map +0 -1
  17. package/dist/cli/newpr.test.js +0 -1182
  18. package/dist/cli/newpr.test.js.map +0 -1
  19. package/dist/cli/prs.test.d.ts +0 -8
  20. package/dist/cli/prs.test.d.ts.map +0 -1
  21. package/dist/cli/prs.test.js +0 -463
  22. package/dist/cli/prs.test.js.map +0 -1
  23. package/dist/cli/wt/clean.test.d.ts +0 -8
  24. package/dist/cli/wt/clean.test.d.ts.map +0 -1
  25. package/dist/cli/wt/clean.test.js +0 -624
  26. package/dist/cli/wt/clean.test.js.map +0 -1
  27. package/dist/cli/wt/completion.test.d.ts +0 -5
  28. package/dist/cli/wt/completion.test.d.ts.map +0 -1
  29. package/dist/cli/wt/completion.test.js +0 -275
  30. package/dist/cli/wt/completion.test.js.map +0 -1
  31. package/dist/cli/wt/config.test.d.ts +0 -7
  32. package/dist/cli/wt/config.test.d.ts.map +0 -1
  33. package/dist/cli/wt/config.test.js +0 -440
  34. package/dist/cli/wt/config.test.js.map +0 -1
  35. package/dist/cli/wt/entry.test.d.ts +0 -8
  36. package/dist/cli/wt/entry.test.d.ts.map +0 -1
  37. package/dist/cli/wt/entry.test.js +0 -201
  38. package/dist/cli/wt/entry.test.js.map +0 -1
  39. package/dist/cli/wt/init.test.d.ts +0 -5
  40. package/dist/cli/wt/init.test.d.ts.map +0 -1
  41. package/dist/cli/wt/init.test.js +0 -165
  42. package/dist/cli/wt/init.test.js.map +0 -1
  43. package/dist/cli/wt/init.unit.test.d.ts +0 -5
  44. package/dist/cli/wt/init.unit.test.d.ts.map +0 -1
  45. package/dist/cli/wt/init.unit.test.js +0 -432
  46. package/dist/cli/wt/init.unit.test.js.map +0 -1
  47. package/dist/cli/wt/interactive-menu.test.d.ts +0 -12
  48. package/dist/cli/wt/interactive-menu.test.d.ts.map +0 -1
  49. package/dist/cli/wt/interactive-menu.test.js +0 -796
  50. package/dist/cli/wt/interactive-menu.test.js.map +0 -1
  51. package/dist/cli/wt/list.test.d.ts +0 -10
  52. package/dist/cli/wt/list.test.d.ts.map +0 -1
  53. package/dist/cli/wt/list.test.js +0 -157
  54. package/dist/cli/wt/list.test.js.map +0 -1
  55. package/dist/cli/wt/prs.test.d.ts +0 -5
  56. package/dist/cli/wt/prs.test.d.ts.map +0 -1
  57. package/dist/cli/wt/prs.test.js +0 -410
  58. package/dist/cli/wt/prs.test.js.map +0 -1
  59. package/dist/cli/wt/run-command.test.d.ts +0 -5
  60. package/dist/cli/wt/run-command.test.d.ts.map +0 -1
  61. package/dist/cli/wt/run-command.test.js +0 -88
  62. package/dist/cli/wt/run-command.test.js.map +0 -1
  63. package/dist/cli/wt/state.test.d.ts +0 -9
  64. package/dist/cli/wt/state.test.d.ts.map +0 -1
  65. package/dist/cli/wt/state.test.js +0 -127
  66. package/dist/cli/wt/state.test.js.map +0 -1
  67. package/dist/cli/wt/wt.test.d.ts +0 -8
  68. package/dist/cli/wt/wt.test.d.ts.map +0 -1
  69. package/dist/cli/wt/wt.test.js +0 -739
  70. package/dist/cli/wt/wt.test.js.map +0 -1
  71. package/dist/cli/wt.unit.test.d.ts +0 -7
  72. package/dist/cli/wt.unit.test.d.ts.map +0 -1
  73. package/dist/cli/wt.unit.test.js +0 -160
  74. package/dist/cli/wt.unit.test.js.map +0 -1
  75. package/dist/cli/wtconfig.test.d.ts +0 -5
  76. package/dist/cli/wtconfig.test.d.ts.map +0 -1
  77. package/dist/cli/wtconfig.test.js +0 -1289
  78. package/dist/cli/wtconfig.test.js.map +0 -1
  79. package/dist/cli/wtlink.test.d.ts +0 -2
  80. package/dist/cli/wtlink.test.d.ts.map +0 -1
  81. package/dist/cli/wtlink.test.js +0 -249
  82. package/dist/cli/wtlink.test.js.map +0 -1
  83. package/dist/cli/wtstate.test.d.ts +0 -5
  84. package/dist/cli/wtstate.test.d.ts.map +0 -1
  85. package/dist/cli/wtstate.test.js +0 -193
  86. package/dist/cli/wtstate.test.js.map +0 -1
  87. package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts +0 -2
  88. package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts.map +0 -1
  89. package/dist/e2e/cleanpr/cleanpr.e2e.test.js +0 -326
  90. package/dist/e2e/cleanpr/cleanpr.e2e.test.js.map +0 -1
  91. package/dist/e2e/cli.e2e.test.d.ts +0 -2
  92. package/dist/e2e/cli.e2e.test.d.ts.map +0 -1
  93. package/dist/e2e/cli.e2e.test.js +0 -417
  94. package/dist/e2e/cli.e2e.test.js.map +0 -1
  95. package/dist/e2e/lswt/lswt.e2e.test.d.ts +0 -2
  96. package/dist/e2e/lswt/lswt.e2e.test.d.ts.map +0 -1
  97. package/dist/e2e/lswt/lswt.e2e.test.js +0 -361
  98. package/dist/e2e/lswt/lswt.e2e.test.js.map +0 -1
  99. package/dist/e2e/newpr/newpr.e2e.test.d.ts +0 -2
  100. package/dist/e2e/newpr/newpr.e2e.test.d.ts.map +0 -1
  101. package/dist/e2e/newpr/newpr.e2e.test.js +0 -286
  102. package/dist/e2e/newpr/newpr.e2e.test.js.map +0 -1
  103. package/dist/e2e/newpr/scenarios.e2e.test.d.ts +0 -2
  104. package/dist/e2e/newpr/scenarios.e2e.test.d.ts.map +0 -1
  105. package/dist/e2e/newpr/scenarios.e2e.test.js +0 -426
  106. package/dist/e2e/newpr/scenarios.e2e.test.js.map +0 -1
  107. package/dist/e2e/newpr-full-flow.e2e.test.d.ts +0 -2
  108. package/dist/e2e/newpr-full-flow.e2e.test.d.ts.map +0 -1
  109. package/dist/e2e/newpr-full-flow.e2e.test.js +0 -280
  110. package/dist/e2e/newpr-full-flow.e2e.test.js.map +0 -1
  111. package/dist/e2e/prs/prs.e2e.test.d.ts +0 -7
  112. package/dist/e2e/prs/prs.e2e.test.d.ts.map +0 -1
  113. package/dist/e2e/prs/prs.e2e.test.js +0 -606
  114. package/dist/e2e/prs/prs.e2e.test.js.map +0 -1
  115. package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts +0 -2
  116. package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts.map +0 -1
  117. package/dist/e2e/workflows/pr-lifecycle.e2e.test.js +0 -298
  118. package/dist/e2e/workflows/pr-lifecycle.e2e.test.js.map +0 -1
  119. package/dist/e2e/wt/interactive-menu.e2e.test.d.ts +0 -8
  120. package/dist/e2e/wt/interactive-menu.e2e.test.d.ts.map +0 -1
  121. package/dist/e2e/wt/interactive-menu.e2e.test.js +0 -583
  122. package/dist/e2e/wt/interactive-menu.e2e.test.js.map +0 -1
  123. package/dist/e2e/wt/wt.e2e.test.d.ts +0 -9
  124. package/dist/e2e/wt/wt.e2e.test.d.ts.map +0 -1
  125. package/dist/e2e/wt/wt.e2e.test.js +0 -597
  126. package/dist/e2e/wt/wt.e2e.test.js.map +0 -1
  127. package/dist/e2e/wtlink/wtlink.e2e.test.d.ts +0 -2
  128. package/dist/e2e/wtlink/wtlink.e2e.test.d.ts.map +0 -1
  129. package/dist/e2e/wtlink/wtlink.e2e.test.js +0 -416
  130. package/dist/e2e/wtlink/wtlink.e2e.test.js.map +0 -1
  131. package/dist/integration/git.integration.test.d.ts +0 -2
  132. package/dist/integration/git.integration.test.d.ts.map +0 -1
  133. package/dist/integration/git.integration.test.js +0 -336
  134. package/dist/integration/git.integration.test.js.map +0 -1
  135. package/dist/integration/lswt-remote-pr.integration.test.d.ts +0 -2
  136. package/dist/integration/lswt-remote-pr.integration.test.d.ts.map +0 -1
  137. package/dist/integration/lswt-remote-pr.integration.test.js +0 -222
  138. package/dist/integration/lswt-remote-pr.integration.test.js.map +0 -1
  139. package/dist/integration/newpr-branchfrom-head.integration.test.d.ts +0 -2
  140. package/dist/integration/newpr-branchfrom-head.integration.test.d.ts.map +0 -1
  141. package/dist/integration/newpr-branchfrom-head.integration.test.js +0 -498
  142. package/dist/integration/newpr-branchfrom-head.integration.test.js.map +0 -1
  143. package/dist/integration/newpr.integration.test.d.ts +0 -2
  144. package/dist/integration/newpr.integration.test.d.ts.map +0 -1
  145. package/dist/integration/newpr.integration.test.js +0 -460
  146. package/dist/integration/newpr.integration.test.js.map +0 -1
  147. package/dist/integration/prs.integration.test.d.ts +0 -8
  148. package/dist/integration/prs.integration.test.d.ts.map +0 -1
  149. package/dist/integration/prs.integration.test.js +0 -478
  150. package/dist/integration/prs.integration.test.js.map +0 -1
  151. package/dist/lib/ai/base-provider.test.d.ts +0 -7
  152. package/dist/lib/ai/base-provider.test.d.ts.map +0 -1
  153. package/dist/lib/ai/base-provider.test.js +0 -319
  154. package/dist/lib/ai/base-provider.test.js.map +0 -1
  155. package/dist/lib/ai/cli-provider.test.d.ts +0 -5
  156. package/dist/lib/ai/cli-provider.test.d.ts.map +0 -1
  157. package/dist/lib/ai/cli-provider.test.js +0 -460
  158. package/dist/lib/ai/cli-provider.test.js.map +0 -1
  159. package/dist/lib/ai/fallback-provider.test.d.ts +0 -7
  160. package/dist/lib/ai/fallback-provider.test.d.ts.map +0 -1
  161. package/dist/lib/ai/fallback-provider.test.js +0 -165
  162. package/dist/lib/ai/fallback-provider.test.js.map +0 -1
  163. package/dist/lib/ai/generation-service.test.d.ts +0 -7
  164. package/dist/lib/ai/generation-service.test.d.ts.map +0 -1
  165. package/dist/lib/ai/generation-service.test.js +0 -213
  166. package/dist/lib/ai/generation-service.test.js.map +0 -1
  167. package/dist/lib/ai/provider-manager.test.d.ts +0 -5
  168. package/dist/lib/ai/provider-manager.test.d.ts.map +0 -1
  169. package/dist/lib/ai/provider-manager.test.js +0 -312
  170. package/dist/lib/ai/provider-manager.test.js.map +0 -1
  171. package/dist/lib/ai/repo-docs.test.d.ts +0 -5
  172. package/dist/lib/ai/repo-docs.test.d.ts.map +0 -1
  173. package/dist/lib/ai/repo-docs.test.js +0 -357
  174. package/dist/lib/ai/repo-docs.test.js.map +0 -1
  175. package/dist/lib/cleanpr/args.test.d.ts +0 -2
  176. package/dist/lib/cleanpr/args.test.d.ts.map +0 -1
  177. package/dist/lib/cleanpr/args.test.js +0 -269
  178. package/dist/lib/cleanpr/args.test.js.map +0 -1
  179. package/dist/lib/cleanpr/cleanup.test.d.ts +0 -2
  180. package/dist/lib/cleanpr/cleanup.test.d.ts.map +0 -1
  181. package/dist/lib/cleanpr/cleanup.test.js +0 -296
  182. package/dist/lib/cleanpr/cleanup.test.js.map +0 -1
  183. package/dist/lib/cleanpr/worktree-info.test.d.ts +0 -2
  184. package/dist/lib/cleanpr/worktree-info.test.d.ts.map +0 -1
  185. package/dist/lib/cleanpr/worktree-info.test.js +0 -228
  186. package/dist/lib/cleanpr/worktree-info.test.js.map +0 -1
  187. package/dist/lib/colors.test.d.ts +0 -2
  188. package/dist/lib/colors.test.d.ts.map +0 -1
  189. package/dist/lib/colors.test.js +0 -142
  190. package/dist/lib/colors.test.js.map +0 -1
  191. package/dist/lib/config-editor.test.d.ts +0 -11
  192. package/dist/lib/config-editor.test.d.ts.map +0 -1
  193. package/dist/lib/config-editor.test.js +0 -526
  194. package/dist/lib/config-editor.test.js.map +0 -1
  195. package/dist/lib/config-migration/detector.test.d.ts +0 -5
  196. package/dist/lib/config-migration/detector.test.d.ts.map +0 -1
  197. package/dist/lib/config-migration/detector.test.js +0 -201
  198. package/dist/lib/config-migration/detector.test.js.map +0 -1
  199. package/dist/lib/config-migration/reporter.test.d.ts +0 -5
  200. package/dist/lib/config-migration/reporter.test.d.ts.map +0 -1
  201. package/dist/lib/config-migration/reporter.test.js +0 -305
  202. package/dist/lib/config-migration/reporter.test.js.map +0 -1
  203. package/dist/lib/config-migration/runner.test.d.ts +0 -5
  204. package/dist/lib/config-migration/runner.test.d.ts.map +0 -1
  205. package/dist/lib/config-migration/runner.test.js +0 -235
  206. package/dist/lib/config-migration/runner.test.js.map +0 -1
  207. package/dist/lib/config-validation.test.d.ts +0 -5
  208. package/dist/lib/config-validation.test.d.ts.map +0 -1
  209. package/dist/lib/config-validation.test.js +0 -423
  210. package/dist/lib/config-validation.test.js.map +0 -1
  211. package/dist/lib/config.test.d.ts +0 -2
  212. package/dist/lib/config.test.d.ts.map +0 -1
  213. package/dist/lib/config.test.js +0 -554
  214. package/dist/lib/config.test.js.map +0 -1
  215. package/dist/lib/constants.test.d.ts +0 -5
  216. package/dist/lib/constants.test.d.ts.map +0 -1
  217. package/dist/lib/constants.test.js +0 -180
  218. package/dist/lib/constants.test.js.map +0 -1
  219. package/dist/lib/deprecation.test.d.ts +0 -2
  220. package/dist/lib/deprecation.test.d.ts.map +0 -1
  221. package/dist/lib/deprecation.test.js +0 -71
  222. package/dist/lib/deprecation.test.js.map +0 -1
  223. package/dist/lib/errors.test.d.ts +0 -2
  224. package/dist/lib/errors.test.d.ts.map +0 -1
  225. package/dist/lib/errors.test.js +0 -117
  226. package/dist/lib/errors.test.js.map +0 -1
  227. package/dist/lib/git.test.d.ts +0 -2
  228. package/dist/lib/git.test.d.ts.map +0 -1
  229. package/dist/lib/git.test.js +0 -608
  230. package/dist/lib/git.test.js.map +0 -1
  231. package/dist/lib/github.test.d.ts +0 -2
  232. package/dist/lib/github.test.d.ts.map +0 -1
  233. package/dist/lib/github.test.js +0 -441
  234. package/dist/lib/github.test.js.map +0 -1
  235. package/dist/lib/global-check.test.d.ts +0 -5
  236. package/dist/lib/global-check.test.d.ts.map +0 -1
  237. package/dist/lib/global-check.test.js +0 -150
  238. package/dist/lib/global-check.test.js.map +0 -1
  239. package/dist/lib/global-config.test.d.ts +0 -5
  240. package/dist/lib/global-config.test.d.ts.map +0 -1
  241. package/dist/lib/global-config.test.js +0 -282
  242. package/dist/lib/global-config.test.js.map +0 -1
  243. package/dist/lib/hooks/confirmation.test.d.ts +0 -7
  244. package/dist/lib/hooks/confirmation.test.d.ts.map +0 -1
  245. package/dist/lib/hooks/confirmation.test.js +0 -300
  246. package/dist/lib/hooks/confirmation.test.js.map +0 -1
  247. package/dist/lib/hooks/executor.test.d.ts +0 -5
  248. package/dist/lib/hooks/executor.test.d.ts.map +0 -1
  249. package/dist/lib/hooks/executor.test.js +0 -648
  250. package/dist/lib/hooks/executor.test.js.map +0 -1
  251. package/dist/lib/hooks/templates.test.d.ts +0 -5
  252. package/dist/lib/hooks/templates.test.d.ts.map +0 -1
  253. package/dist/lib/hooks/templates.test.js +0 -163
  254. package/dist/lib/hooks/templates.test.js.map +0 -1
  255. package/dist/lib/hooks/types.test.d.ts +0 -5
  256. package/dist/lib/hooks/types.test.d.ts.map +0 -1
  257. package/dist/lib/hooks/types.test.js +0 -132
  258. package/dist/lib/hooks/types.test.js.map +0 -1
  259. package/dist/lib/json-output.test.d.ts +0 -5
  260. package/dist/lib/json-output.test.d.ts.map +0 -1
  261. package/dist/lib/json-output.test.js +0 -261
  262. package/dist/lib/json-output.test.js.map +0 -1
  263. package/dist/lib/logger.test.d.ts +0 -14
  264. package/dist/lib/logger.test.d.ts.map +0 -1
  265. package/dist/lib/logger.test.js +0 -692
  266. package/dist/lib/logger.test.js.map +0 -1
  267. package/dist/lib/lswt/action-executors.test.d.ts +0 -2
  268. package/dist/lib/lswt/action-executors.test.d.ts.map +0 -1
  269. package/dist/lib/lswt/action-executors.test.js +0 -1127
  270. package/dist/lib/lswt/action-executors.test.js.map +0 -1
  271. package/dist/lib/lswt/actions.test.d.ts +0 -2
  272. package/dist/lib/lswt/actions.test.d.ts.map +0 -1
  273. package/dist/lib/lswt/actions.test.js +0 -497
  274. package/dist/lib/lswt/actions.test.js.map +0 -1
  275. package/dist/lib/lswt/args.test.d.ts +0 -2
  276. package/dist/lib/lswt/args.test.d.ts.map +0 -1
  277. package/dist/lib/lswt/args.test.js +0 -195
  278. package/dist/lib/lswt/args.test.js.map +0 -1
  279. package/dist/lib/lswt/environment.test.d.ts +0 -2
  280. package/dist/lib/lswt/environment.test.d.ts.map +0 -1
  281. package/dist/lib/lswt/environment.test.js +0 -544
  282. package/dist/lib/lswt/environment.test.js.map +0 -1
  283. package/dist/lib/lswt/formatters.test.d.ts +0 -2
  284. package/dist/lib/lswt/formatters.test.d.ts.map +0 -1
  285. package/dist/lib/lswt/formatters.test.js +0 -323
  286. package/dist/lib/lswt/formatters.test.js.map +0 -1
  287. package/dist/lib/lswt/fuzzy-search.test.d.ts +0 -5
  288. package/dist/lib/lswt/fuzzy-search.test.d.ts.map +0 -1
  289. package/dist/lib/lswt/fuzzy-search.test.js +0 -207
  290. package/dist/lib/lswt/fuzzy-search.test.js.map +0 -1
  291. package/dist/lib/lswt/interactive.test.d.ts +0 -2
  292. package/dist/lib/lswt/interactive.test.d.ts.map +0 -1
  293. package/dist/lib/lswt/interactive.test.js +0 -771
  294. package/dist/lib/lswt/interactive.test.js.map +0 -1
  295. package/dist/lib/lswt/table.test.d.ts +0 -5
  296. package/dist/lib/lswt/table.test.d.ts.map +0 -1
  297. package/dist/lib/lswt/table.test.js +0 -262
  298. package/dist/lib/lswt/table.test.js.map +0 -1
  299. package/dist/lib/lswt/worktree-info.test.d.ts +0 -2
  300. package/dist/lib/lswt/worktree-info.test.d.ts.map +0 -1
  301. package/dist/lib/lswt/worktree-info.test.js +0 -484
  302. package/dist/lib/lswt/worktree-info.test.js.map +0 -1
  303. package/dist/lib/newpr/action-deps.test.d.ts +0 -5
  304. package/dist/lib/newpr/action-deps.test.d.ts.map +0 -1
  305. package/dist/lib/newpr/action-deps.test.js +0 -111
  306. package/dist/lib/newpr/action-deps.test.js.map +0 -1
  307. package/dist/lib/newpr/actions.test.d.ts +0 -2
  308. package/dist/lib/newpr/actions.test.d.ts.map +0 -1
  309. package/dist/lib/newpr/actions.test.js +0 -254
  310. package/dist/lib/newpr/actions.test.js.map +0 -1
  311. package/dist/lib/newpr/args.test.d.ts +0 -2
  312. package/dist/lib/newpr/args.test.d.ts.map +0 -1
  313. package/dist/lib/newpr/args.test.js +0 -479
  314. package/dist/lib/newpr/args.test.js.map +0 -1
  315. package/dist/lib/newpr/hook-runner.test.d.ts +0 -7
  316. package/dist/lib/newpr/hook-runner.test.d.ts.map +0 -1
  317. package/dist/lib/newpr/hook-runner.test.js +0 -422
  318. package/dist/lib/newpr/hook-runner.test.js.map +0 -1
  319. package/dist/lib/newpr/plan-generator.test.d.ts +0 -7
  320. package/dist/lib/newpr/plan-generator.test.d.ts.map +0 -1
  321. package/dist/lib/newpr/plan-generator.test.js +0 -387
  322. package/dist/lib/newpr/plan-generator.test.js.map +0 -1
  323. package/dist/lib/newpr/scenario-handler.test.d.ts +0 -2
  324. package/dist/lib/newpr/scenario-handler.test.d.ts.map +0 -1
  325. package/dist/lib/newpr/scenario-handler.test.js +0 -256
  326. package/dist/lib/newpr/scenario-handler.test.js.map +0 -1
  327. package/dist/lib/prompts.test.d.ts +0 -2
  328. package/dist/lib/prompts.test.d.ts.map +0 -1
  329. package/dist/lib/prompts.test.js +0 -807
  330. package/dist/lib/prompts.test.js.map +0 -1
  331. package/dist/lib/prs/actions.test.d.ts +0 -5
  332. package/dist/lib/prs/actions.test.d.ts.map +0 -1
  333. package/dist/lib/prs/actions.test.js +0 -356
  334. package/dist/lib/prs/actions.test.js.map +0 -1
  335. package/dist/lib/prs/command.test.d.ts +0 -11
  336. package/dist/lib/prs/command.test.d.ts.map +0 -1
  337. package/dist/lib/prs/command.test.js +0 -409
  338. package/dist/lib/prs/command.test.js.map +0 -1
  339. package/dist/lib/prs/data.test.d.ts +0 -5
  340. package/dist/lib/prs/data.test.d.ts.map +0 -1
  341. package/dist/lib/prs/data.test.js +0 -417
  342. package/dist/lib/prs/data.test.js.map +0 -1
  343. package/dist/lib/prs/details.test.d.ts +0 -5
  344. package/dist/lib/prs/details.test.d.ts.map +0 -1
  345. package/dist/lib/prs/details.test.js +0 -325
  346. package/dist/lib/prs/details.test.js.map +0 -1
  347. package/dist/lib/prs/filters.test.d.ts +0 -5
  348. package/dist/lib/prs/filters.test.d.ts.map +0 -1
  349. package/dist/lib/prs/filters.test.js +0 -312
  350. package/dist/lib/prs/filters.test.js.map +0 -1
  351. package/dist/lib/prs/formatters.test.d.ts +0 -2
  352. package/dist/lib/prs/formatters.test.d.ts.map +0 -1
  353. package/dist/lib/prs/formatters.test.js +0 -387
  354. package/dist/lib/prs/formatters.test.js.map +0 -1
  355. package/dist/lib/prs/interactive.test.d.ts +0 -5
  356. package/dist/lib/prs/interactive.test.d.ts.map +0 -1
  357. package/dist/lib/prs/interactive.test.js +0 -517
  358. package/dist/lib/prs/interactive.test.js.map +0 -1
  359. package/dist/lib/schema.test.d.ts +0 -10
  360. package/dist/lib/schema.test.d.ts.map +0 -1
  361. package/dist/lib/schema.test.js +0 -309
  362. package/dist/lib/schema.test.js.map +0 -1
  363. package/dist/lib/state-detection.test.d.ts +0 -2
  364. package/dist/lib/state-detection.test.d.ts.map +0 -1
  365. package/dist/lib/state-detection.test.js +0 -451
  366. package/dist/lib/state-detection.test.js.map +0 -1
  367. package/dist/lib/ui/error.test.d.ts +0 -2
  368. package/dist/lib/ui/error.test.d.ts.map +0 -1
  369. package/dist/lib/ui/error.test.js +0 -143
  370. package/dist/lib/ui/error.test.js.map +0 -1
  371. package/dist/lib/ui/output.test.d.ts +0 -2
  372. package/dist/lib/ui/output.test.d.ts.map +0 -1
  373. package/dist/lib/ui/output.test.js +0 -59
  374. package/dist/lib/ui/output.test.js.map +0 -1
  375. package/dist/lib/ui/status.test.d.ts +0 -2
  376. package/dist/lib/ui/status.test.d.ts.map +0 -1
  377. package/dist/lib/ui/status.test.js +0 -158
  378. package/dist/lib/ui/status.test.js.map +0 -1
  379. package/dist/lib/ui/table.test.d.ts +0 -2
  380. package/dist/lib/ui/table.test.d.ts.map +0 -1
  381. package/dist/lib/ui/table.test.js +0 -115
  382. package/dist/lib/ui/table.test.js.map +0 -1
  383. package/dist/lib/ui/theme.test.d.ts +0 -2
  384. package/dist/lib/ui/theme.test.d.ts.map +0 -1
  385. package/dist/lib/ui/theme.test.js +0 -76
  386. package/dist/lib/ui/theme.test.js.map +0 -1
  387. package/dist/lib/wtconfig/config-manager.test.d.ts +0 -5
  388. package/dist/lib/wtconfig/config-manager.test.d.ts.map +0 -1
  389. package/dist/lib/wtconfig/config-manager.test.js +0 -501
  390. package/dist/lib/wtconfig/config-manager.test.js.map +0 -1
  391. package/dist/lib/wtconfig/environment.test.d.ts +0 -5
  392. package/dist/lib/wtconfig/environment.test.d.ts.map +0 -1
  393. package/dist/lib/wtconfig/environment.test.js +0 -285
  394. package/dist/lib/wtconfig/environment.test.js.map +0 -1
  395. package/dist/lib/wtlink/config-manifest.test.d.ts +0 -2
  396. package/dist/lib/wtlink/config-manifest.test.d.ts.map +0 -1
  397. package/dist/lib/wtlink/config-manifest.test.js +0 -486
  398. package/dist/lib/wtlink/config-manifest.test.js.map +0 -1
  399. package/dist/lib/wtlink/link-configs.test.d.ts +0 -2
  400. package/dist/lib/wtlink/link-configs.test.d.ts.map +0 -1
  401. package/dist/lib/wtlink/link-configs.test.js +0 -612
  402. package/dist/lib/wtlink/link-configs.test.js.map +0 -1
  403. package/dist/lib/wtlink/main-menu.test.d.ts +0 -5
  404. package/dist/lib/wtlink/main-menu.test.d.ts.map +0 -1
  405. package/dist/lib/wtlink/main-menu.test.js +0 -126
  406. package/dist/lib/wtlink/main-menu.test.js.map +0 -1
  407. package/dist/lib/wtlink/manage-manifest.test.d.ts +0 -2
  408. package/dist/lib/wtlink/manage-manifest.test.d.ts.map +0 -1
  409. package/dist/lib/wtlink/manage-manifest.test.js +0 -714
  410. package/dist/lib/wtlink/manage-manifest.test.js.map +0 -1
  411. package/dist/lib/wtlink/validate-manifest.test.d.ts +0 -2
  412. package/dist/lib/wtlink/validate-manifest.test.d.ts.map +0 -1
  413. package/dist/lib/wtlink/validate-manifest.test.js +0 -220
  414. package/dist/lib/wtlink/validate-manifest.test.js.map +0 -1
  415. package/dist/lib/wtstate/analyze.test.d.ts +0 -5
  416. package/dist/lib/wtstate/analyze.test.d.ts.map +0 -1
  417. package/dist/lib/wtstate/analyze.test.js +0 -282
  418. package/dist/lib/wtstate/analyze.test.js.map +0 -1
  419. package/dist/lib/wtstate/args.test.d.ts +0 -5
  420. package/dist/lib/wtstate/args.test.d.ts.map +0 -1
  421. package/dist/lib/wtstate/args.test.js +0 -120
  422. package/dist/lib/wtstate/args.test.js.map +0 -1
  423. package/dist/mcp/server.test.d.ts +0 -9
  424. package/dist/mcp/server.test.d.ts.map +0 -1
  425. package/dist/mcp/server.test.js +0 -550
  426. package/dist/mcp/server.test.js.map +0 -1
@@ -1,1127 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { executeAction, createDefaultExecutorDeps, formatBranchAsTitle, } from './action-executors.js';
3
- // Mock inquirer
4
- vi.mock('inquirer', () => ({
5
- default: {
6
- prompt: vi.fn(),
7
- },
8
- }));
9
- // Mock github
10
- vi.mock('../github.js', () => ({
11
- getPr: vi.fn(),
12
- getPrByBranch: vi.fn(),
13
- createPr: vi.fn(),
14
- }));
15
- // Mock git
16
- vi.mock('../git.js', () => ({
17
- getRepoRoot: vi.fn(),
18
- removeWorktree: vi.fn(),
19
- getMainWorktreeRoot: vi.fn(),
20
- addWorktree: vi.fn(),
21
- deleteBranch: vi.fn(),
22
- exec: vi.fn(),
23
- }));
24
- // Mock child_process for spawn/spawnSync
25
- vi.mock('child_process', async () => {
26
- const actual = await vi.importActual('child_process');
27
- return {
28
- ...actual,
29
- spawnSync: vi.fn(),
30
- spawn: actual.spawn,
31
- };
32
- });
33
- import inquirer from 'inquirer';
34
- import * as github from '../github.js';
35
- import * as git from '../git.js';
36
- import { spawnSync } from 'child_process';
37
- describe('lswt/action-executors', () => {
38
- beforeEach(() => {
39
- vi.clearAllMocks();
40
- // Reset spawnSync mock for WSL path conversion
41
- vi.mocked(spawnSync).mockReturnValue({
42
- status: 0,
43
- stdout: '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo',
44
- stderr: '',
45
- pid: 0,
46
- signal: null,
47
- output: ['', '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo', ''],
48
- });
49
- });
50
- afterEach(() => {
51
- vi.restoreAllMocks();
52
- });
53
- const makeWorktree = (overrides = {}) => ({
54
- path: '/home/user/repo',
55
- name: 'repo',
56
- branch: 'main',
57
- commit: 'abc123',
58
- type: 'main',
59
- prNumber: null,
60
- prState: null,
61
- isDraft: null,
62
- hasChanges: false,
63
- ...overrides,
64
- });
65
- const makeEnv = (overrides = {}) => ({
66
- hasVscode: true,
67
- hasCursor: false,
68
- defaultEditor: 'vscode',
69
- platform: 'linux',
70
- isInteractive: true,
71
- shell: '/bin/bash',
72
- gitVersion: { major: 2, minor: 39, patch: 0, raw: 'git version 2.39.0' },
73
- isWSL: false,
74
- ...overrides,
75
- });
76
- const makeConfig = (overrides = {}) => ({
77
- baseBranch: 'main',
78
- worktreePattern: '{repo}.pr{number}',
79
- worktreeParent: '..',
80
- draftPr: false,
81
- sharedRepos: [],
82
- branchPrefix: 'feature',
83
- previewLabel: 'preview',
84
- syncPatterns: [],
85
- preferredEditor: 'auto',
86
- ai: { provider: 'none' },
87
- hooks: {},
88
- hookDefaults: { timeout: 30000, maxTimeout: 60000 },
89
- plugins: [],
90
- generators: {},
91
- integrations: {},
92
- logging: { level: 'info', timestamps: true },
93
- global: { warnNotGlobal: true },
94
- ...overrides,
95
- });
96
- const makeDeps = (overrides = {}) => ({
97
- execCommand: vi.fn(),
98
- spawnDetached: vi.fn(),
99
- copyToClipboard: vi.fn(),
100
- openUrl: vi.fn(),
101
- wslPathToWindows: vi.fn().mockReturnValue('\\\\wsl.localhost\\Ubuntu\\home\\user\\repo'),
102
- ...overrides,
103
- });
104
- describe('createDefaultExecutorDeps', () => {
105
- it('returns an object with all required methods', () => {
106
- const deps = createDefaultExecutorDeps();
107
- expect(deps).toHaveProperty('execCommand');
108
- expect(deps).toHaveProperty('spawnDetached');
109
- expect(deps).toHaveProperty('copyToClipboard');
110
- expect(deps).toHaveProperty('openUrl');
111
- expect(deps).toHaveProperty('wslPathToWindows');
112
- expect(typeof deps.execCommand).toBe('function');
113
- expect(typeof deps.spawnDetached).toBe('function');
114
- expect(typeof deps.copyToClipboard).toBe('function');
115
- expect(typeof deps.openUrl).toBe('function');
116
- expect(typeof deps.wslPathToWindows).toBe('function');
117
- });
118
- });
119
- describe('executeAction', () => {
120
- describe('back action', () => {
121
- it('returns success with no message', async () => {
122
- const result = await executeAction('back', makeWorktree(), makeEnv(), makeConfig());
123
- expect(result).toEqual({ success: true });
124
- });
125
- });
126
- describe('exit action', () => {
127
- it('returns success with shouldExit true', async () => {
128
- const result = await executeAction('exit', makeWorktree(), makeEnv(), makeConfig());
129
- expect(result).toEqual({ success: true, shouldExit: true });
130
- });
131
- });
132
- describe('open_editor action', () => {
133
- it('spawns VSCode when preferredEditor is vscode', async () => {
134
- const deps = makeDeps();
135
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
136
- const env = makeEnv({ hasVscode: true, defaultEditor: 'vscode' });
137
- const config = makeConfig({ preferredEditor: 'vscode' });
138
- const result = await executeAction('open_editor', worktree, env, config, deps);
139
- expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
140
- expect(result.success).toBe(true);
141
- expect(result.message).toContain('VSCode');
142
- });
143
- it('spawns Cursor when preferredEditor is cursor', async () => {
144
- const deps = makeDeps();
145
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
146
- const env = makeEnv({ hasCursor: true, defaultEditor: 'cursor' });
147
- const config = makeConfig({ preferredEditor: 'cursor' });
148
- const result = await executeAction('open_editor', worktree, env, config, deps);
149
- expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
150
- expect(result.success).toBe(true);
151
- expect(result.message).toContain('Cursor');
152
- });
153
- it('uses VSCode when preferredEditor is auto and VSCode available', async () => {
154
- const deps = makeDeps();
155
- const worktree = makeWorktree();
156
- const env = makeEnv({ hasVscode: true, hasCursor: false, defaultEditor: 'vscode' });
157
- const config = makeConfig({ preferredEditor: 'auto' });
158
- const result = await executeAction('open_editor', worktree, env, config, deps);
159
- expect(deps.spawnDetached).toHaveBeenCalledWith('code', expect.any(Array));
160
- expect(result.success).toBe(true);
161
- });
162
- it('uses Cursor when preferredEditor is auto and only Cursor available', async () => {
163
- const deps = makeDeps();
164
- const worktree = makeWorktree();
165
- const env = makeEnv({ hasVscode: false, hasCursor: true, defaultEditor: 'cursor' });
166
- const config = makeConfig({ preferredEditor: 'auto' });
167
- const result = await executeAction('open_editor', worktree, env, config, deps);
168
- expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', expect.any(Array));
169
- expect(result.success).toBe(true);
170
- });
171
- it('returns error when no editor is available', async () => {
172
- const deps = makeDeps();
173
- const worktree = makeWorktree();
174
- const env = makeEnv({ hasVscode: false, hasCursor: false, defaultEditor: null });
175
- const config = makeConfig({ preferredEditor: 'auto' });
176
- const result = await executeAction('open_editor', worktree, env, config, deps);
177
- expect(result.success).toBe(false);
178
- expect(result.message).toContain('No editor found');
179
- expect(deps.spawnDetached).not.toHaveBeenCalled();
180
- });
181
- it('handles spawn errors gracefully', async () => {
182
- const deps = makeDeps({
183
- spawnDetached: vi.fn().mockImplementation(() => {
184
- throw new Error('spawn failed');
185
- }),
186
- });
187
- const worktree = makeWorktree();
188
- const env = makeEnv({ hasVscode: true, defaultEditor: 'vscode' });
189
- const config = makeConfig();
190
- const result = await executeAction('open_editor', worktree, env, config, deps);
191
- expect(result.success).toBe(false);
192
- expect(result.message).toContain('Failed to open editor');
193
- });
194
- });
195
- describe('copy_path action', () => {
196
- it('copies path to clipboard', async () => {
197
- const deps = makeDeps();
198
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
199
- const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
200
- expect(deps.copyToClipboard).toHaveBeenCalledWith('/home/user/repo.pr1');
201
- expect(result.success).toBe(true);
202
- expect(result.message).toContain('Copied');
203
- expect(result.message).toContain('/home/user/repo.pr1');
204
- });
205
- it('handles clipboard errors gracefully', async () => {
206
- const deps = makeDeps({
207
- copyToClipboard: vi.fn().mockImplementation(() => {
208
- throw new Error('clipboard unavailable');
209
- }),
210
- });
211
- const worktree = makeWorktree();
212
- const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
213
- expect(result.success).toBe(false);
214
- expect(result.message).toContain('Failed to copy');
215
- });
216
- });
217
- describe('show_details action', () => {
218
- it('returns success (details are printed to console)', async () => {
219
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
220
- const worktree = makeWorktree({
221
- type: 'pr',
222
- prNumber: 42,
223
- prState: 'OPEN',
224
- branch: 'feature-42',
225
- });
226
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
227
- expect(result.success).toBe(true);
228
- consoleSpy.mockRestore();
229
- });
230
- });
231
- describe('open_pr_url action', () => {
232
- it('returns error when worktree has no PR number', async () => {
233
- const deps = makeDeps();
234
- const worktree = makeWorktree({ type: 'branch', prNumber: null });
235
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
236
- expect(result.success).toBe(false);
237
- expect(result.message).toContain('No PR associated');
238
- expect(deps.openUrl).not.toHaveBeenCalled();
239
- });
240
- });
241
- });
242
- describe('createDefaultExecutorDeps', () => {
243
- it('returns object with all required methods', () => {
244
- const deps = createDefaultExecutorDeps();
245
- expect(deps).toHaveProperty('execCommand');
246
- expect(deps).toHaveProperty('spawnDetached');
247
- expect(deps).toHaveProperty('copyToClipboard');
248
- expect(deps).toHaveProperty('openUrl');
249
- expect(deps).toHaveProperty('wslPathToWindows');
250
- expect(typeof deps.execCommand).toBe('function');
251
- expect(typeof deps.spawnDetached).toBe('function');
252
- expect(typeof deps.copyToClipboard).toBe('function');
253
- expect(typeof deps.openUrl).toBe('function');
254
- expect(typeof deps.wslPathToWindows).toBe('function');
255
- });
256
- });
257
- describe('open_terminal action', () => {
258
- it('spawns terminal on Linux', async () => {
259
- const deps = makeDeps();
260
- const worktree = makeWorktree({ path: '/home/user/repo' });
261
- const env = makeEnv({ platform: 'linux' });
262
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
263
- // Should attempt to spawn a terminal
264
- expect(deps.spawnDetached).toHaveBeenCalled();
265
- expect(result.success).toBe(true);
266
- expect(result.message).toContain('terminal');
267
- });
268
- it('uses Windows Terminal via cmd.exe in WSL', async () => {
269
- const deps = makeDeps();
270
- const worktree = makeWorktree({ path: '/home/user/repo' });
271
- const env = makeEnv({ platform: 'linux', isWSL: true });
272
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
273
- // Should call deps.wslPathToWindows to convert path
274
- expect(deps.wslPathToWindows).toHaveBeenCalledWith('/home/user/repo');
275
- // Should try to use cmd.exe to launch Windows Terminal
276
- expect(deps.execCommand).toHaveBeenCalledWith(expect.stringContaining('cmd.exe'));
277
- expect(result.success).toBe(true);
278
- });
279
- it('shows cd command fallback when WSL Windows Terminal fails', async () => {
280
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
281
- const deps = makeDeps({
282
- execCommand: vi.fn().mockImplementation(() => {
283
- throw new Error('cmd.exe failed');
284
- }),
285
- });
286
- const worktree = makeWorktree({ path: '/home/user/repo' });
287
- const env = makeEnv({ platform: 'linux', isWSL: true });
288
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
289
- // Should fall back to showing cd command
290
- expect(result.success).toBe(true);
291
- expect(result.message).toContain('copy the cd command');
292
- // Should print cd command to console
293
- const output = consoleSpy.mock.calls.map((c) => String(c[0])).join('\n');
294
- expect(output).toContain('cd');
295
- consoleSpy.mockRestore();
296
- });
297
- it('uses osascript on macOS', async () => {
298
- const deps = makeDeps();
299
- const worktree = makeWorktree({ path: '/Users/user/repo' });
300
- const env = makeEnv({ platform: 'darwin' });
301
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
302
- expect(deps.execCommand).toHaveBeenCalledWith(expect.stringContaining('osascript'));
303
- expect(result.success).toBe(true);
304
- });
305
- it('tries Windows Terminal on Windows', async () => {
306
- const deps = makeDeps();
307
- const worktree = makeWorktree({ path: 'C:\\Users\\user\\repo' });
308
- const env = makeEnv({ platform: 'win32' });
309
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
310
- // Should try wt first
311
- expect(deps.spawnDetached).toHaveBeenCalledWith('wt', expect.any(Array));
312
- expect(result.success).toBe(true);
313
- });
314
- it('falls back to cmd on Windows when wt fails', async () => {
315
- let callCount = 0;
316
- const deps = makeDeps({
317
- spawnDetached: vi.fn().mockImplementation((cmd) => {
318
- callCount++;
319
- if (cmd === 'wt') {
320
- throw new Error('wt not found');
321
- }
322
- }),
323
- });
324
- const worktree = makeWorktree({ path: 'C:\\Users\\user\\repo' });
325
- const env = makeEnv({ platform: 'win32' });
326
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
327
- // Should have tried wt, then cmd
328
- expect(callCount).toBe(2);
329
- expect(result.success).toBe(true);
330
- });
331
- it('handles no terminal available on Linux', async () => {
332
- const deps = makeDeps({
333
- spawnDetached: vi.fn().mockImplementation(() => {
334
- throw new Error('terminal not found');
335
- }),
336
- });
337
- const worktree = makeWorktree();
338
- const env = makeEnv({ platform: 'linux' });
339
- const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
340
- expect(result.success).toBe(false);
341
- expect(result.message).toContain('No terminal emulator found');
342
- });
343
- });
344
- describe('remove_worktree action', () => {
345
- it('returns error for main worktree', async () => {
346
- const worktree = makeWorktree({ type: 'main' });
347
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
348
- expect(result.success).toBe(false);
349
- expect(result.message).toContain('Cannot remove main worktree');
350
- });
351
- it('cancels when user declines confirmation', async () => {
352
- vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: false });
353
- const worktree = makeWorktree({
354
- type: 'branch',
355
- branch: 'feature-branch',
356
- name: 'feature-branch',
357
- });
358
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
359
- expect(result.success).toBe(true);
360
- expect(result.message).toBe('Cancelled');
361
- expect(git.removeWorktree).not.toHaveBeenCalled();
362
- });
363
- it('removes worktree when user confirms', async () => {
364
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
365
- vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
366
- vi.mocked(git.removeWorktree).mockImplementation(() => { });
367
- const worktree = makeWorktree({
368
- type: 'branch',
369
- branch: 'feature-branch',
370
- name: 'my-worktree',
371
- });
372
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
373
- expect(result.success).toBe(true);
374
- expect(result.message).toContain('Removed worktree');
375
- expect(result.shouldRefresh).toBe(true);
376
- expect(git.removeWorktree).toHaveBeenCalledWith(worktree.path);
377
- consoleSpy.mockRestore();
378
- });
379
- it('warns about uncommitted changes', async () => {
380
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
381
- vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: false });
382
- const worktree = makeWorktree({
383
- type: 'branch',
384
- branch: 'dirty-branch',
385
- hasChanges: true,
386
- });
387
- await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
388
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
389
- expect(output).toContain('uncommitted changes');
390
- consoleSpy.mockRestore();
391
- });
392
- it('prompts to delete branch for merged PR worktrees', async () => {
393
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
394
- vi.mocked(inquirer.prompt)
395
- .mockResolvedValueOnce({ confirm: true })
396
- .mockResolvedValueOnce({ shouldDelete: true });
397
- vi.mocked(git.removeWorktree).mockImplementation(() => { });
398
- vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
399
- const worktree = makeWorktree({
400
- type: 'pr',
401
- prNumber: 42,
402
- prState: 'MERGED',
403
- branch: 'feature-42',
404
- name: 'repo.pr42',
405
- });
406
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
407
- expect(result.success).toBe(true);
408
- expect(inquirer.prompt).toHaveBeenCalledTimes(2);
409
- consoleSpy.mockRestore();
410
- });
411
- it('prompts to delete branch for closed PR worktrees', async () => {
412
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
413
- vi.mocked(inquirer.prompt)
414
- .mockResolvedValueOnce({ confirm: true })
415
- .mockResolvedValueOnce({ shouldDelete: false });
416
- vi.mocked(git.removeWorktree).mockImplementation(() => { });
417
- const worktree = makeWorktree({
418
- type: 'pr',
419
- prNumber: 42,
420
- prState: 'CLOSED',
421
- branch: 'feature-42',
422
- name: 'repo.pr42',
423
- });
424
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
425
- expect(result.success).toBe(true);
426
- expect(inquirer.prompt).toHaveBeenCalledTimes(2);
427
- consoleSpy.mockRestore();
428
- });
429
- it('continues successfully when branch deletion fails', async () => {
430
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
431
- vi.mocked(inquirer.prompt)
432
- .mockResolvedValueOnce({ confirm: true })
433
- .mockResolvedValueOnce({ shouldDelete: true });
434
- vi.mocked(git.removeWorktree).mockImplementation(() => { });
435
- vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
436
- // Branch deletion fails (branch might not exist locally)
437
- vi.mocked(git.deleteBranch).mockImplementation(() => {
438
- throw new Error('Branch not found');
439
- });
440
- const worktree = makeWorktree({
441
- type: 'pr',
442
- prNumber: 42,
443
- prState: 'MERGED',
444
- branch: 'feature-42',
445
- name: 'repo.pr42',
446
- });
447
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
448
- // Should still succeed even though branch deletion failed
449
- expect(result.success).toBe(true);
450
- expect(result.message).toContain('Removed worktree');
451
- consoleSpy.mockRestore();
452
- });
453
- it('handles removal failure', async () => {
454
- vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
455
- vi.mocked(git.removeWorktree).mockImplementation(() => {
456
- throw new Error('Worktree has changes');
457
- });
458
- const worktree = makeWorktree({
459
- type: 'branch',
460
- branch: 'feature-branch',
461
- name: 'my-worktree',
462
- });
463
- const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
464
- expect(result.success).toBe(false);
465
- expect(result.message).toContain('Failed to remove worktree');
466
- });
467
- });
468
- describe('create_pr action', () => {
469
- it('returns error for detached HEAD', async () => {
470
- const worktree = makeWorktree({ type: 'detached', branch: null });
471
- const result = await executeAction('create_pr', worktree, makeEnv(), makeConfig(), makeDeps());
472
- expect(result.success).toBe(false);
473
- expect(result.message).toContain('detached HEAD');
474
- });
475
- it('returns error when PR already exists for branch', async () => {
476
- vi.mocked(github.getPrByBranch).mockReturnValue({
477
- number: 42,
478
- url: 'https://github.com/owner/repo/pull/42',
479
- state: 'OPEN',
480
- isDraft: false,
481
- title: 'Existing PR',
482
- headBranch: 'feature-branch',
483
- baseBranch: 'main',
484
- });
485
- const worktree = makeWorktree({
486
- type: 'branch',
487
- branch: 'feature-branch',
488
- });
489
- const result = await executeAction('create_pr', worktree, makeEnv(), makeConfig(), makeDeps());
490
- expect(result.success).toBe(false);
491
- expect(result.message).toContain('PR already exists');
492
- expect(result.message).toContain('#42');
493
- });
494
- it('creates PR successfully with configured draftPr', async () => {
495
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
496
- vi.mocked(github.getPrByBranch).mockReturnValue(null);
497
- vi.mocked(github.createPr).mockReturnValue({
498
- number: 123,
499
- url: 'https://github.com/owner/repo/pull/123',
500
- state: 'OPEN',
501
- isDraft: true,
502
- title: 'New PR',
503
- headBranch: 'feature-branch',
504
- baseBranch: 'main',
505
- });
506
- vi.mocked(inquirer.prompt).mockResolvedValue({ title: 'My New PR' });
507
- const worktree = makeWorktree({
508
- type: 'branch',
509
- branch: 'feature-branch',
510
- });
511
- const config = makeConfig({ draftPr: true });
512
- const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
513
- expect(result.success).toBe(true);
514
- expect(result.message).toContain('Created PR #123');
515
- expect(result.shouldRefresh).toBe(true);
516
- expect(github.createPr).toHaveBeenCalledWith(expect.objectContaining({ draft: true }), expect.any(String));
517
- consoleSpy.mockRestore();
518
- });
519
- it('prompts for draft status when not configured', async () => {
520
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
521
- vi.mocked(github.getPrByBranch).mockReturnValue(null);
522
- vi.mocked(github.createPr).mockReturnValue({
523
- number: 123,
524
- url: 'https://github.com/owner/repo/pull/123',
525
- state: 'OPEN',
526
- isDraft: false,
527
- title: 'New PR',
528
- headBranch: 'feature-branch',
529
- baseBranch: 'main',
530
- });
531
- vi.mocked(inquirer.prompt)
532
- .mockResolvedValueOnce({ title: 'My PR Title' })
533
- .mockResolvedValueOnce({ draft: false });
534
- const worktree = makeWorktree({
535
- type: 'branch',
536
- branch: 'feature-branch',
537
- });
538
- const config = makeConfig({ draftPr: undefined });
539
- const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
540
- expect(result.success).toBe(true);
541
- expect(inquirer.prompt).toHaveBeenCalledTimes(2);
542
- consoleSpy.mockRestore();
543
- });
544
- it('handles PR creation failure', async () => {
545
- vi.mocked(github.getPrByBranch).mockReturnValue(null);
546
- vi.mocked(github.createPr).mockImplementation(() => {
547
- throw new Error('GitHub API error');
548
- });
549
- vi.mocked(inquirer.prompt).mockResolvedValue({ title: 'My PR' });
550
- const worktree = makeWorktree({
551
- type: 'branch',
552
- branch: 'feature-branch',
553
- });
554
- const config = makeConfig({ draftPr: false });
555
- const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
556
- expect(result.success).toBe(false);
557
- expect(result.message).toContain('Failed to create PR');
558
- });
559
- });
560
- describe('link_configs action', () => {
561
- it('returns error when repo root not found', async () => {
562
- vi.mocked(git.getRepoRoot).mockReturnValue(null);
563
- const worktree = makeWorktree({
564
- type: 'branch',
565
- branch: 'feature-branch',
566
- });
567
- const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
568
- expect(result.success).toBe(false);
569
- expect(result.message).toContain('Could not find repository root');
570
- });
571
- it('returns error for main worktree', async () => {
572
- vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
573
- const worktree = makeWorktree({ type: 'main' });
574
- const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
575
- expect(result.success).toBe(false);
576
- expect(result.message).toContain('Cannot link configs to main worktree');
577
- });
578
- it('handles link configs failure', async () => {
579
- vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
580
- // Mock the dynamic import to throw
581
- vi.doMock('../wtlink/link-configs.js', () => ({
582
- run: vi.fn().mockRejectedValue(new Error('Link failed')),
583
- }));
584
- const worktree = makeWorktree({
585
- type: 'branch',
586
- branch: 'feature-branch',
587
- path: '/home/user/feature-branch',
588
- });
589
- const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
590
- // Either succeeds or fails with proper message
591
- if (!result.success) {
592
- expect(result.message).toContain('Failed to link configs');
593
- }
594
- });
595
- it('successfully links configs', async () => {
596
- vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
597
- // Mock the dynamic import to succeed
598
- vi.doMock('../wtlink/link-configs.js', () => ({
599
- run: vi.fn().mockResolvedValue(undefined),
600
- }));
601
- const worktree = makeWorktree({
602
- type: 'branch',
603
- branch: 'feature-branch',
604
- path: '/home/user/feature-branch',
605
- });
606
- const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
607
- // Should succeed
608
- expect(result.success).toBe(true);
609
- expect(result.message).toContain('linked successfully');
610
- });
611
- });
612
- describe('show_details action', () => {
613
- it('shows draft indicator for draft PRs', async () => {
614
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
615
- const worktree = makeWorktree({
616
- type: 'pr',
617
- prNumber: 42,
618
- prState: 'OPEN',
619
- isDraft: true,
620
- branch: 'feature-42',
621
- });
622
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
623
- expect(result.success).toBe(true);
624
- // Check that draft was mentioned in output
625
- const calls = consoleSpy.mock.calls.map((call) => String(call[0]));
626
- expect(calls.some((c) => c.includes('Draft'))).toBe(true);
627
- consoleSpy.mockRestore();
628
- });
629
- it('shows branch worktree details', async () => {
630
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
631
- const worktree = makeWorktree({
632
- type: 'branch',
633
- branch: 'feature-branch',
634
- hasChanges: true,
635
- });
636
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
637
- expect(result.success).toBe(true);
638
- consoleSpy.mockRestore();
639
- });
640
- it('shows detached worktree details', async () => {
641
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
642
- const worktree = makeWorktree({
643
- type: 'detached',
644
- branch: null,
645
- });
646
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
647
- expect(result.success).toBe(true);
648
- consoleSpy.mockRestore();
649
- });
650
- it('shows all worktree fields in output', async () => {
651
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
652
- const worktree = makeWorktree({
653
- path: '/home/user/repo.pr1',
654
- name: 'repo.pr1',
655
- type: 'pr',
656
- prNumber: 123,
657
- prState: 'OPEN',
658
- branch: 'feat/test',
659
- commit: 'abc123def',
660
- hasChanges: false,
661
- });
662
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
663
- expect(result.success).toBe(true);
664
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
665
- expect(output).toContain('Path');
666
- expect(output).toContain('Name');
667
- expect(output).toContain('Branch');
668
- expect(output).toContain('Commit');
669
- expect(output).toContain('Type');
670
- consoleSpy.mockRestore();
671
- });
672
- it('shows clean status for worktree without changes', async () => {
673
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
674
- const worktree = makeWorktree({
675
- type: 'branch',
676
- branch: 'clean-branch',
677
- hasChanges: false,
678
- });
679
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
680
- expect(result.success).toBe(true);
681
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
682
- expect(output).toContain('Clean');
683
- consoleSpy.mockRestore();
684
- });
685
- it('shows uncommitted changes warning', async () => {
686
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
687
- const worktree = makeWorktree({
688
- type: 'branch',
689
- branch: 'dirty-branch',
690
- hasChanges: true,
691
- });
692
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
693
- expect(result.success).toBe(true);
694
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
695
- expect(output).toContain('uncommitted');
696
- consoleSpy.mockRestore();
697
- });
698
- it('shows PR URL from github.getPr when prUrl is not stored', async () => {
699
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
700
- vi.mocked(github.getPr).mockReturnValue({
701
- number: 42,
702
- url: 'https://github.com/owner/repo/pull/42',
703
- state: 'OPEN',
704
- isDraft: false,
705
- title: 'Test PR',
706
- headBranch: 'feature-42',
707
- baseBranch: 'main',
708
- });
709
- const worktree = makeWorktree({
710
- type: 'pr',
711
- prNumber: 42,
712
- prState: 'OPEN',
713
- branch: 'feature-42',
714
- // prUrl is not set
715
- });
716
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
717
- expect(result.success).toBe(true);
718
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
719
- expect(output).toContain('https://github.com/owner/repo/pull/42');
720
- consoleSpy.mockRestore();
721
- });
722
- it('handles PR URL fetch failure gracefully', async () => {
723
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
724
- vi.mocked(github.getPr).mockReturnValue(null);
725
- const worktree = makeWorktree({
726
- type: 'pr',
727
- prNumber: 42,
728
- prState: 'OPEN',
729
- branch: 'feature-42',
730
- // prUrl is not set
731
- });
732
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
733
- // Should succeed even if PR URL fetch fails
734
- expect(result.success).toBe(true);
735
- consoleSpy.mockRestore();
736
- });
737
- it('shows recent commits when git log succeeds', async () => {
738
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
739
- vi.mocked(git.exec).mockReturnValue('abc1234 First commit\ndef5678 Second commit\nghi9012 Third commit');
740
- const worktree = makeWorktree({
741
- type: 'branch',
742
- branch: 'feature-branch',
743
- });
744
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
745
- expect(result.success).toBe(true);
746
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
747
- expect(output).toContain('Recent commits');
748
- expect(output).toContain('First commit');
749
- consoleSpy.mockRestore();
750
- });
751
- it('handles git log failure gracefully', async () => {
752
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
753
- vi.mocked(git.exec).mockImplementation(() => {
754
- throw new Error('git log failed');
755
- });
756
- const worktree = makeWorktree({
757
- type: 'branch',
758
- branch: 'feature-branch',
759
- });
760
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
761
- // Should succeed even if git log fails
762
- expect(result.success).toBe(true);
763
- consoleSpy.mockRestore();
764
- });
765
- });
766
- describe('open_pr_url action with mocked github', () => {
767
- it('returns error when PR is not found', async () => {
768
- vi.mocked(github.getPr).mockReturnValue(null);
769
- const deps = makeDeps();
770
- const worktree = makeWorktree({
771
- type: 'pr',
772
- prNumber: 999,
773
- prState: 'OPEN',
774
- });
775
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
776
- expect(result.success).toBe(false);
777
- expect(result.message).toContain('Could not find PR');
778
- });
779
- it('opens PR URL successfully when PR is found', async () => {
780
- vi.mocked(github.getPr).mockReturnValue({
781
- number: 42,
782
- url: 'https://github.com/owner/repo/pull/42',
783
- state: 'OPEN',
784
- isDraft: false,
785
- title: 'Test PR',
786
- headBranch: 'feature-42',
787
- baseBranch: 'main',
788
- });
789
- const deps = makeDeps();
790
- const worktree = makeWorktree({
791
- type: 'pr',
792
- prNumber: 42,
793
- prState: 'OPEN',
794
- });
795
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
796
- expect(result.success).toBe(true);
797
- expect(result.message).toContain('Opened PR');
798
- expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
799
- });
800
- it('handles error when opening URL fails', async () => {
801
- vi.mocked(github.getPr).mockReturnValue({
802
- number: 42,
803
- url: 'https://github.com/owner/repo/pull/42',
804
- state: 'OPEN',
805
- isDraft: false,
806
- title: 'Test PR',
807
- headBranch: 'feature-42',
808
- baseBranch: 'main',
809
- });
810
- const deps = makeDeps({
811
- openUrl: vi.fn().mockImplementation(() => {
812
- throw new Error('Failed to open browser');
813
- }),
814
- });
815
- const worktree = makeWorktree({
816
- type: 'pr',
817
- prNumber: 42,
818
- prState: 'OPEN',
819
- });
820
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
821
- expect(result.success).toBe(false);
822
- expect(result.message).toContain('Failed to open PR');
823
- });
824
- });
825
- describe('open_editor action edge cases', () => {
826
- it('prefers vscode when preferredEditor is vscode even if cursor available', async () => {
827
- const deps = makeDeps();
828
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
829
- const env = makeEnv({ hasVscode: true, hasCursor: true, defaultEditor: 'vscode' });
830
- const config = makeConfig({ preferredEditor: 'vscode' });
831
- const result = await executeAction('open_editor', worktree, env, config, deps);
832
- expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
833
- expect(result.success).toBe(true);
834
- });
835
- it('prefers cursor when preferredEditor is cursor even if vscode available', async () => {
836
- const deps = makeDeps();
837
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
838
- const env = makeEnv({ hasVscode: true, hasCursor: true, defaultEditor: 'vscode' });
839
- const config = makeConfig({ preferredEditor: 'cursor' });
840
- const result = await executeAction('open_editor', worktree, env, config, deps);
841
- expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
842
- expect(result.success).toBe(true);
843
- });
844
- it('falls back to cursor when preferredEditor is vscode but vscode not installed', async () => {
845
- const deps = makeDeps();
846
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
847
- const env = makeEnv({ hasVscode: false, hasCursor: true, defaultEditor: 'cursor' });
848
- const config = makeConfig({ preferredEditor: 'vscode' });
849
- const result = await executeAction('open_editor', worktree, env, config, deps);
850
- expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
851
- expect(result.success).toBe(true);
852
- expect(result.message).toContain('Cursor');
853
- });
854
- it('falls back to vscode when preferredEditor is cursor but cursor not installed', async () => {
855
- const deps = makeDeps();
856
- const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
857
- const env = makeEnv({ hasVscode: true, hasCursor: false, defaultEditor: 'vscode' });
858
- const config = makeConfig({ preferredEditor: 'cursor' });
859
- const result = await executeAction('open_editor', worktree, env, config, deps);
860
- expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
861
- expect(result.success).toBe(true);
862
- expect(result.message).toContain('VSCode');
863
- });
864
- });
865
- describe('copy_path action', () => {
866
- it('copies the correct path', async () => {
867
- const deps = makeDeps();
868
- const worktree = makeWorktree({ path: '/home/user/my-project' });
869
- const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
870
- expect(deps.copyToClipboard).toHaveBeenCalledWith('/home/user/my-project');
871
- expect(result.success).toBe(true);
872
- expect(result.message).toContain('/home/user/my-project');
873
- });
874
- });
875
- describe('formatBranchAsTitle', () => {
876
- it('removes feat/ prefix', () => {
877
- expect(formatBranchAsTitle('feat/add-new-api')).toBe('Add new api');
878
- });
879
- it('removes fix/ prefix', () => {
880
- expect(formatBranchAsTitle('fix/bad-login')).toBe('Bad login');
881
- });
882
- it('removes chore/ prefix', () => {
883
- expect(formatBranchAsTitle('chore/update-deps')).toBe('Update deps');
884
- });
885
- it('removes docs/ prefix', () => {
886
- expect(formatBranchAsTitle('docs/add-guide')).toBe('Add guide');
887
- });
888
- it('removes refactor/ prefix', () => {
889
- expect(formatBranchAsTitle('refactor/clean-code')).toBe('Clean code');
890
- });
891
- it('removes test/ prefix', () => {
892
- expect(formatBranchAsTitle('test/add-more-tests')).toBe('Add more tests');
893
- });
894
- it('removes style/ prefix', () => {
895
- expect(formatBranchAsTitle('style/fix-lint')).toBe('Fix lint');
896
- });
897
- it('removes feature/ prefix', () => {
898
- expect(formatBranchAsTitle('feature/new-api')).toBe('New api');
899
- });
900
- it('removes bugfix/ prefix', () => {
901
- expect(formatBranchAsTitle('bugfix/fix-null')).toBe('Fix null');
902
- });
903
- it('removes trailing random suffixes', () => {
904
- expect(formatBranchAsTitle('feat/add-login-abc123')).toBe('Add login');
905
- expect(formatBranchAsTitle('add-feature-xyz789')).toBe('Add feature');
906
- });
907
- it('replaces hyphens with spaces', () => {
908
- // Note: 'branch' is 6 chars so it gets removed as a suffix
909
- expect(formatBranchAsTitle('my-feature-thing')).toBe('My feature thing');
910
- });
911
- it('replaces underscores with spaces', () => {
912
- expect(formatBranchAsTitle('my_new_api')).toBe('My new api');
913
- });
914
- it('capitalizes first letter', () => {
915
- expect(formatBranchAsTitle('lower')).toBe('Lower');
916
- });
917
- it('removes trailing 6+ char random suffix', () => {
918
- // Words like 'branch' (6 chars) are also removed - intentional behavior
919
- expect(formatBranchAsTitle('my-feature-branch')).toBe('My feature');
920
- });
921
- it('handles already capitalized input', () => {
922
- expect(formatBranchAsTitle('Already-Capitalized')).toBe('Already Capitalized');
923
- });
924
- it('handles simple branch name', () => {
925
- expect(formatBranchAsTitle('main')).toBe('Main');
926
- });
927
- it('handles complex branch names', () => {
928
- expect(formatBranchAsTitle('feat/make-lswt-more-interactive-b5y1o2')).toBe('Make lswt more interactive');
929
- });
930
- it('handles mixed separators', () => {
931
- expect(formatBranchAsTitle('my_feature-branch_name')).toBe('My feature branch name');
932
- });
933
- });
934
- describe('checkout_pr action', () => {
935
- it('returns error for non-remote_pr worktree type', async () => {
936
- const worktree = makeWorktree({
937
- type: 'pr',
938
- prNumber: 42,
939
- prState: 'OPEN',
940
- });
941
- const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
942
- expect(result.success).toBe(false);
943
- expect(result.message).toContain('Can only checkout remote PRs');
944
- });
945
- it('returns error when worktree has no PR number', async () => {
946
- const worktree = makeWorktree({
947
- type: 'remote_pr',
948
- prNumber: null,
949
- prState: 'OPEN',
950
- });
951
- const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
952
- expect(result.success).toBe(false);
953
- expect(result.message).toContain('Can only checkout remote PRs');
954
- });
955
- it('returns error when worktree has no branch', async () => {
956
- const worktree = makeWorktree({
957
- type: 'remote_pr',
958
- prNumber: 42,
959
- prState: 'OPEN',
960
- branch: null,
961
- });
962
- const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
963
- expect(result.success).toBe(false);
964
- expect(result.message).toContain('no associated branch');
965
- });
966
- it('returns error when repo root cannot be found', async () => {
967
- vi.mocked(git.getMainWorktreeRoot).mockReturnValue(null);
968
- const worktree = makeWorktree({
969
- type: 'remote_pr',
970
- prNumber: 42,
971
- prState: 'OPEN',
972
- branch: 'feat/remote-feature',
973
- });
974
- const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
975
- expect(result.success).toBe(false);
976
- expect(result.message).toContain('Could not find repository root');
977
- });
978
- it('successfully creates worktree for remote PR', async () => {
979
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
980
- vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
981
- vi.mocked(git.addWorktree).mockImplementation(() => { });
982
- // Mock git.exec to return empty string (git fetch succeeds)
983
- vi.mocked(git.exec).mockReturnValue('');
984
- const worktree = makeWorktree({
985
- type: 'remote_pr',
986
- prNumber: 42,
987
- prState: 'OPEN',
988
- branch: 'feat/remote-feature',
989
- prTitle: 'Add remote feature',
990
- prUrl: 'https://github.com/owner/repo/pull/42',
991
- });
992
- const config = makeConfig({ worktreePattern: '{repo}.pr{number}', worktreeParent: '..' });
993
- const result = await executeAction('checkout_pr', worktree, makeEnv(), config, makeDeps());
994
- expect(result.success).toBe(true);
995
- expect(result.message).toContain('Created worktree for PR #42');
996
- expect(result.shouldRefresh).toBe(true);
997
- expect(git.addWorktree).toHaveBeenCalledWith(expect.stringContaining('.pr42'), 'feat/remote-feature', expect.objectContaining({ cwd: '/home/user/repo' }));
998
- consoleSpy.mockRestore();
999
- });
1000
- it('handles git fetch failure', async () => {
1001
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
1002
- vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
1003
- // Mock git.exec to throw (git fetch fails)
1004
- vi.mocked(git.exec).mockImplementation(() => {
1005
- throw new Error('Failed to fetch branch');
1006
- });
1007
- const worktree = makeWorktree({
1008
- type: 'remote_pr',
1009
- prNumber: 42,
1010
- prState: 'OPEN',
1011
- branch: 'feat/remote-feature',
1012
- });
1013
- const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
1014
- expect(result.success).toBe(false);
1015
- expect(result.message).toContain('Failed to checkout PR');
1016
- consoleSpy.mockRestore();
1017
- });
1018
- });
1019
- describe('show_details action for remote_pr', () => {
1020
- it('shows PR title for remote PRs', async () => {
1021
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
1022
- const worktree = makeWorktree({
1023
- type: 'remote_pr',
1024
- prNumber: 42,
1025
- prState: 'OPEN',
1026
- branch: 'feat/remote-feature',
1027
- prTitle: 'Add amazing new feature',
1028
- prUrl: 'https://github.com/owner/repo/pull/42',
1029
- });
1030
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
1031
- expect(result.success).toBe(true);
1032
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
1033
- expect(output).toContain('Add amazing new feature');
1034
- consoleSpy.mockRestore();
1035
- });
1036
- it('shows PR URL for remote PRs', async () => {
1037
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
1038
- const worktree = makeWorktree({
1039
- type: 'remote_pr',
1040
- prNumber: 42,
1041
- prState: 'OPEN',
1042
- branch: 'feat/remote-feature',
1043
- prTitle: 'Add feature',
1044
- prUrl: 'https://github.com/owner/repo/pull/42',
1045
- });
1046
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
1047
- expect(result.success).toBe(true);
1048
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
1049
- expect(output).toContain('https://github.com/owner/repo/pull/42');
1050
- consoleSpy.mockRestore();
1051
- });
1052
- it('shows message about no local checkout for remote PRs', async () => {
1053
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
1054
- const worktree = makeWorktree({
1055
- type: 'remote_pr',
1056
- prNumber: 42,
1057
- prState: 'OPEN',
1058
- branch: 'feat/remote-feature',
1059
- prTitle: 'Add feature',
1060
- prUrl: 'https://github.com/owner/repo/pull/42',
1061
- });
1062
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
1063
- expect(result.success).toBe(true);
1064
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
1065
- expect(output).toContain('No local checkout');
1066
- consoleSpy.mockRestore();
1067
- });
1068
- it('does not show "Changes" line for remote PRs', async () => {
1069
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
1070
- const worktree = makeWorktree({
1071
- type: 'remote_pr',
1072
- prNumber: 42,
1073
- prState: 'OPEN',
1074
- branch: 'feat/remote-feature',
1075
- prTitle: 'Add feature',
1076
- prUrl: 'https://github.com/owner/repo/pull/42',
1077
- hasChanges: false,
1078
- });
1079
- const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
1080
- expect(result.success).toBe(true);
1081
- const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
1082
- // For remote PRs, "Changes:" line should not appear since there's no local path
1083
- expect(output).not.toMatch(/Changes:.*Clean/);
1084
- consoleSpy.mockRestore();
1085
- });
1086
- });
1087
- describe('open_pr_url action for remote_pr', () => {
1088
- it('uses stored prUrl for remote PRs', async () => {
1089
- const deps = makeDeps();
1090
- const worktree = makeWorktree({
1091
- type: 'remote_pr',
1092
- prNumber: 42,
1093
- prState: 'OPEN',
1094
- prUrl: 'https://github.com/owner/repo/pull/42',
1095
- });
1096
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
1097
- expect(result.success).toBe(true);
1098
- expect(result.message).toContain('Opened PR #42');
1099
- expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
1100
- // Should not call github.getPr since we have the URL stored
1101
- expect(github.getPr).not.toHaveBeenCalled();
1102
- });
1103
- it('falls back to fetching URL when prUrl is not stored', async () => {
1104
- vi.mocked(github.getPr).mockReturnValue({
1105
- number: 42,
1106
- url: 'https://github.com/owner/repo/pull/42',
1107
- state: 'OPEN',
1108
- isDraft: false,
1109
- title: 'Test PR',
1110
- headBranch: 'feature-42',
1111
- baseBranch: 'main',
1112
- });
1113
- const deps = makeDeps();
1114
- const worktree = makeWorktree({
1115
- type: 'remote_pr',
1116
- prNumber: 42,
1117
- prState: 'OPEN',
1118
- prUrl: undefined, // No stored URL
1119
- });
1120
- const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
1121
- expect(result.success).toBe(true);
1122
- expect(github.getPr).toHaveBeenCalledWith(42);
1123
- expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
1124
- });
1125
- });
1126
- });
1127
- //# sourceMappingURL=action-executors.test.js.map