Skip to content
Development
Skill

/loop-gh-pr

Compose a /loop that watches all open GitHub PRs for activity (new PRs, new commits, new comments) and auto-prepares a READ-ONLY /lets:review draft for each code change - never posting. Use when the user says "watch PRs", "monitor github PRs", "loop-gh-pr", "стеж за PR", "поллі

From plugin
lets-workflow
1722 skills15 agents26 commands
Install
$ npx -y skills add restarter/lets-workflow --skill loop-gh-pr --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/loop-gh-pr

Context preview

The summary Claude sees to decide when to auto-load this skill.

Compose a /loop that watches all open GitHub PRs for activity (new PRs, new commits, new comments) and auto-prepares a READ-ONLY /lets:review draft for each code change - never posting. Use when the user says "watch PRs", "monitor github PRs", "loop-gh-pr", "стеж за PR", "поллі

SKILL.md

loop-gh-pr.SKILL.md
name: loop-gh-pr
description: Compose a /loop that watches all open GitHub PRs for activity (new PRs, new commits, new comments) and auto-prepares a READ-ONLY /lets:review draft for each code change - never posting. Use when the user says "watch PRs", "monitor github PRs", "loop-gh-pr", "стеж за PR", "поллі гітхаб PR", or wants a hands-near PR-review watcher. Composes a paste-ready /loop block - does NOT invoke /loop (UI command). Local test skill.

loop-gh-pr

Compose a `/loop` that periodically lists open GitHub PRs via `gh`, detects what is new since the last tick (new PR / new commits / new comments), auto-runs a READ-ONLY `/lets:review <N> --json` draft for each code change, fires a `lets cmux notify`, and reschedules - WITHOUT ever posting to a PR.

The skill **never invokes `/loop` itself** - Claude Code's `/loop` is a UI command. It composes the prompt and presents it for paste; once active, the autonomous run uses `ScheduleWakeup` per tick.

**Autonomy boundary (the whole point):** `/lets:review --json` is read-only - it computes findings and posts NOTHING. So the loop safely auto-runs review. Posting (inline / general / approve / merge) is ALWAYS the human's call, out-of-band. The loop stops at a ready draft in `.lets/reviews/` + a notification and keeps watching. Never saw -> reviewed -> posted.

> **IMPORTANT:** If the spec below invokes any deferred tool (e.g. `AskUserQuestion`), you MUST load and call it as specified.

Step 1: Pre-flight (always run)

Confirm github flow + `gh` auth + that listing PRs works, before composing a long-lived loop.

LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)

# 1. github flow only (v1). LETS_PR_FLOW is substituted by the orchestrator from LETS Config.
#    If LETS_PR_FLOW != github -> surface "loop-gh-pr is github-only in v1 (LETS_PR_FLOW={value});
#    bitbucket waits on lets-6pxvm" and exit.

# 2. gh present + authed
gh auth status 2>&1 | head -5 || { echo "gh not authed - run: gh auth login"; exit 1; }

# 3. smoke: list open PRs (the watch set). If `comments` is rejected as a list field,
#    drop it here and have the loop fall back to per-PR `gh pr view <N> --json comments`.
gh pr list --state open --json number,title,headRefOid,isDraft,url,updatedAt,comments --limit 30 2>&1 | head -40

Surface a short summary of the current open non-draft PRs (number, title, head SHA short, comment count) so the user sees exactly what the loop will watch. If `gh auth status` fails -> surface and exit. If `gh pr list` errors (e.g. not a GitHub remote) -> surface verbatim and exit. Never compose a loop the autonomous run cannot execute.

Step 2: State file

`.lets/pr-watch-state.json` - one file, keyed by PR number. `.lets/` is gitignored and symlinked across worktrees (per-repo state - fine; we key by PR number).

Shape:

{
  "captured_at": "<iso8601>",
  "prs": {
    "42": {
      "title": "...",
      "head_sha": "<headRefOid>",
      "comment_count": 3,
      "updated_at": "<updatedAt from gh>",
      "last_reviewed_sha": "<headRefOid the latest draft covers, or null if not yet/failed>",
      "last_seen": "<iso8601>"
    }
  }
}
  • `head_sha` vs stored -> new-commits signal.
  • `comment_count` vs stored -> new-comments signal; `updated_at` is a cheap secondary signal.
  • `last_reviewed_sha == head_sha` -> already reviewed at this SHA; skip re-review (dedup).
  • `last_reviewed_sha == null` (review never ran, or review FAILED) -> a failed review keeps it `null` so the next tick re-attempts; never advance it on failure.
  • A PR absent from the current `gh pr list` but present in state -> merged/closed; drop it and report once.
  • The baseline is captured ONCE on first tick; never silently re-baselined. On tick-1, PRs beyond `INIT_REVIEW_CAP` are baselined with `last_reviewed_sha = head_sha` (seen, not reviewed) and surfaced for manual review.

Step 3: Compose /loop

Template (dynamic mode - the model self-paces via ScheduleWakeup). The body is always English. Present this for paste:

/loop Watch all open GitHub PRs for activity and prepare READ-ONLY review drafts. NEVER post to any PR.

State file: .lets/pr-watch-state.json (keyed by PR number). Initial-review cap: 5.

Each tick:
1. Resolve root: LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)
2. Fetch the watch set (READ-ONLY):
   gh pr list --state open --json number,title,headRefOid,isDraft,url,updatedAt,comments --limit 30
   If `comments` is rejected as a list field, re-run without it and read each count via
   `gh pr view <number> --json comments -q '.comments | length'`.
   Drop isDraft=true entries.

Review primitive (used below): run `/lets:review <number> --json`. It saves
.lets/reviews/<date>-PR-<number>.json + .md and posts NOTHING, and must run with NO interactive
prompt. If it returns no JSON artifact (failed/empty), treat the review as FAILED: do NOT advance
last_reviewed_sha for that PR (leave it null), surface "review failed for #<number> - will retry",
and let the next tick re-attempt. Never mark a PR reviewed when it was not.

FIRST tick (.lets/pr-watch-state.json does not exist):
- Create the file. Take the open non-draft PRs in listed order.
- For the FIRST 5: run the review primitive, read verdict + findings_count from the saved JSON,
  set last_reviewed_sha=head_sha only on success (null on failure).
- For any open non-draft PR BEYOND the first 5: baseline as already-seen (last_reviewed_sha=head_sha)
  WITHOUT reviewing, and add to a "baselined without initial review (large open set): #a #b ..." line.
- Write baseline per PR: {title, head_sha=headRefOid, comment_count=(comments|length),
  updated_at=updatedAt, last_reviewed_sha=(per above), last_seen=now}.
- Surface a combined summary (one line per reviewed PR: #N "title" -> verdict, K findings ->
  .lets/reviews/<file>), plus the baselined-without-review line if any.
- Build the notify body safely, then fire one notification:
    BODY=$(printf '%s' "<combined summary>")
    let
Read more
Ships withlets-workflow

A development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.

Get the whole plugin

Other skills on lets-workflow.