paider/paider

A PHP-native AI coding agent.

Maintainers

Package info

github.com/shoemoney/paider

Homepage

pkg:composer/paider/paider

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 1

v0.1.0 2026-08-03 04:22 UTC

This package is auto-updated.

Last update: 2026-08-03 04:33:03 UTC


README

🐘 Paider

A PHP-native AI coding agent β€” that lives inside your Laravel app. πŸ€–

status php license packagist ci tests cold start

Built on Laravel Zero Β· Laravel Prompts Β· Termwind Β· MCP PHP SDK (v0.2)

🚦 Status, honestly

Alpha. The commands work, the ledger reports real money, and it has talked to real models. What it has never done is drive an end-to-end edit in someone else's repo.

Built in public from commit one, wrong turns left in. Here is precisely what that means today:

state evidence
🧱 v0.1 command surface βœ… built paider chat, commit, cost, config:provider, config:show all register and run
πŸ”§ six native tools βœ… built read_file, write_file, patch_file, run_shell, git, artisan
πŸ—„οΈ SQLite event log + cost ledger βœ… built append-only, ledger is a pure projection; stored in .paider/ (gitignored locally)
πŸ§ͺ test suite βœ… 175 passing, 630 assertions hermetic by default; 3 live tests via vendor/bin/pest --group=live
🌐 talking to a real LLM βœ… verified live OpenRouter, Anthropic, xAI; cost ledger reconciles to provider usage
πŸ“¦ published on Packagist βœ… published paider/paider at https://packagist.org/packages/paider/paider
πŸ“¦ curl | sh installer ⬜ not built the binary is measured, the installer is not written
🏷️ tagged release βœ… v0.1.0 composer require paider/paider resolves without a stability flag

Do not install this expecting a working agent. The wiring is real and tested; the last mile β€” an actual API key, an actual model, an actual edit landing in your repo β€” is unproven.

πŸ™… What this is not

It is not the first PHP coding agent β€” neuron-core/maestro got there first and its README says so correctly. It is not faster than a Go or Rust agent; PHP's interpreter floor is 48.6ms against Python's 21.5ms and ripgrep's 3.7ms, and no amount of care changes that. If raw startup is what you want, use something compiled.

πŸ“Š Honest comparison

Stars and dates pulled live from the GitHub API on 2026-08-02:

🐘 Paider neuron-core/maestro Aider-AI/aider
language PHP PHP Python
stars unreleased 38β˜… 47,886β˜…
last pushed active 2026-06-19 2026-05-22 ☠️
open issues β€” 0 1,770
first PHP agent? ❌ no βœ… yes, and says so n/a
shipping today? ❌ not yet βœ… yes ⚠️ stalled
named cost tiers βœ… 4, with a ledger ❌ ❌ main/weak/editor, unlabeled
open-weight presets βœ… 2 ❌ ❌
commercial coupling none Inspector.dev SaaS none

Read that table honestly: Maestro ships today and Paider does not. Maestro is also 38 stars beside its own 2,038-star underlying SDK β€” so "PHP developers want an agent CLI in PHP" is unproven, not confirmed, even by the one entrant that exists. Aider proves the opposite risk: 48k stars mean nothing once the maintainer goes quiet.

What Paider bets on is the two columns nobody else fills in β€” named cost tiers with a checkable ledger, and an agent that lives inside your Laravel app.

πŸ’‘ What it is

Two bets.

1. An agent that lives inside your Laravel app knows things an external one has to be told.

laravel/mcp builds MCP servers; the PHP SDK consumes them. A Laravel application can be both ends of the protocol at once. Shipped as a package, Paider turns your own models, jobs, queues and domain logic into tools the agent can call β€” defined in the framework's idiom, not hand-rolled JSON schemas. No Python or Go CLI can do that for a Laravel developer.

The full version of this β€” any MCP client driving Paider's tools β€” is v1.0, gated on the MCP PHP SDK maturing past pre-1.0. The intended first step is ArtisanTool, reading route:list as structured data instead of shell text when pointed at a Laravel repo.

βœ… ArtisanTool is built. One hardcoded call (php artisan route:list --json), not a general Artisan passthrough β€” arbitrary code execution needs its own approval gate rather than a slot in ShellTool. Registered only when an artisan file exists at the project root (detects Laravel apps). See PLAN.md Β§ Sequencing.

