feat: add GitLab support for pr command#26
Conversation
Add support for GitLab CLI (glab) alongside GitHub CLI (gh) for the pr command. - Auto-detect git provider from remote URL (github.com or gitlab.com) - Add config option: wt config set provider gh|glab - Adapt pr command to work with both GitHub PRs and GitLab MRs - Update error messages to be provider-specific (PR vs MR, GitHub vs GitLab) - Add comprehensive documentation in README The tool automatically detects the provider from the remote URL. Manual configuration is available as fallback via config command. Backward compatible: defaults to gh (GitHub CLI) maintaining existing behavior.
WalkthroughThe changes introduce multi-provider Git CLI support for GitHub (gh) and GitLab (glab) with automatic provider detection from remote URLs, new configuration options for provider selection, safety checks preventing operations on bare repositories, and three new worktree management commands. User-facing terminology and documentation updated to reference PR/MR interchangeably. Changes
Sequence DiagramsequenceDiagram
actor User
participant CLI as CLI Handler<br/>(prWorktreeHandler)
participant Config as Config Module<br/>(getGitProvider)
participant GitUtil as Git Utilities<br/>(detectGitProvider)
participant GitRemote as Git Remote
participant GitCLI as gh/glab CLI
participant Repo as Local Repo
User->>CLI: pr checkout <pr-number>
CLI->>Config: getGitProvider()
Config-->>CLI: configured provider or 'gh'
CLI->>GitUtil: detectGitProvider()
GitUtil->>GitRemote: git remote get-url origin
GitRemote-->>GitUtil: remote URL
GitUtil-->>CLI: detected provider (gh/glab)
alt Provider mismatch detected
CLI-->>User: ⚠️ Warning: switching to detected provider
end
CLI->>Repo: isWorktreeClean check
Repo-->>CLI: clean/dirty status
CLI->>GitCLI: gh pr view / glab mr view <pr-number>
GitCLI-->>CLI: branch name
CLI->>Repo: git checkout <branch>
Repo-->>CLI: ✓ Branch checked out
CLI->>Repo: Create worktree for PR/MR
Repo-->>CLI: ✓ Worktree created
CLI-->>User: ✓ Ready to work on PR/MR
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
build/commands/config.js (1)
2-2: Generated config handler matches TypeScript sourceThis built version correctly reflects the
providersupport and validation already reviewed insrc/commands/config.ts; no additional issues beyond the source.Also applies to: 11-14, 17-18, 26-34, 39-42, 45-46
🧹 Nitpick comments (11)
src/utils/git.ts (1)
85-89: The'gitlab'substring check may cause false positives.The check
remoteUrl.includes('gitlab')(line 87) could match unintended URLs likemygitlabbackup.com,notgitlab.org, or paths containing "gitlab" in directory names. Consider being more precise:- if (remoteUrl.includes('github.com')) { - return 'gh'; - } else if (remoteUrl.includes('gitlab.com') || remoteUrl.includes('gitlab')) { - return 'glab'; - } + if (remoteUrl.includes('github.com')) { + return 'gh'; + } else if (remoteUrl.includes('gitlab.com') || remoteUrl.match(/[:\/]gitlab[.\/]/)) { + return 'glab'; + }Alternatively, if the intent is to support self-hosted GitLab instances with varied domains, consider documenting this behavior or requiring explicit configuration for non-
gitlab.comhosts.build/commands/remove.js (1)
45-45: Remove development marker comment.The comment
// >>> ADD SAFETY CHECK HERE <<<appears to be a leftover development note that should be cleaned up before merging.- // >>> ADD SAFETY CHECK HERE <<<src/index.ts (1)
103-124: Update PR command description for GitLab support.The argument description (line 107) and command description (lines 121-123) still reference only GitHub. Per the PR objectives to use provider-specific terminology (PR vs MR), consider updating these descriptions:
program .command("pr") .argument( "<prNumber>", - "GitHub Pull Request number to create a worktree from" + "Pull Request (GitHub) or Merge Request (GitLab) number to create a worktree from" ) .option( "-p, --path <path>", "Specify a custom path for the worktree (defaults to repoName-branchName)" ) .option( "-i, --install <packageManager>", "Package manager to use for installing dependencies (npm, pnpm, bun, etc.)" ) .option( "-e, --editor <editor>", "Editor to use for opening the worktree (overrides default editor)" ) .description( - "Fetch the branch for a given GitHub PR number and create a worktree." + "Fetch the branch for a given PR/MR number and create a worktree." ) .action(prWorktreeHandler);build/utils/git.js (1)
41-65: Git utility helpers are correct; Windows path edge case is minor
isMainRepoBare,getRepoRoot, anddetectGitProviderare implemented safely with good error handling and sensible fallbacks; using.gitascwdstill works even whenendsWith('/.git')doesn’t match on Windows, so the small path‑separator nuance isn’t a blocker.If you want to be extra defensive cross‑platform, consider trimming both
/.gitand\\\.gitwhen computingmainRepoDir, but it’s not strictly necessary.Also applies to: 66-75, 76-92
build/commands/pr.js (2)
60-70: Provider detection and override behavior is reasonableDeriving
providerfrom config, then overriding withdetectGitProvider()when it disagrees (with a clear yellow warning) is a good balance between explicit configuration and auto-detection; theisPR/requestTypebranching built on top of that reads cleanly.If you ever need to guard against a corrupted config file, a small sanity check like
if (provider !== 'gh' && provider !== 'glab') provider = detectedProvider ?? 'gh';would make this even more bulletproof, but it’s not strictly required given the Conf schema.
215-220:detached: true+await execa(...)blocks CLI despite fire-and-forget intentThe current code will wait for the editor process to exit because
await execa(...)waits for the child to complete, even withdetached: true. Thedetachedflag only separates the process group (OS-dependent behavior) but doesn't prevent the parent from waiting unless you also call.unref()on the child.To truly fire-and-forget the editor, remove the
awaitand call.unref():try { - await execa(editorCommand, [resolvedPath], { stdio: "ignore", detached: true }); + const child = execa(editorCommand, [resolvedPath], { stdio: "ignore", detached: true }); + child.unref(); + child.catch(editorError => { + console.error(chalk.red(`Failed to open editor "${editorCommand}". Please ensure it's installed and in your PATH.`)); + console.warn(chalk.yellow(`Worktree is ready at ${resolvedPath}. You can open it manually.`)); + }); }This allows
wt prto exit promptly while still reporting spawn failures via the error handler.README.md (1)
53-61: Docs accurately describe PR/MR + provider behavior (with a tiny naming nit)The README updates for
wt prand “Configure Git Provider” clearly explain dual GitHub/GitLab support, auto-detection from the remote URL, and thewt config set/get providerflow, matching the implementation inprWorktreeHandlerandconfigHandler. The only small polish nit is wording like “(github.com or gitlab.com/gitlab.*)”—when referring to the product name (not the literal domain), consider “GitHub” with a capital “H” for consistency.Also applies to: 66-67, 104-121, 194-201
src/commands/pr.ts (4)
5-6: Consider centralizing the GitProvider type definition.The
GitProvidertype is defined locally here but may be needed across multiple files (config, utils, other commands). To prevent duplication and ensure consistency, consider moving this type to a shared location likesrc/types.tsor exporting it fromsrc/config.ts.Apply this approach:
- Create or use an existing shared types file:
// src/types.ts export type GitProvider = 'gh' | 'glab';
- Import from the shared location:
-import { getDefaultEditor, getGitProvider } from "../config.js"; +import { getDefaultEditor, getGitProvider, GitProvider } from "../config.js"; import { getCurrentBranch, isWorktreeClean, isMainRepoBare, detectGitProvider } from "../utils/git.js"; -type GitProvider = 'gh' | 'glab';Also applies to: 8-8
105-123: Consider showing CLI output to users for better transparency.Line 110 uses
stdio: 'pipe'which captures and hides output from thegh/glabcheckout command. For CLI tools, users typically expect to see progress messages, warnings, and other output from underlying commands. This also aids debugging when issues occur.Apply this diff to show checkout output:
- await execa(cliName, [subCommand, "checkout", prNumber], { stdio: 'pipe' }); + await execa(cliName, [subCommand, "checkout", prNumber], { stdio: 'inherit' });Note: If you need to suppress output only on success but show it on error, you can use a try-catch with conditional output handling.
128-135: Clarify the branch restoration logic.The else-if condition at Line 132 handles cases where the current branch is neither
prBranchNamenororiginalBranch. The message says "Assuming ${cliName} handled checkout correctly" but then proceeds to checkoutoriginalBranchanyway. This messaging is confusing—if we're assuming it handled checkout correctly, why are we changing branches?Consider clarifying the logic:
if (currentBranchAfterCheckout === prBranchName && currentBranchAfterCheckout !== originalBranch) { console.log(chalk.blue(`Switching main worktree back to "${originalBranch}" before creating worktree...`)); await execa("git", ["checkout", originalBranch]); - } else if (currentBranchAfterCheckout !== originalBranch) { - console.log(chalk.yellow(`Current branch is ${currentBranchAfterCheckout}, not ${prBranchName}. Assuming ${cliName} handled checkout correctly.`)); + } else if (currentBranchAfterCheckout !== originalBranch) { + console.log(chalk.yellow(`Warning: Current branch is "${currentBranchAfterCheckout}", expected "${originalBranch}". Restoring original branch...`)); await execa("git", ["checkout", originalBranch]); }
242-257: Fragile error message string matching.Line 251 checks for
"already warned"in the error message to avoid duplicate warnings. This string-matching approach is fragile and could break if error messages change.Consider using a flag to track whether a warning was already issued:
} catch (error: any) { console.error(chalk.red("❌ Failed to set up worktree from PR/MR:"), error.message || error); if (error.stack && !(error.stderr || error.stdout)) { console.error(error.stack); } process.exit(1); } finally { // 12. Ensure we are back on the original branch in the main worktree + let finalCheckoutWarned = false; if (originalBranch) { try { const currentBranchNow = await getCurrentBranch(); if (currentBranchNow !== originalBranch) { console.log(chalk.blue(`Ensuring main worktree is back on "${originalBranch}"...`)); await execa("git", ["checkout", originalBranch]); } } catch (checkoutError: any) { - if (!checkoutError.message.includes("already warned")) { + if (!finalCheckoutWarned) { + finalCheckoutWarned = true; console.warn(chalk.yellow(`⚠️ Warning: Final check failed to switch main worktree back to original branch "${originalBranch}". Please check manually.`)); console.warn(checkoutError.stderr || checkoutError.message); } } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
README.md(4 hunks)build/commands/config.js(3 hunks)build/commands/merge.js(2 hunks)build/commands/new.js(2 hunks)build/commands/pr.js(5 hunks)build/commands/remove.js(2 hunks)build/config.js(2 hunks)build/index.js(4 hunks)build/utils/git.js(1 hunks)src/commands/config.ts(2 hunks)src/commands/pr.ts(5 hunks)src/config.ts(3 hunks)src/index.ts(2 hunks)src/utils/git.ts(1 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/config.tssrc/commands/pr.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 (8)
📓 Common learnings
Learnt from: juristr
Repo: johnlindquist/worktree-cli PR: 20
File: src/commands/extract.ts:124-127
Timestamp: 2025-08-04T14:22:29.156Z
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.
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
Learning: Applies to src/commands/list.ts : Provide functionality to list existing Git worktrees in src/commands/list.ts.
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
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
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
Learning: Applies to src/commands/remove.ts : Handle removal of Git worktrees, including support for force deletion, in src/commands/remove.ts.
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
Learning: Applies to src/commands/list.ts : Provide functionality to list existing Git worktrees in src/commands/list.ts.
Applied to files:
src/utils/git.tsbuild/commands/remove.jsbuild/commands/merge.jsbuild/utils/git.jsREADME.mdbuild/commands/pr.jssrc/commands/pr.tsbuild/commands/new.jsbuild/index.js
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
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:
build/commands/remove.jsbuild/commands/merge.jsREADME.mdbuild/commands/pr.jssrc/commands/pr.tsbuild/commands/new.jsbuild/index.js
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
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:
build/commands/remove.jsbuild/commands/merge.jssrc/commands/config.tsREADME.mdbuild/commands/pr.jssrc/commands/pr.tsbuild/commands/new.jsbuild/index.js
📚 Learning: 2025-08-04T14:22:29.156Z
Learnt from: juristr
Repo: johnlindquist/worktree-cli PR: 20
File: src/commands/extract.ts:124-127
Timestamp: 2025-08-04T14:22:29.156Z
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.
Applied to files:
build/commands/remove.jsREADME.mdbuild/commands/pr.jssrc/commands/pr.tsbuild/commands/new.jsbuild/index.js
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
Learning: Applies to src/commands/*.ts : Leverage Execa to execute Git commands and other external processes.
Applied to files:
build/commands/merge.jsbuild/index.js
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
Learning: Applies to src/index.ts : Utilize Commander for parsing CLI commands and handling options.
Applied to files:
src/index.tsbuild/index.js
📚 Learning: 2025-08-04T13:02:29.847Z
Learnt from: CR
Repo: johnlindquist/worktree-cli PR: 0
File: .cursor/rules/project.mdc:0-0
Timestamp: 2025-08-04T13:02:29.847Z
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:
build/index.js
🧬 Code graph analysis (11)
build/config.js (3)
src/config.ts (2)
getGitProvider(48-50)setGitProvider(53-55)build/commands/config.js (1)
provider(12-12)build/commands/pr.js (1)
provider(61-61)
build/commands/remove.js (2)
build/utils/git.js (1)
isMainRepoBare(42-65)src/utils/git.ts (1)
isMainRepoBare(44-68)
build/commands/merge.js (2)
build/utils/git.js (1)
isMainRepoBare(42-65)src/utils/git.ts (1)
isMainRepoBare(44-68)
src/commands/config.ts (2)
build/config.js (3)
getGitProvider(36-38)setDefaultEditor(32-34)setGitProvider(40-42)src/config.ts (3)
getGitProvider(48-50)setDefaultEditor(43-45)setGitProvider(53-55)
src/index.ts (2)
build/commands/config.js (1)
configHandler(3-67)src/commands/config.ts (1)
configHandler(4-60)
build/utils/git.js (1)
src/utils/git.ts (3)
isMainRepoBare(44-68)getRepoRoot(70-78)detectGitProvider(80-96)
src/config.ts (3)
build/config.js (3)
getGitProvider(36-38)config(23-26)setGitProvider(40-42)build/commands/config.js (1)
provider(12-12)build/commands/pr.js (1)
provider(61-61)
build/commands/config.js (3)
build/commands/pr.js (1)
provider(61-61)build/config.js (2)
getGitProvider(36-38)setGitProvider(40-42)src/config.ts (2)
getGitProvider(48-50)setGitProvider(53-55)
src/commands/pr.ts (1)
src/utils/git.ts (4)
detectGitProvider(80-96)isWorktreeClean(15-40)getCurrentBranch(4-13)isMainRepoBare(44-68)
build/commands/new.js (2)
src/commands/new.ts (1)
newWorktreeHandler(8-129)src/utils/git.ts (2)
isWorktreeClean(15-40)isMainRepoBare(44-68)
build/index.js (5)
src/commands/setup.ts (1)
setupWorktreeHandler(9-232)src/commands/open.ts (1)
openWorktreeHandler(7-107)src/commands/extract.ts (1)
extractWorktreeHandler(8-153)build/commands/config.js (1)
configHandler(3-67)src/commands/config.ts (1)
configHandler(4-60)
🪛 LanguageTool
README.md
[uncategorized] ~120-~120: The official name of this software platform is spelled with a capital “H”.
Context: ...t provider by analyzing the remote URL (github.com or gitlab.com/gitlab.*). The config...
(GITHUB)
🔇 Additional comments (16)
src/utils/git.ts (1)
80-96: LGTM on the function structure and error handling.The function follows the established patterns in this file: async/await with execa, appropriate error handling with chalk-colored warnings, and sensible default return values. The return type
'gh' | 'glab' | nullis well-typed for downstream usage.build/commands/merge.js (2)
4-4: LGTM on import.The import correctly references the utility from the shared git utilities module.
58-63: LGTM on bare repository guard.The safety check correctly prevents worktree removal when the main repository is bare. The error messages are clear and provide actionable guidance. The placement after the merge operation (line 54) is reasonable—it allows the merge to proceed while blocking only the cleanup step that would fail.
build/config.js (2)
17-21: LGTM on schema definition.The
gitProviderconfiguration follows the same pattern asdefaultEditor. The enum constraint will be enforced by theConflibrary's schema validation at runtime.
35-42: LGTM on getter/setter functions.The functions follow the established pattern from
getDefaultEditor/setDefaultEditor. Input validation for the provider value is handled at the command handler level (src/commands/config.tslines 24-27), which is consistent with the existing approach for the editor configuration.build/commands/remove.js (1)
46-51: LGTM on bare repository guard.The safety check is correctly placed before the destructive
git worktree removeoperation and follows the same pattern used inmerge.js. Error messages are consistent and actionable.src/index.ts (2)
157-176: LGTM on config set subcommands.The provider subcommand follows the established pattern from the editor subcommand. The structure using
addCommandis clean and maintainable. Based on learnings, this utilizes Commander for parsing CLI commands as expected.
186-190: LGTM on config get provider subcommand.The get provider subcommand correctly mirrors the structure of the get editor subcommand, maintaining consistency in the CLI interface.
build/commands/new.js (1)
6-6: New safety checks for dirty/bare repos look solidImporting
isWorktreeClean/isMainRepoBareand hard‑failing when the main worktree is dirty or the repo is bare is consistent with the other commands and avoids subtle git corruption. No issues here.Also applies to: 14-17, 72-77
src/commands/config.ts (1)
2-2: Provider config wiring is correct and user-friendly
providerhandling mirrorseditorsemantics, validates to'gh' | 'glab', and surfaces clear error/help messages including the available keys; this should integrate cleanly with the new gitProvider field insrc/config.ts.Also applies to: 11-13, 16-16, 24-31, 35-37, 40-41
src/config.ts (1)
13-16: gitProvider schema and accessors are well-definedAdding
gitProviderwith an enum schema and default'gh', plusgetGitProvider/setGitProvider, cleanly exposes provider choice while keeping existing configs backward compatible via Conf’s defaults. No changes needed.Also applies to: 21-30, 47-55
build/commands/pr.js (4)
71-81: Worktree clean check and gh/glab checkout flow are robustThe clean‑worktree guard before touching branches, the provider‑aware
cliName/subCommandselection, and the detailed error mapping for checkout failures (already checked out, not found, CLI missing) give a solid UX and minimize risky git states.Also applies to: 83-91, 93-113
181-199: Bare-repo guard before worktree creation is consistent with other commandsChecking
isMainRepoBare()right beforegit worktree addand aborting with a clear explanation and recovery hint (git config core.bare false) aligns well with the safety checks innew/setup/extractand should prevent confusing failures in misconfigured repos.
225-233: Top-level error reporting is clear and PR/MR-neutralThe final catch logs a provider‑agnostic failure message (
Failed to set up worktree from PR/MR) and optionally prints a stack trace when stderr/stdout aren’t present, which is helpful for debugging without overwhelming normal users.
5-7: gh/glab branch resolution looks correct for PR/MR head branchesThe helper correctly:
- Uses
gh pr view <n> --json headRefName -q .headRefNamefor GitHub, which matches current CLI docs for retrievingheadRefName.- Uses
glab mr view <n> -F jsonand readssource_branch, which is consistent with the GitLab MR JSON schema.Error handling distinguishes between "not found" and missing CLI binaries with provider-specific messages, which should give clear feedback to users.
src/commands/pr.ts (1)
29-42: The code correctly usessource_branchas the field name. The glab CLI's JSON output formr viewfollows the GitLab Merge Request API schema, which documentssource_branchas the standard field containing the source branch name. The assumption in the code is accurate and aligns with official glab and GitLab documentation.
| // 6. Switch back to original branch IMMEDIATELY after checkout ensures the main worktree is clean | ||
| if (originalBranch) { | ||
| try { | ||
| const currentBranchAfterGh = await getCurrentBranch(); | ||
| if (currentBranchAfterGh === prBranchName && currentBranchAfterGh !== originalBranch) { | ||
| const currentBranchAfterCheckout = await getCurrentBranch(); | ||
| if (currentBranchAfterCheckout === prBranchName && currentBranchAfterCheckout !== originalBranch) { | ||
| console.log(chalk.blue(`Switching main worktree back to "${originalBranch}" before creating worktree...`)); | ||
| await execa("git", ["checkout", originalBranch]); | ||
| } | ||
| else if (currentBranchAfterGh !== originalBranch) { | ||
| console.log(chalk.yellow(`Current branch is ${currentBranchAfterGh}, not ${prBranchName}. Assuming gh handled checkout correctly.`)); | ||
| // If gh failed but left us on a different branch, still try to go back | ||
| else if (currentBranchAfterCheckout !== originalBranch) { | ||
| console.log(chalk.yellow(`Current branch is ${currentBranchAfterCheckout}, not ${prBranchName}. Assuming ${cliName} handled checkout correctly.`)); | ||
| await execa("git", ["checkout", originalBranch]); | ||
| } | ||
| } | ||
| catch (checkoutError) { | ||
| console.warn(chalk.yellow(`⚠️ Warning: Failed to switch main worktree back to original branch "${originalBranch}" after gh checkout. Please check manually.`)); | ||
| console.warn(chalk.yellow(`⚠️ Warning: Failed to switch main worktree back to original branch "${originalBranch}" after ${cliName} checkout. Please check manually.`)); | ||
| console.warn(checkoutError.stderr || checkoutError.message); | ||
| // Proceed with caution, worktree add might fail | ||
| } | ||
| } |
There was a problem hiding this comment.
Duplicate branch-restore warnings and unused “already warned” check
Both the mid‑flow restore block and the finally block try to switch back to originalBranch, but the "already warned" string is never set, so the final block will always log its warning if checkout fails, potentially duplicating messages.
You can simplify and de‑duplicate this by tracking a boolean flag instead of inspecting checkoutError.message, for example:
- finally {
- // 12. Ensure we are back on the original branch in the main worktree
- if (originalBranch) {
- try {
- const currentBranchNow = await getCurrentBranch();
- if (currentBranchNow !== originalBranch) {
- console.log(chalk.blue(`Ensuring main worktree is back on "${originalBranch}"...`));
- await execa("git", ["checkout", originalBranch]);
- }
- }
- catch (checkoutError) {
- if (!checkoutError.message.includes("already warned")) {
- console.warn(chalk.yellow(`⚠️ Warning: Final check failed to switch main worktree back to original branch "${originalBranch}". Please check manually.`));
- console.warn(checkoutError.stderr || checkoutError.message);
- }
- }
- }
- }
+ finally {
+ if (originalBranch) {
+ try {
+ const currentBranchNow = await getCurrentBranch();
+ if (currentBranchNow !== originalBranch) {
+ console.log(chalk.blue(`Ensuring main worktree is back on "${originalBranch}"...`));
+ await execa("git", ["checkout", originalBranch]);
+ }
+ } catch (checkoutError) {
+ console.warn(chalk.yellow(`⚠️ Warning: Final check failed to switch main worktree back to original branch "${originalBranch}". Please check manually.`));
+ console.warn(checkoutError.stderr || checkoutError.message);
+ }
+ }
+ }Also applies to: 237-252
🤖 Prompt for AI Agents
In build/commands/pr.js around lines 115 to 132 (and similarly at 237-252), the
branch-restore logic can emit duplicate warnings because the "already warned"
string is never set; replace that pattern with a boolean flag (e.g.,
restoredMainWorktreeWarningEmitted) initialized false, set it true when you log
the warning in the mid-flow catch, and check that flag in the finally block
before logging so the warning is only emitted once; ensure both restore
locations use the same flag and avoid relying on checkoutError.message content.
| program | ||
| .command("setup") | ||
| .argument("[branchName]", "Name of the branch to base this worktree on") | ||
| .option("-p, --path <path>", "Relative path/folder name for new worktree") | ||
| .option("-c, --checkout", "Create new branch if it doesn't exist and checkout automatically", false) | ||
| .option("-i, --install <packageManager>", "Package manager to use for installing dependencies (npm, pnpm, bun, etc.)") | ||
| .option("-e, --editor <editor>", "Editor to use for opening the worktree (e.g., code, webstorm, windsurf, etc.)") | ||
| .description("Create a new worktree and run setup scripts from worktrees.json or .cursor/worktrees.json") | ||
| .action(setupWorktreeHandler); |
There was a problem hiding this comment.
CLI wiring looks good; update pr help text to mention GitLab/MRs
The new setup, open, and extract commands, plus the config set/get provider subcommands, are wired correctly and follow the existing Commander patterns. The only mismatch is in the pr command help:
- Argument description:
"GitHub Pull Request number to create a worktree from" - Command description:
"Fetch the branch for a given GitHub PR number and create a worktree."
Now that prWorktreeHandler supports both GitHub PRs and GitLab MRs based on provider detection, this help text should be updated to something like “Pull Request / Merge Request number” and remove the GitHub‑only wording.
- .argument("<prNumber>", "GitHub Pull Request number to create a worktree from")
+ .argument("<prNumber>", "Pull Request / Merge Request number to create a worktree from")
@@
- .description("Fetch the branch for a given GitHub PR number and create a worktree.")
+ .description("Fetch the branch for a given PR/MR number and create a worktree using the appropriate provider (GitHub or GitLab).")Also applies to: 59-65, 67-79, 81-104
🤖 Prompt for AI Agents
In build/index.js around lines 27 to 35 (and similarly at 59-65, 67-79, 81-104),
the CLI help text for the `pr` command is GitHub-specific; update the argument
and command descriptions to be provider-agnostic (e.g., use "Pull Request /
Merge Request number" and remove "GitHub" wording) so the help reflects support
for both GitHub PRs and GitLab MRs; change any occurrences of "GitHub" in those
descriptions to neutral phrasing and ensure examples/help consistently reference
"PR/MR" or "Pull Request / Merge Request" as appropriate.
| // 2. Determine git provider (from config or auto-detect) | ||
| let provider = getGitProvider(); | ||
| const detectedProvider = await detectGitProvider(); | ||
| if (detectedProvider && detectedProvider !== provider) { | ||
| console.log(chalk.yellow(`Detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository, but config is set to '${provider}'.`)); | ||
| console.log(chalk.yellow(`Using detected provider: ${detectedProvider}`)); | ||
| provider = detectedProvider; | ||
| } | ||
| const isPR = provider === 'gh'; | ||
| const requestType = isPR ? "PR" : "MR"; | ||
|
|
There was a problem hiding this comment.
Provider precedence contradicts the PR description.
The PR description states: "automatic detection with the manual config as a fallback", which implies the config setting should take precedence. However, the current implementation does the opposite—when detectedProvider differs from provider, it overrides the config value and uses the detected one.
This could confuse users who explicitly run wt config set provider glab expecting their choice to be respected.
If the config should take precedence (as the PR description suggests), apply this diff:
- // 2. Determine git provider (from config or auto-detect)
- let provider = getGitProvider();
- const detectedProvider = await detectGitProvider();
- if (detectedProvider && detectedProvider !== provider) {
- console.log(chalk.yellow(`Detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository, but config is set to '${provider}'.`));
- console.log(chalk.yellow(`Using detected provider: ${detectedProvider}`));
- provider = detectedProvider;
- }
+ // 2. Determine git provider (prefer config, fallback to auto-detect)
+ let provider = getGitProvider();
+ const detectedProvider = await detectGitProvider();
+ if (!provider && detectedProvider) {
+ console.log(chalk.blue(`Auto-detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository.`));
+ provider = detectedProvider;
+ } else if (detectedProvider && detectedProvider !== provider) {
+ console.log(chalk.yellow(`Detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository, but using configured provider '${provider}'.`));
+ }Alternatively, if auto-detection should override (current behavior), please update the PR description to reflect this design choice.
📝 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.
| // 2. Determine git provider (from config or auto-detect) | |
| let provider = getGitProvider(); | |
| const detectedProvider = await detectGitProvider(); | |
| if (detectedProvider && detectedProvider !== provider) { | |
| console.log(chalk.yellow(`Detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository, but config is set to '${provider}'.`)); | |
| console.log(chalk.yellow(`Using detected provider: ${detectedProvider}`)); | |
| provider = detectedProvider; | |
| } | |
| const isPR = provider === 'gh'; | |
| const requestType = isPR ? "PR" : "MR"; | |
| // 2. Determine git provider (prefer config, fallback to auto-detect) | |
| let provider = getGitProvider(); | |
| const detectedProvider = await detectGitProvider(); | |
| if (!provider && detectedProvider) { | |
| console.log(chalk.blue(`Auto-detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository.`)); | |
| provider = detectedProvider; | |
| } else if (detectedProvider && detectedProvider !== provider) { | |
| console.log(chalk.yellow(`Detected ${detectedProvider === 'gh' ? 'GitHub' : 'GitLab'} repository, but using configured provider '${provider}'.`)); | |
| } | |
| const isPR = provider === 'gh'; | |
| const requestType = isPR ? "PR" : "MR"; |
🤖 Prompt for AI Agents
In src/commands/pr.ts around lines 68 to 78, the code currently lets
auto-detection override an explicit config value, which contradicts the PR
description that config should take precedence; change the logic so the
configured provider is used when present and detection is only a fallback: only
assign detectedProvider to provider when provider is falsy/undefined and
detectedProvider exists, and adjust the console messages to only announce
detection when the config was absent and detection is used.
- Auto-detect git provider from remote URL (github.com or gitlab.com) - Add config option: wt config set provider gh|glab - Adapt pr command to work with both GitHub PRs and GitLab MRs - Update error messages to be provider-specific (PR vs MR, GitHub vs GitLab) - Add detectGitProvider function to git.ts - Add getGitProvider/setGitProvider to config.ts The tool automatically detects the provider from the remote URL. Manual configuration is available as fallback via config command. Backward compatible: defaults to gh (GitHub CLI) maintaining existing behavior. Integrates PR #26 by @Cl3MM
* fix: Address issues #27, #28, #29 - #27: Editor 'none' now skips opening editor instead of failing Added shouldSkipEditor() to config.ts and updated all command handlers to check for 'none' value before attempting to open editor - #28: wt new without branch name now shows clear error Added validation at start of newWorktreeHandler to require branch name - #29: build/index.js now has execute permissions after build Added postbuild script to package.json that runs chmod +x Also updated .gitignore to exclude .beads/ and .vscode/ directories * feat: Add GitLab support for pr command (PR #26) - Auto-detect git provider from remote URL (github.com or gitlab.com) - Add config option: wt config set provider gh|glab - Adapt pr command to work with both GitHub PRs and GitLab MRs - Update error messages to be provider-specific (PR vs MR, GitHub vs GitLab) - Add detectGitProvider function to git.ts - Add getGitProvider/setGitProvider to config.ts The tool automatically detects the provider from the remote URL. Manual configuration is available as fallback via config command. Backward compatible: defaults to gh (GitHub CLI) maintaining existing behavior. Integrates PR #26 by @Cl3MM * feat: Add configurable default worktree directory (PR #24) - Add defaultWorktreePath config option - Add wt config set worktreepath <path> command - Add wt config get worktreepath command - Add wt config clear worktreepath command (reverts to sibling behavior) - Update new, setup, pr, and extract commands to use configured path - Support ~ expansion in path (expands to home directory) Path resolution priority: 1. --path flag (highest priority) 2. defaultWorktreePath config setting 3. Sibling directory behavior (default fallback) Example: wt config set worktreepath ~/worktrees wt new feature/login -> creates ~/worktrees/login Integrates PR #24 by @trevortrinh * docs: Add JSDoc comments to key functions (PR #23) - Add detailed docstring to setupWorktreeHandler explaining parameters and behavior - Add docstring to isMainRepoBare function - Add docstring to getRepoRoot function Integrates documentation improvements from PR #23 by @coderabbitai * docs: Update README with new features - Document GitLab support for wt pr command - Document editor 'none' option to skip opening - Document git provider configuration - Document configurable default worktree directory - Update requirements section * feat: Add --setup flag to wt pr command (closes #25) - Create shared runSetupScripts utility in src/utils/setup.ts - Add -s, --setup option to pr command - Execute setup scripts from worktrees.json or .cursor/worktrees.json - Update README with --setup documentation This allows users to run setup scripts when creating a worktree from a PR/MR: wt pr 123 --setup * fix: Address CodeRabbit PR feedback - Fix GitLab MR branch lookup: use -o json instead of -F json - Add JSON parse error handling for clearer error messages - Improve GitLab detection: parse hostname properly instead of includes() - Fix magic string check: use hasWarnedAboutCheckout flag instead - Remove redundant default: undefined from config schema - Add HOME/USERPROFILE validation for tilde expansion - Make postbuild script cross-platform (works on Windows) - Add JSDoc documentation for detectGitProvider and getRemoteHostname
Add support for GitLab CLI (glab) alongside GitHub CLI (gh) for the pr command.
The tool automatically detects the provider from the remote URL. Manual configuration is available as fallback via config command.
Backward compatible: defaults to gh (GitHub CLI) maintaining existing behavior.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.