@linchpinagency/skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +337 -0
  2. package/bin/install.mjs +231 -0
  3. package/package.json +44 -0
  4. package/skills/browser-automation/SKILL.md +93 -0
  5. package/skills/commit-and-release/SKILL.md +135 -0
  6. package/skills/dependency-updates/SKILL.md +102 -0
  7. package/skills/design-previews/SKILL.md +118 -0
  8. package/skills/engagement-types/SKILL.md +108 -0
  9. package/skills/investigate/SKILL.md +95 -0
  10. package/skills/project-context/SKILL.md +89 -0
  11. package/skills/quality-gates/SKILL.md +94 -0
  12. package/skills/quality-gates/references/toolchain.md +104 -0
  13. package/skills/safety-hooks/SKILL.md +121 -0
  14. package/skills/safety-hooks/scripts/check-destructive.sh +80 -0
  15. package/skills/safety-hooks/scripts/check-edit-boundary.sh +65 -0
  16. package/skills/support-triage/SKILL.md +103 -0
  17. package/skills/task-tracking/SKILL.md +243 -0
  18. package/skills/web-qa/SKILL.md +108 -0
  19. package/skills/web-qa/references/qa-checklist.md +98 -0
  20. package/skills/wordpress-blocks/SKILL.md +110 -0
  21. package/skills/wordpress-blocks/references/block-grammar.md +94 -0
  22. package/skills/wordpress-blocks/references/core-blocks.md +123 -0
  23. package/skills/wordpress-blocks/references/patterns-and-parts.md +70 -0
  24. package/skills/wordpress-blocks/references/recipes/faq.md +56 -0
  25. package/skills/wordpress-blocks/references/recipes/hero.md +74 -0
  26. package/skills/wordpress-blocks/references/recipes/pricing-table.md +77 -0
  27. package/skills/wordpress-blocks/references/tool-contract.md +167 -0
  28. package/skills/wordpress-blocks/references/validation.md +38 -0
  29. package/skills/wp-audit/SKILL.md +115 -0
  30. package/skills/wp-block-conventions/SKILL.md +134 -0
  31. package/skills/wp-block-conventions/references/block-anatomy.md +175 -0
  32. package/skills/wp-implementation-choice/SKILL.md +88 -0
  33. package/skills/wp-local-setup/SKILL.md +262 -0
  34. package/skills/wp-pressable/SKILL.md +172 -0
  35. package/skills/wp-studio-cli/SKILL.md +165 -0
  36. package/skills/write-a-linchpin-skill/SKILL.md +195 -0
  37. package/skills/write-a-linchpin-skill/references/template.md +83 -0
  38. package/upstream.json +20 -0
