Merge deps
Agent skills by Titus Kirch — installable via skills.sh in Claude Code, Codex, Cursor, OpenCode and friends.
npx -y skills add TitusKirch/skills --skill merge-depsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Triages and merges a repo's open Dependabot pull requests, selected strictly by author (app/dependabot) so no human's and no other bot's PR is ever touched. Verifies each update on its own branch first, because a Dependabot PR into an integration branch often runs no meaningful CI at all. Merging is opt-in per repo via mergeDeps.merge; once opted in, mergeDeps.confirm (default major) lets the low-risk tier merge on that standing opt-in while major bumps still wait for a human. Forge chosen per-repo by config (root forge key); v1 is GitHub via the gh CLI. Invoke manually only — this skill never fires proactively and never opens a pull request. Use when the user asks to triage, review or merge Dependabot PRs or dependency updates, mentions the Dependabot queue, dependency bumps or Dependabot alerts, or says things like "merge the dependabot PRs", "check the dependency updates", "Dependabot PRs mergen".
SKILL.md
11.0 KB, as published. Nobody here has run it
merge-deps
Work the Dependabot queue — read the open Dependabot pull requests and the repo's Dependabot alerts, establish which updates are actually safe, and merge the ones the repo has opted into. Manual invocation only: nothing here fires on its own; merging is opt-in, and a major bump always waits for a human. The forge is chosen by config (the root forge key); GitHub (via gh) is the only forge implemented in v1.
Opted out? If the repo config sets mergeDeps to false, this skill is disabled for the repo — stop immediately and tell the user the merge-deps skill is turned off in .tituskirch-skills.json. An absent mergeDeps block is not disabled; it means report-only. Check .mergeDeps == false on the resolved config before any action. A missing jq or config exits non-zero too, so a pass is not evidence the config was read.
Workflow
1. Detect (read the repo — never assume)
- Backend — from the root
forgekey (v1: onlygithubis implemented; any other value → say it is not supported yet and stop). Confirm the repo is reachable:gh repo view --json nameWithOwner,defaultBranchRef. If it fails (no GitHub remote, orghnot authenticated), stop. - Dependabot config — read
.github/dependabot.ymlfor context only: which ecosystems exist, theirtarget-branch, theirgroups, theircooldown. It tells you what to expect; it is never a selection input. Nodependabot.yml→ Dependabot may still be raising security PRs; carry on. - Config —
.tituskirch-skills.jsonat the repo root (optional, committed). Keys: REFERENCE.md.
2. Select — Dependabot only
Select strictly by author. This is the skill's one hard constraint and it has no exceptions:
gh pr list --state open --search "author:app/dependabot" \
--json number,title,headRefName,baseRefName,isDraft,mergeable,mergeStateStatus
- Re-assert the author per PR before touching it —
gh pr view <n> --json author --jq '.author.login'must equalapp/dependabot. The search narrows; this is what proves it. - Never select by label or title. The
dependencieslabel and abuild(deps)title are settable by anyone; authorship is not. A human's PR wearing thedependencieslabel must come back from step 2 empty-handed. - Everything else is invisible — not merged, not commented on, not closed, not rebased, and not reported on. Other automation counts: a rollup or release PR authored by
app/github-actionsis another bot's PR, and this skill has nothing to say about it either.
Nothing selected → say so and stop. That is the normal, healthy result.
3. Assess — "green" is a claim you have to earn
Per selected PR, gather facts. Never merge on a heuristic.
- Base branch — read
baseRefNameper PR; never assume one base. Version updates followtarget-branch(e.g.dev), but security updates ignoretarget-branchand target the default branch (why this matters). The queue is routinely mixed. - Mergeability —
mergeable/mergeStateStatus.CONFLICTING→ step 4's rebase path.UNKNOWNmeans GitHub has not computed it yet, not that it is fine — re-poll. - Checks —
gh pr checks <n>, then the question that matters: which checks does this PR's base actually trigger? Read the workflows (.github/workflows/*.yml) and compare theiron.pull_request.branchesagainstbaseRefName.
An empty or irrelevant check list is
unknown, nevergreen. A workflow gated onbranches: [main]does not run for a PR intodev, so its absence is not a pass — there was no verdict at all. A suite that only scans source for vulnerabilities (CodeQL) says nothing about whether a lockfile still installs or the repo still lints. Counting either as "checks green" is how an unverified bump gets merged. Never merge onunknown.
- Verify locally — this is the primary gate, not a fallback. Run
mergeDeps.verifyagainst the PR's own head in a throwaway worktree, so the user's tree is never touched (recipe). Install the head's lockfile first — an uninstalled worktree resolves the command against whatever is onPATH, which is green or red by accident and never touches the versions the PR pins (how). CI, where it genuinely ran, is corroboration. - Update type — grouped / patch / minor / major, read from Dependabot's own artifacts (the group name in the head branch, the
Updates X from A to Blines in the body). Cannot be determined with confidence → hold the PR. Do not guess a bump level.
No mergeDeps.verify configured and the base's checks don't cover the change → hold and report. The skill has no basis to call it safe, and says so rather than merging.
4. Merge — directly, with gh pr merge
Gated by mergeDeps.merge (modes); default false — report-only — so merging is opt-in. Once opted in, mergeDeps.confirm (when it asks) decides which merges still wait for a human: a major always does; the low-risk tier the mode allows (patch / minor / grouped) rides the opt-in unless confirm is "always".
- Confirm where it counts, not on every merge. At the default
mergeDeps.confirmof"major", a patch/minor/grouped PR that has cleared assessment merges on the standing opt-in — no second yes — while a major waits for an explicit one."always"restores a confirmation on every merge. The plan/report is shown first either way;confirmnever raises the ceiling or lowers a gate. - Merge directly; never by comment. GitHub removed the
@dependabot merge/squash and mergecomment commands on 27 January 2026. The comment still posts, nothing listens, and nothing errors — a silent no-op that reads as success (why).@dependabot rebaseandrecreateare unaffected. - The merge method comes from the base's ruleset, never a hardcoded default — read
allowed_merge_methodsfor the PR's ownbaseRefName, the same sourcereleasereads; unrestricted → prefer squash, keeping onebuild(deps)commit per group. Add--delete-branch: the branch close-out was Dependabot's and is now this skill's (recipe). - The merge is the authenticated user's act, not a bot's — and for the auto-merged low-risk tier nothing stands between assessment and the merged commit at all. That is exactly why step 3's local verify is the gate,
mergeDeps.mergedefaults tofalse, and a major never auto-merges: a green check run is not evidence a semver-breaking change is safe. - Merging one PR stales the rest — drive the rebase. Dependabot used to cascade that itself. After each merge,
@dependabot rebaseevery remaining selected PR on that base and re-read mergeability before the next one (cascading rebase). - Conflicts →
@dependabot rebaseand report it. Never resolve a dependency conflict by hand — the lockfile is Dependabot's to regenerate. - Held back is an outcome, not a failure. A major bump under
"grouped", an undeterminable update type, a red verify, anunknowncheck list — report each with its reason and move on.
Respect mergeDeps.cap — the most PRs one run may merge.
5. Security alerts
gh api "repos/$owner/$repo/dependabot/alerts" --paginate \
--jq '.[] | select(.state == "open")'
Map each open alert to the Dependabot PR that fixes it, if one exists. Report alerts with no PR behind them — they are the ones nothing is coming for. Alerts with no fix available get reported too, in their own bucket, every run; a vulnerability nobody can patch yet is exactly the thing that should stay visible.
The endpoint needs security_events scope — no access → say the alerts could not be read, and do not silently report zero.
6. Report
- Merged — number, title, update type.
- Held — number and the reason (mode, unknown checks, failed verify, conflict, undeterminable type).
- Alerts — open ones, which have a PR, which have none, which have no fix.
- Findings — a base whose checks don't cover its PRs is a repo problem worth naming, not a per-run footnote. Say it once, plainly.
Guardrails
- Dependabot-authored PRs only, matched on author. Never any other PR, under any circumstance, for any reason. Not a comment, not a label, not a mention in the report.
- Manual invocation only. Never fire proactively — not on a push, not because bumps "look due". Someone asks, or this skill does nothing.
- Plan first; then merge only what the config authorizes. The plan/report is always shown before any merge. A major bump waits for an explicit confirmation even when opted in; the low-risk tier merges on the standing opt-in unless
mergeDeps.confirmis"always". Plan-only triggers ("nur den Plan", "dry run", "just show me", "nicht mergen") → print the plan and the exactghcommands, then stop. - Never opens a PR. In any mode. A missing Dependabot PR is a finding to report, never a gap to fill by hand.
- An empty check list is never green. Absence of a verdict is
unknown, andunknownnever merges. - Never resolve conflicts, never edit a lockfile, never force-push a Dependabot branch. Hand it back with
@dependabot rebase. - Attribution-free — no
Generated with/🤖 line, no session url, no agent self-naming in any comment it posts. - GitHub forge (v1). No GitHub remote /
ghunavailable → stop; never fall back to rawgitplumbing or the API by hand.
Reference
Config keys, the merge modes, the two-bases problem, the gh/git recipes, the assessment checklist, and the reasoning behind the defaults: REFERENCE.md.