pr-shepherd 0.56.6 → 0.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +150 -280
- 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/codex-notices.d.mts +8 -0
- package/bin/classify/standard/codex-notices.mjs +34 -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 +7 -0
- package/bin/classify/standard/index.mjs +21 -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 +57 -16
- 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/captured-proxy-agent.d.mts +3 -0
- package/bin/github/captured-proxy-agent.mjs +8 -0
- 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 +32 -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/legacy-dispatcher.d.mts +6 -0
- package/bin/github/legacy-dispatcher.mjs +44 -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.d.mts +6 -1
- package/bin/github/poll-summary-readiness.mjs +13 -4
- 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 +35 -20
- 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 +4 -3
- 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 +3 -1
- 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,9 +1,11 @@
|
|
|
1
|
+
import { mostConservativeSample } from "./rate-limit-sample.mjs";
|
|
1
2
|
export function emptyAggregate() {
|
|
2
3
|
return {
|
|
3
4
|
eventCount: 0,
|
|
4
5
|
credentialSources: new Map(),
|
|
5
6
|
graphql: { requestCount: 0, measuredQueryCost: 0, unmeasuredRequestCount: 0, nodeCount: 0 },
|
|
6
7
|
rest: new Map(),
|
|
8
|
+
unmeteredRestRequestCount: 0,
|
|
7
9
|
};
|
|
8
10
|
}
|
|
9
11
|
export function aggregateEvents(events) {
|
|
@@ -25,12 +27,19 @@ export function aggregateEvents(events) {
|
|
|
25
27
|
}
|
|
26
28
|
continue;
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (event.rateLimit !== undefined) {
|
|
32
|
-
group.rateLimit = selectAuthoritativeRateLimit(group.rateLimit, event.rateLimit);
|
|
30
|
+
if (event.rateLimit === undefined && !event.notModified) {
|
|
31
|
+
aggregate.unmeteredRestRequestCount += 1;
|
|
32
|
+
continue;
|
|
33
33
|
}
|
|
34
|
+
const resource = event.rateLimit?.resource ?? (event.notModified ? "core" : "unknown");
|
|
35
|
+
const group = aggregate.rest.get(resource) ?? { requestCount: 0, notModifiedCount: 0 };
|
|
36
|
+
if (event.notModified)
|
|
37
|
+
group.notModifiedCount += 1;
|
|
38
|
+
else
|
|
39
|
+
group.requestCount += 1;
|
|
40
|
+
const sample = event.rateLimit;
|
|
41
|
+
if (sample)
|
|
42
|
+
adoptRestRateLimit(group, sample, event.credentialFingerprint, event.sequence);
|
|
34
43
|
aggregate.rest.set(resource, group);
|
|
35
44
|
}
|
|
36
45
|
return aggregate;
|
|
@@ -47,14 +56,16 @@ export function mergeAggregate(target, source) {
|
|
|
47
56
|
target.graphql.measuredQueryCost += source.graphql.measuredQueryCost;
|
|
48
57
|
target.graphql.unmeasuredRequestCount += source.graphql.unmeasuredRequestCount;
|
|
49
58
|
target.graphql.nodeCount += source.graphql.nodeCount;
|
|
59
|
+
target.unmeteredRestRequestCount += source.unmeteredRestRequestCount;
|
|
50
60
|
if (source.graphql.rateLimit !== undefined) {
|
|
51
61
|
adoptGraphqlRateLimit(target.graphql, source.graphql.rateLimit, source.graphql.credentialFingerprint);
|
|
52
62
|
}
|
|
53
63
|
for (const [resource, sourceGroup] of source.rest) {
|
|
54
|
-
const targetGroup = target.rest.get(resource) ?? { requestCount: 0 };
|
|
64
|
+
const targetGroup = target.rest.get(resource) ?? { requestCount: 0, notModifiedCount: 0 };
|
|
55
65
|
targetGroup.requestCount += sourceGroup.requestCount;
|
|
66
|
+
targetGroup.notModifiedCount += sourceGroup.notModifiedCount;
|
|
56
67
|
if (sourceGroup.rateLimit !== undefined) {
|
|
57
|
-
targetGroup.rateLimit
|
|
68
|
+
adoptRestRateLimit(targetGroup, sourceGroup.rateLimit, sourceGroup.credentialFingerprint, sourceGroup.latestCredentialSequence, sourceGroup.latestCredentialFingerprint ?? sourceGroup.credentialFingerprint, sourceGroup.latestCredentialSequence);
|
|
58
69
|
}
|
|
59
70
|
target.rest.set(resource, targetGroup);
|
|
60
71
|
}
|
|
@@ -66,25 +77,53 @@ export function aggregateStore(active) {
|
|
|
66
77
|
return all;
|
|
67
78
|
}
|
|
68
79
|
function adoptGraphqlRateLimit(graphql, candidate, fingerprint) {
|
|
69
|
-
|
|
80
|
+
// Two credentials' samples are compared whole, never mixed: a mixed sample would be
|
|
81
|
+
// attributed to whichever credential supplied the changed half.
|
|
82
|
+
const sameCredential = fingerprint === undefined ||
|
|
83
|
+
graphql.credentialFingerprint === undefined ||
|
|
84
|
+
fingerprint === graphql.credentialFingerprint;
|
|
85
|
+
const next = selectAuthoritativeRateLimit(graphql.rateLimit, candidate, sameCredential);
|
|
70
86
|
if (next !== graphql.rateLimit)
|
|
71
87
|
graphql.credentialFingerprint = fingerprint;
|
|
72
88
|
graphql.rateLimit = next;
|
|
73
89
|
}
|
|
74
|
-
function
|
|
90
|
+
function adoptRestRateLimit(group, candidate, fingerprint, sequence, latestCredentialFingerprint = fingerprint, latestCredentialSequence = sequence) {
|
|
91
|
+
const credentialChanged = group.latestCredentialFingerprint !== undefined &&
|
|
92
|
+
latestCredentialFingerprint !== undefined &&
|
|
93
|
+
group.latestCredentialFingerprint !== latestCredentialFingerprint;
|
|
94
|
+
const olderCredentialObservation = credentialChanged &&
|
|
95
|
+
group.latestCredentialSequence !== undefined &&
|
|
96
|
+
latestCredentialSequence !== undefined &&
|
|
97
|
+
latestCredentialSequence < group.latestCredentialSequence;
|
|
98
|
+
if (olderCredentialObservation)
|
|
99
|
+
return;
|
|
100
|
+
if (group.rateLimit === undefined || credentialChanged) {
|
|
101
|
+
group.rateLimit = { ...candidate };
|
|
102
|
+
group.credentialFingerprint = fingerprint;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const next = selectAuthoritativeRateLimit(group.rateLimit, candidate);
|
|
106
|
+
if (next !== group.rateLimit) {
|
|
107
|
+
group.rateLimit = next;
|
|
108
|
+
group.credentialFingerprint = fingerprint;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (latestCredentialFingerprint !== undefined &&
|
|
112
|
+
(group.latestCredentialSequence === undefined ||
|
|
113
|
+
latestCredentialSequence === undefined ||
|
|
114
|
+
latestCredentialSequence >= group.latestCredentialSequence)) {
|
|
115
|
+
group.latestCredentialFingerprint = latestCredentialFingerprint;
|
|
116
|
+
group.latestCredentialSequence = latestCredentialSequence;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function selectAuthoritativeRateLimit(current, candidate, sameCredential = true) {
|
|
75
120
|
if (current === undefined)
|
|
76
121
|
return { ...candidate };
|
|
77
122
|
if (candidate.resetAt !== current.resetAt) {
|
|
78
123
|
return candidate.resetAt > current.resetAt ? { ...candidate } : current;
|
|
79
124
|
}
|
|
80
|
-
if (
|
|
125
|
+
if (!sameCredential)
|
|
81
126
|
return candidate.remaining < current.remaining ? { ...candidate } : current;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (candidate.used === undefined)
|
|
85
|
-
return current;
|
|
86
|
-
if (current.used === undefined || candidate.used > current.used)
|
|
87
|
-
return { ...candidate };
|
|
88
|
-
}
|
|
89
|
-
return current;
|
|
127
|
+
const merged = mostConservativeSample(current, candidate);
|
|
128
|
+
return merged.remaining === current.remaining && merged.used === current.used ? current : merged;
|
|
90
129
|
}
|
|
@@ -7,6 +7,8 @@ export interface ApiTelemetryEvent {
|
|
|
7
7
|
/** Truncated SHA-256 of the credential that produced this request. */
|
|
8
8
|
credentialFingerprint?: string;
|
|
9
9
|
rateLimit?: RateLimitInfo;
|
|
10
|
+
/** HTTP 304 to a conditional read: consumes no primary quota, so it is not a counted request. */
|
|
11
|
+
notModified?: boolean;
|
|
10
12
|
}
|
|
11
13
|
/** Isolates a top-level CLI/MCP command while allowing nested iterate ticks to aggregate. */
|
|
12
14
|
export declare function withApiTelemetryScope<T>(fn: () => Promise<T>): Promise<T>;
|
|
@@ -14,4 +16,6 @@ export declare function recordApiTelemetry(event: ApiTelemetryEvent): void;
|
|
|
14
16
|
export declare function mergeGraphqlRateLimit(headerRateLimit: RateLimitInfo | null, data: unknown): RateLimitInfo | null;
|
|
15
17
|
/** Attach the credential fingerprint without adding it when this command has none. */
|
|
16
18
|
export declare function withGraphqlCredentialFingerprint<T extends object>(sample: T): T;
|
|
19
|
+
/** Attach the REST core credential fingerprint without exposing it in API usage output. */
|
|
20
|
+
export declare function withRestCoreCredentialFingerprint<T extends object>(sample: T): T;
|
|
17
21
|
export declare function summarizeApiTelemetry(): ApiUsage | undefined;
|
|
@@ -72,11 +72,23 @@ function graphqlQuotaCredentialFingerprint() {
|
|
|
72
72
|
return undefined;
|
|
73
73
|
return aggregateStore(active).graphql.credentialFingerprint;
|
|
74
74
|
}
|
|
75
|
+
/** Fingerprint of the credential that owns the authoritative REST core sample. */
|
|
76
|
+
function restCoreQuotaCredentialFingerprint() {
|
|
77
|
+
const active = store();
|
|
78
|
+
if (active === undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
return aggregateStore(active).rest.get("core")?.credentialFingerprint;
|
|
81
|
+
}
|
|
75
82
|
/** Attach the credential fingerprint without adding it when this command has none. */
|
|
76
83
|
export function withGraphqlCredentialFingerprint(sample) {
|
|
77
84
|
const credentialFingerprint = graphqlQuotaCredentialFingerprint();
|
|
78
85
|
return credentialFingerprint === undefined ? sample : { ...sample, credentialFingerprint };
|
|
79
86
|
}
|
|
87
|
+
/** Attach the REST core credential fingerprint without exposing it in API usage output. */
|
|
88
|
+
export function withRestCoreCredentialFingerprint(sample) {
|
|
89
|
+
const credentialFingerprint = restCoreQuotaCredentialFingerprint();
|
|
90
|
+
return credentialFingerprint === undefined ? sample : { ...sample, credentialFingerprint };
|
|
91
|
+
}
|
|
80
92
|
export function summarizeApiTelemetry() {
|
|
81
93
|
const active = store();
|
|
82
94
|
if (active === undefined)
|
|
@@ -89,32 +101,34 @@ export function summarizeApiTelemetry() {
|
|
|
89
101
|
.map(([source]) => source);
|
|
90
102
|
const graphql = summarizeGraphql(selected.graphql);
|
|
91
103
|
const rest = summarizeRest(selected.rest);
|
|
104
|
+
const unmeteredRestRequestCount = selected.unmeteredRestRequestCount;
|
|
92
105
|
return {
|
|
93
106
|
credentialSources,
|
|
94
107
|
...(graphql !== undefined && { graphql }),
|
|
95
108
|
...(rest.length > 0 && { rest }),
|
|
109
|
+
...(unmeteredRestRequestCount > 0 && { unmeteredRestRequestCount }),
|
|
96
110
|
};
|
|
97
111
|
}
|
|
98
112
|
function summarizeGraphql(selected) {
|
|
99
|
-
|
|
100
|
-
if (rateLimit === undefined)
|
|
113
|
+
if (selected.rateLimit === undefined)
|
|
101
114
|
return undefined;
|
|
102
115
|
return {
|
|
103
|
-
...resourceUsage(rateLimit, selected.requestCount, "graphql"),
|
|
116
|
+
...resourceUsage(selected.rateLimit, selected.requestCount, "graphql"),
|
|
104
117
|
measuredQueryCost: selected.measuredQueryCost,
|
|
105
118
|
unmeasuredRequestCount: selected.unmeasuredRequestCount,
|
|
106
119
|
nodeCount: selected.nodeCount,
|
|
107
120
|
};
|
|
108
121
|
}
|
|
109
122
|
function summarizeRest(selected) {
|
|
110
|
-
return [...selected.entries()].flatMap(([resource, group]) =>
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
return [...selected.entries()].flatMap(([resource, group]) => group.rateLimit
|
|
124
|
+
? [resourceUsage(group.rateLimit, group.requestCount, resource, group.notModifiedCount)]
|
|
125
|
+
: []);
|
|
113
126
|
}
|
|
114
|
-
function resourceUsage(rateLimit, requestCount, fallbackResource) {
|
|
127
|
+
function resourceUsage(rateLimit, requestCount, fallbackResource, notModified = 0) {
|
|
115
128
|
return {
|
|
116
129
|
resource: rateLimit.resource ?? fallbackResource,
|
|
117
|
-
requestCount,
|
|
130
|
+
...(requestCount > 0 && { requestCount }),
|
|
131
|
+
...(notModified > 0 && { notModified }),
|
|
118
132
|
limit: rateLimit.limit,
|
|
119
133
|
...(rateLimit.used !== undefined && { used: rateLimit.used }),
|
|
120
134
|
remaining: rateLimit.remaining,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RepoInfo } from "./client.mts";
|
|
2
|
+
import { type ReplyEvidence } from "./reply-recovery-read.mts";
|
|
3
|
+
/**
|
|
4
|
+
* Reads, in one GraphQL request, what `apply review` with reply IDs otherwise reads three times:
|
|
5
|
+
* the PR head for `--require-sha`, the pre-reply transcripts for seen markers, and the reply
|
|
6
|
+
* recovery evidence for the first mutation batch. Every field is checked exactly as its
|
|
7
|
+
* standalone read checks it; anything missing is left to that read.
|
|
8
|
+
*/
|
|
9
|
+
export interface ApplyReviewPreflight {
|
|
10
|
+
headSha?: string;
|
|
11
|
+
/** Pre-reply transcripts for threads whose comments fit on the first page. */
|
|
12
|
+
transcripts: Map<string, string>;
|
|
13
|
+
/** Complete recovery evidence per requested thread. */
|
|
14
|
+
evidence: Map<string, ReplyEvidence>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* GraphQL-only; returns null when the preflight does not apply (REST transport, no reply IDs,
|
|
18
|
+
* more than one transcript batch, or REST thread handles). Request errors propagate.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readApplyReviewPreflight(pr: number, repo: RepoInfo, replyIds: readonly string[]): Promise<ApplyReviewPreflight | null>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { graphqlWithRateLimit } from "./client.mjs";
|
|
2
|
+
import { githubOperation } from "./transport.mjs";
|
|
3
|
+
import { REPLY_EVIDENCE_THREAD_SELECTION, threadReplyEvidence, } from "./reply-recovery-read.mjs";
|
|
4
|
+
import { threadTranscriptBodies } from "../threads/transcript.mjs";
|
|
5
|
+
import { mapPool } from "../util/pool.mjs";
|
|
6
|
+
const MAX_IDS = 20;
|
|
7
|
+
const QUERY = `query ApplyReviewPreflight($owner: String!, $repo: String!, $pr: Int!, $ids: [ID!]!, $cursor: String) {
|
|
8
|
+
_shepherdRateLimit: rateLimit { cost limit nodeCount remaining resetAt used }
|
|
9
|
+
repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { headRefOid } }
|
|
10
|
+
viewer { login }
|
|
11
|
+
nodes(ids: $ids) { __typename ${REPLY_EVIDENCE_THREAD_SELECTION} }
|
|
12
|
+
}`;
|
|
13
|
+
/**
|
|
14
|
+
* GraphQL-only; returns null when the preflight does not apply (REST transport, no reply IDs,
|
|
15
|
+
* more than one transcript batch, or REST thread handles). Request errors propagate.
|
|
16
|
+
*/
|
|
17
|
+
export function readApplyReviewPreflight(pr, repo, replyIds) {
|
|
18
|
+
const ids = [...new Set(replyIds)];
|
|
19
|
+
if (ids.length === 0 || ids.length > MAX_IDS || ids.some((id) => id.startsWith("rest-thread-")))
|
|
20
|
+
return Promise.resolve(null);
|
|
21
|
+
return githubOperation("ApplyReviewPreflight", async () => {
|
|
22
|
+
const { data } = await graphqlWithRateLimit(QUERY, {
|
|
23
|
+
owner: repo.owner,
|
|
24
|
+
repo: repo.name,
|
|
25
|
+
pr,
|
|
26
|
+
ids,
|
|
27
|
+
cursor: null,
|
|
28
|
+
});
|
|
29
|
+
const headSha = data.repository?.pullRequest?.headRefOid;
|
|
30
|
+
const evidence = await mapPool(ids, 4, async (id) => {
|
|
31
|
+
try {
|
|
32
|
+
return [id, await threadReplyEvidence(repo, pr, id, data)];
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
...(typeof headSha === "string" && headSha && { headSha }),
|
|
40
|
+
transcripts: firstPageTranscripts(data, pr, repo, new Set(ids)),
|
|
41
|
+
evidence: new Map(evidence.filter((entry) => entry !== null)),
|
|
42
|
+
};
|
|
43
|
+
}, async () => null);
|
|
44
|
+
}
|
|
45
|
+
/** The same acceptance rules as `ReplyThreadTranscripts`, limited to single-page threads. */
|
|
46
|
+
function firstPageTranscripts(data, pr, repo, requested) {
|
|
47
|
+
const expectedRepo = `${repo.owner}/${repo.name}`.toLowerCase();
|
|
48
|
+
const transcripts = new Map();
|
|
49
|
+
for (const node of data.nodes) {
|
|
50
|
+
if (node?.__typename !== "PullRequestReviewThread" ||
|
|
51
|
+
!requested.has(node.id) ||
|
|
52
|
+
node.pullRequest?.number !== pr ||
|
|
53
|
+
node.pullRequest.repository.nameWithOwner.toLowerCase() !== expectedRepo ||
|
|
54
|
+
!node.comments ||
|
|
55
|
+
node.comments.pageInfo.hasNextPage ||
|
|
56
|
+
node.comments.nodes.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
transcripts.set(node.id, threadTranscriptBodies(node.comments.nodes.map((c) => c.body)));
|
|
59
|
+
}
|
|
60
|
+
return transcripts;
|
|
61
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type StateKey } from "../state/rest-cache.mts";
|
|
2
|
+
/**
|
|
3
|
+
* Where a GraphQL BaseBehind result may be cached. The count is a pure function of two commits,
|
|
4
|
+
* so an entry keyed on the live base tip and the head commit never goes stale.
|
|
5
|
+
*/
|
|
6
|
+
export interface BaseBehindCacheOptions {
|
|
7
|
+
stateKey: StateKey;
|
|
8
|
+
/** Live base tip from the same tick's `BatchPr`; without it the cache is not read. */
|
|
9
|
+
baseTipOid?: string;
|
|
10
|
+
/** Derived-state slot; a stack layer keeps its trunk compare apart from its base compare. */
|
|
11
|
+
slot?: "base-behind" | "trunk-behind";
|
|
12
|
+
}
|
|
13
|
+
export declare function cachedBaseBehind(cache: BaseBehindCacheOptions | undefined, headOid: string): Promise<number | undefined>;
|
|
14
|
+
/** Stores a compare under the base tip it actually used, which may be newer than BatchPr's. */
|
|
15
|
+
export declare function storeBaseBehind(cache: BaseBehindCacheOptions | undefined, baseTipOid: string | undefined, headOid: string, behindBy: number): Promise<void>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { loadDerived, storeDerived } from "../state/rest-cache.mjs";
|
|
2
|
+
const COMMIT_OID = /^[0-9a-f]{40}$/;
|
|
3
|
+
export async function cachedBaseBehind(cache, headOid) {
|
|
4
|
+
if (!cache?.baseTipOid || !COMMIT_OID.test(headOid))
|
|
5
|
+
return undefined;
|
|
6
|
+
const entry = await loadDerived(cache.stateKey, cache.slot ?? "base-behind");
|
|
7
|
+
const value = entry?.value;
|
|
8
|
+
if (value?.baseTipOid !== cache.baseTipOid || value.headOid !== headOid)
|
|
9
|
+
return undefined;
|
|
10
|
+
return Number.isSafeInteger(value.behindBy) && value.behindBy >= 0 ? value.behindBy : undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Stores a compare under the base tip it actually used, which may be newer than BatchPr's. */
|
|
13
|
+
export async function storeBaseBehind(cache, baseTipOid, headOid, behindBy) {
|
|
14
|
+
if (!cache || !baseTipOid || !COMMIT_OID.test(headOid))
|
|
15
|
+
return;
|
|
16
|
+
const entry = { baseTipOid, headOid, behindBy };
|
|
17
|
+
await storeDerived(cache.stateKey, cache.slot ?? "base-behind", entry);
|
|
18
|
+
}
|
|
@@ -4,6 +4,8 @@ import type { RawPr } from "./batch-raw-types.mts";
|
|
|
4
4
|
export declare function parseCheckSuitesComplete(raw: RawPr): boolean;
|
|
5
5
|
/** True only when the head commit's suite page finished and listed nothing. */
|
|
6
6
|
export declare function parseHeadCheckSuitesEmpty(raw: RawPr): boolean;
|
|
7
|
-
/** Actions suites on the head. Third-party suites with no workflow run are omitted. */
|
|
8
|
-
export declare function parseHeadWorkflowSuites(raw: RawPr): WorkflowSuiteSnapshot[];
|
|
9
7
|
export declare function parseSuiteStartupFailures(raw: RawPr): CheckRun[];
|
|
8
|
+
/** `headWorkflowSuites`, omitted when the head has none. */
|
|
9
|
+
export declare function workflowSuites(raw: RawPr): {
|
|
10
|
+
headWorkflowSuites?: WorkflowSuiteSnapshot[];
|
|
11
|
+
};
|
|
@@ -7,7 +7,7 @@ export function parseHeadCheckSuitesEmpty(raw) {
|
|
|
7
7
|
return suites?.pageInfo.hasNextPage === false && suites.nodes.length === 0;
|
|
8
8
|
}
|
|
9
9
|
/** Actions suites on the head. Third-party suites with no workflow run are omitted. */
|
|
10
|
-
|
|
10
|
+
function parseHeadWorkflowSuites(raw) {
|
|
11
11
|
const nodes = raw.commits.nodes[0]?.commit.checkSuites?.nodes ?? [];
|
|
12
12
|
return nodes.flatMap((node) => {
|
|
13
13
|
if (!node.workflowRun)
|
|
@@ -43,3 +43,8 @@ export function parseSuiteStartupFailures(raw) {
|
|
|
43
43
|
];
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
+
/** `headWorkflowSuites`, omitted when the head has none. */
|
|
47
|
+
export function workflowSuites(raw) {
|
|
48
|
+
const suites = parseHeadWorkflowSuites(raw);
|
|
49
|
+
return suites.length > 0 ? { headWorkflowSuites: suites } : {};
|
|
50
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toCheckAnnotation } from "./check-annotation-shape.mjs";
|
|
1
2
|
import { normalizeAuthorType } from "../comments/authors.mjs";
|
|
2
3
|
export function mapAuthorType(typeName, login) {
|
|
3
4
|
return normalizeAuthorType(typeName, login);
|
|
@@ -21,7 +22,9 @@ export function mapCheckRunNode(node) {
|
|
|
21
22
|
const event = node.checkSuite?.workflowRun?.event ?? null;
|
|
22
23
|
const workflowName = node.checkSuite?.workflowRun?.workflow?.name?.trim() || undefined;
|
|
23
24
|
const workflowId = resolveWorkflowId(node.checkSuite?.workflowRun?.workflow?.databaseId);
|
|
24
|
-
const runId =
|
|
25
|
+
const runId = node.checkSuite?.workflowRun?.databaseId != null
|
|
26
|
+
? String(node.checkSuite.workflowRun.databaseId)
|
|
27
|
+
: extractRunId(node.detailsUrl);
|
|
25
28
|
const summary = extractCheckRunSummary(node.title, node.summary);
|
|
26
29
|
const rawCreatedAt = node.checkSuite?.workflowRun?.createdAt ?? node.checkSuite?.createdAt;
|
|
27
30
|
const rawUpdatedAt = node.checkSuite?.workflowRun?.updatedAt ?? node.checkSuite?.updatedAt;
|
|
@@ -45,7 +48,42 @@ export function mapCheckRunNode(node) {
|
|
|
45
48
|
...(completedAtUnix !== undefined && { completedAtUnix }),
|
|
46
49
|
...(updatedAtUnix !== undefined && { updatedAtUnix }),
|
|
47
50
|
...(summary !== undefined && { summary }),
|
|
48
|
-
...((node.annotations?.nodes.length ?? 0) > 0 && {
|
|
51
|
+
...(((node.annotations?.totalCount ?? 0) > 0 || (node.annotations?.nodes.length ?? 0) > 0) && {
|
|
52
|
+
hasAnnotations: true,
|
|
53
|
+
}),
|
|
54
|
+
...inlineAnnotationsField(node.id, node.annotations),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The complete annotation list when the first page already holds every annotation with its full
|
|
59
|
+
* selection. A partial page (more than one annotation, or a node without the full fields) yields
|
|
60
|
+
* nothing, so the caller still reads the run's annotations separately.
|
|
61
|
+
*/
|
|
62
|
+
function inlineAnnotationsField(checkRunId, annotations) {
|
|
63
|
+
const total = annotations?.totalCount;
|
|
64
|
+
if (total === undefined || total < 1 || annotations.nodes.length !== total)
|
|
65
|
+
return {};
|
|
66
|
+
const raws = annotations.nodes.map(fullRawAnnotation);
|
|
67
|
+
if (raws.some((raw) => raw === null))
|
|
68
|
+
return {};
|
|
69
|
+
return {
|
|
70
|
+
inlineAnnotations: raws.map((raw) => toCheckAnnotation(checkRunId, raw)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function fullRawAnnotation(node) {
|
|
74
|
+
if (typeof node.path !== "string" || typeof node.annotationLevel !== "string")
|
|
75
|
+
return null;
|
|
76
|
+
if (node.location === undefined || node.fullDatabaseId === undefined)
|
|
77
|
+
return null;
|
|
78
|
+
return {
|
|
79
|
+
fullDatabaseId: node.fullDatabaseId,
|
|
80
|
+
path: node.path,
|
|
81
|
+
annotationLevel: node.annotationLevel,
|
|
82
|
+
title: node.title ?? null,
|
|
83
|
+
message: node.message,
|
|
84
|
+
rawDetails: node.rawDetails ?? null,
|
|
85
|
+
blobUrl: node.blobUrl ?? null,
|
|
86
|
+
location: node.location,
|
|
49
87
|
};
|
|
50
88
|
}
|
|
51
89
|
function extractCheckRunSummary(title, summary) {
|
|
@@ -138,6 +138,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
|
|
|
138
138
|
},
|
|
139
139
|
baseRefName: raw.baseRefName,
|
|
140
140
|
...(raw.baseRefOid && { baseRefOid: raw.baseRefOid }),
|
|
141
|
+
...(raw.baseRef?.target?.oid && { baseTipOid: raw.baseRef.target.oid }),
|
|
141
142
|
reviewRequests,
|
|
142
143
|
latestReviews,
|
|
143
144
|
reviewThreads,
|
|
@@ -13,6 +13,7 @@ export interface RawBranchProtectionRule {
|
|
|
13
13
|
requiredDeploymentEnvironments?: string[] | null;
|
|
14
14
|
}
|
|
15
15
|
import type { RawContextNode } from "./batch-raw-types.mts";
|
|
16
|
+
import type { StackMemberRefs } from "./stack-read.mts";
|
|
16
17
|
interface RawCheckCommit {
|
|
17
18
|
oid: string;
|
|
18
19
|
committedDate?: string;
|
|
@@ -60,6 +61,27 @@ interface RawStack {
|
|
|
60
61
|
number: number;
|
|
61
62
|
size: number;
|
|
62
63
|
baseRefName: string;
|
|
64
|
+
/** Selected only by `BatchPr`: the topology `PollStackTopology` would otherwise read. */
|
|
65
|
+
id?: string;
|
|
66
|
+
entries?: {
|
|
67
|
+
pageInfo: {
|
|
68
|
+
hasNextPage: boolean;
|
|
69
|
+
endCursor: string | null;
|
|
70
|
+
};
|
|
71
|
+
nodes: Array<{
|
|
72
|
+
position: number;
|
|
73
|
+
pullRequest: StackMemberRefs | null;
|
|
74
|
+
} | null>;
|
|
75
|
+
};
|
|
76
|
+
/** Selected only by `BatchPr`: the bottom entry, whose base is normally the trunk. */
|
|
77
|
+
trunkEntry?: {
|
|
78
|
+
nodes: Array<{
|
|
79
|
+
pullRequest: {
|
|
80
|
+
baseRefName: string;
|
|
81
|
+
baseRef: RawBaseRef | null;
|
|
82
|
+
} | null;
|
|
83
|
+
} | null>;
|
|
84
|
+
};
|
|
63
85
|
}
|
|
64
86
|
export interface RawRepositoryRule {
|
|
65
87
|
type: string;
|
|
@@ -87,6 +109,10 @@ export interface RawBaseRef {
|
|
|
87
109
|
};
|
|
88
110
|
nodes: RawRepositoryRule[];
|
|
89
111
|
} | null;
|
|
112
|
+
/** Live base branch tip; selected only by `BatchPr`. */
|
|
113
|
+
target?: {
|
|
114
|
+
oid?: string;
|
|
115
|
+
} | null;
|
|
90
116
|
}
|
|
91
117
|
export interface RawPrMergeFields {
|
|
92
118
|
isInMergeQueue?: boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CommentAuthorAssociation } from "../types/github.mts";
|
|
2
2
|
import type { RawPrMergeFields } from "./batch-raw-rules.mts";
|
|
3
|
+
import type { RawCheckAnnotation } from "./check-annotation-shape.mts";
|
|
3
4
|
export interface RawBatchResponse {
|
|
4
5
|
viewer?: {
|
|
5
6
|
login: string | null;
|
|
@@ -140,7 +141,7 @@ interface RawAuthor {
|
|
|
140
141
|
}
|
|
141
142
|
export interface RawThreadComment {
|
|
142
143
|
id: string;
|
|
143
|
-
isMinimized
|
|
144
|
+
isMinimized?: boolean;
|
|
144
145
|
url: string;
|
|
145
146
|
authorAssociation?: CommentAuthorAssociation;
|
|
146
147
|
viewerDidAuthor?: boolean;
|
|
@@ -157,10 +158,10 @@ export interface RawThreadComment {
|
|
|
157
158
|
}
|
|
158
159
|
export interface RawThread {
|
|
159
160
|
id: string;
|
|
160
|
-
isResolved
|
|
161
|
-
isOutdated
|
|
162
|
-
viewerCanReply
|
|
163
|
-
viewerCanResolve
|
|
161
|
+
isResolved?: boolean;
|
|
162
|
+
isOutdated?: boolean;
|
|
163
|
+
viewerCanReply?: boolean;
|
|
164
|
+
viewerCanResolve?: boolean;
|
|
164
165
|
path?: string | null;
|
|
165
166
|
line?: number | null;
|
|
166
167
|
startLine?: number | null;
|
|
@@ -228,7 +229,7 @@ export type RawContextNode = {
|
|
|
228
229
|
summary: string | null;
|
|
229
230
|
annotations?: {
|
|
230
231
|
totalCount?: number;
|
|
231
|
-
nodes: Array<{
|
|
232
|
+
nodes: Array<Partial<RawCheckAnnotation> & {
|
|
232
233
|
message: string;
|
|
233
234
|
}>;
|
|
234
235
|
};
|
|
@@ -236,6 +237,7 @@ export type RawContextNode = {
|
|
|
236
237
|
createdAt?: string;
|
|
237
238
|
updatedAt?: string;
|
|
238
239
|
workflowRun: {
|
|
240
|
+
databaseId?: number;
|
|
239
241
|
event: string;
|
|
240
242
|
createdAt?: string;
|
|
241
243
|
updatedAt?: string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RawPrMergeFields } from "./batch-raw-rules.mts";
|
|
2
|
+
import type { StackMemberRefs, StackRead } from "./stack-read.mts";
|
|
3
|
+
/** Required contexts and live tip of a native stack's trunk, read in `BatchPr`. */
|
|
4
|
+
export interface TrunkRules {
|
|
5
|
+
refName: string;
|
|
6
|
+
contexts: string[];
|
|
7
|
+
tipOid?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Stack reads that `BatchPr`'s first page already answered. Each part is present
|
|
11
|
+
* only when complete; a caller falls back to the standalone query otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export interface BatchStackEvidence {
|
|
14
|
+
topology?: StackRead<StackMemberRefs>;
|
|
15
|
+
trunk?: TrunkRules;
|
|
16
|
+
}
|
|
17
|
+
/** Validate the embedded stack selections of one `BatchPr` first page. */
|
|
18
|
+
export declare function stackEvidenceFromRaw(raw: RawPrMergeFields & {
|
|
19
|
+
number: number;
|
|
20
|
+
baseRefName: string;
|
|
21
|
+
}, data: {
|
|
22
|
+
viewer?: {
|
|
23
|
+
login: string | null;
|
|
24
|
+
} | null;
|
|
25
|
+
repository?: {
|
|
26
|
+
viewerCanAdminister?: boolean;
|
|
27
|
+
} | null;
|
|
28
|
+
}): BatchStackEvidence | undefined;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { parseBranchRules } from "./batch-parsers-rules.mjs";
|
|
2
|
+
import { orderedStackMembers } from "./stack-order.mjs";
|
|
3
|
+
/** Validate the embedded stack selections of one `BatchPr` first page. */
|
|
4
|
+
export function stackEvidenceFromRaw(raw, data) {
|
|
5
|
+
const stack = raw.stack;
|
|
6
|
+
if (!stack)
|
|
7
|
+
return undefined;
|
|
8
|
+
const viewer = {
|
|
9
|
+
login: data.viewer?.login ?? null,
|
|
10
|
+
canAdminister: data.repository?.viewerCanAdminister,
|
|
11
|
+
};
|
|
12
|
+
const topology = embeddedTopology(stack, raw.number, viewer);
|
|
13
|
+
const trunk = embeddedTrunkRules(stack, raw);
|
|
14
|
+
if (!topology && !trunk)
|
|
15
|
+
return undefined;
|
|
16
|
+
return { ...(topology && { topology }), ...(trunk && { trunk }) };
|
|
17
|
+
}
|
|
18
|
+
function embeddedTopology(stack, anchor, viewer) {
|
|
19
|
+
const entries = stack.entries;
|
|
20
|
+
// Past one page, PollStackTopology reads the full membership instead.
|
|
21
|
+
if (!entries || entries.pageInfo.hasNextPage)
|
|
22
|
+
return undefined;
|
|
23
|
+
const members = [];
|
|
24
|
+
for (const node of entries.nodes) {
|
|
25
|
+
if (!node?.pullRequest)
|
|
26
|
+
return undefined;
|
|
27
|
+
members.push({ position: node.position, pullRequest: node.pullRequest });
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
stackNumber: stack.number,
|
|
32
|
+
stackSize: stack.size,
|
|
33
|
+
viewerLogin: viewer.login,
|
|
34
|
+
...(viewer.canAdminister !== undefined && { viewerCanAdminister: viewer.canAdminister }),
|
|
35
|
+
ordered: orderedStackMembers(members, anchor, stack.size),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The trunk's rules come from whichever ref in this page is the trunk itself:
|
|
44
|
+
* this PR's own base for the bottom layer, otherwise the first entry's base.
|
|
45
|
+
*/
|
|
46
|
+
function embeddedTrunkRules(stack, raw) {
|
|
47
|
+
const trunk = stack.baseRefName;
|
|
48
|
+
if (raw.baseRefName === trunk && raw.baseRef)
|
|
49
|
+
return trunkRules(trunk, raw.baseRef);
|
|
50
|
+
const bottom = stack.trunkEntry?.nodes[0]?.pullRequest;
|
|
51
|
+
if (bottom?.baseRefName !== trunk || !bottom.baseRef)
|
|
52
|
+
return undefined;
|
|
53
|
+
return trunkRules(trunk, bottom.baseRef);
|
|
54
|
+
}
|
|
55
|
+
function trunkRules(refName, ref) {
|
|
56
|
+
const tipOid = ref.target?.oid;
|
|
57
|
+
return {
|
|
58
|
+
refName,
|
|
59
|
+
contexts: parseBranchRules(ref).requiredStatusCheckContexts,
|
|
60
|
+
...(tipOid && { tipOid }),
|
|
61
|
+
};
|
|
62
|
+
}
|
package/bin/github/batch.d.mts
CHANGED
|
@@ -3,7 +3,8 @@ import type { WorkflowSuiteSnapshot } from "../checks/unreported-required.mts";
|
|
|
3
3
|
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
4
4
|
import type { BatchPrData } from "../types.mts";
|
|
5
5
|
import { type PrFingerprint } from "./fingerprint.mts";
|
|
6
|
-
|
|
6
|
+
import { type BatchStackEvidence } from "./batch-stack-evidence.mts";
|
|
7
|
+
export interface BatchResult {
|
|
7
8
|
data: BatchPrData;
|
|
8
9
|
fingerprint?: PrFingerprint;
|
|
9
10
|
rateLimit?: RateLimitInfo;
|
|
@@ -15,7 +16,15 @@ interface BatchResult {
|
|
|
15
16
|
headWorkflowSuites?: WorkflowSuiteSnapshot[];
|
|
16
17
|
/** Internal READY-receipt evidence from the same request, if complete. */
|
|
17
18
|
receiptSummary?: RawSummaryPr;
|
|
19
|
+
/** Native-stack topology and trunk rules from the same first page, if complete. */
|
|
20
|
+
stackEvidence?: BatchStackEvidence;
|
|
18
21
|
}
|
|
22
|
+
/** A fingerprint hit, plus the live base tip and stack evidence from the same first page. */
|
|
23
|
+
type ReusedBatch<T> = {
|
|
24
|
+
reused: T;
|
|
25
|
+
baseTipOid?: string;
|
|
26
|
+
stackEvidence?: BatchStackEvidence;
|
|
27
|
+
};
|
|
19
28
|
interface FetchPrBatchOptions {
|
|
20
29
|
/**
|
|
21
30
|
* When false (default), the first-page approvedReviews are returned but
|
|
@@ -27,9 +36,13 @@ interface FetchPrBatchOptions {
|
|
|
27
36
|
*/
|
|
28
37
|
paginateApprovedReviews?: boolean;
|
|
29
38
|
includeReceiptSummary?: boolean;
|
|
39
|
+
terminalShortCircuit?: boolean;
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
32
|
-
*
|
|
42
|
+
* With `reuse`, the GraphQL first page's fingerprint is offered to it before any supplement
|
|
43
|
+
* runs; a non-null answer is returned as `{ reused }` instead of a snapshot. REST snapshots never
|
|
44
|
+
* call `reuse`: they have their own conditional-read reuse.
|
|
33
45
|
*/
|
|
46
|
+
export declare function fetchPrBatch<T>(pr: number, repo: RepoInfo, opts: FetchPrBatchOptions, reuse: (fingerprint: PrFingerprint) => Promise<T | null>): Promise<BatchResult | ReusedBatch<T>>;
|
|
34
47
|
export declare function fetchPrBatch(pr: number, repo: RepoInfo, opts?: FetchPrBatchOptions): Promise<BatchResult>;
|
|
35
48
|
export {};
|