pr-shepherd 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +150 -297
- package/bin/classify/standard/codex-notices.d.mts +3 -2
- package/bin/classify/standard/codex-notices.mjs +4 -6
- package/bin/classify/standard/gemini-notices.d.mts +1 -1
- package/bin/classify/standard/gemini-notices.mjs +2 -2
- package/bin/classify/standard/index.d.mts +4 -1
- package/bin/classify/standard/index.mjs +4 -5
- package/bin/classify/standard/kilo-no-issues.d.mts +1 -1
- package/bin/classify/standard/kilo-no-issues.mjs +2 -2
- package/bin/classify/standard/sourcery-notices.d.mts +1 -1
- package/bin/classify/standard/sourcery-notices.mjs +2 -2
- package/bin/commands/iterate/index.mjs +31 -12
- package/bin/github/captured-proxy-agent.d.mts +3 -0
- package/bin/github/captured-proxy-agent.mjs +8 -0
- package/bin/github/github-fetch.mjs +19 -6
- package/bin/github/legacy-dispatcher.d.mts +6 -0
- package/bin/github/legacy-dispatcher.mjs +44 -0
- package/bin/github/poll-summary-readiness.d.mts +6 -1
- package/bin/github/poll-summary-readiness.mjs +9 -3
- package/bin/github/poll-summary-review.mjs +13 -10
- package/package.json +3 -3
- package/plugins/pr-shepherd/.claude-plugin/plugin.json +15 -0
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
- package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
- package/bin/classify/standard/coderabbit-review-summary.d.mts +0 -8
- package/bin/classify/standard/coderabbit-review-summary.mjs +0 -37
- package/bin/classify/standard/codex-review-summary.d.mts +0 -7
- package/bin/classify/standard/codex-review-summary.mjs +0 -20
package/README.md
CHANGED
|
@@ -1,375 +1,228 @@
|
|
|
1
1
|
# pr-shepherd
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Drive a pull request to merge-ready with a coding agent. It works with Claude Code, Codex, Grok, and any stdio MCP client.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Estimated per PR session: −42% cost and −19% turns vs. `gh`, −77% cost vs. the GitHub MCP server. While waiting on CI: ~60 GraphQL points an hour vs. ~360 for `gh pr checks --watch`.**
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Outcomes
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Outcome | Headline | Evidence |
|
|
10
|
+
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
11
|
+
| [Context and correctness](#context-and-correctness) | 60 GitHub edge cases cataloged, backed by 147 scenario fixtures; on 8 of them, evals show the agent does worse without the plugin | [Handled cases](docs/handled-cases.md) |
|
|
12
|
+
| [Fewer tokens, lower cost](#fewer-tokens-and-lower-cost) | Cost −42% vs. `gh` and −77% vs. MCP per PR session; turns −19% / −70% (estimates) | [Token bench](evals/tokens/README.md) |
|
|
13
|
+
| [Clearer agent direction](#clearer-agent-direction) | GitHub gotchas (cancelled runs, merge queues, stacks): eval score 0.82 → 0.94, about two-thirds fewer misses | [Behavior evals](evals/README.md) |
|
|
14
|
+
| [Lower GitHub API quota use](#lower-github-api-quota-use) | ~60 GraphQL points per CI-wait hour vs. ~360 for `gh`; ~4 when idle. REST core per PR session 4.8 vs. 9 (`gh`), 78.3 (MCP) | [GitHub API](docs/github-api.md), [GraphQL usage](docs/graphql-usage.md) |
|
|
15
|
+
| [Safe by construction](#safe-by-construction) | 0 git mutations; human handoff limited to 10 escalation triggers for a single PR | [Escalations](docs/escalations.md) |
|
|
16
|
+
| [Works across agents and stacks](#works-across-agents-and-environments) | 3 agent plugins plus any stdio MCP client; GraphQL and REST transports; native stacked PRs | [MCP](docs/mcp.md), [cloud](docs/cloud.md) |
|
|
17
|
+
|
|
18
|
+
**[jongleberry.com/pr-shepherd](https://jongleberry.com/pr-shepherd/)** covers the why and the design principles.
|
|
10
19
|
|
|
11
20
|
## What it does
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
- Gathers the PR's review items, CI failures, and merge requirements in one call.
|
|
23
|
+
- Returns one action with numbered instructions and exact commands. `CANCEL` ends the loop and carries none.
|
|
24
|
+
- Never mutates git. The agent edits, commits, pushes, and decides what each comment deserves.
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
```mermaid
|
|
27
|
+
flowchart LR
|
|
28
|
+
A[Agent] -->|iterate PR| S[pr-shepherd]
|
|
29
|
+
S -->|batched, conditional reads| G[(GitHub)]
|
|
30
|
+
G --> S
|
|
31
|
+
S -->|one action + numbered instructions| A
|
|
32
|
+
A -->|fix, push, run the printed commands| G
|
|
33
|
+
```
|
|
17
34
|
|
|
18
|
-
|
|
19
|
-
- 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.
|
|
20
|
-
- Handles GitHub comment types (comments, threads, replies) and their states, including first-look, outdated, resolved, minimized, and edited.
|
|
21
|
-
- `apply` batches resolve / reply / minimize / dismiss. `build_suggestion_patches` validates and returns ordered diffs without mutating git.
|
|
22
|
-
- `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.
|
|
35
|
+
Without Shepherd, an agent fans out across `gh`, the GitHub MCP server, and GraphQL to rebuild PR state, then guesses the next step. Details for each outcome follow; more features are in [docs/features.md](docs/features.md).
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
## Outcome details
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
### Context and correctness
|
|
27
40
|
|
|
28
|
-
`
|
|
41
|
+
One `iterate` call returns everything the agent needs about the PR:
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
- review threads with replies, PR comments, and review summaries;
|
|
44
|
+
- each CI failure, with its failed step and a log excerpt when triage can fetch them;
|
|
45
|
+
- mergeability and the actual merge requirements;
|
|
46
|
+
- first-look, outdated, and edited items, and stack topology.
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
One exception: a `COMMENTED` review that was already minimized before the first tick is not fetched.
|
|
33
49
|
|
|
34
|
-
-
|
|
35
|
-
- `MARK_READY` — the CLI converted an eligible draft PR to ready; continue polling.
|
|
36
|
-
- `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.
|
|
37
|
-
- `MERGE` — run the emitted head-pinned merge or queue command for the selected transport. GitHub is authoritative for the result and reports any authorization failure. Queue operations can use REST.
|
|
38
|
-
- `CANCEL` — stop polling this pull request because it merged, closed, or completed its ready-delay. It prints no instructions; continue any remaining pull requests or issues from the original request.
|
|
39
|
-
- `ESCALATE` — stop polling until a human provides direction. Native stacks reach this only after their autonomous one-PR sessions are exhausted.
|
|
50
|
+
The agent reasons less and misses less. [docs/handled-cases.md](docs/handled-cases.md) catalogs the GitHub edge cases this covers. These are the gotchas where the [behavior evals](evals/README.md) measured the agent going wrong without the plugin (score with vs. without):
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
| GitHub gotcha | With | Without |
|
|
53
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---: | ------: |
|
|
54
|
+
| On a stack layer, the latest run of a required check was cancelled while an older run passed. GitHub still blocks the merge ([#567](https://github.com/jonathanong/pr-shepherd/pull/567), [gh-stack#466](https://github.com/github/gh-stack/issues/466)) | 1.00 | 0.44 |
|
|
55
|
+
| A bot `CHANGES_REQUESTED` review and a human thread arrive together | 0.89 | 0.33 |
|
|
56
|
+
| A cancelled run needs a rerun, not a code fix | 1.00 | 0.50 |
|
|
57
|
+
| Every stack layer is merged, so the loop must stop | 1.00 | 0.50 |
|
|
58
|
+
| GitHub denied a reply: skip it once instead of retrying | 0.72 | 0.33 |
|
|
59
|
+
| A maintainer removed the PR from the merge queue | 1.00 | 0.67 |
|
|
60
|
+
| A required run was cancelled in the merge queue: escalate, don't requeue | 0.75 | 0.42 |
|
|
61
|
+
| The REST transport cannot run the queue recovery | 1.00 | 0.83 |
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
### Fewer tokens and lower cost
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
> pr-shepherd 123
|
|
65
|
+
Batching, noise filtering, fingerprint skips, seen markers, and lean output cut tokens and agent turns. Estimated cost per session from the [token bench](evals/tokens/README.md) ([report](evals/tokens/REPORT.md)):
|
|
48
66
|
|
|
49
|
-
|
|
67
|
+
| Session | Cost vs. `gh` | Cost vs. GitHub MCP |
|
|
68
|
+
| --------- | ------------: | ------------------: |
|
|
69
|
+
| Single PR | **−42%** | **−77%** |
|
|
70
|
+
| PR stack | **−45%** | **−63%** |
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
**summary** 3 passing
|
|
72
|
+
These are offline estimates at 3.5 characters per token over recorded sessions, not measured billing. Two cases do not save money:
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
- A single fresh read-and-reply tick is roughly even with `gh`.
|
|
75
|
+
- Confirming an already-merged PR costs about 5% more ([report](evals/tokens/REPORT.md)).
|
|
55
76
|
|
|
56
|
-
###
|
|
77
|
+
### Clearer agent direction
|
|
57
78
|
|
|
58
|
-
|
|
79
|
+
For a single PR, each tick returns exactly one action: `WAIT`, `READY`, `MARK_READY`, `FIX_CODE`, `MERGE`, `CANCEL`, or `ESCALATE`. A `--stack` poll can also return `SHEPHERD`, which starts one-PR sessions for owned layers. Every action except `CANCEL` comes with numbered `## Instructions` and exact commands, so the agent does not invent policy. In the [behavior evals](evals/README.md), Sonnet 5.5 at low effort scores **0.94** with the plugin and **0.82** without it on the 36 GitHub-gotcha cases, so it misses about two-thirds fewer graded checks (0.18 → 0.06). On the eight gotchas listed under [Context and correctness](#context-and-correctness) it goes from 0.50 to 0.92. Weighted toward those gotchas, the mean Δ over all 52 cases is **+0.14**, and no case has a regression whose interval excludes 0. See [docs/actions.md](docs/actions.md) for the output contract.
|
|
59
80
|
|
|
60
|
-
|
|
81
|
+
### Lower GitHub API quota use
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
> oxfmt
|
|
83
|
+
GitHub meters two tiers ([details](docs/github-api.md#github-metering)):
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
- Primary hourly budgets: GraphQL points and REST `core` requests.
|
|
86
|
+
- Secondary limits: per-minute points, where each GraphQL query or REST `GET` costs 1 and each mutation costs 5, plus concurrency and content-creation limits.
|
|
66
87
|
|
|
67
|
-
1.
|
|
68
|
-
2. Triage `## Failing checks`. Playbook: "CI failure triage".
|
|
69
|
-
3. Commit and push any changes.
|
|
70
|
-
4. Journal key decisions or rejections with URLs or IDs: `pr-shepherd apply journal 123 '- <decision>'`
|
|
71
|
-
5. `$DISMISS_MESSAGE`: one sentence on what changed. Run even if no code changed: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha HEAD`
|
|
72
|
-
6. Rerun Shepherd now.
|
|
73
|
-
```
|
|
88
|
+
Waiting is where most quota goes, and where Shepherd saves the most. By default `gh pr checks --watch` refreshes every 10 seconds, at 1 GraphQL point per refresh. That is about 360 points an hour for each watched PR, so a few agents waiting on CI can drain the shared 5,000-point hourly budget. Shepherd polls every 60 seconds by default:
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
- A typical tick is one batched GraphQL query (1 point). Large or paginated PRs add supplement queries. When nothing changed, the fingerprint skip returns the stored report from that same query.
|
|
91
|
+
- While CI runs, that is about 60 points an hour for a typical PR, against about 360 for `gh pr checks --watch` at its default interval. The token bench models `gh` refreshing every 60 seconds, which ties Shepherd at 60 ([report](evals/tokens/REPORT.md)).
|
|
92
|
+
- When no check is running, conditional REST change detectors run first. A `304` consumes no primary quota. An hour of unchanged idle waiting costs about 4 GraphQL points instead of 60 ([graphql-usage.md](docs/graphql-usage.md)). That holds only when the stored report is eligible for the detectors. Native-stack layers, `READY` reports, incomplete snapshots, and detector failures still run the batched query each tick ([graphql.md](docs/graphql.md#idle-wait-change-detectors)).
|
|
76
93
|
|
|
77
|
-
|
|
94
|
+
Fewer requests help on both tiers. The GitHub MCP server issues one or more REST requests per tool call, so unbatched calls drain `core` and the per-minute pool faster.
|
|
78
95
|
|
|
79
|
-
|
|
96
|
+
Per session, assumed and deterministic, from the [token bench](evals/tokens/README.md):
|
|
80
97
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- Draft PRs can be marked ready automatically when clean; disable with `actions.autoMarkReady: false` or `--no-auto-mark-ready`.
|
|
86
|
-
- 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.
|
|
87
|
-
- 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.
|
|
88
|
-
- Generated iterate mutations use capability fields when the transport supplies them. With REST, capability is unknown and otherwise eligible mutations are attempted; GitHub's response is authoritative. Explicit `apply` operations forward the caller's requested IDs and surface GitHub's result.
|
|
89
|
-
- `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.
|
|
98
|
+
| Session | GraphQL points: Shepherd / `gh` / MCP | REST core: Shepherd / `gh` / MCP |
|
|
99
|
+
| --------- | ------------------------------------: | -------------------------------: |
|
|
100
|
+
| Single PR | 30.8 / 36.5 / 12 | 4.8 / 9 / 78.3 |
|
|
101
|
+
| PR stack | 11 / 27 / 12 | 2 / 6 / 74 |
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
- On the default GraphQL transport, Shepherd spends fewer GraphQL points than `gh`. It makes fewer REST requests than `gh` and far fewer than MCP.
|
|
104
|
+
- On a single PR, MCP spends fewer GraphQL points but about 16× the REST requests.
|
|
105
|
+
- The REST transport used in cloud sessions spends no GraphQL points. It does spend more `core` requests than `gh` or MCP (175 single PR, 432 stack).
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
See [docs/graphql-usage.md](docs/graphql-usage.md) and [docs/rest-usage.md](docs/rest-usage.md).
|
|
94
108
|
|
|
95
|
-
|
|
109
|
+
### Safe by construction
|
|
96
110
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
- The CLI runs read-only git only. It emits patches and commit commands for the agent to run ([AGENTS.md](AGENTS.md#git-operations)).
|
|
112
|
+
- Shepherd never merges by itself. With `--merge`, it emits a merge or queue command for the agent to run. The command is head-pinned for a single PR and guarded by READY receipts for a stack.
|
|
113
|
+
- Human handoff is a [finite escalation list](docs/escalations.md).
|
|
114
|
+
- The shipped skill tells the agent to treat review titles, comments, and CI logs as untrusted data. Shell and other MCP callers must apply that rule themselves.
|
|
115
|
+
- A denied reply or resolve is a one-look skip, not a retry loop.
|
|
101
116
|
|
|
102
|
-
|
|
117
|
+
### Works across agents and environments
|
|
103
118
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/
|
|
107
|
-
|
|
119
|
+
- The plugin ships [skills](docs/skills.md) for Claude Code, Codex, and Grok.
|
|
120
|
+
- Cursor and other stdio clients use the [MCP server](docs/mcp.md).
|
|
121
|
+
- Shell and CI use the [CLI](docs/cli-usage.md).
|
|
122
|
+
- In Claude Code cloud sessions, Shepherd switches to the [REST transport](docs/rest.md) and [event mode](docs/cloud.md).
|
|
123
|
+
- `--stack` reports every layer of a native GitHub stacked PR and shepherds the layers you own.
|
|
108
124
|
|
|
109
|
-
|
|
125
|
+
## Use cases
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
/pr-shepherd 42
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
MCP clients call `iterate` once per tick, then use `apply` for review/file/journal mutations and validated queue-removal acknowledgments 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.
|
|
117
|
-
|
|
118
|
-
The CLI remains useful for shell workflows. Its canonical polling form is:
|
|
119
|
-
|
|
120
|
-
```sh
|
|
121
|
-
pr-shepherd 42 # poll until non-WAIT or timeout
|
|
122
|
-
pr-shepherd 42 --interval 60s --timeout 270s
|
|
123
|
-
pr-shepherd 42 --quiet-status # print only changed WAIT status snapshots
|
|
124
|
-
pr-shepherd 42 --until-terminal # continue through WAIT/MARK_READY until work or terminal state
|
|
125
|
-
pr-shepherd 42 --debounce 5m # wait 5m after first FIX_CODE or stack SHEPHERD, then return one batched tick
|
|
126
|
-
pr-shepherd 42 --ready-delay 15m
|
|
127
|
-
pr-shepherd 42 --merge # request head-pinned auto-merge/queue; GitHub reports the result
|
|
128
|
-
pr-shepherd iterate 42 # single tick
|
|
129
|
-
pr-shepherd 42 --instructions playbook # fold invariant fix_code steps into a playbook pointer (default: inline)
|
|
130
|
-
pr-shepherd 42 --poll-mode event # one tick, no sleeping; prints nextCheck (default under CLAUDE_CODE_REMOTE=true)
|
|
131
|
-
pr-shepherd playbook "Fix-code loop" # print a bundled skill playbook (no name lists them)
|
|
132
|
-
pr-shepherd owner/repo#42 # poll a PR in an explicit repository
|
|
133
|
-
pr-shepherd https://github.com/owner/repo/pull/42
|
|
134
|
-
pr-shepherd 42 43 44 # summarize an explicit same-repository set
|
|
135
|
-
pr-shepherd --stack 43 # summarize every PR in a native GitHub stack
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
A user-supplied `--merge` authorizes the agent to run the emitted merge/enqueue commands for the
|
|
139
|
-
selected PRs or stack without another conversational confirmation. Host permission checks still apply.
|
|
140
|
-
|
|
141
|
-
Multi-PR and `--stack` polling use compact, read-only summaries from the selected transport.
|
|
142
|
-
GraphQL mode uses GraphQL summaries; REST mode preserves unavailable fields as unknown. They return when work is
|
|
143
|
-
needed, every selected PR is complete, the bounded timeout expires, or `--until-terminal` crosses a
|
|
144
|
-
configured GraphQL quota-warning band. Explicit PR sets give each actionable row an exact single-PR
|
|
145
|
-
`pollCommand`, so independent rows can proceed before the next aggregate poll.
|
|
146
|
-
|
|
147
|
-
Native-stack rows are ordered bottom-to-top. `--stack` never performs a mutation itself. Every
|
|
148
|
-
row marks ownership by comparing its author with the authenticated viewer; REST reads `/user`
|
|
149
|
-
for that identity without inferring viewer permissions. Sessions act only on rows marked `owned`. Every
|
|
150
|
-
owned layer that still has work gets its own one-PR session on the same tick, including a clean draft
|
|
151
|
-
whose session marks it ready. Layers do not wait for a lower layer's READY receipt, so their
|
|
152
|
-
ready-delays overlap. With automatic mark-ready disabled, the instructions ask the agent to mark
|
|
153
|
-
a clean draft ready after its probe. A queued stack, or one whose remaining layers can only wait,
|
|
154
|
-
returns `WAIT`; an idle `WAIT` that stays unchanged past the stall timeout returns `ESCALATE` with
|
|
155
|
-
`stall-timeout`. A terminal READY or fully merged stack returns `CANCEL`. Closed or unverified
|
|
156
|
-
topology returns `ESCALATE` for human direction after any other shepherdable PRs are handled;
|
|
157
|
-
until then, `SHEPHERD` remains the immediate action and lists the human blockers too.
|
|
158
|
-
|
|
159
|
-
With `--stack --merge`, the highest open layer whose open lower layers all have current READY
|
|
160
|
-
receipts, and whose bottom open layer GitHub has retargeted onto the stack base, returns `MERGE`
|
|
161
|
-
with the transport-specific stack merge command and allowed method. GraphQL mode uses
|
|
162
|
-
`gh stack merge <that PR number> --yes`; REST mode uses the SHA-pinned
|
|
163
|
-
`pr-shepherd apply merge` command, using the trunk's known queue policy even when the named layer
|
|
164
|
-
targets its parent branch. The command binds the observed stack, trunk, and ordered prefix with
|
|
165
|
-
`--expected-stack` and rejects a changed stack before submission. Unknown REST queue policy uses
|
|
166
|
-
`default` without a merge method. Known queue requests omit the configured direct merge method.
|
|
167
|
-
That lands the named layer and every
|
|
168
|
-
unmerged layer below it. When the base uses a merge queue, the same command queues the prefix
|
|
169
|
-
together and GitHub evaluates each layer from the bottom; a failure ejects that layer and those
|
|
170
|
-
above it. For a `failed_checks` removal on the layer's first ejection, the one-PR session emits a head-, queue-commit-, and timestamp-pinned local acknowledgment command; run it only when the failure does not reproduce after updating from the latest base, the head did not change, and no source changes or other blockers remain. Fresh source checks and a new READY receipt then let the aggregate selector recover the eligible prefix. Manual or stale removals cannot use this path. Layers above the prefix keep their one-PR sessions. After the merge, GitHub retargets
|
|
171
|
-
the next layer, so the rerun continues until the stack returns `CANCEL`. API and MCP aggregate
|
|
172
|
-
calls perform one summary tick and leave recurrence to the caller.
|
|
173
|
-
|
|
174
|
-
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`, `quietStatus`, and `reconcileSeconds` (built-in 900; the longest an idle GraphQL wait replays its last snapshot while REST change detectors are unchanged). 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.
|
|
175
|
-
|
|
176
|
-
### Apply Review And Journal Changes, Or Select Files
|
|
177
|
-
|
|
178
|
-
Use `apply` with ordered operations to reply/resolve/minimize/dismiss review items, mark selected changed files as viewed, append an idempotent Shepherd Journal item, or record a validated native-stack CI queue-removal acknowledgment. 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.
|
|
179
|
-
|
|
180
|
-
### Extract Shepherd Journal Entries
|
|
181
|
-
|
|
182
|
-
The pure `pr-shepherd/journal` entry point can extract one validated journal without GitHub access:
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
import { extractShepherdJournal } from "pr-shepherd/journal";
|
|
186
|
-
|
|
187
|
-
const result = extractShepherdJournal(prBody);
|
|
188
|
-
if (!result.ok) throw new Error(result.error);
|
|
189
|
-
|
|
190
|
-
for (const entry of result.journal?.entries ?? []) console.log(entry);
|
|
191
|
-
```
|
|
127
|
+
- **Autonomous agents.** An agent opens a PR and drives it to merge-ready, or merged when you allow it. Nobody has to watch CI and reviews.
|
|
128
|
+
- **Exhaustive AI code review.** Some PRs get many bot reviews (CodeRabbit, Codex, Copilot, and others). Shepherd shows every thread and comment once and tracks first-look, edited, and outdated items. It batches the replies, resolves, and minimizes.
|
|
192
129
|
|
|
193
|
-
|
|
194
|
-
complete Markdown list item with LF line endings. It fails closed for malformed or ambiguous
|
|
195
|
-
containers and ignores journal-shaped examples hidden in Markdown constructs. The full journal API,
|
|
196
|
-
including append and reconciliation helpers, is documented in [docs/api.md](docs/api.md).
|
|
130
|
+
## How to use
|
|
197
131
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
Both return the typed extraction JSON in `structuredContent` and `content`; neither mutates the PR.
|
|
132
|
+
1. **Install the MCP server and the CLI.** See [Install](#install) below.
|
|
133
|
+
2. **Write your policy into a skill or agent instruction.** Examples: "skip non-blocking review comments", "reply instead of fixing nits", "never merge". Shepherd reports state and the next action. Your instructions decide how the agent treats each comment. The shipped `pr-shepherd` skill is a thin dispatcher you can wrap ([docs/skills.md](docs/skills.md)).
|
|
134
|
+
3. **Configure Shepherd.** In `.pr-shepherdrc.yml`, turn on the bundled classification rules. They drop known bot noise before it reaches the agent, which saves more tokens and tool calls. With the default `actions.autoMinimizeSuppressed: true`, several rules also minimize matching comments on GitHub and log a Shepherd Journal entry; set it to `false` to keep comments untouched. See [docs/configuration.md](docs/configuration.md#classifystandard--default-).
|
|
202
135
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
It writes one JSON line containing the typed extraction result. It never reads GitHub credentials,
|
|
210
|
-
configuration, or Shepherd logs. On POSIX, its final body-file path entry must be a readable regular
|
|
211
|
-
file in a trusted parent directory; symlinks, FIFOs, and devices are rejected with exit code 66.
|
|
212
|
-
Unsupported platforms fail closed with that same exit code.
|
|
213
|
-
|
|
214
|
-
### Clean Local State
|
|
215
|
-
|
|
216
|
-
In Claude Code cloud sessions, [event mode](docs/cloud.md) runs one tick per event or wake-up instead of sleeping, and the default state directory moves into the repository's common directory.
|
|
217
|
-
|
|
218
|
-
`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](docs/configuration.md#environment-variables)).
|
|
219
|
-
|
|
220
|
-
```sh
|
|
221
|
-
pr-shepherd admin clean current
|
|
222
|
-
pr-shepherd admin clean repo
|
|
223
|
-
pr-shepherd admin clean all --dry-run
|
|
224
|
-
pr-shepherd admin log-file
|
|
225
|
-
```
|
|
136
|
+
```yaml
|
|
137
|
+
classify:
|
|
138
|
+
standard:
|
|
139
|
+
all: true
|
|
140
|
+
```
|
|
226
141
|
|
|
227
142
|
## Install
|
|
228
143
|
|
|
229
|
-
The plugin
|
|
230
|
-
|
|
231
|
-
To register the MCP server without the plugin, or to wire a local checkout, see [docs/mcp.md](docs/mcp.md).
|
|
232
|
-
|
|
233
|
-
### Claude Code
|
|
144
|
+
The plugin starts the version-matched `pr-shepherd-mcp` server automatically.
|
|
234
145
|
|
|
235
146
|
```bash
|
|
147
|
+
# Claude Code
|
|
236
148
|
claude /plugin marketplace add jonathanong/pr-shepherd
|
|
237
149
|
claude /plugin install pr-shepherd
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Codex
|
|
241
150
|
|
|
242
|
-
|
|
151
|
+
# Codex (then enable the pr-shepherd plugin)
|
|
243
152
|
codex plugin marketplace add jonathanong/pr-shepherd
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Or pin a ref:
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
codex plugin marketplace add jonathanong/pr-shepherd --ref main
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
For local development:
|
|
253
|
-
|
|
254
|
-
```bash
|
|
255
|
-
git clone https://github.com/jonathanong/pr-shepherd ~/.codex/plugin-sources/pr-shepherd
|
|
256
|
-
codex plugin marketplace add ~/.codex/plugin-sources/pr-shepherd
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
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/`.
|
|
260
|
-
|
|
261
|
-
### Grok
|
|
262
153
|
|
|
263
|
-
|
|
154
|
+
# Grok
|
|
264
155
|
grok plugin marketplace add jonathanong/pr-shepherd
|
|
265
156
|
grok plugin install pr-shepherd --trust
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
Grok starts a plugin's MCP server only after the plugin is trusted. Confirm with `grok mcp list` or `/mcps`.
|
|
269
|
-
|
|
270
|
-
### MCP server only
|
|
271
|
-
|
|
272
|
-
Any stdio MCP client can run the published binary without installing the plugin:
|
|
273
|
-
|
|
274
|
-
```bash
|
|
275
|
-
claude mcp add --transport stdio --scope user pr-shepherd -- \
|
|
276
|
-
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
277
|
-
codex mcp add pr-shepherd -- \
|
|
278
|
-
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
279
|
-
grok mcp add pr-shepherd -- \
|
|
280
|
-
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
281
|
-
```
|
|
282
157
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
## Configuration
|
|
286
|
-
|
|
287
|
-
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.
|
|
288
|
-
|
|
289
|
-
```yaml
|
|
290
|
-
cliCommand: [pnpm, exec, pr-shepherd] # launcher for emitted commands; defaults to [pr-shepherd]
|
|
291
|
-
ignoreChecks:
|
|
292
|
-
- "Kilo Code Review"
|
|
293
|
-
iterate:
|
|
294
|
-
fixAttemptsPerThread: 5
|
|
295
|
-
stallTimeoutMinutes: 60
|
|
296
|
-
minimizeApprovals: false
|
|
297
|
-
minimizeComments: all # all | bots | none
|
|
298
|
-
checks:
|
|
299
|
-
ciTriggerEvents:
|
|
300
|
-
- pull_request
|
|
301
|
-
- pull_request_target
|
|
302
|
-
merge:
|
|
303
|
-
method: squash
|
|
304
|
-
commandArgs:
|
|
305
|
-
- --delete-branch
|
|
306
|
-
actions:
|
|
307
|
-
autoMinimizeSuppressed: true
|
|
308
|
-
autoMarkReady: false
|
|
158
|
+
# CLI, for shells and CI
|
|
159
|
+
npm install -g pr-shepherd
|
|
309
160
|
```
|
|
310
161
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
- `GH_TOKEN` / `GITHUB_TOKEN` / `GITHUB_PERSONAL_ACCESS_TOKEN` for auth; `gh auth token` is used as a fallback. See [GitHub authentication and token access](docs/authentication.md) for required PAT permissions.
|
|
314
|
-
- `PR_SHEPHERD_STATE_DIR` to override state and log location.
|
|
315
|
-
- `PR_SHEPHERD_LOG_DISABLED=1` to disable per-worktree debug logging.
|
|
162
|
+
For the MCP server without the plugin, or a local checkout, see [docs/mcp.md](docs/mcp.md).
|
|
316
163
|
|
|
317
|
-
|
|
164
|
+
Requirements:
|
|
318
165
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
166
|
+
- Node.js >= 22.19.0, Bun, or Deno
|
|
167
|
+
- `git`
|
|
168
|
+
- A GitHub token or an authenticated `gh` CLI with [the required access](docs/authentication.md)
|
|
322
169
|
|
|
323
|
-
|
|
170
|
+
## Quick start
|
|
324
171
|
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
all: true
|
|
172
|
+
```text
|
|
173
|
+
/goal /pr-shepherd:pr-shepherd 42 # Claude Code
|
|
174
|
+
/goal $pr-shepherd 42 # Codex
|
|
175
|
+
/pr-shepherd 42 # Grok
|
|
330
176
|
```
|
|
331
177
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
178
|
+
```sh
|
|
179
|
+
pr-shepherd 42 # shell: poll for work (270s cap; --until-terminal removes it)
|
|
180
|
+
```
|
|
335
181
|
|
|
336
|
-
|
|
337
|
-
// .pr-shepherd/classification/gemini-quota.mts
|
|
338
|
-
import type { ClassifyRule } from "pr-shepherd/classify";
|
|
182
|
+
Leave out the PR number to use the current branch. Example `FIX_CODE` output:
|
|
339
183
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (!/You have reached your daily quota limit/i.test(item.body)) return null;
|
|
343
|
-
return { suppress: true, autoResolve: true };
|
|
344
|
-
};
|
|
345
|
-
export default rule;
|
|
346
|
-
```
|
|
184
|
+
```text
|
|
185
|
+
# PR #123 [FIX_CODE]
|
|
347
186
|
|
|
348
|
-
|
|
187
|
+
**status** `UNRESOLVED_COMMENTS` · **repo** `owner/repo`
|
|
188
|
+
**summary** 3 passing
|
|
349
189
|
|
|
350
|
-
|
|
190
|
+
## Review threads
|
|
351
191
|
|
|
352
|
-
|
|
192
|
+
### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice · MEMBER)
|
|
353
193
|
|
|
354
|
-
|
|
194
|
+
> The variable name is misleading.
|
|
355
195
|
|
|
356
|
-
|
|
196
|
+
## Failing checks
|
|
357
197
|
|
|
358
|
-
|
|
198
|
+
- `24697658766` — `CI › lint / typecheck / test (22.x)` [conclusion: FAILURE]
|
|
199
|
+
> oxfmt
|
|
359
200
|
|
|
360
|
-
|
|
361
|
-
- A GitHub token or authenticated `gh` CLI with the [required repository access](docs/authentication.md). A classic PAT needs the `repo` scope for complete operation.
|
|
362
|
-
- `git`
|
|
201
|
+
## Instructions
|
|
363
202
|
|
|
364
|
-
|
|
203
|
+
1. Fix each warranted item.
|
|
204
|
+
2. Triage `## Failing checks`. Playbook: "CI failure triage".
|
|
205
|
+
3. Commit and push any changes.
|
|
206
|
+
4. Journal key decisions or rejections with URLs or IDs: `pr-shepherd apply journal 123 '- <decision>'`
|
|
207
|
+
5. `$DISMISS_MESSAGE`: one sentence on what changed. Run even if no code changed: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha HEAD`
|
|
208
|
+
6. Rerun Shepherd now.
|
|
209
|
+
```
|
|
365
210
|
|
|
366
|
-
|
|
211
|
+
## Documentation
|
|
367
212
|
|
|
368
|
-
|
|
213
|
+
| Topic | Doc |
|
|
214
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
215
|
+
| Index of every doc | [docs/README.md](docs/README.md) |
|
|
216
|
+
| Features, workflow assumptions, limits | [docs/features.md](docs/features.md) |
|
|
217
|
+
| Commands, flags, stacks, polling | [docs/cli-usage.md](docs/cli-usage.md) |
|
|
218
|
+
| Actions and exit codes | [docs/actions.md](docs/actions.md), [docs/exit-codes.md](docs/exit-codes.md) |
|
|
219
|
+
| Configuration and classification rules | [docs/configuration.md](docs/configuration.md), [examples/classification/](examples/classification/) |
|
|
220
|
+
| Journal, library API | [docs/api.md](docs/api.md) |
|
|
221
|
+
| Compared with Cursor, Copilot, CodeRabbit, … | [docs/comparison.md](docs/comparison.md) |
|
|
369
222
|
|
|
370
|
-
|
|
223
|
+
## Harness ecosystem
|
|
371
224
|
|
|
372
|
-
- [auto-harness](https://github.com/jonathanong/auto-harness)
|
|
373
|
-
- [agent-blackboard](https://github.com/jonathanong/agent-blackboard)
|
|
374
|
-
- [pr-shepherd](https://github.com/jonathanong/pr-shepherd)
|
|
375
|
-
- [no-mistakes](https://github.com/jonathanong/no-mistakes)
|
|
225
|
+
- [auto-harness](https://github.com/jonathanong/auto-harness): non-interactive agent CLI orchestration across sandboxes
|
|
226
|
+
- [agent-blackboard](https://github.com/jonathanong/agent-blackboard): session-scoped telemetry for autonomous agents
|
|
227
|
+
- [pr-shepherd](https://github.com/jonathanong/pr-shepherd): autonomous pull request shepherd
|
|
228
|
+
- [no-mistakes](https://github.com/jonathanong/no-mistakes): deterministic AST-based codebase intelligence, test selection, and linting for agents
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ClassifyRule } from "../types.mts";
|
|
2
2
|
/**
|
|
3
|
-
* Codex quota notices, the review-activity status table, and no-findings notices
|
|
4
|
-
* matches the whole comment, so a notice with any appended finding stays
|
|
3
|
+
* Codex quota notices, the review-activity status table, and no-findings notices on conversation
|
|
4
|
+
* comments. Each pattern matches the whole comment, so a notice with any appended finding stays
|
|
5
|
+
* visible. Review summaries and inline comments are left for the normal review path.
|
|
5
6
|
*/
|
|
6
7
|
declare const rule: ClassifyRule;
|
|
7
8
|
export default rule;
|
|
@@ -10,19 +10,17 @@ const ACTIVITY_SUMMARY = /^<!-- codex-pull-request-review-summary -->\n(?:<!-- c
|
|
|
10
10
|
const CLEAN_REVIEW = /^Codex Review: Didn't find any major issues\. (?:Bravo\.|Breezy!|Hooray!|:\+1:)\n\n\*\*Reviewed commit:\*\* `[a-f0-9]+`$/;
|
|
11
11
|
const CLEAN_SECURITY = /^### 🛡️ Codex Security Review(?: · _Automatically triggered_)?\n\nSecurity review completed\. No security issues were found in this pull request\.\n\n\*\*Reviewed commit:\*\* `[a-f0-9]+`\n\n\[View security finding report\]\(https:\/\/chatgpt.com\/codex\/cloud\/tasks\/[a-zA-Z0-9_]+\)\n\n_Only the user who started this review can view the report in Codex\._$/;
|
|
12
12
|
/**
|
|
13
|
-
* Codex quota notices, the review-activity status table, and no-findings notices
|
|
14
|
-
* matches the whole comment, so a notice with any appended finding stays
|
|
13
|
+
* Codex quota notices, the review-activity status table, and no-findings notices on conversation
|
|
14
|
+
* comments. Each pattern matches the whole comment, so a notice with any appended finding stays
|
|
15
|
+
* visible. Review summaries and inline comments are left for the normal review path.
|
|
15
16
|
*/
|
|
16
17
|
const rule = (item) => {
|
|
17
|
-
if (!isBotItem(item, ["chatgpt-codex-connector"], ["pr-comment"
|
|
18
|
+
if (!isBotItem(item, ["chatgpt-codex-connector"], ["pr-comment"]))
|
|
18
19
|
return null;
|
|
19
|
-
}
|
|
20
20
|
const body = item.body.trim();
|
|
21
21
|
if (QUOTA_NOTICES.some((notice) => notice.test(body))) {
|
|
22
22
|
return { autoResolve: true, suppress: true, reason: "Codex usage-limit notice" };
|
|
23
23
|
}
|
|
24
|
-
if (item.kind !== "pr-comment")
|
|
25
|
-
return null;
|
|
26
24
|
// Strip only the trailing About block; extra findings after it stay visible.
|
|
27
25
|
const content = body.replace(ABOUT_BLOCK, "").trim();
|
|
28
26
|
if (ACTIVITY_SUMMARY.test(content)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ClassifyRule } from "../types.mts";
|
|
2
|
-
/** Gemini Code Assist daily-quota and review-limit notices. A heading means a real review. */
|
|
2
|
+
/** Gemini Code Assist daily-quota and review-limit notices on conversation comments. A heading means a real review. */
|
|
3
3
|
declare const rule: ClassifyRule;
|
|
4
4
|
export default rule;
|