pr-shepherd 0.41.0 → 0.43.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 +14 -9
- package/bin/api.d.mts +12 -1
- package/bin/api.mjs +34 -1
- package/bin/checks/classify.d.mts +3 -1
- package/bin/checks/classify.mjs +5 -2
- package/bin/cli/args.mjs +1 -0
- package/bin/cli/default-poll.mjs +1 -0
- package/bin/cli/fix-formatter.mjs +12 -2
- package/bin/cli/formatters.d.mts +2 -1
- package/bin/cli/formatters.mjs +33 -0
- package/bin/cli/handlers.d.mts +1 -0
- package/bin/cli/handlers.mjs +21 -1
- package/bin/cli/help-command-pages.d.mts +22 -6
- package/bin/cli/help-command-pages.mjs +20 -4
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +7 -2
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +3 -3
- package/bin/cli/help.d.mts +23 -7
- package/bin/cli/iterate-flags.d.mts +1 -0
- package/bin/cli/iterate-flags.mjs +2 -0
- package/bin/cli/iterate-formatter.mjs +4 -1
- package/bin/cli/iterate-instructions.mjs +15 -0
- package/bin/cli/iterate-lean.d.mts +1 -2
- package/bin/cli/iterate-lean.mjs +12 -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/poll-handler.mjs +1 -0
- package/bin/cli/runner.mjs +4 -3
- package/bin/cli/suggestion-patch-flags.d.mts +11 -0
- package/bin/cli/suggestion-patch-flags.mjs +83 -0
- package/bin/cli-parser.mjs +6 -2
- package/bin/commands/check-terminal-report.mjs +1 -0
- package/bin/commands/check.mjs +42 -1
- package/bin/commands/commit-suggestion-instruction.d.mts +1 -1
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/commit-suggestion.d.mts +1 -1
- package/bin/commands/commit-suggestion.mjs +19 -138
- package/bin/commands/iterate/base.d.mts +5 -0
- package/bin/commands/iterate/base.mjs +25 -0
- package/bin/commands/iterate/escalate.d.mts +3 -1
- package/bin/commands/iterate/escalate.mjs +18 -2
- package/bin/commands/iterate/fix-code.mjs +16 -0
- package/bin/commands/iterate/helpers.mjs +6 -2
- package/bin/commands/iterate/index.mjs +29 -35
- package/bin/commands/iterate/merge-state.d.mts +15 -0
- package/bin/commands/iterate/merge-state.mjs +53 -0
- package/bin/commands/iterate/merge.d.mts +13 -0
- package/bin/commands/iterate/merge.mjs +46 -0
- package/bin/commands/poll.mjs +3 -1
- package/bin/commands/suggestion-patch-git.d.mts +6 -0
- package/bin/commands/suggestion-patch-git.mjs +54 -0
- package/bin/commands/suggestion-patch-item.d.mts +15 -0
- package/bin/commands/suggestion-patch-item.mjs +106 -0
- package/bin/commands/suggestion-patches.d.mts +6 -0
- package/bin/commands/suggestion-patches.mjs +108 -0
- package/bin/config/load.d.mts +4 -0
- 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.mjs +23 -26
- package/bin/github/batch-raw-rules.d.mts +48 -0
- package/bin/github/batch-raw-types.mjs +0 -1
- package/bin/github/batch.mjs +2 -0
- package/bin/github/gql/batch-pr.gql +92 -0
- package/bin/github/gql/commit-check-contexts.gql +58 -0
- package/bin/github/gql/{commit-suggestion-thread.gql → suggestion-threads.gql} +2 -2
- 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 +3 -1
- package/bin/github/queries.mjs +3 -1
- package/bin/github/suggestion-thread.d.mts +3 -3
- package/bin/github/suggestion-thread.mjs +8 -5
- package/bin/mcp/server.d.mts +1 -1
- package/bin/mcp/server.mjs +24 -3
- package/bin/reporters/agent.mjs +6 -3
- package/bin/types/activity.d.mts +2 -0
- package/bin/types/escalate.d.mts +30 -0
- package/bin/types/escalate.mjs +1 -0
- package/bin/types/github.d.mts +8 -0
- package/bin/types/github.mjs +0 -3
- package/bin/types/iterate.d.mts +14 -31
- 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 +8 -19
- package/bin/types/suggestion-patch.d.mts +30 -0
- package/bin/types/suggestion-patch.mjs +1 -0
- package/bin/types.d.mts +4 -0
- package/bin/types.mjs +4 -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/pr-shepherd/SKILL.md +11 -10
|
@@ -18,10 +18,45 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
18
18
|
baseRefName
|
|
19
19
|
isInMergeQueue
|
|
20
20
|
isMergeQueueEnabled
|
|
21
|
+
autoMergeRequest {
|
|
22
|
+
enabledAt
|
|
23
|
+
mergeMethod
|
|
24
|
+
enabledBy {
|
|
25
|
+
login
|
|
26
|
+
}
|
|
27
|
+
}
|
|
21
28
|
mergeQueueEntry {
|
|
22
29
|
position
|
|
23
30
|
state
|
|
24
31
|
estimatedTimeToMerge
|
|
32
|
+
enqueuedAt
|
|
33
|
+
enqueuer {
|
|
34
|
+
login
|
|
35
|
+
}
|
|
36
|
+
headCommit {
|
|
37
|
+
...QueueCheckCommit
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
mergeQueueAdditions: timelineItems(last: 1, itemTypes: [ADDED_TO_MERGE_QUEUE_EVENT]) {
|
|
41
|
+
nodes {
|
|
42
|
+
... on AddedToMergeQueueEvent {
|
|
43
|
+
createdAt
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
mergeQueueRemovals: timelineItems(last: 1, itemTypes: [REMOVED_FROM_MERGE_QUEUE_EVENT]) {
|
|
48
|
+
nodes {
|
|
49
|
+
... on RemovedFromMergeQueueEvent {
|
|
50
|
+
reason
|
|
51
|
+
createdAt
|
|
52
|
+
actor {
|
|
53
|
+
login
|
|
54
|
+
}
|
|
55
|
+
beforeCommit {
|
|
56
|
+
...QueueCheckCommit
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
25
60
|
}
|
|
26
61
|
stack {
|
|
27
62
|
number
|
|
@@ -289,3 +324,60 @@ query BatchPr($owner: String!, $repo: String!, $pr: Int!) {
|
|
|
289
324
|
}
|
|
290
325
|
}
|
|
291
326
|
}
|
|
327
|
+
|
|
328
|
+
fragment QueueCheckCommit on Commit {
|
|
329
|
+
oid
|
|
330
|
+
committedDate
|
|
331
|
+
parents(first: 100) {
|
|
332
|
+
nodes {
|
|
333
|
+
oid
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
statusCheckRollup {
|
|
337
|
+
contexts(first: 100) {
|
|
338
|
+
pageInfo {
|
|
339
|
+
hasNextPage
|
|
340
|
+
endCursor
|
|
341
|
+
}
|
|
342
|
+
nodes {
|
|
343
|
+
__typename
|
|
344
|
+
... on CheckRun {
|
|
345
|
+
id
|
|
346
|
+
name
|
|
347
|
+
status
|
|
348
|
+
conclusion
|
|
349
|
+
detailsUrl
|
|
350
|
+
completedAt
|
|
351
|
+
startedAt
|
|
352
|
+
title
|
|
353
|
+
summary
|
|
354
|
+
annotations(first: 1) {
|
|
355
|
+
nodes {
|
|
356
|
+
message
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
checkSuite {
|
|
360
|
+
createdAt
|
|
361
|
+
updatedAt
|
|
362
|
+
workflowRun {
|
|
363
|
+
event
|
|
364
|
+
createdAt
|
|
365
|
+
updatedAt
|
|
366
|
+
workflow {
|
|
367
|
+
name
|
|
368
|
+
databaseId
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
... on StatusContext {
|
|
374
|
+
context
|
|
375
|
+
state
|
|
376
|
+
createdAt
|
|
377
|
+
targetUrl
|
|
378
|
+
description
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
query
|
|
1
|
+
query SuggestionThreads($owner: String!, $repo: String!, $pr: Int!, $threadIds: [ID!]!) {
|
|
2
2
|
repository(owner: $owner, name: $repo) {
|
|
3
3
|
pullRequest(number: $pr) {
|
|
4
4
|
headRefOid
|
|
@@ -8,7 +8,7 @@ query CommitSuggestionThread($owner: String!, $repo: String!, $pr: Int!, $thread
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
nodes(ids: $threadIds) {
|
|
12
12
|
... on PullRequestReviewThread {
|
|
13
13
|
id
|
|
14
14
|
isResolved
|
|
@@ -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
|
@@ -11,11 +11,13 @@ export declare const BATCH_PR_QUERY: string;
|
|
|
11
11
|
/** Slim @include follow-up for outstanding batch-query connections. */
|
|
12
12
|
export declare const BATCH_PR_PAGE_QUERY: string;
|
|
13
13
|
/** PR head fields plus a single review thread for `commit-suggestion`. */
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const SUGGESTION_THREADS_QUERY: string;
|
|
15
15
|
/** Fetches additional comments for a single review thread when its nested connection paginates. */
|
|
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
|
@@ -14,11 +14,13 @@ export const BATCH_PR_QUERY = gql("batch-pr.gql");
|
|
|
14
14
|
/** Slim @include follow-up for outstanding batch-query connections. */
|
|
15
15
|
export const BATCH_PR_PAGE_QUERY = gql("batch-pr-page.gql");
|
|
16
16
|
/** PR head fields plus a single review thread for `commit-suggestion`. */
|
|
17
|
-
export const
|
|
17
|
+
export const SUGGESTION_THREADS_QUERY = gql("suggestion-threads.gql");
|
|
18
18
|
/** Fetches additional comments for a single review thread when its nested connection paginates. */
|
|
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). */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ReviewThread } from "../types.mts";
|
|
2
2
|
import type { RepoInfo } from "./client.mts";
|
|
3
|
-
export interface
|
|
3
|
+
export interface SuggestionThreadsResult {
|
|
4
4
|
headRefOid: string;
|
|
5
5
|
headRefName: string;
|
|
6
6
|
headRepoWithOwner: string | null;
|
|
7
|
-
|
|
7
|
+
threads: (ReviewThread | null)[];
|
|
8
8
|
}
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function fetchSuggestionThreads(pr: number, repo: RepoInfo, threadIds: readonly string[]): Promise<SuggestionThreadsResult>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { graphql } from "./client.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { SUGGESTION_THREADS_QUERY } from "./queries.mjs";
|
|
3
3
|
import { mapAuthorType, parseCreatedAt } from "./batch-parser-helpers.mjs";
|
|
4
|
-
export async function
|
|
5
|
-
const result = await graphql(
|
|
4
|
+
export async function fetchSuggestionThreads(pr, repo, threadIds) {
|
|
5
|
+
const result = await graphql(SUGGESTION_THREADS_QUERY, {
|
|
6
6
|
owner: repo.owner,
|
|
7
7
|
repo: repo.name,
|
|
8
8
|
pr,
|
|
9
|
-
|
|
9
|
+
threadIds,
|
|
10
10
|
});
|
|
11
11
|
const pull = result.data.repository.pullRequest;
|
|
12
12
|
if (!pull) {
|
|
@@ -16,7 +16,10 @@ export async function fetchSuggestionThread(pr, repo, threadId) {
|
|
|
16
16
|
headRefOid: pull.headRefOid,
|
|
17
17
|
headRefName: pull.headRefName,
|
|
18
18
|
headRepoWithOwner: pull.headRepository?.nameWithOwner ?? null,
|
|
19
|
-
|
|
19
|
+
threads: threadIds.map((threadId) => {
|
|
20
|
+
const raw = result.data.nodes.find((candidate) => candidate?.id === threadId) ?? null;
|
|
21
|
+
return parseThread(raw, pr, threadId);
|
|
22
|
+
}),
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
function parseThread(raw, pr, threadId) {
|
package/bin/mcp/server.d.mts
CHANGED
|
@@ -4,5 +4,5 @@ export interface CreatePrShepherdMcpServerOptions extends CreatePrShepherdOption
|
|
|
4
4
|
/** Optional injection point for embedding hosts and focused tests. */
|
|
5
5
|
shepherd?: PrShepherd;
|
|
6
6
|
}
|
|
7
|
-
/** Creates a local-only MCP server with
|
|
7
|
+
/** Creates a local-only MCP server with Shepherd's public operations. */
|
|
8
8
|
export declare function createPrShepherdMcpServer(options?: CreatePrShepherdMcpServerOptions): McpServer;
|
package/bin/mcp/server.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { createPrShepherd, PartialApplyError, PrShepherdValidationError, } from "../api.mjs";
|
|
6
6
|
import { isRepositoryQualifiedPrReference } from "../pr-reference.mjs";
|
|
7
7
|
import { formatJournalResult } from "../cli/journal-formatter.mjs";
|
|
8
|
-
import { formatCommitSuggestionResult, formatIterateResult, formatMarkFilesAsViewedResult, formatMutateResult, } from "../cli/formatters.mjs";
|
|
8
|
+
import { formatCommitSuggestionResult, formatSuggestionPatchesResult, formatIterateResult, formatMarkFilesAsViewedResult, formatMutateResult, } from "../cli/formatters.mjs";
|
|
9
9
|
import { errorToExitCode, EXIT } from "../exit-codes.mjs";
|
|
10
10
|
const QUALIFIED_PR_ERROR = "pr must be a GitHub pull-request URL or an owner/repo#number reference";
|
|
11
11
|
const pr = z
|
|
@@ -18,6 +18,7 @@ const iterateInputSchema = z.object({
|
|
|
18
18
|
readyDelaySeconds: z.number().nonnegative().optional(),
|
|
19
19
|
stallTimeoutSeconds: z.number().nonnegative().optional(),
|
|
20
20
|
noAutoMarkReady: z.boolean().optional(),
|
|
21
|
+
merge: z.boolean().optional(),
|
|
21
22
|
noAutoCancelActionable: z.boolean().optional(),
|
|
22
23
|
neverCancelRuns: z.array(z.string()).optional(),
|
|
23
24
|
});
|
|
@@ -57,7 +58,17 @@ const suggestionPatchInputSchema = z.object({
|
|
|
57
58
|
message: z.string().min(1),
|
|
58
59
|
description: z.string().optional(),
|
|
59
60
|
});
|
|
60
|
-
|
|
61
|
+
const suggestionPatchesInputSchema = z.object({
|
|
62
|
+
pr,
|
|
63
|
+
suggestions: z
|
|
64
|
+
.array(z.object({
|
|
65
|
+
threadId: z.string().min(1),
|
|
66
|
+
message: z.string().min(1),
|
|
67
|
+
description: z.string().optional(),
|
|
68
|
+
}))
|
|
69
|
+
.min(1),
|
|
70
|
+
});
|
|
71
|
+
/** Creates a local-only MCP server with Shepherd's public operations. */
|
|
61
72
|
export function createPrShepherdMcpServer(options = {}) {
|
|
62
73
|
const shepherd = options.shepherd ?? createPrShepherd({ cwd: options.cwd });
|
|
63
74
|
const server = new McpServer({ name: "pr-shepherd", version: readPackageVersion() });
|
|
@@ -81,8 +92,18 @@ export function createPrShepherdMcpServer(options = {}) {
|
|
|
81
92
|
openWorldHint: true,
|
|
82
93
|
},
|
|
83
94
|
}, async (input) => runTool(() => shepherd.apply(requireRepositoryQualifiedPr(input)), formatApplyResult));
|
|
95
|
+
server.registerTool("build_suggestion_patches", {
|
|
96
|
+
description: "Build, but never apply, an ordered list of eligible review suggestion patches.",
|
|
97
|
+
inputSchema: suggestionPatchesInputSchema,
|
|
98
|
+
annotations: {
|
|
99
|
+
readOnlyHint: true,
|
|
100
|
+
destructiveHint: false,
|
|
101
|
+
idempotentHint: true,
|
|
102
|
+
openWorldHint: true,
|
|
103
|
+
},
|
|
104
|
+
}, async (input) => runTool(() => shepherd.buildSuggestionPatches(requireRepositoryQualifiedPr(input)), formatSuggestionPatchesResult));
|
|
84
105
|
server.registerTool("build_suggestion_patch", {
|
|
85
|
-
description: "
|
|
106
|
+
description: "Deprecated: use build_suggestion_patches with a one-item suggestions array.",
|
|
86
107
|
inputSchema: suggestionPatchInputSchema,
|
|
87
108
|
annotations: {
|
|
88
109
|
readOnlyHint: true,
|
package/bin/reporters/agent.mjs
CHANGED
|
@@ -61,6 +61,8 @@ export function toAgentCheck(c) {
|
|
|
61
61
|
...(c.summary !== undefined && { summary: c.summary }),
|
|
62
62
|
...(c.logExcerpt !== undefined && { logExcerpt: c.logExcerpt }),
|
|
63
63
|
...(c.annotations !== undefined && { annotations: c.annotations }),
|
|
64
|
+
...(c.scope !== undefined && { scope: c.scope }),
|
|
65
|
+
...(c.commitOid !== undefined && { commitOid: c.commitOid }),
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
export function toAgentStalledCheck(c, nowSeconds) {
|
|
@@ -85,13 +87,14 @@ export function toAgentStalledCheck(c, nowSeconds) {
|
|
|
85
87
|
* log tail, so they are all kept.
|
|
86
88
|
*/
|
|
87
89
|
export function toAgentChecks(checks) {
|
|
88
|
-
const
|
|
90
|
+
const seenKeys = new Set();
|
|
89
91
|
const result = [];
|
|
90
92
|
for (const c of checks) {
|
|
91
93
|
if (c.runId === null) {
|
|
92
|
-
|
|
94
|
+
const key = `${c.scope ?? "pr"}:${c.commitOid ?? "head"}:${c.name}`;
|
|
95
|
+
if (seenKeys.has(key))
|
|
93
96
|
continue;
|
|
94
|
-
|
|
97
|
+
seenKeys.add(key);
|
|
95
98
|
}
|
|
96
99
|
result.push(toAgentCheck(c));
|
|
97
100
|
}
|
package/bin/types/activity.d.mts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
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" | "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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/bin/types/github.d.mts
CHANGED
|
@@ -19,6 +19,10 @@ export interface CheckRun {
|
|
|
19
19
|
detailsUrl: string;
|
|
20
20
|
event: string | null;
|
|
21
21
|
runId: string | null;
|
|
22
|
+
/** Commit scope that supplied this check. Omitted for ordinary PR-head checks. */
|
|
23
|
+
scope?: "merge_group";
|
|
24
|
+
/** Synthetic merge-group commit OID, when `scope` is `merge_group`. */
|
|
25
|
+
commitOid?: string;
|
|
22
26
|
createdAtUnix?: number;
|
|
23
27
|
startedAtUnix?: number;
|
|
24
28
|
completedAtUnix?: number;
|
|
@@ -133,6 +137,10 @@ export interface BatchPrData extends BatchPrMergeFields {
|
|
|
133
137
|
reviewSummaries: Review[];
|
|
134
138
|
approvedReviews: Review[];
|
|
135
139
|
checks: CheckRun[];
|
|
140
|
+
mergeQueueChecks?: CheckRun[];
|
|
141
|
+
mergeQueueChecksIncomplete?: true;
|
|
142
|
+
removedMergeQueueChecks?: CheckRun[];
|
|
143
|
+
removedMergeQueueChecksIncomplete?: true;
|
|
136
144
|
branchProtection: BranchProtection | null;
|
|
137
145
|
activity?: PrActivitySummary;
|
|
138
146
|
}
|
package/bin/types/github.mjs
CHANGED
package/bin/types/iterate.d.mts
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
1
|
import type { AgentThread, AgentComment, AgentCheck, GlobalOptions, RelevantCheck, ShepherdStatus, FirstLookThread, FirstLookComment } from "./report.mts";
|
|
2
2
|
import type { ActiveCheck, PrActivitySummary } from "./activity.mts";
|
|
3
|
-
import type { BranchProtection,
|
|
3
|
+
import type { BranchProtection, MergeStateStatus, Review, ReviewDecision, ReviewThread, ShepherdMergeStatus } from "./github.mts";
|
|
4
4
|
import type { MergeRequirements } from "./merge-requirements.mts";
|
|
5
|
+
import type { EscalateDetails } from "./escalate.mts";
|
|
6
|
+
import type { MergeCommandPlan } from "./merge-action.mts";
|
|
5
7
|
import type { ProtectedRun } from "./protected-run.mts";
|
|
6
|
-
export type ShepherdAction = "wait" | "fix_code" | "mark_ready" | "cancel" | "escalate";
|
|
7
|
-
export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "thread-missing-location" | "bot-cr-not-dismissed";
|
|
8
|
-
export interface AgentStalledCheck {
|
|
9
|
-
name: string;
|
|
10
|
-
status: CheckStatus;
|
|
11
|
-
source: "check_run" | "status_context" | "startup_failure";
|
|
12
|
-
runId: string | null;
|
|
13
|
-
detailsUrl: string | null;
|
|
14
|
-
createdAtUnix?: number;
|
|
15
|
-
startedAtUnix?: number;
|
|
16
|
-
updatedAtUnix?: number;
|
|
17
|
-
ageSeconds: number;
|
|
18
|
-
summary?: string;
|
|
19
|
-
}
|
|
20
|
-
export interface EscalateDetails {
|
|
21
|
-
triggers: EscalateTrigger[];
|
|
22
|
-
unresolvedThreads: AgentThread[];
|
|
23
|
-
ambiguousComments: AgentComment[];
|
|
24
|
-
changesRequestedReviews: Review[];
|
|
25
|
-
/** Pending/unstarted CI checks that exceeded the stall timeout. */
|
|
26
|
-
stalledChecks?: AgentStalledCheck[];
|
|
27
|
-
/** Populated when fix-thrash triggered — threads that have been attempted too many times. */
|
|
28
|
-
thrashHistory?: Array<{
|
|
29
|
-
threadId: string;
|
|
30
|
-
attempts: number;
|
|
31
|
-
}>;
|
|
32
|
-
suggestion: string;
|
|
33
|
-
humanMessage: string;
|
|
34
|
-
}
|
|
8
|
+
export type ShepherdAction = "wait" | "fix_code" | "mark_ready" | "merge" | "cancel" | "escalate";
|
|
35
9
|
export interface IterateResultSummary {
|
|
36
10
|
passing: number;
|
|
37
11
|
skipped: number;
|
|
@@ -66,6 +40,7 @@ export interface IterateResultBase {
|
|
|
66
40
|
ignoredNames?: string[];
|
|
67
41
|
supersededNames?: string[];
|
|
68
42
|
activity?: PrActivitySummary;
|
|
43
|
+
mergeQueue?: import("./merge-queue.mts").MergeQueueReport;
|
|
69
44
|
}
|
|
70
45
|
interface IterateResultWait extends IterateResultBase {
|
|
71
46
|
action: "wait";
|
|
@@ -123,6 +98,8 @@ interface FixRebaseAndPush {
|
|
|
123
98
|
inProgressRunIds: string[];
|
|
124
99
|
/** Workflow runs deliberately excluded from cancellation by actions.neverCancelRuns. */
|
|
125
100
|
protectedRuns: ProtectedRun[];
|
|
101
|
+
/** Requeue command emitted after merge-group remediation. */
|
|
102
|
+
requeue?: MergeCommandPlan;
|
|
126
103
|
/** First-look threads — previously hidden, surfaced for acknowledgment only. */
|
|
127
104
|
firstLookThreads: FirstLookThread[];
|
|
128
105
|
/** First-look comments — previously hidden, surfaced for acknowledgment only. */
|
|
@@ -138,11 +115,15 @@ interface IterateResultMarkReady extends IterateResultBase {
|
|
|
138
115
|
markedReady: boolean;
|
|
139
116
|
log: string;
|
|
140
117
|
}
|
|
118
|
+
export interface IterateResultMerge extends IterateResultBase {
|
|
119
|
+
action: "merge";
|
|
120
|
+
merge: MergeCommandPlan;
|
|
121
|
+
}
|
|
141
122
|
interface IterateResultEscalate extends IterateResultBase {
|
|
142
123
|
action: "escalate";
|
|
143
124
|
escalate: EscalateDetails;
|
|
144
125
|
}
|
|
145
|
-
export type IterateResult = IterateResultWait | IterateResultCancel | IterateResultFixCode | IterateResultMarkReady | IterateResultEscalate;
|
|
126
|
+
export type IterateResult = IterateResultWait | IterateResultCancel | IterateResultFixCode | IterateResultMarkReady | IterateResultMerge | IterateResultEscalate;
|
|
146
127
|
export interface IterateCommandOptions extends GlobalOptions {
|
|
147
128
|
readyDelaySeconds?: number;
|
|
148
129
|
noAutoMarkReady?: boolean;
|
|
@@ -152,5 +133,7 @@ export interface IterateCommandOptions extends GlobalOptions {
|
|
|
152
133
|
/** Case-insensitive workflow/check glob patterns Shepherd must not cancel. */
|
|
153
134
|
neverCancelRuns?: string[];
|
|
154
135
|
persistSeen?: boolean;
|
|
136
|
+
/** Shepherd through readiness and emit the exact merge/queue command when ready. */
|
|
137
|
+
merge?: boolean;
|
|
155
138
|
}
|
|
156
139
|
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface ShellCommand {
|
|
2
|
+
argv: string[];
|
|
3
|
+
}
|
|
4
|
+
export interface MergeCommandPlan {
|
|
5
|
+
mode: "auto" | "queue";
|
|
6
|
+
command: ShellCommand;
|
|
7
|
+
/** Ordinary merge retry without `--auto`, used only when auto-merge is unavailable. */
|
|
8
|
+
fallbackCommand?: ShellCommand;
|
|
9
|
+
/** Direct enqueuePullRequest fallback for the known gh CLI queue limitation. */
|
|
10
|
+
queueApiFallbackCommand?: ShellCommand;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AutoMergeRequestStatus, MergeQueueEntryStatus, MergeQueueRemovalStatus } from "./merge-requirements.mts";
|
|
2
|
+
export interface MergeQueueReport {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
inQueue: boolean;
|
|
5
|
+
autoMergeRequest?: AutoMergeRequestStatus;
|
|
6
|
+
entry?: MergeQueueEntryStatus;
|
|
7
|
+
latestRemoval?: MergeQueueRemovalStatus;
|
|
8
|
+
checkCommitOid?: string;
|
|
9
|
+
/** GitHub reported more than the first 100 contexts on the queue commit. */
|
|
10
|
+
checksIncomplete?: true;
|
|
11
|
+
/** The current PR head is not a parent of the removed synthetic queue commit. */
|
|
12
|
+
headUpdatedAfterRemoval?: true;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,6 +17,21 @@ export interface MergeQueueEntryStatus {
|
|
|
17
17
|
position: number;
|
|
18
18
|
state: string;
|
|
19
19
|
estimatedTimeToMerge: number | null;
|
|
20
|
+
headCommitOid?: string;
|
|
21
|
+
enqueuedAtUnix?: number;
|
|
22
|
+
enqueuer?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AutoMergeRequestStatus {
|
|
25
|
+
enabledAtUnix: number;
|
|
26
|
+
mergeMethod: string;
|
|
27
|
+
enabledBy?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface MergeQueueRemovalStatus {
|
|
30
|
+
reason: string | null;
|
|
31
|
+
actor?: string;
|
|
32
|
+
createdAtUnix: number;
|
|
33
|
+
beforeCommitOid?: string;
|
|
34
|
+
beforeCommitParentOids?: string[];
|
|
20
35
|
}
|
|
21
36
|
export interface StackStatus {
|
|
22
37
|
number: number;
|
|
@@ -30,6 +45,8 @@ export interface BatchPrMergeFields {
|
|
|
30
45
|
isInMergeQueue?: boolean;
|
|
31
46
|
isMergeQueueEnabled?: boolean;
|
|
32
47
|
mergeQueueEntry?: MergeQueueEntryStatus | null;
|
|
48
|
+
autoMergeRequest?: AutoMergeRequestStatus | null;
|
|
49
|
+
latestMergeQueueRemoval?: MergeQueueRemovalStatus | null;
|
|
33
50
|
stack?: StackStatus | null;
|
|
34
51
|
}
|
|
35
52
|
/** Snapshot of "why can't I merge" requirements vs current PR state. */
|