${var} — Optional. Override the Vercel project slug. If empty, watches [REPLACE: VERCEL_PROJECT].
Today is ${today}. Watch Vercel deployments for [REPLACE: VERCEL_PROJECT] and alert on [REPLACE: ALERT_ON] within the last [REPLACE: LOOKBACK_HOURS] hours.
Required secrets
VERCEL_TOKEN — personal access token from https://vercel.com/account/tokens. Read scope is enough.
- (optional)
VERCEL_TEAM_ID — if the project lives under a team, set this so the API queries the right scope.
If either secret is missing, log DEPLOY_WATCH_NO_TOKEN and exit cleanly — never abort the workflow.
Steps
Resolve scope:
PROJECT="${var:-[REPLACE: VERCEL_PROJECT]}"
SCOPE_QS=""
if [ -n "${VERCEL_TEAM_ID:-}" ]; then
SCOPE_QS="&teamId=$VERCEL_TEAM_ID"
fi
Fetch recent deploys — Vercel API v6 lists deployments for a project:
SINCE_MS=$(( $(date -u +%s) * 1000 - [REPLACE: LOOKBACK_HOURS] * 3600 * 1000 ))
URL="https://api.vercel.com/v6/deployments?projectId=$PROJECT&since=$SINCE_MS$SCOPE_QS&limit=20"
curl -sf -H "Authorization: Bearer $VERCEL_TOKEN" "$URL" > .vercel-deploys.json || \
echo "DEPLOY_WATCH_FETCH_FAIL: $?"
Make every Vercel call in-run with ./secretcurl (write the key as {VERCEL_TOKEN} — a bare $VERCEL_TOKEN on the line is refused by the Bash permission layer). Read-only status checks and any irreversible action (e.g. triggering a deploy) both run in-run — the irreversible one as the skill's final, fail-closed action. Never defer a read.
Parse and classify — for each deploy, capture: uid, state (READY / ERROR / CANCELED / BUILDING / QUEUED), url, target (production / preview), creator, createdAt, meta.githubCommitMessage.
Apply the alert filter — [REPLACE: ALERT_ON] is one of:
production-failures → alert when target=production AND state in {ERROR, CANCELED}.
any-failures → alert on any state in {ERROR, CANCELED}.
slow-builds → alert when build time > 10× the last-week median for this project.
all → alert on every state transition (noisy — only useful while debugging the skill).
Compare against last-success baseline — if alerting on a failure, also fetch the most recent successful production deploy and include in the notification: "last green: [commit] · [N hours] ago".
Dedup — track alerted deploy UIDs in memory/topics/[REPLACE: SKILL_NAME]-alerted.json. Never re-alert for the same UID.
Notify on every new alert via ./notify:
*Deploy alert — [REPLACE: VERCEL_PROJECT]*
${state}: ${commit_message}
${target} build by ${creator} · ${ago}
Last green: ${last_green_commit} · ${last_green_ago}
Inspect: https://vercel.com/${owner}/${PROJECT}/${uid}
Write a roll-up to output/articles/[REPLACE: SKILL_NAME]-${today}.md: total deploys, success/fail counts per target, average build time, list of failed UIDs with commit messages.
Log to memory/logs/${today}.md:
## [REPLACE: SKILL_NAME]
- **Deploys (${LOOKBACK_HOURS}h)**: total=N, ready=X, error=Y, canceled=Z, building=W
- **Alerts fired**: N (deduped from M raw matches)
- **Status**: DEPLOY_OK | DEPLOY_QUIET (no deploys) | DEPLOY_ALERT | DEPLOY_DEGRADED
Network note
The Vercel API requires Authorization: Bearer {VERCEL_TOKEN}. A bare $SECRET on a command line is refused by the Bash permission layer, so make every Vercel call in-run with ./secretcurl (write the key as the {VERCEL_TOKEN} placeholder — it keeps the secret off the line). Both the read-only status checks and any irreversible action (like triggering a deploy) run in-run; the irreversible one goes last, as the skill's final fail-closed action. Never defer a read.
Constraints
- Dedup is non-negotiable. Re-running the same alert for the same deploy will train operators to mute the channel — once alerted, never again unless the deploy changes state.
- Production beats preview for alerting. A failed preview deploy is interesting but not urgent. Default to
production-failures until the operator opts into more.
- Compare against baseline. A failed build means more when paired with "last green was 3 hours ago" than alone.
1---2name: deploy-watcher3description: > **${var}** — Optional. Override the Vercel project slug. If empty, watches `[REPLACE: VERCEL_PROJECT]`.4---56> **${var}** — Optional. Override the Vercel project slug. If empty, watches `[REPLACE: VERCEL_PROJECT]`.78Today is ${today}. Watch Vercel deployments for **[REPLACE: VERCEL_PROJECT]** and alert on **[REPLACE: ALERT_ON]** within the last **[REPLACE: LOOKBACK_HOURS]** hours.910## Required secrets1112- `VERCEL_TOKEN` — personal access token from https://vercel.com/account/tokens. Read scope is enough.13- (optional) `VERCEL_TEAM_ID` — if the project lives under a team, set this so the API queries the right scope.1415If either secret is missing, log `DEPLOY_WATCH_NO_TOKEN` and exit cleanly — never abort the workflow.1617## Steps18191. **Resolve scope**:20 ```bash21 PROJECT="${var:-[REPLACE: VERCEL_PROJECT]}"22 SCOPE_QS=""23 if [ -n "${VERCEL_TEAM_ID:-}" ]; then24 SCOPE_QS="&teamId=$VERCEL_TEAM_ID"25 fi26 ```27282. **Fetch recent deploys** — Vercel API v6 lists deployments for a project:29 ```bash30 SINCE_MS=$(( $(date -u +%s) * 1000 - [REPLACE: LOOKBACK_HOURS] * 3600 * 1000 ))31 URL="https://api.vercel.com/v6/deployments?projectId=$PROJECT&since=$SINCE_MS$SCOPE_QS&limit=20"32 curl -sf -H "Authorization: Bearer $VERCEL_TOKEN" "$URL" > .vercel-deploys.json || \33 echo "DEPLOY_WATCH_FETCH_FAIL: $?"34 ```3536 Make every Vercel call in-run with `./secretcurl` (write the key as `{VERCEL_TOKEN}` — a bare `$VERCEL_TOKEN` on the line is refused by the Bash permission layer). Read-only status checks and any **irreversible action** (e.g. triggering a deploy) both run in-run — the irreversible one as the skill's final, fail-closed action. Never defer a read.37383. **Parse and classify** — for each deploy, capture: `uid`, `state` (READY / ERROR / CANCELED / BUILDING / QUEUED), `url`, `target` (production / preview), `creator`, `createdAt`, `meta.githubCommitMessage`.39404. **Apply the alert filter** — `[REPLACE: ALERT_ON]` is one of:41 - `production-failures` → alert when `target=production` AND `state in {ERROR, CANCELED}`.42 - `any-failures` → alert on any `state in {ERROR, CANCELED}`.43 - `slow-builds` → alert when build time > 10× the last-week median for this project.44 - `all` → alert on every state transition (noisy — only useful while debugging the skill).45465. **Compare against last-success baseline** — if alerting on a failure, also fetch the most recent successful production deploy and include in the notification: "last green: [commit] · [N hours] ago".47486. **Dedup** — track alerted deploy UIDs in `memory/topics/[REPLACE: SKILL_NAME]-alerted.json`. Never re-alert for the same UID.49507. **Notify on every new alert** via `./notify`:51 ```52 *Deploy alert — [REPLACE: VERCEL_PROJECT]*53 ${state}: ${commit_message}54 ${target} build by ${creator} · ${ago}55 Last green: ${last_green_commit} · ${last_green_ago}56 Inspect: https://vercel.com/${owner}/${PROJECT}/${uid}57 ```58598. **Write a roll-up** to `output/articles/[REPLACE: SKILL_NAME]-${today}.md`: total deploys, success/fail counts per target, average build time, list of failed UIDs with commit messages.60619. **Log** to `memory/logs/${today}.md`:62 ```63 ## [REPLACE: SKILL_NAME]64 - **Deploys (${LOOKBACK_HOURS}h)**: total=N, ready=X, error=Y, canceled=Z, building=W65 - **Alerts fired**: N (deduped from M raw matches)66 - **Status**: DEPLOY_OK | DEPLOY_QUIET (no deploys) | DEPLOY_ALERT | DEPLOY_DEGRADED67 ```6869## Network note7071The Vercel API requires `Authorization: Bearer {VERCEL_TOKEN}`. A bare `$SECRET` on a command line is refused by the Bash permission layer, so make **every** Vercel call in-run with `./secretcurl` (write the key as the `{VERCEL_TOKEN}` placeholder — it keeps the secret off the line). Both the read-only status checks and any **irreversible** action (like triggering a deploy) run in-run; the irreversible one goes last, as the skill's final fail-closed action. Never defer a read.7273## Constraints7475- **Dedup is non-negotiable**. Re-running the same alert for the same deploy will train operators to mute the channel — once alerted, never again unless the deploy changes state.76- **Production beats preview** for alerting. A failed preview deploy is interesting but not urgent. Default to `production-failures` until the operator opts into more.77- **Compare against baseline**. A failed build means more when paired with "last green was 3 hours ago" than alone.