wagnerfnds / ralph-for-laravel
Runs a Ralph-style loop (100% PHP) from ralph.md or ralph.json in a Laravel project.
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- symfony/process: ^6.0|^7.0
Requires (Dev)
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^10.0|^11.0
This package is auto-updated.
Last update: 2026-03-21 11:41:14 UTC
README
A 100% PHP, Laravel-friendly "Ralph Loop" runner that reads ralph.md or ralph.json from your project root and iteratively drives an external coding agent (CLI) to complete tasks. It keeps state in .ralph/, optionally runs checks (tests/lint), and can optionally commit changes to Git as each task passes.
No Bash required. Everything is orchestrated in PHP using Laravel + Symfony Process.
Features
- ✅
php artisan ralphruns the loop - ✅ Reads tasks from project root:
ralph.md(Markdown checkboxes)ralph.json(PRD-like JSON)
- ✅ Keeps loop state in
.ralph/:.ralph/prd.json(normalized tasks + pass/fail state).ralph/progress.log(optional log file).ralph/run.lock(prevents multiple concurrent runs)
- ✅ Calls any external agent via CLI (configured with
.env/config/ralph.php) - ✅ Runs configurable checks (tests, lint, etc.)
- ✅ Optional Git workflow (branch + commits per completed story)
Requirements
- PHP 8.2+
- Laravel 10 / 11 / 12
- An external coding agent CLI installed on your machine/CI (e.g.
claude,codex, etc.) - (Optional)
gitinstalled if you enable Git features
Installation
composer require wagnerfnds/ralph-for-laravel
Laravel package discovery should register the service provider automatically.
Quick Start
1) Initialize state directory and create an example ralph.md
php artisan ralph:init --gitignore
This will:
- Create
.ralph/directory - Create
.ralph/progress.log(empty) - Create an example
ralph.mdif it does not exist - Create
.ralph/.gitignoreso the state directory is not committed
2) Add your tasks in ralph.md
Example:
# Ralph TODO - [ ] Create invoices migration - [ ] Create Invoice model + casts - [ ] Build CRUD endpoints for /api/invoices - [ ] Add Feature tests for the CRUD
3) Configure your agent in .env
See the Environment Variables section below.
4) Run the loop
php artisan ralph
You can override iterations:
php artisan ralph --max=15
Dry-run mode (prints what it would do, no agent/checks/git):
php artisan ralph --dry-run
Disable checks:
php artisan ralph --no-checks
Disable Git actions:
php artisan ralph --no-git
Input Formats
ralph.md (Markdown)
- Each checkbox becomes a user story.
- Checked items (
[x]) are treated as already passed (passes=true). - The first markdown heading (e.g.,
# Invoice Feature) is used to generate a friendly branch name.
Example:
# Invoice Feature - [ ] Create invoices migration - [ ] Create Invoice model + casts - [ ] Build CRUD endpoints for /api/invoices - [ ] Add Feature tests for the CRUD
This will create a branch named: ralph/invoice-feature-20250121-143022 (with timestamp to avoid conflicts)
ralph.json (PRD JSON)
If ralph.json exists in the project root, it takes precedence over ralph.md.
Example:
{
"project": "my-laravel-app",
"branchName": "ralph/feature-invoices",
"description": "Invoices feature",
"userStories": [
{
"id": "US-001",
"title": "Create invoices migration",
"description": "As a dev, I want an invoices table so we can store billing data.",
"priority": 1,
"passes": false
}
]
}
Missing fields are normalized automatically:
passesdefaults tofalseprioritydefaults to incremental orderdescriptiondefaults totitleiddefaults toUS-001,US-002, ...
State Directory
By default, the package stores generated state in:
.ralph/
prd.json
progress.log
run.lock
prd.jsonis the normalized PRD used by the loop.run.lockprevents running multiple loops concurrently.
Tip: use php artisan ralph:init --gitignore so .ralph/ is ignored by Git.
Environment Variables (.env)
Add these to your .env:
# Max attempts per story (retries if agent/checks fail) RALPH_MAX_ITERATIONS=10 # If true, enables Git operations (branch/commit). You can still disable per-run with --no-git. RALPH_GIT=true # Optional: create branches prefixed with this value RALPH_BRANCH_PREFIX=ralph/ # Optional: push branch after commits (useful in CI) RALPH_PUSH=false # Your external agent CLI command (must exist on PATH) RALPH_AGENT_CMD=claude # Optional: additional arguments for the agent CLI (space-separated) # Examples: --verbose, --stream, --debug RALPH_AGENT_ARGS="--verbose"
Notes:
RALPH_AGENT_CMDcan be any executable available in your environment:claude,codex, a custom wrapper script, etc.RALPH_AGENT_ARGSallows you to pass additional flags to see the agent's "thinking" process in real-time- The process output streams in real-time with TTY support when available
Configuration (config/ralph.php)
Publish the config:
php artisan vendor:publish --tag=ralph-config
Then edit config/ralph.php:
return [ 'inputs' => [ 'json' => base_path('ralph.json'), 'md' => base_path('ralph.md'), ], 'state_dir' => base_path('.ralph'), 'max_iterations' => (int) env('RALPH_MAX_ITERATIONS', 10), 'agent' => [ 'command' => env('RALPH_AGENT_CMD', 'claude'), 'args' => [], 'timeout' => null, ], 'checks' => [ // Examples: // ['php', 'artisan', 'test'], // ['php', 'artisan', 'pint', '--test'], ], 'git' => [ 'enabled' => (bool) env('RALPH_GIT', true), 'base_branch' => env('RALPH_BASE_BRANCH', null), 'branch_prefix' => env('RALPH_BRANCH_PREFIX', 'ralph/'), 'push' => (bool) env('RALPH_PUSH', false), ], ];
Checks
You can define any number of checks that run after the agent completes an iteration:
'checks' => [ ['php', 'artisan', 'test'], ['php', 'artisan', 'pint', '--test'], ],
If any check fails, the loop stops and reports the failure.
Git Workflow
If Git is enabled, the runner can:
- ensure you are inside a Git repository
- create/switch to a
branchName(from the PRD or generated) - commit changes after each story is marked passed
Disable Git for a single run:
php artisan ralph --no-git
Safety / Notes
- This package executes external commands (agent CLI, checks, git) in your project directory.
- Review changes before merging.
- Consider running in a dedicated branch.
- In CI, use
RALPH_PUSH=trueonly if your credentials are configured safely.
Development (Local Testing)
During development, you can install this package into a Laravel app using a Composer path repository:
{
"repositories": [
{ "type": "path", "url": "../ralph-for-laravel", "options": { "symlink": true } }
],
"require": {
"wagnerfnds/ralph-for-laravel": "dev-main"
}
}
Then:
composer update wagnerfnds/ralph-for-laravel php artisan ralph:init --gitignore php artisan ralph
Roadmap
- Agent adapters (Claude, Codex, etc.) via Contracts
- Per-iteration logs (
.ralph/iterations/0001.json) - Auto-push + PR creation (GitHub CLI)
- Better prompt templates and context strategies
License
MIT