Skip to content

Add WordPress integration skill for self-hosted sites - #279

Merged
gewenyu99 merged 4 commits into
PostHog:mainfrom
Ref34t:pr-wordpress-skill
Jul 27, 2026
Merged

Add WordPress integration skill for self-hosted sites#279
gewenyu99 merged 4 commits into
PostHog:mainfrom
Ref34t:pr-wordpress-skill

Conversation

@Ref34t

@Ref34t Ref34t commented Jul 25, 2026

Copy link
Copy Markdown

Adds a wordpress variant to the integration skill group, plus a minimal example plugin demonstrating client-side autocapture (matching the existing WordPress docs snippet pattern) combined with one server-side PHP SDK capture on the comment_post action.

Scoped to self-hosted WordPress (VIP, WP Engine, Pantheon, or any host with file access) rather than WordPress.com, since the coding agent needs filesystem access the way it already does for the Laravel and PHP skills.

Registered everywhere laravel and php are

laravel and php each appear in four places; WordPress initially appeared in one. The second commit closes that, so the wizard has something to say about WordPress beyond installation:

integration error-tracking feature-flags commandments.yaml
laravel
php
wordpress

The error-tracking and feature-flags entries follow the docs-only shape every existing variant in those two files uses — id, display_name, tags, docs_urls, no example app. Each is backed by a WordPress-specific rule in commandments.yaml rather than only re-pointing at the PHP docs:

  • feature flags — evaluate client-side on pages served by full-page caching (Varnish, batcache, WP Super Cache); a server-side check is baked into the cached HTML every visitor then receives
  • error tracking — WordPress catches fatals with its own WP_Fatal_Error_Handler and renders the recovery-mode page, so a fatal can be swallowed before a PHP handler reports it

The other commandments cover the rules that decide whether a WordPress integration survives: a plugin rather than the active theme's functions.php (a theme update silently removes it), the ABSPATH guard, esc_js() on the interpolated token, the token in a wp-config.php constant, client SDK for pageviews with the PHP SDK reserved for server-side actions, and a flush after each capture.

On example-app scope

The example plugin is deliberately minimal — autocapture, one server-side event, error tracking enabled in init — matching this repo's own "model airplane" philosophy and the shape of example-apps/php. An earlier draft of the plugin also demonstrated identify and feature flags; those came out. That trim is about the example app only. It is unrelated to the skill variants above, which are docs-only entries by the convention of the files they live in.

Verified

  • npm test — 137/137 passing
  • npm run build — emits integration-wordpress.zip, error-tracking-wordpress.zip, and feature-flags-wordpress.zip, with correct manifest entries
  • composer install in the example plugin resolves posthog/posthog-php cleanly

Related

PostHog/wizard#983 (draft) adds the other half — Integration.wordpress and the detector. The wizard's variant-resolution.test.ts asserts every framework in its enum resolves to a context-mill variant, so that PR depends on the framework: wordpress variant here. Merge order: this PR first, then the wizard one.

Adds a wordpress variant to the integration skill group, plus a
minimal example plugin demonstrating client-side autocapture
(matching the existing WordPress docs snippet pattern) combined with
one server-side PHP SDK capture on the comment_post action.

Scoped to self-hosted WordPress (VIP, WP Engine, Pantheon, or any
host with file access) rather than WordPress.com, since the coding
agent needs filesystem access the way it already does for the
Laravel and PHP skills.

Kept intentionally small to match this repo's own 'model airplane'
example-app philosophy — an earlier draft also covered identify,
feature flags, and exception tracking, but that was more than needed
to prove the skill builds correctly.

Verified: npm test (137/137 passing), npm run build (emits
integration-wordpress.zip with correct manifest entry), composer
install resolves posthog/posthog-php cleanly.
@Ref34t
Ref34t requested a review from a team as a code owner July 25, 2026 10:51
…ents

The integration skill knew WordPress, but the rest of the system did not:
error-tracking and feature-flags list php and laravel as variants, and
commandments.yaml carries per-framework rules for both. WordPress had an
entry in none of them, so the wizard could install PostHog on a WordPress
site but had nothing to say about error tracking or feature flags there.

