From wordpress-expert
Analyzes code quality in custom WordPress themes and plugins using two-pass approach: quick grep scans for deprecated functions and anti-patterns, then AI contextual review of flagged PHP/JS files.
npx claudepluginhub dr-robert-li/cowork-wordpress-expertThis skill uses the workspace's default tool permissions.
This skill performs comprehensive code quality analysis on custom WordPress code using a tiered approach to maximize efficiency while providing deep, actionable insights.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
This skill performs comprehensive code quality analysis on custom WordPress code using a tiered approach to maximize efficiency while providing deep, actionable insights.
Analyze: Active theme + custom (non-WP.org) plugins only Skip: Core WordPress files and plugins from the WordPress.org repository (version audit handles those)
Before analysis, determine which code to scan based on the connected WordPress site.
Get the active theme (parent) and child theme if applicable:
# Get active parent theme
ssh {user}@{host} "cd {wp_path} && {wp_cli_path} option get template"
# Get active stylesheet (child theme if different from template)
ssh {user}@{host} "cd {wp_path} && {wp_cli_path} option get stylesheet"
Scan targets: All PHP and JS files in:
.sites/{site-name}/wp-content/themes/{template}/ (parent theme).sites/{site-name}/wp-content/themes/{stylesheet}/ (child theme if different)For each plugin directory in .sites/{site-name}/wp-content/plugins/, determine if it's a custom plugin or from WordPress.org:
WP.org Plugin Detection Heuristic:
readme.txt exists with "Stable tag:" headercurl -s "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]={dirname}" | grep -q '"error"'
Alternative: Check plugin headers for "Plugin URI" pointing to wordpress.org domains
Before proceeding, output a list of all selected targets:
Code Quality Scan Targets:
- Theme: {theme-name} ({file-count} files)
- Child Theme: {child-theme-name} ({file-count} files) [if applicable]
- Custom Plugins:
- {plugin-1-name} ({file-count} files)
- {plugin-2-name} ({file-count} files)
Total scan targets: {N} files across {M} directories
Use grep to scan locally synced files for known anti-patterns. This provides fast initial detection before deeper AI analysis.
Detect usage of deprecated or removed WordPress and PHP functions:
grep -rn -E "(mysql_query|mysql_connect|mysql_real_escape_string|get_bloginfo\s*\(\s*['\"]url|wp_specialchars|get_settings\s*\(|create_function|[^a-z]each\s*\()" {target_dir} --include="*.php"
Pattern matches:
mysql_query, mysql_connect, mysql_real_escape_string - Removed in PHP 7+get_bloginfo('url') - Use home_url() insteadwp_specialchars - Use esc_html() insteadget_settings - Use get_option() insteadcreate_function - Use closures/anonymous functions insteadeach() - Use foreach insteadFor each match: Record file path, line number, matched function, and pattern category.
Detect potentially unsafe database queries:
grep -rn '\$wpdb->query\|\$wpdb->get_results\|\$wpdb->get_row\|\$wpdb->get_var' {target_dir} --include="*.php"
For each match:
prepare appears within the context$) appear directly in SQL strings without prepare()prepare() found or if direct variable interpolation detectedHigh-risk patterns:
$wpdb->query("SELECT ... WHERE field = {$_GET['id']}")$wpdb->get_results("... WHERE ... " . $var)Detect direct superglobal usage without sanitization:
grep -rn '\$_GET\[\|\$_POST\[\|\$_REQUEST\[' {target_dir} --include="*.php"
For each match:
sanitize_*, esc_*, absint(), intval(), wp_unslash()Exception: Variable assignment followed by sanitization on next line is acceptable.
Detect form/AJAX handlers processing POST data without nonce checks:
grep -rn 'wp_ajax_\|admin_post_' {target_dir} --include="*.php"
For each AJAX/form handler function:
wp_verify_nonce or check_ajax_referer$_POST usage found without nonce verification in the same functionDetect potential exposed secrets:
grep -rn -E "(api_key|apikey|api_secret|password|secret_key|access_token)\s*=\s*['\"][^'\"]+['\"]" {target_dir} --include="*.php" --include="*.js"
For each match:
Note: Constants defined as empty strings or obvious placeholders are Info-level only.
Detect dangerous extract() usage which can overwrite variables:
grep -rn "extract\s*(" {target_dir} --include="*.php"
Flag all matches - extract() is considered dangerous as it can:
Recommendation: Use explicit array key access instead.
Detect potential local/remote file inclusion vulnerabilities:
grep -rn -E "(include|require)(_once)?\s*\(.*\\\$_(GET|POST|REQUEST)" {target_dir} --include="*.php"
For each match:
After completing all pattern scans, create a summary:
Pass 1 Results: {N} potential issues detected across {M} files
Flagged Files (for Pass 2 deep analysis):
- {file1.php} - {count} matches [{pattern-types}]
- {file2.php} - {count} matches [{pattern-types}]
...
For each file with pattern matches from Pass 1, perform deep contextual analysis.
Read the complete file to understand context, architecture, and patterns.
Analyze the file for WordPress-specific code quality issues:
Hook Usage Patterns:
Class Structure & Separation of Concerns:
Error Handling:
WP_Error objects used for API boundaries instead of exceptions?Script/Style Enqueueing:
wp_enqueue_*?<script> or <link> tags in PHP files?Database Operations:
dbDelta()?$wpdb->prefix used correctly for custom tables?WordPress API Usage:
For each pattern match found in Pass 1, analyze with full context:
Determine if it's a real issue or false positive:
$wpdb->query actually missing prepare(), or is it used with a safe static string?$_GET usage actually unsanitized, or is sanitization on the next line?Classify severity with context:
Beyond pattern matches, identify broader code quality problems:
Architecture Issues:
Performance Issues:
Security Beyond Patterns:
current_user_can())Each finding must be structured with the following fields:
{
"id": "CODE-{CHECK_TYPE}-{3-char-hash}",
"severity": "Critical | Warning | Info",
"category": "Code Quality",
"title": "Descriptive finding title",
"summary": "One sentence non-technical explanation for stakeholders",
"detail": "Technical explanation with problematic code snippet and context",
"location": "{file-path}:{line-number}",
"fix": {
"before": "// Problematic code snippet",
"after": "// Fixed code snippet"
}
}
Generate deterministic IDs based on finding type and location:
Format: CODE-{TYPE}-{HASH}
Type Codes:
DEPR - Deprecated function usageSQLI - SQL injection riskSANI - Missing sanitizationNONC - Missing nonce verificationCRED - Hardcoded credentialsEXTR - extract() usageINCL - Unsafe file inclusionARCH - Architecture/design issuePERF - Performance issueHOOK - Hook usage issueAUTH - Missing authorization checkHash: First 3 characters of MD5 hash of {file-path}:{line-number}
Example IDs:
CODE-SQLI-a3f - SQL injection at specific locationCODE-DEPR-b12 - Deprecated function usageCODE-NONC-c9d - Missing nonce checkCritical:
Warning:
Info:
Write non-technical summaries that stakeholders can understand:
Good: "A database query is built using user-provided data without safety checks, which could allow attackers to manipulate the database."
Bad: "$wpdb->prepare() not used on line 142."
Provide technical context with code evidence:
Detail: The function `handle_user_query()` at wp-content/themes/custom/functions.php:142
passes user input directly into a SQL query without using $wpdb->prepare():
$results = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE ID = {$_GET['id']}");
This allows an attacker to inject arbitrary SQL by manipulating the 'id' parameter.
Provide before/after code snippets showing the exact fix:
Example 1: SQL Injection
Before:
$results = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE ID = {$_GET['id']}");
After:
$post_id = absint($_GET['id']);
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE ID = %d", $post_id));
Example 2: Missing Sanitization
Before:
$user_name = $_POST['name'];
update_user_meta($user_id, 'display_name', $user_name);
After:
$user_name = sanitize_text_field($_POST['name']);
update_user_meta($user_id, 'display_name', $user_name);
Example 3: Deprecated Function
Before:
$site_url = get_bloginfo('url');
After:
$site_url = home_url();
Example 4: extract() Usage
Before:
extract($_POST);
echo $user_email;
After:
$user_email = isset($_POST['user_email']) ? sanitize_email($_POST['user_email']) : '';
echo $user_email;
Return all findings as a JSON array that can be processed by the reporting system:
[
{
"id": "CODE-SQLI-a3f",
"severity": "Critical",
"category": "Code Quality",
"title": "SQL query without prepared statement",
"summary": "A database query is built using user-provided data without safety checks, which could allow attackers to manipulate the database.",
"detail": "The function handle_user_query() at wp-content/themes/custom/functions.php:142 passes user input directly into SQL without using $wpdb->prepare():\n\n $wpdb->get_results(\"SELECT * FROM {$wpdb->posts} WHERE ID = {$_GET['id']}\")\n\nThis allows SQL injection via the 'id' parameter.",
"location": "wp-content/themes/custom/functions.php:142",
"fix": {
"before": "$wpdb->get_results(\"SELECT * FROM {$wpdb->posts} WHERE ID = {$_GET['id']}\")",
"after": "$post_id = absint($_GET['id']);\n$wpdb->get_results($wpdb->prepare(\"SELECT * FROM {$wpdb->posts} WHERE ID = %d\", $post_id))"
}
},
{
"id": "CODE-DEPR-b12",
"severity": "Warning",
"category": "Code Quality",
"title": "Deprecated function get_bloginfo('url')",
"summary": "The code uses an outdated WordPress function that may be removed in future versions.",
"detail": "At wp-content/themes/custom/header.php:23, get_bloginfo('url') is used:\n\n $home = get_bloginfo('url');\n\nThis function parameter is deprecated since WordPress 2.2. It may be removed in future versions.",
"location": "wp-content/themes/custom/header.php:23",
"fix": {
"before": "$home = get_bloginfo('url');",
"after": "$home = home_url();"
}
}
]
After completing analysis, provide a summary:
=== Code Quality Analysis Complete ===
Targets Analyzed:
- {N} files across {M} themes/plugins
Pass 1 (Pattern Scan):
- {X} potential issues detected
- {Y} files flagged for deep analysis
Pass 2 (AI Analysis):
- {Z} confirmed issues
- {W} false positives dismissed
- {V} additional issues discovered
Total Findings: {total}
- Critical: {count}
- Warning: {count}
- Info: {count}
Output: JSON findings array ready for report generation
Note: This skill is designed to work with locally synced WordPress files (via rsync). All file paths are relative to .sites/{site-name}/ directory. SSH commands are used only for WordPress information queries (active theme detection), not for code analysis itself.