pr-shepherd 0.43.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 +18 -17
- package/bin/api.d.mts +3 -2
- package/bin/api.mjs +13 -18
- package/bin/cli/args.mjs +13 -9
- package/bin/cli/fix-formatter.mjs +8 -6
- 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 +2 -2
- 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-formatter.d.mts +1 -1
- package/bin/cli/iterate-formatter.mjs +5 -5
- package/bin/cli/iterate-lean.mjs +4 -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/commands/check.mjs +22 -6
- 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/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 +1 -1
- package/bin/commands/iterate/escalate.mjs +11 -0
- package/bin/commands/iterate/fix-code.mjs +81 -42
- package/bin/commands/iterate/helpers.d.mts +0 -2
- package/bin/commands/iterate/helpers.mjs +0 -15
- package/bin/commands/iterate/index.mjs +6 -12
- 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.mjs +38 -9
- package/bin/commands/iterate/merge.mjs +0 -1
- 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/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 +1 -1
- package/bin/github/batch-parsers.d.mts +2 -2
- package/bin/github/batch-parsers.mjs +17 -1
- package/bin/github/batch-raw-types.d.mts +12 -0
- package/bin/github/batch.mjs +1 -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 +13 -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/suggestion-thread.mjs +1 -0
- package/bin/mcp/server.mjs +12 -6
- package/bin/pr-reference.d.mts +13 -0
- package/bin/pr-reference.mjs +23 -1
- package/bin/reporters/agent.mjs +5 -0
- package/bin/threads/transcript.d.mts +2 -0
- package/bin/threads/transcript.mjs +2 -0
- package/bin/types/agent-thread.d.mts +1 -0
- package/bin/types/escalate.d.mts +6 -1
- package/bin/types/github.d.mts +19 -0
- package/bin/types/iterate.d.mts +9 -8
- package/bin/types/report.d.mts +14 -1
- package/bin/types/review-thread.d.mts +1 -0
- package/bin/types/suggestion-patch.d.mts +1 -1
- 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 +15 -13
- package/bin/commands/iterate/reruns.d.mts +0 -20
- package/bin/commands/iterate/reruns.mjs +0 -105
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BatchPrData } from "../types.mts";
|
|
2
|
-
import type { RawPr, RawThread, RawComment, RawReview, RawReviewSummary, RawContextNode } from "./batch-raw-types.mts";
|
|
3
|
-
export declare function parseRawPr(raw: RawPr, rawThreadPages: RawThread[], rawCommentNodes: RawComment[], rawReviewNodes: RawReview[], rawReviewSummaryNodes: RawReviewSummary[], rawApprovedReviewNodes: RawReviewSummary[], rawCheckNodes: RawContextNode[]): BatchPrData;
|
|
2
|
+
import type { RawBatchResponse, RawPr, RawThread, RawComment, RawReview, RawReviewSummary, RawContextNode } from "./batch-raw-types.mts";
|
|
3
|
+
export declare function parseRawPr(raw: RawPr, rawThreadPages: RawThread[], rawCommentNodes: RawComment[], rawReviewNodes: RawReview[], rawReviewSummaryNodes: RawReviewSummary[], rawApprovedReviewNodes: RawReviewSummary[], rawCheckNodes: RawContextNode[], repository?: Pick<NonNullable<RawBatchResponse["repository"]>, "viewerPermission" | "viewerCanAdminister">): BatchPrData;
|
|
@@ -10,6 +10,7 @@ function parseReviewNode(r) {
|
|
|
10
10
|
author: r.author?.login ?? "unknown",
|
|
11
11
|
authorType: mapAuthorType(r.author?.__typename, r.author?.login),
|
|
12
12
|
...(r.authorAssociation !== undefined && { authorAssociation: r.authorAssociation }),
|
|
13
|
+
viewerCanMinimize: "viewerCanMinimize" in r && r.viewerCanMinimize === true,
|
|
13
14
|
body: r.body,
|
|
14
15
|
createdAtUnix: r.createdAt ? parseCreatedAt(r.createdAt) : 0,
|
|
15
16
|
};
|
|
@@ -18,7 +19,7 @@ function parseReviewNode(r) {
|
|
|
18
19
|
}
|
|
19
20
|
return base;
|
|
20
21
|
}
|
|
21
|
-
export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes, rawReviewSummaryNodes, rawApprovedReviewNodes, rawCheckNodes) {
|
|
22
|
+
export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes, rawReviewSummaryNodes, rawApprovedReviewNodes, rawCheckNodes, repository) {
|
|
22
23
|
const reviewRequests = (raw.reviewRequests?.nodes ?? []).flatMap((n) => {
|
|
23
24
|
const login = n.requestedReviewer?.login ?? n.requestedReviewer?.name;
|
|
24
25
|
return login ? [{ login }] : [];
|
|
@@ -37,6 +38,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
37
38
|
author: c.author?.login ?? "unknown",
|
|
38
39
|
authorType: mapAuthorType(c.author?.__typename, c.author?.login),
|
|
39
40
|
...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
|
|
41
|
+
...(c.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
40
42
|
body: c.body,
|
|
41
43
|
url: c.url,
|
|
42
44
|
createdAtUnix: c.createdAt ? parseCreatedAt(c.createdAt) : 0,
|
|
@@ -46,6 +48,8 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
46
48
|
isResolved: t.isResolved,
|
|
47
49
|
isOutdated: t.isOutdated,
|
|
48
50
|
isMinimized: comment?.isMinimized ?? false,
|
|
51
|
+
viewerCanReply: t.viewerCanReply === true,
|
|
52
|
+
viewerCanResolve: t.viewerCanResolve === true,
|
|
49
53
|
path: t.path ?? comment?.path ?? null,
|
|
50
54
|
line: t.line ?? comment?.line ?? null,
|
|
51
55
|
startLine: t.startLine ?? comment?.startLine ?? null,
|
|
@@ -55,6 +59,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
55
59
|
...(comment?.authorAssociation !== undefined && {
|
|
56
60
|
authorAssociation: comment.authorAssociation,
|
|
57
61
|
}),
|
|
62
|
+
...(comment?.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
58
63
|
body: comment?.body ?? "",
|
|
59
64
|
url: comment?.url ?? "",
|
|
60
65
|
createdAtUnix: comment?.createdAt ? parseCreatedAt(comment.createdAt) : 0,
|
|
@@ -64,6 +69,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
64
69
|
const comments = rawCommentNodes.map((c) => ({
|
|
65
70
|
id: c.id,
|
|
66
71
|
isMinimized: c.isMinimized,
|
|
72
|
+
viewerCanMinimize: c.viewerCanMinimize === true,
|
|
67
73
|
author: c.author?.login ?? "unknown",
|
|
68
74
|
authorType: mapAuthorType(c.author?.__typename, c.author?.login),
|
|
69
75
|
...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
|
|
@@ -108,6 +114,16 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
108
114
|
headRefOid: raw.headRefOid,
|
|
109
115
|
headRefName: raw.headRefName,
|
|
110
116
|
headRepoWithOwner: raw.headRepository?.nameWithOwner ?? null,
|
|
117
|
+
viewerAuthorization: {
|
|
118
|
+
repositoryPermission: repository?.viewerPermission ?? null,
|
|
119
|
+
viewerCanAdminister: repository?.viewerCanAdminister === true,
|
|
120
|
+
viewerDidAuthor: raw.viewerDidAuthor === true,
|
|
121
|
+
viewerCanUpdate: raw.viewerCanUpdate === true,
|
|
122
|
+
viewerCanEnableAutoMerge: raw.viewerCanEnableAutoMerge === true,
|
|
123
|
+
viewerCanEditFiles: raw.viewerCanEditFiles === true,
|
|
124
|
+
headRepositoryPermission: raw.headRepository?.viewerPermission ??
|
|
125
|
+
null,
|
|
126
|
+
},
|
|
111
127
|
baseRefName: raw.baseRefName,
|
|
112
128
|
reviewRequests,
|
|
113
129
|
latestReviews,
|
|
@@ -2,6 +2,8 @@ import type { CommentAuthorAssociation } from "../types/github.mts";
|
|
|
2
2
|
import type { RawPrMergeFields } from "./batch-raw-rules.mts";
|
|
3
3
|
export interface RawBatchResponse {
|
|
4
4
|
repository: {
|
|
5
|
+
viewerPermission: string | null;
|
|
6
|
+
viewerCanAdminister: boolean;
|
|
5
7
|
pullRequest: RawPr | null;
|
|
6
8
|
} | null;
|
|
7
9
|
}
|
|
@@ -10,6 +12,10 @@ export interface RawPr extends RawPrMergeFields {
|
|
|
10
12
|
number: number;
|
|
11
13
|
state: string;
|
|
12
14
|
isDraft: boolean;
|
|
15
|
+
viewerDidAuthor: boolean;
|
|
16
|
+
viewerCanUpdate: boolean;
|
|
17
|
+
viewerCanEnableAutoMerge: boolean;
|
|
18
|
+
viewerCanEditFiles: boolean;
|
|
13
19
|
mergeable: string;
|
|
14
20
|
mergeStateStatus: string;
|
|
15
21
|
reviewDecision: string | null;
|
|
@@ -17,6 +23,7 @@ export interface RawPr extends RawPrMergeFields {
|
|
|
17
23
|
headRefName: string;
|
|
18
24
|
headRepository: {
|
|
19
25
|
nameWithOwner: string;
|
|
26
|
+
viewerPermission: string | null;
|
|
20
27
|
} | null;
|
|
21
28
|
baseRefName: string;
|
|
22
29
|
baseRef: import("./batch-raw-rules.mts").RawBaseRef | null;
|
|
@@ -117,6 +124,7 @@ export interface RawThreadComment {
|
|
|
117
124
|
isMinimized: boolean;
|
|
118
125
|
url: string;
|
|
119
126
|
authorAssociation?: CommentAuthorAssociation;
|
|
127
|
+
viewerDidAuthor?: boolean;
|
|
120
128
|
author: RawAuthor | null;
|
|
121
129
|
pullRequestReview?: {
|
|
122
130
|
id: string;
|
|
@@ -131,6 +139,8 @@ export interface RawThread {
|
|
|
131
139
|
id: string;
|
|
132
140
|
isResolved: boolean;
|
|
133
141
|
isOutdated: boolean;
|
|
142
|
+
viewerCanReply: boolean;
|
|
143
|
+
viewerCanResolve: boolean;
|
|
134
144
|
path?: string | null;
|
|
135
145
|
line?: number | null;
|
|
136
146
|
startLine?: number | null;
|
|
@@ -157,6 +167,7 @@ export interface RawReviewThreadCommentsResponse {
|
|
|
157
167
|
export interface RawComment {
|
|
158
168
|
id: string;
|
|
159
169
|
isMinimized: boolean;
|
|
170
|
+
viewerCanMinimize: boolean;
|
|
160
171
|
url: string;
|
|
161
172
|
authorAssociation?: CommentAuthorAssociation;
|
|
162
173
|
author: RawAuthor | null;
|
|
@@ -176,6 +187,7 @@ export interface RawReview {
|
|
|
176
187
|
export interface RawReviewSummary {
|
|
177
188
|
id: string;
|
|
178
189
|
isMinimized: boolean;
|
|
190
|
+
viewerCanMinimize: boolean;
|
|
179
191
|
authorAssociation?: CommentAuthorAssociation;
|
|
180
192
|
author: RawAuthor | null;
|
|
181
193
|
body: string;
|
package/bin/github/batch.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export async function fetchPrBatch(pr, repo, opts = {}) {
|
|
|
20
20
|
await hydrateMergeQueueChecks(raw, repo);
|
|
21
21
|
const paged = await paginateBatchConnections(pr, repo, raw, opts, result.rateLimit);
|
|
22
22
|
const rawThreadPages = await hydrateThreadCommentPages(paged.threads);
|
|
23
|
-
const data = parseRawPr(raw, rawThreadPages, paged.comments, paged.changesRequested, paged.reviewSummaries, paged.approvedReviews, paged.checks);
|
|
23
|
+
const data = parseRawPr(raw, rawThreadPages, paged.comments, paged.changesRequested, paged.reviewSummaries, paged.approvedReviews, paged.checks, result.data.repository);
|
|
24
24
|
data.checks = mergeStartupFailureChecks(data.checks, parseSuiteStartupFailures(raw));
|
|
25
25
|
return {
|
|
26
26
|
data,
|
package/bin/github/client.d.mts
CHANGED
|
@@ -30,6 +30,7 @@ export declare function getPrHeadSha(pr: number, owner: string, name: string): P
|
|
|
30
30
|
export declare function getPullRequestBody(pr: number, owner: string, name: string): Promise<{
|
|
31
31
|
nodeId: string;
|
|
32
32
|
body: string;
|
|
33
|
+
viewerCanUpdate?: boolean;
|
|
33
34
|
}>;
|
|
34
35
|
/** Overwrites the PR body. */
|
|
35
36
|
export declare function updatePullRequestBody(pullRequestId: string, body: string): Promise<void>;
|
package/bin/github/client.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
1
2
|
/**
|
|
2
3
|
* High-level GitHub client — wraps http.mts for application-level concerns.
|
|
3
4
|
*
|
|
@@ -76,7 +77,13 @@ export async function getPullRequestBody(pr, owner, name) {
|
|
|
76
77
|
: "PR not found or access denied";
|
|
77
78
|
throw new Error(`Could not fetch body for ${owner}/${name} PR #${pr}: ${detail}`);
|
|
78
79
|
}
|
|
79
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
nodeId: pullRequest.id,
|
|
82
|
+
body: pullRequest.body ?? "",
|
|
83
|
+
...(pullRequest.viewerCanUpdate !== undefined && {
|
|
84
|
+
viewerCanUpdate: pullRequest.viewerCanUpdate,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
80
87
|
}
|
|
81
88
|
/** Overwrites the PR body. */
|
|
82
89
|
export async function updatePullRequestBody(pullRequestId, body) {
|
|
@@ -29,6 +29,8 @@ query BatchPrPage(
|
|
|
29
29
|
id
|
|
30
30
|
isResolved
|
|
31
31
|
isOutdated
|
|
32
|
+
viewerCanReply
|
|
33
|
+
viewerCanResolve
|
|
32
34
|
path
|
|
33
35
|
line
|
|
34
36
|
startLine
|
|
@@ -42,6 +44,7 @@ query BatchPrPage(
|
|
|
42
44
|
isMinimized
|
|
43
45
|
url
|
|
44
46
|
authorAssociation
|
|
47
|
+
viewerDidAuthor
|
|
45
48
|
author {
|
|
46
49
|
__typename
|
|
47
50
|
login
|
|
@@ -66,6 +69,7 @@ query BatchPrPage(
|
|
|
66
69
|
nodes {
|
|
67
70
|
id
|
|
68
71
|
isMinimized
|
|
72
|
+
viewerCanMinimize
|
|
69
73
|
url
|
|
70
74
|
authorAssociation
|
|
71
75
|
author {
|
|
@@ -108,6 +112,7 @@ query BatchPrPage(
|
|
|
108
112
|
nodes {
|
|
109
113
|
id
|
|
110
114
|
isMinimized
|
|
115
|
+
viewerCanMinimize
|
|
111
116
|
authorAssociation
|
|
112
117
|
author {
|
|
113
118
|
__typename
|
|
@@ -126,6 +131,7 @@ query BatchPrPage(
|
|
|
126
131
|
nodes {
|
|
127
132
|
id
|
|
128
133
|
isMinimized
|
|
134
|
+
viewerCanMinimize
|
|
129
135
|
authorAssociation
|
|
130
136
|
author {
|
|
131
137
|
__typename
|
|
@@ -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,6 +20,7 @@ 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
|
|
@@ -146,6 +153,8 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
146
153
|
id
|
|
147
154
|
isResolved
|
|
148
155
|
isOutdated
|
|
156
|
+
viewerCanReply
|
|
157
|
+
viewerCanResolve
|
|
149
158
|
path
|
|
150
159
|
line
|
|
151
160
|
startLine
|
|
@@ -159,6 +168,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
159
168
|
isMinimized
|
|
160
169
|
url
|
|
161
170
|
authorAssociation
|
|
171
|
+
viewerDidAuthor
|
|
162
172
|
author {
|
|
163
173
|
__typename
|
|
164
174
|
login
|
|
@@ -183,6 +193,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
183
193
|
nodes {
|
|
184
194
|
id
|
|
185
195
|
isMinimized
|
|
196
|
+
viewerCanMinimize
|
|
186
197
|
url
|
|
187
198
|
authorAssociation
|
|
188
199
|
author {
|
|
@@ -220,6 +231,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
220
231
|
nodes {
|
|
221
232
|
id
|
|
222
233
|
isMinimized
|
|
234
|
+
viewerCanMinimize
|
|
223
235
|
authorAssociation
|
|
224
236
|
author {
|
|
225
237
|
__typename
|
|
@@ -240,6 +252,7 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
240
252
|
nodes {
|
|
241
253
|
id
|
|
242
254
|
isMinimized
|
|
255
|
+
viewerCanMinimize
|
|
243
256
|
authorAssociation
|
|
244
257
|
author {
|
|
245
258
|
__typename
|
|
@@ -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,7 +11,7 @@ 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,
|
|
@@ -19,8 +19,14 @@ const iterateInputSchema = z.object({
|
|
|
19
19
|
stallTimeoutSeconds: z.number().nonnegative().optional(),
|
|
20
20
|
noAutoMarkReady: z.boolean().optional(),
|
|
21
21
|
merge: z.boolean().optional(),
|
|
22
|
-
noAutoCancelActionable: z
|
|
23
|
-
|
|
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."),
|
|
24
30
|
});
|
|
25
31
|
const reviewMutationsOperationSchema = z.object({
|
|
26
32
|
type: z.literal("review_mutations"),
|
|
@@ -34,7 +40,7 @@ const reviewMutationsOperationSchema = z.object({
|
|
|
34
40
|
const markFilesViewedOperationSchema = z.object({
|
|
35
41
|
type: z.literal("mark_files_viewed"),
|
|
36
42
|
files: z.array(z.string().min(1)).optional(),
|
|
37
|
-
tests: z.boolean().optional(),
|
|
43
|
+
tests: z.boolean().optional().describe("Select changed test files; no mutation occurs."),
|
|
38
44
|
matchPatterns: z.array(z.string().min(1)).optional(),
|
|
39
45
|
});
|
|
40
46
|
const appendJournalOperationSchema = z.object({
|
|
@@ -73,7 +79,7 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
73
79
|
const shepherd = options.shepherd ?? createPrShepherd({ cwd: options.cwd });
|
|
74
80
|
const server = new McpServer({ name: "pr-shepherd", version: readPackageVersion() });
|
|
75
81
|
server.registerTool("iterate", {
|
|
76
|
-
description: "Inspect the
|
|
82
|
+
description: "Inspect the specified pull request and return the next Shepherd state.",
|
|
77
83
|
inputSchema: iterateInputSchema,
|
|
78
84
|
annotations: {
|
|
79
85
|
readOnlyHint: false,
|
|
@@ -83,7 +89,7 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
83
89
|
},
|
|
84
90
|
}, async (input) => runTool(() => shepherd.iterate(requireRepositoryQualifiedPr(input)), formatIterateResult));
|
|
85
91
|
server.registerTool("apply", {
|
|
86
|
-
description: "Apply ordered review, file-view,
|
|
92
|
+
description: "Apply ordered authorized review and journal operations, or run selection-only file-view diagnostics, after prevalidation.",
|
|
87
93
|
inputSchema: applyInputSchema,
|
|
88
94
|
annotations: {
|
|
89
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 }),
|
|
@@ -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/escalate.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentComment, AgentThread } from "./report.mts";
|
|
2
2
|
import type { CheckStatus, Review } from "./github.mts";
|
|
3
3
|
import type { MergeQueueRemovalStatus } from "./merge-requirements.mts";
|
|
4
|
-
export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "thread-missing-location" | "bot-cr-not-dismissed" | "merge-queue-removed";
|
|
4
|
+
export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "thread-missing-location" | "authorization-required" | "bot-cr-not-dismissed" | "merge-queue-removed";
|
|
5
5
|
export interface AgentStalledCheck {
|
|
6
6
|
name: string;
|
|
7
7
|
status: CheckStatus;
|
|
@@ -27,4 +27,9 @@ export interface EscalateDetails {
|
|
|
27
27
|
suggestion: string;
|
|
28
28
|
humanMessage: string;
|
|
29
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
|
+
}>;
|
|
30
35
|
}
|
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 {
|
|
@@ -40,6 +51,8 @@ export interface ReviewThread {
|
|
|
40
51
|
isResolved: boolean;
|
|
41
52
|
isOutdated: boolean;
|
|
42
53
|
isMinimized: boolean;
|
|
54
|
+
viewerCanReply?: boolean;
|
|
55
|
+
viewerCanResolve?: boolean;
|
|
43
56
|
path: string | null;
|
|
44
57
|
line: number | null;
|
|
45
58
|
/** Start of the comment's line range. Null for single-line comments (use `line` for both). */
|
|
@@ -49,6 +62,7 @@ export interface ReviewThread {
|
|
|
49
62
|
author: string;
|
|
50
63
|
authorType: AuthorType;
|
|
51
64
|
authorAssociation?: CommentAuthorAssociation;
|
|
65
|
+
viewerDidAuthor?: true;
|
|
52
66
|
body: string;
|
|
53
67
|
url: string;
|
|
54
68
|
createdAtUnix?: number;
|
|
@@ -70,6 +84,7 @@ export interface SuggestionBlock {
|
|
|
70
84
|
export interface PrComment {
|
|
71
85
|
id: string;
|
|
72
86
|
isMinimized: boolean;
|
|
87
|
+
viewerCanMinimize?: boolean;
|
|
73
88
|
author: string;
|
|
74
89
|
authorType: AuthorType;
|
|
75
90
|
authorAssociation?: CommentAuthorAssociation;
|
|
@@ -82,6 +97,8 @@ export interface Review {
|
|
|
82
97
|
author: string;
|
|
83
98
|
authorType: AuthorType;
|
|
84
99
|
authorAssociation?: CommentAuthorAssociation;
|
|
100
|
+
/** Present for minimizable COMMENTED/APPROVED reviews; false for non-minimizable review rows. */
|
|
101
|
+
viewerCanMinimize?: boolean;
|
|
85
102
|
body: string;
|
|
86
103
|
createdAtUnix?: number;
|
|
87
104
|
edited?: boolean;
|
|
@@ -123,6 +140,7 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
123
140
|
headRefName: string;
|
|
124
141
|
/** `"owner/name"` of the head repository; null when the fork has been deleted. */
|
|
125
142
|
headRepoWithOwner: string | null;
|
|
143
|
+
viewerAuthorization?: ViewerAuthorization;
|
|
126
144
|
baseRefName: string;
|
|
127
145
|
reviewRequests: Array<{
|
|
128
146
|
login: string;
|
|
@@ -144,3 +162,4 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
144
162
|
branchProtection: BranchProtection | null;
|
|
145
163
|
activity?: PrActivitySummary;
|
|
146
164
|
}
|
|
165
|
+
export {};
|
package/bin/types/iterate.d.mts
CHANGED
|
@@ -59,9 +59,9 @@ export interface ResolveCommand {
|
|
|
59
59
|
requiresHeadSha: boolean;
|
|
60
60
|
/** Whether the model must substitute $DISMISS_MESSAGE with a specific description of the fix. */
|
|
61
61
|
requiresDismissMessage: boolean;
|
|
62
|
-
/** Thread IDs that should receive a reply
|
|
62
|
+
/** Thread IDs that should receive a reply. Viewer-authored human IDs may also appear in resolveThreadIds. */
|
|
63
63
|
replyThreadIds?: string[];
|
|
64
|
-
/** Thread IDs that should be resolved on GitHub. Human
|
|
64
|
+
/** Thread IDs that should be resolved on GitHub. Human IDs are allowed only for authenticated-viewer reply+resolve pairs or marker-ended retries. */
|
|
65
65
|
resolveThreadIds?: string[];
|
|
66
66
|
/** Bot/non-human CHANGES_REQUESTED review IDs to dismiss. Human-authored IDs must not appear here. */
|
|
67
67
|
dismissReviewIds?: string[];
|
|
@@ -69,8 +69,8 @@ export interface ResolveCommand {
|
|
|
69
69
|
hasMutations: boolean;
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
|
-
* Default fix_code variant: agent applies edits locally, commits
|
|
73
|
-
* then runs the pre-built apply command. Emitted under `## Post-fix
|
|
72
|
+
* Default fix_code variant: agent applies edits locally, commits when needed,
|
|
73
|
+
* then runs the pre-built apply command. Emitted under `## Post-fix actions`.
|
|
74
74
|
*/
|
|
75
75
|
interface FixRebaseAndPush {
|
|
76
76
|
threads: AgentThread[];
|
|
@@ -90,13 +90,13 @@ interface FixRebaseAndPush {
|
|
|
90
90
|
changesRequestedReviews: Review[];
|
|
91
91
|
/** Pre-built apply command. Run after committing and pushing. */
|
|
92
92
|
resolveCommand: ResolveCommand;
|
|
93
|
-
/** When present, run this command first (no SHA substitution needed)
|
|
93
|
+
/** When present, run this command first (no SHA substitution needed) for standalone thread resolves and comment minimization, independent of any push. */
|
|
94
94
|
resolveOnlyCommand?: ResolveCommand;
|
|
95
95
|
/** Ordered steps for the model to follow. */
|
|
96
96
|
instructions: string[];
|
|
97
|
-
/**
|
|
97
|
+
/** Reserved compatibility field. Always empty because Shepherd cannot verify workflow-cancellation authorization. */
|
|
98
98
|
inProgressRunIds: string[];
|
|
99
|
-
/**
|
|
99
|
+
/** Reserved compatibility field. Always empty because Shepherd never recommends workflow cancellation. */
|
|
100
100
|
protectedRuns: ProtectedRun[];
|
|
101
101
|
/** Requeue command emitted after merge-group remediation. */
|
|
102
102
|
requeue?: MergeCommandPlan;
|
|
@@ -127,10 +127,11 @@ export type IterateResult = IterateResultWait | IterateResultCancel | IterateRes
|
|
|
127
127
|
export interface IterateCommandOptions extends GlobalOptions {
|
|
128
128
|
readyDelaySeconds?: number;
|
|
129
129
|
noAutoMarkReady?: boolean;
|
|
130
|
+
/** Legacy no-op retained for API compatibility; workflow runs are never cancelled. */
|
|
130
131
|
noAutoCancelActionable?: boolean;
|
|
131
132
|
/** Override stall timeout seconds. Defaults to config.iterate.stallTimeoutMinutes * 60. */
|
|
132
133
|
stallTimeoutSeconds?: number;
|
|
133
|
-
/**
|
|
134
|
+
/** Legacy per-invocation no-op retained for API compatibility. */
|
|
134
135
|
neverCancelRuns?: string[];
|
|
135
136
|
persistSeen?: boolean;
|
|
136
137
|
/** Shepherd through readiness and emit the exact merge/queue command when ready. */
|