@aku11i/phantom 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +4 -1
  2. package/phantom.js +581 -330
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aku11i/phantom",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "A powerful CLI tool for managing Git worktrees for parallel development",
5
5
  "keywords": [
6
6
  "git",
@@ -31,5 +31,8 @@
31
31
  "README.md",
32
32
  "LICENSE"
33
33
  ],
34
+ "engines": {
35
+ "node": ">=22.0.0"
36
+ },
34
37
  "dependencies": {}
35
38
  }
package/phantom.js CHANGED
@@ -131,19 +131,6 @@ import { argv, exit as exit2 } from "node:process";
131
131
  // src/handlers/attach.ts
132
132
  import { parseArgs } from "node:util";
133
133
 
134
- // ../core/src/paths.ts
135
- import { join } from "node:path";
136
- function getPhantomDirectory(gitRoot) {
137
- return join(gitRoot, ".git", "phantom", "worktrees");
138
- }
139
- function getWorktreePath(gitRoot, name) {
140
- return join(getPhantomDirectory(gitRoot), name);
141
- }
142
-
143
- // ../core/src/config/loader.ts
144
- import fs from "node:fs/promises";
145
- import path from "node:path";
146
-
147
134
  // ../shared/src/types/result.ts
148
135
  var ok = (value) => ({
149
136
  ok: true,
@@ -164,6 +151,10 @@ var exitCodes = {
164
151
  validationError: 3
165
152
  };
166
153
 
154
+ // ../core/src/config/loader.ts
155
+ import fs from "node:fs/promises";
156
+ import path from "node:path";
157
+
167
158
  // ../../node_modules/.pnpm/zod@3.25.64/node_modules/zod/dist/esm/v3/external.js
168
159
  var external_exports = {};
169
160
  __export(external_exports, {
@@ -4211,13 +4202,16 @@ var phantomConfigSchema = external_exports.object({
4211
4202
  postCreate: external_exports.object({
4212
4203
  copyFiles: external_exports.array(external_exports.string()).optional(),
4213
4204
  commands: external_exports.array(external_exports.string()).optional()
4214
- }).passthrough().optional()
4205
+ }).passthrough().optional(),
4206
+ preDelete: external_exports.object({
4207
+ commands: external_exports.array(external_exports.string()).optional()
4208
+ }).passthrough().optional(),
4209
+ worktreesDirectory: external_exports.string().optional()
4215
4210
  }).passthrough();
4216
4211
  function validateConfig(config) {
4217
4212
  const result = phantomConfigSchema.safeParse(config);
4218
4213
  if (!result.success) {
4219
4214
  const error = result.error;
4220
- const formattedError = error.format();
4221
4215
  const firstError = error.errors[0];
4222
4216
  const path3 = firstError.path.join(".");
4223
4217
  const message = path3 ? `${path3}: ${firstError.message}` : firstError.message;
@@ -4265,6 +4259,30 @@ async function loadConfig(gitRoot) {
4265
4259
  }
4266
4260
  }
4267
4261
 
4262
+ // ../core/src/paths.ts
4263
+ import { isAbsolute, join } from "node:path";
4264
+ function getWorktreesDirectory(gitRoot, worktreesDirectory) {
4265
+ if (worktreesDirectory) {
4266
+ return isAbsolute(worktreesDirectory) ? worktreesDirectory : join(gitRoot, worktreesDirectory);
4267
+ }
4268
+ return join(gitRoot, ".git", "phantom", "worktrees");
4269
+ }
4270
+ function getWorktreePathFromDirectory(worktreeDirectory, name) {
4271
+ return join(worktreeDirectory, name);
4272
+ }
4273
+
4274
+ // ../core/src/context.ts
4275
+ async function createContext(gitRoot) {
4276
+ const configResult = await loadConfig(gitRoot);
4277
+ const config = isOk(configResult) ? configResult.value : null;
4278
+ const worktreesDirectory = config?.worktreesDirectory;
4279
+ return {
4280
+ gitRoot,
4281
+ worktreesDirectory: getWorktreesDirectory(gitRoot, worktreesDirectory),
4282
+ config
4283
+ };
4284
+ }
4285
+
4268
4286
  // ../core/src/worktree/errors.ts
4269
4287
  var WorktreeError = class extends Error {
4270
4288
  constructor(message) {
@@ -4387,7 +4405,7 @@ async function fetch(options = {}) {
4387
4405
  }
4388
4406
 
4389
4407
  // ../git/src/libs/list-worktrees.ts
4390
- async function listWorktrees(gitRoot) {
4408
+ async function listWorktrees(_gitRoot) {
4391
4409
  const { stdout: stdout2 } = await executeGitCommand([
4392
4410
  "worktree",
4393
4411
  "list",
@@ -4517,10 +4535,170 @@ async function copyFiles(sourceDir, targetDir, files) {
4517
4535
  return ok({ copiedFiles, skippedFiles });
4518
4536
  }
4519
4537
 
4538
+ // ../process/src/errors.ts
4539
+ var ProcessError = class extends Error {
4540
+ exitCode;
4541
+ constructor(message, exitCode) {
4542
+ super(message);
4543
+ this.name = this.constructor.name;
4544
+ this.exitCode = exitCode;
4545
+ }
4546
+ };
4547
+ var ProcessExecutionError = class extends ProcessError {
4548
+ constructor(command2, exitCode) {
4549
+ super(`Command '${command2}' failed with exit code ${exitCode}`, exitCode);
4550
+ this.name = "ProcessExecutionError";
4551
+ }
4552
+ };
4553
+ var ProcessSignalError = class extends ProcessError {
4554
+ constructor(signal) {
4555
+ const exitCode = 128 + (signal === "SIGTERM" ? 15 : 1);
4556
+ super(`Command terminated by signal: ${signal}`, exitCode);
4557
+ this.name = "ProcessSignalError";
4558
+ }
4559
+ };
4560
+ var ProcessSpawnError = class extends ProcessError {
4561
+ constructor(command2, details) {
4562
+ super(`Error executing command '${command2}': ${details}`);
4563
+ this.name = "ProcessSpawnError";
4564
+ }
4565
+ };
4566
+
4567
+ // ../process/src/spawn.ts
4568
+ import {
4569
+ spawn as nodeSpawn
4570
+ } from "node:child_process";
4571
+ async function spawnProcess(config) {
4572
+ return new Promise((resolve2) => {
4573
+ const { command: command2, args: args2 = [], options = {} } = config;
4574
+ const childProcess = nodeSpawn(command2, args2, {
4575
+ stdio: "inherit",
4576
+ ...options
4577
+ });
4578
+ childProcess.on("error", (error) => {
4579
+ resolve2(err(new ProcessSpawnError(command2, error.message)));
4580
+ });
4581
+ childProcess.on("exit", (code, signal) => {
4582
+ if (signal) {
4583
+ resolve2(err(new ProcessSignalError(signal)));
4584
+ } else {
4585
+ const exitCode = code ?? 0;
4586
+ if (exitCode === 0) {
4587
+ resolve2(ok({ exitCode }));
4588
+ } else {
4589
+ resolve2(err(new ProcessExecutionError(command2, exitCode)));
4590
+ }
4591
+ }
4592
+ });
4593
+ });
4594
+ }
4595
+
4596
+ // ../process/src/tmux.ts
4597
+ async function isInsideTmux() {
4598
+ return process.env.TMUX !== void 0;
4599
+ }
4600
+ async function executeTmuxCommand(options) {
4601
+ const { direction, command: command2, args: args2, cwd, env, windowName } = options;
4602
+ const tmuxArgs = [];
4603
+ switch (direction) {
4604
+ case "new":
4605
+ tmuxArgs.push("new-window");
4606
+ if (windowName) {
4607
+ tmuxArgs.push("-n", windowName);
4608
+ }
4609
+ break;
4610
+ case "vertical":
4611
+ tmuxArgs.push("split-window", "-v");
4612
+ break;
4613
+ case "horizontal":
4614
+ tmuxArgs.push("split-window", "-h");
4615
+ break;
4616
+ }
4617
+ if (cwd) {
4618
+ tmuxArgs.push("-c", cwd);
4619
+ }
4620
+ if (env) {
4621
+ for (const [key, value] of Object.entries(env)) {
4622
+ tmuxArgs.push("-e", `${key}=${value}`);
4623
+ }
4624
+ }
4625
+ tmuxArgs.push(command2);
4626
+ if (args2 && args2.length > 0) {
4627
+ tmuxArgs.push(...args2);
4628
+ }
4629
+ const result = await spawnProcess({
4630
+ command: "tmux",
4631
+ args: tmuxArgs
4632
+ });
4633
+ return result;
4634
+ }
4635
+
4636
+ // ../process/src/env.ts
4637
+ function getPhantomEnv(worktreeName, worktreePath) {
4638
+ return {
4639
+ PHANTOM: "1",
4640
+ PHANTOM_NAME: worktreeName,
4641
+ PHANTOM_PATH: worktreePath
4642
+ };
4643
+ }
4644
+
4645
+ // ../process/src/fzf.ts
4646
+ import { spawn } from "node:child_process";
4647
+ async function selectWithFzf(items, options = {}) {
4648
+ return new Promise((resolve2) => {
4649
+ const args2 = [];
4650
+ if (options.prompt) {
4651
+ args2.push("--prompt", options.prompt);
4652
+ }
4653
+ if (options.header) {
4654
+ args2.push("--header", options.header);
4655
+ }
4656
+ if (options.previewCommand) {
4657
+ args2.push("--preview", options.previewCommand);
4658
+ }
4659
+ const fzf = spawn("fzf", args2, {
4660
+ stdio: ["pipe", "pipe", "pipe"]
4661
+ });
4662
+ let result = "";
4663
+ let errorOutput = "";
4664
+ fzf.stdout.on("data", (data) => {
4665
+ result += data.toString();
4666
+ });
4667
+ if (fzf.stderr) {
4668
+ fzf.stderr.on("data", (data) => {
4669
+ errorOutput += data.toString();
4670
+ });
4671
+ }
4672
+ fzf.on("error", (error) => {
4673
+ if (error.message.includes("ENOENT")) {
4674
+ resolve2(
4675
+ err(new Error("fzf command not found. Please install fzf first."))
4676
+ );
4677
+ } else {
4678
+ resolve2(err(error));
4679
+ }
4680
+ });
4681
+ fzf.on("close", (code) => {
4682
+ if (code === 0) {
4683
+ const selected = result.trim();
4684
+ resolve2(ok(selected || null));
4685
+ } else if (code === 1) {
4686
+ resolve2(ok(null));
4687
+ } else if (code === 130) {
4688
+ resolve2(ok(null));
4689
+ } else {
4690
+ resolve2(err(new Error(`fzf exited with code ${code}: ${errorOutput}`)));
4691
+ }
4692
+ });
4693
+ fzf.stdin.write(items.join("\n"));
4694
+ fzf.stdin.end();
4695
+ });
4696
+ }
4697
+
4520
4698
  // ../core/src/worktree/validate.ts
4521
4699
  import fs2 from "node:fs/promises";
4522
- async function validateWorktreeExists(gitRoot, name) {
4523
- const worktreePath = getWorktreePath(gitRoot, name);
4700
+ async function validateWorktreeExists(_gitRoot, worktreeDirectory, name) {
4701
+ const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4524
4702
  try {
4525
4703
  await fs2.access(worktreePath);
4526
4704
  return ok({ path: worktreePath });
@@ -4528,8 +4706,8 @@ async function validateWorktreeExists(gitRoot, name) {
4528
4706
  return err(new WorktreeNotFoundError(name));
4529
4707
  }
4530
4708
  }
4531
- async function validateWorktreeDoesNotExist(gitRoot, name) {
4532
- const worktreePath = getWorktreePath(gitRoot, name);
4709
+ async function validateWorktreeDoesNotExist(_gitRoot, worktreeDirectory, name) {
4710
+ const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4533
4711
  try {
4534
4712
  await fs2.access(worktreePath);
4535
4713
  return err(new WorktreeAlreadyExistsError(name));
@@ -4555,21 +4733,91 @@ function validateWorktreeName(name) {
4555
4733
  return ok(void 0);
4556
4734
  }
4557
4735
 
4736
+ // ../core/src/exec.ts
4737
+ async function execInWorktree(gitRoot, worktreeDirectory, worktreeName, command2, options) {
4738
+ const validation = await validateWorktreeExists(
4739
+ gitRoot,
4740
+ worktreeDirectory,
4741
+ worktreeName
4742
+ );
4743
+ if (isErr(validation)) {
4744
+ return err(validation.error);
4745
+ }
4746
+ const worktreePath = validation.value.path;
4747
+ const [cmd, ...args2] = command2;
4748
+ const stdio = options?.interactive ? "inherit" : ["ignore", "inherit", "inherit"];
4749
+ return spawnProcess({
4750
+ command: cmd,
4751
+ args: args2,
4752
+ options: {
4753
+ cwd: worktreePath,
4754
+ stdio
4755
+ }
4756
+ });
4757
+ }
4758
+
4759
+ // ../core/src/worktree/post-create.ts
4760
+ async function executePostCreateCommands(options) {
4761
+ const { gitRoot, worktreesDirectory, worktreeName, commands: commands2 } = options;
4762
+ const executedCommands = [];
4763
+ for (const command2 of commands2) {
4764
+ console.log(`Executing: ${command2}`);
4765
+ const shell = process.env.SHELL || "/bin/sh";
4766
+ const cmdResult = await execInWorktree(
4767
+ gitRoot,
4768
+ worktreesDirectory,
4769
+ worktreeName,
4770
+ [shell, "-c", command2]
4771
+ );
4772
+ if (isErr(cmdResult)) {
4773
+ const errorMessage = cmdResult.error instanceof Error ? cmdResult.error.message : String(cmdResult.error);
4774
+ return err(
4775
+ new Error(
4776
+ `Failed to execute post-create command "${command2}": ${errorMessage}`
4777
+ )
4778
+ );
4779
+ }
4780
+ if (cmdResult.value.exitCode !== 0) {
4781
+ return err(
4782
+ new Error(
4783
+ `Post-create command failed with exit code ${cmdResult.value.exitCode}: ${command2}`
4784
+ )
4785
+ );
4786
+ }
4787
+ executedCommands.push(command2);
4788
+ }
4789
+ return ok({ executedCommands });
4790
+ }
4791
+ async function copyFilesToWorktree(gitRoot, worktreesDirectory, worktreeName, filesToCopy) {
4792
+ const worktreePath = getWorktreePathFromDirectory(
4793
+ worktreesDirectory,
4794
+ worktreeName
4795
+ );
4796
+ const copyResult = await copyFiles(gitRoot, worktreePath, filesToCopy);
4797
+ if (isErr(copyResult)) {
4798
+ return err(copyResult.error);
4799
+ }
4800
+ return ok(void 0);
4801
+ }
4802
+
4558
4803
  // ../core/src/worktree/create.ts
4559
- async function createWorktree(gitRoot, name, options = {}) {
4804
+ async function createWorktree(gitRoot, worktreeDirectory, name, options, postCreateCopyFiles, postCreateCommands) {
4560
4805
  const nameValidation = validateWorktreeName(name);
4561
4806
  if (isErr(nameValidation)) {
4562
4807
  return nameValidation;
4563
4808
  }
4564
4809
  const { branch = name, base = "HEAD" } = options;
4565
- const worktreesPath = getPhantomDirectory(gitRoot);
4566
- const worktreePath = getWorktreePath(gitRoot, name);
4810
+ const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4567
4811
  try {
4568
- await fs3.access(worktreesPath);
4812
+ await fs3.access(worktreeDirectory);
4569
4813
  } catch {
4570
- await fs3.mkdir(worktreesPath, { recursive: true });
4814
+ await fs3.mkdir(worktreeDirectory, { recursive: true });
4571
4815
  }
4572
- const validation = await validateWorktreeDoesNotExist(gitRoot, name);
4816
+ const validation = await validateWorktreeDoesNotExist(
4817
+ gitRoot,
4818
+ worktreeDirectory,
4819
+ name
4820
+ );
4573
4821
  if (isErr(validation)) {
4574
4822
  return err(validation.error);
4575
4823
  }
@@ -4595,6 +4843,31 @@ async function createWorktree(gitRoot, name, options = {}) {
4595
4843
  copyError = copyResult.error.message;
4596
4844
  }
4597
4845
  }
4846
+ if (postCreateCopyFiles && postCreateCopyFiles.length > 0) {
4847
+ const copyResult = await copyFilesToWorktree(
4848
+ gitRoot,
4849
+ worktreeDirectory,
4850
+ name,
4851
+ postCreateCopyFiles
4852
+ );
4853
+ if (isErr(copyResult)) {
4854
+ if (!copyError) {
4855
+ copyError = copyResult.error.message;
4856
+ }
4857
+ }
4858
+ }
4859
+ if (postCreateCommands && postCreateCommands.length > 0) {
4860
+ console.log("\nRunning post-create commands...");
4861
+ const commandsResult = await executePostCreateCommands({
4862
+ gitRoot,
4863
+ worktreesDirectory: worktreeDirectory,
4864
+ worktreeName: name,
4865
+ commands: postCreateCommands
4866
+ });
4867
+ if (isErr(commandsResult)) {
4868
+ return err(new WorktreeError(commandsResult.error.message));
4869
+ }
4870
+ }
4598
4871
  return ok({
4599
4872
  message: `Created worktree '${name}' at ${worktreePath}`,
4600
4873
  path: worktreePath,
@@ -4608,6 +4881,39 @@ async function createWorktree(gitRoot, name, options = {}) {
4608
4881
  }
4609
4882
  }
4610
4883
 
4884
+ // ../core/src/worktree/pre-delete.ts
4885
+ async function executePreDeleteCommands(options) {
4886
+ const { gitRoot, worktreesDirectory, worktreeName, commands: commands2 } = options;
4887
+ const executedCommands = [];
4888
+ for (const command2 of commands2) {
4889
+ console.log(`Executing pre-delete command: ${command2}`);
4890
+ const shell = process.env.SHELL || "/bin/sh";
4891
+ const cmdResult = await execInWorktree(
4892
+ gitRoot,
4893
+ worktreesDirectory,
4894
+ worktreeName,
4895
+ [shell, "-c", command2]
4896
+ );
4897
+ if (isErr(cmdResult)) {
4898
+ const errorMessage = cmdResult.error instanceof Error ? cmdResult.error.message : String(cmdResult.error);
4899
+ return err(
4900
+ new Error(
4901
+ `Failed to execute pre-delete command "${command2}": ${errorMessage}`
4902
+ )
4903
+ );
4904
+ }
4905
+ if (cmdResult.value.exitCode !== 0) {
4906
+ return err(
4907
+ new Error(
4908
+ `Pre-delete command failed with exit code ${cmdResult.value.exitCode}: ${command2}`
4909
+ )
4910
+ );
4911
+ }
4912
+ executedCommands.push(command2);
4913
+ }
4914
+ return ok({ executedCommands });
4915
+ }
4916
+
4611
4917
  // ../core/src/worktree/delete.ts
4612
4918
  async function getWorktreeChangesStatus(worktreePath) {
4613
4919
  try {
@@ -4629,19 +4935,14 @@ async function getWorktreeChangesStatus(worktreePath) {
4629
4935
  };
4630
4936
  }
4631
4937
  async function removeWorktree(gitRoot, worktreePath, force = false) {
4632
- try {
4633
- await executeGitCommand(["worktree", "remove", worktreePath], {
4634
- cwd: gitRoot
4635
- });
4636
- } catch (error) {
4637
- try {
4638
- await executeGitCommand(["worktree", "remove", "--force", worktreePath], {
4639
- cwd: gitRoot
4640
- });
4641
- } catch {
4642
- throw new Error("Failed to remove worktree");
4643
- }
4938
+ const args2 = ["worktree", "remove"];
4939
+ if (force) {
4940
+ args2.push("--force");
4644
4941
  }
4942
+ args2.push(worktreePath);
4943
+ await executeGitCommand(args2, {
4944
+ cwd: gitRoot
4945
+ });
4645
4946
  }
4646
4947
  async function deleteBranch(gitRoot, branchName) {
4647
4948
  try {
@@ -4652,9 +4953,13 @@ async function deleteBranch(gitRoot, branchName) {
4652
4953
  return err(new WorktreeError(`branch delete failed: ${errorMessage}`));
4653
4954
  }
4654
4955
  }
4655
- async function deleteWorktree(gitRoot, name, options = {}) {
4656
- const { force = false } = options;
4657
- const validation = await validateWorktreeExists(gitRoot, name);
4956
+ async function deleteWorktree(gitRoot, worktreeDirectory, name, options, preDeleteCommands) {
4957
+ const { force = false } = options || {};
4958
+ const validation = await validateWorktreeExists(
4959
+ gitRoot,
4960
+ worktreeDirectory,
4961
+ name
4962
+ );
4658
4963
  if (isErr(validation)) {
4659
4964
  return err(validation.error);
4660
4965
  }
@@ -4667,6 +4972,18 @@ async function deleteWorktree(gitRoot, name, options = {}) {
4667
4972
  )
4668
4973
  );
4669
4974
  }
4975
+ if (preDeleteCommands && preDeleteCommands.length > 0) {
4976
+ console.log("\nRunning pre-delete commands...");
4977
+ const preDeleteResult = await executePreDeleteCommands({
4978
+ gitRoot,
4979
+ worktreesDirectory: worktreeDirectory,
4980
+ worktreeName: name,
4981
+ commands: preDeleteCommands
4982
+ });
4983
+ if (isErr(preDeleteResult)) {
4984
+ return err(new WorktreeError(preDeleteResult.error.message));
4985
+ }
4986
+ }
4670
4987
  try {
4671
4988
  await removeWorktree(gitRoot, worktreePath, force);
4672
4989
  const branchName = name;
@@ -4706,12 +5023,11 @@ async function getWorktreeStatus(worktreePath) {
4706
5023
  return true;
4707
5024
  }
4708
5025
  }
4709
- async function listWorktrees2(gitRoot) {
5026
+ async function listWorktrees2(gitRoot, worktreeDirectory) {
4710
5027
  try {
4711
5028
  const gitWorktrees = await listWorktrees(gitRoot);
4712
- const phantomDir = getPhantomDirectory(gitRoot);
4713
5029
  const phantomWorktrees = gitWorktrees.filter(
4714
- (worktree) => worktree.path.startsWith(phantomDir)
5030
+ (worktree) => worktree.path.startsWith(worktreeDirectory)
4715
5031
  );
4716
5032
  if (phantomWorktrees.length === 0) {
4717
5033
  return ok({
@@ -4721,7 +5037,7 @@ async function listWorktrees2(gitRoot) {
4721
5037
  }
4722
5038
  const worktrees = await Promise.all(
4723
5039
  phantomWorktrees.map(async (gitWorktree) => {
4724
- const name = gitWorktree.path.substring(phantomDir.length + 1);
5040
+ const name = gitWorktree.path.substring(worktreeDirectory.length + 1);
4725
5041
  const isClean = await getWorktreeStatus(gitWorktree.path);
4726
5042
  return {
4727
5043
  name,
@@ -4742,12 +5058,12 @@ async function listWorktrees2(gitRoot) {
4742
5058
 
4743
5059
  // ../core/src/worktree/attach.ts
4744
5060
  import { existsSync } from "node:fs";
4745
- async function attachWorktreeCore(gitRoot, name) {
5061
+ async function attachWorktreeCore(gitRoot, worktreeDirectory, name, postCreateCopyFiles, postCreateCommands) {
4746
5062
  const validation = validateWorktreeName(name);
4747
5063
  if (isErr(validation)) {
4748
5064
  return validation;
4749
5065
  }
4750
- const worktreePath = getWorktreePath(gitRoot, name);
5066
+ const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4751
5067
  if (existsSync(worktreePath)) {
4752
5068
  return err(new WorktreeAlreadyExistsError(name));
4753
5069
  }
@@ -4762,12 +5078,41 @@ async function attachWorktreeCore(gitRoot, name) {
4762
5078
  if (isErr(attachResult)) {
4763
5079
  return err(attachResult.error);
4764
5080
  }
5081
+ if (postCreateCopyFiles && postCreateCopyFiles.length > 0) {
5082
+ const copyResult = await copyFilesToWorktree(
5083
+ gitRoot,
5084
+ worktreeDirectory,
5085
+ name,
5086
+ postCreateCopyFiles
5087
+ );
5088
+ if (isErr(copyResult)) {
5089
+ console.warn(
5090
+ `Warning: Failed to copy some files: ${copyResult.error.message}`
5091
+ );
5092
+ }
5093
+ }
5094
+ if (postCreateCommands && postCreateCommands.length > 0) {
5095
+ console.log("\nRunning post-create commands...");
5096
+ const commandsResult = await executePostCreateCommands({
5097
+ gitRoot,
5098
+ worktreesDirectory: worktreeDirectory,
5099
+ worktreeName: name,
5100
+ commands: postCreateCommands
5101
+ });
5102
+ if (isErr(commandsResult)) {
5103
+ return err(new WorktreeError(commandsResult.error.message));
5104
+ }
5105
+ }
4765
5106
  return ok(worktreePath);
4766
5107
  }
4767
5108
 
4768
5109
  // ../core/src/worktree/where.ts
4769
- async function whereWorktree(gitRoot, name) {
4770
- const validation = await validateWorktreeExists(gitRoot, name);
5110
+ async function whereWorktree(gitRoot, worktreeDirectory, name) {
5111
+ const validation = await validateWorktreeExists(
5112
+ gitRoot,
5113
+ worktreeDirectory,
5114
+ name
5115
+ );
4771
5116
  if (isErr(validation)) {
4772
5117
  return err(validation.error);
4773
5118
  }
@@ -4776,169 +5121,9 @@ async function whereWorktree(gitRoot, name) {
4776
5121
  });
4777
5122
  }
4778
5123
 
4779
- // ../process/src/errors.ts
4780
- var ProcessError = class extends Error {
4781
- exitCode;
4782
- constructor(message, exitCode) {
4783
- super(message);
4784
- this.name = this.constructor.name;
4785
- this.exitCode = exitCode;
4786
- }
4787
- };
4788
- var ProcessExecutionError = class extends ProcessError {
4789
- constructor(command2, exitCode) {
4790
- super(`Command '${command2}' failed with exit code ${exitCode}`, exitCode);
4791
- this.name = "ProcessExecutionError";
4792
- }
4793
- };
4794
- var ProcessSignalError = class extends ProcessError {
4795
- constructor(signal) {
4796
- const exitCode = 128 + (signal === "SIGTERM" ? 15 : 1);
4797
- super(`Command terminated by signal: ${signal}`, exitCode);
4798
- this.name = "ProcessSignalError";
4799
- }
4800
- };
4801
- var ProcessSpawnError = class extends ProcessError {
4802
- constructor(command2, details) {
4803
- super(`Error executing command '${command2}': ${details}`);
4804
- this.name = "ProcessSpawnError";
4805
- }
4806
- };
4807
-
4808
- // ../process/src/spawn.ts
4809
- import {
4810
- spawn as nodeSpawn
4811
- } from "node:child_process";
4812
- async function spawnProcess(config) {
4813
- return new Promise((resolve2) => {
4814
- const { command: command2, args: args2 = [], options = {} } = config;
4815
- const childProcess = nodeSpawn(command2, args2, {
4816
- stdio: "inherit",
4817
- ...options
4818
- });
4819
- childProcess.on("error", (error) => {
4820
- resolve2(err(new ProcessSpawnError(command2, error.message)));
4821
- });
4822
- childProcess.on("exit", (code, signal) => {
4823
- if (signal) {
4824
- resolve2(err(new ProcessSignalError(signal)));
4825
- } else {
4826
- const exitCode = code ?? 0;
4827
- if (exitCode === 0) {
4828
- resolve2(ok({ exitCode }));
4829
- } else {
4830
- resolve2(err(new ProcessExecutionError(command2, exitCode)));
4831
- }
4832
- }
4833
- });
4834
- });
4835
- }
4836
-
4837
- // ../process/src/tmux.ts
4838
- async function isInsideTmux() {
4839
- return process.env.TMUX !== void 0;
4840
- }
4841
- async function executeTmuxCommand(options) {
4842
- const { direction, command: command2, args: args2, cwd, env, windowName } = options;
4843
- const tmuxArgs = [];
4844
- switch (direction) {
4845
- case "new":
4846
- tmuxArgs.push("new-window");
4847
- if (windowName) {
4848
- tmuxArgs.push("-n", windowName);
4849
- }
4850
- break;
4851
- case "vertical":
4852
- tmuxArgs.push("split-window", "-v");
4853
- break;
4854
- case "horizontal":
4855
- tmuxArgs.push("split-window", "-h");
4856
- break;
4857
- }
4858
- if (cwd) {
4859
- tmuxArgs.push("-c", cwd);
4860
- }
4861
- if (env) {
4862
- for (const [key, value] of Object.entries(env)) {
4863
- tmuxArgs.push("-e", `${key}=${value}`);
4864
- }
4865
- }
4866
- tmuxArgs.push(command2);
4867
- if (args2 && args2.length > 0) {
4868
- tmuxArgs.push(...args2);
4869
- }
4870
- const result = await spawnProcess({
4871
- command: "tmux",
4872
- args: tmuxArgs
4873
- });
4874
- return result;
4875
- }
4876
-
4877
- // ../process/src/env.ts
4878
- function getPhantomEnv(worktreeName, worktreePath) {
4879
- return {
4880
- PHANTOM: "1",
4881
- PHANTOM_NAME: worktreeName,
4882
- PHANTOM_PATH: worktreePath
4883
- };
4884
- }
4885
-
4886
- // ../process/src/fzf.ts
4887
- import { spawn } from "node:child_process";
4888
- async function selectWithFzf(items, options = {}) {
4889
- return new Promise((resolve2) => {
4890
- const args2 = [];
4891
- if (options.prompt) {
4892
- args2.push("--prompt", options.prompt);
4893
- }
4894
- if (options.header) {
4895
- args2.push("--header", options.header);
4896
- }
4897
- if (options.previewCommand) {
4898
- args2.push("--preview", options.previewCommand);
4899
- }
4900
- const fzf = spawn("fzf", args2, {
4901
- stdio: ["pipe", "pipe", "pipe"]
4902
- });
4903
- let result = "";
4904
- let errorOutput = "";
4905
- fzf.stdout.on("data", (data) => {
4906
- result += data.toString();
4907
- });
4908
- if (fzf.stderr) {
4909
- fzf.stderr.on("data", (data) => {
4910
- errorOutput += data.toString();
4911
- });
4912
- }
4913
- fzf.on("error", (error) => {
4914
- if (error.message.includes("ENOENT")) {
4915
- resolve2(
4916
- err(new Error("fzf command not found. Please install fzf first."))
4917
- );
4918
- } else {
4919
- resolve2(err(error));
4920
- }
4921
- });
4922
- fzf.on("close", (code) => {
4923
- if (code === 0) {
4924
- const selected = result.trim();
4925
- resolve2(ok(selected || null));
4926
- } else if (code === 1) {
4927
- resolve2(ok(null));
4928
- } else if (code === 130) {
4929
- resolve2(ok(null));
4930
- } else {
4931
- resolve2(err(new Error(`fzf exited with code ${code}: ${errorOutput}`)));
4932
- }
4933
- });
4934
- fzf.stdin.write(items.join("\n"));
4935
- fzf.stdin.end();
4936
- });
4937
- }
4938
-
4939
5124
  // ../core/src/worktree/select.ts
4940
- async function selectWorktreeWithFzf(gitRoot) {
4941
- const listResult = await listWorktrees2(gitRoot);
5125
+ async function selectWorktreeWithFzf(gitRoot, worktreeDirectory) {
5126
+ const listResult = await listWorktrees2(gitRoot, worktreeDirectory);
4942
5127
  if (isErr(listResult)) {
4943
5128
  return listResult;
4944
5129
  }
@@ -4985,28 +5170,13 @@ async function selectWorktreeWithFzf(gitRoot) {
4985
5170
  };
4986
5171
  }
4987
5172
 
4988
- // ../core/src/exec.ts
4989
- async function execInWorktree(gitRoot, worktreeName, command2, options = {}) {
4990
- const validation = await validateWorktreeExists(gitRoot, worktreeName);
4991
- if (isErr(validation)) {
4992
- return err(validation.error);
4993
- }
4994
- const worktreePath = validation.value.path;
4995
- const [cmd, ...args2] = command2;
4996
- const stdio = options.interactive ? "inherit" : ["ignore", "inherit", "inherit"];
4997
- return spawnProcess({
4998
- command: cmd,
4999
- args: args2,
5000
- options: {
5001
- cwd: worktreePath,
5002
- stdio
5003
- }
5004
- });
5005
- }
5006
-
5007
5173
  // ../core/src/shell.ts
5008
- async function shellInWorktree(gitRoot, worktreeName) {
5009
- const validation = await validateWorktreeExists(gitRoot, worktreeName);
5174
+ async function shellInWorktree(gitRoot, worktreeDirectory, worktreeName) {
5175
+ const validation = await validateWorktreeExists(
5176
+ gitRoot,
5177
+ worktreeDirectory,
5178
+ worktreeName
5179
+ );
5010
5180
  if (isErr(validation)) {
5011
5181
  return err(validation.error);
5012
5182
  }
@@ -5085,7 +5255,14 @@ async function attachHandler(args2) {
5085
5255
  );
5086
5256
  }
5087
5257
  const gitRoot = await getGitRoot();
5088
- const result = await attachWorktreeCore(gitRoot, branchName);
5258
+ const context = await createContext(gitRoot);
5259
+ const result = await attachWorktreeCore(
5260
+ context.gitRoot,
5261
+ context.worktreesDirectory,
5262
+ branchName,
5263
+ context.config?.postCreate?.copyFiles,
5264
+ context.config?.postCreate?.commands
5265
+ );
5089
5266
  if (isErr(result)) {
5090
5267
  const error = result.error;
5091
5268
  if (error instanceof WorktreeAlreadyExistsError) {
@@ -5096,17 +5273,21 @@ async function attachHandler(args2) {
5096
5273
  }
5097
5274
  exitWithError(error.message, exitCodes.generalError);
5098
5275
  }
5099
- const worktreePath = result.value;
5100
5276
  output.log(`Attached phantom: ${branchName}`);
5101
5277
  if (values.shell) {
5102
- const shellResult = await shellInWorktree(gitRoot, branchName);
5278
+ const shellResult = await shellInWorktree(
5279
+ context.gitRoot,
5280
+ context.worktreesDirectory,
5281
+ branchName
5282
+ );
5103
5283
  if (isErr(shellResult)) {
5104
5284
  exitWithError(shellResult.error.message, exitCodes.generalError);
5105
5285
  }
5106
5286
  } else if (values.exec) {
5107
5287
  const shell = process.env.SHELL || "/bin/sh";
5108
5288
  const execResult = await execInWorktree(
5109
- gitRoot,
5289
+ context.gitRoot,
5290
+ context.worktreesDirectory,
5110
5291
  branchName,
5111
5292
  [shell, "-c", values.exec],
5112
5293
  { interactive: true }
@@ -5618,27 +5799,26 @@ async function createHandler(args2) {
5618
5799
  }
5619
5800
  try {
5620
5801
  const gitRoot = await getGitRoot();
5802
+ const context = await createContext(gitRoot);
5621
5803
  let filesToCopy = [];
5622
- const configResult = await loadConfig(gitRoot);
5623
- if (isOk(configResult)) {
5624
- if (configResult.value.postCreate?.copyFiles) {
5625
- filesToCopy = [...configResult.value.postCreate.copyFiles];
5626
- }
5627
- } else {
5628
- if (configResult.error instanceof ConfigValidationError) {
5629
- output.warn(`Configuration warning: ${configResult.error.message}`);
5630
- } else if (configResult.error instanceof ConfigParseError) {
5631
- output.warn(`Configuration warning: ${configResult.error.message}`);
5632
- }
5804
+ if (context.config?.postCreate?.copyFiles) {
5805
+ filesToCopy = [...context.config.postCreate.copyFiles];
5633
5806
  }
5634
5807
  if (copyFileOptions && copyFileOptions.length > 0) {
5635
5808
  const cliFiles = Array.isArray(copyFileOptions) ? copyFileOptions : [copyFileOptions];
5636
5809
  filesToCopy = [.../* @__PURE__ */ new Set([...filesToCopy, ...cliFiles])];
5637
5810
  }
5638
- const result = await createWorktree(gitRoot, worktreeName, {
5639
- copyFiles: filesToCopy.length > 0 ? filesToCopy : void 0,
5640
- base: baseOption
5641
- });
5811
+ const result = await createWorktree(
5812
+ context.gitRoot,
5813
+ context.worktreesDirectory,
5814
+ worktreeName,
5815
+ {
5816
+ copyFiles: filesToCopy.length > 0 ? filesToCopy : void 0,
5817
+ base: baseOption
5818
+ },
5819
+ filesToCopy.length > 0 ? filesToCopy : void 0,
5820
+ context.config?.postCreate?.commands
5821
+ );
5642
5822
  if (isErr(result)) {
5643
5823
  const exitCode = result.error instanceof WorktreeAlreadyExistsError ? exitCodes.validationError : exitCodes.generalError;
5644
5824
  exitWithError(result.error.message, exitCode);
@@ -5650,30 +5830,6 @@ async function createHandler(args2) {
5650
5830
  Warning: Failed to copy some files: ${result.value.copyError}`
5651
5831
  );
5652
5832
  }
5653
- if (isOk(configResult) && configResult.value.postCreate?.commands) {
5654
- const commands2 = configResult.value.postCreate.commands;
5655
- output.log("\nRunning post-create commands...");
5656
- for (const command2 of commands2) {
5657
- output.log(`Executing: ${command2}`);
5658
- const shell = process.env.SHELL || "/bin/sh";
5659
- const cmdResult = await execInWorktree(gitRoot, worktreeName, [
5660
- shell,
5661
- "-c",
5662
- command2
5663
- ]);
5664
- if (isErr(cmdResult)) {
5665
- output.error(`Failed to execute command: ${cmdResult.error.message}`);
5666
- const exitCode = "exitCode" in cmdResult.error ? cmdResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
5667
- exitWithError(`Post-create command failed: ${command2}`, exitCode);
5668
- }
5669
- if (cmdResult.value.exitCode !== 0) {
5670
- exitWithError(
5671
- `Post-create command failed: ${command2}`,
5672
- cmdResult.value.exitCode
5673
- );
5674
- }
5675
- }
5676
- }
5677
5833
  if (execCommand && isOk(result)) {
5678
5834
  output.log(
5679
5835
  `
@@ -5681,7 +5837,8 @@ Executing command in worktree '${worktreeName}': ${execCommand}`
5681
5837
  );
5682
5838
  const shell = process.env.SHELL || "/bin/sh";
5683
5839
  const execResult = await execInWorktree(
5684
- gitRoot,
5840
+ context.gitRoot,
5841
+ context.worktreesDirectory,
5685
5842
  worktreeName,
5686
5843
  [shell, "-c", execCommand],
5687
5844
  { interactive: true }
@@ -5699,7 +5856,11 @@ Executing command in worktree '${worktreeName}': ${execCommand}`
5699
5856
  Entering worktree '${worktreeName}' at ${result.value.path}`
5700
5857
  );
5701
5858
  output.log("Type 'exit' to return to your original directory\n");
5702
- const shellResult = await shellInWorktree(gitRoot, worktreeName);
5859
+ const shellResult = await shellInWorktree(
5860
+ context.gitRoot,
5861
+ context.worktreesDirectory,
5862
+ worktreeName
5863
+ );
5703
5864
  if (isErr(shellResult)) {
5704
5865
  output.error(shellResult.error.message);
5705
5866
  const exitCode = "exitCode" in shellResult.error ? shellResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
@@ -5779,6 +5940,7 @@ async function deleteHandler(args2) {
5779
5940
  const forceDelete = values.force ?? false;
5780
5941
  try {
5781
5942
  const gitRoot = await getGitRoot();
5943
+ const context = await createContext(gitRoot);
5782
5944
  let worktreeName;
5783
5945
  if (deleteCurrent) {
5784
5946
  const currentWorktree = await getCurrentWorktree(gitRoot);
@@ -5790,7 +5952,10 @@ async function deleteHandler(args2) {
5790
5952
  }
5791
5953
  worktreeName = currentWorktree;
5792
5954
  } else if (useFzf) {
5793
- const selectResult = await selectWorktreeWithFzf(gitRoot);
5955
+ const selectResult = await selectWorktreeWithFzf(
5956
+ context.gitRoot,
5957
+ context.worktreesDirectory
5958
+ );
5794
5959
  if (isErr(selectResult)) {
5795
5960
  exitWithError(selectResult.error.message, exitCodes.generalError);
5796
5961
  }
@@ -5801,9 +5966,15 @@ async function deleteHandler(args2) {
5801
5966
  } else {
5802
5967
  worktreeName = positionals[0];
5803
5968
  }
5804
- const result = await deleteWorktree(gitRoot, worktreeName, {
5805
- force: forceDelete
5806
- });
5969
+ const result = await deleteWorktree(
5970
+ context.gitRoot,
5971
+ context.worktreesDirectory,
5972
+ worktreeName,
5973
+ {
5974
+ force: forceDelete
5975
+ },
5976
+ context.config?.preDelete?.commands
5977
+ );
5807
5978
  if (isErr(result)) {
5808
5979
  const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.validationError : result.error instanceof WorktreeError && result.error.message.includes("uncommitted changes") ? exitCodes.validationError : exitCodes.generalError;
5809
5980
  exitWithError(result.error.message, exitCode);
@@ -5878,6 +6049,7 @@ async function execHandler(args2) {
5878
6049
  }
5879
6050
  try {
5880
6051
  const gitRoot = await getGitRoot();
6052
+ const context = await createContext(gitRoot);
5881
6053
  if (tmuxOption && !await isInsideTmux()) {
5882
6054
  exitWithError(
5883
6055
  "The --tmux option can only be used inside a tmux session",
@@ -5886,7 +6058,10 @@ async function execHandler(args2) {
5886
6058
  }
5887
6059
  let worktreeName;
5888
6060
  if (useFzf) {
5889
- const selectResult = await selectWorktreeWithFzf(gitRoot);
6061
+ const selectResult = await selectWorktreeWithFzf(
6062
+ context.gitRoot,
6063
+ context.worktreesDirectory
6064
+ );
5890
6065
  if (isErr(selectResult)) {
5891
6066
  exitWithError(selectResult.error.message, exitCodes.generalError);
5892
6067
  }
@@ -5897,7 +6072,11 @@ async function execHandler(args2) {
5897
6072
  } else {
5898
6073
  worktreeName = positionals[0];
5899
6074
  }
5900
- const validation = await validateWorktreeExists(gitRoot, worktreeName);
6075
+ const validation = await validateWorktreeExists(
6076
+ context.gitRoot,
6077
+ context.worktreesDirectory,
6078
+ worktreeName
6079
+ );
5901
6080
  if (isErr(validation)) {
5902
6081
  exitWithError(validation.error.message, exitCodes.generalError);
5903
6082
  }
@@ -5922,7 +6101,8 @@ async function execHandler(args2) {
5922
6101
  exitWithSuccess();
5923
6102
  }
5924
6103
  const result = await execInWorktree(
5925
- gitRoot,
6104
+ context.gitRoot,
6105
+ context.worktreesDirectory,
5926
6106
  worktreeName,
5927
6107
  commandArgs,
5928
6108
  { interactive: true }
@@ -9387,36 +9567,62 @@ async function checkoutIssue(issue, base) {
9387
9567
  );
9388
9568
  }
9389
9569
  const gitRoot = await getGitRoot();
9390
- const worktreeName = `issue-${issue.number}`;
9391
- const branchName = `issue-${issue.number}`;
9392
- const worktreePath = getWorktreePath(gitRoot, worktreeName);
9393
- const result = await createWorktree(gitRoot, worktreeName, {
9394
- branch: branchName,
9395
- base
9396
- });
9570
+ const context = await createContext(gitRoot);
9571
+ const worktreeName = `issues/${issue.number}`;
9572
+ const branchName = `issues/${issue.number}`;
9573
+ const existsResult = await validateWorktreeExists(
9574
+ context.gitRoot,
9575
+ context.worktreesDirectory,
9576
+ worktreeName
9577
+ );
9578
+ if (!isErr(existsResult)) {
9579
+ return ok({
9580
+ message: `Issue #${issue.number} is already checked out`,
9581
+ worktree: worktreeName,
9582
+ path: existsResult.value.path,
9583
+ alreadyExists: true
9584
+ });
9585
+ }
9586
+ const result = await createWorktree(
9587
+ context.gitRoot,
9588
+ context.worktreesDirectory,
9589
+ worktreeName,
9590
+ {
9591
+ branch: branchName,
9592
+ base
9593
+ },
9594
+ context.config?.postCreate?.copyFiles,
9595
+ context.config?.postCreate?.commands
9596
+ );
9397
9597
  if (isErr(result)) {
9398
- if (result.error instanceof WorktreeAlreadyExistsError) {
9399
- return ok({
9400
- message: `Worktree for issue #${issue.number} is already checked out`,
9401
- worktree: worktreeName,
9402
- path: worktreePath,
9403
- alreadyExists: true
9404
- });
9405
- }
9406
9598
  return err(result.error);
9407
9599
  }
9408
9600
  return ok({
9409
9601
  message: result.value.message,
9410
9602
  worktree: worktreeName,
9411
- path: worktreePath
9603
+ path: result.value.path
9412
9604
  });
9413
9605
  }
9414
9606
 
9415
9607
  // ../github/src/checkout/pr.ts
9416
9608
  async function checkoutPullRequest(pullRequest) {
9417
9609
  const gitRoot = await getGitRoot();
9418
- const worktreeName = `pr-${pullRequest.number}`;
9419
- const localBranch = `pr-${pullRequest.number}`;
9610
+ const context = await createContext(gitRoot);
9611
+ const worktreeName = `pulls/${pullRequest.number}`;
9612
+ const localBranch = `pulls/${pullRequest.number}`;
9613
+ const existsResult = await validateWorktreeExists(
9614
+ context.gitRoot,
9615
+ context.worktreesDirectory,
9616
+ worktreeName
9617
+ );
9618
+ if (!isErr(existsResult)) {
9619
+ return ok({
9620
+ message: `PR #${pullRequest.number} is already checked out`,
9621
+ worktree: worktreeName,
9622
+ path: existsResult.value.path,
9623
+ alreadyExists: true
9624
+ });
9625
+ }
9420
9626
  const upstream = pullRequest.isFromFork ? `origin/pull/${pullRequest.number}/head` : `origin/${pullRequest.head.ref}`;
9421
9627
  const refspec = `${upstream.replace("origin/", "")}:${localBranch}`;
9422
9628
  const fetchResult = await fetch({ refspec });
@@ -9437,24 +9643,21 @@ async function checkoutPullRequest(pullRequest) {
9437
9643
  `Warning: Could not set upstream branch: ${setUpstreamResult.error.message}`
9438
9644
  );
9439
9645
  }
9440
- const attachResult = await attachWorktreeCore(gitRoot, worktreeName);
9441
- const worktreePath = getWorktreePath(gitRoot, worktreeName);
9646
+ const attachResult = await attachWorktreeCore(
9647
+ context.gitRoot,
9648
+ context.worktreesDirectory,
9649
+ worktreeName,
9650
+ context.config?.postCreate?.copyFiles,
9651
+ context.config?.postCreate?.commands
9652
+ );
9442
9653
  if (isErr(attachResult)) {
9443
- if (attachResult.error instanceof WorktreeAlreadyExistsError) {
9444
- return ok({
9445
- message: `Worktree for PR #${pullRequest.number} is already checked out`,
9446
- worktree: worktreeName,
9447
- path: worktreePath,
9448
- alreadyExists: true
9449
- });
9450
- }
9451
9654
  return err(attachResult.error);
9452
9655
  }
9453
9656
  const message = pullRequest.isFromFork ? `Checked out PR #${pullRequest.number} from fork ${pullRequest.head.repo.full_name}` : `Checked out PR #${pullRequest.number} from branch ${pullRequest.head.ref}`;
9454
9657
  return ok({
9455
9658
  message,
9456
9659
  worktree: worktreeName,
9457
- path: worktreePath
9660
+ path: attachResult.value
9458
9661
  });
9459
9662
  }
9460
9663
 
@@ -9707,8 +9910,8 @@ var githubCheckoutHelp = {
9707
9910
  }
9708
9911
  ],
9709
9912
  notes: [
9710
- "For PRs: Creates worktree named 'pr-{number}' with the PR's branch",
9711
- "For Issues: Creates worktree named 'issue-{number}' with a new branch",
9913
+ "For PRs: Creates worktree named 'pulls/{number}' with the PR's branch",
9914
+ "For Issues: Creates worktree named 'issues/{number}' with a new branch",
9712
9915
  "",
9713
9916
  "Requirements:",
9714
9917
  " - GitHub CLI (gh) must be installed",
@@ -9745,8 +9948,12 @@ async function listHandler(args2 = []) {
9745
9948
  });
9746
9949
  try {
9747
9950
  const gitRoot = await getGitRoot();
9951
+ const context = await createContext(gitRoot);
9748
9952
  if (values.fzf) {
9749
- const selectResult = await selectWorktreeWithFzf(gitRoot);
9953
+ const selectResult = await selectWorktreeWithFzf(
9954
+ context.gitRoot,
9955
+ context.worktreesDirectory
9956
+ );
9750
9957
  if (isErr(selectResult)) {
9751
9958
  exitWithError(selectResult.error.message, exitCodes.generalError);
9752
9959
  }
@@ -9754,7 +9961,10 @@ async function listHandler(args2 = []) {
9754
9961
  output.log(selectResult.value.name);
9755
9962
  }
9756
9963
  } else {
9757
- const result = await listWorktrees2(gitRoot);
9964
+ const result = await listWorktrees2(
9965
+ context.gitRoot,
9966
+ context.worktreesDirectory
9967
+ );
9758
9968
  if (isErr(result)) {
9759
9969
  exitWithError("Failed to list worktrees", exitCodes.generalError);
9760
9970
  }
@@ -13057,7 +13267,7 @@ var StdioServerTransport = class {
13057
13267
  // ../mcp/package.json
13058
13268
  var package_default = {
13059
13269
  name: "@aku11i/phantom-mcp",
13060
- version: "1.2.0",
13270
+ version: "2.0.0",
13061
13271
  private: true,
13062
13272
  type: "module",
13063
13273
  main: "./src/index.ts",
@@ -13092,10 +13302,18 @@ var createWorktreeTool = {
13092
13302
  inputSchema: schema,
13093
13303
  handler: async ({ name, baseBranch }) => {
13094
13304
  const gitRoot = await getGitRoot();
13095
- const result = await createWorktree(gitRoot, name, {
13096
- branch: name,
13097
- base: baseBranch
13098
- });
13305
+ const context = await createContext(gitRoot);
13306
+ const result = await createWorktree(
13307
+ context.gitRoot,
13308
+ context.worktreesDirectory,
13309
+ name,
13310
+ {
13311
+ branch: name,
13312
+ base: baseBranch
13313
+ },
13314
+ context.config?.postCreate?.copyFiles,
13315
+ context.config?.postCreate?.commands
13316
+ );
13099
13317
  if (!isOk(result)) {
13100
13318
  throw new Error(result.error.message);
13101
13319
  }
@@ -13130,7 +13348,16 @@ var deleteWorktreeTool = {
13130
13348
  inputSchema: schema2,
13131
13349
  handler: async ({ name, force }) => {
13132
13350
  const gitRoot = await getGitRoot();
13133
- const result = await deleteWorktree(gitRoot, name, { force });
13351
+ const context = await createContext(gitRoot);
13352
+ const result = await deleteWorktree(
13353
+ context.gitRoot,
13354
+ context.worktreesDirectory,
13355
+ name,
13356
+ {
13357
+ force
13358
+ },
13359
+ context.config?.preDelete?.commands
13360
+ );
13134
13361
  if (!isOk(result)) {
13135
13362
  throw new Error(result.error.message);
13136
13363
  }
@@ -13195,7 +13422,11 @@ var listWorktreesTool = {
13195
13422
  inputSchema: schema4,
13196
13423
  handler: async () => {
13197
13424
  const gitRoot = await getGitRoot();
13198
- const result = await listWorktrees2(gitRoot);
13425
+ const context = await createContext(gitRoot);
13426
+ const result = await listWorktrees2(
13427
+ context.gitRoot,
13428
+ context.worktreesDirectory
13429
+ );
13199
13430
  if (!isOk(result)) {
13200
13431
  throw new Error("Failed to list worktrees");
13201
13432
  }
@@ -13370,6 +13601,7 @@ async function shellHandler(args2) {
13370
13601
  let worktreeName;
13371
13602
  try {
13372
13603
  const gitRoot = await getGitRoot();
13604
+ const context = await createContext(gitRoot);
13373
13605
  if (tmuxOption && !await isInsideTmux()) {
13374
13606
  exitWithError(
13375
13607
  "The --tmux option can only be used inside a tmux session",
@@ -13377,7 +13609,10 @@ async function shellHandler(args2) {
13377
13609
  );
13378
13610
  }
13379
13611
  if (useFzf) {
13380
- const selectResult = await selectWorktreeWithFzf(gitRoot);
13612
+ const selectResult = await selectWorktreeWithFzf(
13613
+ context.gitRoot,
13614
+ context.worktreesDirectory
13615
+ );
13381
13616
  if (isErr(selectResult)) {
13382
13617
  exitWithError(selectResult.error.message, exitCodes.generalError);
13383
13618
  }
@@ -13388,7 +13623,11 @@ async function shellHandler(args2) {
13388
13623
  } else {
13389
13624
  worktreeName = positionals[0];
13390
13625
  }
13391
- const validation = await validateWorktreeExists(gitRoot, worktreeName);
13626
+ const validation = await validateWorktreeExists(
13627
+ context.gitRoot,
13628
+ context.worktreesDirectory,
13629
+ worktreeName
13630
+ );
13392
13631
  if (isErr(validation)) {
13393
13632
  exitWithError(validation.error.message, exitCodes.generalError);
13394
13633
  }
@@ -13415,7 +13654,11 @@ async function shellHandler(args2) {
13415
13654
  `Entering worktree '${worktreeName}' at ${validation.value.path}`
13416
13655
  );
13417
13656
  output.log("Type 'exit' to return to your original directory\n");
13418
- const result = await shellInWorktree(gitRoot, worktreeName);
13657
+ const result = await shellInWorktree(
13658
+ context.gitRoot,
13659
+ context.worktreesDirectory,
13660
+ worktreeName
13661
+ );
13419
13662
  if (isErr(result)) {
13420
13663
  const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.notFound : result.error.exitCode || exitCodes.generalError;
13421
13664
  exitWithError(result.error.message, exitCode);
@@ -13435,7 +13678,7 @@ import { parseArgs as parseArgs9 } from "node:util";
13435
13678
  // package.json
13436
13679
  var package_default2 = {
13437
13680
  name: "@aku11i/phantom-cli",
13438
- version: "1.2.0",
13681
+ version: "2.0.0",
13439
13682
  private: true,
13440
13683
  type: "module",
13441
13684
  scripts: {
@@ -13511,8 +13754,12 @@ async function whereHandler(args2) {
13511
13754
  exitCodes.generalError
13512
13755
  );
13513
13756
  }
13757
+ const context = await createContext(gitRoot);
13514
13758
  if (useFzf) {
13515
- const selectResult = await selectWorktreeWithFzf(gitRoot);
13759
+ const selectResult = await selectWorktreeWithFzf(
13760
+ context.gitRoot,
13761
+ context.worktreesDirectory
13762
+ );
13516
13763
  if (isErr(selectResult)) {
13517
13764
  exitWithError(selectResult.error.message, exitCodes.generalError);
13518
13765
  }
@@ -13523,7 +13770,11 @@ async function whereHandler(args2) {
13523
13770
  } else {
13524
13771
  worktreeName = positionals[0];
13525
13772
  }
13526
- const result = await whereWorktree(gitRoot, worktreeName);
13773
+ const result = await whereWorktree(
13774
+ context.gitRoot,
13775
+ context.worktreesDirectory,
13776
+ worktreeName
13777
+ );
13527
13778
  if (isErr(result)) {
13528
13779
  exitWithError(result.error.message, exitCodes.notFound);
13529
13780
  }