| name | deploy |
| description | Deploy Butler to production (Cloudflare Workers API + Pages frontend) with pre-flight checks and a post-deploy /health smoke check |
| disable-model-invocation | true |
Deploy
Ship Butler to production: deploy the @butler/api worker and the @butler/frontend
site to Cloudflare, then confirm the API is live with a /health smoke check.
Human-invoked only. Deploying is a side-effecting, production-facing action. This
skill has disable-model-invocation: true and must NEVER run automatically — only when
a human explicitly runs /deploy. On main, CI already auto-deploys on push
(.github/workflows/ci.yml); use this skill for an intentional manual deploy.
Prerequisites
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID set in the environment (same secrets
CI uses). If unset, stop and ask the human to provide them — do not read them from any
service .env.
Step 1: Pre-flight checks
Refuse to deploy unless all of these hold (report which failed and stop):
- Clean working tree —
git status --porcelain is empty (no uncommitted changes).
- On
main and up to date — git rev-parse --abbrev-ref HEAD is main, and
git fetch origin && git status -sb shows no divergence from origin/main.
- CI green on the commit being deployed —
gh run list --branch main --limit 1 --json conclusion,headSha reports success
for the current HEAD SHA. Never deploy a commit whose CI failed or is pending.
- Gates pass locally (fast confirmation):
pnpm --filter @butler/shared build && pnpm run type-check && pnpm run lint.
Step 2: Deploy the API (Cloudflare Workers)
cd packages/api && pnpm run deploy
Capture the deployed URL that wrangler prints (e.g. https://butler-api.<subdomain>.workers.dev).
Save it as API_URL for the smoke check. If you cannot parse it from the output, run
pnpm exec wrangler deployments list or ask the human for the production API URL — do
not guess a URL.
Step 3: Deploy the frontend (Cloudflare Pages)
cd packages/frontend && pnpm run build
pnpm exec wrangler pages deploy dist --project-name=butler-frontend
Note the Pages URL wrangler prints for reference.
Step 4: Post-deploy smoke check
Hit the live API health endpoint (GET /health returns { "status": "ok" }):
curl -fsS "$API_URL/health"
- Pass if the response is HTTP 2xx and the body is
{"status":"ok"}.
- Fail on any non-2xx, a connection error, or a body whose
status is not "ok".
Report the outcome explicitly, e.g.:
✅ Deploy OK — API healthy at <API_URL>/health (status: ok); frontend at <PAGES_URL>
❌ Smoke check FAILED — <API_URL>/health returned <code>/<body>. Investigate before announcing the deploy; consider rolling back (Cloudflare dashboard or wrangler rollback).
Notes
- Deploy commands and secret names mirror the CI
deploy job in
.github/workflows/ci.yml — keep them in sync if CI changes.
- This skill does not modify code or commit; it only deploys what is already on
main.
- The smoke check is intentionally minimal (liveness). For deeper verification, exercise
an authenticated route manually after deploy.