@camaradesuk/git-worktree-tools 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -3
- package/dist/api/clean.d.ts +65 -0
- package/dist/api/clean.d.ts.map +1 -0
- package/dist/api/clean.js +209 -0
- package/dist/api/clean.js.map +1 -0
- package/dist/api/create.d.ts +88 -0
- package/dist/api/create.d.ts.map +1 -0
- package/dist/api/create.js +373 -0
- package/dist/api/create.js.map +1 -0
- package/dist/api/index.d.ts +15 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +19 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/list.d.ts +74 -0
- package/dist/api/list.d.ts.map +1 -0
- package/dist/api/list.js +80 -0
- package/dist/api/list.js.map +1 -0
- package/dist/api/state.d.ts +43 -0
- package/dist/api/state.d.ts.map +1 -0
- package/dist/api/state.js +70 -0
- package/dist/api/state.js.map +1 -0
- package/dist/cli/cleanpr.js +171 -28
- package/dist/cli/cleanpr.js.map +1 -1
- package/dist/cli/cleanpr.test.js +459 -7
- package/dist/cli/cleanpr.test.js.map +1 -1
- package/dist/cli/newpr.js +189 -28
- package/dist/cli/newpr.js.map +1 -1
- package/dist/cli/newpr.test.js +349 -0
- package/dist/cli/newpr.test.js.map +1 -1
- package/dist/cli/wtconfig.d.ts +14 -0
- package/dist/cli/wtconfig.d.ts.map +1 -0
- package/dist/cli/wtconfig.js +948 -0
- package/dist/cli/wtconfig.js.map +1 -0
- package/dist/cli/wtconfig.test.d.ts +5 -0
- package/dist/cli/wtconfig.test.d.ts.map +1 -0
- package/dist/cli/wtconfig.test.js +1281 -0
- package/dist/cli/wtconfig.test.js.map +1 -0
- package/dist/cli/wtlink.js +5 -0
- package/dist/cli/wtlink.js.map +1 -1
- package/dist/cli/wtstate.d.ts +8 -0
- package/dist/cli/wtstate.d.ts.map +1 -0
- package/dist/cli/wtstate.js +83 -0
- package/dist/cli/wtstate.js.map +1 -0
- package/dist/cli/wtstate.test.d.ts +5 -0
- package/dist/cli/wtstate.test.d.ts.map +1 -0
- package/dist/cli/wtstate.test.js +193 -0
- package/dist/cli/wtstate.test.js.map +1 -0
- package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts +2 -0
- package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts.map +1 -0
- package/dist/e2e/cleanpr/cleanpr.e2e.test.js +326 -0
- package/dist/e2e/cleanpr/cleanpr.e2e.test.js.map +1 -0
- package/dist/e2e/helpers/cli-runner.d.ts +103 -0
- package/dist/e2e/helpers/cli-runner.d.ts.map +1 -0
- package/dist/e2e/helpers/cli-runner.js +200 -0
- package/dist/e2e/helpers/cli-runner.js.map +1 -0
- package/dist/e2e/helpers/gh-mock.d.ts +87 -0
- package/dist/e2e/helpers/gh-mock.d.ts.map +1 -0
- package/dist/e2e/helpers/gh-mock.js +384 -0
- package/dist/e2e/helpers/gh-mock.js.map +1 -0
- package/dist/e2e/helpers/index.d.ts +12 -0
- package/dist/e2e/helpers/index.d.ts.map +1 -0
- package/dist/e2e/helpers/index.js +16 -0
- package/dist/e2e/helpers/index.js.map +1 -0
- package/dist/e2e/helpers/pty-wrapper.d.ts +118 -0
- package/dist/e2e/helpers/pty-wrapper.d.ts.map +1 -0
- package/dist/e2e/helpers/pty-wrapper.js +276 -0
- package/dist/e2e/helpers/pty-wrapper.js.map +1 -0
- package/dist/e2e/helpers/scenario-harness.d.ts +55 -0
- package/dist/e2e/helpers/scenario-harness.d.ts.map +1 -0
- package/dist/e2e/helpers/scenario-harness.js +360 -0
- package/dist/e2e/helpers/scenario-harness.js.map +1 -0
- package/dist/e2e/helpers/test-context.d.ts +120 -0
- package/dist/e2e/helpers/test-context.d.ts.map +1 -0
- package/dist/e2e/helpers/test-context.js +263 -0
- package/dist/e2e/helpers/test-context.js.map +1 -0
- package/dist/e2e/lswt/lswt.e2e.test.d.ts +2 -0
- package/dist/e2e/lswt/lswt.e2e.test.d.ts.map +1 -0
- package/dist/e2e/lswt/lswt.e2e.test.js +328 -0
- package/dist/e2e/lswt/lswt.e2e.test.js.map +1 -0
- package/dist/e2e/newpr/newpr.e2e.test.d.ts +2 -0
- package/dist/e2e/newpr/newpr.e2e.test.d.ts.map +1 -0
- package/dist/e2e/newpr/newpr.e2e.test.js +286 -0
- package/dist/e2e/newpr/newpr.e2e.test.js.map +1 -0
- package/dist/e2e/newpr/scenarios.e2e.test.d.ts +2 -0
- package/dist/e2e/newpr/scenarios.e2e.test.d.ts.map +1 -0
- package/dist/e2e/newpr/scenarios.e2e.test.js +426 -0
- package/dist/e2e/newpr/scenarios.e2e.test.js.map +1 -0
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts +2 -0
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts.map +1 -0
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.js +298 -0
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.js.map +1 -0
- package/dist/e2e/wtlink/wtlink.e2e.test.d.ts +2 -0
- package/dist/e2e/wtlink/wtlink.e2e.test.d.ts.map +1 -0
- package/dist/e2e/wtlink/wtlink.e2e.test.js +364 -0
- package/dist/e2e/wtlink/wtlink.e2e.test.js.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/ai/base-provider.d.ts +58 -0
- package/dist/lib/ai/base-provider.d.ts.map +1 -0
- package/dist/lib/ai/base-provider.js +246 -0
- package/dist/lib/ai/base-provider.js.map +1 -0
- package/dist/lib/ai/base-provider.test.d.ts +7 -0
- package/dist/lib/ai/base-provider.test.d.ts.map +1 -0
- package/dist/lib/ai/base-provider.test.js +320 -0
- package/dist/lib/ai/base-provider.test.js.map +1 -0
- package/dist/lib/ai/cli-provider.d.ts +87 -0
- package/dist/lib/ai/cli-provider.d.ts.map +1 -0
- package/dist/lib/ai/cli-provider.js +280 -0
- package/dist/lib/ai/cli-provider.js.map +1 -0
- package/dist/lib/ai/cli-provider.test.d.ts +5 -0
- package/dist/lib/ai/cli-provider.test.d.ts.map +1 -0
- package/dist/lib/ai/cli-provider.test.js +462 -0
- package/dist/lib/ai/cli-provider.test.js.map +1 -0
- package/dist/lib/ai/fallback-provider.d.ts +20 -0
- package/dist/lib/ai/fallback-provider.d.ts.map +1 -0
- package/dist/lib/ai/fallback-provider.js +125 -0
- package/dist/lib/ai/fallback-provider.js.map +1 -0
- package/dist/lib/ai/fallback-provider.test.d.ts +7 -0
- package/dist/lib/ai/fallback-provider.test.d.ts.map +1 -0
- package/dist/lib/ai/fallback-provider.test.js +165 -0
- package/dist/lib/ai/fallback-provider.test.js.map +1 -0
- package/dist/lib/ai/generation-service.d.ts +44 -0
- package/dist/lib/ai/generation-service.d.ts.map +1 -0
- package/dist/lib/ai/generation-service.js +107 -0
- package/dist/lib/ai/generation-service.js.map +1 -0
- package/dist/lib/ai/generation-service.test.d.ts +7 -0
- package/dist/lib/ai/generation-service.test.d.ts.map +1 -0
- package/dist/lib/ai/generation-service.test.js +213 -0
- package/dist/lib/ai/generation-service.test.js.map +1 -0
- package/dist/lib/ai/index.d.ts +19 -0
- package/dist/lib/ai/index.d.ts.map +1 -0
- package/dist/lib/ai/index.js +22 -0
- package/dist/lib/ai/index.js.map +1 -0
- package/dist/lib/ai/provider-manager.d.ts +109 -0
- package/dist/lib/ai/provider-manager.d.ts.map +1 -0
- package/dist/lib/ai/provider-manager.js +270 -0
- package/dist/lib/ai/provider-manager.js.map +1 -0
- package/dist/lib/ai/provider-manager.test.d.ts +5 -0
- package/dist/lib/ai/provider-manager.test.d.ts.map +1 -0
- package/dist/lib/ai/provider-manager.test.js +312 -0
- package/dist/lib/ai/provider-manager.test.js.map +1 -0
- package/dist/lib/ai/types.d.ts +166 -0
- package/dist/lib/ai/types.d.ts.map +1 -0
- package/dist/lib/ai/types.js +19 -0
- package/dist/lib/ai/types.js.map +1 -0
- package/dist/lib/cleanpr/args.d.ts.map +1 -1
- package/dist/lib/cleanpr/args.js +18 -0
- package/dist/lib/cleanpr/args.js.map +1 -1
- package/dist/lib/cleanpr/args.test.js +88 -11
- package/dist/lib/cleanpr/args.test.js.map +1 -1
- package/dist/lib/cleanpr/cleanup.d.ts +2 -0
- package/dist/lib/cleanpr/cleanup.d.ts.map +1 -1
- package/dist/lib/cleanpr/cleanup.js +30 -2
- package/dist/lib/cleanpr/cleanup.js.map +1 -1
- package/dist/lib/cleanpr/cleanup.test.js +37 -5
- package/dist/lib/cleanpr/cleanup.test.js.map +1 -1
- package/dist/lib/cleanpr/types.d.ts +10 -0
- package/dist/lib/cleanpr/types.d.ts.map +1 -1
- package/dist/lib/cleanpr/worktree-info.test.js +72 -1
- package/dist/lib/cleanpr/worktree-info.test.js.map +1 -1
- package/dist/lib/config.d.ts +170 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +129 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.js +406 -2
- package/dist/lib/config.test.js.map +1 -1
- package/dist/lib/hooks/executor.d.ts +35 -0
- package/dist/lib/hooks/executor.d.ts.map +1 -0
- package/dist/lib/hooks/executor.js +401 -0
- package/dist/lib/hooks/executor.js.map +1 -0
- package/dist/lib/hooks/executor.test.d.ts +5 -0
- package/dist/lib/hooks/executor.test.d.ts.map +1 -0
- package/dist/lib/hooks/executor.test.js +648 -0
- package/dist/lib/hooks/executor.test.js.map +1 -0
- package/dist/lib/hooks/index.d.ts +25 -0
- package/dist/lib/hooks/index.d.ts.map +1 -0
- package/dist/lib/hooks/index.js +26 -0
- package/dist/lib/hooks/index.js.map +1 -0
- package/dist/lib/hooks/templates.d.ts +74 -0
- package/dist/lib/hooks/templates.d.ts.map +1 -0
- package/dist/lib/hooks/templates.js +270 -0
- package/dist/lib/hooks/templates.js.map +1 -0
- package/dist/lib/hooks/templates.test.d.ts +5 -0
- package/dist/lib/hooks/templates.test.d.ts.map +1 -0
- package/dist/lib/hooks/templates.test.js +163 -0
- package/dist/lib/hooks/templates.test.js.map +1 -0
- package/dist/lib/hooks/types.d.ts +161 -0
- package/dist/lib/hooks/types.d.ts.map +1 -0
- package/dist/lib/hooks/types.js +73 -0
- package/dist/lib/hooks/types.js.map +1 -0
- package/dist/lib/hooks/types.test.d.ts +5 -0
- package/dist/lib/hooks/types.test.d.ts.map +1 -0
- package/dist/lib/hooks/types.test.js +132 -0
- package/dist/lib/hooks/types.test.js.map +1 -0
- package/dist/lib/json-output.d.ts +172 -0
- package/dist/lib/json-output.d.ts.map +1 -0
- package/dist/lib/json-output.js +134 -0
- package/dist/lib/json-output.js.map +1 -0
- package/dist/lib/json-output.test.d.ts +5 -0
- package/dist/lib/json-output.test.d.ts.map +1 -0
- package/dist/lib/json-output.test.js +259 -0
- package/dist/lib/json-output.test.js.map +1 -0
- package/dist/lib/lswt/action-executors.test.js +6 -0
- package/dist/lib/lswt/action-executors.test.js.map +1 -1
- package/dist/lib/newpr/action-deps.d.ts +15 -0
- package/dist/lib/newpr/action-deps.d.ts.map +1 -0
- package/dist/lib/newpr/action-deps.js +22 -0
- package/dist/lib/newpr/action-deps.js.map +1 -0
- package/dist/lib/newpr/args.d.ts.map +1 -1
- package/dist/lib/newpr/args.js +56 -0
- package/dist/lib/newpr/args.js.map +1 -1
- package/dist/lib/newpr/hook-runner.d.ts +80 -0
- package/dist/lib/newpr/hook-runner.d.ts.map +1 -0
- package/dist/lib/newpr/hook-runner.js +182 -0
- package/dist/lib/newpr/hook-runner.js.map +1 -0
- package/dist/lib/newpr/hook-runner.test.d.ts +7 -0
- package/dist/lib/newpr/hook-runner.test.d.ts.map +1 -0
- package/dist/lib/newpr/hook-runner.test.js +301 -0
- package/dist/lib/newpr/hook-runner.test.js.map +1 -0
- package/dist/lib/newpr/index.d.ts +3 -0
- package/dist/lib/newpr/index.d.ts.map +1 -1
- package/dist/lib/newpr/index.js +3 -0
- package/dist/lib/newpr/index.js.map +1 -1
- package/dist/lib/newpr/types.d.ts +9 -0
- package/dist/lib/newpr/types.d.ts.map +1 -1
- package/dist/lib/wtconfig/config-manager.d.ts +72 -0
- package/dist/lib/wtconfig/config-manager.d.ts.map +1 -0
- package/dist/lib/wtconfig/config-manager.js +408 -0
- package/dist/lib/wtconfig/config-manager.js.map +1 -0
- package/dist/lib/wtconfig/config-manager.test.d.ts +5 -0
- package/dist/lib/wtconfig/config-manager.test.d.ts.map +1 -0
- package/dist/lib/wtconfig/config-manager.test.js +501 -0
- package/dist/lib/wtconfig/config-manager.test.js.map +1 -0
- package/dist/lib/wtconfig/environment.d.ts +23 -0
- package/dist/lib/wtconfig/environment.d.ts.map +1 -0
- package/dist/lib/wtconfig/environment.js +242 -0
- package/dist/lib/wtconfig/environment.js.map +1 -0
- package/dist/lib/wtconfig/environment.test.d.ts +5 -0
- package/dist/lib/wtconfig/environment.test.d.ts.map +1 -0
- package/dist/lib/wtconfig/environment.test.js +246 -0
- package/dist/lib/wtconfig/environment.test.js.map +1 -0
- package/dist/lib/wtconfig/index.d.ts +7 -0
- package/dist/lib/wtconfig/index.d.ts.map +1 -0
- package/dist/lib/wtconfig/index.js +8 -0
- package/dist/lib/wtconfig/index.js.map +1 -0
- package/dist/lib/wtconfig/types.d.ts +97 -0
- package/dist/lib/wtconfig/types.d.ts.map +1 -0
- package/dist/lib/wtconfig/types.js +5 -0
- package/dist/lib/wtconfig/types.js.map +1 -0
- package/dist/lib/wtstate/analyze.d.ts +13 -0
- package/dist/lib/wtstate/analyze.d.ts.map +1 -0
- package/dist/lib/wtstate/analyze.js +165 -0
- package/dist/lib/wtstate/analyze.js.map +1 -0
- package/dist/lib/wtstate/analyze.test.d.ts +5 -0
- package/dist/lib/wtstate/analyze.test.d.ts.map +1 -0
- package/dist/lib/wtstate/analyze.test.js +282 -0
- package/dist/lib/wtstate/analyze.test.js.map +1 -0
- package/dist/lib/wtstate/args.d.ts +17 -0
- package/dist/lib/wtstate/args.d.ts.map +1 -0
- package/dist/lib/wtstate/args.js +91 -0
- package/dist/lib/wtstate/args.js.map +1 -0
- package/dist/lib/wtstate/args.test.d.ts +5 -0
- package/dist/lib/wtstate/args.test.d.ts.map +1 -0
- package/dist/lib/wtstate/args.test.js +120 -0
- package/dist/lib/wtstate/args.test.js.map +1 -0
- package/dist/lib/wtstate/index.d.ts +7 -0
- package/dist/lib/wtstate/index.d.ts.map +1 -0
- package/dist/lib/wtstate/index.js +8 -0
- package/dist/lib/wtstate/index.js.map +1 -0
- package/dist/lib/wtstate/types.d.ts +64 -0
- package/dist/lib/wtstate/types.d.ts.map +1 -0
- package/dist/lib/wtstate/types.js +5 -0
- package/dist/lib/wtstate/types.js.map +1 -0
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +339 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/server.test.d.ts +9 -0
- package/dist/mcp/server.test.d.ts.map +1 -0
- package/dist/mcp/server.test.js +390 -0
- package/dist/mcp/server.test.js.map +1 -0
- 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
|