PluginWorld
Pr

pr-shepherd

Claude Code✓ SPEC VERIFIED

Autonomous PR CI monitor and review-comment resolver for agentic coding tools

@jonathanong · v0.56.1 · MIT · updated 2d ago

SECURITY

A

SCORE

76

INSTALLS

▲ 23K

PLUG IN

/plugin marketplace add jonathanong/pr-shepherd
/plugin install pr-shepherd

README

pr-shepherd

Autonomous PR CI monitor and review-comment resolver for agentic coding tools, including Claude Code and Codex.

jongleberry.com/pr-shepherd — the why and the principles behind the design, for humans and agents.

Why

An agent finishing a PR should think about code, not reconstruct GitHub state or invent a next-step policy each tick. Without Shepherd it fans out across GitHub MCP, gh, and GraphQL, then guesses what to do with the result.

What it does

  1. Gather all context for a PR in one invocation: review threads, comments, replies, summaries, CI, mergeability, merge requirements, first-look / outdated / edited items, and author provenance.
  2. Provide deterministic actions for the agent: exactly one of WAIT, MARK_READY, FIX_CODE, MERGE, CANCEL, or ESCALATE, plus numbered ## Instructions and explicit commands. The agent still decides whether a comment or CI failure needs a code change. Shepherd does not classify signal vs noise and does not mutate git.

Highlights:

  • Batched GraphQL reads and writes (plus REST where GraphQL cannot) so one poll replaces a tool-call fan-out. MCP iterate is one tick and the client owns recurrence; --debounce is a poll-dispatcher settle window, not an MCP tool.
  • CI summaries include failed checks, and the failed job/step plus a log excerpt when triage can fetch them. Job and log details are omitted for STARTUP_FAILURE and CANCELLED; agents may still inspect logs.
  • Handles GitHub comment types (comments, threads, replies) and their states, including first-look, outdated, resolved, minimized, and edited.
  • apply batches resolve / reply / minimize / dismiss. build_suggestion_patches validates and returns ordered diffs without mutating git.
  • BEHIND is mergeability information, not a rebase or a guarantee that the next push is at the default-branch tip. The agent can update the branch before pushing.

Full reference: docs/README.md. Feature matrix: docs/features.md.

How It Works

pr-shepherd moves deterministic PR orchestration into a local MCP server, with a CLI for shells and CI. Both interfaces fetch the same GitHub state, emit raw-enough context, and return a numbered plan for the calling agent to follow.

The MCP server exposes iterate, apply, build_suggestion_patches, extract_journal, and get_journal. apply accepts ordered review mutations, file-view mutations, and journal entries; the deprecated singular suggestion tool remains temporarily as an adapter. PR-targeted MCP calls require a repository-qualified pr: a GitHub PR URL or owner/repo#N; the explicit repository is the target for GitHub I/O, even when it differs from the local checkout. extract_journal takes a Markdown body string and performs no I/O. The CLI and programmatic API also retain bare-number and current-branch PR discovery. The shipped skills are thin dispatchers for those tools.

Each tick returns exactly one action:

  • WAIT — no immediate action; continue with the next poll.
  • MARK_READY — the CLI converted an eligible draft PR to ready; continue polling.
  • FIX_CODE — agent work is required; complete it, push when needed, then continue polling. Push access to the PR head branch is a usage precondition.
  • MERGE — run the emitted head-pinned auto-merge or queue command. Ordinary merges include a plain-merge fallback; queue merges include a GraphQL enqueue fallback. GitHub is authoritative for the result and reports any authorization failure.
  • CANCEL — stop polling because the PR merged, closed, or completed its ready-delay.
  • ESCALATE — stop polling until a human provides direction. Native stacks reach this only after their autonomous one-PR sessions are exhausted.

Native-stack summaries additionally use stack-level SHEPHERD: run the listed one-PR sessions, then recheck the stack. It is not a per-PR FIX_CODE action.

Example shape:

