From resolve-prs
Resolve open dependency-update PRs on GitHub repos (Dependabot, Renovate, pyup, plus human-authored "chore(deps)" / "build(deps)" / "bump" PRs). Assesses each PR, merges safe ones, fixes and merges fixable ones, and closes broken ones with explanations. Use --all to process every git repo in the current directory in parallel. Use --dry-run to assess without taking action.
How this skill is triggered — by the user, by Claude, or both
Slash command
/resolve-prs:resolve-prs [--all] [--dry-run] [owner/repo][--all] [--dry-run] [owner/repo]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are resolving open dependency-update PRs on GitHub repositories. The primary case is Dependabot, but the same workflow applies to Renovate, pyup, and human-authored dep bumps that follow conventional-commit naming. Follow this methodology precisely.
You are resolving open dependency-update PRs on GitHub repositories. The primary case is Dependabot, but the same workflow applies to Renovate, pyup, and human-authored dep bumps that follow conventional-commit naming. Follow this methodology precisely.
gh CLI authenticated with access to the target repo(s)origin remote to determine owner/repo)--all: find all git repos in the current directory and process each one in parallel using a Claude team (one agent per repo)--dry-run: assess and report on all PRs without merging, fixing, or closing anythingowner/repo: process a specific GitHub repoFlags can be combined, e.g. --all --dry-run.
Raw arguments: $ARGUMENTS
If --all flag is present:
find . -maxdepth 2 -name .git -type d to discover reposrepo X: 3 merged, 1 fixed & merged, 1 closed, 0 failed)Otherwise, determine the single target repo:
owner/repo argument is given, use thatgit remote get-url origingh pr list --repo OWNER/REPO --state open --json number,title,author,mergeable,mergeStateStatus,headRefName,labels,statusCheckRollup
Filter to dependency-update PRs. A PR qualifies if EITHER:
dependabot[bot], dependabot-preview[bot], renovate[bot], renovate-bot, pyup-bot, pre-commit-ci[bot]. Substring matches on dependabot, renovate, or pyup are also fine.chore(deps):, chore(deps-dev):, build(deps):, build(deps-dev):, bump , or Bump (case-insensitive). This catches human-authored PRs that were opened before the bot was configured, or one-off manual upgrades.If no PRs match, report that and stop.
.resolve-prs-ignore (if present)If a .resolve-prs-ignore file exists at the repo root, exclude any PR whose updated dependency matches a pattern in the file. Use this for pinned-on-purpose deps (Expo SDK pins, packages on a fork, deps awaiting a coordinated bump, etc.) so the skill stops trying to merge them every run.
File format: one pattern per line; blank lines and # comments ignored. Patterns are matched against the dependency name from the PR's title/diff. Glob support: * matches anything within a name segment.
# .resolve-prs-ignore example
expo-* # pinned by Expo SDK; do not bump
react-native # pinned by Expo SDK
react-native-mmkv # we're on a fork
@auth0/auth0-react # waiting for v3
Matching: identify each PR's dependency name from the PR title (Dependabot/Renovate convention is bump <package> from X to Y / chore(deps): update <package> to Y) or, if ambiguous, from the changed package.json diff lines. A PR is ignored if the dependency name matches any pattern. Skipped PRs are reported with Action = Skipped and Reason = "matches .resolve-prs-ignore: <pattern>".
The ignore file is per-repo. With --all, each repo reads its own.
For each PR, before assessing risk, try to fetch release notes or changelogs:
gh api repos/OWNER/DEPENDENCY/releases/latest --jq '.body' 2>/dev/null
cat node_modules/PACKAGE/CHANGELOG.md 2>/dev/null | head -100
Use this information to anticipate breaking changes before testing. If the changelog explicitly mentions breaking changes or migration steps, factor that into the risk assessment and keep the info handy for Step 7 (fixing).
For each PR, get the diff:
gh pr diff NUMBER --repo OWNER/REPO
Categorize each PR by risk level:
Even low-risk PRs require a green signal before merge. Never merge purely on the version-number heuristic. Semver is aspirational (libraries break in patch versions by accident), and supply-chain attacks ride patch/minor bumps (e.g. eslint-config-prettier 8.10.1, xz-utils). Acceptable signals, in order of preference:
statusCheckRollup from Step 2 is SUCCESS (CI passed on the PR)tsc --noEmit or the compile script) plus lint
If neither signal is available (no CI configured, no compile/lint script), treat the PR as Medium Risk and follow Step 6.Always cross-reference with the "Common Breaking Change Patterns" section below and any changelog info from Step 3.
Before testing, detect the project's package manager from the repo root:
bun.lock or bun.lockb -> bunyarn.lock -> yarnpnpm-lock.yaml -> pnpmpackage-lock.json -> npmUse the detected package manager for all install/run commands throughout. The package manager is determined once per repo, even in monorepos; the lockfile lives at the root.
Many repos contain more than one package.json: workspaces (npm/yarn/pnpm/bun workspaces, Turborepo, Nx) and "two unrelated apps in one repo" cases (e.g. app/ + worker/). The wrong directory means the wrong typecheck context, so the validation in Step 6 runs from the directory the PR actually touches.
package.json files were modified:
gh pr diff NUMBER --repo OWNER/REPO --name-only | grep '/package.json$\|^package.json$'
package.json, dropping the filename). Call this list pkg_dirs. For most PRs pkg_dirs has exactly one entry; coordinated workspace bumps may have several.package.json contains a "workspaces" field, ORpnpm-workspace.yaml, turbo.json, or nx.jsoninstall_dir and validate_dirs:
install_dir = "." (root); validate_dirs = pkg_dirs. The workspace tool resolves all packages from the root install.app/ + worker/ with separate lockfiles or no root lockfile): install_dir = pkg_dir; validate_dirs = [pkg_dir]. Run install AND validation inside each affected directory.".".Step 6 uses install_dir for the install and iterates validate_dirs for typecheck/lint/test.
Use a git worktree to isolate the test environment. This guarantees the install resolves against the PR's actual lockfile (not the default branch's), never leaks state into the user's working tree if validation crashes, and avoids package-manager-specific --no-save flags.
For low-risk PRs without a green CI signal, run a smoke test (typecheck + lint). For medium/high risk PRs, run the full validation suite (typecheck + lint + tests).
Resolve the default branch name once at the start of the run (don't assume main):
default_branch=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
Use $default_branch anywhere the workflow refers to "main".
Fetch remote branches: git fetch origin
Create a temporary worktree on the PR branch and run validation inside it. Wrap setup, validation, and teardown in a single bash invocation so the trap cleans up reliably on failure. Use install_dir and validate_dirs from Step 5.5:
worktree_dir=$(mktemp -d -t resolve-prs-XXXXXX)
trap 'git worktree remove "$worktree_dir" --force 2>/dev/null' EXIT
git worktree add "$worktree_dir" "origin/BRANCH"
# Install once, in the right place
cd "$worktree_dir/$install_dir"
<pkg-manager> install # uses PR's lockfile - real resolution
# Validate in each affected package
for d in "${validate_dirs[@]}"; do
cd "$worktree_dir/$d"
<pkg-manager> run compile # or `tsc --noEmit` if no compile script
<pkg-manager> run lint:check # or whichever lint script exists
<pkg-manager> test # medium/high risk only
done
The worktree carries the PR's package.json AND lockfile, so the install matches what would actually land on $default_branch. No flags needed; the lockfile pins versions exactly.
The user's main checkout is never modified. There is no per-PR restore step. Each PR gets its own fresh worktree; the trap removes it whether validation passes, fails, or crashes.
If --dry-run is set, skip this step entirely. Just report the assessment from Step 9.
A PR is mergeable only if it has a green signal: statusCheckRollup is SUCCESS, or local validation from Step 6 passed. Never merge purely on the Step 4 risk classification.
gh pr merge NUMBER --repo OWNER/REPO --merge
Use a worktree for the same reasons as Step 6: the user's main checkout stays clean.
worktree_dir=$(mktemp -d -t resolve-prs-fix-XXXXXX)
git worktree add -B BRANCH "$worktree_dir" "origin/BRANCH"
cd "$worktree_dir/$install_dir" # install_dir from Step 5.5 (root for workspaces, subpackage for multi-package repos)
<pkg-manager> installgit push origin BRANCHcd - && git worktree remove "$worktree_dir" --forcegh pr merge NUMBER --repo OWNER/REPO --mergegh pr close NUMBER --repo OWNER/REPO --comment "Reason for closing..."
Always include a clear, specific reason:
Stop the bot from reopening it next week. gh pr close does not stop Dependabot or Renovate from re-creating the same PR on the next scheduled run. When closing, append a config snippet to the close comment so the user can paste it into their bot config:
For Dependabot (append to .github/dependabot.yml under the matching updates: entry):
ignore:
- dependency-name: "PACKAGE"
versions: ["X.Y.Z"] # or use update-types: ["version-update:semver-major"]
For Renovate (append to renovate.json):
"packageRules": [
{ "matchPackageNames": ["PACKAGE"], "matchUpdateTypes": ["major"], "enabled": false }
]
If the package is one the user already pins on purpose, also suggest they add it to .resolve-prs-ignore so this skill skips it on every future run too.
When you successfully fix a breaking change that is NOT already listed in the "Common Breaking Change Patterns" section below, add it. Auto-learned patterns can over-fit to one project or grow stale across versions, so the format below tracks provenance and freshness. Verify a pattern before trusting it blindly.
Only record generalizable patterns. The entry should help with ANY project that hits this upgrade, not just the current one. Example of good: "react-native-mmkv 3 -> 4: new MMKV() -> createMMKV()". Example of bad: "fixed import in src/utils/storage.ts".
One line per pattern, with dates. Format: **package X -> Y** (learned YYYY-MM, last verified YYYY-MM): brief description of what changed and how to fix it. Both dates equal the current month when first written. Hand-curated patterns (the ones already in this file without dates) are trusted as-is and don't need backfilling.
Refresh the verified date on reuse. When an existing pattern correctly applies to a new PR (its suggested fix worked), update its last verified date to the current month before merging. This keeps useful patterns fresh and lets unused ones age out visually.
Treat patterns older than 6 months as hints, not rules. Before applying any auto-learned pattern whose last verified date is >6 months old, re-verify by testing. Don't trust it blindly. If it still applies, refresh the date per rule 3. If it doesn't, fix or remove the pattern.
Cap at 30 entries per subsection. If a subsection hits 30, evict the entry with the oldest last verified date (least-recently-useful), not the oldest learned date. Patterns that keep proving useful stay; patterns nobody hits get culled.
Don't duplicate. If a similar pattern already exists, update it (refresh the dates and merge the description) rather than adding a new one.
Where to write. Edit the SKILL.md file directly in the skill's directory. The file location depends on where the skill is installed:
~/.claude/skills/resolve-prs/SKILL.md.claude/skills/resolve-prs/SKILL.mdls ~/.claude/plugins/*/skills/resolve-prs/SKILL.mdUse the Edit tool to append to the appropriate subsection under "Common Breaking Change Patterns".
The worktree-based approach in Steps 6 and 7 means the user's working tree was never touched, so there's no stash to restore and no branch to switch back from.
git worktree list | grep -E 'resolve-prs-(fix-)?[A-Za-z0-9]+' && \
git worktree list | awk '/resolve-prs-/ {print $1}' | xargs -I {} git worktree remove --force {}
git pull origin "$default_branch" (only if the user is on $default_branch; otherwise skip, and don't switch branches on their behalf).Present a summary table:
| PR | Title | Risk | Action | Reason |
|---|---|---|---|---|
| #N | ... | Low/Medium/High | Merged / Fixed & Merged / Closed / Skipped / Would merge (dry-run) / Would close (dry-run) | ... |
If --dry-run, use "Would merge", "Would close", "Would fix & merge" in the Action column. PRs filtered out by .resolve-prs-ignore use Skipped (the action would not have been taken anyway, so --dry-run doesn't change it).
If any new patterns were learned, mention them at the bottom:
Learned N new breaking change pattern(s) - the skill will handle these automatically next time.
Reference these when assessing PRs. This is not exhaustive - always verify by testing. New patterns are added automatically via auto-learn (Step 8).
.eslintrc.* -> eslint.config.js). Check if eslint-config-* packages support flat config before merging.FlatESLint/LegacyESLint exports, breaking typescript-eslint <8.56.0 and @eslint/js <10. Bump @eslint/js to ^10.0.0 and typescript-eslint to ^8.56.0+. Note: eslint-plugin-react-hooks may lack ESLint 10 peer dep but works anyway.{ reactRefresh }, and configs.vite becomes configs.vite() (function call). Also customHOCs renamed to extraHOCs.jest-expo, react-scripts). Check the preset's peer dependencies.@types/* packages and build tools ship compatible definitions.baseUrl and moduleResolution: "node" are deprecated (error by default). Fix: remove baseUrl (default is .), change moduleResolution to "bundler". Also, TS 6 no longer auto-includes all @types/* from typeRoots. Add an explicit "types" array listing needed type packages (e.g., ["react", "jest", "node"]).react version exactly.new MMKV() to createMMKV(), .delete() renamed to .remove(), requires Nitro Modules jest mock.npx expo install --fix. Don't bump packages that Expo constrains.@react-navigation/* packages.B@^2.0 but the PR bumps B to 3.0, close it.Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub gijungkim/resolve-prs --plugin resolve-prs