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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { readRestPages, restRepoPath, restCollection, restString, restNumber, malformedRest, } from "./rest-reader-core.mjs";
|
|
2
|
+
import { recordRestIdentity } from "./rest-identities.mjs";
|
|
3
|
+
import { readRest as rest } from "./rest-reader-core.mjs";
|
|
4
|
+
export async function readRestCommitChecks(oid, repo, pr = 0) {
|
|
5
|
+
const prefix = restRepoPath(repo);
|
|
6
|
+
const checks = await readRestPages(`${prefix}/commits/${encodeURIComponent(oid)}/check-runs?filter=latest`, (body) => restCollection(body, "check_runs"));
|
|
7
|
+
const suites = await readRestPages(`${prefix}/commits/${encodeURIComponent(oid)}/check-suites`, (body) => restCollection(body, "check_suites"));
|
|
8
|
+
const statuses = await readRestPages(`${prefix}/commits/${encodeURIComponent(oid)}/statuses`);
|
|
9
|
+
const workflowRuns = await readRestPages(`${prefix}/actions/runs?head_sha=${encodeURIComponent(oid)}`, (body) => restCollection(body, "workflow_runs"));
|
|
10
|
+
for (const suite of suites.nodes) {
|
|
11
|
+
restNumber(suite.id, "check suite id");
|
|
12
|
+
restString(suite.status, "check suite status");
|
|
13
|
+
if (suite.conclusion !== null)
|
|
14
|
+
restString(suite.conclusion, "check suite conclusion");
|
|
15
|
+
}
|
|
16
|
+
for (const run of workflowRuns.nodes) {
|
|
17
|
+
restNumber(run.id, "workflow run id");
|
|
18
|
+
restNumber(run.check_suite_id, "workflow check suite id");
|
|
19
|
+
restNumber(run.workflow_id, "workflow id");
|
|
20
|
+
restString(run.event, "workflow event");
|
|
21
|
+
}
|
|
22
|
+
const workflows = new Map(workflowRuns.nodes.map((run) => [run.check_suite_id, run]));
|
|
23
|
+
const suiteMap = new Map(suites.nodes.map((suite) => [suite.id, suite]));
|
|
24
|
+
const nodes = [];
|
|
25
|
+
const checkIds = new Set();
|
|
26
|
+
for (const check of checks.nodes) {
|
|
27
|
+
restNumber(check.id, "check run id");
|
|
28
|
+
restString(check.node_id, "check run node_id");
|
|
29
|
+
restString(check.name, "check run name");
|
|
30
|
+
restString(check.status, "check run status");
|
|
31
|
+
if (!["QUEUED", "IN_PROGRESS", "COMPLETED", "WAITING", "PENDING", "REQUESTED"].includes(check.status.toUpperCase()))
|
|
32
|
+
malformedRest("unknown check run status");
|
|
33
|
+
if (check.conclusion !== null &&
|
|
34
|
+
![
|
|
35
|
+
"ACTION_REQUIRED",
|
|
36
|
+
"CANCELLED",
|
|
37
|
+
"FAILURE",
|
|
38
|
+
"NEUTRAL",
|
|
39
|
+
"SKIPPED",
|
|
40
|
+
"STALE",
|
|
41
|
+
"STARTUP_FAILURE",
|
|
42
|
+
"SUCCESS",
|
|
43
|
+
"TIMED_OUT",
|
|
44
|
+
].includes(restString(check.conclusion, "check conclusion").toUpperCase()))
|
|
45
|
+
malformedRest("unknown check conclusion");
|
|
46
|
+
restNumber(check.output?.annotations_count, "check run annotation count");
|
|
47
|
+
if (checkIds.has(check.node_id))
|
|
48
|
+
throw new Error("REST check run pagination duplicated an ID");
|
|
49
|
+
checkIds.add(check.node_id);
|
|
50
|
+
await recordRestIdentity(repo, pr, check.node_id, String(check.id), "check");
|
|
51
|
+
const suite = check.check_suite ? suiteMap.get(check.check_suite.id) : undefined;
|
|
52
|
+
const run = check.check_suite ? workflows.get(check.check_suite.id) : undefined;
|
|
53
|
+
nodes.push({
|
|
54
|
+
__typename: "CheckRun",
|
|
55
|
+
id: check.node_id,
|
|
56
|
+
name: check.name,
|
|
57
|
+
status: check.status.toUpperCase(),
|
|
58
|
+
conclusion: check.conclusion?.toUpperCase() ?? null,
|
|
59
|
+
detailsUrl: check.details_url,
|
|
60
|
+
startedAt: check.started_at,
|
|
61
|
+
completedAt: check.completed_at,
|
|
62
|
+
title: check.output.title,
|
|
63
|
+
summary: check.output.summary,
|
|
64
|
+
annotations: { totalCount: check.output.annotations_count, nodes: [] },
|
|
65
|
+
checkSuite: suite
|
|
66
|
+
? {
|
|
67
|
+
createdAt: suite.created_at,
|
|
68
|
+
updatedAt: suite.updated_at,
|
|
69
|
+
workflowRun: run
|
|
70
|
+
? {
|
|
71
|
+
databaseId: run.id,
|
|
72
|
+
event: run.event,
|
|
73
|
+
createdAt: run.created_at,
|
|
74
|
+
updatedAt: run.updated_at,
|
|
75
|
+
workflow: { name: run.name, databaseId: run.workflow_id },
|
|
76
|
+
}
|
|
77
|
+
: null,
|
|
78
|
+
}
|
|
79
|
+
: null,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// REST statuses include history; only the latest status for each context is current.
|
|
83
|
+
const latest = new Map();
|
|
84
|
+
for (const status of statuses.nodes) {
|
|
85
|
+
restString(status.context, "status context");
|
|
86
|
+
restString(status.state, "status state");
|
|
87
|
+
if (!["PENDING", "EXPECTED", "SUCCESS", "FAILURE", "ERROR"].includes(status.state.toUpperCase()))
|
|
88
|
+
malformedRest("unknown status context state");
|
|
89
|
+
if (!Number.isFinite(Date.parse(status.created_at)))
|
|
90
|
+
malformedRest("status context created_at");
|
|
91
|
+
const old = latest.get(status.context);
|
|
92
|
+
if (!old ||
|
|
93
|
+
Date.parse(status.created_at) > Date.parse(old.created_at) ||
|
|
94
|
+
(status.created_at === old.created_at && status.id > old.id))
|
|
95
|
+
latest.set(status.context, status);
|
|
96
|
+
}
|
|
97
|
+
for (const status of latest.values())
|
|
98
|
+
nodes.push({
|
|
99
|
+
__typename: "StatusContext",
|
|
100
|
+
context: status.context,
|
|
101
|
+
state: status.state.toUpperCase(),
|
|
102
|
+
targetUrl: status.target_url,
|
|
103
|
+
description: status.description,
|
|
104
|
+
createdAt: status.created_at,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
nodes,
|
|
108
|
+
suites: suites.nodes,
|
|
109
|
+
workflowRuns: workflowRuns.nodes,
|
|
110
|
+
rateLimit: workflowRuns.rateLimit ?? checks.rateLimit,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export async function readRestAnnotationCounts(oid, repo) {
|
|
114
|
+
const checks = await readRestPages(`${restRepoPath(repo)}/commits/${encodeURIComponent(oid)}/check-runs?filter=latest`, (body) => restCollection(body, "check_runs"));
|
|
115
|
+
return checks.nodes.map((check) => ({
|
|
116
|
+
__typename: "CheckRun",
|
|
117
|
+
id: restString(check.node_id, "check node_id"),
|
|
118
|
+
annotations: { totalCount: restNumber(check.output?.annotations_count, "annotations_count") },
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
export async function readRestBehind(repo, base, head) {
|
|
122
|
+
const comparison = await rest("GET", `${restRepoPath(repo)}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}`);
|
|
123
|
+
return restNumber(comparison.behind_by, "compare behind_by");
|
|
124
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RestComment } from "./rest-feedback-read.mts";
|
|
2
|
+
export declare function restCommentAuthorFields(comment: RestComment, viewerLogin?: string | null): {
|
|
3
|
+
author: string;
|
|
4
|
+
authorType: import("../types.mts").AuthorType;
|
|
5
|
+
viewerDidAuthor?: true | undefined;
|
|
6
|
+
authorAssociation: import("../types.mts").CommentAuthorAssociation;
|
|
7
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mapAuthorType } from "./batch-parser-helpers.mjs";
|
|
2
|
+
export function restCommentAuthorFields(comment, viewerLogin) {
|
|
3
|
+
const login = typeof comment.user?.login === "string" ? comment.user.login : undefined;
|
|
4
|
+
return {
|
|
5
|
+
author: login ?? "unknown",
|
|
6
|
+
authorType: mapAuthorType(comment.user?.type, login),
|
|
7
|
+
...(viewerLogin &&
|
|
8
|
+
login?.toLowerCase() === viewerLogin.toLowerCase() && { viewerDidAuthor: true }),
|
|
9
|
+
...(comment.author_association && { authorAssociation: comment.author_association }),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StateKey } from "../state/rest-cache.mts";
|
|
2
|
+
export interface RestSnapshotState {
|
|
3
|
+
/**
|
|
4
|
+
* True when at least one conditional read answered 304 and every other read was a settled body
|
|
5
|
+
* without an ETag (fingerprinted by content instead). Any changed ETag-bearing body is fresh.
|
|
6
|
+
*/
|
|
7
|
+
allNotModified: boolean;
|
|
8
|
+
/** Stable digest of every (path, validator) the snapshot observed. */
|
|
9
|
+
digest: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function withRestConditionalScope<T>(key: StateKey, fn: () => Promise<T>): Promise<T>;
|
|
12
|
+
export declare function currentRestConditionalKey(): StateKey | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Record how one read was answered. `contentBody` is passed only for a settled 200 that carried
|
|
15
|
+
* no ETag: a route that cannot answer 304 (the Claude Code cloud proxy's `/ccr/review_threads`
|
|
16
|
+
* may not send validators) is then validated by a hash of its content, so an unchanged body
|
|
17
|
+
* still lets the snapshot's digest match. Every other non-304 read is fresh and blocks reuse.
|
|
18
|
+
*/
|
|
19
|
+
export declare function recordRestConditionalRead(path: string, notModified: boolean, etag: string | undefined, contentBody?: unknown): void;
|
|
20
|
+
export declare function restSnapshotState(): RestSnapshotState | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
export function withRestConditionalScope(key, fn) {
|
|
5
|
+
return storage.run({ key, fresh: 0, notModified: 0, etags: new Map() }, fn);
|
|
6
|
+
}
|
|
7
|
+
export function currentRestConditionalKey() {
|
|
8
|
+
return storage.getStore()?.key;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Record how one read was answered. `contentBody` is passed only for a settled 200 that carried
|
|
12
|
+
* no ETag: a route that cannot answer 304 (the Claude Code cloud proxy's `/ccr/review_threads`
|
|
13
|
+
* may not send validators) is then validated by a hash of its content, so an unchanged body
|
|
14
|
+
* still lets the snapshot's digest match. Every other non-304 read is fresh and blocks reuse.
|
|
15
|
+
*/
|
|
16
|
+
export function recordRestConditionalRead(path, notModified, etag, contentBody) {
|
|
17
|
+
const scope = storage.getStore();
|
|
18
|
+
if (scope === undefined)
|
|
19
|
+
return;
|
|
20
|
+
if (notModified)
|
|
21
|
+
scope.notModified += 1;
|
|
22
|
+
else if (etag !== undefined || contentBody === undefined)
|
|
23
|
+
scope.fresh += 1;
|
|
24
|
+
scope.etags.set(path, etag ?? (contentBody === undefined ? "" : `content:${contentDigest(contentBody)}`));
|
|
25
|
+
}
|
|
26
|
+
function contentDigest(body) {
|
|
27
|
+
return createHash("sha256")
|
|
28
|
+
.update(JSON.stringify(body) ?? "")
|
|
29
|
+
.digest("hex")
|
|
30
|
+
.slice(0, 16);
|
|
31
|
+
}
|
|
32
|
+
export function restSnapshotState() {
|
|
33
|
+
const scope = storage.getStore();
|
|
34
|
+
if (scope === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
const hash = createHash("sha256");
|
|
37
|
+
for (const [path, etag] of [...scope.etags].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
|
|
38
|
+
hash.update(`${path}\0${etag}\0`);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
allNotModified: scope.fresh === 0 && scope.notModified > 0,
|
|
42
|
+
digest: hash.digest("hex").slice(0, 16),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
/** One principal read for this explicit PR chunk, without assuming its PRs are standalone. */
|
|
3
|
+
export declare function readRestExplicitSummary(prs: number[], repo: RepoInfo): Promise<import("./poll-summary-raw.mts").RawSummaryPr[]>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { fetchRestRawSummaryPr } from "./rest-batch-read.mjs";
|
|
2
|
+
import { readRestViewerLogin } from "./rest-viewer-read.mjs";
|
|
3
|
+
import { mapPool } from "../util/pool.mjs";
|
|
4
|
+
/** One principal read for this explicit PR chunk, without assuming its PRs are standalone. */
|
|
5
|
+
export async function readRestExplicitSummary(prs, repo) {
|
|
6
|
+
const viewerLogin = await readRestViewerLogin();
|
|
7
|
+
return mapPool(prs, 4, (pr) => fetchRestRawSummaryPr(pr, repo, undefined, viewerLogin));
|
|
8
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RepoInfo } from "./client.mts";
|
|
2
|
+
import type { PrComment, Review, ReviewThread, CommentAuthorAssociation } from "../types.mts";
|
|
3
|
+
export interface RestComment {
|
|
4
|
+
id: number;
|
|
5
|
+
node_id: string;
|
|
6
|
+
body: string;
|
|
7
|
+
html_url: string;
|
|
8
|
+
created_at: string;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
user: {
|
|
11
|
+
login: string;
|
|
12
|
+
type: string;
|
|
13
|
+
} | null;
|
|
14
|
+
author_association: CommentAuthorAssociation;
|
|
15
|
+
in_reply_to_id?: number;
|
|
16
|
+
pull_request_review_id?: number;
|
|
17
|
+
path?: string;
|
|
18
|
+
line?: number | null;
|
|
19
|
+
start_line?: number | null;
|
|
20
|
+
}
|
|
21
|
+
export interface RestReview extends RestComment {
|
|
22
|
+
state: string;
|
|
23
|
+
commit_id: string;
|
|
24
|
+
submitted_at?: string | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function readRestFeedback(pr: number, repo: RepoInfo, knownViewerLogin?: string | null): Promise<{
|
|
27
|
+
threads: ReviewThread[];
|
|
28
|
+
comments: PrComment[];
|
|
29
|
+
reviews: RestReview[];
|
|
30
|
+
unavailable: {
|
|
31
|
+
field: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}[];
|
|
34
|
+
viewerLogin: string | null;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function restReviewToReview(review: RestReview): Review;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { isCcrTransport } from "./transport.mjs";
|
|
2
|
+
import { GitHubRequestError } from "./errors.mjs";
|
|
3
|
+
import { EXIT } from "../exit-codes.mjs";
|
|
4
|
+
import { readRestPages, restRepoPath, restArray, restObject, restNumber, restBoolean, malformedRest, } from "./rest-reader-core.mjs";
|
|
5
|
+
import { recordRestIdentity, recordThreadIdentity, restThreadId } from "./rest-identities.mjs";
|
|
6
|
+
import { parseCreatedAt } from "./batch-parser-helpers.mjs";
|
|
7
|
+
import { restCommentAuthorFields as authorFields } from "./rest-comment-authorship.mjs";
|
|
8
|
+
import { validateRestReviews } from "./rest-review-read.mjs";
|
|
9
|
+
import { validateRestComments, feedbackIdsMatch } from "./rest-feedback-validation.mjs";
|
|
10
|
+
import { readRestViewerLogin } from "./rest-viewer-read.mjs";
|
|
11
|
+
export async function readRestFeedback(pr, repo, knownViewerLogin) {
|
|
12
|
+
const prefix = restRepoPath(repo);
|
|
13
|
+
let inline = (await readRestPages(`${prefix}/pulls/${pr}/comments`)).nodes;
|
|
14
|
+
const issue = (await readRestPages(`${prefix}/issues/${pr}/comments`)).nodes;
|
|
15
|
+
const reviews = (await readRestPages(`${prefix}/pulls/${pr}/reviews`)).nodes;
|
|
16
|
+
if (new Set(issue.map((comment) => comment.id)).size !== issue.length ||
|
|
17
|
+
new Set(reviews.map((review) => review.id)).size !== reviews.length)
|
|
18
|
+
malformedRest("duplicate feedback IDs");
|
|
19
|
+
validateRestReviews(reviews);
|
|
20
|
+
validateRestComments([...inline, ...issue, ...reviews]);
|
|
21
|
+
let statuses = await readRestThreadStatuses(`${prefix}/pulls/${pr}/ccr/review_threads`);
|
|
22
|
+
if (statuses && !feedbackIdsMatch(inline, statuses)) {
|
|
23
|
+
// A reviewer can add or delete comments between these independently paginated reads.
|
|
24
|
+
// Retry both resources, with thread membership first, before certifying the join.
|
|
25
|
+
statuses = await readRestThreadStatuses(`${prefix}/pulls/${pr}/ccr/review_threads`);
|
|
26
|
+
inline = (await readRestPages(`${prefix}/pulls/${pr}/comments`)).nodes;
|
|
27
|
+
validateRestComments(inline);
|
|
28
|
+
if (statuses && !feedbackIdsMatch(inline, statuses))
|
|
29
|
+
throw new GitHubRequestError("Pull request feedback changed while REST snapshot was being read; retry", { status: 409, exitCodeOverride: EXIT.TEMPFAIL });
|
|
30
|
+
}
|
|
31
|
+
for (const comment of [...inline, ...issue, ...reviews]) {
|
|
32
|
+
await recordRestIdentity(repo, pr, comment.node_id, String(comment.id), "state" in comment ? "review" : "comment");
|
|
33
|
+
}
|
|
34
|
+
let unavailable = [];
|
|
35
|
+
if (!statuses && inline.length > 0)
|
|
36
|
+
unavailable = [
|
|
37
|
+
{
|
|
38
|
+
field: "reviewThreads.status",
|
|
39
|
+
reason: "REST has no resolved/outdated thread state; CCR review_threads is unavailable",
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
const inlineMap = new Map(inline.map((comment) => [comment.id, comment]));
|
|
43
|
+
if (inlineMap.size !== inline.length)
|
|
44
|
+
malformedRest("duplicate inline comment IDs");
|
|
45
|
+
const groups = [];
|
|
46
|
+
if (statuses) {
|
|
47
|
+
for (const status of statuses) {
|
|
48
|
+
const comments = status.comment_ids.map((id) => inlineMap.get(id));
|
|
49
|
+
const root = comments[0];
|
|
50
|
+
if (root.in_reply_to_id != null)
|
|
51
|
+
malformedRest("CCR thread root is a reply");
|
|
52
|
+
for (const reply of comments.slice(1))
|
|
53
|
+
if (reply.in_reply_to_id !== root.id)
|
|
54
|
+
malformedRest("CCR reply does not belong to its root");
|
|
55
|
+
groups.push({ id: root.id, comments, status });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
for (const comment of inline.filter((comment) => comment.in_reply_to_id == null)) {
|
|
60
|
+
groups.push({
|
|
61
|
+
id: comment.id,
|
|
62
|
+
comments: [comment, ...inline.filter((reply) => reply.in_reply_to_id === comment.id)],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (groups.reduce((sum, group) => sum + group.comments.length, 0) !== inline.length)
|
|
66
|
+
malformedRest("inline comment reply has no root");
|
|
67
|
+
}
|
|
68
|
+
const threads = [];
|
|
69
|
+
const viewerLogin = knownViewerLogin === undefined ? await readRestViewerLogin() : knownViewerLogin;
|
|
70
|
+
for (const group of groups) {
|
|
71
|
+
const root = group.comments[0];
|
|
72
|
+
const id = restThreadId(String(group.id));
|
|
73
|
+
await recordThreadIdentity(repo, pr, id, String(group.id));
|
|
74
|
+
const review = reviews.find((review) => review.id === root.pull_request_review_id);
|
|
75
|
+
threads.push({
|
|
76
|
+
id,
|
|
77
|
+
...(group.status
|
|
78
|
+
? { isResolved: group.status.resolved, isOutdated: group.status.outdated }
|
|
79
|
+
: {}),
|
|
80
|
+
path: group.status ? group.status.path : (root.path ?? null),
|
|
81
|
+
line: group.status ? group.status.line : (root.line ?? null),
|
|
82
|
+
startLine: root.start_line ?? null,
|
|
83
|
+
...(review ? { reviewId: review.node_id } : {}),
|
|
84
|
+
...authorFields(root, viewerLogin),
|
|
85
|
+
body: root.body ?? "",
|
|
86
|
+
url: root.html_url,
|
|
87
|
+
createdAtUnix: parseCreatedAt(root.created_at),
|
|
88
|
+
comments: group.comments.map((comment) => ({
|
|
89
|
+
id: comment.node_id,
|
|
90
|
+
...(review ? { reviewId: review.node_id } : {}),
|
|
91
|
+
...authorFields(comment, viewerLogin),
|
|
92
|
+
body: comment.body ?? "",
|
|
93
|
+
url: comment.html_url,
|
|
94
|
+
createdAtUnix: parseCreatedAt(comment.created_at),
|
|
95
|
+
})),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const comments = issue.map((comment) => ({
|
|
99
|
+
id: comment.node_id,
|
|
100
|
+
...authorFields(comment),
|
|
101
|
+
body: comment.body ?? "",
|
|
102
|
+
url: comment.html_url,
|
|
103
|
+
createdAtUnix: parseCreatedAt(comment.created_at),
|
|
104
|
+
}));
|
|
105
|
+
return { threads, comments, reviews, unavailable, viewerLogin };
|
|
106
|
+
}
|
|
107
|
+
async function readRestThreadStatuses(path) {
|
|
108
|
+
if (!isCcrTransport())
|
|
109
|
+
return undefined;
|
|
110
|
+
try {
|
|
111
|
+
const statuses = (await readRestPages(path)).nodes;
|
|
112
|
+
for (const status of statuses) {
|
|
113
|
+
restObject(status, "CCR thread");
|
|
114
|
+
restBoolean(status.resolved, "CCR thread resolved");
|
|
115
|
+
restBoolean(status.outdated, "CCR thread outdated");
|
|
116
|
+
restArray(status.comment_ids, "CCR comment_ids");
|
|
117
|
+
if (status.path !== null && typeof status.path !== "string")
|
|
118
|
+
malformedRest("CCR thread path");
|
|
119
|
+
if (status.line !== null)
|
|
120
|
+
restNumber(status.line, "CCR thread line");
|
|
121
|
+
if (status.comment_ids.length === 0)
|
|
122
|
+
malformedRest("CCR thread has no root comment");
|
|
123
|
+
for (const id of status.comment_ids)
|
|
124
|
+
restNumber(id, "CCR comment ID");
|
|
125
|
+
}
|
|
126
|
+
return statuses;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (!(error instanceof GitHubRequestError) || error.status !== 404)
|
|
130
|
+
throw error;
|
|
131
|
+
// The proxy answers a missing PR with 404 too; that is not an unavailable route.
|
|
132
|
+
if (/could not resolve to a pullrequest/i.test(error.responseMessage ?? error.message)) {
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export function restReviewToReview(review) {
|
|
139
|
+
return {
|
|
140
|
+
id: review.node_id,
|
|
141
|
+
...authorFields(review),
|
|
142
|
+
body: review.body ?? "",
|
|
143
|
+
url: review.html_url,
|
|
144
|
+
...(review.commit_id && { commitOid: review.commit_id }),
|
|
145
|
+
...(review.submitted_at && { createdAtUnix: parseCreatedAt(review.submitted_at) }),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { restObject, restString, restNumber, malformedRest } from "./rest-reader-core.mjs";
|
|
2
|
+
export function validateRestComments(comments) {
|
|
3
|
+
for (const comment of comments) {
|
|
4
|
+
restObject(comment, "feedback comment");
|
|
5
|
+
restString(comment.html_url, "feedback html_url");
|
|
6
|
+
if (!("state" in comment)) {
|
|
7
|
+
restString(comment.created_at, "comment created_at");
|
|
8
|
+
restString(comment.updated_at, "comment updated_at");
|
|
9
|
+
if (!Number.isFinite(Date.parse(comment.created_at)) ||
|
|
10
|
+
!Number.isFinite(Date.parse(comment.updated_at)))
|
|
11
|
+
malformedRest("feedback timestamps");
|
|
12
|
+
}
|
|
13
|
+
restNumber(comment.id, "feedback id");
|
|
14
|
+
restString(comment.node_id, "feedback node_id");
|
|
15
|
+
if (typeof comment.body !== "string" && comment.body !== null)
|
|
16
|
+
malformedRest("feedback body");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function feedbackIdsMatch(inline, statuses) {
|
|
20
|
+
const inlineIds = new Set(inline.map((comment) => comment.id));
|
|
21
|
+
if (inlineIds.size !== inline.length)
|
|
22
|
+
malformedRest("duplicate inline comment IDs");
|
|
23
|
+
const threadIds = statuses.flatMap((status) => status.comment_ids);
|
|
24
|
+
const covered = new Set(threadIds);
|
|
25
|
+
if (covered.size !== threadIds.length)
|
|
26
|
+
malformedRest("repeated CCR thread comment IDs");
|
|
27
|
+
return covered.size === inlineIds.size && threadIds.every((id) => inlineIds.has(id));
|
|
28
|
+
}
|
|
@@ -3,6 +3,12 @@ import { type RateLimitInfo } from "./http-utils.mts";
|
|
|
3
3
|
export interface RestResult<T = unknown> {
|
|
4
4
|
data: T;
|
|
5
5
|
rateLimit?: RateLimitInfo;
|
|
6
|
+
/** HTTP status, including documented non-2xx statuses accepted by the caller. */
|
|
7
|
+
status?: number;
|
|
8
|
+
/** Validator of a conditional read: the stored ETag on a 304, the response ETag on a 200. */
|
|
9
|
+
etag?: string;
|
|
10
|
+
/** GitHub pagination header, retained for named REST readers. */
|
|
11
|
+
link?: string;
|
|
6
12
|
}
|
|
7
13
|
/**
|
|
8
14
|
* Enables `If-None-Match` conditional requests for a REST call, backed by a
|
|
@@ -11,11 +17,17 @@ export interface RestResult<T = unknown> {
|
|
|
11
17
|
* the primary REST rate-limit quota.
|
|
12
18
|
*/
|
|
13
19
|
export interface RestRequestOptions {
|
|
20
|
+
/** Endpoints such as merge-async return useful state on documented non-2xx responses. */
|
|
21
|
+
acceptStatuses?: readonly number[];
|
|
14
22
|
conditional?: {
|
|
15
23
|
key: StateKey;
|
|
16
24
|
/** Logical cache name; must be unique per distinct resource + page. */
|
|
17
25
|
name: string;
|
|
18
26
|
headSha?: string;
|
|
27
|
+
/** Return false to keep a 200 body out of the cache (e.g. a still-computing resource). */
|
|
28
|
+
shouldStore?: (body: unknown) => boolean;
|
|
29
|
+
/** Send no validator, forcing a fresh body that then replaces the cached entry. */
|
|
30
|
+
revalidate?: boolean;
|
|
19
31
|
};
|
|
20
32
|
}
|
|
21
33
|
export declare function rest<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
|