2. You can see exactly where the money went.

Four tiers, named for what they are for:

tier job why it matters
orchestrator plans, decomposes, reviews low volume, high value
coder writes the diff runs in a loop, latency compounds
research reads docs, greps, summarises high volume, low difficulty β€” where the money quietly goes
fast commit messages, retries trivial work at trivial cost

Nobody else names a research tier. It is the one that ingests 50k tokens to extract 500, and paying orchestrator rates for it is how agent bills get absurd.

Because the tiers are named, Paider can account for them separately β€” and answer a question no other agent CLI can:

$ paider cost

  tier            calls      in        out       spend    share
  ───────────────────────────────────────────────────────────────
  orchestrator       14    61.2k      19.8k     $0.801    84.9%
  coder             203     1.4M     287.1k     $0.079     8.4%
  research          118     1.8M      34.6k     $0.058     6.2%
  fast               77    98.4k      12.2k     $0.005     0.5%
  ───────────────────────────────────────────────────────────────
  session                  3.36M     353.7k     $0.943

  97.8% of your tokens went through tiers costing 15.1% of your spend.
  Same work on all-Opus 5: $25.64  Β·  you saved $24.70

Modelled session, real command. βœ… paider cost prints all of the above β€” every column, the ratio line and the all-Opus comparison β€” straight off the event log. The token volumes shown are a modelled session, but the arithmetic is the shipped code's, not a mockup's. Each tier_call event is priced at write time from config/prices.php by exact model id. A model with no entry there stores cost_usd as NULL, not $0.00 β€” those surface as unpriced calls naming the specific model, rather than silently undercounting the total.

Two tests hold this block and the code together from opposite ends: CostTableTest parses this table and recomputes it from config/prices.php, and CostReadmeGoldenTest seeds the event log with these volumes, runs the real command, and asserts the output matches. The table cannot drift from the code in either direction without a failing test.

That last line is the product in one sentence. Most agent tools show you a total, if anything. Paider shows you the ratio β€” and the ratio is the whole argument for routing, once it's wired up.

It also keeps us honest. The 95.3% figure below is a modelled session; the ledger is what confirms or refutes it on real work. A cost claim you cannot check is marketing, and this one is checkable by the person paying.

The presets

Eleven ship in config/presets.php, every model ID and price verified against the live OpenRouter catalogue. Modelled on a session planning 50k/20k and working 2M/300k:

stack cost
all Opus 5 $18.25
all Sonnet 5 $7.30
default β€” Opus 5 to think, qwen3.7-flash to do $0.85

There is also an open-weight stack β€” kimi-k3 orchestrating with qwen3.7-flash on the other three tiers, self-hostable end to end, for people who will not send their code to a US frontier lab. As far as we can tell nobody in PHP ships a preset for them.

paider config:provider open      # kimi-k3 + qwen3.7-flash, open weights
paider config:provider balanced  # opus-5 to think, qwen3.7-flash to do
paider config:provider kimi      # single-provider stacks for all the majors
paider config:show               # what am I actually running?

πŸ•³οΈ Why build it at all

Aider-AI/aider has 47,886 stars, 1,770 open issues, and no commit since 2026-05-22. Its most-repeated issues are not feature requests β€” they are #4613 "where is Paul?" and #4648 "what is the intended future of Aider?", closed not-planned. There are 4,805 forks and none has consolidated the userbase; the leading one holds 0.8% of upstream's stars.

It died of abandoned stewardship, not a technical flaw. That is the thing worth designing against, and it is why PLAN.md has a Non-goals section longer than its feature list.

πŸ–₯️ On the terminal UI

The fashionable agent CLIs render with Ink, React for the terminal. It is good, and it is not obviously better than what PHP already has for this shape of program. Symfony Console is twenty years mature; laravel/prompts does streaming output (stream(), task()->partial()) and every interactive input, with non-TTY fallback built in; Termwind does Tailwind-style layout for static output; and Collision renders errors better than most things in any language.

The Windows caveat you will read about β€” "laravel/prompts is WSL-only" β€” does not apply here, and that was measured, not assumed. Illuminate\Console\Command already calls Prompt::fallbackWhen(windows_os()) and registers Symfony Question Helper fallbacks, so Laravel Zero inherits a working Windows path for free. The streaming output side is byte-identical under the fallback (all 47 lines of a captured run); only the interactive input prompts change, from arrow-key selection to typed numbers. Windows Terminal is the documented baseline β€” legacy cmd.exe mangles the box-drawing glyphs. Full measurement in DECISIONS.md Β§10.

