pr-shepherd 0.42.0 → 0.44.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 +1 -1
- package/README.md +24 -18
- package/bin/api.d.mts +3 -2
- package/bin/api.mjs +13 -18
- package/bin/checks/classify.d.mts +3 -1
- package/bin/checks/classify.mjs +5 -2
- package/bin/cli/args.mjs +14 -9
- package/bin/cli/default-poll.mjs +1 -0
- package/bin/cli/fix-formatter.mjs +20 -8
- package/bin/cli/handlers.mjs +1 -0
- package/bin/cli/help-command-pages.d.mts +9 -6
- package/bin/cli/help-command-pages.mjs +7 -4
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +9 -4
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +2 -2
- package/bin/cli/help.d.mts +10 -7
- package/bin/cli/iterate-flags.d.mts +1 -0
- package/bin/cli/iterate-flags.mjs +2 -0
- package/bin/cli/iterate-formatter.d.mts +1 -1
- package/bin/cli/iterate-formatter.mjs +9 -6
- package/bin/cli/iterate-instructions.mjs +15 -0
- package/bin/cli/iterate-lean.d.mts +1 -2
- package/bin/cli/iterate-lean.mjs +16 -17
- package/bin/cli/iterate-merge-formatter.d.mts +3 -0
- package/bin/cli/iterate-merge-formatter.mjs +52 -0
- package/bin/cli/iterate-verbose.d.mts +6 -0
- package/bin/cli/iterate-verbose.mjs +14 -0
- package/bin/cli/journal-formatter.mjs +3 -0
- package/bin/cli/journal-handler.mjs +11 -7
- package/bin/cli/list-formatters.d.mts +3 -1
- package/bin/cli/list-formatters.mjs +9 -7
- package/bin/cli/mark-files-as-viewed-formatter.mjs +7 -1
- package/bin/cli/mutate-formatter.mjs +4 -0
- package/bin/cli/poll-handler.mjs +1 -0
- package/bin/cli/runner.mjs +4 -3
- package/bin/commands/check-terminal-report.mjs +1 -0
- package/bin/commands/check.mjs +64 -7
- package/bin/commands/commit-suggestion-instruction.d.mts +1 -1
- package/bin/commands/commit-suggestion-instruction.mjs +2 -2
- package/bin/commands/commit-suggestion.mjs +3 -1
- package/bin/commands/iterate/base.d.mts +5 -0
- package/bin/commands/iterate/base.mjs +25 -0
- package/bin/commands/iterate/check-instructions.d.mts +5 -19
- package/bin/commands/iterate/check-instructions.mjs +24 -25
- package/bin/commands/iterate/classify.d.mts +2 -2
- package/bin/commands/iterate/classify.mjs +43 -25
- package/bin/commands/iterate/escalate.d.mts +3 -1
- package/bin/commands/iterate/escalate.mjs +29 -2
- package/bin/commands/iterate/fix-code.mjs +81 -26
- package/bin/commands/iterate/helpers.d.mts +0 -2
- package/bin/commands/iterate/helpers.mjs +6 -17
- package/bin/commands/iterate/index.mjs +31 -43
- package/bin/commands/iterate/mark-ready.d.mts +2 -0
- package/bin/commands/iterate/mark-ready.mjs +38 -0
- package/bin/commands/iterate/merge-state.d.mts +15 -0
- package/bin/commands/iterate/merge-state.mjs +82 -0
- package/bin/commands/iterate/merge.d.mts +13 -0
- package/bin/commands/iterate/merge.mjs +45 -0
- package/bin/commands/iterate/render.d.mts +2 -2
- package/bin/commands/iterate/render.mjs +19 -20
- package/bin/commands/iterate/stall.mjs +4 -2
- package/bin/commands/iterate/thread-mutation-routing.d.mts +9 -0
- package/bin/commands/iterate/thread-mutation-routing.mjs +29 -0
- package/bin/commands/journal/index.d.mts +5 -0
- package/bin/commands/journal/index.mjs +11 -2
- package/bin/commands/mark-files-as-viewed.d.mts +2 -1
- package/bin/commands/mark-files-as-viewed.mjs +5 -92
- package/bin/commands/poll.mjs +3 -1
- package/bin/commands/resolve-mutate.mjs +51 -9
- package/bin/commands/shepherd-journal.d.mts +1 -1
- package/bin/commands/shepherd-journal.mjs +2 -2
- package/bin/commands/suggestion-patch-item.d.mts +1 -1
- package/bin/commands/suggestion-patch-item.mjs +3 -11
- package/bin/commands/suggestion-patches.mjs +3 -3
- package/bin/comments/authors.d.mts +6 -0
- package/bin/comments/authors.mjs +5 -0
- package/bin/comments/marker.d.mts +6 -1
- package/bin/comments/marker.mjs +8 -1
- package/bin/comments/resolve.d.mts +4 -0
- package/bin/comments/thread-visibility.mjs +10 -10
- package/bin/comments/visible-comments.mjs +2 -1
- package/bin/config/load.d.mts +5 -1
- package/bin/config/load.mjs +34 -0
- package/bin/config/merge-command-args.d.mts +2 -0
- package/bin/config/merge-command-args.mjs +44 -0
- package/bin/config.json +3 -0
- package/bin/exit-codes.d.mts +2 -0
- package/bin/exit-codes.mjs +4 -0
- package/bin/github/batch-parse-checks.d.mts +3 -0
- package/bin/github/batch-parse-checks.mjs +29 -0
- package/bin/github/batch-parsers-rules.d.mts +3 -1
- package/bin/github/batch-parsers-rules.mjs +33 -0
- package/bin/github/batch-parsers.d.mts +2 -2
- package/bin/github/batch-parsers.mjs +40 -27
- package/bin/github/batch-raw-rules.d.mts +48 -0
- package/bin/github/batch-raw-types.d.mts +12 -0
- package/bin/github/batch-raw-types.mjs +0 -1
- package/bin/github/batch.mjs +3 -1
- package/bin/github/client.d.mts +1 -0
- package/bin/github/client.mjs +8 -1
- package/bin/github/gql/batch-pr-page.gql +6 -0
- package/bin/github/gql/batch-pr.gql +105 -0
- package/bin/github/gql/commit-check-contexts.gql +58 -0
- package/bin/github/gql/get-pr-body.gql +1 -0
- package/bin/github/gql/review-thread-comments.gql +1 -0
- package/bin/github/gql/suggestion-threads.gql +1 -0
- package/bin/github/merge-queue-checks.d.mts +4 -0
- package/bin/github/merge-queue-checks.mjs +48 -0
- package/bin/github/queries.d.mts +2 -0
- package/bin/github/queries.mjs +2 -0
- package/bin/github/suggestion-thread.mjs +1 -0
- package/bin/mcp/server.mjs +13 -6
- package/bin/pr-reference.d.mts +13 -0
- package/bin/pr-reference.mjs +23 -1
- package/bin/reporters/agent.mjs +11 -3
- package/bin/threads/transcript.d.mts +2 -0
- package/bin/threads/transcript.mjs +2 -0
- package/bin/types/activity.d.mts +2 -0
- package/bin/types/agent-thread.d.mts +1 -0
- package/bin/types/escalate.d.mts +35 -0
- package/bin/types/escalate.mjs +1 -0
- package/bin/types/github.d.mts +27 -0
- package/bin/types/github.mjs +0 -3
- package/bin/types/iterate.d.mts +23 -39
- package/bin/types/merge-action.d.mts +12 -0
- package/bin/types/merge-action.mjs +1 -0
- package/bin/types/merge-queue.d.mts +13 -0
- package/bin/types/merge-queue.mjs +1 -0
- package/bin/types/merge-requirements.d.mts +17 -0
- package/bin/types/report.d.mts +22 -1
- package/bin/types/review-thread.d.mts +1 -0
- package/bin/types/suggestion-patch.d.mts +1 -1
- package/bin/types.d.mts +3 -0
- package/bin/types.mjs +3 -0
- package/bin/util/markdown.d.mts +2 -0
- package/bin/util/markdown.mjs +7 -0
- package/package.json +1 -1
- 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 +3 -3
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +17 -15
- package/bin/commands/iterate/reruns.d.mts +0 -20
- package/bin/commands/iterate/reruns.mjs +0 -105
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
# re-select every connection (GraphQL points scale with possible nodes).
|
|
3
3
|
query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
4
4
|
repository(owner: $owner, name: $repo) {
|
|
5
|
+
viewerPermission
|
|
6
|
+
viewerCanAdminister
|
|
5
7
|
pullRequest(number: $pr) {
|
|
6
8
|
id
|
|
7
9
|
number
|
|
8
10
|
state
|
|
9
11
|
isDraft
|
|
12
|
+
viewerDidAuthor
|
|
13
|
+
viewerCanUpdate
|
|
14
|
+
viewerCanEnableAutoMerge
|
|
15
|
+
viewerCanEditFiles
|
|
10
16
|
mergeable
|
|
11
17
|
mergeStateStatus
|
|
12
18
|
reviewDecision
|
|
@@ -14,14 +20,50 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
14
20
|
headRefName
|
|
15
21
|
headRepository {
|
|
16
22
|
nameWithOwner
|
|
23
|
+
viewerPermission
|
|
17
24
|
}
|
|
18
25
|
baseRefName
|
|
19
26
|
isInMergeQueue
|
|
20
27
|
isMergeQueueEnabled
|
|
28
|
+
autoMergeRequest {
|
|
29
|
+
enabledAt
|
|
30
|
+
mergeMethod
|
|
31
|
+
enabledBy {
|
|
32
|
+
login
|
|
33
|
+
}
|
|
34
|
+
}
|
|
21
35
|
mergeQueueEntry {
|
|
22
36
|
position
|
|
23
37
|
state
|
|
24
38
|
estimatedTimeToMerge
|
|
39
|
+
enqueuedAt
|
|
40
|
+
enqueuer {
|
|
41
|
+
login
|
|
42
|
+
}
|
|
43
|
+
headCommit {
|
|
44
|
+
...QueueCheckCommit
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
mergeQueueAdditions: timelineItems(last: 1, itemTypes: [ADDED_TO_MERGE_QUEUE_EVENT]) {
|
|
48
|
+
nodes {
|
|
49
|
+
... on AddedToMergeQueueEvent {
|
|
50
|
+
createdAt
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
mergeQueueRemovals: timelineItems(last: 1, itemTypes: [REMOVED_FROM_MERGE_QUEUE_EVENT]) {
|
|
55
|
+
nodes {
|
|
56
|
+
... on RemovedFromMergeQueueEvent {
|
|
57
|
+
reason
|
|
58
|
+
createdAt
|
|
59
|
+
actor {
|
|
60
|
+
login
|
|
61
|
+
}
|
|
62
|
+
beforeCommit {
|
|
63
|
+
...QueueCheckCommit
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
25
67
|
}
|
|
26
68
|
stack {
|
|
27
69
|
number
|
|
@@ -111,6 +153,8 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
111
153
|
id
|
|
112
154
|
isResolved
|
|
113
155
|
isOutdated
|
|
156
|
+
viewerCanReply
|
|
157
|
+
viewerCanResolve
|
|
114
158
|
path
|
|
115
159
|
line
|
|
116
160
|
startLine
|
|
@@ -124,6 +168,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
124
168
|
isMinimized
|
|
125
169
|
url
|
|
126
170
|
authorAssociation
|
|
171
|
+
viewerDidAuthor
|
|
127
172
|
author {
|
|
128
173
|
__typename
|
|
129
174
|
login
|
|
@@ -148,6 +193,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
148
193
|
nodes {
|
|
149
194
|
id
|
|
150
195
|
isMinimized
|
|
196
|
+
viewerCanMinimize
|
|
151
197
|
url
|
|
152
198
|
authorAssociation
|
|
153
199
|
author {
|
|
@@ -185,6 +231,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
185
231
|
nodes {
|
|
186
232
|
id
|
|
187
233
|
isMinimized
|
|
234
|
+
viewerCanMinimize
|
|
188
235
|
authorAssociation
|
|
189
236
|
author {
|
|
190
237
|
__typename
|
|
@@ -205,6 +252,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
205
252
|
nodes {
|
|
206
253
|
id
|
|
207
254
|
isMinimized
|
|
255
|
+
viewerCanMinimize
|
|
208
256
|
authorAssociation
|
|
209
257
|
author {
|
|
210
258
|
__typename
|
|
@@ -289,3 +337,60 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
289
337
|
}
|
|
290
338
|
}
|
|
291
339
|
}
|
|
340
|
+
|
|
341
|
+
fragment QueueCheckCommit on Commit {
|
|
342
|
+
oid
|
|
343
|
+
committedDate
|
|
344
|
+
parents(first: 100) {
|
|
345
|
+
nodes {
|
|
346
|
+
oid
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
statusCheckRollup {
|
|
350
|
+
contexts(first: 100) {
|
|
351
|
+
pageInfo {
|
|
352
|
+
hasNextPage
|
|
353
|
+
endCursor
|
|
354
|
+
}
|
|
355
|
+
nodes {
|
|
356
|
+
__typename
|
|
357
|
+
... on CheckRun {
|
|
358
|
+
id
|
|
359
|
+
name
|
|
360
|
+
status
|
|
361
|
+
conclusion
|
|
362
|
+
detailsUrl
|
|
363
|
+
completedAt
|
|
364
|
+
startedAt
|
|
365
|
+
title
|
|
366
|
+
summary
|
|
367
|
+
annotations(first: 1) {
|
|
368
|
+
nodes {
|
|
369
|
+
message
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
checkSuite {
|
|
373
|
+
createdAt
|
|
374
|
+
updatedAt
|
|
375
|
+
workflowRun {
|
|
376
|
+
event
|
|
377
|
+
createdAt
|
|
378
|
+
updatedAt
|
|
379
|
+
workflow {
|
|
380
|
+
name
|
|
381
|
+
databaseId
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
... on StatusContext {
|
|
387
|
+
context
|
|
388
|
+
state
|
|
389
|
+
createdAt
|
|
390
|
+
targetUrl
|
|
391
|
+
description
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
query CommitCheckContexts($owner: String!, $repo: String!, $oid: String!, $cursor: String) {
|
|
2
|
+
repository(owner: $owner, name: $repo) {
|
|
3
|
+
object(expression: $oid) {
|
|
4
|
+
__typename
|
|
5
|
+
... on Commit {
|
|
6
|
+
oid
|
|
7
|
+
statusCheckRollup {
|
|
8
|
+
contexts(first: 100, after: $cursor) {
|
|
9
|
+
pageInfo {
|
|
10
|
+
hasNextPage
|
|
11
|
+
endCursor
|
|
12
|
+
}
|
|
13
|
+
nodes {
|
|
14
|
+
__typename
|
|
15
|
+
... on CheckRun {
|
|
16
|
+
id
|
|
17
|
+
name
|
|
18
|
+
status
|
|
19
|
+
conclusion
|
|
20
|
+
detailsUrl
|
|
21
|
+
completedAt
|
|
22
|
+
startedAt
|
|
23
|
+
title
|
|
24
|
+
summary
|
|
25
|
+
annotations(first: 1) {
|
|
26
|
+
nodes {
|
|
27
|
+
message
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
checkSuite {
|
|
31
|
+
createdAt
|
|
32
|
+
updatedAt
|
|
33
|
+
workflowRun {
|
|
34
|
+
event
|
|
35
|
+
createdAt
|
|
36
|
+
updatedAt
|
|
37
|
+
workflow {
|
|
38
|
+
name
|
|
39
|
+
databaseId
|
|
40
|
+
}
|
|
41
|
+
databaseId
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
... on StatusContext {
|
|
46
|
+
context
|
|
47
|
+
state
|
|
48
|
+
description
|
|
49
|
+
targetUrl
|
|
50
|
+
createdAt
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
import type { RawPr } from "./batch-raw-types.mts";
|
|
3
|
+
/** Hydrate all status contexts for the active or most recently removed queue commit. */
|
|
4
|
+
export declare function hydrateMergeQueueChecks(raw: RawPr, repo: RepoInfo): Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { graphql } from "./client.mjs";
|
|
2
|
+
import { requireContextNodes } from "./batch-response.mjs";
|
|
3
|
+
import { COMMIT_CHECK_CONTEXTS_QUERY } from "./queries.mjs";
|
|
4
|
+
async function hydrateCommitContexts(commit, repo) {
|
|
5
|
+
const contexts = commit.statusCheckRollup?.contexts;
|
|
6
|
+
if (!contexts)
|
|
7
|
+
return;
|
|
8
|
+
contexts.nodes = requireContextNodes(contexts.nodes);
|
|
9
|
+
if (contexts.pageInfo.hasNextPage && !contexts.pageInfo.endCursor) {
|
|
10
|
+
throw new Error(`Merge queue check pagination interrupted: GitHub omitted the next cursor for ${commit.oid}. Retry.`);
|
|
11
|
+
}
|
|
12
|
+
let cursor = contexts.pageInfo.hasNextPage ? contexts.pageInfo.endCursor : null;
|
|
13
|
+
while (cursor) {
|
|
14
|
+
// eslint-disable-next-line no-await-in-loop
|
|
15
|
+
const result = await graphql(COMMIT_CHECK_CONTEXTS_QUERY, {
|
|
16
|
+
owner: repo.owner,
|
|
17
|
+
repo: repo.name,
|
|
18
|
+
oid: commit.oid,
|
|
19
|
+
cursor,
|
|
20
|
+
});
|
|
21
|
+
const object = result.data.repository?.object;
|
|
22
|
+
if (object?.__typename !== "Commit" || object.oid !== commit.oid) {
|
|
23
|
+
throw new Error(`Merge queue check pagination interrupted: commit ${commit.oid} disappeared or changed. Retry.`);
|
|
24
|
+
}
|
|
25
|
+
const next = object.statusCheckRollup?.contexts;
|
|
26
|
+
if (!next) {
|
|
27
|
+
throw new Error(`Merge queue check pagination interrupted: statusCheckRollup disappeared for ${commit.oid}. Retry.`);
|
|
28
|
+
}
|
|
29
|
+
contexts.nodes.push(...requireContextNodes(next.nodes));
|
|
30
|
+
contexts.pageInfo = next.pageInfo;
|
|
31
|
+
cursor = next.pageInfo.hasNextPage ? next.pageInfo.endCursor : null;
|
|
32
|
+
if (next.pageInfo.hasNextPage && !cursor) {
|
|
33
|
+
throw new Error(`Merge queue check pagination interrupted: GitHub omitted the next cursor for ${commit.oid}. Retry.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Hydrate all status contexts for the active or most recently removed queue commit. */
|
|
38
|
+
export async function hydrateMergeQueueChecks(raw, repo) {
|
|
39
|
+
const active = raw.mergeQueueEntry?.headCommit;
|
|
40
|
+
const removal = raw.mergeQueueRemovals?.nodes[0];
|
|
41
|
+
const addition = raw.mergeQueueAdditions?.nodes[0];
|
|
42
|
+
const removalIsCurrent = Boolean(removal && (!addition || Date.parse(removal.createdAt) >= Date.parse(addition.createdAt)));
|
|
43
|
+
const removed = removalIsCurrent ? removal?.beforeCommit : undefined;
|
|
44
|
+
if (active)
|
|
45
|
+
await hydrateCommitContexts(active, repo);
|
|
46
|
+
if (removed && removed.oid !== active?.oid)
|
|
47
|
+
await hydrateCommitContexts(removed, repo);
|
|
48
|
+
}
|
package/bin/github/queries.d.mts
CHANGED
|
@@ -16,6 +16,8 @@ export declare const SUGGESTION_THREADS_QUERY: string;
|
|
|
16
16
|
export declare const REVIEW_THREAD_COMMENTS_QUERY: string;
|
|
17
17
|
/** Fetch inline annotations for a single CheckRun by node ID. */
|
|
18
18
|
export declare const CHECK_RUN_ANNOTATIONS_QUERY: string;
|
|
19
|
+
/** Fetches additional status contexts for a merge queue synthetic commit. */
|
|
20
|
+
export declare const COMMIT_CHECK_CONTEXTS_QUERY: string;
|
|
19
21
|
/** Returns the current head commit SHA for a PR. Used by waitForSha polling. */
|
|
20
22
|
export declare const GET_PR_HEAD_SHA_QUERY: string;
|
|
21
23
|
/** Look up PR number by branch name (for getCurrentPrNumber). */
|
package/bin/github/queries.mjs
CHANGED
|
@@ -19,6 +19,8 @@ export const SUGGESTION_THREADS_QUERY = gql("suggestion-threads.gql");
|
|
|
19
19
|
export const REVIEW_THREAD_COMMENTS_QUERY = gql("review-thread-comments.gql");
|
|
20
20
|
/** Fetch inline annotations for a single CheckRun by node ID. */
|
|
21
21
|
export const CHECK_RUN_ANNOTATIONS_QUERY = gql("check-run-annotations.gql");
|
|
22
|
+
/** Fetches additional status contexts for a merge queue synthetic commit. */
|
|
23
|
+
export const COMMIT_CHECK_CONTEXTS_QUERY = gql("commit-check-contexts.gql");
|
|
22
24
|
/** Returns the current head commit SHA for a PR. Used by waitForSha polling. */
|
|
23
25
|
export const GET_PR_HEAD_SHA_QUERY = gql("get-pr-head-sha.gql");
|
|
24
26
|
/** Look up PR number by branch name (for getCurrentPrNumber). */
|
|
@@ -41,6 +41,7 @@ function parseThread(raw, pr, threadId) {
|
|
|
41
41
|
...(comment?.authorAssociation !== undefined && {
|
|
42
42
|
authorAssociation: comment.authorAssociation,
|
|
43
43
|
}),
|
|
44
|
+
...(comment?.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
44
45
|
body: comment?.body ?? "",
|
|
45
46
|
url: comment?.url ?? "",
|
|
46
47
|
createdAtUnix: comment?.createdAt ? parseCreatedAt(comment.createdAt) : 0,
|
package/bin/mcp/server.mjs
CHANGED
|
@@ -11,15 +11,22 @@ const QUALIFIED_PR_ERROR = "pr must be a GitHub pull-request URL or an owner/rep
|
|
|
11
11
|
const pr = z
|
|
12
12
|
.string()
|
|
13
13
|
.refine(isRepositoryQualifiedPrReference, { message: QUALIFIED_PR_ERROR })
|
|
14
|
-
.describe("GitHub pull-request URL or owner/repo#number");
|
|
14
|
+
.describe("GitHub pull-request URL or owner/repo#number; the explicit repository may differ from the server working directory");
|
|
15
15
|
const ids = z.array(z.string().min(1)).optional();
|
|
16
16
|
const iterateInputSchema = z.object({
|
|
17
17
|
pr,
|
|
18
18
|
readyDelaySeconds: z.number().nonnegative().optional(),
|
|
19
19
|
stallTimeoutSeconds: z.number().nonnegative().optional(),
|
|
20
20
|
noAutoMarkReady: z.boolean().optional(),
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
merge: z.boolean().optional(),
|
|
22
|
+
noAutoCancelActionable: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Deprecated no-op; Shepherd never cancels workflow runs."),
|
|
26
|
+
neverCancelRuns: z
|
|
27
|
+
.array(z.string())
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Deprecated per-call no-op retained for compatibility."),
|
|
23
30
|
});
|
|
24
31
|
const reviewMutationsOperationSchema = z.object({
|
|
25
32
|
type: z.literal("review_mutations"),
|
|
@@ -33,7 +40,7 @@ const reviewMutationsOperationSchema = z.object({
|
|
|
33
40
|
const markFilesViewedOperationSchema = z.object({
|
|
34
41
|
type: z.literal("mark_files_viewed"),
|
|
35
42
|
files: z.array(z.string().min(1)).optional(),
|
|
36
|
-
tests: z.boolean().optional(),
|
|
43
|
+
tests: z.boolean().optional().describe("Select changed test files; no mutation occurs."),
|
|
37
44
|
matchPatterns: z.array(z.string().min(1)).optional(),
|
|
38
45
|
});
|
|
39
46
|
const appendJournalOperationSchema = z.object({
|
|
@@ -72,7 +79,7 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
72
79
|
const shepherd = options.shepherd ?? createPrShepherd({ cwd: options.cwd });
|
|
73
80
|
const server = new McpServer({ name: "pr-shepherd", version: readPackageVersion() });
|
|
74
81
|
server.registerTool("iterate", {
|
|
75
|
-
description: "Inspect the
|
|
82
|
+
description: "Inspect the specified pull request and return the next Shepherd state.",
|
|
76
83
|
inputSchema: iterateInputSchema,
|
|
77
84
|
annotations: {
|
|
78
85
|
readOnlyHint: false,
|
|
@@ -82,7 +89,7 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
82
89
|
},
|
|
83
90
|
}, async (input) => runTool(() => shepherd.iterate(requireRepositoryQualifiedPr(input)), formatIterateResult));
|
|
84
91
|
server.registerTool("apply", {
|
|
85
|
-
description: "Apply ordered review, file-view,
|
|
92
|
+
description: "Apply ordered authorized review and journal operations, or run selection-only file-view diagnostics, after prevalidation.",
|
|
86
93
|
inputSchema: applyInputSchema,
|
|
87
94
|
annotations: {
|
|
88
95
|
readOnlyHint: false,
|
package/bin/pr-reference.d.mts
CHANGED
|
@@ -2,6 +2,19 @@ export interface ParsedPrReference {
|
|
|
2
2
|
number?: number;
|
|
3
3
|
repository?: string;
|
|
4
4
|
}
|
|
5
|
+
export interface ResolvedPrTarget {
|
|
6
|
+
prNumber?: number;
|
|
7
|
+
targetRepository?: {
|
|
8
|
+
owner: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
5
12
|
/** Parses API PR references without performing repository or GitHub I/O. */
|
|
6
13
|
export declare function parsePrReference(pr: number | string | undefined): ParsedPrReference | null;
|
|
14
|
+
/** Parses a positional CLI PR reference, including a bare numeric string. */
|
|
15
|
+
export declare function parseCliPrReference(value: string): ParsedPrReference | null;
|
|
16
|
+
/** Converts a validated parsed reference into the command-layer target shape. */
|
|
17
|
+
export declare function resolveParsedPrTarget(parsed: ParsedPrReference): ResolvedPrTarget;
|
|
18
|
+
/** Canonical collision-safe PR reference for generated commands and handoffs. */
|
|
19
|
+
export declare function formatPrUrl(repository: string, prNumber: number): string;
|
|
7
20
|
export declare function isRepositoryQualifiedPrReference(pr: unknown): pr is string;
|
package/bin/pr-reference.mjs
CHANGED
|
@@ -18,7 +18,7 @@ export function parsePrReference(pr) {
|
|
|
18
18
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
19
19
|
if ((url.protocol === "https:" || url.protocol === "http:") &&
|
|
20
20
|
(url.hostname === "github.com" || url.hostname === "www.github.com") &&
|
|
21
|
-
parts.length
|
|
21
|
+
parts.length >= 4 &&
|
|
22
22
|
parts[2] === "pull" &&
|
|
23
23
|
/^[1-9][0-9]*$/.test(parts[3])) {
|
|
24
24
|
return { number: Number(parts[3]), repository: `${parts[0]}/${parts[1]}` };
|
|
@@ -29,6 +29,28 @@ export function parsePrReference(pr) {
|
|
|
29
29
|
}
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
|
+
/** Parses a positional CLI PR reference, including a bare numeric string. */
|
|
33
|
+
export function parseCliPrReference(value) {
|
|
34
|
+
if (/^[0-9]+$/.test(value))
|
|
35
|
+
return { number: Number(value) };
|
|
36
|
+
return parsePrReference(value);
|
|
37
|
+
}
|
|
38
|
+
/** Converts a validated parsed reference into the command-layer target shape. */
|
|
39
|
+
export function resolveParsedPrTarget(parsed) {
|
|
40
|
+
if (parsed.repository === undefined)
|
|
41
|
+
return { prNumber: parsed.number };
|
|
42
|
+
const [owner, name] = parsed.repository.split("/");
|
|
43
|
+
if (!owner || !name)
|
|
44
|
+
throw new Error(`Invalid repository reference: ${parsed.repository}`);
|
|
45
|
+
return {
|
|
46
|
+
prNumber: parsed.number,
|
|
47
|
+
targetRepository: { owner, name },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Canonical collision-safe PR reference for generated commands and handoffs. */
|
|
51
|
+
export function formatPrUrl(repository, prNumber) {
|
|
52
|
+
return `https://github.com/${repository}/pull/${prNumber}`;
|
|
53
|
+
}
|
|
32
54
|
export function isRepositoryQualifiedPrReference(pr) {
|
|
33
55
|
if (typeof pr !== "string")
|
|
34
56
|
return false;
|
package/bin/reporters/agent.mjs
CHANGED
|
@@ -13,6 +13,8 @@ export function toAgentThread(t) {
|
|
|
13
13
|
const suggestion = extractSuggestion(t) ?? undefined;
|
|
14
14
|
return {
|
|
15
15
|
id: t.id,
|
|
16
|
+
...(t.viewerCanReply === false && { viewerCanReply: false }),
|
|
17
|
+
...(t.viewerCanResolve === false && { viewerCanResolve: false }),
|
|
16
18
|
...(t.reviewId !== undefined && { reviewId: t.reviewId }),
|
|
17
19
|
path: t.path,
|
|
18
20
|
line: t.line,
|
|
@@ -22,6 +24,7 @@ export function toAgentThread(t) {
|
|
|
22
24
|
author: t.author,
|
|
23
25
|
...(t.authorType !== undefined && { authorType: t.authorType }),
|
|
24
26
|
...(t.authorAssociation !== undefined && { authorAssociation: t.authorAssociation }),
|
|
27
|
+
...(t.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
25
28
|
body: t.body,
|
|
26
29
|
url: t.url,
|
|
27
30
|
...(t.edited === true && { edited: true }),
|
|
@@ -31,6 +34,7 @@ export function toAgentThread(t) {
|
|
|
31
34
|
author: c.author,
|
|
32
35
|
...(c.authorType !== undefined && { authorType: c.authorType }),
|
|
33
36
|
...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
|
|
37
|
+
...(c.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
34
38
|
body: c.body,
|
|
35
39
|
url: c.url,
|
|
36
40
|
})),
|
|
@@ -41,6 +45,7 @@ export function toAgentThread(t) {
|
|
|
41
45
|
export function toAgentComment(c) {
|
|
42
46
|
return {
|
|
43
47
|
id: c.id,
|
|
48
|
+
...(c.viewerCanMinimize === false && { viewerCanMinimize: false }),
|
|
44
49
|
author: c.author,
|
|
45
50
|
...(c.authorType !== undefined && { authorType: c.authorType }),
|
|
46
51
|
...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
|
|
@@ -61,6 +66,8 @@ export function toAgentCheck(c) {
|
|
|
61
66
|
...(c.summary !== undefined && { summary: c.summary }),
|
|
62
67
|
...(c.logExcerpt !== undefined && { logExcerpt: c.logExcerpt }),
|
|
63
68
|
...(c.annotations !== undefined && { annotations: c.annotations }),
|
|
69
|
+
...(c.scope !== undefined && { scope: c.scope }),
|
|
70
|
+
...(c.commitOid !== undefined && { commitOid: c.commitOid }),
|
|
64
71
|
};
|
|
65
72
|
}
|
|
66
73
|
export function toAgentStalledCheck(c, nowSeconds) {
|
|
@@ -85,13 +92,14 @@ export function toAgentStalledCheck(c, nowSeconds) {
|
|
|
85
92
|
* log tail, so they are all kept.
|
|
86
93
|
*/
|
|
87
94
|
export function toAgentChecks(checks) {
|
|
88
|
-
const
|
|
95
|
+
const seenKeys = new Set();
|
|
89
96
|
const result = [];
|
|
90
97
|
for (const c of checks) {
|
|
91
98
|
if (c.runId === null) {
|
|
92
|
-
|
|
99
|
+
const key = `${c.scope ?? "pr"}:${c.commitOid ?? "head"}:${c.name}`;
|
|
100
|
+
if (seenKeys.has(key))
|
|
93
101
|
continue;
|
|
94
|
-
|
|
102
|
+
seenKeys.add(key);
|
|
95
103
|
}
|
|
96
104
|
result.push(toAgentCheck(c));
|
|
97
105
|
}
|
|
@@ -4,11 +4,13 @@ export declare function threadComments(thread: {
|
|
|
4
4
|
author: string;
|
|
5
5
|
authorType?: ReviewThreadComment["authorType"];
|
|
6
6
|
authorAssociation?: ReviewThreadComment["authorAssociation"];
|
|
7
|
+
viewerDidAuthor?: true;
|
|
7
8
|
url?: string;
|
|
8
9
|
createdAtUnix?: number;
|
|
9
10
|
comments?: Array<Pick<ReviewThreadComment, "id" | "author" | "body" | "url"> & {
|
|
10
11
|
authorType?: ReviewThreadComment["authorType"];
|
|
11
12
|
authorAssociation?: ReviewThreadComment["authorAssociation"];
|
|
13
|
+
viewerDidAuthor?: true;
|
|
12
14
|
} & Partial<Pick<ReviewThreadComment, "isMinimized" | "createdAtUnix">>>;
|
|
13
15
|
}): ReviewThreadComment[];
|
|
14
16
|
export declare function threadTranscriptBody(thread: ReviewThread, appendedBodies?: string[]): string;
|
|
@@ -6,6 +6,7 @@ export function threadComments(thread) {
|
|
|
6
6
|
author: c.author,
|
|
7
7
|
authorType: c.authorType ?? "Unknown",
|
|
8
8
|
...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
|
|
9
|
+
...(c.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
9
10
|
body: c.body,
|
|
10
11
|
url: c.url,
|
|
11
12
|
createdAtUnix: c.createdAtUnix ?? 0,
|
|
@@ -20,6 +21,7 @@ export function threadComments(thread) {
|
|
|
20
21
|
...(thread.authorAssociation !== undefined && {
|
|
21
22
|
authorAssociation: thread.authorAssociation,
|
|
22
23
|
}),
|
|
24
|
+
...(thread.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
23
25
|
body: thread.body,
|
|
24
26
|
url: thread.url ?? "",
|
|
25
27
|
createdAtUnix: thread.createdAtUnix ?? 0,
|
package/bin/types/activity.d.mts
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AgentComment, AgentThread } from "./report.mts";
|
|
2
|
+
import type { CheckStatus, Review } from "./github.mts";
|
|
3
|
+
import type { MergeQueueRemovalStatus } from "./merge-requirements.mts";
|
|
4
|
+
export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "thread-missing-location" | "authorization-required" | "bot-cr-not-dismissed" | "merge-queue-removed";
|
|
5
|
+
export interface AgentStalledCheck {
|
|
6
|
+
name: string;
|
|
7
|
+
status: CheckStatus;
|
|
8
|
+
source: "check_run" | "status_context" | "startup_failure";
|
|
9
|
+
runId: string | null;
|
|
10
|
+
detailsUrl: string | null;
|
|
11
|
+
createdAtUnix?: number;
|
|
12
|
+
startedAtUnix?: number;
|
|
13
|
+
updatedAtUnix?: number;
|
|
14
|
+
ageSeconds: number;
|
|
15
|
+
summary?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface EscalateDetails {
|
|
18
|
+
triggers: EscalateTrigger[];
|
|
19
|
+
unresolvedThreads: AgentThread[];
|
|
20
|
+
ambiguousComments: AgentComment[];
|
|
21
|
+
changesRequestedReviews: Review[];
|
|
22
|
+
stalledChecks?: AgentStalledCheck[];
|
|
23
|
+
thrashHistory?: Array<{
|
|
24
|
+
threadId: string;
|
|
25
|
+
attempts: number;
|
|
26
|
+
}>;
|
|
27
|
+
suggestion: string;
|
|
28
|
+
humanMessage: string;
|
|
29
|
+
mergeQueueRemoval?: MergeQueueRemovalStatus;
|
|
30
|
+
authorization?: Array<{
|
|
31
|
+
action: "reply-thread" | "resolve-thread" | "dismiss-review" | "mark-ready" | "merge-or-enqueue";
|
|
32
|
+
targetIds: string[];
|
|
33
|
+
reason: "denied-or-unverifiable";
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/bin/types/github.d.mts
CHANGED
|
@@ -7,6 +7,17 @@ export type MergeableState = "CONFLICTING" | "MERGEABLE" | "UNKNOWN";
|
|
|
7
7
|
export type MergeStateStatus = "BEHIND" | "BLOCKED" | "CLEAN" | "DIRTY" | "DRAFT" | "HAS_HOOKS" | "UNKNOWN" | "UNSTABLE";
|
|
8
8
|
export type ReviewDecision = "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null;
|
|
9
9
|
export type AuthorType = "User" | "Bot" | "Unknown";
|
|
10
|
+
type RepositoryPermission = "NONE" | "READ" | "TRIAGE" | "WRITE" | "MAINTAIN" | "ADMIN";
|
|
11
|
+
/** Raw GitHub viewer fields used to gate remote actions. */
|
|
12
|
+
export interface ViewerAuthorization {
|
|
13
|
+
repositoryPermission: RepositoryPermission | null;
|
|
14
|
+
viewerCanAdminister: boolean;
|
|
15
|
+
viewerDidAuthor: boolean;
|
|
16
|
+
viewerCanUpdate: boolean;
|
|
17
|
+
viewerCanEnableAutoMerge: boolean;
|
|
18
|
+
viewerCanEditFiles: boolean;
|
|
19
|
+
headRepositoryPermission: RepositoryPermission | null;
|
|
20
|
+
}
|
|
10
21
|
/** Raw relationship between a comment author and the repository, as reported by GitHub. */
|
|
11
22
|
export type CommentAuthorAssociation = "COLLABORATOR" | "CONTRIBUTOR" | "FIRST_TIMER" | "FIRST_TIME_CONTRIBUTOR" | "MANNEQUIN" | "MEMBER" | "NONE" | "OWNER";
|
|
12
23
|
export interface CheckRun {
|
|
@@ -19,6 +30,10 @@ export interface CheckRun {
|
|
|
19
30
|
detailsUrl: string;
|
|
20
31
|
event: string | null;
|
|
21
32
|
runId: string | null;
|
|
33
|
+
/** Commit scope that supplied this check. Omitted for ordinary PR-head checks. */
|
|
34
|
+
scope?: "merge_group";
|
|
35
|
+
/** Synthetic merge-group commit OID, when `scope` is `merge_group`. */
|
|
36
|
+
commitOid?: string;
|
|
22
37
|
createdAtUnix?: number;
|
|
23
38
|
startedAtUnix?: number;
|
|
24
39
|
completedAtUnix?: number;
|
|
@@ -36,6 +51,8 @@ export interface ReviewThread {
|
|
|
36
51
|
isResolved: boolean;
|
|
37
52
|
isOutdated: boolean;
|
|
38
53
|
isMinimized: boolean;
|
|
54
|
+
viewerCanReply?: boolean;
|
|
55
|
+
viewerCanResolve?: boolean;
|
|
39
56
|
path: string | null;
|
|
40
57
|
line: number | null;
|
|
41
58
|
/** Start of the comment's line range. Null for single-line comments (use `line` for both). */
|
|
@@ -45,6 +62,7 @@ export interface ReviewThread {
|
|
|
45
62
|
author: string;
|
|
46
63
|
authorType: AuthorType;
|
|
47
64
|
authorAssociation?: CommentAuthorAssociation;
|
|
65
|
+
viewerDidAuthor?: true;
|
|
48
66
|
body: string;
|
|
49
67
|
url: string;
|
|
50
68
|
createdAtUnix?: number;
|
|
@@ -66,6 +84,7 @@ export interface SuggestionBlock {
|
|
|
66
84
|
export interface PrComment {
|
|
67
85
|
id: string;
|
|
68
86
|
isMinimized: boolean;
|
|
87
|
+
viewerCanMinimize?: boolean;
|
|
69
88
|
author: string;
|
|
70
89
|
authorType: AuthorType;
|
|
71
90
|
authorAssociation?: CommentAuthorAssociation;
|
|
@@ -78,6 +97,8 @@ export interface Review {
|
|
|
78
97
|
author: string;
|
|
79
98
|
authorType: AuthorType;
|
|
80
99
|
authorAssociation?: CommentAuthorAssociation;
|
|
100
|
+
/** Present for minimizable COMMENTED/APPROVED reviews; false for non-minimizable review rows. */
|
|
101
|
+
viewerCanMinimize?: boolean;
|
|
81
102
|
body: string;
|
|
82
103
|
createdAtUnix?: number;
|
|
83
104
|
edited?: boolean;
|
|
@@ -119,6 +140,7 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
119
140
|
headRefName: string;
|
|
120
141
|
/** `"owner/name"` of the head repository; null when the fork has been deleted. */
|
|
121
142
|
headRepoWithOwner: string | null;
|
|
143
|
+
viewerAuthorization?: ViewerAuthorization;
|
|
122
144
|
baseRefName: string;
|
|
123
145
|
reviewRequests: Array<{
|
|
124
146
|
login: string;
|
|
@@ -133,6 +155,11 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
133
155
|
reviewSummaries: Review[];
|
|
134
156
|
approvedReviews: Review[];
|
|
135
157
|
checks: CheckRun[];
|
|
158
|
+
mergeQueueChecks?: CheckRun[];
|
|
159
|
+
mergeQueueChecksIncomplete?: true;
|
|
160
|
+
removedMergeQueueChecks?: CheckRun[];
|
|
161
|
+
removedMergeQueueChecksIncomplete?: true;
|
|
136
162
|
branchProtection: BranchProtection | null;
|
|
137
163
|
activity?: PrActivitySummary;
|
|
138
164
|
}
|
|
165
|
+
export {};
|