pr-shepherd 0.56.6 → 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 +26 -9
- 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.mjs +11 -0
- package/bin/commands/iterate/render.d.mts +8 -2
- package/bin/commands/iterate/render.mjs +28 -42
- package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
- 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/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 +12 -3
- package/bin/github/merge-target-rules.mjs +71 -34
- 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 +3 -1
- package/bin/github/poll-summary-unreported.mjs +6 -3
- package/bin/github/poll-summary.d.mts +4 -1
- package/bin/github/poll-summary.mjs +47 -33
- package/bin/github/queries.d.mts +7 -3
- package/bin/github/queries.mjs +8 -4
- 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 -5
- package/bin/github/stack-read.mjs +22 -24
- 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,14 +1,16 @@
|
|
|
1
1
|
import { normalizeBotUsernames } from "../comments/authors.mjs";
|
|
2
2
|
import { applyRules } from "../classify/apply.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { loadClassifyRules } from "../classify/loader.mjs";
|
|
4
4
|
import { loadConfig } from "../config/load.mjs";
|
|
5
5
|
import { getEffectiveCwd } from "../execution-context.mjs";
|
|
6
6
|
import { classifyItem } from "../state/seen-comments.mjs";
|
|
7
|
+
import { mutationWasDenied } from "../state/seen-comments.mjs";
|
|
8
|
+
import { canGenerateGithubMutation } from "./mutation-policy.mjs";
|
|
7
9
|
const THREAD_COMMENT_SEPARATOR = "\n\n--- thread comment ---\n\n";
|
|
8
10
|
export async function summarizePollSummaryReview(raw, seen, viewerCanAdminister) {
|
|
9
11
|
const config = loadConfig();
|
|
10
12
|
const bots = normalizeBotUsernames(config.botUsernames);
|
|
11
|
-
const rules = await
|
|
13
|
+
const rules = await loadClassifyRules(getEffectiveCwd(), config.standardRules);
|
|
12
14
|
let actionable = 0;
|
|
13
15
|
let incomplete = raw.comments.pageInfo.hasPreviousPage ||
|
|
14
16
|
raw.reviews.pageInfo.hasPreviousPage ||
|
|
@@ -37,24 +39,31 @@ export async function summarizePollSummaryReview(raw, seen, viewerCanAdminister)
|
|
|
37
39
|
if (isSuppressed(rules, "changes-requested", review))
|
|
38
40
|
continue;
|
|
39
41
|
const isBot = review.author?.__typename === "Bot" || bots.has(review.author?.login.toLowerCase() ?? "");
|
|
40
|
-
if ((isBot &&
|
|
42
|
+
if ((isBot &&
|
|
43
|
+
canGenerateGithubMutation(viewerCanAdminister, "dismiss") &&
|
|
44
|
+
!mutationWasDenied(review.id, review.body, seen)) ||
|
|
41
45
|
classifyItem(review.id, review.body, seen) !== "unchanged")
|
|
42
46
|
actionable += 1;
|
|
43
47
|
}
|
|
44
48
|
for (const thread of raw.reviewThreads.nodes) {
|
|
45
|
-
|
|
49
|
+
// The window holds the first comments. Past it, a new reply leaves the window
|
|
50
|
+
// unchanged, so a long thread cannot prove it was seen.
|
|
51
|
+
const truncated = thread.comments.pageInfo.hasPreviousPage;
|
|
52
|
+
if (truncated)
|
|
46
53
|
incomplete = true;
|
|
47
|
-
const root = thread.
|
|
54
|
+
const root = thread.comments.nodes[0];
|
|
48
55
|
const transcript = thread.comments.nodes
|
|
49
56
|
.map((comment) => comment.body)
|
|
50
57
|
.join(THREAD_COMMENT_SEPARATOR);
|
|
51
58
|
if (isSuppressed(rules, "review-thread", root, thread.id, thread.path, transcript))
|
|
52
59
|
continue;
|
|
53
|
-
const repeatable =
|
|
54
|
-
root?.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
const repeatable = !mutationWasDenied(thread.id, transcript, seen) &&
|
|
61
|
+
(root?.viewerDidAuthor === true ||
|
|
62
|
+
root?.author?.__typename === "Bot" ||
|
|
63
|
+
bots.has(root?.author?.login.toLowerCase() ?? ""));
|
|
64
|
+
const unseen = truncated || classifyItem(thread.id, transcript, seen) !== "unchanged";
|
|
65
|
+
if ((thread.isResolved === false && (repeatable || unseen)) ||
|
|
66
|
+
(thread.isResolved === undefined && unseen) ||
|
|
58
67
|
((thread.isResolved || thread.isOutdated) && unseen))
|
|
59
68
|
actionable += 1;
|
|
60
69
|
}
|
|
@@ -81,5 +90,8 @@ function isSuppressed(rules, kind, raw, id = raw?.id ?? "unknown", path, body =
|
|
|
81
90
|
...(raw?.url && { url: raw.url }),
|
|
82
91
|
...(kind === "review-thread" && { path }),
|
|
83
92
|
};
|
|
84
|
-
|
|
93
|
+
const action = applyRules(rules, item);
|
|
94
|
+
// An auto-resolve match is hidden only once the full check minimizes or resolves it, or
|
|
95
|
+
// surfaces a denied mutation once; until then it stays actionable so that check runs.
|
|
96
|
+
return action.suppress === true && (action.autoResolve !== true || kind === "changes-requested");
|
|
85
97
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { loadConfig } from "../config/load.mjs";
|
|
2
|
+
import { isKnownMergeStateStatus, transportReadinessGaps } from "./transport-evidence.mjs";
|
|
3
|
+
import { canGenerateGithubMutation } from "./mutation-policy.mjs";
|
|
4
|
+
import { getGithubTransport, isCcrTransport } from "./transport.mjs";
|
|
2
5
|
export function routePollSummary(raw, checks, review, opts) {
|
|
3
6
|
const actions = loadConfig().actions;
|
|
4
7
|
const state = normalizePollSummaryState(raw.state);
|
|
@@ -22,7 +25,13 @@ export function routePollSummary(raw, checks, review, opts) {
|
|
|
22
25
|
return { action: "fix_code", reasons: ["unreported-required-checks"] };
|
|
23
26
|
}
|
|
24
27
|
if ((checks.inProgress ?? 0) > 0 ||
|
|
28
|
+
!isKnownMergeStateStatus(raw.mergeStateStatus) ||
|
|
25
29
|
raw.mergeable === "UNKNOWN" ||
|
|
30
|
+
raw.mergeStateStatus === "UNKNOWN")
|
|
31
|
+
return { action: "wait", reasons: ["pending-or-unknown"] };
|
|
32
|
+
if (transportReadinessGaps(raw, raw.mergeStateStatus).length > 0)
|
|
33
|
+
return { action: "escalate", reasons: ["transport-unsupported"] };
|
|
34
|
+
if (raw.mergeable === "UNKNOWN" ||
|
|
26
35
|
raw.mergeStateStatus === "UNKNOWN" ||
|
|
27
36
|
raw.mergeStateStatus === "BEHIND" ||
|
|
28
37
|
raw.mergeStateStatus === "BLOCKED" ||
|
|
@@ -33,15 +42,22 @@ export function routePollSummary(raw, checks, review, opts) {
|
|
|
33
42
|
const autoMarkReadyDisabled = opts.noAutoMarkReady || actions.autoMarkReady === false;
|
|
34
43
|
// The stack selector asks the agent to mark a disabled draft ready, so
|
|
35
44
|
// that transition needs the same capability as the automatic one.
|
|
36
|
-
if (!raw.viewerCanUpdate
|
|
45
|
+
if (!canGenerateGithubMutation(raw.viewerCanUpdate, "ready") &&
|
|
46
|
+
(!autoMarkReadyDisabled || opts.stackPrNumber !== undefined)) {
|
|
47
|
+
const unsupported = getGithubTransport() === "rest" && raw.viewerCanUpdate !== false && !isCcrTransport();
|
|
48
|
+
if (unsupported)
|
|
49
|
+
return { action: "escalate", reasons: ["transport-unsupported"] };
|
|
37
50
|
return { action: "escalate", reasons: ["mark-ready-authorization-required"] };
|
|
38
51
|
}
|
|
39
52
|
return autoMarkReadyDisabled
|
|
40
53
|
? { action: "wait", reasons: ["draft-auto-mark-ready-disabled"] }
|
|
41
54
|
: { action: "mark_ready", reasons: ["draft-appears-ready"] };
|
|
42
55
|
}
|
|
43
|
-
if (opts.merge && raw.isInMergeQueue) {
|
|
44
|
-
return {
|
|
56
|
+
if (opts.merge && (raw.isInMergeQueue || raw.autoMergeRequest)) {
|
|
57
|
+
return {
|
|
58
|
+
action: "wait",
|
|
59
|
+
reasons: [raw.isInMergeQueue ? "already-in-merge-queue" : "already-auto-merging"],
|
|
60
|
+
};
|
|
45
61
|
}
|
|
46
62
|
if (opts.merge && raw.stack && opts.stackPrNumber === undefined) {
|
|
47
63
|
return { action: "fix_code", reasons: ["authoritative-poll-required"] };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
3
|
+
import { type StackRead } from "./stack-read.mts";
|
|
4
|
+
/**
|
|
5
|
+
* Read the hydrated stack in pages of at most `stackSize` entries.
|
|
6
|
+
* A wide check matrix can blow GitHub's per-query resource limit on the full
|
|
7
|
+
* page. Halve `first` down to one entry and let `readStack` follow `after`.
|
|
8
|
+
* The shared summary fragment stays intact: one layer is the same shape as an
|
|
9
|
+
* explicit summary, and check hydration still completes windows past 100.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readGraphqlStackSummary(anchor: number, repo: RepoInfo, stackSize: number): Promise<StackRead<RawSummaryPr>>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GitHubRequestError, isRetryableGraphQlResourceLimit } from "./errors.mjs";
|
|
2
|
+
import { POLL_STACK_SUMMARY_QUERY } from "./queries.mjs";
|
|
3
|
+
import { MAX_STACK_ENTRIES_PER_PAGE, readStack } from "./stack-read.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Read the hydrated stack in pages of at most `stackSize` entries.
|
|
6
|
+
* A wide check matrix can blow GitHub's per-query resource limit on the full
|
|
7
|
+
* page. Halve `first` down to one entry and let `readStack` follow `after`.
|
|
8
|
+
* The shared summary fragment stays intact: one layer is the same shape as an
|
|
9
|
+
* explicit summary, and check hydration still completes windows past 100.
|
|
10
|
+
*/
|
|
11
|
+
export async function readGraphqlStackSummary(anchor, repo, stackSize) {
|
|
12
|
+
let ceiling = MAX_STACK_ENTRIES_PER_PAGE;
|
|
13
|
+
for (;;) {
|
|
14
|
+
const first = Math.max(1, Math.min(stackSize, ceiling));
|
|
15
|
+
try {
|
|
16
|
+
return await readStack(POLL_STACK_SUMMARY_QUERY, anchor, repo, { first }, ceiling);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (!(err instanceof GitHubRequestError) ||
|
|
20
|
+
!isRetryableGraphQlResourceLimit(err.graphqlErrors) ||
|
|
21
|
+
first <= 1) {
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
ceiling = Math.floor(first / 2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -3,5 +3,7 @@ import type { PollSummaryChecks } from "../types.mts";
|
|
|
3
3
|
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
4
4
|
/** Current trunk rules may remain available on a merged layer's base ref. */
|
|
5
5
|
export declare function trunkRequiredContexts(ordered: readonly RawSummaryPr[], repo: RepoInfo): Promise<string[] | undefined>;
|
|
6
|
+
/** Required contexts of the layer's merge target: the trunk's for a stack layer when known. */
|
|
7
|
+
export declare function summaryRequiredContexts(raw: RawSummaryPr, mergeTargetContexts: readonly string[] | undefined): string[];
|
|
6
8
|
/** Record missing required contexts. A running Actions workflow does not clear the names. */
|
|
7
|
-
export declare function applyUnreportedRequiredChecks(raw: RawSummaryPr, checks: PollSummaryChecks,
|
|
9
|
+
export declare function applyUnreportedRequiredChecks(raw: RawSummaryPr, checks: PollSummaryChecks, required: readonly string[]): void;
|
|
@@ -16,15 +16,18 @@ export async function trunkRequiredContexts(ordered, repo) {
|
|
|
16
16
|
const loaded = await loadRefRules(repo.owner, repo.name, `refs/heads/${trunk}`, bottom.headRefOid);
|
|
17
17
|
return loaded.contexts;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
export function
|
|
19
|
+
/** Required contexts of the layer's merge target: the trunk's for a stack layer when known. */
|
|
20
|
+
export function summaryRequiredContexts(raw, mergeTargetContexts) {
|
|
21
21
|
const trunk = raw.stack?.baseRefName;
|
|
22
|
-
|
|
22
|
+
return selectMergeTargetContexts({
|
|
23
23
|
localContexts: parseBranchRules(raw.baseRef).requiredStatusCheckContexts,
|
|
24
24
|
...(mergeTargetContexts !== undefined && { trunkContexts: mergeTargetContexts }),
|
|
25
25
|
baseRefName: raw.baseRefName,
|
|
26
26
|
...(trunk !== undefined && mergeTargetContexts !== undefined && { trunkRefName: trunk }),
|
|
27
27
|
});
|
|
28
|
+
}
|
|
29
|
+
/** Record missing required contexts. A running Actions workflow does not clear the names. */
|
|
30
|
+
export function applyUnreportedRequiredChecks(raw, checks, required) {
|
|
28
31
|
const missing = unreportedRequiredContexts(required, reportedNames(raw));
|
|
29
32
|
if (missing.length === 0)
|
|
30
33
|
return;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { PollSummaryCommandOptions, PollSummaryItem, PollSummarySelection, PollSummaryStackAncestry } from "../types.mts";
|
|
2
2
|
import { type RepoInfo } from "./client.mts";
|
|
3
3
|
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
4
|
+
import type { StackHandoffSource } from "./stack-handoff-page.mts";
|
|
4
5
|
export interface FetchedPollSummary {
|
|
5
6
|
selection: PollSummarySelection;
|
|
6
7
|
prs: PollSummaryItem[];
|
|
7
8
|
stackAncestry?: PollSummaryStackAncestry[];
|
|
8
9
|
allowedMergeMethods?: import("../config/merge-method.mts").MergeMethod[];
|
|
10
|
+
/** GraphQL stack reads only: the raw layers a routing tick hands to its one-PR sessions. */
|
|
11
|
+
handoffSource?: StackHandoffSource;
|
|
9
12
|
}
|
|
10
|
-
export declare function fetchPollSummary(opts: PollSummaryCommandOptions, repo: RepoInfo): Promise<FetchedPollSummary>;
|
|
11
13
|
/** Fresh, read-only snapshot used to bind a one-PR READY receipt to stack routing. */
|
|
12
14
|
export declare function fetchRawSummaryPr(pr: number, repo: RepoInfo): Promise<RawSummaryPr>;
|
|
15
|
+
export declare function fetchPollSummary(opts: PollSummaryCommandOptions, repo: RepoInfo): Promise<FetchedPollSummary>;
|
|
@@ -1,22 +1,29 @@
|
|
|
1
|
+
import { readRestStackSummary } from "./rest-stack-summary.mjs";
|
|
2
|
+
import { githubOperation, getGithubTransport, runWithGithubTransport } from "./transport.mjs";
|
|
3
|
+
import { readRestExplicitSummary } from "./rest-explicit-summary-read.mjs";
|
|
4
|
+
import { withRestConditionalScope } from "./rest-conditional-scope.mjs";
|
|
1
5
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
2
6
|
import { graphqlWithRateLimit } from "./client.mjs";
|
|
3
|
-
import {
|
|
7
|
+
import { missingRepositoryError } from "./errors.mjs";
|
|
4
8
|
import { hydratePollSummaryChecks } from "./poll-summary-check-hydration.mjs";
|
|
5
9
|
import { refreshUnknownSummaryMergeability } from "./poll-summary-mergeability.mjs";
|
|
6
10
|
import { summarizePollSummaryPr } from "./poll-summary-projector.mjs";
|
|
7
11
|
import { trunkRequiredContexts } from "./poll-summary-unreported.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
12
|
+
import { loadStackSizeHint, storeStackSizeHint } from "./stack-size-hint.mjs";
|
|
13
|
+
import { POLL_SUMMARY_FRAGMENT } from "./queries.mjs";
|
|
14
|
+
import { stackAncestryGaps } from "./stack-read.mjs";
|
|
15
|
+
import { readGraphqlStackSummary } from "./poll-summary-stack-page.mjs";
|
|
16
|
+
import { stackHandoffSource } from "./stack-handoff.mjs";
|
|
10
17
|
const MAX_EXPLICIT_PRS_PER_QUERY = 50;
|
|
11
|
-
|
|
18
|
+
async function fetchSelectedPollSummary(opts, repo) {
|
|
12
19
|
if (opts.stackPrNumber !== undefined)
|
|
13
20
|
return fetchStackSummary(opts, repo);
|
|
14
|
-
const requested =
|
|
21
|
+
const requested = [...new Set(opts.prNumbers ?? [])];
|
|
15
22
|
if (requested.length === 0) {
|
|
16
23
|
throw new ShepherdError("Aggregate poll requires at least two PRs or --stack <PR>", EXIT.USAGE);
|
|
17
24
|
}
|
|
18
25
|
const raw = [];
|
|
19
|
-
let viewerCanAdminister
|
|
26
|
+
let viewerCanAdminister;
|
|
20
27
|
for (let offset = 0; offset < requested.length; offset += MAX_EXPLICIT_PRS_PER_QUERY) {
|
|
21
28
|
const chunk = requested.slice(offset, offset + MAX_EXPLICIT_PRS_PER_QUERY);
|
|
22
29
|
const fetched = await fetchExplicitChunk(chunk, repo);
|
|
@@ -33,7 +40,7 @@ export async function fetchRawSummaryPr(pr, repo) {
|
|
|
33
40
|
const fetched = await fetchExplicitChunk([pr], repo);
|
|
34
41
|
return fetched.prs[0];
|
|
35
42
|
}
|
|
36
|
-
async function
|
|
43
|
+
async function fetchGraphqlExplicitChunk(prs, repo) {
|
|
37
44
|
const declarations = prs.map((_, index) => `$pr${index}: Int!`).join(", ");
|
|
38
45
|
const aliases = prs
|
|
39
46
|
.map((_, index) => `pr${index}: pullRequest(number: $pr${index}) { ...PollSummaryPr }`)
|
|
@@ -59,18 +66,39 @@ async function fetchExplicitChunk(prs, repo) {
|
|
|
59
66
|
}
|
|
60
67
|
return { prs: rawPrs, viewerCanAdminister: result.data.repository.viewerCanAdminister };
|
|
61
68
|
}
|
|
69
|
+
async function fetchExplicitChunk(prs, repo) {
|
|
70
|
+
return githubOperation("PollSummary", () => runWithGithubTransport("graphql", () => fetchGraphqlExplicitChunk(prs, repo)), async () => ({
|
|
71
|
+
prs: await withSummaryConditionalScope(repo, prs[0], () => readRestExplicitSummary(prs, repo)),
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Entries on an unsized first summary page. GitHub prices `first`, not the
|
|
76
|
+
* nodes returned, and four layers are the most that still price at 1 point.
|
|
77
|
+
* Larger stacks page on, sized to the entries that remain.
|
|
78
|
+
*/
|
|
79
|
+
const UNSIZED_STACK_SUMMARY_FIRST = 4;
|
|
62
80
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
81
|
+
* Size the hydrated page to the stack so it requests only as many entries as the
|
|
82
|
+
* stack holds. The size comes from the anchor's last summary; the first summary
|
|
83
|
+
* reads four entries and pages on, which never costs more than reading the
|
|
84
|
+
* topology first. A stack that has since grown pages on, and one that shrank
|
|
85
|
+
* overpays once.
|
|
65
86
|
*/
|
|
66
87
|
async function fetchStackSummary(opts, repo) {
|
|
67
88
|
const anchor = opts.stackPrNumber;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
89
|
+
const stateKey = { owner: repo.owner, repo: repo.name, pr: anchor };
|
|
90
|
+
const graphql = getGithubTransport() !== "rest";
|
|
91
|
+
const hint = graphql ? await loadStackSizeHint(stateKey) : 0;
|
|
92
|
+
const sizeHint = hint || UNSIZED_STACK_SUMMARY_FIRST;
|
|
93
|
+
const read = await readStackSummary(anchor, repo, sizeHint);
|
|
94
|
+
const { stackNumber, stackSize, viewerLogin, viewerCanAdminister, ordered, allowedMergeMethods } = read;
|
|
95
|
+
if (graphql && stackSize !== hint)
|
|
96
|
+
await storeStackSizeHint(stateKey, stackSize);
|
|
70
97
|
for (const pr of ordered) {
|
|
71
98
|
await hydratePollSummaryChecks(pr, repo);
|
|
72
99
|
await refreshUnknownSummaryMergeability(pr, repo);
|
|
73
100
|
}
|
|
101
|
+
const handoffSource = getGithubTransport() === "rest" ? undefined : stackHandoffSource(read);
|
|
74
102
|
const stackAncestry = stackAncestryGaps(ordered);
|
|
75
103
|
const required = await trunkRequiredContexts(ordered, repo);
|
|
76
104
|
return {
|
|
@@ -78,30 +106,16 @@ async function fetchStackSummary(opts, repo) {
|
|
|
78
106
|
prs: await Promise.all(ordered.map((pr) => summarizePollSummaryPr(pr, repo, opts, viewerCanAdminister, required, viewerLogin))),
|
|
79
107
|
...(stackAncestry.length > 0 && { stackAncestry }),
|
|
80
108
|
...(allowedMergeMethods && { allowedMergeMethods }),
|
|
109
|
+
...(handoffSource && { handoffSource }),
|
|
81
110
|
};
|
|
82
111
|
}
|
|
83
|
-
/**
|
|
84
|
-
* A wide check matrix can blow GitHub's per-query resource limit on the full
|
|
85
|
-
* page. Halve `first` down to one entry and let `readStack` follow `after`.
|
|
86
|
-
* The shared summary fragment stays intact: one layer is the same shape as an
|
|
87
|
-
* explicit summary, and check hydration still completes windows past 100.
|
|
88
|
-
*/
|
|
89
112
|
async function readStackSummary(anchor, repo, stackSize) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
catch (err) {
|
|
96
|
-
if (!(err instanceof GitHubRequestError) ||
|
|
97
|
-
!isRetryableGraphQlResourceLimit(err.graphqlErrors) ||
|
|
98
|
-
first <= 1) {
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
101
|
-
first = Math.floor(first / 2);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
113
|
+
return githubOperation("PollStackSummary", () => readGraphqlStackSummary(anchor, repo, stackSize), () => withSummaryConditionalScope(repo, anchor, () => readRestStackSummary(anchor, repo)));
|
|
114
|
+
}
|
|
115
|
+
/** REST summary reads are conditional under the anchor PR's state: an unchanged body is a free 304. */
|
|
116
|
+
function withSummaryConditionalScope(repo, pr, read) {
|
|
117
|
+
return withRestConditionalScope({ owner: repo.owner, repo: repo.name, pr }, read);
|
|
104
118
|
}
|
|
105
|
-
function
|
|
106
|
-
return
|
|
119
|
+
export function fetchPollSummary(opts, repo) {
|
|
120
|
+
return githubOperation("PollSummarySelection", () => runWithGithubTransport("graphql", () => fetchSelectedPollSummary(opts, repo)), () => fetchSelectedPollSummary(opts, repo));
|
|
107
121
|
}
|
package/bin/github/queries.d.mts
CHANGED
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
export declare const BATCH_PR_QUERY: string;
|
|
11
11
|
/** Slim @include follow-up for outstanding batch-query connections. */
|
|
12
12
|
export declare const BATCH_PR_PAGE_QUERY: string;
|
|
13
|
-
/** Cheap PR fingerprint used to skip an unchanged BatchPr snapshot. */
|
|
14
|
-
export declare const PR_FINGERPRINT_QUERY: string;
|
|
15
13
|
/** Compact per-PR fields shared by explicit-list and native-stack summary queries. */
|
|
16
14
|
export declare const POLL_SUMMARY_FRAGMENT: string;
|
|
17
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Exact compact summary sibling only when a one-PR READY receipt needs evidence.
|
|
17
|
+
* The batch's first context page shrinks to 50 so the sibling still prices at 1 point;
|
|
18
|
+
* `BatchPrPage` reads any further contexts.
|
|
19
|
+
*/
|
|
18
20
|
export declare const BATCH_PR_RECEIPT_QUERY: string;
|
|
19
21
|
/** Trunk branch rules plus how far `headRef` is behind that branch. */
|
|
20
22
|
export declare const REF_RULES_QUERY: string;
|
|
@@ -68,3 +70,5 @@ export declare const MARK_PR_READY_MUTATION: string;
|
|
|
68
70
|
export declare const GET_PR_BODY_QUERY: string;
|
|
69
71
|
/** Update the PR body. */
|
|
70
72
|
export declare const UPDATE_PR_BODY_MUTATION: string;
|
|
73
|
+
/** CheckRun titles, summaries, and first annotations for routed stack-layer handoffs. */
|
|
74
|
+
export declare const STACK_HANDOFF_CHECK_RUNS_QUERY: string;
|
package/bin/github/queries.mjs
CHANGED
|
@@ -14,13 +14,15 @@ const withSharedFragments = (query) => `${gql("ref-rules.gql")}\n${gql("pr-merge
|
|
|
14
14
|
export const BATCH_PR_QUERY = withSharedFragments(gql("batch-pr.gql"));
|
|
15
15
|
/** Slim @include follow-up for outstanding batch-query connections. */
|
|
16
16
|
export const BATCH_PR_PAGE_QUERY = gql("batch-pr-page.gql");
|
|
17
|
-
/** Cheap PR fingerprint used to skip an unchanged BatchPr snapshot. */
|
|
18
|
-
export const PR_FINGERPRINT_QUERY = withSharedFragments(gql("pr-fingerprint.gql"));
|
|
19
17
|
const POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT = gql("poll-summary-check-contexts.gql");
|
|
20
18
|
/** Compact per-PR fields shared by explicit-list and native-stack summary queries. */
|
|
21
19
|
export const POLL_SUMMARY_FRAGMENT = `${gql("ref-rules.gql")}\n${POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT}\n${gql("poll-summary-fragment.gql")}`;
|
|
22
|
-
/**
|
|
23
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Exact compact summary sibling only when a one-PR READY receipt needs evidence.
|
|
22
|
+
* The batch's first context page shrinks to 50 so the sibling still prices at 1 point;
|
|
23
|
+
* `BatchPrPage` reads any further contexts.
|
|
24
|
+
*/
|
|
25
|
+
export const BATCH_PR_RECEIPT_QUERY = `${POLL_SUMMARY_CHECK_CONTEXTS_FRAGMENT}\n${gql("poll-summary-fragment.gql")}\n${BATCH_PR_QUERY.replace(" pullRequest(number: $pr) {", " receiptSummary: pullRequest(number: $pr) { ...PollSummaryPr }\n pullRequest(number: $pr) {").replace("contexts(first: 100) {", "contexts(first: 50) {")}`;
|
|
24
26
|
/** Trunk branch rules plus how far `headRef` is behind that branch. */
|
|
25
27
|
export const REF_RULES_QUERY = `${gql("ref-rules.gql")}\n${gql("ref-rules-query.gql")}`;
|
|
26
28
|
/** How far a non-stack head is behind its PR base. One compare, no rules payload. */
|
|
@@ -73,3 +75,5 @@ export const MARK_PR_READY_MUTATION = gql("mark-pr-ready.gql");
|
|
|
73
75
|
export const GET_PR_BODY_QUERY = gql("get-pr-body.gql");
|
|
74
76
|
/** Update the PR body. */
|
|
75
77
|
export const UPDATE_PR_BODY_MUTATION = gql("update-pr-body.gql");
|
|
78
|
+
/** CheckRun titles, summaries, and first annotations for routed stack-layer handoffs. */
|
|
79
|
+
export const STACK_HANDOFF_CHECK_RUNS_QUERY = gql("stack-handoff-check-runs.gql");
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RateLimitInfo } from "./http-utils.mts";
|
|
2
|
+
/**
|
|
3
|
+
* Same reset window: the lowest remaining and the highest used, taken independently. The Claude
|
|
4
|
+
* Code proxy can answer one tick's routes with `X-RateLimit-Used` values that disagree with each
|
|
5
|
+
* other and with `X-RateLimit-Remaining`, so neither half of one sample is trusted over the other.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mostConservativeSample<T extends Pick<RateLimitInfo, "remaining" | "used">>(current: T, candidate: T): T;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same reset window: the lowest remaining and the highest used, taken independently. The Claude
|
|
3
|
+
* Code proxy can answer one tick's routes with `X-RateLimit-Used` values that disagree with each
|
|
4
|
+
* other and with `X-RateLimit-Remaining`, so neither half of one sample is trusted over the other.
|
|
5
|
+
*/
|
|
6
|
+
export function mostConservativeSample(current, candidate) {
|
|
7
|
+
const base = candidate.remaining < current.remaining ? candidate : current;
|
|
8
|
+
const used = current.used === undefined || candidate.used === undefined
|
|
9
|
+
? (current.used ?? candidate.used)
|
|
10
|
+
: Math.max(current.used, candidate.used);
|
|
11
|
+
return { ...base, ...(used !== undefined && { used }) };
|
|
12
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type RepoInfo } from "./client.mts";
|
|
2
|
+
export interface ReplyEvidence {
|
|
3
|
+
viewer: string;
|
|
4
|
+
comments: Array<{
|
|
5
|
+
id: string;
|
|
6
|
+
body: string;
|
|
7
|
+
author: string | null;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
interface Page {
|
|
11
|
+
totalCount: number;
|
|
12
|
+
pageInfo: {
|
|
13
|
+
hasNextPage: boolean;
|
|
14
|
+
endCursor: string | null;
|
|
15
|
+
};
|
|
16
|
+
nodes: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
databaseId: number;
|
|
19
|
+
body: string;
|
|
20
|
+
author: {
|
|
21
|
+
login: string;
|
|
22
|
+
} | null;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
interface Node {
|
|
26
|
+
id: string;
|
|
27
|
+
pullRequest: {
|
|
28
|
+
number: number;
|
|
29
|
+
repository: {
|
|
30
|
+
nameWithOwner: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
comments: Page;
|
|
34
|
+
}
|
|
35
|
+
export interface ReplyEvidenceResponse {
|
|
36
|
+
viewer: {
|
|
37
|
+
login: string;
|
|
38
|
+
} | null;
|
|
39
|
+
nodes: Array<Node | null>;
|
|
40
|
+
}
|
|
41
|
+
/** Thread selection shared with the `apply review` preflight, which needs `$cursor: String`. */
|
|
42
|
+
export declare const REPLY_EVIDENCE_THREAD_SELECTION = "... on PullRequestReviewThread {\n id pullRequest { number repository { nameWithOwner } }\n comments(first: 100, after: $cursor) {\n totalCount\n pageInfo { hasNextPage endCursor }\n nodes { id databaseId body author { login } }\n }\n }";
|
|
43
|
+
/**
|
|
44
|
+
* Complete evidence for one GraphQL thread, starting from a response that already holds its first
|
|
45
|
+
* comment page. Later pages use `ReplyRecoveryEvidence`. Throws when anything is unverifiable.
|
|
46
|
+
*/
|
|
47
|
+
export declare function threadReplyEvidence(repo: RepoInfo, pr: number, id: string, first: ReplyEvidenceResponse): Promise<ReplyEvidence>;
|
|
48
|
+
/** Complete, bounded evidence for one mutation batch; unavailable evidence never certifies a retry. */
|
|
49
|
+
export declare function readReplyRecoveryEvidence(repo: RepoInfo, pr: number, requestedIds: readonly string[]): Promise<Map<string, ReplyEvidence>>;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { graphqlWithRateLimit } from "./client.mjs";
|
|
2
|
+
import { githubOperation } from "./transport.mjs";
|
|
3
|
+
import { recordThreadIdentity, resolveGraphqlThreadId, resolveRestThreadRoot, } from "./rest-identities.mjs";
|
|
4
|
+
import { readRest, readRestPages, restRepoPath } from "./rest-reader-core.mjs";
|
|
5
|
+
import { mapPool } from "../util/pool.mjs";
|
|
6
|
+
/** Thread selection shared with the `apply review` preflight, which needs `$cursor: String`. */
|
|
7
|
+
export const REPLY_EVIDENCE_THREAD_SELECTION = `... on PullRequestReviewThread {
|
|
8
|
+
id pullRequest { number repository { nameWithOwner } }
|
|
9
|
+
comments(first: 100, after: $cursor) {
|
|
10
|
+
totalCount
|
|
11
|
+
pageInfo { hasNextPage endCursor }
|
|
12
|
+
nodes { id databaseId body author { login } }
|
|
13
|
+
}
|
|
14
|
+
}`;
|
|
15
|
+
const QUERY = `query ReplyRecoveryEvidence($ids: [ID!]!, $cursor: String) {
|
|
16
|
+
_shepherdRateLimit: rateLimit { cost limit nodeCount remaining resetAt used }
|
|
17
|
+
viewer { login }
|
|
18
|
+
nodes(ids: $ids) { ${REPLY_EVIDENCE_THREAD_SELECTION} }
|
|
19
|
+
}`;
|
|
20
|
+
const readEvidencePage = (ids, cursor) => graphqlWithRateLimit(QUERY, { ids, cursor });
|
|
21
|
+
/**
|
|
22
|
+
* Complete evidence for one GraphQL thread, starting from a response that already holds its first
|
|
23
|
+
* comment page. Later pages use `ReplyRecoveryEvidence`. Throws when anything is unverifiable.
|
|
24
|
+
*/
|
|
25
|
+
export async function threadReplyEvidence(repo, pr, id, first) {
|
|
26
|
+
const viewer = first.viewer?.login;
|
|
27
|
+
if (typeof viewer !== "string" || !viewer)
|
|
28
|
+
throw new Error("Reply recovery viewer is unavailable");
|
|
29
|
+
let node = first.nodes.find((item) => item?.id === id);
|
|
30
|
+
const comments = [];
|
|
31
|
+
const cursors = new Set();
|
|
32
|
+
const total = node?.comments?.totalCount;
|
|
33
|
+
const root = node?.comments?.nodes?.[0]?.databaseId;
|
|
34
|
+
const seenIds = new Set();
|
|
35
|
+
for (let page = 0; page < 100; page++) {
|
|
36
|
+
if (!node ||
|
|
37
|
+
node.pullRequest?.number !== pr ||
|
|
38
|
+
node.pullRequest.repository.nameWithOwner.toLowerCase() !==
|
|
39
|
+
`${repo.owner}/${repo.name}`.toLowerCase() ||
|
|
40
|
+
!node.comments?.pageInfo ||
|
|
41
|
+
typeof node.comments.pageInfo.hasNextPage !== "boolean" ||
|
|
42
|
+
!Array.isArray(node.comments.nodes) ||
|
|
43
|
+
!Number.isSafeInteger(total) ||
|
|
44
|
+
total < 1 ||
|
|
45
|
+
node.comments.totalCount !== total)
|
|
46
|
+
throw new Error("Reply recovery transcript is unavailable");
|
|
47
|
+
if (page === 0) {
|
|
48
|
+
if (!Number.isSafeInteger(root) || root < 1)
|
|
49
|
+
throw new Error("Reply recovery root identity is unavailable");
|
|
50
|
+
await recordThreadIdentity(repo, pr, id, root);
|
|
51
|
+
}
|
|
52
|
+
for (const comment of node.comments.nodes) {
|
|
53
|
+
if (typeof comment.id !== "string" ||
|
|
54
|
+
!comment.id ||
|
|
55
|
+
seenIds.has(comment.id) ||
|
|
56
|
+
typeof comment.body !== "string" ||
|
|
57
|
+
(comment.author !== null && typeof comment.author?.login !== "string"))
|
|
58
|
+
throw new Error("Reply recovery comment is incomplete");
|
|
59
|
+
seenIds.add(comment.id);
|
|
60
|
+
comments.push({ id: comment.id, body: comment.body, author: comment.author?.login ?? null });
|
|
61
|
+
}
|
|
62
|
+
if (!node.comments.pageInfo.hasNextPage && comments.length === total)
|
|
63
|
+
return { viewer, comments };
|
|
64
|
+
if (!node.comments.pageInfo.hasNextPage)
|
|
65
|
+
break;
|
|
66
|
+
const cursor = node.comments.pageInfo.endCursor;
|
|
67
|
+
if (!cursor || cursors.has(cursor))
|
|
68
|
+
break;
|
|
69
|
+
cursors.add(cursor);
|
|
70
|
+
const next = await readEvidencePage([id], cursor);
|
|
71
|
+
if (next.data.viewer?.login !== viewer)
|
|
72
|
+
throw new Error("Reply recovery viewer changed");
|
|
73
|
+
node = next.data.nodes.find((item) => item?.id === id);
|
|
74
|
+
}
|
|
75
|
+
throw new Error("Reply recovery transcript pagination is incomplete");
|
|
76
|
+
}
|
|
77
|
+
/** Complete, bounded evidence for one mutation batch; unavailable evidence never certifies a retry. */
|
|
78
|
+
export async function readReplyRecoveryEvidence(repo, pr, requestedIds) {
|
|
79
|
+
if (requestedIds.length === 0)
|
|
80
|
+
return new Map();
|
|
81
|
+
if (requestedIds.length > 10)
|
|
82
|
+
throw new Error("Reply recovery batch exceeds 10 threads");
|
|
83
|
+
return githubOperation("ReplyRecoveryEvidence", async () => {
|
|
84
|
+
const ids = await Promise.all(requestedIds.map(resolveGraphqlThreadId));
|
|
85
|
+
const first = await readEvidencePage(ids, null);
|
|
86
|
+
const viewer = first.data.viewer?.login;
|
|
87
|
+
if (typeof viewer !== "string" || !viewer)
|
|
88
|
+
throw new Error("Reply recovery viewer is unavailable");
|
|
89
|
+
const entries = await mapPool(ids, 4, async (id, index) => [requestedIds[index], await threadReplyEvidence(repo, pr, id, first.data)]);
|
|
90
|
+
return new Map(entries);
|
|
91
|
+
}, async () => {
|
|
92
|
+
const viewer = await readRest("GET", "/user");
|
|
93
|
+
if (typeof viewer.login !== "string" || !viewer.login)
|
|
94
|
+
throw new Error("Reply recovery viewer is unavailable");
|
|
95
|
+
const inline = (await readRestPages(`${restRepoPath(repo)}/pulls/${pr}/comments`)).nodes;
|
|
96
|
+
const entries = await Promise.all(requestedIds.map(async (id) => {
|
|
97
|
+
const root = await resolveRestThreadRoot(repo, pr, id);
|
|
98
|
+
if (!inline.some((comment) => String(comment.id) === root))
|
|
99
|
+
throw new Error("Reply recovery thread is unavailable");
|
|
100
|
+
const comments = inline
|
|
101
|
+
.filter((comment) => String(comment.id) === root || String(comment.in_reply_to_id) === root)
|
|
102
|
+
.map((comment) => {
|
|
103
|
+
if (typeof comment.node_id !== "string" ||
|
|
104
|
+
!comment.node_id ||
|
|
105
|
+
typeof comment.body !== "string" ||
|
|
106
|
+
(comment.user !== null && typeof comment.user?.login !== "string"))
|
|
107
|
+
throw new Error("Reply recovery comment is incomplete");
|
|
108
|
+
return {
|
|
109
|
+
id: comment.node_id,
|
|
110
|
+
body: comment.body,
|
|
111
|
+
author: comment.user?.login ?? null,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
return [id, { viewer: viewer.login, comments }];
|
|
115
|
+
}));
|
|
116
|
+
return new Map(entries);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { type RepoInfo } from "./client.mts";
|
|
2
|
-
/** Best-effort transcript evidence; it never filters user-supplied mutation IDs. */
|
|
3
2
|
export declare function fetchReplyThreadTranscripts(pr: number, repo: RepoInfo, requestedIds: readonly string[]): Promise<Map<string, string>>;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { githubOperation } from "./transport.mjs";
|
|
2
|
+
import { readRestFeedback } from "./rest-feedback-read.mjs";
|
|
3
|
+
import { resolveRestThreadRoot, restThreadId } from "./rest-identities.mjs";
|
|
1
4
|
import { graphqlWithRateLimit } from "./client.mjs";
|
|
2
5
|
import { GitHubRequestError } from "./errors.mjs";
|
|
3
6
|
import { rateLimitKind } from "./rate-limit-kind.mjs";
|
|
@@ -8,7 +11,7 @@ const IDS_PER_REQUEST = 20;
|
|
|
8
11
|
const CONCURRENCY = 4;
|
|
9
12
|
const MAX_COMMENT_PAGES = 100;
|
|
10
13
|
/** Best-effort transcript evidence; it never filters user-supplied mutation IDs. */
|
|
11
|
-
|
|
14
|
+
async function fetchGraphqlReplyThreadTranscripts(pr, repo, requestedIds) {
|
|
12
15
|
const ids = [...new Set(requestedIds)];
|
|
13
16
|
if (ids.length === 0)
|
|
14
17
|
return new Map();
|
|
@@ -87,3 +90,23 @@ async function completeThread(thread, isStopped, stop) {
|
|
|
87
90
|
function isThrottle(error) {
|
|
88
91
|
return error instanceof GitHubRequestError && rateLimitKind(error) !== null;
|
|
89
92
|
}
|
|
93
|
+
export function fetchReplyThreadTranscripts(pr, repo, requestedIds) {
|
|
94
|
+
if (requestedIds.length === 0)
|
|
95
|
+
return Promise.resolve(new Map());
|
|
96
|
+
return githubOperation("ReplyThreadTranscripts", () => fetchGraphqlReplyThreadTranscripts(pr, repo, requestedIds), async () => {
|
|
97
|
+
const feedback = await readRestFeedback(pr, repo);
|
|
98
|
+
const result = new Map();
|
|
99
|
+
for (const id of new Set(requestedIds)) {
|
|
100
|
+
try {
|
|
101
|
+
const root = await resolveRestThreadRoot(repo, pr, id);
|
|
102
|
+
const thread = feedback.threads.find((thread) => thread.id === restThreadId(root));
|
|
103
|
+
if (thread)
|
|
104
|
+
result.set(id, threadTranscriptBodies((thread.comments ?? []).map((comment) => comment.body)));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* Transcript evidence never prohibits an explicit mutation. */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CheckAnnotation } from "../types.mts";
|
|
2
|
+
import type { AnnotationCacheOptions } from "./check-annotation-cache.mts";
|
|
3
|
+
/** An annotation fallback can follow a GraphQL snapshot with no persisted REST check IDs. */
|
|
4
|
+
export declare function ensureRestCheckIdentities(checkRunIds: string[], scope?: AnnotationCacheOptions): Promise<void>;
|
|
5
|
+
export declare function readRestCheckAnnotations(checkRunId: string, scope?: AnnotationCacheOptions): Promise<CheckAnnotation[]>;
|