Where Ink genuinely wins is a full-screen alternate-buffer app with many live reactive panes. A coding agent is mostly streaming text, a spinner, a diff and a confirm β€” and PHP is fine at those. If Paider ever needs a real reactive TUI, that is the moment to reconsider, not before.

πŸ—οΈ Architecture

Everything durable goes through one SQLite file. The cost ledger is not a balance anyone increments β€” it is a projection replayed over an append-only event log, which is why /undo and the audit trail come free rather than being features someone has to maintain.

you β†’ ChatCommand β†’ Session β†’ Loop ─┬─ TierRouter β†’ provider (Anthropic | OpenAI-compatible)
                                    └─ Approval Gate β†’ tools β†’ PathGuard
                                                          ↓
                                              events (append-only) β†’ CostLedger
πŸ—ΊοΈ Same thing as a diagram (renders on GitHub)
flowchart TB
    User([πŸ‘€ you]) -->|prompt| Chat[πŸ’¬ ChatCommand]
    Chat --> Session[🧠 Session]
    Session --> Loop[πŸ” Loop]

    Loop -->|picks a tier| Router{{🎚️ TierRouter}}
    Router -->|orchestrator| P1[🌐 AnthropicClient]
    Router -->|coder · research · fast| P2[🌐 OpenAiCompatibleClient]

    Loop -->|proposes a tool call| Gate{πŸ” Approval Gate}
    Gate -->|denied| Loop
    Gate -->|allowed| Tools

    subgraph Tools [πŸ”§ native tools]
        direction LR
        T1[read_file] ~~~ T2[write_file] ~~~ T3[patch_file]
        T4[run_shell] ~~~ T5[git] ~~~ T6[artisan]
    end

    Tools -->|every path checked| Guard[πŸ›‘οΈ PathGuard]
    Tools --> Log[(πŸ—„οΈ events Β· append-only)]
    P1 & P2 --> Log
    Log -->|projection| Ledger[πŸ’° CostLedger]

    style Gate fill:#f96,stroke:#333,color:#000
    style Guard fill:#9cf,stroke:#333,color:#000
    style Log fill:#cfc,stroke:#333,color:#000
Loading
πŸ” One turn, end to end
sequenceDiagram
    autonumber
    participant U as πŸ‘€ you
    participant L as πŸ” Loop
    participant R as 🎚️ TierRouter
    participant M as 🌐 model
    participant G as πŸ” Gate
    participant T as πŸ”§ tool
    participant E as πŸ—„οΈ events

    U->>L: "fix the failing test"
    L->>R: which model for `orchestrator`?
    R-->>L: anthropic/claude-opus-5
    L->>M: prompt + tool contracts
    M-->>L: fenced tool call
    Note over L,M: text-fenced blocks, not native tool-calling β€”<br/>providers disagree on the format, qwen3.7-flash<br/>reports structured_outputs=false
    L->>G: patch_file(app/Foo.php)
    G->>U: show diff Β· allow once / session / deny
    U-->>G: allow once
    G->>T: execute
    T->>T: stamp check β†’ parse hunks β†’ syntax gate
    T-->>L: ToolResult
    L->>E: append(tool_result, …)
    E-->>L: uuid7
    Note over E: append only β€” no update(),<br/>no delete(), anywhere in the class
Loading

πŸ§ͺ Test suites β€” hermetic by default, live on demand

Two test runs, two purposes:

# Default: hermetic, no network, no cost
# βœ… safe to run on every commit, in CI, anywhere
vendor/bin/pest

# Live: real API calls, real spend (measured and reconciled)
# costs money Β· requires credentials Β· read the ledger output
vendor/bin/pest --group=live

Hermetic suite (vendor/bin/pest, 175 tests) β€” all provider interactions mocked via Guzzle; proves self-consistency, zero cost. Excluded group: live. This is the number in the badge above; the live suite is 3 more on top, not part of it.

Live suite (vendor/bin/pest --group=live, 3 tests) β€” real round-trips to api.openrouter.ai, api.anthropic.com, and api.x.ai (xAI fallback when ANTHROPIC_API_KEY absent). Discovers shape mismatches, usage-field placement, and actual token costs. Tests skip gracefully (no failure) when credentials are absent, so CI stays green in sandboxes.

