pr-shepherd 0.34.0 → 0.35.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.
Files changed (187) hide show
  1. package/.claude-plugin/plugin.json +3 -2
  2. package/.grok-plugin/marketplace.json +17 -0
  3. package/README.md +56 -39
  4. package/bin/api.d.mts +80 -0
  5. package/bin/api.mjs +236 -0
  6. package/bin/checks/classify.d.mts +41 -0
  7. package/bin/checks/startup-failures.d.mts +2 -0
  8. package/bin/checks/superseded.d.mts +21 -0
  9. package/bin/checks/triage.d.mts +4 -0
  10. package/bin/classify/apply.d.mts +18 -0
  11. package/bin/classify/apply.mjs +4 -0
  12. package/bin/classify/loader.d.mts +10 -0
  13. package/bin/classify/types.d.mts +35 -0
  14. package/bin/cli/args.d.mts +18 -0
  15. package/bin/cli/clean-formatter.d.mts +2 -0
  16. package/bin/cli/default-poll.d.mts +2 -0
  17. package/bin/cli/duration-flag.d.mts +2 -0
  18. package/bin/cli/duration.d.mts +13 -0
  19. package/bin/cli/fence.d.mts +1 -0
  20. package/bin/cli/fix-formatter-extra.d.mts +3 -0
  21. package/bin/cli/fix-formatter.d.mts +2 -0
  22. package/bin/cli/fix-formatter.mjs +6 -6
  23. package/bin/cli/formatters.d.mts +7 -0
  24. package/bin/cli/handlers.d.mts +4 -0
  25. package/bin/cli/handlers.mjs +11 -10
  26. package/bin/cli/help-command-pages.d.mts +231 -0
  27. package/bin/cli/help-command-pages.mjs +106 -1
  28. package/bin/cli/help-iterate-poll-pages.d.mts +4 -0
  29. package/bin/cli/help-iterate-poll-pages.mjs +4 -2
  30. package/bin/cli/help-log-file-page.d.mts +1 -0
  31. package/bin/cli/help-top-page.d.mts +1 -0
  32. package/bin/cli/help-top-page.mjs +14 -15
  33. package/bin/cli/help.d.mts +236 -0
  34. package/bin/cli/help.mjs +17 -0
  35. package/bin/cli/iterate-emitter.d.mts +8 -0
  36. package/bin/cli/iterate-flags.d.mts +11 -0
  37. package/bin/cli/iterate-formatter.d.mts +19 -0
  38. package/bin/cli/iterate-instructions.d.mts +6 -0
  39. package/bin/cli/iterate-lean.d.mts +12 -0
  40. package/bin/cli/journal-formatter.d.mts +2 -0
  41. package/bin/cli/journal-formatter.mjs +15 -0
  42. package/bin/cli/journal-handler.d.mts +1 -0
  43. package/bin/cli/journal-handler.mjs +7 -20
  44. package/bin/cli/list-formatters.d.mts +76 -0
  45. package/bin/cli/list-formatters.mjs +9 -9
  46. package/bin/cli/mark-files-as-viewed-flags.d.mts +11 -0
  47. package/bin/cli/mark-files-as-viewed-formatter.d.mts +2 -0
  48. package/bin/cli/mutate-formatter.d.mts +2 -0
  49. package/bin/cli/poll-handler.d.mts +1 -0
  50. package/bin/cli/poll-handler.mjs +2 -2
  51. package/bin/cli/resolve-validators.d.mts +3 -0
  52. package/bin/cli/resolve-validators.mjs +3 -3
  53. package/bin/cli/runner.d.mts +7 -0
  54. package/bin/cli/suggestion-renderer.d.mts +3 -0
  55. package/bin/cli/validate-default-args.d.mts +6 -0
  56. package/bin/cli-parser.d.mts +2 -0
  57. package/bin/cli-parser.mjs +72 -16
  58. package/bin/commands/check-annotations.d.mts +5 -0
  59. package/bin/commands/check-status.d.mts +3 -0
  60. package/bin/commands/check-terminal-report.d.mts +5 -0
  61. package/bin/commands/check.d.mts +6 -0
  62. package/bin/commands/check.mjs +2 -1
  63. package/bin/commands/clean.d.mts +21 -0
  64. package/bin/commands/commit-suggestion-instruction.d.mts +8 -0
  65. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  66. package/bin/commands/commit-suggestion.d.mts +8 -0
  67. package/bin/commands/commit-suggestion.mjs +11 -10
  68. package/bin/commands/iterate/check-instructions.d.mts +16 -0
  69. package/bin/commands/iterate/check-instructions.mjs +3 -3
  70. package/bin/commands/iterate/classify.d.mts +18 -0
  71. package/bin/commands/iterate/classify.mjs +4 -4
  72. package/bin/commands/iterate/escalate.d.mts +31 -0
  73. package/bin/commands/iterate/escalate.mjs +7 -4
  74. package/bin/commands/iterate/fix-code.d.mts +25 -0
  75. package/bin/commands/iterate/helpers.d.mts +13 -0
  76. package/bin/commands/iterate/helpers.mjs +4 -1
  77. package/bin/commands/iterate/index.d.mts +2 -0
  78. package/bin/commands/iterate/index.mjs +5 -2
  79. package/bin/commands/iterate/render.d.mts +5 -0
  80. package/bin/commands/iterate/render.mjs +8 -6
  81. package/bin/commands/iterate/reruns.d.mts +20 -0
  82. package/bin/commands/iterate/stall.d.mts +6 -0
  83. package/bin/commands/journal/index.d.mts +14 -0
  84. package/bin/commands/journal/index.mjs +1 -0
  85. package/bin/commands/journal/transform.d.mts +22 -0
  86. package/bin/commands/log-file.d.mts +5 -0
  87. package/bin/commands/mark-files-as-viewed.d.mts +26 -0
  88. package/bin/commands/mark-files-as-viewed.mjs +1 -0
  89. package/bin/commands/poll.d.mts +10 -0
  90. package/bin/commands/poll.mjs +1 -0
  91. package/bin/commands/ready-delay.d.mts +29 -0
  92. package/bin/commands/ready-mergeability.d.mts +15 -0
  93. package/bin/commands/resolve-mutate.d.mts +4 -0
  94. package/bin/commands/resolve-mutate.mjs +1 -0
  95. package/bin/commands/resolve.d.mts +4 -0
  96. package/bin/commands/shepherd-journal.d.mts +7 -0
  97. package/bin/commands/shepherd-journal.mjs +2 -2
  98. package/bin/comments/authors.d.mts +14 -0
  99. package/bin/comments/marker.d.mts +2 -0
  100. package/bin/comments/minimize-policy.d.mts +4 -0
  101. package/bin/comments/pending-ops.d.mts +15 -0
  102. package/bin/comments/rate-limit.d.mts +18 -0
  103. package/bin/comments/resolve.d.mts +34 -0
  104. package/bin/comments/resolve.mjs +1 -0
  105. package/bin/comments/review-thread-markers.d.mts +8 -0
  106. package/bin/comments/review-visibility.d.mts +28 -0
  107. package/bin/comments/sha-poll.d.mts +2 -0
  108. package/bin/comments/thread-visibility.d.mts +11 -0
  109. package/bin/comments/visible-comments.d.mts +11 -0
  110. package/bin/config/load.d.mts +60 -0
  111. package/bin/config/load.mjs +72 -1
  112. package/bin/config.json +0 -2
  113. package/bin/execution-context.d.mts +9 -0
  114. package/bin/execution-context.mjs +19 -0
  115. package/bin/exit-codes.d.mts +51 -0
  116. package/bin/github/activity.d.mts +3 -0
  117. package/bin/github/activity.mjs +7 -0
  118. package/bin/github/batch-parser-helpers.d.mts +22 -0
  119. package/bin/github/batch-parsers.d.mts +3 -0
  120. package/bin/github/batch-parsers.mjs +6 -0
  121. package/bin/github/batch-raw-types.d.mts +207 -0
  122. package/bin/github/batch-response.d.mts +4 -0
  123. package/bin/github/batch.d.mts +22 -0
  124. package/bin/github/branch-protection.d.mts +3 -0
  125. package/bin/github/check-annotations.d.mts +2 -0
  126. package/bin/github/client.d.mts +46 -0
  127. package/bin/github/client.mjs +7 -2
  128. package/bin/github/errors.d.mts +25 -0
  129. package/bin/github/gql/batch-pr.gql +5 -0
  130. package/bin/github/gql/review-thread-comments.gql +1 -0
  131. package/bin/github/graphql-http.d.mts +19 -0
  132. package/bin/github/graphql-response.d.mts +7 -0
  133. package/bin/github/http-auth.d.mts +4 -0
  134. package/bin/github/http-request.d.mts +7 -0
  135. package/bin/github/http-utils.d.mts +11 -0
  136. package/bin/github/http.d.mts +5 -0
  137. package/bin/github/pagination.d.mts +45 -0
  138. package/bin/github/queries.d.mts +24 -0
  139. package/bin/github/rest-http.d.mts +2 -0
  140. package/bin/github/thread-comments.d.mts +2 -0
  141. package/bin/index.d.mts +10 -0
  142. package/bin/index.mjs +1 -1
  143. package/bin/log/log-file.d.mts +28 -0
  144. package/bin/log/session.d.mts +31 -0
  145. package/bin/log/setup.d.mts +6 -0
  146. package/bin/mcp/index.d.mts +5 -0
  147. package/bin/mcp/index.mjs +8 -0
  148. package/bin/mcp/server.d.mts +8 -0
  149. package/bin/mcp/server.mjs +157 -0
  150. package/bin/mcp-stdio.d.mts +2 -0
  151. package/bin/mcp-stdio.mjs +7 -0
  152. package/bin/merge-status/derive.d.mts +19 -0
  153. package/bin/reporters/agent.d.mts +23 -0
  154. package/bin/reporters/agent.mjs +3 -0
  155. package/bin/state/base.d.mts +1 -0
  156. package/bin/state/bot-cr-seen.d.mts +51 -0
  157. package/bin/state/bot-cr-seen.mjs +1 -1
  158. package/bin/state/fix-attempts.d.mts +27 -0
  159. package/bin/state/iterate-stall.d.mts +27 -0
  160. package/bin/state/seen-comments.d.mts +62 -0
  161. package/bin/suggestions/extract.d.mts +8 -0
  162. package/bin/suggestions/parse.d.mts +48 -0
  163. package/bin/suggestions/patch.d.mts +14 -0
  164. package/bin/threads/transcript.d.mts +14 -0
  165. package/bin/threads/transcript.mjs +4 -0
  166. package/bin/types/activity.d.mts +30 -0
  167. package/bin/types/agent-thread.d.mts +9 -0
  168. package/bin/types/check-annotations.d.mts +14 -0
  169. package/bin/types/check-classification.d.mts +19 -0
  170. package/bin/types/github.d.mts +139 -0
  171. package/bin/types/iterate.d.mts +157 -0
  172. package/bin/types/protected-run.d.mts +6 -0
  173. package/bin/types/report.d.mts +176 -0
  174. package/bin/types/review-thread.d.mts +12 -0
  175. package/bin/types.d.mts +9 -0
  176. package/bin/util/markdown.d.mts +1 -0
  177. package/bin/util/path-segment.d.mts +2 -0
  178. package/bin/util/sleep.d.mts +1 -0
  179. package/bin/util/worktree.d.mts +9 -0
  180. package/bin/util/worktree.mjs +4 -1
  181. package/package.json +51 -37
  182. package/plugins/pr-shepherd/.codex-plugin/plugin.json +3 -2
  183. package/plugins/pr-shepherd/.codex.mcp.json +8 -0
  184. package/plugins/pr-shepherd/.mcp.json +6 -0
  185. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +5 -19
  186. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -15
  187. package/src/classify/types.mts +12 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Persistent stall-detection state for the iterate loop.
