$npx -y skills add warpdotdev/common-skills --skill scan-new-specsScan warpdotdev/warp and warp-server for recently merged PRODUCT.md specs that don't yet have a corresponding docs PR in warpdotdev/docs. When a complete spec is found, auto-generates a full docs draft PR and tags the engineer. When a spec is too thin to draft from, pings the eng
| 1 | # scan-new-specs |
| 2 | |
| 3 | Scan `warpdotdev/warp` and `warp-server` for recently merged product or tech specs that lack a corresponding docs draft. For each gap: |
| 4 | |
| 5 | - **If the spec is complete** — automatically run `write-feature-docs` in ambient mode to generate a full draft PR in `warpdotdev/docs`, then ping the engineer to review it |
| 6 | - **If the spec is thin** — ping the engineer directly to either flesh out the spec or kick off the docs workflow manually |
| 7 | |
| 8 | In both cases, post a summary to `#growth-docs`. |
| 9 | |
| 10 | ## Configuration |
| 11 | |
| 12 | Before running, confirm these values (or accept the defaults): |
| 13 | |
| 14 | | Setting | Default | Description | |
| 15 | |---|---|---| |
| 16 | | `LOOKBACK_DAYS` | `3` | How many days back to scan for merged spec PRs | |
| 17 | | `SLACK_CHANNEL` | `#growth-docs` | Slack channel for engineer pings and summaries | |
| 18 | | `SLACK_BOT_TOKEN` | From `buzz` environment | Slack bot token for posting via API (already available in the `buzz` Oz environment) | |
| 19 | |
| 20 | This skill uses the Slack API (`chat.postMessage`) rather than an incoming webhook, which enables real user pings. The `buzz` Oz environment already has the required `SLACK_BOT_TOKEN`. No new secrets setup is needed. |
| 21 | |
| 22 | If `SLACK_BOT_TOKEN` is not set, print all messages to stdout instead. |
| 23 | |
| 24 | ## Step 1: Find recently merged specs |
| 25 | |
| 26 | List merged PRs from both repos since the lookback date, then filter by changed files. Do not use `--search "in:files"` (GitHub does not support file-path filtering in PR search) and use a portable date command that works on both Linux and macOS: |
| 27 | |
| 28 | ```bash |
| 29 | # Portable date calculation (GNU/Linux and BSD/macOS compatible) |
| 30 | SINCE=$(date -d "-${LOOKBACK_DAYS} days" +%Y-%m-%d 2>/dev/null \ |
| 31 | || date -v-${LOOKBACK_DAYS}d +%Y-%m-%d) |
| 32 | |
| 33 | # List all recently merged PRs (no file-path filter -- we check files next) |
| 34 | gh pr list \ |
| 35 | --repo warpdotdev/warp \ |
| 36 | --state merged \ |
| 37 | --search "merged:>${SINCE}" \ |
| 38 | --json number,title,author,mergedAt,url \ |
| 39 | --limit 100 |
| 40 | |
| 41 | # Repeat for warp-server |
| 42 | gh pr list \ |
| 43 | --repo warpdotdev/warp-server \ |
| 44 | --state merged \ |
| 45 | --search "merged:>${SINCE}" \ |
| 46 | --json number,title,author,mergedAt,url \ |
| 47 | --limit 100 |
| 48 | ``` |
| 49 | |
| 50 | For each PR returned, check whether it actually contains a new `specs/*/PRODUCT.md` by inspecting the changed files (this is the correct filter step): |
| 51 | |
| 52 | ```bash |
| 53 | gh pr view <number> --repo warpdotdev/<repo> --json files -q '.files[].path' \ |
| 54 | | grep -E '^specs/.+/PRODUCT\.md$' |
| 55 | ``` |
| 56 | |
| 57 | Collect the list of: spec ID (the directory name under `specs/`), spec PR number and URL, PR author GitHub username, repo (`warp` or `warp-server`), and merge date. |
| 58 | |
| 59 | For each PR author's GitHub username, resolve their Slack identity: |
| 60 | |
| 61 | ```bash |
| 62 | # Get the engineer's name and email from GitHub |
| 63 | ENG_NAME=$(gh api users/<github-username> -q '.name // .login') |
| 64 | ENG_EMAIL=$(gh api users/<github-username> -q '.email // empty') |
| 65 | |
| 66 | # Look up their Slack user ID by email (real ping, not just a name mention) |
| 67 | if [ -n "$ENG_EMAIL" ]; then |
| 68 | SLACK_USER_ID=$(curl -s -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ |
| 69 | "https://slack.com/api/users.lookupByEmail?email=${ENG_EMAIL}" \ |
| 70 | | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['user']['id'] if d.get('ok') else '')") |
| 71 | fi |
| 72 | |
| 73 | # Use <@USER_ID> for a real ping if lookup succeeded; fall back to @name if not |
| 74 | if [ -n "$SLACK_USER_ID" ]; then |
| 75 | ENG_MENTION="<@${SLACK_USER_ID}>" |
| 76 | else |
| 77 | ENG_MENTION="@${ENG_NAME} _(Slack ID not found — verify this is the right person)_" |
| 78 | fi |
| 79 | ``` |
| 80 | |
| 81 | Store `ENG_MENTION`, `ENG_NAME`, and `ENG_GITHUB` for use in Slack messages. |
| 82 | |
| 83 | ## Step 2: Check for existing docs coverage |
| 84 | |
| 85 | For each spec found, check `warpdotdev/docs` for an open/draft or merged PR that mentions the spec ID. Run two separate queries to avoid counting closed-unmerged PRs as coverage: |
| 86 | |
| 87 | ```bash |
| 88 | # Check for open or draft PRs |
| 89 | gh pr list \ |
| 90 | --repo warpdotdev/docs \ |
| 91 | --state open \ |
| 92 | --search "<spec-id>" \ |
| 93 | --json number,title,state,url \ |
| 94 | --limit 5 |
| 95 | |
| 96 | # Check for merged PRs |
| 97 | gh pr list \ |
| 98 | --repo warpdotdev/docs \ |
| 99 | --state merged \ |
| 100 | --search "<spec-id>" \ |
| 101 | --json number,title,state,url \ |
| 102 | --limit 5 |
| 103 | ``` |
| 104 | |
| 105 | A spec is considered **covered** if either query returns results (open, draft, or merged PR exists). Skip covered specs. |
| 106 | |
| 107 | A spec is **uncovered** if neither query returns results. Closed-unmerged PRs do **not** count as coverage — a closed PR signals abandoned work that needs re-triggering. |
| 108 | |
| 109 | ## Step 3: Assess spec completeness |
| 110 | |
| 111 | For each uncovered spec, read `specs/<id>/PRODUCT.md` and assess whether it has enough content to auto-draft from: |
| 112 | |
| 113 | **Complete** ( |