Environment variables for live suite:

  • OPENROUTER_API_KEY β€” enables OpenRouter test (qwen/qwen3-max)
  • ANTHROPIC_API_KEY β€” enables Anthropic test (claude-opus-5-latest)
  • XAI_API_KEY β€” fallback when Anthropic key absent (Claude via xAI's Anthropic-format endpoint)
πŸ“Š Live test discoveries

Three real calls across two tiers; the cost ledger reconciles exactly:

  • OpenRouter round-trip β€” qwen3-max responds with usage fields where the parser expects them
  • Anthropic wire format β€” novel finding: grok-4 returns a thinking content block before text, and AnthropicClient filters on type === 'text'. Every hand-written fixture was text-only, so this filter had never been exercised against reality until now. βœ… holds.
  • Cost ledger reconciliation β€” provider-reported token counts match our projection's totals to 1e-9; spend matches the price sheet exactly; tiers partition the session correctly.

⌨️ CLI reference

command what it does state
paider / paider chat πŸ’¬ interactive session rooted at the cwd βœ… built
paider commit πŸ“ stage everything, generate a message on the fast tier, commit βœ… built
paider cost πŸ’° per-tier calls, tokens, and spend from the event log βœ… built
paider config:provider <preset> πŸŽ›οΈ switch the active tier stack βœ… built
paider config:show πŸ‘€ show the active preset and the model per tier βœ… built

In-session slash commands β€” aider's proven UX, not a Paider invention:

slash effect
/add <file> βž• put a file in context (and stamp it for staleness detection)
/drop <file> βž– take it back out
/diff πŸ” show pending changes
/undo ↩️ roll back the last applied change
/tier <name> <model> 🎚️ override one tier for this session
/quit πŸ‘‹ leave

πŸ” The parts we were paranoid about

Every row below is a bug that was found and fixed by adversarial review, not a design someone got right first try:

guard what it stops
πŸ›‘οΈ PathGuard .. traversal in a non-existent tail and an existing intermediate dir symlinked out of the project
πŸ” Gate only ever caches a grant β€” there is no path that reads a cached deny as an allow
πŸ” approval bypass the model's own tool-call input never contains the approval key β€” Loop deletes it before the gate runs, so a model trying {"approval":"allow-once"} cannot self-approve
🧾 EventLog no update(), no delete(), anywhere β€” append-only is structural, not a comment
🀫 SecretsGuard redaction before anything reaches a model
πŸ’Έ QwenPlanKeyGuard refuses an sk-sp- plan key paired with a PAYG base URL, which would silently bill you
🚫 strict JSON six paths where a lenient decode turned a failed call into a successful-looking empty one
⏱️ ShellTool timeout SIGTERM then SIGKILL after 0.5s β€” proc_close() blocks until the child exits, so a trap '' TERM command ran the full 20s against a 1s timeout and reported exit 0
πŸ“Ž patch_file + secrets creating a new .env/id_rsa skipped approval entirely, because stamp='__new_file__' needs no prior read β€” which is where the gate used to catch it
βœ… result checking paider commit returned SUCCESS when nothing was committed, and fed a SecretsGuard refusal to the model as though it were a diff
πŸ”¬ Where these came from

Every row above was found by an adversarial review pass that read the code on disk rather than the author's summary, then re-verified against the committed result. Three are worth calling out because the tests were green the whole time:

  • Approval gate bypass (critical). Loop::dispatchArtisan() and Loop::dispatchShell() passed the model's own tool-call input straight through to the tool without scrubbing. If that input contained {"approval": "yes"}, it bypassed the gate entirely β€” the approval callback never ran. ArtisanTool accepts any non-deny value, and Loop::systemInstruction() JSON-encodes the tool's inputSchema() into the prompt, so the model was explicitly told the field name and values. A model reply of {"name":"artisan","input":{"approval":"allow-once"}} would run service providers with zero human approval: arbitrary code execution. Fixed by unset($input['approval']) in both dispatchers before the gate. Two regression tests now assert the gate runs and its answer wins when a model tries to self-approve.
  • /add was silently inert. Loop never read Session::contextFiles(), so added files never reached the model β€” and without their sha256 the model could not supply the stamp patch_file requires. The headline workflow did nothing, with no error.
  • PathGuard had two independent escapes, found by two different reviewers. The first was .. in a non-existent tail. The second was an existing intermediate directory symlinked out of the project, which the first fix said nothing about.

πŸ“š Read the thinking

  • STORAGE.md β€” one SQLite file, no services. Why not Redis.
  • EXTENSIONS.md β€” the eleven extensions that ship, and what was cut.
  • PLAN.md β€” thesis, non-goals, v0.1 scope, architecture, milestones, risks.
  • DECISIONS.md β€” how we got here, measured, with the wrong turns left in: recommending the wrong repo, picking a model off a spec sheet that a practitioner knew was a dead zone, and asserting a gap that turned out to be occupied.

πŸ“ Measured, not estimated

Startup, one machine, medians:

ripgrep (Rust) 3.7ms
gh (Go) 20.0ms
Python, bare 21.5ms
PHP, bare 48.6ms
Laravel Zero, lean ini 95.9ms
cecli (1,548 modules) ~710ms

πŸ“¦ Distribution

Two channels, because there are exactly two users:

composer require paider/paider               # inside your Laravel app β€” this is the thesis
composer global require paider/paider        # as a standalone CLI
curl -fsSL paider.dev/install | sh           # planned: standalone binary, not built yet

Tagged releases start at v0.1.0, so a bare composer require resolves under Composer's default minimum-stability: stable. Track the branch with paider/paider:dev-main if you want unreleased work β€” that is where the sharp edges live.

The package is non-negotiable: an agent that turns your models and jobs into tools has to be a dependency of your app, and a compiled binary cannot be one.

The binary is planned to be a FrankenPHP embed (11,263β˜…, Go, built on Caddy), which produces a self-executable with PHP inside and supports CLI β€” ./my-app php-cli bin/console β€” not just HTTP. It selects extensions from composer.json, so the shipped tool never inherits a user's dev ini. That matters: 76 extensions on the author's machine cost 94ms of a 143ms startup.

βœ… Measured 2026-08-02, round 2 β€” trimmed and built, both risks resolved

Round 1 measured the stock off-the-shelf binary and could only clear the cold-start risk, leaving size conditional. Round 2 actually built the trimmed binary natively β€” and it revises round 1's cold-start conclusion, not just its size one. Full numbers: DECISIONS.md Β§9.

round 1 (stock, 77 ext) round 2 (trimmed, 11 ext)
Size 178MB 111.3MB (106.2 MiB) β€” βˆ’37.5%
Compressed (zstd -19) 60.4MB 40.6MB
Cold start vs. lean-ini PHP ~23% slower parity (1.01x, i.e. no measurable penalty)
Cold start vs. stock binary β€” 1.20x faster

Cold start: the penalty is gone, not just smaller. Round 1's "~23% slower than lean-ini PHP" is superseded β€” that number came from the stock binary dynamically initialising 66 unwanted extensions on every invocation. Trim to the eleven Paider actually needs and the penalty disappears: 94.8ms Β±1.3ms vs. 95.9ms Β±1.3ms for lean-ini system PHP, a statistical tie. Say it as "the cold-start penalty is eliminated," not "FrankenPHP is faster than PHP" β€” it isn't, it's even.

Size: 111MB on disk, honestly stated β€” but 40.6MB is what an installer downloads. That lands right alongside the ~40MB Go binaries competing agents ship, which makes the curl | sh story viable. Getting there required fixing a real bug first: the "documented nine" extensions produced a binary that could not boot β€” laravel-zero/framework calls Phar::running() unconditionally and needs ext-phar present, undeclared in its own composer.json. Paider's required set is eleven, not nine (adding phar and filter cost +283KB). See EXTENSIONS.md for the trap and the full extension table.

Full round 2 hyperfine table (3 warmup + 30 timed runs, -N)
command mean std dev
php -n application --version (lean-ini system PHP 8.5.8) 95.9ms Β±1.3ms
php application --version (real Homebrew ini, 73 ext) 192.9ms Β±1.4ms
stock frankenphp, 77 ext, 178MB 113.5ms Β±1.4ms
trimmed frankenphp, 11 ext, 111MB 94.8ms Β±1.3ms

The maintainer's 20–30MB estimate was not reached, and now we know why: build-static.sh always links the full Caddy server and Go HTTP stack, even for a binary only ever invoked as php-cli β€” there's no CLI-only mode in the script. Not claimed impossible in general, just not what the supported build path produces. Whether a Caddy-free CLI-only build could get closer is open β€” see PLAN.md β€” but it's a nice-to-have now, not a blocker.

Build cost is cheap. Built natively (not Docker β€” the Docker static-builder emits Linux binaries only) in ~7 minutes, PHP 8.5.9 / Caddy v2.11.4 / Go 1.26.5. build-static.sh cannot cross-compile, so shipping is still a CI matrix (ubuntu-latest, ubuntu-24.04-arm, macos-latest, macos-13), but 7 minutes/platform is cheap enough for that matrix to be routine.

A Windows binary does exist (frankenphp-windows-x86_64), and the laravel/prompts half of the Windows question is resolved β€” Laravel's own ConfiguresPrompts already handles it, and the streaming path is unchanged. See DECISIONS.md Β§10. Windows is shippable.

Functional check under the trimmed binary, for the record: application list renders correctly, pdo_sqlite round-trips an in-memory DB (see STORAGE.md), stream_isatty() behaves correctly under a non-TTY pipe, and PHP_VERSION reports 8.5.9 β€” comfortably above the ^8.4 floor. Full writeup: DECISIONS.md Β§9.

The distribution decision is now confirmed on both axes β€” cold start and size β€” where round 1 could only confirm one. curl -fsSL paider.dev/install | sh above is still planned, not built; the binary math behind it now checks out.

No PHAR. It needs PHP installed but is not a composer dependency, so it serves neither user better than the two above. A third channel is maintenance forever for an audience of nobody.

No Docker. Container start would eat the entire startup budget.

πŸ—ΊοΈ Roadmap

πŸ“ˆ Milestone flow (renders on GitHub)
flowchart LR
    V01["🧱 v0.1<br/>commands Β· tools Β· ledger"] --> V02["πŸ”Œ v0.2<br/>MCP client Β· agent roster"]
    V02 --> V10["πŸ›οΈ v1.0<br/>Paider as an MCP server"]

    style V01 fill:#cfc,stroke:#2a2,color:#000
    style V02 fill:#ffd,stroke:#aa2,color:#000
    style V10 fill:#eee,stroke:#999,color:#000
Loading
milestone scope state
v0.1 5 commands, 6 tools, approval gate, event log, cost ledger, tier router, CI pipeline, ~94.8ms cold start πŸ”¨ in progress
v0.2 mcp/sdk client, paider run --yes, repo-map on the research tier, test-feedback loop ⬜ planned
v1.0 MCP server mode β€” external clients drive Paider's tools; published semver policy ⬜ planned
❓ Why is v0.1 still πŸ”¨ when the code is written and 175 tests pass?

Because PLAN.md wrote v0.1's definition of done before the code existed, and grading against it honestly leaves one box unticked:

v0.1 definition-of-done state
the four commands βœ… built
the five native tools + ArtisanTool βœ… six built
sk-sp- key/base-URL guard βœ… built
diff-apply staleness, syntax gate, /undo, secrets guard βœ… built
honest comparison table vs Maestro βœ… added above
live provider round-trips βœ… 3 tests, ledger reconciles
published on Packagist βœ… published β€” paider/paider, tagged v0.1.0
CI pipeline with hermetic + live suites βœ… shipped β€” .github/workflows/tests.yml runs on PR, --prefer-lowest catches version leaks
end-to-end on a real repo with a real API key ⬜ never attempted

The one remaining box β€” running a full session against a real repo with a real API key and watching an edit land β€” is the last blocker to shipping v0.1. Everything else is done. The rule in this repo is that a green checkbox is a promise a grep or a test run can keep β€” eight checked above are testable / grepable; the unchecked one requires end-to-end human verification.

πŸ“œ License

Apache-2.0 β€” see the full text, which is byte-for-byte the official one (md5 3b83ef96387f14655fc854ddc3c6bd57).

Dependency licenses audited and compatible: 93 MIT Β· 26 BSD-3-Clause Β· 1 Apache-2.0. All permissive, no copyleft.

Written in PHP on purpose. 🐘

Not because PHP is fast β€” it isn't, and DECISIONS.md Β§3 says so with numbers β€” but because an agent that lives inside your Laravel app knows things an external one has to be told.

Every βœ… above is backed by a passing test. Every ⬜ is honest about not existing yet.