@aku11i/phantom 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +33 -3
- package/README.md +33 -3
- package/dist/phantom.js +486 -40
- package/dist/phantom.js.map +4 -4
- package/package.json +1 -1
package/dist/phantom.js
CHANGED
|
@@ -44,9 +44,9 @@ async function executeGitCommandInDirectory(directory, args2) {
|
|
|
44
44
|
|
|
45
45
|
// src/core/git/libs/get-git-root.ts
|
|
46
46
|
async function getGitRoot() {
|
|
47
|
-
const { stdout } = await executeGitCommand(["rev-parse", "--git-common-dir"]);
|
|
48
|
-
if (
|
|
49
|
-
return resolve(process.cwd(), dirname(
|
|
47
|
+
const { stdout: stdout2 } = await executeGitCommand(["rev-parse", "--git-common-dir"]);
|
|
48
|
+
if (stdout2.endsWith("/.git") || stdout2 === ".git") {
|
|
49
|
+
return resolve(process.cwd(), dirname(stdout2));
|
|
50
50
|
}
|
|
51
51
|
const { stdout: toplevel } = await executeGitCommand([
|
|
52
52
|
"rev-parse",
|
|
@@ -216,18 +216,20 @@ async function spawnProcess(config) {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// src/core/process/exec.ts
|
|
219
|
-
async function execInWorktree(gitRoot, worktreeName, command2) {
|
|
219
|
+
async function execInWorktree(gitRoot, worktreeName, command2, options = {}) {
|
|
220
220
|
const validation = await validateWorktreeExists(gitRoot, worktreeName);
|
|
221
221
|
if (!validation.exists) {
|
|
222
222
|
return err(new WorktreeNotFoundError(worktreeName));
|
|
223
223
|
}
|
|
224
224
|
const worktreePath = validation.path;
|
|
225
225
|
const [cmd, ...args2] = command2;
|
|
226
|
+
const stdio = options.interactive ? "inherit" : ["ignore", "inherit", "inherit"];
|
|
226
227
|
return spawnProcess({
|
|
227
228
|
command: cmd,
|
|
228
229
|
args: args2,
|
|
229
230
|
options: {
|
|
230
|
-
cwd: worktreePath
|
|
231
|
+
cwd: worktreePath,
|
|
232
|
+
stdio
|
|
231
233
|
}
|
|
232
234
|
});
|
|
233
235
|
}
|
|
@@ -407,7 +409,8 @@ async function attachHandler(args2) {
|
|
|
407
409
|
const execResult = await execInWorktree(
|
|
408
410
|
gitRoot,
|
|
409
411
|
branchName,
|
|
410
|
-
values.exec.split(" ")
|
|
412
|
+
values.exec.split(" "),
|
|
413
|
+
{ interactive: true }
|
|
411
414
|
);
|
|
412
415
|
if (isErr(execResult)) {
|
|
413
416
|
exitWithError(execResult.error.message, exitCodes.generalError);
|
|
@@ -458,6 +461,20 @@ function validateConfig(config) {
|
|
|
458
461
|
);
|
|
459
462
|
}
|
|
460
463
|
}
|
|
464
|
+
if (postCreate.commands !== void 0) {
|
|
465
|
+
if (!Array.isArray(postCreate.commands)) {
|
|
466
|
+
return err(
|
|
467
|
+
new ConfigValidationError("postCreate.commands must be an array")
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
if (!postCreate.commands.every((c) => typeof c === "string")) {
|
|
471
|
+
return err(
|
|
472
|
+
new ConfigValidationError(
|
|
473
|
+
"postCreate.commands must contain only strings"
|
|
474
|
+
)
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
461
478
|
}
|
|
462
479
|
return ok(config);
|
|
463
480
|
}
|
|
@@ -741,17 +758,42 @@ async function createHandler(args2) {
|
|
|
741
758
|
Warning: Failed to copy some files: ${result.value.copyError}`
|
|
742
759
|
);
|
|
743
760
|
}
|
|
761
|
+
if (isOk(configResult) && configResult.value.postCreate?.commands) {
|
|
762
|
+
const commands2 = configResult.value.postCreate.commands;
|
|
763
|
+
output.log("\nRunning post-create commands...");
|
|
764
|
+
for (const command2 of commands2) {
|
|
765
|
+
output.log(`Executing: ${command2}`);
|
|
766
|
+
const shell = process.env.SHELL || "/bin/sh";
|
|
767
|
+
const cmdResult = await execInWorktree(gitRoot, worktreeName, [
|
|
768
|
+
shell,
|
|
769
|
+
"-c",
|
|
770
|
+
command2
|
|
771
|
+
]);
|
|
772
|
+
if (isErr(cmdResult)) {
|
|
773
|
+
output.error(`Failed to execute command: ${cmdResult.error.message}`);
|
|
774
|
+
const exitCode = "exitCode" in cmdResult.error ? cmdResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
|
|
775
|
+
exitWithError(`Post-create command failed: ${command2}`, exitCode);
|
|
776
|
+
}
|
|
777
|
+
if (cmdResult.value.exitCode !== 0) {
|
|
778
|
+
exitWithError(
|
|
779
|
+
`Post-create command failed: ${command2}`,
|
|
780
|
+
cmdResult.value.exitCode
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
744
785
|
if (execCommand && isOk(result)) {
|
|
745
786
|
output.log(
|
|
746
787
|
`
|
|
747
788
|
Executing command in worktree '${worktreeName}': ${execCommand}`
|
|
748
789
|
);
|
|
749
790
|
const shell = process.env.SHELL || "/bin/sh";
|
|
750
|
-
const execResult = await execInWorktree(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
execCommand
|
|
754
|
-
|
|
791
|
+
const execResult = await execInWorktree(
|
|
792
|
+
gitRoot,
|
|
793
|
+
worktreeName,
|
|
794
|
+
[shell, "-c", execCommand],
|
|
795
|
+
{ interactive: true }
|
|
796
|
+
);
|
|
755
797
|
if (isErr(execResult)) {
|
|
756
798
|
output.error(execResult.error.message);
|
|
757
799
|
const exitCode = "exitCode" in execResult.error ? execResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
|
|
@@ -809,14 +851,14 @@ import { parseArgs as parseArgs3 } from "node:util";
|
|
|
809
851
|
|
|
810
852
|
// src/core/git/libs/list-worktrees.ts
|
|
811
853
|
async function listWorktrees(gitRoot) {
|
|
812
|
-
const { stdout } = await executeGitCommand([
|
|
854
|
+
const { stdout: stdout2 } = await executeGitCommand([
|
|
813
855
|
"worktree",
|
|
814
856
|
"list",
|
|
815
857
|
"--porcelain"
|
|
816
858
|
]);
|
|
817
859
|
const worktrees = [];
|
|
818
860
|
let currentWorktree = {};
|
|
819
|
-
const lines =
|
|
861
|
+
const lines = stdout2.split("\n").filter((line) => line.length > 0);
|
|
820
862
|
for (const line of lines) {
|
|
821
863
|
if (line.startsWith("worktree ")) {
|
|
822
864
|
if (currentWorktree.path) {
|
|
@@ -870,14 +912,14 @@ async function getCurrentWorktree(gitRoot) {
|
|
|
870
912
|
// src/core/worktree/delete.ts
|
|
871
913
|
async function getWorktreeStatus(worktreePath) {
|
|
872
914
|
try {
|
|
873
|
-
const { stdout } = await executeGitCommandInDirectory(worktreePath, [
|
|
915
|
+
const { stdout: stdout2 } = await executeGitCommandInDirectory(worktreePath, [
|
|
874
916
|
"status",
|
|
875
917
|
"--porcelain"
|
|
876
918
|
]);
|
|
877
|
-
if (
|
|
919
|
+
if (stdout2) {
|
|
878
920
|
return {
|
|
879
921
|
hasUncommittedChanges: true,
|
|
880
|
-
changedFiles:
|
|
922
|
+
changedFiles: stdout2.split("\n").length
|
|
881
923
|
};
|
|
882
924
|
}
|
|
883
925
|
} catch {
|
|
@@ -1033,7 +1075,12 @@ async function execHandler(args2) {
|
|
|
1033
1075
|
const [worktreeName, ...commandArgs] = positionals;
|
|
1034
1076
|
try {
|
|
1035
1077
|
const gitRoot = await getGitRoot();
|
|
1036
|
-
const result = await execInWorktree(
|
|
1078
|
+
const result = await execInWorktree(
|
|
1079
|
+
gitRoot,
|
|
1080
|
+
worktreeName,
|
|
1081
|
+
commandArgs,
|
|
1082
|
+
{ interactive: true }
|
|
1083
|
+
);
|
|
1037
1084
|
if (isErr(result)) {
|
|
1038
1085
|
const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.notFound : result.error.exitCode || exitCodes.generalError;
|
|
1039
1086
|
exitWithError(result.error.message, exitCode);
|
|
@@ -1053,11 +1100,11 @@ import { parseArgs as parseArgs5 } from "node:util";
|
|
|
1053
1100
|
// src/core/worktree/list.ts
|
|
1054
1101
|
async function getWorktreeStatus2(worktreePath) {
|
|
1055
1102
|
try {
|
|
1056
|
-
const { stdout } = await executeGitCommandInDirectory(worktreePath, [
|
|
1103
|
+
const { stdout: stdout2 } = await executeGitCommandInDirectory(worktreePath, [
|
|
1057
1104
|
"status",
|
|
1058
1105
|
"--porcelain"
|
|
1059
1106
|
]);
|
|
1060
|
-
return !
|
|
1107
|
+
return !stdout2;
|
|
1061
1108
|
} catch {
|
|
1062
1109
|
return true;
|
|
1063
1110
|
}
|
|
@@ -1179,7 +1226,7 @@ import { parseArgs as parseArgs7 } from "node:util";
|
|
|
1179
1226
|
var package_default = {
|
|
1180
1227
|
name: "@aku11i/phantom",
|
|
1181
1228
|
packageManager: "pnpm@10.8.1",
|
|
1182
|
-
version: "0.
|
|
1229
|
+
version: "0.7.0",
|
|
1183
1230
|
description: "A powerful CLI tool for managing Git worktrees for parallel development",
|
|
1184
1231
|
keywords: [
|
|
1185
1232
|
"git",
|
|
@@ -1295,55 +1342,446 @@ async function whereHandler(args2) {
|
|
|
1295
1342
|
}
|
|
1296
1343
|
}
|
|
1297
1344
|
|
|
1345
|
+
// src/cli/help.ts
|
|
1346
|
+
import { stdout } from "node:process";
|
|
1347
|
+
var HelpFormatter = class {
|
|
1348
|
+
width;
|
|
1349
|
+
indent = " ";
|
|
1350
|
+
constructor() {
|
|
1351
|
+
this.width = stdout.columns || 80;
|
|
1352
|
+
}
|
|
1353
|
+
formatMainHelp(commands2) {
|
|
1354
|
+
const lines = [];
|
|
1355
|
+
lines.push(this.bold("Phantom - Git Worktree Manager"));
|
|
1356
|
+
lines.push("");
|
|
1357
|
+
lines.push(
|
|
1358
|
+
this.dim(
|
|
1359
|
+
"A CLI tool for managing Git worktrees with enhanced functionality"
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
lines.push("");
|
|
1363
|
+
lines.push(this.section("USAGE"));
|
|
1364
|
+
lines.push(`${this.indent}phantom <command> [options]`);
|
|
1365
|
+
lines.push("");
|
|
1366
|
+
lines.push(this.section("COMMANDS"));
|
|
1367
|
+
const maxNameLength = Math.max(...commands2.map((cmd) => cmd.name.length));
|
|
1368
|
+
for (const cmd of commands2) {
|
|
1369
|
+
const paddedName = cmd.name.padEnd(maxNameLength + 2);
|
|
1370
|
+
lines.push(`${this.indent}${this.cyan(paddedName)}${cmd.description}`);
|
|
1371
|
+
}
|
|
1372
|
+
lines.push("");
|
|
1373
|
+
lines.push(this.section("GLOBAL OPTIONS"));
|
|
1374
|
+
const helpOption = "-h, --help";
|
|
1375
|
+
const versionOption = "-v, --version";
|
|
1376
|
+
const globalOptionWidth = Math.max(helpOption.length, versionOption.length) + 2;
|
|
1377
|
+
lines.push(
|
|
1378
|
+
`${this.indent}${this.cyan(helpOption.padEnd(globalOptionWidth))}Show help`
|
|
1379
|
+
);
|
|
1380
|
+
lines.push(
|
|
1381
|
+
`${this.indent}${this.cyan(versionOption.padEnd(globalOptionWidth))}Show version`
|
|
1382
|
+
);
|
|
1383
|
+
lines.push("");
|
|
1384
|
+
lines.push(
|
|
1385
|
+
this.dim(
|
|
1386
|
+
"Run 'phantom <command> --help' for more information on a command."
|
|
1387
|
+
)
|
|
1388
|
+
);
|
|
1389
|
+
return lines.join("\n");
|
|
1390
|
+
}
|
|
1391
|
+
formatCommandHelp(help) {
|
|
1392
|
+
const lines = [];
|
|
1393
|
+
lines.push(this.bold(`phantom ${help.name}`));
|
|
1394
|
+
lines.push(this.dim(help.description));
|
|
1395
|
+
lines.push("");
|
|
1396
|
+
lines.push(this.section("USAGE"));
|
|
1397
|
+
lines.push(`${this.indent}${help.usage}`);
|
|
1398
|
+
lines.push("");
|
|
1399
|
+
if (help.options && help.options.length > 0) {
|
|
1400
|
+
lines.push(this.section("OPTIONS"));
|
|
1401
|
+
const maxOptionLength = Math.max(
|
|
1402
|
+
...help.options.map((opt) => this.formatOptionName(opt).length)
|
|
1403
|
+
);
|
|
1404
|
+
for (const option of help.options) {
|
|
1405
|
+
const optionName = this.formatOptionName(option);
|
|
1406
|
+
const paddedName = optionName.padEnd(maxOptionLength + 2);
|
|
1407
|
+
const description = this.wrapText(
|
|
1408
|
+
option.description,
|
|
1409
|
+
maxOptionLength + 4
|
|
1410
|
+
);
|
|
1411
|
+
lines.push(`${this.indent}${this.cyan(paddedName)}${description[0]}`);
|
|
1412
|
+
for (let i = 1; i < description.length; i++) {
|
|
1413
|
+
lines.push(
|
|
1414
|
+
`${this.indent}${" ".repeat(maxOptionLength + 2)}${description[i]}`
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
if (option.example) {
|
|
1418
|
+
const exampleIndent = " ".repeat(maxOptionLength + 4);
|
|
1419
|
+
lines.push(
|
|
1420
|
+
`${this.indent}${exampleIndent}${this.dim(`Example: ${option.example}`)}`
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
lines.push("");
|
|
1425
|
+
}
|
|
1426
|
+
if (help.examples && help.examples.length > 0) {
|
|
1427
|
+
lines.push(this.section("EXAMPLES"));
|
|
1428
|
+
for (const example of help.examples) {
|
|
1429
|
+
lines.push(`${this.indent}${this.dim(example.description)}`);
|
|
1430
|
+
lines.push(`${this.indent}${this.indent}$ ${example.command}`);
|
|
1431
|
+
lines.push("");
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
if (help.notes && help.notes.length > 0) {
|
|
1435
|
+
lines.push(this.section("NOTES"));
|
|
1436
|
+
for (const note of help.notes) {
|
|
1437
|
+
const wrappedNote = this.wrapText(note, 2);
|
|
1438
|
+
for (const line of wrappedNote) {
|
|
1439
|
+
lines.push(`${this.indent}${line}`);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
lines.push("");
|
|
1443
|
+
}
|
|
1444
|
+
return lines.join("\n");
|
|
1445
|
+
}
|
|
1446
|
+
formatOptionName(option) {
|
|
1447
|
+
const parts = [];
|
|
1448
|
+
if (option.short) {
|
|
1449
|
+
parts.push(`-${option.short},`);
|
|
1450
|
+
}
|
|
1451
|
+
parts.push(`--${option.name}`);
|
|
1452
|
+
if (option.type === "string") {
|
|
1453
|
+
parts.push(option.multiple ? "<value>..." : "<value>");
|
|
1454
|
+
}
|
|
1455
|
+
return parts.join(" ");
|
|
1456
|
+
}
|
|
1457
|
+
wrapText(text, indent) {
|
|
1458
|
+
const maxWidth = this.width - indent - 2;
|
|
1459
|
+
const words = text.split(" ");
|
|
1460
|
+
const lines = [];
|
|
1461
|
+
let currentLine = "";
|
|
1462
|
+
for (const word of words) {
|
|
1463
|
+
if (currentLine.length + word.length + 1 > maxWidth) {
|
|
1464
|
+
lines.push(currentLine);
|
|
1465
|
+
currentLine = word;
|
|
1466
|
+
} else {
|
|
1467
|
+
currentLine = currentLine ? `${currentLine} ${word}` : word;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (currentLine) {
|
|
1471
|
+
lines.push(currentLine);
|
|
1472
|
+
}
|
|
1473
|
+
return lines;
|
|
1474
|
+
}
|
|
1475
|
+
section(text) {
|
|
1476
|
+
return this.bold(text);
|
|
1477
|
+
}
|
|
1478
|
+
bold(text) {
|
|
1479
|
+
return `\x1B[1m${text}\x1B[0m`;
|
|
1480
|
+
}
|
|
1481
|
+
dim(text) {
|
|
1482
|
+
return `\x1B[2m${text}\x1B[0m`;
|
|
1483
|
+
}
|
|
1484
|
+
cyan(text) {
|
|
1485
|
+
return `\x1B[36m${text}\x1B[0m`;
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
var helpFormatter = new HelpFormatter();
|
|
1489
|
+
|
|
1490
|
+
// src/cli/help/attach.ts
|
|
1491
|
+
var attachHelp = {
|
|
1492
|
+
name: "attach",
|
|
1493
|
+
description: "Attach to an existing branch by creating a new worktree",
|
|
1494
|
+
usage: "phantom attach <worktree-name> <branch-name> [options]",
|
|
1495
|
+
options: [
|
|
1496
|
+
{
|
|
1497
|
+
name: "shell",
|
|
1498
|
+
short: "s",
|
|
1499
|
+
type: "boolean",
|
|
1500
|
+
description: "Open an interactive shell in the worktree after attaching"
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
name: "exec",
|
|
1504
|
+
short: "x",
|
|
1505
|
+
type: "string",
|
|
1506
|
+
description: "Execute a command in the worktree after attaching",
|
|
1507
|
+
example: "--exec 'git pull'"
|
|
1508
|
+
}
|
|
1509
|
+
],
|
|
1510
|
+
examples: [
|
|
1511
|
+
{
|
|
1512
|
+
description: "Attach to an existing branch",
|
|
1513
|
+
command: "phantom attach review-pr main"
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
description: "Attach to a remote branch and open a shell",
|
|
1517
|
+
command: "phantom attach hotfix origin/hotfix-v1.2 --shell"
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
description: "Attach to a branch and pull latest changes",
|
|
1521
|
+
command: "phantom attach staging origin/staging --exec 'git pull'"
|
|
1522
|
+
}
|
|
1523
|
+
],
|
|
1524
|
+
notes: [
|
|
1525
|
+
"The branch must already exist (locally or remotely)",
|
|
1526
|
+
"If attaching to a remote branch, it will be checked out locally",
|
|
1527
|
+
"Only one of --shell or --exec options can be used at a time"
|
|
1528
|
+
]
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
// src/cli/help/create.ts
|
|
1532
|
+
var createHelp = {
|
|
1533
|
+
name: "create",
|
|
1534
|
+
description: "Create a new Git worktree (phantom)",
|
|
1535
|
+
usage: "phantom create <name> [options]",
|
|
1536
|
+
options: [
|
|
1537
|
+
{
|
|
1538
|
+
name: "shell",
|
|
1539
|
+
short: "s",
|
|
1540
|
+
type: "boolean",
|
|
1541
|
+
description: "Open an interactive shell in the new worktree after creation"
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
name: "exec",
|
|
1545
|
+
short: "x",
|
|
1546
|
+
type: "string",
|
|
1547
|
+
description: "Execute a command in the new worktree after creation",
|
|
1548
|
+
example: "--exec 'npm install'"
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
name: "tmux",
|
|
1552
|
+
short: "t",
|
|
1553
|
+
type: "boolean",
|
|
1554
|
+
description: "Open the worktree in a new tmux window (requires being inside tmux)"
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
name: "tmux-vertical",
|
|
1558
|
+
type: "boolean",
|
|
1559
|
+
description: "Open the worktree in a vertical tmux pane (requires being inside tmux)"
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: "tmux-horizontal",
|
|
1563
|
+
type: "boolean",
|
|
1564
|
+
description: "Open the worktree in a horizontal tmux pane (requires being inside tmux)"
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
name: "copy-file",
|
|
1568
|
+
type: "string",
|
|
1569
|
+
multiple: true,
|
|
1570
|
+
description: "Copy specified files from the current worktree to the new one. Can be used multiple times",
|
|
1571
|
+
example: "--copy-file .env --copy-file config.local.json"
|
|
1572
|
+
}
|
|
1573
|
+
],
|
|
1574
|
+
examples: [
|
|
1575
|
+
{
|
|
1576
|
+
description: "Create a new worktree named 'feature-auth'",
|
|
1577
|
+
command: "phantom create feature-auth"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
description: "Create a worktree and open a shell in it",
|
|
1581
|
+
command: "phantom create bugfix-123 --shell"
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
description: "Create a worktree and run npm install",
|
|
1585
|
+
command: "phantom create new-feature --exec 'npm install'"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
description: "Create a worktree in a new tmux window",
|
|
1589
|
+
command: "phantom create experiment --tmux"
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
description: "Create a worktree and copy environment files",
|
|
1593
|
+
command: "phantom create staging --copy-file .env --copy-file database.yml"
|
|
1594
|
+
}
|
|
1595
|
+
],
|
|
1596
|
+
notes: [
|
|
1597
|
+
"The worktree name will be used as the branch name",
|
|
1598
|
+
"Only one of --shell, --exec, or --tmux options can be used at a time",
|
|
1599
|
+
"File copying can also be configured in phantom.config.json"
|
|
1600
|
+
]
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1603
|
+
// src/cli/help/delete.ts
|
|
1604
|
+
var deleteHelp = {
|
|
1605
|
+
name: "delete",
|
|
1606
|
+
description: "Delete a Git worktree (phantom)",
|
|
1607
|
+
usage: "phantom delete <name> [options]",
|
|
1608
|
+
options: [
|
|
1609
|
+
{
|
|
1610
|
+
name: "force",
|
|
1611
|
+
short: "f",
|
|
1612
|
+
type: "boolean",
|
|
1613
|
+
description: "Force deletion even if the worktree has uncommitted or unpushed changes"
|
|
1614
|
+
}
|
|
1615
|
+
],
|
|
1616
|
+
examples: [
|
|
1617
|
+
{
|
|
1618
|
+
description: "Delete a worktree",
|
|
1619
|
+
command: "phantom delete feature-auth"
|
|
1620
|
+
},
|
|
1621
|
+
{
|
|
1622
|
+
description: "Force delete a worktree with uncommitted changes",
|
|
1623
|
+
command: "phantom delete experimental --force"
|
|
1624
|
+
}
|
|
1625
|
+
],
|
|
1626
|
+
notes: [
|
|
1627
|
+
"By default, deletion will fail if the worktree has uncommitted changes",
|
|
1628
|
+
"The associated branch will also be deleted if it's not checked out elsewhere"
|
|
1629
|
+
]
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
// src/cli/help/exec.ts
|
|
1633
|
+
var execHelp = {
|
|
1634
|
+
name: "exec",
|
|
1635
|
+
description: "Execute a command in a worktree directory",
|
|
1636
|
+
usage: "phantom exec <worktree-name> <command> [args...]",
|
|
1637
|
+
examples: [
|
|
1638
|
+
{
|
|
1639
|
+
description: "Run npm test in a worktree",
|
|
1640
|
+
command: "phantom exec feature-auth npm test"
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
description: "Check git status in a worktree",
|
|
1644
|
+
command: "phantom exec bugfix-123 git status"
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
description: "Run a complex command with arguments",
|
|
1648
|
+
command: "phantom exec staging npm run build -- --production"
|
|
1649
|
+
}
|
|
1650
|
+
],
|
|
1651
|
+
notes: [
|
|
1652
|
+
"The command is executed with the worktree directory as the working directory",
|
|
1653
|
+
"All arguments after the worktree name are passed to the command",
|
|
1654
|
+
"The exit code of the executed command is preserved"
|
|
1655
|
+
]
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
// src/cli/help/list.ts
|
|
1659
|
+
var listHelp = {
|
|
1660
|
+
name: "list",
|
|
1661
|
+
description: "List all Git worktrees (phantoms)",
|
|
1662
|
+
usage: "phantom list",
|
|
1663
|
+
examples: [
|
|
1664
|
+
{
|
|
1665
|
+
description: "List all worktrees",
|
|
1666
|
+
command: "phantom list"
|
|
1667
|
+
}
|
|
1668
|
+
],
|
|
1669
|
+
notes: [
|
|
1670
|
+
"Shows all worktrees with their paths and associated branches",
|
|
1671
|
+
"The main worktree is marked as '(bare)' if using a bare repository"
|
|
1672
|
+
]
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
// src/cli/help/shell.ts
|
|
1676
|
+
var shellHelp = {
|
|
1677
|
+
name: "shell",
|
|
1678
|
+
description: "Open an interactive shell in a worktree directory",
|
|
1679
|
+
usage: "phantom shell <worktree-name>",
|
|
1680
|
+
examples: [
|
|
1681
|
+
{
|
|
1682
|
+
description: "Open a shell in a worktree",
|
|
1683
|
+
command: "phantom shell feature-auth"
|
|
1684
|
+
}
|
|
1685
|
+
],
|
|
1686
|
+
notes: [
|
|
1687
|
+
"Uses your default shell from the SHELL environment variable",
|
|
1688
|
+
"The shell starts with the worktree directory as the working directory",
|
|
1689
|
+
"Type 'exit' to return to your original directory"
|
|
1690
|
+
]
|
|
1691
|
+
};
|
|
1692
|
+
|
|
1693
|
+
// src/cli/help/version.ts
|
|
1694
|
+
var versionHelp = {
|
|
1695
|
+
name: "version",
|
|
1696
|
+
description: "Display phantom version information",
|
|
1697
|
+
usage: "phantom version",
|
|
1698
|
+
examples: [
|
|
1699
|
+
{
|
|
1700
|
+
description: "Show version",
|
|
1701
|
+
command: "phantom version"
|
|
1702
|
+
}
|
|
1703
|
+
],
|
|
1704
|
+
notes: ["Also accessible via 'phantom --version' or 'phantom -v'"]
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1707
|
+
// src/cli/help/where.ts
|
|
1708
|
+
var whereHelp = {
|
|
1709
|
+
name: "where",
|
|
1710
|
+
description: "Output the filesystem path of a specific worktree",
|
|
1711
|
+
usage: "phantom where <worktree-name>",
|
|
1712
|
+
examples: [
|
|
1713
|
+
{
|
|
1714
|
+
description: "Get the path of a worktree",
|
|
1715
|
+
command: "phantom where feature-auth"
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
description: "Change directory to a worktree",
|
|
1719
|
+
command: "cd $(phantom where staging)"
|
|
1720
|
+
}
|
|
1721
|
+
],
|
|
1722
|
+
notes: [
|
|
1723
|
+
"Outputs only the path, making it suitable for use in scripts",
|
|
1724
|
+
"Exits with an error code if the worktree doesn't exist"
|
|
1725
|
+
]
|
|
1726
|
+
};
|
|
1727
|
+
|
|
1298
1728
|
// src/bin/phantom.ts
|
|
1299
1729
|
var commands = [
|
|
1300
1730
|
{
|
|
1301
1731
|
name: "create",
|
|
1302
|
-
description: "Create a new worktree
|
|
1303
|
-
handler: createHandler
|
|
1732
|
+
description: "Create a new Git worktree (phantom)",
|
|
1733
|
+
handler: createHandler,
|
|
1734
|
+
help: createHelp
|
|
1304
1735
|
},
|
|
1305
1736
|
{
|
|
1306
1737
|
name: "attach",
|
|
1307
|
-
description: "Attach to an existing branch
|
|
1308
|
-
handler: attachHandler
|
|
1738
|
+
description: "Attach to an existing branch by creating a new worktree",
|
|
1739
|
+
handler: attachHandler,
|
|
1740
|
+
help: attachHelp
|
|
1309
1741
|
},
|
|
1310
1742
|
{
|
|
1311
1743
|
name: "list",
|
|
1312
|
-
description: "List all worktrees",
|
|
1313
|
-
handler: listHandler
|
|
1744
|
+
description: "List all Git worktrees (phantoms)",
|
|
1745
|
+
handler: listHandler,
|
|
1746
|
+
help: listHelp
|
|
1314
1747
|
},
|
|
1315
1748
|
{
|
|
1316
1749
|
name: "where",
|
|
1317
|
-
description: "Output the path of a specific worktree",
|
|
1318
|
-
handler: whereHandler
|
|
1750
|
+
description: "Output the filesystem path of a specific worktree",
|
|
1751
|
+
handler: whereHandler,
|
|
1752
|
+
help: whereHelp
|
|
1319
1753
|
},
|
|
1320
1754
|
{
|
|
1321
1755
|
name: "delete",
|
|
1322
|
-
description: "Delete a worktree (
|
|
1323
|
-
handler: deleteHandler
|
|
1756
|
+
description: "Delete a Git worktree (phantom)",
|
|
1757
|
+
handler: deleteHandler,
|
|
1758
|
+
help: deleteHelp
|
|
1324
1759
|
},
|
|
1325
1760
|
{
|
|
1326
1761
|
name: "exec",
|
|
1327
1762
|
description: "Execute a command in a worktree directory",
|
|
1328
|
-
handler: execHandler
|
|
1763
|
+
handler: execHandler,
|
|
1764
|
+
help: execHelp
|
|
1329
1765
|
},
|
|
1330
1766
|
{
|
|
1331
1767
|
name: "shell",
|
|
1332
|
-
description: "Open interactive shell in a worktree directory",
|
|
1333
|
-
handler: shellHandler
|
|
1768
|
+
description: "Open an interactive shell in a worktree directory",
|
|
1769
|
+
handler: shellHandler,
|
|
1770
|
+
help: shellHelp
|
|
1334
1771
|
},
|
|
1335
1772
|
{
|
|
1336
1773
|
name: "version",
|
|
1337
|
-
description: "Display phantom version",
|
|
1338
|
-
handler: versionHandler
|
|
1774
|
+
description: "Display phantom version information",
|
|
1775
|
+
handler: versionHandler,
|
|
1776
|
+
help: versionHelp
|
|
1339
1777
|
}
|
|
1340
1778
|
];
|
|
1341
1779
|
function printHelp(commands2) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1780
|
+
const simpleCommands = commands2.map((cmd) => ({
|
|
1781
|
+
name: cmd.name,
|
|
1782
|
+
description: cmd.description
|
|
1783
|
+
}));
|
|
1784
|
+
console.log(helpFormatter.formatMainHelp(simpleCommands));
|
|
1347
1785
|
}
|
|
1348
1786
|
function findCommand(args2, commands2) {
|
|
1349
1787
|
if (args2.length === 0) {
|
|
@@ -1381,6 +1819,14 @@ if (!command || !command.handler) {
|
|
|
1381
1819
|
printHelp(commands);
|
|
1382
1820
|
exit(1);
|
|
1383
1821
|
}
|
|
1822
|
+
if (remainingArgs.includes("--help") || remainingArgs.includes("-h")) {
|
|
1823
|
+
if (command.help) {
|
|
1824
|
+
console.log(helpFormatter.formatCommandHelp(command.help));
|
|
1825
|
+
} else {
|
|
1826
|
+
console.log(`Help not available for command '${command.name}'`);
|
|
1827
|
+
}
|
|
1828
|
+
exit(0);
|
|
1829
|
+
}
|
|
1384
1830
|
try {
|
|
1385
1831
|
await command.handler(remainingArgs);
|
|
1386
1832
|
} catch (error) {
|