auto-merge
Skill776 starsChanged 2 months agoMITView on GitHub
Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts
- Reads credentials
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 4 are distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Very specific: line 19
Read memory/MEMORY.md and memory/watched-repos.md for repos to target.
- Very specific: line 20
Read the last 2 days of memory/logs/ to avoid re-logging PRs already merged.
- Very specific: line 35
Checks: every entry in statusCheckRollup has conclusion in {SUCCESS, NEUTRAL, SKIPPED}.
- Very specific: line 71
If still UNKNOWN after the retry, skip the PR with reason UNKNOWN-persistent and let the next run retry.
- Very specific: line 83
If AUTO_MERGE_DRY_RUN=1, log DRY_RUN:would-merge #N and continue — do NOT invoke merge.
- Distinctive: line 96
Send a notification: only when at least one real (non-dry-run) merge succeeded or at least one PR has hit the retry cap (5b below).
- Very specific: line 125
If zero qualified, include a verdict breakdown: AUTO_MERGE_SKIP: 0/Z qualifying (behind=B blocked=L failing=F draft=D already-queued=U …
- Distinctive: line 133
Never merge a PR whose author is not allowlisted, even if every other gate is green.
- Very specific: line 134
Never bypass the size cap without the explicit auto-merge-large label (set by a human, not a bot).
- Very specific: line 135
Never auto-retry a MERGE_FAIL within the same run — if the first merge attempt fails, log and move on.
- Distinctive: line 136
After 3 failed attempts across runs, stop retrying that PR.
- Distinctive: line 137
Do not modify PR state other than merging (no comments, no label edits, no branch updates).
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- autoresearch: variation C — safety-hardened (author allowlist, size cap, UNKNOWN retry, fork block, dry-run mode) so an autonomous agent with merge credentials cannot accidentally ship a hostile or oversized PR -->${var} — Repo (owner/repo) to target. If empty, uses every repo in memory/watched-repos.md. Env:
AUTO_MERGE_DRY_RUN=1logs intent without merging.MAX_AUTO_MERGE=Ncaps merges per run (default 3).
Merge open PRs that are fully green and pass an explicit safety policy. The policy exists because this skill runs autonomously with write access — a bug in the gate is a bug that ships to main.
Read memory/MEMORY.md and memory/watched-repos.md for repos to target. Read the last 2 days of memory/logs/ to avoid re-logging PRs already merged.
Safety policy
A PR merges only when every one of the following holds:
- Author allowlist:
author.loginis one ofdependabot[bot],renovate[bot],github-actions[bot], OR appears under a## Trusted Authorssection in memory/watched-repos.md. No allowlist → only the three bot logins are eligible. - Size cap:
additions + deletions ≤ 500. Override by applying the labelauto-merge-largeon the PR. - Base branch:
baseRefNameismainormaster. Refuse any other target. - Not a fork:
isCrossRepository == false(fork CI can be tampered with). - Not draft:
isDraft == false. - Not already queued:
autoMergeRequest == null(avoid fighting GitHub's native auto-merge if a human enabled it) and the PR is not already in a merge queue (mergeQueueEntry == null, checked in step 2b). - No opt-out label: none of {
do-not-merge,wip,hold,needs-review,blocked} present. - Mergeable state:
mergeStateStatus == "CLEAN"(this is stricter thanmergeable == "MERGEABLE"— CLEAN additionally requires branch-protection gates to be satisfied). - Reviews:
reviewDecision != "CHANGES_REQUESTED". - Checks: every entry in
statusCheckRolluphasconclusionin{SUCCESS, NEUTRAL, SKIPPED}. AnyFAILURE,TIMED_OUT,CANCELLED,PENDING, ornullconclusion disqualifies the PR. - Retry cap: this PR has been attempted fewer than 3 times. A PR that has hit
MERGE_FAILthree times across runs is paused — repeated failure on a CLEAN-looking PR usually means something subtle (a required check that didn't surface, branch-protection drift, token scope drift). Surface it and stop looping.
Steps
- Bootstrap state — per-PR retry counter lives in
memory/topics/auto-merge-state.json:
Schema:mkdir -p memory/topics [ -f memory/topics/auto-merge-state.json ] || echo '{"prs":{},"last_run":null}' > memory/topics/auto-merge-state.json
PR keys are{ "last_run": "2026-05-23T08:00:00Z", "prs": { "owner/repo#123": { "first_seen": "2026-05-21T10:00:00Z", "last_attempt": "2026-05-23T08:00:00Z", "attempts": 2, "last_outcome": "merge_failed", "last_error": "Pull Request is in unstable state" } } }<owner>/<repo>#<number>so state survives multi-repo runs. Cap to 50 most-recent entries (LRU bylast_attempt). Validate withjq emptyafter write; restore from.bakon failure. - List open PRs for each watched repo with the full field set:
gh pr list -R owner/repo --state open --json number,title,author,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,autoMergeRequest,isCrossRepository,labels,additions,deletions,baseRefName Handle UNKNOWN state — GitHub computes
mergeStateStatuslazily. If a PR returnsUNKNOWN, sleep 3 seconds and re-query once:sleep 3 && gh pr view NUMBER -R owner/repo --json mergeStateStatus,mergeable,statusCheckRollupIf still UNKNOWN after the retry, skip the PR with reason
UNKNOWN-persistentand let the next run retry.2b. Skip PRs already in a merge queue. A PR this skill queued on an earlier run is still open, so it would be merged again, bump
attempts, and hit the retry cap while it waits.gh pr listdoes not expose the queue, so ask GraphQL per PR:gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){pullRequest(number:$n){mergeQueueEntry{state}}}}' \ -f o=owner -f r=repo -F n=NUMBER --jq '.data.repository.pullRequest.mergeQueueEntry.state // ""'A non-empty state (e.g.
QUEUED,AWAITING_CHECKS,MERGEABLE) skips the PR asSKIP:already-queued:<state>. A failed lookup skips it asSKIP:merge-queue-unknown. Neither counts as an attempt: do not touch itsattempts, and leave its state entry as is.- Apply the safety policy to each PR. Record a verdict for every PR: either
MERGEorSKIP:<specific-reason>. Reasons must name the failing gate — e.g.SKIP:author-not-allowlisted:contributor123,SKIP:size-cap:823-lines,SKIP:mergeStateStatus=BEHIND,SKIP:label:do-not-merge,SKIP:check-failed:lint,SKIP:retry-cap:3-attempts. Vague reasons likeSKIP:not-readyare not acceptable. - Merge qualifying PRs, up to MAX_AUTO_MERGE (default 3):
- If
AUTO_MERGE_DRY_RUN=1, logDRY_RUN:would-merge #Nand continue — do NOT invoke merge. - Otherwise:
A zero exit is not proof of a merge: on a repo with a merge queue,gh pr merge NUMBER -R owner/repo --squash --delete-branchgh pr mergeexits 0 after only adding the PR to the queue. Confirm with GitHub before counting or reporting it:
Onlygh pr view NUMBER -R owner/repo --json state,mergeCommit --jq '[.state, (.mergeCommit.oid // "")] | @tsv'MERGEDwith a commit SHA is a merge: report that SHA, never one inferred from the command output.OPENmeans it was queued: logQUEUED #N, recordlast_outcome: queued, and report it as queued, not merged. Any other result, or a failed lookup, is logged asMERGE_UNCONFIRMED #Nand reported as such. Both count towardMAX_AUTO_MERGE: a merge was attempted and GitHub accepted the command, so the cap still bounds how much this run can send tomain. Incrementstate.prs["<owner>/<repo>#<N>"].attemptson every attempt regardless of outcome. Setfirst_seenif absent. Reset to 0 (delete the entry) for PRs that no longer appear in the open list (already merged or closed since the last run). If the merge fails (non-zero exit), capture stderr and logMERGE_FAIL #N: <stderr>. Recordlast_outcome: merge_failedandlast_error: <stderr ≤200 chars>on the state entry. A failed merge does NOT count toward the per-runMAX_AUTO_MERGEcap — continue to the next qualifying PR. A PR whoseattemptshas reached 3 is filtered out in step 3 withSKIP:retry-cap:3-attempts; surface it in step 5b instead of retrying.
- If
Send a notification only when at least one real (non-dry-run) merge succeeded or at least one PR has hit the retry cap (5b below). No merges and no cap hits → no notification, just a log entry.
5a. At least one merge succeeded:
*Auto Merge — ${today}* Merged N PR(s) on owner/repo: - #123: PR title (+45/-12, by @author) — squash merged abc1234 (the mergeCommit SHA GitHub returned) Queue cleared. Self-improve cycle unblocked.5b. Retry cap reached on ≥1 PR (
AUTO_MERGE_RETRY_CAP) — include in the same message if both fire, otherwise stand-alone:*Auto Merge — retry cap* Hit retry cap (3 attempts) on: - owner/repo#40 — last error: "Pull Request is in unstable state" Stopping auto-merge attempts on this PR. Investigate manually.Dedup: suppress re-notify if the exact same set of cap-hit PR keys already notified within the last 24h (grep
memory/logs/for priorAUTO_MERGE_RETRY_CAPentries).- Persist state — write the updated
memory/topics/auto-merge-state.json. Updatelast_runto current timestamp. Validate withjq empty; on failure restore from a.bakwritten before this run. - Log to memory/logs/${today}.md under an
### auto-mergeheading:Mode: live | dry-runRepo(s): listMerged:#N title @author +A-D SHAper line, confirmedMERGEDby GitHubQueued:#Nper line (merge queue accepted it; not merged yet)Skipped:#N SKIP:<reason>per lineRetry-capped:owner/repo#N — <last_error>per line (empty if none)Totals:merged=X queued=Q qualified=Y considered=Z retry_capped=R- If zero qualified, include a verdict breakdown:
AUTO_MERGE_SKIP: 0/Z qualifying (behind=B blocked=L failing=F draft=D already-queued=U author-blocked=A size-blocked=S retry-capped=R)
Network note
gh authenticates via the workflow's GITHUB_TOKEN — no curl needed. If gh pr merge fails with Resource not accessible by integration, the workflow token lacks merge permission on that repo; log once and notify at most once per 7 days (check memory/logs/ for prior notification) to avoid alert spam.
Constraints
- Never merge a PR whose author is not allowlisted, even if every other gate is green.
- Never bypass the size cap without the explicit
auto-merge-largelabel (set by a human, not a bot). - Never auto-retry a
MERGE_FAILwithin the same run — if the first merge attempt fails, log and move on. - After 3 failed attempts across runs, stop retrying that PR. Surface it once via the retry-cap notification and let the operator investigate.
- Do not modify PR state other than merging (no comments, no label edits, no branch updates).
Running this as an agent-shipping loop
To close the loop on PRs the agent itself opens (from feature, external-feature, self-improve, etc.), add the agent's GitHub identity under a ## Trusted Authors section in memory/watched-repos.md:
## Trusted Authors
- aeon-bot
- claude-code[bot]Once allowlisted, agent PRs flow through the same safety policy as bot PRs and get auto-merged on green CI. The retry cap protects against runaway behavior on a stuck PR.
Other versions4 projects use a version of this.
Word for word · 3
- aeonfun/aeon★ 782100% same
- MiroShark/miroshark-aeon★ 19100% same
- aaronjmars/miroshark-aeon★ 18100% same
Edited · 0
None found.
Wrote their own version · 1
- getsentry/outpost★ 5same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository89 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- llms.txtllms.txt
- aeonSkill
- action-converterSkill
- aeon-doctorSkill
- aeon-updateSkill
- anatomySkill
- arc-studioSkill
- articleSkill
- autoresearchSkill
- auto-workflowSkill
- base-mcpSkill
- bd-radarSkill
- changelogSkill
- competitor-monitorSkill
- compute-resellSkill
- cortx-reliabilitySkill
- create-proveSkill
- create-skillSkill
- defi-overviewSkill
- deploy-prototypeSkill
- deploy-uni-hookSkill
- digestSkill
- distribute-tokensSkill
- executor-mcpSkill
- featureSkill
- feedback-builderSkill
- fetch-tweetsSkill
- finance-district-mcpSkill
- fleet-controlSkill
- fork-fleetSkill
- github-monitorSkill
- github-trendingSkill
- glim-mcpSkill
- heartbeatSkill
- higgsfieldSkill
- hunter-22Skill
- idea-forgeSkill
- idea-pipelineSkill
- inbox-triageSkill
- install-skillSkill
- investigation-reportSkill
- last30Skill
- memory-flushSkill
- mention-radarSkill
- miroshark-matchdaySkill
- monitor-polymarketSkill
- narrative-convergenceSkill
- narrative-trackerSkill
- onchain-monitorSkill
- operator-scorecardSkill
- pack-submitSkill
- picks-trackerSkill
- pm-manipulationSkill
- posthog-errorsSkill
- price-alertSkill
- pr-reviewSkill
- pr-triageSkill
- remotionSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

