**Status**: Production Ready
/plugin marketplace add secondsky/claude-skills/plugin install wordpress-plugin-core@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/example-template.txtexamples/github-updater.phpreferences/advanced-topics.mdreferences/common-hooks.mdreferences/common-patterns.mdreferences/error-catalog.mdreferences/example-reference.mdreferences/github-auto-updates.mdreferences/plugin-architectures.mdreferences/security-checklist.mdscripts/example-script.shscripts/scaffold-plugin.shtemplates/examples/ajax-handler.phptemplates/examples/custom-post-type.phptemplates/examples/meta-box.phptemplates/examples/rest-endpoint.phptemplates/examples/settings-page.phptemplates/plugin-oop/README.mdtemplates/plugin-oop/my-oop-plugin.phptemplates/plugin-oop/uninstall.phpStatus: Production Ready Last Updated: 2025-11-27 Dependencies: None (WordPress 5.9+, PHP 7.4+) Latest Versions: WordPress 6.7+, PHP 8.0+ recommended
Three architecture patterns available (see references/plugin-architectures.md for detailed examples):
Every plugin MUST have a header comment in the main file:
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: Brief description.
* Version: 1.0.0
* Requires at least: 5.9
* Requires PHP: 7.4
* Text Domain: my-plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit;
CRITICAL: Plugin Name is required, Text Domain must match plugin slug exactly.
// 1. Unique Prefix (4-5 chars)
function mypl_init() { /* code */ }
add_action( 'init', 'mypl_init' );
// 2. ABSPATH Check (every file)
if ( ! defined( 'ABSPATH' ) ) exit;
// 3. Nonces for Forms
wp_nonce_field( 'mypl_action', 'mypl_nonce' );
// 4. Sanitize Input, Escape Output
$clean = sanitize_text_field( $_POST['input'] );
echo esc_html( $output );
// 5. Prepared Statements
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $id ) );
Rules: 4-5 chars minimum, apply to functions, classes, constants, options, transients, meta keys. Avoid wp_, __, _.
// GOOD
function mypl_init() {}
class MyPL_Settings {}
add_option( 'mypl_option', 'value' );
// BAD - Will conflict
function init() {}
class Settings {}
// WRONG
if ( is_admin() ) { /* SECURITY HOLE */ }
// CORRECT
if ( current_user_can( 'manage_options' ) ) { /* Secure */ }
Common Capabilities: manage_options (Admin), edit_posts (Editor), publish_posts (Author)
Input → Processing → Output (Sanitize → Validate → Escape):
// SANITIZATION (Input)
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
$url = esc_url_raw( $_POST['url'] );
$html = wp_kses_post( $_POST['content'] );
// VALIDATION (Logic)
if ( ! is_email( $email ) ) wp_die( 'Invalid email' );
// ESCAPING (Output)
echo esc_html( $name );
echo '<a href="' . esc_url( $url ) . '">' . esc_html( $text ) . '</a>';
Rule: Sanitize INPUT, escape OUTPUT. Never trust user data.
One-time tokens proving requests came from your site.
// Form
<form method="post">
<?php wp_nonce_field( 'mypl_action', 'mypl_nonce' ); ?>
<input type="text" name="data" />
</form>
// Verify
if ( ! wp_verify_nonce( $_POST['mypl_nonce'], 'mypl_action' ) ) wp_die( 'Security check failed' );
// AJAX
check_ajax_referer( 'mypl-ajax-nonce', 'nonce' );
CRITICAL: Always use $wpdb->prepare() for user input.
// WRONG - SQL Injection
$wpdb->get_results( "SELECT * FROM {$wpdb->prefix}table WHERE id = {$_GET['id']}" );
// CORRECT
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $_GET['id'] ) );
Placeholders: %s (String), %d (Integer), %f (Float)
LIKE Queries: Use $wpdb->esc_like() before adding wildcards:
$search = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare( "... WHERE title LIKE %s", $search ) );
✅ Use unique prefix (4-5 chars) for all global code (functions, classes, options, transients)
✅ Add ABSPATH check to every PHP file: if ( ! defined( 'ABSPATH' ) ) exit;
✅ Check capabilities (current_user_can()) not just is_admin()
✅ Verify nonces for all forms and AJAX requests
✅ Use $wpdb->prepare() for all database queries with user input
✅ Sanitize input with sanitize_*() functions before saving
✅ Escape output with esc_*() functions before displaying
✅ Flush rewrite rules on activation when registering custom post types
✅ Use uninstall.php for permanent cleanup (not deactivation hook)
✅ Follow WordPress Coding Standards (tabs for indentation, Yoda conditions)
❌ Never use extract() - Creates security vulnerabilities
❌ Never trust $_POST/$_GET without sanitization
❌ Never concatenate user input into SQL - Always use prepare()
❌ Never use is_admin() alone for permission checks
❌ Never output unsanitized data - Always escape
❌ Never use generic function/class names - Always prefix
❌ Never use short PHP tags <? or <?= - Use <?php only
❌ Never delete user data on deactivation - Only on uninstall
❌ Never register uninstall hook repeatedly - Only once on activation
❌ Never use register_uninstall_hook() in main flow - Use uninstall.php instead
This skill prevents 20 documented issues:
Error: Database compromised via unescaped user input
Source: https://patchstack.com/articles/sql-injection/ (15% of all vulnerabilities)
Why It Happens: Direct concatenation of user input into SQL queries
Prevention: Always use $wpdb->prepare() with placeholders
// VULNERABLE
$wpdb->query( "DELETE FROM {$wpdb->prefix}table WHERE id = {$_GET['id']}" );
// SECURE
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}table WHERE id = %d", $_GET['id'] ) );
Error: Malicious JavaScript executed in user browsers Source: https://patchstack.com (35% of all vulnerabilities) Why It Happens: Outputting unsanitized user data to HTML Prevention: Always escape output with context-appropriate function
// VULNERABLE
echo $_POST['name'];
echo '<div class="' . $_POST['class'] . '">';
// SECURE
echo esc_html( $_POST['name'] );
echo '<div class="' . esc_attr( $_POST['class'] ) . '">';
Error: Unauthorized actions performed on behalf of users
Source: https://blog.nintechnet.com/25-wordpress-plugins-vulnerable-to-csrf-attacks/
Why It Happens: No verification that requests originated from your site
Prevention: Use nonces with wp_nonce_field() and wp_verify_nonce()
// VULNERABLE
if ( $_POST['action'] == 'delete' ) {
delete_user( $_POST['user_id'] );
}
// SECURE
if ( ! wp_verify_nonce( $_POST['nonce'], 'mypl_delete_user' ) ) {
wp_die( 'Security check failed' );
}
delete_user( absint( $_POST['user_id'] ) );
Error: Regular users can access admin functions
Source: WordPress Security Review Guidelines
Why It Happens: Using is_admin() instead of current_user_can()
Prevention: Always check capabilities, not just admin context
// VULNERABLE
if ( is_admin() ) {
// Any logged-in user can trigger this
}
// SECURE
if ( current_user_can( 'manage_options' ) ) {
// Only administrators can trigger this
}
Error: PHP files executed outside WordPress context Source: WordPress Plugin Handbook Why It Happens: No ABSPATH check at top of file Prevention: Add ABSPATH check to every PHP file
// Add to top of EVERY PHP file
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
For comprehensive error coverage beyond the Top 5, load references/error-catalog.md which includes:
Each issue includes: error description, source, why it happens, prevention code, impact severity, and frequency.
Choose the right architecture for your plugin size and complexity:
Simple (Functions Only)
OOP (Singleton Pattern)
PSR-4 (Namespaced + Composer)
For full implementation examples with directory structure, activation hooks, and code patterns, load references/plugin-architectures.md.
This skill provides production-ready patterns for 8 common WordPress plugin features:
For complete implementation code, load references/common-patterns.md when implementing any of these features. Each pattern includes:
Plugins hosted outside WordPress.org can provide automatic updates using Plugin Update Checker by YahnisElsts (recommended).
Quick Solutions:
For complete implementation, load references/github-auto-updates.md which includes:
Required:
Optional:
Fatal Errors: Enable WP_DEBUG, check wp-content/debug.log, verify prefixed names
404 on CPTs: Flush rewrite rules (add flush_rewrite_rules(); temporarily in wp-admin)
Nonce Failures: Check matching names, correct action, 24-hour expiration
AJAX Returns 0/-1: Verify action name matches wp_ajax_{action}, nonce sent/verified, handler hooked
HTML Stripped: Use wp_kses_post() instead of sanitize_text_field()
DB Queries Fail: Always use $wpdb->prepare(), include $wpdb->prefix, verify syntax
This skill uses progressive disclosure - main file contains essentials, reference files have detailed implementation. Load references based on your current task:
references/common-patterns.md (465 lines)Load when: Implementing specific WordPress features Contains:
references/plugin-architectures.md (220 lines)Load when: Choosing plugin structure or migrating between patterns Contains:
references/error-catalog.md (Issues #6-20, 573 lines)Load when: Debugging issues beyond Top 5 security vulnerabilities Contains:
references/advanced-topics.md (150 lines)Load when: Implementing i18n, WP-CLI, cron jobs, or dependency checking Contains:
references/security-checklist.md (527 lines)Load when: Performing security audit or reviewing code for vulnerabilities Contains:
references/github-auto-updates.md (1,224 lines)Load when: Setting up auto-updates for plugins hosted outside WordPress.org Contains:
references/common-hooks.md (234 lines)Load when: Working with WordPress hooks and need hook reference Contains:
Use this checklist to verify your plugin:
Questions? Issues?
references/error-catalog.md for additional issues #6-20This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.