$npx -y skills add vouchdev/vouch --skill pr-precheckUse when the user invokes /pr-precheck <repo> <topic> or — implicitly, before any other skill opens a pull request — to check the target repo's merged and closed PR history for prior attempts at the same fix, so the agent doesn't open the Nth duplicate of an already-rejected ap
| 1 | # PR Precheck |
| 2 | |
| 3 | **Goal:** before you raise a PR, find out whether someone already tried this fix — especially whether anyone tried and got *rejected*. The cost of asking is seconds; the cost of a duplicate PR is reviewer attention, contributor reputation, and (on Bittensor-style mining repos) wasted scoring weight. |
| 4 | |
| 5 | This skill is a thin orchestration layer over the `vouch pr-cache` CLI shipped in vouch ≥ this PR. It's safe to invoke directly **and** to chain ahead of `pr-fix` / `prs-auto`. |
| 6 | |
| 7 | ## Invocation |
| 8 | |
| 9 | Direct: |
| 10 | |
| 11 | ``` |
| 12 | /pr-precheck <repo> <topic-summary> |
| 13 | /pr-precheck <repo> <topic-summary> --files a.py,b.py |
| 14 | ``` |
| 15 | |
| 16 | Implicit (auto-fires inside `pr-fix` / `prs-auto` before the "open PR" step): |
| 17 | |
| 18 | ``` |
| 19 | /pr-fix https://github.com/owner/repo/issues/N |
| 20 | └─ before pushing the branch, run pr-precheck on the planned PR title + files |
| 21 | ``` |
| 22 | |
| 23 | `<repo>` may be: |
| 24 | |
| 25 | - `https://github.com/<owner>/<name>` |
| 26 | - `git@github.com:<owner>/<name>.git` |
| 27 | - `<owner>/<name>` (shorthand) |
| 28 | |
| 29 | `<topic-summary>` is the title-like phrase that describes the PR you're about to raise. Be specific — short generic strings ("fix bug") will match too many cached PRs. |
| 30 | |
| 31 | ## Prerequisites |
| 32 | |
| 33 | - **`vouch` CLI** on `PATH` (`pip install vouch-kb` ≥ the version that ships `pr-cache`). |
| 34 | - **`gh` CLI** authenticated for the target repo's read scope (`gh auth status` returns a session). |
| 35 | - *Optional:* `claude` CLI on `PATH` **or** `ANTHROPIC_API_KEY` in env — only needed if you pass `--analyze-closed` and want LLM-summarised "why was this closed" notes attached to each closed PR record. |
| 36 | |
| 37 | If `vouch` is missing, **stop and tell the user**. Don't fall back to manual `gh` queries — the whole value of this skill is the cached, ranked, dedup-aware view. |
| 38 | |
| 39 | ## Step 1 — Ensure cache is present and fresh |
| 40 | |
| 41 | The cache lives at `~/.cache/vouch/pr-cache/<owner>__<name>.json`. Build it if missing, or if it's older than 24 h, or if the user passes `--rebuild`. |
| 42 | |
| 43 | ```bash |
| 44 | CACHE=~/.cache/vouch/pr-cache/${OWNER}__${REPO}.json |
| 45 | if [ ! -f "$CACHE" ] || [ "$(find "$CACHE" -mmin +1440 -print 2>/dev/null)" ]; then |
| 46 | vouch pr-cache build "$REPO_URL" --state all --limit 200 |
| 47 | fi |
| 48 | ``` |
| 49 | |
| 50 | For repos with > 200 PRs per state, bump `--limit` (the cap is gh API rate limit, not vouch). If the target repo has a known cluster of duplicate-prone areas (e.g. Bittensor mining repos), prefer `--analyze-closed` once so closed-PR rejection reasons are cached for future runs: |
| 51 | |
| 52 | ```bash |
| 53 | vouch pr-cache build "$REPO_URL" --analyze-closed |
| 54 | ``` |
| 55 | |
| 56 | This calls the local `claude` CLI per closed PR (no API key needed) — slow on the first run, cheap on every subsequent `check` because the analysis is persisted. |
| 57 | |
| 58 | ## Step 2 — Run the check |
| 59 | |
| 60 | ```bash |
| 61 | vouch pr-cache check "$REPO" \ |
| 62 | --topic "$TOPIC" \ |
| 63 | ${FILES:+--files "$FILES"} \ |
| 64 | --top-k 5 |
| 65 | ``` |
| 66 | |
| 67 | Output is JSON with shape: |
| 68 | |
| 69 | ```json |
| 70 | { |
| 71 | "verdict": "likely_duplicate" | "review_candidates" | "no_match", |
| 72 | "cache_size": 335, |
| 73 | "candidates": [ |
| 74 | { |
| 75 | "number": 1419, |
| 76 | "state": "closed", |
| 77 | "title": "...", |
| 78 | "url": "...", |
| 79 | "score": 0.6, |
| 80 | "title_overlap": 0.6, |
| 81 | "path_overlap": 0.0, |
| 82 | "close_analysis": { "reason": "...", "do_not_repeat": [...] } | null |
| 83 | } |
| 84 | ] |
| 85 | } |
| 86 | ``` |
| 87 | |
| 88 | Score is 0..1 — overlap coefficient on title+body tokens, optionally blended with file-path Jaccard when `--files` is passed. Closed-not-merged PRs outrank merged ones on score ties (the "this was rejected before" signal is the higher-value one). |
| 89 | |
| 90 | ## Step 3 — Branch on the verdict |
| 91 | |
| 92 | | Verdict | Score range | What you do | |
| 93 | |---------|-------------|-------------| |
| 94 | | `likely_duplicate` | top score ≥ 0.70 | **Stop.** Surface the top 3 candidates to the user with their titles, states, URLs, and any cached `close_analysis.reason` / `do_not_repeat` bullets. Ask: "Open anyway, fold this into one of those, or skip?" | |
| 95 | | `review_candidates` | any candidate ≥ 0.15, top < 0.70 | **Pause.** Present the top 3. Most often these are *related* PRs, not exact duplicates. Read the closed ones' titles + comments; if any of them tried exactly your approach and got rejected, that's a strong signal to change tactics. Ask the user to confirm before proceeding. | |
| 96 | | `no_match` | no candidate ≥ 0.15 | **Proceed.** Log the cache size and the topic so the trail is auditable, then carry on with the originating workflow. | |
| 97 | |
| 98 | ## Step 4 — Report to the user |
| 99 | |
| 100 | Always print, even on `no_match`, so the trail is visible: |
| 101 | |
| 102 | ``` |
| 103 | PR Precheck (entrius/gittensor) |
| 104 | Topic: "normalize github_id to str for miner matching" |
| 105 | Cache: 335 PRs (built 14m ago) |
| 106 | Verdict: review_candidates |
| 107 | Top matches: |