with one click
new-plugin
Scaffold a new WordPress plugin in packages/plugins/.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Scaffold a new WordPress plugin in packages/plugins/.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Add a WordPress AJAX handler to a plugin, with nonce verification, input sanitisation, and JSON response.
Register a block pattern in a theme or plugin, using the WordPress 6.0+ file-based pattern system.
Add a new Gutenberg block to an existing plugin or theme.
Register a WP-CLI command in a plugin using a command class, following Dekode conventions.
Add a WordPress action or filter hook to a plugin or theme.
Scaffold a new must-use plugin in packages/mu-plugins/ - either a simple utility or a service-class integration.
| name | new-plugin |
| description | Scaffold a new WordPress plugin in packages/plugins/. |
Create a new WordPress plugin in packages/plugins/ following the project's established conventions.
Ask the user: "What will this plugin do?" — a brief description of its purpose and the problem it solves.
Based on the description, suggest 2–3 slug and name pairs, for example:
Based on your description, here are some suggestions:
# Slug Name 1 event-managerEvent Manager 2 eventsEvents 3 wp-eventsWP Events Which would you like to use, or do you have your own preference?
Once the purpose and identity are confirmed, apply the find-plugin skill to determine whether:
Only proceed to the steps below if Core and existing plugins do not cover the need, or if the user has explicitly agreed to build a custom plugin after reviewing the recommendation.
| Input | Required | Notes |
|---|---|---|
| Plugin slug | Yes | kebab-case, e.g. acme-blocks. Becomes the directory name, text domain, and Composer package name. |
| Plugin title | Yes | Human-readable name for the Plugin Name: header, e.g. Acme Blocks |
| Plugin description | Yes | Short description for the Description: plugin header |
| PHP namespace | Yes | PascalCase, e.g. Acme\Blocks. Check sibling plugins for the project's convention. |
| Plugin type | Yes | block-library (contains Gutenberg blocks) or general (functionality plugin, no blocks) |
| Project vendor prefix | Yes (block-library only) | e.g. acme — used for block names like acme/hero-banner. Check sibling plugins' block.json files to confirm. |
Scan packages/plugins/ for an existing plugin to use as a structural reference.
Block-library plugin:
packages/plugins/<plugin-slug>/
├── plugin.php (plugin header + block auto-loader)
├── src/ (block source directories — see add-block skill)
├── languages/
│ └── .gitkeep
├── package.json
├── composer.json
└── phpcs.xml
General plugin:
packages/plugins/<plugin-slug>/
├── plugin.php (plugin header + bootstrap)
├── src/
│ ├── Plugin.php (main class, singleton)
│ ├── Admin/ (admin screens — add if needed)
│ ├── Api/ (REST controllers — add if needed)
│ └── Frontend/ (front-end hooks — add if needed)
├── languages/
│ └── .gitkeep
├── package.json
├── composer.json
└── phpcs.xml
plugin.phpBlock-library variant:
<?php
/**
* Plugin Name: <Plugin Title>
* Description: <Plugin description>
* Version: 1.0.0
* Requires at least: 6.4
* Requires PHP: 8.1
* Author: Dekodeinteraktiv
* Text Domain: <plugin-slug>
* Domain Path: /languages
*
* @package <PhpNamespace>
*/
declare( strict_types=1 );
namespace <PhpNamespace>;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Load all blocks (auto-discovered from build/).
foreach ( glob( __DIR__ . '/build/*/block.php' ) as $block ) {
require_once $block;
}
/**
* Load plugin text domain.
*/
function load_textdomain(): void {
load_plugin_textdomain( '<plugin-slug>', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
add_action( 'init', __NAMESPACE__ . '\\load_textdomain' );
General plugin variant:
<?php
/**
* Plugin Name: <Plugin Title>
* Description: <Plugin description>
* Version: 1.0.0
* Requires at least: 6.4
* Requires PHP: 8.1
* Author: Dekodeinteraktiv
* Text Domain: <plugin-slug>
* Domain Path: /languages
*
* @package <PhpNamespace>
*/
declare( strict_types=1 );
use <PhpNamespace>\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
require_once __DIR__ . '/vendor/autoload.php';
Plugin::instance()->hooks();
src/Plugin.php (general plugin only)<?php
/**
* Main plugin class.
*
* @package <PhpNamespace>
*/
declare( strict_types=1 );
namespace <PhpNamespace>;
/**
* Plugin class.
*/
class Plugin {
private static ?self $instance = null;
private function __construct() {}
public static function instance(): self {
return self::$instance ??= new self();
}
public function hooks(): void {
add_action( 'init', [ $this, 'init' ] );
add_action( 'init', [ $this, 'load_textdomain' ] );
// Registration hooks must be called in the plugin bootstrap file,
// but we keep them here for discoverability.
register_activation_hook( __FILE__, [ $this, 'activate' ] );
register_deactivation_hook( __FILE__, [ $this, 'deactivate' ] );
}
public function init(): void {
// Register CPTs, taxonomies, etc.
}
public function load_textdomain(): void {
load_plugin_textdomain( '<plugin-slug>', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
public function activate(): void {
flush_rewrite_rules();
}
public function deactivate(): void {
flush_rewrite_rules();
}
}
composer.json{
"name": "dekode/<plugin-slug>",
"description": "<Plugin description>",
"type": "wordpress-plugin",
"version": "1.0.0",
"require": {},
"autoload": {
"psr-4": { "<PhpNamespace>\\": "src/" }
},
"scripts": {
"make-pot": "wp i18n make-pot . languages/<plugin-slug>.pot --domain=<plugin-slug> --exclude=vendor,node_modules,build",
"make-json": "wp i18n make-json languages/<plugin-slug>.pot languages --no-purge",
"lint": "@php vendor/bin/phpcs",
"lint-fix": "@php vendor/bin/phpcbf"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
package.json{
"name": "@dekode/<plugin-slug>",
"version": "1.0.0",
"private": true,
"description": "<Plugin description>",
"scripts": {
"build": "wp-scripts build --webpack-copy-php",
"start": "wp-scripts start --webpack-copy-php",
"lint:js": "wp-scripts lint-js src",
"lint:css": "wp-scripts lint-style src",
"format": "wp-scripts format src"
},
"devDependencies": {
"@wordpress/scripts": "*"
}
}
dependencies needed by blocks (e.g. @phosphor-icons/react for icons).phpcs.xml<?xml version="1.0"?>
<ruleset name="<Plugin Title>">
<description>PHP CodeSniffer ruleset for <Plugin Title>.</description>
<file>.</file>
<arg name="extensions" value="php"/>
<arg name="basepath" value="."/>
<exclude-pattern>build/</exclude-pattern>
<exclude-pattern>vendor/</exclude-pattern>
<exclude-pattern>node_modules/</exclude-pattern>
<rule ref="../../../phpcs.xml.dist"/>
</ruleset>
The ../../../phpcs.xml.dist path resolves to the root ruleset. Adjust the relative path if the plugin nesting differs.
Root package.json — add the plugin to the workspaces array:
"workspaces": [
"packages/plugins/<plugin-slug>"
]
Root composer.json — add a path repository and require the package:
"repositories": [
{
"type": "path",
"url": "packages/plugins/<plugin-slug>"
}
]
And in require:
"dekode/<plugin-slug>": "*"
composer install # symlinks plugin into public/content/plugins/
npm install # registers workspace
npm run build # verify build passes
npm run lint:js
npm run lint:css
composer lint
After composer install, confirm the plugin is symlinked into public/content/plugins/<plugin-slug>/ and appears in the WordPress plugin list.
Confirm namespace matches the PSR-4 map in composer.json.
build/*/block.php via glob — never require individual blocks manually in plugin.phppublic/: Composer symlinks the plugin automatically; never manually copy files into public/content/vendor/: All dependencies managed by ComposerWhen done:
packages/plugins/<plugin-slug>/plugin.php — WordPress plugin entry pointpackages/plugins/<plugin-slug>/composer.json — PHP package metadatapackages/plugins/<plugin-slug>/package.json — JS build configpackages/plugins/<plugin-slug>/phpcs.xml — PHP linting rulesetpackages/plugins/<plugin-slug>/src/ — source directory (blocks for block-library; PHP classes for general)packages/plugins/<plugin-slug>/languages/ — translations directoryThe plugin is symlinked into public/content/plugins/<plugin-slug>/ after composer install and is ready to receive blocks via the add-block skill.