> pr-shepherd 123

# PR #123 [FIX_CODE]

**status** `UNRESOLVED_COMMENTS` · **merge** `CLEAN` · **state** `OPEN` · **repo** `owner/repo`
**summary** 3 passing
Approvals: None [Not Required]
Conversations Resolved: No [Not Required]

## Review threads

### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice · User · MEMBER)

> The variable name is misleading.

## Failing checks

- `24697658766` — `CI › lint / typecheck / test (22.x)` [conclusion: FAILURE]
  > oxfmt

## Post-fix actions

- base: `main`
- apply review: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha "$HEAD_SHA"`

## Instructions

1. Review each item under `## Review threads` and `## Failing checks` and decide whether it needs a code change.
2. Apply every warranted review fix in each file referenced above.
3. Triage `## Failing checks`. Playbook: "CI failure triage".
4. If you changed code, commit any remaining changes and push to the PR head branch. If you did not, do not commit.
5. If you did not change code, replace `$HEAD_SHA` with `$(git rev-parse HEAD)` (it must equal the remote PR head). If you did, use the pushed SHA.
6. Replace `$DISMISS_MESSAGE` with one sentence describing what changed.
7. Run the `apply review:` command above. Playbook: "Review-mutation mechanics".
8. `[FIX_CODE]` is non-terminal. Iterate immediately with the same options.

See docs/actions.md for the complete output contract and docs/escalations.md for the exact finite human-handoff boundary. Iterate/poll PR outcomes use exit codes 0 and 10–16; command and GitHub failures use sysexits.h codes — docs/exit-codes.md.

Workflow Assumptions

This system is opinionated and works best with PRs that use required status checks and conversation resolution.

  • A human inline thread whose original comment has viewerDidAuthor: true is replied to and resolved when its latest comment is unmarked. Bot/non-human threads use the same reply-and-resolve pairing. An unmarked other-human inline thread remains reply-only unless iterate.resolveOtherHumanThreads is outdated or always. Human items are never minimized.
  • Detected bots, configured botUsernames, and viewer-authored human review threads are returned until resolved when the required mutation is authorized. Reply and resolve mutations use the thread ID, so they still run when GitHub has cleared the source line. Unauthorized threads are surfaced once and then marker-gated until edited. Bot/non-human threads, PR comments, and review summaries can be resolved or minimized when eligible. Review summaries are not minimized while known inline child threads from that review remain unresolved.
  • Shepherd identifies its own latest reply only when that comment begins <!-- pr-shepherd -->, not from author equality. A marked viewer-authored thread can be resolved without another reply as a retry.
  • Every review thread/comment/review summary is surfaced at least once, even if already outdated, resolved, or minimized; edited items re-surface through seen markers.
  • Draft PRs can be marked ready automatically when clean; disable with actions.autoMarkReady: false or --no-auto-mark-ready.
  • With --merge, actionable review threads/comments/reviews/summaries are held back (WAIT, with raw deferred-work counts) while a PR sits in the merge queue, since a Shepherd-initiated push would eject it; set actions.workWhileQueued: true to act on them immediately instead. Failing checks and merge conflicts are never deferred.
  • The CLI never performs git mutations itself — it only emits commit/push instructions for the agent to run. Push access to the PR head is a usage precondition; GitHub viewer fields do not create a separate push-authorization handoff.
  • Generated iterate mutations and automatic actions are capability-aware and omit unauthorized commands. Explicit apply operations forward the caller's requested IDs without iterate's author or capability policy and surface GitHub's result.
  • build_suggestion_patches turns one or more ordered GitHub suggestion threads into checked patches and commit metadata, but never edits the working tree or git history. Local HEAD may be ahead when the live PR head is its ancestor.

Usage

Iterate A PR

Claude Code:

/goal /pr-shepherd:pr-shepherd        # infer PR from current branch
/goal /pr-shepherd:pr-shepherd 42

Codex:

/goal $pr-shepherd        # infer PR from current branch
/goal $pr-shepherd 42

Grok:

/pr-shepherd                          # infer PR from current branch
/pr-shepherd 42

MCP clients call iterate once per tick, then use apply for review/file/journal mutations and build_suggestion_patches for anchored suggestions. To read journal entries, call extract_journal with a body already in hand or get_journal with a repository-qualified PR reference. iterate returns the same structured action data as the CLI, including its review mutation arguments. The client owns recurrence, so this works consistently in Codex, Claude Code, Grok, and any other stdio MCP client.

The CLI remains useful for shell workflows. Its canonical polling form is:

pr-shepherd 42                         # poll until non-WAIT or timeout
pr-shepherd 42 --interval 60s --timeout 270s
pr-shepherd 42 --quiet-status          # print only changed WAIT status snapshots
pr-shepherd 42 --until-terminal        # continue through WAIT/MARK_READY until work or terminal state
pr-shepherd 42 --debounce 5m           # wait 5m after first FIX_CODE or stack SHEPHERD, then return one batched tick
pr-shepherd 42 --ready-delay 15m
pr-shepherd 42 --merge                  # request head-pinned auto-merge/queue; GitHub reports the result
pr-shepherd iterate 42                 # single tick
pr-shepherd owner/repo#42              # poll a PR in an explicit repository
pr-shepherd https://github.com/owner/repo/pull/42
pr-shepherd 42 43 44                   # summarize an explicit same-repository set
pr-shepherd --stack 43                 # summarize every PR in a native GitHub stack

Multi-PR and --stack polling use compact, read-only GraphQL summaries. They return when work is needed, every selected PR is complete, the bounded timeout expires, or --until-terminal crosses a configured GraphQL quota-warning band. Explicit PR sets give each actionable row an exact single-PR pollCommand, so independent rows can proceed before the next aggregate poll.

Native-stack rows are ordered bottom-to-top. --stack never performs a mutation itself. Every layer that still has work gets its own one-PR session on the same tick, including a clean draft whose session marks it ready. Layers do not wait for a lower layer's READY receipt, so their ready-delays overlap. With automatic mark-ready disabled, the instructions ask the agent to mark a clean draft ready after its probe. A queued stack, or one whose remaining layers can only wait, returns WAIT; an idle WAIT that stays unchanged past the stall timeout returns ESCALATE with stall-timeout. A terminal READY or fully merged stack returns CANCEL. Closed or unverified topology returns ESCALATE for human direction after any other shepherdable PRs are handled; until then, SHEPHERD remains the immediate action and lists the human blockers too.

With --stack --merge, the highest open layer whose open lower layers all have current READY receipts, and whose bottom open layer GitHub has retargeted onto the stack base, returns MERGE with gh stack merge <that PR number> --yes and the allowed method flag (--squash unless config or the repository selects another). That lands the named layer and every unmerged layer below it. When the base uses a merge queue, the same command queues the prefix together and GitHub evaluates each layer from the bottom; a failure ejects that layer and those above it. Layers above the prefix keep their one-PR sessions. After the merge, GitHub retargets the next layer, so the rerun continues until the stack returns CANCEL. API and MCP aggregate calls perform one summary tick and leave recurrence to the caller.

Polling defaults can be set under poll in .pr-shepherdrc.yml: intervalSeconds (built-in 60 for one PR), stackIntervalFactor (built-in 2, so --stack and multi-PR polls wait 120s), timeoutSeconds, debounceSeconds, and quietStatus. Explicit --interval overrides either period for that invocation and is not multiplied. Other explicit flags override configuration, including --no-quiet-status when a shared config enables quiet output. Quiet status remains off by default. Quota-warning bands stay multiples of intervalSeconds; the dispatcher sleeps the slower of the effective interval and the active band, so a default stack waits 120s until a tighter band is slower than that.

Apply Review And Journal Changes, Or Select Files

