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/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
- const result = await promptToRegisterProject(projectCtx.projectPath, config, log);
2049
- if (result) {
2050
- projectCtx.projectName = result.projectName;
2051
- projectCtx.projectConfig = result.projectConfig;
2052
- projectCtx.isRegistered = true;
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
- const branches = await manager.getBranches();
2061
- const currentBranch = await manager.getCurrentBranch();
2062
- baseBranch = await select2({
2063
- message: `Branch '${branch}' doesn't exist. Create from which branch?`,
2064
- choices: branches.map((b) => ({
2065
- name: b === currentBranch ? `${b} (current)` : b,
2066
- value: b
2067
- })),
2068
- default: currentBranch
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
- targetBranch = await select3({
2314
- message: `Merge '${worktree.branch}' into which branch?`,
2315
- choices: targetBranches.map((b) => ({
2316
- name: b,
2317
- value: b
2318
- })),
2319
- default: defaultTarget
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
- if (!options.yes) {
2378
- const shouldDeleteBranch = await confirm6({
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
- if (shouldDeleteBranch) {
2383
- const deleteSpinner = ora3(`Deleting branch '${worktree.branch}'...`).start();
2384
- try {
2385
- const { simpleGit: simpleGit6 } = await import("simple-git");
2386
- const git = simpleGit6(projectPath);
2387
- await git.deleteLocalBranch(worktree.branch, true);
2388
- deleteSpinner.succeed(`Branch '${worktree.branch}' deleted`);
2389
- } catch (error) {
2390
- deleteSpinner.warn(`Failed to delete branch: ${error.message}`);
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
- log.warn("Project is not registered. Opening with basic shell layout.");
2602
- const shouldRegister = await promptToRegisterProject(projectCtx.projectPath, config, log);
2603
- if (shouldRegister) {
2604
- projectCtx.projectName = shouldRegister.projectName;
2605
- projectCtx.projectConfig = shouldRegister.projectConfig;
2606
- projectCtx.isRegistered = true;
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._path = File4.from(worktree.path).absolutify();
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 File5 from "phylo";
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 : File5.cwd().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 ? File5.from(defaults.base).join(projects[projectName].path).absolutePath() : projects[projectName].path;
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 ? File5.from(defaults.base).join(name).path : name
3007
+ default: defaults?.base ? File4.from(defaults.base).join(name).path : name
2973
3008
  });
2974
- let answerFile = File5.from(pathAnswer);
2975
- const defaultBase = defaults?.base ? File5.from(defaults.base).absolutify() : File5.cwd();
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 ? File5.from(defaults.base).absolutify().join(name).path : name;
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 = File5.from(defaults.base).absolutify();
3191
- const pathFile = File5.from(pathInput);
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 = File5.from(defaults.base).absolutify();
3259
- const pathFile = File5.from(pathInput);
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 ? File5.from(defaults.base).join(project.path).path : project.path;
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 = File5.from(projectConfig.path);
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 = File5.from(basePath).absolutify().join(projectConfig.path).path;
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 = File5.from(projectConfig.path);
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 = File5.from(basePath).absolutify().join(projectConfig.path).path;
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 File6 from "phylo";
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(File6.cwd());
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 [projectName, commandsString] = projectParam.split(":");
3896
- const requestedCommands = commandsString ? commandsString.split(",").map((cmd) => cmd.trim()) : null;
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(File6.cwd());
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 File9 from "phylo";
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 File7 from "phylo";
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 ? File7.from(defaults.base).join(name).path : name;
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 = File7.from(value);
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 = File7.from(defaults.base).absolutify();
4377
- const pathFile = File7.from(pathInput);
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 = File7.from(defaults.base).absolutify();
4455
- const pathFile = File7.from(pathInput);
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 ? File7.from(defaults.base).join(project.path).path : project.path;
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 File8 from "phylo";
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 = File8.from(targetPath);
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 = File8.from(defaults.base).absolutify();
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").action(async (options) => {
4759
- const worktreeInfo = await detectWorktreeContext();
4760
- if (!worktreeInfo) {
4761
- log.error("Not in a git repository.");
4762
- process.exit(1);
4763
- }
4764
- if (!worktreeInfo.isWorktree) {
4765
- log.error("You're in the main repository, not a worktree.");
4766
- log.info(`Use 'workon worktrees merge <name>' from the main repository.`);
4767
- process.exit(1);
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
- await mergeCurrentWorktree(worktreeInfo, options, ctx);
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
- targetBranch = await select6({
4856
- message: `Merge '${worktreeInfo.branch}' into which branch?`,
4857
- choices: targetBranches.map((b) => ({ name: b, value: b })),
4858
- default: defaultTarget
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
- log.warn("You need to exit this worktree directory before it can be removed.");
4903
- const shouldContinue = await confirm11({
4904
- message: `Remove worktree '${worktreeInfo.worktreeName}'? (You'll need to cd out first)`,
4905
- default: true
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
- if (!options.yes) {
4923
- const shouldDeleteBranch = await confirm11({
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
- if (shouldDeleteBranch) {
4928
- const deleteSpinner = ora5(`Deleting branch '${worktreeInfo.branch}'...`).start();
4929
- try {
4930
- const git = simpleGit5(worktreeInfo.mainRepoPath);
4931
- await git.deleteLocalBranch(worktreeInfo.branch, true);
4932
- deleteSpinner.succeed(`Branch '${worktreeInfo.branch}' deleted`);
4933
- } catch (error) {
4934
- deleteSpinner.warn(`Failed to delete branch: ${error.message}`);
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(File9.cwd());
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);