This repository contains two CLI tools for managing GitHub PRs:
- cherry-picker: Manages cherry-picks across release branches with AI-assisted conflict resolution
- dep-merger: Manages dependency PRs (those with
type/dependencieslabel) with retry and merge operations
A CLI tool for managing cherry-picks across GitHub repositories using a YAML configuration file to track state.
It is currently highly opinionated and only supports GitHub repositories.
- It uses cursor-agent for AI-assisted conflict resolution.
- It uses semantic versioning for target branches.
- It expects squash merges for PRs.
My use case is to cherry-pick PRs from the main branch to release branches for argoproj/argo-workflows.
I'm open to contributions to make it more flexible and support other versioning schemes and repositories.
Build the tool from source:
make cherry-pickerOr run all checks and build:
make allThis will format the code, run tests, and build the binary.
This tool requires a GitHub Personal Access Token (PAT) with specific permissions to interact with the GitHub API.
For fine-grained personal access tokens, the following repository permissions are required:
- Contents: Read and Write
- Issues: Read and Write
- Pull requests: Read and Write
- Actions: Read and Write
- Metadata: Read
Fewer permissions can be granted but some features will not work. Issues Read/Write is for a future release/issue tracking feature.
- Create a fine-grained personal access token at: https://github.com/settings/personal-access-tokens/new
- Select the repository you want to manage cherry-picks for
- Grant the permissions listed above
- Export the token as an environment variable:
export GITHUB_TOKEN="your_github_token_here"- Contents (Read+Write): Used to read repository files and perform merge operations
- Issues (Read+Write): Used to read PR metadata and update status
- Pull requests (Read+Write): Used to fetch PR details, create cherry-pick PRs, and merge PRs
- Actions (Read+Write): Used to retry failed CI workflows and check CI status
- Metadata (Read): Used to access repository metadata required for API operations
If you can merge PRs in the GitHub UI but the merge command fails:
- Check PAT Permissions: Ensure your token has Contents: Read and Write permission (most common issue)
- Verify Branch Protection: Repository branch protection rules may block API merges while allowing UI merges
- Organization Policies: Some organizations restrict fine-grained PAT merge capabilities
- Test with Classic PAT: Try a classic PAT with
reposcope to isolate permission issues
Common Error: 403 Forbidden or merge not allowed usually indicates missing Contents permission.
Create a new cherry-picks.yaml configuration file:
./cherry-picker config --org myorg --repo myrepo --ai-assistant cursor-agentThis creates a configuration file with the default source branch set to main and configures the AI assistant for conflict resolution.
The --ai-assistant flag is required and specifies which command-line AI tool to use for interactive conflict resolution. Supported options:
- cursor-agent: Anthropic's Cursor AI agent CLI
- claude: Anthropic's Claude CLI
See the AI Assistant Setup section below for installation and configuration details.
Fetch merged PRs from GitHub that have cherry-pick labels:
export GITHUB_TOKEN="your_github_token"
./cherry-picker fetchThis command will:
- Fetch merged PRs to the source branch since the last fetch date (or 30 days ago for first run)
- Only include PRs with
cherry-pick/*labels (e.g.,cherry-pick/3.6for release-3.6) - Check PR comments for bot-created cherry-pick PRs and failures (e.g., argo-cd-cherry-pick-bot)
- Automatically add PRs to tracking with status:
- pending: Bot hasn't attempted cherry-pick yet (label exists but no bot action)
- failed: Bot attempted cherry-pick but failed (e.g., due to conflicts)
- picked: Bot successfully created cherry-pick PR
- merged: Cherry-pick PR has been merged
- Update the last fetch date
Cherry-pick PRs that the automated bot couldn't handle due to conflicts:
./cherry-picker pick 123 release-1.0
./cherry-picker pick 123 # Pick to all failed branchesThis command handles cherry-picks that failed with the automated bot. It will:
- Requirement: PR must have
failedstatus for the target branch - Create a new branch (
cherry-pick-<prnum>-<target>) - Perform
git cherry-pick -xwith AI-assisted conflict resolution - Push the branch and create a cherry-pick PR
- Update the configuration with the new PR details
Note: This command only works on PRs with failed status, meaning the bot attempted cherry-pick but failed (usually due to conflicts). PRs with pending status haven't been attempted by the bot yet and should wait for the bot to try first.
Use --force to amend an existing bot-created cherry-pick PR that needs manual fixes:
./cherry-picker pick 123 release-1.0 --force
./cherry-picker pick 123 --force # Amend all picked branchesThis is useful when:
- CI is failing on a bot-created PR and needs code fixes
- Reviewers requested changes to the cherry-pick
- The bot's cherry-pick was incorrect and needs manual correction
The --force flag will:
- Requirement: PR must have
pickedstatus with an existing cherry-pick PR - Fetch the existing PR branch from GitHub
- Launch AI assistant to help make amendments
- Force push to update the existing PR (CI will re-run)
Retry failed CI workflows for picked PRs:
./cherry-picker retry 123 release-1.0 # Retry specific branch
./cherry-picker retry 123 # Retry all branches with failed CISquash and merge picked PRs with passing CI:
./cherry-picker merge 123 release-1.0 # Merge specific branch
./cherry-picker merge 123 # Merge all eligible branchesThis command performs a squash merge by default, combining all commits in the PR into a single commit. This matches the "Squash and merge" button in the GitHub UI and works with repositories that have merge commits disabled.
View the current status of all tracked PRs:
./cherry-picker statusGenerate a development progress summary for a target branch:
./cherry-picker summary release-1.0This command will:
- Query GitHub for commits to the specified target branch since the last release
- Generate markdown output showing cherry-picked PRs and their status
- Include in-progress items (picked but not yet merged)
- Determine the next patch version based on existing tags
View the current status of all tracked PRs:
./cherry-picker statusThis command will:
- Show all tracked PRs and their status across branches
- Display pending (β³), picked (β /π), merged (β ) states
- Fetch PR details from GitHub (when
GITHUB_TOKENis set):- PR title and GitHub URL
- Merge status (β merged / β not merged)
- CI status (β passing / β failing / π pending / β unknown)
- Show contextual commands directly under each branch status:
- Pending branches:
pickcommand - Picked branches with failing CI:
retrycommand - Picked branches with passing CI:
mergecommand
- Pending branches:
- Provide a summary of total pending and completed picks
Cherry-pick status for myorg/myrepo (source: main)
Fix critical bug (https://github.com/myorg/myrepo/pull/123)
release-1.0 : β³ pending (bot hasn't attempted)
release-2.0 : β failed (bot couldn't cherry-pick)
π‘ ./cherry-picker pick 123 release-2.0
release-3.0 : π picked (https://github.com/myorg/myrepo/pull/456)
Fix critical bug (cherry-pick release-3.0) [β CI failing]
π‘ ./cherry-picker retry 123 release-3.0
Add new feature (https://github.com/myorg/myrepo/pull/125)
release-1.0 : β
picked (https://github.com/myorg/myrepo/pull/457)
Add new feature (cherry-pick release-1.0) [β
CI passing]
π‘ ./cherry-picker merge 125 release-1.0
release-2.0 : β
merged
Summary: 2 PR(s), 1 pending, 1 failed, 3 completed (2 picked, 1 merged)Note: PR details are only fetched when GITHUB_TOKEN environment variable is set. Without it, only PR numbers are shown.
Initialize or update configuration:
--org, -o: GitHub organization or username (auto-detected from git if available)--repo, -r: GitHub repository name (auto-detected from git if available)--source-branch, -s: Source branch name (auto-detected from git if available, defaults to "main")--ai-assistant, -a: Required. AI assistant command for conflict resolution (e.g., "cursor-agent", "claude")--config, -c: Configuration file path (default: "cherry-picks.yaml")
Target branches are automatically determined from cherry-pick/* labels on PRs.
Fetch merged PRs with cherry-pick labels:
--config, -c: Configuration file path (default: "cherry-picks.yaml")--since, -s: Fetch PRs since this date (YYYY-MM-DD), defaults to last fetch date
PRs are automatically added based on their cherry-pick/* labels. For example, a PR with label cherry-pick/3.6 will be tracked for branch release-3.6.
AI-assisted cherry-pick for PRs that bots couldn't handle:
--config, -c: Configuration file path (default: "cherry-picks.yaml")--force: Amend an existing bot-created cherry-pick PR instead of creating a new one
Normal mode (without --force): For PRs with failed status. Creates a new cherry-pick branch and PR with AI-assisted conflict resolution.
Force mode (with --force): For PRs with picked status. Fetches the existing PR branch, allows AI-assisted amendments, and force pushes to update the existing PR.
Retry failed CI workflows:
--config, -c: Configuration file path (default: "cherry-picks.yaml")
Squash and merge picked PRs:
--config, -c: Configuration file path (default: "cherry-picks.yaml")
View current status of tracked PRs:
--config, -c: Configuration file path (default: "cherry-picks.yaml")
Generate development progress summary for a target branch:
--config, -c: Configuration file path (default: "cherry-picks.yaml")
Initialize with custom source branch:
./cherry-picker config --org myorg --repo myrepo --source-branch develop --ai-assistant cursor-agentInitialize with custom config file and claude CLI:
./cherry-picker config --config my-picks.yaml --org myorg --repo myrepo --ai-assistant claudeFetch PRs since a specific date:
./cherry-picker fetch --since 2024-01-01Cherry-picker requires an AI assistant CLI tool for interactive conflict resolution. You must configure one during initialization.
Install cursor-agent:
# Install via npm
npm install -g @anthropic-ai/cursor-agent
# Login (required for first-time use)
cursor-agent loginConfigure cherry-picker to use cursor-agent:
./cherry-picker config --ai-assistant cursor-agentDocumentation: https://docs.cursor.com/agent
Install claude CLI:
# Install via npm
npm install -g @anthropic-ai/claude-cli
# Login (required for first-time use)
claude loginConfigure cherry-picker to use claude:
./cherry-picker config --ai-assistant claudeDocumentation: https://docs.anthropic.com/claude/docs/claude-cli
You can configure any command-line tool that provides an interactive session:
./cherry-picker config --ai-assistant "your-custom-ai-tool"The tool will be launched with stdin/stdout connected for interactive conflict resolution.
When a cherry-pick encounters merge conflicts, the tool launches an interactive AI session to help you resolve them:
- Automatic Detection: When
git cherry-pickfails due to conflicts, the tool detects this automatically - Initial Context: Sends a detailed prompt to the AI about the specific conflicts and cherry-pick context
- Interactive Handover: After providing context, you take control of the conversation
- Guided Resolution: Work with the AI to understand and resolve conflicts step-by-step
- User Decision: After the AI session, you decide whether to proceed with the cherry-pick
The initial context + interactive approach gives you:
- Informed AI: AI starts with full context about the cherry-pick and conflicts
- Direct Communication: Ask specific questions and guide the resolution
- Educational Value: Learn why conflicts occurred and how to resolve them
- Flexible Approach: Guide the AI toward your preferred resolution strategy
- Real-time Feedback: See and approve changes before they're applied
- AI Assistant configured: Set via
--ai-assistantflag duringconfigcommand - Authentication: Run
<ai-tool> loginto authenticate (e.g.,cursor-agent loginorclaude login) - Git Context: The AI can see your repository state and conflicted files
# When conflicts occur during cherry-pick:
./cherry-picker pick 123 release-1.0
# Output:
π― Cherry-pick PR #123: Fix critical bug to branch: release-1.0
β οΈ Cherry-pick conflicts detected. Attempting Cursor AI-assisted resolution...
π Found 2 conflicted file(s): [src/main.go src/config.go]
π€ Launching cursor-agent with initial context...
π‘ Starting AI session with conflict context, then handing control to you.
- The AI will receive details about the cherry-pick conflicts
- You can then guide the resolution process
- Exit the agent when you're satisfied with the resolution
π― Sending initial context to AI...
# AI receives this context automatically:
# "I need help resolving cherry-pick conflicts. Here's the situation:
#
# **Cherry-pick Context:**
# - Attempting to cherry-pick: 448c492 Fix critical bug
# - Number of conflicted files: 2
# - Conflicted files: [src/main.go src/config.go]
#
# Please start by examining the conflicted files and let me know what you see."
# Then you take control of the conversation:
# > "What conflicts do you see in main.go?"
# > "Can you resolve the conflicts in config.go first?"
# > "Please make the changes to merge both approaches"
# After you exit the cursor-agent session:
π Cursor-agent session completed.
- Assuming conflicts have been resolved during the AI session
- Checking if cherry-pick is complete...
π― No conflicts remaining. Completing cherry-pick commit...
β
Cherry-pick completed with AI-assisted conflict resolutionAfter the interactive cursor-agent session, the tool automatically:
- Checks for remaining conflicts: Verifies no conflict markers remain in files
- Detects completion status: Checks if the AI already completed the cherry-pick
- Completes the commit: Runs
git cherry-pick --continueif needed - Proceeds with PR creation: Continues with pushing the branch and creating the PR
If conflicts still remain after the AI session:
- The tool will list the problematic files
- You'll need to resolve them manually and run
git cherry-pick --continue - Or run
git cherry-pick --abortto cancel
For effective AI conflict resolution:
- Be Specific: Ask for help with particular files or conflict types
- Explain Context: Tell the AI about the purpose of the changes being cherry-picked
- Review Changes: Check each file as the AI modifies it
- Ask Questions: Use the AI to understand why conflicts occurred
- Iterate: Work through complex conflicts step-by-step with the AI
When the interactive AI session encounters issues:
- Session Failure: If the configured AI assistant fails to launch, you'll get clear instructions for manual resolution
- User Control: You can exit the AI session at any time and handle conflicts manually
- Git State: Cherry-pick remains in progress, allowing you to continue with standard Git tools
- No Automated Changes: The interactive approach doesn't make changes without your approval
If the AI assistant is not available, the cherry-pick process will abort with a clear error message:
Example failure output:
β Failed to launch AI assistant: exec: "cursor-agent": executable file not found in $PATH
- You can resolve conflicts manually using standard Git tools
- Run 'git cherry-pick --abort' to cancel, or resolve and 'git cherry-pick --continue'If AI assistant is not configured:
β Failed to launch AI assistant: AI assistant command not configured. Set it using: cherry-picker config --ai-assistant <command>The cherry-picks.yaml file stores the repository configuration and tracked PRs:
org: myorg
repo: myrepo
source_branch: main
ai_assistant_command: cursor-agent
last_fetch_date: 2024-01-15T10:30:00Z
tracked_prs:
- number: 123
title: "Fix critical bug"
branches:
release-1.0:
status: pending # Bot hasn't attempted yet
release-2.0:
status: failed # Bot tried but failed (conflicts)
release-3.0:
status: merged # Successfully cherry-picked and merged
pr:
number: 456
title: "Fix critical bug (cherry-pick release-3.0)"
ci_status: "passing"
- number: 124
title: "Add new feature"
branches:
release-1.0:
status: picked # Bot successfully created PR, waiting for merge
pr:
number: 457
title: "Add new feature (cherry-pick release-1.0)"
ci_status: "passing"Each tracked PR has per-branch status tracking:
- number: Original PR number
- title: PR title (fetched from GitHub)
- branches: Map of target branch names to their status:
- status: One of:
pending: Bot hasn't attempted cherry-pick yetfailed: Bot attempted but failed (usually conflicts) - usepickcommandpicked: Bot successfully created cherry-pick PR - usepick --forceto amendmerged: Cherry-pick PR has been merged
- pr: Details of the cherry-pick PR (when status is
pickedormerged):- number: Cherry-pick PR number
- title: Cherry-pick PR title
- ci_status: CI status (
passing,failing,pending,unknown)
- status: One of:
A CLI tool for managing dependency PRs on GitHub repositories. It tracks PRs with the type/dependencies label and provides commands to retry failed CI and merge PRs with passing CI.
| Aspect | Cherry Picker | Dep Merger |
|---|---|---|
| Label | cherry-pick/* |
type/dependencies |
| DCO checks | Filtered out (ignored) | Must pass |
| PR type | Merged PRs needing cherry-pick | Open PRs to merge |
| AI assistant | Required for conflicts | Not needed |
| Branch tracking | Per-branch status | Single PR status |
Build the tool from source:
make dep-mergerOr build both tools:
make buildCreate a new dep-merger.yaml configuration file:
./dep-merger config --org myorg --repo myrepoWhen run from a git repository, org and repo are auto-detected from the git remote.
Fetch open PRs with the type/dependencies label from GitHub:
export GITHUB_TOKEN="your_github_token"
./dep-merger fetchThis command will:
- Search for open PRs with the
type/dependencieslabel - Add new PRs to tracking with their CI status
- Update CI status for already tracked PRs
- Mark PRs as merged if they're no longer open
View the current status of all tracked dependency PRs:
./dep-merger statusExample output:
Dependency PR status for myorg/myrepo
Bump golang.org/x/net from 0.17.0 to 0.23.0 (https://github.com/myorg/myrepo/pull/123)
Status: β
CI passing
π‘ ./dep-merger merge 123
Bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (https://github.com/myorg/myrepo/pull/124)
Status: β CI failing [run attempt 2]
π‘ ./dep-merger retry 124
Bump actions/checkout from 3 to 4 (https://github.com/myorg/myrepo/pull/125)
Status: π CI pending
Summary: 3 PR(s) - 1 passing, 1 failing, 1 pending, 0 merged
Options:
--fetch: Fetch latest data from GitHub before showing status--show-merged: Include merged PRs in the output
Retry failed CI workflows for dependency PRs:
./dep-merger retry 123 # Retry specific PR
./dep-merger retry # Retry all PRs with failing CISquash and merge dependency PRs with passing CI:
./dep-merger merge 123 # Merge specific PR
./dep-merger merge # Merge all PRs with passing CIInitialize or update configuration:
--org, -o: GitHub organization or username (auto-detected from git)--repo, -r: GitHub repository name (auto-detected from git)--config, -c: Configuration file path (default: "dep-merger.yaml")
Fetch open dependency PRs from GitHub:
--config, -c: Configuration file path (default: "dep-merger.yaml")
View current status of tracked PRs:
--config, -c: Configuration file path (default: "dep-merger.yaml")--fetch: Fetch latest data before showing status--show-merged: Show PRs that have been merged
Retry failed CI workflows:
--config, -c: Configuration file path (default: "dep-merger.yaml")
Squash and merge PRs with passing CI:
--config, -c: Configuration file path (default: "dep-merger.yaml")
The dep-merger.yaml file stores the repository configuration and tracked PRs:
org: myorg
repo: myrepo
last_fetch_date: 2024-01-15T10:30:00Z
tracked_prs:
- number: 123
title: "Bump golang.org/x/net from 0.17.0 to 0.23.0"
ci_status: passing
run_attempt: 1
merged: false
- number: 124
title: "Bump github.com/stretchr/testify from 1.8.4 to 1.9.0"
ci_status: failing
run_attempt: 2
merged: false
- number: 125
title: "Bump actions/checkout from 3 to 4"
ci_status: pending
merged: false- number: PR number
- title: PR title
- ci_status: Current CI status (
passing,failing,pending,unknown) - run_attempt: Number of CI run attempts (increments with each retry)
- merged: Whether the PR has been merged
# 1. Initialize (once per repository)
./dep-merger config
# 2. Fetch dependency PRs
./dep-merger fetch
# 3. Check status and see what needs attention
./dep-merger status
# 4. Retry any failing CI
./dep-merger retry
# 5. Wait for CI to pass, then merge
./dep-merger merge
# 6. Repeat steps 2-5 as new dependency PRs arriveRun all checks (format, vet, test):
make checkBuild both binaries:
make buildBuild individual binaries:
make cherry-picker
make dep-mergerClean build artifacts:
make clean