@@ -0,0 +1,262 @@
1
+ ---
2
+ name: wp-local-setup
3
+ description: Stand up the Linchpin baseline WordPress local environment — scaffold a new wp-content-shaped project repo (Composer-managed plugins from wpackagist.org + packagist.linchpin.com, a theme started from base-wp-theme-2026, release-please deploys) and/or wire a project repo into a WordPress Studio site by symlinking the repo in as the site's wp-content while preserving Studio's SQLite runtime pieces. Use when starting a new Linchpin WordPress project or setting up local development for an existing one.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # WordPress local setup (Linchpin baseline)
8
+
9
+ Every Linchpin WordPress project follows the same baseline, driven by three facts:
10
+
11
+ 1. **The project repo IS `wp-content`.** The repo root contains `themes/`, `plugins/`
12
+ (and sometimes `mu-plugins/`) plus tooling — not a full WordPress install. WordPress
13
+ core is never committed.
14
+ 2. **Local dev runs on [WordPress Studio](https://developer.wordpress.com/studio/).**
15
+ The Studio site's `wp-content` directory is replaced with a **symlink to the repo
16
+ checkout**, so edits in the repo are live on the local site instantly.
17
+ 3. **Plugins and themes are Composer dependencies**, resolved from
18
+ [wpackagist.org](https://wpackagist.org) (wordpress.org mirrors) and
19
+ `https://packagist.linchpin.com` (private/premium packages, `linchpin/<slug>`).
20
+ Only project-specific code — the project theme and a client-functionality plugin —
21
+ is committed; everything else is gitignored and installed.
22
+
23
+ ## When to use
24
+
25
+ **Use this when** you're starting a new WordPress project (scaffold the repo) or
26
+ setting up local development for an existing one (wire it into Studio).
27
+
28
+ **Not this for:**
29
+
30
+ - **Operating a running Studio site** (WP-CLI, credentials, `eval`) → `wp-studio-cli`.
31
+ - **Live servers** (Pressable prod/staging, deploy pipeline detail) → `wp-pressable`.
32
+ - **Seeding local content/database** → out of scope; follow that project's own docs.
33
+ - **One site's specific blocks/conventions** → that project's `AGENTS.md`/`CLAUDE.md`.
34
+
35
+ ## Part 1 — Scaffold a new project repo
36
+
37
+ Target shape (the repo root doubles as `wp-content`):
38
+
39
+ ```
40
+ <project>/
41
+ themes/<project>/ # committed — the project theme (from base-wp-theme-2026)
42
+ plugins/<project>-functionality/ # committed — client-functionality plugin (if needed)
43
+ composer.json # plugin/theme dependencies + PHP QA tooling
44
+ package.json # JS tooling (theme builds via @wordpress/scripts)
45
+ index.php # "Silence is golden."
46
+ phpcs.xml.dist # WordPress coding standards config
47
+ release-please-config.json # automated releases (see wp-pressable for the pipeline)
48
+ renovate.json # dependency automation
49
+ .gitignore # allowlist pattern — see below
50
+ ```
51
+
52
+ ### composer.json baseline
53
+
54
+ ```json
55
+ {
56
+ "name": "linchpin/<project>",
57
+ "repositories": [
58
+ { "type": "composer", "url": "https://wpackagist.org" },
59
+ { "type": "composer", "url": "https://packagist.linchpin.com" }
60
+ ],
61
+ "require": {
62
+ "php": ">=8.2"
63
+ },
64
+ "require-dev": {
65
+ "composer/installers": "^2"
66
+ },
67
+ "extra": {
68
+ "installer-paths": {
69
+ "plugins/{$name}/": ["type:wordpress-plugin"],
70
+ "themes/{$name}/": ["type:wordpress-theme"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ - wordpress.org plugins/themes → `wpackagist-plugin/<slug>` / `wpackagist-theme/<slug>`.
77
+ - Premium or shared-private packages → `linchpin/<slug>` from packagist.linchpin.com.
78
+ - A plugin built **only for this client** is committed to `plugins/` instead (and
79
+ allowlisted in `.gitignore`).
80
+ - Which plugins a project needs is project-specific — don't copy another site's list.
81
+ - Mature projects add the PHP QA stack to `require-dev` (`wp-coding-standards/wpcs`,
82
+ `phpstan/phpstan` + `szepeviktor/phpstan-wordpress`, `php-parallel-lint/php-parallel-lint`,
83
+ `friendsofphp/php-cs-fixer`) — mirror `linchpin/linchpin.com` when setting that up.
84
+
85
+ ### .gitignore baseline (allowlist pattern)
86
+
87
+ Ignore everything Composer or the runtime writes; explicitly re-include what's ours:
88
+
89
+ ```gitignore
90
+ # Plugins/themes are Composer-installed — commit only project code
91
+ /plugins/*
92
+ !plugins/<project>-functionality/
93
+ !plugins/<project>-functionality/**
94
+ /themes/*
95
+ !themes/<project>/
96
+ !themes/<project>/**
97
+ /vendor
98
+ node_modules
99
+
100
+ # WordPress runtime
101
+ debug.log
102
+ /uploads/
103
+ /upgrade/
104
+
105
+ # WordPress Studio runtime (SQLite) — never commit these
106
+ db.php
107
+ /database
108
+ /mu-plugins/sqlite-database-integration
109
+ mu-plugins/99-studio-loader.php
110
+ ```
111
+
112
+ Adding a new **committed** plugin later requires a new `!plugins/<name>/` pair —
113
+ otherwise the `/plugins/*` rule silently keeps it out of git.
114
+
115
+ ### The project theme
116
+
117
+ Start from the baseline theme, don't build from scratch:
118
+
119
+ ```bash
120
+ git clone https://github.com/linchpin/base-wp-theme-2026.git themes/<project>
121
+ rm -rf themes/<project>/.git
122
+ ```
123
+
124
+ Then rebrand: update `style.css` (`Theme Name`, `Theme URI`, `Description`,
125
+ `Text Domain`) and `package.json` (`name`), and search-replace the text domain in PHP
126
+ files. It's a block theme (`theme.json`, `templates/`, `parts/`, `patterns/`) built
127
+ with `@wordpress/scripts`:
128
+
129
+ ```bash
130
+ cd themes/<project> && npm install && npm run build # npm start = watch mode
131
+ ```
132
+
133
+ ## Part 2 — Wire the repo into a Studio site
134
+
135
+ Prereq: the `studio` CLI on `PATH` (Studio app → **Settings → General → Studio CLI**),
136
+ and the repo cloned somewhere like `~/GitHub/<project>` — **not** inside `~/Studio`.
137
+
138
+ 1. **Create a Studio site** for the project. Convention: site path `~/Studio/<project>`.
139
+ ```bash
140
+ studio create --path ~/Studio/<project> --name "<Project>"
141
+ ```
142
+ (The Studio app's **Add site** or the Studio MCP `site_create` work too.)
143
+ 2. **Stop the site** before touching its filesystem:
144
+ ```bash
145
+ studio stop --path ~/Studio/<project>
146
+ ```
147
+ 3. **Rescue Studio's SQLite runtime pieces** from the freshly provisioned
148
+ `wp-content` into the repo checkout (they're gitignored, so this is safe):
149
+ ```bash
150
+ SITE=~/Studio/<project>; REPO=~/GitHub/<project>
151
+ mkdir -p "$REPO/mu-plugins"
152
+ cp "$SITE/wp-content/db.php" "$REPO/"
153
+ cp -R "$SITE/wp-content/mu-plugins/." "$REPO/mu-plugins/" # sqlite-database-integration
154
+ cp -R "$SITE/wp-content/database" "$REPO/" # .ht.sqlite lives here
155
+ ```
156
+ (The trailing `/.` merges into an existing committed `mu-plugins/` dir instead of
157
+ nesting a second one inside it.)
158
+ Without these three the site cannot boot — Studio has no MySQL; the `db.php`
159
+ drop-in + SQLite mu-plugin **are** the database layer.
160
+ 4. **Symlink the repo in as `wp-content`:**
161
+ ```bash
162
+ mv "$SITE/wp-content" "$SITE/wp-content-studio-default" # keep as fallback
163
+ ln -s "$REPO" "$SITE/wp-content"
164
+ ```
165
+ 5. **Install dependencies and build:**
166
+ ```bash
167
+ cd "$REPO" && composer install
168
+ cd themes/<project> && npm install && npm run build
169
+ ```
170
+ 6. **Start and activate:**
171
+ ```bash
172
+ studio start --skip-browser --path ~/Studio/<project>
173
+ studio wp theme activate <project> --path ~/Studio/<project>
174
+ studio config set --debug-log --path ~/Studio/<project> # baseline: log to wp-content/debug.log
175
+ ```
176
+
177
+ If the started site can't read the symlinked files (blank page, file-not-found
178
+ errors), allow PHP to follow the symlink out of the site directory:
179
+ `studio config set --file-access all-files --path ~/Studio/<project>`. The default
180
+ `site-directory` mode normally follows the symlink fine — this is a fallback only.
181
+
182
+ ### Verify
183
+
184
+ ```bash
185
+ studio site status --path ~/Studio/<project> --format json # URL + admin credentials
186
+ studio wp theme list --status=active --path ~/Studio/<project>
187
+ studio wp plugin list --path ~/Studio/<project> # Composer-installed plugins visible
188
+ studio wp eval 'echo ABSPATH;' --path ~/Studio/<project> # runtime answers
189
+ ```
190
+
191
+ Then load the site URL — a booted front page on the project theme means the wiring is
192
+ correct. From here, day-to-day operation is `wp-studio-cli`.
193
+
194
+ ## Gotchas
195
+
196
+ - **`--path` targets the SITE, not the repo.** `studio` commands take
197
+ `~/Studio/<project>` (or run from that directory). The repo path only appears in the
198
+ symlink.
199
+ - **`wp-config.php` lives in the site root**, above `wp-content` — never in the repo.
200
+ Studio strips the MySQL `DB_*` constants; don't add them back, and don't reference
201
+ them in code (SQLite handles the connection via `db.php`).
202
+ - **Never commit the Studio runtime pieces** (`db.php`, `/database`,
203
+ `mu-plugins/sqlite-database-integration`). They're local-only; on a Pressable deploy
204
+ they would shadow the host's real MySQL setup.
205
+ - **Never delete them locally either** — the site dies without its database layer.
206
+ - **Composer writes into `plugins/` and `themes/`** (via `installer-paths`) and those
207
+ paths are gitignored — that's by design. CI runs `composer install` at deploy time;
208
+ a plugin "missing from git" is usually just Composer-managed.
209
+ - **The symlink cuts both ways:** edits in the repo are live immediately, but
210
+ `wp plugin update`/`wp plugin install` run against the live site also **write into
211
+ your repo checkout**. Manage plugin versions through `composer.json`, not the admin
212
+ or WP-CLI, or the next `composer install` reverts them.
213
+ - **Media isn't in git** (`/uploads/` is ignored). Getting real content/uploads locally
214
+ is per-project — check that project's docs.
215
+
216
+ ## Quick reference
217
+
218
+ | Task | Command |
219
+ | --- | --- |
220
+ | Clone base theme | `git clone https://github.com/linchpin/base-wp-theme-2026.git themes/<project>` (then `rm -rf .git`, rebrand) |
221
+ | Install plugins | `composer install` (repo root) |
222
+ | Add a wordpress.org plugin | `composer require wpackagist-plugin/<slug>` |
223
+ | Add a premium/shared plugin | `composer require linchpin/<slug>` (packagist.linchpin.com) |
224
+ | Build the theme | `npm run build` in `themes/<project>` (`npm start` to watch) |
225
+ | Symlink repo into Studio | `mv <site>/wp-content <site>/wp-content-studio-default && ln -s <repo> <site>/wp-content` |
226
+ | Start the site | `studio start --skip-browser --path ~/Studio/<project>` |
227
+ | Activate the theme | `studio wp theme activate <project> --path ~/Studio/<project>` |
228
+ | Site URL + credentials | `studio site status --path ~/Studio/<project> --format json` |
229
+
230
+ ## Guardrails
231
+
232
+ - **Never commit WordPress core, `wp-config.php`, or Studio's SQLite runtime**
233
+ (`db.php`, `/database`, `mu-plugins/sqlite-database-integration`) — on a real host they
234
+ shadow the live MySQL setup.
235
+ - **Never delete the Studio runtime pieces locally** either; the site dies without them.
236
+ - **Never move `wp-content` without preserving the original.** Rename it
237
+ (`wp-content-studio-default`) before symlinking, so the site can be restored.
238
+ - **Never manage plugin versions through the admin or `wp plugin update`** when the repo is
239
+ symlinked in — those writes land in your checkout and get reverted by the next
240
+ `composer install`. Versions change in `composer.json`.
241
+ - **Never commit Composer-installed plugins/themes** — `installer-paths` writes into
242
+ gitignored directories by design.
243
+ - Don't hand-edit versions or `CHANGELOG.md` in a scaffolded repo — release-please owns
244
+ them ([`commit-and-release`](../commit-and-release/SKILL.md)).
245
+
246
+ ## Done
247
+
248
+ - [ ] Repo root is `wp-content`-shaped (`themes/`, `plugins/`, tooling) with no core files.
249
+ - [ ] `composer install` resolves all plugins from wpackagist / packagist.linchpin.com.
250
+ - [ ] The Studio site's `wp-content` is a symlink to the repo, with the original preserved
251
+ and the SQLite runtime pieces intact.
252
+ - [ ] The site loads, the project theme is active, and the admin URL/credentials are known.
253
+ - [ ] `.gitignore` allowlists only project code; `git status` is clean of vendored plugins.
254
+
255
+ ## Related skills
256
+
257
+ - [`wp-studio-cli`](../wp-studio-cli/SKILL.md) — operating the running Studio site (WP-CLI
258
+ passthrough, `eval`, the PHP-WASM `ABSPATH` rule).
259
+ - [`wp-pressable`](../wp-pressable/SKILL.md) — the hosted environments this baseline deploys
260
+ to, and the release-please → `linchpin/actions` pipeline.
261
+ - [`task-tracking`](../task-tracking/SKILL.md) — tie the setup work to a ClickUp task before
262
+ committing.
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: wp-pressable
3
+ description: Operate a Pressable-hosted WordPress site (production or staging) from an agent — connect via the Pressable MCP or SSH+WP-CLI, run read-first diagnostics, and safely fix the classic "renders locally but not on production" Full-Site-Editing bug where database template/part overrides shadow the deployed theme files. Use for any WordPress project hosted on Pressable when you need to inspect or change the live server (not just the repo).
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # WordPress on Pressable (agent operations)
8
+
9
+ [Pressable](https://pressable.com/) is Automattic's managed WordPress host. Code reaches it
10
+ through a deploy pipeline (Linchpin convention: `release-please` → a published GitHub
11
+ Release → `linchpin/actions` deploy workflow), while the **database lives only on the
12
+ server**. Two facts drive everything in this skill:
13
+
14
+ 1. **Change code by deploying, never by editing files on the server.** The next deploy
15
+ overwrites server files.
16
+ 2. **Change content/templates by operating on the server database** (MCP or SSH+WP-CLI),
17
+ because **deploys never touch the database**.
18
+
19
+ Most "I shipped it but it's not on prod" tickets are a collision of those two facts. See
20
+ [The signature bug](#the-signature-bug-it-shows-locally-but-not-on-production).
21
+
22
+ ## When to use
23
+
24
+ **Use this when** you need to inspect or change a **live Pressable environment**
25
+ (production or staging) — its database, templates/parts, caches, users, or runtime state —
26
+ via the Pressable MCP or SSH+WP-CLI.
27
+
28
+ **Not this for:**
29
+
30
+ - **Local development** → use `wp-studio-cli` (Studio / PHP-WASM).
31
+ - **Shipping code** → code reaches Pressable only through the deploy pipeline; never edit
32
+ theme/plugin files on the server (the next deploy overwrites them).
33
+ - **Task tracking / commits** → that's `task-tracking`.
34
+
35
+ This skill operates the **server and its database** — nothing else.
36
+
37
+ ## Two ways in
38
+
39
+ ### A) Pressable MCP (preferred when connected)
40
+
41
+ A remote MCP server at `https://mcp.pressable.com/mcp`, authenticated with a bearer token.
42
+
43
+ ```bash
44
+ # Native HTTP transport — cleaner than the mcp-remote wrapper.
45
+ claude mcp add --transport http pressable https://mcp.pressable.com/mcp \
46
+ --header "Authorization: Bearer <TOKEN>"
47
+ ```
48
+
49
+ - **Quote the whole header as one argument.** A common footgun is
50
+ `--header Authorization:Bearer <TOKEN>` (no space / unquoted) — the shell splits it into
51
+ separate argv tokens and the server gets a malformed header, then fails to connect.
52
+ - **Restart to connect.** MCP servers connect at agent **startup**. After adding/rotating,
53
+ restart the session, then confirm with `claude mcp list` → `pressable … ✔ Connected`.
54
+ - **`401 Invalid Authorization token` = dead token.** Regenerate it from your Pressable
55
+ account and re-add the server. Verify a token fast without restarting:
56
+ ```bash
57
+ curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.pressable.com/mcp \
58
+ -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
59
+ -H "Accept: application/json, text/event-stream" \
60
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"diag","version":"1"}}}'
61
+ # 200 = good, 401 = regenerate the token
62
+ ```
63
+ - **Don't leave the raw token in `~/.claude.json`.** Pull it from a secret manager
64
+ (1Password, etc.). The token grants control of the host.
65
+
66
+ ### B) SSH + WP-CLI (always available; the fallback and the scripting route)
67
+
68
+ When the MCP is down (or you want deterministic, scriptable fixes), use SSH.
69
+
70
+ - Get SSH/SFTP credentials from the Pressable dashboard (**Sites → <site> → SSH/SFTP**).
71
+ - Connect; you land in the site root with `wp` on `PATH`. Confirm: `wp --info`.
72
+ - Everything below is plain WP-CLI, so it works identically whether you reach `wp` over SSH
73
+ or through the MCP's WP-CLI tool.
74
+
75
+ ## Guardrails
76
+
77
+ - **It's production.** Confirm with the user before any mutating action on a live prod
78
+ site; lead with read-only diagnostics and back up first.
79
+ - **Read before write.** List and inspect first; mutate only after you've confirmed the
80
+ cause.
81
+ - **Back up before any DB change:** `wp db export ~/backup-$(date +%F-%H%M).sql`.
82
+ - **Never edit theme/plugin files on the server** — deploys overwrite them. File changes go
83
+ through the repo + deploy pipeline.
84
+ - **Flush caches after content/DB changes.** Pressable runs object + edge (page) cache:
85
+ `wp cache flush`, then clear the page cache from the Pressable dashboard (or
86
+ `wp pressable-cache clear` if the helper plugin is present). Stale cache makes a correct
87
+ fix look like it didn't work.
88
+
89
+ ## The signature bug: "it shows locally but not on production"
90
+
91
+ **Symptom.** A block / pattern / section lives in the theme's template **files** and renders
92
+ on your local site, but is missing on the Pressable site — even though the theme and plugin
93
+ are deployed.
94
+
95
+ **Cause.** In block (Full-Site-Editing) themes, the moment a template or template part is
96
+ edited in the Site Editor, WordPress writes a copy to the database (a `wp_template` or
97
+ `wp_template_part` post) and **that DB copy permanently shadows the theme file**. Deploys
98
+ ship files, not the DB — so a block you added to the file never appears, because the server
99
+ keeps serving the older DB copy.
100
+
101
+ **Diagnose (read-only):**
102
+
103
+ ```bash
104
+ # 1. Is the code actually deployed? (rules out a stale release)
105
+ wp plugin get <plugin-slug> --field=version
106
+ wp eval 'var_export( WP_Block_Type_Registry::get_instance()->is_registered("<namespace/block>") );'
107
+ # -> false or an old version => deploy problem, not a DB problem. Ship/redeploy.
108
+
109
+ # 2. Which templates/parts are DB overrides that shadow the files?
110
+ wp post list --post_type=wp_template --post_status=any --fields=ID,post_name,post_title
111
+ wp post list --post_type=wp_template_part --post_status=any --fields=ID,post_name,post_title
112
+ # -> a row for the template that should show the block (e.g. front-page, page, home) is an override.
113
+
114
+ # 3. Confirm the override is what's hiding it
115
+ wp eval 'echo has_block("<namespace/block>", get_post(<ID>)->post_content) ? "HAS" : "MISSING";'
116
+ # -> "MISSING" confirms the stale DB copy is the culprit.
117
+ ```
118
+
119
+ **Fix (pick one):**
120
+
121
+ - **Clean revert to the deployed file** (recommended when templates are version-controlled
122
+ in the repo): delete the DB override so WordPress falls back to the theme file, which
123
+ already has the block in the right place.
124
+ ```bash
125
+ wp post delete <ID> --force # repeat per template
126
+ ```
127
+ ⚠️ This discards **all** Site-Editor edits to that template, reverting it entirely to the
128
+ repo's file. Admin equivalent: **Appearance → Editor → Templates → ⋯ → Clear
129
+ customizations**.
130
+ - **Surgical** (when production has intentional Site-Editor-only edits you must keep): add
131
+ the block in the Site Editor, or `wp post update <ID>` the override's `post_content`,
132
+ instead of deleting.
133
+
134
+ Then `wp cache flush` and clear the Pressable page cache.
135
+
136
+ ## Deploy & rollback awareness (Linchpin convention)
137
+
138
+ - `main` → `release-please` opens a release PR → merging it **publishes a GitHub Release** →
139
+ `linchpin/actions` deploy workflow builds and deploys to Pressable (`environment:
140
+ production`), attaching a `release.zip`.
141
+ - **Staging** deploys on push to the `staging` branch.
142
+ - **Rollback** re-deploys a previous `release.zip` (no rebuild) via the repo's
143
+ `rollback.yml` workflow.
144
+ - **Implication:** a **code** fix is not live until a release is published and deployed — a
145
+ merge to `main` alone is not on prod. A **content/template** fix is applied directly on
146
+ the server (MCP/SSH), as above, and is independent of deploys.
147
+
148
+ ## Quick reference
149
+
150
+ | Task | Command |
151
+ | --- | --- |
152
+ | Add the MCP | `claude mcp add --transport http pressable https://mcp.pressable.com/mcp --header "Authorization: Bearer <TOKEN>"` |
153
+ | Verify MCP connects | `claude mcp list` → `pressable … ✔ Connected` (restart session after adding) |
154
+ | SSH in | dashboard **Sites → <site> → SSH/SFTP**, then `wp --info` |
155
+ | Back up DB | `wp db export ~/backup-$(date +%F-%H%M).sql` |
156
+ | Deployed plugin version | `wp plugin get <plugin> --field=version` |
157
+ | Is a block registered | `wp eval 'var_export( WP_Block_Type_Registry::get_instance()->is_registered("<ns/block>") );'` |
158
+ | List template overrides | `wp post list --post_type=wp_template --post_status=any --fields=ID,post_name,post_title` |
159
+ | List part overrides | `wp post list --post_type=wp_template_part --post_status=any --fields=ID,post_name,post_title` |
160
+ | Override has block? | `wp eval 'echo has_block("<ns/block>", get_post(<ID>)->post_content) ? "HAS" : "MISSING";'` |
161
+ | Revert template to file | `wp post delete <ID> --force` (wipes that template's Site-Editor edits) |
162
+ | Flush caches | `wp cache flush` + clear page cache in the Pressable dashboard |
163
+
164
+ ## Done
165
+
166
+ - [ ] The environment you touched (production vs staging) is stated explicitly.
167
+ - [ ] Diagnosis ran read-only first, and the cause is named — deploy gap vs DB override.
168
+ - [ ] Any DB change was preceded by `wp db export` and confirmed with the user.
169
+ - [ ] No theme or plugin file was edited on the server.
170
+ - [ ] Object and page caches flushed, and the fix verified on the live URL.
171
+ - [ ] If the fix belongs in code, it's tracked back to the repo and the deploy pipeline —
172
+ the server change is not the permanent fix.
@@ -0,0 +1,165 @@
1
+ ---
2
+ name: wp-studio-cli
3
+ description: Operate a local WordPress Studio site — preferring the wordpress-studio MCP (site_list, site_start, wp_cli, validate_blocks, take_screenshot, inspect_design) and falling back to the `studio` CLI when MCP isn't connected. Use whenever working against a Linchpin local dev install, running WP-CLI locally, reading admin credentials, validating block markup, or screenshotting a local site. Studio runs PHP-WASM, so file paths inside `wp eval` must use ABSPATH (resolves to /wordpress/), never host filesystem paths.
4
+ version: 1.1.0
5
+ ---
6
+
7
+ # WordPress Studio
8
+
9
+ [WordPress Studio](https://developer.wordpress.com/studio/) is **Linchpin's default local
10
+ environment**, running each site in a **PHP-WASM** runtime. Older projects still on wp-env
11
+ or LocalWP predate that switch — new work is Studio.
12
+
13
+ Two interfaces reach the same site: the **wordpress-studio MCP** (structured tools, richer
14
+ capabilities) and the **`studio` CLI** (always present with the app). Prefer MCP; the CLI is
15
+ the fallback and the scripting route.
16
+
17
+ ## When to use
18
+
19
+ - Running WP-CLI, inspecting options/posts, or evaluating PHP against a local Studio site.
20
+ - Needing the site's admin URL or credentials.
21
+ - Validating serialized block markup, or screenshotting a local page.
22
+ - Reproducing a bug locally before touching a server.
23
+
24
+ **Not this skill:** creating the site or symlinking a repo into it —
25
+ [`wp-local-setup`](../wp-local-setup/SKILL.md). Live servers —
26
+ [`wp-pressable`](../wp-pressable/SKILL.md). Performance/accessibility audits —
27
+ [`wp-audit`](../wp-audit/SKILL.md). A legacy project on wp-env or LocalWP — use that
28
+ project's tooling ([`quality-gates`](../quality-gates/SKILL.md) detects which).
29
+
30
+ ## Preflight — pick the interface
31
+
32
+ 1. **Try MCP first.** A cheap call like `site_list` confirms it's connected.
33
+ 2. **Fall back to the CLI** when MCP is absent, erroring, or you need something scriptable
34
+ in a shell pipeline: `studio site list`.
35
+ 3. **If neither works**, say so — Studio isn't installed or the CLI isn't enabled. Don't
36
+ guess at site paths.
37
+
38
+ Whichever you use, **resolve the target site explicitly**. Never assume the current
39
+ directory is the site.
40
+
41
+ ## MCP tools (preferred)
42
+
43
+ | Need | Tool |
44
+ | --- | --- |
45
+ | List sites, find paths/URLs | `site_list` |
46
+ | Site detail, admin credentials | `site_info` |
47
+ | Start / stop a site | `site_start`, `site_stop` |
48
+ | Run any WP-CLI command | `wp_cli` |
49
+ | Validate serialized block markup | `validate_blocks` |
50
+ | Screenshot a page | `take_screenshot` |
51
+ | Inspect rendered DOM / computed styles | `inspect_design` |
52
+ | Push/pull against a connected remote | `site_push`, `site_pull` |
53
+
54
+ `wp_cli` is the general-purpose escape hatch — anything the CLI can do, it can do. Reach for
55
+ a shell only when you need to pipe output into other commands.
56
+
57
+ **`site_push` / `site_pull` move real data.** Treat them like a deploy: confirm the direction
58
+ and the target with the user first, and never push to a production-connected site from here.
59
+
60
+ ## CLI fallback
61
+
62
+ Every `studio` command takes a global `--path` pointing at the site's WordPress files. It
63
+ **defaults to the current directory**, so `cd` in or pass `--path`. Running outside a
64
+ registered site path fails with "The specified directory is not added to Studio."
65
+
66
+ ```bash
67
+ studio site list # names, paths, URLs, online status
68
+ studio site start --path ~/Studio/<site> # a site must be Online for `wp`
69
+ studio site status --path ~/Studio/<site> --format json # adminUsername/adminPassword/URL
70
+ studio wp <args> --path ~/Studio/<site> # full WP-CLI passthrough
71
+ ```
72
+
73
+ ## Eval PHP — and the ABSPATH rule
74
+
75
+ This applies to **both** interfaces — MCP `wp_cli` and `studio wp` run in the same runtime.
76
+
77
+ > **The PHP-WASM runtime cannot see your host filesystem.** Inside `eval`, file paths must
78
+ > be WASM paths. Use the `ABSPATH` constant, which resolves to `/wordpress/` — not
79
+ > `/Users/...` host paths.
80
+
81
+ ```bash
82
+ # ✅ correct — ABSPATH-relative path inside the WASM FS
83
+ studio wp eval 'echo ABSPATH;' --path ~/Studio/<site>
84
+ # -> /wordpress/
85
+
86
+ # ✅ read/render a theme file from inside the site
87
+ studio wp eval 'echo file_get_contents(ABSPATH . "wp-content/themes/<theme>/style.css");' \
88
+ --path ~/Studio/<site>
89
+
90
+ # ❌ wrong — a host path is invisible to PHP-WASM and returns nothing/false
91
+ studio wp eval 'echo file_get_contents("/Users/me/Documents/GitHub/<repo>/themes/<theme>/style.css");' \
92
+ --path ~/Studio/<site>
93
+ ```
94
+
95
+ Even when Studio **hardlinks** `wp-content/themes/<x>` and `wp-content/plugins/<x>` from a
96
+ host repo (so editor changes show up live), the PHP runtime still resolves them only via
97
+ WASM/`ABSPATH` paths.
98
+
99
+ To operate on host files (grep, list, edit), use ordinary shell tools against the **repo
100
+ path**; switch to `eval` + `ABSPATH` only when the running site must read or execute them.
101
+
102
+ ## Common recipes
103
+
104
+ ```bash
105
+ # Render a block pattern's PHP to inspect its output
106
+ studio wp eval 'ob_start(); include ABSPATH . "wp-content/themes/<theme>/patterns/<name>.php"; echo ob_get_clean();' \
107
+ --path ~/Studio/<site>
108
+
109
+ # Flush rewrite rules / caches after changes
110
+ studio wp rewrite flush --path ~/Studio/<site>
111
+ studio wp cache flush --path ~/Studio/<site>
112
+
113
+ # Search-replace a URL across the DB (dry run first!)
114
+ studio wp search-replace 'http://old.test' 'https://new.test' --dry-run --path ~/Studio/<site>
115
+ ```
116
+
117
+ ## Gotchas
118
+
119
+ - **Deprecation noise:** Studio's WP-CLI may print `Deprecated: Case statements followed
120
+ by a semicolon …` lines (from a bundled dependency). They're benign — the real result is
121
+ still on stdout. When scripting, parse stdout and ignore stderr (`2>/dev/null`), and be
122
+ ready to strip a stray leading/trailing deprecation line.
123
+ - **`--path` is required** on the CLI unless you `cd` into the site directory first.
124
+ - **Site must be Online** before `wp` — `site_start` or `studio site start`.
125
+ - **Block markup gets validated, not eyeballed.** When you write serialized block markup
126
+ into templates or content, run `validate_blocks` and repair until clean — see
127
+ [`wordpress-blocks`](../wordpress-blocks/SKILL.md).
128
+
129
+ ## Quick reference
130
+
131
+ | Task | MCP | CLI fallback |
132
+ | --- | --- | --- |
133
+ | List sites + paths | `site_list` | `studio site list` |
134
+ | Start a site | `site_start` | `studio site start --path ~/Studio/<site>` |
135
+ | Admin user/pass | `site_info` | `studio site status --path … --format json` |
136
+ | Run WP-CLI | `wp_cli` | `studio wp <args> --path ~/Studio/<site>` |
137
+ | Eval PHP | `wp_cli` (`eval`, use `ABSPATH`) | `studio wp eval '<php>' --path …` |
138
+ | Validate blocks | `validate_blocks` | — |
139
+ | Screenshot | `take_screenshot` | — |
140
+
141
+ ## Guardrails
142
+
143
+ - **Never use host paths inside `eval`** — PHP-WASM can't see them and returns `false`
144
+ rather than an error, which reads as "the file is empty" and sends you debugging the wrong
145
+ thing. Always build paths from `ABSPATH`.
146
+ - **`--dry-run` first** on anything that rewrites the database (`search-replace`, bulk
147
+ `wp post` operations). A local site is cheap to break but expensive to re-seed.
148
+ - **Never `site_push`** without explicit confirmation of direction and target — it writes to
149
+ a remote site.
150
+ - **Don't edit files through the site directory** when `wp-content` is a symlink to a repo
151
+ checkout — edit the repo, which is the same files with git history attached.
152
+ - **Don't treat a green local result as a production result.** Studio runs SQLite and
153
+ PHP-WASM; MySQL-specific behavior and server caches differ. Verify on the server via
154
+ [`wp-pressable`](../wp-pressable/SKILL.md) when it matters.
155
+ - Don't fabricate output when a command prints deprecation noise — parse stdout, and say so
156
+ if a command produced nothing.
157
+
158
+ ## Done
159
+
160
+ - [ ] Interface chosen deliberately — MCP tried first, CLI fallback only if needed.
161
+ - [ ] The target site was resolved explicitly, not assumed from the working directory.
162
+ - [ ] The site was Online before running `wp`.
163
+ - [ ] Any `eval` path is `ABSPATH`-relative.
164
+ - [ ] Destructive DB commands were dry-run first; no unconfirmed `site_push`.
165
+ - [ ] Reported output is what the command actually returned, deprecation noise excluded.