@joshuafolkken/kit 1.1166.0 → 1.1229.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +9 -5
- package/.claude/skills/workflow-commands/backlogrun-park.md +29 -9
- package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -2
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +24 -15
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +5 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +10 -5
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +47 -33
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +48 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +26 -19
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-DYAVAM6S.js → chunk-55HHG5PX.js} +1405 -631
- package/dist/hooks/{chunk-A6P7PN7P.js → chunk-5VMKCCC4.js} +25 -10
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/{chunk-5KTCMCOT.js → chunk-6HJJXRBX.js} +4 -2
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-ENA7P5FB.js → chunk-F56MFS4B.js} +3 -3
- package/dist/hooks/{chunk-ANJ2WGFD.js → chunk-FIVDHQGT.js} +7 -6
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-GU5MATVE.js → chunk-JUDKDCDP.js} +499 -389
- package/dist/hooks/{chunk-ZDZ555YH.js → chunk-NV2JRZZO.js} +743 -909
- package/dist/hooks/{chunk-V2BTOZHT.js → chunk-RNWHN5BP.js} +3 -3
- package/dist/hooks/{chunk-URJA6XY4.js → chunk-TQNPDZNH.js} +4 -4
- package/dist/hooks/{chunk-GYNGP7HI.js → chunk-VC3PZPZO.js} +1 -1
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +232 -210
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +81 -59
- package/dist/josh.js +872 -75
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +31 -9
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +7 -6
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-rank.ts +18 -9
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-restructure.ts +152 -0
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/backlog/backlog-waves.ts +15 -17
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/cost-runtime/context-cut-threshold.ts +1 -9
- package/scripts/cost-runtime/cost-verdict.ts +15 -28
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-followup-stages.ts +9 -0
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +5 -130
- package/scripts/gate/core-budget.ts +69 -1
- package/scripts/gate/gate-plan.ts +47 -7
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +27 -8
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-issue.ts +5 -3
- package/scripts/gh/git-gh-pr-read.ts +68 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +9 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +18 -6
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +12 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/{schemas.ts → git-schemas.ts} +2 -0
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/git/main-merge.ts +57 -21
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +5 -15
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +1 -1
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +45 -18
- package/scripts/issue/issue-file.ts +24 -1
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +11 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +11 -1
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-lane.ts +22 -0
- package/scripts/josh/josh-commands-maintenance.ts +4 -4
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +33 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +35 -3
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +1 -1
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-notify.ts +26 -1
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +12 -6
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +3 -3
- package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +2 -2
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +2 -2
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +29 -8
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +13 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +66 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +10 -6
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +13 -0
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +15 -8
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/ship/run-ship-sync.ts +128 -0
- package/scripts/run/{run-ship.ts → ship/run-ship.ts} +6 -0
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +31 -9
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +99 -17
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +8 -2
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/test-red-logic.ts +17 -2
- package/scripts/test/test-red.ts +8 -7
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/time-runtime/time-transcript-line.ts +27 -1
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +17 -17
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +2 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -2,6 +2,7 @@ import { repo_map_logic } from '#scripts/discovery/repo-map-logic'
|
|
|
2
2
|
import { repo_origin } from '#scripts/discovery/repo-origin'
|
|
3
3
|
import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
|
|
4
4
|
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
5
|
+
import { GH_API_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
5
6
|
import { propagate_publish } from '#scripts/propagate/propagate-publish'
|
|
6
7
|
import { derive_versions_endpoint } from '#scripts/version/version-command-config'
|
|
7
8
|
import { load } from 'js-yaml'
|
|
@@ -69,7 +70,6 @@ function is_published(repo: string, version: string): boolean {
|
|
|
69
70
|
return propagate_publish.is_version_published(versions, version)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
const GH_TIMEOUT_MS = 20_000
|
|
73
73
|
// The manifest arrives base64-encoded in the API's `content` field; `jq` decodes and reads it.
|
|
74
74
|
const VERSION_JQ = '.content | @base64d | fromjson | {version, private, workspaces} | tojson'
|
|
75
75
|
const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'
|
|
@@ -145,7 +145,7 @@ function pnpm_workspace_path(repo: string): string {
|
|
|
145
145
|
|
|
146
146
|
// A read that failed for any reason other than a missing file leaves the layout unknown.
|
|
147
147
|
function classify_workspace_failure(repo: string): WorkspaceCheck {
|
|
148
|
-
const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo),
|
|
148
|
+
const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo), GH_API_TIMEOUT_MS)
|
|
149
149
|
|
|
150
150
|
return status === NOT_FOUND_STATUS ? 'single' : 'unreadable'
|
|
151
151
|
}
|
|
@@ -154,7 +154,7 @@ function read_pnpm_workspace(repo: string): WorkspaceCheck {
|
|
|
154
154
|
const stdout = git_gh_exec.read_gh_api_sync({
|
|
155
155
|
path: pnpm_workspace_path(repo),
|
|
156
156
|
jq_filter: WORKSPACE_JQ,
|
|
157
|
-
timeout_ms:
|
|
157
|
+
timeout_ms: GH_API_TIMEOUT_MS,
|
|
158
158
|
})
|
|
159
159
|
if (stdout === undefined) return classify_workspace_failure(repo)
|
|
160
160
|
|
|
@@ -229,7 +229,7 @@ function to_manifest(repo: string, stdout: string): ManifestAnswer {
|
|
|
229
229
|
// here is trustworthy in a way a registry 404 is not: the repository's issues are already being read,
|
|
230
230
|
// so access is established and what is missing is the file.
|
|
231
231
|
function classify_manifest_failure(repo: string): ManifestAnswer {
|
|
232
|
-
const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo),
|
|
232
|
+
const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo), GH_API_TIMEOUT_MS)
|
|
233
233
|
|
|
234
234
|
return { kind: status === NOT_FOUND_STATUS ? 'absent' : 'unreadable' }
|
|
235
235
|
}
|
|
@@ -238,7 +238,7 @@ function fetch_manifest(repo: string): ManifestAnswer {
|
|
|
238
238
|
const stdout = git_gh_exec.read_gh_api_sync({
|
|
239
239
|
path: manifest_path(repo),
|
|
240
240
|
jq_filter: VERSION_JQ,
|
|
241
|
-
timeout_ms:
|
|
241
|
+
timeout_ms: GH_API_TIMEOUT_MS,
|
|
242
242
|
})
|
|
243
243
|
|
|
244
244
|
return stdout === undefined ? classify_manifest_failure(repo) : to_manifest(repo, stdout)
|
|
@@ -121,7 +121,7 @@ function find_stuck_children(children: ReadonlyArray<EpicChild>): Array<string>
|
|
|
121
121
|
const index = index_children(children)
|
|
122
122
|
const remaining = new Set(children.map((child) => key_of(child)))
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
function peel(): number {
|
|
125
125
|
const ready = [...remaining].filter((node) => !has_remaining_blocker(index, node, remaining))
|
|
126
126
|
|
|
127
127
|
for (const node of ready) remaining.delete(node)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
2
3
|
import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
3
4
|
import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
|
|
4
|
-
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
5
5
|
import { EPIC_LABEL, has_any_label } from '#scripts/issue/issue-labels'
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
import { epic_parse } from './epic-parse'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_issue_rest } from '#scripts/gh/git-gh-issue-rest'
|
|
2
|
+
import { blocked_by_schema } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { blocked_by_schema } from '#scripts/git/schemas'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import type { IssueReference } from './epic-reference'
|
|
6
6
|
|
|
@@ -29,11 +29,7 @@ const PULL_REQUEST_SEGMENT = '/pull/'
|
|
|
29
29
|
const epic_issue_schema = z.object({
|
|
30
30
|
number: z.number(),
|
|
31
31
|
title: z.string().default(''),
|
|
32
|
-
body: z
|
|
33
|
-
.string()
|
|
34
|
-
.nullable()
|
|
35
|
-
.default('')
|
|
36
|
-
.transform((value) => value ?? ''),
|
|
32
|
+
body: z.union([z.string(), z.null().transform(() => '')]).default(''),
|
|
37
33
|
state: z.string().default(UNKNOWN_STATE),
|
|
38
34
|
// The issue endpoint answers for a pull request as readily as for an issue, and nothing in the
|
|
39
35
|
// other fields separates them — an open PR reports `state: OPEN`, a merged one `MERGED`. The URL
|
|
@@ -187,10 +187,10 @@ function solo_offer(
|
|
|
187
187
|
read: BusyRead,
|
|
188
188
|
request: LaneRequest,
|
|
189
189
|
): LaneOffer {
|
|
190
|
-
const { offered, notice } = epic_solo.select(answer.children, read, request.repo)
|
|
190
|
+
const { offered, notice = '' } = epic_solo.select(answer.children, read, request.repo)
|
|
191
191
|
|
|
192
192
|
if (offered.length === NO_LANES && answer.children.length > NO_LANES) {
|
|
193
|
-
return { children: [], verdict: WAIT_VERDICT, notice
|
|
193
|
+
return { children: [], verdict: WAIT_VERDICT, notice }
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
return { ...answer, children: offered, notice: offered_notice(offered, read, request) }
|
|
@@ -5,6 +5,7 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
|
5
5
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
6
6
|
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
7
7
|
import { lane_capacity } from '#scripts/lane/lane-capacity'
|
|
8
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
8
9
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
9
10
|
import { epic_classify } from './epic-classify'
|
|
10
11
|
import { epic_cross_repo } from './epic-cross-repo'
|
|
@@ -31,12 +32,10 @@ import { epic_report, type EpicNextResult } from './epic-report'
|
|
|
31
32
|
const SUCCESS_EXIT_CODE = 0
|
|
32
33
|
const FAILURE_EXIT_CODE = 1
|
|
33
34
|
const ARGV_OFFSET = 2
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
// changed
|
|
37
|
-
|
|
38
|
-
const LANES_FLAG = '--lanes'
|
|
39
|
-
const FLAG_PREFIX = '--'
|
|
35
|
+
// Asking for every free lane (`--lanes`) is opt-in, so the answer stays one token for a caller that
|
|
36
|
+
// has not been changed. `--repo` alone still prints exactly one number — what joshuafolkken/kit#1491
|
|
37
|
+
// changed for it is *when* that number appears, not how many arrive (joshuafolkken/kit#1491).
|
|
38
|
+
const OPTIONS = { repo: { type: 'string' }, lanes: { type: 'boolean' } } as const
|
|
40
39
|
const USAGE =
|
|
41
40
|
'Usage: josh epic:next <epic-number|owner/repo#number>... [--repo <owner/repo>] [--lanes]'
|
|
42
41
|
// A repository that could not be read is refused rather than stood in for. Since
|
|
@@ -66,30 +65,6 @@ interface NextOptions {
|
|
|
66
65
|
usage?: string
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
// A value that is itself a flag is no value at all. `--repo --lanes` used to narrow to a repository
|
|
70
|
-
// literally named `--lanes` and report `No runnable child in --lanes` with exit 0; refused here, it
|
|
71
|
-
// reaches the usage line instead — the caller meant a repository and named none.
|
|
72
|
-
function parse_repo(rest: ReadonlyArray<string>): string | undefined {
|
|
73
|
-
const flag_index = rest.indexOf(REPO_FLAG)
|
|
74
|
-
const value = flag_index === -1 ? undefined : rest[flag_index + 1]
|
|
75
|
-
|
|
76
|
-
return value?.startsWith(FLAG_PREFIX) === true ? undefined : value
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// The leading arguments are epic references and everything from the first flag on is options. Split
|
|
80
|
-
// on the flag rather than taking a fixed count, so `epic:next 858 909 --repo X` reads two epics and
|
|
81
|
-
// `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493).
|
|
82
|
-
function split_at_flag(argv: ReadonlyArray<string>): {
|
|
83
|
-
head: ReadonlyArray<string>
|
|
84
|
-
rest: ReadonlyArray<string>
|
|
85
|
-
} {
|
|
86
|
-
const flag_at = argv.findIndex((entry) => entry.startsWith(FLAG_PREFIX))
|
|
87
|
-
|
|
88
|
-
return flag_at === -1
|
|
89
|
-
? { head: argv, rest: [] }
|
|
90
|
-
: { head: argv.slice(0, flag_at), rest: argv.slice(flag_at) }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
68
|
// Every leading entry, or nothing. One entry that does not parse fails the whole read rather than
|
|
94
69
|
// being dropped: a mistyped second epic would otherwise run the first one alone and report nothing
|
|
95
70
|
// at all about the one that was missed — an unattended run silently doing half of what was asked.
|
|
@@ -106,21 +81,34 @@ function parse_references(head: ReadonlyArray<string>): ReadonlyArray<EpicRefere
|
|
|
106
81
|
return references.length === 0 ? undefined : references
|
|
107
82
|
}
|
|
108
83
|
|
|
84
|
+
function with_repo(
|
|
85
|
+
references: ReadonlyArray<EpicReference>,
|
|
86
|
+
values: { repo?: string; lanes?: boolean },
|
|
87
|
+
): NextOptions {
|
|
88
|
+
const { repo, lanes: is_all_lanes = false } = values
|
|
89
|
+
|
|
90
|
+
if (repo === undefined) return is_all_lanes ? { usage: USAGE } : { references }
|
|
91
|
+
|
|
92
|
+
return { references, repo, is_all_lanes }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The positional arguments are epic references and the flags are options, so `epic:next 858 909 --repo
|
|
96
|
+
// X` reads two epics and `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493). The read is
|
|
97
|
+
// strict: an unknown flag reaches the usage line rather than being ignored, and a value that is itself
|
|
98
|
+
// a flag is no value at all — `--repo --lanes` used to narrow to a repository literally named
|
|
99
|
+
// `--lanes` and report `No runnable child in --lanes` with exit 0.
|
|
100
|
+
//
|
|
109
101
|
// `--lanes` without `--repo` is refused rather than ignored: the lane count is a property of one
|
|
110
102
|
// repository, so the aggregate listing has nothing to apply it to, and a flag that silently does
|
|
111
|
-
// nothing is a caller believing it asked for something.
|
|
103
|
+
// nothing is a caller believing it asked for something. An empty name (`--repo=` from an unset
|
|
104
|
+
// variable) is no repository either: narrowed to it, the answer is `No runnable child in .` and exit 0.
|
|
112
105
|
function parse_options(argv: ReadonlyArray<string>): NextOptions {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const is_all_lanes = rest.includes(LANES_FLAG)
|
|
118
|
-
|
|
119
|
-
if (repo === undefined) {
|
|
120
|
-
return is_all_lanes || rest.includes(REPO_FLAG) ? { usage: USAGE } : { references }
|
|
121
|
-
}
|
|
106
|
+
const parsed = cli_flags.arguments_of(argv, OPTIONS)
|
|
107
|
+
if (parsed === undefined) return { usage: USAGE }
|
|
108
|
+
const references = parse_references(parsed.positionals)
|
|
109
|
+
if (references === undefined || parsed.values.repo === '') return { usage: USAGE }
|
|
122
110
|
|
|
123
|
-
return
|
|
111
|
+
return with_repo(references, parsed.values)
|
|
124
112
|
}
|
|
125
113
|
|
|
126
114
|
// The answer for one epic, from an already-fetched snapshot. Split from the fetch so the whole
|
|
@@ -446,7 +434,6 @@ const epic_next = {
|
|
|
446
434
|
is_order_declared,
|
|
447
435
|
repo_verdict,
|
|
448
436
|
UNCHECKED_EXCLUSION,
|
|
449
|
-
split_at_flag,
|
|
450
437
|
parse_options,
|
|
451
438
|
refuse_reads,
|
|
452
439
|
views_of,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
2
|
-
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
2
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
3
3
|
import { has_any_label, IN_PROGRESS_LABEL, RUN_SOLO_LABEL } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { lane_await } from '#scripts/lane/lane-await'
|
|
5
5
|
import { lane_registry } from '#scripts/lane/lane-registry'
|
|
@@ -20,12 +20,16 @@ type TriageVerdict = 'triage'
|
|
|
20
20
|
|
|
21
21
|
const TRIAGE_VERDICT: TriageVerdict = 'triage'
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
// Read off a bare label list too, so `josh issue:file` refuses an untriaged `auto-ok` filing by the
|
|
24
|
+
// same answer the offer paths withhold it by (joshuafolkken/kit#3313).
|
|
25
|
+
function has_triage_label(labels: ReadonlyArray<string>): boolean {
|
|
26
26
|
return has_label_name(labels, RUN_SOLO_LABEL) || has_label_name(labels, RUN_LANE_LABEL)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function is_triaged(child: EpicChild): boolean {
|
|
30
|
+
return has_triage_label(child.labels)
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
// The candidates nobody has judged, in the order they were given.
|
|
30
34
|
function untriaged(children: ReadonlyArray<EpicChild>): ReadonlyArray<EpicChild> {
|
|
31
35
|
return children.filter((child) => !is_triaged(child))
|
|
@@ -41,7 +45,7 @@ function message(children: ReadonlyArray<EpicChild>, repo: string): string {
|
|
|
41
45
|
)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
const epic_triage = { TRIAGE_VERDICT, is_triaged, message, untriaged }
|
|
48
|
+
const epic_triage = { TRIAGE_VERDICT, has_triage_label, is_triaged, message, untriaged }
|
|
45
49
|
|
|
46
50
|
export type { TriageVerdict }
|
|
47
51
|
export { epic_triage }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { epic_subject_schema } from '#scripts/git/git-schemas'
|
|
1
2
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
2
|
-
import { epic_subject_schema } from '#scripts/git/schemas'
|
|
3
3
|
import { EPIC_LABEL, has_label_name } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { epic_parse, type DeclarationState } from './epic-parse'
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { git_next_issues } from '#scripts/issue/git-next-issues'
|
|
2
2
|
import { review_attest } from '#scripts/review/review-attest'
|
|
3
3
|
import { review_stamps } from '#scripts/review/review-stamps'
|
|
4
|
-
import { run_hold } from '#scripts/run/run-hold'
|
|
5
|
-
import { run_progress_clock } from '#scripts/run/run-progress-clock'
|
|
4
|
+
import { run_hold } from '#scripts/run/hold/run-hold'
|
|
5
|
+
import { run_progress_clock } from '#scripts/run/progress/run-progress-clock'
|
|
6
6
|
import { parse_completed_issue_number } from './followup-issue-number'
|
|
7
7
|
import { git_followup_cleanup, type CleanupStep } from './git-followup-cleanup'
|
|
8
8
|
import { git_followup_pending } from './git-followup-pending'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { issue_label_schema } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { issue_label_schema } from '#scripts/git/schemas'
|
|
4
4
|
import { IN_PROGRESS_LABEL, label_name_of } from '#scripts/issue/issue-labels'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ import { z } from 'zod'
|
|
|
19
19
|
// No procedure covered the ordinary ending: merged, closed, and still marked as running.
|
|
20
20
|
|
|
21
21
|
// The `number,labels,body` read answers labels as objects, and this is the one field of it this
|
|
22
|
-
// step needs. The element shape is `schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
22
|
+
// step needs. The element shape is `git-schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
23
23
|
// is local because a read's *field list* is what multiplies helpers when it is shared
|
|
24
24
|
// (`git-gh-issue-read.ts`).
|
|
25
25
|
const labels_read_schema = z.object({ labels: z.array(issue_label_schema).optional() })
|
|
@@ -66,8 +66,8 @@ function is_posted_since(comment: IssueComment, merged_at: string): boolean {
|
|
|
66
66
|
return Date.parse(comment.created_at) >= Date.parse(merged_at)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function parse_comments(comments_json: string | undefined): Array<IssueComment> {
|
|
70
|
-
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json
|
|
69
|
+
function parse_comments(comments_json: string | undefined = ''): Array<IssueComment> {
|
|
70
|
+
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json))
|
|
71
71
|
|
|
72
72
|
return parsed.success ? parsed.data : []
|
|
73
73
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PrConflictError } from '#scripts/gh/git-pr-checks-eval'
|
|
1
2
|
import { time_format } from '#scripts/time-runtime/time-format'
|
|
2
3
|
|
|
3
4
|
// Where `pnpm josh followup` spends its own wall clock (joshuafolkken/kit#1349).
|
|
@@ -73,8 +74,15 @@ const STAGE = {
|
|
|
73
74
|
// blocker and on a red check, and those are the runs whose wait is longest — reporting nothing for
|
|
74
75
|
// them would leave the measurement blind to exactly the invocations worth measuring.
|
|
75
76
|
interrupted: 'interrupted',
|
|
77
|
+
// The interrupted lap when the wait failed on a merge conflict (joshuafolkken/kit#3221), so the
|
|
78
|
+
// stage block names the cause a resumed session used to look up with `gh pr view`.
|
|
79
|
+
conflict: 'conflict',
|
|
76
80
|
} as const
|
|
77
81
|
|
|
82
|
+
function stopped_lap(error: unknown): string {
|
|
83
|
+
return error instanceof PrConflictError ? STAGE.conflict : STAGE.interrupted
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
interface FollowupStage {
|
|
79
87
|
name: string
|
|
80
88
|
duration_ms: number
|
|
@@ -143,6 +151,7 @@ const git_followup_stages = {
|
|
|
143
151
|
total_ms,
|
|
144
152
|
format_stages,
|
|
145
153
|
print_stages,
|
|
154
|
+
stopped_lap,
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
export type { FollowupStage, StageLog }
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_STABLE_READS,
|
|
5
|
+
git_pr_checks,
|
|
6
|
+
WATCH_CONFIRMED_STABLE_READS,
|
|
7
|
+
} from '#scripts/gh/git-pr-checks'
|
|
8
|
+
import { is_coderabbit_check } from '#scripts/gh/git-pr-checks-eval'
|
|
9
|
+
import { CHECK_STATUS_PASS, type PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
|
|
10
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
11
|
+
|
|
12
|
+
// The check wait and the notes read off its snapshot — split out of `git-pr-followup.ts` when it
|
|
13
|
+
// reached its line limit (joshuafolkken/kit#3264). `git-pr-followup` re-exports each name it always
|
|
14
|
+
// exported, so the move changed no call site and no suite that imports from it.
|
|
15
|
+
|
|
16
|
+
// Named so a test can pin the note without restating it (joshuafolkken/kit#999).
|
|
17
|
+
const WATCH_FAILED_NOTE = 'pr checks --watch failed; falling through to polling'
|
|
18
|
+
|
|
19
|
+
// The watch is a look ahead, not a gate. It fails when **any** check has failed — CodeRabbit
|
|
20
|
+
// included — and letting that escape ended the run before `evaluate_pr_state` could apply kit#753's
|
|
21
|
+
// CodeRabbit exemption at all. It only ever bit where every check finished inside the two-minute
|
|
22
|
+
// window, so a repository with a slow E2E never saw it and a fast one always would
|
|
23
|
+
// (joshuafolkken/kit#999). What the watch is has since changed — `gh pr checks --watch` went through
|
|
24
|
+
// GraphQL, so joshuafolkken/kit#1028 replaced it with the same poll loop bounded to two minutes —
|
|
25
|
+
// but its place here has not.
|
|
26
|
+
//
|
|
27
|
+
// Falling through costs nothing: whether the merge may proceed is decided in one place below, and a
|
|
28
|
+
// genuinely failing non-CodeRabbit check still ends the wait on the first poll by kit#990's
|
|
29
|
+
// fast-fail rather than by that failure.
|
|
30
|
+
// The watch fails for two different reasons — "a check failed" and "no checks reported on this
|
|
31
|
+
// branch" — and its own error does not say which. The pull request itself can: a failed check leaves
|
|
32
|
+
// a rollup, and a branch with no checks leaves it empty. Falling through on the empty case would
|
|
33
|
+
// trade a failure reported in seconds for the whole budget spent waiting on a required check that is
|
|
34
|
+
// missing rather than pending, so that one is rethrown and the old behavior kept.
|
|
35
|
+
//
|
|
36
|
+
// **Read from the raw payload, not from `parse_pr_state_snapshot`.** That parser never throws: a
|
|
37
|
+
// malformed answer or a schema mismatch degrades to `rollup: []`, which is indistinguishable there
|
|
38
|
+
// from a branch that genuinely has no checks. Rethrowing on it would put an unreadable answer back
|
|
39
|
+
// on the path this whole change exists to remove. So the question asked here is the narrow one —
|
|
40
|
+
// *is this definitely an empty rollup* — and every other outcome falls through.
|
|
41
|
+
function reads_as_empty_rollup(raw_json: string): boolean {
|
|
42
|
+
const parsed = json_value.parse_or_undefined(raw_json)
|
|
43
|
+
|
|
44
|
+
if (typeof parsed !== 'object' || parsed === null || !('statusCheckRollup' in parsed)) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { statusCheckRollup: rollup } = parsed
|
|
49
|
+
|
|
50
|
+
return Array.isArray(rollup) && rollup.length === 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The read throws rather than answering `undefined`, so the `catch` is its only failure path.
|
|
54
|
+
//
|
|
55
|
+
// The **checks** half, not the whole snapshot: the question here is about `statusCheckRollup` alone,
|
|
56
|
+
// and reading the review listing to answer it paged a whole conversation for nothing
|
|
57
|
+
// (joshuafolkken/kit#1043).
|
|
58
|
+
async function has_no_checks(branch_name: string): Promise<boolean> {
|
|
59
|
+
try {
|
|
60
|
+
const checks = await git_gh_command.pr_get_checks_snapshot(branch_name)
|
|
61
|
+
|
|
62
|
+
return reads_as_empty_rollup(checks.snapshot_json)
|
|
63
|
+
} catch {
|
|
64
|
+
// The read refines the swallow; it is not a gate of its own. An answer it cannot get is not
|
|
65
|
+
// evidence of anything, so prefer falling through — the poll below reads the same state.
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function handle_watch_failure(branch_name: string, error: unknown): Promise<void> {
|
|
71
|
+
if (await has_no_checks(branch_name)) throw error
|
|
72
|
+
|
|
73
|
+
// Swallowed, but never silently: the reason a run stopped early used to be this line, so it
|
|
74
|
+
// stays visible even though it no longer decides anything.
|
|
75
|
+
console.info(`⚠️ ${WATCH_FAILED_NOTE}: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// **Returns whether the watch confirmed completion** (joshuafolkken/kit#2029): `true` only when
|
|
79
|
+
// `pr_checks_watch` saw every check finish, `false` when it timed out with checks still pending or
|
|
80
|
+
// fell through to polling on a swallowed failure. `run_checks` reads it to decide how many stable poll
|
|
81
|
+
// reads the wait still needs. A timed-out watch (`timed_out: true`) never spanned the pending→settled
|
|
82
|
+
// window, so it must keep the full stable-read window rather than claim the confirmation. A rethrown
|
|
83
|
+
// failure — the empty-rollup case — never reaches a caller, so it needs no answer here.
|
|
84
|
+
async function watch_before_polling(branch_name: string): Promise<boolean> {
|
|
85
|
+
console.info('')
|
|
86
|
+
console.info('📊 Watching PR checks...')
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = await git_gh_command.pr_checks_watch(branch_name)
|
|
90
|
+
|
|
91
|
+
return !result.timed_out
|
|
92
|
+
} catch (error) {
|
|
93
|
+
await handle_watch_failure(branch_name, error)
|
|
94
|
+
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// **A confirmed watch lowers the poll's stable-read requirement to one** (joshuafolkken/kit#2029). The
|
|
100
|
+
// watch is the first confirmation that every check settled, so the poll that follows need only agree
|
|
101
|
+
// once rather than twice — which removes the extra interval `followup` spent re-proving a settled run.
|
|
102
|
+
// A skipped or fallen-through watch keeps `DEFAULT_STABLE_READS`, and a check that turned red is still
|
|
103
|
+
// caught: the poll evaluates the full merge gate and throws on failure before any stable read counts.
|
|
104
|
+
async function run_checks(input: {
|
|
105
|
+
branch_name: string
|
|
106
|
+
is_skip_watch: boolean
|
|
107
|
+
}): Promise<PrStateSnapshot> {
|
|
108
|
+
const is_watch_confirmed = !input.is_skip_watch && (await watch_before_polling(input.branch_name))
|
|
109
|
+
const stable_reads = is_watch_confirmed ? WATCH_CONFIRMED_STABLE_READS : DEFAULT_STABLE_READS
|
|
110
|
+
|
|
111
|
+
return await git_pr_checks.wait_for_pr_success(input.branch_name, stable_reads)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Temporary (kit#753): record every CodeRabbit check that was not passing when the merge gate
|
|
115
|
+
// opened, so a merge shipped without CodeRabbit review stays auditable.
|
|
116
|
+
function read_coderabbit_skip_notes(snapshot: PrStateSnapshot): Array<string> {
|
|
117
|
+
return snapshot.rollup
|
|
118
|
+
.filter((check) => is_coderabbit_check(check.name))
|
|
119
|
+
.filter((check) => check.status !== CHECK_STATUS_PASS)
|
|
120
|
+
.map(
|
|
121
|
+
(check) =>
|
|
122
|
+
`CodeRabbit check skipped (kit#753): ${check.name} was ${check.status} at merge time`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// The skip is printed where the run is being watched, not only carried into the Telegram body it
|
|
127
|
+
// ends up in. A merge that went ahead while CodeRabbit had not come back is the one thing kit#753's
|
|
128
|
+
// exemption trades away, and it used to be invisible in `followup`'s own output
|
|
129
|
+
// (joshuafolkken/kit#1217).
|
|
130
|
+
function log_skip_notes(notes: ReadonlyArray<string>): void {
|
|
131
|
+
for (const note of notes) {
|
|
132
|
+
console.info(`⏭ ${note}`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const git_pr_followup_checks = {
|
|
137
|
+
run_checks,
|
|
138
|
+
read_coderabbit_skip_notes,
|
|
139
|
+
log_skip_notes,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { git_pr_followup_checks, WATCH_FAILED_NOTE }
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
-
import { git_gh_helpers } from '#scripts/gh/git-gh-helpers'
|
|
3
2
|
import { git_pr_ai_review, type TelegramContext } from '#scripts/gh/git-pr-ai-review'
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_STABLE_READS,
|
|
6
|
-
git_pr_checks,
|
|
7
|
-
WATCH_CONFIRMED_STABLE_READS,
|
|
8
|
-
} from '#scripts/gh/git-pr-checks'
|
|
9
|
-
import { is_coderabbit_check } from '#scripts/gh/git-pr-checks-eval'
|
|
10
|
-
import { CHECK_STATUS_PASS, type PrStateSnapshot } from '#scripts/gh/git-pr-checks-parse'
|
|
11
3
|
import { git_pr_coderabbit } from '#scripts/gh/git-pr-coderabbit'
|
|
12
4
|
import { git_pr_managed_config } from '#scripts/gh/git-pr-managed-config'
|
|
13
5
|
import { github_issue_url } from '#scripts/gh/github-issue-url'
|
|
14
6
|
import { git_closes_keyword } from '#scripts/git/git-closes-keyword'
|
|
15
|
-
import { json_value } from '#scripts/lib/json-value'
|
|
16
7
|
import type { GitNotifyConfig } from '#scripts/notify/git-notify'
|
|
17
8
|
import {
|
|
18
9
|
telegram_notify,
|
|
@@ -22,12 +13,12 @@ import {
|
|
|
22
13
|
import { git_followup_merged } from './git-followup-merged'
|
|
23
14
|
import { git_followup_pending } from './git-followup-pending'
|
|
24
15
|
import { git_followup_stages, type StageLog } from './git-followup-stages'
|
|
16
|
+
import { git_pr_followup_checks } from './git-pr-followup-checks'
|
|
25
17
|
import { git_pr_followup_wrapup } from './git-pr-followup-wrapup'
|
|
26
18
|
|
|
27
19
|
const { STAGE, lap } = git_followup_stages
|
|
20
|
+
const { run_checks, read_coderabbit_skip_notes, log_skip_notes } = git_pr_followup_checks
|
|
28
21
|
|
|
29
|
-
// Named so a test can pin the note without restating it (joshuafolkken/kit#999).
|
|
30
|
-
const WATCH_FAILED_NOTE = 'pr checks --watch failed; falling through to polling'
|
|
31
22
|
const REPO_NAME_SEPARATOR = '/'
|
|
32
23
|
|
|
33
24
|
function parse_repo_name(name_with_owner: string | undefined): string | undefined {
|
|
@@ -107,123 +98,6 @@ async function warn_if_missing_closes(branch_name: string): Promise<string | und
|
|
|
107
98
|
return undefined
|
|
108
99
|
}
|
|
109
100
|
|
|
110
|
-
// The watch is a look ahead, not a gate. It fails when **any** check has failed — CodeRabbit
|
|
111
|
-
// included — and letting that escape ended the run before `evaluate_pr_state` could apply kit#753's
|
|
112
|
-
// CodeRabbit exemption at all. It only ever bit where every check finished inside the two-minute
|
|
113
|
-
// window, so a repository with a slow E2E never saw it and a fast one always would
|
|
114
|
-
// (joshuafolkken/kit#999). What the watch is has since changed — `gh pr checks --watch` went through
|
|
115
|
-
// GraphQL, so joshuafolkken/kit#1028 replaced it with the same poll loop bounded to two minutes —
|
|
116
|
-
// but its place here has not.
|
|
117
|
-
//
|
|
118
|
-
// Falling through costs nothing: whether the merge may proceed is decided in one place below, and a
|
|
119
|
-
// genuinely failing non-CodeRabbit check still ends the wait on the first poll by kit#990's
|
|
120
|
-
// fast-fail rather than by that failure.
|
|
121
|
-
// The watch fails for two different reasons — "a check failed" and "no checks reported on this
|
|
122
|
-
// branch" — and its own error does not say which. The pull request itself can: a failed check leaves
|
|
123
|
-
// a rollup, and a branch with no checks leaves it empty. Falling through on the empty case would
|
|
124
|
-
// trade a failure reported in seconds for the whole budget spent waiting on a required check that is
|
|
125
|
-
// missing rather than pending, so that one is rethrown and the old behavior kept.
|
|
126
|
-
//
|
|
127
|
-
// **Read from the raw payload, not from `parse_pr_state_snapshot`.** That parser never throws: a
|
|
128
|
-
// malformed answer or a schema mismatch degrades to `rollup: []`, which is indistinguishable there
|
|
129
|
-
// from a branch that genuinely has no checks. Rethrowing on it would put an unreadable answer back
|
|
130
|
-
// on the path this whole change exists to remove. So the question asked here is the narrow one —
|
|
131
|
-
// *is this definitely an empty rollup* — and every other outcome falls through.
|
|
132
|
-
function reads_as_empty_rollup(raw_json: string): boolean {
|
|
133
|
-
const parsed = json_value.parse_or_undefined(raw_json)
|
|
134
|
-
|
|
135
|
-
if (typeof parsed !== 'object' || parsed === null || !('statusCheckRollup' in parsed)) {
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const { statusCheckRollup: rollup } = parsed
|
|
140
|
-
|
|
141
|
-
return Array.isArray(rollup) && rollup.length === 0
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// The read throws rather than answering `undefined`, so the `catch` is its only failure path.
|
|
145
|
-
//
|
|
146
|
-
// The **checks** half, not the whole snapshot: the question here is about `statusCheckRollup` alone,
|
|
147
|
-
// and reading the review listing to answer it paged a whole conversation for nothing
|
|
148
|
-
// (joshuafolkken/kit#1043).
|
|
149
|
-
async function has_no_checks(branch_name: string): Promise<boolean> {
|
|
150
|
-
try {
|
|
151
|
-
const checks = await git_gh_command.pr_get_checks_snapshot(branch_name)
|
|
152
|
-
|
|
153
|
-
return reads_as_empty_rollup(checks.snapshot_json)
|
|
154
|
-
} catch {
|
|
155
|
-
// The read refines the swallow; it is not a gate of its own. An answer it cannot get is not
|
|
156
|
-
// evidence of anything, so prefer falling through — the poll below reads the same state.
|
|
157
|
-
return false
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function handle_watch_failure(branch_name: string, error: unknown): Promise<void> {
|
|
162
|
-
if (await has_no_checks(branch_name)) throw error
|
|
163
|
-
|
|
164
|
-
// Swallowed, but never silently: the reason a run stopped early used to be this line, so it
|
|
165
|
-
// stays visible even though it no longer decides anything.
|
|
166
|
-
console.info(`⚠️ ${WATCH_FAILED_NOTE}: ${git_gh_helpers.get_error_message_with_stderr(error)}`)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// **Returns whether the watch confirmed completion** (joshuafolkken/kit#2029): `true` only when
|
|
170
|
-
// `pr_checks_watch` saw every check finish, `false` when it timed out with checks still pending or
|
|
171
|
-
// fell through to polling on a swallowed failure. `run_checks` reads it to decide how many stable poll
|
|
172
|
-
// reads the wait still needs. A timed-out watch (`timed_out: true`) never spanned the pending→settled
|
|
173
|
-
// window, so it must keep the full stable-read window rather than claim the confirmation. A rethrown
|
|
174
|
-
// failure — the empty-rollup case — never reaches a caller, so it needs no answer here.
|
|
175
|
-
async function watch_before_polling(branch_name: string): Promise<boolean> {
|
|
176
|
-
console.info('')
|
|
177
|
-
console.info('📊 Watching PR checks...')
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const result = await git_gh_command.pr_checks_watch(branch_name)
|
|
181
|
-
|
|
182
|
-
return !result.timed_out
|
|
183
|
-
} catch (error) {
|
|
184
|
-
await handle_watch_failure(branch_name, error)
|
|
185
|
-
|
|
186
|
-
return false
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// **A confirmed watch lowers the poll's stable-read requirement to one** (joshuafolkken/kit#2029). The
|
|
191
|
-
// watch is the first confirmation that every check settled, so the poll that follows need only agree
|
|
192
|
-
// once rather than twice — which removes the extra interval `followup` spent re-proving a settled run.
|
|
193
|
-
// A skipped or fallen-through watch keeps `DEFAULT_STABLE_READS`, and a check that turned red is still
|
|
194
|
-
// caught: the poll evaluates the full merge gate and throws on failure before any stable read counts.
|
|
195
|
-
async function run_checks(input: {
|
|
196
|
-
branch_name: string
|
|
197
|
-
is_skip_watch: boolean
|
|
198
|
-
}): Promise<PrStateSnapshot> {
|
|
199
|
-
const is_watch_confirmed = !input.is_skip_watch && (await watch_before_polling(input.branch_name))
|
|
200
|
-
const stable_reads = is_watch_confirmed ? WATCH_CONFIRMED_STABLE_READS : DEFAULT_STABLE_READS
|
|
201
|
-
|
|
202
|
-
return await git_pr_checks.wait_for_pr_success(input.branch_name, stable_reads)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Temporary (kit#753): record every CodeRabbit check that was not passing when the merge gate
|
|
206
|
-
// opened, so a merge shipped without CodeRabbit review stays auditable.
|
|
207
|
-
function read_coderabbit_skip_notes(snapshot: PrStateSnapshot): Array<string> {
|
|
208
|
-
return snapshot.rollup
|
|
209
|
-
.filter((check) => is_coderabbit_check(check.name))
|
|
210
|
-
.filter((check) => check.status !== CHECK_STATUS_PASS)
|
|
211
|
-
.map(
|
|
212
|
-
(check) =>
|
|
213
|
-
`CodeRabbit check skipped (kit#753): ${check.name} was ${check.status} at merge time`,
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// The skip is printed where the run is being watched, not only carried into the Telegram body it
|
|
218
|
-
// ends up in. A merge that went ahead while CodeRabbit had not come back is the one thing kit#753's
|
|
219
|
-
// exemption trades away, and it used to be invisible in `followup`'s own output
|
|
220
|
-
// (joshuafolkken/kit#1217).
|
|
221
|
-
function log_skip_notes(notes: ReadonlyArray<string>): void {
|
|
222
|
-
for (const note of notes) {
|
|
223
|
-
console.info(`⏭ ${note}`)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
101
|
// **The one read of the four that can depend on another.** Without an issue number on the command
|
|
228
102
|
// line it is the pull request body that names it (joshuafolkken/kit#1539), so the title read waits
|
|
229
103
|
// for exactly that one and for nothing else. With a number in hand the `??` short-circuits and the
|
|
@@ -437,7 +311,7 @@ async function run(input: FollowupInput): Promise<string | undefined> {
|
|
|
437
311
|
try {
|
|
438
312
|
return await run_stages(input, log)
|
|
439
313
|
} catch (error) {
|
|
440
|
-
lap(log,
|
|
314
|
+
lap(log, git_followup_stages.stopped_lap(error))
|
|
441
315
|
|
|
442
316
|
throw error
|
|
443
317
|
} finally {
|
|
@@ -451,7 +325,6 @@ const git_pr_followup = {
|
|
|
451
325
|
|
|
452
326
|
export {
|
|
453
327
|
git_pr_followup,
|
|
454
|
-
WATCH_FAILED_NOTE,
|
|
455
328
|
run_checks,
|
|
456
329
|
build_issue_url,
|
|
457
330
|
parse_repo_name,
|
|
@@ -464,3 +337,5 @@ export {
|
|
|
464
337
|
}
|
|
465
338
|
export type { FollowupInput }
|
|
466
339
|
export type { TelegramContext } from '#scripts/gh/git-pr-ai-review'
|
|
340
|
+
|
|
341
|
+
export { WATCH_FAILED_NOTE } from './git-pr-followup-checks'
|