Skip to content

Conversation

@juristr
Copy link
Contributor

@juristr juristr commented Aug 4, 2025

adds a new extract command that allows you to extract an existing branch into a worktree.

Summary by CodeRabbit

  • New Features
    • Introduced a new CLI command to extract an existing branch as a separate Git worktree.
    • Users can specify the branch, target directory, dependency installation command, and preferred editor when extracting a worktree.

@juristr
Copy link
Contributor Author

juristr commented Aug 4, 2025

As per this comment: #17 (comment)

Ping @johnlindquist 😬 👆

@juristr
Copy link
Contributor Author

juristr commented Aug 4, 2025

holy...hold on. Just noticed it committed build files 🤔. let me force push a fix.

Are those supposed to be committed? 🤔

@juristr juristr force-pushed the feat/add-extract-command branch from 0ba8d9b to a70ec52 Compare August 4, 2025 13:49
@coderabbitai
Copy link

coderabbitai bot commented Aug 4, 2025

Walkthrough

A new CLI command, extract, has been introduced to automate extracting a Git branch into a new worktree. The implementation includes a handler that validates the environment, manages worktree creation, optionally installs dependencies, and can open the worktree in an editor. Command registration and argument parsing were added to the CLI entry point.

Changes

Cohort / File(s) Change Summary
Extract Worktree Handler
src/commands/extract.ts
Introduced extractWorktreeHandler to automate extracting a branch as a new Git worktree, with validation, install, and editor support.
CLI Command Registration
src/index.ts
Registered a new extract CLI command, parsing arguments and options, and wired it to the new handler.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI (index.ts)
    participant Handler (extract.ts)
    participant Git
    participant Editor

    User->>CLI (index.ts): Run `extract [branch]` command
    CLI (index.ts)->>Handler (extract.ts): Call extractWorktreeHandler(branch, options)
    Handler (extract.ts)->>Git: Validate worktree, check branch, list worktrees
    Handler (extract.ts)->>Git: Create worktree (and local branch if needed)
    alt install option provided
        Handler (extract.ts)->>Git: Run install command in worktree dir
    end
    alt editor option provided
        Handler (extract.ts)->>Editor: Open worktree in editor
    end
    Handler (extract.ts)->>CLI (index.ts): Log result or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

🐇
A branch hops out, a worktree anew,
With checks and installs, it’s ready for you!
Editors may open, dependencies bloom,
All from a command in your terminal room.
So hop along, extract with glee—
Git magic made easy, for you and me!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@johnlindquist
Copy link
Owner

holy...hold on. Just noticed it committed build files 🤔. let me force push a fix.

Are those supposed to be committed? 🤔

If this is one of the oclif projects, I think so. I never really paid attention 🫣

@juristr
Copy link
Contributor Author

juristr commented Aug 4, 2025

🤷‍♂️ 😅. I excluded them for now. They got in probably only because I ran a local build and pnpm link to test it out before pushing.

Any release process on this will probably build it first so it should be good I guess :)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/commands/extract.ts (2)

61-64: Consider supporting multiple remotes

The current implementation only considers branches from the 'origin' remote. If the repository has multiple remotes configured, branches from other remotes won't be detected.

Consider enhancing to support all remotes:

-        const remoteBranchList = remoteBranches
-            .split('\n')
-            .filter(b => b.trim() !== '' && b.startsWith('origin/'))
-            .map(b => b.replace('origin/', ''));
+        const remoteBranchList = remoteBranches
+            .split('\n')
+            .filter(b => b.trim() !== '')
+            .map(b => {
+                // Extract branch name after remote name (e.g., "origin/feature" -> "feature")
+                const parts = b.split('/');
+                return parts.slice(1).join('/');
+            });

96-98: Add comment to clarify intentional empty catch block

The empty catch block is intentional but could benefit from a comment explaining the logic.

         } catch (error) {
-            // Directory doesn't exist, continue with creation
+            // Directory doesn't exist, which is expected - continue with creation
         }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 342b320 and a70ec52.

📒 Files selected for processing (2)
  • src/commands/extract.ts (1 hunks)
  • src/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/commands/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/project.mdc)

