@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.
- package/package.json +4 -1
- package/phantom.js +581 -330
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aku11i/phantom",
|
|
3
|
-
"version": "
|
|
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(
|
|
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(
|
|
4523
|
-
const worktreePath =
|
|
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(
|
|
4532
|
-
const worktreePath =
|
|
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
|
|
4566
|
-
const worktreePath = getWorktreePath(gitRoot, name);
|
|
4810
|
+
const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
|
|
4567
4811
|
try {
|
|
4568
|
-
await fs3.access(
|
|
4812
|
+
await fs3.access(worktreeDirectory);
|
|
4569
4813
|
} catch {
|
|
4570
|
-
await fs3.mkdir(
|
|
4814
|
+
await fs3.mkdir(worktreeDirectory, { recursive: true });
|
|
4571
4815
|
}
|
|
4572
|
-
const validation = await validateWorktreeDoesNotExist(
|
|
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
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
5623
|
-
|
|
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(
|
|
5639
|
-
|
|
5640
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
5805
|
-
|
|
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(
|
|
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(
|
|
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
|
|
9391
|
-
const
|
|
9392
|
-
const
|
|
9393
|
-
const
|
|
9394
|
-
|
|
9395
|
-
|
|
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:
|
|
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
|
|
9419
|
-
const
|
|
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(
|
|
9441
|
-
|
|
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:
|
|
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 '
|
|
9711
|
-
"For Issues: Creates worktree named '
|
|
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(
|
|
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(
|
|
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: "
|
|
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
|
|
13096
|
-
|
|
13097
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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: "
|
|
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(
|
|
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(
|
|
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
|
}
|