3
+ *
4
+ * Tracks the fingerprint of the last iterate result and when that fingerprint
5
+ * was first seen. If the fingerprint does not change for stallTimeoutSeconds
6
+ * the iterate command escalates instead of repeating the same action.
7
+ *
8
+ * State lives in `$TMPDIR/pr-shepherd-state/<owner>-<repo>/<pr>/iterate-stall.json`.
9
+ */
10
+ interface StallState {
11
+ /** Canonicalized JSON fingerprint of the material iterate inputs. */
12
+ fingerprint: string;
13
+ /** Unix timestamp (seconds) when this fingerprint was first seen. */
14
+ firstSeenAt: number;
15
+ }
16
+ interface StateKey {
17
+ owner: string;
18
+ repo: string;
19
+ pr: number;
20
+ }
21
+ /** Read the current stall state. Returns null on miss, corrupt data, or invalid shape. */
22
+ export declare function readStallState(key: StateKey): Promise<StallState | null>;
23
+ /** Clear stall state so the next invocation starts a fresh timer (fire-and-forget — never throws). */
24
+ export declare function clearStallState(key: StateKey): Promise<void>;
25
+ /** Write stall state (fire-and-forget — never throws). */
26
+ export declare function writeStallState(key: StateKey, state: StallState): Promise<void>;
27
+ export {};
@@ -0,0 +1,62 @@
1
+ export interface SeenMarker {
2
+ seenAt: number;
3
+ id?: string;
4
+ [key: string]: unknown;
5
+ }
6
+ interface StateKey {
7
+ owner: string;
8
+ repo: string;
9
+ pr: number;
10
+ }
11
+ /** Compute a 16-hex-char SHA-256 prefix of a comment body. */
12
+ export declare function hashBody(body: string): string;
13
+ /**
14
+ * Classify a candidate item against the seen map.
15
+ *
16
+ * - "new" — no marker exists; surface the body and write the marker.
17
+ * - "edited" — marker exists but stored hash differs from the current body;
18
+ * surface the updated body and update the marker hash.
19
+ * - "unchanged" — marker exists and hash matches (or marker has no hash, which
20
+ * is treated conservatively as unchanged).
21
+ */
22
+ export declare function classifyItem(id: string, body: string, map: Map<string, SeenMarker>): "new" | "edited" | "unchanged";
23
+ /**
24
+ * Read the seen/ directory once and return a Set of already-seen IDs.
25
+ * Prefer this over repeated hasSeen() calls to avoid EMFILE on large PRs.
26
+ * Returns an empty Set if the directory does not yet exist.
27
+ */
28
+ export declare function loadSeenSet(key: StateKey): Promise<Set<string>>;
29
+ /**
30
+ * Read the seen/ directory and return a Map from ID to SeenMarker.
31
+ * Used when the caller needs the stored bodyHash to detect in-place edits.
32
+ * Returns an empty Map if the directory does not yet exist.
33
+ *
34
+ * Map keys are the stored `id` field when present (guarding against
35
+ * case-insensitive filesystem collisions), falling back to the filename for
36
+ * legacy markers that predate this field.
37
+ */
38
+ export declare function loadSeenMap(key: StateKey): Promise<Map<string, SeenMarker>>;
39
+ /** Return true if a "seen" marker exists for this id. */
40
+ export declare function hasSeen(key: StateKey, id: string): Promise<boolean>;
41
+ /**
42
+ * Write (or update) a "seen" marker for this id, storing the body hash so
43
+ * in-place edits can be detected on future fetches.
44
+ *
45
+ * - First call (no existing marker): creates `{ seenAt: now, bodyHash, id }`.
46
+ * - Subsequent call, hash unchanged: no-op (skips the write).
47
+ * - Subsequent call, hash changed: updates `bodyHash`, preserves original `seenAt`.
48
+ *
49
+ * All errors are silently swallowed — the marker is best-effort.
50
+ */
51
+ export declare function markSeen(key: StateKey, id: string, body: string): Promise<void>;
52
+ export declare function markReviewInlineThreads(key: StateKey, reviewId: string, inlineThreadIds: readonly string[]): Promise<void>;
53
+ /**
54
+ * Write a marker after Shepherd successfully replies to a review thread.
55
+ *
56
+ * `previousBody` suppresses stale GitHub fetches that have not yet included the new reply.
57
+ * `body` suppresses the expected final transcript once GitHub includes Shepherd's reply.
58
+ */
59
+ export declare function markReplySeen(key: StateKey, id: string, previousBody: string, body: string, replyBody: string): Promise<void>;
60
+ /** Read the full marker for inspection (returns null on miss or error). */
61
+ export declare function readSeenMarker(key: StateKey, id: string): Promise<SeenMarker | null>;
62
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { SuggestionBlock } from "../types.mts";
2
+ export declare function extractSuggestion(thread: {
3
+ path: string | null;
4
+ line: number | null;
5
+ startLine: number | null;
6
+ body: string;
7
+ author: string;
8
+ }): SuggestionBlock | null;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Parse GitHub review-comment "suggestion" blocks.
3
+ *
4
+ * GitHub's "Commit suggestion" UI button treats the first ```suggestion fenced
5
+ * block in a review comment as a replacement for the commented line range.
6
+ * This module extracts that block from the comment body.
7
+ *
8
+ * There is no GitHub API for applying suggestions — tools that reproduce the
9
+ * button (this one included) must parse + commit themselves.
10
+ */
11
+ interface ParsedSuggestion {
12
+ /**
13
+ * Replacement lines to splice in. Empty array means "delete these lines".
14
+ * `[""]` means "replace with a single blank line". Array length equals the
15
+ * number of replacement lines in the suggestion snippet (not the number of
16
+ * lines in the resulting file).
17
+ */
18
+ lines: readonly string[];
19
+ }
20
+ /**
21
+ * Return the first ```suggestion block from a review-comment body, or null if none.
22
+ *
23
+ * Handles three distinct cases:
24
+ * - Empty block (` ```suggestion\n``` `) → `lines: []` (deletion).
25
+ * - Blank-line-only body (` ```suggestion\n\n``` `) → `lines: [""]`.
26
+ * - Non-empty body → split into lines.
27
+ *
28
+ * The opening fence may be 3+ backticks; the closing fence must be at least
29
+ * as many backticks, at the start of a line (after the captured prefix). This
30
+ * means content lines that contain ` ``` ` in the middle (not at line-start)
31
+ * are treated as content rather than a closing fence — fixing the silent
32
+ * truncation in the original regex approach (issue #68).
33
+ *
34
+ * When the block is embedded in a quoted reply (e.g. `> ```suggestion …`),
35
+ * the leading prefix captured from the opening fence is stripped from each
36
+ * body line — but only when that exact prefix is present, so legitimate `>`
37
+ * characters inside the suggested code survive.
38
+ */
39
+ export declare function parseSuggestion(body: string): ParsedSuggestion | null;
40
+ /**
41
+ * True when a parsed suggestion's replacement is safe to commit: the joined
42
+ * replacement contains no nested ` ```suggestion ` marker and no unmatched
43
+ * ` ``` ` run (odd count). Both shapes previously masked silent truncation
44
+ * (issue #68). Conservative by design: reviewers whose suggestion content
45
+ * legitimately includes these markers must apply the change manually.
46
+ */
47
+ export declare function isCommittableSuggestion(parsed: ParsedSuggestion): boolean;
48
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build a git-apply-compatible unified diff for a single suggestion hunk.
3
+ *
4
+ * The diff uses `--- a/<path>` / `+++ b/<path>` headers so `git apply` and
5
+ * `git apply --check` accept it without a `diff --git` preamble.
6
+ */
7
+ export declare function buildUnifiedDiff({ path, originalContent, startLine, endLine, replacementLines, context, }: {
8
+ path: string;
9
+ originalContent: string;
10
+ startLine: number;
11
+ endLine: number;
12
+ replacementLines: readonly string[];
13
+ context?: number;
14
+ }): string;
@@ -0,0 +1,14 @@
1
+ import type { ReviewThread, ReviewThreadComment } from "../types.mts";
2
+ export declare function threadComments(thread: {
3
+ body: string;
4
+ author: string;
5
+ authorType?: ReviewThreadComment["authorType"];
6
+ authorAssociation?: ReviewThreadComment["authorAssociation"];
7
+ url?: string;
8
+ createdAtUnix?: number;
9
+ comments?: Array<Pick<ReviewThreadComment, "id" | "author" | "body" | "url"> & {
10
+ authorType?: ReviewThreadComment["authorType"];
11
+ authorAssociation?: ReviewThreadComment["authorAssociation"];
12
+ } & Partial<Pick<ReviewThreadComment, "isMinimized" | "createdAtUnix">>>;
13
+ }): ReviewThreadComment[];
14
+ export declare function threadTranscriptBody(thread: ReviewThread, appendedBodies?: string[]): string;
@@ -5,6 +5,7 @@ export function threadComments(thread) {
5
5
  isMinimized: c.isMinimized ?? false,
6
6
  author: c.author,
7
7
  authorType: c.authorType ?? "Unknown",
8
+ ...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
8
9
  body: c.body,
9
10
  url: c.url,
10
11
  createdAtUnix: c.createdAtUnix ?? 0,
@@ -16,6 +17,9 @@ export function threadComments(thread) {
16
17
  isMinimized: false,
17
18
  author: thread.author,
18
19
  authorType: thread.authorType ?? "Unknown",
20
+ ...(thread.authorAssociation !== undefined && {
21
+ authorAssociation: thread.authorAssociation,
22
+ }),
19
23
  body: thread.body,
20
24
  url: thread.url ?? "",
21
25
  createdAtUnix: thread.createdAtUnix ?? 0,
@@ -0,0 +1,30 @@
1
+ import type { AuthorType, CheckStatus, CommentAuthorAssociation } from "./github.mts";
2
+ type ReviewActivityKind = "pr-comment" | "review-thread-comment" | "review-summary" | "approved-review" | "changes-requested-review";
3
+ export interface ReviewActivityItem {
4
+ kind: ReviewActivityKind;
5
+ id: string;
6
+ author: string;
7
+ authorType: AuthorType;
8
+ authorAssociation?: CommentAuthorAssociation;
9
+ body: string;
10
+ url?: string;
11
+ createdAtUnix: number;
12
+ threadId?: string;
13
+ path?: string | null;
14
+ line?: number | null;
15
+ }
16
+ export interface PrActivitySummary {
17
+ commitCount: number;
18
+ reviewRoundCount: number;
19
+ latestCommitCommittedAtUnix: number | null;
20
+ reviewItemsSinceLatestCommit: ReviewActivityItem[];
21
+ }
22
+ export interface ActiveCheck {
23
+ name: string;
24
+ status: CheckStatus;
25
+ runId: string | null;
26
+ detailsUrl: string | null;
27
+ workflowName?: string;
28
+ summary?: string;
29
+ }
30
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { AuthorType, CommentAuthorAssociation } from "./github.mts";
2
+ export interface AgentThreadComment {
3
+ id: string;
4
+ author: string;
5
+ authorType?: AuthorType;
6
+ authorAssociation?: CommentAuthorAssociation;
7
+ body: string;
8
+ url: string;
9
+ }
@@ -0,0 +1,14 @@
1
+ export interface CheckAnnotation {
2
+ /** Stable marker id used by Shepherd; prefixed to avoid collisions with comment/review IDs. */
3
+ id: string;
4
+ path: string;
5
+ startLine: number | null;
6
+ endLine: number | null;
7
+ startColumn?: number | null;
8
+ endColumn?: number | null;
9
+ level: string;
10
+ title?: string;
11
+ message: string;
12
+ rawDetails?: string;
13
+ blobUrl?: string;
14
+ }
@@ -0,0 +1,19 @@
1
+ import type { CheckAnnotation } from "./check-annotations.mts";
2
+ import type { CheckRun } from "./github.mts";
3
+ type CheckCategory = "passed" | "failing" | "in_progress" | "skipped" | "filtered" | "ignored" | "superseded";
4
+ export interface ClassifiedCheck extends CheckRun {
5
+ category: CheckCategory;
6
+ }
7
+ export interface TriagedCheck extends ClassifiedCheck {
8
+ /** Workflow display name (e.g. `"CI"`). Populated when available from the jobs API; may be `undefined` on fetch failure or when no matching job is found. */
9
+ workflowName?: string;
10
+ /** Name of the matched job (e.g. `"tests (ubuntu)"`). Distinct from the check name for matrix builds. */
11
+ jobName?: string;
12
+ /** Name of the first failed step in the matched job (e.g. `"Run tests"`). */
13
+ failedStep?: string;
14
+ /** Bounded raw excerpt from the matched failed job log, when GitHub exposes one. */
15
+ logExcerpt?: string;
16
+ /** Inline annotations attached to this failing check run, surfaced once per PR. */
17
+ annotations?: CheckAnnotation[];
18
+ }
19
+ export {};
@@ -0,0 +1,139 @@
1
+ import type { ReviewThreadComment } from "./review-thread.mts";
2
+ import type { PrActivitySummary } from "./activity.mts";
3
+ export type CheckConclusion = "ACTION_REQUIRED" | "CANCELLED" | "FAILURE" | "NEUTRAL" | "SKIPPED" | "STALE" | "STARTUP_FAILURE" | "SUCCESS" | "TIMED_OUT" | null;
4
+ export type CheckStatus = "COMPLETED" | "IN_PROGRESS" | "PENDING" | "QUEUED" | "REQUESTED" | "WAITING";
5
+ export type MergeableState = "CONFLICTING" | "MERGEABLE" | "UNKNOWN";
6
+ export type MergeStateStatus = "BEHIND" | "BLOCKED" | "CLEAN" | "DIRTY" | "DRAFT" | "HAS_HOOKS" | "UNKNOWN" | "UNSTABLE";
7
+ export type ReviewDecision = "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null;
8
+ export type AuthorType = "User" | "Bot" | "Unknown";
9
+ /** Raw relationship between a comment author and the repository, as reported by GitHub. */
10
+ export type CommentAuthorAssociation = "COLLABORATOR" | "CONTRIBUTOR" | "FIRST_TIMER" | "FIRST_TIME_CONTRIBUTOR" | "MANNEQUIN" | "MEMBER" | "NONE" | "OWNER";
11
+ export interface CheckRun {
12
+ /** GitHub node ID for CheckRun contexts. Missing for StatusContext and synthetic checks. */
13
+ id?: string | null;
14
+ name: string;
15
+ status: CheckStatus;
16
+ conclusion: CheckConclusion;
17
+ source?: "check_run" | "status_context" | "startup_failure";
18
+ detailsUrl: string;
19
+ event: string | null;
20
+ runId: string | null;
21
+ createdAtUnix?: number;
22
+ startedAtUnix?: number;
23
+ completedAtUnix?: number;
24
+ updatedAtUnix?: number;
25
+ /** One-line status text shown in the GitHub UI (CheckRun.title or first line of summary; StatusContext.description). */
26
+ summary?: string;
27
+ /** Workflow display name for GitHub Actions check runs, when GraphQL exposes it. */
28
+ workflowName?: string;
29
+ workflowId?: string;
30
+ }
31
+ export interface ReviewThread {
32
+ id: string;
33
+ isResolved: boolean;
34
+ isOutdated: boolean;
35
+ isMinimized: boolean;
36
+ path: string | null;
37
+ line: number | null;
38
+ /** Start of the comment's line range. Null for single-line comments (use `line` for both). */
39
+ startLine: number | null;
40
+ /** PullRequestReview ID that created the top comment, when GitHub exposes it. */
41
+ reviewId?: string;
42
+ author: string;
43
+ authorType: AuthorType;
44
+ authorAssociation?: CommentAuthorAssociation;
45
+ body: string;
46
+ url: string;
47
+ createdAtUnix?: number;
48
+ comments?: ReviewThreadComment[];
49
+ /** True when Shepherd re-surfaced this thread because its body changed after first look. */
50
+ edited?: boolean;
51
+ }
52
+ /**
53
+ * Parsed GitHub ```suggestion block, attached to a review thread when the
54
+ * reviewer left a machine-applicable replacement. The `lines` are the
55
+ * exact text the agent (or the CLI's commit-suggestions path) would write
56
+ * into the file in place of lines [startLine..endLine].
57
+ */
58
+ export interface SuggestionBlock {
59
+ /** 1-indexed inclusive start line. Equal to `endLine` for single-line suggestions. */
60
+ startLine: number;
61
+ /** 1-indexed inclusive end line. */
62
+ endLine: number;
63
+ /** Replacement lines verbatim. Empty array = delete; `[""]` = single blank line. Join with `"\n"` to display as a string. */
64
+ lines: readonly string[];
65
+ /** Reviewer login, surfaced so callers can co-credit them in commits. */
66
+ author: string;
67
+ }
68
+ export interface PrComment {
69
+ id: string;
70
+ isMinimized: boolean;
71
+ author: string;
72
+ authorType: AuthorType;
73
+ authorAssociation?: CommentAuthorAssociation;
74
+ body: string;
75
+ url: string;
76
+ createdAtUnix: number;
77
+ }
78
+ export interface Review {
79
+ id: string;
80
+ author: string;
81
+ authorType: AuthorType;
82
+ authorAssociation?: CommentAuthorAssociation;
83
+ body: string;
84
+ createdAtUnix?: number;
85
+ edited?: boolean;
86
+ /** True for bot CR re-surfaced after first look (body unchanged). Terse render. */
87
+ staleBotCr?: boolean;
88
+ /** Commit OID the review was made against, when available from GraphQL. */
89
+ commitOid?: string;
90
+ /** True when commitOid !== headRefOid AND all associated threads are resolved/outdated. Distinct from `staleBotCr` (time-based). Reviews with no threads are not marked stale. */
91
+ staleReview?: boolean;
92
+ }
93
+ export interface BranchProtection {
94
+ requiresApprovingReviews: boolean;
95
+ requiredApprovingReviewCount: number;
96
+ requiresConversationResolution: boolean;
97
+ requiresStatusChecks: boolean;
98
+ /** Required status check context names (e.g. "ci/build", "ci/test"). Empty when requiresStatusChecks is false, or when requiresStatusChecks is true but GitHub returns no explicit contexts. */
99
+ requiredStatusCheckContexts: string[];
100
+ }
101
+ export type ShepherdMergeStatus = "CLEAN" | "BEHIND" | "CONFLICTS" | "BLOCKED" | "UNSTABLE" | "DRAFT" | "UNKNOWN";
102
+ export interface MergeStatusResult {
103
+ status: ShepherdMergeStatus;
104
+ state: "OPEN" | "CLOSED" | "MERGED";
105
+ isDraft: boolean;
106
+ mergeable: MergeableState;
107
+ reviewDecision: ReviewDecision;
108
+ blockingBotReviewInProgress: boolean;
109
+ mergeStateStatus: MergeStateStatus;
110
+ }
111
+ export interface BatchPrData {
112
+ nodeId: string;
113
+ number: number;
114
+ state: "OPEN" | "CLOSED" | "MERGED";
115
+ isDraft: boolean;
116
+ mergeable: MergeableState;
117
+ mergeStateStatus: MergeStateStatus;
118
+ reviewDecision: ReviewDecision;
119
+ headRefOid: string;
120
+ headRefName: string;
121
+ /** `"owner/name"` of the head repository; null when the fork has been deleted. */
122
+ headRepoWithOwner: string | null;
123
+ baseRefName: string;
124
+ reviewRequests: Array<{
125
+ login: string;
126
+ }>;
127
+ latestReviews: Array<{
128
+ login: string;
129
+ state: string;
130
+ }>;
131
+ reviewThreads: ReviewThread[];
132
+ comments: PrComment[];
133
+ changesRequestedReviews: Review[];
134
+ reviewSummaries: Review[];
135
+ approvedReviews: Review[];
136
+ checks: CheckRun[];
137
+ branchProtection: BranchProtection | null;
138
+ activity?: PrActivitySummary;
139
+ }
@@ -0,0 +1,157 @@
1
+ import type { AgentThread, AgentComment, AgentCheck, GlobalOptions, RelevantCheck, ShepherdStatus, FirstLookThread, FirstLookComment } from "./report.mts";
2
+ import type { ActiveCheck, PrActivitySummary } from "./activity.mts";
3
+ import type { BranchProtection, CheckStatus, MergeStateStatus, Review, ReviewDecision, ReviewThread, ShepherdMergeStatus } from "./github.mts";
4
+ import type { ProtectedRun } from "./protected-run.mts";
5
+ export type ShepherdAction = "wait" | "fix_code" | "mark_ready" | "cancel" | "escalate";
6
+ export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "thread-missing-location" | "bot-cr-not-dismissed";
7
+ export interface AgentStalledCheck {
8
+ name: string;
9
+ status: CheckStatus;
10
+ source: "check_run" | "status_context" | "startup_failure";
11
+ runId: string | null;
12
+ detailsUrl: string | null;
13
+ createdAtUnix?: number;
14
+ startedAtUnix?: number;
15
+ updatedAtUnix?: number;
16
+ ageSeconds: number;
17
+ summary?: string;
18
+ }
19
+ export interface EscalateDetails {
20
+ triggers: EscalateTrigger[];
21
+ unresolvedThreads: AgentThread[];
22
+ ambiguousComments: AgentComment[];
23
+ changesRequestedReviews: Review[];
24
+ /** Pending/unstarted CI checks that exceeded the stall timeout. */
25
+ stalledChecks?: AgentStalledCheck[];
26
+ /** Populated when fix-thrash triggered — threads that have been attempted too many times. */
27
+ thrashHistory?: Array<{
28
+ threadId: string;
29
+ attempts: number;
30
+ }>;
31
+ suggestion: string;
32
+ humanMessage: string;
33
+ }
34
+ export interface IterateResultSummary {
35
+ passing: number;
36
+ skipped: number;
37
+ filtered: number;
38
+ inProgress: number;
39
+ superseded: number;
40
+ }
41
+ export interface IterateResultBase {
42
+ pr: number;
43
+ repo: string;
44
+ status: ShepherdStatus;
45
+ state: "OPEN" | "CLOSED" | "MERGED" | "UNKNOWN";
46
+ mergeStateStatus: MergeStateStatus;
47
+ mergeStatus: ShepherdMergeStatus;
48
+ reviewDecision: ReviewDecision;
49
+ blockingBotReviewInProgress: boolean;
50
+ isDraft: boolean;
51
+ shouldCancel: boolean;
52
+ remainingSeconds: number;
53
+ summary: IterateResultSummary;
54
+ /** Validated base branch (e.g. "main") for this PR. */
55
+ baseBranch: string;
56
+ /** Branch protection rule for the PR's base branch. Null when no rule exists or the base ref is unavailable. */
57
+ branchProtection: BranchProtection | null;
58
+ /**
59
+ * All CI checks that are relevant to PR readiness: triggered by a PR event
60
+ * (pull_request / pull_request_target, or StatusContext with null event),
61
+ * completed (status === COMPLETED), and not skipped/neutral.
62
+ *
63
+ * Includes both passing and failing checks. Failing entries carry
64
+ * `workflowName`, `jobName`, `failedStep`, and `summary`.
65
+ */
66
+ checks: RelevantCheck[];
67
+ inProgressChecks?: ActiveCheck[];
68
+ ignoredNames?: string[];
69
+ supersededNames?: string[];
70
+ activity?: PrActivitySummary;
71
+ }
72
+ interface IterateResultWait extends IterateResultBase {
73
+ action: "wait";
74
+ log: string;
75
+ }
76
+ export type CancelReason = "merged" | "closed" | "ready-delay-elapsed";
77
+ interface IterateResultCancel extends IterateResultBase {
78
+ action: "cancel";
79
+ reason: CancelReason;
80
+ log: string;
81
+ }
82
+ export interface ResolveCommand {
83
+ /** Argv for spawn-style execution. May contain a `$DISMISS_MESSAGE` placeholder. `$HEAD_SHA` is never in `argv` — `renderResolveCommand` (from `commands/iterate/render.mts`) appends `--require-sha "$HEAD_SHA"` when rendering if `requiresHeadSha` is true. Use `renderResolveCommand` to render as a command string; don't naive-join. */
84
+ argv: string[];
85
+ /** When true, `renderResolveCommand` appends `--require-sha "$HEAD_SHA"` to the rendered command. */
86
+ requiresHeadSha: boolean;
87
+ /** Whether the model must substitute $DISMISS_MESSAGE with a specific description of the fix. */
88
+ requiresDismissMessage: boolean;
89
+ /** Thread IDs that should receive a reply instead of a resolve mutation. */
90
+ replyThreadIds?: string[];
91
+ /** Thread IDs that should be resolved on GitHub. Human-authored IDs must not appear here. */
92
+ resolveThreadIds?: string[];
93
+ /** Bot/non-human CHANGES_REQUESTED review IDs to dismiss. Human-authored IDs must not appear here. */
94
+ dismissReviewIds?: string[];
95
+ /** True when any mutation flag was appended (threads/comments/reviews). False for a bare runner-specific `pr-shepherd apply review <PR>` with nothing to do. Callers use this to gate emitting an apply instruction — coupling to argv length would break silently if the base argv ever grew a global flag. */
96
+ hasMutations: boolean;
97
+ }
98
+ /**
99
+ * Default fix_code variant: agent applies edits locally, commits, pushes,
100
+ * then runs the pre-built apply command. Emitted under `## Post-fix push`.
101
+ */
102
+ interface FixRebaseAndPush {
103
+ threads: AgentThread[];
104
+ /** Unresolved threads that should be resolved on GitHub without requiring code edits. */
105
+ resolutionOnlyThreads: ReviewThread[];
106
+ /** PR comment bodies surfaced to the agent for evaluation, including previously filtered bot/noise comments; do not treat `actionableComments.length` as a proxy for "must push code". */
107
+ actionableComments: AgentComment[];
108
+ /** Review IDs (COMMENTED summaries and, if opted in, APPROVED reviews) to minimize — no code change needed. */
109
+ reviewSummaryIds: string[];
110
+ /** COMMENTED review summaries surfaced to the agent for the first time this iteration — body shown inline. */
111
+ firstLookSummaries: Review[];
112
+ /** COMMENTED review summaries whose body changed since the agent first saw them. Body shown inline; IDs not in `reviewSummaryIds`. */
113
+ editedSummaries: Review[];
114
+ /** APPROVED-state reviews surfaced for visibility (when `iterate.minimizeApprovals` is false). */
115
+ surfacedApprovals: Review[];
116
+ checks: AgentCheck[];
117
+ changesRequestedReviews: Review[];
118
+ /** Pre-built apply command. Run after committing and pushing. */
119
+ resolveCommand: ResolveCommand;
120
+ /** When present, run this command first (no SHA substitution needed) to resolve bot threads and minimize comments, independent of any push. */
121
+ resolveOnlyCommand?: ResolveCommand;
122
+ /** Ordered steps for the model to follow. */
123
+ instructions: string[];
124
+ /** Run IDs of in-progress GitHub Actions checks. The agent should cancel these before pushing new commits; if it decides not to push (e.g. resolve-only), it may skip cancellation. Empty when all in-progress runs are external status checks or already cancelled. */
125
+ inProgressRunIds: string[];
126
+ /** Workflow runs deliberately excluded from cancellation by actions.neverCancelRuns. */
127
+ protectedRuns: ProtectedRun[];
128
+ /** First-look threads — previously hidden, surfaced for acknowledgment only. */
129
+ firstLookThreads: FirstLookThread[];
130
+ /** First-look comments — previously hidden, surfaced for acknowledgment only. */
131
+ firstLookComments: FirstLookComment[];
132
+ }
133
+ export interface IterateResultFixCode extends IterateResultBase {
134
+ action: "fix_code";
135
+ fix: FixRebaseAndPush;
136
+ cancelled: string[];
137
+ }
138
+ interface IterateResultMarkReady extends IterateResultBase {
139
+ action: "mark_ready";
140
+ markedReady: boolean;
141
+ log: string;
142
+ }
143
+ interface IterateResultEscalate extends IterateResultBase {
144
+ action: "escalate";
145
+ escalate: EscalateDetails;
146
+ }
147
+ export type IterateResult = IterateResultWait | IterateResultCancel | IterateResultFixCode | IterateResultMarkReady | IterateResultEscalate;
148
+ export interface IterateCommandOptions extends GlobalOptions {
149
+ readyDelaySeconds?: number;
150
+ noAutoMarkReady?: boolean;
151
+ noAutoCancelActionable?: boolean;
152
+ /** Override stall timeout seconds. Defaults to config.iterate.stallTimeoutMinutes * 60. */
153
+ stallTimeoutSeconds?: number;
154
+ /** Case-insensitive workflow/check glob patterns Shepherd must not cancel. */
155
+ neverCancelRuns?: string[];
156
+ }
157
+ export {};
@@ -0,0 +1,6 @@
1
+ export interface ProtectedRun {
2
+ runId: string;
3
+ matchedPattern: string;
4
+ checkNames: string[];
5
+ workflowName?: string;
6
+ }