@aku11i/phantom 1.3.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +4 -1
  2. package/phantom.js +279 -61
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aku11i/phantom",
3
- "version": "1.3.0",
3
+ "version": "2.1.0",
4
4
  "description": "A powerful CLI tool for managing Git worktrees for parallel development",
5
5
  "keywords": [
6
6
  "git",
@@ -31,5 +31,8 @@
31
31
  "README.md",
32
32
  "LICENSE"
33
33
  ],
34
+ "engines": {
35
+ "node": ">=22.0.0"
36
+ },
34
37
  "dependencies": {}
35
38
  }
package/phantom.js CHANGED
@@ -4203,13 +4203,16 @@ var phantomConfigSchema = external_exports.object({
4203
4203
  copyFiles: external_exports.array(external_exports.string()).optional(),
4204
4204
  commands: external_exports.array(external_exports.string()).optional()
4205
4205
  }).passthrough().optional(),
4206
- worktreesDirectory: external_exports.string().optional()
4206
+ preDelete: external_exports.object({
4207
+ commands: external_exports.array(external_exports.string()).optional()
4208
+ }).passthrough().optional(),
4209
+ worktreesDirectory: external_exports.string().optional(),
4210
+ defaultBranch: external_exports.string().optional()
4207
4211
  }).passthrough();
