Eamann Plugin
Template for Foundry-enabled WordPress plugins.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/ericmann/eamann-plugin-template/archive/refs/heads/main.zipA GitHub template repository for WordPress plugins that are built with Foundry: spec in, plan out, unattended build, mechanical review. It ships the foundation a Foundry flight would otherwise spend its first phases producing.
What you get on day zero:
- A composition root and four leaf classes (
Plugin,Keys,Flags,Hooks,Clock,NotImplementedException) that encode the rules the reviewer checks: names in one place, options read in one place, hooks registered in one place and fired in one place, time injected. - A working
wp <slug> doctorsmoke command with a WP-CLI shim so it is unit-testable without WP-CLI. - wp-env with an idempotent post-start script, optional multisite, and an optional MU-plugin loader for hosts like WordPress VIP.
- PHPUnit split into a Docker-free
unitsuite and anintegrationsuite that runs inside wp-env, plusSpy_ContainerandMultisite_Fixture. - phpcs (WordPress-VIP-Go), PHPStan level 6, a test-map check and a GitHub Actions workflow that runs all of it.
- Foundry wiring: a
docs/SPEC.mdin the shape the planner reads, adocs/foundry.base.jsonwith role routing and thirteen self-tested constraints, aCLAUDE.mdand adocs/CONTRIBUTING.md.
Start a project
-
On GitHub, Use this template → create your repository, clone it.
-
Rename everything once:
bash bin/init.sh --slug acme-widgets --namespace 'Acme\Widgets' \ --name 'Acme Widgets' --vendor acme git add -A && git commit -m "Initialise acme-widgets from template" rm bin/init.sh -
Install and prove the shell is green:
composer install && npm install composer verify:static # lint, analyse, test:map, unit tests npx wp-env start # first run takes a few minutes composer verify # adds the integration suite npx wp-env run cli wp acme-widgets doctor -
Write the spec. Fill in
docs/SPEC.mdsections 1, 2, 8 and 9 and extend 3 through 6 with the project's own rules, modules and interfaces. Foundry'sdocs/writing-specs.mdexplains what each section is for. -
Fly:
claude > /foundry:go-flight
Layout
<slug>.php bootstrap: autoloader, version guards, Plugin::boot()
includes/
class-plugin.php composition root; the only add_action/add_filter site
class-keys.php every name as a constant
class-flags.php option reads
class-hooks.php the only do_action/apply_filters site
class-clock.php Clock, SystemClock, FixedClock
class-cli.php wp <slug> doctor
class-assets.php base stylesheet registration
class-rest.php <slug>/v1 namespace, /eam lineage route
class-not-implemented-exception.php
assets/css/base.css the one stylesheet; --eam-* design tokens
tests/
bootstrap.php unit on the host, integration inside wp-env
class-*-shim.php WP_CLI shims
class-spy-container.php "nothing was called" mocks for flag-gate tests
class-multisite-fixture.php
unit/ integration/
bin/
setup-wp-env.sh post-start: activate, optional multisite
test.sh phpunit inside tests-cli
test-map.php every class has a test
wp-env.conf MULTISITE, SITES, PLUGIN_SLUG
init.sh one-shot rename
docs/
SPEC.md the build spec Foundry plans from
foundry.base.json starter verify commands, roles, constraints
HOOKS.md CONTRIBUTING.md adr/
Choices you may want to flip
| Choice | Default | Change |
|---|---|---|
| Plugin kind | wp-content/plugins/ |
MU on VIP: see bin/mu-loader.php.example |
| Multisite | off | MULTISITE=1 in bin/wp-env.conf |
vendor/ |
ignored | commit it for hosts with no build step (CONTRIBUTING.md) |
| PHP / WP minimums | 8.1 / 6.5 | Keys::MIN_PHP, Keys::MIN_WP, composer.json platform, CI matrix |
| Foundry models | Fable plan, Sonnet implement, Opus 5.5 review and summarize | docs/foundry.base.json roles |
Why these rules
They come from a real flight: a VIP multisite plugin whose review rounds
converged in two passes because every principle was a grep, not an opinion.
The constraints in docs/foundry.base.json self-test against fixture lines,
so a rule that stops catching a shape fails loudly instead of silently.
Attribution
Derivatives carry passive, read-only attribution to the template: a served
humans.txt in the plugin root, a lineage row in wp <slug> doctor, a
static GET /wp-json/<slug>/v1/eam route, the --eam-epoch custom property
in the base stylesheet, @author file docblocks, and Keys::LINEAGE,
Keys::EPOCH and Keys::ASSET_VERSION. Nothing phones home. bin/init.sh
leaves these untouched; if you fork the template itself, change them to your
own.
License
GPL-2.0-or-later.