pr-shepherd 0.56.6 → 0.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +150 -280
- package/bin/api.d.mts +25 -3
- package/bin/api.mjs +51 -13
- package/bin/checks/classify.d.mts +1 -0
- package/bin/checks/classify.mjs +1 -1
- package/bin/checks/conclusions.d.mts +5 -1
- package/bin/checks/conclusions.mjs +8 -2
- package/bin/checks/superseded.d.mts +8 -2
- package/bin/checks/superseded.mjs +24 -7
- package/bin/classify/loader.d.mts +2 -0
- package/bin/classify/loader.mjs +15 -0
- package/bin/classify/standard/author.d.mts +3 -0
- package/bin/classify/standard/author.mjs +10 -0
- package/bin/classify/standard/codecov-report.d.mts +4 -0
- package/bin/classify/standard/codecov-report.mjs +10 -0
- package/bin/classify/standard/coderabbit-notices.d.mts +8 -0
- package/bin/classify/standard/coderabbit-notices.mjs +38 -0
- package/bin/classify/standard/codex-notices.d.mts +8 -0
- package/bin/classify/standard/codex-notices.mjs +34 -0
- package/bin/classify/standard/dependabot-notices.d.mts +4 -0
- package/bin/classify/standard/dependabot-notices.mjs +15 -0
- package/bin/classify/standard/gemini-notices.d.mts +4 -0
- package/bin/classify/standard/gemini-notices.mjs +13 -0
- package/bin/classify/standard/index.d.mts +7 -0
- package/bin/classify/standard/index.mjs +21 -0
- package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
- package/bin/classify/standard/kilo-no-issues.mjs +17 -0
- package/bin/classify/standard/sourcery-notices.d.mts +8 -0
- package/bin/classify/standard/sourcery-notices.mjs +20 -0
- package/bin/cli/api-usage-formatter.mjs +6 -1
- package/bin/cli/apply-merge-handler.d.mts +3 -0
- package/bin/cli/apply-merge-handler.mjs +73 -0
- package/bin/cli/arg-flags.d.mts +2 -0
- package/bin/cli/arg-flags.mjs +47 -0
- package/bin/cli/args.mjs +10 -41
- package/bin/cli/clean-handler.d.mts +1 -0
- package/bin/cli/clean-handler.mjs +71 -0
- package/bin/cli/default-poll.mjs +3 -0
- package/bin/cli/event-state-scope.d.mts +19 -0
- package/bin/cli/event-state-scope.mjs +60 -0
- package/bin/cli/fix-formatter.mjs +5 -11
- package/bin/cli/handlers.d.mts +0 -1
- package/bin/cli/handlers.mjs +8 -64
- package/bin/cli/help-command-pages.d.mts +62 -18
- package/bin/cli/help-command-pages.mjs +59 -15
- package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
- package/bin/cli/help-iterate-poll-pages.mjs +6 -0
- package/bin/cli/help-log-file-page.d.mts +1 -1
- package/bin/cli/help-log-file-page.mjs +4 -1
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +5 -0
- package/bin/cli/help.d.mts +63 -19
- package/bin/cli/help.mjs +2 -0
- package/bin/cli/iterate-branch-segment.d.mts +2 -0
- package/bin/cli/iterate-branch-segment.mjs +12 -7
- package/bin/cli/iterate-flags.d.mts +7 -1
- package/bin/cli/iterate-flags.mjs +38 -0
- package/bin/cli/iterate-formatter.d.mts +6 -6
- package/bin/cli/iterate-formatter.mjs +56 -22
- package/bin/cli/iterate-instructions.mjs +45 -15
- package/bin/cli/iterate-lean.mjs +39 -14
- package/bin/cli/iterate-merge-formatter.d.mts +6 -0
- package/bin/cli/iterate-merge-formatter.mjs +24 -14
- package/bin/cli/iterate-verbose.mjs +3 -6
- package/bin/cli/list-formatters.mjs +3 -1
- package/bin/cli/mutate-formatter.mjs +10 -5
- package/bin/cli/next-check-format.d.mts +7 -0
- package/bin/cli/next-check-format.mjs +10 -0
- package/bin/cli/playbook-handler.d.mts +5 -0
- package/bin/cli/playbook-handler.mjs +40 -0
- package/bin/cli/poll-handler.mjs +7 -6
- package/bin/cli/poll-summary-formatter.mjs +11 -2
- package/bin/cli/poll-targets.mjs +2 -0
- package/bin/cli/resolve-validators.mjs +2 -2
- package/bin/cli/runner.mjs +6 -1
- package/bin/cli/stack-layer-view.d.mts +33 -0
- package/bin/cli/stack-layer-view.mjs +1 -0
- package/bin/cli/stack-overview.d.mts +10 -28
- package/bin/cli/stack-overview.mjs +39 -14
- package/bin/cli/transport-args.d.mts +5 -0
- package/bin/cli/transport-args.mjs +23 -0
- package/bin/cli/transport-formatter.d.mts +9 -0
- package/bin/cli/transport-formatter.mjs +13 -0
- package/bin/cli-parser.mjs +62 -7
- package/bin/commands/apply-merge-options.d.mts +4 -0
- package/bin/commands/apply-merge-options.mjs +34 -0
- package/bin/commands/apply-merge-output.d.mts +7 -0
- package/bin/commands/apply-merge-output.mjs +20 -0
- package/bin/commands/apply-merge-pull.d.mts +20 -0
- package/bin/commands/apply-merge-pull.mjs +15 -0
- package/bin/commands/apply-merge.d.mts +18 -0
- package/bin/commands/apply-merge.mjs +126 -0
- package/bin/commands/apply-queue-removal.mjs +7 -0
- package/bin/commands/check-annotations.mjs +28 -18
- package/bin/commands/check-execution-context.d.mts +5 -0
- package/bin/commands/check-execution-context.mjs +16 -1
- package/bin/commands/check-fingerprint.d.mts +26 -2
- package/bin/commands/check-fingerprint.mjs +64 -28
- package/bin/commands/check-terminal-report.mjs +11 -1
- package/bin/commands/check-unreported.d.mts +6 -2
- package/bin/commands/check-unreported.mjs +32 -12
- package/bin/commands/check-wait-detectors.d.mts +35 -0
- package/bin/commands/check-wait-detectors.mjs +107 -0
- package/bin/commands/check.d.mts +4 -2
- package/bin/commands/check.mjs +130 -40
- package/bin/commands/event-instructions.d.mts +11 -0
- package/bin/commands/event-instructions.mjs +27 -0
- package/bin/commands/event-mode.d.mts +14 -0
- package/bin/commands/event-mode.mjs +138 -0
- package/bin/commands/iterate/api-usage.mjs +4 -2
- package/bin/commands/iterate/base.mjs +4 -0
- package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
- package/bin/commands/iterate/check-instructions.d.mts +9 -10
- package/bin/commands/iterate/check-instructions.mjs +23 -22
- package/bin/commands/iterate/classify.mjs +19 -5
- package/bin/commands/iterate/escalate.mjs +13 -3
- package/bin/commands/iterate/fix-code.mjs +26 -9
- package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
- package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
- package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
- package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
- package/bin/commands/iterate/helpers.mjs +15 -2
- package/bin/commands/iterate/index.mjs +57 -16
- package/bin/commands/iterate/mark-ready.mjs +50 -10
- package/bin/commands/iterate/merge-plan.d.mts +19 -0
- package/bin/commands/iterate/merge-plan.mjs +88 -0
- package/bin/commands/iterate/merge-state.mjs +3 -0
- package/bin/commands/iterate/merge.d.mts +4 -15
- package/bin/commands/iterate/merge.mjs +18 -51
- package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
- package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
- package/bin/commands/iterate/native-stack-rebase.mjs +11 -0
- package/bin/commands/iterate/render.d.mts +8 -2
- package/bin/commands/iterate/render.mjs +28 -42
- package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
- package/bin/commands/iterate/stale-ancestry.mjs +4 -1
- package/bin/commands/iterate/stall.d.mts +5 -1
- package/bin/commands/iterate/stall.mjs +19 -7
- package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
- package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
- package/bin/commands/iterate/unreported-required.mjs +5 -2
- package/bin/commands/mark-files-as-viewed.mjs +7 -0
- package/bin/commands/next-check.d.mts +31 -0
- package/bin/commands/next-check.mjs +46 -0
- package/bin/commands/playbook.d.mts +13 -0
- package/bin/commands/playbook.mjs +48 -0
- package/bin/commands/poll-mode.d.mts +8 -0
- package/bin/commands/poll-mode.mjs +14 -0
- package/bin/commands/poll-progress.d.mts +1 -0
- package/bin/commands/poll-progress.mjs +6 -3
- package/bin/commands/poll-quota.d.mts +3 -2
- package/bin/commands/poll-quota.mjs +7 -3
- package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
- package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
- package/bin/commands/poll-summary-instructions.mjs +16 -17
- package/bin/commands/poll-summary.mjs +42 -17
- package/bin/commands/poll.d.mts +0 -1
- package/bin/commands/poll.mjs +11 -19
- package/bin/commands/quota-selection.d.mts +2 -1
- package/bin/commands/quota-selection.mjs +12 -7
- package/bin/commands/resolve-mutate.mjs +33 -5
- package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
- package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
- package/bin/commands/rule-auto-resolve.mjs +9 -3
- package/bin/commands/shepherd-journal.d.mts +5 -4
- package/bin/commands/shepherd-journal.mjs +9 -8
- package/bin/commands/stack-drain.d.mts +1 -9
- package/bin/commands/stack-drain.mjs +50 -37
- package/bin/commands/stack-handoffs.d.mts +10 -0
- package/bin/commands/stack-handoffs.mjs +25 -0
- package/bin/commands/stack-stall.d.mts +5 -0
- package/bin/commands/stack-stall.mjs +11 -0
- package/bin/commands/stack-work.d.mts +5 -0
- package/bin/commands/stack-work.mjs +32 -0
- package/bin/comments/existing-reply-scan.d.mts +17 -0
- package/bin/comments/existing-reply-scan.mjs +41 -0
- package/bin/comments/rate-limit.mjs +2 -2
- package/bin/comments/resolve.d.mts +7 -1
- package/bin/comments/resolve.mjs +108 -18
- package/bin/comments/rest-reply.d.mts +9 -0
- package/bin/comments/rest-reply.mjs +95 -0
- package/bin/comments/rest-review-mutations.d.mts +22 -0
- package/bin/comments/rest-review-mutations.mjs +125 -0
- package/bin/comments/review-visibility.d.mts +1 -1
- package/bin/comments/review-visibility.mjs +3 -2
- package/bin/comments/sha-poll.mjs +7 -3
- package/bin/comments/thread-visibility.mjs +10 -5
- package/bin/comments/uncertain-replies.d.mts +11 -0
- package/bin/comments/uncertain-replies.mjs +121 -0
- package/bin/comments/uncertain-reply-guards.d.mts +10 -0
- package/bin/comments/uncertain-reply-guards.mjs +94 -0
- package/bin/comments/visible-comments.d.mts +1 -1
- package/bin/comments/visible-comments.mjs +4 -2
- package/bin/config/load.d.mts +37 -1
- package/bin/config/load.mjs +73 -1
- package/bin/config.json +6 -2
- package/bin/exit-codes.d.mts +3 -0
- package/bin/exit-codes.mjs +8 -0
- package/bin/github/api-telemetry-aggregate.d.mts +15 -4
- package/bin/github/api-telemetry-aggregate.mjs +57 -18
- package/bin/github/api-telemetry.d.mts +4 -0
- package/bin/github/api-telemetry.mjs +22 -8
- package/bin/github/apply-review-preflight.d.mts +20 -0
- package/bin/github/apply-review-preflight.mjs +61 -0
- package/bin/github/base-behind-cache.d.mts +15 -0
- package/bin/github/base-behind-cache.mjs +18 -0
- package/bin/github/batch-parse-suites.d.mts +4 -2
- package/bin/github/batch-parse-suites.mjs +6 -1
- package/bin/github/batch-parser-helpers.mjs +40 -2
- package/bin/github/batch-parsers.mjs +1 -0
- package/bin/github/batch-raw-rules.d.mts +26 -0
- package/bin/github/batch-raw-types.d.mts +8 -6
- package/bin/github/batch-stack-evidence.d.mts +28 -0
- package/bin/github/batch-stack-evidence.mjs +62 -0
- package/bin/github/batch.d.mts +15 -2
- package/bin/github/batch.mjs +34 -17
- package/bin/github/captured-proxy-agent.d.mts +3 -0
- package/bin/github/captured-proxy-agent.mjs +8 -0
- package/bin/github/check-annotations-batch.d.mts +0 -5
- package/bin/github/check-annotations-batch.mjs +25 -4
- package/bin/github/check-annotations.mjs +3 -1
- package/bin/github/client.d.mts +8 -19
- package/bin/github/client.mjs +45 -13
- package/bin/github/fingerprint-fields.d.mts +8 -0
- package/bin/github/fingerprint-fields.mjs +6 -1
- package/bin/github/fingerprint.d.mts +4 -2
- package/bin/github/fingerprint.mjs +6 -38
- package/bin/github/github-fetch.d.mts +2 -0
- package/bin/github/github-fetch.mjs +32 -0
- package/bin/github/gql/base-behind.gql +3 -0
- package/bin/github/gql/batch-pr-page.gql +16 -0
- package/bin/github/gql/batch-pr.gql +57 -0
- package/bin/github/gql/commit-check-contexts.gql +17 -0
- package/bin/github/gql/get-pr-body.gql +1 -0
- package/bin/github/gql/poll-stack-summary.gql +1 -0
- package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
- package/bin/github/gql/poll-summary-fragment.gql +65 -15
- package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
- package/bin/github/graphql-http.mjs +9 -4
- package/bin/github/graphql-internal-retry.d.mts +2 -0
- package/bin/github/graphql-internal-retry.mjs +1 -1
- package/bin/github/graphql-quota-probe.d.mts +6 -0
- package/bin/github/graphql-quota-probe.mjs +56 -0
- package/bin/github/graphql-session-refusal.d.mts +8 -0
- package/bin/github/graphql-session-refusal.mjs +14 -0
- package/bin/github/http-request.d.mts +1 -1
- package/bin/github/http-request.mjs +22 -6
- package/bin/github/http-utils.mjs +6 -0
- package/bin/github/legacy-dispatcher.d.mts +6 -0
- package/bin/github/legacy-dispatcher.mjs +44 -0
- package/bin/github/merge-target-rules.d.mts +12 -3
- package/bin/github/merge-target-rules.mjs +71 -34
- package/bin/github/mutation-policy.d.mts +6 -0
- package/bin/github/mutation-policy.mjs +21 -0
- package/bin/github/poll-summary-annotation-probe.mjs +17 -10
- package/bin/github/poll-summary-check-blockers.d.mts +1 -1
- package/bin/github/poll-summary-check-blockers.mjs +2 -2
- package/bin/github/poll-summary-check-hydration.mjs +25 -10
- package/bin/github/poll-summary-checks.d.mts +2 -2
- package/bin/github/poll-summary-checks.mjs +9 -6
- package/bin/github/poll-summary-command.d.mts +2 -0
- package/bin/github/poll-summary-command.mjs +24 -0
- package/bin/github/poll-summary-fingerprint.d.mts +1 -1
- package/bin/github/poll-summary-fingerprint.mjs +4 -2
- package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
- package/bin/github/poll-summary-handoff-fields.mjs +73 -0
- package/bin/github/poll-summary-projector.d.mts +1 -1
- package/bin/github/poll-summary-projector.mjs +17 -24
- package/bin/github/poll-summary-raw.d.mts +70 -10
- package/bin/github/poll-summary-readiness.d.mts +6 -1
- package/bin/github/poll-summary-readiness.mjs +13 -4
- package/bin/github/poll-summary-rest-queue.d.mts +3 -0
- package/bin/github/poll-summary-rest-queue.mjs +12 -0
- package/bin/github/poll-summary-review.d.mts +1 -1
- package/bin/github/poll-summary-review.mjs +35 -20
- package/bin/github/poll-summary-route.mjs +19 -3
- package/bin/github/poll-summary-stack-page.d.mts +11 -0
- package/bin/github/poll-summary-stack-page.mjs +27 -0
- package/bin/github/poll-summary-unreported.d.mts +3 -1
- package/bin/github/poll-summary-unreported.mjs +6 -3
- package/bin/github/poll-summary.d.mts +4 -1
- package/bin/github/poll-summary.mjs +47 -33
- package/bin/github/queries.d.mts +7 -3
- package/bin/github/queries.mjs +8 -4
- package/bin/github/rate-limit-sample.d.mts +7 -0
- package/bin/github/rate-limit-sample.mjs +12 -0
- package/bin/github/reply-recovery-read.d.mts +50 -0
- package/bin/github/reply-recovery-read.mjs +118 -0
- package/bin/github/reply-thread-transcripts.d.mts +0 -1
- package/bin/github/reply-thread-transcripts.mjs +24 -1
- package/bin/github/rest-annotation-read.d.mts +5 -0
- package/bin/github/rest-annotation-read.mjs +44 -0
- package/bin/github/rest-batch-read.d.mts +12 -0
- package/bin/github/rest-batch-read.mjs +57 -0
- package/bin/github/rest-batch-snapshot.d.mts +30 -0
- package/bin/github/rest-batch-snapshot.mjs +115 -0
- package/bin/github/rest-batch-unavailable.d.mts +5 -0
- package/bin/github/rest-batch-unavailable.mjs +16 -0
- package/bin/github/rest-check-read.d.mts +34 -0
- package/bin/github/rest-check-read.mjs +124 -0
- package/bin/github/rest-comment-authorship.d.mts +7 -0
- package/bin/github/rest-comment-authorship.mjs +11 -0
- package/bin/github/rest-conditional-scope.d.mts +20 -0
- package/bin/github/rest-conditional-scope.mjs +44 -0
- package/bin/github/rest-explicit-summary-read.d.mts +3 -0
- package/bin/github/rest-explicit-summary-read.mjs +8 -0
- package/bin/github/rest-feedback-read.d.mts +36 -0
- package/bin/github/rest-feedback-read.mjs +147 -0
- package/bin/github/rest-feedback-validation.d.mts +5 -0
- package/bin/github/rest-feedback-validation.mjs +28 -0
- package/bin/github/rest-http.d.mts +12 -0
- package/bin/github/rest-http.mjs +108 -23
- package/bin/github/rest-identities.d.mts +22 -0
- package/bin/github/rest-identities.mjs +134 -0
- package/bin/github/rest-merge-stack-guard.d.mts +14 -0
- package/bin/github/rest-merge-stack-guard.mjs +41 -0
- package/bin/github/rest-merge.d.mts +29 -0
- package/bin/github/rest-merge.mjs +56 -0
- package/bin/github/rest-merged-base-read.d.mts +9 -0
- package/bin/github/rest-merged-base-read.mjs +48 -0
- package/bin/github/rest-pr-core.d.mts +52 -0
- package/bin/github/rest-pr-core.mjs +60 -0
- package/bin/github/rest-reader-core.d.mts +16 -0
- package/bin/github/rest-reader-core.mjs +154 -0
- package/bin/github/rest-review-read.d.mts +11 -0
- package/bin/github/rest-review-read.mjs +47 -0
- package/bin/github/rest-rules-read.d.mts +9 -0
- package/bin/github/rest-rules-read.mjs +154 -0
- package/bin/github/rest-session-refusal.d.mts +5 -0
- package/bin/github/rest-session-refusal.mjs +21 -0
- package/bin/github/rest-snapshot-context.d.mts +10 -0
- package/bin/github/rest-snapshot-context.mjs +1 -0
- package/bin/github/rest-stack-read.d.mts +21 -0
- package/bin/github/rest-stack-read.mjs +47 -0
- package/bin/github/rest-stack-summary.d.mts +8 -0
- package/bin/github/rest-stack-summary.mjs +62 -0
- package/bin/github/rest-summary-read.d.mts +7 -0
- package/bin/github/rest-summary-read.mjs +112 -0
- package/bin/github/rest-terminal-batch.d.mts +14 -0
- package/bin/github/rest-terminal-batch.mjs +45 -0
- package/bin/github/rest-text.mjs +7 -3
- package/bin/github/rest-thread-read.d.mts +4 -0
- package/bin/github/rest-thread-read.mjs +35 -0
- package/bin/github/rest-viewer-read.d.mts +2 -0
- package/bin/github/rest-viewer-read.mjs +41 -0
- package/bin/github/stack-bottom.d.mts +3 -0
- package/bin/github/stack-bottom.mjs +14 -0
- package/bin/github/stack-handoff-context.d.mts +25 -0
- package/bin/github/stack-handoff-context.mjs +12 -0
- package/bin/github/stack-handoff-page.d.mts +17 -0
- package/bin/github/stack-handoff-page.mjs +306 -0
- package/bin/github/stack-handoff.d.mts +16 -0
- package/bin/github/stack-handoff.mjs +115 -0
- package/bin/github/stack-order.d.mts +6 -0
- package/bin/github/stack-order.mjs +13 -0
- package/bin/github/stack-read.d.mts +7 -5
- package/bin/github/stack-read.mjs +22 -24
- package/bin/github/stack-size-hint.d.mts +3 -0
- package/bin/github/stack-size-hint.mjs +14 -0
- package/bin/github/suggestion-thread.mjs +27 -1
- package/bin/github/thread-comments.mjs +9 -1
- package/bin/github/transport-evidence.d.mts +20 -0
- package/bin/github/transport-evidence.mjs +26 -0
- package/bin/github/transport-mode.d.mts +2 -0
- package/bin/github/transport-mode.mjs +5 -0
- package/bin/github/transport.d.mts +19 -0
- package/bin/github/transport.mjs +126 -0
- package/bin/github/unsupported-rest.d.mts +4 -0
- package/bin/github/unsupported-rest.mjs +7 -0
- package/bin/mcp/server.mjs +52 -3
- package/bin/merge-status/derive.d.mts +2 -0
- package/bin/merge-status/derive.mjs +17 -13
- package/bin/merge-status/required-check-requirements.d.mts +7 -0
- package/bin/merge-status/required-check-requirements.mjs +15 -0
- package/bin/merge-status/requirements-format.d.mts +5 -0
- package/bin/merge-status/requirements-format.mjs +41 -12
- package/bin/merge-status/requirements.mjs +18 -7
- package/bin/state/base.mjs +41 -0
- package/bin/state/durable-state.d.mts +6 -0
- package/bin/state/durable-state.mjs +41 -0
- package/bin/state/graphql-quota-policy.mjs +23 -16
- package/bin/state/merge-request.d.mts +25 -0
- package/bin/state/merge-request.mjs +145 -0
- package/bin/state/pr-fingerprint.d.mts +15 -0
- package/bin/state/pr-fingerprint.mjs +22 -7
- package/bin/state/rest-cache.d.mts +3 -0
- package/bin/state/rest-cache.mjs +1 -0
- package/bin/state/rest-snapshot-report.d.mts +21 -0
- package/bin/state/rest-snapshot-report.mjs +52 -0
- package/bin/state/seen-comments.d.mts +3 -0
- package/bin/state/seen-comments.mjs +18 -0
- package/bin/types/api-usage.d.mts +9 -1
- package/bin/types/escalate.d.mts +1 -1
- package/bin/types/github.d.mts +23 -9
- package/bin/types/iterate.d.mts +24 -2
- package/bin/types/merge-action.d.mts +1 -1
- package/bin/types/merge-requirements.d.mts +7 -7
- package/bin/types/next-check.d.mts +12 -0
- package/bin/types/next-check.mjs +1 -0
- package/bin/types/poll-summary.d.mts +21 -0
- package/bin/types/report.d.mts +13 -1
- package/bin/types/review-thread.d.mts +1 -1
- package/bin/types.d.mts +1 -0
- package/bin/types.mjs +1 -0
- package/package.json +4 -3
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
- package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
- package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
- package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
- package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
- package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +3 -1
- package/bin/github/gql/pr-fingerprint.gql +0 -75
- package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
- package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
- package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
|
@@ -1,32 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PollSummaryResult, PollSummaryStackAncestry, StackNextAction } from "../types.mts";
|
|
2
2
|
import type { ApiUsage, GraphqlQuotaWarning } from "../types/api-usage.mts";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
pr: number;
|
|
6
|
-
title: string;
|
|
7
|
-
url: string;
|
|
8
|
-
state: PollSummaryItem["state"];
|
|
9
|
-
shepherded: boolean;
|
|
10
|
-
mergeable: boolean;
|
|
11
|
-
blocker?: string;
|
|
12
|
-
author?: string;
|
|
13
|
-
owned?: true;
|
|
14
|
-
isDraft?: true;
|
|
15
|
-
isInMergeQueue?: true;
|
|
16
|
-
position?: number;
|
|
17
|
-
stackSize?: number;
|
|
18
|
-
baseRefName: string;
|
|
19
|
-
failing?: number;
|
|
20
|
-
inProgress?: number;
|
|
21
|
-
actionable?: number;
|
|
22
|
-
checksIncomplete?: true;
|
|
23
|
-
reviewIncomplete?: true;
|
|
24
|
-
queueRemoval?: {
|
|
25
|
-
reason: string | null;
|
|
26
|
-
actor?: string;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
3
|
+
import type { StackLayerView } from "./stack-layer-view.mts";
|
|
4
|
+
import type { NextCheck } from "../types/next-check.mts";
|
|
29
5
|
export interface StackOverview {
|
|
6
|
+
transport?: "rest";
|
|
7
|
+
transportUnavailable?: Array<{
|
|
8
|
+
field: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
}>;
|
|
30
11
|
mode: "summary";
|
|
31
12
|
repo: string;
|
|
32
13
|
selection: Extract<PollSummaryResult["selection"], {
|
|
@@ -40,9 +21,10 @@ export interface StackOverview {
|
|
|
40
21
|
instructions?: string[];
|
|
41
22
|
apiUsage?: ApiUsage;
|
|
42
23
|
quotaWarning?: GraphqlQuotaWarning;
|
|
24
|
+
pollMode?: "event";
|
|
25
|
+
nextCheck?: NextCheck;
|
|
43
26
|
}
|
|
44
27
|
/** Lean stack payload shared by Markdown, JSON, and MCP. */
|
|
45
28
|
export declare function projectStackOverview(result: PollSummaryResult): StackOverview;
|
|
46
29
|
/** Markdown for a stack overview. */
|
|
47
30
|
export declare function formatStackOverview(overview: StackOverview): string;
|
|
48
|
-
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { stackLayerBlockReason } from "../commands/stack-layer-readiness.mjs";
|
|
2
2
|
import { formatApiUsage, formatQuotaWarning } from "./api-usage-formatter.mjs";
|
|
3
|
+
import { formatTransportEvidence } from "./transport-formatter.mjs";
|
|
4
|
+
import { formatNextCheckLines } from "./next-check-format.mjs";
|
|
5
|
+
import { formatPrUrl } from "../pr-reference.mjs";
|
|
3
6
|
/** Lean stack payload shared by Markdown, JSON, and MCP. */
|
|
4
7
|
export function projectStackOverview(result) {
|
|
5
8
|
const selection = result.selection;
|
|
@@ -10,32 +13,43 @@ export function projectStackOverview(result) {
|
|
|
10
13
|
return {
|
|
11
14
|
mode: "summary",
|
|
12
15
|
repo: result.repo,
|
|
16
|
+
...(result.transport && { transport: result.transport }),
|
|
17
|
+
...(result.transportUnavailable?.length && {
|
|
18
|
+
transportUnavailable: result.transportUnavailable,
|
|
19
|
+
}),
|
|
13
20
|
selection,
|
|
14
21
|
reason: result.reason,
|
|
15
|
-
...(result.stackMergeable
|
|
22
|
+
...(result.stackMergeable && { stackMergeable: true }),
|
|
16
23
|
...(result.nextAction && { nextAction: result.nextAction }),
|
|
17
24
|
prs: result.prs.map((item) => projectLayer(item, stale.has(item.pr))),
|
|
18
25
|
...(result.stackAncestry?.length && { stackAncestry: result.stackAncestry }),
|
|
19
26
|
...(result.instructions?.length && { instructions: result.instructions }),
|
|
20
27
|
...(result.apiUsage && { apiUsage: result.apiUsage }),
|
|
21
28
|
...(result.quotaWarning && { quotaWarning: result.quotaWarning }),
|
|
29
|
+
...(result.pollMode && { pollMode: result.pollMode }),
|
|
30
|
+
...(result.nextCheck && { nextCheck: result.nextCheck }),
|
|
22
31
|
};
|
|
23
32
|
}
|
|
24
33
|
function projectLayer(item, stale) {
|
|
25
|
-
|
|
34
|
+
// A merged or closed layer's state already says it will not merge; skip the `closed` blocker.
|
|
35
|
+
const open = item.state === "OPEN";
|
|
36
|
+
const blocker = open ? layerBlocker(item, stale) : undefined;
|
|
26
37
|
const removal = item.queueRemoval;
|
|
27
38
|
return {
|
|
28
39
|
pr: item.pr,
|
|
40
|
+
...(item.transport && { transport: item.transport }),
|
|
41
|
+
...(item.transportUnavailable?.length && { transportUnavailable: item.transportUnavailable }),
|
|
29
42
|
title: item.title,
|
|
30
43
|
url: item.url,
|
|
31
44
|
state: item.state,
|
|
32
|
-
|
|
33
|
-
mergeable: blocker === undefined,
|
|
45
|
+
...(item.readyReceipt === true && { shepherded: true }),
|
|
46
|
+
...(open && { mergeable: blocker === undefined }),
|
|
34
47
|
...(blocker && { blocker }),
|
|
35
48
|
...(item.authorLogin && { author: item.authorLogin }),
|
|
36
49
|
...(item.owned && { owned: true }),
|
|
37
50
|
...(item.isDraft && { isDraft: true }),
|
|
38
51
|
...(item.isInMergeQueue && { isInMergeQueue: true }),
|
|
52
|
+
...(item.requiresMergeQueue !== undefined && { requiresMergeQueue: item.requiresMergeQueue }),
|
|
39
53
|
...(item.stack && { position: item.stack.position, stackSize: item.stack.size }),
|
|
40
54
|
baseRefName: item.baseRefName,
|
|
41
55
|
...(blocker === "failing-checks" &&
|
|
@@ -80,15 +94,15 @@ export function formatStackOverview(overview) {
|
|
|
80
94
|
const lines = [
|
|
81
95
|
`# ${overview.repo} stack #${stack.stackNumber}${stackTerminalTag(overview.nextAction)} — ${overview.reason}`,
|
|
82
96
|
"",
|
|
83
|
-
`
|
|
84
|
-
...(overview.stackMergeable
|
|
85
|
-
? [`stackMergeable: ${overview.stackMergeable}`]
|
|
86
|
-
: []),
|
|
97
|
+
`anchor PR #${stack.anchor} · ${stack.stackSize} layers · mode \`${overview.mode}\`${overview.pollMode ? ` · pollMode \`${overview.pollMode}\`` : ""}`,
|
|
98
|
+
...(overview.stackMergeable ? ["stackMergeable: true"] : []),
|
|
87
99
|
...(overview.nextAction ? [`nextAction: ${overview.nextAction}`] : []),
|
|
100
|
+
...formatNextCheckLines(overview.nextCheck),
|
|
88
101
|
"",
|
|
89
102
|
"## Layers",
|
|
90
103
|
"",
|
|
91
|
-
...overview.prs.flatMap(formatLayerLines),
|
|
104
|
+
...overview.prs.flatMap((layer, i) => formatLayerLines(layer, i, overview)),
|
|
105
|
+
...formatTransportEvidence(overview),
|
|
92
106
|
];
|
|
93
107
|
if (overview.stackAncestry?.length) {
|
|
94
108
|
lines.push("", "## Stack ancestry", "");
|
|
@@ -107,10 +121,16 @@ export function formatStackOverview(overview) {
|
|
|
107
121
|
lines.push("", usage);
|
|
108
122
|
return lines.join("\n");
|
|
109
123
|
}
|
|
110
|
-
|
|
124
|
+
// `inOrder`: the list index shows the position; a row links only a URL the repo/number don't imply.
|
|
125
|
+
function formatLayerLines(layer, index, overview) {
|
|
126
|
+
const inOrder = layer.position === index + 1 && layer.stackSize === overview.selection.stackSize;
|
|
111
127
|
const facts = [
|
|
112
|
-
layer.shepherded ? "shepherded" :
|
|
113
|
-
layer.mergeable
|
|
128
|
+
layer.shepherded ? "shepherded" : undefined,
|
|
129
|
+
layer.mergeable === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: layer.mergeable
|
|
132
|
+
? "mergeable"
|
|
133
|
+
: `not mergeable (\`${layer.blocker ?? "unknown"}\`)`,
|
|
114
134
|
layer.author ? `owner \`@${layer.author}\`` : undefined,
|
|
115
135
|
layer.owned ? "owned" : undefined,
|
|
116
136
|
].filter((fact) => fact !== undefined);
|
|
@@ -118,7 +138,10 @@ function formatLayerLines(layer) {
|
|
|
118
138
|
layer.state,
|
|
119
139
|
layer.isDraft ? "draft" : undefined,
|
|
120
140
|
layer.isInMergeQueue ? "in merge queue" : undefined,
|
|
121
|
-
layer.
|
|
141
|
+
layer.requiresMergeQueue !== undefined
|
|
142
|
+
? `merge queue ${layer.requiresMergeQueue ? "required" : "not required"}`
|
|
143
|
+
: undefined,
|
|
144
|
+
!inOrder && layer.position !== undefined && layer.stackSize !== undefined
|
|
122
145
|
? `position ${layer.position}/${layer.stackSize}`
|
|
123
146
|
: undefined,
|
|
124
147
|
`base \`${layer.baseRefName}\``,
|
|
@@ -132,7 +155,9 @@ function formatLayerLines(layer) {
|
|
|
132
155
|
: undefined,
|
|
133
156
|
].filter((detail) => detail !== undefined);
|
|
134
157
|
return [
|
|
135
|
-
`- [PR #${layer.pr}: ${layer.title}](${layer.url}) — ${facts.join(" · ")}`,
|
|
158
|
+
`- ${layer.url === formatPrUrl(overview.repo, layer.pr) ? `PR #${layer.pr}: ${layer.title}` : `[PR #${layer.pr}: ${layer.title}](${layer.url})`}${facts.length > 0 ? ` — ${facts.join(" · ")}` : ""}`,
|
|
136
159
|
` - ${details.join(" · ")}`,
|
|
160
|
+
...(layer.transport ? [` - transport \`${layer.transport}\``] : []),
|
|
161
|
+
...(layer.transportUnavailable ?? []).map(({ field, reason }) => ` - unavailable \`${field}\`: ${reason}`),
|
|
137
162
|
];
|
|
138
163
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Strip the global flag before subcommand detection; validation follows help. */
|
|
2
|
+
export function extractTransportArgs(raw) {
|
|
3
|
+
const args = [];
|
|
4
|
+
let transport;
|
|
5
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
6
|
+
const arg = raw[index];
|
|
7
|
+
if (arg.startsWith("--transport=")) {
|
|
8
|
+
transport = arg.slice("--transport=".length);
|
|
9
|
+
}
|
|
10
|
+
else if (arg === "--transport") {
|
|
11
|
+
const next = raw[index + 1];
|
|
12
|
+
transport = next && !next.startsWith("-") ? raw[++index] : "";
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
args.push(arg);
|
|
16
|
+
// A flag-looking string can be another option's literal value.
|
|
17
|
+
if (FLAGS_WITH_VALUES.has(arg) && index + 1 < raw.length)
|
|
18
|
+
args.push(raw[++index]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { args, transport };
|
|
22
|
+
}
|
|
23
|
+
import { FLAGS_WITH_VALUES } from "./arg-flags.mjs";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function formatTransportEvidence(value) {
|
|
2
|
+
return [
|
|
3
|
+
...(value.transport ? [`**transport** \`${value.transport}\``] : []),
|
|
4
|
+
...(value.transportUnavailable?.length
|
|
5
|
+
? [
|
|
6
|
+
"",
|
|
7
|
+
"## Unavailable transport fields",
|
|
8
|
+
"",
|
|
9
|
+
...value.transportUnavailable.map(({ field, reason }) => `- \`${field}\`: ${reason}`),
|
|
10
|
+
]
|
|
11
|
+
: []),
|
|
12
|
+
];
|
|
13
|
+
}
|
package/bin/cli-parser.mjs
CHANGED
|
@@ -1,25 +1,60 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
/** CLI argument parsing and subcommand dispatch for pr-shepherd. See --help for usage. */
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
-
import { EXIT, errorToExitCode } from "./exit-codes.mjs";
|
|
4
|
+
import { EXIT, applyReviewResultToExitCode, errorToExitCode } from "./exit-codes.mjs";
|
|
5
5
|
import { runResolveMutate } from "./commands/resolve.mjs";
|
|
6
6
|
import { runLogFile } from "./commands/log-file.mjs";
|
|
7
|
+
import { handleClean } from "./cli/clean-handler.mjs";
|
|
7
8
|
import { parseCommonArgs, getFlag, hasFlag, parseList } from "./cli/args.mjs";
|
|
8
9
|
import { isDefaultPollInvocation, validateDefaultPollArgs } from "./cli/default-poll.mjs";
|
|
9
10
|
import { USAGE, helpKeyForArgs, maybePrintHelp } from "./cli/help.mjs";
|
|
10
11
|
import { formatMutateResult } from "./cli/formatters.mjs";
|
|
11
|
-
import {
|
|
12
|
+
import { handleCommitSuggestion, handleSuggestionPatches, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
|
|
12
13
|
import { handleJournal } from "./cli/journal-handler.mjs";
|
|
13
14
|
import { handleJournalExtract } from "./cli/journal-extract-handler.mjs";
|
|
15
|
+
import { handlePlaybook } from "./cli/playbook-handler.mjs";
|
|
14
16
|
import { handlePoll } from "./cli/poll-handler.mjs";
|
|
15
17
|
import { warnPrrcThreadIds, validateRequireSha, rejectPrrcMinimizeIds, } from "./cli/resolve-validators.mjs";
|
|
16
18
|
import { handleCheckBlocker } from "./cli/check-blocker-handler.mjs";
|
|
17
19
|
import { handleQueueRemoval } from "./cli/queue-removal-handler.mjs";
|
|
18
20
|
import { setupLog } from "./log/setup.mjs";
|
|
21
|
+
import { rejectInvalidPollModeFlag, runInEventStateScope, runInPollModeScope, } from "./cli/event-state-scope.mjs";
|
|
22
|
+
import { handleApplyMerge } from "./cli/apply-merge-handler.mjs";
|
|
23
|
+
import { getLocalHeadSha } from "./commands/suggestion-patch-git.mjs";
|
|
24
|
+
import { extractTransportArgs } from "./cli/transport-args.mjs";
|
|
25
|
+
import { parseGithubTransport } from "./github/transport-mode.mjs";
|
|
26
|
+
import { runWithGithubTransport } from "./github/transport.mjs";
|
|
19
27
|
// ---------------------------------------------------------------------------
|
|
20
28
|
// Entry
|
|
21
29
|
// ---------------------------------------------------------------------------
|
|
22
30
|
export async function main(argv) {
|
|
31
|
+
const extracted = extractTransportArgs(argv.slice(2));
|
|
32
|
+
const dispatched = [...argv.slice(0, 2), ...extracted.args];
|
|
33
|
+
// Help and version retain their I/O-free short circuit, even with invalid flags.
|
|
34
|
+
if (extracted.args.some((arg) => arg === "--help" || arg === "-h") ||
|
|
35
|
+
extracted.args[0] === "--version" ||
|
|
36
|
+
extracted.args[0] === "-v" ||
|
|
37
|
+
extracted.args[0] === "log-file" ||
|
|
38
|
+
extracted.args[0] === "playbook" ||
|
|
39
|
+
(extracted.args[0] === "admin" && extracted.args[1] === "log-file") ||
|
|
40
|
+
(extracted.args[0] === "journal" && extracted.args[1] === "extract")) {
|
|
41
|
+
return dispatch(dispatched);
|
|
42
|
+
}
|
|
43
|
+
let mode;
|
|
44
|
+
try {
|
|
45
|
+
mode =
|
|
46
|
+
extracted.transport === undefined ? undefined : parseGithubTransport(extracted.transport);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
process.stderr.write(`pr-shepherd: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
50
|
+
process.exitCode = EXIT.USAGE;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
return runWithGithubTransport(mode, () => dispatch(dispatched), {
|
|
54
|
+
verbose: extracted.args.includes("--verbose"),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function dispatch(argv) {
|
|
23
58
|
const args = argv.slice(2); // strip node + script path
|
|
24
59
|
const subcommand = args[0];
|
|
25
60
|
if (subcommand === "--version" || subcommand === "-v") {
|
|
@@ -69,6 +104,17 @@ export async function main(argv) {
|
|
|
69
104
|
if (subcommand !== undefined && legacyReplacement[subcommand] !== undefined) {
|
|
70
105
|
warnLegacyAlias(subcommand, legacyReplacement[subcommand]);
|
|
71
106
|
}
|
|
107
|
+
// A local read of shipped files: no GitHub, git, or log I/O.
|
|
108
|
+
if (subcommand === "playbook") {
|
|
109
|
+
handlePlaybook(args.slice(1));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Event mode enters the durable state scope before the log resolves its path.
|
|
113
|
+
return runInEventStateScope(args, () => dispatchLogged(argv));
|
|
114
|
+
}
|
|
115
|
+
async function dispatchLogged(argv) {
|
|
116
|
+
const args = argv.slice(2);
|
|
117
|
+
const subcommand = args[0];
|
|
72
118
|
// Initialize the per-worktree log and install a stdout tee.
|
|
73
119
|
await setupLog(argv);
|
|
74
120
|
if (isDefaultPollInvocation(subcommand)) {
|
|
@@ -124,6 +170,9 @@ function warnLegacyAlias(alias, replacement) {
|
|
|
124
170
|
async function handleApply(args) {
|
|
125
171
|
const action = args[0];
|
|
126
172
|
switch (action) {
|
|
173
|
+
case "merge":
|
|
174
|
+
await handleApplyMerge(args.slice(1));
|
|
175
|
+
return;
|
|
127
176
|
case "review":
|
|
128
177
|
await handleResolve(args.slice(1));
|
|
129
178
|
return;
|
|
@@ -167,13 +216,16 @@ function readVersion() {
|
|
|
167
216
|
async function handleLogFile(args, usageKey = "log-file") {
|
|
168
217
|
if (maybePrintHelp(args, usageKey))
|
|
169
218
|
return;
|
|
219
|
+
if (rejectInvalidPollModeFlag(args, usageKey))
|
|
220
|
+
return;
|
|
170
221
|
const jsonOut = args.some((a) => a === "--format=json") ||
|
|
171
222
|
(() => {
|
|
172
223
|
const idx = args.indexOf("--format");
|
|
173
224
|
return idx !== -1 && args[idx + 1] === "json";
|
|
174
225
|
})();
|
|
175
226
|
try {
|
|
176
|
-
|
|
227
|
+
// Report the log the matching tick writes: event mode keeps it in durable state.
|
|
228
|
+
const result = await runInPollModeScope(args, () => runLogFile());
|
|
177
229
|
process.stdout.write(jsonOut ? `${JSON.stringify(result, null, 2)}\n` : `${result.path}\n`);
|
|
178
230
|
}
|
|
179
231
|
catch (e) {
|
|
@@ -189,6 +241,7 @@ async function handleResolve(args, command = "apply review") {
|
|
|
189
241
|
const dismissReviewIds = parseList(getFlag(extra, "--dismiss-review-ids"));
|
|
190
242
|
const dismissMessage = getFlag(extra, "--message") ?? undefined;
|
|
191
243
|
const requireSha = getFlag(extra, "--require-sha") ?? undefined;
|
|
244
|
+
const adoptExistingReplies = hasFlag(extra, "--adopt-existing-replies");
|
|
192
245
|
warnPrrcThreadIds(resolveThreadIds);
|
|
193
246
|
if (!validateRequireSha(requireSha))
|
|
194
247
|
return;
|
|
@@ -216,12 +269,14 @@ async function handleResolve(args, command = "apply review") {
|
|
|
216
269
|
minimizeCommentIds,
|
|
217
270
|
dismissReviewIds,
|
|
218
271
|
dismissMessage,
|
|
219
|
-
|
|
272
|
+
// `HEAD` reads this checkout's commit (read-only `git rev-parse HEAD`), as the shell would.
|
|
273
|
+
requireSha: requireSha === "HEAD" ? await getLocalHeadSha() : requireSha,
|
|
274
|
+
...(adoptExistingReplies && { adoptExistingReplies }),
|
|
220
275
|
});
|
|
221
276
|
process.stdout.write(globalOpts.format === "json"
|
|
222
277
|
? `${JSON.stringify(result, null, 2)}\n`
|
|
223
278
|
: `${formatMutateResult(result)}\n`);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
279
|
+
const exitCode = applyReviewResultToExitCode(result);
|
|
280
|
+
if (exitCode !== EXIT.OK)
|
|
281
|
+
process.exitCode = exitCode;
|
|
227
282
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RestMergeOptions } from "../github/rest-merge.mts";
|
|
2
|
+
export declare function validateApplyMergeOptions(input: RestMergeOptions): void;
|
|
3
|
+
export declare function sameMergeOptions(left: RestMergeOptions, right: RestMergeOptions): boolean;
|
|
4
|
+
export declare function validateApplyMergeTarget(input: RestMergeOptions, pr: number | undefined): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
2
|
+
import { restMergeStackGuardKey, validateRestMergeStackGuard, } from "../github/rest-merge-stack-guard.mjs";
|
|
3
|
+
export function validateApplyMergeOptions(input) {
|
|
4
|
+
if (input.expectedStack !== undefined) {
|
|
5
|
+
try {
|
|
6
|
+
validateRestMergeStackGuard(input.expectedStack);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
throw new ShepherdError(error instanceof Error ? error.message : String(error), EXIT.DATAERR);
|
|
10
|
+
}
|
|
11
|
+
if (input.expectedStack.prefix.at(-1)?.headRefOid !== input.requireSha)
|
|
12
|
+
throw new ShepherdError("expectedStack prefix head must match --require-sha", EXIT.DATAERR);
|
|
13
|
+
}
|
|
14
|
+
if (typeof input.requireSha !== "string" || !/^[0-9a-f]{40}$/.test(input.requireSha))
|
|
15
|
+
throw new ShepherdError("--require-sha must be a full 40-character lowercase hex SHA", EXIT.DATAERR);
|
|
16
|
+
if (!["direct_merge", "merge_queue", "default"].includes(input.mergeAction))
|
|
17
|
+
throw new ShepherdError("--merge-action must be direct_merge, merge_queue, or default", EXIT.USAGE);
|
|
18
|
+
if (input.mergeMethod !== undefined && !["merge", "squash", "rebase"].includes(input.mergeMethod))
|
|
19
|
+
throw new ShepherdError("--method must be merge, squash, or rebase", EXIT.USAGE);
|
|
20
|
+
if (input.mergeMethod !== undefined && input.mergeAction !== "direct_merge")
|
|
21
|
+
throw new ShepherdError("--method is supported only with --merge-action direct_merge", EXIT.USAGE);
|
|
22
|
+
}
|
|
23
|
+
export function sameMergeOptions(left, right) {
|
|
24
|
+
return (left.requireSha === right.requireSha &&
|
|
25
|
+
left.mergeAction === right.mergeAction &&
|
|
26
|
+
left.mergeMethod === right.mergeMethod &&
|
|
27
|
+
restMergeStackGuardKey(left.expectedStack) === restMergeStackGuardKey(right.expectedStack));
|
|
28
|
+
}
|
|
29
|
+
export function validateApplyMergeTarget(input, pr) {
|
|
30
|
+
if (pr !== undefined &&
|
|
31
|
+
input.expectedStack !== undefined &&
|
|
32
|
+
input.expectedStack.prefix.at(-1)?.pr !== pr)
|
|
33
|
+
throw new ShepherdError("expectedStack prefix must end at the requested PR", EXIT.DATAERR);
|
|
34
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RepoInfo } from "../github/client.mts";
|
|
2
|
+
import type { RestMergeOptions, RestMergeResponse } from "../github/rest-merge.mts";
|
|
3
|
+
import type { ApplyMergeResult } from "./apply-merge.mts";
|
|
4
|
+
export declare function mergeResultFactory(pr: number, repo: RepoInfo, options: RestMergeOptions): {
|
|
5
|
+
failed: (message: string, uncertain?: true) => ApplyMergeResult;
|
|
6
|
+
project: (response: RestMergeResponse) => ApplyMergeResult;
|
|
7
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function mergeResultFactory(pr, repo, options) {
|
|
2
|
+
const failed = (message, uncertain) => ({
|
|
3
|
+
pr,
|
|
4
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
5
|
+
status: "failed",
|
|
6
|
+
details: {
|
|
7
|
+
message,
|
|
8
|
+
expected_head_sha: options.requireSha,
|
|
9
|
+
merge_action: options.mergeAction,
|
|
10
|
+
...(options.mergeMethod && { merge_method: options.mergeMethod }),
|
|
11
|
+
},
|
|
12
|
+
...(uncertain && { uncertain }),
|
|
13
|
+
});
|
|
14
|
+
const project = (response) => ({
|
|
15
|
+
pr,
|
|
16
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
17
|
+
...response,
|
|
18
|
+
});
|
|
19
|
+
return { failed, project };
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RestMergeResponse } from "../github/rest-merge.mts";
|
|
2
|
+
export interface PullState {
|
|
3
|
+
state: string;
|
|
4
|
+
merged: boolean;
|
|
5
|
+
draft: boolean;
|
|
6
|
+
head: {
|
|
7
|
+
sha: string;
|
|
8
|
+
};
|
|
9
|
+
merge_commit_sha?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Read the pull conditionally: inside the PR's conditional scope an unchanged pull answers a free
|
|
13
|
+
* 304 whose stored body equals GitHub's current one.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readPull(repo: {
|
|
16
|
+
owner: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}, pr: number): Promise<PullState>;
|
|
19
|
+
/** The merged outcome of a pull already merged, carrying its merge commit when GitHub reports one. */
|
|
20
|
+
export declare function mergedResponse(pull: PullState, message: string): RestMergeResponse;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readRest, restRepoPath } from "../github/rest-reader-core.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Read the pull conditionally: inside the PR's conditional scope an unchanged pull answers a free
|
|
4
|
+
* 304 whose stored body equals GitHub's current one.
|
|
5
|
+
*/
|
|
6
|
+
export function readPull(repo, pr) {
|
|
7
|
+
return readRest("GET", `${restRepoPath(repo)}/pulls/${pr}`);
|
|
8
|
+
}
|
|
9
|
+
/** The merged outcome of a pull already merged, carrying its merge commit when GitHub reports one. */
|
|
10
|
+
export function mergedResponse(pull, message) {
|
|
11
|
+
return {
|
|
12
|
+
status: "merged",
|
|
13
|
+
details: { message, ...(pull.merge_commit_sha && { sha: pull.merge_commit_sha }) },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { validateApplyMergeOptions } from "./apply-merge-options.mts";
|
|
2
|
+
import { type RestMergeOptions, type RestMergeResponse } from "../github/rest-merge.mts";
|
|
3
|
+
export interface ApplyMergeInput extends RestMergeOptions {
|
|
4
|
+
prNumber?: number;
|
|
5
|
+
targetRepository?: {
|
|
6
|
+
owner: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface ApplyMergeResult extends RestMergeResponse {
|
|
11
|
+
pr: number;
|
|
12
|
+
repo: string;
|
|
13
|
+
/** A mutation may already have reached GitHub. No new request will be issued. */
|
|
14
|
+
uncertain?: true;
|
|
15
|
+
note?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Submit one guarded asynchronous request, or resume its existing UUID without resubmitting. */
|
|
18
|
+
export declare function runApplyMerge(input: ApplyMergeInput): Promise<ApplyMergeResult>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { mergeResultFactory } from "./apply-merge-output.mjs";
|
|
2
|
+
import { validateApplyMergeOptions, validateApplyMergeTarget, sameMergeOptions, } from "./apply-merge-options.mjs";
|
|
3
|
+
import { validateRestStackMergeReadiness } from "./rest-stack-merge-readiness.mjs";
|
|
4
|
+
export { validateApplyMergeOptions } from "./apply-merge-options.mjs";
|
|
5
|
+
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
6
|
+
import { getCurrentPrNumber, getRepoInfo } from "../github/client.mjs";
|
|
7
|
+
import { GitHubRequestError } from "../github/errors.mjs";
|
|
8
|
+
import { mergedResponse, readPull } from "./apply-merge-pull.mjs";
|
|
9
|
+
import { withRestConditionalScope } from "../github/rest-conditional-scope.mjs";
|
|
10
|
+
import { getGithubTransport } from "../github/transport.mjs";
|
|
11
|
+
import { getRestMerge, matchesRestMergeRequest, requestRestMerge, } from "../github/rest-merge.mjs";
|
|
12
|
+
import { claimMergeRequest, replaceFailedMergeRequest, readMergeRequest, writeMergeRequest, writeMergeRequestStatus, } from "../state/merge-request.mjs";
|
|
13
|
+
/** Submit one guarded asynchronous request, or resume its existing UUID without resubmitting. */
|
|
14
|
+
export async function runApplyMerge(input) {
|
|
15
|
+
validateApplyMergeOptions(input);
|
|
16
|
+
validateApplyMergeTarget(input, input.prNumber);
|
|
17
|
+
if (getGithubTransport() !== "rest")
|
|
18
|
+
throw new ShepherdError("apply merge requires REST transport; pass --transport rest", EXIT.USAGE);
|
|
19
|
+
const repo = input.targetRepository ?? (await getRepoInfo());
|
|
20
|
+
const pr = input.prNumber ?? (await getCurrentPrNumber());
|
|
21
|
+
if (!pr)
|
|
22
|
+
throw new ShepherdError("No open PR found for current branch. Pass a PR explicitly.", EXIT.UNAVAILABLE);
|
|
23
|
+
validateApplyMergeTarget(input, pr);
|
|
24
|
+
const key = { owner: repo.owner, repo: repo.name, pr };
|
|
25
|
+
// The PR's own conditional scope: the pull and stack reads share the ETag cache the iterate tick
|
|
26
|
+
// that printed this command just filled, so an unchanged resource answers a free 304 whose
|
|
27
|
+
// stored body equals GitHub's current one. The merge-async GET and PUT stay unconditional.
|
|
28
|
+
return withRestConditionalScope(key, () => submitApplyMerge(input, repo, pr, key));
|
|
29
|
+
}
|
|
30
|
+
async function submitApplyMerge(input, repo, pr, key) {
|
|
31
|
+
const options = {
|
|
32
|
+
requireSha: input.requireSha,
|
|
33
|
+
mergeAction: input.mergeAction,
|
|
34
|
+
...(input.mergeMethod && { mergeMethod: input.mergeMethod }),
|
|
35
|
+
...(input.expectedStack && { expectedStack: input.expectedStack }),
|
|
36
|
+
};
|
|
37
|
+
const { failed, project } = mergeResultFactory(pr, repo, options);
|
|
38
|
+
const existing = await readMergeRequest(key);
|
|
39
|
+
const pull = await readPull(repo, pr);
|
|
40
|
+
if (pull.merged === true)
|
|
41
|
+
return project(mergedResponse(pull, "Pull request is merged."));
|
|
42
|
+
if (pull.head?.sha !== input.requireSha)
|
|
43
|
+
return failed("Current PR head does not match --require-sha; no merge request was submitted.");
|
|
44
|
+
if (pull.state !== "open" || pull.draft !== false)
|
|
45
|
+
return failed("Pull request must be open and ready for review.");
|
|
46
|
+
const replaceFailed = existing &&
|
|
47
|
+
!existing.uncertain &&
|
|
48
|
+
((existing.response?.status === "failed" && !sameMergeOptions(existing.options, options)) ||
|
|
49
|
+
(existing.response?.status === "enqueued" &&
|
|
50
|
+
existing.options.requireSha !== options.requireSha));
|
|
51
|
+
if (existing && !replaceFailed) {
|
|
52
|
+
if (!sameMergeOptions(existing.options, options))
|
|
53
|
+
return failed("A persisted asynchronous merge request has different options; reconcile its outcome before requesting another merge.", true);
|
|
54
|
+
if (existing.response?.status !== undefined && existing.response.status !== "pending")
|
|
55
|
+
return {
|
|
56
|
+
...project(existing.response),
|
|
57
|
+
...(existing.uncertain && { uncertain: true }),
|
|
58
|
+
...(existing.response.status === "enqueued" && {
|
|
59
|
+
note: "This is the recorded request outcome; current merge-queue membership is not confirmed.",
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
if (!existing.uuid)
|
|
63
|
+
return failed("The previous merge submission has an unknown outcome and no UUID. The PR is not merged; no duplicate request was submitted.", true);
|
|
64
|
+
try {
|
|
65
|
+
const response = await getRestMerge(repo, pr, existing.uuid);
|
|
66
|
+
if (response.status === "pending" &&
|
|
67
|
+
(!matchesRestMergeRequest(response, options) || response.details.uuid !== existing.uuid))
|
|
68
|
+
return failed("Asynchronous merge status does not match the persisted guarded request.", true);
|
|
69
|
+
await writeMergeRequestStatus(key, { ...existing, response });
|
|
70
|
+
return project(response);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof GitHubRequestError && error.status === 404) {
|
|
74
|
+
const latest = await readPull(repo, pr);
|
|
75
|
+
if (latest.merged === true)
|
|
76
|
+
return project(mergedResponse(latest, "Pull request is merged; its asynchronous request has expired."));
|
|
77
|
+
return failed("The asynchronous merge UUID expired or is unavailable. The PR is not merged; its request outcome is unknown and no duplicate was submitted.", true);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
let record = {
|
|
83
|
+
version: 1,
|
|
84
|
+
options,
|
|
85
|
+
startedAtUnix: Math.floor(Date.now() / 1000),
|
|
86
|
+
};
|
|
87
|
+
await validateRestStackMergeReadiness(pr, repo, input.requireSha, input.expectedStack);
|
|
88
|
+
if (!(existing
|
|
89
|
+
? await replaceFailedMergeRequest(key, existing, record)
|
|
90
|
+
: await claimMergeRequest(key, record)))
|
|
91
|
+
return failed("Another merge request is already being submitted; resume this command after its outcome is recorded.", true);
|
|
92
|
+
// Recovery may have selected an older durable intent; retain its unique generation token.
|
|
93
|
+
record = (await readMergeRequest(key));
|
|
94
|
+
let response;
|
|
95
|
+
try {
|
|
96
|
+
response = await requestRestMerge(repo, pr, options);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof GitHubRequestError && error.status >= 400 && error.status < 500) {
|
|
100
|
+
await writeMergeRequest(key, {
|
|
101
|
+
...record,
|
|
102
|
+
response: {
|
|
103
|
+
status: "failed",
|
|
104
|
+
details: { message: error.responseMessage ?? error.message },
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
return failed(`Merge submission outcome is unknown: ${error instanceof Error ? error.message : String(error)}. No duplicate request will be submitted.`, true);
|
|
110
|
+
}
|
|
111
|
+
if (response.status === "pending" && !matchesRestMergeRequest(response, options)) {
|
|
112
|
+
const mismatch = failed("GitHub returned a pending merge with different or unverifiable options; its UUID was not adopted.", true);
|
|
113
|
+
await writeMergeRequest(key, {
|
|
114
|
+
...record,
|
|
115
|
+
response: { status: mismatch.status, details: mismatch.details },
|
|
116
|
+
uncertain: true,
|
|
117
|
+
});
|
|
118
|
+
return mismatch;
|
|
119
|
+
}
|
|
120
|
+
await writeMergeRequest(key, {
|
|
121
|
+
...record,
|
|
122
|
+
...(response.details.uuid && response.status === "pending" && { uuid: response.details.uuid }),
|
|
123
|
+
response,
|
|
124
|
+
});
|
|
125
|
+
return project(response);
|
|
126
|
+
}
|
|
@@ -2,15 +2,18 @@ import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
|
2
2
|
import { fetchPrBatch } from "../github/batch.mjs";
|
|
3
3
|
import { queueRemovalAppliesToHead } from "../github/queue-removal-freshness.mjs";
|
|
4
4
|
import { getCurrentPrNumber, getRepoInfo } from "../github/client.mjs";
|
|
5
|
+
import { getGithubTransport } from "../github/transport.mjs";
|
|
5
6
|
import { isCiQueueRemovalReason, writeQueueRemovalAcknowledgment, } from "../state/queue-removal-ack.mjs";
|
|
6
7
|
/** Validate an observed native-stack CI queue removal before recording the caller's acknowledgment. */
|
|
7
8
|
export async function applyQueueRemovalAck(input) {
|
|
9
|
+
requireQueueRemovalTransport();
|
|
8
10
|
const repo = input.targetRepository ?? (await getRepoInfo());
|
|
9
11
|
const pr = input.prNumber ?? (await getCurrentPrNumber());
|
|
10
12
|
if (pr === null) {
|
|
11
13
|
throw new ShepherdError("No open PR found for current branch. Pass a PR number explicitly.", EXIT.UNAVAILABLE);
|
|
12
14
|
}
|
|
13
15
|
const { data } = await fetchPrBatch(pr, repo);
|
|
16
|
+
requireQueueRemovalTransport(data.transport);
|
|
14
17
|
const removal = data.latestMergeQueueRemoval;
|
|
15
18
|
if (data.state !== "OPEN" ||
|
|
16
19
|
!data.stack ||
|
|
@@ -41,3 +44,7 @@ export async function applyQueueRemovalAck(input) {
|
|
|
41
44
|
}
|
|
42
45
|
return { pr, repo: `${repo.owner}/${repo.name}`, acknowledgment };
|
|
43
46
|
}
|
|
47
|
+
function requireQueueRemovalTransport(snapshotTransport) {
|
|
48
|
+
if (snapshotTransport === "rest" || getGithubTransport() === "rest")
|
|
49
|
+
throw new ShepherdError("Queue-removal acknowledgment is transport-unsupported: REST cannot verify current merge-queue removal evidence.", EXIT.UNAVAILABLE);
|
|
50
|
+
}
|