pr-shepherd 0.43.0 → 0.44.1
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/exit-codes.d.mts +1 -1
- package/bin/exit-codes.mjs +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/errors.d.mts +6 -0
- package/bin/github/errors.mjs +27 -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/graphql-http.mjs +3 -2
- package/bin/github/graphql-internal-retry.d.mts +2 -0
- package/bin/github/graphql-internal-retry.mjs +50 -0
- package/bin/github/graphql-response.mjs +11 -1
- 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
|
@@ -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. */
|
package/bin/types/report.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthorType, ReviewThread, PrComment, Review, MergeStatusResult, CheckConclusion, SuggestionBlock } from "./github.mts";
|
|
1
|
+
import type { AuthorType, ReviewThread, PrComment, Review, MergeStatusResult, ViewerAuthorization, CheckConclusion, SuggestionBlock } from "./github.mts";
|
|
2
2
|
import type { ClassifiedCheck, TriagedCheck } from "./check-classification.mts";
|
|
3
3
|
import type { AgentThreadComment } from "./agent-thread.mts";
|
|
4
4
|
import type { CheckAnnotation } from "./check-annotations.mts";
|
|
@@ -24,6 +24,8 @@ export interface ShepherdReport {
|
|
|
24
24
|
/** GitHub PR head OID from the same batch used to decide the action. */
|
|
25
25
|
headSha?: string;
|
|
26
26
|
repo: string;
|
|
27
|
+
/** Raw GitHub viewer fields used to decide which remote actions may be offered. */
|
|
28
|
+
viewerAuthorization?: ViewerAuthorization;
|
|
27
29
|
status: ShepherdStatus;
|
|
28
30
|
/** PR base branch from the GraphQL batch. */
|
|
29
31
|
baseBranch: string;
|
|
@@ -87,6 +89,10 @@ export interface ResolveOptions {
|
|
|
87
89
|
}
|
|
88
90
|
export interface AgentThread {
|
|
89
91
|
id: string;
|
|
92
|
+
/** Raw GitHub capability; omitted only in legacy/incomplete snapshots. */
|
|
93
|
+
viewerCanReply?: boolean;
|
|
94
|
+
/** Raw GitHub capability; omitted only in legacy/incomplete snapshots. */
|
|
95
|
+
viewerCanResolve?: boolean;
|
|
90
96
|
reviewId?: string;
|
|
91
97
|
path: string | null;
|
|
92
98
|
line: number | null;
|
|
@@ -94,6 +100,7 @@ export interface AgentThread {
|
|
|
94
100
|
author: string;
|
|
95
101
|
authorType?: AuthorType;
|
|
96
102
|
authorAssociation?: import("./github.mts").CommentAuthorAssociation;
|
|
103
|
+
viewerDidAuthor?: true;
|
|
97
104
|
body: string;
|
|
98
105
|
url: string;
|
|
99
106
|
comments?: AgentThreadComment[];
|
|
@@ -103,6 +110,8 @@ export interface AgentThread {
|
|
|
103
110
|
/** Comment shape emitted to the iterate agent — stripped of always-false flags. */
|
|
104
111
|
export interface AgentComment {
|
|
105
112
|
id: string;
|
|
113
|
+
/** Raw GitHub capability; omitted only in legacy/incomplete snapshots. */
|
|
114
|
+
viewerCanMinimize?: boolean;
|
|
106
115
|
author: string;
|
|
107
116
|
authorType?: AuthorType;
|
|
108
117
|
authorAssociation?: import("./github.mts").CommentAuthorAssociation;
|
|
@@ -160,6 +169,10 @@ export interface RelevantCheck {
|
|
|
160
169
|
}
|
|
161
170
|
export interface GlobalOptions {
|
|
162
171
|
prNumber?: number;
|
|
172
|
+
targetRepository?: {
|
|
173
|
+
owner: string;
|
|
174
|
+
name: string;
|
|
175
|
+
};
|
|
163
176
|
format: "text" | "json";
|
|
164
177
|
verbose?: boolean;
|
|
165
178
|
}
|
|
@@ -18,7 +18,7 @@ export interface BuildSuggestionPatchesResult {
|
|
|
18
18
|
repo: string;
|
|
19
19
|
/** Suggestion patches in caller-supplied application order. */
|
|
20
20
|
patches: SuggestionPatchResult[];
|
|
21
|
-
/** Steps for applying and committing every patch
|
|
21
|
+
/** Steps for applying and committing every patch; branch updates require separate authorization. */
|
|
22
22
|
postActionInstructions: string[];
|
|
23
23
|
}
|
|
24
24
|
/** Compatibility result for the singular suggestion-patch adapter. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mark-files-as-viewed
|
|
3
|
-
description: "
|
|
3
|
+
description: "Select changed pull-request files and report viewed-state authorization diagnostics with pr-shepherd (MCP or CLI)."
|
|
4
4
|
user-invocable: true
|
|
5
5
|
argument-hint: "[PR number or URL] [files|tests|--tests|--match REGEX]"
|
|
6
6
|
allowed-tools: ["MCP", "Bash", "Read", "Grep", "Glob"]
|
|
@@ -8,10 +8,10 @@ allowed-tools: ["MCP", "Bash", "Read", "Grep", "Glob"]
|
|
|
8
8
|
|
|
9
9
|
# mark-files-as-viewed
|
|
10
10
|
|
|
11
|
-
Thin dispatcher for marking PR
|
|
11
|
+
Thin dispatcher for selection-only file-view authorization diagnostics. GitHub exposes no exact viewer capability for marking a PR file viewed, so this operation never recommends or attempts that mutation. Use the MCP server when it is available; otherwise use the CLI.
|
|
12
12
|
|
|
13
13
|
## Arguments: $ARGUMENTS
|
|
14
14
|
|
|
15
15
|
1. Parse an optional PR number, repository-qualified `owner/repo#N`, or GitHub PR URL. Treat standalone `tests` as `--tests`; preserve explicit paths and `--match <regex>` selectors.
|
|
16
16
|
|
|
17
|
-
2. If the `apply` MCP tool is available, first obtain a repository-qualified reference: use a supplied GitHub PR URL or `owner/repo#N` unchanged; for a bare number, run `gh pr view <number> --json url --jq .url`; when omitted, run `gh pr view --json url --jq .url`. If that does not produce one qualified PR reference, stop and report that MCP cannot safely determine the PR. Otherwise call `apply` with that qualified reference and one `mark_files_viewed` operation, then print the full result. If MCP is unavailable
|
|
17
|
+
2. If the `apply` MCP tool is available, first obtain a repository-qualified reference: use a supplied GitHub PR URL or `owner/repo#N` unchanged; for a bare number, run `gh pr view <number> --json url --jq .url`; when omitted, run `gh pr view --json url --jq .url`. If that does not produce one qualified PR reference, stop and report that MCP cannot safely determine the PR. Otherwise call `apply` with that qualified reference and one `mark_files_viewed` operation, then print the full result. If MCP is unavailable, convert a supplied `owner/repo#N` to `https://github.com/owner/repo/pull/N` and otherwise pass the parsed PR unchanged to `pr-shepherd apply files` with the selectors, then print the full result. A qualified reference may target a fork or upstream repository; the current checkout remains the local git/config/rules context.
|
|
@@ -14,7 +14,7 @@ Thin dispatcher for iterating a PR. Poll with the CLI; use MCP `iterate` only wh
|
|
|
14
14
|
|
|
15
15
|
1. Parse an optional PR number, repository-qualified `owner/repo#N`, or GitHub PR URL and an optional `--merge` flag from `$ARGUMENTS`; otherwise let pr-shepherd infer the current branch PR. Reject any remaining argument. Follow the target repository's local `AGENTS.md` and `CLAUDE.md` standards while making changes.
|
|
16
16
|
|
|
17
|
-
2.
|
|
17
|
+
2. For the CLI, convert supplied `owner/repo#N` to `https://github.com/owner/repo/pull/N`; otherwise pass the supplied URL or bare number unchanged, then run the poll command `pr-shepherd` with the optional PR argument and forward `--merge` when supplied. A qualified reference may name a fork or upstream repository: it is the GitHub target, while the current checkout continues to supply local git/config/rules context. Do not run `pr-shepherd iterate`. If the CLI is unavailable and the `iterate` MCP tool is available, first obtain a repository-qualified reference: use a supplied GitHub PR URL or `owner/repo#N` unchanged; for a bare number, run `gh pr view <number> --json url --jq .url`; when omitted, run `gh pr view --json url --jq .url`. If that does not produce one qualified PR reference, stop and report that MCP cannot safely determine the PR. Otherwise call `iterate` with that qualified reference, plus `merge: true` when `--merge` was supplied, and print its full result.
|
|
18
18
|
|
|
19
19
|
3. Print the full result and follow every returned `## Instructions` step exactly. For CLI output, run each printed mutation command when instructed. For MCP output, use MCP `apply` and `build_suggestion_patches` with the same qualified PR reference; do not run a shell `pr-shepherd apply` command.
|
|
20
20
|
|
|
@@ -28,11 +28,11 @@ mechanics every tick. Apply the referenced playbook in full whenever a step poin
|
|
|
28
28
|
### Suggestion patches
|
|
29
29
|
|
|
30
30
|
- Run one plural `build-suggestion-patches` command with a repeated `--thread-id … --message … [--description …]` group for every marked thread in displayed order.
|
|
31
|
-
- The CLI only builds patches. Apply, stage, and commit the returned patches in order
|
|
31
|
+
- The CLI only builds patches. Apply, stage, and commit the returned patches in order. Push only when authorization has been established outside Shepherd; GitHub viewer fields cannot verify the local Git credential.
|
|
32
32
|
- The command builds from the fetched PR head and accepts a clean local descendant only when the complete ordered patch stream passes `git apply --check`.
|
|
33
33
|
- If the command refuses because a suggestion is unsafe or no longer applies, inspect the current source, the displayed replacement block, and reviewer intent before editing manually. Do not apply a stale numeric range blindly or retry unchanged input.
|
|
34
34
|
- A returned patch was checked against the then-current worktree. If it later fails, re-inspect the worktree because it changed after validation.
|
|
35
|
-
- Keep
|
|
35
|
+
- Keep the generated thread IDs and flag placement unchanged. Viewer-authored human feedback may intentionally appear in both reply and resolve flags; unmarked other-human feedback remains reply-only. Marker-ended other-human feedback is already acknowledged and has no generated mutation.
|
|
36
36
|
|
|
37
37
|
### CI failure triage
|
|
38
38
|
|
|
@@ -40,18 +40,20 @@ Match each failure's `[conclusion: …]` tag under `## Failing checks` to a rule
|
|
|
40
40
|
|
|
41
41
|
More specific rows win over the general "GitHub Actions failure" row — check conclusion first.
|
|
42
42
|
|
|
43
|
-
| Tag / kind | Do
|
|
44
|
-
| ------------------------------------------------------------------------ |
|
|
45
|
-
| GitHub Actions failure (has a run ID, not `CANCELLED`/`STARTUP_FAILURE`) | Read the included log excerpt if one is rendered. If missing or insufficient,
|
|
46
|
-
| Transient infrastructure failure |
|
|
47
|
-
| Real test or build failure | Apply a code fix — do not rerun.
|
|
48
|
-
| `[conclusion: CANCELLED]` | No log excerpt is rendered
|
|
49
|
-
| `[conclusion: STARTUP_FAILURE]` | No log excerpt is rendered
|
|
50
|
-
| `external` (no run ID, has a URL) |
|
|
43
|
+
| Tag / kind | Do |
|
|
44
|
+
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| GitHub Actions failure (has a run ID, not `CANCELLED`/`STARTUP_FAILURE`) | Read the included log excerpt if one is rendered. If it is missing or insufficient, hand off the displayed run ID/URL; Shepherd cannot verify authorization for another Actions read or mutation. |
|
|
46
|
+
| Transient infrastructure failure | Record the diagnosis. Shepherd does not recommend a rerun because GitHub exposes no exact viewer capability for that workflow-run action. |
|
|
47
|
+
| Real test or build failure | Apply a code fix — do not rerun. |
|
|
48
|
+
| `[conclusion: CANCELLED]` | No log excerpt is rendered. Hand off the displayed metadata; Shepherd cannot verify authorization for another Actions read or mutation. |
|
|
49
|
+
| `[conclusion: STARTUP_FAILURE]` | No log excerpt is rendered. Hand off the displayed metadata; Shepherd cannot verify authorization for another Actions read or mutation. |
|
|
50
|
+
| `external` (no run ID, has a URL) | Preserve the URL in the handoff. Shepherd does not recommend opening it because it cannot verify the current viewer's access to the external system. |
|
|
51
51
|
|
|
52
52
|
### Review-mutation mechanics
|
|
53
53
|
|
|
54
|
-
Applies to every `apply review:` / `resolve-only:` command the CLI prints. Covers only what stays safe if you run the printed command **unmodified** — `$HEAD_SHA`/`$DISMISS_MESSAGE` substitution
|
|
54
|
+
Applies to every `apply review:` / `resolve-only:` command the CLI prints. Covers only what stays safe if you run the printed command **unmodified** — `$HEAD_SHA`/`$DISMISS_MESSAGE` substitution remains a separate CLI-printed step because the command is unsafe by default without those placeholders.
|
|
55
|
+
|
|
56
|
+
The CLI only includes IDs whose per-object GitHub viewer capability authorizes the corresponding action, and `apply review` repeats that authorization check immediately before mutating.
|
|
55
57
|
|
|
56
58
|
- Run every generated `apply review:` / `resolve-only:` command even when no code change is warranted. The command records the agent's disposition of the included review items; skipping it leaves bot threads active and can eventually trigger `fix-thrash`.
|
|
57
59
|
- Never add first-look-only or check-annotation IDs to `--reply-thread-ids`, `--resolve-thread-ids`, `--dismiss-review-ids`, or `--minimize-comment-ids` — those flags are pre-populated by the CLI.
|
|
@@ -59,7 +61,7 @@ Applies to every `apply review:` / `resolve-only:` command the CLI prints. Cover
|
|
|
59
61
|
|
|
60
62
|
### Review-mutation routing
|
|
61
63
|
|
|
62
|
-
For threads under both `## Review threads` and `## Review threads to resolve`, evaluate every thread before running mutations. Keep bot and non-human IDs in `--resolve-thread-ids`, including when the feedback is advisory, already satisfied, or otherwise warrants no code change.
|
|
64
|
+
For threads under both `## Review threads` and `## Review threads to resolve`, evaluate every thread before running mutations. Keep bot and non-human IDs in `--resolve-thread-ids`, including when the feedback is advisory, already satisfied, or otherwise warrants no code change. Unmarked other-human inline-thread IDs use `--reply-thread-ids` only. When the original human inline comment has `viewerDidAuthor: true` and its latest comment is unmarked, keep that same ID in both `--reply-thread-ids` and `--resolve-thread-ids`: the reply runs before the resolve. When the latest comment begins `<!-- pr-shepherd -->`, it is an established Shepherd reply—not merely a same-account comment. A marker-ended viewer-authored thread may be resolve-only for retry; a marker-ended other-human thread is already acknowledged and has no generated mutation. Do not add an unmarked human ID to `--resolve-thread-ids` without its paired generated reply, and do not move IDs between flags.
|
|
63
65
|
|
|
64
66
|
### Shepherd Journal
|
|
65
67
|
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ShepherdReport } from "../../types.mts";
|
|
2
|
-
import type { ProtectedRun } from "../../types/protected-run.mts";
|
|
3
|
-
type FailingCheck = ShepherdReport["checks"]["failing"][number];
|
|
4
|
-
type InProgressCheck = ShepherdReport["checks"]["inProgress"][number];
|
|
5
|
-
type CancellableCheck = FailingCheck | InProgressCheck;
|
|
6
|
-
interface RunProtection {
|
|
7
|
-
protectedRunIds: Set<string>;
|
|
8
|
-
protectedRuns: ProtectedRun[];
|
|
9
|
-
}
|
|
10
|
-
interface BuildRunIdOptions {
|
|
11
|
-
protectedRunIds?: Set<string>;
|
|
12
|
-
}
|
|
13
|
-
export declare function buildAutoCancelRunIdsWithOptions(report: ShepherdReport, opts?: BuildRunIdOptions): string[];
|
|
14
|
-
interface BuildInProgressRunIdOptions {
|
|
15
|
-
suppressProtectedFreshReruns?: boolean;
|
|
16
|
-
protectedRunIds?: Set<string>;
|
|
17
|
-
}
|
|
18
|
-
export declare function buildInProgressRunIds(report: ShepherdReport, cancelledSet: Set<string>, opts?: BuildInProgressRunIdOptions): string[];
|
|
19
|
-
export declare function buildRunProtection(checks: CancellableCheck[], patterns?: string[]): RunProtection;
|
|
20
|
-
export {};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import picomatch from "picomatch";
|
|
2
|
-
function matchesRerunCheck(failure, check) {
|
|
3
|
-
if (failure.runId !== null && check.runId !== null) {
|
|
4
|
-
return failure.runId === check.runId && failure.name === check.name;
|
|
5
|
-
}
|
|
6
|
-
return failure.runId === null && check.runId === null && failure.name === check.name;
|
|
7
|
-
}
|
|
8
|
-
function isProtectedByFreshRerun(failure, check) {
|
|
9
|
-
const attemptStartedAt = check.startedAtUnix ?? check.updatedAtUnix ?? check.createdAtUnix;
|
|
10
|
-
return (attemptStartedAt !== undefined &&
|
|
11
|
-
matchesRerunCheck(failure, check) &&
|
|
12
|
-
failure.completedAtUnix !== undefined &&
|
|
13
|
-
attemptStartedAt >= failure.completedAtUnix &&
|
|
14
|
-
(failure.startedAtUnix === undefined || failure.startedAtUnix < attemptStartedAt));
|
|
15
|
-
}
|
|
16
|
-
function hasProtectedFreshRerun(failure, checks) {
|
|
17
|
-
return checks.some((check) => isProtectedByFreshRerun(failure, check));
|
|
18
|
-
}
|
|
19
|
-
function isProtectedFreshRerun(check, failures) {
|
|
20
|
-
const matchingFailures = failures.filter((failure) => matchesRerunCheck(failure, check));
|
|
21
|
-
return (matchingFailures.length > 0 &&
|
|
22
|
-
matchingFailures.every((failure) => isProtectedByFreshRerun(failure, check)));
|
|
23
|
-
}
|
|
24
|
-
function protectedFreshRerunIds(report) {
|
|
25
|
-
return new Set(report.checks.inProgress
|
|
26
|
-
.filter((check) => isProtectedFreshRerun(check, report.checks.failing))
|
|
27
|
-
.map((check) => check.runId)
|
|
28
|
-
.filter((id) => id !== null));
|
|
29
|
-
}
|
|
30
|
-
export function buildAutoCancelRunIdsWithOptions(report, opts = {}) {
|
|
31
|
-
return [
|
|
32
|
-
...new Set(report.checks.failing
|
|
33
|
-
.filter((check) => !hasProtectedFreshRerun(check, report.checks.inProgress))
|
|
34
|
-
.map((check) => check.runId)
|
|
35
|
-
.filter((id) => id !== null && !(opts.protectedRunIds?.has(id) ?? false))),
|
|
36
|
-
];
|
|
37
|
-
}
|
|
38
|
-
export function buildInProgressRunIds(report, cancelledSet, opts = {}) {
|
|
39
|
-
const protectedRunIds = opts.suppressProtectedFreshReruns === false ? new Set() : protectedFreshRerunIds(report);
|
|
40
|
-
return [
|
|
41
|
-
...new Set(report.checks.inProgress
|
|
42
|
-
.map((check) => check.runId)
|
|
43
|
-
.filter((id) => id !== null &&
|
|
44
|
-
!cancelledSet.has(id) &&
|
|
45
|
-
!protectedRunIds.has(id) &&
|
|
46
|
-
!(opts.protectedRunIds?.has(id) ?? false))),
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
|
-
export function buildRunProtection(checks, patterns = []) {
|
|
50
|
-
if (patterns.length === 0)
|
|
51
|
-
return { protectedRunIds: new Set(), protectedRuns: [] };
|
|
52
|
-
const matchers = patterns.map((pattern) => ({
|
|
53
|
-
pattern,
|
|
54
|
-
isMatch: picomatch(pattern, { nocase: true }),
|
|
55
|
-
}));
|
|
56
|
-
const byRunId = new Map();
|
|
57
|
-
for (const check of checks) {
|
|
58
|
-
if (check.runId === null)
|
|
59
|
-
continue;
|
|
60
|
-
const match = findProtectionMatch(check, matchers);
|
|
61
|
-
if (match === null)
|
|
62
|
-
continue;
|
|
63
|
-
addProtectedRun(byRunId, check.runId, check, match);
|
|
64
|
-
}
|
|
65
|
-
const protectedRuns = [...byRunId.values()];
|
|
66
|
-
return {
|
|
67
|
-
protectedRunIds: new Set(protectedRuns.map((run) => run.runId)),
|
|
68
|
-
protectedRuns,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function addProtectedRun(byRunId, runId, check, matchedPattern) {
|
|
72
|
-
const existing = byRunId.get(runId);
|
|
73
|
-
if (existing) {
|
|
74
|
-
updateProtectedRun(existing, check);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
byRunId.set(runId, createProtectedRun(runId, check, matchedPattern));
|
|
78
|
-
}
|
|
79
|
-
function updateProtectedRun(run, check) {
|
|
80
|
-
if (!run.checkNames.includes(check.name))
|
|
81
|
-
run.checkNames.push(check.name);
|
|
82
|
-
if (run.workflowName === undefined && check.workflowName !== undefined) {
|
|
83
|
-
run.workflowName = check.workflowName;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function createProtectedRun(runId, check, matchedPattern) {
|
|
87
|
-
return {
|
|
88
|
-
runId,
|
|
89
|
-
matchedPattern,
|
|
90
|
-
checkNames: [check.name],
|
|
91
|
-
...(check.workflowName !== undefined && { workflowName: check.workflowName }),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function findProtectionMatch(check, matchers) {
|
|
95
|
-
const candidates = [
|
|
96
|
-
check.workflowName,
|
|
97
|
-
"jobName" in check ? check.jobName : undefined,
|
|
98
|
-
check.name,
|
|
99
|
-
].filter((value) => value !== undefined && value.trim() !== "");
|
|
100
|
-
for (const matcher of matchers) {
|
|
101
|
-
if (candidates.some((candidate) => matcher.isMatch(candidate)))
|
|
102
|
-
return matcher.pattern;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|