| name | run-seo-office |
| description | Use when asked to run, boot, start, dev-serve, smoke-test, or drive the SEO Office app, or to confirm a change works end-to-end in the real running app (UI → orchestrator → brain → specialist). Covers launching the Next.js dev server and exercising the client/job/SSE API. |
Run SEO Office
Boot the local Next.js 16 app and drive a real client → specialist → artifact flow.
This recipe was verified live; follow it verbatim before improvising.
Launch
pnpm is not on the non-interactive PATH — pnpm/next/tsc resolve only
through corepack, which ships next to the nvm-managed node. Prefix every command:
export PATH="$(ls -d ~/.nvm/versions/node/*/bin | tail -1):$PATH"
cd "/home/agricidaniel/Desktop/SEO Office"
corepack pnpm exec next dev --turbopack -p 3100
Server reports ✓ Ready in ~160ms, but the first request to any route compiles
it (use a generous curl timeout). Confirm it serves and the registry loaded:
curl -s http://localhost:3100/api/specialists | head -c 200
- LLM provider is
SEO_OFFICE_LLM_PROVIDER=claude-cli → the claude CLI must be on
PATH. No ANTHROPIC_API_KEY is needed.
- gcloud application-default creds expire →
google-analytics/google-search-console
specialists fail with invalid_grant. Fix: gcloud auth application-default login.
The same-origin write rule
POST/DELETE routes call sameOriginWriteAllowed. If the Origin header is
absent the request passes (server-to-server) — so plain curl works. A browser
passes when same-origin. A forged cross-origin Origin gets 403. Don't send an
Origin header from curl.
End-to-end smoke flow (verified)
Specialists take an empty payload and read the target from the client manifest.
Use a clearly-named throwaway slug — .seo-office/vaults/ holds REAL user vaults.
SLUG=e2e-smoke-example
curl -s -X POST localhost:3100/api/clients -H 'Content-Type: application/json' \
-d '{"siteUrl":"https://example.com","clientName":"E2E Smoke Example","owner":"e2e-smoke"}'
JOB=$(curl -s -X POST localhost:3100/api/clients/$SLUG/jobs \
-H 'Content-Type: application/json' -d '{"specialist":"vault-linter"}' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["job"]["id"])')
curl -sN "localhost:3100/api/jobs/$JOB/events?slug=$SLUG"
cat ".seo-office/vaults/$SLUG/wiki/deliverables/"*vault-lint.md
curl -s -X DELETE "localhost:3100/api/clients/$SLUG?confirm=1"
rm -rf ".seo-office/backups/clients/$SLUG-"*
For a real deliverable (fetch + LLM), use technical-auditor instead of
vault-linter at step 2. Expect 130s and real cost ($0.61 on Opus 4.7 via the
claude CLI). It writes wiki/audits/<date>-technical.md + .data.json + an HTML
report under reports/, and the orchestrator review layer writes wiki/reviews/.
Verify the UI layer
Common mistakes
| Mistake | Fix |
|---|
pnpm: command not found (exit 127) | Prefix with corepack after adding nvm bin to PATH. |
Wrapping pnpm x; echo EXIT=$? and trusting the harness exit code | The wrapper exits with the echo's code, masking failure. Read the EXIT= line inside the log. |
SSE returns {"ok":false,"error":"slug query param is required"} | Add ?slug=<slug> to /api/jobs/<id>/events. |
DELETE returns 400 "destructive" | Add ?confirm=1. |
| Deleting a vault directly off disk | Use DELETE /api/clients/<slug>?confirm=1 so the SQLite index cascades and a backup is made. Never touch real vaults. |
| Port 3000 hangs (HTTP 000) | A stale next-server is squatting it. Launch on a different port. |