- error-tracking + feature-flags: add a wordpress variant alongside laravel,
  pointing at the existing PHP installation docs plus the WordPress library doc
- commandments.yaml: seven WordPress rules (plugin over functions.php, ABSPATH
  guard, esc_js on the token, wp-config constant over hardcoding, client SDK for
  pageviews, flush after capture, client-side flags behind full-page caching)
- example app: enable error_tracking in PostHog::init, matching example-apps/php
- README: add Purpose, Code Structure, Key Implementation Patterns, Running
  Without PostHog, and Next Steps, matching the example-apps/php README shape

npm test 137/137. npm run build now emits integration-wordpress.zip,
error-tracking-wordpress.zip, and feature-flags-wordpress.zip.
@Ref34t

Ref34t commented Jul 25, 2026

Copy link
Copy Markdown
Author

Pushed a second commit that widens this PR.

The first commit taught the integration skill about WordPress. While checking it against how laravel and php are wired up, I noticed they each appear in four places, and WordPress only appeared in one:

integration error-tracking feature-flags commandments.yaml
laravel
php
wordpress (before)

So the wizard could install PostHog on a WordPress site, but had nothing to say about error tracking or feature flags there. The new commit fixes that:

  • error-tracking + feature-flags: a wordpress variant alongside laravel, pointing at the existing PHP installation docs plus the WordPress library doc
  • commandments.yaml: seven WordPress rules — plugin instead of functions.php (a theme update wipes it), ABSPATH guard, esc_js on the interpolated token, read the token from a wp-config.php constant, client SDK for pageviews and the PHP SDK only for real server-side hooks, flush after capture, and evaluate feature flags client-side behind full-page caching (Varnish/batcache/WP Super Cache bake a server-side flag check into the HTML every visitor then receives)
  • example app: error_tracking enabled in PostHog::init(...), matching example-apps/php
  • README: added Purpose, Code Structure, Key Implementation Patterns, Running Without PostHog, and Next Steps, to match the example-apps/php README shape

npm test → 137/137. npm run build now emits integration-wordpress.zip, error-tracking-wordpress.zip, and feature-flags-wordpress.zip.

Happy to split the commandments/skill registration into its own PR if you'd rather keep this one scoped to the integration skill.

@Ref34t

Ref34t commented Jul 25, 2026

Copy link
Copy Markdown
Author

Follow-up: I opened PostHog/wizard#983 (draft) with the other half of this — Integration.wordpress plus detection for the four shapes a WordPress tree takes (installed site, unpacked core, Composer-managed/Bedrock, standalone plugin or theme directory).

Worth flagging the link, since the wizard's own tests enforce it: variant-resolution.test.ts asserts every framework in the Integration enum resolves to a context-mill variant, so Integration.wordpress only passes once the integration-wordpress variant in this PR — the one carrying framework: wordpress — is released. Merge order is this PR first, then the wizard one.

Kept the wizard PR as a draft for that reason. No rush on either; happy to adjust the approach on both if you'd rather they land differently.

The wordpress variant added to the error-tracking skill re-pointed at the PHP
docs without carrying any WordPress-specific guidance. WordPress catches fatals
with its own WP_Fatal_Error_Handler and renders the recovery-mode page, so a
fatal can be swallowed before a PHP handler reports it — that is the rule worth
shipping, and it gives the variant content of its own.

npm test 137/137. npm run build emits it into error-tracking-wordpress.zip.
@Ref34t

Ref34t commented Jul 26, 2026

Copy link
Copy Markdown
Author

This is ready for review whenever someone has time.

Summary of where it stands:

  • WordPress is now registered everywhere laravel and php are — integration, error-tracking, feature-flags, and commandments.yaml — with the two skill variants following the docs-only shape those files already use, each backed by a WordPress-specific rule rather than only re-pointing at the PHP docs
  • example plugin is deliberately minimal, matching example-apps/php in size and shape
  • npm test 137/137, npm run build emits integration-wordpress.zip, error-tracking-wordpress.zip, and feature-flags-wordpress.zip

