This is a content repository: JSON manifests, Markdown components, two shell hooks, a reference PHP config, and a Python validator. There is no build step. Testing means validating structure, then confirming the reference config is one the named tools accept.
- Manifest / component validation —
./scripts/validate.sh(also run by CI): bothplugin.jsonmanifests, dual-host parity, declared paths, kebab-case names, hook JSON, and frontmatter (nameequals the directory or filename, agents declaretools:and notallowed-tools:, descriptions fit 1,024 characters). It also checks that everyphp-codingreference is linked from the index, that the Cursor rule names every reference, and that the Cursor rule and agents name only references that exist. If Python 3 is missing the wrapper warns and exits 0. CI installs Python 3 and runspython3 scripts/validate.pydirectly. - Official validator —
claude plugin validate .checks the manifest and the hook config. Fix its warnings as well as its errors. CI does not run it. It does not catch an unquoted:in frontmatter; onlyscripts/validate.pydoes. - Reference config — in a scratch project, copy
references/php-cs-fixer.phpto.php-cs-fixer.phpandreferences/phpstan.neontophpstan.neon, then runvendor/bin/php-cs-fixer fix --dry-run --diffandvendor/bin/phpstan analyse. Rector, only if you opted in:vendor/bin/rector process --dry-run --config=rector.php, then the fixer again.
Checked 2026-09-30 against php-cs-fixer 3.95.27 (describe @PER-CS prints @PER-CS3x0), PHPStan 2.2.16, PHPUnit 12.5.37, and Rector 2.6.7. The fixer rewrote a missing declare(strict_types=1) and the brace layout, and left ==, an empty catch, and a line past 120 characters alone. PHPStan level 8 with phpVersion: 80400 reported a string|null passed to strlen and an implicitly nullable parameter, and did not report the == or the empty catch. Rector's PHP 8.4 set loaded and rewrote only the implicitly nullable parameter.
Both scripts always exit 0. Run them from the root of a PHP project, with P set to this repo's path:
P=/path/to/php-coding-plugin
bash "$P/hooks/session-start.sh" # one standards line; nothing outside a PHP workspace
printf '{"tool_input":{"file_path":"src/Foo.php"}}' | bash "$P/hooks/format-on-save.sh" # Claude Code payload
printf '{"file_path":"/abs/path/src/Foo.php"}' | bash "$P/hooks/format-on-save.sh" # Cursor afterFileEdit payloadWith a .php-cs-fixer.php and a fixer binary present, the named file is reformatted. Without either, nothing happens.
evals/ holds cases for claude plugin eval (docs). Each case runs three times with the plugin and three times without, and reports both scores and the difference.
claude plugin eval . --trust-plugin # the whole suite, with a no-plugin baseline
claude plugin eval . --trust-plugin --case 'review-*' --runs 1 # one quick pass while editing a case
claude plugin eval . --trust-plugin --ablation none # with-arm only, half the costEvery run is a full Claude session on your own credential, so the whole suite costs real model calls. Results land in evals/results/, which is gitignored.
Load a working copy (see install.md), then:
- Session-start hook — open a repo with
composer.jsonor a*.phpfile; one standards line should print. php-codingindex — ask which standard applies; it should name a command and a reference.- References — a type question or an empty
catchreadsidioms.md, a test readstesting.md, a PDO query built from input readssecurity.md, a Guzzle call readsguzzle.md, a change to a published interface readscompatibility.md. The transcript shows theReadof the reference file. php-reviewer— a review request loadsphp-coding, reads the references the diff maps to, returns severity-ranked findings, and does not edit or spawn agents./php-lint-setup— writes the reference config and does not overwrite an existing file unprompted.- Cursor rule — open a
.phpfile and confirmphp-context.mdcattaches. - Cursor hooks — in a PHP workspace other than this repo, confirm the session-start line prints and a
.phpedit is reformatted. Cursor does not document how a plugin hook's relative command path resolves, so this is the only check that it works.
After editing content, reinstall or restart the session.