@aku11i/phantom 1.2.0 → 1.3.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 +1 -1
- package/phantom.js +499 -293
package/package.json
CHANGED
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,7 +4202,8 @@ 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
|
+
worktreesDirectory: external_exports.string().optional()
|
|
4215
4207
|
}).passthrough();
|
|
4216
4208
|
function validateConfig(config) {
|
|
4217
4209
|
const result = phantomConfigSchema.safeParse(config);
|
|
@@ -4265,6 +4257,30 @@ async function loadConfig(gitRoot) {
|
|
|
4265
4257
|
}
|
|
4266
4258
|
}
|
|
4267
4259
|
|
|
4260
|
+
// ../core/src/paths.ts
|
|
4261
|
+
import { isAbsolute, join } from "node:path";
|
|
4262
|
+
function getWorktreesDirectory(gitRoot, worktreesDirectory) {
|
|
4263
|
+
if (worktreesDirectory) {
|
|
4264
|
+
return isAbsolute(worktreesDirectory) ? worktreesDirectory : join(gitRoot, worktreesDirectory);
|
|
4265
|
+
}
|
|
4266
|
+
return join(gitRoot, ".git", "phantom", "worktrees");
|
|
4267
|
+
}
|
|
4268
|
+
function getWorktreePathFromDirectory(worktreeDirectory, name) {
|
|
4269
|
+
return join(worktreeDirectory, name);
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
// ../core/src/context.ts
|
|
4273
|
+
async function createContext(gitRoot) {
|
|
4274
|
+
const configResult = await loadConfig(gitRoot);
|
|
4275
|
+
const config = isOk(configResult) ? configResult.value : null;
|
|
4276
|
+
const worktreesDirectory = config?.worktreesDirectory;
|
|
4277
|
+
return {
|
|
4278
|
+
gitRoot,
|
|
4279
|
+
worktreesDirectory: getWorktreesDirectory(gitRoot, worktreesDirectory),
|
|
4280
|
+
config
|
|
4281
|
+
};
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4268
4284
|
// ../core/src/worktree/errors.ts
|
|
4269
4285
|
var WorktreeError = class extends Error {
|
|
4270
4286
|
constructor(message) {
|
|
@@ -4517,10 +4533,170 @@ async function copyFiles(sourceDir, targetDir, files) {
|
|
|
4517
4533
|
return ok({ copiedFiles, skippedFiles });
|
|
4518
4534
|
}
|
|
4519
4535
|
|
|
4536
|
+
// ../process/src/errors.ts
|
|
4537
|
+
var ProcessError = class extends Error {
|
|
4538
|
+
exitCode;
|
|
4539
|
+
constructor(message, exitCode) {
|
|
4540
|
+
super(message);
|
|
4541
|
+
this.name = this.constructor.name;
|
|
4542
|
+
this.exitCode = exitCode;
|
|
4543
|
+
}
|
|
4544
|
+
};
|
|
4545
|
+
var ProcessExecutionError = class extends ProcessError {
|
|
4546
|
+
constructor(command2, exitCode) {
|
|
4547
|
+
super(`Command '${command2}' failed with exit code ${exitCode}`, exitCode);
|
|
4548
|
+
this.name = "ProcessExecutionError";
|
|
4549
|
+
}
|
|
4550
|
+
};
|
|
4551
|
+
var ProcessSignalError = class extends ProcessError {
|
|
4552
|
+
constructor(signal) {
|
|
4553
|
+
const exitCode = 128 + (signal === "SIGTERM" ? 15 : 1);
|
|
4554
|
+
super(`Command terminated by signal: ${signal}`, exitCode);
|
|
4555
|
+
this.name = "ProcessSignalError";
|
|
4556
|
+
}
|
|
4557
|
+
};
|
|
4558
|
+
var ProcessSpawnError = class extends ProcessError {
|
|
4559
|
+
constructor(command2, details) {
|
|
4560
|
+
super(`Error executing command '${command2}': ${details}`);
|
|
4561
|
+
this.name = "ProcessSpawnError";
|
|
4562
|
+
}
|
|
4563
|
+
};
|
|
4564
|
+
|
|
4565
|
+
// ../process/src/spawn.ts
|
|
4566
|
+
import {
|
|
4567
|
+
spawn as nodeSpawn
|
|
4568
|
+
} from "node:child_process";
|
|
4569
|
+
async function spawnProcess(config) {
|
|
4570
|
+
return new Promise((resolve2) => {
|
|
4571
|
+
const { command: command2, args: args2 = [], options = {} } = config;
|
|
4572
|
+
const childProcess = nodeSpawn(command2, args2, {
|
|
4573
|
+
stdio: "inherit",
|
|
4574
|
+
...options
|
|
4575
|
+
});
|
|
4576
|
+
childProcess.on("error", (error) => {
|
|
4577
|
+
resolve2(err(new ProcessSpawnError(command2, error.message)));
|
|
4578
|
+
});
|
|
4579
|
+
childProcess.on("exit", (code, signal) => {
|
|
4580
|
+
if (signal) {
|
|
4581
|
+
resolve2(err(new ProcessSignalError(signal)));
|
|
4582
|
+
} else {
|
|
4583
|
+
const exitCode = code ?? 0;
|
|
4584
|
+
if (exitCode === 0) {
|
|
4585
|
+
resolve2(ok({ exitCode }));
|
|
4586
|
+
} else {
|
|
4587
|
+
resolve2(err(new ProcessExecutionError(command2, exitCode)));
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
});
|
|
4591
|
+
});
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
// ../process/src/tmux.ts
|
|
4595
|
+
async function isInsideTmux() {
|
|
4596
|
+
return process.env.TMUX !== void 0;
|
|
4597
|
+
}
|
|
4598
|
+
async function executeTmuxCommand(options) {
|
|
4599
|
+
const { direction, command: command2, args: args2, cwd, env, windowName } = options;
|
|
4600
|
+
const tmuxArgs = [];
|
|
4601
|
+
switch (direction) {
|
|
4602
|
+
case "new":
|
|
4603
|
+
tmuxArgs.push("new-window");
|
|
4604
|
+
if (windowName) {
|
|
4605
|
+
tmuxArgs.push("-n", windowName);
|
|
4606
|
+
}
|
|
4607
|
+
break;
|
|
4608
|
+
case "vertical":
|
|
4609
|
+
tmuxArgs.push("split-window", "-v");
|
|
4610
|
+
break;
|
|
4611
|
+
case "horizontal":
|
|
4612
|
+
tmuxArgs.push("split-window", "-h");
|
|
4613
|
+
break;
|
|
4614
|
+
}
|
|
4615
|
+
if (cwd) {
|
|
4616
|
+
tmuxArgs.push("-c", cwd);
|
|
4617
|
+
}
|
|
4618
|
+
if (env) {
|
|
4619
|
+
for (const [key, value] of Object.entries(env)) {
|
|
4620
|
+
tmuxArgs.push("-e", `${key}=${value}`);
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
tmuxArgs.push(command2);
|
|
4624
|
+
if (args2 && args2.length > 0) {
|
|
4625
|
+
tmuxArgs.push(...args2);
|
|
4626
|
+
}
|
|
4627
|
+
const result = await spawnProcess({
|
|
4628
|
+
command: "tmux",
|
|
4629
|
+
args: tmuxArgs
|
|
4630
|
+
});
|
|
4631
|
+
return result;
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4634
|
+
// ../process/src/env.ts
|
|
4635
|
+
function getPhantomEnv(worktreeName, worktreePath) {
|
|
4636
|
+
return {
|
|
4637
|
+
PHANTOM: "1",
|
|
4638
|
+
PHANTOM_NAME: worktreeName,
|
|
4639
|
+
PHANTOM_PATH: worktreePath
|
|
4640
|
+
};
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
// ../process/src/fzf.ts
|
|
4644
|
+
import { spawn } from "node:child_process";
|
|
4645
|
+
async function selectWithFzf(items, options = {}) {
|
|
4646
|
+
return new Promise((resolve2) => {
|
|
4647
|
+
const args2 = [];
|
|
4648
|
+
if (options.prompt) {
|
|
4649
|
+
args2.push("--prompt", options.prompt);
|
|
4650
|
+
}
|
|
4651
|
+
if (options.header) {
|
|
4652
|
+
args2.push("--header", options.header);
|
|
4653
|
+
}
|
|
4654
|
+
if (options.previewCommand) {
|
|
4655
|
+
args2.push("--preview", options.previewCommand);
|
|
4656
|
+
}
|
|
4657
|
+
const fzf = spawn("fzf", args2, {
|
|
4658
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4659
|
+
});
|
|
4660
|
+
let result = "";
|
|
4661
|
+
let errorOutput = "";
|
|
4662
|
+
fzf.stdout.on("data", (data) => {
|
|
4663
|
+
result += data.toString();
|
|
4664
|
+
});
|
|
4665
|
+
if (fzf.stderr) {
|
|
4666
|
+
fzf.stderr.on("data", (data) => {
|
|
4667
|
+
errorOutput += data.toString();
|
|
4668
|
+
});
|
|
4669
|
+
}
|
|
4670
|
+
fzf.on("error", (error) => {
|
|
4671
|
+
if (error.message.includes("ENOENT")) {
|
|
4672
|
+
resolve2(
|
|
4673
|
+
err(new Error("fzf command not found. Please install fzf first."))
|
|
4674
|
+
);
|
|
4675
|
+
} else {
|
|
4676
|
+
resolve2(err(error));
|
|
4677
|
+
}
|
|
4678
|
+
});
|
|
4679
|
+
fzf.on("close", (code) => {
|
|
4680
|
+
if (code === 0) {
|
|
4681
|
+
const selected = result.trim();
|
|
4682
|
+
resolve2(ok(selected || null));
|
|
4683
|
+
} else if (code === 1) {
|
|
4684
|
+
resolve2(ok(null));
|
|
4685
|
+
} else if (code === 130) {
|
|
4686
|
+
resolve2(ok(null));
|
|
4687
|
+
} else {
|
|
4688
|
+
resolve2(err(new Error(`fzf exited with code ${code}: ${errorOutput}`)));
|
|
4689
|
+
}
|
|
4690
|
+
});
|
|
4691
|
+
fzf.stdin.write(items.join("\n"));
|
|
4692
|
+
fzf.stdin.end();
|
|
4693
|
+
});
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4520
4696
|
// ../core/src/worktree/validate.ts
|
|
4521
4697
|
import fs2 from "node:fs/promises";
|
|
4522
|
-
async function validateWorktreeExists(gitRoot, name) {
|
|
4523
|
-
const worktreePath =
|
|
4698
|
+
async function validateWorktreeExists(gitRoot, worktreeDirectory, name) {
|
|
4699
|
+
const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
|
|
4524
4700
|
try {
|
|
4525
4701
|
await fs2.access(worktreePath);
|
|
4526
4702
|
return ok({ path: worktreePath });
|
|
@@ -4528,8 +4704,8 @@ async function validateWorktreeExists(gitRoot, name) {
|
|
|
4528
4704
|
return err(new WorktreeNotFoundError(name));
|
|
4529
4705
|
}
|
|
4530
4706
|
}
|
|
4531
|
-
async function validateWorktreeDoesNotExist(gitRoot, name) {
|
|
4532
|
-
const worktreePath =
|
|
4707
|
+
async function validateWorktreeDoesNotExist(gitRoot, worktreeDirectory, name) {
|
|
4708
|
+
const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
|
|
4533
4709
|
try {
|
|
4534
4710
|
await fs2.access(worktreePath);
|
|
4535
4711
|
return err(new WorktreeAlreadyExistsError(name));
|
|
@@ -4555,21 +4731,91 @@ function validateWorktreeName(name) {
|
|
|
4555
4731
|
return ok(void 0);
|
|
4556
4732
|
}
|
|
4557
4733
|
|
|
4734
|
+
// ../core/src/exec.ts
|
|
4735
|
+
async function execInWorktree(gitRoot, worktreeDirectory, worktreeName, command2, options) {
|
|
4736
|
+
const validation = await validateWorktreeExists(
|
|
4737
|
+
gitRoot,
|
|
4738
|
+
worktreeDirectory,
|
|
4739
|
+
worktreeName
|
|
4740
|
+
);
|
|
4741
|
+
if (isErr(validation)) {
|
|
4742
|
+
return err(validation.error);
|
|
4743
|
+
}
|
|
4744
|
+
const worktreePath = validation.value.path;
|
|
4745
|
+
const [cmd, ...args2] = command2;
|
|
4746
|
+
const stdio = options?.interactive ? "inherit" : ["ignore", "inherit", "inherit"];
|
|
4747
|
+
return spawnProcess({
|
|
4748
|
+
command: cmd,
|
|
4749
|
+
args: args2,
|
|
4750
|
+
options: {
|
|
4751
|
+
cwd: worktreePath,
|
|
4752
|
+
stdio
|
|
4753
|
+
}
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
// ../core/src/worktree/post-create.ts
|
|
4758
|
+
async function executePostCreateCommands(options) {
|
|
4759
|
+
const { gitRoot, worktreesDirectory, worktreeName, commands: commands2 } = options;
|
|
4760
|
+
const executedCommands = [];
|
|
4761
|
+
for (const command2 of commands2) {
|
|
4762
|
+
console.log(`Executing: ${command2}`);
|
|
4763
|
+
const shell = process.env.SHELL || "/bin/sh";
|
|
4764
|
+
const cmdResult = await execInWorktree(
|
|
4765
|
+
gitRoot,
|
|
4766
|
+
worktreesDirectory,
|
|
4767
|
+
worktreeName,
|
|
4768
|
+
[shell, "-c", command2]
|
|
4769
|
+
);
|
|
4770
|
+
if (isErr(cmdResult)) {
|
|
4771
|
+
const errorMessage = cmdResult.error instanceof Error ? cmdResult.error.message : String(cmdResult.error);
|
|
4772
|
+
return err(
|
|
4773
|
+
new Error(
|
|
4774
|
+
`Failed to execute post-create command "${command2}": ${errorMessage}`
|
|
4775
|
+
)
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4778
|
+
if (cmdResult.value.exitCode !== 0) {
|
|
4779
|
+
return err(
|
|
4780
|
+
new Error(
|
|
4781
|
+
`Post-create command failed with exit code ${cmdResult.value.exitCode}: ${command2}`
|
|
4782
|
+
)
|
|
4783
|
+
);
|
|
4784
|
+
}
|
|
4785
|
+
executedCommands.push(command2);
|
|
4786
|
+
}
|
|
4787
|
+
return ok({ executedCommands });
|
|
4788
|
+
}
|
|
4789
|
+
async function copyFilesToWorktree(gitRoot, worktreesDirectory, worktreeName, filesToCopy) {
|
|
4790
|
+
const worktreePath = getWorktreePathFromDirectory(
|
|
4791
|
+
worktreesDirectory,
|
|
4792
|
+
worktreeName
|
|
4793
|
+
);
|
|
4794
|
+
const copyResult = await copyFiles(gitRoot, worktreePath, filesToCopy);
|
|
4795
|
+
if (isErr(copyResult)) {
|
|
4796
|
+
return err(copyResult.error);
|
|
4797
|
+
}
|
|
4798
|
+
return ok(void 0);
|
|
4799
|
+
}
|
|
4800
|
+
|
|
4558
4801
|
// ../core/src/worktree/create.ts
|
|
4559
|
-
async function createWorktree(gitRoot, name, options
|
|
4802
|
+
async function createWorktree(gitRoot, worktreeDirectory, name, options, postCreateCopyFiles, postCreateCommands) {
|
|
4560
4803
|
const nameValidation = validateWorktreeName(name);
|
|
4561
4804
|
if (isErr(nameValidation)) {
|
|
4562
4805
|
return nameValidation;
|
|
4563
4806
|
}
|
|
4564
4807
|
const { branch = name, base = "HEAD" } = options;
|
|
4565
|
-
const
|
|
4566
|
-
const worktreePath = getWorktreePath(gitRoot, name);
|
|
4808
|
+
const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
|
|
4567
4809
|
try {
|
|
4568
|
-
await fs3.access(
|
|
4810
|
+
await fs3.access(worktreeDirectory);
|
|
4569
4811
|
} catch {
|
|
4570
|
-
await fs3.mkdir(
|
|
4812
|
+
await fs3.mkdir(worktreeDirectory, { recursive: true });
|
|
4571
4813
|
}
|
|
4572
|
-
const validation = await validateWorktreeDoesNotExist(
|
|
4814
|
+
const validation = await validateWorktreeDoesNotExist(
|
|
4815
|
+
gitRoot,
|
|
4816
|
+
worktreeDirectory,
|
|
4817
|
+
name
|
|
4818
|
+
);
|
|
4573
4819
|
if (isErr(validation)) {
|
|
4574
4820
|
return err(validation.error);
|
|
4575
4821
|
}
|
|
@@ -4595,6 +4841,31 @@ async function createWorktree(gitRoot, name, options = {}) {
|
|
|
4595
4841
|
copyError = copyResult.error.message;
|
|
4596
4842
|
}
|
|
4597
4843
|
}
|
|
4844
|
+
if (postCreateCopyFiles && postCreateCopyFiles.length > 0) {
|
|
4845
|
+
const copyResult = await copyFilesToWorktree(
|
|
4846
|
+
gitRoot,
|
|
4847
|
+
worktreeDirectory,
|
|
4848
|
+
name,
|
|
4849
|
+
postCreateCopyFiles
|
|
4850
|
+
);
|
|
4851
|
+
if (isErr(copyResult)) {
|
|
4852
|
+
if (!copyError) {
|
|
4853
|
+
copyError = copyResult.error.message;
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
if (postCreateCommands && postCreateCommands.length > 0) {
|
|
4858
|
+
console.log("\nRunning post-create commands...");
|
|
4859
|
+
const commandsResult = await executePostCreateCommands({
|
|
4860
|
+
gitRoot,
|
|
4861
|
+
worktreesDirectory: worktreeDirectory,
|
|
4862
|
+
worktreeName: name,
|
|
4863
|
+
commands: postCreateCommands
|
|
4864
|
+
});
|
|
4865
|
+
if (isErr(commandsResult)) {
|
|
4866
|
+
return err(new WorktreeError(commandsResult.error.message));
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4598
4869
|
return ok({
|
|
4599
4870
|
message: `Created worktree '${name}' at ${worktreePath}`,
|
|
4600
4871
|
path: worktreePath,
|
|
@@ -4652,9 +4923,13 @@ async function deleteBranch(gitRoot, branchName) {
|
|
|
4652
4923
|
return err(new WorktreeError(`branch delete failed: ${errorMessage}`));
|
|
4653
4924
|
}
|
|
4654
4925
|
}
|
|
4655
|
-
async function deleteWorktree(gitRoot, name, options
|
|
4656
|
-
const { force = false } = options;
|
|
4657
|
-
const validation = await validateWorktreeExists(
|
|
4926
|
+
async function deleteWorktree(gitRoot, worktreeDirectory, name, options) {
|
|
4927
|
+
const { force = false } = options || {};
|
|
4928
|
+
const validation = await validateWorktreeExists(
|
|
4929
|
+
gitRoot,
|
|
4930
|
+
worktreeDirectory,
|
|
4931
|
+
name
|
|
4932
|
+
);
|
|
4658
4933
|
if (isErr(validation)) {
|
|
4659
4934
|
return err(validation.error);
|
|
4660
4935
|
}
|
|
@@ -4706,12 +4981,11 @@ async function getWorktreeStatus(worktreePath) {
|
|
|
4706
4981
|
return true;
|
|
4707
4982
|
}
|
|
4708
4983
|
}
|
|
4709
|
-
async function listWorktrees2(gitRoot) {
|
|
4984
|
+
async function listWorktrees2(gitRoot, worktreeDirectory) {
|
|
4710
4985
|
try {
|
|
4711
4986
|
const gitWorktrees = await listWorktrees(gitRoot);
|
|
4712
|
-
const phantomDir = getPhantomDirectory(gitRoot);
|
|
4713
4987
|
const phantomWorktrees = gitWorktrees.filter(
|
|
4714
|
-
(worktree) => worktree.path.startsWith(
|
|
4988
|
+
(worktree) => worktree.path.startsWith(worktreeDirectory)
|
|
4715
4989
|
);
|
|
4716
4990
|
if (phantomWorktrees.length === 0) {
|
|
4717
4991
|
return ok({
|
|
@@ -4721,7 +4995,7 @@ async function listWorktrees2(gitRoot) {
|
|
|
4721
4995
|
}
|
|
4722
4996
|
const worktrees = await Promise.all(
|
|
4723
4997
|
phantomWorktrees.map(async (gitWorktree) => {
|
|
4724
|
-
const name = gitWorktree.path.substring(
|
|
4998
|
+
const name = gitWorktree.path.substring(worktreeDirectory.length + 1);
|
|
4725
4999
|
const isClean = await getWorktreeStatus(gitWorktree.path);
|
|
4726
5000
|
return {
|
|
4727
5001
|
name,
|
|
@@ -4742,12 +5016,12 @@ async function listWorktrees2(gitRoot) {
|
|
|
4742
5016
|
|
|
4743
5017
|
// ../core/src/worktree/attach.ts
|
|
4744
5018
|
import { existsSync } from "node:fs";
|
|
4745
|
-
async function attachWorktreeCore(gitRoot, name) {
|
|
5019
|
+
async function attachWorktreeCore(gitRoot, worktreeDirectory, name, postCreateCopyFiles, postCreateCommands) {
|
|
4746
5020
|
const validation = validateWorktreeName(name);
|
|
4747
5021
|
if (isErr(validation)) {
|
|
4748
5022
|
return validation;
|
|
4749
5023
|
}
|
|
4750
|
-
const worktreePath =
|
|
5024
|
+
const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
|
|
4751
5025
|
if (existsSync(worktreePath)) {
|
|
4752
5026
|
return err(new WorktreeAlreadyExistsError(name));
|
|
4753
5027
|
}
|
|
@@ -4762,12 +5036,41 @@ async function attachWorktreeCore(gitRoot, name) {
|
|
|
4762
5036
|
if (isErr(attachResult)) {
|
|
4763
5037
|
return err(attachResult.error);
|
|
4764
5038
|
}
|
|
5039
|
+
if (postCreateCopyFiles && postCreateCopyFiles.length > 0) {
|
|
5040
|
+
const copyResult = await copyFilesToWorktree(
|
|
5041
|
+
gitRoot,
|
|
5042
|
+
worktreeDirectory,
|
|
5043
|
+
name,
|
|
5044
|
+
postCreateCopyFiles
|
|
5045
|
+
);
|
|
5046
|
+
if (isErr(copyResult)) {
|
|
5047
|
+
console.warn(
|
|
5048
|
+
`Warning: Failed to copy some files: ${copyResult.error.message}`
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
if (postCreateCommands && postCreateCommands.length > 0) {
|
|
5053
|
+
console.log("\nRunning post-create commands...");
|
|
5054
|
+
const commandsResult = await executePostCreateCommands({
|
|
5055
|
+
gitRoot,
|
|
5056
|
+
worktreesDirectory: worktreeDirectory,
|
|
5057
|
+
worktreeName: name,
|
|
5058
|
+
commands: postCreateCommands
|
|
5059
|
+
});
|
|
5060
|
+
if (isErr(commandsResult)) {
|
|
5061
|
+
return err(new WorktreeError(commandsResult.error.message));
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
4765
5064
|
return ok(worktreePath);
|
|
4766
5065
|
}
|
|
4767
5066
|
|
|
4768
5067
|
// ../core/src/worktree/where.ts
|
|
4769
|
-
async function whereWorktree(gitRoot, name) {
|
|
4770
|
-
const validation = await validateWorktreeExists(
|
|
5068
|
+
async function whereWorktree(gitRoot, worktreeDirectory, name) {
|
|
5069
|
+
const validation = await validateWorktreeExists(
|
|
5070
|
+
gitRoot,
|
|
5071
|
+
worktreeDirectory,
|
|
5072
|
+
name
|
|
5073
|
+
);
|
|
4771
5074
|
if (isErr(validation)) {
|
|
4772
5075
|
return err(validation.error);
|
|
4773
5076
|
}
|
|
@@ -4776,169 +5079,9 @@ async function whereWorktree(gitRoot, name) {
|
|
|
4776
5079
|
});
|
|
4777
5080
|
}
|
|
4778
5081
|
|
|
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
5082
|
// ../core/src/worktree/select.ts
|
|
4940
|
-
async function selectWorktreeWithFzf(gitRoot) {
|
|
4941
|
-
const listResult = await listWorktrees2(gitRoot);
|
|
5083
|
+
async function selectWorktreeWithFzf(gitRoot, worktreeDirectory) {
|
|
5084
|
+
const listResult = await listWorktrees2(gitRoot, worktreeDirectory);
|
|
4942
5085
|
if (isErr(listResult)) {
|
|
4943
5086
|
return listResult;
|
|
4944
5087
|
}
|
|
@@ -4985,28 +5128,13 @@ async function selectWorktreeWithFzf(gitRoot) {
|
|
|
4985
5128
|
};
|
|
4986
5129
|
}
|
|
4987
5130
|
|
|
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
5131
|
// ../core/src/shell.ts
|
|
5008
|
-
async function shellInWorktree(gitRoot, worktreeName) {
|
|
5009
|
-
const validation = await validateWorktreeExists(
|
|
5132
|
+
async function shellInWorktree(gitRoot, worktreeDirectory, worktreeName) {
|
|
5133
|
+
const validation = await validateWorktreeExists(
|
|
5134
|
+
gitRoot,
|
|
5135
|
+
worktreeDirectory,
|
|
5136
|
+
worktreeName
|
|
5137
|
+
);
|
|
5010
5138
|
if (isErr(validation)) {
|
|
5011
5139
|
return err(validation.error);
|
|
5012
5140
|
}
|
|
@@ -5085,7 +5213,14 @@ async function attachHandler(args2) {
|
|
|
5085
5213
|
);
|
|
5086
5214
|
}
|
|
5087
5215
|
const gitRoot = await getGitRoot();
|
|
5088
|
-
const
|
|
5216
|
+
const context = await createContext(gitRoot);
|
|
5217
|
+
const result = await attachWorktreeCore(
|
|
5218
|
+
context.gitRoot,
|
|
5219
|
+
context.worktreesDirectory,
|
|
5220
|
+
branchName,
|
|
5221
|
+
context.config?.postCreate?.copyFiles,
|
|
5222
|
+
context.config?.postCreate?.commands
|
|
5223
|
+
);
|
|
5089
5224
|
if (isErr(result)) {
|
|
5090
5225
|
const error = result.error;
|
|
5091
5226
|
if (error instanceof WorktreeAlreadyExistsError) {
|
|
@@ -5099,14 +5234,19 @@ async function attachHandler(args2) {
|
|
|
5099
5234
|
const worktreePath = result.value;
|
|
5100
5235
|
output.log(`Attached phantom: ${branchName}`);
|
|
5101
5236
|
if (values.shell) {
|
|
5102
|
-
const shellResult = await shellInWorktree(
|
|
5237
|
+
const shellResult = await shellInWorktree(
|
|
5238
|
+
context.gitRoot,
|
|
5239
|
+
context.worktreesDirectory,
|
|
5240
|
+
branchName
|
|
5241
|
+
);
|
|
5103
5242
|
if (isErr(shellResult)) {
|
|
5104
5243
|
exitWithError(shellResult.error.message, exitCodes.generalError);
|
|
5105
5244
|
}
|
|
5106
5245
|
} else if (values.exec) {
|
|
5107
5246
|
const shell = process.env.SHELL || "/bin/sh";
|
|
5108
5247
|
const execResult = await execInWorktree(
|
|
5109
|
-
gitRoot,
|
|
5248
|
+
context.gitRoot,
|
|
5249
|
+
context.worktreesDirectory,
|
|
5110
5250
|
branchName,
|
|
5111
5251
|
[shell, "-c", values.exec],
|
|
5112
5252
|
{ interactive: true }
|
|
@@ -5618,27 +5758,26 @@ async function createHandler(args2) {
|
|
|
5618
5758
|
}
|
|
5619
5759
|
try {
|
|
5620
5760
|
const gitRoot = await getGitRoot();
|
|
5761
|
+
const context = await createContext(gitRoot);
|
|
5621
5762
|
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
|
-
}
|
|
5763
|
+
if (context.config?.postCreate?.copyFiles) {
|
|
5764
|
+
filesToCopy = [...context.config.postCreate.copyFiles];
|
|
5633
5765
|
}
|
|
5634
5766
|
if (copyFileOptions && copyFileOptions.length > 0) {
|
|
5635
5767
|
const cliFiles = Array.isArray(copyFileOptions) ? copyFileOptions : [copyFileOptions];
|
|
5636
5768
|
filesToCopy = [.../* @__PURE__ */ new Set([...filesToCopy, ...cliFiles])];
|
|
5637
5769
|
}
|
|
5638
|
-
const result = await createWorktree(
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5770
|
+
const result = await createWorktree(
|
|
5771
|
+
context.gitRoot,
|
|
5772
|
+
context.worktreesDirectory,
|
|
5773
|
+
worktreeName,
|
|
5774
|
+
{
|
|
5775
|
+
copyFiles: filesToCopy.length > 0 ? filesToCopy : void 0,
|
|
5776
|
+
base: baseOption
|
|
5777
|
+
},
|
|
5778
|
+
filesToCopy.length > 0 ? filesToCopy : void 0,
|
|
5779
|
+
context.config?.postCreate?.commands
|
|
5780
|
+
);
|
|
5642
5781
|
if (isErr(result)) {
|
|
5643
5782
|
const exitCode = result.error instanceof WorktreeAlreadyExistsError ? exitCodes.validationError : exitCodes.generalError;
|
|
5644
5783
|
exitWithError(result.error.message, exitCode);
|
|
@@ -5650,30 +5789,6 @@ async function createHandler(args2) {
|
|
|
5650
5789
|
Warning: Failed to copy some files: ${result.value.copyError}`
|
|
5651
5790
|
);
|
|
5652
5791
|
}
|
|
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
5792
|
if (execCommand && isOk(result)) {
|
|
5678
5793
|
output.log(
|
|
5679
5794
|
`
|
|
@@ -5681,7 +5796,8 @@ Executing command in worktree '${worktreeName}': ${execCommand}`
|
|
|
5681
5796
|
);
|
|
5682
5797
|
const shell = process.env.SHELL || "/bin/sh";
|
|
5683
5798
|
const execResult = await execInWorktree(
|
|
5684
|
-
gitRoot,
|
|
5799
|
+
context.gitRoot,
|
|
5800
|
+
context.worktreesDirectory,
|
|
5685
5801
|
worktreeName,
|
|
5686
5802
|
[shell, "-c", execCommand],
|
|
5687
5803
|
{ interactive: true }
|
|
@@ -5699,7 +5815,11 @@ Executing command in worktree '${worktreeName}': ${execCommand}`
|
|
|
5699
5815
|
Entering worktree '${worktreeName}' at ${result.value.path}`
|
|
5700
5816
|
);
|
|
5701
5817
|
output.log("Type 'exit' to return to your original directory\n");
|
|
5702
|
-
const shellResult = await shellInWorktree(
|
|
5818
|
+
const shellResult = await shellInWorktree(
|
|
5819
|
+
context.gitRoot,
|
|
5820
|
+
context.worktreesDirectory,
|
|
5821
|
+
worktreeName
|
|
5822
|
+
);
|
|
5703
5823
|
if (isErr(shellResult)) {
|
|
5704
5824
|
output.error(shellResult.error.message);
|
|
5705
5825
|
const exitCode = "exitCode" in shellResult.error ? shellResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
|
|
@@ -5779,6 +5899,7 @@ async function deleteHandler(args2) {
|
|
|
5779
5899
|
const forceDelete = values.force ?? false;
|
|
5780
5900
|
try {
|
|
5781
5901
|
const gitRoot = await getGitRoot();
|
|
5902
|
+
const context = await createContext(gitRoot);
|
|
5782
5903
|
let worktreeName;
|
|
5783
5904
|
if (deleteCurrent) {
|
|
5784
5905
|
const currentWorktree = await getCurrentWorktree(gitRoot);
|
|
@@ -5790,7 +5911,10 @@ async function deleteHandler(args2) {
|
|
|
5790
5911
|
}
|
|
5791
5912
|
worktreeName = currentWorktree;
|
|
5792
5913
|
} else if (useFzf) {
|
|
5793
|
-
const selectResult = await selectWorktreeWithFzf(
|
|
5914
|
+
const selectResult = await selectWorktreeWithFzf(
|
|
5915
|
+
context.gitRoot,
|
|
5916
|
+
context.worktreesDirectory
|
|
5917
|
+
);
|
|
5794
5918
|
if (isErr(selectResult)) {
|
|
5795
5919
|
exitWithError(selectResult.error.message, exitCodes.generalError);
|
|
5796
5920
|
}
|
|
@@ -5801,9 +5925,14 @@ async function deleteHandler(args2) {
|
|
|
5801
5925
|
} else {
|
|
5802
5926
|
worktreeName = positionals[0];
|
|
5803
5927
|
}
|
|
5804
|
-
const result = await deleteWorktree(
|
|
5805
|
-
|
|
5806
|
-
|
|
5928
|
+
const result = await deleteWorktree(
|
|
5929
|
+
context.gitRoot,
|
|
5930
|
+
context.worktreesDirectory,
|
|
5931
|
+
worktreeName,
|
|
5932
|
+
{
|
|
5933
|
+
force: forceDelete
|
|
5934
|
+
}
|
|
5935
|
+
);
|
|
5807
5936
|
if (isErr(result)) {
|
|
5808
5937
|
const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.validationError : result.error instanceof WorktreeError && result.error.message.includes("uncommitted changes") ? exitCodes.validationError : exitCodes.generalError;
|
|
5809
5938
|
exitWithError(result.error.message, exitCode);
|
|
@@ -5878,6 +6007,7 @@ async function execHandler(args2) {
|
|
|
5878
6007
|
}
|
|
5879
6008
|
try {
|
|
5880
6009
|
const gitRoot = await getGitRoot();
|
|
6010
|
+
const context = await createContext(gitRoot);
|
|
5881
6011
|
if (tmuxOption && !await isInsideTmux()) {
|
|
5882
6012
|
exitWithError(
|
|
5883
6013
|
"The --tmux option can only be used inside a tmux session",
|
|
@@ -5886,7 +6016,10 @@ async function execHandler(args2) {
|
|
|
5886
6016
|
}
|
|
5887
6017
|
let worktreeName;
|
|
5888
6018
|
if (useFzf) {
|
|
5889
|
-
const selectResult = await selectWorktreeWithFzf(
|
|
6019
|
+
const selectResult = await selectWorktreeWithFzf(
|
|
6020
|
+
context.gitRoot,
|
|
6021
|
+
context.worktreesDirectory
|
|
6022
|
+
);
|
|
5890
6023
|
if (isErr(selectResult)) {
|
|
5891
6024
|
exitWithError(selectResult.error.message, exitCodes.generalError);
|
|
5892
6025
|
}
|
|
@@ -5897,7 +6030,11 @@ async function execHandler(args2) {
|
|
|
5897
6030
|
} else {
|
|
5898
6031
|
worktreeName = positionals[0];
|
|
5899
6032
|
}
|
|
5900
|
-
const validation = await validateWorktreeExists(
|
|
6033
|
+
const validation = await validateWorktreeExists(
|
|
6034
|
+
context.gitRoot,
|
|
6035
|
+
context.worktreesDirectory,
|
|
6036
|
+
worktreeName
|
|
6037
|
+
);
|
|
5901
6038
|
if (isErr(validation)) {
|
|
5902
6039
|
exitWithError(validation.error.message, exitCodes.generalError);
|
|
5903
6040
|
}
|
|
@@ -5922,7 +6059,8 @@ async function execHandler(args2) {
|
|
|
5922
6059
|
exitWithSuccess();
|
|
5923
6060
|
}
|
|
5924
6061
|
const result = await execInWorktree(
|
|
5925
|
-
gitRoot,
|
|
6062
|
+
context.gitRoot,
|
|
6063
|
+
context.worktreesDirectory,
|
|
5926
6064
|
worktreeName,
|
|
5927
6065
|
commandArgs,
|
|
5928
6066
|
{ interactive: true }
|
|
@@ -9387,15 +9525,26 @@ async function checkoutIssue(issue, base) {
|
|
|
9387
9525
|
);
|
|
9388
9526
|
}
|
|
9389
9527
|
const gitRoot = await getGitRoot();
|
|
9528
|
+
const context = await createContext(gitRoot);
|
|
9390
9529
|
const worktreeName = `issue-${issue.number}`;
|
|
9391
9530
|
const branchName = `issue-${issue.number}`;
|
|
9392
|
-
const
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9531
|
+
const result = await createWorktree(
|
|
9532
|
+
context.gitRoot,
|
|
9533
|
+
context.worktreesDirectory,
|
|
9534
|
+
worktreeName,
|
|
9535
|
+
{
|
|
9536
|
+
branch: branchName,
|
|
9537
|
+
base
|
|
9538
|
+
},
|
|
9539
|
+
context.config?.postCreate?.copyFiles,
|
|
9540
|
+
context.config?.postCreate?.commands
|
|
9541
|
+
);
|
|
9397
9542
|
if (isErr(result)) {
|
|
9398
9543
|
if (result.error instanceof WorktreeAlreadyExistsError) {
|
|
9544
|
+
const worktreePath = getWorktreePathFromDirectory(
|
|
9545
|
+
context.worktreesDirectory,
|
|
9546
|
+
worktreeName
|
|
9547
|
+
);
|
|
9399
9548
|
return ok({
|
|
9400
9549
|
message: `Worktree for issue #${issue.number} is already checked out`,
|
|
9401
9550
|
worktree: worktreeName,
|
|
@@ -9408,13 +9557,14 @@ async function checkoutIssue(issue, base) {
|
|
|
9408
9557
|
return ok({
|
|
9409
9558
|
message: result.value.message,
|
|
9410
9559
|
worktree: worktreeName,
|
|
9411
|
-
path:
|
|
9560
|
+
path: result.value.path
|
|
9412
9561
|
});
|
|
9413
9562
|
}
|
|
9414
9563
|
|
|
9415
9564
|
// ../github/src/checkout/pr.ts
|
|
9416
9565
|
async function checkoutPullRequest(pullRequest) {
|
|
9417
9566
|
const gitRoot = await getGitRoot();
|
|
9567
|
+
const context = await createContext(gitRoot);
|
|
9418
9568
|
const worktreeName = `pr-${pullRequest.number}`;
|
|
9419
9569
|
const localBranch = `pr-${pullRequest.number}`;
|
|
9420
9570
|
const upstream = pullRequest.isFromFork ? `origin/pull/${pullRequest.number}/head` : `origin/${pullRequest.head.ref}`;
|
|
@@ -9437,10 +9587,19 @@ async function checkoutPullRequest(pullRequest) {
|
|
|
9437
9587
|
`Warning: Could not set upstream branch: ${setUpstreamResult.error.message}`
|
|
9438
9588
|
);
|
|
9439
9589
|
}
|
|
9440
|
-
const attachResult = await attachWorktreeCore(
|
|
9441
|
-
|
|
9590
|
+
const attachResult = await attachWorktreeCore(
|
|
9591
|
+
context.gitRoot,
|
|
9592
|
+
context.worktreesDirectory,
|
|
9593
|
+
worktreeName,
|
|
9594
|
+
context.config?.postCreate?.copyFiles,
|
|
9595
|
+
context.config?.postCreate?.commands
|
|
9596
|
+
);
|
|
9442
9597
|
if (isErr(attachResult)) {
|
|
9443
9598
|
if (attachResult.error instanceof WorktreeAlreadyExistsError) {
|
|
9599
|
+
const worktreePath = getWorktreePathFromDirectory(
|
|
9600
|
+
context.worktreesDirectory,
|
|
9601
|
+
worktreeName
|
|
9602
|
+
);
|
|
9444
9603
|
return ok({
|
|
9445
9604
|
message: `Worktree for PR #${pullRequest.number} is already checked out`,
|
|
9446
9605
|
worktree: worktreeName,
|
|
@@ -9454,7 +9613,7 @@ async function checkoutPullRequest(pullRequest) {
|
|
|
9454
9613
|
return ok({
|
|
9455
9614
|
message,
|
|
9456
9615
|
worktree: worktreeName,
|
|
9457
|
-
path:
|
|
9616
|
+
path: attachResult.value
|
|
9458
9617
|
});
|
|
9459
9618
|
}
|
|
9460
9619
|
|
|
@@ -9745,8 +9904,12 @@ async function listHandler(args2 = []) {
|
|
|
9745
9904
|
});
|
|
9746
9905
|
try {
|
|
9747
9906
|
const gitRoot = await getGitRoot();
|
|
9907
|
+
const context = await createContext(gitRoot);
|
|
9748
9908
|
if (values.fzf) {
|
|
9749
|
-
const selectResult = await selectWorktreeWithFzf(
|
|
9909
|
+
const selectResult = await selectWorktreeWithFzf(
|
|
9910
|
+
context.gitRoot,
|
|
9911
|
+
context.worktreesDirectory
|
|
9912
|
+
);
|
|
9750
9913
|
if (isErr(selectResult)) {
|
|
9751
9914
|
exitWithError(selectResult.error.message, exitCodes.generalError);
|
|
9752
9915
|
}
|
|
@@ -9754,7 +9917,10 @@ async function listHandler(args2 = []) {
|
|
|
9754
9917
|
output.log(selectResult.value.name);
|
|
9755
9918
|
}
|
|
9756
9919
|
} else {
|
|
9757
|
-
const result = await listWorktrees2(
|
|
9920
|
+
const result = await listWorktrees2(
|
|
9921
|
+
context.gitRoot,
|
|
9922
|
+
context.worktreesDirectory
|
|
9923
|
+
);
|
|
9758
9924
|
if (isErr(result)) {
|
|
9759
9925
|
exitWithError("Failed to list worktrees", exitCodes.generalError);
|
|
9760
9926
|
}
|
|
@@ -13057,7 +13223,7 @@ var StdioServerTransport = class {
|
|
|
13057
13223
|
// ../mcp/package.json
|
|
13058
13224
|
var package_default = {
|
|
13059
13225
|
name: "@aku11i/phantom-mcp",
|
|
13060
|
-
version: "1.
|
|
13226
|
+
version: "1.3.0",
|
|
13061
13227
|
private: true,
|
|
13062
13228
|
type: "module",
|
|
13063
13229
|
main: "./src/index.ts",
|
|
@@ -13092,10 +13258,18 @@ var createWorktreeTool = {
|
|
|
13092
13258
|
inputSchema: schema,
|
|
13093
13259
|
handler: async ({ name, baseBranch }) => {
|
|
13094
13260
|
const gitRoot = await getGitRoot();
|
|
13095
|
-
const
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13261
|
+
const context = await createContext(gitRoot);
|
|
13262
|
+
const result = await createWorktree(
|
|
13263
|
+
context.gitRoot,
|
|
13264
|
+
context.worktreesDirectory,
|
|
13265
|
+
name,
|
|
13266
|
+
{
|
|
13267
|
+
branch: name,
|
|
13268
|
+
base: baseBranch
|
|
13269
|
+
},
|
|
13270
|
+
context.config?.postCreate?.copyFiles,
|
|
13271
|
+
context.config?.postCreate?.commands
|
|
13272
|
+
);
|
|
13099
13273
|
if (!isOk(result)) {
|
|
13100
13274
|
throw new Error(result.error.message);
|
|
13101
13275
|
}
|
|
@@ -13130,7 +13304,15 @@ var deleteWorktreeTool = {
|
|
|
13130
13304
|
inputSchema: schema2,
|
|
13131
13305
|
handler: async ({ name, force }) => {
|
|
13132
13306
|
const gitRoot = await getGitRoot();
|
|
13133
|
-
const
|
|
13307
|
+
const context = await createContext(gitRoot);
|
|
13308
|
+
const result = await deleteWorktree(
|
|
13309
|
+
context.gitRoot,
|
|
13310
|
+
context.worktreesDirectory,
|
|
13311
|
+
name,
|
|
13312
|
+
{
|
|
13313
|
+
force
|
|
13314
|
+
}
|
|
13315
|
+
);
|
|
13134
13316
|
if (!isOk(result)) {
|
|
13135
13317
|
throw new Error(result.error.message);
|
|
13136
13318
|
}
|
|
@@ -13195,7 +13377,11 @@ var listWorktreesTool = {
|
|
|
13195
13377
|
inputSchema: schema4,
|
|
13196
13378
|
handler: async () => {
|
|
13197
13379
|
const gitRoot = await getGitRoot();
|
|
13198
|
-
const
|
|
13380
|
+
const context = await createContext(gitRoot);
|
|
13381
|
+
const result = await listWorktrees2(
|
|
13382
|
+
context.gitRoot,
|
|
13383
|
+
context.worktreesDirectory
|
|
13384
|
+
);
|
|
13199
13385
|
if (!isOk(result)) {
|
|
13200
13386
|
throw new Error("Failed to list worktrees");
|
|
13201
13387
|
}
|
|
@@ -13370,6 +13556,7 @@ async function shellHandler(args2) {
|
|
|
13370
13556
|
let worktreeName;
|
|
13371
13557
|
try {
|
|
13372
13558
|
const gitRoot = await getGitRoot();
|
|
13559
|
+
const context = await createContext(gitRoot);
|
|
13373
13560
|
if (tmuxOption && !await isInsideTmux()) {
|
|
13374
13561
|
exitWithError(
|
|
13375
13562
|
"The --tmux option can only be used inside a tmux session",
|
|
@@ -13377,7 +13564,10 @@ async function shellHandler(args2) {
|
|
|
13377
13564
|
);
|
|
13378
13565
|
}
|
|
13379
13566
|
if (useFzf) {
|
|
13380
|
-
const selectResult = await selectWorktreeWithFzf(
|
|
13567
|
+
const selectResult = await selectWorktreeWithFzf(
|
|
13568
|
+
context.gitRoot,
|
|
13569
|
+
context.worktreesDirectory
|
|
13570
|
+
);
|
|
13381
13571
|
if (isErr(selectResult)) {
|
|
13382
13572
|
exitWithError(selectResult.error.message, exitCodes.generalError);
|
|
13383
13573
|
}
|
|
@@ -13388,7 +13578,11 @@ async function shellHandler(args2) {
|
|
|
13388
13578
|
} else {
|
|
13389
13579
|
worktreeName = positionals[0];
|
|
13390
13580
|
}
|
|
13391
|
-
const validation = await validateWorktreeExists(
|
|
13581
|
+
const validation = await validateWorktreeExists(
|
|
13582
|
+
context.gitRoot,
|
|
13583
|
+
context.worktreesDirectory,
|
|
13584
|
+
worktreeName
|
|
13585
|
+
);
|
|
13392
13586
|
if (isErr(validation)) {
|
|
13393
13587
|
exitWithError(validation.error.message, exitCodes.generalError);
|
|
13394
13588
|
}
|
|
@@ -13415,7 +13609,11 @@ async function shellHandler(args2) {
|
|
|
13415
13609
|
`Entering worktree '${worktreeName}' at ${validation.value.path}`
|
|
13416
13610
|
);
|
|
13417
13611
|
output.log("Type 'exit' to return to your original directory\n");
|
|
13418
|
-
const result = await shellInWorktree(
|
|
13612
|
+
const result = await shellInWorktree(
|
|
13613
|
+
context.gitRoot,
|
|
13614
|
+
context.worktreesDirectory,
|
|
13615
|
+
worktreeName
|
|
13616
|
+
);
|
|
13419
13617
|
if (isErr(result)) {
|
|
13420
13618
|
const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.notFound : result.error.exitCode || exitCodes.generalError;
|
|
13421
13619
|
exitWithError(result.error.message, exitCode);
|
|
@@ -13435,7 +13633,7 @@ import { parseArgs as parseArgs9 } from "node:util";
|
|
|
13435
13633
|
// package.json
|
|
13436
13634
|
var package_default2 = {
|
|
13437
13635
|
name: "@aku11i/phantom-cli",
|
|
13438
|
-
version: "1.
|
|
13636
|
+
version: "1.3.0",
|
|
13439
13637
|
private: true,
|
|
13440
13638
|
type: "module",
|
|
13441
13639
|
scripts: {
|
|
@@ -13511,8 +13709,12 @@ async function whereHandler(args2) {
|
|
|
13511
13709
|
exitCodes.generalError
|
|
13512
13710
|
);
|
|
13513
13711
|
}
|
|
13712
|
+
const context = await createContext(gitRoot);
|
|
13514
13713
|
if (useFzf) {
|
|
13515
|
-
const selectResult = await selectWorktreeWithFzf(
|
|
13714
|
+
const selectResult = await selectWorktreeWithFzf(
|
|
13715
|
+
context.gitRoot,
|
|
13716
|
+
context.worktreesDirectory
|
|
13717
|
+
);
|
|
13516
13718
|
if (isErr(selectResult)) {
|
|
13517
13719
|
exitWithError(selectResult.error.message, exitCodes.generalError);
|
|
13518
13720
|
}
|
|
@@ -13523,7 +13725,11 @@ async function whereHandler(args2) {
|
|
|
13523
13725
|
} else {
|
|
13524
13726
|
worktreeName = positionals[0];
|
|
13525
13727
|
}
|
|
13526
|
-
const result = await whereWorktree(
|
|
13728
|
+
const result = await whereWorktree(
|
|
13729
|
+
context.gitRoot,
|
|
13730
|
+
context.worktreesDirectory,
|
|
13731
|
+
worktreeName
|
|
13732
|
+
);
|
|
13527
13733
|
if (isErr(result)) {
|
|
13528
13734
|
exitWithError(result.error.message, exitCodes.notFound);
|
|
13529
13735
|
}
|