When a dependency breaks your code, Patchery fixes it and proves the fix.
Your project runs on other people's code. When they change how it works, your app stops working. Patchery finds what broke, rewrites the lines that need rewriting, and checks the result against your own tests before it shows you anything — and throws its own work away if that check goes badly.
Dependabot bumps the version and leaves you with a red build. This action goes one step further: it reads the changelog, migrates the call sites to the new API, runs your tests, and opens a pull request only if those tests pass.
Tell an AI to make your tests pass and it can pass them by deleting them. It can soften a check, stub out the function, or edit the very file that decides what "working" means. Every one of those makes a broken project look fixed.
So this action never reads the agent's own account of what it did. It looks at the files themselves, throws the whole attempt away if anything off-limits moved, and re-runs your test command itself.
1. Run the tests -> Is it actually broken? If it passes, do nothing.
2. Run the agent -> Read the changelog, migrate the call sites.
3. Check with git -> What changed? Touched a test or node_modules? REVERT.
4. Run the tests again -> Never trust the agent's word; measure it.
5. Open a PR -> Only if steps 1-4 came back clean.
Steps 3 and 4 are the point of this project.
Blocked changes — if any of these show up in the diff, the whole run is reverted and no PR is opened:
- test files —
*.test.*,*.spec.*,test/,tests/,__tests__/,__mocks__/ node_modules/.github/- lockfiles —
package-lock.json,pnpm-lock.yaml,yarn.lock - anything outside
target-dir— unless you list it inallowed-paths - any deleted tracked file — unless you set
allow-deletions: true
The last two exist because a migration edits call sites; it does not delete files or wander into unrelated directories. Anything that does is either the agent exceeding its brief or another process dirtying the tree mid-run — and Patchery cannot tell those apart, so it treats both the same way: show you the diff, revert, open nothing. Whatever it blocks, it names, with the input that would allow it.
That guard lives in scripts/guard.mjs as pure functions, and
is covered by scripts/selftest.mjs, which runs on every
push, offline, in about a second, with no API key.
One pull request, opened end to end with no human in the loop:
patchery-dev/Patchery#2 — fake-lib
went 1.x → 2.0.0 and made formatPrice's second argument required. The action ran
the tests (failed), read the changelog, changed one line in one file, checked the
diff against the guard, ran the tests again (passed), and opened the pull request.
The first human to see it was the reviewer.
| files | lines | tests | turns | cost at list rates |
|---|---|---|---|---|
| 1 | +1 −1 | failed → passed | 10 | $0.1958 |
And the same job by hand, on projects we don't own — no agent involved in either, they are here because doing the work manually is how we learned what the automated version has to survive:
| Repo | What changed |
|---|---|
| ianarawjo/ChainForge#416 | OpenAI SDK v3 → v4: three call sites, plus response unwrapping and APIError handling |
| ToolJet/ToolJet#17829 | Gemini plugin off @google/generative-ai, end-of-life since 30 Nov 2025 |
All three are open. None has been merged. We will change this sentence the day that changes — and patchery.dev checks it against the GitHub API every time someone loads the page, so if it ever stops being true the site says so before we do.
Repo → Settings → Secrets and variables → Actions → New repository secret:
| Secret | Required | Purpose |
|---|---|---|
ANTHROPIC_AUTH_TOKEN |
yes | The agent's API key / token |
ANTHROPIC_BASE_URL |
no | Only if you use a compatible custom endpoint |
ANTHROPIC_MODEL |
no | Model name (default if empty) |
Never inline the key in a workflow file — Actions logs can be public.
Repo → Settings → Actions → General → Workflow permissions → tick "Allow GitHub Actions to create and approve pull requests".
Without it the agent runs, fixes your code, verifies the tests — and then the last step fails with:
GitHub Actions is not permitted to create or approve pull requests.
The work is done at that point but has nowhere to go, and the error does not say which setting to change. If your repository belongs to an organisation, the same switch also has to be on at the org level (Organisation settings → Actions → General); a repository cannot grant itself more than the org allows.
Save examples/self-maintain.yml in your own repository
as .github/workflows/self-maintain.yml.
Repo → Actions → self-maintain → Run workflow, then fill in which package broke and which directory to fix.
There is deliberately no scheduling or automatic package scanning in this version. You decide which package gets handled. Full automation is the next step.
| Input | Default | Description |
|---|---|---|
package |
(required) | Name of the package that broke |
target-dir |
. |
Project directory to fix |
test-command |
npm test |
Verification command (runs before and after) |
changelog |
"" |
Changelog path or URL; empty means the agent looks for it |
allowed-paths |
"" |
Paths outside target-dir the run may still change, one per line |
allow-deletions |
false |
Allow the run to delete tracked files |
max-turns |
25 |
Agent turn limit — your cost brake |
extra-instructions |
"" |
Extra instructions for the agent |
require-failing-baseline |
true |
Skip the agent entirely if tests already pass |
dry-run |
false |
Only measure the baseline test run |
node-version |
22 |
Node version your tests run on (Node 20 is end-of-life) |
anthropic-auth-token |
(required) | Pass from secrets |
anthropic-base-url |
"" |
Pass from secrets |
anthropic-model |
"" |
Pass from secrets |
| Output | Description |
|---|---|
changed |
true / false — whether files actually changed |
tests-passed |
Whether tests passed after the fix |
files |
Changed files, one per line (ready for add-paths) |
pr-body-file |
Path to the generated PR body (ready for body-path) |
summary |
One-line summary |
test-fixture/ is broken on purpose: fake-lib went from 1.x to
2.0.0 and formatPrice(amount) became formatPrice(amount, currency).
test-fixture/app.js still makes the old call, so npm test fails.
Go to Actions → self-maintain (demo) → Run workflow. Expected result: the agent
fixes app.js, the tests pass, and a PR appears on the self-maintain/fake-lib branch.
Do not merge the demo PR — the fixture has to stay broken, otherwise later runs will correctly report "nothing to fix".
To try it locally, without Actions:
npm install
SMA_PACKAGE=fake-lib \
SMA_TARGET_DIR=test-fixture \
SMA_CHANGELOG=node_modules/fake-lib/CHANGELOG.md \
ANTHROPIC_AUTH_TOKEN=... \
node scripts/agent.mjsTrue today
- Anyone can install it right now, and read every line of it. MIT licensed.
- One pull request opened start to finish with no human in the loop.
- The off-limits check is pure, unit-tested, and runs on every push with no AI.
- Keys and passwords are redacted before anything reaches a log or a PR body.
- Works on Node / npm projects. You decide what counts as a passing test.
Not yet
- Nothing hosted. It runs in your Actions runner, not ours.
- No revenue, no users, no logo wall. Nobody is paying for this.
- No open-source pull request accepted yet — two are waiting.
- One package per run. It does not resolve cascading breakages.
- Whether a break reproduces at all can depend on the runtime. Your
node-versiondecides which Node the tests run on, and a break that is real on an older one can pass on a newer one — we hit exactly that, and the run said "nothing to fix". It now flags the case instead of staying silent, but it cannot know which runtime you meant. - Ecosystems beyond Node / npm are plausible, but nobody has proven it.
- Cost varies per run;
max-turnsis the brake. - The resulting PR needs human review. There is no auto-merge, and there should not be.
- The agent sees your source code. Check what your model provider does with it before you point this at anything private.
We would rather you audit this than trust it. Every number above comes from a run you can open and read.
MIT