One thing worth knowing when you schedule it: PostHog/wizard#983 (the Integration.wordpress detector) is blocked on this. The wizard's variant-resolution.test.ts asserts every framework in its enum resolves to a context-mill variant, so that PR can't pass CI until the framework: wordpress variant here is released. It's a draft for that reason. No urgency from my side — just flagging the order so it isn't a surprise.

Happy to split this into separate PRs, narrow the scope, or change the approach if any of it cuts against how you'd want it done.

@gewenyu99 gewenyu99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks great! I have a question about the example, and maybe we don't even need an example?

My thought is that wordpress sites like this should basically get the autocapture snippet and that's about it.

Generally, that's enough for the type of static sites you'd build on Wordpress.

Is my read accurate?


foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
$line = trim($line);
if ($line === '' || str_starts_with($line, '#') || !str_contains($line, '=')) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This honestly feels pretty silly.

Is there another good built in utility for environment configs? Surely there's another way :P

@gewenyu99
gewenyu99 merged commit 61c75a7 into PostHog:main Jul 27, 2026
9 checks passed
@Ref34t

Ref34t commented Jul 27, 2026

Copy link
Copy Markdown
Author

Thanks for the merge!

To answer your question for the record: your read is accurate for most WordPress — content sites get the snippet and they're done, and that's what libraries/wordpress.md already teaches. Where it stops being enough is WooCommerce. Autocapture sees the "Place order" click and the order-received pageview, but form values are deliberately not captured, so nothing carries the revenue or the line items — woocommerce_thankyou has the full order. And some order events never touch the customer's browser at all: subscription renewals, refunds, and the moment a bank-transfer order actually gets paid. That's what the server-side half of the example (and the "reserve PostHog::capture for real server-side actions" commandment) is there for.

On the .env parser — you were right, and it contradicted a commandment in this same PR. 6ad0ca0 (included in the merge) replaced it: the plugin now reads POSTHOG_PROJECT_TOKEN / POSTHOG_HOST from wp-config.php, WordPress's built-in place for config and the same file that already holds DB_PASSWORD. The parser and .env.example are gone.

Next on my side: PostHog/wizard#983 has the wizard-side half of this — the Integration.wordpress enum entry plus detection — and can come out of draft now that this is on main.

Ref34t pushed a commit to Ref34t/wizard that referenced this pull request Jul 27, 2026
The wizard had no way to recognize a WordPress project, so `npx @posthog/wizard`
in a WordPress directory detected nothing and fell through to the Node fallback
whenever a theme build had left a package.json behind.

- `Integration.wordpress`, ordered after laravel and before the language
  fallbacks, so a site with a theme package.json resolves to WordPress
- `src/frameworks/wordpress/` — a FrameworkConfig following the shape in
  .claude/skills/adding-framework-support, with composer package management and
  no package.json checks
- detection covers the four real shapes: an installed site (wp-config.php,
  wp-load.php, wp-includes/version.php), unpacked core before install
  (wp-config-sample.php), a Composer-managed install where core is not in the
  root (roots/wordpress, johnpbloch/wordpress, wpackagist-*), and a standalone
  plugin or theme directory identified by its `Plugin Name:` / `Theme Name:`
  header
- context gathering reports project type, plugins directory (including
  Bedrock's web/app/plugins), and an existing plugin entry file so the agent
  extends that plugin instead of creating a second one
- prompts carry the WordPress-specific rules: plugin over functions.php,
  ABSPATH guard, esc_js on the token, client SDK for pageviews and the PHP SDK
  for server-side actions, flush after capture
- version read from $wp_version in wp-includes/version.php, bucketed as 6.4.x
- 9 detection tests, including the negative cases (a Laravel project and a
  plain Composer PHP project must not be claimed)

Depends on PostHog/context-mill#279, which adds the matching
`integration-wordpress` variant with `framework: wordpress`. The pinned
skill-menu fixture in variant-resolution.test.ts is updated to match — without
that variant released, the cross-repo contract test fails for wordpress.

pnpm build, pnpm test (1576 passing), pnpm fix — all clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants