pr-shepherd 0.56.5 → 0.57.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 +44 -27
- package/bin/api.d.mts +25 -3
- package/bin/api.mjs +51 -13
- package/bin/checks/classify.d.mts +1 -0
- package/bin/checks/classify.mjs +1 -1
- package/bin/checks/conclusions.d.mts +5 -1
- package/bin/checks/conclusions.mjs +8 -2
- package/bin/checks/superseded.d.mts +8 -2
- package/bin/checks/superseded.mjs +24 -7
- package/bin/classify/loader.d.mts +2 -0
- package/bin/classify/loader.mjs +15 -0
- package/bin/classify/standard/author.d.mts +3 -0
- package/bin/classify/standard/author.mjs +10 -0
- package/bin/classify/standard/codecov-report.d.mts +4 -0
- package/bin/classify/standard/codecov-report.mjs +10 -0
- package/bin/classify/standard/coderabbit-notices.d.mts +8 -0
- package/bin/classify/standard/coderabbit-notices.mjs +38 -0
- package/bin/classify/standard/coderabbit-review-summary.d.mts +8 -0
- package/bin/classify/standard/coderabbit-review-summary.mjs +37 -0
- package/bin/classify/standard/codex-notices.d.mts +7 -0
- package/bin/classify/standard/codex-notices.mjs +36 -0
- package/bin/classify/standard/codex-review-summary.d.mts +7 -0
- package/bin/classify/standard/codex-review-summary.mjs +20 -0
- package/bin/classify/standard/dependabot-notices.d.mts +4 -0
- package/bin/classify/standard/dependabot-notices.mjs +15 -0
- package/bin/classify/standard/gemini-notices.d.mts +4 -0
- package/bin/classify/standard/gemini-notices.mjs +13 -0
- package/bin/classify/standard/index.d.mts +4 -0
- package/bin/classify/standard/index.mjs +22 -0
- package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
- package/bin/classify/standard/kilo-no-issues.mjs +17 -0
- package/bin/classify/standard/sourcery-notices.d.mts +8 -0
- package/bin/classify/standard/sourcery-notices.mjs +20 -0
- package/bin/cli/api-usage-formatter.mjs +6 -1
- package/bin/cli/apply-merge-handler.d.mts +3 -0
- package/bin/cli/apply-merge-handler.mjs +73 -0
- package/bin/cli/arg-flags.d.mts +2 -0
- package/bin/cli/arg-flags.mjs +47 -0
- package/bin/cli/args.mjs +10 -41
- package/bin/cli/clean-handler.d.mts +1 -0
- package/bin/cli/clean-handler.mjs +71 -0
- package/bin/cli/default-poll.mjs +3 -0
- package/bin/cli/event-state-scope.d.mts +19 -0
- package/bin/cli/event-state-scope.mjs +60 -0
- package/bin/cli/fix-formatter.mjs +5 -11
- package/bin/cli/handlers.d.mts +0 -1
- package/bin/cli/handlers.mjs +8 -64
- package/bin/cli/help-command-pages.d.mts +62 -18
- package/bin/cli/help-command-pages.mjs +59 -15
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +6 -0
- package/bin/cli/help-log-file-page.d.mts +1 -1
- package/bin/cli/help-log-file-page.mjs +4 -1
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +5 -0
- package/bin/cli/help.d.mts +63 -19
- package/bin/cli/help.mjs +2 -0
- package/bin/cli/iterate-branch-segment.d.mts +2 -0
- package/bin/cli/iterate-branch-segment.mjs +12 -7
- package/bin/cli/iterate-flags.d.mts +7 -1
- package/bin/cli/iterate-flags.mjs +38 -0
- package/bin/cli/iterate-formatter.d.mts +6 -6
- package/bin/cli/iterate-formatter.mjs +56 -22
- package/bin/cli/iterate-instructions.mjs +45 -15
- package/bin/cli/iterate-lean.mjs +39 -14
- package/bin/cli/iterate-merge-formatter.d.mts +6 -0
- package/bin/cli/iterate-merge-formatter.mjs +24 -14
- package/bin/cli/iterate-verbose.mjs +3 -6
- package/bin/cli/list-formatters.mjs +3 -1
- package/bin/cli/mutate-formatter.mjs +10 -5
- package/bin/cli/next-check-format.d.mts +7 -0
- package/bin/cli/next-check-format.mjs +10 -0
- package/bin/cli/playbook-handler.d.mts +5 -0
- package/bin/cli/playbook-handler.mjs +40 -0
- package/bin/cli/poll-handler.mjs +7 -6
- package/bin/cli/poll-summary-formatter.mjs +11 -2
- package/bin/cli/poll-targets.mjs +2 -0
- package/bin/cli/resolve-validators.mjs +2 -2
- package/bin/cli/runner.mjs +6 -1
- package/bin/cli/stack-layer-view.d.mts +33 -0
- package/bin/cli/stack-layer-view.mjs +1 -0
- package/bin/cli/stack-overview.d.mts +10 -28
- package/bin/cli/stack-overview.mjs +39 -14
- package/bin/cli/transport-args.d.mts +5 -0
- package/bin/cli/transport-args.mjs +23 -0
- package/bin/cli/transport-formatter.d.mts +9 -0
- package/bin/cli/transport-formatter.mjs +13 -0
- package/bin/cli-parser.mjs +62 -7
- package/bin/commands/apply-merge-options.d.mts +4 -0
- package/bin/commands/apply-merge-options.mjs +34 -0
- package/bin/commands/apply-merge-output.d.mts +7 -0
- package/bin/commands/apply-merge-output.mjs +20 -0
- package/bin/commands/apply-merge-pull.d.mts +20 -0
- package/bin/commands/apply-merge-pull.mjs +15 -0
- package/bin/commands/apply-merge.d.mts +18 -0
- package/bin/commands/apply-merge.mjs +126 -0
- package/bin/commands/apply-queue-removal.mjs +7 -0
- package/bin/commands/check-annotations.mjs +28 -18
- package/bin/commands/check-execution-context.d.mts +5 -0
- package/bin/commands/check-execution-context.mjs +16 -1
- package/bin/commands/check-fingerprint.d.mts +26 -2
- package/bin/commands/check-fingerprint.mjs +64 -28
- package/bin/commands/check-terminal-report.mjs +11 -1
- package/bin/commands/check-unreported.d.mts +6 -2
- package/bin/commands/check-unreported.mjs +32 -12
- package/bin/commands/check-wait-detectors.d.mts +35 -0
- package/bin/commands/check-wait-detectors.mjs +107 -0
- package/bin/commands/check.d.mts +4 -2
- package/bin/commands/check.mjs +130 -40
- package/bin/commands/event-instructions.d.mts +11 -0
- package/bin/commands/event-instructions.mjs +27 -0
- package/bin/commands/event-mode.d.mts +14 -0
- package/bin/commands/event-mode.mjs +138 -0
- package/bin/commands/iterate/api-usage.mjs +4 -2
- package/bin/commands/iterate/base.mjs +4 -0
- package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
- package/bin/commands/iterate/check-instructions.d.mts +9 -10
- package/bin/commands/iterate/check-instructions.mjs +23 -22
- package/bin/commands/iterate/classify.mjs +19 -5
- package/bin/commands/iterate/escalate.mjs +13 -3
- package/bin/commands/iterate/fix-code.mjs +40 -19
- package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
- package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
- package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
- package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
- package/bin/commands/iterate/helpers.mjs +15 -2
- package/bin/commands/iterate/index.mjs +26 -4
- package/bin/commands/iterate/mark-ready.mjs +50 -10
- package/bin/commands/iterate/merge-plan.d.mts +19 -0
- package/bin/commands/iterate/merge-plan.mjs +88 -0
- package/bin/commands/iterate/merge-state.mjs +3 -0
- package/bin/commands/iterate/merge.d.mts +4 -15
- package/bin/commands/iterate/merge.mjs +18 -51
- package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
- package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
- package/bin/commands/iterate/native-stack-rebase.d.mts +6 -5
- package/bin/commands/iterate/native-stack-rebase.mjs +24 -7
- package/bin/commands/iterate/render.d.mts +8 -2
- package/bin/commands/iterate/render.mjs +28 -42
- package/bin/commands/iterate/stack-trunk-conflict.d.mts +4 -2
- package/bin/commands/iterate/stack-trunk-conflict.mjs +44 -19
- package/bin/commands/iterate/stale-ancestry.mjs +4 -1
- package/bin/commands/iterate/stall.d.mts +5 -1
- package/bin/commands/iterate/stall.mjs +19 -7
- package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
- package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
- package/bin/commands/iterate/unreported-required.mjs +5 -2
- package/bin/commands/mark-files-as-viewed.mjs +7 -0
- package/bin/commands/next-check.d.mts +31 -0
- package/bin/commands/next-check.mjs +46 -0
- package/bin/commands/playbook.d.mts +13 -0
- package/bin/commands/playbook.mjs +48 -0
- package/bin/commands/poll-mode.d.mts +8 -0
- package/bin/commands/poll-mode.mjs +14 -0
- package/bin/commands/poll-progress.d.mts +1 -0
- package/bin/commands/poll-progress.mjs +6 -3
- package/bin/commands/poll-quota.d.mts +3 -2
- package/bin/commands/poll-quota.mjs +7 -3
- package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
- package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
- package/bin/commands/poll-summary-instructions.mjs +16 -17
- package/bin/commands/poll-summary.mjs +42 -17
- package/bin/commands/poll.d.mts +0 -1
- package/bin/commands/poll.mjs +11 -19
- package/bin/commands/quota-selection.d.mts +2 -1
- package/bin/commands/quota-selection.mjs +12 -7
- package/bin/commands/resolve-mutate.mjs +33 -5
- package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
- package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
- package/bin/commands/rule-auto-resolve.mjs +9 -3
- package/bin/commands/shepherd-journal.d.mts +5 -4
- package/bin/commands/shepherd-journal.mjs +9 -8
- package/bin/commands/stack-drain.d.mts +1 -9
- package/bin/commands/stack-drain.mjs +50 -37
- package/bin/commands/stack-handoffs.d.mts +10 -0
- package/bin/commands/stack-handoffs.mjs +25 -0
- package/bin/commands/stack-stall.d.mts +5 -0
- package/bin/commands/stack-stall.mjs +11 -0
- package/bin/commands/stack-work.d.mts +5 -0
- package/bin/commands/stack-work.mjs +32 -0
- package/bin/comments/existing-reply-scan.d.mts +17 -0
- package/bin/comments/existing-reply-scan.mjs +41 -0
- package/bin/comments/rate-limit.mjs +2 -2
- package/bin/comments/resolve.d.mts +7 -1
- package/bin/comments/resolve.mjs +108 -18
- package/bin/comments/rest-reply.d.mts +9 -0
- package/bin/comments/rest-reply.mjs +95 -0
- package/bin/comments/rest-review-mutations.d.mts +22 -0
- package/bin/comments/rest-review-mutations.mjs +125 -0
- package/bin/comments/review-visibility.d.mts +1 -1
- package/bin/comments/review-visibility.mjs +3 -2
- package/bin/comments/sha-poll.mjs +7 -3
- package/bin/comments/thread-visibility.mjs +10 -5
- package/bin/comments/uncertain-replies.d.mts +11 -0
- package/bin/comments/uncertain-replies.mjs +121 -0
- package/bin/comments/uncertain-reply-guards.d.mts +10 -0
- package/bin/comments/uncertain-reply-guards.mjs +94 -0
- package/bin/comments/visible-comments.d.mts +1 -1
- package/bin/comments/visible-comments.mjs +4 -2
- package/bin/config/load.d.mts +37 -1
- package/bin/config/load.mjs +73 -1
- package/bin/config.json +6 -2
- package/bin/exit-codes.d.mts +3 -0
- package/bin/exit-codes.mjs +8 -0
- package/bin/github/api-telemetry-aggregate.d.mts +15 -4
- package/bin/github/api-telemetry-aggregate.mjs +57 -18
- package/bin/github/api-telemetry.d.mts +4 -0
- package/bin/github/api-telemetry.mjs +22 -8
- package/bin/github/apply-review-preflight.d.mts +20 -0
- package/bin/github/apply-review-preflight.mjs +61 -0
- package/bin/github/base-behind-cache.d.mts +15 -0
- package/bin/github/base-behind-cache.mjs +18 -0
- package/bin/github/batch-parse-suites.d.mts +4 -2
- package/bin/github/batch-parse-suites.mjs +6 -1
- package/bin/github/batch-parser-helpers.mjs +40 -2
- package/bin/github/batch-parsers.mjs +1 -0
- package/bin/github/batch-raw-rules.d.mts +26 -0
- package/bin/github/batch-raw-types.d.mts +8 -6
- package/bin/github/batch-stack-evidence.d.mts +28 -0
- package/bin/github/batch-stack-evidence.mjs +62 -0
- package/bin/github/batch.d.mts +15 -2
- package/bin/github/batch.mjs +34 -17
- package/bin/github/check-annotations-batch.d.mts +0 -5
- package/bin/github/check-annotations-batch.mjs +25 -4
- package/bin/github/check-annotations.mjs +3 -1
- package/bin/github/client.d.mts +8 -19
- package/bin/github/client.mjs +45 -13
- package/bin/github/fingerprint-fields.d.mts +8 -0
- package/bin/github/fingerprint-fields.mjs +6 -1
- package/bin/github/fingerprint.d.mts +4 -2
- package/bin/github/fingerprint.mjs +6 -38
- package/bin/github/github-fetch.d.mts +2 -0
- package/bin/github/github-fetch.mjs +19 -0
- package/bin/github/gql/base-behind.gql +3 -0
- package/bin/github/gql/batch-pr-page.gql +16 -0
- package/bin/github/gql/batch-pr.gql +57 -0
- package/bin/github/gql/commit-check-contexts.gql +17 -0
- package/bin/github/gql/get-pr-body.gql +1 -0
- package/bin/github/gql/poll-stack-summary.gql +1 -0
- package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
- package/bin/github/gql/poll-summary-fragment.gql +65 -15
- package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
- package/bin/github/gql/upper-layer-conflict-target.gql +0 -15
- package/bin/github/graphql-http.mjs +9 -4
- package/bin/github/graphql-internal-retry.d.mts +2 -0
- package/bin/github/graphql-internal-retry.mjs +1 -1
- package/bin/github/graphql-quota-probe.d.mts +6 -0
- package/bin/github/graphql-quota-probe.mjs +56 -0
- package/bin/github/graphql-session-refusal.d.mts +8 -0
- package/bin/github/graphql-session-refusal.mjs +14 -0
- package/bin/github/http-request.d.mts +1 -1
- package/bin/github/http-request.mjs +22 -6
- package/bin/github/http-utils.mjs +6 -0
- package/bin/github/merge-target-rules.d.mts +16 -3
- package/bin/github/merge-target-rules.mjs +79 -44
- package/bin/github/mutation-policy.d.mts +6 -0
- package/bin/github/mutation-policy.mjs +21 -0
- package/bin/github/poll-summary-annotation-probe.mjs +17 -10
- package/bin/github/poll-summary-check-blockers.d.mts +1 -1
- package/bin/github/poll-summary-check-blockers.mjs +2 -2
- package/bin/github/poll-summary-check-hydration.mjs +25 -10
- package/bin/github/poll-summary-checks.d.mts +2 -2
- package/bin/github/poll-summary-checks.mjs +9 -6
- package/bin/github/poll-summary-command.d.mts +2 -0
- package/bin/github/poll-summary-command.mjs +24 -0
- package/bin/github/poll-summary-fingerprint.d.mts +1 -1
- package/bin/github/poll-summary-fingerprint.mjs +4 -2
- package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
- package/bin/github/poll-summary-handoff-fields.mjs +73 -0
- package/bin/github/poll-summary-projector.d.mts +1 -1
- package/bin/github/poll-summary-projector.mjs +17 -24
- package/bin/github/poll-summary-raw.d.mts +70 -10
- package/bin/github/poll-summary-readiness.mjs +4 -1
- package/bin/github/poll-summary-rest-queue.d.mts +3 -0
- package/bin/github/poll-summary-rest-queue.mjs +12 -0
- package/bin/github/poll-summary-review.d.mts +1 -1
- package/bin/github/poll-summary-review.mjs +23 -11
- package/bin/github/poll-summary-route.mjs +19 -3
- package/bin/github/poll-summary-stack-page.d.mts +11 -0
- package/bin/github/poll-summary-stack-page.mjs +27 -0
- package/bin/github/poll-summary-unreported.d.mts +6 -3
- package/bin/github/poll-summary-unreported.mjs +15 -7
- package/bin/github/poll-summary.d.mts +4 -1
- package/bin/github/poll-summary.mjs +48 -34
- package/bin/github/queries.d.mts +9 -5
- package/bin/github/queries.mjs +10 -6
- package/bin/github/rate-limit-sample.d.mts +7 -0
- package/bin/github/rate-limit-sample.mjs +12 -0
- package/bin/github/reply-recovery-read.d.mts +50 -0
- package/bin/github/reply-recovery-read.mjs +118 -0
- package/bin/github/reply-thread-transcripts.d.mts +0 -1
- package/bin/github/reply-thread-transcripts.mjs +24 -1
- package/bin/github/rest-annotation-read.d.mts +5 -0
- package/bin/github/rest-annotation-read.mjs +44 -0
- package/bin/github/rest-batch-read.d.mts +12 -0
- package/bin/github/rest-batch-read.mjs +57 -0
- package/bin/github/rest-batch-snapshot.d.mts +30 -0
- package/bin/github/rest-batch-snapshot.mjs +115 -0
- package/bin/github/rest-batch-unavailable.d.mts +5 -0
- package/bin/github/rest-batch-unavailable.mjs +16 -0
- package/bin/github/rest-check-read.d.mts +34 -0
- package/bin/github/rest-check-read.mjs +124 -0
- package/bin/github/rest-comment-authorship.d.mts +7 -0
- package/bin/github/rest-comment-authorship.mjs +11 -0
- package/bin/github/rest-conditional-scope.d.mts +20 -0
- package/bin/github/rest-conditional-scope.mjs +44 -0
- package/bin/github/rest-explicit-summary-read.d.mts +3 -0
- package/bin/github/rest-explicit-summary-read.mjs +8 -0
- package/bin/github/rest-feedback-read.d.mts +36 -0
- package/bin/github/rest-feedback-read.mjs +147 -0
- package/bin/github/rest-feedback-validation.d.mts +5 -0
- package/bin/github/rest-feedback-validation.mjs +28 -0
- package/bin/github/rest-http.d.mts +12 -0
- package/bin/github/rest-http.mjs +108 -23
- package/bin/github/rest-identities.d.mts +22 -0
- package/bin/github/rest-identities.mjs +134 -0
- package/bin/github/rest-merge-stack-guard.d.mts +14 -0
- package/bin/github/rest-merge-stack-guard.mjs +41 -0
- package/bin/github/rest-merge.d.mts +29 -0
- package/bin/github/rest-merge.mjs +56 -0
- package/bin/github/rest-merged-base-read.d.mts +9 -0
- package/bin/github/rest-merged-base-read.mjs +48 -0
- package/bin/github/rest-pr-core.d.mts +52 -0
- package/bin/github/rest-pr-core.mjs +60 -0
- package/bin/github/rest-reader-core.d.mts +16 -0
- package/bin/github/rest-reader-core.mjs +154 -0
- package/bin/github/rest-review-read.d.mts +11 -0
- package/bin/github/rest-review-read.mjs +47 -0
- package/bin/github/rest-rules-read.d.mts +9 -0
- package/bin/github/rest-rules-read.mjs +154 -0
- package/bin/github/rest-session-refusal.d.mts +5 -0
- package/bin/github/rest-session-refusal.mjs +21 -0
- package/bin/github/rest-snapshot-context.d.mts +10 -0
- package/bin/github/rest-snapshot-context.mjs +1 -0
- package/bin/github/rest-stack-read.d.mts +21 -0
- package/bin/github/rest-stack-read.mjs +47 -0
- package/bin/github/rest-stack-summary.d.mts +8 -0
- package/bin/github/rest-stack-summary.mjs +62 -0
- package/bin/github/rest-summary-read.d.mts +7 -0
- package/bin/github/rest-summary-read.mjs +112 -0
- package/bin/github/rest-terminal-batch.d.mts +14 -0
- package/bin/github/rest-terminal-batch.mjs +45 -0
- package/bin/github/rest-text.mjs +7 -3
- package/bin/github/rest-thread-read.d.mts +4 -0
- package/bin/github/rest-thread-read.mjs +35 -0
- package/bin/github/rest-viewer-read.d.mts +2 -0
- package/bin/github/rest-viewer-read.mjs +41 -0
- package/bin/github/stack-bottom.d.mts +3 -0
- package/bin/github/stack-bottom.mjs +14 -0
- package/bin/github/stack-handoff-context.d.mts +25 -0
- package/bin/github/stack-handoff-context.mjs +12 -0
- package/bin/github/stack-handoff-page.d.mts +17 -0
- package/bin/github/stack-handoff-page.mjs +306 -0
- package/bin/github/stack-handoff.d.mts +16 -0
- package/bin/github/stack-handoff.mjs +115 -0
- package/bin/github/stack-order.d.mts +6 -0
- package/bin/github/stack-order.mjs +13 -0
- package/bin/github/stack-read.d.mts +7 -3
- package/bin/github/stack-read.mjs +22 -11
- package/bin/github/stack-size-hint.d.mts +3 -0
- package/bin/github/stack-size-hint.mjs +14 -0
- package/bin/github/suggestion-thread.mjs +27 -1
- package/bin/github/thread-comments.mjs +9 -1
- package/bin/github/transport-evidence.d.mts +20 -0
- package/bin/github/transport-evidence.mjs +26 -0
- package/bin/github/transport-mode.d.mts +2 -0
- package/bin/github/transport-mode.mjs +5 -0
- package/bin/github/transport.d.mts +19 -0
- package/bin/github/transport.mjs +126 -0
- package/bin/github/unsupported-rest.d.mts +4 -0
- package/bin/github/unsupported-rest.mjs +7 -0
- package/bin/mcp/server.mjs +52 -3
- package/bin/merge-status/derive.d.mts +2 -0
- package/bin/merge-status/derive.mjs +17 -13
- package/bin/merge-status/required-check-requirements.d.mts +7 -0
- package/bin/merge-status/required-check-requirements.mjs +15 -0
- package/bin/merge-status/requirements-format.d.mts +5 -0
- package/bin/merge-status/requirements-format.mjs +41 -12
- package/bin/merge-status/requirements.mjs +18 -7
- package/bin/state/base.mjs +41 -0
- package/bin/state/durable-state.d.mts +6 -0
- package/bin/state/durable-state.mjs +41 -0
- package/bin/state/graphql-quota-policy.mjs +23 -16
- package/bin/state/merge-request.d.mts +25 -0
- package/bin/state/merge-request.mjs +145 -0
- package/bin/state/pr-fingerprint.d.mts +15 -0
- package/bin/state/pr-fingerprint.mjs +22 -7
- package/bin/state/rest-cache.d.mts +3 -0
- package/bin/state/rest-cache.mjs +1 -0
- package/bin/state/rest-snapshot-report.d.mts +21 -0
- package/bin/state/rest-snapshot-report.mjs +52 -0
- package/bin/state/seen-comments.d.mts +3 -0
- package/bin/state/seen-comments.mjs +18 -0
- package/bin/types/api-usage.d.mts +9 -1
- package/bin/types/escalate.d.mts +1 -1
- package/bin/types/github.d.mts +23 -9
- package/bin/types/iterate.d.mts +24 -2
- package/bin/types/merge-action.d.mts +1 -1
- package/bin/types/merge-requirements.d.mts +7 -7
- package/bin/types/next-check.d.mts +12 -0
- package/bin/types/next-check.mjs +1 -0
- package/bin/types/poll-summary.d.mts +21 -0
- package/bin/types/report.d.mts +13 -1
- package/bin/types/review-thread.d.mts +1 -1
- package/bin/types.d.mts +1 -0
- package/bin/types.mjs +1 -0
- package/package.json +3 -2
- 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 +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
- package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
- package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -0
- package/bin/github/gql/pr-fingerprint.gql +0 -75
- package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
- package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
- package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// The `[FIX_CODE]` heading and the skill already say the action is non-terminal.
|
|
3
|
+
const FIX_CODE_CONTINUATION = "Rerun Shepherd now.";
|
|
4
|
+
/** The `[FIX_CODE]` recurrence step, whether plain or rewritten by the quota warning. */
|
|
5
|
+
export function isFixCodeContinuation(step) {
|
|
6
|
+
return step === FIX_CODE_CONTINUATION || /\[FIX_CODE\].*non-terminal/i.test(step);
|
|
7
|
+
}
|
|
8
|
+
/** Build the stale-CR step for `## Changes-requested reviews`. Empty when no human CR is stale. */
|
|
4
9
|
export function buildCrStaleClause(reviews) {
|
|
5
|
-
|
|
6
|
-
? "
|
|
10
|
+
return reviews.some((r) => r.staleReview && !r.staleBotCr)
|
|
11
|
+
? "`[stale]` changes-requested reviews are human CRs on an old commit. Ask the reviewer to re-review."
|
|
7
12
|
: "";
|
|
8
|
-
return human;
|
|
9
13
|
}
|
|
10
14
|
/**
|
|
11
15
|
* Build the optional branch-update hint. Empty unless the branch is behind or conflicts with its
|
|
@@ -42,27 +46,21 @@ export function buildRepeatedWorkflowBranchRecoveryInstructions(baseBranch, hasE
|
|
|
42
46
|
return instructions;
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
|
-
* Build the `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* Contrast with what *does* stay in the skill's "Review-mutation mechanics" playbook —
|
|
51
|
-
* dismiss-ID retention. The pointer below is load-bearing: without it, nothing in CLI output
|
|
52
|
-
* tells the agent that playbook exists.
|
|
49
|
+
* Build the `apply review` step, with the rendered command inline. The step stays here (not in
|
|
50
|
+
* the skill) because the *as-printed* command is invalid without `$DISMISS_MESSAGE`: an empty
|
|
51
|
+
* `--message` makes `apply review` reject the mutation. `--require-sha` needs no substitution:
|
|
52
|
+
* the rendered command reads the pushed HEAD itself. "Even if you changed no code": the
|
|
53
|
+
* command records each item's disposition either way.
|
|
53
54
|
*/
|
|
54
|
-
export function buildResolveCommandInstruction(resolveCommand) {
|
|
55
|
+
export function buildResolveCommandInstruction(resolveCommand, rendered) {
|
|
55
56
|
if (!resolveCommand.hasMutations)
|
|
56
57
|
return [];
|
|
57
|
-
const instructions = [];
|
|
58
|
-
if (resolveCommand.requiresHeadSha) {
|
|
59
|
-
instructions.push("If you did not change code, replace `$HEAD_SHA` with `$(git rev-parse HEAD)` (it must equal the remote PR head). If you did, use the pushed SHA.");
|
|
60
|
-
}
|
|
61
58
|
if (resolveCommand.requiresDismissMessage) {
|
|
62
|
-
|
|
59
|
+
return [
|
|
60
|
+
`\`$DISMISS_MESSAGE\`: one sentence on what changed. Run even if no code changed: \`${rendered}\``,
|
|
61
|
+
];
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
return instructions;
|
|
63
|
+
return [`Run, even if no code changed: \`${rendered}\``];
|
|
66
64
|
}
|
|
67
65
|
/** One pointer. Conclusion, rerun, and bare-check rules live in the CI playbook. */
|
|
68
66
|
export function buildFailingCheckInstructions(checks) {
|
|
@@ -72,9 +70,12 @@ export function buildFailingCheckInstructions(checks) {
|
|
|
72
70
|
}
|
|
73
71
|
/** Update the PR branch after an external blocker merges or closes. Never rerun that job. */
|
|
74
72
|
export function buildReleasedBlockerInstruction(prNumber) {
|
|
75
|
-
return
|
|
73
|
+
return getGithubTransport() === "rest"
|
|
74
|
+
? "Update this PR branch from its base using the repository's branch-update procedure. Do not rerun the job; a rerun retests the old merge ref."
|
|
75
|
+
: `Update this PR branch from its base with \`gh pr update-branch ${prNumber} --rebase\`. Do not rerun the job; a rerun retests the old merge ref.`;
|
|
76
76
|
}
|
|
77
77
|
/** Recurrence only. Commit, push, rerun, and SHA steps are earlier instructions. */
|
|
78
78
|
export function buildFixCompletionInstruction() {
|
|
79
79
|
return FIX_CODE_CONTINUATION;
|
|
80
80
|
}
|
|
81
|
+
import { getGithubTransport } from "../../github/transport.mjs";
|
|
@@ -2,6 +2,8 @@ import { buildPrShepherdCommand } from "../../cli/runner.mjs";
|
|
|
2
2
|
import { shouldMinimizeAuthor } from "../../comments/minimize-policy.mjs";
|
|
3
3
|
import { isConfiguredBotAuthor, isHumanAuthor, } from "../../comments/authors.mjs";
|
|
4
4
|
import { buildThreadMutationRouting } from "./thread-mutation-routing.mjs";
|
|
5
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
6
|
+
import { durableStateRequested } from "../../state/durable-state.mjs";
|
|
5
7
|
function dedupeIds(ids) {
|
|
6
8
|
const seen = new Set();
|
|
7
9
|
const out = [];
|
|
@@ -15,7 +17,7 @@ function dedupeIds(ids) {
|
|
|
15
17
|
}
|
|
16
18
|
export function classifyReviewSummaries(summaries, approvals, minimizeApprovals, minimizeComments = "all", botUsernames = new Set(), unresolvedThreads = [], ruleAutoResolveIds = []) {
|
|
17
19
|
const blockedReviewIds = new Set(unresolvedThreads.flatMap((t) => (t.reviewId !== undefined ? [t.reviewId] : [])));
|
|
18
|
-
const eligible = (r) => r.viewerCanMinimize
|
|
20
|
+
const eligible = (r) => canGenerateGithubMutation(r.viewerCanMinimize, "minimize") &&
|
|
19
21
|
shouldMinimizeAuthor(r.authorType, minimizeComments, r.author, botUsernames) &&
|
|
20
22
|
!blockedReviewIds.has(r.id);
|
|
21
23
|
// First-look summaries still need one tick to surface their body to the agent,
|
|
@@ -36,7 +38,7 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
36
38
|
if (minimizeApprovals) {
|
|
37
39
|
const surfacedApprovals = [];
|
|
38
40
|
for (const r of approvals) {
|
|
39
|
-
if (r.viewerCanMinimize
|
|
41
|
+
if (canGenerateGithubMutation(r.viewerCanMinimize, "minimize") &&
|
|
40
42
|
shouldMinimizeAuthor(r.authorType, minimizeComments, r.author, botUsernames))
|
|
41
43
|
minimizeIds.push(r.id);
|
|
42
44
|
else
|
|
@@ -58,14 +60,24 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
58
60
|
surfacedApprovals: approvals,
|
|
59
61
|
};
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* A durable-state session (event mode or a cloud session) can lose its uncertain-reply records
|
|
65
|
+
* between ticks. Its generated reply command opts in to adopting a reply GitHub already shows,
|
|
66
|
+
* so rerunning the same printed command cannot double-post. Direct apply requests never do.
|
|
67
|
+
*/
|
|
68
|
+
function pushAdoptExistingReplies(argv, hasReply) {
|
|
69
|
+
if (hasReply && durableStateRequested())
|
|
70
|
+
argv.push("--adopt-existing-replies");
|
|
71
|
+
}
|
|
61
72
|
export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentIds, reviews, checks, prReference, botUsernames = new Set(), ruleAutoResolveThreadIds = [], viewerAuthorization, authorizationThreads = [], resolveOtherHumanThreads = "none") {
|
|
62
73
|
const allThreads = [...threads, ...resolutionOnlyThreads];
|
|
63
74
|
const routed = buildThreadMutationRouting(allThreads, botUsernames, ruleAutoResolveThreadIds, resolveOtherHumanThreads);
|
|
64
75
|
const canReply = new Set(authorizationThreads
|
|
65
|
-
.filter((thread) => thread.viewerCanReply
|
|
76
|
+
.filter((thread) => canGenerateGithubMutation(thread.viewerCanReply, "reply"))
|
|
66
77
|
.map((thread) => thread.id));
|
|
67
78
|
const canResolve = new Set(authorizationThreads
|
|
68
|
-
.filter((thread) => thread.
|
|
79
|
+
.filter((thread) => thread.isResolved === false &&
|
|
80
|
+
canGenerateGithubMutation(thread.viewerCanResolve, "resolve"))
|
|
69
81
|
.map((thread) => thread.id));
|
|
70
82
|
const pairedResolveIds = new Set(routed.pairedResolveThreadIds);
|
|
71
83
|
const replyThreadIds = routed.replyThreadIds.filter((id) => canReply.has(id) && (!pairedResolveIds.has(id) || canResolve.has(id)));
|
|
@@ -76,7 +88,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
76
88
|
const resolveThreadIds = dedupeIds([...pairedResolveThreadIds, ...standaloneResolveThreadIds]);
|
|
77
89
|
// Bot/non-human CHANGES_REQUESTED reviews are auto-dismissed after the agent pushes a fix.
|
|
78
90
|
// Human reviews are left for the reviewer to re-review or dismiss themselves.
|
|
79
|
-
const dismissReviewIds = dedupeIds(viewerAuthorization?.viewerCanAdminister
|
|
91
|
+
const dismissReviewIds = dedupeIds(canGenerateGithubMutation(viewerAuthorization?.viewerCanAdminister, "dismiss")
|
|
80
92
|
? reviews
|
|
81
93
|
.filter((r) => !isHumanAuthor(r) || isConfiguredBotAuthor(r, botUsernames))
|
|
82
94
|
.map((r) => r.id)
|
|
@@ -99,6 +111,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
99
111
|
resolveArgv.push("--resolve-thread-ids", pairedResolveThreadIds.join(","));
|
|
100
112
|
}
|
|
101
113
|
resolveArgv.push("--message", "$DISMISS_MESSAGE");
|
|
114
|
+
pushAdoptExistingReplies(resolveArgv, hasReply);
|
|
102
115
|
if (hasDismiss) {
|
|
103
116
|
resolveArgv.push("--dismiss-review-ids", dismissReviewIds.join(","));
|
|
104
117
|
}
|
|
@@ -139,6 +152,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
139
152
|
if (replyThreadIds.length > 0) {
|
|
140
153
|
argv.push("--reply-thread-ids", replyThreadIds.join(","));
|
|
141
154
|
argv.push("--message", "$DISMISS_MESSAGE");
|
|
155
|
+
pushAdoptExistingReplies(argv, true);
|
|
142
156
|
}
|
|
143
157
|
if (resolveThreadIds.length > 0) {
|
|
144
158
|
argv.push("--resolve-thread-ids", resolveThreadIds.join(","));
|
|
@@ -151,9 +151,19 @@ export function buildEscalateHumanMessage(escalate, pr, opts) {
|
|
|
151
151
|
const loc = t.path ? `\`${t.path}:${t.line ?? "?"}\`` : "(no location)";
|
|
152
152
|
lines.push(`- thread \`${t.id}\` — ${loc} (${renderEscalateAuthor(t)}):`);
|
|
153
153
|
lines.push("");
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
if (!t.comments?.length) {
|
|
155
|
+
for (const bodyLine of t.body.split("\n"))
|
|
156
|
+
lines.push(` > ${bodyLine}`);
|
|
157
|
+
lines.push("");
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// The transcript starts with the root comment, whose body is the thread body.
|
|
161
|
+
for (const c of t.comments) {
|
|
162
|
+
lines.push(` - comment \`${c.id}\` (${renderEscalateAuthor(c)}):`, "");
|
|
163
|
+
for (const bodyLine of c.body.split("\n"))
|
|
164
|
+
lines.push(` > ${bodyLine}`);
|
|
165
|
+
lines.push("");
|
|
166
|
+
}
|
|
157
167
|
}
|
|
158
168
|
for (const r of escalate.changesRequestedReviews) {
|
|
159
169
|
lines.push(`- review \`${r.id}\` (${renderEscalateAuthor(r)}):`);
|
|
@@ -20,6 +20,7 @@ import { annotationMarkerBody, checksWithActionableAnnotations } from "../check-
|
|
|
20
20
|
import { threadTranscriptBody } from "../../threads/transcript.mjs";
|
|
21
21
|
import { isHumanAuthor, isConfiguredBotAuthor } from "../../comments/authors.mjs";
|
|
22
22
|
import { canRerunWorkflows } from "../../checks/conclusions.mjs";
|
|
23
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
23
24
|
import { loadConfig } from "../../config/load.mjs";
|
|
24
25
|
import { formatPrUrl } from "../../pr-reference.mjs";
|
|
25
26
|
const EMPTY_RELEASED = new Set();
|
|
@@ -87,10 +88,12 @@ export async function handleFixCode(ctx) {
|
|
|
87
88
|
const routedThreadMutations = buildThreadMutationRouting(allThreads, botUsernames, [...ruleAutoResolveIds], resolveOtherHumanThreads);
|
|
88
89
|
const replyIdSet = new Set(routedThreadMutations.replyThreadIds);
|
|
89
90
|
const resolveIdSet = new Set(routedThreadMutations.resolveThreadIds);
|
|
90
|
-
const unauthorizedReplies = allThreads.filter((thread) => replyIdSet.has(thread.id) && thread.viewerCanReply
|
|
91
|
-
const unauthorizedResolves = allThreads.filter((thread) => resolveIdSet.has(thread.id) &&
|
|
91
|
+
const unauthorizedReplies = allThreads.filter((thread) => replyIdSet.has(thread.id) && !canGenerateGithubMutation(thread.viewerCanReply, "reply"));
|
|
92
|
+
const unauthorizedResolves = allThreads.filter((thread) => resolveIdSet.has(thread.id) &&
|
|
93
|
+
(thread.isResolved !== false ||
|
|
94
|
+
!canGenerateGithubMutation(thread.viewerCanResolve, "resolve")));
|
|
92
95
|
const unauthorizedDismissals = report.changesRequestedReviews.filter((review) => (!isHumanAuthor(review) || isConfiguredBotAuthor(review, botUsernames)) &&
|
|
93
|
-
report.viewerAuthorization?.viewerCanAdminister
|
|
96
|
+
!canGenerateGithubMutation(report.viewerAuthorization?.viewerCanAdminister, "dismiss"));
|
|
94
97
|
const skippedThreadIds = new Set([...unauthorizedReplies, ...unauthorizedResolves].map((thread) => thread.id));
|
|
95
98
|
const mutationActionableThreads = report.threads.actionable.filter((thread) => !skippedThreadIds.has(thread.id) &&
|
|
96
99
|
((thread.path !== null && thread.line !== null) ||
|
|
@@ -98,6 +101,7 @@ export async function handleFixCode(ctx) {
|
|
|
98
101
|
const retryableActionableThreads = mutationActionableThreads.filter((thread) => thread.path !== null && thread.line !== null);
|
|
99
102
|
const protectedRuns = [];
|
|
100
103
|
const stored = await readFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber });
|
|
104
|
+
await aliasThreadFixAttempts({ owner: repoOwner, name: repoName }, prNumber, retryableActionableThreads.map(({ id }) => id), stored);
|
|
101
105
|
const countFixCodeAttempt = opts.persistSeen !== false;
|
|
102
106
|
const priorThreadAttempts = previousFixAttempts(stored, retryableActionableThreads);
|
|
103
107
|
const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads, countFixCodeAttempt);
|
|
@@ -154,7 +158,7 @@ export async function handleFixCode(ctx) {
|
|
|
154
158
|
const threads = report.threads.actionable.map(toAgentThread);
|
|
155
159
|
const resolutionOnlyThreads = report.threads.resolutionOnly;
|
|
156
160
|
const actionableComments = report.comments.actionable.map(toAgentComment);
|
|
157
|
-
const rerunAuthorized = canRerunWorkflows(report.viewerAuthorization);
|
|
161
|
+
const rerunAuthorized = canRerunWorkflows(report.viewerAuthorization, report.transport);
|
|
158
162
|
// A workflow run can only be rerun once it has fully completed; a runId still present among
|
|
159
163
|
// in-progress checks (a sibling job from the same run) is not yet eligible.
|
|
160
164
|
const inProgressWorkflowRunIds = new Set(report.checks.inProgress.flatMap((c) => (c.runId !== null ? [c.runId] : [])));
|
|
@@ -192,7 +196,7 @@ export async function handleFixCode(ctx) {
|
|
|
192
196
|
const { changesRequestedReviews } = report;
|
|
193
197
|
const hasConflicts = report.mergeStatus.status === "CONFLICTS";
|
|
194
198
|
const isBehind = report.mergeStatus.status === "BEHIND";
|
|
195
|
-
const { behindBaseHint } = loadConfig().iterate;
|
|
199
|
+
const { behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
|
|
196
200
|
// Only surface in-progress runs when a push is plausible — resolution-only and
|
|
197
201
|
// summary-only iterations have no path to a push, so listing runs would prompt
|
|
198
202
|
// unnecessary cancellation.
|
|
@@ -301,18 +305,26 @@ export async function handleFixCode(ctx) {
|
|
|
301
305
|
baseRefOid: report.baseRefOid,
|
|
302
306
|
})
|
|
303
307
|
: [];
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
// A bottom open layer can still retain its merged parent's PR base. Its update starts
|
|
309
|
+
// at trunk regardless of that base comparison; true upper layers first check the parent.
|
|
310
|
+
const trunkConflict = hasConflicts && stack && report.stackBottomPr === prNumber
|
|
311
|
+
? { trunk: stack.baseRefName, bottomPr: prNumber }
|
|
312
|
+
: hasConflicts && stack && headRef && baseLookup.branch !== stack.baseRefName
|
|
313
|
+
? await lookupUpperLayerTrunkConflict({
|
|
314
|
+
owner: repoOwner,
|
|
315
|
+
name: repoName,
|
|
316
|
+
pr: prNumber,
|
|
317
|
+
headRef,
|
|
318
|
+
trunk: stack.baseRefName,
|
|
319
|
+
bottomPr: report.stackBottomPr,
|
|
320
|
+
})
|
|
321
|
+
: undefined;
|
|
322
|
+
const recovery = buildRemovedQueueRecovery(report, failingAgentChecks, opts.merge);
|
|
323
|
+
const requeue = recovery && !("unavailable" in recovery) ? recovery : undefined;
|
|
324
|
+
const acknowledgmentRecovery = buildStackQueueRemovalAcknowledgment(report, failingAgentChecks);
|
|
325
|
+
const queueRemovalAcknowledgment = acknowledgmentRecovery && !("unavailable" in acknowledgmentRecovery)
|
|
326
|
+
? acknowledgmentRecovery
|
|
313
327
|
: undefined;
|
|
314
|
-
const requeue = buildRemovedQueueRecovery(report, failingAgentChecks, opts.merge);
|
|
315
|
-
const queueRemovalAcknowledgment = buildStackQueueRemovalAcknowledgment(report, failingAgentChecks);
|
|
316
328
|
const queueEjection = !currentEjectionCommit(report, failingAgentChecks)
|
|
317
329
|
? undefined
|
|
318
330
|
: requeue
|
|
@@ -323,9 +335,13 @@ export async function handleFixCode(ctx) {
|
|
|
323
335
|
// Conflicts, a behind branch whose rerun already failed, and a queue ejection that may be
|
|
324
336
|
// reproduced on the latest base all print the branch update route.
|
|
325
337
|
const stackRebase = hasConflicts || (isBehind && exhaustedAttempts.length > 0) || queueEjection
|
|
326
|
-
? buildNativeStackLayerRebase(report.repo, { number: prNumber, baseBranch: baseLookup.branch }, stack, trunkConflict)
|
|
338
|
+
? buildNativeStackLayerRebase(report.repo, { number: prNumber, baseBranch: baseLookup.branch }, stack, trunkConflict, report.stackBottomPr)
|
|
327
339
|
: undefined;
|
|
328
|
-
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, cancelled.length, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, inProgressRunIds, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, report.viewerAuthorization?.viewerCanUpdate
|
|
340
|
+
const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, cancelled.length, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, inProgressRunIds, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready"), exhaustedAttempts.length > 0, stackRebase, queueEjection, opts.instructions ?? configuredInstructions);
|
|
341
|
+
if (recovery && "unavailable" in recovery)
|
|
342
|
+
instructions.unshift(recovery.unavailable);
|
|
343
|
+
if (acknowledgmentRecovery && "unavailable" in acknowledgmentRecovery)
|
|
344
|
+
instructions.unshift(acknowledgmentRecovery.unavailable);
|
|
329
345
|
if (failingAgentChecks.some((check) => releasedCheckNames.has(check.name))) {
|
|
330
346
|
const completion = instructions.pop();
|
|
331
347
|
instructions.push(buildReleasedBlockerInstruction(prNumber));
|
|
@@ -336,7 +352,11 @@ export async function handleFixCode(ctx) {
|
|
|
336
352
|
instructions.unshift(...repairInstructions);
|
|
337
353
|
}
|
|
338
354
|
if (mergedBasePullRequests.length > 0) {
|
|
339
|
-
|
|
355
|
+
// `gh pr edit` goes through GraphQL; REST callers retarget with the pulls PATCH endpoint.
|
|
356
|
+
const retarget = report.transport === "rest"
|
|
357
|
+
? `gh api --method PATCH repos/${report.repo}/pulls/${prNumber} -f base=<verified-parent-base>`
|
|
358
|
+
: `gh pr edit ${prReference} --base <verified-parent-base>`;
|
|
359
|
+
instructions.unshift(`Inspect every PR under \`## Merged PRs matching the current base\`. If this PR is the remaining layer intended for a merged parent's base branch, run \`${retarget}\` after replacing \`<verified-parent-base>\` with that parent's shell-quoted base branch, then rerun Shepherd immediately and follow its fresh instructions instead of the remaining steps here. Otherwise keep the current base and follow the remaining conflict-resolution steps.`);
|
|
340
360
|
}
|
|
341
361
|
const checkRunCount = countReportedChecks(report.checks);
|
|
342
362
|
const suitesEmpty = report.headCheckSuitesEmpty === true;
|
|
@@ -386,3 +406,4 @@ export async function handleFixCode(ctx) {
|
|
|
386
406
|
}
|
|
387
407
|
return result;
|
|
388
408
|
}
|
|
409
|
+
import { aliasThreadFixAttempts } from "../../github/rest-identities.mjs";
|
|
@@ -4,11 +4,3 @@ export declare function partitionFixThreads(threads: AgentThread[], resolveComma
|
|
|
4
4
|
unlocatedMutatedThreads: AgentThread[];
|
|
5
5
|
unlocatedThreads: AgentThread[];
|
|
6
6
|
};
|
|
7
|
-
export declare function reviewSectionRefs(input: {
|
|
8
|
-
hasReviewThreads: boolean;
|
|
9
|
-
hasUnlocatedSkipThreads: boolean;
|
|
10
|
-
hasActionableComments: boolean;
|
|
11
|
-
hasFailingChecks: boolean;
|
|
12
|
-
hasAnnotations: boolean;
|
|
13
|
-
hasChangesRequested: boolean;
|
|
14
|
-
}): string[];
|
|
@@ -14,19 +14,3 @@ export function partitionFixThreads(threads, resolveCommand, resolveOnlyCommand)
|
|
|
14
14
|
unlocatedThreads: threads.filter((thread) => (thread.path === null || thread.line === null) && !mutatedThreadIds.has(thread.id)),
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
export function reviewSectionRefs(input) {
|
|
18
|
-
const sections = [];
|
|
19
|
-
if (input.hasReviewThreads)
|
|
20
|
-
sections.push("`## Review threads`");
|
|
21
|
-
if (input.hasUnlocatedSkipThreads)
|
|
22
|
-
sections.push("`## Unlocated review threads (logged once — no mutation)`");
|
|
23
|
-
if (input.hasActionableComments)
|
|
24
|
-
sections.push("`## Actionable comments`");
|
|
25
|
-
if (input.hasFailingChecks)
|
|
26
|
-
sections.push("`## Failing checks`");
|
|
27
|
-
if (input.hasAnnotations)
|
|
28
|
-
sections.push("`## Check annotations`");
|
|
29
|
-
if (input.hasChangesRequested)
|
|
30
|
-
sections.push("`## Changes-requested reviews`");
|
|
31
|
-
return sections;
|
|
32
|
-
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InstructionStyle } from "../../config/load.mts";
|
|
2
|
+
/**
|
|
3
|
+
* The generic commit/push step and the journal step. Playbook style folds both into one pointer
|
|
4
|
+
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
5
|
+
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
6
|
+
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
7
|
+
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildPushJournalSteps(style: InstructionStyle, prReference: string | number, genericPush: boolean, wantsJournal: boolean): string[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { renderShellCommand } from "../../cli/runner.mjs";
|
|
2
|
+
import { loadConfig } from "../../config/load.mjs";
|
|
3
|
+
import { buildShepherdJournalCommand, buildShepherdJournalInstruction, } from "../shepherd-journal.mjs";
|
|
4
|
+
import { playbookPointer } from "../playbook-pointer.mjs";
|
|
5
|
+
const FIX_CODE_LOOP_PLAYBOOK = "Fix-code loop";
|
|
6
|
+
/**
|
|
7
|
+
* Playbook-style replacement for the invariant commit/push and journal steps. The
|
|
8
|
+
* procedure text lives in the "Fix-code loop" playbook; only the trigger and the concrete
|
|
9
|
+
* journal command stay in the per-tick output. `inline` style never calls this.
|
|
10
|
+
*/
|
|
11
|
+
function buildFixLoopInstruction(prReference, journal) {
|
|
12
|
+
const parts = ["Commit and push any changes."];
|
|
13
|
+
if (journal)
|
|
14
|
+
parts.push(`Journal substantial decisions or rejections with \`${buildShepherdJournalCommand(prReference)}\`.`);
|
|
15
|
+
const cli = renderShellCommand([...loadConfig().cliCommand, "playbook", FIX_CODE_LOOP_PLAYBOOK]);
|
|
16
|
+
parts.push(`${playbookPointer(FIX_CODE_LOOP_PLAYBOOK)} CLI: \`${cli}\`.`);
|
|
17
|
+
return parts.join(" ");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The generic commit/push step and the journal step. Playbook style folds both into one pointer
|
|
21
|
+
* only when the generic push applies: the "Fix-code loop" playbook tells the caller to push to the
|
|
22
|
+
* PR head branch, which would conflict with a conflict or queue-recovery push step. Without the
|
|
23
|
+
* generic push, the journal step stays inline with its citation clause. The rerun
|
|
24
|
+
* step stays inline because it is the one step a halted loop cannot recover from.
|
|
25
|
+
*/
|
|
26
|
+
export function buildPushJournalSteps(style, prReference, genericPush, wantsJournal) {
|
|
27
|
+
if (style === "playbook" && genericPush)
|
|
28
|
+
return [buildFixLoopInstruction(prReference, wantsJournal)];
|
|
29
|
+
const steps = [];
|
|
30
|
+
if (genericPush)
|
|
31
|
+
steps.push("Commit and push any changes.");
|
|
32
|
+
if (wantsJournal)
|
|
33
|
+
steps.push(buildShepherdJournalInstruction(prReference));
|
|
34
|
+
return steps;
|
|
35
|
+
}
|
|
@@ -23,6 +23,10 @@ export function buildTerminalCancelResult(report) {
|
|
|
23
23
|
return {
|
|
24
24
|
pr: report.pr,
|
|
25
25
|
repo: report.repo,
|
|
26
|
+
...(report.transport && { transport: report.transport }),
|
|
27
|
+
...(report.transportUnavailable?.length && {
|
|
28
|
+
transportUnavailable: report.transportUnavailable,
|
|
29
|
+
}),
|
|
26
30
|
status: report.status,
|
|
27
31
|
mergeStateStatus: report.mergeStatus.mergeStateStatus,
|
|
28
32
|
mergeStatus: report.mergeStatus.status,
|
|
@@ -103,7 +107,12 @@ export function buildActiveChecks(report) {
|
|
|
103
107
|
}
|
|
104
108
|
export function buildWaitLog(base) {
|
|
105
109
|
const { summary, remainingSeconds } = base;
|
|
106
|
-
|
|
110
|
+
// Zero counts are trivial defaults; the line keeps whichever count is non-zero.
|
|
111
|
+
const counts = [
|
|
112
|
+
...(summary.passing > 0 ? [`${summary.passing} passing`] : []),
|
|
113
|
+
...(summary.inProgress > 0 ? [`${summary.inProgress} in-progress`] : []),
|
|
114
|
+
];
|
|
115
|
+
const parts = counts.length > 0 ? [counts.join(", ")] : [];
|
|
107
116
|
if ((base.inProgressChecks?.length ?? 0) > 0) {
|
|
108
117
|
parts.push(`active checks: ${base
|
|
109
118
|
.inProgressChecks.slice(0, 5)
|
|
@@ -133,7 +142,11 @@ export function buildWaitLog(base) {
|
|
|
133
142
|
if (remainingSeconds > 0) {
|
|
134
143
|
parts.push(`${remainingSeconds}s until auto-cancel`);
|
|
135
144
|
}
|
|
136
|
-
|
|
145
|
+
if (parts.length > 0)
|
|
146
|
+
return `WAIT: ${parts.join(" — ")}`;
|
|
147
|
+
// Skipped, filtered, or superseded checks still count as reported CI data.
|
|
148
|
+
const reported = summary.skipped + summary.filtered + summary.superseded > 0;
|
|
149
|
+
return `WAIT: ${reported ? "no passing or in-progress checks" : "no checks reported"}`;
|
|
137
150
|
}
|
|
138
151
|
export function blockedCancelNote(base) {
|
|
139
152
|
if (base.mergeStatus !== "BLOCKED")
|
|
@@ -28,6 +28,8 @@ import { findStaleNativeStackAncestry } from "./stale-ancestry.mjs";
|
|
|
28
28
|
import { annotateBlockedWait, resolveCheckBlockerGate } from "./check-blocker-gate.mjs";
|
|
29
29
|
import { buildUnreportedFixResult, planUnreportedRequired } from "./unreported-required.mjs";
|
|
30
30
|
import { createCheckExecutionContext } from "../check-execution-context.mjs";
|
|
31
|
+
import { transportReadinessGaps } from "../../github/transport-evidence.mjs";
|
|
32
|
+
import { buildEscalateHumanMessage } from "./escalate.mjs";
|
|
31
33
|
export function runIterate(opts) {
|
|
32
34
|
return withIterateApiUsage(opts, () => runIterateCore(opts));
|
|
33
35
|
}
|
|
@@ -194,7 +196,7 @@ async function runIterateCore(opts) {
|
|
|
194
196
|
});
|
|
195
197
|
}
|
|
196
198
|
if (unreportedRepair.length > 0) {
|
|
197
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, buildUnreportedFixResult(base, report, unreportedRepair), reportForWork, reviewSummaryIds), blockerGate);
|
|
199
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, buildUnreportedFixResult(base, report, unreportedRepair), reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
198
200
|
}
|
|
199
201
|
const mergeStateResult = await handleActiveMergeState({
|
|
200
202
|
enabled: opts.merge,
|
|
@@ -234,6 +236,24 @@ async function runIterateCore(opts) {
|
|
|
234
236
|
const canMarkReady = report.status === "READY" &&
|
|
235
237
|
report.mergeStatus.isDraft &&
|
|
236
238
|
!report.mergeStatus.blockingBotReviewInProgress;
|
|
239
|
+
const gaps = transportReadinessGaps(report, report.mergeStatus.mergeStateStatus);
|
|
240
|
+
if (gaps.length > 0 &&
|
|
241
|
+
report.checks.inProgress.length === 0 &&
|
|
242
|
+
report.mergeStatus.mergeStateStatus !== "UNKNOWN") {
|
|
243
|
+
await clearReadyReceipt(receiptKey);
|
|
244
|
+
const escalation = {
|
|
245
|
+
triggers: ["transport-unsupported"],
|
|
246
|
+
unresolvedThreads: [],
|
|
247
|
+
ambiguousComments: [],
|
|
248
|
+
changesRequestedReviews: [],
|
|
249
|
+
suggestion: gaps.map(({ field, reason }) => `${field}: ${reason}`).join("; "),
|
|
250
|
+
};
|
|
251
|
+
return {
|
|
252
|
+
...base,
|
|
253
|
+
action: "escalate",
|
|
254
|
+
escalate: { ...escalation, humanMessage: buildEscalateHumanMessage(escalation, report.pr) },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
237
257
|
const autoMarkReady = !opts.noAutoMarkReady && config.actions.autoMarkReady;
|
|
238
258
|
const markReadyResult = await markReadyIfAuthorized(canMarkReady && autoMarkReady, base, report);
|
|
239
259
|
if (markReadyResult)
|
|
@@ -250,7 +270,7 @@ async function runIterateCore(opts) {
|
|
|
250
270
|
shouldCancel: false,
|
|
251
271
|
log: `WAIT: PR #${base.pr} reached ready-delay but its stack readiness receipt could not be persisted`,
|
|
252
272
|
};
|
|
253
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, receiptWait, reportForWork, reviewSummaryIds), blockerGate);
|
|
273
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, receiptWait, reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
254
274
|
}
|
|
255
275
|
await clearReadyDelay(report.pr, repoOwner, repoName);
|
|
256
276
|
await clearStallState(stallKey);
|
|
@@ -283,14 +303,15 @@ async function runIterateCore(opts) {
|
|
|
283
303
|
log: buildWaitLog(base),
|
|
284
304
|
...(hold && { stackDraftHold: hold }),
|
|
285
305
|
};
|
|
286
|
-
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, wait, reportForWork, reviewSummaryIds), blockerGate);
|
|
306
|
+
return annotateBlockedWait(await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, wait, reportForWork, reviewSummaryIds, opts.stallDeadlineSink), blockerGate);
|
|
287
307
|
}
|
|
288
308
|
async function recordReadyReceipt(key, report, context) {
|
|
289
309
|
if (report.status !== "READY" ||
|
|
290
310
|
report.mergeStatus.state !== "OPEN" ||
|
|
291
311
|
report.mergeStatus.isDraft ||
|
|
292
312
|
!report.headSha ||
|
|
293
|
-
!report.baseRefOid
|
|
313
|
+
!report.baseRefOid ||
|
|
314
|
+
transportReadinessGaps(report, report.mergeStatus.mergeStateStatus).length > 0)
|
|
294
315
|
return false;
|
|
295
316
|
try {
|
|
296
317
|
const raw = await receiptSummaryForReport(key, report, context);
|
|
@@ -357,6 +378,7 @@ async function revalidateReadyReceipt(key, report, hasReadinessWork, context) {
|
|
|
357
378
|
report.mergeStatus.isDraft ||
|
|
358
379
|
!report.headSha ||
|
|
359
380
|
!report.baseRefOid ||
|
|
381
|
+
transportReadinessGaps(report, report.mergeStatus.mergeStateStatus).length > 0 ||
|
|
360
382
|
(!retainQueuedReceipt && (report.status !== "READY" || hasReadinessWork))) {
|
|
361
383
|
await clearReadyReceipt(key);
|
|
362
384
|
return false;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { graphql } from "../../github/http.mjs";
|
|
2
2
|
import { MARK_PR_READY_MUTATION } from "../../github/queries.mjs";
|
|
3
3
|
import { buildEscalateHumanMessage, buildEscalateSuggestion } from "./escalate.mjs";
|
|
4
|
+
import { rest } from "../../github/http.mjs";
|
|
5
|
+
import { githubOperation, isCcrTransport } from "../../github/transport.mjs";
|
|
6
|
+
import { canGenerateGithubMutation } from "../../github/mutation-policy.mjs";
|
|
7
|
+
import { UnsupportedRestOperationError } from "../../github/unsupported-rest.mjs";
|
|
8
|
+
import { GitHubRequestError } from "../../github/errors.mjs";
|
|
9
|
+
import { isRestSessionRefusal } from "../../github/rest-session-refusal.mjs";
|
|
10
|
+
import { rateLimitKind } from "../../github/rate-limit-kind.mjs";
|
|
11
|
+
import { nonDraftMergeStatus } from "../../merge-status/derive.mjs";
|
|
4
12
|
export async function markReadyIfAuthorized(enabled, base, report) {
|
|
5
13
|
if (!enabled)
|
|
6
14
|
return null;
|
|
@@ -12,17 +20,49 @@ export async function markReadyIfAuthorized(enabled, base, report) {
|
|
|
12
20
|
log: `READY: PR #${report.pr} is ready to mark; refresh required before converting draft`,
|
|
13
21
|
};
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
let unsupported;
|
|
24
|
+
if (canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready")) {
|
|
25
|
+
try {
|
|
26
|
+
await githubOperation("MarkPrReady", async () => {
|
|
27
|
+
await graphql(MARK_PR_READY_MUTATION, { pullRequestId: report.nodeId });
|
|
28
|
+
}, async () => {
|
|
29
|
+
if (!isCcrTransport())
|
|
30
|
+
throw new UnsupportedRestOperationError("markPullRequestReadyForReview", "ready-for-review requires the cloud CCR proxy");
|
|
31
|
+
const response = await rest("POST", `/repos/${report.repo}/pulls/${report.pr}/ccr/ready_for_review`);
|
|
32
|
+
if (response?.draft !== false)
|
|
33
|
+
throw new Error("CCR ready_for_review did not confirm draft:false; refresh the PR before retrying");
|
|
34
|
+
}, { mutation: true });
|
|
35
|
+
// The snapshot predates the conversion; GitHub just confirmed the PR is no longer a draft.
|
|
36
|
+
// A raw DRAFT merge state has not been recomputed yet, so it becomes UNKNOWN.
|
|
37
|
+
const mergeStateStatus = base.mergeStateStatus === "DRAFT" ? "UNKNOWN" : base.mergeStateStatus;
|
|
38
|
+
return {
|
|
39
|
+
...base,
|
|
40
|
+
isDraft: false,
|
|
41
|
+
mergeStateStatus,
|
|
42
|
+
...(base.mergeStatus === "DRAFT" && { mergeStatus: nonDraftMergeStatus(mergeStateStatus) }),
|
|
43
|
+
action: "mark_ready",
|
|
44
|
+
markedReady: true,
|
|
45
|
+
log: `MARKED READY: PR #${report.pr} converted from draft to ready for review`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof UnsupportedRestOperationError ||
|
|
50
|
+
isRestSessionRefusal(error) ||
|
|
51
|
+
(error instanceof GitHubRequestError && error.status === 404))
|
|
52
|
+
unsupported = error.message;
|
|
53
|
+
else if (!(error instanceof GitHubRequestError) ||
|
|
54
|
+
error.status !== 403 ||
|
|
55
|
+
rateLimitKind(error) !== null)
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (report.transport === "rest" && report.viewerAuthorization?.viewerCanUpdate !== false) {
|
|
60
|
+
unsupported = "Ready-for-review requires the cloud CCR proxy";
|
|
23
61
|
}
|
|
24
62
|
const escalation = {
|
|
25
|
-
triggers: [
|
|
63
|
+
triggers: [
|
|
64
|
+
unsupported ? "transport-unsupported" : "authorization-required",
|
|
65
|
+
],
|
|
26
66
|
unresolvedThreads: [],
|
|
27
67
|
ambiguousComments: [],
|
|
28
68
|
changesRequestedReviews: [],
|
|
@@ -33,7 +73,7 @@ export async function markReadyIfAuthorized(enabled, base, report) {
|
|
|
33
73
|
reason: "denied-or-unverifiable",
|
|
34
74
|
},
|
|
35
75
|
],
|
|
36
|
-
suggestion: buildEscalateSuggestion(["authorization-required"]),
|
|
76
|
+
suggestion: unsupported ?? buildEscalateSuggestion(["authorization-required"]),
|
|
37
77
|
};
|
|
38
78
|
return {
|
|
39
79
|
...base,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type MergeMethod } from "../../config/merge-method.mts";
|
|
2
|
+
import type { MergeCommandPlan } from "../../types.mts";
|
|
3
|
+
import type { RestMergeStackGuard } from "../../github/rest-merge.mts";
|
|
4
|
+
export interface MergePlanInput {
|
|
5
|
+
transport?: "rest";
|
|
6
|
+
pr: number;
|
|
7
|
+
repo: string;
|
|
8
|
+
nodeId: string;
|
|
9
|
+
headSha: string;
|
|
10
|
+
queue: boolean;
|
|
11
|
+
queueKnown?: boolean;
|
|
12
|
+
/** Omitted when the batch did not select repository merge settings. */
|
|
13
|
+
allowedMergeMethods?: readonly MergeMethod[];
|
|
14
|
+
expectedStack?: RestMergeStackGuard;
|
|
15
|
+
}
|
|
16
|
+
export interface MergeMethodUnavailable {
|
|
17
|
+
unavailable: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function buildMergeCommandPlan(input: MergePlanInput): MergeCommandPlan | MergeMethodUnavailable;
|