
Wp Guard
Run an automated WordPress plugin/theme review for security, i18n, and performance right after your agent edits PHP or block code.
Overview
WP Guard is an agent skill for the Ship phase that reviews WordPress code changes for security, internationalization, and performance issues.
Install
npx skills add https://github.com/amelnagdy/guard-skills --skill wp-guardWhat is this skill?
- Post-change review prompt for security, internationalization, and performance on WordPress code
- Wrapper selection table: __, _e, esc_html__, esc_attr__, _x, _n, _nx with when-to-use guidance
- Requires combined translate-and-escape wrappers because translation files are treated as untrusted XSS input
- Text domain must be a literal string matching the plugin slug for translate.wordpress.org and static analysis
- Covers placeholders, translator comments, plurals, sentence assembly, JS i18n, dates, numbers, RTL, and multilingual-plu
Adoption & trust: 1 installs on skills.sh; 474 GitHub stars; trending (+100% hot-view momentum).
What problem does it solve?
AI agents ship WordPress patches fast but often break i18n rules, skip esc_* on translated output, or use invalid text domains that fail org review and static analysis.
Who is it for?
Solo builders shipping WordPress plugins, blocks, or themes who want a consistent agent-driven review after every PHP or JS i18n touch.
Skip if: Teams not on WordPress, greenfield non-PHP stacks, or repos where you only need generic OWASP review with zero WP i18n context.
When should I use this skill?
After WordPress code is written or changed, to review security, i18n, and performance.
What do I get? / Deliverables
You get a structured post-change review aligned to WordPress wrapper and text-domain rules so fixes land before commit, org submission, or production deploy.
- Review findings on security, i18n wrappers, text domains, and performance
- Actionable fixes mapped to WordPress core conventions
Recommended Skills
Journey fit
WP Guard is invoked after code exists, as a ship-time quality gate before merge or release—not during initial scaffolding. Review subphase is where checker skills audit agent output against platform rules before launch prep or security hardening.
How it compares
Use instead of generic PHP linters when you need WordPress.org-ready i18n and escaping rules, not just syntax checks.
Common Questions / FAQ
Who is wp-guard for?
WordPress plugin and theme authors using Claude Code, Cursor, or similar agents who need platform-correct security and i18n review after automated edits.
When should I use wp-guard?
After any WordPress code write or change during Ship review, before merge or directory submission; also when hardening an extension ahead of Launch distribution.
Is wp-guard safe to install?
Review the Security Audits panel on this Prism page for ingest-time findings; the skill guides review only and does not by itself execute shell or network actions unless your agent is configured to.
SKILL.md
READMESKILL.md - Wp Guard
interface: display_name: "WP Guard" short_description: "WordPress code guardrails for AI agents" default_prompt: "Use $wp-guard after WordPress code is written or changed to review it for security, i18n, and performance issues." # WP Guard — Internationalization Reference ## Contents - Wrapper selection - Text domain rules - Placeholders and translator comments - Plurals - Sentence assembly - JavaScript i18n - Dates, numbers, RTL - Multilingual-plugin gotchas ## Wrapper selection | Situation | Use | |---|---| | Return a translated string | `__( 'Text', 'my-plugin' )` | | Echo a translated string | `_e( 'Text', 'my-plugin' )` — or better, `esc_html_e()` | | Translate + escape for HTML | `esc_html__()` / `esc_html_e()` | | Translate + escape for attribute | `esc_attr__()` / `esc_attr_e()` | | Ambiguous word needing context | `_x( 'Post', 'verb', 'my-plugin' )` | | Plural | `_n( '%s item', '%s items', $count, 'my-plugin' )` | | Plural + context | `_nx()` | When a string is both translated and output, the combined wrappers (`esc_html__`) are required — translations are untrusted input like any other (a compromised translation file is an XSS vector). ## Text domain rules - Literal string, always: `__( 'Text', 'my-plugin' )`. Never `__( 'Text', PLUGIN_DOMAIN )`, never a variable — static analysis tools and translate.wordpress.org both fail on non-literal domains. - Must match the plugin slug exactly (the WordPress.org directory enforces this). - One domain per plugin/theme. AI agents copying snippets from other projects routinely import foreign text domains — grep for domains that don't match the project's. ## Placeholders and translator comments ```php /* translators: 1: customer name, 2: order number. */ $message = sprintf( __( 'Hi %1$s, your order #%2$d is on its way.', 'my-plugin' ), $customer_name, $order_id ); ``` - Numbered placeholders (`%1$s`) whenever there is more than one — translators reorder words. - A `/* translators: … */` comment on every string with placeholders, immediately above the line. - Never put variables or HTML soup inside the translatable string when it can sit outside. ## Plurals `_n()` exists because languages have between one and six plural forms. Never: ```php // Wrong — English-only logic. $label = $count === 1 ? __( 'item', 'my-plugin' ) : __( 'items', 'my-plugin' ); ``` Always `_n( '%s item', '%s items', $count, 'my-plugin' )`, then `sprintf()` with `number_format_i18n( $count )`. ## Sentence assembly Never build sentences by concatenation — word order differs across languages: ```php // Wrong: translators get fragments they cannot reorder. echo __( 'Imported', 'my-plugin' ) . ' ' . $count . ' ' . __( 'products', 'my-plugin' ); // Right: one string, one placeholder, full context. /* translators: %s: number of imported products. */ printf( esc_html__( 'Imported %s products.', 'my-plugin' ), number_format_i18n( $count ) ); ``` ## JavaScript i18n - `wp_set_script_translations( 'my-handle', 'my-plugin' )` after enqueueing; use `__()` from `@wordpress/i18n` in the JS. - Legacy pattern (`wp_localize_script` with pre-translated strings) is acceptable in legacy codebases — match the project. ## Dates, numbers, RTL - Dates: `wp_date()` / `date_i18n()` with the site's format options — never raw `date()` for display. - Numbers: `number_format_i18n()`. - CSS: logical properties (`margin-inline-start`, not `margin-left`) for new styles; don't hand-write directional CSS — build `-rtl.css` files with RTLCSS. Auto-loading them applies only to core styles and block.json-registered styles; classic plugin/theme handles must opt in with `wp_style_add_data( $handle, 'rtl', 'replace' )`. ## Multilingual-plugin gotchas (WPML / Polylang) - Strings stored in options/meta are NOT translated by `__()` — they need string registration (WPML String Translation / `pll_register_string`). Flag stored user-facing strings during review. - IDs are language-specific: a hardcoded `page_id` points at o