Install
$ agentstack add skill-iwritec0de-wp-dev-wordpress-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
WordPress Development Patterns
This skill covers common WordPress development patterns including Custom Post Types, taxonomies, meta boxes, REST API endpoints, Settings API, cron, transients, and enqueue patterns.
Custom Post Types
add_action( 'init', 'myplugin_register_post_types' );
function myplugin_register_post_types(): void {
$labels = array(
'name' => _x( 'Events', 'post type general name', 'myplugin' ),
'singular_name' => _x( 'Event', 'post type singular name', 'myplugin' ),
'menu_name' => _x( 'Events', 'admin menu', 'myplugin' ),
'add_new' => _x( 'Add New', 'event', 'myplugin' ),
'add_new_item' => __( 'Add New Event', 'myplugin' ),
'edit_item' => __( 'Edit Event', 'myplugin' ),
'new_item' => __( 'New Event', 'myplugin' ),
'view_item' => __( 'View Event', 'myplugin' ),
'search_items' => __( 'Search Events', 'myplugin' ),
'not_found' => __( 'No events found.', 'myplugin' ),
'not_found_in_trash' => __( 'No events found in Trash.', 'myplugin' ),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_rest' => true, // Required for Gutenberg
'query_var' => true,
'rewrite' => array( 'slug' => 'events' ),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 20,
'menu_icon' => 'dashicons-calendar-alt',
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ),
);
register_post_type( 'myplugin_event', $args );
}
Custom Taxonomies
add_action( 'init', 'myplugin_register_taxonomies' );
function myplugin_register_taxonomies(): void {
$labels = array(
'name' => _x( 'Event Types', 'taxonomy general name', 'myplugin' ),
'singular_name' => _x( 'Event Type', 'taxonomy singular name', 'myplugin' ),
'search_items' => __( 'Search Event Types', 'myplugin' ),
'all_items' => __( 'All Event Types', 'myplugin' ),
'edit_item' => __( 'Edit Event Type', 'myplugin' ),
'update_item' => __( 'Update Event Type', 'myplugin' ),
'add_new_item' => __( 'Add New Event Type', 'myplugin' ),
'new_item_name' => __( 'New Event Type Name', 'myplugin' ),
'menu_name' => __( 'Event Types', 'myplugin' ),
);
register_taxonomy( 'myplugin_event_type', array( 'myplugin_event' ), array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_rest' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'event-type' ),
) );
}
Meta Boxes
add_action( 'add_meta_boxes', 'myplugin_add_meta_boxes' );
add_action( 'save_post_myplugin_event', 'myplugin_save_meta_box' );
function myplugin_add_meta_boxes(): void {
add_meta_box(
'myplugin_event_details',
__( 'Event Details', 'myplugin' ),
'myplugin_render_meta_box',
'myplugin_event',
'normal',
'high'
);
}
function myplugin_render_meta_box( WP_Post $post ): void {
wp_nonce_field( 'myplugin_save_event_details', 'myplugin_event_nonce' );
$date = get_post_meta( $post->ID, '_myplugin_event_date', true );
$location = get_post_meta( $post->ID, '_myplugin_event_location', true );
?>
">
" class="widefat">
'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
) );
add_settings_section(
'myplugin_general_section',
__( 'General Settings', 'myplugin' ),
'myplugin_general_section_cb',
'myplugin-settings'
);
add_settings_field(
'myplugin_api_key',
__( 'API Key', 'myplugin' ),
'myplugin_api_key_field_cb',
'myplugin-settings',
'myplugin_general_section'
);
}
function myplugin_general_section_cb(): void {
echo '' . esc_html__( 'Configure your plugin settings.', 'myplugin' ) . '';
}
function myplugin_api_key_field_cb(): void {
$value = get_option( 'myplugin_api_key', '' );
echo '';
}
function myplugin_render_settings_page(): void {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
?>
WP_REST_Server::READABLE,
'callback' => 'myplugin_rest_get_events',
'permission_callback' => '__return_true', // Public endpoint
'args' => array(
'per_page' => array(
'default' => 10,
'sanitize_callback' => 'absint',
'validate_callback' => function ( $value ): bool {
return $value > 0 && $value WP_REST_Server::CREATABLE,
'callback' => 'myplugin_rest_create_event',
'permission_callback' => function (): bool {
return current_user_can( 'publish_posts' );
},
'args' => array(
'title' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
),
),
) );
register_rest_route( 'myplugin/v1', '/events/(?P\d+)', array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'myplugin_rest_get_event',
'permission_callback' => '__return_true',
'args' => array(
'id' => array(
'sanitize_callback' => 'absint',
),
),
) );
}
function myplugin_rest_get_events( WP_REST_Request $request ): WP_REST_Response {
$per_page = $request->get_param( 'per_page' );
$query = new WP_Query( array(
'post_type' => 'myplugin_event',
'posts_per_page' => $per_page,
'post_status' => 'publish',
) );
$events = array_map( function ( WP_Post $post ): array {
return array(
'id' => $post->ID,
'title' => $post->post_title,
'date' => get_post_meta( $post->ID, '_myplugin_event_date', true ),
);
}, $query->posts );
return new WP_REST_Response( $events, 200 );
}
Cron / Scheduled Events
// Schedule on activation:
register_activation_hook( __FILE__, 'myplugin_activate_cron' );
register_deactivation_hook( __FILE__, 'myplugin_deactivate_cron' );
function myplugin_activate_cron(): void {
if ( ! wp_next_scheduled( 'myplugin_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'myplugin_daily_cleanup' );
}
}
function myplugin_deactivate_cron(): void {
wp_clear_scheduled_hook( 'myplugin_daily_cleanup' );
}
add_action( 'myplugin_daily_cleanup', 'myplugin_run_cleanup' );
function myplugin_run_cleanup(): void {
// Cleanup logic here.
}
// Custom interval:
add_filter( 'cron_schedules', 'myplugin_add_cron_interval' );
function myplugin_add_cron_interval( array $schedules ): array {
$schedules['myplugin_fifteen_minutes'] = array(
'interval' => 900,
'display' => esc_html__( 'Every 15 Minutes', 'myplugin' ),
);
return $schedules;
}
Transients (Caching)
function myplugin_get_api_data(): array {
$data = get_transient( 'myplugin_api_data' );
if ( false === $data ) {
$response = wp_remote_get( 'https://api.example.com/data' );
if ( is_wp_error( $response ) ) {
return array();
}
$data = json_decode( wp_remote_retrieve_body( $response ), true );
set_transient( 'myplugin_api_data', $data, HOUR_IN_SECONDS );
}
return $data;
}
// Invalidate when settings change:
add_action( 'update_option_myplugin_api_key', function (): void {
delete_transient( 'myplugin_api_data' );
} );
Enqueue Scripts & Styles
add_action( 'wp_enqueue_scripts', 'myplugin_enqueue_public' );
add_action( 'admin_enqueue_scripts', 'myplugin_enqueue_admin' );
function myplugin_enqueue_public(): void {
wp_enqueue_style(
'myplugin-public',
plugin_dir_url( __FILE__ ) . 'public/css/style.css',
array(),
MYPLUGIN_VERSION
);
wp_enqueue_script(
'myplugin-public',
plugin_dir_url( __FILE__ ) . 'public/js/script.js',
array( 'jquery' ),
MYPLUGIN_VERSION,
true // Load in footer
);
wp_localize_script( 'myplugin-public', 'myPluginData', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'myplugin_public_nonce' ),
) );
}
function myplugin_enqueue_admin( string $hook ): void {
// Only load on our admin pages:
if ( 'settings_page_myplugin-settings' !== $hook ) {
return;
}
wp_enqueue_style(
'myplugin-admin',
plugin_dir_url( __FILE__ ) . 'admin/css/admin.css',
array(),
MYPLUGIN_VERSION
);
}
Admin Notices
add_action( 'admin_notices', 'myplugin_admin_notice' );
function myplugin_admin_notice(): void {
if ( get_option( 'myplugin_api_key' ) ) {
return;
}
$settings_url = admin_url( 'options-general.php?page=myplugin-settings' );
?>
Configure it here.', 'myplugin' ),
array( 'a' => array( 'href' => array() ) )
),
esc_url( $settings_url )
);
?>
$title,
'post_type' => 'myplugin_event',
'post_status' => 'draft',
] );
if ( is_wp_error( $post_id ) ) {
wp_send_json_error( $post_id->get_error_message() );
}
wp_send_json_success( [ 'post_id' => $post_id ] );
}
Public AJAX (No Login Required)
// Register for both logged-in and logged-out users
add_action( 'wp_ajax_myplugin_search', 'myplugin_ajax_search' );
add_action( 'wp_ajax_nopriv_myplugin_search', 'myplugin_ajax_search' );
function myplugin_ajax_search(): void {
check_ajax_referer( 'myplugin_public_nonce', 'nonce' );
$query = sanitize_text_field( wp_unslash( $_GET['q'] ?? '' ) );
$results = new WP_Query( [
'post_type' => 'myplugin_event',
'posts_per_page' => 10,
's' => $query,
'post_status' => 'publish',
] );
$items = array_map( fn( WP_Post $post ): array => [
'id' => $post->ID,
'title' => $post->post_title,
'url' => get_permalink( $post ),
], $results->posts );
wp_send_json_success( $items );
}
Enqueuing with AJAX Support
add_action( 'wp_enqueue_scripts', 'myplugin_enqueue_ajax' );
function myplugin_enqueue_ajax(): void {
wp_enqueue_script(
'myplugin-ajax',
plugin_dir_url( __FILE__ ) . 'public/js/ajax.js',
[],
MYPLUGIN_VERSION,
true
);
wp_localize_script( 'myplugin-ajax', 'myPluginAjax', [
'url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'myplugin_public_nonce' ),
] );
}
JavaScript AJAX Call
// Using fetch (modern)
async function saveData( title ) {
const formData = new FormData();
formData.append( 'action', 'myplugin_save_data' );
formData.append( 'nonce', myPluginAjax.nonce );
formData.append( 'title', title );
const response = await fetch( myPluginAjax.url, {
method: 'POST',
body: formData,
} );
const result = await response.json();
if ( result.success ) {
console.log( 'Saved:', result.data.post_id );
} else {
console.error( 'Error:', result.data );
}
}
AJAX Security Checklist
| Requirement | Implementation | |-------------|---------------| | Nonce | wp_create_nonce() + check_ajax_referer() | | Capability check | current_user_can() after nonce verification | | Input sanitization | sanitize_text_field( wp_unslash( ... ) ) | | Response format | wp_send_json_success() / wp_send_json_error() | | Public endpoint | Register both wp_ajax_ and wp_ajax_nopriv_ | | Admin-only endpoint | Register only wp_ajax_ (no nopriv) |
For Gutenberg block development patterns, see references/gutenberg-patterns.md.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: iwritec0de
- Source: iwritec0de/wp-dev
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.