11# Laravel Upgrade Agent
22
3- Disposable Docker image that runs Claude Code autonomously to upgrade any Laravel application.
3+ Disposable Docker image that runs [ Claude Code] ( https://docs.anthropic.com/en/docs/claude-code ) autonomously to upgrade any Laravel application. Point it at a repo, tell it the target version, and it produces an upgrade branch with one commit per phase .
44
5- ## How It Works
6-
7- 1 . Clones your repo, creates an ` upgrade/laravel-{version} ` branch
8- 2 . Runs baseline verification (tests, routes, build)
9- 3 . Drops upgrade plan, checklist, and agent guidelines into the repo
10- 4 . Launches Claude Code with full permissions via a restart loop ("Ralph loop")
11- 5 . Claude works through 6 upgrade phases, committing after each
12- 6 . Pushes the branch when done; artifacts copied to ` /output `
13-
14- ## Usage
5+ ** Multi-arch:** ` linux/amd64 ` and ` linux/arm64 ` — runs on any major cloud/k8s provider.
156
16- ### With Claude Max (recommended)
7+ ## Quick Start
178
18- Generate a token with ` claude setup-token ` , then:
9+ ### Pull from GHCR (recommended)
1910
2011``` bash
21- docker build -t laravel-upgrade-agent .
22-
2312docker run --rm \
24- -e REPO_URL=git@github.com:org/repo .git \
25- -e TARGET_LARAVEL=13 \
13+ -e REPO_URL=git@github.com:your- org/your-app .git \
14+ -e TARGET_LARAVEL=12 \
2615 -e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
2716 -e GIT_SSH_KEY_B64=$( base64 < ~ /.ssh/deploy_key) \
2817 -v ./output:/output \
29- laravel-upgrade-agent
18+ ghcr.io/reyemtech/ laravel-upgrade-agent:latest
3019```
3120
32- ### With Anthropic API Key
21+ ### Build locally
3322
3423``` bash
24+ docker build -t laravel-upgrade-agent .
25+
3526docker run --rm \
36- -e REPO_URL=git@github.com:org/repo .git \
37- -e TARGET_LARAVEL=13 \
38- -e ANTHROPIC_API_KEY= $ANTHROPIC_API_KEY \
27+ -e REPO_URL=git@github.com:your- org/your-app .git \
28+ -e TARGET_LARAVEL=12 \
29+ -e CLAUDE_CODE_OAUTH_TOKEN= $CLAUDE_CODE_OAUTH_TOKEN \
3930 -e GIT_SSH_KEY_B64=$( base64 < ~ /.ssh/deploy_key) \
4031 -v ./output:/output \
4132 laravel-upgrade-agent
4233```
4334
35+ ## How It Works
36+
37+ 1 . Clones your repo, creates an ` upgrade/laravel-{version} ` branch
38+ 2 . Installs dependencies, runs baseline verification
39+ 3 . Runs ** recon** — analyzes package usage, component counts, test suite shape
40+ 4 . Fetches the official Laravel upgrade guide for the target version
41+ 5 . Launches Claude Code inside a restart loop ("Ralph loop")
42+ 6 . Claude works through ** 6 upgrade phases** , committing after each
43+ 7 . Captures before/after dependency snapshots for review
44+ 8 . Pushes the branch (optionally creates a PR with a generated changelog)
45+ 9 . Writes structured results to ` /output `
46+
47+ ## Upgrade Phases
48+
49+ | Phase | What it does |
50+ | -------| -------------|
51+ | 1. Core Framework | ` laravel/framework ` to target version, fix breaking changes |
52+ | 2. First-Party Packages | Horizon, Telescope, Sanctum, Breeze, Pennant, etc. |
53+ | 3. Filament + Livewire | Major version bumps (Filament v4/v5, Flux) |
54+ | 4. Third-Party Composer | Remaining Composer packages |
55+ | 5. NPM + Frontend | Tailwind v4, Vite, build tooling |
56+ | 6. Config Drift | Reconcile config files against latest Laravel stubs |
57+
58+ Unused packages are ** removed** instead of upgraded. Each phase runs verification before committing.
59+
60+ ## Authentication
61+
62+ You need ** one** of:
63+
64+ ### Claude Max (recommended)
65+
66+ Generate a token with ` claude setup-token ` , then pass ` CLAUDE_CODE_OAUTH_TOKEN ` .
67+
68+ ### Anthropic API Key
69+
70+ Pass ` ANTHROPIC_API_KEY ` from [ console.anthropic.com] ( https://console.anthropic.com ) .
71+
4472## Environment Variables
4573
4674| Variable | Required | Default | Description |
4775| ----------| ----------| ---------| -------------|
4876| ` REPO_URL ` | Yes | — | Git clone URL (SSH) |
49- | ` TARGET_LARAVEL ` | Yes | — | Target major version (e.g., ` 13 ` ) |
50- | ` CLAUDE_CODE_OAUTH_TOKEN ` | One of | — | Claude Max token (from ` claude setup-token ` ) |
51- | ` ANTHROPIC_API_KEY ` | One of | — | Anthropic API key (from console.anthropic.com) |
77+ | ` TARGET_LARAVEL ` | Yes | — | Target major version (e.g., ` 12 ` , ` 13 ` ) |
78+ | ` CLAUDE_CODE_OAUTH_TOKEN ` | One of | — | Claude Max token |
79+ | ` ANTHROPIC_API_KEY ` | One of | — | Anthropic API key |
5280| ` GIT_SSH_KEY_B64 ` | Yes | — | Base64-encoded deploy key |
5381| ` GIT_PUSH ` | No | ` true ` | Push branch on completion |
54- | ` UPGRADE_FILAMENT ` | No | ` true ` | Include Filament upgrade phase |
55- | ` MAX_RESTARTS ` | No | ` 5 ` | Max Claude Code restarts |
56-
57- ## Upgrade Phases
58-
59- 1 . ** Core Framework** — ` laravel/framework ` to target version
60- 2 . ** First-Party Packages** — Horizon, Telescope, Sanctum, etc.
61- 3 . ** Filament + Livewire** — major version bumps
62- 4 . ** Third-Party Composer** — remaining packages
63- 5 . ** NPM + Frontend** — deps + build
64- 6 . ** Config Drift** — reconcile against stubs
82+ | ` GH_TOKEN ` | No | — | GitHub token — creates a PR with changelog as body |
83+ | ` BRANCH_SUFFIX ` | No | — | Append to branch name (e.g., ` 2026-02-26 ` -> ` upgrade/laravel-12-2026-02-26 ` ) |
84+ | ` MAX_RESTARTS ` | No | ` 5 ` | Max Claude Code restart attempts |
85+ | ` MAX_TURNS ` | No | ` 200 ` | Max agentic turns per Claude Code session |
6586
6687## Output
6788
6889After a run, the ` /output ` volume contains:
6990
70- - ` baseline.log ` — pre-upgrade test results
71- - ` run-log.md ` — agent decision log
72- - ` checklist.yaml ` — final phase statuses
73- - ` plan.md ` — the upgrade plan used
74- - ` commits.log ` — all commits on the upgrade branch
91+ | File | Description |
92+ | ------| -------------|
93+ | ` result.json ` | Structured outcome — ` success ` or ` incomplete ` , phase counts, elapsed time |
94+ | ` status.json ` | Last status update (poll this for monitoring) |
95+ | ` changelog.md ` | Agent-maintained changelog (also used as PR body) |
96+ | ` before-composer.json ` | Pre-upgrade Composer packages |
97+ | ` after-composer.json ` | Post-upgrade Composer packages |
98+ | ` before-npm.json ` / ` after-npm.json ` | Pre/post NPM packages |
99+ | ` before-versions.txt ` / ` after-versions.txt ` | Pre/post Laravel/PHP versions |
100+ | ` run-log.md ` | Agent decision log |
101+ | ` checklist.yaml ` | Final phase statuses |
102+ | ` commits.log ` | Git log of the upgrade branch |
103+ | ` baseline.log ` | Pre-upgrade verification output |
104+ | ` recon.log ` | Repo analysis output |
105+
106+ ### Monitoring a running container
107+
108+ ``` bash
109+ # Watch status
110+ watch -n5 cat output/status.json
111+
112+ # Check result when done
113+ cat output/result.json
114+ # {"outcome":"success","exit_code":0,"total_phases":6,"completed":6,"failed":0,...}
115+
116+ # Compare dependency changes
117+ diff <( jq -r ' .installed[].name' output/before-composer.json | sort) \
118+ <( jq -r ' .installed[].name' output/after-composer.json | sort)
119+ ```
120+
121+ ## Advanced Usage
122+
123+ ### Auto-create PR
124+
125+ Pass a GitHub token to automatically create a pull request with the changelog as the body:
126+
127+ ``` bash
128+ docker run --rm \
129+ -e REPO_URL=git@github.com:your-org/your-app.git \
130+ -e TARGET_LARAVEL=12 \
131+ -e GH_TOKEN=$GH_TOKEN \
132+ -e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
133+ -e GIT_SSH_KEY_B64=$( base64 < ~ /.ssh/deploy_key) \
134+ -v ./output:/output \
135+ ghcr.io/reyemtech/laravel-upgrade-agent:latest
136+ ```
137+
138+ ### Repeat runs (avoid branch collision)
139+
140+ ``` bash
141+ docker run --rm \
142+ -e REPO_URL=git@github.com:your-org/your-app.git \
143+ -e TARGET_LARAVEL=12 \
144+ -e BRANCH_SUFFIX=$( date +%Y-%m-%d) \
145+ -e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
146+ -e GIT_SSH_KEY_B64=$( base64 < ~ /.ssh/deploy_key) \
147+ -v ./output:/output \
148+ ghcr.io/reyemtech/laravel-upgrade-agent:latest
149+ ```
150+
151+ ### Dry run (no push)
152+
153+ ``` bash
154+ docker run --rm \
155+ -e REPO_URL=git@github.com:your-org/your-app.git \
156+ -e TARGET_LARAVEL=12 \
157+ -e GIT_PUSH=false \
158+ -e CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN \
159+ -e GIT_SSH_KEY_B64=$( base64 < ~ /.ssh/deploy_key) \
160+ -v ./output:/output \
161+ ghcr.io/reyemtech/laravel-upgrade-agent:latest
162+ ```
163+
164+ ### Run on Kubernetes
165+
166+ ``` yaml
167+ apiVersion : batch/v1
168+ kind : Job
169+ metadata :
170+ name : laravel-upgrade
171+ spec :
172+ template :
173+ spec :
174+ containers :
175+ - name : upgrade-agent
176+ image : ghcr.io/reyemtech/laravel-upgrade-agent:latest
177+ env :
178+ - name : REPO_URL
179+ value : " git@github.com:your-org/your-app.git"
180+ - name : TARGET_LARAVEL
181+ value : " 12"
182+ - name : CLAUDE_CODE_OAUTH_TOKEN
183+ valueFrom :
184+ secretKeyRef :
185+ name : upgrade-agent-secrets
186+ key : claude-token
187+ - name : GIT_SSH_KEY_B64
188+ valueFrom :
189+ secretKeyRef :
190+ name : upgrade-agent-secrets
191+ key : deploy-key-b64
192+ - name : GH_TOKEN
193+ valueFrom :
194+ secretKeyRef :
195+ name : upgrade-agent-secrets
196+ key : gh-token
197+ volumeMounts :
198+ - name : output
199+ mountPath : /output
200+ volumes :
201+ - name : output
202+ emptyDir : {}
203+ restartPolicy : Never
204+ backoffLimit : 0
205+ ` ` `
206+
207+ ## How the Agent Stays on Track
208+
209+ - **Recon before action** — ` recon.sh` maps the repo (package usage, component counts, test shape) so the agent can plan ahead
210+ - **Three-file memory** — `plan.md`, `checklist.yaml`, and `run-log.md` survive context compaction and restarts
211+ - **Ralph loop** — if Claude Code exits before the checklist is complete, it restarts with full context (up to `MAX_RESTARTS` times)
212+ - **Loop breaker** — after 3 failed attempts on the same error, the agent logs the failure, marks the phase as `failed`, and moves on
213+ - **Verification gates** — fast verification after every change, full verification before each phase commit
214+ - **No CLAUDE.md overwrite** — upgrade instructions go to `.upgrade/CLAUDE.md`; your project's own `CLAUDE.md` is preserved
75215
76216# # Security
77217
@@ -80,6 +220,6 @@ After a run, the `/output` volume contains:
80220- Container is ephemeral — destroyed after run
81221- Review the upgrade branch before merging
82222
83- ## Ralph Loop
223+ # # License
84224
85- The agent runs inside a restart loop. If Claude Code exits before the checklist is complete, it restarts with full context (reads ` run-log.md ` and ` checklist.yaml ` on startup). Max restarts default to 5.
225+ MIT
0 commit comments