@refactco/refact-os 1.5.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.
- package/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/bin/refact-os.js +154 -0
- package/lib/adapters.js +302 -0
- package/lib/company.js +76 -0
- package/lib/frontmatter.js +30 -0
- package/lib/migrate.js +116 -0
- package/lib/project-utils.js +179 -0
- package/lib/refact-config.js +324 -0
- package/lib/scaffold.js +329 -0
- package/lib/validate.js +145 -0
- package/package.json +46 -0
- package/templates/base/AGENTS.md +9 -0
- package/templates/base/CLAUDE.md +3 -0
- package/templates/base/README.md +54 -0
- package/templates/base/agent/AGENTS.md +60 -0
- package/templates/base/agent/CLAUDE.md +7 -0
- package/templates/base/agent/claude-hooks.json +32 -0
- package/templates/base/agent/hooks/claude-sync-transcript.py +236 -0
- package/templates/base/agent/hooks/preflight-metadata.mjs +202 -0
- package/templates/base/agent/hooks/send-transcript-to-remote-server.py +238 -0
- package/templates/base/agent/hooks/sync-chat-transcript.py +188 -0
- package/templates/base/agent/hooks.json +29 -0
- package/templates/base/agent/scripts/import-project-chat-history.py +196 -0
- package/templates/base/agent/scripts/sync-asana.mjs +408 -0
- package/templates/base/agent/skills/adopt/SKILL.md +46 -0
- package/templates/base/agent/skills/close-ticket/SKILL.md +31 -0
- package/templates/base/agent/skills/extract-learnings/SKILL.md +90 -0
- package/templates/base/agent/skills/git-it/SKILL.md +138 -0
- package/templates/base/agent/skills/import-chat-history/SKILL.md +85 -0
- package/templates/base/agent/skills/ingest-input/SKILL.md +43 -0
- package/templates/base/agent/skills/open-ticket/SKILL.md +36 -0
- package/templates/base/agent/skills/process-docs/SKILL.md +69 -0
- package/templates/base/agent/skills/project-status/SKILL.md +35 -0
- package/templates/base/agent/skills/project-status/scripts/scan-status.mjs +153 -0
- package/templates/base/agent/skills/refact/SKILL.md +139 -0
- package/templates/base/agent/skills/setup-project/SKILL.md +140 -0
- package/templates/base/agent/skills/sync-asana/SKILL.md +106 -0
- package/templates/base/agent/skills/update-canonical-record/SKILL.md +28 -0
- package/templates/base/agent/skills/update-package/SKILL.md +51 -0
- package/templates/base/docs/context/project.md +30 -0
- package/templates/base/docs/decisions.md +22 -0
- package/templates/base/docs/index.md +31 -0
- package/templates/base/docs/sources/raw/.gitkeep +0 -0
- package/templates/base/docs/task/.gitkeep +0 -0
- package/templates/base/env.example +14 -0
- package/templates/base/gitignore +34 -0
- package/templates/overlays/client/agent/skills/create-deliverable/SKILL.md +29 -0
- package/templates/overlays/client/docs/deliverables/.gitkeep +0 -0
- package/templates/overlays/code/agent/skills/add-codebase/SKILL.md +239 -0
- package/templates/overlays/code/agent/skills/code-development/SKILL.md +58 -0
- package/templates/overlays/code/agent/skills/code-development/references/gitflow.md +144 -0
- package/templates/overlays/nextjs/agent/skills/nextjs-dev/SKILL.md +93 -0
- package/templates/overlays/nextjs/agent/skills/setup-netlify-deploy/SKILL.md +143 -0
- package/templates/overlays/nextjs/agent/skills/setup-nextjs-app/SKILL.md +118 -0
- package/templates/overlays/nextjs/agent/skills/setup-vercel-deploy/SKILL.md +116 -0
- package/templates/overlays/wordpress/agent/skills/install-wp-skills/SKILL.md +130 -0
- package/templates/overlays/wordpress/agent/skills/setup-kinsta-deploy/SKILL.md +201 -0
- package/templates/overlays/wordpress/agent/skills/wp-env/SKILL.md +478 -0
- package/templates/overlays/wordpress/wp-cli.yml.example +46 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wp-env
|
|
3
|
+
description: Manage the local WordPress stack via wp-env — setup, pull plugins/mu-plugins/db from staging, reset, custom local domain.
|
|
4
|
+
pattern: procedure
|
|
5
|
+
when_to_use: /refact wp-env setup | pull [plugins|mu-plugins|db] | reset | domain <host>.
|
|
6
|
+
when_not_to_use: Non-WordPress projects.
|
|
7
|
+
next_skills: []
|
|
8
|
+
sub_agents: []
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# wp-env Reference
|
|
12
|
+
|
|
13
|
+
Use this reference when the user invokes any of:
|
|
14
|
+
|
|
15
|
+
- `/refact wp-env setup` — bring up a fresh local WordPress stack with a sample DB.
|
|
16
|
+
- `/refact wp-env pull` — alias for **pull plugins + mu-plugins + db** (staging → local).
|
|
17
|
+
- `/refact wp-env pull plugins`
|
|
18
|
+
- `/refact wp-env pull mu-plugins`
|
|
19
|
+
- `/refact wp-env pull db`
|
|
20
|
+
- `/refact wp-env reset` — destroy containers + volumes and rebuild from scratch.
|
|
21
|
+
- `/refact wp-env domain set <hostname>` — front the local env with a `.local` hostname over HTTPS via Caddy.
|
|
22
|
+
- `/refact wp-env domain clear` — remove the hostname mapping and revert to `http://localhost:8888`.
|
|
23
|
+
|
|
24
|
+
## What this does
|
|
25
|
+
|
|
26
|
+
Manages the local WordPress development stack for engagements scaffolded with `/refact add codebase wordpress`. Local code lives in `apps/wordpress/wp-content/`, which is the same tree the Kinsta deploy workflows push (see [`kinsta-auto-deploy.md`](./kinsta-auto-deploy.md)). The local env mirrors staging by pulling plugins, mu-plugins, and the DB over SSH using the credentials in `agent/AGENTS.md`.
|
|
27
|
+
|
|
28
|
+
## Canonical layout
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
<project-root>/
|
|
32
|
+
├── .wp-env.json ← wp-env config (created by setup)
|
|
33
|
+
├── apps/wordpress/
|
|
34
|
+
│ └── wp-content/
|
|
35
|
+
│ ├── plugins/ ← rsync target for `pull plugins`
|
|
36
|
+
│ ├── themes/ ← (not pulled by default — usually in-repo)
|
|
37
|
+
│ └── mu-plugins/ ← rsync target for `pull mu-plugins`
|
|
38
|
+
└── package.json ← gets `wp:*` scripts on setup
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`.wp-env.json` maps `wp-content/{plugins,themes,mu-plugins}` from inside `apps/wordpress/wp-content/` so the container sees the same files the deploy workflow ships. **Never** create a parallel `./wp-content/` at the repo root — it splits the source of truth and breaks the Kinsta deploy filter.
|
|
42
|
+
|
|
43
|
+
## Optional local domain (`<project>.local`)
|
|
44
|
+
|
|
45
|
+
wp-env binds to `127.0.0.1:8888` by default. To use a hostname like `https://website.local` instead, this flow can layer a [Caddy](https://caddyserver.com) reverse proxy on top: Caddy issues a trusted local cert via its own CA, fronts wp-env on `:443`, and you get clean URLs without ports.
|
|
46
|
+
|
|
47
|
+
The hostname is stored at `.refact-os.json` › `wpEnv.localDomain` so every teammate gets the same URL after `/refact wp-env setup`. Per-project Caddy config lives outside the repo at `~/.refact/caddy/<project-slug>.caddyfile`, imported by a global `~/.refact/Caddyfile`. See **Step 4 — `domain set` / `domain clear`** below.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Preflight (always)
|
|
52
|
+
|
|
53
|
+
1. `.refact-os.json` › `stack` has a `wordpress` entry. If not, stop and ask the user to confirm — this flow is WordPress-specific.
|
|
54
|
+
2. `apps/wordpress/` exists. If not, delegate to [`add-codebase.md`](./add-codebase.md) Flow B (`/refact add codebase wordpress`), then continue.
|
|
55
|
+
3. Docker is reachable: `docker info` exits 0. If not, stop and tell the user to start Docker Desktop / Colima.
|
|
56
|
+
4. Node is **18+**: `node --version`. wp-env requires it.
|
|
57
|
+
|
|
58
|
+
For `pull` flows only — also verify the SSH fields in `agent/AGENTS.md` are filled (not the literal `<ssh-staging-…>` placeholders). If any placeholder remains, stop and tell the user to run `/refact init` first.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Step 1 — `setup`
|
|
63
|
+
|
|
64
|
+
Idempotent. Each sub-step is verified independently; skip silently if already met.
|
|
65
|
+
|
|
66
|
+
### 1a. Ensure `apps/wordpress/wp-content/` subfolders
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
mkdir -p apps/wordpress/wp-content/plugins
|
|
70
|
+
mkdir -p apps/wordpress/wp-content/themes
|
|
71
|
+
mkdir -p apps/wordpress/wp-content/mu-plugins
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 1b. Write `.wp-env.json` (skip if present)
|
|
75
|
+
|
|
76
|
+
If `.wp-env.json` already exists, **inspect it**:
|
|
77
|
+
|
|
78
|
+
- If it maps `wp-content/*` from `./apps/wordpress/wp-content/*` — leave the mappings alone. If a Caddy domain is configured, make sure neither `.wp-env.json` nor `.wp-env.override.json` defines `config.WP_HOME` / `config.WP_SITEURL`; see Step 3.
|
|
79
|
+
- If it maps from `./wp-content/*` (the legacy layout) — show the diff and ask the user before rewriting. Some older scaffolds had `wp-content/` at the root; the new convention is `apps/wordpress/wp-content/` so deploy and local share one tree.
|
|
80
|
+
|
|
81
|
+
Resolve the local URL **before** writing the config block:
|
|
82
|
+
|
|
83
|
+
- If `.refact-os.json` › `wpEnv.localDomain` is set → `LOCAL_URL="https://<that-host>"`.
|
|
84
|
+
- Otherwise → `LOCAL_URL="http://localhost:8888"`.
|
|
85
|
+
|
|
86
|
+
Template body when creating:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"$schema": "https://schemas.wp.org/trunk/wp-env.json",
|
|
91
|
+
"core": null,
|
|
92
|
+
"phpVersion": "8.2",
|
|
93
|
+
"themes": [],
|
|
94
|
+
"plugins": [],
|
|
95
|
+
"mappings": {
|
|
96
|
+
"wp-content/plugins": "./apps/wordpress/wp-content/plugins",
|
|
97
|
+
"wp-content/themes": "./apps/wordpress/wp-content/themes",
|
|
98
|
+
"wp-content/mu-plugins": "./apps/wordpress/wp-content/mu-plugins"
|
|
99
|
+
},
|
|
100
|
+
"config": {
|
|
101
|
+
"WP_DEBUG": true,
|
|
102
|
+
"WP_DEBUG_LOG": true,
|
|
103
|
+
"WP_DEBUG_DISPLAY": false,
|
|
104
|
+
"SCRIPT_DEBUG": true,
|
|
105
|
+
"WP_ENVIRONMENT_TYPE": "local"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Omit `WP_HOME` / `WP_SITEURL` from `.wp-env.json` and `.wp-env.override.json`. wp-env defines its own URL constants and may force its internal `:8888` port into WordPress URLs; when Caddy is in use, keep the public hostname in the DB plus the local-only mu-plugin from Step 3 instead.
|
|
111
|
+
|
|
112
|
+
### 1c. Install `@wordpress/env`
|
|
113
|
+
|
|
114
|
+
If `@wordpress/env` is missing from `package.json` › `devDependencies`:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install --save-dev @wordpress/env
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 1d. Add `wp:*` scripts to `package.json`
|
|
121
|
+
|
|
122
|
+
Add only the ones missing — never overwrite existing entries:
|
|
123
|
+
|
|
124
|
+
| Script | Command |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `wp:start` | `wp-env start` |
|
|
127
|
+
| `wp:stop` | `wp-env stop` |
|
|
128
|
+
| `wp:destroy` | `wp-env destroy` |
|
|
129
|
+
| `wp:clean` | `wp-env clean all` |
|
|
130
|
+
| `wp:logs` | `wp-env logs all` |
|
|
131
|
+
| `wp:cli` | `wp-env run cli wp` |
|
|
132
|
+
| `wp:shell` | `wp-env run cli bash` |
|
|
133
|
+
|
|
134
|
+
### 1e. Start the stack
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npx wp-env start
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
First run downloads images and may take a few minutes. If it fails on `port already in use`, surface the exact error — don't guess at an alternate port without asking.
|
|
141
|
+
|
|
142
|
+
### 1f. Report
|
|
143
|
+
|
|
144
|
+
Print to the user:
|
|
145
|
+
|
|
146
|
+
- Site URL: `<LOCAL_URL>` (the value resolved in 1b — either `https://<domain>` or `http://localhost:8888`).
|
|
147
|
+
- Admin URL: `<LOCAL_URL>/wp-admin` — login `admin` / `password` (wp-env defaults).
|
|
148
|
+
- Test connectivity: `npm run wp:cli -- --info`.
|
|
149
|
+
|
|
150
|
+
If a domain is configured but Caddy isn't running yet, also remind the user to run `/refact wp-env domain set <hostname>` (or, if already set, `caddy start --config ~/.refact/Caddyfile` to bring the proxy up).
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Step 2 — `pull`
|
|
155
|
+
|
|
156
|
+
`/refact wp-env pull` runs **2b → 2c → 2d** in order. The single-target variants (`pull plugins` / `pull mu-plugins` / `pull db`) run just that step. All variants share the preflight in 2a.
|
|
157
|
+
|
|
158
|
+
### 2a. Resolve SSH target from `agent/AGENTS.md`
|
|
159
|
+
|
|
160
|
+
Parse the **SSH access** section of `agent/AGENTS.md`. The init flow fills these placeholders:
|
|
161
|
+
|
|
162
|
+
| agent/AGENTS.md field | Variable |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `<ssh-staging-user>` | `SSH_USER` |
|
|
165
|
+
| `<ssh-staging-host>` | `SSH_HOST` |
|
|
166
|
+
| `<ssh-staging-port>` | `SSH_PORT` |
|
|
167
|
+
| `<staging-document-root>` | `DOC_ROOT` |
|
|
168
|
+
| `<staging-url>` (from the branch→env table) | `STAGING_URL` |
|
|
169
|
+
|
|
170
|
+
Build the SSH connection string:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
SSH_TARGET="${SSH_USER}@${SSH_HOST}"
|
|
174
|
+
SSH_OPTS="-p ${SSH_PORT}"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Verify the connection before doing anything destructive:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
ssh ${SSH_OPTS} "${SSH_TARGET}" "test -d '${DOC_ROOT}/wp-content' && echo ok"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If it doesn't print `ok`, stop. Common causes: wrong port, SSH key not registered with the host, doc root path wrong.
|
|
184
|
+
|
|
185
|
+
> **Production guard.** This flow only ever pulls from **staging**. If the user explicitly asks to pull from production, refuse and explain the safer path: pull staging instead, or have the user export a sanitized DB from prod manually. Production WP-CLI requires owner approval per `agent/AGENTS.md`.
|
|
186
|
+
|
|
187
|
+
### 2b. Pull `plugins`
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
rsync -avz --delete \
|
|
191
|
+
-e "ssh ${SSH_OPTS}" \
|
|
192
|
+
--exclude='index.php' \
|
|
193
|
+
"${SSH_TARGET}:${DOC_ROOT}/wp-content/plugins/" \
|
|
194
|
+
apps/wordpress/wp-content/plugins/
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Notes:
|
|
198
|
+
|
|
199
|
+
- `--delete` mirrors staging exactly. Warn the user once: "this removes any local-only plugins under `apps/wordpress/wp-content/plugins/`. Confirm?" If they decline, drop `--delete`.
|
|
200
|
+
- Some hosts inject plugins that don't belong in the repo (e.g. `kinsta-mu-plugins` lives in `mu-plugins/`, not here, so it shouldn't appear; but check). Don't auto-exclude anything beyond `index.php` without asking.
|
|
201
|
+
- After the rsync, remind the user to inspect `git status` for new tracked paths in `apps/wordpress/wp-content/plugins/`. If something new should reach Kinsta on deploy, the nested `apps/wordpress/.gitignore` needs an explicit `!` exception — see [`kinsta-auto-deploy.md`](./kinsta-auto-deploy.md) § "Adding new tracked files".
|
|
202
|
+
|
|
203
|
+
### 2c. Pull `mu-plugins`
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
rsync -avz --delete \
|
|
207
|
+
-e "ssh ${SSH_OPTS}" \
|
|
208
|
+
--exclude='index.php' \
|
|
209
|
+
--exclude='00-wp-env-local-url.php' \
|
|
210
|
+
--exclude='kinsta-mu-plugins/' \
|
|
211
|
+
--exclude='kinsta-mu-plugins.php' \
|
|
212
|
+
"${SSH_TARGET}:${DOC_ROOT}/wp-content/mu-plugins/" \
|
|
213
|
+
apps/wordpress/wp-content/mu-plugins/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The Kinsta-injected mu-plugin is excluded because Kinsta manages it server-side; it isn't useful locally and shouldn't end up in git. For other hosts (WP Engine's `wpengine-common`, Pantheon's `pantheon.php`, etc.), apply the same logic — ask the user if you spot one you don't recognize before adding it to the exclude list permanently.
|
|
217
|
+
|
|
218
|
+
`00-wp-env-local-url.php` is a local-only helper created by `domain set`; keep excluding it so `pull mu-plugins --delete` doesn't remove the local Caddy URL fix.
|
|
219
|
+
|
|
220
|
+
### 2d. Pull `db`
|
|
221
|
+
|
|
222
|
+
The local stack **must be running** for this step. If `npx wp-env run cli wp core is-installed` errors, run `npx wp-env start` first.
|
|
223
|
+
|
|
224
|
+
Resolve `LOCAL_URL` the same way Step 1b does: `https://<wpEnv.localDomain>` if it's set in `.refact-os.json`, otherwise `http://localhost:8888`.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# 1. Dump staging DB and pipe straight into the wp-env container.
|
|
228
|
+
ssh ${SSH_OPTS} "${SSH_TARGET}" \
|
|
229
|
+
"cd '${DOC_ROOT}' && wp db export --single-transaction -" \
|
|
230
|
+
| npx wp-env run cli wp db import -
|
|
231
|
+
|
|
232
|
+
# 2. Rewrite URLs from staging → local.
|
|
233
|
+
npx wp-env run cli wp search-replace "${STAGING_URL}" "${LOCAL_URL}" \
|
|
234
|
+
--skip-columns=guid --all-tables
|
|
235
|
+
|
|
236
|
+
# If the staging URL in wp_options has no trailing slash, make sure home/siteurl land exactly.
|
|
237
|
+
npx wp-env run cli wp db query "UPDATE wp_options SET option_value='${LOCAL_URL}' WHERE option_name IN ('home','siteurl')"
|
|
238
|
+
|
|
239
|
+
# 3. Flush caches.
|
|
240
|
+
npx wp-env run cli wp cache flush
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Print `STAGING_URL → LOCAL_URL` before running `search-replace` and ask the user to confirm. A bad replace can rewrite half the DB to the wrong host and is painful to undo.
|
|
244
|
+
|
|
245
|
+
After import:
|
|
246
|
+
|
|
247
|
+
- Reset the admin password so the user can log in locally:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npx wp-env run cli wp user update admin --user_pass=password --skip-email
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
(Skip silently if `admin` doesn't exist — some sites use a different super-admin username; surface the existing admin list with `wp user list --role=administrator` and ask the user.)
|
|
254
|
+
|
|
255
|
+
- Remind the user to run `npm run wp:cli -- option get siteurl` to verify the rewrite landed.
|
|
256
|
+
- If a Caddy domain is enabled, also verify `curl -k -I -L --max-redirs 1 "https://<hostname>/wp-admin/"` ends on `https://<hostname>/wp-login.php...`, **not** `https://<hostname>:8888/...`.
|
|
257
|
+
|
|
258
|
+
If the staging host has no `wp-cli` on `$PATH`, **stop** rather than silently falling back — surface the error and the user can decide whether to install wp-cli on the server or pull a manual `mysqldump`. Don't invent a fallback in this flow.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Step 3 — `domain set <hostname>` / `domain clear`
|
|
263
|
+
|
|
264
|
+
Front the wp-env stack with a Caddy reverse proxy so the local site answers on `https://<hostname>` instead of `http://localhost:8888`. The hostname is stored in `.refact-os.json` › `wpEnv.localDomain`, the proxy config lives in `~/.refact/`, and `/etc/hosts` maps the name to `127.0.0.1`.
|
|
265
|
+
|
|
266
|
+
### 3a. Preflight
|
|
267
|
+
|
|
268
|
+
- `caddy` is on `$PATH`. If missing, stop and tell the user to install it (macOS: `brew install caddy`; Linux: see [caddyserver.com/docs/install](https://caddyserver.com/docs/install)).
|
|
269
|
+
- The hostname ends in `.local`, `.test`, or `.localhost`. Refuse public-looking suffixes (`.com`, `.io`, etc.) — Caddy would try to reach Let's Encrypt and fail, and the user almost certainly meant a local-only name. If they really want a registered TLD, surface the risk and ask again.
|
|
270
|
+
- The hostname is lowercase, kebab-case ASCII (`^[a-z0-9][a-z0-9-]*\.(local|test|localhost)$`).
|
|
271
|
+
- wp-env is running (`docker ps | grep -q wordpress`). If not, run `npx wp-env start` first — Caddy needs the upstream up.
|
|
272
|
+
|
|
273
|
+
### 3b. `domain set <hostname>`
|
|
274
|
+
|
|
275
|
+
1. Persist the value:
|
|
276
|
+
|
|
277
|
+
```jsonc
|
|
278
|
+
// .refact-os.json
|
|
279
|
+
{
|
|
280
|
+
"stack": {
|
|
281
|
+
"wordpress": { "hosting": null, "runtime": null, "environments": {} }
|
|
282
|
+
},
|
|
283
|
+
"wpEnv": {
|
|
284
|
+
"localDomain": "<hostname>"
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Read, merge, write back via the same JSON the rest of `/refact` uses — don't hand-edit other keys.
|
|
290
|
+
|
|
291
|
+
2. Ensure the global Caddy scaffold exists:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
mkdir -p ~/.refact/caddy
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Write `~/.refact/Caddyfile` (only if missing):
|
|
298
|
+
|
|
299
|
+
```caddyfile
|
|
300
|
+
# Managed by /refact wp-env. Per-project site blocks live in ~/.refact/caddy/.
|
|
301
|
+
{
|
|
302
|
+
# global options
|
|
303
|
+
}
|
|
304
|
+
import caddy/*.caddyfile
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
3. Write the per-project site block at `~/.refact/caddy/<project-slug>.caddyfile`. `<project-slug>` is `basename` of the project root, lowercased, with non-alphanumerics replaced by `-`:
|
|
308
|
+
|
|
309
|
+
```caddyfile
|
|
310
|
+
<hostname> {
|
|
311
|
+
reverse_proxy 127.0.0.1:8888 {
|
|
312
|
+
# wp-env may emit redirects to its internal port. Keep browser traffic on Caddy's HTTPS origin.
|
|
313
|
+
header_down Location "https://<hostname>:8888/" "https://<hostname>/"
|
|
314
|
+
header_down Location "https://localhost:8888/" "https://<hostname>/"
|
|
315
|
+
header_down Location "http://localhost:8888/" "https://<hostname>/"
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
If the file already exists for this slug, overwrite it — this verb is the source of truth for the project's site block.
|
|
321
|
+
|
|
322
|
+
4. Trust the Caddy local CA (idempotent — silently noops if already trusted):
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
caddy trust
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
On macOS this prompts for a sudo password the first time so it can add the root cert to the system keychain. Surface the prompt to the user; do not bypass.
|
|
329
|
+
|
|
330
|
+
5. Add the `/etc/hosts` entry. This requires `sudo`. Don't run `sudo` directly from the flow — print the exact command and ask the user to run it themselves:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# The user runs this:
|
|
334
|
+
echo "127.0.0.1 <hostname>" | sudo tee -a /etc/hosts
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Before printing, check whether the entry already exists (`grep -E "^127\.0\.0\.1[[:space:]]+<hostname>$" /etc/hosts`). If it's there, skip this step silently.
|
|
338
|
+
|
|
339
|
+
6. Start or reload Caddy:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# If not already running:
|
|
343
|
+
caddy start --config ~/.refact/Caddyfile --adapter caddyfile
|
|
344
|
+
|
|
345
|
+
# If already running:
|
|
346
|
+
caddy reload --config ~/.refact/Caddyfile --adapter caddyfile
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Detect "already running" with `pgrep -x caddy` or `caddy list-modules` exit status. Don't try both — pick one and stick with it.
|
|
350
|
+
|
|
351
|
+
7. Do **not** set `.wp-env.json` or `.wp-env.override.json` › `config.WP_HOME` / `config.WP_SITEURL` for the Caddy hostname. wp-env rewrites URL constants to include its internal `:8888` port, which causes browsers to follow HTTPS redirects to the plain-HTTP wp-env port and fail with `SSL_ERROR_RX_RECORD_TOO_LONG`. Keep the public URL in the DB instead, and restart the stack after Caddy config changes:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
npx wp-env stop
|
|
355
|
+
npx wp-env start
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
If the DB already has the old URL baked in (it usually does — WP stores `siteurl` and `home` in `wp_options`), run a one-shot search-replace:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
npx wp-env run cli wp search-replace "http://localhost:8888" "https://<hostname>" \
|
|
362
|
+
--skip-columns=guid --all-tables
|
|
363
|
+
npx wp-env run cli wp db query "UPDATE wp_options SET option_value='https://<hostname>' WHERE option_name IN ('home','siteurl')"
|
|
364
|
+
npx wp-env run cli wp cache flush
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Confirm the source/target URLs with the user before running.
|
|
368
|
+
|
|
369
|
+
8. Write a local-only mu-plugin at `apps/wordpress/wp-content/mu-plugins/00-wp-env-local-url.php` (substitute `<hostname>`). This file is intentionally ignored by `apps/wordpress/.gitignore`; do not add a gitignore exception for it.
|
|
370
|
+
|
|
371
|
+
```php
|
|
372
|
+
<?php
|
|
373
|
+
/**
|
|
374
|
+
* Local wp-env URL overrides.
|
|
375
|
+
*
|
|
376
|
+
* This file is ignored by git via apps/wordpress/.gitignore and should not be
|
|
377
|
+
* deployed. It keeps browser-facing URLs on the Caddy HTTPS origin instead of
|
|
378
|
+
* wp-env's internal :8888 port.
|
|
379
|
+
*/
|
|
380
|
+
|
|
381
|
+
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && 'local' === WP_ENVIRONMENT_TYPE ) {
|
|
382
|
+
$local_url = 'https://<hostname>';
|
|
383
|
+
|
|
384
|
+
$rewrite_local_url = static function ( $url ) use ( $local_url ) {
|
|
385
|
+
if ( ! is_string( $url ) ) {
|
|
386
|
+
return $url;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return str_replace(
|
|
390
|
+
array(
|
|
391
|
+
'https://<hostname>:8888',
|
|
392
|
+
'http://<hostname>:8888',
|
|
393
|
+
'https://localhost:8888',
|
|
394
|
+
'http://localhost:8888',
|
|
395
|
+
),
|
|
396
|
+
$local_url,
|
|
397
|
+
$url
|
|
398
|
+
);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
add_filter( 'option_home', static fn () => $local_url, PHP_INT_MAX );
|
|
402
|
+
add_filter( 'option_siteurl', static fn () => $local_url, PHP_INT_MAX );
|
|
403
|
+
|
|
404
|
+
foreach ( array( 'home_url', 'site_url', 'admin_url', 'includes_url', 'content_url', 'plugins_url', 'network_site_url' ) as $url_filter ) {
|
|
405
|
+
add_filter( $url_filter, $rewrite_local_url, PHP_INT_MAX );
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
9. Verify the browser-facing URLs:
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
npx wp-env run cli wp cache flush
|
|
414
|
+
npm run wp:cli -- option get home
|
|
415
|
+
npm run wp:cli -- option get siteurl
|
|
416
|
+
curl -k -I -L --max-redirs 1 "https://<hostname>/wp-admin/"
|
|
417
|
+
if curl -k -sS "https://<hostname>/wp-login.php" | grep ':8888'; then
|
|
418
|
+
echo "unexpected :8888 URL"
|
|
419
|
+
fi
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Expected: `home` and `siteurl` print `https://<hostname>`, `/wp-admin/` redirects to `https://<hostname>/wp-login.php?...`, and the login page contains no `:8888` asset/form URLs. If the browser shows `SSL_ERROR_RX_RECORD_TOO_LONG`, it is almost certainly following an HTTPS URL on `:8888`; inspect the `Location` header and login markup.
|
|
423
|
+
|
|
424
|
+
10. Report:
|
|
425
|
+
|
|
426
|
+
- Site URL: `https://<hostname>`
|
|
427
|
+
- Admin URL: `https://<hostname>/wp-admin`
|
|
428
|
+
- Caddyfile: `~/.refact/caddy/<project-slug>.caddyfile`
|
|
429
|
+
- Local URL helper: `apps/wordpress/wp-content/mu-plugins/00-wp-env-local-url.php` (gitignored)
|
|
430
|
+
- That the value is persisted in `.refact-os.json` so other teammates pick it up on their next `/refact wp-env setup`.
|
|
431
|
+
|
|
432
|
+
### 3c. `domain clear`
|
|
433
|
+
|
|
434
|
+
1. Remove `wpEnv.localDomain` from `.refact-os.json` (delete the key; if `wpEnv` becomes empty, remove that too).
|
|
435
|
+
2. Delete `~/.refact/caddy/<project-slug>.caddyfile`. If the directory is now empty, leave the directory in place — another project may need it.
|
|
436
|
+
3. Delete `apps/wordpress/wp-content/mu-plugins/00-wp-env-local-url.php` if it exists.
|
|
437
|
+
4. Reload Caddy if it's running (`caddy reload …`). Don't stop the global Caddy process — other projects may rely on it.
|
|
438
|
+
5. Tell the user the `/etc/hosts` entry was **not** removed automatically (it's harmless and removing it would need another sudo prompt). Print the exact `sudo sed -i ''` command they can run if they want it gone.
|
|
439
|
+
6. Keep `.wp-env.json` and `.wp-env.override.json` free of `WP_HOME` / `WP_SITEURL`, restart wp-env, then run a search-replace from `https://<old-hostname>` back to `http://localhost:8888` so the DB matches.
|
|
440
|
+
|
|
441
|
+
### 3d. Multi-project caveat
|
|
442
|
+
|
|
443
|
+
Caddy binds `:80` and `:443`, so only one Caddy process can run per machine. The `~/.refact/Caddyfile` `import` pattern handles this: each project owns its own site block under `~/.refact/caddy/`, and the single running Caddy serves all of them. **Don't** scaffold a project-local Caddyfile inside the repo — it would either bind-collide or get accidentally committed.
|
|
444
|
+
|
|
445
|
+
If two projects pick the same hostname, the second `domain set` overwrites the first project's site block. Detect this in 4b step 3 — if the existing file's contents reference a different `127.0.0.1:<port>` upstream than what we're about to write, ask the user to confirm before overwriting.
|
|
446
|
+
|
|
447
|
+
## Step 4 — `reset`
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
npx wp-env destroy
|
|
451
|
+
npx wp-env start
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
`destroy` removes the MySQL volume; the next `start` rebuilds with the wp-env default sample install. **Local DB changes are lost** — confirm with the user before running. Files under `apps/wordpress/wp-content/` are not touched (they live on the host, not in the volume).
|
|
455
|
+
|
|
456
|
+
After reset, the user typically wants `/refact wp-env pull db` to restore staging state.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Guardrails
|
|
461
|
+
|
|
462
|
+
- **`apps/wordpress/wp-content/` is the source of truth.** Never write to `./wp-content/` at the repo root — that's the legacy layout. If you find both, ask the user which one is current before any pull.
|
|
463
|
+
- **Never pull from production.** The flow is staging-only. If the user explicitly insists on prod, stop and require explicit owner approval (per `agent/AGENTS.md` § Deployment safety).
|
|
464
|
+
- **Never run `search-replace` without confirming the URLs.** A bad replace can rewrite half the DB to the wrong host and is painful to undo. Always print the source and target URL once before executing.
|
|
465
|
+
- **Never `wp-env destroy` without confirmation.** It nukes the local DB. `reset` confirms; ad-hoc destroy elsewhere should too.
|
|
466
|
+
- **Never invent a fallback** when staging's wp-cli or rsync isn't available. Surface the error, let the user decide.
|
|
467
|
+
- **Never edit `agent/AGENTS.md` to fill missing SSH fields from this flow.** Send the user to `/refact init` so the placeholders are filled in one place.
|
|
468
|
+
- **Never commit pulled DB dumps.** If you write a `.sql` file at any point during this flow, place it in a gitignored path (e.g. `./.wp-env-dumps/`) and delete it after import.
|
|
469
|
+
- **Never write Caddyfiles into the project tree.** Per-project site blocks live under `~/.refact/caddy/`. Project-local files would either bind-collide with another project's Caddy instance or end up committed by accident.
|
|
470
|
+
- **Never accept a public-TLD hostname** for `domain set` (`.com`, `.io`, etc.) without explicit user confirmation — Caddy would try Let's Encrypt and either fail or, worse, attempt ACME against a domain the user doesn't control.
|
|
471
|
+
- **Never run `sudo` from this flow.** `/etc/hosts` edits and `caddy trust` print the command for the user to run; they keep the credential prompt in their own hands.
|
|
472
|
+
|
|
473
|
+
## When to stop and ask the user
|
|
474
|
+
|
|
475
|
+
- `git status` shows large, unexpected diffs after `pull plugins` (e.g. an entire vendored plugin you didn't know was there) → surface before staging the changes.
|
|
476
|
+
- The staging URL parsed from `agent/AGENTS.md` doesn't match the user's stated staging URL → resolve before running `search-replace`.
|
|
477
|
+
- A pull would `--delete` a local-only plugin that looks like work-in-progress (no matching commit history) → ask first.
|
|
478
|
+
- The user asks to point this flow at a different remote (e.g. "pull from dev instead") → that's an architectural change; confirm whether to extend `agent/AGENTS.md` with a `<dev-*>` block or treat it as a one-off prompt.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# wp-cli.yml — remote environment aliases for this engagement.
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to `wp-cli.yml` and fill in the host-specific values for your
|
|
4
|
+
# project. Keep `wp-cli.yml` committed; it is non-secret routing only. SSH keys,
|
|
5
|
+
# tokens, and passwords stay out of this file (1Password, OS keychain, ~/.ssh/).
|
|
6
|
+
#
|
|
7
|
+
# Once configured, agents and engineers run remote WP-CLI like:
|
|
8
|
+
# wp @staging plugin list
|
|
9
|
+
# wp @development cache flush
|
|
10
|
+
#
|
|
11
|
+
# Host-specific setup (SSH config, key registration, dashboards) lives in
|
|
12
|
+
# 1Password and the host's own docs. WP Engine uses an SSH gateway with a
|
|
13
|
+
# /sites/<env>/ layout; Kinsta uses a custom port per site; Pantheon uses
|
|
14
|
+
# Terminus instead of WP-CLI aliases (see the Pantheon block below).
|
|
15
|
+
|
|
16
|
+
# --- WP Engine example ------------------------------------------------------
|
|
17
|
+
# Requires `~/.ssh/config` entries `wpe-staging` and `wpe-development`.
|
|
18
|
+
# @staging:
|
|
19
|
+
# ssh: wpe-staging:/sites/<staging-env>
|
|
20
|
+
# @development:
|
|
21
|
+
# ssh: wpe-development:/sites/<dev-env>
|
|
22
|
+
|
|
23
|
+
# --- Kinsta example ---------------------------------------------------------
|
|
24
|
+
# Requires `~/.ssh/config` entries `kinsta-staging` and `kinsta-development`
|
|
25
|
+
# (each with the custom port from MyKinsta).
|
|
26
|
+
# @staging:
|
|
27
|
+
# ssh: kinsta-staging:~/public
|
|
28
|
+
# @development:
|
|
29
|
+
# ssh: kinsta-development:~/public
|
|
30
|
+
|
|
31
|
+
# --- Generic SSH host -------------------------------------------------------
|
|
32
|
+
# @staging:
|
|
33
|
+
# ssh: deploy@staging.example.com:/var/www/staging/public_html
|
|
34
|
+
# @development:
|
|
35
|
+
# ssh: deploy@dev.example.com:/var/www/dev/public_html
|
|
36
|
+
|
|
37
|
+
# --- Pantheon ---------------------------------------------------------------
|
|
38
|
+
# Pantheon uses Terminus rather than WP-CLI aliases. Do not add `@staging` /
|
|
39
|
+
# `@development` here for Pantheon projects; use:
|
|
40
|
+
# terminus remote:wp <site>.<env> -- <command>
|
|
41
|
+
|
|
42
|
+
# --- Production -------------------------------------------------------------
|
|
43
|
+
# Intentionally NOT defined here. Production WP-CLI requires explicit owner
|
|
44
|
+
# approval and a verified backup (per .cursor/rules/30-security.mdc). When
|
|
45
|
+
# approved, run it manually in a one-off shell — do not commit a
|
|
46
|
+
# `@production` alias.
|