@camaradesuk/git-worktree-tools 1.3.0 → 1.4.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 (284) hide show
  1. package/README.md +290 -3
  2. package/dist/api/clean.d.ts +65 -0
  3. package/dist/api/clean.d.ts.map +1 -0
  4. package/dist/api/clean.js +209 -0
  5. package/dist/api/clean.js.map +1 -0
  6. package/dist/api/create.d.ts +88 -0
  7. package/dist/api/create.d.ts.map +1 -0
  8. package/dist/api/create.js +370 -0
  9. package/dist/api/create.js.map +1 -0
  10. package/dist/api/index.d.ts +15 -0
  11. package/dist/api/index.d.ts.map +1 -0
  12. package/dist/api/index.js +19 -0
  13. package/dist/api/index.js.map +1 -0
  14. package/dist/api/list.d.ts +74 -0
  15. package/dist/api/list.d.ts.map +1 -0
  16. package/dist/api/list.js +80 -0
  17. package/dist/api/list.js.map +1 -0
  18. package/dist/api/state.d.ts +43 -0
  19. package/dist/api/state.d.ts.map +1 -0
  20. package/dist/api/state.js +70 -0
  21. package/dist/api/state.js.map +1 -0
  22. package/dist/cli/cleanpr.js +171 -28
  23. package/dist/cli/cleanpr.js.map +1 -1
  24. package/dist/cli/cleanpr.test.js +459 -7
  25. package/dist/cli/cleanpr.test.js.map +1 -1
  26. package/dist/cli/newpr.js +189 -28
  27. package/dist/cli/newpr.js.map +1 -1
  28. package/dist/cli/newpr.test.js +349 -0
  29. package/dist/cli/newpr.test.js.map +1 -1
  30. package/dist/cli/wtconfig.d.ts +14 -0
  31. package/dist/cli/wtconfig.d.ts.map +1 -0
  32. package/dist/cli/wtconfig.js +948 -0
  33. package/dist/cli/wtconfig.js.map +1 -0
  34. package/dist/cli/wtconfig.test.d.ts +5 -0
  35. package/dist/cli/wtconfig.test.d.ts.map +1 -0
  36. package/dist/cli/wtconfig.test.js +1281 -0
  37. package/dist/cli/wtconfig.test.js.map +1 -0
  38. package/dist/cli/wtlink.js +5 -0
  39. package/dist/cli/wtlink.js.map +1 -1
  40. package/dist/cli/wtstate.d.ts +8 -0
  41. package/dist/cli/wtstate.d.ts.map +1 -0
  42. package/dist/cli/wtstate.js +83 -0
  43. package/dist/cli/wtstate.js.map +1 -0
  44. package/dist/cli/wtstate.test.d.ts +5 -0
  45. package/dist/cli/wtstate.test.d.ts.map +1 -0
  46. package/dist/cli/wtstate.test.js +193 -0
  47. package/dist/cli/wtstate.test.js.map +1 -0
  48. package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts +2 -0
  49. package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts.map +1 -0
  50. package/dist/e2e/cleanpr/cleanpr.e2e.test.js +326 -0
  51. package/dist/e2e/cleanpr/cleanpr.e2e.test.js.map +1 -0
  52. package/dist/e2e/helpers/cli-runner.d.ts +103 -0
  53. package/dist/e2e/helpers/cli-runner.d.ts.map +1 -0
  54. package/dist/e2e/helpers/cli-runner.js +200 -0
  55. package/dist/e2e/helpers/cli-runner.js.map +1 -0
  56. package/dist/e2e/helpers/gh-mock.d.ts +87 -0
  57. package/dist/e2e/helpers/gh-mock.d.ts.map +1 -0
  58. package/dist/e2e/helpers/gh-mock.js +384 -0
  59. package/dist/e2e/helpers/gh-mock.js.map +1 -0
  60. package/dist/e2e/helpers/index.d.ts +12 -0
  61. package/dist/e2e/helpers/index.d.ts.map +1 -0
  62. package/dist/e2e/helpers/index.js +16 -0
  63. package/dist/e2e/helpers/index.js.map +1 -0
  64. package/dist/e2e/helpers/pty-wrapper.d.ts +118 -0
  65. package/dist/e2e/helpers/pty-wrapper.d.ts.map +1 -0
  66. package/dist/e2e/helpers/pty-wrapper.js +276 -0
  67. package/dist/e2e/helpers/pty-wrapper.js.map +1 -0
  68. package/dist/e2e/helpers/scenario-harness.d.ts +55 -0
  69. package/dist/e2e/helpers/scenario-harness.d.ts.map +1 -0
  70. package/dist/e2e/helpers/scenario-harness.js +360 -0
  71. package/dist/e2e/helpers/scenario-harness.js.map +1 -0
  72. package/dist/e2e/helpers/test-context.d.ts +120 -0
  73. package/dist/e2e/helpers/test-context.d.ts.map +1 -0
  74. package/dist/e2e/helpers/test-context.js +263 -0
  75. package/dist/e2e/helpers/test-context.js.map +1 -0
  76. package/dist/e2e/lswt/lswt.e2e.test.d.ts +2 -0
  77. package/dist/e2e/lswt/lswt.e2e.test.d.ts.map +1 -0
  78. package/dist/e2e/lswt/lswt.e2e.test.js +328 -0
  79. package/dist/e2e/lswt/lswt.e2e.test.js.map +1 -0
  80. package/dist/e2e/newpr/newpr.e2e.test.d.ts +2 -0
  81. package/dist/e2e/newpr/newpr.e2e.test.d.ts.map +1 -0
  82. package/dist/e2e/newpr/newpr.e2e.test.js +286 -0
  83. package/dist/e2e/newpr/newpr.e2e.test.js.map +1 -0
  84. package/dist/e2e/newpr/scenarios.e2e.test.d.ts +2 -0
  85. package/dist/e2e/newpr/scenarios.e2e.test.d.ts.map +1 -0
  86. package/dist/e2e/newpr/scenarios.e2e.test.js +426 -0
  87. package/dist/e2e/newpr/scenarios.e2e.test.js.map +1 -0
  88. package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts +2 -0
  89. package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts.map +1 -0
  90. package/dist/e2e/workflows/pr-lifecycle.e2e.test.js +298 -0
  91. package/dist/e2e/workflows/pr-lifecycle.e2e.test.js.map +1 -0
  92. package/dist/e2e/wtlink/wtlink.e2e.test.d.ts +2 -0
  93. package/dist/e2e/wtlink/wtlink.e2e.test.d.ts.map +1 -0
  94. package/dist/e2e/wtlink/wtlink.e2e.test.js +364 -0
  95. package/dist/e2e/wtlink/wtlink.e2e.test.js.map +1 -0
  96. package/dist/index.d.ts +15 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +14 -0
  99. package/dist/index.js.map +1 -1
  100. package/dist/lib/ai/base-provider.d.ts +58 -0
  101. package/dist/lib/ai/base-provider.d.ts.map +1 -0
  102. package/dist/lib/ai/base-provider.js +246 -0
  103. package/dist/lib/ai/base-provider.js.map +1 -0
  104. package/dist/lib/ai/base-provider.test.d.ts +7 -0
  105. package/dist/lib/ai/base-provider.test.d.ts.map +1 -0
  106. package/dist/lib/ai/base-provider.test.js +320 -0
  107. package/dist/lib/ai/base-provider.test.js.map +1 -0
  108. package/dist/lib/ai/cli-provider.d.ts +87 -0
  109. package/dist/lib/ai/cli-provider.d.ts.map +1 -0
  110. package/dist/lib/ai/cli-provider.js +280 -0
  111. package/dist/lib/ai/cli-provider.js.map +1 -0
  112. package/dist/lib/ai/cli-provider.test.d.ts +5 -0
  113. package/dist/lib/ai/cli-provider.test.d.ts.map +1 -0
  114. package/dist/lib/ai/cli-provider.test.js +462 -0
  115. package/dist/lib/ai/cli-provider.test.js.map +1 -0
  116. package/dist/lib/ai/fallback-provider.d.ts +20 -0
  117. package/dist/lib/ai/fallback-provider.d.ts.map +1 -0
  118. package/dist/lib/ai/fallback-provider.js +125 -0
  119. package/dist/lib/ai/fallback-provider.js.map +1 -0
  120. package/dist/lib/ai/fallback-provider.test.d.ts +7 -0
  121. package/dist/lib/ai/fallback-provider.test.d.ts.map +1 -0
  122. package/dist/lib/ai/fallback-provider.test.js +165 -0
  123. package/dist/lib/ai/fallback-provider.test.js.map +1 -0
  124. package/dist/lib/ai/generation-service.d.ts +44 -0
  125. package/dist/lib/ai/generation-service.d.ts.map +1 -0
  126. package/dist/lib/ai/generation-service.js +107 -0
  127. package/dist/lib/ai/generation-service.js.map +1 -0
  128. package/dist/lib/ai/generation-service.test.d.ts +7 -0
  129. package/dist/lib/ai/generation-service.test.d.ts.map +1 -0
  130. package/dist/lib/ai/generation-service.test.js +213 -0
  131. package/dist/lib/ai/generation-service.test.js.map +1 -0
  132. package/dist/lib/ai/index.d.ts +19 -0
  133. package/dist/lib/ai/index.d.ts.map +1 -0
  134. package/dist/lib/ai/index.js +22 -0
  135. package/dist/lib/ai/index.js.map +1 -0
  136. package/dist/lib/ai/provider-manager.d.ts +109 -0
  137. package/dist/lib/ai/provider-manager.d.ts.map +1 -0
  138. package/dist/lib/ai/provider-manager.js +270 -0
  139. package/dist/lib/ai/provider-manager.js.map +1 -0
  140. package/dist/lib/ai/provider-manager.test.d.ts +5 -0
  141. package/dist/lib/ai/provider-manager.test.d.ts.map +1 -0
  142. package/dist/lib/ai/provider-manager.test.js +312 -0
  143. package/dist/lib/ai/provider-manager.test.js.map +1 -0
  144. package/dist/lib/ai/types.d.ts +166 -0
  145. package/dist/lib/ai/types.d.ts.map +1 -0
  146. package/dist/lib/ai/types.js +19 -0
  147. package/dist/lib/ai/types.js.map +1 -0
  148. package/dist/lib/cleanpr/args.d.ts.map +1 -1
  149. package/dist/lib/cleanpr/args.js +18 -0
  150. package/dist/lib/cleanpr/args.js.map +1 -1
  151. package/dist/lib/cleanpr/args.test.js +88 -11
  152. package/dist/lib/cleanpr/args.test.js.map +1 -1
  153. package/dist/lib/cleanpr/cleanup.d.ts +2 -0
  154. package/dist/lib/cleanpr/cleanup.d.ts.map +1 -1
  155. package/dist/lib/cleanpr/cleanup.js +30 -2
  156. package/dist/lib/cleanpr/cleanup.js.map +1 -1
  157. package/dist/lib/cleanpr/cleanup.test.js +37 -5
  158. package/dist/lib/cleanpr/cleanup.test.js.map +1 -1
  159. package/dist/lib/cleanpr/types.d.ts +10 -0
  160. package/dist/lib/cleanpr/types.d.ts.map +1 -1
  161. package/dist/lib/cleanpr/worktree-info.test.js +72 -1
  162. package/dist/lib/cleanpr/worktree-info.test.js.map +1 -1
  163. package/dist/lib/config.d.ts +170 -1
  164. package/dist/lib/config.d.ts.map +1 -1
  165. package/dist/lib/config.js +129 -2
  166. package/dist/lib/config.js.map +1 -1
  167. package/dist/lib/config.test.js +406 -2
  168. package/dist/lib/config.test.js.map +1 -1
  169. package/dist/lib/hooks/executor.d.ts +35 -0
  170. package/dist/lib/hooks/executor.d.ts.map +1 -0
  171. package/dist/lib/hooks/executor.js +401 -0
  172. package/dist/lib/hooks/executor.js.map +1 -0
  173. package/dist/lib/hooks/executor.test.d.ts +5 -0
  174. package/dist/lib/hooks/executor.test.d.ts.map +1 -0
  175. package/dist/lib/hooks/executor.test.js +648 -0
  176. package/dist/lib/hooks/executor.test.js.map +1 -0
  177. package/dist/lib/hooks/index.d.ts +25 -0
  178. package/dist/lib/hooks/index.d.ts.map +1 -0
  179. package/dist/lib/hooks/index.js +26 -0
  180. package/dist/lib/hooks/index.js.map +1 -0
  181. package/dist/lib/hooks/templates.d.ts +74 -0
  182. package/dist/lib/hooks/templates.d.ts.map +1 -0
  183. package/dist/lib/hooks/templates.js +270 -0
  184. package/dist/lib/hooks/templates.js.map +1 -0
  185. package/dist/lib/hooks/templates.test.d.ts +5 -0
  186. package/dist/lib/hooks/templates.test.d.ts.map +1 -0
  187. package/dist/lib/hooks/templates.test.js +163 -0
  188. package/dist/lib/hooks/templates.test.js.map +1 -0
  189. package/dist/lib/hooks/types.d.ts +161 -0
  190. package/dist/lib/hooks/types.d.ts.map +1 -0
  191. package/dist/lib/hooks/types.js +73 -0
  192. package/dist/lib/hooks/types.js.map +1 -0
  193. package/dist/lib/hooks/types.test.d.ts +5 -0
  194. package/dist/lib/hooks/types.test.d.ts.map +1 -0
  195. package/dist/lib/hooks/types.test.js +132 -0
  196. package/dist/lib/hooks/types.test.js.map +1 -0
  197. package/dist/lib/json-output.d.ts +172 -0
  198. package/dist/lib/json-output.d.ts.map +1 -0
  199. package/dist/lib/json-output.js +134 -0
  200. package/dist/lib/json-output.js.map +1 -0
  201. package/dist/lib/json-output.test.d.ts +5 -0
  202. package/dist/lib/json-output.test.d.ts.map +1 -0
  203. package/dist/lib/json-output.test.js +259 -0
  204. package/dist/lib/json-output.test.js.map +1 -0
  205. package/dist/lib/lswt/action-executors.test.js +6 -0
  206. package/dist/lib/lswt/action-executors.test.js.map +1 -1
  207. package/dist/lib/newpr/action-deps.d.ts +15 -0
  208. package/dist/lib/newpr/action-deps.d.ts.map +1 -0
  209. package/dist/lib/newpr/action-deps.js +22 -0
  210. package/dist/lib/newpr/action-deps.js.map +1 -0
  211. package/dist/lib/newpr/args.d.ts.map +1 -1
  212. package/dist/lib/newpr/args.js +56 -0
  213. package/dist/lib/newpr/args.js.map +1 -1
  214. package/dist/lib/newpr/hook-runner.d.ts +80 -0
  215. package/dist/lib/newpr/hook-runner.d.ts.map +1 -0
  216. package/dist/lib/newpr/hook-runner.js +182 -0
  217. package/dist/lib/newpr/hook-runner.js.map +1 -0
  218. package/dist/lib/newpr/hook-runner.test.d.ts +7 -0
  219. package/dist/lib/newpr/hook-runner.test.d.ts.map +1 -0
  220. package/dist/lib/newpr/hook-runner.test.js +301 -0
  221. package/dist/lib/newpr/hook-runner.test.js.map +1 -0
  222. package/dist/lib/newpr/index.d.ts +3 -0
  223. package/dist/lib/newpr/index.d.ts.map +1 -1
  224. package/dist/lib/newpr/index.js +3 -0
  225. package/dist/lib/newpr/index.js.map +1 -1
  226. package/dist/lib/newpr/types.d.ts +9 -0
  227. package/dist/lib/newpr/types.d.ts.map +1 -1
  228. package/dist/lib/wtconfig/config-manager.d.ts +72 -0
  229. package/dist/lib/wtconfig/config-manager.d.ts.map +1 -0
  230. package/dist/lib/wtconfig/config-manager.js +408 -0
  231. package/dist/lib/wtconfig/config-manager.js.map +1 -0
  232. package/dist/lib/wtconfig/config-manager.test.d.ts +5 -0
  233. package/dist/lib/wtconfig/config-manager.test.d.ts.map +1 -0
  234. package/dist/lib/wtconfig/config-manager.test.js +501 -0
  235. package/dist/lib/wtconfig/config-manager.test.js.map +1 -0
  236. package/dist/lib/wtconfig/environment.d.ts +23 -0
  237. package/dist/lib/wtconfig/environment.d.ts.map +1 -0
  238. package/dist/lib/wtconfig/environment.js +238 -0
  239. package/dist/lib/wtconfig/environment.js.map +1 -0
  240. package/dist/lib/wtconfig/environment.test.d.ts +5 -0
  241. package/dist/lib/wtconfig/environment.test.d.ts.map +1 -0
  242. package/dist/lib/wtconfig/environment.test.js +246 -0
  243. package/dist/lib/wtconfig/environment.test.js.map +1 -0
  244. package/dist/lib/wtconfig/index.d.ts +7 -0
  245. package/dist/lib/wtconfig/index.d.ts.map +1 -0
  246. package/dist/lib/wtconfig/index.js +8 -0
  247. package/dist/lib/wtconfig/index.js.map +1 -0
  248. package/dist/lib/wtconfig/types.d.ts +97 -0
  249. package/dist/lib/wtconfig/types.d.ts.map +1 -0
  250. package/dist/lib/wtconfig/types.js +5 -0
  251. package/dist/lib/wtconfig/types.js.map +1 -0
  252. package/dist/lib/wtstate/analyze.d.ts +13 -0
  253. package/dist/lib/wtstate/analyze.d.ts.map +1 -0
  254. package/dist/lib/wtstate/analyze.js +165 -0
  255. package/dist/lib/wtstate/analyze.js.map +1 -0
  256. package/dist/lib/wtstate/analyze.test.d.ts +5 -0
  257. package/dist/lib/wtstate/analyze.test.d.ts.map +1 -0
  258. package/dist/lib/wtstate/analyze.test.js +282 -0
  259. package/dist/lib/wtstate/analyze.test.js.map +1 -0
  260. package/dist/lib/wtstate/args.d.ts +17 -0
  261. package/dist/lib/wtstate/args.d.ts.map +1 -0
  262. package/dist/lib/wtstate/args.js +91 -0
  263. package/dist/lib/wtstate/args.js.map +1 -0
  264. package/dist/lib/wtstate/args.test.d.ts +5 -0
  265. package/dist/lib/wtstate/args.test.d.ts.map +1 -0
  266. package/dist/lib/wtstate/args.test.js +120 -0
  267. package/dist/lib/wtstate/args.test.js.map +1 -0
  268. package/dist/lib/wtstate/index.d.ts +7 -0
  269. package/dist/lib/wtstate/index.d.ts.map +1 -0
  270. package/dist/lib/wtstate/index.js +8 -0
  271. package/dist/lib/wtstate/index.js.map +1 -0
  272. package/dist/lib/wtstate/types.d.ts +64 -0
  273. package/dist/lib/wtstate/types.d.ts.map +1 -0
  274. package/dist/lib/wtstate/types.js +5 -0
  275. package/dist/lib/wtstate/types.js.map +1 -0
  276. package/dist/mcp/server.d.ts +14 -0
  277. package/dist/mcp/server.d.ts.map +1 -0
  278. package/dist/mcp/server.js +339 -0
  279. package/dist/mcp/server.js.map +1 -0
  280. package/dist/mcp/server.test.d.ts +9 -0
  281. package/dist/mcp/server.test.d.ts.map +1 -0
  282. package/dist/mcp/server.test.js +390 -0
  283. package/dist/mcp/server.test.js.map +1 -0
  284. package/package.json +8 -2