Use apply with ordered operations to reply/resolve/minimize/dismiss review items, mark selected changed files as viewed, or append an idempotent Shepherd Journal item. Explicit operations are attempted and surface GitHub's per-operation results; generated iterate guidance remains capability-filtered. Use build_suggestion_patches to turn ordered review suggestions into checked patches and commit metadata; it never changes the worktree or git history.

Extract Shepherd Journal Entries

The pure pr-shepherd/journal entry point can extract one validated journal without GitHub access:

import { extractShepherdJournal } from "pr-shepherd/journal";

const result = extractShepherdJournal(prBody);
if (!result.ok) throw new Error(result.error);

for (const entry of result.journal?.entries ?? []) console.log(entry);

The result identifies canonical details versus historical legacy H2 journals and returns each complete Markdown list item with LF line endings. It fails closed for malformed or ambiguous containers and ignores journal-shaped examples hidden in Markdown constructs. The full journal API, including append and reconciliation helpers, is documented in docs/api.md.

MCP clients can call extract_journal({ body: prBody }) for the same pure result without writing a file, or get_journal({ pr: "owner/repo#123" }) to fetch a PR body through GraphQL and extract it. Both return the typed extraction JSON in structuredContent and content; neither mutates the PR.

For shell automation that already has a PR body, use the equivalent local-only command:

pr-shepherd journal extract --body-file pr-body.md

It writes one JSON line containing the typed extraction result. It never reads GitHub credentials, configuration, or Shepherd logs. On POSIX, its final body-file path entry must be a readable regular file in a trusted parent directory; symlinks, FIFOs, and devices are rejected with exit code 66. Unsupported platforms fail closed with that same exit code.

Clean Local State

pr-shepherd stores seen markers, fix-attempt counters, stall fingerprints, ready-delay markers, and logs under $PR_SHEPHERD_STATE_DIR (default: pr-shepherd-state in the per-user temp dir; see configuration).

pr-shepherd admin clean current
pr-shepherd admin clean repo
pr-shepherd admin clean all --dry-run
pr-shepherd admin log-file

Install

The plugin launches the version-matched pr-shepherd-mcp binary from the pr-shepherd npm package automatically. Install the pr-shepherd CLI separately only when you want the shell interface.

To register the MCP server without the plugin, or to wire a local checkout, see docs/mcp.md.

Claude Code

claude /plugin marketplace add jonathanong/pr-shepherd
claude /plugin install pr-shepherd

Codex

codex plugin marketplace add jonathanong/pr-shepherd

Or pin a ref:

codex plugin marketplace add jonathanong/pr-shepherd --ref main

For local development:

git clone https://github.com/jonathanong/pr-shepherd ~/.codex/plugin-sources/pr-shepherd
codex plugin marketplace add ~/.codex/plugin-sources/pr-shepherd

After adding the marketplace, install/enable the pr-shepherd plugin from Codex. The marketplace root must contain .agents/plugins/marketplace.json and plugins/pr-shepherd/.

Grok

grok plugin marketplace add jonathanong/pr-shepherd
grok plugin install pr-shepherd --trust

Grok starts a plugin's MCP server only after the plugin is trusted. Confirm with grok mcp list or /mcps.

MCP server only

Any stdio MCP client can run the published binary without installing the plugin:

claude mcp add --transport stdio --scope user pr-shepherd -- \
  npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
codex mcp add pr-shepherd -- \
  npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
grok mcp add pr-shepherd -- \
  npx --yes --package pr-shepherd@<version> pr-shepherd-mcp

Replace <version> with a published version. Full config-file examples, tool schemas, and local-checkout wiring are in docs/mcp.md.

Configuration

Create .pr-shepherdrc.yml in your project root, an ancestor directory, or $HOME. Every file on the walk is deep-merged; closer directories override farther ones.

cliCommand: [pnpm, exec, pr-shepherd] # launcher for emitted commands; defaults to [pr-shepherd]
ignoreChecks:
  - "Kilo Code Review"