src/commands/*.ts: Leverage Execa to execute Git commands and other external processes.
Provide clear, colored console feedback for success and error messages in CLI commands.

Files:

  • src/commands/extract.ts
src/index.ts

📄 CodeRabbit Inference Engine (.cursor/rules/project.mdc)

src/index.ts: The main entry point for the CLI is src/index.ts, which sets up CLI commands and orchestrates command handlers.
Utilize Commander for parsing CLI commands and handling options.

Files:

  • src/index.ts
🧠 Learnings (7)
📚 Learning: applies to src/commands/new.ts : implement logic for creating new git worktrees, including options f...
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/new.ts : Implement logic for creating new Git worktrees, including options for branch creation, dependency installation, and opening in an editor, in src/commands/new.ts.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/commands/remove.ts : handle removal of git worktrees, including support for force del...
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/remove.ts : Handle removal of Git worktrees, including support for force deletion, in src/commands/remove.ts.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/commands/list.ts : provide functionality to list existing git worktrees in src/comman...
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/list.ts : Provide functionality to list existing Git worktrees in src/commands/list.ts.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/commands/*.ts : leverage execa to execute git commands and other external processes....
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/*.ts : Leverage Execa to execute Git commands and other external processes.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/index.ts : utilize commander for parsing cli commands and handling options....
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/index.ts : Utilize Commander for parsing CLI commands and handling options.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/commands/*.ts : provide clear, colored console feedback for success and error message...
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/*.ts : Provide clear, colored console feedback for success and error messages in CLI commands.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
📚 Learning: applies to src/index.ts : the main entry point for the cli is src/index.ts, which sets up cli comman...
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/index.ts : The main entry point for the CLI is src/index.ts, which sets up CLI commands and orchestrates command handlers.

Applied to files:

  • src/commands/extract.ts
  • src/index.ts
🔇 Additional comments (1)
src/index.ts (1)

113-128: LGTM!

The command registration follows the established patterns and is consistent with other commands in the CLI.

Comment on lines +124 to +127
if (options.install) {
console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`));
await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" });
}
Copy link

@coderabbitai coderabbitai bot Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate the install command for security

The install command is executed without validation, which could allow arbitrary command execution.

Consider validating against a whitelist of known package managers:

         if (options.install) {
+            const allowedPackageManagers = ['npm', 'yarn', 'pnpm', 'bun'];
+            if (!allowedPackageManagers.includes(options.install)) {
+                console.error(chalk.red(`❌ Error: Unknown package manager "${options.install}".`));
+                console.error(chalk.yellow(`Allowed package managers: ${allowedPackageManagers.join(', ')}`));
+                process.exit(1);
+            }
             console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`));
             await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" });
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (options.install) {
console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`));
await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" });
}
if (options.install) {
const allowedPackageManagers = ['npm', 'yarn', 'pnpm', 'bun'];
if (!allowedPackageManagers.includes(options.install)) {
console.error(chalk.red(`❌ Error: Unknown package manager "${options.install}".`));
console.error(chalk.yellow(`Allowed package managers: ${allowedPackageManagers.join(', ')}`));
process.exit(1);
}
console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`));
await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" });
}
🤖 Prompt for AI Agents
In src/commands/extract.ts around lines 124 to 127, the install command from
options.install is executed directly without validation, posing a security risk.
Fix this by validating options.install against a whitelist of allowed package
managers (e.g., "npm", "yarn", "pnpm") before executing it. If the command is
not in the whitelist, skip execution or throw an error to prevent arbitrary
command execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Played by the rules of the existing "new.ts" command as this should work pretty much the same, but just extracting an existing branch rather than creating a new one.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

I understand you're following the existing patterns from new.ts for consistency. Let me check how the install command is handled there to provide guidance that maintains consistency while addressing security concerns.


🏁 Script executed:

#!/bin/bash
# Check how the install command is handled in the new.ts command
cat src/commands/new.ts | grep -A 10 -B 5 "install"

Length of output: 2772


@juristr You're absolutely right about following the existing patterns for consistency! I can see that new.ts uses the exact same approach for handling the install command. However, this means both commands have the same security vulnerability where arbitrary commands can be executed through the --install option.

Since you're maintaining consistency with the existing codebase, I'd recommend addressing this security issue holistically across both commands. This way, you maintain the consistency principle while improving the overall security posture.

The security concern applies to both src/commands/new.ts and src/commands/extract.ts. Consider validating the install command against a whitelist of known package managers in both files:

if (options.install) {
    const allowedPackageManagers = ['npm', 'yarn', 'pnpm', 'bun'];
    if (!allowedPackageManagers.includes(options.install)) {
        console.error(chalk.red(`❌ Error: Unknown package manager "${options.install}".`));
        console.error(chalk.yellow(`Allowed package managers: ${allowedPackageManagers.join(', ')}`));
        process.exit(1);
    }
    console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`));
    await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" });
}

This approach maintains consistency while preventing arbitrary command execution. Would you like me to help identify any other commands that might have similar patterns?


✏️ Learnings added
Learnt from: juristr
PR: johnlindquist/worktree-cli#20
File: src/commands/extract.ts:124-127
Timestamp: 2025-08-04T14:22:29.053Z
Learning: The worktree-cli project prioritizes consistency across commands. When implementing new commands like `extract`, developers follow existing patterns from similar commands like `new` to maintain API and implementation consistency.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/new.ts : Implement logic for creating new Git worktrees, including options for branch creation, dependency installation, and opening in an editor, in src/commands/new.ts.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/remove.ts : Handle removal of Git worktrees, including support for force deletion, in src/commands/remove.ts.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/list.ts : Provide functionality to list existing Git worktrees in src/commands/list.ts.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/*.ts : Leverage Execa to execute Git commands and other external processes.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/index.ts : Utilize Commander for parsing CLI commands and handling options.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/index.ts : The main entry point for the CLI is src/index.ts, which sets up CLI commands and orchestrates command handlers.

Learnt from: CR
PR: johnlindquist/worktree-cli#0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.835Z
Learning: Applies to src/commands/*.ts : Provide clear, colored console feedback for success and error messages in CLI commands.

Comment on lines +130 to +139
const configuredEditor = getDefaultEditor();
const editorCommand = options.editor || configuredEditor;
console.log(chalk.blue(`Opening ${resolvedPath} in ${editorCommand}...`));

try {
await execa(editorCommand, [resolvedPath], { stdio: "inherit" });
} catch (editorError) {
console.error(chalk.red(`Failed to open editor "${editorCommand}". Please ensure it's installed and in your PATH.`));
console.warn(chalk.yellow(`Continuing without opening editor.`));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider validating the editor command

Similar to the install command, the editor command could potentially execute arbitrary commands.

Consider implementing a validation mechanism or at least warning the user:

         const configuredEditor = getDefaultEditor();
         const editorCommand = options.editor || configuredEditor;
+        
+        // Warn if using a custom editor command
+        if (options.editor) {
+            console.log(chalk.yellow(`⚠️  Using custom editor command: ${options.editor}`));
+        }
+        
         console.log(chalk.blue(`Opening ${resolvedPath} in ${editorCommand}...`));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const configuredEditor = getDefaultEditor();
const editorCommand = options.editor || configuredEditor;
console.log(chalk.blue(`Opening ${resolvedPath} in ${editorCommand}...`));
try {
await execa(editorCommand, [resolvedPath], { stdio: "inherit" });
} catch (editorError) {
console.error(chalk.red(`Failed to open editor "${editorCommand}". Please ensure it's installed and in your PATH.`));
console.warn(chalk.yellow(`Continuing without opening editor.`));
}
const configuredEditor = getDefaultEditor();
const editorCommand = options.editor || configuredEditor;
// Warn if using a custom editor command
if (options.editor) {
console.log(chalk.yellow(`⚠️ Using custom editor command: ${options.editor}`));
}
console.log(chalk.blue(`Opening ${resolvedPath} in ${editorCommand}...`));
try {
await execa(editorCommand, [resolvedPath], { stdio: "inherit" });
} catch (editorError) {
console.error(chalk.red(`Failed to open editor "${editorCommand}". Please ensure it's installed and in your PATH.`));
console.warn(chalk.yellow(`Continuing without opening editor.`));
}
🤖 Prompt for AI Agents
In src/commands/extract.ts around lines 130 to 139, the editor command passed to
execa is not validated, which could allow execution of arbitrary commands. Add a
validation step before running the editorCommand to ensure it matches a
whitelist of allowed editor commands or patterns. If the command is invalid,
warn the user and skip attempting to open the editor to prevent potential
security risks.

@johnlindquist johnlindquist merged commit 972a449 into johnlindquist:main Aug 4, 2025
1 check passed
@github-actions
Copy link

github-actions bot commented Aug 4, 2025

🎉 This PR is included in version 2.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants