From superpowers-sage
Guides adding, modifying, and debugging WordPress hooks in Sage/Acorn: actions vs filters, lifecycle order, priorities, ServiceProvider boot registration.
npx claudepluginhub codigodoleo/superpowers-sage --plugin superpowers-sageThis skill uses the workspace's default tool permissions.
When adding, modifying, or debugging WordPress hooks (actions and filters) within a Sage/Acorn project. This covers where to place hooks in the Sage architecture, understanding execution order, managing priority, using dependency injection in callbacks, and diagnosing hook-related issues.
Explains WordPress hooks for WooCommerce: actions, filters, priorities, and key hooks for products, cart, checkout. Guides adding functionality and tracing execution flow.
Guides WordPress plugin development: architecture/hooks, activation/deactivation/uninstall, admin UI/Settings API, data storage/cron, security (nonces/capabilities/sanitization), release packaging.
Manages Roots Sage WordPress themes with Acorn and Lando: environment setup, ACF blocks/fields, Blade components/composers, Tailwind v4/Vite frontend, service providers, CPTs, and generators.
Share bugs, ideas, or general feedback.
When adding, modifying, or debugging WordPress hooks (actions and filters) within a Sage/Acorn project. This covers where to place hooks in the Sage architecture, understanding execution order, managing priority, using dependency injection in callbacks, and diagnosing hook-related issues.
add_action('hook_name', $callback, $priority, $accepted_args)add_filter('hook_name', $callback, $priority, $accepted_args)See references/hook-timing.md for the full boot sequence and early/late hook guidance.
Quick reference:
plugins_loaded → after_setup_theme → init → wp_loaded → wp_enqueue_scripts → the_content
See references/acorn-hook-patterns.md for the three correct hook locations in Acorn.
Preferred: boot() method in a ServiceProvider:
class ProjectServiceProvider extends ServiceProvider
{
public function boot(): void
{
add_action('init', [$this, 'registerPostTypes']);
add_action('wp_enqueue_scripts', [$this, 'enqueueAssets']);
add_filter('the_content', [$this, 'appendProjectMeta']);
add_filter('pre_get_posts', [$this, 'modifyProjectQuery']);
}
}
// Default priority is 10, default accepted_args is 1
add_action('save_post', [$this, 'onSave'], 10, 3);
// Lower number = runs earlier
add_action('init', [$this, 'earlyInit'], 5);
add_action('init', [$this, 'lateInit'], 20);
// accepted_args must match callback parameters
add_filter('the_title', function (string $title, int $post_id) {
return $title . ' #' . $post_id;
}, 10, 2);
| Hook | Type | Typical use |
|---|---|---|
after_setup_theme | Action | Theme supports, nav menus, image sizes |
wp_enqueue_scripts | Action | Enqueue front-end CSS/JS |
init | Action | Register CPTs, taxonomies, shortcodes |
save_post | Action | Post save side effects (cache invalidation, sync) |
pre_get_posts | Action | Modify main query before execution |
the_content | Filter | Modify post content output |
rest_api_init | Action | Register REST routes |
admin_enqueue_scripts | Action | Enqueue admin CSS/JS |
See references/filter-gotchas.md for wptexturize, wpautop, and wp_kses_post pitfalls that affect Tailwind v4 themes.
Key fix for Tailwind arbitrary variants:
// In ThemeServiceProvider::boot()
add_filter('run_wptexturize', '__return_false');
did_action('hook_name') returns the expected count at the point in code where you need it.| Symptom | Cause | Fix |
|---|---|---|
| Hook never fires | Registered too late (after WordPress already fired it) | Check lifecycle order in references/hook-timing.md |
| Filter returns null | Missing return statement in filter callback | All data for that filter becomes null/empty |
| Hook fires multiple times | Common with save_post (autosaves, revisions) | Guard with wp_is_post_autosave() and wp_is_post_revision() |
| Cannot remove hook | Original was added with anonymous closure or different object instance | Cannot be removed without modifying the source |
| Wrong number of arguments | Callback receives fewer arguments than expected | Check $accepted_args parameter matches |
| Infinite loop | Filter modifies data that triggers the same filter | Use remove_action before the triggering call, then re-add |
| Hooks not firing from provider | Provider not registered | Verify provider is listed in config/app.php under providers |
$wp_filter['hook_name'] to see all registered callbacks and their priorities.config/app.php under providers.WP_DEBUG_LOG and use error_log() with timestamps at each hook point to trace execution flow.