@@ -0,0 +1,948 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * wtconfig - Configuration management for git-worktree-tools
4
+ *
5
+ * Commands:
6
+ * wtconfig init - Run interactive setup wizard
7
+ * wtconfig show - Show current configuration
8
+ * wtconfig set <key> <val> - Set a configuration value
9
+ * wtconfig get <key> - Get a configuration value
10
+ * wtconfig edit - Open config in editor
11
+ * wtconfig validate - Validate configuration
12
+ */
13
+ import { execSync } from 'child_process';
14
+ import inquirer from 'inquirer';
15
+ import * as colors from '../lib/colors.js';
16
+ import * as git from '../lib/git.js';
17
+ import { getDefaultConfig } from '../lib/config.js';
18
+ /**
19
+ * Safely get repository root, returning null if not in a git repo
20
+ */
21
+ function findRepoRoot() {
22
+ try {
23
+ return git.getRepoRoot();
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ import { detectEnvironment, detectDefaultBranch, getInstallCommand, getEditorCommand, getConfigSource, loadMergedConfig, loadRepoConfig, loadGlobalConfig, saveRepoConfig, saveGlobalConfig, setConfigValue, getConfigValue, validateConfig, formatConfigDisplay, getGlobalConfigPath, getDefaultRepoConfigPath, } from '../lib/wtconfig/index.js';
30
+ // Parse command line arguments
31
+ const args = process.argv.slice(2);
32
+ const command = args[0] || 'show';
33
+ // Entry point
34
+ main().catch((error) => {
35
+ console.error(colors.error(`Error: ${error instanceof Error ? error.message : String(error)}`));
36
+ process.exit(1);
37
+ });
38
+ async function main() {
39
+ switch (command) {
40
+ case 'init':
41
+ case 'wizard':
42
+ await runWizard();
43
+ break;
44
+ case 'show':
45
+ await showConfig();
46
+ break;
47
+ case 'set':
48
+ await setConfig(args[1], args[2]);
49
+ break;
50
+ case 'get':
51
+ await getConfig(args[1]);
52
+ break;
53
+ case 'edit':
54
+ await editConfig();
55
+ break;
56
+ case 'validate':
57
+ await validateCurrentConfig();
58
+ break;
59
+ case 'help':
60
+ case '--help':
61
+ case '-h':
62
+ showHelp();
63
+ break;
64
+ default:
65
+ console.error(colors.error(`Unknown command: ${command}`));
66
+ showHelp();
67
+ process.exit(1);
68
+ }
69
+ }
70
+ function showHelp() {
71
+ console.log(`
72
+ ${colors.info('wtconfig')} - Configuration management for git-worktree-tools
73
+
74
+ ${colors.warning('Usage:')}
75
+ wtconfig init Run interactive setup wizard
76
+ wtconfig show Show current configuration
77
+ wtconfig set <key> <val> Set a configuration value (e.g., "baseBranch main")
78
+ wtconfig get <key> Get a configuration value (e.g., "ai.provider")
79
+ wtconfig edit Open config in default editor
80
+ wtconfig validate Validate current configuration
81
+ wtconfig help Show this help message
82
+
83
+ ${colors.warning('Configuration Locations:')}
84
+ Global: ~/.worktreerc (applies to all repos)
85
+ Repository: .worktreerc or .worktreerc.json (repo-specific)
86
+
87
+ ${colors.warning('Examples:')}
88
+ wtconfig init # Start interactive setup
89
+ wtconfig set baseBranch develop # Set base branch
90
+ wtconfig set ai.provider claude # Set AI provider
91
+ wtconfig set hooks.post-worktree "npm install"
92
+ wtconfig get ai.provider # Get AI provider setting
93
+ wtconfig validate # Check for configuration errors
94
+ `);
95
+ }
96
+ async function showConfig() {
97
+ const repoRoot = findRepoRoot();
98
+ const source = getConfigSource(repoRoot ?? undefined);
99
+ const config = loadMergedConfig(repoRoot ?? undefined);
100
+ const defaults = getDefaultConfig();
101
+ console.log(colors.info('Current Configuration'));
102
+ console.log();
103
+ if (source.type === 'none') {
104
+ console.log(colors.dim('No configuration file found. Using defaults.'));
105
+ console.log(colors.dim(`Run 'wtconfig init' to create a configuration file.`));
106
+ console.log();
107
+ }
108
+ else {
109
+ console.log(colors.dim(`Source: ${source.path}`));
110
+ console.log();
111
+ }
112
+ // Show merged config with sources indicated
113
+ const mergedDisplay = formatConfigWithDefaults(config, defaults, source.type !== 'none');
114
+ console.log(mergedDisplay);
115
+ }
116
+ function formatConfigWithDefaults(config, defaults, hasUserConfig) {
117
+ const lines = [];
118
+ const addLine = (key, value, defaultValue) => {
119
+ const isDefault = JSON.stringify(value) === JSON.stringify(defaultValue);
120
+ const valueStr = JSON.stringify(value);
121
+ if (isDefault && !hasUserConfig) {
122
+ lines.push(` ${colors.dim(`${key}:`)} ${colors.dim(valueStr)} ${colors.dim('(default)')}`);
123
+ }
124
+ else if (isDefault) {
125
+ lines.push(` ${key}: ${valueStr} ${colors.dim('(default)')}`);
126
+ }
127
+ else {
128
+ lines.push(` ${colors.success(key + ':')} ${valueStr}`);
129
+ }
130
+ };
131
+ lines.push('{');
132
+ addLine('baseBranch', config.baseBranch ?? defaults.baseBranch, defaults.baseBranch);
133
+ addLine('draftPr', config.draftPr ?? defaults.draftPr, defaults.draftPr);
134
+ addLine('branchPrefix', config.branchPrefix ?? defaults.branchPrefix, defaults.branchPrefix);
135
+ addLine('worktreePattern', config.worktreePattern ?? defaults.worktreePattern, defaults.worktreePattern);
136
+ addLine('worktreeParent', config.worktreeParent ?? defaults.worktreeParent, defaults.worktreeParent);
137
+ addLine('preferredEditor', config.preferredEditor ?? defaults.preferredEditor, defaults.preferredEditor);
138
+ if (config.sharedRepos && config.sharedRepos.length > 0) {
139
+ addLine('sharedRepos', config.sharedRepos, defaults.sharedRepos);
140
+ }
141
+ if (config.syncPatterns && config.syncPatterns.length > 0) {
142
+ addLine('syncPatterns', config.syncPatterns, defaults.syncPatterns);
143
+ }
144
+ // AI config
145
+ if (config.ai) {
146
+ lines.push(` ${colors.info('ai:')} {`);
147
+ const ai = config.ai;
148
+ const defAi = defaults.ai;
149
+ if (ai.provider !== undefined)
150
+ addLine(' provider', ai.provider, defAi.provider);
151
+ if (ai.branchName !== undefined)
152
+ addLine(' branchName', ai.branchName, defAi.branchName);
153
+ if (ai.prTitle !== undefined)
154
+ addLine(' prTitle', ai.prTitle, defAi.prTitle);
155
+ if (ai.prDescription !== undefined)
156
+ addLine(' prDescription', ai.prDescription, defAi.prDescription);
157
+ lines.push(' }');
158
+ }
159
+ // Hooks config
160
+ if (config.hooks && Object.keys(config.hooks).length > 0) {
161
+ lines.push(` ${colors.info('hooks:')} {`);
162
+ for (const [hookName, hookDef] of Object.entries(config.hooks)) {
163
+ const hookStr = typeof hookDef === 'string' ? `"${hookDef}"` : JSON.stringify(hookDef);
164
+ lines.push(` ${hookName}: ${hookStr}`);
165
+ }
166
+ lines.push(' }');
167
+ }
168
+ // Plugins
169
+ if (config.plugins && config.plugins.length > 0) {
170
+ addLine('plugins', config.plugins, defaults.plugins);
171
+ }
172
+ // Generators
173
+ if (config.generators && Object.keys(config.generators).length > 0) {
174
+ lines.push(` ${colors.info('generators:')} {`);
175
+ for (const [genName, genPath] of Object.entries(config.generators)) {
176
+ lines.push(` ${genName}: "${genPath}"`);
177
+ }
178
+ lines.push(' }');
179
+ }
180
+ // Integrations
181
+ if (config.integrations && Object.keys(config.integrations).length > 0) {
182
+ lines.push(` ${colors.info('integrations:')} {`);
183
+ for (const [intName, intConfig] of Object.entries(config.integrations)) {
184
+ lines.push(` ${intName}: ${JSON.stringify(intConfig)}`);
185
+ }
186
+ lines.push(' }');
187
+ }
188
+ lines.push('}');
189
+ return lines.join('\n');
190
+ }
191
+ async function setConfig(key, value) {
192
+ if (!key) {
193
+ console.error(colors.error('Usage: wtconfig set <key> <value>'));
194
+ console.error(colors.dim('Example: wtconfig set baseBranch develop'));
195
+ process.exit(1);
196
+ }
197
+ if (value === undefined) {
198
+ console.error(colors.error(`Missing value for key: ${key}`));
199
+ process.exit(1);
200
+ }
201
+ // Determine scope
202
+ const repoRoot = findRepoRoot();
203
+ const { saveLocation } = await inquirer.prompt([
204
+ {
205
+ type: 'list',
206
+ name: 'saveLocation',
207
+ message: 'Where should this setting be saved?',
208
+ choices: [
209
+ { name: 'Repository (.worktreerc)', value: 'repo', disabled: !repoRoot },
210
+ { name: 'Global (~/.worktreerc)', value: 'global' },
211
+ ],
212
+ default: repoRoot ? 'repo' : 'global',
213
+ },
214
+ ]);
215
+ const currentConfig = saveLocation === 'repo' && repoRoot ? loadRepoConfig(repoRoot) || {} : loadGlobalConfig() || {};
216
+ try {
217
+ const newConfig = setConfigValue(currentConfig, key, value);
218
+ // Validate before saving
219
+ const validation = validateConfig(newConfig);
220
+ if (!validation.valid) {
221
+ console.error(colors.error('Configuration validation failed:'));
222
+ for (const error of validation.errors) {
223
+ console.error(colors.error(` ${error.path}: ${error.message}`));
224
+ }
225
+ process.exit(1);
226
+ }
227
+ // Show warnings
228
+ for (const warning of validation.warnings) {
229
+ console.warn(colors.warning(`Warning: ${warning.path}: ${warning.message}`));
230
+ }
231
+ // Save
232
+ if (saveLocation === 'repo' && repoRoot) {
233
+ saveRepoConfig(repoRoot, newConfig);
234
+ console.log(colors.success(`Set ${key} = ${value} in .worktreerc`));
235
+ }
236
+ else {
237
+ saveGlobalConfig(newConfig);
238
+ console.log(colors.success(`Set ${key} = ${value} in ~/.worktreerc`));
239
+ }
240
+ }
241
+ catch (error) {
242
+ console.error(colors.error(`Failed to set value: ${error instanceof Error ? error.message : String(error)}`));
243
+ process.exit(1);
244
+ }
245
+ }
246
+ async function getConfig(key) {
247
+ if (!key) {
248
+ console.error(colors.error('Usage: wtconfig get <key>'));
249
+ console.error(colors.dim('Example: wtconfig get ai.provider'));
250
+ process.exit(1);
251
+ }
252
+ const repoRoot = findRepoRoot();
253
+ const config = loadMergedConfig(repoRoot ?? undefined);
254
+ const defaults = getDefaultConfig();
255
+ // Get value from merged config or defaults
256
+ let value = getConfigValue(config, key);
257
+ if (value === undefined) {
258
+ value = getConfigValue(defaults, key);
259
+ }
260
+ if (value === undefined) {
261
+ console.error(colors.error(`Unknown configuration key: ${key}`));
262
+ process.exit(1);
263
+ }
264
+ if (typeof value === 'object') {
265
+ console.log(JSON.stringify(value, null, 2));
266
+ }
267
+ else {
268
+ console.log(String(value));
269
+ }
270
+ }
271
+ async function editConfig() {
272
+ const repoRoot = findRepoRoot();
273
+ // Determine which file to edit
274
+ const { editLocation } = await inquirer.prompt([
275
+ {
276
+ type: 'list',
277
+ name: 'editLocation',
278
+ message: 'Which configuration file would you like to edit?',
279
+ choices: [
280
+ { name: 'Repository (.worktreerc)', value: 'repo', disabled: !repoRoot },
281
+ { name: 'Global (~/.worktreerc)', value: 'global' },
282
+ ],
283
+ default: repoRoot ? 'repo' : 'global',
284
+ },
285
+ ]);
286
+ const configPath = editLocation === 'repo' && repoRoot
287
+ ? getDefaultRepoConfigPath(repoRoot)
288
+ : getGlobalConfigPath();
289
+ // Create file with defaults if it doesn't exist
290
+ const fs = await import('fs');
291
+ if (!fs.existsSync(configPath)) {
292
+ const { create } = await inquirer.prompt([
293
+ {
294
+ type: 'confirm',
295
+ name: 'create',
296
+ message: `${configPath} does not exist. Create it?`,
297
+ default: true,
298
+ },
299
+ ]);
300
+ if (!create) {
301
+ console.log(colors.dim('Cancelled.'));
302
+ return;
303
+ }
304
+ fs.writeFileSync(configPath, '{\n \n}\n', 'utf8');
305
+ }
306
+ // Open in editor
307
+ const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
308
+ console.log(colors.dim(`Opening ${configPath} in ${editor}...`));
309
+ try {
310
+ execSync(`${editor} "${configPath}"`, { stdio: 'inherit' });
311
+ }
312
+ catch {
313
+ console.error(colors.error('Failed to open editor'));
314
+ process.exit(1);
315
+ }
316
+ }
317
+ async function validateCurrentConfig() {
318
+ const repoRoot = findRepoRoot();
319
+ const source = getConfigSource(repoRoot ?? undefined);
320
+ if (source.type === 'none') {
321
+ console.log(colors.success('No configuration file found. Nothing to validate.'));
322
+ return;
323
+ }
324
+ console.log(colors.info(`Validating: ${source.path}`));
325
+ const config = loadMergedConfig(repoRoot ?? undefined);
326
+ const result = validateConfig(config);
327
+ if (result.valid && result.warnings.length === 0) {
328
+ console.log(colors.success('Configuration is valid.'));
329
+ return;
330
+ }
331
+ if (result.errors.length > 0) {
332
+ console.log(colors.error('\nErrors:'));
333
+ for (const error of result.errors) {
334
+ console.log(colors.error(` ${error.path}: ${error.message}`));
335
+ }
336
+ }
337
+ if (result.warnings.length > 0) {
338
+ console.log(colors.warning('\nWarnings:'));
339
+ for (const warning of result.warnings) {
340
+ console.log(colors.warning(` ${warning.path}: ${warning.message}`));
341
+ }
342
+ }
343
+ if (!result.valid) {
344
+ process.exit(1);
345
+ }
346
+ }
347
+ async function runWizard() {
348
+ console.log();
349
+ console.log(colors.info('┌' + '─'.repeat(56) + '┐'));
350
+ console.log(colors.info('│') + ' git-worktree-tools Setup Wizard ' + colors.info('│'));
351
+ console.log(colors.info('└' + '─'.repeat(56) + '┘'));
352
+ console.log();
353
+ // Detect environment
354
+ console.log(colors.dim('Detecting your environment...'));
355
+ console.log();
356
+ const repoRoot = findRepoRoot();
357
+ const env = detectEnvironment(repoRoot ?? undefined);
358
+ // Show detected environment
359
+ displayEnvironment(env);
360
+ // Run wizard steps
361
+ const state = await runWizardSteps(env, repoRoot);
362
+ // Build configuration
363
+ const config = buildConfigFromState(state, env);
364
+ // Preview configuration
365
+ console.log();
366
+ console.log(colors.info('Configuration Preview:'));
367
+ console.log();
368
+ console.log(formatConfigDisplay(config));
369
+ console.log();
370
+ // Confirm save
371
+ const { saveChoice } = await inquirer.prompt([
372
+ {
373
+ type: 'list',
374
+ name: 'saveChoice',
375
+ message: 'Save configuration?',
376
+ choices: [
377
+ { name: 'Save to repository (.worktreerc)', value: 'repo', disabled: !repoRoot },
378
+ { name: 'Save globally (~/.worktreerc)', value: 'global' },
379
+ { name: 'Cancel', value: 'cancel' },
380
+ ],
381
+ default: repoRoot ? 'repo' : 'global',
382
+ },
383
+ ]);
384
+ if (saveChoice === 'cancel') {
385
+ console.log(colors.dim('Setup cancelled.'));
386
+ return;
387
+ }
388
+ // Save configuration
389
+ if (saveChoice === 'repo' && repoRoot) {
390
+ saveRepoConfig(repoRoot, config);
391
+ console.log();
392
+ console.log(colors.success(`Configuration saved to ${getDefaultRepoConfigPath(repoRoot)}`));
393
+ }
394
+ else {
395
+ saveGlobalConfig(config);
396
+ console.log();
397
+ console.log(colors.success(`Configuration saved to ${getGlobalConfigPath()}`));
398
+ }
399
+ // Show quick start
400
+ console.log();
401
+ console.log(colors.info('Quick Start:'));
402
+ console.log(` ${colors.warning('newpr "Add feature"')} Create a new PR`);
403
+ console.log(` ${colors.warning('lswt')} List worktrees`);
404
+ console.log(` ${colors.warning('cleanpr')} Clean merged PRs`);
405
+ console.log();
406
+ }
407
+ function displayEnvironment(env) {
408
+ const check = colors.success('✓');
409
+ const cross = colors.error('✗');
410
+ const warn = colors.warning('○');
411
+ // Git
412
+ if (env.git.version) {
413
+ if (env.git.configured) {
414
+ console.log(`${check} Git ${env.git.version} configured (${env.git.email})`);
415
+ }
416
+ else {
417
+ console.log(`${warn} Git ${env.git.version} (not configured - run: git config --global user.name/email)`);
418
+ }
419
+ }
420
+ else {
421
+ console.log(`${cross} Git not found`);
422
+ }
423
+ // GitHub CLI
424
+ if (env.github.installed) {
425
+ if (env.github.authenticated) {
426
+ console.log(`${check} GitHub CLI authenticated${env.github.user ? ` (${env.github.user})` : ''}`);
427
+ }
428
+ else {
429
+ console.log(`${warn} GitHub CLI installed but not authenticated (run: gh auth login)`);
430
+ }
431
+ }
432
+ else {
433
+ console.log(`${cross} GitHub CLI not installed (optional, install from: https://cli.github.com)`);
434
+ }
435
+ // AI tools
436
+ const aiTools = [];
437
+ if (env.ai.claudeCode)
438
+ aiTools.push('Claude Code');
439
+ if (env.ai.geminiCLI)
440
+ aiTools.push('Gemini CLI');
441
+ if (env.ai.ollama)
442
+ aiTools.push('Ollama');
443
+ if (env.ai.openaiKey)
444
+ aiTools.push('OpenAI API');
445
+ if (aiTools.length > 0) {
446
+ console.log(`${check} AI tools: ${aiTools.join(', ')}`);
447
+ }
448
+ else {
449
+ console.log(`${colors.dim('○')} No AI tools detected (optional)`);
450
+ }
451
+ // Package manager
452
+ if (env.packageManager) {
453
+ console.log(`${check} Package manager: ${env.packageManager}`);
454
+ }
455
+ // IDE
456
+ const ides = [];
457
+ if (env.ide.vscode)
458
+ ides.push('VS Code');
459
+ if (env.ide.cursor)
460
+ ides.push('Cursor');
461
+ if (ides.length > 0) {
462
+ console.log(`${check} IDE: ${ides.join(', ')}`);
463
+ }
464
+ console.log();
465
+ }
466
+ async function runWizardSteps(env, repoRoot) {
467
+ // Step 1: Base Configuration
468
+ console.log(colors.info('Step 1/4: Base Configuration'));
469
+ console.log();
470
+ const defaultBranch = repoRoot ? detectDefaultBranch(repoRoot) : 'main';
471
+ const step1 = await inquirer.prompt([
472
+ {
473
+ type: 'list',
474
+ name: 'baseBranch',
475
+ message: 'Default base branch for PRs?',
476
+ choices: [
477
+ { name: `${defaultBranch} (detected)`, value: defaultBranch },
478
+ ...(defaultBranch !== 'main' ? [{ name: 'main', value: 'main' }] : []),
479
+ ...(defaultBranch !== 'master' ? [{ name: 'master', value: 'master' }] : []),
480
+ { name: 'develop', value: 'develop' },
481
+ { name: 'Other...', value: '__other__' },
482
+ ],
483
+ default: defaultBranch,
484
+ },
485
+ {
486
+ type: 'confirm',
487
+ name: 'draftPr',
488
+ message: 'Create PRs as drafts by default?',
489
+ default: false,
490
+ },
491
+ {
492
+ type: 'list',
493
+ name: 'branchPrefix',
494
+ message: 'Branch name prefix?',
495
+ choices: [
496
+ { name: 'feat (feature branches)', value: 'feat' },
497
+ { name: 'feature (feature branches)', value: 'feature' },
498
+ { name: 'fix (bug fixes)', value: 'fix' },
499
+ { name: 'None (no prefix)', value: '' },
500
+ ],
501
+ default: 'feat',
502
+ },
503
+ ]);
504
+ let baseBranch = step1.baseBranch;
505
+ if (baseBranch === '__other__') {
506
+ const { customBranch } = await inquirer.prompt([
507
+ {
508
+ type: 'input',
509
+ name: 'customBranch',
510
+ message: 'Enter custom base branch name:',
511
+ validate: (input) => input.trim().length > 0 || 'Branch name is required',
512
+ },
513
+ ]);
514
+ baseBranch = customBranch;
515
+ }
516
+ // Step 2: Worktree Location
517
+ console.log();
518
+ console.log(colors.info('Step 2/4: Worktree Location'));
519
+ console.log();
520
+ const step2 = await inquirer.prompt([
521
+ {
522
+ type: 'list',
523
+ name: 'worktreeLocation',
524
+ message: 'Where should worktrees be created?',
525
+ choices: [
526
+ { name: 'Sibling to main repo (../repo.pr42)', value: 'sibling' },
527
+ { name: 'Inside .worktrees folder (.worktrees/pr42)', value: 'inside' },
528
+ { name: 'Custom location...', value: 'custom' },
529
+ ],
530
+ default: 'sibling',
531
+ },
532
+ {
533
+ type: 'list',
534
+ name: 'worktreePattern',
535
+ message: 'Worktree naming pattern?',
536
+ choices: [
537
+ { name: '{repo}.pr{number} (e.g., myapp.pr42)', value: '{repo}.pr{number}' },
538
+ { name: 'pr-{number} (e.g., pr-42)', value: 'pr-{number}' },
539
+ { name: '{branch} (e.g., feat-dark-mode)', value: '{branch}' },
540
+ ],
541
+ default: '{repo}.pr{number}',
542
+ },
543
+ ]);
544
+ let worktreeParent = '..';
545
+ if (step2.worktreeLocation === 'inside') {
546
+ worktreeParent = '.worktrees';
547
+ }
548
+ else if (step2.worktreeLocation === 'custom') {
549
+ const { customParent } = await inquirer.prompt([
550
+ {
551
+ type: 'input',
552
+ name: 'customParent',
553
+ message: 'Enter worktree parent directory (absolute or relative to repo):',
554
+ default: '..',
555
+ },
556
+ ]);
557
+ worktreeParent = customParent;
558
+ }
559
+ // Step 3: AI Integration
560
+ console.log();
561
+ console.log(colors.info('Step 3/4: AI Integration'));
562
+ console.log();
563
+ let aiEnabled = false;
564
+ let aiProvider = 'none';
565
+ let aiBranchName = false;
566
+ let aiPrDescription = false;
567
+ const hasAI = env.ai.claudeCode || env.ai.geminiCLI || env.ai.ollama || env.ai.openaiKey;
568
+ if (hasAI) {
569
+ const detectedProvider = env.ai.claudeCode
570
+ ? 'Claude Code'
571
+ : env.ai.geminiCLI
572
+ ? 'Gemini CLI'
573
+ : env.ai.ollama
574
+ ? 'Ollama'
575
+ : 'OpenAI';
576
+ const step3a = await inquirer.prompt([
577
+ {
578
+ type: 'list',
579
+ name: 'aiChoice',
580
+ message: `${detectedProvider} detected! Enable AI content generation?`,
581
+ choices: [
582
+ { name: `Yes - Use detected provider (${detectedProvider})`, value: 'yes' },
583
+ { name: 'Yes - Configure manually', value: 'configure' },
584
+ { name: 'No - Skip AI features', value: 'no' },
585
+ ],
586
+ default: 'yes',
587
+ },
588
+ ]);
589
+ if (step3a.aiChoice !== 'no') {
590
+ aiEnabled = true;
591
+ if (step3a.aiChoice === 'yes') {
592
+ aiProvider = env.ai.claudeCode
593
+ ? 'auto'
594
+ : env.ai.geminiCLI
595
+ ? 'gemini'
596
+ : env.ai.ollama
597
+ ? 'ollama'
598
+ : 'openai';
599
+ }
600
+ else {
601
+ const { manualProvider } = await inquirer.prompt([
602
+ {
603
+ type: 'list',
604
+ name: 'manualProvider',
605
+ message: 'Select AI provider:',
606
+ choices: [
607
+ { name: 'Auto-detect', value: 'auto' },
608
+ { name: 'Claude', value: 'claude' },
609
+ { name: 'Gemini', value: 'gemini' },
610
+ { name: 'OpenAI', value: 'openai' },
611
+ { name: 'Ollama (local)', value: 'ollama' },
612
+ ],
613
+ },
614
+ ]);
615
+ aiProvider = manualProvider;
616
+ }
617
+ const step3b = await inquirer.prompt([
618
+ {
619
+ type: 'checkbox',
620
+ name: 'aiFeatures',
621
+ message: 'Which AI features would you like to enable?',
622
+ choices: [
623
+ { name: 'Generate branch names from description', value: 'branchName', checked: true },
624
+ {
625
+ name: 'Generate PR descriptions from changes',
626
+ value: 'prDescription',
627
+ checked: true,
628
+ },
629
+ ],
630
+ },
631
+ ]);
632
+ aiBranchName = step3b.aiFeatures.includes('branchName');
633
+ aiPrDescription = step3b.aiFeatures.includes('prDescription');
634
+ }
635
+ }
636
+ else {
637
+ console.log(colors.dim('No AI tools detected. Skipping AI configuration.'));
638
+ console.log(colors.dim('Install Claude Code, Gemini CLI, or Ollama to enable AI features.'));
639
+ }
640
+ // Step 4: Automation Hooks
641
+ console.log();
642
+ console.log(colors.info('Step 4/4: Automation Hooks'));
643
+ console.log();
644
+ const hookChoices = [];
645
+ if (env.packageManager) {
646
+ hookChoices.push({
647
+ name: `auto-deps: Run "${getInstallCommand(env.packageManager)}" after worktree creation`,
648
+ value: 'autoDeps',
649
+ checked: true,
650
+ });
651
+ }
652
+ const editorCmd = getEditorCommand(env.ide, 'auto');
653
+ if (editorCmd) {
654
+ const editorName = editorCmd.startsWith('cursor') ? 'Cursor' : 'VS Code';
655
+ hookChoices.push({
656
+ name: `open-editor: Open worktree in ${editorName}`,
657
+ value: 'openEditor',
658
+ checked: false,
659
+ });
660
+ }
661
+ let autoDeps = false;
662
+ let openEditor = false;
663
+ let preferredEditor = 'auto';
664
+ if (hookChoices.length > 0) {
665
+ const step4 = await inquirer.prompt([
666
+ {
667
+ type: 'checkbox',
668
+ name: 'hooks',
669
+ message: 'Install automation hooks?',
670
+ choices: hookChoices,
671
+ },
672
+ ]);
673
+ autoDeps = step4.hooks.includes('autoDeps');
674
+ openEditor = step4.hooks.includes('openEditor');
675
+ if (openEditor && env.ide.cursor && env.ide.vscode) {
676
+ const { editorChoice } = await inquirer.prompt([
677
+ {
678
+ type: 'list',
679
+ name: 'editorChoice',
680
+ message: 'Preferred editor?',
681
+ choices: [
682
+ { name: 'VS Code', value: 'vscode' },
683
+ { name: 'Cursor', value: 'cursor' },
684
+ { name: 'Auto-detect', value: 'auto' },
685
+ ],
686
+ },
687
+ ]);
688
+ preferredEditor = editorChoice;
689
+ }
690
+ else if (env.ide.cursor) {
691
+ preferredEditor = 'cursor';
692
+ }
693
+ else {
694
+ preferredEditor = 'vscode';
695
+ }
696
+ }
697
+ else {
698
+ console.log(colors.dim('No automation hooks available for your environment.'));
699
+ }
700
+ // Step 5: Advanced Configuration (optional)
701
+ console.log();
702
+ const { configureAdvanced } = await inquirer.prompt([
703
+ {
704
+ type: 'confirm',
705
+ name: 'configureAdvanced',
706
+ message: 'Configure advanced settings (plugins, generators, integrations)?',
707
+ default: false,
708
+ },
709
+ ]);
710
+ let plugins = [];
711
+ const generators = {};
712
+ const integrations = {};
713
+ if (configureAdvanced) {
714
+ console.log();
715
+ console.log(colors.info('Step 5: Advanced Configuration'));
716
+ console.log();
717
+ // Plugins
718
+ const { addPlugins } = await inquirer.prompt([
719
+ {
720
+ type: 'confirm',
721
+ name: 'addPlugins',
722
+ message: 'Add plugins (npm packages or local scripts)?',
723
+ default: false,
724
+ },
725
+ ]);
726
+ if (addPlugins) {
727
+ const { pluginList } = await inquirer.prompt([
728
+ {
729
+ type: 'input',
730
+ name: 'pluginList',
731
+ message: 'Enter plugin names/paths (comma-separated):',
732
+ default: '',
733
+ },
734
+ ]);
735
+ plugins = pluginList
736
+ .split(',')
737
+ .map((p) => p.trim())
738
+ .filter((p) => p.length > 0);
739
+ }
740
+ // Custom generators
741
+ const { useGenerators } = await inquirer.prompt([
742
+ {
743
+ type: 'confirm',
744
+ name: 'useGenerators',
745
+ message: 'Use custom generator scripts (instead of built-in AI)?',
746
+ default: false,
747
+ },
748
+ ]);
749
+ if (useGenerators) {
750
+ const genPrompts = await inquirer.prompt([
751
+ {
752
+ type: 'input',
753
+ name: 'branchNameGen',
754
+ message: 'Branch name generator script path (leave empty to skip):',
755
+ default: '',
756
+ },
757
+ {
758
+ type: 'input',
759
+ name: 'prTitleGen',
760
+ message: 'PR title generator script path (leave empty to skip):',
761
+ default: '',
762
+ },
763
+ {
764
+ type: 'input',
765
+ name: 'prDescGen',
766
+ message: 'PR description generator script path (leave empty to skip):',
767
+ default: '',
768
+ },
769
+ {
770
+ type: 'input',
771
+ name: 'commitMsgGen',
772
+ message: 'Commit message generator script path (leave empty to skip):',
773
+ default: '',
774
+ },
775
+ ]);
776
+ if (genPrompts.branchNameGen)
777
+ generators.branchName = genPrompts.branchNameGen;
778
+ if (genPrompts.prTitleGen)
779
+ generators.prTitle = genPrompts.prTitleGen;
780
+ if (genPrompts.prDescGen)
781
+ generators.prDescription = genPrompts.prDescGen;
782
+ if (genPrompts.commitMsgGen)
783
+ generators.commitMessage = genPrompts.commitMsgGen;
784
+ }
785
+ // Integrations
786
+ const { integrationsToAdd } = await inquirer.prompt([
787
+ {
788
+ type: 'checkbox',
789
+ name: 'integrationsToAdd',
790
+ message: 'Configure integrations?',
791
+ choices: [
792
+ { name: 'Linear (issue tracker)', value: 'linear' },
793
+ { name: 'Jira (issue tracker)', value: 'jira' },
794
+ { name: 'Slack (notifications)', value: 'slack' },
795
+ ],
796
+ },
797
+ ]);
798
+ if (integrationsToAdd.includes('linear')) {
799
+ const linearConfig = await inquirer.prompt([
800
+ {
801
+ type: 'input',
802
+ name: 'teamId',
803
+ message: 'Linear team ID:',
804
+ default: '',
805
+ },
806
+ {
807
+ type: 'input',
808
+ name: 'apiKeyEnv',
809
+ message: 'Environment variable for Linear API key:',
810
+ default: 'LINEAR_API_KEY',
811
+ },
812
+ ]);
813
+ integrations.linear = {
814
+ teamId: linearConfig.teamId || undefined,
815
+ apiKeyEnv: linearConfig.apiKeyEnv || undefined,
816
+ };
817
+ }
818
+ if (integrationsToAdd.includes('jira')) {
819
+ const jiraConfig = await inquirer.prompt([
820
+ {
821
+ type: 'input',
822
+ name: 'projectKey',
823
+ message: 'Jira project key (e.g., PROJ):',
824
+ default: '',
825
+ },
826
+ {
827
+ type: 'input',
828
+ name: 'baseUrl',
829
+ message: 'Jira base URL:',
830
+ default: '',
831
+ },
832
+ {
833
+ type: 'input',
834
+ name: 'apiTokenEnv',
835
+ message: 'Environment variable for Jira API token:',
836
+ default: 'JIRA_API_TOKEN',
837
+ },
838
+ ]);
839
+ integrations.jira = {
840
+ projectKey: jiraConfig.projectKey || undefined,
841
+ baseUrl: jiraConfig.baseUrl || undefined,
842
+ apiTokenEnv: jiraConfig.apiTokenEnv || undefined,
843
+ };
844
+ }
845
+ if (integrationsToAdd.includes('slack')) {
846
+ const slackConfig = await inquirer.prompt([
847
+ {
848
+ type: 'input',
849
+ name: 'webhookUrl',
850
+ message: 'Slack webhook URL (or env var name):',
851
+ default: 'SLACK_WEBHOOK_URL',
852
+ },
853
+ {
854
+ type: 'input',
855
+ name: 'channel',
856
+ message: 'Default Slack channel:',
857
+ default: '',
858
+ },
859
+ ]);
860
+ integrations.slack = {
861
+ webhookUrl: slackConfig.webhookUrl || undefined,
862
+ channel: slackConfig.channel || undefined,
863
+ };
864
+ }
865
+ }
866
+ return {
867
+ baseBranch,
868
+ draftPr: step1.draftPr,
869
+ worktreeLocation: step2.worktreeLocation,
870
+ worktreePattern: step2.worktreePattern,
871
+ worktreeParent,
872
+ branchPrefix: step1.branchPrefix,
873
+ preferredEditor,
874
+ aiEnabled,
875
+ aiProvider,
876
+ aiBranchName,
877
+ aiPrDescription,
878
+ hooks: {
879
+ autoDeps,
880
+ openEditor,
881
+ },
882
+ plugins,
883
+ generators,
884
+ integrations,
885
+ };
886
+ }
887
+ function buildConfigFromState(state, env) {
888
+ const config = {};
889
+ // Only include non-default values
890
+ const defaults = getDefaultConfig();
891
+ if (state.baseBranch !== defaults.baseBranch) {
892
+ config.baseBranch = state.baseBranch;
893
+ }
894
+ if (state.draftPr !== defaults.draftPr) {
895
+ config.draftPr = state.draftPr;
896
+ }
897
+ if (state.branchPrefix !== defaults.branchPrefix) {
898
+ config.branchPrefix = state.branchPrefix;
899
+ }
900
+ if (state.worktreePattern !== defaults.worktreePattern) {
901
+ config.worktreePattern = state.worktreePattern;
902
+ }
903
+ if (state.worktreeParent !== defaults.worktreeParent) {
904
+ config.worktreeParent = state.worktreeParent;
905
+ }
906
+ if (state.preferredEditor !== defaults.preferredEditor) {
907
+ config.preferredEditor = state.preferredEditor;
908
+ }
909
+ // AI configuration
910
+ if (state.aiEnabled) {
911
+ config.ai = {
912
+ provider: state.aiProvider,
913
+ branchName: state.aiBranchName,
914
+ prDescription: state.aiPrDescription,
915
+ };
916
+ }
917
+ // Hooks
918
+ const hooks = [];
919
+ if (state.hooks.autoDeps && env.packageManager) {
920
+ hooks.push(getInstallCommand(env.packageManager));
921
+ }
922
+ if (state.hooks.openEditor) {
923
+ const editorCmd = getEditorCommand(env.ide, state.preferredEditor);
924
+ if (editorCmd) {
925
+ hooks.push(editorCmd);
926
+ }
927
+ }
928
+ if (hooks.length > 0) {
929
+ config.hooks = {
930
+ 'post-worktree': hooks.length === 1 ? hooks[0] : hooks,
931
+ };
932
+ }
933
+ // Phase 8: Advanced configuration
934
+ // Plugins
935
+ if (state.plugins && state.plugins.length > 0) {
936
+ config.plugins = state.plugins;
937
+ }
938
+ // Custom generators
939
+ if (state.generators && Object.keys(state.generators).length > 0) {
940
+ config.generators = state.generators;
941
+ }
942
+ // Integrations
943
+ if (state.integrations && Object.keys(state.integrations).length > 0) {
944
+ config.integrations = state.integrations;
945
+ }
946
+ return config;
947
+ }
948
+ //# sourceMappingURL=wtconfig.js.map