Agentimus ships as a plain WordPress plugin with a small Vue 3 admin app compiled on top. There is no framework, no build step for the PHP, and no runtime bundler — the only compiled artifact is the admin UI. This page covers everything you need to build the plugin from a clean checkout, run the test suite, and open a pull request.
Requirements
Two sets of requirements matter: what the plugin needs to run, and what a contributor needs to build and test it.
Runtime (what the plugin needs to run)
| Requirement | Version | Source of truth |
|---|---|---|
| WordPress | 6.0 or newer (tested up to 7.0) | Requires at least: 6.0 in agentimus.php and readme.txt |
| PHP | 7.4 or newer | Requires PHP: 7.4 in agentimus.php; "php": ">=7.4" in composer.json |
The plugin has one PHP runtime dependency, loaded only on demand: since 1.22.0,
composer.json requires wordpress/mcp-adapter (plus automattic/jetpack-autoloader, the
ecosystem’s version arbiter when several plugins bundle the same library). It powers the
opt-in MCP server and is required only when the owner turns that switch on — everything
else under inc/ is vanilla PHP, PSR-4 autoloaded by a hand-rolled loader in agentimus.php.
A checkout without composer install still runs fine: the MCP switch simply no-ops (the
settings card says why), and nothing else notices. The release ZIP ships a production
vendor/ tree built in CI with composer install --no-dev.
Build & test toolchain (what a contributor needs)
| Tool | Used for | Notes |
|---|---|---|
| Node.js + npm | Building the Vue admin app | Vite 5 is the build tool, so Node 18+ is the practical floor |
| Composer | Installing the test dependency (PHPUnit) | Only needed to run the test suite, not to run the plugin |
| PHP 7.4+ CLI | Running PHPUnit locally | The suite runs against PHP 7.4 and 8.2 in CI |
Repository layout
A clean checkout looks like this (build output and dependency folders are created on demand and are git-ignored):
agentimus/
├── agentimus.php # Main plugin file: header, constants, PSR-4 autoloader, boot
├── uninstall.php # Uninstall cleanup
├── inc/ # PHP source — namespace Agentimus\, PSR-4 (ships)
│ ├── Plugin.php # orchestrator
│ ├── Settings.php # single option store
│ ├── Endpoints.php Markdown.php Schema.php Readiness.php Rest.php Admin.php …
│ ├── Discovery/ # Registry, Resource, Envelope (the discovery contract)
│ ├── Activity/ Visibility/ …
├── resources/ # Vue 3 admin source — the input to Vite (ships)
│ └── admin/
│ ├── main.js # Vite entry point
│ ├── App.vue
│ ├── api.js app.css livecheck.js tiers.js confirm.js
│ └── components/ # SettingsForm.vue, DiscoveryHub.vue, ReadinessPanel.vue, …
├── assets/
│ ├── admin/ # BUILD OUTPUT from Vite — git-ignored, ships in the .zip
│ └── webmcp.js # committed static asset
├── vite.config.js # Vite build config (ships)
├── package.json # npm scripts + Vue/Vite deps (ships)
├── tests/ # PHPUnit suite + bootstrap.php (dev-only)
├── phpunit.xml.dist # PHPUnit config (dev-only)
├── composer.json # Runtime dep (MCP adapter) + dev deps (PHPUnit) + `test` script
├── examples/ # Copy-paste hook/integration reference files
├── readme.txt # WordPress.org readme (ships)
├── README.md # GitHub readme (dev-only)
├── RELEASING.md # Release runbook (dev-only)
├── LICENSE # GPL-2.0-or-later
├── .distignore # What is excluded from the distributed .zip / SVN
└── .github/workflows/ # CI: ci.yml, php-compat.yml, deploy.yml, wordpress-org-assets.yml
What ships vs. what stays in the repo
The distributed plugin (the WordPress.org .zip / SVN trunk) is not the same set of files
as the Git repository. .distignore is the authoritative manifest. In short:
- Ships in the
.zip:agentimus.php,uninstall.php,readme.txt,LICENSE,inc/, the builtassets/, and the Vue sourceresources/plusvite.config.jsandpackage.json. The source and build config ship on purpose — see the no minified-only code policy. - Excluded from the
.zip(via.distignore):.git,.github,.wordpress-org,node_modules,tests,phpunit.xml.dist,composer.json,composer.lock,package-lock.json,README.md, andRELEASING.md. Since 1.22.0,vendor/ships — built fresh in CI withcomposer install --no-devso only production packages land — minus targeted trims (the adapter’s standalone plugin-entry file, executables, package manifests).
Building the Vue admin
The admin UI is a Vue 3 app (Options API) built with Vite. The PHP side enqueues the compiled bundle; it does not read a manifest, so the output filenames are pinned.
npm install # install Vue + Vite (into node_modules/, git-ignored)
npm run build # one-off production build into assets/admin/
npm run dev # rebuild on every change (vite build --watch)
Both scripts are defined in package.json:
"scripts": {
"dev": "vite build --watch",
"build": "vite build"
}
What the build produces
vite.config.js is configured so PHP can enqueue the result without any manifest lookup:
- Output directory:
assets/admin/(emptyOutDir: true, so it is wiped and rebuilt). - Entry point:
resources/admin/main.js. - Stable filenames: the JS is always
app.jsand the CSS is alwaysapp.css(cssCodeSplit: false,manifest: false). Other emitted assets land underassets/admin/assets/[name][extname]. - IIFE format: the bundle is emitted as an IIFE (
format: 'iife'). This is deliberate — the admin bundle is enqueued as a classic script, so it must not leak top-level bindings into the global scope. A minifier-chosen name likewporlodashat the top level would collide with WordPress’s own globals (Identifier 'wp' has already been declared). Wrapping everything in a function scope avoids that.
All of assets/ is git-ignored build output
Two halves, both listed in .gitignore and neither ever committed:
# Build output (regenerated by `npm run build`)
assets/admin/
assets/*.js
assets/admin/ is the Vue admin app, bundled by Vite. assets/*.js are the three standalone
front-end scripts — the WebMCP bridge, the AI-referral beacon and the Focus live counter —
minified from resources/public/ by esbuild. npm run build runs both steps.
The source of every one of them lives in resources/, which is what the repo keeps and
what ships in the zip beside the built files, so the build stays reproducible and the code
stays readable. Edit resources/public/webmcp.js, never assets/webmcp.js: the latter is
overwritten by the next build.
If you clone the repo and do not run the build, the admin screens have no JS/CSS and the
front-end scripts 404 — run npm install && npm run build first.
Running the PHPUnit test suite
The tests are pure-logic unit tests — they exercise the discovery contract (Resource
validation, the Registry collector, Envelope derivation), Markdown/Schema output, the Guard,
Topics, Readiness and more, without a full WordPress install. tests/bootstrap.php
defines the minimal WordPress function/constant surface the tested classes touch and autoloads
the Agentimus\ classes straight from inc/.
composer install # installs the MCP adapter (runtime) + PHPUnit ^9 (dev) into vendor/ (git-ignored)
composer test # runs phpunit against phpunit.xml.dist
composer test is just an alias for phpunit (defined in composer.json), so you can also
invoke the binary directly:
./vendor/bin/phpunit
How the suite is wired
- Config:
phpunit.xml.dist— bootstrap istests/bootstrap.php, and the single test suiteagentimus-unitruns every test in thetests/directory (34 test files at the time of writing, e.g.RegistryTest.php,MarkdownTest.php,GuardTest.php,TopicsTest.php). - No WordPress required:
bootstrap.phpstubs functions likeapply_filters,get_option,get_post,wp_get_post_terms, and friends with small stateful implementations, and defines constants such asAGENTIMUS_DIRandAGENTIMUS_VERSION. This means the suite runs on a bare PHP 7.4+ CLI with only Composer’s dev dependency installed — no database, nowp-load.php. - Determinism helpers: helpers such as
_af_reset_options()and_af_reset_registry()reset the global stubs and theRegistrysingleton between tests.
What CI runs
.github/workflows/ci.yml runs the suite on a matrix of PHP 7.4 and 8.2 via
composer install → composer test. Separately, .github/workflows/php-compat.yml guards
the declared PHP floor two ways:
php -lacross PHP 7.4 → 8.3 to catch syntax a version cannot parse (match, enums, nullsafe, constructor promotion, named args, …).- PHPCompatibilityWP against 7.4 to catch API usage newer than the floor (e.g.
str_contains) while knowing what WordPress core polyfills.
Run the tests locally before pushing; a red CI check will block a release.
The “no minified-only code” policy
Agentimus deliberately ships the human-readable Vue source alongside the built bundle so the plugin is fully auditable and the build is reproducible. From the README’s License section:
The admin app is built from Vue source in
resources/with Vite — no minified-only code ships, so the build is reproducible.
Concretely:
resources/(the Vue source),vite.config.js, andpackage.jsonare included in the distributed.zip(they are intentionally absent from.distignore).- The compiled
assets/admin/app.jsis minified by Vite, but it is never the only copy of that code — anyone can re-runnpm install && npm run buildfrom the shipped source and reproduce it.
Practical consequence for contributors: do not introduce vendored, pre-minified, or obfuscated blobs as the sole form of any code. If it runs, its readable source must ship too.
PHP conventions
- Namespace & autoloading: all PHP lives under the
Agentimus\namespace and is PSR-4 autoloaded by a minimal loader defined inagentimus.php—Agentimus\Foo\Barmaps toinc/Foo/Bar.php. There is no Composer autoloader in production and no framework; a full framework would undercut the point of a lightweight machine-readability plugin. - Coding target: write for PHP 7.4. The
php-compatworkflow will fail the build if you use syntax or APIs newer than 7.4 that WordPress does not polyfill. - Extension seams: new behaviour that a companion or Pro add-on might need should hook the
documented actions and filters rather than editing core paths. See the Hooks & filters
reference page, and the copy-paste examples in
examples/(e.g.examples/all-hooks-reference.php,examples/integrate-your-plugin.php).
A typical contribution loop
- Branch off
main. - Make your change under
inc/(PHP) and/orresources/(Vue). - If you touched the admin UI, run
npm run build(or keepnpm run devrunning) so the change is reflected inassets/admin/locally — but do not commitassets/admin/. - Run
composer testand make sure it printsOK. - Push and open a pull request. CI (
ci.yml+php-compat.yml) runs the PHPUnit matrix and the PHP compatibility checks; both must be green.
Releasing
Publishing a new version to WordPress.org is a separate, tag-driven process — see the RELEASING.md runbook in the repo root for the full step-by-step. The essentials:
- Bump the version in three places and keep them identical: the
Version:header and theAGENTIMUS_VERSIONconstant (both inagentimus.php), andStable tag:inreadme.txt. - Add a changelog and upgrade-notice entry to
readme.txt. - Commit and push to
main, then push a tag whose name is numbers only (1.5.1, neverv1.5.1). Pushing the tag triggers.github/workflows/deploy.yml, which builds the plugin and deploys code, readme, and screenshots to WordPress.org. - Never commit
assets/admin/— the release pipeline builds it.
License
Agentimus is licensed under GPL-2.0-or-later (see the LICENSE file and the
License: GPL-2.0-or-later header in agentimus.php). Because the Vue source ships alongside
the compiled admin bundle, the entire distributed plugin is auditable and reproducible from
source.