abdelhmed / chimera
Describe a feature in plain language; Chimera plans it with Gemini, generates the files in batches, injects them into your Laravel app with AST, and verifies them with tests.
Requires
- php: ^8.1
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- laravel/prompts: ^0.1.15|^0.2|^0.3
- nikic/php-parser: ^5.0
- symfony/process: ^6.0|^7.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Describe a feature in plain language. Chimera plans it with Google Gemini, generates the files in batches, merges them into your Laravel app through the PHP AST, runs the tests it wrote, and lets you undo everything with one command.
⚠️ Development tool only. Chimera writes PHP files, injects routes and runs migrations. Install it with
--dev; it refuses to run whenAPP_ENV=production. Review every change before committing.
How it works
php artisan chimera:morph "A task manager with projects and due dates"
│
▼
1. Scan AST summary of your models, controllers, routes, tables (never reads .env)
2. Conventions how YOUR project writes validation, responses, models, tests (learned from your AST)
3. Plan Gemini answers with a BLUEPRINT: the list of files and what each is for (no code yet)
4. Confirm you see the plan as a file tree; --dry-run stops here
5. Generate files are written in stages sized by TOKENS, not file count (see "Big plans" below).
Each stage is saved; a truncated reply is split in half; PHP that does not parse is sent
back with the error
6. Audit generated code is checked against your conventions; violations go back to the AI
BEFORE anything touches your disk
7. Apply a checkpoint is opened, originals are backed up, files are created / modified / injected
8. Migrate only the migrations generated by this run
9. Verify the generated tests run on SQLite :memory:; failures are fed back to Gemini (self-healing)
10. Trust deliberate bugs are planted in the generated code to see whether those tests notice
11. Summary file tree with each file's state, trust score, what to `composer require`, how to roll back
What you see in the terminal
Every step is announced as it happens (so VS Code's terminal, CI logs and SSH all read the same), and the plan and the result are drawn as a file tree. While files are being read, a one-line status (analysing app/Models/User.php) is overwritten in place on real terminals.
▸ Scanning your project reading models, controllers, routes and migrations
✔ Scanning your project 13 files analysed with the AST (0.7s)
▸ Loading your conventions how your project writes validation, responses, models and tests
✔ Loading your conventions 16 active conventions, learned from your code just now (0.7s)
▸ Planning with the AI sending your request and the project summary to the AI
✔ Planning with the AI 3 files planned (0.7s)
▸ Reviewing the plan Notes API
app/Http/
├─ Controllers/
│ └─ ○ NoteController.php controller · CREATE — store a note
└─ Requests/
└─ ○ StoreNoteRequest.php request · CREATE — validates title
tests/Feature/
└─ ○ NoteTest.php test · CREATE — store test
✔ Reviewing the plan approved (0.7s)
▸ Generating code 3 files in 2 batch(es)
● generated app/Http/Requests/StoreNoteRequest.php batch 1/2
● generated app/Http/Controllers/NoteController.php batch 1/2
● generated tests/Feature/NoteTest.php batch 2/2
✔ Generating code 3 files generated and syntax-checked (0.7s)
▸ Checking your conventions checking the generated code against 16 conventions
✔ Checking your conventions fixed 3 violation(s) in 1 file(s) before writing anything (0.7s)
▸ Applying changes backing up originals, then writing files
✔ written app/Http/Requests/StoreNoteRequest.php created
✔ written app/Http/Controllers/NoteController.php created
✔ written tests/Feature/NoteTest.php created
✔ Applying changes 3 created, 0 modified, 0 injected (0.7s)
▸ Verifying with tests running the generated tests on an isolated database
✔ Verifying with tests tests pass (0.7s)
▸ Stress-testing the tests planting deliberate bugs in the generated code to see whether the tests notice
✔ Stress-testing the tests trust score 75% (3 of 4 bugs caught) (0.7s)
app/Http/
├─ Controllers/
│ └─ ✔ NoteController.php created
└─ Requests/
└─ ✔ StoreNoteRequest.php created
tests/Feature/
└─ ✔ NoteTest.php created
Trust score 75%: the tests caught 3 of 4 deliberate bugs.
These bugs went unnoticed, so the tests do not cover them yet:
- app/Http/Controllers/NoteController.php:14 changed status code 403 to 404 in abort()
Set NO_COLOR=1 to disable colours. This sample is the real pipeline and renderer run with a scripted AI: the AI's first controller broke three of the project's conventions, so it was sent back and fixed before anything was written.
Installation
composer require --dev abdelhmed/chimera
php artisan vendor:publish --tag=chimera-config # optional
GEMINI_API_KEY=your-key CHIMERA_GEMINI_MODEL=gemini-2.5-flash # any Gemini model your key can use
Requires PHP 8.1+ and Laravel 10, 11 or 12.
Commands
| Command | What it does |
|---|---|
chimera:morph {prompt?} |
Run the full flow. Options: --dry-run, --yes, --no-migrate, --no-tests, --no-trust, --no-conventions, --max-tokens=N |
chimera:resume {id?} |
Continue a saved plan or an interrupted run without planning or regenerating again |
chimera:learn |
Learn your project's conventions, choose which to keep, save .chimera/conventions.json (--show only prints, --yes saves all) |
chimera:history {id?} |
List runs (or the files of one run) |
chimera:rollback {id?} |
Roll back the run's migrations, restore modified files, delete created ones (default: latest) |
Skeletons: the AI writes only what is specific
Every new file of a known kind starts from a fixed skeleton that Chimera builds itself: the namespace, the class declaration, the base class and the standard traits. The AI returns only the new members (properties, methods, use imports), and Chimera injects them into the skeleton with the same AST injector it uses for your existing files.
| New file | The skeleton already has |
|---|---|
| Model | namespace, class X extends Model, use HasFactory |
| Controller / Form Request / API Resource | namespace, extends Controller / FormRequest / JsonResource |
| Job | implements ShouldQueue and the four standard job traits |
| Event | the three standard event traits |
| Listener / Middleware / Service / Policy | namespace and the class |
| Factory / Seeder / Notification | namespace and extends Factory / Seeder / Notification |
| Enum | enum X: string (the AI may choose int) and its cases |
| Migration | return new class extends Migration (the AI writes up() and down()) |
| Routes file | the header and use Route (the AI writes only the routes) |
Tests, views, config and other free-form files have no skeleton; the AI writes them whole.
- It still shows as
created. A skeleton file is a new file, so a missingroutes/api.phpor a newVendormodel is reported as created, whileUser.php(which exists) is reported as injected. - The skeleton follows your project.
declare(strict_types=1);is added to new files underapp/only if your conventions say so, andHasFactoryonly if your models use it. - Safe against a wandering AI. If the AI repeats the namespace,
extendsor a trait, the merge ignores the duplicates. If its members cannot be merged, the answer is sent back for a retry before anything is written. - Less to write, less to pay for. The skeleton is not generated by the model, so the output is shorter. The estimator assumes about 30% fewer tokens for a skeleton file; that is a guess, not a measurement.
- Consistent by construction. Every model starts the same way, so you no longer get
casts()in one file and$castsin the next for the shell parts. - Set
generation.skeletonstofalseto go back to whole files from the AI.
Big plans: 40, 60, 80 files
One reply can only hold so much, so Chimera never asks for everything at once.
- Estimate first. After the plan, before you approve it, you see what it will cost:
Estimated generation: 20 AI call(s), about 71k input + 38k output tokens. It is a heuristic (about 3.5 bytes per token, typical size per file type), so expect ±40%, and retries and test healing are extra.--dry-runshows it without spending anything;--max-tokens=150000stops the run when the estimate is above your limit. SetCHIMERA_PRICE_INPUT/CHIMERA_PRICE_OUTPUT(per million tokens) to also see a cost. - Stages sized to the model. Files are generated in dependency order (migrations, models, ... tests last). A stage is closed when it reaches
generation.files_per_batchfiles orgeneration.output_tokens_per_batchestimated tokens, whichever comes first, so 60 files become as many calls as needed. - Adaptive. If the model still cuts a reply short, that batch is split in half and every following batch gets smaller.
- Patient. On 429, 5xx or network errors Chimera waits and tries again: 2s, 4s, 8s, 16s... (capped, with jitter, and never less than the wait the model asks for), up to
retry.attemptstimes. Permanent errors such as a bad key fail immediately. - Plan saved immediately. Planning is the slowest single call (minutes on a thinking model). Its result is saved the moment it arrives, so
--dry-run, answering "no", or a crash never costs it twice:php artisan chimera:resumereviews the saved plan and generates it. - Resumable. After every stage the plan and the files generated so far are saved to
storage/chimera/runs/. If a stage fails for good, nothing is lost:
php artisan chimera:resume # continues the latest unfinished run
php artisan chimera:resume run_20261024_101500_ab12
Resuming skips scanning, planning and the files that are already done, so you only pay for what is left. Nothing has been written to your project at that point; files are applied only after every stage has been generated.
If it feels slow
Most of the wait is the model, not Chimera. Many Gemini models think before answering and bill that thinking as output tokens, which adds minutes to a 40-file plan. Planning and code generation are structured tasks that rarely need it:
CHIMERA_THINKING_BUDGET=0 # Gemini 2.5 Flash: thinking off (2.5 Pro cannot be turned off; its minimum is 128) CHIMERA_THINKING_LEVEL=low # newer models: minimal | low | medium | high
Set one of them (not both) and compare. They are off by default because not every model accepts every option. A shorter plan also helps: ask for a smaller feature first, then extend it. After the plan, Chimera forecasts the generation time from how fast the plan itself was produced.
Trust Score: do the AI's tests actually test anything?
Green tests are not proof. AI-written tests often pass because they assert very little. After the generated tests pass, Chimera plants small deliberate bugs in the generated application code (through the AST) and re-runs the tests for each one. A bug the tests notice is killed; a bug nobody notices survived. The score is the share killed.
| Deliberate bug | Example |
|---|---|
| comparison flipped | !== becomes ===, >= becomes < |
| logic flipped | && becomes || |
| negation removed | if (! $task->isOpen()) becomes if ($task->isOpen()) |
| status code changed | json($x, 201) becomes 200, abort(403) becomes 404 |
| validation weakened | 'title' => 'required|string' becomes 'string' |
| guard deleted | $this->authorize(...), abort_if(...) removed |
Survivors are listed with file, line and what was changed, so you know exactly which test is missing. Notes:
- It only measures code Chimera created or fully rewrote; INJECT fragments, migrations, routes, views and tests are not mutated.
- Some mutants are equivalent (the change cannot alter behaviour), so 100% is not always reachable. Treat the score as a guide, not a grade.
- It adds one (fail-fast) test run per bug, capped by
trust.max_mutants(default 20) andtrust.mutant_timeout. Disable with--no-trustortrust.enabled. - Your files are changed on disk for a moment. The original is restored in a
finallyblock and also kept instorage/chimera/mutation-recovery.json; if the process is killed mid-run, the nextchimera:morphrestores it first.
Conventions: code that looks like your code
php artisan chimera:learn reads your project through the AST and writes down what it finds, for example:
validation.form-request Validate input with dedicated Form Request classes; never validate inline in controllers. (8 of 8 store/update actions)
architecture.services Put business logic in service classes under app/Services; keep controllers thin. (4 classes)
php.strict-types Start every PHP file under app/ with declare(strict_types=1); (12 of 12 files)
tests.pest Write tests with Pest: it()/test() closures and expect() assertions. (3 of 3 test files)
- A habit becomes a convention only when it clearly dominates (at least 70% of a meaningful sample); mixed projects get no rule rather than a wrong one.
- The command lets you switch conventions off, and saves
.chimera/conventions.json. Commit it: your team shares the same rules. Add your own by hand with"source": "custom"; relearning keeps your choices and custom rules. - Without the file, Chimera learns on the fly in memory, so the first run already benefits.
- Every convention is shown to the AI in the plan and in every code request. The ones that can be checked mechanically (Form Requests instead of inline validation, no queries in controllers,
strict_types, return types, Pest vs PHPUnit) are also verified on the generated code; violations are sent back to the AI once, with the exact problem and its own code, before any file is written. Anything still wrong is listed in the summary. - Disable with
--no-conventionsorconventions.enabled.
INJECT: changing files you already own
The model never rewrites your existing classes blindly. For existing PHP files it returns a skeleton with only the new members, and Chimera merges it through the AST, keeping your formatting:
- new methods, properties, constants and traits are added; members that already exist are left untouched
- list properties such as
$fillableget only the missing entries useimports are merged without duplicates or alias clashes- route statements are appended to the routes file, skipping any that are already there
- injecting the same snippet twice changes nothing
Safety
- Paths from the model are validated: only
app/ routes/ database/ resources/ tests/ config/ lang/are writable;.., absolute paths,.env*,vendor/,storage/,.git/are always rejected. - Existing files are never silently overwritten: a
CREATEon an existing path becomes a visibleMODIFY, and every touched file is backed up instorage/chimera/checkpoints/<id>/. - If applying fails half-way, the checkpoint is rolled back automatically.
- Migrations run with
--path, so your own pending migrations are never executed. - Chimera never edits
.envorcomposer.json; it tells you what to add.
Architecture
src/
├── Contracts/ LlmClient, ProjectFiles, ProjectScanner, Injector, CheckpointStore, MigrationRunner, TestRunner, Reporter, ProgressTracker, ConventionStore, MutationRecovery
├── Data/ Immutable DTOs: FileSpec, Blueprint, GeneratedFile, ExecutionReport, TestResult, Checkpoint
├── Enums/ FileAction (CREATE/MODIFY/INJECT), FileType (also defines generation order)
├── Analysis/ ClassSummarizer, RouteSummarizer, AstProjectScanner, PhpSyntax (php-parser)
├── Planning/ BlueprintPlanner, BlueprintParser, BlueprintNormalizer, BatchPlanner
├── Generation/ FileGenerator, BatchPromptBuilder, GeneratedFileValidator
├── Skeleton/ SkeletonCatalog (the shell of each kind of file), SkeletonRenderer, SkeletonExpander
├── Injection/ ClassMembersInjector, RouteInjector, ImportMerger, FormatPreservingEditor, InjectorRegistry
├── Application/ ChangeApplier
├── Checkpoint/ FilesystemCheckpointStore, RollbackService
├── Verification/ ProcessTestRunner, HealingService
├── Conventions/ ConventionLearner + 8 AST Detectors, ConventionChecker + 6 Checks, FilesystemConventionStore
├── Estimation/ TokenEstimator, FileSizeEstimator, GenerationEstimator (the token forecast shown before you approve)
├── Runs/ RunState, FilesystemRunStore (what makes chimera:resume possible)
├── Trust/ MutantGenerator + 6 Operators, MutationRunner (restores files), TrustAnalyzer, mutation recovery
├── Llm/ GeminiClient (the only class that knows Gemini's REST format), RetryingLlmClient + RetryPolicy (backoff for any model)
├── Pipeline/ MorphPipeline + one small Stage per step (Scan, Plan, Confirm, Generate, Apply, Migrate, Verify, Summary)
└── Console/ Thin commands + ConsoleReporter (Laravel Prompts)
Patterns: Pipeline / Chain of Responsibility (stages), Strategy / Adapter (LlmClient, TestRunner, CheckpointStore, MigrationRunner), Registry (InjectorRegistry), Visitor via nikic/php-parser, immutable DTOs. Everything outside Llm/, Console/, Migration/ and ChimeraServiceProvider is framework-agnostic and unit-tested with fakes.
Customising
- Prompts:
php artisan vendor:publish --tag=chimera-promptscopiesblueprint.system.stub,generate.system.stubandheal.system.stubtostubs/chimera/. Edit them to enforce your house style (e.g. "always use Actions, never Services"). - Another model: implement
Abdelhmed\Chimera\Contracts\LlmClient(one method) and bind it in your own service provider. - Another checkpoint strategy (e.g. git commits): implement
CheckpointStore. - Bigger / smaller batches:
generation.files_per_batch. Lower it if your model has a small output limit.
Known limits
-
Token estimates are heuristics. If your prompt is mostly non-Latin text or your files are unusually big, adjust
generation.output_tokens_per_batch; a wrong guess is corrected automatically (the batch is split and later batches shrink). -
The retry notice is printed while a Laravel Prompts spinner may be running; I could not check how that looks in a real terminal.
-
Conventions are statistical: a habit seen in too few files is ignored, and a learned rule can be wrong for new code. Review
.chimera/conventions.jsononce. -
Trust scoring measures the generated tests against six kinds of bugs, not every possible bug.
-
Route summaries do not resolve
Route::groupprefixes. -
Healing only fixes files the model can see in the test output; failures caused by missing packages or
.envvalues need your help. -
If
routes/api.phpdoes not exist (Laravel 11+), Chimera creates it but warns you to register it (php artisan install:api).
Testing the package
composer install vendor/bin/pest
License
MIT