workon 3.6.0 → 3.7.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/README.md +125 -0
- package/dist/cli.js +205 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -283,6 +283,13 @@ var init_project = __esm({
|
|
|
283
283
|
}
|
|
284
284
|
return this._path;
|
|
285
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Override the project path with an absolute path.
|
|
288
|
+
* Used when targeting a worktree instead of the main project directory.
|
|
289
|
+
*/
|
|
290
|
+
overridePath(absolutePath) {
|
|
291
|
+
this._path = File.from(absolutePath).absolutify();
|
|
292
|
+
}
|
|
286
293
|
set branch(branch) {
|
|
287
294
|
this._branch = branch;
|
|
288
295
|
}
|
|
@@ -1421,24 +1428,6 @@ var init_tmux = __esm({
|
|
|
1421
1428
|
}
|
|
1422
1429
|
});
|
|
1423
1430
|
|
|
1424
|
-
// src/types/constants.ts
|
|
1425
|
-
var IDE_CHOICES;
|
|
1426
|
-
var init_constants = __esm({
|
|
1427
|
-
"src/types/constants.ts"() {
|
|
1428
|
-
"use strict";
|
|
1429
|
-
IDE_CHOICES = [
|
|
1430
|
-
{ name: "Cursor", value: "cursor" },
|
|
1431
|
-
{ name: "Visual Studio Code", value: "vscode" },
|
|
1432
|
-
{ name: "Visual Studio Code (code)", value: "code" },
|
|
1433
|
-
{ name: "IntelliJ IDEA", value: "idea" },
|
|
1434
|
-
{ name: "Atom", value: "atom" },
|
|
1435
|
-
{ name: "Sublime Text", value: "subl" },
|
|
1436
|
-
{ name: "Vim", value: "vim" },
|
|
1437
|
-
{ name: "Emacs", value: "emacs" }
|
|
1438
|
-
];
|
|
1439
|
-
}
|
|
1440
|
-
});
|
|
1441
|
-
|
|
1442
1431
|
// src/lib/worktree.ts
|
|
1443
1432
|
import { exec as execCallback2 } from "child_process";
|
|
1444
1433
|
import { promisify as promisify2 } from "util";
|
|
@@ -1771,6 +1760,24 @@ var init_worktree = __esm({
|
|
|
1771
1760
|
}
|
|
1772
1761
|
});
|
|
1773
1762
|
|
|
1763
|
+
// src/types/constants.ts
|
|
1764
|
+
var IDE_CHOICES;
|
|
1765
|
+
var init_constants = __esm({
|
|
1766
|
+
"src/types/constants.ts"() {
|
|
1767
|
+
"use strict";
|
|
1768
|
+
IDE_CHOICES = [
|
|
1769
|
+
{ name: "Cursor", value: "cursor" },
|
|
1770
|
+
{ name: "Visual Studio Code", value: "vscode" },
|
|
1771
|
+
{ name: "Visual Studio Code (code)", value: "code" },
|
|
1772
|
+
{ name: "IntelliJ IDEA", value: "idea" },
|
|
1773
|
+
{ name: "Atom", value: "atom" },
|
|
1774
|
+
{ name: "Sublime Text", value: "subl" },
|
|
1775
|
+
{ name: "Vim", value: "vim" },
|
|
1776
|
+
{ name: "Emacs", value: "emacs" }
|
|
1777
|
+
];
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1774
1781
|
// src/commands/worktrees/utils.ts
|
|
1775
1782
|
import File3 from "phylo";
|
|
1776
1783
|
import path from "path";
|
|
@@ -2035,7 +2042,7 @@ import ora from "ora";
|
|
|
2035
2042
|
import { select as select2, confirm as confirm4 } from "@inquirer/prompts";
|
|
2036
2043
|
function createAddCommand(ctx) {
|
|
2037
2044
|
const { config, log } = ctx;
|
|
2038
|
-
return new Command2("add").description("Create a new worktree for a branch").argument("<branch>", "Branch name for the worktree").option("-b, --base <branch>", "Base branch to create new branch from").option("-f, --force", "Overwrite existing worktree").option("-o, --open", "Open the worktree after creation").option("--no-hook", "Skip running the post-setup hook").action(async (branch, options) => {
|
|
2045
|
+
return new Command2("add").description("Create a new worktree for a branch").argument("<branch>", "Branch name for the worktree").option("-b, --base <branch>", "Base branch to create new branch from").option("-f, --force", "Overwrite existing worktree").option("-o, --open", "Open the worktree after creation").option("--no-hook", "Skip running the post-setup hook").option("-y, --yes", "Skip all confirmation prompts (non-interactive mode)").action(async (branch, options) => {
|
|
2039
2046
|
const projectCtx = await resolveProjectFromCwd(config, log);
|
|
2040
2047
|
if (!projectCtx) {
|
|
2041
2048
|
log.error("Not in a git repository. Run this command from within a git project.");
|
|
@@ -2045,11 +2052,15 @@ function createAddCommand(ctx) {
|
|
|
2045
2052
|
process.exit(1);
|
|
2046
2053
|
}
|
|
2047
2054
|
if (!projectCtx.isRegistered) {
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
projectCtx.
|
|
2052
|
-
|
|
2055
|
+
if (options.yes) {
|
|
2056
|
+
log.info("Project is not registered. Proceeding without registration.");
|
|
2057
|
+
} else {
|
|
2058
|
+
const result = await promptToRegisterProject(projectCtx.projectPath, config, log);
|
|
2059
|
+
if (result) {
|
|
2060
|
+
projectCtx.projectName = result.projectName;
|
|
2061
|
+
projectCtx.projectConfig = result.projectConfig;
|
|
2062
|
+
projectCtx.isRegistered = true;
|
|
2063
|
+
}
|
|
2053
2064
|
}
|
|
2054
2065
|
}
|
|
2055
2066
|
const { projectPath, projectName } = projectCtx;
|
|
@@ -2057,16 +2068,21 @@ function createAddCommand(ctx) {
|
|
|
2057
2068
|
const branchExists = await manager.branchExists(branch);
|
|
2058
2069
|
let baseBranch = options.base;
|
|
2059
2070
|
if (!branchExists && !baseBranch) {
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2071
|
+
if (options.yes) {
|
|
2072
|
+
baseBranch = await manager.getCurrentBranch();
|
|
2073
|
+
log.info(`Branch '${branch}' doesn't exist. Creating from '${baseBranch}'.`);
|
|
2074
|
+
} else {
|
|
2075
|
+
const branches = await manager.getBranches();
|
|
2076
|
+
const currentBranch = await manager.getCurrentBranch();
|
|
2077
|
+
baseBranch = await select2({
|
|
2078
|
+
message: `Branch '${branch}' doesn't exist. Create from which branch?`,
|
|
2079
|
+
choices: branches.map((b) => ({
|
|
2080
|
+
name: b === currentBranch ? `${b} (current)` : b,
|
|
2081
|
+
value: b
|
|
2082
|
+
})),
|
|
2083
|
+
default: currentBranch
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2070
2086
|
}
|
|
2071
2087
|
const spinner = ora(`Creating worktree for branch '${branch}'...`).start();
|
|
2072
2088
|
try {
|
|
@@ -2099,6 +2115,9 @@ ${chalk2.bold("Worktree details:")}`);
|
|
|
2099
2115
|
if (projectCtx.isRegistered && projectName) {
|
|
2100
2116
|
if (options.open) {
|
|
2101
2117
|
await openWorktreeSession(projectCtx, worktree.name, config, log);
|
|
2118
|
+
} else if (options.yes) {
|
|
2119
|
+
console.log(`
|
|
2120
|
+
To open later: ${chalk2.cyan(`workon worktrees open ${worktree.name}`)}`);
|
|
2102
2121
|
} else {
|
|
2103
2122
|
const shouldOpen = await confirm4({
|
|
2104
2123
|
message: "Open workon session in this worktree?",
|
|
@@ -2268,7 +2287,7 @@ import path4 from "path";
|
|
|
2268
2287
|
import { select as select3, confirm as confirm6 } from "@inquirer/prompts";
|
|
2269
2288
|
function createMergeCommand(ctx) {
|
|
2270
2289
|
const { config, log } = ctx;
|
|
2271
|
-
return new Command4("merge").description("Merge a worktree branch and optionally remove the worktree").argument("<name>", "Worktree name").option("-i, --into <branch>", "Target branch to merge into").option("-s, --squash", "Use squash merge").option("-k, --keep", "Keep the worktree after merging").option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
|
|
2290
|
+
return new Command4("merge").description("Merge a worktree branch and optionally remove the worktree").argument("<name>", "Worktree name").option("-i, --into <branch>", "Target branch to merge into").option("-s, --squash", "Use squash merge").option("-k, --keep", "Keep the worktree after merging").option("-y, --yes", "Skip confirmation prompts").option("--delete-branch", "Delete the merged branch after merge").action(async (name, options) => {
|
|
2272
2291
|
const projectCtx = await resolveProjectFromCwd(config, log);
|
|
2273
2292
|
if (!projectCtx) {
|
|
2274
2293
|
log.error("Not in a git repository. Run this command from within a git project.");
|
|
@@ -2310,14 +2329,19 @@ function createMergeCommand(ctx) {
|
|
|
2310
2329
|
}
|
|
2311
2330
|
const commonTargets = ["main", "master", "develop", "dev"];
|
|
2312
2331
|
const defaultTarget = commonTargets.find((t) => targetBranches.includes(t)) || targetBranches[0];
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2332
|
+
if (options.yes) {
|
|
2333
|
+
targetBranch = defaultTarget;
|
|
2334
|
+
log.info(`Auto-selected target branch: '${targetBranch}'`);
|
|
2335
|
+
} else {
|
|
2336
|
+
targetBranch = await select3({
|
|
2337
|
+
message: `Merge '${worktree.branch}' into which branch?`,
|
|
2338
|
+
choices: targetBranches.map((b) => ({
|
|
2339
|
+
name: b,
|
|
2340
|
+
value: b
|
|
2341
|
+
})),
|
|
2342
|
+
default: defaultTarget
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2321
2345
|
}
|
|
2322
2346
|
if (!await manager.branchExists(targetBranch)) {
|
|
2323
2347
|
log.error(`Target branch '${targetBranch}' does not exist.`);
|
|
@@ -2359,6 +2383,11 @@ ${chalk4.bold("Merge operation:")}`);
|
|
|
2359
2383
|
log.info("You may need to resolve conflicts manually.");
|
|
2360
2384
|
process.exit(1);
|
|
2361
2385
|
}
|
|
2386
|
+
if (options.keep && options.deleteBranch) {
|
|
2387
|
+
log.warn(
|
|
2388
|
+
"--delete-branch is ignored when --keep is set (branch is needed by the worktree)."
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
2362
2391
|
if (!options.keep) {
|
|
2363
2392
|
const tmux = new TmuxManager();
|
|
2364
2393
|
const sessionName = tmux.getWorktreeSessionName(displayName, name);
|
|
@@ -2374,21 +2403,22 @@ ${chalk4.bold("Merge operation:")}`);
|
|
|
2374
2403
|
removeSpinner.warn(`Failed to remove worktree: ${error.message}`);
|
|
2375
2404
|
log.info(`You can remove it manually with: workon worktrees remove ${name}`);
|
|
2376
2405
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2406
|
+
let shouldDeleteBranch = options.deleteBranch || false;
|
|
2407
|
+
if (!shouldDeleteBranch && !options.yes) {
|
|
2408
|
+
shouldDeleteBranch = await confirm6({
|
|
2379
2409
|
message: `Delete the merged branch '${worktree.branch}'?`,
|
|
2380
2410
|
default: false
|
|
2381
2411
|
});
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
}
|
|
2390
|
-
|
|
2391
|
-
}
|
|
2412
|
+
}
|
|
2413
|
+
if (shouldDeleteBranch) {
|
|
2414
|
+
const deleteSpinner = ora3(`Deleting branch '${worktree.branch}'...`).start();
|
|
2415
|
+
try {
|
|
2416
|
+
const { simpleGit: simpleGit6 } = await import("simple-git");
|
|
2417
|
+
const git = simpleGit6(projectPath);
|
|
2418
|
+
await git.deleteLocalBranch(worktree.branch, true);
|
|
2419
|
+
deleteSpinner.succeed(`Branch '${worktree.branch}' deleted`);
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
deleteSpinner.warn(`Failed to delete branch: ${error.message}`);
|
|
2392
2422
|
}
|
|
2393
2423
|
}
|
|
2394
2424
|
}
|
|
@@ -2414,7 +2444,7 @@ import { confirm as confirm7 } from "@inquirer/prompts";
|
|
|
2414
2444
|
import { simpleGit as simpleGit4 } from "simple-git";
|
|
2415
2445
|
function createBranchCommand(ctx) {
|
|
2416
2446
|
const { config, log } = ctx;
|
|
2417
|
-
return new Command5("branch").description("Create a branch from a worktree (useful for detached HEAD state)").argument("<worktree>", "Worktree name").argument("<branch>", "New branch name to create").option("-p, --push", "Push the branch to origin after creating").option("-f, --force", "Overwrite existing branch").action(async (worktreeName, branchName, options) => {
|
|
2447
|
+
return new Command5("branch").description("Create a branch from a worktree (useful for detached HEAD state)").argument("<worktree>", "Worktree name").argument("<branch>", "New branch name to create").option("-p, --push", "Push the branch to origin after creating").option("-f, --force", "Overwrite existing branch").option("-y, --yes", "Skip all confirmation prompts (non-interactive mode)").action(async (worktreeName, branchName, options) => {
|
|
2418
2448
|
const projectCtx = await resolveProjectFromCwd(config, log);
|
|
2419
2449
|
if (!projectCtx) {
|
|
2420
2450
|
log.error("Not in a git repository. Run this command from within a git project.");
|
|
@@ -2477,6 +2507,8 @@ ${chalk5.bold("Action:")}`);
|
|
|
2477
2507
|
pushSpinner.fail(`Failed to push: ${error.message}`);
|
|
2478
2508
|
log.info(`You can push manually with: git push -u origin ${branchName}`);
|
|
2479
2509
|
}
|
|
2510
|
+
} else if (options.yes) {
|
|
2511
|
+
log.info(`You can push manually with: git push -u origin ${branchName}`);
|
|
2480
2512
|
} else {
|
|
2481
2513
|
const shouldPush = await confirm7({
|
|
2482
2514
|
message: "Push branch to origin for PR?",
|
|
@@ -2579,13 +2611,12 @@ __export(open_exports, {
|
|
|
2579
2611
|
});
|
|
2580
2612
|
import { Command as Command7 } from "commander";
|
|
2581
2613
|
import chalk6 from "chalk";
|
|
2582
|
-
import File4 from "phylo";
|
|
2583
2614
|
import path6 from "path";
|
|
2584
2615
|
import { exec as execCallback3 } from "child_process";
|
|
2585
2616
|
import { promisify as promisify3 } from "util";
|
|
2586
2617
|
function createOpenCommand(ctx) {
|
|
2587
2618
|
const { config, log } = ctx;
|
|
2588
|
-
return new Command7("open").description("Open a workon session in a worktree").argument("<name>", "Worktree name").option("-d, --debug", "Enable debug logging").option("--shell", "Output shell commands instead of spawning processes").action(async (name, options) => {
|
|
2619
|
+
return new Command7("open").description("Open a workon session in a worktree").argument("<name>", "Worktree name").option("-d, --debug", "Enable debug logging").option("--shell", "Output shell commands instead of spawning processes").option("-y, --yes", "Skip all confirmation prompts (non-interactive mode)").action(async (name, options) => {
|
|
2589
2620
|
if (options.debug) {
|
|
2590
2621
|
log.setLogLevel("debug");
|
|
2591
2622
|
}
|
|
@@ -2598,12 +2629,16 @@ function createOpenCommand(ctx) {
|
|
|
2598
2629
|
process.exit(1);
|
|
2599
2630
|
}
|
|
2600
2631
|
if (!projectCtx.isRegistered) {
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
projectCtx.
|
|
2606
|
-
|
|
2632
|
+
if (options.yes) {
|
|
2633
|
+
log.info("Project is not registered. Opening with basic shell layout.");
|
|
2634
|
+
} else {
|
|
2635
|
+
log.warn("Project is not registered. Opening with basic shell layout.");
|
|
2636
|
+
const shouldRegister = await promptToRegisterProject(projectCtx.projectPath, config, log);
|
|
2637
|
+
if (shouldRegister) {
|
|
2638
|
+
projectCtx.projectName = shouldRegister.projectName;
|
|
2639
|
+
projectCtx.projectConfig = shouldRegister.projectConfig;
|
|
2640
|
+
projectCtx.isRegistered = true;
|
|
2641
|
+
}
|
|
2607
2642
|
}
|
|
2608
2643
|
}
|
|
2609
2644
|
await runWorktreeOpen(projectCtx, name, options, { config, log });
|
|
@@ -2636,7 +2671,7 @@ async function runWorktreeOpen(projectCtx, worktreeName, options, ctx) {
|
|
|
2636
2671
|
let hasNpmEvent = false;
|
|
2637
2672
|
if (isRegistered && projectName && projectConfig) {
|
|
2638
2673
|
project = new Project(projectName, projectConfig, defaults);
|
|
2639
|
-
project.
|
|
2674
|
+
project.overridePath(worktree.path);
|
|
2640
2675
|
const events = project.events || {};
|
|
2641
2676
|
hasClaudeEvent = !!events.claude;
|
|
2642
2677
|
hasNpmEvent = !!events.npm;
|
|
@@ -2855,7 +2890,7 @@ __export(interactive_exports, {
|
|
|
2855
2890
|
});
|
|
2856
2891
|
import { select as select4, input as input5, checkbox as checkbox2, confirm as confirm8 } from "@inquirer/prompts";
|
|
2857
2892
|
import { existsSync as existsSync5 } from "fs";
|
|
2858
|
-
import
|
|
2893
|
+
import File4 from "phylo";
|
|
2859
2894
|
import path7 from "path";
|
|
2860
2895
|
import deepAssign2 from "deep-assign";
|
|
2861
2896
|
import chalk7 from "chalk";
|
|
@@ -2863,7 +2898,7 @@ async function runInteractive(ctx) {
|
|
|
2863
2898
|
const { config, log, environment, suggestedName } = ctx;
|
|
2864
2899
|
showLogo(config);
|
|
2865
2900
|
log.log("");
|
|
2866
|
-
const defaultName = suggestedName ?? (environment.$isProjectEnvironment ? environment.project.name :
|
|
2901
|
+
const defaultName = suggestedName ?? (environment.$isProjectEnvironment ? environment.project.name : File4.cwd().name);
|
|
2867
2902
|
const fromUser = !!suggestedName;
|
|
2868
2903
|
await startInteractive(defaultName, fromUser, ctx);
|
|
2869
2904
|
}
|
|
@@ -2964,15 +2999,15 @@ async function initProject(defaultName, fromUser, ctx) {
|
|
|
2964
2999
|
let basePath;
|
|
2965
3000
|
if (isBranch) {
|
|
2966
3001
|
const projectName = name.substring(0, name.indexOf("#"));
|
|
2967
|
-
basePath = defaults?.base ?
|
|
3002
|
+
basePath = defaults?.base ? File4.from(defaults.base).join(projects[projectName].path).absolutePath() : projects[projectName].path;
|
|
2968
3003
|
log.log(`Project path: ${basePath}`);
|
|
2969
3004
|
} else {
|
|
2970
3005
|
const pathAnswer = await input5({
|
|
2971
3006
|
message: "What is the path to the project?",
|
|
2972
|
-
default: defaults?.base ?
|
|
3007
|
+
default: defaults?.base ? File4.from(defaults.base).join(name).path : name
|
|
2973
3008
|
});
|
|
2974
|
-
let answerFile =
|
|
2975
|
-
const defaultBase = defaults?.base ?
|
|
3009
|
+
let answerFile = File4.from(pathAnswer);
|
|
3010
|
+
const defaultBase = defaults?.base ? File4.from(defaults.base).absolutify() : File4.cwd();
|
|
2976
3011
|
if (!answerFile.isAbsolute()) {
|
|
2977
3012
|
answerFile = defaultBase.join(answerFile.path);
|
|
2978
3013
|
}
|
|
@@ -3180,15 +3215,15 @@ async function createProjectManage(ctx) {
|
|
|
3180
3215
|
return true;
|
|
3181
3216
|
}
|
|
3182
3217
|
});
|
|
3183
|
-
const defaultPath = defaults?.base ?
|
|
3218
|
+
const defaultPath = defaults?.base ? File4.from(defaults.base).absolutify().join(name).path : name;
|
|
3184
3219
|
const pathInput = await input5({
|
|
3185
3220
|
message: "Project path:",
|
|
3186
3221
|
default: defaultPath
|
|
3187
3222
|
});
|
|
3188
3223
|
let relativePath = pathInput;
|
|
3189
3224
|
if (defaults?.base) {
|
|
3190
|
-
const baseDir =
|
|
3191
|
-
const pathFile =
|
|
3225
|
+
const baseDir = File4.from(defaults.base).absolutify();
|
|
3226
|
+
const pathFile = File4.from(pathInput);
|
|
3192
3227
|
try {
|
|
3193
3228
|
if (pathFile.isAbsolute()) {
|
|
3194
3229
|
const relPath = pathFile.relativize(baseDir.path);
|
|
@@ -3255,8 +3290,8 @@ async function editProjectManage(ctx) {
|
|
|
3255
3290
|
});
|
|
3256
3291
|
let relativePath = pathInput;
|
|
3257
3292
|
if (defaults?.base) {
|
|
3258
|
-
const baseDir =
|
|
3259
|
-
const pathFile =
|
|
3293
|
+
const baseDir = File4.from(defaults.base).absolutify();
|
|
3294
|
+
const pathFile = File4.from(pathInput);
|
|
3260
3295
|
try {
|
|
3261
3296
|
if (pathFile.isAbsolute()) {
|
|
3262
3297
|
const relPath = pathFile.relativize(baseDir.path);
|
|
@@ -3359,7 +3394,7 @@ function listProjectsManage(ctx) {
|
|
|
3359
3394
|
const baseProjects = Object.keys(projects).filter((name) => !name.includes("#"));
|
|
3360
3395
|
for (const name of baseProjects) {
|
|
3361
3396
|
const project = projects[name];
|
|
3362
|
-
const fullPath = defaults?.base ?
|
|
3397
|
+
const fullPath = defaults?.base ? File4.from(defaults.base).join(project.path).path : project.path;
|
|
3363
3398
|
console.log(` ${name}`);
|
|
3364
3399
|
console.log(` Path: ${fullPath}`);
|
|
3365
3400
|
console.log(` IDE: ${project.ide || "not set"}`);
|
|
@@ -3483,11 +3518,11 @@ async function manageWorktrees(projectName, ctx) {
|
|
|
3483
3518
|
const projectConfig = projects[projectName];
|
|
3484
3519
|
const basePath = defaults?.base || "";
|
|
3485
3520
|
let projectPath;
|
|
3486
|
-
const configPath =
|
|
3521
|
+
const configPath = File4.from(projectConfig.path);
|
|
3487
3522
|
if (configPath.path.startsWith("/") || configPath.path.startsWith("~")) {
|
|
3488
3523
|
projectPath = configPath.absolutify().path;
|
|
3489
3524
|
} else if (basePath) {
|
|
3490
|
-
projectPath =
|
|
3525
|
+
projectPath = File4.from(basePath).absolutify().join(projectConfig.path).path;
|
|
3491
3526
|
} else {
|
|
3492
3527
|
projectPath = configPath.absolutify().path;
|
|
3493
3528
|
}
|
|
@@ -3618,11 +3653,11 @@ async function openWorktreeManage(projectName, manager, config, log) {
|
|
|
3618
3653
|
const projectConfig = projects[projectName];
|
|
3619
3654
|
const basePath = defaults?.base || "";
|
|
3620
3655
|
let projectPath;
|
|
3621
|
-
const configPath =
|
|
3656
|
+
const configPath = File4.from(projectConfig.path);
|
|
3622
3657
|
if (configPath.path.startsWith("/") || configPath.path.startsWith("~")) {
|
|
3623
3658
|
projectPath = configPath.absolutify().path;
|
|
3624
3659
|
} else if (basePath) {
|
|
3625
|
-
projectPath =
|
|
3660
|
+
projectPath = File4.from(basePath).absolutify().join(projectConfig.path).path;
|
|
3626
3661
|
} else {
|
|
3627
3662
|
projectPath = configPath.absolutify().path;
|
|
3628
3663
|
}
|
|
@@ -3865,7 +3900,7 @@ __export(open_exports2, {
|
|
|
3865
3900
|
runOpen: () => runOpen
|
|
3866
3901
|
});
|
|
3867
3902
|
import { Command as Command8 } from "commander";
|
|
3868
|
-
import
|
|
3903
|
+
import File5 from "phylo";
|
|
3869
3904
|
async function runOpen(projectArg, options, ctx) {
|
|
3870
3905
|
const { log } = ctx;
|
|
3871
3906
|
if (options.debug) {
|
|
@@ -3884,7 +3919,7 @@ function createOpenCommand2(ctx) {
|
|
|
3884
3919
|
} else {
|
|
3885
3920
|
log.debug("No project name provided, starting interactive mode");
|
|
3886
3921
|
const { runInteractive: runInteractive2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
|
|
3887
|
-
const environment = await EnvironmentRecognizer.recognize(
|
|
3922
|
+
const environment = await EnvironmentRecognizer.recognize(File5.cwd());
|
|
3888
3923
|
await runInteractive2({ config, log, environment });
|
|
3889
3924
|
}
|
|
3890
3925
|
});
|
|
@@ -3892,17 +3927,20 @@ function createOpenCommand2(ctx) {
|
|
|
3892
3927
|
}
|
|
3893
3928
|
async function processProject(projectParam, options, ctx) {
|
|
3894
3929
|
const { config, log } = ctx;
|
|
3895
|
-
const
|
|
3896
|
-
const
|
|
3930
|
+
const parts = projectParam.split(":");
|
|
3931
|
+
const projectName = parts[0];
|
|
3932
|
+
const commandsString = parts[1] || null;
|
|
3933
|
+
const worktreeName = parts[2] || null;
|
|
3934
|
+
const requestedCommands = commandsString && commandsString !== "help" ? commandsString.split(",").map((cmd) => cmd.trim()) : null;
|
|
3897
3935
|
if (commandsString === "help") {
|
|
3898
3936
|
await showProjectHelp(projectName, ctx);
|
|
3899
3937
|
return;
|
|
3900
3938
|
}
|
|
3901
3939
|
log.debug(
|
|
3902
|
-
`Project: ${projectName}, Commands: ${requestedCommands ? requestedCommands.join(", ") : "all"}`
|
|
3940
|
+
`Project: ${projectName}, Commands: ${requestedCommands ? requestedCommands.join(", ") : "all"}` + (worktreeName ? `, Worktree: ${worktreeName}` : "")
|
|
3903
3941
|
);
|
|
3904
3942
|
const projects = config.getProjects();
|
|
3905
|
-
const environment = await EnvironmentRecognizer.recognize(
|
|
3943
|
+
const environment = await EnvironmentRecognizer.recognize(File5.cwd());
|
|
3906
3944
|
if (environment.$isProjectEnvironment && (projectName === "this" || projectName === ".")) {
|
|
3907
3945
|
log.info(`Opening current project: ${environment.project.name}`);
|
|
3908
3946
|
await switchTo(environment, requestedCommands, options, ctx);
|
|
@@ -3915,6 +3953,22 @@ async function processProject(projectParam, options, ctx) {
|
|
|
3915
3953
|
validateRequestedCommands(requestedCommands, projectCfg, projectName);
|
|
3916
3954
|
}
|
|
3917
3955
|
const projectEnv = ProjectEnvironment.load(projectCfg, config.getDefaults());
|
|
3956
|
+
if (worktreeName) {
|
|
3957
|
+
const projectPath = projectEnv.project.path.path;
|
|
3958
|
+
const manager = new WorktreeManager(projectPath, projectName);
|
|
3959
|
+
const worktree = await manager.get(worktreeName);
|
|
3960
|
+
if (!worktree) {
|
|
3961
|
+
log.error(`Worktree '${worktreeName}' not found for project '${projectName}'.`);
|
|
3962
|
+
const worktrees = await manager.listManagedWorktrees();
|
|
3963
|
+
if (worktrees.length > 0) {
|
|
3964
|
+
log.info("Available worktrees:");
|
|
3965
|
+
worktrees.forEach((wt) => log.info(` - ${wt.name}`));
|
|
3966
|
+
}
|
|
3967
|
+
process.exit(1);
|
|
3968
|
+
}
|
|
3969
|
+
log.debug(`Using worktree path: ${worktree.path}`);
|
|
3970
|
+
projectEnv.project.overridePath(worktree.path);
|
|
3971
|
+
}
|
|
3918
3972
|
await switchTo(projectEnv, requestedCommands, options, ctx);
|
|
3919
3973
|
} else {
|
|
3920
3974
|
log.error(`Project '${projectName}' not found.`);
|
|
@@ -4175,7 +4229,9 @@ Available commands for '${projectName}':`);
|
|
|
4175
4229
|
const twoCommands = configuredEvents.slice(0, 2).join(",");
|
|
4176
4230
|
console.log(` workon ${projectName}:${twoCommands.padEnd(12)} # Multiple commands`);
|
|
4177
4231
|
}
|
|
4178
|
-
console.log(` workon ${projectName}:cwd --shell # Output shell commands
|
|
4232
|
+
console.log(` workon ${projectName}:cwd --shell # Output shell commands`);
|
|
4233
|
+
console.log(` workon ${projectName}:cwd:my-worktree # Run in a worktree`);
|
|
4234
|
+
console.log(` workon ${projectName}::my-worktree # All commands in a worktree
|
|
4179
4235
|
`);
|
|
4180
4236
|
}
|
|
4181
4237
|
var init_open2 = __esm({
|
|
@@ -4184,6 +4240,7 @@ var init_open2 = __esm({
|
|
|
4184
4240
|
init_environment();
|
|
4185
4241
|
init_tmux();
|
|
4186
4242
|
init_registry();
|
|
4243
|
+
init_worktree();
|
|
4187
4244
|
}
|
|
4188
4245
|
});
|
|
4189
4246
|
|
|
@@ -4198,7 +4255,7 @@ import { join as join2, dirname as dirname2 } from "path";
|
|
|
4198
4255
|
import { fileURLToPath } from "url";
|
|
4199
4256
|
import loog from "loog";
|
|
4200
4257
|
import omelette from "omelette";
|
|
4201
|
-
import
|
|
4258
|
+
import File8 from "phylo";
|
|
4202
4259
|
|
|
4203
4260
|
// src/commands/config/index.ts
|
|
4204
4261
|
import { Command as Command12 } from "commander";
|
|
@@ -4295,7 +4352,7 @@ init_registry();
|
|
|
4295
4352
|
init_constants();
|
|
4296
4353
|
import { Command as Command13 } from "commander";
|
|
4297
4354
|
import { select as select5, input as input6, confirm as confirm9, checkbox as checkbox3 } from "@inquirer/prompts";
|
|
4298
|
-
import
|
|
4355
|
+
import File6 from "phylo";
|
|
4299
4356
|
function createManageCommand(ctx) {
|
|
4300
4357
|
const { log } = ctx;
|
|
4301
4358
|
return new Command13("manage").description("Interactive project management").option("-d, --debug", "Enable debug logging").action(async (options) => {
|
|
@@ -4354,12 +4411,12 @@ async function createProject(ctx) {
|
|
|
4354
4411
|
return true;
|
|
4355
4412
|
}
|
|
4356
4413
|
});
|
|
4357
|
-
const defaultPath = defaults?.base ?
|
|
4414
|
+
const defaultPath = defaults?.base ? File6.from(defaults.base).join(name).path : name;
|
|
4358
4415
|
const pathInput = await input6({
|
|
4359
4416
|
message: "Project path:",
|
|
4360
4417
|
default: defaultPath,
|
|
4361
4418
|
validate: (value) => {
|
|
4362
|
-
const path8 =
|
|
4419
|
+
const path8 = File6.from(value);
|
|
4363
4420
|
try {
|
|
4364
4421
|
const exists = path8.exists();
|
|
4365
4422
|
if (!exists) return `Path does not exist: ${value}`;
|
|
@@ -4373,8 +4430,8 @@ async function createProject(ctx) {
|
|
|
4373
4430
|
});
|
|
4374
4431
|
let relativePath = pathInput;
|
|
4375
4432
|
if (defaults?.base) {
|
|
4376
|
-
const baseDir =
|
|
4377
|
-
const pathFile =
|
|
4433
|
+
const baseDir = File6.from(defaults.base).absolutify();
|
|
4434
|
+
const pathFile = File6.from(pathInput);
|
|
4378
4435
|
try {
|
|
4379
4436
|
const relPath = pathFile.relativize(baseDir.path);
|
|
4380
4437
|
if (relPath && !relPath.path.startsWith("..")) {
|
|
@@ -4451,8 +4508,8 @@ async function editProject(ctx) {
|
|
|
4451
4508
|
});
|
|
4452
4509
|
let relativePath = pathInput;
|
|
4453
4510
|
if (defaults?.base) {
|
|
4454
|
-
const baseDir =
|
|
4455
|
-
const pathFile =
|
|
4511
|
+
const baseDir = File6.from(defaults.base).absolutify();
|
|
4512
|
+
const pathFile = File6.from(pathInput);
|
|
4456
4513
|
try {
|
|
4457
4514
|
if (pathFile.isAbsolute()) {
|
|
4458
4515
|
const relPath = pathFile.relativize(baseDir.path);
|
|
@@ -4552,7 +4609,7 @@ async function listProjects(ctx) {
|
|
|
4552
4609
|
const defaults = config.getDefaults();
|
|
4553
4610
|
console.log("\nConfigured projects:\n");
|
|
4554
4611
|
for (const [name, project] of Object.entries(projects)) {
|
|
4555
|
-
const fullPath = defaults?.base ?
|
|
4612
|
+
const fullPath = defaults?.base ? File6.from(defaults.base).join(project.path).path : project.path;
|
|
4556
4613
|
console.log(` ${name}`);
|
|
4557
4614
|
console.log(` Path: ${fullPath}`);
|
|
4558
4615
|
console.log(` IDE: ${project.ide || "not set"}`);
|
|
@@ -4568,7 +4625,7 @@ async function listProjects(ctx) {
|
|
|
4568
4625
|
import { Command as Command14 } from "commander";
|
|
4569
4626
|
import { existsSync as existsSync6, readFileSync } from "fs";
|
|
4570
4627
|
import { basename as basename2, resolve } from "path";
|
|
4571
|
-
import
|
|
4628
|
+
import File7 from "phylo";
|
|
4572
4629
|
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
4573
4630
|
function createAddCommand2(ctx) {
|
|
4574
4631
|
const { log } = ctx;
|
|
@@ -4592,7 +4649,7 @@ async function addProject(pathArg, options, ctx) {
|
|
|
4592
4649
|
log.error(`Path does not exist: ${targetPath}`);
|
|
4593
4650
|
process.exit(1);
|
|
4594
4651
|
}
|
|
4595
|
-
const pathFile =
|
|
4652
|
+
const pathFile = File7.from(targetPath);
|
|
4596
4653
|
try {
|
|
4597
4654
|
const stat = pathFile.stat();
|
|
4598
4655
|
if (!stat.isDirectory()) {
|
|
@@ -4626,7 +4683,7 @@ async function addProject(pathArg, options, ctx) {
|
|
|
4626
4683
|
log.debug(`IDE: ${ide}`);
|
|
4627
4684
|
let relativePath = targetPath;
|
|
4628
4685
|
if (defaults?.base) {
|
|
4629
|
-
const baseDir =
|
|
4686
|
+
const baseDir = File7.from(defaults.base).absolutify();
|
|
4630
4687
|
try {
|
|
4631
4688
|
const relPath = pathFile.relativize(baseDir.path);
|
|
4632
4689
|
if (relPath && !relPath.path.startsWith("..")) {
|
|
@@ -4755,19 +4812,21 @@ function createWorktreeCommand(ctx) {
|
|
|
4755
4812
|
}
|
|
4756
4813
|
await showWorktreeStatus(worktreeInfo, log);
|
|
4757
4814
|
});
|
|
4758
|
-
command.command("merge").description("Merge this worktree branch into a target branch").option("-i, --into <branch>", "Target branch to merge into").option("-s, --squash", "Use squash merge").option("-k, --keep", "Keep the worktree after merging").option("-y, --yes", "Skip confirmation prompts").
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4815
|
+
command.command("merge").description("Merge this worktree branch into a target branch").option("-i, --into <branch>", "Target branch to merge into").option("-s, --squash", "Use squash merge").option("-k, --keep", "Keep the worktree after merging").option("-y, --yes", "Skip confirmation prompts").option("--delete-branch", "Delete the merged branch after merge").action(
|
|
4816
|
+
async (options) => {
|
|
4817
|
+
const worktreeInfo = await detectWorktreeContext();
|
|
4818
|
+
if (!worktreeInfo) {
|
|
4819
|
+
log.error("Not in a git repository.");
|
|
4820
|
+
process.exit(1);
|
|
4821
|
+
}
|
|
4822
|
+
if (!worktreeInfo.isWorktree) {
|
|
4823
|
+
log.error("You're in the main repository, not a worktree.");
|
|
4824
|
+
log.info(`Use 'workon worktrees merge <name>' from the main repository.`);
|
|
4825
|
+
process.exit(1);
|
|
4826
|
+
}
|
|
4827
|
+
await mergeCurrentWorktree(worktreeInfo, options, ctx);
|
|
4768
4828
|
}
|
|
4769
|
-
|
|
4770
|
-
});
|
|
4829
|
+
);
|
|
4771
4830
|
command.command("remove").description("Remove the current worktree").option("-f, --force", "Force removal even with uncommitted changes").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
4772
4831
|
const worktreeInfo = await detectWorktreeContext();
|
|
4773
4832
|
if (!worktreeInfo) {
|
|
@@ -4852,11 +4911,16 @@ async function mergeCurrentWorktree(worktreeInfo, options, ctx) {
|
|
|
4852
4911
|
}
|
|
4853
4912
|
const commonTargets = ["main", "master", "develop", "dev"];
|
|
4854
4913
|
const defaultTarget = commonTargets.find((t) => targetBranches.includes(t)) || targetBranches[0];
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4914
|
+
if (options.yes) {
|
|
4915
|
+
targetBranch = defaultTarget;
|
|
4916
|
+
log.info(`Auto-selected target branch: '${targetBranch}'`);
|
|
4917
|
+
} else {
|
|
4918
|
+
targetBranch = await select6({
|
|
4919
|
+
message: `Merge '${worktreeInfo.branch}' into which branch?`,
|
|
4920
|
+
choices: targetBranches.map((b) => ({ name: b, value: b })),
|
|
4921
|
+
default: defaultTarget
|
|
4922
|
+
});
|
|
4923
|
+
}
|
|
4860
4924
|
}
|
|
4861
4925
|
if (!await manager.branchExists(targetBranch)) {
|
|
4862
4926
|
log.error(`Target branch '${targetBranch}' does not exist.`);
|
|
@@ -4898,12 +4962,18 @@ ${chalk8.bold("Merge operation:")}`);
|
|
|
4898
4962
|
log.info("You may need to resolve conflicts manually.");
|
|
4899
4963
|
process.exit(1);
|
|
4900
4964
|
}
|
|
4965
|
+
if (options.keep && options.deleteBranch) {
|
|
4966
|
+
log.warn("--delete-branch is ignored when --keep is set (branch is needed by the worktree).");
|
|
4967
|
+
}
|
|
4901
4968
|
if (!options.keep) {
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4969
|
+
let shouldContinue = true;
|
|
4970
|
+
if (!options.yes) {
|
|
4971
|
+
log.warn("You need to exit this worktree directory before it can be removed.");
|
|
4972
|
+
shouldContinue = await confirm11({
|
|
4973
|
+
message: `Remove worktree '${worktreeInfo.worktreeName}'? (You'll need to cd out first)`,
|
|
4974
|
+
default: true
|
|
4975
|
+
});
|
|
4976
|
+
}
|
|
4907
4977
|
if (shouldContinue) {
|
|
4908
4978
|
const tmux = new TmuxManager();
|
|
4909
4979
|
const sessionName = tmux.getWorktreeSessionName(
|
|
@@ -4919,20 +4989,21 @@ To complete removal, run from the main project directory:`);
|
|
|
4919
4989
|
console.log(chalk8.cyan(` cd ${worktreeInfo.mainRepoPath}`));
|
|
4920
4990
|
console.log(chalk8.cyan(` workon worktrees remove ${worktreeInfo.worktreeName}`));
|
|
4921
4991
|
}
|
|
4922
|
-
|
|
4923
|
-
|
|
4992
|
+
let shouldDeleteBranch = options.deleteBranch || false;
|
|
4993
|
+
if (!shouldDeleteBranch && !options.yes) {
|
|
4994
|
+
shouldDeleteBranch = await confirm11({
|
|
4924
4995
|
message: `Delete the merged branch '${worktreeInfo.branch}'?`,
|
|
4925
4996
|
default: false
|
|
4926
4997
|
});
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
}
|
|
4934
|
-
|
|
4935
|
-
}
|
|
4998
|
+
}
|
|
4999
|
+
if (shouldDeleteBranch) {
|
|
5000
|
+
const deleteSpinner = ora5(`Deleting branch '${worktreeInfo.branch}'...`).start();
|
|
5001
|
+
try {
|
|
5002
|
+
const git = simpleGit5(worktreeInfo.mainRepoPath);
|
|
5003
|
+
await git.deleteLocalBranch(worktreeInfo.branch, true);
|
|
5004
|
+
deleteSpinner.succeed(`Branch '${worktreeInfo.branch}' deleted`);
|
|
5005
|
+
} catch (error) {
|
|
5006
|
+
deleteSpinner.warn(`Failed to delete branch: ${error.message}`);
|
|
4936
5007
|
}
|
|
4937
5008
|
}
|
|
4938
5009
|
}
|
|
@@ -5046,7 +5117,7 @@ function createCli() {
|
|
|
5046
5117
|
await runOpen2(project, { debug: options.debug, shell: options.shell }, { config, log });
|
|
5047
5118
|
return;
|
|
5048
5119
|
}
|
|
5049
|
-
const environment = await EnvironmentRecognizer.recognize(
|
|
5120
|
+
const environment = await EnvironmentRecognizer.recognize(File8.cwd());
|
|
5050
5121
|
program2.setOptionValue("_environment", environment);
|
|
5051
5122
|
program2.setOptionValue("_config", config);
|
|
5052
5123
|
program2.setOptionValue("_log", log);
|