pr-shepherd 0.34.0 → 0.36.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/plugin.json +3 -2
- package/.grok-plugin/marketplace.json +17 -0
- package/README.md +80 -43
- package/bin/api.d.mts +80 -0
- package/bin/api.mjs +236 -0
- package/bin/checks/classify.d.mts +41 -0
- package/bin/checks/startup-failures.d.mts +2 -0
- package/bin/checks/superseded.d.mts +21 -0
- package/bin/checks/triage.d.mts +4 -0
- package/bin/checks/triage.mjs +11 -3
- package/bin/classify/apply.d.mts +18 -0
- package/bin/classify/apply.mjs +4 -0
- package/bin/classify/loader.d.mts +10 -0
- package/bin/classify/types.d.mts +35 -0
- package/bin/cli/args.d.mts +18 -0
- package/bin/cli/args.mjs +1 -0
- package/bin/cli/clean-formatter.d.mts +2 -0
- package/bin/cli/default-poll.d.mts +2 -0
- package/bin/cli/default-poll.mjs +1 -0
- package/bin/cli/duration-flag.d.mts +2 -0
- package/bin/cli/duration.d.mts +13 -0
- package/bin/cli/fence.d.mts +1 -0
- package/bin/cli/fix-formatter-extra.d.mts +3 -0
- package/bin/cli/fix-formatter.d.mts +2 -0
- package/bin/cli/fix-formatter.mjs +6 -6
- package/bin/cli/formatters.d.mts +7 -0
- package/bin/cli/handlers.d.mts +4 -0
- package/bin/cli/handlers.mjs +11 -10
- package/bin/cli/help-command-pages.d.mts +231 -0
- package/bin/cli/help-command-pages.mjs +106 -1
- package/bin/cli/help-iterate-poll-pages.d.mts +4 -0
- package/bin/cli/help-iterate-poll-pages.mjs +14 -7
- package/bin/cli/help-log-file-page.d.mts +1 -0
- package/bin/cli/help-top-page.d.mts +1 -0
- package/bin/cli/help-top-page.mjs +16 -16
- package/bin/cli/help.d.mts +236 -0
- package/bin/cli/help.mjs +17 -0
- package/bin/cli/iterate-emitter.d.mts +8 -0
- package/bin/cli/iterate-flags.d.mts +11 -0
- package/bin/cli/iterate-formatter.d.mts +19 -0
- package/bin/cli/iterate-formatter.mjs +11 -2
- package/bin/cli/iterate-instructions.d.mts +6 -0
- package/bin/cli/iterate-lean.d.mts +12 -0
- package/bin/cli/iterate-lean.mjs +1 -0
- package/bin/cli/journal-formatter.d.mts +2 -0
- package/bin/cli/journal-formatter.mjs +15 -0
- package/bin/cli/journal-handler.d.mts +1 -0
- package/bin/cli/journal-handler.mjs +7 -20
- package/bin/cli/list-formatters.d.mts +76 -0
- package/bin/cli/list-formatters.mjs +9 -9
- package/bin/cli/mark-files-as-viewed-flags.d.mts +11 -0
- package/bin/cli/mark-files-as-viewed-formatter.d.mts +2 -0
- package/bin/cli/mutate-formatter.d.mts +2 -0
- package/bin/cli/poll-handler.d.mts +1 -0
- package/bin/cli/poll-handler.mjs +9 -2
- package/bin/cli/resolve-validators.d.mts +3 -0
- package/bin/cli/resolve-validators.mjs +3 -3
- package/bin/cli/runner.d.mts +7 -0
- package/bin/cli/suggestion-renderer.d.mts +3 -0
- package/bin/cli/validate-default-args.d.mts +6 -0
- package/bin/cli-parser.d.mts +2 -0
- package/bin/cli-parser.mjs +72 -16
- package/bin/commands/check-annotations.d.mts +5 -0
- package/bin/commands/check-status.d.mts +3 -0
- package/bin/commands/check-terminal-report.d.mts +5 -0
- package/bin/commands/check.d.mts +7 -0
- package/bin/commands/check.mjs +28 -23
- package/bin/commands/clean.d.mts +21 -0
- package/bin/commands/commit-suggestion-instruction.d.mts +8 -0
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/commit-suggestion.d.mts +8 -0
- package/bin/commands/commit-suggestion.mjs +20 -13
- package/bin/commands/iterate/check-instructions.d.mts +16 -0
- package/bin/commands/iterate/check-instructions.mjs +3 -3
- package/bin/commands/iterate/classify.d.mts +18 -0
- package/bin/commands/iterate/classify.mjs +4 -4
- package/bin/commands/iterate/escalate.d.mts +31 -0
- package/bin/commands/iterate/escalate.mjs +7 -4
- package/bin/commands/iterate/fix-code.d.mts +25 -0
- package/bin/commands/iterate/fix-code.mjs +1 -1
- package/bin/commands/iterate/helpers.d.mts +14 -0
- package/bin/commands/iterate/helpers.mjs +19 -7
- package/bin/commands/iterate/index.d.mts +2 -0
- package/bin/commands/iterate/index.mjs +8 -12
- package/bin/commands/iterate/render.d.mts +5 -0
- package/bin/commands/iterate/render.mjs +8 -6
- package/bin/commands/iterate/reruns.d.mts +20 -0
- package/bin/commands/iterate/stall.d.mts +6 -0
- package/bin/commands/journal/index.d.mts +14 -0
- package/bin/commands/journal/index.mjs +1 -0
- package/bin/commands/journal/transform.d.mts +22 -0
- package/bin/commands/log-file.d.mts +5 -0
- package/bin/commands/mark-files-as-viewed.d.mts +26 -0
- package/bin/commands/mark-files-as-viewed.mjs +1 -0
- package/bin/commands/poll.d.mts +12 -0
- package/bin/commands/poll.mjs +52 -17
- package/bin/commands/ready-delay.d.mts +29 -0
- package/bin/commands/ready-delay.mjs +3 -13
- package/bin/commands/ready-mergeability.d.mts +15 -0
- package/bin/commands/resolve-mutate.d.mts +4 -0
- package/bin/commands/resolve-mutate.mjs +1 -0
- package/bin/commands/resolve.d.mts +4 -0
- package/bin/commands/shepherd-journal.d.mts +7 -0
- package/bin/commands/shepherd-journal.mjs +2 -2
- package/bin/comments/authors.d.mts +14 -0
- package/bin/comments/marker.d.mts +2 -0
- package/bin/comments/minimize-policy.d.mts +4 -0
- package/bin/comments/pending-ops.d.mts +15 -0
- package/bin/comments/rate-limit.d.mts +18 -0
- package/bin/comments/resolve.d.mts +34 -0
- package/bin/comments/resolve.mjs +1 -0
- package/bin/comments/review-thread-markers.d.mts +8 -0
- package/bin/comments/review-visibility.d.mts +28 -0
- package/bin/comments/sha-poll.d.mts +2 -0
- package/bin/comments/thread-visibility.d.mts +11 -0
- package/bin/comments/visible-comments.d.mts +11 -0
- package/bin/config/load.d.mts +60 -0
- package/bin/config/load.mjs +129 -16
- package/bin/config.json +0 -2
- package/bin/execution-context.d.mts +9 -0
- package/bin/execution-context.mjs +19 -0
- package/bin/exit-codes.d.mts +51 -0
- package/bin/github/activity.d.mts +3 -0
- package/bin/github/activity.mjs +7 -0
- package/bin/github/batch-page-helpers.d.mts +45 -0
- package/bin/github/batch-page-helpers.mjs +63 -0
- package/bin/github/batch-page.d.mts +14 -0
- package/bin/github/batch-page.mjs +62 -0
- package/bin/github/batch-parse-suites.d.mts +4 -0
- package/bin/github/batch-parse-suites.mjs +25 -0
- package/bin/github/batch-parser-helpers.d.mts +22 -0
- package/bin/github/batch-parsers-rules.d.mts +6 -0
- package/bin/github/batch-parsers-rules.mjs +122 -0
- package/bin/github/batch-parsers.d.mts +3 -0
- package/bin/github/batch-parsers.mjs +12 -0
- package/bin/github/batch-raw-rules.d.mts +59 -0
- package/bin/github/batch-raw-rules.mjs +1 -0
- package/bin/github/batch-raw-types.d.mts +216 -0
- package/bin/github/batch-response.d.mts +4 -0
- package/bin/github/batch.d.mts +24 -0
- package/bin/github/batch.mjs +14 -120
- package/bin/github/branch-protection.d.mts +3 -0
- package/bin/github/check-annotations.d.mts +2 -0
- package/bin/github/client.d.mts +46 -0
- package/bin/github/client.mjs +7 -2
- package/bin/github/errors.d.mts +25 -0
- package/bin/github/gql/batch-pr-page.gql +189 -0
- package/bin/github/gql/batch-pr.gql +79 -21
- package/bin/github/gql/commit-suggestion-thread.gql +40 -0
- package/bin/github/gql/review-thread-comments.gql +1 -0
- package/bin/github/graphql-http.d.mts +19 -0
- package/bin/github/graphql-response.d.mts +7 -0
- package/bin/github/http-auth.d.mts +4 -0
- package/bin/github/http-request.d.mts +7 -0
- package/bin/github/http-utils.d.mts +11 -0
- package/bin/github/http.d.mts +6 -0
- package/bin/github/http.mjs +2 -1
- package/bin/github/pagination.d.mts +46 -0
- package/bin/github/pagination.mjs +3 -2
- package/bin/github/queries.d.mts +28 -0
- package/bin/github/queries.mjs +4 -0
- package/bin/github/rest-http.d.mts +7 -0
- package/bin/github/rest-http.mjs +25 -86
- package/bin/github/rest-text.d.mts +1 -0
- package/bin/github/rest-text.mjs +88 -0
- package/bin/github/suggestion-thread.d.mts +9 -0
- package/bin/github/suggestion-thread.mjs +45 -0
- package/bin/github/thread-comments.d.mts +2 -0
- package/bin/github/thread-comments.mjs +12 -8
- package/bin/index.d.mts +10 -0
- package/bin/index.mjs +1 -1
- package/bin/log/log-file.d.mts +28 -0
- package/bin/log/session.d.mts +31 -0
- package/bin/log/setup.d.mts +6 -0
- package/bin/mcp/index.d.mts +5 -0
- package/bin/mcp/index.mjs +8 -0
- package/bin/mcp/server.d.mts +8 -0
- package/bin/mcp/server.mjs +157 -0
- package/bin/mcp-stdio.d.mts +2 -0
- package/bin/mcp-stdio.mjs +7 -0
- package/bin/merge-status/derive.d.mts +19 -0
- package/bin/merge-status/derive.mjs +2 -0
- package/bin/merge-status/requirements-format.d.mts +3 -0
- package/bin/merge-status/requirements-format.mjs +88 -0
- package/bin/merge-status/requirements.d.mts +2 -0
- package/bin/merge-status/requirements.mjs +51 -0
- package/bin/reporters/agent.d.mts +23 -0
- package/bin/reporters/agent.mjs +3 -0
- package/bin/state/base.d.mts +10 -0
- package/bin/state/base.mjs +23 -0
- package/bin/state/bot-cr-seen.d.mts +51 -0
- package/bin/state/bot-cr-seen.mjs +4 -14
- package/bin/state/fix-attempts.d.mts +27 -0
- package/bin/state/fix-attempts.mjs +3 -13
- package/bin/state/iterate-stall.d.mts +27 -0
- package/bin/state/iterate-stall.mjs +3 -13
- package/bin/state/seen-comments.d.mts +62 -0
- package/bin/state/seen-comments.mjs +6 -13
- package/bin/suggestions/extract.d.mts +8 -0
- package/bin/suggestions/parse.d.mts +48 -0
- package/bin/suggestions/patch.d.mts +14 -0
- package/bin/threads/transcript.d.mts +14 -0
- package/bin/threads/transcript.mjs +4 -0
- package/bin/types/activity.d.mts +30 -0
- package/bin/types/agent-thread.d.mts +9 -0
- package/bin/types/check-annotations.d.mts +14 -0
- package/bin/types/check-classification.d.mts +19 -0
- package/bin/types/github.d.mts +136 -0
- package/bin/types/iterate.d.mts +156 -0
- package/bin/types/merge-requirements.d.mts +82 -0
- package/bin/types/merge-requirements.mjs +2 -0
- package/bin/types/protected-run.d.mts +6 -0
- package/bin/types/report.d.mts +176 -0
- package/bin/types/review-thread.d.mts +12 -0
- package/bin/types.d.mts +10 -0
- package/bin/types.mjs +1 -0
- package/bin/util/markdown.d.mts +1 -0
- package/bin/util/path-segment.d.mts +4 -0
- package/bin/util/path-segment.mjs +2 -0
- package/bin/util/pool.d.mts +2 -0
- package/bin/util/pool.mjs +18 -0
- package/bin/util/sleep.d.mts +1 -0
- package/bin/util/worktree.d.mts +9 -0
- package/bin/util/worktree.mjs +4 -1
- package/package.json +51 -37
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +3 -2
- package/plugins/pr-shepherd/.codex.mcp.json +8 -0
- package/plugins/pr-shepherd/.mcp.json +6 -0
- package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +5 -19
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -15
- package/src/classify/types.mts +12 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-shepherd",
|
|
3
3
|
"description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.36.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jonathan Ong",
|
|
7
7
|
"email": "jonathanrichardong@gmail.com"
|
|
@@ -10,5 +10,6 @@
|
|
|
10
10
|
"repository": "https://github.com/jonathanong/pr-shepherd",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"keywords": ["github", "pull-request", "ci", "code-review", "automation"],
|
|
13
|
-
"skills": "./plugins/pr-shepherd/skills/"
|
|
13
|
+
"skills": "./plugins/pr-shepherd/skills/",
|
|
14
|
+
"mcpServers": "./plugins/pr-shepherd/.mcp.json"
|
|
14
15
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jonathanong",
|
|
3
|
+
"description": "Jonathan Ong's Grok plugins",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Jonathan Ong",
|
|
6
|
+
"email": "jonathanrichardong@gmail.com"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "pr-shepherd",
|
|
11
|
+
"description": "Autonomous PR CI monitor and review-comment resolver",
|
|
12
|
+
"source": { "type": "local", "path": "./plugins/pr-shepherd" },
|
|
13
|
+
"category": "productivity",
|
|
14
|
+
"homepage": "https://github.com/jonathanong/pr-shepherd"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
package/README.md
CHANGED
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
Autonomous PR CI monitor and review-comment resolver for agentic coding tools, including Claude Code and Codex.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
2. **Provide deterministic actions for the agent**: exactly one of `WAIT`, `MARK_READY`, `FIX_CODE`, `CANCEL`, or `ESCALATE`, plus numbered `## Instructions` and explicit `apply` / `build_suggestion_patch` mutations. 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.
|
|
13
|
+
|
|
14
|
+
Highlights:
|
|
15
|
+
|
|
16
|
+
- 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.
|
|
17
|
+
- 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.
|
|
18
|
+
- Handles GitHub comment types (comments, threads, replies) and their states, including first-look, outdated, resolved, minimized, and edited.
|
|
19
|
+
- `apply` batches resolve / reply / minimize / dismiss. `build_suggestion_patch` emits a unified diff in output, not a patch file, and does not mutate git.
|
|
20
|
+
- `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.
|
|
21
|
+
|
|
22
|
+
Full reference: [docs/README.md](docs/README.md). Feature matrix: [docs/features.md](docs/features.md).
|
|
6
23
|
|
|
7
24
|
## How It Works
|
|
8
25
|
|
|
9
|
-
`pr-shepherd` moves deterministic PR orchestration into a CLI.
|
|
26
|
+
`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.
|
|
10
27
|
|
|
11
|
-
The
|
|
28
|
+
The MCP server exposes three tools: `iterate`, `apply`, and `build_suggestion_patch`. `apply` accepts ordered review mutations, file-view mutations, and journal entries. The shipped skills are thin dispatchers for those tools.
|
|
12
29
|
|
|
13
30
|
Each tick returns exactly one action:
|
|
14
31
|
|
|
@@ -25,12 +42,14 @@ Example shape:
|
|
|
25
42
|
|
|
26
43
|
# PR #123 [FIX_CODE]
|
|
27
44
|
|
|
28
|
-
**status** `UNRESOLVED_COMMENTS` · **merge** `
|
|
45
|
+
**status** `UNRESOLVED_COMMENTS` · **merge** `CLEAN` · **state** `OPEN` · **repo** `owner/repo`
|
|
29
46
|
**summary** 3 passing
|
|
47
|
+
Approvals: None [Not Required]
|
|
48
|
+
Conversations Resolved: No [Not Required]
|
|
30
49
|
|
|
31
50
|
## Review threads
|
|
32
51
|
|
|
33
|
-
### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice)
|
|
52
|
+
### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice · User · MEMBER)
|
|
34
53
|
|
|
35
54
|
> The variable name is misleading.
|
|
36
55
|
|
|
@@ -42,17 +61,17 @@ Example shape:
|
|
|
42
61
|
## Post-fix push
|
|
43
62
|
|
|
44
63
|
- base: `main`
|
|
45
|
-
-
|
|
64
|
+
- apply review: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha "$HEAD_SHA"`
|
|
46
65
|
|
|
47
66
|
## Instructions
|
|
48
67
|
|
|
49
|
-
1. Decide for each item under `## Review threads` and `## Failing checks` whether a code change is warranted. If code changes are needed, apply edits, commit,
|
|
68
|
+
1. Decide for each item under `## Review threads` and `## Failing checks` whether a code change is warranted. If code changes are needed, apply edits, commit, push, then run the `apply review:` command.
|
|
50
69
|
2. For each failing check under `## Failing checks`: fetch logs when needed and decide whether to rerun or fix.
|
|
51
|
-
3. Run the `
|
|
70
|
+
3. Run the `apply review:` command shown above, substituting `$HEAD_SHA` and `$DISMISS_MESSAGE`.
|
|
52
71
|
4. Stop this iteration.
|
|
53
72
|
```
|
|
54
73
|
|
|
55
|
-
See [docs/actions.md](docs/actions.md) for the complete output contract.
|
|
74
|
+
See [docs/actions.md](docs/actions.md) for the complete output contract. Iterate/poll PR outcomes use exit codes `0` and `10`–`14`; command and GitHub failures use `sysexits.h` codes — [docs/exit-codes.md](docs/exit-codes.md).
|
|
56
75
|
|
|
57
76
|
## Workflow Assumptions
|
|
58
77
|
|
|
@@ -64,7 +83,7 @@ This system is opinionated and works best with PRs that use required status chec
|
|
|
64
83
|
- 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.
|
|
65
84
|
- Draft PRs can be marked ready automatically when clean; disable with `actions.autoMarkReady: false` or `--no-auto-mark-ready`.
|
|
66
85
|
- The CLI never performs git mutations. It emits instructions; the caller commits, rebases, pushes, and handles repository hooks.
|
|
67
|
-
- `
|
|
86
|
+
- `build_suggestion_patch` turns one GitHub suggestion thread into a patch and commit metadata, but never edits the working tree or git history.
|
|
68
87
|
|
|
69
88
|
## Usage
|
|
70
89
|
|
|
@@ -84,57 +103,47 @@ Codex:
|
|
|
84
103
|
/goal $pr-shepherd 42
|
|
85
104
|
```
|
|
86
105
|
|
|
87
|
-
|
|
106
|
+
Grok:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
/pr-shepherd # infer PR from current branch
|
|
110
|
+
/pr-shepherd 42
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
MCP clients call `iterate` once per tick, then use `apply` for review/file/journal mutations and `build_suggestion_patch` for an anchored suggestion. `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.
|
|
114
|
+
|
|
115
|
+
The CLI remains useful for shell workflows. Its canonical polling form is:
|
|
88
116
|
|
|
89
117
|
```sh
|
|
90
118
|
pr-shepherd 42 # poll until non-WAIT or timeout
|
|
91
119
|
pr-shepherd 42 --interval 60s --timeout 270s
|
|
92
120
|
pr-shepherd 42 --quiet-status # print only changed WAIT status snapshots
|
|
93
121
|
pr-shepherd 42 --until-terminal # continue through WAIT/MARK_READY until work or terminal state
|
|
122
|
+
pr-shepherd 42 --debounce 5m # wait 5m after first FIX_CODE, then return one batched tick
|
|
94
123
|
pr-shepherd 42 --ready-delay 15m
|
|
95
124
|
pr-shepherd iterate 42 # single tick
|
|
96
|
-
pr-shepherd poll 42 # explicit poll command
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Resolve Review Items
|
|
100
|
-
|
|
101
|
-
```sh
|
|
102
|
-
pr-shepherd resolve 42 --reply-thread-ids PRRT_abc --message "Renamed the variable for clarity." --require-sha "$(git rev-parse HEAD)"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Use `pr-shepherd iterate 42` or `pr-shepherd 42` to fetch the next PR action. `resolve` requires at least one action flag and only applies explicit review-state mutations.
|
|
106
|
-
|
|
107
|
-
### Apply One Suggestion Thread
|
|
108
|
-
|
|
109
|
-
```sh
|
|
110
|
-
pr-shepherd commit-suggestion 42 --thread-id PRRT_abc --message "rename value for clarity"
|
|
111
125
|
```
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
### Mark Files As Viewed
|
|
116
|
-
|
|
117
|
-
```sh
|
|
118
|
-
pr-shepherd mark-files-as-viewed 42 --tests
|
|
119
|
-
pr-shepherd mark-files-as-viewed 42 src/a.ts --match '^docs/'
|
|
120
|
-
```
|
|
127
|
+
### Apply Review, File, And Journal Changes
|
|
121
128
|
|
|
122
|
-
|
|
129
|
+
Use `apply` with ordered operations to reply/resolve/minimize/dismiss review items, mark changed files viewed, or append an idempotent Shepherd Journal item. Use `build_suggestion_patch` to turn one review suggestion into a validated patch and commit metadata; it never changes the worktree or git history.
|
|
123
130
|
|
|
124
131
|
### Clean Local State
|
|
125
132
|
|
|
126
133
|
`pr-shepherd` stores seen markers, fix-attempt counters, stall fingerprints, ready-delay markers, and logs under `$PR_SHEPHERD_STATE_DIR` (default `$TMPDIR/pr-shepherd-state`).
|
|
127
134
|
|
|
128
135
|
```sh
|
|
129
|
-
pr-shepherd clean current
|
|
130
|
-
pr-shepherd clean repo
|
|
131
|
-
pr-shepherd clean all --dry-run
|
|
132
|
-
pr-shepherd log-file
|
|
136
|
+
pr-shepherd admin clean current
|
|
137
|
+
pr-shepherd admin clean repo
|
|
138
|
+
pr-shepherd admin clean all --dry-run
|
|
139
|
+
pr-shepherd admin log-file
|
|
133
140
|
```
|
|
134
141
|
|
|
135
142
|
## Install
|
|
136
143
|
|
|
137
|
-
|
|
144
|
+
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.
|
|
145
|
+
|
|
146
|
+
To register the MCP server without the plugin, or to wire a local checkout, see [docs/mcp.md](docs/mcp.md).
|
|
138
147
|
|
|
139
148
|
### Claude Code
|
|
140
149
|
|
|
@@ -164,9 +173,33 @@ codex plugin marketplace add ~/.codex/plugin-sources/pr-shepherd
|
|
|
164
173
|
|
|
165
174
|
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/`.
|
|
166
175
|
|
|
176
|
+
### Grok
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
grok plugin marketplace add jonathanong/pr-shepherd
|
|
180
|
+
grok plugin install pr-shepherd --trust
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Grok starts a plugin's MCP server only after the plugin is trusted. Confirm with `grok mcp list` or `/mcps`.
|
|
184
|
+
|
|
185
|
+
### MCP server only
|
|
186
|
+
|
|
187
|
+
Any stdio MCP client can run the published binary without installing the plugin:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
claude mcp add --transport stdio --scope user pr-shepherd -- \
|
|
191
|
+
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
192
|
+
codex mcp add pr-shepherd -- \
|
|
193
|
+
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
194
|
+
grok mcp add pr-shepherd -- \
|
|
195
|
+
npx --yes --package pr-shepherd@<version> pr-shepherd-mcp
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Replace `<version>` with a published version. Full config-file examples, tool schemas, and local-checkout wiring are in [docs/mcp.md](docs/mcp.md).
|
|
199
|
+
|
|
167
200
|
## Configuration
|
|
168
201
|
|
|
169
|
-
Create `.pr-shepherdrc.yml` in your project root
|
|
202
|
+
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.
|
|
170
203
|
|
|
171
204
|
```yaml
|
|
172
205
|
ignoreChecks:
|
|
@@ -175,7 +208,7 @@ iterate:
|
|
|
175
208
|
fixAttemptsPerThread: 5
|
|
176
209
|
stallTimeoutMinutes: 60
|
|
177
210
|
minimizeApprovals: false
|
|
178
|
-
minimizeComments: all # all | bots |
|
|
211
|
+
minimizeComments: all # all | bots | none
|
|
179
212
|
checks:
|
|
180
213
|
ciTriggerEvents:
|
|
181
214
|
- pull_request
|
|
@@ -218,6 +251,10 @@ TypeScript rules are loaded by the runtime's native TypeScript support; keep the
|
|
|
218
251
|
|
|
219
252
|
Ready-to-use examples for common patterns are in [`examples/classification/`](examples/classification/).
|
|
220
253
|
|
|
254
|
+
## CLI aliases
|
|
255
|
+
|
|
256
|
+
`poll`, `resolve`, `commit-suggestion`, `mark-files-as-viewed`, `journal`, `clean`, and `log-file` are CLI aliases. Prefer default polling/`iterate` in a shell and the MCP `iterate`, `apply`, and `build_suggestion_patch` tools in an agent client.
|
|
257
|
+
|
|
221
258
|
## Requirements
|
|
222
259
|
|
|
223
260
|
- Node.js >= 22.18.0, Bun, or Deno
|
|
@@ -226,7 +263,7 @@ Ready-to-use examples for common patterns are in [`examples/classification/`](ex
|
|
|
226
263
|
|
|
227
264
|
## Docs
|
|
228
265
|
|
|
229
|
-
Full reference: [docs/README.md](docs/README.md).
|
|
266
|
+
Full reference, grouped by the two jobs (gather context / emit actions): [docs/README.md](docs/README.md).
|
|
230
267
|
|
|
231
268
|
## Harness Ecosystem
|
|
232
269
|
|
package/bin/api.d.mts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type JournalResult } from "./commands/journal/index.mts";
|
|
2
|
+
import { type MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
|
|
3
|
+
import type { ResolveResult } from "./comments/resolve.mts";
|
|
4
|
+
import type { CommitSuggestionResult, IterateCommandOptions, IterateResult } from "./types.mts";
|
|
5
|
+
export interface CreatePrShepherdOptions {
|
|
6
|
+
/** Working directory used for git, config, and classification-rule lookups. */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
/** A positive PR number or canonical GitHub pull-request URL. */
|
|
10
|
+
export type PrReference = number | string;
|
|
11
|
+
export type IterateInput = Omit<IterateCommandOptions, "format" | "prNumber"> & {
|
|
12
|
+
pr?: PrReference;
|
|
13
|
+
};
|
|
14
|
+
export interface ReviewMutationsOperation {
|
|
15
|
+
type: "review_mutations";
|
|
16
|
+
resolveThreadIds?: string[];
|
|
17
|
+
replyThreadIds?: string[];
|
|
18
|
+
minimizeCommentIds?: string[];
|
|
19
|
+
dismissReviewIds?: string[];
|
|
20
|
+
/** Required when replying to a thread or dismissing a review. */
|
|
21
|
+
message?: string;
|
|
22
|
+
requireSha?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface MarkFilesViewedOperation {
|
|
25
|
+
type: "mark_files_viewed";
|
|
26
|
+
files?: string[];
|
|
27
|
+
tests?: boolean;
|
|
28
|
+
matchPatterns?: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface AppendJournalOperation {
|
|
31
|
+
type: "append_journal";
|
|
32
|
+
item: string;
|
|
33
|
+
dryRun?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Mutations are run in this exact list order after every operation is validated. */
|
|
36
|
+
export type ApplyOperation = ReviewMutationsOperation | MarkFilesViewedOperation | AppendJournalOperation;
|
|
37
|
+
export interface ApplyInput {
|
|
38
|
+
/** PR shared by every operation in this ordered apply request. */
|
|
39
|
+
pr?: PrReference;
|
|
40
|
+
operations: ApplyOperation[];
|
|
41
|
+
}
|
|
42
|
+
export type ApplyOperationResult = {
|
|
43
|
+
type: "review_mutations";
|
|
44
|
+
result: ResolveResult;
|
|
45
|
+
} | {
|
|
46
|
+
type: "mark_files_viewed";
|
|
47
|
+
result: MarkFilesAsViewedResult;
|
|
48
|
+
} | {
|
|
49
|
+
type: "append_journal";
|
|
50
|
+
result: JournalResult;
|
|
51
|
+
};
|
|
52
|
+
export interface ApplyResult {
|
|
53
|
+
operations: ApplyOperationResult[];
|
|
54
|
+
}
|
|
55
|
+
export interface BuildSuggestionPatchInput {
|
|
56
|
+
pr?: PrReference;
|
|
57
|
+
threadId: string;
|
|
58
|
+
message: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface PrShepherd {
|
|
62
|
+
iterate(input?: IterateInput): Promise<IterateResult>;
|
|
63
|
+
apply(input: ApplyInput): Promise<ApplyResult>;
|
|
64
|
+
buildSuggestionPatch(input: BuildSuggestionPatchInput): Promise<CommitSuggestionResult>;
|
|
65
|
+
}
|
|
66
|
+
/** Raised before any API mutation when an input cannot be validated. */
|
|
67
|
+
export declare class PrShepherdValidationError extends Error {
|
|
68
|
+
constructor(message: string);
|
|
69
|
+
}
|
|
70
|
+
/** Raised when a later ordered apply operation fails after earlier operations completed. */
|
|
71
|
+
export declare class PartialApplyError extends Error {
|
|
72
|
+
readonly failedIndex: number;
|
|
73
|
+
readonly completed: ApplyOperationResult[];
|
|
74
|
+
constructor(failedIndex: number, completed: ApplyOperationResult[], cause: unknown);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates the programmatic API. The returned object intentionally exposes only
|
|
78
|
+
* read/plan, ordered apply, and suggestion-patch operations.
|
|
79
|
+
*/
|
|
80
|
+
export declare function createPrShepherd(options?: CreatePrShepherdOptions): PrShepherd;
|
package/bin/api.mjs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { runCommitSuggestion } from "./commands/commit-suggestion.mjs";
|
|
4
|
+
import { runIterate } from "./commands/iterate/index.mjs";
|
|
5
|
+
import { runJournal } from "./commands/journal/index.mjs";
|
|
6
|
+
import { validateJournalItem } from "./commands/journal/transform.mjs";
|
|
7
|
+
import { runMarkFilesAsViewed, } from "./commands/mark-files-as-viewed.mjs";
|
|
8
|
+
import { runResolveMutate } from "./commands/resolve-mutate.mjs";
|
|
9
|
+
import { runWithExecutionCwd } from "./execution-context.mjs";
|
|
10
|
+
import { getRepoInfo } from "./github/client.mjs";
|
|
11
|
+
/** Raised before any API mutation when an input cannot be validated. */
|
|
12
|
+
export class PrShepherdValidationError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "PrShepherdValidationError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Raised when a later ordered apply operation fails after earlier operations completed. */
|
|
19
|
+
export class PartialApplyError extends Error {
|
|
20
|
+
failedIndex;
|
|
21
|
+
completed;
|
|
22
|
+
constructor(failedIndex, completed, cause) {
|
|
23
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
24
|
+
super(`apply operation ${failedIndex} failed: ${message}`, { cause });
|
|
25
|
+
this.name = "PartialApplyError";
|
|
26
|
+
this.failedIndex = failedIndex;
|
|
27
|
+
this.completed = completed;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the programmatic API. The returned object intentionally exposes only
|
|
32
|
+
* read/plan, ordered apply, and suggestion-patch operations.
|
|
33
|
+
*/
|
|
34
|
+
export function createPrShepherd(options = {}) {
|
|
35
|
+
const cwd = options.cwd === undefined ? undefined : resolve(options.cwd);
|
|
36
|
+
return Object.freeze({
|
|
37
|
+
iterate(input = {}) {
|
|
38
|
+
const { pr: _pr, ...options } = input;
|
|
39
|
+
return runWithExecutionCwd(cwd, async () => {
|
|
40
|
+
const prNumber = await resolvePrReference(input.pr);
|
|
41
|
+
return runIterate({ ...options, prNumber, format: "json" });
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
apply(input) {
|
|
45
|
+
return runWithExecutionCwd(cwd, async () => {
|
|
46
|
+
validateApplyInput(input);
|
|
47
|
+
const prNumber = await resolvePrReference(input.pr);
|
|
48
|
+
const results = [];
|
|
49
|
+
for (let index = 0; index < input.operations.length; index += 1) {
|
|
50
|
+
const operation = input.operations[index];
|
|
51
|
+
try {
|
|
52
|
+
switch (operation.type) {
|
|
53
|
+
case "review_mutations": {
|
|
54
|
+
const { type: _type, message, ...options } = operation;
|
|
55
|
+
const result = await runResolveMutate({
|
|
56
|
+
...options,
|
|
57
|
+
prNumber,
|
|
58
|
+
dismissMessage: message,
|
|
59
|
+
format: "json",
|
|
60
|
+
});
|
|
61
|
+
results.push({ type: operation.type, result });
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case "mark_files_viewed": {
|
|
65
|
+
const result = await runMarkFilesAsViewed({
|
|
66
|
+
prNumber,
|
|
67
|
+
files: operation.files ?? [],
|
|
68
|
+
tests: operation.tests,
|
|
69
|
+
matchPatterns: operation.matchPatterns,
|
|
70
|
+
format: "json",
|
|
71
|
+
});
|
|
72
|
+
results.push({ type: operation.type, result });
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "append_journal": {
|
|
76
|
+
const result = await runJournal({
|
|
77
|
+
prNumber,
|
|
78
|
+
rawItem: operation.item,
|
|
79
|
+
dryRun: operation.dryRun ?? false,
|
|
80
|
+
});
|
|
81
|
+
results.push({ type: operation.type, result });
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (results.length === 0)
|
|
88
|
+
throw error;
|
|
89
|
+
throw new PartialApplyError(index, results, error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { operations: results };
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
buildSuggestionPatch(input) {
|
|
96
|
+
validateSuggestionPatchInput(input);
|
|
97
|
+
const { pr: _pr, ...options } = input;
|
|
98
|
+
return runWithExecutionCwd(cwd, async () => {
|
|
99
|
+
const prNumber = await resolvePrReference(input.pr);
|
|
100
|
+
return runCommitSuggestion({ ...options, prNumber, format: "json" });
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function validateApplyInput(input) {
|
|
106
|
+
if (!input || !Array.isArray(input.operations) || input.operations.length === 0) {
|
|
107
|
+
throw new PrShepherdValidationError("apply requires a non-empty operations array");
|
|
108
|
+
}
|
|
109
|
+
for (const operation of input.operations)
|
|
110
|
+
validateOperation(operation);
|
|
111
|
+
parsePrReference(input.pr);
|
|
112
|
+
}
|
|
113
|
+
function validateOperation(operation) {
|
|
114
|
+
if (!operation || typeof operation !== "object") {
|
|
115
|
+
throw new PrShepherdValidationError("apply operation must be an object");
|
|
116
|
+
}
|
|
117
|
+
switch (operation.type) {
|
|
118
|
+
case "review_mutations":
|
|
119
|
+
validateReviewMutations(operation);
|
|
120
|
+
return;
|
|
121
|
+
case "mark_files_viewed":
|
|
122
|
+
validateMarkFilesViewed(operation);
|
|
123
|
+
return;
|
|
124
|
+
case "append_journal":
|
|
125
|
+
if (typeof operation.item !== "string") {
|
|
126
|
+
throw new PrShepherdValidationError("append_journal.item must be a string");
|
|
127
|
+
}
|
|
128
|
+
if (operation.dryRun !== undefined && typeof operation.dryRun !== "boolean") {
|
|
129
|
+
throw new PrShepherdValidationError("append_journal.dryRun must be a boolean");
|
|
130
|
+
}
|
|
131
|
+
{
|
|
132
|
+
const validation = validateJournalItem(operation.item);
|
|
133
|
+
if (!validation.ok)
|
|
134
|
+
throw new PrShepherdValidationError(validation.error);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
default:
|
|
138
|
+
throw new PrShepherdValidationError(`Unsupported apply operation: ${JSON.stringify(operation.type)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function validateReviewMutations(operation) {
|
|
142
|
+
const ids = [
|
|
143
|
+
operation.resolveThreadIds,
|
|
144
|
+
operation.replyThreadIds,
|
|
145
|
+
operation.minimizeCommentIds,
|
|
146
|
+
operation.dismissReviewIds,
|
|
147
|
+
];
|
|
148
|
+
if (!ids.some((value) => value !== undefined && value.length > 0)) {
|
|
149
|
+
throw new PrShepherdValidationError("review_mutations requires at least one mutation ID");
|
|
150
|
+
}
|
|
151
|
+
for (const value of ids)
|
|
152
|
+
validateStringArray(value, "review mutation IDs");
|
|
153
|
+
const needsMessage = (operation.replyThreadIds?.length ?? 0) > 0 || (operation.dismissReviewIds?.length ?? 0) > 0;
|
|
154
|
+
if (needsMessage && (!operation.message || operation.message.trim() === "")) {
|
|
155
|
+
throw new PrShepherdValidationError("review_mutations.message is required for replies or review dismissals");
|
|
156
|
+
}
|
|
157
|
+
if (operation.message !== undefined && typeof operation.message !== "string") {
|
|
158
|
+
throw new PrShepherdValidationError("review_mutations.message must be a string");
|
|
159
|
+
}
|
|
160
|
+
if (operation.requireSha !== undefined && !/^[0-9a-f]{40}$/.test(operation.requireSha)) {
|
|
161
|
+
throw new PrShepherdValidationError("review_mutations.requireSha must be a full 40-character lowercase hex SHA");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function validateMarkFilesViewed(operation) {
|
|
165
|
+
validateStringArray(operation.files, "mark_files_viewed.files");
|
|
166
|
+
validateStringArray(operation.matchPatterns, "mark_files_viewed.matchPatterns");
|
|
167
|
+
if (operation.tests !== undefined && typeof operation.tests !== "boolean") {
|
|
168
|
+
throw new PrShepherdValidationError("mark_files_viewed.tests must be a boolean");
|
|
169
|
+
}
|
|
170
|
+
if ((operation.files?.length ?? 0) === 0 &&
|
|
171
|
+
(operation.matchPatterns?.length ?? 0) === 0 &&
|
|
172
|
+
operation.tests !== true) {
|
|
173
|
+
throw new PrShepherdValidationError("mark_files_viewed requires files, matchPatterns, or tests: true");
|
|
174
|
+
}
|
|
175
|
+
for (const pattern of operation.matchPatterns ?? []) {
|
|
176
|
+
try {
|
|
177
|
+
new RegExp(pattern, "i");
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
+
throw new PrShepherdValidationError(`Invalid mark_files_viewed match pattern ${JSON.stringify(pattern)}: ${message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function validateSuggestionPatchInput(input) {
|
|
186
|
+
if (!input || typeof input.threadId !== "string" || input.threadId === "") {
|
|
187
|
+
throw new PrShepherdValidationError("buildSuggestionPatch.threadId is required");
|
|
188
|
+
}
|
|
189
|
+
if (typeof input.message !== "string" || input.message.trim() === "") {
|
|
190
|
+
throw new PrShepherdValidationError("buildSuggestionPatch.message is required and must be non-empty");
|
|
191
|
+
}
|
|
192
|
+
if (input.description !== undefined && typeof input.description !== "string") {
|
|
193
|
+
throw new PrShepherdValidationError("buildSuggestionPatch.description must be a string");
|
|
194
|
+
}
|
|
195
|
+
parsePrReference(input.pr);
|
|
196
|
+
}
|
|
197
|
+
function parsePrReference(pr) {
|
|
198
|
+
if (pr === undefined)
|
|
199
|
+
return {};
|
|
200
|
+
if (typeof pr === "number" && Number.isInteger(pr) && pr > 0)
|
|
201
|
+
return { number: pr };
|
|
202
|
+
if (typeof pr === "string") {
|
|
203
|
+
try {
|
|
204
|
+
const url = new URL(pr);
|
|
205
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
206
|
+
if ((url.protocol === "https:" || url.protocol === "http:") &&
|
|
207
|
+
(url.hostname === "github.com" || url.hostname === "www.github.com") &&
|
|
208
|
+
parts.length === 4 &&
|
|
209
|
+
parts[2] === "pull" &&
|
|
210
|
+
/^[1-9][0-9]*$/.test(parts[3])) {
|
|
211
|
+
return { number: Number(parts[3]), repository: `${parts[0]}/${parts[1]}` };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Construct the uniform public validation error below.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw new PrShepherdValidationError("pr must be a positive number or a GitHub pull-request URL");
|
|
219
|
+
}
|
|
220
|
+
async function resolvePrReference(pr) {
|
|
221
|
+
const parsed = parsePrReference(pr);
|
|
222
|
+
if (parsed.repository !== undefined) {
|
|
223
|
+
const repo = await getRepoInfo();
|
|
224
|
+
const currentRepository = `${repo.owner}/${repo.name}`;
|
|
225
|
+
if (parsed.repository.toLowerCase() !== currentRepository.toLowerCase()) {
|
|
226
|
+
throw new PrShepherdValidationError(`PR URL repository ${parsed.repository} does not match the configured repository ${currentRepository}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return parsed.number;
|
|
230
|
+
}
|
|
231
|
+
function validateStringArray(value, label) {
|
|
232
|
+
if (value !== undefined &&
|
|
233
|
+
(!Array.isArray(value) || value.some((item) => typeof item !== "string" || item === ""))) {
|
|
234
|
+
throw new PrShepherdValidationError(`${label} must be an array of non-empty strings`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classifies check runs into shepherd categories and filters out irrelevant ones.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* 1. Skip checks whose workflow event is NOT `pull_request` or `pull_request_target`.
|
|
6
|
+
* Push-triggered, merge-queue, schedule, and workflow-dispatch runs are irrelevant
|
|
7
|
+
* to PR readiness.
|
|
8
|
+
* 2. Drop checks with `conclusion == SKIPPED` or `conclusion == NEUTRAL` from the
|
|
9
|
+
* pass/fail tally. Report them as "skipped" for transparency but don't block on them.
|
|
10
|
+
* 3. Reclassify `CANCELLED` checks as "superseded" (non-blocking) when a newer run of
|
|
11
|
+
* the same workflow exists on the same commit — this is GitHub's concurrency-group
|
|
12
|
+
* eviction behavior, not a real failure. GitHub branch protection itself resolves
|
|
13
|
+
* required status checks by latest-run-per-name and merges past these; mirroring
|
|
14
|
+
* that here keeps shepherd's verdict aligned with what GitHub will actually allow.
|
|
15
|
+
*/
|
|
16
|
+
import type { CheckRun, ClassifiedCheck } from "../types.mts";
|
|
17
|
+
/**
|
|
18
|
+
* Classify a list of raw check runs into shepherd categories.
|
|
19
|
+
*
|
|
20
|
+
* @param checks Raw check runs from the batch query.
|
|
21
|
+
* @returns Classified checks. "filtered" items were excluded from the tally.
|
|
22
|
+
*/
|
|
23
|
+
export declare function classifyChecks(checks: CheckRun[]): ClassifiedCheck[];
|
|
24
|
+
export interface CiVerdict {
|
|
25
|
+
/** True when all relevant (non-filtered, non-skipped, non-ignored) checks passed. */
|
|
26
|
+
allPassed: boolean;
|
|
27
|
+
/** True when at least one relevant (non-filtered, non-skipped, non-ignored) check exists. */
|
|
28
|
+
hasChecks: boolean;
|
|
29
|
+
/** True when at least one check is still running/queued. */
|
|
30
|
+
anyInProgress: boolean;
|
|
31
|
+
/** True when at least one check failed. */
|
|
32
|
+
anyFailing: boolean;
|
|
33
|
+
/** Names of checks that were filtered out (triggered by non-PR events). */
|
|
34
|
+
filteredNames: string[];
|
|
35
|
+
/** Names of checks suppressed by the user's ignoreChecks config. */
|
|
36
|
+
ignoredNames: string[];
|
|
37
|
+
/** Names of CANCELLED checks superseded by a newer run of the same workflow (concurrency-group eviction). */
|
|
38
|
+
supersededNames: string[];
|
|
39
|
+
}
|
|
40
|
+
/** Compute a high-level CI verdict from a list of classified checks. */
|
|
41
|
+
export declare function getCiVerdict(classified: ClassifiedCheck[]): CiVerdict;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects check runs that are `CANCELLED` because a newer run of the *same workflow*
|
|
3
|
+
* superseded them on the same commit (concurrency-group eviction), rather than a genuine
|
|
4
|
+
* cancellation. Split out of classify.mts to stay under the file-length cap.
|
|
5
|
+
*/
|
|
6
|
+
import type { CheckRun } from "../types.mts";
|
|
7
|
+
/**
|
|
8
|
+
* Grouping key is `workflowId ?? workflowName` — the numeric GitHub Actions workflow database
|
|
9
|
+
* ID when available, falling back to the display name. Checks with neither a workflow identity
|
|
10
|
+
* nor a numeric `runId` (status contexts, startup-failure synthetics) never participate: they
|
|
11
|
+
* can neither be marked superseded nor count as evidence of a newer run.
|
|
12
|
+
*
|
|
13
|
+
* A check is superseded iff its own conclusion is `CANCELLED` and some other check sharing its
|
|
14
|
+
* workflow key has a strictly greater `runId`. The newest run for a workflow is therefore never
|
|
15
|
+
* superseded, even if it is itself cancelled — that case stays "failing" so the agent can decide
|
|
16
|
+
* whether to rerun it.
|
|
17
|
+
*
|
|
18
|
+
* @returns Indices into `checks` (not object identities, since check-run objects are not
|
|
19
|
+
* deduplicated by reference elsewhere) that should be reclassified as "superseded".
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildSupersededIndices(checks: CheckRun[]): Set<number>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CheckRun, ClassifiedCheck, TriagedCheck } from "../types.mts";
|
|
2
|
+
import type { RepoInfo } from "../github/client.mts";
|
|
3
|
+
export declare function triageFailingChecks(failingChecks: ClassifiedCheck[], repo: RepoInfo): Promise<TriagedCheck[]>;
|
|
4
|
+
export declare function fetchStartupFailureChecks(repo: RepoInfo, headSha: string, prNumber: number): Promise<CheckRun[]>;
|