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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
import type { StackRead, StackMemberRefs } from "./stack-read.mts";
|
|
3
|
+
interface RestStack {
|
|
4
|
+
number: number;
|
|
5
|
+
node_id: string;
|
|
6
|
+
base: {
|
|
7
|
+
ref: string;
|
|
8
|
+
};
|
|
9
|
+
pull_requests: Array<{
|
|
10
|
+
number: number;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
/** The official native-stack endpoint is authoritative; missing API access cannot prove standalone membership. */
|
|
14
|
+
export declare function readRestStackMembership(anchor: number, repo: RepoInfo): Promise<RestStack | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Topology reads are conditional. Outside a PR snapshot (iterate's stale-ancestry check runs after
|
|
17
|
+
* the snapshot returns) they use the anchor PR's ETag cache, so reads the snapshot already made
|
|
18
|
+
* (the stack list, the stack, the anchor's pull, `/user`) and the repeat membership read answer 304.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readRestStackTopology(anchor: number, repo: RepoInfo): Promise<StackRead<StackMemberRefs>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readRest as rest } from "./rest-reader-core.mjs";
|
|
2
|
+
import { readRestPages, restRepoPath, restObject, restString, restNumber, restArray, malformedRest, } from "./rest-reader-core.mjs";
|
|
3
|
+
import { readRestPull, restPullRefs } from "./rest-pr-core.mjs";
|
|
4
|
+
import { mapPool } from "../util/pool.mjs";
|
|
5
|
+
import { readRestViewerLogin } from "./rest-viewer-read.mjs";
|
|
6
|
+
import { currentRestConditionalKey, withRestConditionalScope } from "./rest-conditional-scope.mjs";
|
|
7
|
+
/** The official native-stack endpoint is authoritative; missing API access cannot prove standalone membership. */
|
|
8
|
+
export async function readRestStackMembership(anchor, repo) {
|
|
9
|
+
const listed = (await readRestPages(`${restRepoPath(repo)}/stacks?pull_request=${anchor}`)).nodes;
|
|
10
|
+
if (listed.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
if (listed.length !== 1)
|
|
13
|
+
malformedRest("multiple native stacks for one PR");
|
|
14
|
+
const number = restNumber(listed[0].number, "stack number");
|
|
15
|
+
const stack = restObject(await rest("GET", `${restRepoPath(repo)}/stacks/${number}`), "stack");
|
|
16
|
+
if (restNumber(stack.number, "stack number") !== number)
|
|
17
|
+
malformedRest("stack identity drift");
|
|
18
|
+
restString(stack.node_id, "stack node_id");
|
|
19
|
+
restString(stack.base?.ref, "stack base ref");
|
|
20
|
+
restArray(stack.pull_requests, "stack pull_requests");
|
|
21
|
+
const numbers = stack.pull_requests.map((pull) => restNumber(pull.number, "stack member number"));
|
|
22
|
+
if (!numbers.includes(anchor) || new Set(numbers).size !== numbers.length)
|
|
23
|
+
malformedRest("stack membership omitted anchor or repeated member");
|
|
24
|
+
return stack;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Topology reads are conditional. Outside a PR snapshot (iterate's stale-ancestry check runs after
|
|
28
|
+
* the snapshot returns) they use the anchor PR's ETag cache, so reads the snapshot already made
|
|
29
|
+
* (the stack list, the stack, the anchor's pull, `/user`) and the repeat membership read answer 304.
|
|
30
|
+
*/
|
|
31
|
+
export function readRestStackTopology(anchor, repo) {
|
|
32
|
+
if (currentRestConditionalKey() !== undefined)
|
|
33
|
+
return readTopology(anchor, repo);
|
|
34
|
+
return withRestConditionalScope({ owner: repo.owner, repo: repo.name, pr: anchor }, () => readTopology(anchor, repo));
|
|
35
|
+
}
|
|
36
|
+
async function readTopology(anchor, repo) {
|
|
37
|
+
const stack = await readRestStackMembership(anchor, repo);
|
|
38
|
+
if (!stack)
|
|
39
|
+
throw new Error(`PR #${anchor} is not part of a native GitHub stack`);
|
|
40
|
+
const ordered = await mapPool(stack.pull_requests, 4, async (pull) => restPullRefs(await readRestPull(pull.number, repo)));
|
|
41
|
+
const verified = await readRestStackMembership(anchor, repo);
|
|
42
|
+
if (!verified || JSON.stringify(verified) !== JSON.stringify(stack))
|
|
43
|
+
malformedRest("native stack changed during topology read");
|
|
44
|
+
// Installation tokens may read native stacks without supporting authenticated-user identity.
|
|
45
|
+
const viewerLogin = await readRestViewerLogin();
|
|
46
|
+
return { stackNumber: stack.number, stackSize: ordered.length, ordered, viewerLogin };
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
export declare function readRestStackSummary(anchor: number, repo: RepoInfo): Promise<{
|
|
3
|
+
stackNumber: number;
|
|
4
|
+
stackSize: number;
|
|
5
|
+
viewerLogin: string | null;
|
|
6
|
+
ordered: import("./poll-summary-raw.mts").RawSummaryPr[];
|
|
7
|
+
allowedMergeMethods?: import("../config/merge-method.mts").MergeMethod[] | undefined;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readRest, restRepoPath, restObject } from "./rest-reader-core.mjs";
|
|
2
|
+
import { readRestViewerLogin } from "./rest-viewer-read.mjs";
|
|
3
|
+
import { readAllowedMergeMethods } from "../config/merge-method.mjs";
|
|
4
|
+
import { readRestStackMembership } from "./rest-stack-read.mjs";
|
|
5
|
+
import { fetchRestRawSummaryPr } from "./rest-batch-read.mjs";
|
|
6
|
+
import { mapPool } from "../util/pool.mjs";
|
|
7
|
+
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
8
|
+
import { GitHubRequestError } from "./errors.mjs";
|
|
9
|
+
import { readRestPull, restPullRevision } from "./rest-pr-core.mjs";
|
|
10
|
+
import { readRestBranchRules } from "./rest-rules-read.mjs";
|
|
11
|
+
export async function readRestStackSummary(anchor, repo) {
|
|
12
|
+
const stack = await readRestStackMembership(anchor, repo);
|
|
13
|
+
if (!stack)
|
|
14
|
+
throw new ShepherdError(`PR #${anchor} is not part of a native GitHub stack`, EXIT.UNAVAILABLE);
|
|
15
|
+
const settings = restObject(await readRest("GET", restRepoPath(repo)), "repository merge settings");
|
|
16
|
+
const viewerLogin = await readRestViewerLogin();
|
|
17
|
+
const branchRules = new Map();
|
|
18
|
+
const pullRevisions = new Map();
|
|
19
|
+
const context = {
|
|
20
|
+
stack,
|
|
21
|
+
repository: settings,
|
|
22
|
+
viewerLogin,
|
|
23
|
+
readBranchRules(branch) {
|
|
24
|
+
let rules = branchRules.get(branch);
|
|
25
|
+
if (!rules) {
|
|
26
|
+
rules = readRestBranchRules(repo, branch);
|
|
27
|
+
branchRules.set(branch, rules);
|
|
28
|
+
}
|
|
29
|
+
return rules;
|
|
30
|
+
},
|
|
31
|
+
recordPullRevision(pr, revision) {
|
|
32
|
+
pullRevisions.set(pr, revision);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const ordered = await mapPool(stack.pull_requests, 4, (member) => fetchRestRawSummaryPr(member.number, repo, context));
|
|
36
|
+
await mapPool(ordered, 4, async (member) => {
|
|
37
|
+
const latest = await readRestPull(member.number, repo);
|
|
38
|
+
if (restPullRevision(latest) !== pullRevisions.get(member.number))
|
|
39
|
+
stackChanged();
|
|
40
|
+
});
|
|
41
|
+
const fresh = await readRestStackMembership(anchor, repo);
|
|
42
|
+
if (!fresh || JSON.stringify(fresh) !== JSON.stringify(stack))
|
|
43
|
+
stackChanged();
|
|
44
|
+
const allowedMergeMethods = readAllowedMergeMethods({
|
|
45
|
+
mergeCommitAllowed: typeof settings.allow_merge_commit === "boolean" ? settings.allow_merge_commit : undefined,
|
|
46
|
+
squashMergeAllowed: typeof settings.allow_squash_merge === "boolean" ? settings.allow_squash_merge : undefined,
|
|
47
|
+
rebaseMergeAllowed: typeof settings.allow_rebase_merge === "boolean" ? settings.allow_rebase_merge : undefined,
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
stackNumber: stack.number,
|
|
51
|
+
stackSize: ordered.length,
|
|
52
|
+
viewerLogin,
|
|
53
|
+
ordered,
|
|
54
|
+
...(allowedMergeMethods && { allowedMergeMethods }),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function stackChanged() {
|
|
58
|
+
throw new GitHubRequestError("Native stack changed during REST summary read; retry", {
|
|
59
|
+
status: 409,
|
|
60
|
+
exitCodeOverride: EXIT.TEMPFAIL,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BatchPrData } from "../types.mts";
|
|
2
|
+
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
3
|
+
import type { RestPull } from "./rest-pr-core.mts";
|
|
4
|
+
import type { readRestFeedback } from "./rest-feedback-read.mts";
|
|
5
|
+
import type { readRestCommitChecks } from "./rest-check-read.mts";
|
|
6
|
+
import type { readRestBranchRules } from "./rest-rules-read.mts";
|
|
7
|
+
export declare function restSummary(data: BatchPrData, pull: RestPull, feedback: Awaited<ReturnType<typeof readRestFeedback>>, checks: Awaited<ReturnType<typeof readRestCommitChecks>>, baseRef: Awaited<ReturnType<typeof readRestBranchRules>>["baseRef"]): RawSummaryPr;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export function restSummary(data, pull, feedback, checks, baseRef) {
|
|
2
|
+
const connection = (nodes) => ({
|
|
3
|
+
nodes,
|
|
4
|
+
totalCount: nodes.length,
|
|
5
|
+
pageInfo: { hasPreviousPage: false },
|
|
6
|
+
});
|
|
7
|
+
const comments = feedback.comments.map((comment) => ({
|
|
8
|
+
id: comment.id,
|
|
9
|
+
body: comment.body,
|
|
10
|
+
author: { login: comment.author, __typename: comment.authorType },
|
|
11
|
+
url: comment.url,
|
|
12
|
+
authorAssociation: comment.authorAssociation,
|
|
13
|
+
}));
|
|
14
|
+
return {
|
|
15
|
+
number: pull.number,
|
|
16
|
+
title: pull.title,
|
|
17
|
+
url: pull.html_url,
|
|
18
|
+
author: pull.user,
|
|
19
|
+
state: data.state,
|
|
20
|
+
isDraft: data.isDraft,
|
|
21
|
+
...(data.autoMergeRequest && { autoMergeRequest: data.autoMergeRequest }),
|
|
22
|
+
updatedAt: pull.updated_at,
|
|
23
|
+
headRefName: data.headRefName,
|
|
24
|
+
headRefOid: data.headRefOid,
|
|
25
|
+
baseRefOid: pull.base.sha,
|
|
26
|
+
baseRefName: data.baseRefName,
|
|
27
|
+
...(baseRef && { baseRef }),
|
|
28
|
+
mergeable: data.mergeable,
|
|
29
|
+
mergeStateStatus: data.mergeStateStatus,
|
|
30
|
+
reviewDecision: null,
|
|
31
|
+
reviewRequests: {
|
|
32
|
+
nodes: data.reviewRequests.map((request) => ({ requestedReviewer: request })),
|
|
33
|
+
},
|
|
34
|
+
latestReviews: {
|
|
35
|
+
nodes: data.latestReviews.map((review) => ({
|
|
36
|
+
state: review.state,
|
|
37
|
+
author: { login: review.login },
|
|
38
|
+
})),
|
|
39
|
+
},
|
|
40
|
+
mergeQueueEntry: null,
|
|
41
|
+
stack: data.stack
|
|
42
|
+
? { number: data.stack.number, size: data.stack.size, baseRefName: data.stack.baseRefName }
|
|
43
|
+
: null,
|
|
44
|
+
stackEntry: data.stack ? { position: data.stack.position } : null,
|
|
45
|
+
comments: connection(comments),
|
|
46
|
+
reviews: connection(feedback.reviews.map((review) => ({
|
|
47
|
+
id: review.node_id,
|
|
48
|
+
body: review.body ?? "",
|
|
49
|
+
state: review.state,
|
|
50
|
+
author: review.user ? { login: review.user.login, __typename: review.user.type } : null,
|
|
51
|
+
authorAssociation: review.author_association,
|
|
52
|
+
url: review.html_url,
|
|
53
|
+
}))),
|
|
54
|
+
reviewThreads: connection(data.reviewThreads.map((thread) => ({
|
|
55
|
+
id: thread.id,
|
|
56
|
+
...(thread.isResolved !== undefined && { isResolved: thread.isResolved }),
|
|
57
|
+
...(thread.isOutdated !== undefined && { isOutdated: thread.isOutdated }),
|
|
58
|
+
path: thread.path,
|
|
59
|
+
comments: connection((thread.comments ?? []).map((comment) => ({
|
|
60
|
+
id: comment.id,
|
|
61
|
+
...(comment.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
62
|
+
body: comment.body,
|
|
63
|
+
author: { login: comment.author, __typename: comment.authorType },
|
|
64
|
+
url: comment.url,
|
|
65
|
+
authorAssociation: comment.authorAssociation,
|
|
66
|
+
}))),
|
|
67
|
+
}))),
|
|
68
|
+
commits: {
|
|
69
|
+
nodes: [
|
|
70
|
+
{
|
|
71
|
+
commit: {
|
|
72
|
+
oid: data.headRefOid,
|
|
73
|
+
statusCheckRollup: {
|
|
74
|
+
contexts: connection(checks.nodes.map((check) => check.__typename === "CheckRun"
|
|
75
|
+
? {
|
|
76
|
+
...check,
|
|
77
|
+
detailsUrl: check.detailsUrl ?? undefined,
|
|
78
|
+
annotations: { totalCount: check.annotations.totalCount },
|
|
79
|
+
checkSuite: check.checkSuite
|
|
80
|
+
? {
|
|
81
|
+
...check.checkSuite,
|
|
82
|
+
workflowRun: check.checkSuite.workflowRun
|
|
83
|
+
? {
|
|
84
|
+
...check.checkSuite.workflowRun,
|
|
85
|
+
workflow: check.checkSuite.workflowRun.workflow
|
|
86
|
+
? {
|
|
87
|
+
...check.checkSuite.workflowRun.workflow,
|
|
88
|
+
databaseId: check.checkSuite.workflowRun.workflow.databaseId,
|
|
89
|
+
}
|
|
90
|
+
: null,
|
|
91
|
+
}
|
|
92
|
+
: null,
|
|
93
|
+
}
|
|
94
|
+
: null,
|
|
95
|
+
}
|
|
96
|
+
: check)),
|
|
97
|
+
},
|
|
98
|
+
checkSuites: {
|
|
99
|
+
nodes: checks.suites.map((suite) => ({
|
|
100
|
+
status: suite.status.toUpperCase(),
|
|
101
|
+
conclusion: suite.conclusion?.toUpperCase() ?? null,
|
|
102
|
+
workflowRun: checks.workflowRuns.find((run) => run.check_suite_id === suite.id) ?? null,
|
|
103
|
+
})),
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
transport: "rest",
|
|
110
|
+
transportUnavailable: data.transportUnavailable,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BatchPrData } from "../types.mts";
|
|
2
|
+
import { type RestPull } from "./rest-pr-core.mts";
|
|
3
|
+
/**
|
|
4
|
+
* A merged pull is immutable, so its first read is final. A closed pull is not (it can be
|
|
5
|
+
* reopened during the snapshot), so it keeps the full snapshot and its closing re-read.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isRestPullMerged(pull: RestPull): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Batch data for a merged PR, built from the first pull read alone. A terminal tick reports
|
|
10
|
+
* only identity and merge state (`buildTerminalReport`), so the feedback, checks, rules, stack
|
|
11
|
+
* and repository reads of a full snapshot would be discarded unread. The unread resources are
|
|
12
|
+
* listed as unavailable, not presented as empty, so no merge requirement is derived from them.
|
|
13
|
+
*/
|
|
14
|
+
export declare function restMergedBatchData(pull: RestPull): BatchPrData;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { restPullRefs } from "./rest-pr-core.mjs";
|
|
2
|
+
import { REST_BATCH_UNAVAILABLE } from "./rest-batch-unavailable.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* A merged pull is immutable, so its first read is final. A closed pull is not (it can be
|
|
5
|
+
* reopened during the snapshot), so it keeps the full snapshot and its closing re-read.
|
|
6
|
+
*/
|
|
7
|
+
export function isRestPullMerged(pull) {
|
|
8
|
+
return Boolean(pull.merged_at);
|
|
9
|
+
}
|
|
10
|
+
const NOT_READ = "not read for a merged pull request";
|
|
11
|
+
/**
|
|
12
|
+
* Batch data for a merged PR, built from the first pull read alone. A terminal tick reports
|
|
13
|
+
* only identity and merge state (`buildTerminalReport`), so the feedback, checks, rules, stack
|
|
14
|
+
* and repository reads of a full snapshot would be discarded unread. The unread resources are
|
|
15
|
+
* listed as unavailable, not presented as empty, so no merge requirement is derived from them.
|
|
16
|
+
*/
|
|
17
|
+
export function restMergedBatchData(pull) {
|
|
18
|
+
const refs = restPullRefs(pull);
|
|
19
|
+
return {
|
|
20
|
+
nodeId: pull.node_id,
|
|
21
|
+
...refs,
|
|
22
|
+
state: "MERGED",
|
|
23
|
+
isDraft: pull.draft,
|
|
24
|
+
mergeable: pull.mergeable === true ? "MERGEABLE" : pull.mergeable === false ? "CONFLICTING" : "UNKNOWN",
|
|
25
|
+
mergeStateStatus: pull.mergeable_state.toUpperCase(),
|
|
26
|
+
reviewDecision: null,
|
|
27
|
+
headRepoWithOwner: pull.head.repo?.full_name ?? null,
|
|
28
|
+
reviewRequests: [],
|
|
29
|
+
latestReviews: [],
|
|
30
|
+
reviewThreads: [],
|
|
31
|
+
comments: [],
|
|
32
|
+
changesRequestedReviews: [],
|
|
33
|
+
reviewSummaries: [],
|
|
34
|
+
approvedReviews: [],
|
|
35
|
+
checks: [],
|
|
36
|
+
branchProtection: null,
|
|
37
|
+
stack: null,
|
|
38
|
+
transport: "rest",
|
|
39
|
+
transportUnavailable: [
|
|
40
|
+
...REST_BATCH_UNAVAILABLE,
|
|
41
|
+
{ field: "branchProtection", reason: NOT_READ },
|
|
42
|
+
{ field: "mergeRequirements", reason: NOT_READ },
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
package/bin/github/rest-text.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appendEntry, nextEntry } from "../log/log-file.mjs";
|
|
2
2
|
import { formatRequestEntry, formatResponseEntry } from "../log/session.mjs";
|
|
3
3
|
import { GitHubRequestError } from "./errors.mjs";
|
|
4
|
+
import { githubFetch } from "./github-fetch.mjs";
|
|
4
5
|
import { makeAuthHeaders } from "./http-auth.mjs";
|
|
5
6
|
import { requestWithTokenRetry } from "./http-request.mjs";
|
|
6
7
|
import { parseRateLimit, parseRetryAfter, redactUrl, sanitizeBody, } from "./http-utils.mjs";
|
|
@@ -13,10 +14,12 @@ export async function restText(path, onRateLimit) {
|
|
|
13
14
|
appendEntry(formatRequestEntry({ n, kind: "restText", method: "GET", url }));
|
|
14
15
|
const t0 = performance.now();
|
|
15
16
|
let authSource = "unknown";
|
|
17
|
+
let credentialFingerprint;
|
|
16
18
|
const { res, attempt, retryT0 } = await requestWithTokenRetry(async () => {
|
|
17
19
|
const auth = await makeAuthHeaders();
|
|
18
20
|
authSource = auth.source;
|
|
19
|
-
|
|
21
|
+
credentialFingerprint = auth.fingerprint;
|
|
22
|
+
return githubFetch(url, { method: "GET", headers: auth.headers, redirect: "manual" });
|
|
20
23
|
}, t0, (response, durationMs) => recordIntermediateResponse({
|
|
21
24
|
n,
|
|
22
25
|
kind: "restText",
|
|
@@ -25,12 +28,13 @@ export async function restText(path, onRateLimit) {
|
|
|
25
28
|
response,
|
|
26
29
|
durationMs,
|
|
27
30
|
authSource,
|
|
31
|
+
credentialFingerprint,
|
|
28
32
|
}));
|
|
29
33
|
const durationMs = Math.round(performance.now() - retryT0);
|
|
30
34
|
const rateLimit = parseRateLimit(res.headers) ?? undefined;
|
|
31
35
|
onRateLimit?.(rateLimit);
|
|
32
36
|
const retryAfterSeconds = parseRetryAfter(res.headers);
|
|
33
|
-
recordApiTelemetry({ kind: "REST", method: "GET", authSource, rateLimit });
|
|
37
|
+
recordApiTelemetry({ kind: "REST", method: "GET", authSource, credentialFingerprint, rateLimit });
|
|
34
38
|
if ([301, 302, 307, 308].includes(res.status)) {
|
|
35
39
|
const redirected = await followRestTextRedirect(res, {
|
|
36
40
|
n,
|
|
@@ -101,7 +105,7 @@ async function followRestTextRedirect(res, entry) {
|
|
|
101
105
|
const logUrl = redactUrl(location);
|
|
102
106
|
appendEntry(formatRequestEntry({ n: n2, kind: "restText", method: "GET", url: logUrl }));
|
|
103
107
|
const t1 = performance.now();
|
|
104
|
-
const redirectRes = await
|
|
108
|
+
const redirectRes = await githubFetch(location);
|
|
105
109
|
appendEntry(formatResponseEntry({
|
|
106
110
|
n: n2,
|
|
107
111
|
kind: "restText",
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
import type { RawThread } from "./batch-raw-types.mts";
|
|
3
|
+
/** Complete REST reply chain, keeping the caller's opaque thread ID. */
|
|
4
|
+
export declare function readRestRawThread(id: string, repo?: RepoInfo, pr?: number): Promise<RawThread>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readRestFeedback } from "./rest-feedback-read.mjs";
|
|
2
|
+
import { resolveRestIdentity, resolveRestThreadRoot, restThreadId } from "./rest-identities.mjs";
|
|
3
|
+
/** Complete REST reply chain, keeping the caller's opaque thread ID. */
|
|
4
|
+
export async function readRestRawThread(id, repo, pr) {
|
|
5
|
+
const identity = repo && pr ? { repo, pr } : await resolveRestIdentity(id, "thread");
|
|
6
|
+
const root = await resolveRestThreadRoot(identity.repo, identity.pr, id);
|
|
7
|
+
const feedback = await readRestFeedback(identity.pr, identity.repo);
|
|
8
|
+
const thread = feedback.threads.find((thread) => thread.id === restThreadId(root));
|
|
9
|
+
if (!thread)
|
|
10
|
+
throw new Error(`Review thread ${id} is absent from the REST snapshot`);
|
|
11
|
+
const comments = (thread.comments ?? []).map((comment) => ({
|
|
12
|
+
id: comment.id,
|
|
13
|
+
body: comment.body,
|
|
14
|
+
url: comment.url,
|
|
15
|
+
author: { login: comment.author, __typename: comment.authorType },
|
|
16
|
+
...(comment.viewerDidAuthor === true && { viewerDidAuthor: true }),
|
|
17
|
+
...(comment.authorAssociation && { authorAssociation: comment.authorAssociation }),
|
|
18
|
+
...(comment.reviewId && { pullRequestReview: { id: comment.reviewId } }),
|
|
19
|
+
path: thread.path,
|
|
20
|
+
line: thread.line,
|
|
21
|
+
startLine: thread.startLine,
|
|
22
|
+
...(comment.createdAtUnix && {
|
|
23
|
+
createdAt: new Date(comment.createdAtUnix * 1000).toISOString(),
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
...(thread.isResolved !== undefined && { isResolved: thread.isResolved }),
|
|
29
|
+
...(thread.isOutdated !== undefined && { isOutdated: thread.isOutdated }),
|
|
30
|
+
path: thread.path,
|
|
31
|
+
line: thread.line,
|
|
32
|
+
startLine: thread.startLine,
|
|
33
|
+
comments: { pageInfo: { hasNextPage: false, endCursor: null }, nodes: comments },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readRest } from "./rest-reader-core.mjs";
|
|
2
|
+
import { GitHubRequestError } from "./errors.mjs";
|
|
3
|
+
import { rateLimitKind } from "./rate-limit-kind.mjs";
|
|
4
|
+
import { isRestSessionRefusal } from "./rest-session-refusal.mjs";
|
|
5
|
+
import { currentRestConditionalKey } from "./rest-conditional-scope.mjs";
|
|
6
|
+
import { makeAuthHeaders } from "./http-auth.mjs";
|
|
7
|
+
import { loadDerived, storeDerived } from "../state/rest-cache.mjs";
|
|
8
|
+
/**
|
|
9
|
+
* GitHub never answers an error status with 304, so a token that cannot read `/user` would be
|
|
10
|
+
* charged for the same denial on every poll tick. The denial is remembered per credential for
|
|
11
|
+
* an hour; a token change, or the hour passing, asks GitHub again.
|
|
12
|
+
*/
|
|
13
|
+
const UNAVAILABLE_TTL_MS = 60 * 60 * 1000;
|
|
14
|
+
/** Installation tokens may read PRs without supporting authenticated-user identity. */
|
|
15
|
+
export async function readRestViewerLogin() {
|
|
16
|
+
const key = currentRestConditionalKey();
|
|
17
|
+
const name = key && `viewer-unavailable:${(await makeAuthHeaders()).fingerprint}`;
|
|
18
|
+
if (key && name) {
|
|
19
|
+
const denied = await loadDerived(key, name);
|
|
20
|
+
if (denied && Date.now() - denied.storedAt < UNAVAILABLE_TTL_MS)
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const viewer = await readRest("GET", "/user");
|
|
25
|
+
if (!viewer || typeof viewer !== "object" || Array.isArray(viewer))
|
|
26
|
+
return null;
|
|
27
|
+
const login = viewer.login;
|
|
28
|
+
return typeof login === "string" && login.length > 0 ? login : null;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error instanceof GitHubRequestError &&
|
|
32
|
+
[401, 403, 404].includes(error.status) &&
|
|
33
|
+
rateLimitKind(error) === null &&
|
|
34
|
+
!isRestSessionRefusal(error)) {
|
|
35
|
+
if (key && name)
|
|
36
|
+
await storeDerived(key, name, true);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { StackMemberRefs } from "./stack-read.mts";
|
|
2
|
+
/** The first open member can replace merged lower layers only after a fully merged prefix. */
|
|
3
|
+
export declare function verifiedBottomOpenLayer<Pr extends StackMemberRefs>(ordered: readonly Pr[], anchor: number): Pr;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
2
|
+
/** The first open member can replace merged lower layers only after a fully merged prefix. */
|
|
3
|
+
export function verifiedBottomOpenLayer(ordered, anchor) {
|
|
4
|
+
const index = ordered.findIndex((member) => member.state === "OPEN");
|
|
5
|
+
const bottom = ordered[index];
|
|
6
|
+
if (!bottom) {
|
|
7
|
+
throw new ShepherdError(`Native stack for PR #${anchor} has no open layer`, EXIT.TEMPFAIL);
|
|
8
|
+
}
|
|
9
|
+
const predecessor = ordered.slice(0, index).find((member) => member.state !== "MERGED");
|
|
10
|
+
if (predecessor) {
|
|
11
|
+
throw new ShepherdError(`Native stack for PR #${anchor} has PR #${predecessor.number} in state ${predecessor.state} before open PR #${bottom.number}; cannot verify the bottom open layer`, EXIT.TEMPFAIL);
|
|
12
|
+
}
|
|
13
|
+
return bottom;
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface RepositoryHandoffFields {
|
|
2
|
+
viewerPermission?: string | null;
|
|
3
|
+
mergeCommitAllowed?: boolean;
|
|
4
|
+
squashMergeAllowed?: boolean;
|
|
5
|
+
rebaseMergeAllowed?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** What a routed-layer handoff needs from a stack read besides the layers themselves. */
|
|
8
|
+
export interface StackHandoffContext {
|
|
9
|
+
/** Stack identity and each ordered layer's entry position. */
|
|
10
|
+
stack?: {
|
|
11
|
+
id: string;
|
|
12
|
+
positions: number[];
|
|
13
|
+
};
|
|
14
|
+
viewerPermission?: string | null;
|
|
15
|
+
mergeSettings?: Omit<RepositoryHandoffFields, "viewerPermission">;
|
|
16
|
+
}
|
|
17
|
+
export declare function stackHandoffContext(repository: RepositoryHandoffFields, id: string, entries: Array<{
|
|
18
|
+
position: number;
|
|
19
|
+
pullRequest: {
|
|
20
|
+
number: number;
|
|
21
|
+
};
|
|
22
|
+
}>, ordered: Array<{
|
|
23
|
+
number: number;
|
|
24
|
+
}>): StackHandoffContext;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { readAllowedMergeMethods } from "../config/merge-method.mjs";
|
|
2
|
+
export function stackHandoffContext(repository, id, entries, ordered) {
|
|
3
|
+
const position = new Map(entries.map((entry) => [entry.pullRequest.number, entry.position]));
|
|
4
|
+
const { viewerPermission, mergeCommitAllowed, squashMergeAllowed, rebaseMergeAllowed } = repository;
|
|
5
|
+
return {
|
|
6
|
+
stack: { id, positions: ordered.map((pr) => position.get(pr.number)) },
|
|
7
|
+
...(viewerPermission !== undefined && { viewerPermission }),
|
|
8
|
+
...(readAllowedMergeMethods(repository) && {
|
|
9
|
+
mergeSettings: { mergeCommitAllowed, squashMergeAllowed, rebaseMergeAllowed },
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RawBatchResponse, RawContextNode } from "./batch-raw-types.mts";
|
|
2
|
+
import type { RawSummaryPr } from "./poll-summary-raw.mts";
|
|
3
|
+
import type { StackRead } from "./stack-read.mts";
|
|
4
|
+
/** The routing tick's summary read: every layer as read, before projection. */
|
|
5
|
+
export interface StackHandoffSource {
|
|
6
|
+
read: Omit<StackRead<RawSummaryPr>, "ordered">;
|
|
7
|
+
layers: RawSummaryPr[];
|
|
8
|
+
readAt: number;
|
|
9
|
+
}
|
|
10
|
+
export type CheckRunDetail = Pick<Extract<RawContextNode, {
|
|
11
|
+
__typename: "CheckRun";
|
|
12
|
+
}>, "title" | "summary" | "annotations">;
|
|
13
|
+
export declare function checkRunIds(layer: RawSummaryPr): string[];
|
|
14
|
+
/** True when every window `BatchPr`'s first page reads is complete in this summary. */
|
|
15
|
+
export declare function summaryComplete(layer: RawSummaryPr, source: StackHandoffSource): boolean;
|
|
16
|
+
/** The `BatchPr` first page this layer's summary answers, field by field; null if incomplete. */
|
|
17
|
+
export declare function batchResponseFromSummary(layer: RawSummaryPr, source: StackHandoffSource, runs: ReadonlyMap<string, CheckRunDetail>): RawBatchResponse | null;
|