iterate:
  fixAttemptsPerThread: 5
  stallTimeoutMinutes: 60
  minimizeApprovals: false
  minimizeComments: all # all | bots | none
checks:
  ciTriggerEvents:
    - pull_request
    - pull_request_target
merge:
  method: squash
  commandArgs:
    - --delete-branch
actions:
  autoMinimizeSuppressed: true
  autoMarkReady: false

Environment variables:

  • GH_TOKEN / GITHUB_TOKEN / GITHUB_PERSONAL_ACCESS_TOKEN for auth; gh auth token is used as a fallback. See GitHub authentication and token access for required PAT permissions.
  • PR_SHEPHERD_STATE_DIR to override state and log location.
  • PR_SHEPHERD_LOG_DISABLED=1 to disable per-worktree debug logging.

See docs/configuration.md for the full reference.

For guided noise reduction, use the plugin's reduce-pr-noise skill. It loads focused guidance for bot-comment classifiers or settings only when relevant; see docs/skills.md.

Classification rules

Drop .ts / .mts / .mjs / .js files under .pr-shepherd/classification/ to suppress and/or auto-resolve specific bot comments — useful for silencing repetitive noise like rate-limit notices from gemini-code-assist or "Reviews paused" from coderabbitai.

// .pr-shepherd/classification/gemini-quota.mts
import type { ClassifyRule } from "pr-shepherd/classify";

const rule: ClassifyRule = (item) => {
  if (item.author !== "gemini-code-assist") return null;
  if (!/You have reached your daily quota limit/i.test(item.body)) return null;
  return { suppress: true, autoResolve: true };
};
export default rule;

suppress: true hides the item from agent output. autoResolve: true queues it for the minimize/resolve mutation. reason is an optional note. When both flags apply together and actions.autoMinimizeSuppressed is true (the default), Shepherd resolves the thread or minimizes the comment or review summary during iterate only when GitHub reports the exact per-object capability. Confirmed successes are recorded on threads.autoResolved and comments.autoMinimized, printed once as ## Classification auto-resolve before ## Instructions (the same ruleAutoResolve object in JSON), and appended as one Shepherd Journal list item attributed to the token login. Distinct rule reasons are included. Failed IDs stay on the generated apply review command, and each failure is an extra bullet in that section. A journal write failure is reported the same way and does not undo the GitHub resolve or minimize. actions.autoMinimizeSuppressed: false leaves the IDs on that command and does not mutate, journal, or print the line. Denied or unverifiable items return to the normal first-look/edit visibility gate and produce no mutation recommendation.

TypeScript rules are loaded by the runtime's native TypeScript support; keep them to erasable syntax such as type annotations and import type. Runtime TypeScript features that need transpilation, such as enums, namespaces, parameter properties, and decorators, are not supported. Use .mts for portable ESM rules across Node, Bun, and Deno.

Ready-to-use examples for common patterns are in examples/classification/.

CLI aliases

poll, resolve, build-suggestion-patch, commit-suggestion, mark-files-as-viewed, journal, clean, and log-file are CLI aliases or deprecated adapters. Prefer default polling/iterate in a shell and the MCP iterate, apply, and build_suggestion_patches tools in an agent client.

Requirements

  • Node.js >= 22.18.0, Bun, or Deno
  • A GitHub token or authenticated gh CLI with the required repository access. A classic PAT needs the repo scope for complete operation.
  • git

Docs

Full reference, grouped by the two jobs (gather context / emit actions): docs/README.md.

Harness Ecosystem

This is part of the following harness ecosystem:

  • auto-harness - non-interactive agent CLI orchestration across sandboxes
  • agent-blackboard - session-scoped telemetry for autonomous agents
  • pr-shepherd - autonomous pull request shepherd
  • no-mistakes - deterministic AST-based codebase intelligence, test selection, and linting for agents

SIMILAR PLUGINS