4208
4212
  function validateConfig(config) {
4209
4213
  const result = phantomConfigSchema.safeParse(config);
4210
4214
  if (!result.success) {
4211
4215
  const error = result.error;
4212
- const formattedError = error.format();
4213
4216
  const firstError = error.errors[0];
4214
4217
  const path3 = firstError.path.join(".");
4215
4218
  const message = path3 ? `${path3}: ${firstError.message}` : firstError.message;
@@ -4403,7 +4406,7 @@ async function fetch(options = {}) {
4403
4406
  }
4404
4407
 
4405
4408
  // ../git/src/libs/list-worktrees.ts
4406
- async function listWorktrees(gitRoot) {
4409
+ async function listWorktrees(_gitRoot) {
4407
4410
  const { stdout: stdout2 } = await executeGitCommand([
4408
4411
  "worktree",
4409
4412
  "list",
@@ -4695,7 +4698,7 @@ async function selectWithFzf(items, options = {}) {
4695
4698
 
4696
4699
  // ../core/src/worktree/validate.ts
4697
4700
  import fs2 from "node:fs/promises";
4698
- async function validateWorktreeExists(gitRoot, worktreeDirectory, name) {
4701
+ async function validateWorktreeExists(_gitRoot, worktreeDirectory, name) {
4699
4702
  const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4700
4703
  try {
4701
4704
  await fs2.access(worktreePath);
@@ -4704,7 +4707,7 @@ async function validateWorktreeExists(gitRoot, worktreeDirectory, name) {
4704
4707
  return err(new WorktreeNotFoundError(name));
4705
4708
  }
4706
4709
  }
4707
- async function validateWorktreeDoesNotExist(gitRoot, worktreeDirectory, name) {
4710
+ async function validateWorktreeDoesNotExist(_gitRoot, worktreeDirectory, name) {
4708
4711
  const worktreePath = getWorktreePathFromDirectory(worktreeDirectory, name);
4709
4712
  try {
4710
4713
  await fs2.access(worktreePath);
@@ -4879,6 +4882,39 @@ async function createWorktree(gitRoot, worktreeDirectory, name, options, postCre
4879
4882
  }
4880
4883
  }
4881
4884
 
4885
+ // ../core/src/worktree/pre-delete.ts
4886
+ async function executePreDeleteCommands(options) {
4887
+ const { gitRoot, worktreesDirectory, worktreeName, commands: commands2 } = options;
4888
+ const executedCommands = [];
4889
+ for (const command2 of commands2) {
4890
+ console.log(`Executing pre-delete command: ${command2}`);
4891
+ const shell = process.env.SHELL || "/bin/sh";
4892
+ const cmdResult = await execInWorktree(
4893
+ gitRoot,
4894
+ worktreesDirectory,
4895
+ worktreeName,
4896
+ [shell, "-c", command2]
4897
+ );
4898
+ if (isErr(cmdResult)) {
4899
+ const errorMessage = cmdResult.error instanceof Error ? cmdResult.error.message : String(cmdResult.error);
4900
+ return err(
4901
+ new Error(
4902
+ `Failed to execute pre-delete command "${command2}": ${errorMessage}`
4903
+ )
4904
+ );
4905
+ }
4906
+ if (cmdResult.value.exitCode !== 0) {
4907
+ return err(
4908
+ new Error(
4909
+ `Pre-delete command failed with exit code ${cmdResult.value.exitCode}: ${command2}`
4910
+ )
4911
+ );
4912
+ }
4913
+ executedCommands.push(command2);
4914
+ }
4915
+ return ok({ executedCommands });
4916
+ }
4917
+
4882
4918
  // ../core/src/worktree/delete.ts
4883
4919
  async function getWorktreeChangesStatus(worktreePath) {
4884
4920
  try {
@@ -4900,19 +4936,14 @@ async function getWorktreeChangesStatus(worktreePath) {
4900
4936
  };
4901
4937
  }
4902
4938
  async function removeWorktree(gitRoot, worktreePath, force = false) {
4903
- try {
4904
- await executeGitCommand(["worktree", "remove", worktreePath], {
4905
- cwd: gitRoot
4906
- });
4907
- } catch (error) {
4908
- try {
4909
- await executeGitCommand(["worktree", "remove", "--force", worktreePath], {
4910
- cwd: gitRoot
4911
- });
4912
- } catch {
4913
- throw new Error("Failed to remove worktree");
4914
- }
4939
+ const args2 = ["worktree", "remove"];
4940
+ if (force) {
4941
+ args2.push("--force");
4915
4942
  }
4943
+ args2.push(worktreePath);
4944
+ await executeGitCommand(args2, {
4945
+ cwd: gitRoot
4946
+ });
4916
4947
  }
4917
4948
  async function deleteBranch(gitRoot, branchName) {
4918
4949
  try {
@@ -4923,7 +4954,7 @@ async function deleteBranch(gitRoot, branchName) {
4923
4954
  return err(new WorktreeError(`branch delete failed: ${errorMessage}`));
4924
4955
  }
4925
4956
  }
4926
- async function deleteWorktree(gitRoot, worktreeDirectory, name, options) {
4957
+ async function deleteWorktree(gitRoot, worktreeDirectory, name, options, preDeleteCommands) {
4927
4958
  const { force = false } = options || {};
4928
4959
  const validation = await validateWorktreeExists(
4929
4960
  gitRoot,
@@ -4942,6 +4973,18 @@ async function deleteWorktree(gitRoot, worktreeDirectory, name, options) {
4942
4973
  )
4943
4974
  );
4944
4975
  }
4976
+ if (preDeleteCommands && preDeleteCommands.length > 0) {
4977
+ console.log("\nRunning pre-delete commands...");
4978
+ const preDeleteResult = await executePreDeleteCommands({
4979
+ gitRoot,
4980
+ worktreesDirectory: worktreeDirectory,
4981
+ worktreeName: name,
4982
+ commands: preDeleteCommands
4983
+ });
4984
+ if (isErr(preDeleteResult)) {
4985
+ return err(new WorktreeError(preDeleteResult.error.message));
4986
+ }
4987
+ }
4945
4988
  try {
4946
4989
  await removeWorktree(gitRoot, worktreePath, force);
4947
4990
  const branchName = name;
@@ -5231,7 +5274,6 @@ async function attachHandler(args2) {
5231
5274
  }
5232
5275
  exitWithError(error.message, exitCodes.generalError);
5233
5276
  }
5234
- const worktreePath = result.value;
5235
5277
  output.log(`Attached phantom: ${branchName}`);
5236
5278
  if (values.shell) {
5237
5279
  const shellResult = await shellInWorktree(
@@ -5295,6 +5337,7 @@ complete -c phantom -n "__phantom_using_command" -a "list" -d "List all Git work
5295
5337
  complete -c phantom -n "__phantom_using_command" -a "where" -d "Output the filesystem path of a specific worktree"
5296
5338
  complete -c phantom -n "__phantom_using_command" -a "delete" -d "Delete a Git worktree (phantom)"
5297
5339
  complete -c phantom -n "__phantom_using_command" -a "exec" -d "Execute a command in a worktree directory"
5340
+ complete -c phantom -n "__phantom_using_command" -a "review" -d "Review changes in a worktree with a local PR review interface (experimental)"
5298
5341
  complete -c phantom -n "__phantom_using_command" -a "shell" -d "Open an interactive shell in a worktree directory"
5299
5342
  complete -c phantom -n "__phantom_using_command" -a "github" -d "GitHub integration commands"
5300
5343
  complete -c phantom -n "__phantom_using_command" -a "gh" -d "GitHub integration commands (alias)"
@@ -5340,6 +5383,11 @@ complete -c phantom -n "__phantom_using_command exec" -l tmux-vertical -d "Execu
5340
5383
  complete -c phantom -n "__phantom_using_command exec" -l tmux-horizontal -d "Execute command in horizontal split pane"
5341
5384
  complete -c phantom -n "__phantom_using_command exec" -a "(__phantom_list_worktrees)"
5342
5385
 
5386
+ # review command options
5387
+ complete -c phantom -n "__phantom_using_command review" -l fzf -d "Use fzf for interactive selection"
5388
+ complete -c phantom -n "__phantom_using_command review" -l base -d "Base reference for comparison" -x
5389
+ complete -c phantom -n "__phantom_using_command review" -a "(__phantom_list_worktrees)"
5390
+
5343
5391
  # shell command options
5344
5392
  complete -c phantom -n "__phantom_using_command shell" -l fzf -d "Use fzf for interactive selection"
5345
5393
  complete -c phantom -n "__phantom_using_command shell" -l tmux -d "Open shell in new tmux window (-t)"
@@ -5374,6 +5422,7 @@ _phantom() {
5374
5422
  'where:Output the filesystem path of a specific worktree'
5375
5423
  'delete:Delete a Git worktree (phantom)'
5376
5424
  'exec:Execute a command in a worktree directory'
5425
+ 'review:Review changes in a worktree with a local PR review interface (experimental)'
5377
5426
  'shell:Open an interactive shell in a worktree directory'
5378
5427
  'github:GitHub integration commands'
5379
5428
  'gh:GitHub integration commands (alias)'
@@ -5416,13 +5465,18 @@ _phantom() {
5416
5465
  '--fzf[Use fzf for interactive selection]' \\
5417
5466
  '--names[Output only phantom names (for scripts and completion)]'
5418
5467
  ;;
5419
- where|delete|shell)
5468
+ where|delete|review|shell)
5420
5469
  local worktrees
5421
5470
  worktrees=(\${(f)"$(phantom list --names 2>/dev/null)"})
5422
5471
  if [[ \${line[1]} == "where" ]]; then
5423
5472
  _arguments \\
5424
5473
  '--fzf[Use fzf for interactive selection]' \\
5425
5474
  '1:worktree:(\${(q)worktrees[@]})'
5475
+ elif [[ \${line[1]} == "review" ]]; then
5476
+ _arguments \\
5477
+ '--fzf[Use fzf for interactive selection]' \\
5478
+ '--base[Base reference for comparison]:reference:' \\
5479
+ '1:worktree:(\${(q)worktrees[@]})'
5426
5480
  elif [[ \${line[1]} == "shell" ]]; then
5427
5481
  _arguments \\
5428
5482
  '--fzf[Use fzf for interactive selection]' \\
@@ -5488,7 +5542,7 @@ _phantom_completion() {
5488
5542
  local cur prev words cword
5489
5543
  _init_completion || return
5490
5544
 
5491
- local commands="create attach list where delete exec shell github gh version completion mcp"
5545
+ local commands="create attach list where delete exec review shell github gh version completion mcp"
5492
5546
  local global_opts="--help --version"
5493
5547
 
5494
5548
  if [[ \${cword} -eq 1 ]]; then
@@ -5590,6 +5644,24 @@ _phantom_completion() {
5590
5644
  ;;
5591
5645
  esac
5592
5646
  ;;
5647
+ review)
5648
+ case "\${prev}" in
5649
+ --base)
5650
+ # Don't complete anything specific for base reference
5651
+ return 0
5652
+ ;;
5653
+ *)
5654
+ if [[ "\${cur}" == -* ]]; then
5655
+ local opts="--fzf --base"
5656
+ COMPREPLY=( $(compgen -W "\${opts}" -- "\${cur}") )
5657
+ else
5658
+ local worktrees=$(_phantom_list_worktrees)
5659
+ COMPREPLY=( $(compgen -W "\${worktrees}" -- "\${cur}") )
5660
+ fi
5661
+ return 0
5662
+ ;;
5663
+ esac
5664
+ ;;
5593
5665
  shell)
5594
5666
  case "\${prev}" in
5595
5667
  --tmux|-t|--tmux-vertical|--tmux-horizontal)
@@ -5931,7 +6003,8 @@ async function deleteHandler(args2) {
5931
6003
  worktreeName,
5932
6004
  {
5933
6005
  force: forceDelete
5934
- }
6006
+ },
6007
+ context.config?.preDelete?.commands
5935
6008
  );
5936
6009
  if (isErr(result)) {
5937
6010
  const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.validationError : result.error instanceof WorktreeError && result.error.message.includes("uncommitted changes") ? exitCodes.validationError : exitCodes.generalError;
@@ -9526,8 +9599,21 @@ async function checkoutIssue(issue, base) {
9526
9599
  }
9527
9600
  const gitRoot = await getGitRoot();
9528
9601
  const context = await createContext(gitRoot);
9529
- const worktreeName = `issue-${issue.number}`;
9530
- const branchName = `issue-${issue.number}`;
9602
+ const worktreeName = `issues/${issue.number}`;
9603
+ const branchName = `issues/${issue.number}`;
9604
+ const existsResult = await validateWorktreeExists(
9605
+ context.gitRoot,
9606
+ context.worktreesDirectory,
9607
+ worktreeName
9608
+ );
9609
+ if (!isErr(existsResult)) {
9610
+ return ok({
9611
+ message: `Issue #${issue.number} is already checked out`,
9612
+ worktree: worktreeName,
9613
+ path: existsResult.value.path,
9614
+ alreadyExists: true
9615
+ });
9616
+ }
9531
9617
  const result = await createWorktree(
9532
9618
  context.gitRoot,
9533
9619
  context.worktreesDirectory,
@@ -9540,18 +9626,6 @@ async function checkoutIssue(issue, base) {
9540
9626
  context.config?.postCreate?.commands
9541
9627
  );
9542
9628
  if (isErr(result)) {
9543
- if (result.error instanceof WorktreeAlreadyExistsError) {
9544
- const worktreePath = getWorktreePathFromDirectory(
9545
- context.worktreesDirectory,
9546
- worktreeName
9547
- );
9548
- return ok({
9549
- message: `Worktree for issue #${issue.number} is already checked out`,
9550
- worktree: worktreeName,
9551
- path: worktreePath,
9552
- alreadyExists: true
9553
- });
9554
- }
9555
9629
  return err(result.error);
9556
9630
  }
9557
9631
  return ok({
@@ -9565,8 +9639,21 @@ async function checkoutIssue(issue, base) {
9565
9639
  async function checkoutPullRequest(pullRequest) {
9566
9640
  const gitRoot = await getGitRoot();
9567
9641
  const context = await createContext(gitRoot);
9568
- const worktreeName = `pr-${pullRequest.number}`;
9569
- const localBranch = `pr-${pullRequest.number}`;
9642
+ const worktreeName = `pulls/${pullRequest.number}`;
9643
+ const localBranch = `pulls/${pullRequest.number}`;
9644
+ const existsResult = await validateWorktreeExists(
9645
+ context.gitRoot,
9646
+ context.worktreesDirectory,
9647
+ worktreeName
9648
+ );
9649
+ if (!isErr(existsResult)) {
9650
+ return ok({
9651
+ message: `PR #${pullRequest.number} is already checked out`,
9652
+ worktree: worktreeName,
9653
+ path: existsResult.value.path,
9654
+ alreadyExists: true
9655
+ });
9656
+ }
9570
9657
  const upstream = pullRequest.isFromFork ? `origin/pull/${pullRequest.number}/head` : `origin/${pullRequest.head.ref}`;
9571
9658
  const refspec = `${upstream.replace("origin/", "")}:${localBranch}`;
9572
9659
  const fetchResult = await fetch({ refspec });
@@ -9595,18 +9682,6 @@ async function checkoutPullRequest(pullRequest) {
9595
9682
  context.config?.postCreate?.commands
9596
9683
  );
9597
9684
  if (isErr(attachResult)) {
9598
- if (attachResult.error instanceof WorktreeAlreadyExistsError) {
9599
- const worktreePath = getWorktreePathFromDirectory(
9600
- context.worktreesDirectory,
9601
- worktreeName
9602
- );
9603
- return ok({
9604
- message: `Worktree for PR #${pullRequest.number} is already checked out`,
9605
- worktree: worktreeName,
9606
- path: worktreePath,
9607
- alreadyExists: true
9608
- });
9609
- }
9610
9685
  return err(attachResult.error);
9611
9686
  }
9612
9687
  const message = pullRequest.isFromFork ? `Checked out PR #${pullRequest.number} from fork ${pullRequest.head.repo.full_name}` : `Checked out PR #${pullRequest.number} from branch ${pullRequest.head.ref}`;
@@ -9866,8 +9941,8 @@ var githubCheckoutHelp = {
9866
9941
  }
9867
9942
  ],
9868
9943
  notes: [
9869
- "For PRs: Creates worktree named 'pr-{number}' with the PR's branch",
9870
- "For Issues: Creates worktree named 'issue-{number}' with a new branch",
9944
+ "For PRs: Creates worktree named 'pulls/{number}' with the PR's branch",
9945
+ "For Issues: Creates worktree named 'issues/{number}' with a new branch",
9871
9946
  "",
9872
9947
  "Requirements:",
9873
9948
  " - GitHub CLI (gh) must be installed",
@@ -13223,7 +13298,7 @@ var StdioServerTransport = class {
13223
13298
  // ../mcp/package.json
13224
13299
  var package_default = {
13225
13300
  name: "@aku11i/phantom-mcp",
13226
- version: "1.3.0",
13301
+ version: "2.1.0",
13227
13302
  private: true,
13228
13303
  type: "module",
13229
13304
  main: "./src/index.ts",
@@ -13311,7 +13386,8 @@ var deleteWorktreeTool = {
13311
13386
  name,
13312
13387
  {
13313
13388
  force
13314
- }
13389
+ },
13390
+ context.config?.preDelete?.commands
13315
13391
  );
13316
13392
  if (!isOk(result)) {
13317
13393
  throw new Error(result.error.message);
@@ -13501,10 +13577,97 @@ async function mcpHandler(args2 = []) {
13501
13577
  }
13502
13578
  }
13503
13579
 
13504
- // src/handlers/shell.ts
13580
+ // src/handlers/review.ts
13505
13581
  import { parseArgs as parseArgs8 } from "node:util";
13506
- async function shellHandler(args2) {
13582
+ async function reviewHandler(args2) {
13507
13583
  const { positionals, values } = parseArgs8({
13584
+ args: args2,
13585
+ options: {
13586
+ fzf: {
13587
+ type: "boolean",
13588
+ default: false
13589
+ },
13590
+ base: {
13591
+ type: "string"
13592
+ }
13593
+ },
13594
+ strict: true,
13595
+ allowPositionals: true
13596
+ });
13597
+ const useFzf = values.fzf ?? false;
13598
+ const base = values.base;
13599
+ if (useFzf) {
13600
+ if (positionals.length > 0) {
13601
+ exitWithError(
13602
+ "Cannot specify worktree name when using --fzf",
13603
+ exitCodes.validationError
13604
+ );
13605
+ }
13606
+ } else {
13607
+ if (positionals.length !== 1) {
13608
+ exitWithError(
13609
+ "Usage: phantom review <worktree-name> [--base <ref>]",
13610
+ exitCodes.validationError
13611
+ );
13612
+ }
13613
+ }
13614
+ try {
13615
+ const gitRoot = await getGitRoot();
13616
+ const context = await createContext(gitRoot);
13617
+ let worktreeName;
13618
+ if (useFzf) {
13619
+ const selectResult = await selectWorktreeWithFzf(
13620
+ context.gitRoot,
13621
+ context.worktreesDirectory
13622
+ );
13623
+ if (isErr(selectResult)) {
13624
+ exitWithError(selectResult.error.message, exitCodes.generalError);
13625
+ }
13626
+ if (!selectResult.value) {
13627
+ exitWithSuccess();
13628
+ }
13629
+ worktreeName = selectResult.value.name;
13630
+ } else {
13631
+ worktreeName = positionals[0];
13632
+ }
13633
+ const validation = await validateWorktreeExists(
13634
+ context.gitRoot,
13635
+ context.worktreesDirectory,
13636
+ worktreeName
13637
+ );
13638
+ if (isErr(validation)) {
13639
+ exitWithError(validation.error.message, exitCodes.generalError);
13640
+ }
13641
+ const baseRef = base ?? `origin/${context.config?.defaultBranch ?? "main"}`;
13642
+ output.log(`Opening review for worktree '${worktreeName}'...`);
13643
+ output.log(
13644
+ "powered by yoshiko-pg/reviewit (https://github.com/yoshiko-pg/reviewit)"
13645
+ );
13646
+ const command2 = ["reviewit", ".", baseRef];
13647
+ const result = await execInWorktree(
13648
+ context.gitRoot,
13649
+ context.worktreesDirectory,
13650
+ worktreeName,
13651
+ command2,
13652
+ { interactive: true }
13653
+ );
13654
+ if (isErr(result)) {
13655
+ const exitCode = result.error instanceof WorktreeNotFoundError ? exitCodes.notFound : result.error.exitCode || exitCodes.generalError;
13656
+ exitWithError(result.error.message, exitCode);
13657
+ }
13658
+ process.exit(result.value.exitCode);
13659
+ } catch (error) {
13660
+ exitWithError(
13661
+ error instanceof Error ? error.message : String(error),
13662
+ exitCodes.generalError
13663
+ );
13664
+ }
13665
+ }
13666
+
13667
+ // src/handlers/shell.ts
13668
+ import { parseArgs as parseArgs9 } from "node:util";
13669
+ async function shellHandler(args2) {
13670
+ const { positionals, values } = parseArgs9({
13508
13671
  args: args2,
13509
13672
  options: {
13510
13673
  fzf: {
@@ -13628,12 +13791,12 @@ async function shellHandler(args2) {
13628
13791
  }
13629
13792
 
13630
13793
  // src/handlers/version.ts
13631
- import { parseArgs as parseArgs9 } from "node:util";
13794
+ import { parseArgs as parseArgs10 } from "node:util";
13632
13795
 
13633
13796
  // package.json
13634
13797
  var package_default2 = {
13635
13798
  name: "@aku11i/phantom-cli",
13636
- version: "1.3.0",
13799
+ version: "2.1.0",
13637
13800
  private: true,
13638
13801
  type: "module",
13639
13802
  scripts: {
@@ -13661,7 +13824,7 @@ function getVersion() {
13661
13824
 
13662
13825
  // src/handlers/version.ts
13663
13826
  function versionHandler(args2 = []) {
13664
- parseArgs9({
13827
+ parseArgs10({
13665
13828
  args: args2,
13666
13829
  options: {},
13667
13830
  strict: true,
@@ -13673,9 +13836,9 @@ function versionHandler(args2 = []) {
13673
13836
  }
13674
13837
 
13675
13838
  // src/handlers/where.ts
13676
- import { parseArgs as parseArgs10 } from "node:util";
13839
+ import { parseArgs as parseArgs11 } from "node:util";
13677
13840
  async function whereHandler(args2) {
13678
- const { positionals, values } = parseArgs10({
13841
+ const { positionals, values } = parseArgs11({
13679
13842
  args: args2,
13680
13843
  options: {
13681
13844
  fzf: {
@@ -14044,6 +14207,55 @@ var listHelp = {
14044
14207
  ]
14045
14208
  };
14046
14209
 
14210
+ // src/help/review.ts
14211
+ var reviewHelp = {
14212
+ name: "review",
14213
+ description: "Review changes in a worktree with a local PR review interface (experimental)",
14214
+ usage: "phantom review [options] <worktree-name>",
14215
+ options: [
14216
+ {
14217
+ name: "--fzf",
14218
+ type: "boolean",
14219
+ description: "Use fzf for interactive worktree selection"
14220
+ },
14221
+ {
14222
+ name: "--base",
14223
+ type: "string",
14224
+ description: "Base reference for comparison (default: origin/<defaultBranch>)"
14225
+ }
14226
+ ],
14227
+ examples: [
14228
+ {
14229
+ description: "Review changes against default branch",
14230
+ command: "phantom review feature-auth"
14231
+ },
14232
+ {
14233
+ description: "Review changes against specific remote branch",
14234
+ command: "phantom review feature-login --base origin/feature-auth"
14235
+ },
14236
+ {
14237
+ description: "Review changes against local branch",
14238
+ command: "phantom review feature-auth --base main"
14239
+ },
14240
+ {
14241
+ description: "Interactive worktree selection",
14242
+ command: "phantom review --fzf"
14243
+ },
14244
+ {
14245
+ description: "Interactive selection with custom base",
14246
+ command: "phantom review --fzf --base origin/staging"
14247
+ }
14248
+ ],
14249
+ notes: [
14250
+ "\u26A0\uFE0F This is an experimental feature and may change in future versions",
14251
+ "Uses reviewit to provide a GitHub-like PR review interface locally",
14252
+ "Default base is origin/<defaultBranch> where defaultBranch is from config or 'main'",
14253
+ "The --base value is passed directly to reviewit as the comparison reference",
14254
+ "Requires reviewit to be installed separately (e.g., npm install -g reviewit)",
14255
+ "powered by yoshiko-pg/reviewit (https://github.com/yoshiko-pg/reviewit)"
14256
+ ]
14257
+ };
14258
+
14047
14259
  // src/help/shell.ts
14048
14260
  var shellHelp = {
14049
14261
  name: "shell",
@@ -14191,6 +14403,12 @@ var commands = [
14191
14403
  handler: execHandler,
14192
14404
  help: execHelp
14193
14405
  },
14406
+ {
14407
+ name: "review",
14408
+ description: "Review changes in a worktree with a local PR review interface (experimental)",
14409
+ handler: reviewHandler,
14410
+ help: reviewHelp
14411
+ },
14194
14412
  {
14195
14413
  name: "shell",
14196
14414
  description: "Open an interactive shell in a worktree directory",