@desplega.ai/wts 0.1.4 → 0.2.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.
- package/dist/index.js +134 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1877,7 +1877,7 @@ var {
|
|
|
1877
1877
|
// package.json
|
|
1878
1878
|
var package_default = {
|
|
1879
1879
|
name: "@desplega.ai/wts",
|
|
1880
|
-
version: "0.
|
|
1880
|
+
version: "0.2.0",
|
|
1881
1881
|
description: "Git worktree manager with tmux integration",
|
|
1882
1882
|
type: "module",
|
|
1883
1883
|
bin: {
|
|
@@ -2098,6 +2098,10 @@ async function removeWorktree(path, force = false, cwd) {
|
|
|
2098
2098
|
async function pruneWorktrees(cwd) {
|
|
2099
2099
|
await Bun.$`git worktree prune`.cwd(cwd ?? process.cwd());
|
|
2100
2100
|
}
|
|
2101
|
+
async function deleteBranch(branch, force = false, cwd) {
|
|
2102
|
+
const flag = force ? "-D" : "-d";
|
|
2103
|
+
await Bun.$`git branch ${flag} ${branch}`.cwd(cwd ?? process.cwd());
|
|
2104
|
+
}
|
|
2101
2105
|
function generateWorktreePath(baseDir, alias) {
|
|
2102
2106
|
const dirName = generateWorktreeDirName(alias);
|
|
2103
2107
|
return join(baseDir, dirName);
|
|
@@ -2610,7 +2614,8 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
2610
2614
|
var source_default = chalk;
|
|
2611
2615
|
|
|
2612
2616
|
// src/config/local.ts
|
|
2613
|
-
import {
|
|
2617
|
+
import { homedir as homedir3 } from "node:os";
|
|
2618
|
+
import { isAbsolute, join as join3 } from "node:path";
|
|
2614
2619
|
|
|
2615
2620
|
// src/config/global.ts
|
|
2616
2621
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -2620,7 +2625,7 @@ import { join as join2 } from "node:path";
|
|
|
2620
2625
|
var DEFAULT_GLOBAL_CONFIG = {
|
|
2621
2626
|
projects: {},
|
|
2622
2627
|
defaults: {
|
|
2623
|
-
worktreeDir: "
|
|
2628
|
+
worktreeDir: "~/.worktrees",
|
|
2624
2629
|
tmuxWindowTemplate: "{project}-{alias}",
|
|
2625
2630
|
autoTmux: false,
|
|
2626
2631
|
autoClaude: false
|
|
@@ -2704,7 +2709,14 @@ async function resolveConfig(gitRoot) {
|
|
|
2704
2709
|
};
|
|
2705
2710
|
}
|
|
2706
2711
|
function getWorktreeBaseDir(config) {
|
|
2707
|
-
|
|
2712
|
+
let baseDir = config.worktreeDir;
|
|
2713
|
+
if (baseDir.startsWith("~")) {
|
|
2714
|
+
baseDir = join3(homedir3(), baseDir.slice(1));
|
|
2715
|
+
}
|
|
2716
|
+
if (isAbsolute(baseDir)) {
|
|
2717
|
+
return join3(baseDir, config.projectName);
|
|
2718
|
+
}
|
|
2719
|
+
return join3(config.gitRoot, baseDir, config.projectName);
|
|
2708
2720
|
}
|
|
2709
2721
|
|
|
2710
2722
|
// src/utils/prompts.ts
|
|
@@ -2799,7 +2811,7 @@ async function categorizeWorktrees(worktrees, gitRoot, options) {
|
|
|
2799
2811
|
}
|
|
2800
2812
|
return { merged, unmerged, stale, active };
|
|
2801
2813
|
}
|
|
2802
|
-
var cleanupCommand = new Command2("cleanup").description("Remove merged or stale worktrees").option("--dry-run", "Show what would be removed without removing").option("-f, --force", "Force removal without confirmation").option("--older-than <days>", "Include worktrees older than N days").option("--unmerged", "Include all unmerged worktrees").action(async (options) => {
|
|
2814
|
+
var cleanupCommand = new Command2("cleanup").description("Remove merged or stale worktrees").option("--dry-run", "Show what would be removed without removing").option("-f, --force", "Force removal without confirmation").option("--older-than <days>", "Include worktrees older than N days").option("--unmerged", "Include all unmerged worktrees").option("--delete-branches", "Also delete the associated branches").action(async (options) => {
|
|
2803
2815
|
const gitRoot = await getGitRoot();
|
|
2804
2816
|
if (!gitRoot) {
|
|
2805
2817
|
console.error(source_default.red("Error: Not in a git repository"));
|
|
@@ -2851,24 +2863,38 @@ var cleanupCommand = new Command2("cleanup").description("Remove merged or stale
|
|
|
2851
2863
|
}
|
|
2852
2864
|
console.log();
|
|
2853
2865
|
}
|
|
2866
|
+
if (options.deleteBranches) {
|
|
2867
|
+
console.log(source_default.dim("(branches will also be deleted)"));
|
|
2868
|
+
}
|
|
2854
2869
|
if (options.dryRun) {
|
|
2855
2870
|
console.log(source_default.dim("(dry run - no changes made)"));
|
|
2856
2871
|
return;
|
|
2857
2872
|
}
|
|
2858
2873
|
if (!options.force) {
|
|
2859
|
-
const
|
|
2874
|
+
const message = options.deleteBranches ? `Remove ${toRemove.length} worktree(s) and their branches?` : `Remove ${toRemove.length} worktree(s)?`;
|
|
2875
|
+
const shouldProceed = await confirm(message, false);
|
|
2860
2876
|
if (!shouldProceed) {
|
|
2861
2877
|
console.log(source_default.dim("Cancelled"));
|
|
2862
2878
|
return;
|
|
2863
2879
|
}
|
|
2864
2880
|
}
|
|
2865
|
-
let
|
|
2881
|
+
let removedWorktrees = 0;
|
|
2882
|
+
let removedBranches = 0;
|
|
2866
2883
|
let failed = 0;
|
|
2867
2884
|
for (const wt of toRemove) {
|
|
2868
2885
|
try {
|
|
2869
2886
|
console.log(source_default.dim(`Removing ${wt.alias ?? wt.branch}...`));
|
|
2870
2887
|
await removeWorktree(wt.path, true, gitRoot);
|
|
2871
|
-
|
|
2888
|
+
removedWorktrees++;
|
|
2889
|
+
if (options.deleteBranches && wt.branch && wt.branch !== "detached") {
|
|
2890
|
+
try {
|
|
2891
|
+
await deleteBranch(wt.branch, true, gitRoot);
|
|
2892
|
+
removedBranches++;
|
|
2893
|
+
} catch (error) {
|
|
2894
|
+
console.error(source_default.yellow(` Warning: Could not delete branch ${wt.branch}:`));
|
|
2895
|
+
console.error(source_default.dim(` ${error instanceof Error ? error.message : String(error)}`));
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2872
2898
|
} catch (error) {
|
|
2873
2899
|
console.error(source_default.red(` Failed to remove ${wt.alias ?? wt.branch}:`));
|
|
2874
2900
|
console.error(source_default.dim(` ${error instanceof Error ? error.message : String(error)}`));
|
|
@@ -2876,8 +2902,11 @@ var cleanupCommand = new Command2("cleanup").description("Remove merged or stale
|
|
|
2876
2902
|
}
|
|
2877
2903
|
}
|
|
2878
2904
|
console.log();
|
|
2879
|
-
if (
|
|
2880
|
-
console.log(source_default.green(`Removed ${
|
|
2905
|
+
if (removedWorktrees > 0) {
|
|
2906
|
+
console.log(source_default.green(`Removed ${removedWorktrees} worktree(s)`));
|
|
2907
|
+
}
|
|
2908
|
+
if (removedBranches > 0) {
|
|
2909
|
+
console.log(source_default.green(`Deleted ${removedBranches} branch(es)`));
|
|
2881
2910
|
}
|
|
2882
2911
|
if (failed > 0) {
|
|
2883
2912
|
console.log(source_default.red(`Failed to remove ${failed} worktree(s)`));
|
|
@@ -3213,8 +3242,8 @@ Next steps:`));
|
|
|
3213
3242
|
});
|
|
3214
3243
|
async function promptForConfig(existing) {
|
|
3215
3244
|
const config = {};
|
|
3216
|
-
const worktreeDir = await prompt("Worktree directory", existing?.worktreeDir ?? "
|
|
3217
|
-
if (worktreeDir !== "
|
|
3245
|
+
const worktreeDir = await prompt("Worktree directory", existing?.worktreeDir ?? "~/.worktrees");
|
|
3246
|
+
if (worktreeDir !== "~/.worktrees") {
|
|
3218
3247
|
config.worktreeDir = worktreeDir;
|
|
3219
3248
|
}
|
|
3220
3249
|
const autoTmux = await confirm("Auto-open tmux window on create?", existing?.autoTmux ?? false);
|
|
@@ -3292,7 +3321,7 @@ async function listAllProjects(jsonOutput) {
|
|
|
3292
3321
|
}
|
|
3293
3322
|
function printWorktreeTable(worktrees, projectName) {
|
|
3294
3323
|
console.log(source_default.bold.blue(`${projectName}`));
|
|
3295
|
-
console.log(source_default.dim("
|
|
3324
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
3296
3325
|
if (worktrees.length === 0) {
|
|
3297
3326
|
console.log(source_default.dim(" No worktrees"));
|
|
3298
3327
|
return;
|
|
@@ -3307,6 +3336,97 @@ function printWorktreeTable(worktrees, projectName) {
|
|
|
3307
3336
|
}
|
|
3308
3337
|
}
|
|
3309
3338
|
|
|
3339
|
+
// src/commands/merge.ts
|
|
3340
|
+
var mergeCommand = new Command2("merge").description("Merge a worktree branch into main").argument("[alias]", "Alias of the worktree to merge").option("--no-cleanup", "Skip cleanup prompt").option("-f, --force", "Skip confirmations (except cleanup)").action(async (alias, options) => {
|
|
3341
|
+
const gitRoot = await getGitRoot();
|
|
3342
|
+
if (!gitRoot) {
|
|
3343
|
+
console.error(source_default.red("Error: Not in a git repository"));
|
|
3344
|
+
process.exit(1);
|
|
3345
|
+
}
|
|
3346
|
+
const config = await resolveConfig(gitRoot);
|
|
3347
|
+
const worktrees = await listWorktrees(gitRoot, config.projectName);
|
|
3348
|
+
let worktree;
|
|
3349
|
+
if (alias) {
|
|
3350
|
+
worktree = await findWorktreeByAlias(alias, gitRoot);
|
|
3351
|
+
if (!worktree) {
|
|
3352
|
+
console.error(source_default.red(`Error: No worktree found with alias "${alias}"`));
|
|
3353
|
+
process.exit(1);
|
|
3354
|
+
}
|
|
3355
|
+
} else {
|
|
3356
|
+
const nonMain = worktrees.filter((wt) => !wt.isMain);
|
|
3357
|
+
if (nonMain.length === 0) {
|
|
3358
|
+
console.error(source_default.yellow("No worktrees to merge"));
|
|
3359
|
+
process.exit(1);
|
|
3360
|
+
}
|
|
3361
|
+
worktree = await selectWorktree(worktrees, { excludeMain: true });
|
|
3362
|
+
if (!worktree) {
|
|
3363
|
+
console.log(source_default.dim("Cancelled"));
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
if (worktree.isMain) {
|
|
3368
|
+
console.error(source_default.red("Error: Cannot merge the main worktree"));
|
|
3369
|
+
process.exit(1);
|
|
3370
|
+
}
|
|
3371
|
+
const defaultBranch = await getDefaultBranch(gitRoot);
|
|
3372
|
+
const branchToMerge = worktree.branch;
|
|
3373
|
+
console.log(source_default.bold(`
|
|
3374
|
+
Merging ${source_default.cyan(branchToMerge)} into ${source_default.cyan(defaultBranch)}
|
|
3375
|
+
`));
|
|
3376
|
+
if (!options.force) {
|
|
3377
|
+
const proceed = await confirm(`Switch to ${defaultBranch}?`, true);
|
|
3378
|
+
if (!proceed) {
|
|
3379
|
+
console.log(source_default.dim("Cancelled"));
|
|
3380
|
+
return;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
console.log(source_default.dim(`Switching to ${defaultBranch}...`));
|
|
3384
|
+
await Bun.$`git checkout ${defaultBranch}`.cwd(gitRoot);
|
|
3385
|
+
if (!options.force) {
|
|
3386
|
+
const proceed = await confirm(`Pull latest ${defaultBranch}?`, true);
|
|
3387
|
+
if (!proceed) {
|
|
3388
|
+
console.log(source_default.dim("Cancelled"));
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
console.log(source_default.dim(`Pulling latest...`));
|
|
3393
|
+
await Bun.$`git pull`.cwd(gitRoot);
|
|
3394
|
+
if (!options.force) {
|
|
3395
|
+
const proceed = await confirm(`Merge ${branchToMerge}?`, true);
|
|
3396
|
+
if (!proceed) {
|
|
3397
|
+
console.log(source_default.dim("Cancelled"));
|
|
3398
|
+
return;
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
console.log(source_default.dim(`Merging ${branchToMerge}...`));
|
|
3402
|
+
await Bun.$`git merge ${branchToMerge}`.cwd(gitRoot);
|
|
3403
|
+
if (!options.force) {
|
|
3404
|
+
const proceed = await confirm(`Push to origin?`, true);
|
|
3405
|
+
if (!proceed) {
|
|
3406
|
+
console.log(source_default.dim("Skipped push"));
|
|
3407
|
+
} else {
|
|
3408
|
+
console.log(source_default.dim(`Pushing...`));
|
|
3409
|
+
await Bun.$`git push`.cwd(gitRoot);
|
|
3410
|
+
}
|
|
3411
|
+
} else {
|
|
3412
|
+
console.log(source_default.dim(`Pushing...`));
|
|
3413
|
+
await Bun.$`git push`.cwd(gitRoot);
|
|
3414
|
+
}
|
|
3415
|
+
console.log(source_default.green(`
|
|
3416
|
+
✓ Merged ${branchToMerge} into ${defaultBranch}`));
|
|
3417
|
+
if (options.cleanup !== false) {
|
|
3418
|
+
const cleanup = await confirm(`
|
|
3419
|
+
Clean up worktree and branch?`, false);
|
|
3420
|
+
if (cleanup) {
|
|
3421
|
+
console.log(source_default.dim(`Removing worktree...`));
|
|
3422
|
+
await removeWorktree(worktree.path, true, gitRoot);
|
|
3423
|
+
console.log(source_default.dim(`Deleting branch ${branchToMerge}...`));
|
|
3424
|
+
await deleteBranch(branchToMerge, true, gitRoot);
|
|
3425
|
+
console.log(source_default.green(`✓ Cleaned up`));
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
});
|
|
3429
|
+
|
|
3310
3430
|
// src/commands/pr.ts
|
|
3311
3431
|
async function isGhAvailable() {
|
|
3312
3432
|
try {
|
|
@@ -3585,6 +3705,7 @@ program3.addCommand(initCommand);
|
|
|
3585
3705
|
program3.addCommand(listCommand);
|
|
3586
3706
|
program3.addCommand(createCommand3);
|
|
3587
3707
|
program3.addCommand(deleteCommand);
|
|
3708
|
+
program3.addCommand(mergeCommand);
|
|
3588
3709
|
program3.addCommand(cdCommand);
|
|
3589
3710
|
program3.addCommand(switchCommand);
|
|
3590
3711
|
program3.addCommand(prCommand);
|