@aku11i/phantom 0.9.0 → 1.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/dist/phantom.js CHANGED
@@ -67,6 +67,18 @@ var err = (error) => ({
67
67
  var isOk = (result) => result.ok;
68
68
  var isErr = (result) => !result.ok;
69
69
 
70
+ // src/core/worktree/validate.ts
71
+ import fs from "node:fs/promises";
72
+
73
+ // src/core/paths.ts
74
+ import { join } from "node:path";
75
+ function getPhantomDirectory(gitRoot) {
76
+ return join(gitRoot, ".git", "phantom", "worktrees");
77
+ }
78
+ function getWorktreePath(gitRoot, name) {
79
+ return join(getPhantomDirectory(gitRoot), name);
80
+ }
81
+
70
82
  // src/core/worktree/errors.ts
71
83
  var WorktreeError = class extends Error {
72
84
  constructor(message) {
@@ -99,58 +111,39 @@ var BranchNotFoundError = class extends WorktreeError {
99
111
  }
100
112
  };
101
113
 
102
- // src/core/worktree/validate.ts
103
- import fs from "node:fs/promises";
104
-
105
- // src/core/paths.ts
106
- import { join } from "node:path";
107
- function getPhantomDirectory(gitRoot) {
108
- return join(gitRoot, ".git", "phantom", "worktrees");
109
- }
110
- function getWorktreePath(gitRoot, name) {
111
- return join(getPhantomDirectory(gitRoot), name);
112
- }
113
-
114
114
  // src/core/worktree/validate.ts
115
115
  async function validateWorktreeExists(gitRoot, name) {
116
116
  const worktreePath = getWorktreePath(gitRoot, name);
117
117
  try {
118
118
  await fs.access(worktreePath);
119
- return {
120
- exists: true,
121
- path: worktreePath
122
- };
119
+ return ok({ path: worktreePath });
123
120
  } catch {
124
- return {
125
- exists: false,
126
- message: `Worktree '${name}' does not exist`
127
- };
121
+ return err(new WorktreeNotFoundError(name));
128
122
  }
129
123
  }
130
124
  async function validateWorktreeDoesNotExist(gitRoot, name) {
131
125
  const worktreePath = getWorktreePath(gitRoot, name);
132
126
  try {
133
127
  await fs.access(worktreePath);
134
- return {
135
- exists: true,
136
- message: `Worktree '${name}' already exists`
137
- };
128
+ return err(new WorktreeAlreadyExistsError(name));
138
129
  } catch {
139
- return {
140
- exists: false,
141
- path: worktreePath
142
- };
130
+ return ok({ path: worktreePath });
143
131
  }
144
132
  }
145
133
  function validateWorktreeName(name) {
146
134
  if (!name || name.trim() === "") {
147
135
  return err(new Error("Phantom name cannot be empty"));
148
136
  }
149
- if (name.includes("/")) {
150
- return err(new Error("Phantom name cannot contain slashes"));
137
+ const validNamePattern = /^[a-zA-Z0-9\-_.\/]+$/;
138
+ if (!validNamePattern.test(name)) {
139
+ return err(
140
+ new Error(
141
+ "Phantom name can only contain letters, numbers, hyphens, underscores, dots, and slashes"
142
+ )
143
+ );
151
144
  }
152
- if (name.startsWith(".")) {
153
- return err(new Error("Phantom name cannot start with a dot"));
145
+ if (name.includes("..")) {
146
+ return err(new Error("Phantom name cannot contain consecutive dots"));
154
147
  }
155
148
  return ok(void 0);
156
149
  }
@@ -218,10 +211,10 @@ async function spawnProcess(config) {
218
211
  // src/core/process/exec.ts
219
212
  async function execInWorktree(gitRoot, worktreeName, command2, options = {}) {
220
213
  const validation = await validateWorktreeExists(gitRoot, worktreeName);
221
- if (!validation.exists) {
222
- return err(new WorktreeNotFoundError(worktreeName));
214
+ if (isErr(validation)) {
215
+ return err(validation.error);
223
216
  }
224
- const worktreePath = validation.path;
217
+ const worktreePath = validation.value.path;
225
218
  const [cmd, ...args2] = command2;
226
219
  const stdio = options.interactive ? "inherit" : ["ignore", "inherit", "inherit"];
227
220
  return spawnProcess({
@@ -234,13 +227,22 @@ async function execInWorktree(gitRoot, worktreeName, command2, options = {}) {
234
227
  });
235
228
  }
236
229
 
230
+ // src/core/process/env.ts
231
+ function getPhantomEnv(worktreeName, worktreePath) {
232
+ return {
233
+ PHANTOM: "1",
234
+ PHANTOM_NAME: worktreeName,
235
+ PHANTOM_PATH: worktreePath
236
+ };
237
+ }
238
+
237
239
  // src/core/process/shell.ts
238
240
  async function shellInWorktree(gitRoot, worktreeName) {
239
241
  const validation = await validateWorktreeExists(gitRoot, worktreeName);
240
- if (!validation.exists) {
241
- return err(new WorktreeNotFoundError(worktreeName));
242
+ if (isErr(validation)) {
243
+ return err(validation.error);
242
244
  }
243
- const worktreePath = validation.path;
245
+ const worktreePath = validation.value.path;
244
246
  const shell = process.env.SHELL || "/bin/sh";
245
247
  return spawnProcess({
246
248
  command: shell,
@@ -249,9 +251,7 @@ async function shellInWorktree(gitRoot, worktreeName) {
249
251
  cwd: worktreePath,
250
252
  env: {
251
253
  ...process.env,
252
- PHANTOM: "1",
253
- PHANTOM_NAME: worktreeName,
254
- PHANTOM_PATH: worktreePath
254
+ ...getPhantomEnv(worktreeName, worktreePath)
255
255
  }
256
256
  }
257
257
  });
@@ -406,10 +406,11 @@ async function attachHandler(args2) {
406
406
  exitWithError(shellResult.error.message, exitCodes.generalError);
407
407
  }
408
408
  } else if (values.exec) {
409
+ const shell = process.env.SHELL || "/bin/sh";
409
410
  const execResult = await execInWorktree(
410
411
  gitRoot,
411
412
  branchName,
412
- values.exec.split(" "),
413
+ [shell, "-c", values.exec],
413
414
  { interactive: true }
414
415
  );
415
416
  if (isErr(execResult)) {
@@ -715,11 +716,14 @@ async function isInsideTmux() {
715
716
  return process.env.TMUX !== void 0;
716
717
  }
717
718
  async function executeTmuxCommand(options) {
718
- const { direction, command: command2, cwd, env } = options;
719
+ const { direction, command: command2, args: args2, cwd, env, windowName } = options;
719
720
  const tmuxArgs = [];
720
721
  switch (direction) {
721
722
  case "new":
722
723
  tmuxArgs.push("new-window");
724
+ if (windowName) {
725
+ tmuxArgs.push("-n", windowName);
726
+ }
723
727
  break;
724
728
  case "vertical":
725
729
  tmuxArgs.push("split-window", "-v");
@@ -737,6 +741,9 @@ async function executeTmuxCommand(options) {
737
741
  }
738
742
  }
739
743
  tmuxArgs.push(command2);
744
+ if (args2 && args2.length > 0) {
745
+ tmuxArgs.push(...args2);
746
+ }
740
747
  const result = await spawnProcess({
741
748
  command: "tmux",
742
749
  args: tmuxArgs
@@ -811,8 +818,8 @@ async function createWorktree(gitRoot, name, options = {}) {
811
818
  await fs3.mkdir(worktreesPath, { recursive: true });
812
819
  }
813
820
  const validation = await validateWorktreeDoesNotExist(gitRoot, name);
814
- if (validation.exists) {
815
- return err(new WorktreeAlreadyExistsError(name));
821
+ if (isErr(validation)) {
822
+ return err(validation.error);
816
823
  }
817
824
  try {
818
825
  await addWorktree({
@@ -1017,11 +1024,8 @@ Opening worktree '${worktreeName}' in tmux ${tmuxDirection === "new" ? "window"
1017
1024
  direction: tmuxDirection,
1018
1025
  command: shell,
1019
1026
  cwd: result.value.path,
1020
- env: {
1021
- PHANTOM: "1",
1022
- PHANTOM_NAME: worktreeName,
1023
- PHANTOM_PATH: result.value.path
1024
- }
1027
+ env: getPhantomEnv(worktreeName, result.value.path),
1028
+ windowName: tmuxDirection === "new" ? worktreeName : void 0
1025
1029
  });
1026
1030
  if (isErr(tmuxResult)) {
1027
1031
  output.error(tmuxResult.error.message);
@@ -1148,10 +1152,10 @@ async function deleteBranch(gitRoot, branchName) {
1148
1152
  async function deleteWorktree(gitRoot, name, options = {}) {
1149
1153
  const { force = false } = options;
1150
1154
  const validation = await validateWorktreeExists(gitRoot, name);
1151
- if (!validation.exists) {
1152
- return err(new WorktreeNotFoundError(name));
1155
+ if (isErr(validation)) {
1156
+ return err(validation.error);
1153
1157
  }
1154
- const worktreePath = validation.path;
1158
+ const worktreePath = validation.value.path;
1155
1159
  const status = await getWorktreeStatus(worktreePath);
1156
1160
  if (status.hasUncommittedChanges && !force) {
1157
1161
  return err(
@@ -1306,7 +1310,7 @@ async function selectWorktreeWithFzf(gitRoot) {
1306
1310
  });
1307
1311
  const fzfResult = await selectWithFzf(list, {
1308
1312
  prompt: "Select worktree> ",
1309
- header: "Git Worktrees (Phantoms)"
1313
+ header: "Git Worktrees"
1310
1314
  });
1311
1315
  if (isErr(fzfResult)) {
1312
1316
  return fzfResult;
@@ -1420,21 +1424,106 @@ async function deleteHandler(args2) {
1420
1424
  // src/cli/handlers/exec.ts
1421
1425
  import { parseArgs as parseArgs4 } from "node:util";
1422
1426
  async function execHandler(args2) {
1423
- const { positionals } = parseArgs4({
1427
+ const { positionals, values } = parseArgs4({
1424
1428
  args: args2,
1425
- options: {},
1429
+ options: {
1430
+ fzf: {
1431
+ type: "boolean",
1432
+ default: false
1433
+ },
1434
+ tmux: {
1435
+ type: "boolean",
1436
+ short: "t"
1437
+ },
1438
+ "tmux-vertical": {
1439
+ type: "boolean"
1440
+ },
1441
+ "tmux-v": {
1442
+ type: "boolean"
1443
+ },
1444
+ "tmux-horizontal": {
1445
+ type: "boolean"
1446
+ },
1447
+ "tmux-h": {
1448
+ type: "boolean"
1449
+ }
1450
+ },
1426
1451
  strict: true,
1427
1452
  allowPositionals: true
1428
1453
  });
1429
- if (positionals.length < 2) {
1430
- exitWithError(
1431
- "Usage: phantom exec <worktree-name> <command> [args...]",
1432
- exitCodes.validationError
1433
- );
1454
+ const useFzf = values.fzf ?? false;
1455
+ const tmuxOption = values.tmux || values["tmux-vertical"] || values["tmux-v"] || values["tmux-horizontal"] || values["tmux-h"];
1456
+ let tmuxDirection;
1457
+ if (values.tmux) {
1458
+ tmuxDirection = "new";
1459
+ } else if (values["tmux-vertical"] || values["tmux-v"]) {
1460
+ tmuxDirection = "vertical";
1461
+ } else if (values["tmux-horizontal"] || values["tmux-h"]) {
1462
+ tmuxDirection = "horizontal";
1463
+ }
1464
+ let commandArgs;
1465
+ if (useFzf) {
1466
+ if (positionals.length < 1) {
1467
+ exitWithError(
1468
+ "Usage: phantom exec --fzf <command> [args...]",
1469
+ exitCodes.validationError
1470
+ );
1471
+ }
1472
+ commandArgs = positionals;
1473
+ } else {
1474
+ if (positionals.length < 2) {
1475
+ exitWithError(
1476
+ "Usage: phantom exec <worktree-name> <command> [args...]",
1477
+ exitCodes.validationError
1478
+ );
1479
+ }
1480
+ commandArgs = positionals.slice(1);
1434
1481
  }
1435
- const [worktreeName, ...commandArgs] = positionals;
1436
1482
  try {
1437
1483
  const gitRoot = await getGitRoot();
1484
+ if (tmuxOption && !await isInsideTmux()) {
1485
+ exitWithError(
1486
+ "The --tmux option can only be used inside a tmux session",
1487
+ exitCodes.validationError
1488
+ );
1489
+ }
1490
+ let worktreeName;
1491
+ if (useFzf) {
1492
+ const selectResult = await selectWorktreeWithFzf(gitRoot);
1493
+ if (isErr(selectResult)) {
1494
+ exitWithError(selectResult.error.message, exitCodes.generalError);
1495
+ }
1496
+ if (!selectResult.value) {
1497
+ exitWithSuccess();
1498
+ }
1499
+ worktreeName = selectResult.value.name;
1500
+ } else {
1501
+ worktreeName = positionals[0];
1502
+ }
1503
+ const validation = await validateWorktreeExists(gitRoot, worktreeName);
1504
+ if (isErr(validation)) {
1505
+ exitWithError(validation.error.message, exitCodes.generalError);
1506
+ }
1507
+ if (tmuxDirection) {
1508
+ output.log(
1509
+ `Executing command in worktree '${worktreeName}' in tmux ${tmuxDirection === "new" ? "window" : "pane"}...`
1510
+ );
1511
+ const [command2, ...args3] = commandArgs;
1512
+ const tmuxResult = await executeTmuxCommand({
1513
+ direction: tmuxDirection,
1514
+ command: command2,
1515
+ args: args3,
1516
+ cwd: validation.value.path,
1517
+ env: getPhantomEnv(worktreeName, validation.value.path),
1518
+ windowName: tmuxDirection === "new" ? worktreeName : void 0
1519
+ });
1520
+ if (isErr(tmuxResult)) {
1521
+ output.error(tmuxResult.error.message);
1522
+ const exitCode = "exitCode" in tmuxResult.error ? tmuxResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
1523
+ exitWithError("", exitCode);
1524
+ }
1525
+ exitWithSuccess();
1526
+ }
1438
1527
  const result = await execInWorktree(
1439
1528
  gitRoot,
1440
1529
  worktreeName,
@@ -1528,12 +1617,37 @@ async function shellHandler(args2) {
1528
1617
  fzf: {
1529
1618
  type: "boolean",
1530
1619
  default: false
1620
+ },
1621
+ tmux: {
1622
+ type: "boolean",
1623
+ short: "t"
1624
+ },
1625
+ "tmux-vertical": {
1626
+ type: "boolean"
1627
+ },
1628
+ "tmux-v": {
1629
+ type: "boolean"
1630
+ },
1631
+ "tmux-horizontal": {
1632
+ type: "boolean"
1633
+ },
1634
+ "tmux-h": {
1635
+ type: "boolean"
1531
1636
  }
1532
1637
  },
1533
1638
  strict: true,
1534
1639
  allowPositionals: true
1535
1640
  });
1536
1641
  const useFzf = values.fzf ?? false;
1642
+ const tmuxOption = values.tmux || values["tmux-vertical"] || values["tmux-v"] || values["tmux-horizontal"] || values["tmux-h"];
1643
+ let tmuxDirection;
1644
+ if (values.tmux) {
1645
+ tmuxDirection = "new";
1646
+ } else if (values["tmux-vertical"] || values["tmux-v"]) {
1647
+ tmuxDirection = "vertical";
1648
+ } else if (values["tmux-horizontal"] || values["tmux-h"]) {
1649
+ tmuxDirection = "horizontal";
1650
+ }
1537
1651
  if (positionals.length === 0 && !useFzf) {
1538
1652
  exitWithError(
1539
1653
  "Usage: phantom shell <worktree-name> or phantom shell --fzf",
@@ -1549,6 +1663,12 @@ async function shellHandler(args2) {
1549
1663
  let worktreeName;
1550
1664
  try {
1551
1665
  const gitRoot = await getGitRoot();
1666
+ if (tmuxOption && !await isInsideTmux()) {
1667
+ exitWithError(
1668
+ "The --tmux option can only be used inside a tmux session",
1669
+ exitCodes.validationError
1670
+ );
1671
+ }
1552
1672
  if (useFzf) {
1553
1673
  const selectResult = await selectWorktreeWithFzf(gitRoot);
1554
1674
  if (isErr(selectResult)) {
@@ -1562,13 +1682,31 @@ async function shellHandler(args2) {
1562
1682
  worktreeName = positionals[0];
1563
1683
  }
1564
1684
  const validation = await validateWorktreeExists(gitRoot, worktreeName);
1565
- if (!validation.exists) {
1566
- exitWithError(
1567
- validation.message || `Worktree '${worktreeName}' not found`,
1568
- exitCodes.generalError
1685
+ if (isErr(validation)) {
1686
+ exitWithError(validation.error.message, exitCodes.generalError);
1687
+ }
1688
+ if (tmuxDirection) {
1689
+ output.log(
1690
+ `Opening worktree '${worktreeName}' in tmux ${tmuxDirection === "new" ? "window" : "pane"}...`
1569
1691
  );
1692
+ const shell = process.env.SHELL || "/bin/sh";
1693
+ const tmuxResult = await executeTmuxCommand({
1694
+ direction: tmuxDirection,
1695
+ command: shell,
1696
+ cwd: validation.value.path,
1697
+ env: getPhantomEnv(worktreeName, validation.value.path),
1698
+ windowName: tmuxDirection === "new" ? worktreeName : void 0
1699
+ });
1700
+ if (isErr(tmuxResult)) {
1701
+ output.error(tmuxResult.error.message);
1702
+ const exitCode = "exitCode" in tmuxResult.error ? tmuxResult.error.exitCode ?? exitCodes.generalError : exitCodes.generalError;
1703
+ exitWithError("", exitCode);
1704
+ }
1705
+ exitWithSuccess();
1570
1706
  }
1571
- output.log(`Entering worktree '${worktreeName}' at ${validation.path}`);
1707
+ output.log(
1708
+ `Entering worktree '${worktreeName}' at ${validation.value.path}`
1709
+ );
1572
1710
  output.log("Type 'exit' to return to your original directory\n");
1573
1711
  const result = await shellInWorktree(gitRoot, worktreeName);
1574
1712
  if (isErr(result)) {
@@ -1591,7 +1729,7 @@ import { parseArgs as parseArgs7 } from "node:util";
1591
1729
  var package_default = {
1592
1730
  name: "@aku11i/phantom",
1593
1731
  packageManager: "pnpm@10.8.1",
1594
- version: "0.9.0",
1732
+ version: "1.0.0",
1595
1733
  description: "A powerful CLI tool for managing Git worktrees for parallel development",
1596
1734
  keywords: [
1597
1735
  "git",
@@ -1671,11 +1809,11 @@ import { parseArgs as parseArgs8 } from "node:util";
1671
1809
  // src/core/worktree/where.ts
1672
1810
  async function whereWorktree(gitRoot, name) {
1673
1811
  const validation = await validateWorktreeExists(gitRoot, name);
1674
- if (!validation.exists) {
1675
- return err(new WorktreeNotFoundError(name));
1812
+ if (isErr(validation)) {
1813
+ return err(validation.error);
1676
1814
  }
1677
1815
  return ok({
1678
- path: validation.path
1816
+ path: validation.value.path
1679
1817
  });
1680
1818
  }
1681
1819
 
@@ -1955,7 +2093,7 @@ var completionHelp = {
1955
2093
  // src/cli/help/create.ts
1956
2094
  var createHelp = {
1957
2095
  name: "create",
1958
- description: "Create a new Git worktree (phantom)",
2096
+ description: "Create a new Git worktree",
1959
2097
  usage: "phantom create <name> [options]",
1960
2098
  options: [
1961
2099
  {
@@ -2027,7 +2165,7 @@ var createHelp = {
2027
2165
  // src/cli/help/delete.ts
2028
2166
  var deleteHelp = {
2029
2167
  name: "delete",
2030
- description: "Delete a Git worktree (phantom)",
2168
+ description: "Delete a Git worktree",
2031
2169
  usage: "phantom delete <name> [options]",
2032
2170
  options: [
2033
2171
  {
@@ -2076,7 +2214,29 @@ var deleteHelp = {
2076
2214
  var execHelp = {
2077
2215
  name: "exec",
2078
2216
  description: "Execute a command in a worktree directory",
2079
- usage: "phantom exec <worktree-name> <command> [args...]",
2217
+ usage: "phantom exec [options] <worktree-name> <command> [args...]",
2218
+ options: [
2219
+ {
2220
+ name: "--fzf",
2221
+ type: "boolean",
2222
+ description: "Use fzf for interactive worktree selection"
2223
+ },
2224
+ {
2225
+ name: "--tmux, -t",
2226
+ type: "boolean",
2227
+ description: "Execute command in new tmux window"
2228
+ },
2229
+ {
2230
+ name: "--tmux-vertical, --tmux-v",
2231
+ type: "boolean",
2232
+ description: "Execute command in vertical split pane"
2233
+ },
2234
+ {
2235
+ name: "--tmux-horizontal, --tmux-h",
2236
+ type: "boolean",
2237
+ description: "Execute command in horizontal split pane"
2238
+ }
2239
+ ],
2080
2240
  examples: [
2081
2241
  {
2082
2242
  description: "Run npm test in a worktree",
@@ -2089,19 +2249,37 @@ var execHelp = {
2089
2249
  {
2090
2250
  description: "Run a complex command with arguments",
2091
2251
  command: "phantom exec staging npm run build -- --production"
2252
+ },
2253
+ {
2254
+ description: "Execute with interactive selection",
2255
+ command: "phantom exec --fzf npm run dev"
2256
+ },
2257
+ {
2258
+ description: "Run dev server in new tmux window",
2259
+ command: "phantom exec --tmux feature-auth npm run dev"
2260
+ },
2261
+ {
2262
+ description: "Run tests in vertical split pane",
2263
+ command: "phantom exec --tmux-v feature-auth npm test"
2264
+ },
2265
+ {
2266
+ description: "Interactive selection with tmux",
2267
+ command: "phantom exec --fzf --tmux npm run dev"
2092
2268
  }
2093
2269
  ],
2094
2270
  notes: [
2095
2271
  "The command is executed with the worktree directory as the working directory",
2096
2272
  "All arguments after the worktree name are passed to the command",
2097
- "The exit code of the executed command is preserved"
2273
+ "The exit code of the executed command is preserved",
2274
+ "With --fzf, select the worktree interactively before executing the command",
2275
+ "Tmux options require being inside a tmux session"
2098
2276
  ]
2099
2277
  };
2100
2278
 
2101
2279
  // src/cli/help/list.ts
2102
2280
  var listHelp = {
2103
2281
  name: "list",
2104
- description: "List all Git worktrees (phantoms)",
2282
+ description: "List all Git worktrees",
2105
2283
  usage: "phantom list [options]",
2106
2284
  options: [
2107
2285
  {
@@ -2112,7 +2290,7 @@ var listHelp = {
2112
2290
  {
2113
2291
  name: "--names",
2114
2292
  type: "boolean",
2115
- description: "Output only phantom names (for scripts and completion)"
2293
+ description: "Output only worktree names (for scripts and completion)"
2116
2294
  }
2117
2295
  ],
2118
2296
  examples: [
@@ -2147,6 +2325,21 @@ var shellHelp = {
2147
2325
  name: "--fzf",
2148
2326
  type: "boolean",
2149
2327
  description: "Use fzf for interactive selection"
2328
+ },
2329
+ {
2330
+ name: "--tmux, -t",
2331
+ type: "boolean",
2332
+ description: "Open shell in new tmux window"
2333
+ },
2334
+ {
2335
+ name: "--tmux-vertical, --tmux-v",
2336
+ type: "boolean",
2337
+ description: "Open shell in vertical split pane"
2338
+ },
2339
+ {
2340
+ name: "--tmux-horizontal, --tmux-h",
2341
+ type: "boolean",
2342
+ description: "Open shell in horizontal split pane"
2150
2343
  }
2151
2344
  ],
2152
2345
  examples: [
@@ -2157,13 +2350,26 @@ var shellHelp = {
2157
2350
  {
2158
2351
  description: "Open a shell with interactive fzf selection",
2159
2352
  command: "phantom shell --fzf"
2353
+ },
2354
+ {
2355
+ description: "Open a shell in a new tmux window",
2356
+ command: "phantom shell feature-auth --tmux"
2357
+ },
2358
+ {
2359
+ description: "Open a shell in a vertical tmux pane",
2360
+ command: "phantom shell feature-auth --tmux-v"
2361
+ },
2362
+ {
2363
+ description: "Interactive selection with tmux",
2364
+ command: "phantom shell --fzf --tmux"
2160
2365
  }
2161
2366
  ],
2162
2367
  notes: [
2163
2368
  "Uses your default shell from the SHELL environment variable",
2164
2369
  "The shell starts with the worktree directory as the working directory",
2165
2370
  "Type 'exit' to return to your original directory",
2166
- "With --fzf, you can interactively select the worktree to enter"
2371
+ "With --fzf, you can interactively select the worktree to enter",
2372
+ "Tmux options require being inside a tmux session"
2167
2373
  ]
2168
2374
  };
2169
2375