feat: add plugin scaffold generator with flavian-starter reference plugin#70
Merged
Conversation
…ugin
Adds scripts/scaffold-plugin.sh which generates new WordPress plugins under
plugins/ from token-substituted templates in .claude/templates/plugin/.
The generator produces a PSR-4 autoloaded plugin with proper headers,
activation/deactivation hooks, a CPT, a taxonomy, a Settings API page, a
server-rendered block (no build step required), PHPUnit tests via Brain
Monkey, and a WordPress-Extra PHPCS ruleset. Feature flags
(--no-cpt / --no-taxonomy / --no-settings / --no-block / --minimal) trim
the output; --force overwrites and --dry-run previews.
plugins/flavian-starter/ is the canonical scaffold output, generated by:
bash scripts/scaffold-plugin.sh flavian-starter \
--name "Flavian Starter" \
--description "..." \
--author "PMDevSolutions" --force
It doubles as the generator's e2e test — .github/workflows/plugin-validation.yml
regenerates it on every push and fails if the result drifts from the
committed copy.
The validation workflow also runs PHPUnit and PHPCS against every plugin
matching plugins/flavian-*/ via a dynamic matrix (find + jq), so adding a
new first-party plugin requires no workflow edit.
Extends the plugin-developer agent's prompt to point at the scaffold script
and the reference plugin so it stops hand-writing boilerplate.
Note on namespaces in JSON: composer.json templates use {{PLUGIN_NS_JSON}}
instead of {{PLUGIN_NS}} because JSON requires '\' for each literal
backslash in PSR-4 namespace strings.
Note on .gitignore: .claude/templates/ added to the unignore list (previously
caught by the .claude/* default-ignore).
Closes #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
Three classes of CI failures from #70 addressed: 1. PHPUnit ran a test against namespaced code that called __() — Brain Monkey doesn't auto-stub WP i18n. Fix: TestCase::setUp now calls Functions\stubTranslationFunctions() + stubEscapeFunctions(). 2. PHPUnit flagged an Actions\expectAdded test as "risky" because Mockery expectations don't bump PHPUnit's assertion counter. Fix: TestCase now uses Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait, which bridges Mockery's verified expectations into PHPUnit's count. 3. PHPCS exploded on two fronts: - Per-plugin .phpcs.xml.dist allowed PSR-4 filenames but didn't relax short array syntax or class doc-comment requirements. Fix: convert every short array literal in the templates to long-form array(), add class-level docblocks to every class, add method docblocks to every public method, fix aligned-spacing in flavian-starter.php's define() and register_*_hook() calls, rename $post_id in uninstall.php (collides with WP global), broaden the FileName sniff exclusion to the whole sniff rather than two sub-sniffs. - Repo-root .github/workflows/phpcs.yml scans every plugin with WordPress-Extra,WordPress-Docs (without the FileName carve-out the per-plugin config has) and so fights its own per-plugin linter. Fix: add plugins/flavian-*/* to its --ignore list — first-party plugins are now linted exclusively by plugin-validation.yml with their own PSR-4-aware ruleset. - cs2pr received a mix of progress-bar output and checkstyle XML. Fix: pass -q to phpcs in plugin-validation.yml so only the XML reaches the action's parser. Verified locally via php:8.2-cli + composer:2 Docker images: - PHPUnit: 4 tests, 9 assertions, 0 errors, 0 risky. - PHPCS (per-plugin config): 9 files, 0 errors, 0 warnings. - PHPCS (repo-root with new ignore): 0 violations. - Scaffold idempotence: regen → diff -r → identical. Refs #19 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a token-substitution plugin generator that produces a PSR-4 autoloaded WordPress plugin in one command. Ships
plugins/flavian-starter/as the canonical scaffold output and parallel tothemes/flavian-shop/. Closes #19.Produces a runnable plugin with proper headers, activation hooks, a CPT, a taxonomy, a Settings API page, a server-rendered block, PHPUnit tests (Brain Monkey), and a
WordPress-ExtraPHPCS ruleset. Feature flags:--no-cpt,--no-taxonomy,--no-settings,--no-block,--minimal,--force,--dry-run.What the scaffold produces
Decisions locked in (from scoping)
flavian-starterreference pluginplugins/flavian-starter/Flavian\Plugins\<PluginClass>, autoload via composersrc/Blocks/FeaturedEvent.php+assets/blocks/featured-event/(server-rendered, no JS build step)Activator::activate()only callsflush_rewrite_rules()— no demo contentplugin-developeronly, no new agents.claude/agents/plugin-developer.mdupdated; description + body now point at scaffold and reference pluginplugins/flavian-*/globdiscoverjob inplugin-validation.ymlenumerates withfind ... -name 'flavian-*'+jq; adding a new first-party plugin requires no workflow editcomposer.json,phpunit.xml.dist,.phpcs.xml.dist,vendor/— independently testableCI
.github/workflows/plugin-validation.yml(new) runs on PRs touchingplugins/flavian-*/,.claude/templates/plugin/, or the scaffold script:flavian-starterfrom templates,git diff --exit-code. Catches template/reference drift in either direction.plugins/flavian-*/directory.cs2prfor inline PR annotations) per plugin.Implementation notes worth knowing
{{PLUGIN_NS_JSON}}token — composer.json's PSR-4 strings need\per literal backslash in the namespace. The generator computes a JSON-encoded namespace and substitutes it separately from the PHP-source{{PLUGIN_NS}}token. Documented in.claude/templates/plugin/README.md.assets/blocks/featured-event/index.jsuseswindow.wp.blocks/wp.element/wp.blockEditor/wp.i18nruntime globals directly. The plugin is usable the instantcomposer installfinishes; users who want JSX can add@wordpress/scriptsthemselves.class_exists()guards inPlugin::boot()— so users canrm -rf src/PostTypes/after scaffolding without breaking the rest of the plugin. Same pattern inActivator.diff -r). The CI drift check depends on this..gitignorecarve-out —.claude/templates/added to the existing unignore list;.claude/*defaults to ignored.Test plan
commitlintpasses on this PR.plugin-validation / scaffold-driftpasses — regeneratedflavian-startermatches committed copy.plugin-validation / phpunit (flavian-starter)passes — Brain Monkey tests intests/PluginTest.phpandtests/PostTypes/EventTest.phprun green aftercomposer install.plugin-validation / phpcs (flavian-starter)passes — no WordPress-Extra violations in the scaffold output.bash scripts/scaffold-plugin.sh demo --author "Demo" --forceproduces a workingplugins/demo/that passes its owncomposer test.🤖 Generated with Claude Code