@joshuafolkken/kit 1.1050.0 → 1.1128.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/settings.json +9 -8
- package/.claude/skills/epic-commands/SKILL.md +5 -7
- package/.claude/skills/workflow-commands/SKILL.md +20 -12
- package/.claude/skills/workflow-commands/background-commands.md +36 -84
- package/.claude/skills/workflow-commands/backlogrun-child.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +8 -6
- package/.claude/skills/workflow-commands/backlogrun-progress.md +30 -33
- package/.claude/skills/workflow-commands/backlogrun-steps.md +12 -12
- package/.claude/skills/workflow-commands/backlogrun.md +2 -2
- package/.claude/skills/workflow-commands/chain-rule.md +4 -2
- package/.claude/skills/workflow-commands/delegation.md +3 -3
- package/.claude/skills/workflow-commands/followup-reference.md +1 -1
- package/.claude/skills/workflow-commands/followup.md +7 -3
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/fullrun.md +8 -6
- package/.claude/skills/workflow-commands/halfrun.md +19 -21
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/issue-fold-existing.md +10 -10
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +9 -4
- package/.claude/skills/workflow-commands/latest-gate.md +8 -16
- package/.claude/skills/workflow-commands/needs-human-review.md +1 -1
- package/.claude/skills/workflow-commands/observation-filing.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
- package/.claude/skills/workflow-commands/prerequisite.md +1 -1
- package/.claude/skills/workflow-commands/prrun.md +36 -0
- package/.claude/skills/workflow-commands/retrospective.md +1 -1
- package/.claude/skills/workflow-commands/split-assessment.md +4 -18
- package/.claude/skills/workflow-commands/working-tree-hold.md +22 -2
- package/.codex/hooks.json +16 -20
- package/.github/actions/setup-node/action.yml +33 -0
- package/.github/actions/setup-pnpm/action.yml +93 -0
- package/.github/dependabot.yml +5 -1
- package/.github/workflows/auto-tag.yml +2 -3
- package/.github/workflows/ci.yml +31 -134
- package/.github/workflows/dependabot-auto-merge.yml +1 -0
- package/.github/workflows/pr-classification.yml +6 -44
- package/.github/workflows/production.yml +1 -0
- package/.github/workflows/publish.yml +43 -47
- package/.github/workflows/sonar-qube.yml +41 -4
- package/.pnpmfile.mjs +29 -0
- package/AGENTS.md +5 -2
- package/CLAUDE.md +33 -27
- package/GEMINI.md +5 -2
- package/README.md +66 -20
- package/SECURITY.md +37 -6
- package/cspell/index.yaml +6 -1
- package/dist/CLAUDE.basic.md +6 -6
- package/dist/CLAUDE.md +33 -27
- package/dist/CLAUDE.static.md +6 -6
- package/dist/config-merge/index.js +48 -12
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-7VPDUBRN.js → chunk-4JZDP5X5.js} +152 -155
- package/dist/hooks/{chunk-HJIISEAC.js → chunk-5SD2YM7Z.js} +5 -13
- package/dist/hooks/{chunk-NAS4C7GP.js → chunk-77J7IEQM.js} +2 -2
- package/dist/hooks/{chunk-DMQN45FN.js → chunk-7HVB7SZG.js} +3 -3
- package/dist/hooks/{chunk-RLDX5SOC.js → chunk-AIOIOMPX.js} +4413 -1970
- package/dist/hooks/{chunk-5BK3K7V4.js → chunk-M7QKEKWO.js} +873 -2049
- package/dist/hooks/{chunk-WQ4OSBSS.js → chunk-MKVGBQPA.js} +2 -2
- package/dist/hooks/{chunk-EJDX2TOB.js → chunk-NXYMNPUQ.js} +7 -88
- package/dist/hooks/{chunk-NJQOBE6L.js → chunk-QVLT52OV.js} +2 -7
- package/dist/hooks/{chunk-GQ7KP45M.js → chunk-XYUIQOW3.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +12 -48
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +280 -0
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/session-lang.js +0 -1
- package/dist/hooks/stop-guard.js +192 -333
- package/dist/josh.js +240 -148
- package/dist/managed-marker/index.js +2 -1
- package/dist/version/index.js +70 -78
- package/eslint/rules/promise.js +5 -0
- package/eslint/rules/unicorn.js +3 -2
- package/package.json +26 -5
- package/pnpm-workspace.yaml +5 -7
- package/prompts/collaboration-workflow/glossary.md +17 -0
- package/prompts/collaboration-workflow/issue-citation.md +5 -5
- package/prompts/collaboration-workflow/issue-template.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +5 -5
- package/prompts/collaboration-workflow/overview.md +13 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +3 -3
- package/prompts/collaboration-workflow/rule-delivery.md +1 -1
- package/prompts/collaboration-workflow/turn-batching.md +3 -3
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +25 -22
- package/prompts/collaboration-workflow.md +6 -8
- package/prompts/review-rubric.md +1 -1
- package/prompts/review.md +3 -3
- package/prompts/sonar-hotspot-handling.md +11 -3
- package/prompts/testing-guide.md +3 -3
- package/scripts/adopt/adopt-toolkits.ts +0 -2
- package/scripts/adopt/adopt.ts +0 -2
- package/scripts/agent/agent-argv.ts +0 -2
- package/scripts/agent/agent-diagnostics.ts +1 -1
- package/scripts/agent/agent-role-profile.ts +0 -7
- package/scripts/agent/claude-agent-argv.ts +0 -2
- package/scripts/agent/codex-agent-argv.ts +1 -1
- package/scripts/agent/codex-hooks.ts +92 -0
- package/scripts/agent/interactive-ask.ts +1 -1
- package/scripts/auto-ok/auto-ok-cli.ts +6 -16
- package/scripts/backlog/backlog-arrival.ts +30 -10
- package/scripts/backlog/backlog-budget-cli.ts +15 -16
- package/scripts/backlog/backlog-budget.ts +0 -2
- package/scripts/backlog/backlog-defect-priority.ts +2 -2
- package/scripts/backlog/backlog-drive-cli.ts +10 -8
- package/scripts/backlog/backlog-drive-named-offer.ts +16 -3
- package/scripts/backlog/backlog-drive-named.ts +2 -1
- package/scripts/backlog/backlog-drive.ts +76 -16
- package/scripts/backlog/backlog-named.ts +0 -3
- package/scripts/backlog/backlog-next.ts +9 -15
- package/scripts/backlog/backlog-offer-cli.ts +6 -19
- package/scripts/backlog/backlog-plan-cli.ts +1 -7
- package/scripts/backlog/backlog-plan.ts +30 -19
- package/scripts/backlog/backlog-pool.ts +3 -6
- package/scripts/backlog/backlog-rank.ts +2 -2
- package/scripts/backlog/backlog-ready.ts +39 -10
- package/scripts/backlog/backlog-scope.ts +1 -3
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +0 -1
- package/scripts/backlog/backlog-waves.ts +5 -2
- package/scripts/behavior/behavior-cli.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +26 -0
- package/scripts/build/build-hooks.ts +85 -9
- package/scripts/build/hook-bundle-stamp.ts +111 -0
- package/scripts/cases/cases-cli.ts +1 -1
- package/scripts/cases/cases-logic.ts +1 -1
- package/scripts/ci/composite-actions.ts +30 -0
- package/scripts/ci/pr-classification.ts +3 -3
- package/scripts/claude/workflow-destination.ts +10 -1
- package/scripts/clone/clone-fingerprint.ts +0 -1
- package/scripts/clone/clone-scan-cli.ts +1 -1
- package/scripts/clone/clone-scan.ts +1 -1
- package/scripts/config-merge/json-format.ts +0 -1
- package/scripts/config-merge/parse-jsonc.ts +33 -3
- package/scripts/config-merge/patch-json-key.ts +13 -6
- package/scripts/config-merge/patch-yaml-list.ts +1 -1
- package/scripts/cost-runtime/codex-usage.ts +2 -9
- package/scripts/cost-runtime/cost-attribute.ts +0 -1
- package/scripts/cost-runtime/cost-cli.ts +3 -10
- package/scripts/cost-runtime/cost-corpus.ts +0 -2
- package/scripts/cost-runtime/cost-format.ts +0 -3
- package/scripts/cost-runtime/cost-pricing.ts +0 -1
- package/scripts/cost-runtime/cost-tokens.ts +0 -1
- package/scripts/delegation/delegation-cli.ts +1 -1
- package/scripts/delegation/delegation-policy.ts +0 -1
- package/scripts/delegation/duplicate-read-guard.ts +0 -8
- package/scripts/delegation/fanout-cli.ts +1 -1
- package/scripts/delegation/fanout.ts +0 -2
- package/scripts/delegation/investigation-reads.ts +1 -164
- package/scripts/discovery/repo-map-logic.ts +0 -1
- package/scripts/discovery/repo-party-cli.ts +1 -1
- package/scripts/doctor/doctor-consumer.ts +6 -10
- package/scripts/doctor/doctor-io.ts +9 -13
- package/scripts/doctor/doctor-logic.ts +0 -4
- package/scripts/doctor/doctor.ts +14 -9
- package/scripts/document/backlogrun-parent-read-set.ts +12 -1
- package/scripts/document/document-byte-budget.ts +5 -5
- package/scripts/document/document-byte-check.ts +0 -1
- package/scripts/document/document-read-cli.ts +0 -2
- package/scripts/document/document-section-cli.ts +1 -1
- package/scripts/document/document-section.ts +0 -3
- package/scripts/document/edit-files-cli.ts +0 -1
- package/scripts/document/entry-read-budget.ts +21 -7
- package/scripts/document/entry-read-set.ts +32 -8
- package/scripts/document/lane-child-read-set.ts +8 -6
- package/scripts/document/read-files-cli.ts +0 -4
- package/scripts/document/read-set-cli.ts +0 -2
- package/scripts/document/read-set-point-of-use.ts +9 -3
- package/scripts/document/read-set-pricing.ts +0 -1
- package/scripts/document/read-set-trim.ts +4 -2
- package/scripts/epic/epic-add.ts +1 -1
- package/scripts/epic/epic-audit-cli.ts +1 -4
- package/scripts/epic/epic-audit-orphans.ts +1 -3
- package/scripts/epic/epic-audit-rationale.ts +0 -1
- package/scripts/epic/epic-audit-report.ts +0 -2
- package/scripts/epic/epic-audit.ts +8 -22
- package/scripts/epic/epic-bundle-cli.ts +1 -1
- package/scripts/epic/epic-bundle-evidence.ts +0 -1
- package/scripts/epic/epic-bundle-referenced.ts +1 -5
- package/scripts/epic/epic-bundle.ts +0 -4
- package/scripts/epic/epic-busy.ts +4 -5
- package/scripts/epic/epic-candidate-confirm.ts +0 -5
- package/scripts/epic/epic-classify.ts +2 -2
- package/scripts/epic/epic-cli-argv.ts +0 -1
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +5 -2
- package/scripts/epic/epic-cross-repo.ts +2 -4
- package/scripts/epic/epic-decision.ts +0 -1
- package/scripts/epic/epic-fetch.ts +2 -2
- package/scripts/epic/epic-index.ts +2 -3
- package/scripts/epic/epic-issue.ts +1 -15
- package/scripts/epic/epic-lane-offer.ts +9 -8
- package/scripts/epic/epic-nested.ts +1 -1
- package/scripts/epic/epic-next-read.ts +1 -3
- package/scripts/epic/epic-next-views.ts +0 -1
- package/scripts/epic/epic-next.ts +1 -10
- package/scripts/epic/epic-parse.ts +0 -1
- package/scripts/epic/epic-promote.ts +0 -9
- package/scripts/epic/epic-rank.ts +2 -2
- package/scripts/epic/epic-read.ts +1 -2
- package/scripts/epic/epic-reconcile.ts +1 -1
- package/scripts/epic/epic-reference.ts +1 -13
- package/scripts/epic/epic-relation-recheck.ts +1 -1
- package/scripts/epic/epic-relations.ts +1 -1
- package/scripts/epic/epic-remove.ts +1 -1
- package/scripts/epic/epic-report.ts +0 -1
- package/scripts/epic/epic-run.ts +2 -3
- package/scripts/epic/epic-shape.ts +1 -1
- package/scripts/epic/epic-solo-stale.ts +107 -0
- package/scripts/epic/epic-solo.ts +3 -2
- package/scripts/epic/epic-triage.ts +1 -1
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/epic/epic.ts +1 -1
- package/scripts/exports/unused-members-cli.ts +53 -0
- package/scripts/{git → followup}/followup-issue-number.ts +1 -6
- package/scripts/{git → followup}/git-followup-cleanup.ts +2 -1
- package/scripts/{git → followup}/git-followup-finish.ts +1 -3
- package/scripts/{git → followup}/git-followup-flush.ts +2 -2
- package/scripts/{git → followup}/git-followup-issue-close.ts +2 -2
- package/scripts/{git → followup}/git-followup-label.ts +4 -4
- package/scripts/followup/git-followup-merged.ts +112 -0
- package/scripts/{git → followup}/git-followup-pending.ts +1 -1
- package/scripts/{git → followup}/git-followup-workflow.ts +19 -11
- package/scripts/{git → followup}/git-pr-followup-wrapup.ts +9 -3
- package/scripts/{git → followup}/git-pr-followup.ts +53 -16
- package/scripts/gate/core-budget.ts +3 -9
- package/scripts/gate/gate-plan.ts +7 -4
- package/scripts/gate/gate-report.ts +0 -6
- package/scripts/gate/gate-tree.ts +1 -1
- package/scripts/gate/pre-commit-type-check.ts +0 -6
- package/scripts/gate/project-checks.ts +7 -8
- package/scripts/gate/scoped-green.ts +0 -1
- package/scripts/gate/type-check-step.ts +1 -1
- package/scripts/gate/verification-gate.ts +0 -2
- package/scripts/gh/fix-gh-packages.ts +15 -12
- package/scripts/gh/gh-spawn.ts +2 -2
- package/scripts/{git → gh}/git-ai-review-scan.ts +1 -5
- package/scripts/{git → gh}/git-gh-check.ts +2 -1
- package/scripts/{git → gh}/git-gh-exec.ts +1 -1
- package/scripts/{git → gh}/git-gh-failure.ts +1 -1
- package/scripts/{git → gh}/git-gh-helpers.ts +0 -1
- package/scripts/{git → gh}/git-gh-issue-list.ts +13 -10
- package/scripts/{git → gh}/git-gh-issue-read.ts +1 -1
- package/scripts/{git → gh}/git-gh-issue-rest.ts +6 -3
- package/scripts/{git → gh}/git-gh-issue-write.ts +4 -1
- package/scripts/{git → gh}/git-gh-pr-auto-merge.ts +4 -1
- package/scripts/{git → gh}/git-gh-pr-read.ts +24 -0
- package/scripts/{git → gh}/git-gh-pr-rest.ts +5 -2
- package/scripts/{git → gh}/git-gh-pr-review.ts +2 -2
- package/scripts/{git → gh}/git-gh-pr-rollup.ts +1 -3
- package/scripts/{git → gh}/git-gh-pr.ts +3 -1
- package/scripts/{git → gh}/git-pr-ai-review.ts +2 -5
- package/scripts/{git → gh}/git-pr-checks-eval.ts +0 -10
- package/scripts/{git → gh}/git-pr-checks-parse.ts +4 -20
- package/scripts/{git → gh}/git-pr-checks.ts +2 -1
- package/scripts/{git → gh}/git-pr-coderabbit.ts +2 -5
- package/scripts/{git → gh}/git-pr-confirmation.ts +1 -3
- package/scripts/{git → gh}/git-pr-managed-config.ts +2 -3
- package/scripts/{git → gh}/git-pr.ts +3 -3
- package/scripts/git/changed-file-scope.ts +0 -1
- package/scripts/git/git-branch.ts +3 -6
- package/scripts/git/git-command.ts +6 -1
- package/scripts/git/git-commit.ts +1 -1
- package/scripts/git/git-common-directory.ts +80 -24
- package/scripts/git/git-error.ts +66 -9
- package/scripts/git/git-issue.ts +1 -1
- package/scripts/git/git-preflight.ts +2 -2
- package/scripts/git/git-prompt.ts +6 -31
- package/scripts/git/git-push.ts +1 -1
- package/scripts/git/git-spawn-sync.ts +65 -0
- package/scripts/git/git-spawn.ts +5 -4
- package/scripts/git/git-staging.ts +1 -0
- package/scripts/git/git-status.ts +1 -1
- package/scripts/git/git-workflow.ts +1 -1
- package/scripts/git/gone-branch.ts +1 -0
- package/scripts/git/listing-cutoff.ts +1 -1
- package/scripts/git/main-merge.ts +1 -1
- package/scripts/git/main-sync.ts +19 -1
- package/scripts/git/parse-json-array.ts +0 -3
- package/scripts/git/repository-lock.ts +16 -16
- package/scripts/git/{git-stash.ts → stash/git-stash.ts} +1 -1
- package/scripts/git/{stash-pop-args.ts → stash/stash-pop-args.ts} +2 -7
- package/scripts/git/{stash-pop-cli.ts → stash/stash-pop-cli.ts} +0 -1
- package/scripts/git/{stash-sweep-lock.ts → stash/stash-sweep-lock.ts} +3 -3
- package/scripts/hooks/codex-hook-adapter.ts +0 -1
- package/scripts/hooks/edited-cspell.ts +1 -1
- package/scripts/hooks/format-edited-file.ts +2 -0
- package/scripts/hooks/pre-push-unit.ts +0 -4
- package/scripts/hooks/pretool-guard.ts +22 -4
- package/scripts/hooks/reserved-run.ts +1 -1
- package/scripts/hooks/step-zero-notice.ts +76 -0
- package/scripts/hooks/stop-guard.ts +47 -21
- package/scripts/init/claude-plugin-config.ts +4 -5
- package/scripts/init/hook-command-rewrite.ts +16 -8
- package/scripts/init/hook-launch.ts +41 -15
- package/scripts/init/init-basic.ts +2 -5
- package/scripts/init/init-file-action.ts +54 -0
- package/scripts/init/init-logic-migrate.ts +35 -17
- package/scripts/init/init-logic-secretlint.ts +1 -1
- package/scripts/init/init-logic.ts +10 -19
- package/scripts/init/init.ts +11 -71
- package/scripts/init/kit-written-paths.ts +2 -0
- package/scripts/init/project-profile.ts +9 -17
- package/scripts/init/start-exec.ts +1 -1
- package/scripts/init/start-setup-pr.ts +6 -6
- package/scripts/init/start-steps.ts +3 -1
- package/scripts/issue/behavior-change-lint.ts +0 -1
- package/scripts/issue/defect-rate-cli.ts +64 -23
- package/scripts/issue/defect-rate.ts +1 -2
- package/scripts/{git → issue}/git-next-issues.ts +3 -3
- package/scripts/issue/issue-backlinks-cli.ts +1 -5
- package/scripts/issue/issue-backlinks.ts +7 -14
- package/scripts/issue/issue-cite-cli.ts +2 -6
- package/scripts/issue/issue-cite.ts +12 -8
- package/scripts/issue/issue-classification.ts +1 -9
- package/scripts/issue/issue-closing-pr.ts +4 -4
- package/scripts/issue/issue-comment-cli.ts +2 -2
- package/scripts/issue/issue-file-cli.ts +5 -5
- package/scripts/issue/issue-file.ts +7 -14
- package/scripts/issue/issue-fold-cli.ts +1 -1
- package/scripts/issue/issue-fold-existing-cli.ts +1 -1
- package/scripts/{git → issue}/issue-labels.ts +8 -0
- package/scripts/issue/issue-lint-cli.ts +1 -1
- package/scripts/issue/issue-merged.ts +2 -2
- package/scripts/{git → issue}/issue-rank.ts +1 -1
- package/scripts/issue/issue-read-cli.ts +8 -47
- package/scripts/issue/issue-read.ts +0 -2
- package/scripts/issue/issue-report-failures.ts +80 -0
- package/scripts/issue/issue-scout-cli.ts +1 -7
- package/scripts/issue/issue-scout-closed.ts +2 -2
- package/scripts/issue/issue-scout.ts +0 -2
- package/scripts/issue/issue-state-cli.ts +5 -46
- package/scripts/issue/issue-state.ts +1 -5
- package/scripts/issue/markdown-section.ts +21 -6
- package/scripts/issue/measure-rerun-cli.ts +74 -15
- package/scripts/issue/reproduction-measure.ts +1 -1
- package/scripts/josh/cli-body.ts +1 -1
- package/scripts/josh/file-map-stamp.ts +0 -3
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-commands-ai.ts +9 -102
- package/scripts/josh/josh-commands-backlog.ts +5 -1
- package/scripts/josh/josh-commands-development.ts +8 -1
- package/scripts/josh/josh-commands-issue.ts +2 -2
- package/scripts/josh/josh-commands-maintenance.ts +7 -0
- package/scripts/josh/josh-commands-project.ts +7 -1
- package/scripts/josh/josh-commands-run.ts +114 -0
- package/scripts/josh/josh-commands-versioning.ts +8 -0
- package/scripts/josh/josh-commands-workflow.ts +3 -3
- package/scripts/josh/path-decision.ts +0 -3
- package/scripts/josh/process-owner.ts +16 -0
- package/scripts/josh/run-github-prerequisite.ts +3 -6
- package/scripts/josh/session-language-cli.ts +0 -1
- package/scripts/lane/lane-await.ts +46 -19
- package/scripts/lane/lane-capacity.ts +6 -3
- package/scripts/lane/lane-child-invocation.ts +20 -7
- package/scripts/lane/lane-cli.ts +1 -4
- package/scripts/lane/lane-close.ts +5 -4
- package/scripts/lane/lane-dispatch.ts +3 -4
- package/scripts/lane/lane-environment.ts +0 -9
- package/scripts/lane/lane-handoff.ts +38 -0
- package/scripts/lane/lane-launch-cli.ts +31 -27
- package/scripts/lane/lane-open.ts +0 -3
- package/scripts/lane/lane-output.ts +0 -1
- package/scripts/lane/lane-registry.ts +9 -0
- package/scripts/lane/openai-lane-ship.ts +2 -0
- package/scripts/lane/openai-lane-supervisor-decision.ts +2 -1
- package/scripts/lane/openai-lane-supervisor.ts +6 -4
- package/scripts/lane/openai-review-broker.ts +7 -1
- package/scripts/lib/bounded-pool.ts +1 -0
- package/scripts/lib/cli-flags.ts +31 -2
- package/scripts/lib/error-message.ts +12 -1
- package/scripts/lib/json-value.ts +5 -4
- package/scripts/lib/poll.ts +3 -1
- package/scripts/lib/timed-fetch.ts +15 -0
- package/scripts/lib/timeouts.ts +13 -0
- package/scripts/{yaml → lib}/yaml-document.ts +3 -1
- package/scripts/{bytes → lines}/bytes-command.ts +0 -2
- package/scripts/lines/line-targets.ts +0 -11
- package/scripts/lines/lines-command.ts +5 -4
- package/scripts/lint/lint-related-scope.ts +0 -1
- package/scripts/lint/lint-related.ts +1 -1
- package/scripts/{git → notify}/telegram-notify.ts +2 -2
- package/scripts/{git → notify}/telegram-test.ts +4 -4
- package/scripts/observations/observation-ledger-home.ts +0 -1
- package/scripts/observations/observation-ledger-line.ts +0 -1
- package/scripts/observations/observation-ledger-migrate.ts +1 -1
- package/scripts/observations/observations-flush-landing.ts +3 -3
- package/scripts/observations/observations-flush.ts +2 -2
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/package/package-scout-cli.ts +2 -7
- package/scripts/package/package-scout.ts +0 -2
- package/scripts/propagate/propagate-git-failure.ts +0 -1
- package/scripts/propagate/propagate-git.ts +3 -10
- package/scripts/propagate/propagate-publish.ts +2 -3
- package/scripts/propagate/propagate-run.ts +0 -2
- package/scripts/propagate/propagate-steps.ts +3 -5
- package/scripts/propagate/propagate-targets.ts +0 -4
- package/scripts/propagate/propagate.ts +0 -4
- package/scripts/refactor/import-graph.ts +0 -1
- package/scripts/refactor/refactor-lint.ts +0 -2
- package/scripts/refactor/refactor-scan.ts +2 -2
- package/scripts/refactor/refactor-targets.ts +0 -1
- package/scripts/registry-migration/migrate-logic.ts +28 -21
- package/scripts/registry-migration/migrate.ts +25 -14
- package/scripts/release/github-release-cli.ts +19 -6
- package/scripts/release/github-release-environment.ts +46 -0
- package/scripts/release/github-release.ts +151 -94
- package/scripts/release/release-history.ts +0 -1
- package/scripts/release/release-plan.ts +0 -1
- package/scripts/release/release-publish.ts +38 -5
- package/scripts/release/release-scope-cli.ts +1 -2
- package/scripts/release/release-tag.ts +2 -4
- package/scripts/release/release-worktree.ts +97 -2
- package/scripts/repo/auto-merge-setting-logic.ts +1 -4
- package/scripts/repo/repo-setting.ts +1 -1
- package/scripts/repo/repository-labels.ts +3 -3
- package/scripts/repo/required-checks-logic.ts +206 -0
- package/scripts/repo/required-checks-report.ts +71 -0
- package/scripts/repo/ruleset-check-cli.ts +53 -0
- package/scripts/repo/ruleset-check.ts +147 -0
- package/scripts/report/report-lint-cli.ts +0 -4
- package/scripts/review/disposition-cli.ts +1 -1
- package/scripts/review/live-evidence.ts +1 -1
- package/scripts/review/review-attest-cli.ts +0 -6
- package/scripts/review/review-attest.ts +4 -16
- package/scripts/review/review-brief-cli.ts +23 -6
- package/scripts/review/review-brief.ts +21 -8
- package/scripts/review/review-checkout.ts +3 -2
- package/scripts/review/review-diff-parts.ts +89 -0
- package/scripts/review/review-diff-write.ts +97 -0
- package/scripts/review/review-finding-ledger.ts +0 -1
- package/scripts/review/review-record-cli.ts +10 -17
- package/scripts/review/review-record.ts +0 -1
- package/scripts/review/review-round2-cli.ts +1 -1
- package/scripts/review/review-stamps.ts +0 -3
- package/scripts/review/review-tree.ts +14 -2
- package/scripts/rules/decision-oracle-stage.ts +18 -0
- package/scripts/rules/decision-oracle.ts +10 -7
- package/scripts/rules/delivered-rules.ts +9 -1
- package/scripts/rules/destructive-command.ts +119 -0
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +4 -3
- package/scripts/rules/file-body.ts +0 -3
- package/scripts/rules/filing-cap.ts +0 -2
- package/scripts/rules/gh-api.ts +7 -2
- package/scripts/rules/git-argv.ts +18 -7
- package/scripts/rules/git-force.ts +0 -2
- package/scripts/rules/implementation-cut.ts +0 -1
- package/scripts/rules/index-guard.ts +71 -0
- package/scripts/rules/issue-citation.ts +50 -25
- package/scripts/rules/lane-background.ts +15 -7
- package/scripts/rules/lane-park.ts +22 -6
- package/scripts/rules/last-prompt.ts +1 -0
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/oracle-firing.ts +1 -0
- package/scripts/rules/oracle-list-cli.ts +0 -3
- package/scripts/rules/permission-guards.ts +14 -0
- package/scripts/rules/protected-files.ts +74 -0
- package/scripts/rules/reply-language.ts +1 -1
- package/scripts/rules/rule-body-guard.ts +0 -3
- package/scripts/rules/rule-value-cli.ts +0 -1
- package/scripts/rules/shell-body-trigger.ts +0 -1
- package/scripts/rules/shell-segments.ts +0 -1
- package/scripts/rules/stop-rules.ts +20 -9
- package/scripts/rules/tail-commands.ts +1 -1
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +0 -1
- package/scripts/rules/worktree-guard.ts +26 -9
- package/scripts/run/run-carry-args.ts +8 -12
- package/scripts/run/run-carry-cli.ts +0 -2
- package/scripts/run/run-carry-stash.ts +2 -2
- package/scripts/run/run-carry-streak.ts +1 -1
- package/scripts/run/run-cut-args.ts +1 -6
- package/scripts/run/run-cut-cli.ts +0 -7
- package/scripts/run/run-cut.ts +8 -54
- package/scripts/run/run-ending-cli.ts +2 -6
- package/scripts/run/run-ending.ts +1 -4
- package/scripts/run/run-entry-cli.ts +133 -25
- package/scripts/run/run-entry-stop.ts +104 -0
- package/scripts/run/run-entry.ts +0 -1
- package/scripts/run/run-event-cli.ts +0 -2
- package/scripts/run/run-event-follow.ts +1 -0
- package/scripts/run/run-event-render.ts +1 -1
- package/scripts/run/run-event-scope.ts +53 -4
- package/scripts/run/run-event-stream-emit.ts +7 -2
- package/scripts/run/run-event-stream.ts +0 -3
- package/scripts/run/run-event-watch.ts +1 -0
- package/scripts/run/run-halfrun-resume.ts +20 -7
- package/scripts/run/run-headless.ts +2 -2
- package/scripts/run/run-hold-cli.ts +33 -10
- package/scripts/run/run-hold.ts +29 -6
- package/scripts/run/run-issue-number.ts +1 -1
- package/scripts/run/run-liveness-cli.ts +2 -6
- package/scripts/run/run-liveness.ts +2 -3
- package/scripts/run/run-merge-cli.ts +7 -23
- package/scripts/run/run-merge-steps.ts +5 -5
- package/scripts/run/run-merge.ts +1 -1
- package/scripts/run/run-next-cli.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +4 -2
- package/scripts/run/run-preflight.ts +2 -9
- package/scripts/run/run-prep-cli.ts +6 -3
- package/scripts/run/run-prep-locate.ts +1 -1
- package/scripts/run/run-progress-args.ts +20 -4
- package/scripts/run/run-progress-cli.ts +35 -36
- package/scripts/run/run-progress-clock.ts +10 -35
- package/scripts/run/run-progress-config.ts +0 -4
- package/scripts/run/run-progress-read.ts +1 -2
- package/scripts/run/run-progress.ts +1 -2
- package/scripts/run/run-prrun-resume.ts +117 -0
- package/scripts/run/run-report-cli.ts +1 -2
- package/scripts/run/run-report.ts +0 -1
- package/scripts/run/run-retrospective.ts +1 -1
- package/scripts/run/run-review-cli.ts +0 -3
- package/scripts/run/run-review-steps.ts +2 -0
- package/scripts/run/run-review.ts +0 -2
- package/scripts/run/run-ship-cli.ts +29 -19
- package/scripts/run/run-ship-detach.ts +1 -0
- package/scripts/run/run-ship-next.ts +106 -0
- package/scripts/run/run-ship-preflight.ts +1 -1
- package/scripts/run/run-ship-probe.ts +4 -1
- package/scripts/run/run-ship-return.ts +8 -3
- package/scripts/run/run-ship-review-steps.ts +18 -4
- package/scripts/run/run-ship-review.ts +11 -0
- package/scripts/run/run-ship-scoped.ts +1 -0
- package/scripts/run/run-ship-steps.ts +1 -1
- package/scripts/run/run-ship.ts +0 -1
- package/scripts/run/run-stage-read.ts +62 -0
- package/scripts/run/run-stage.ts +160 -0
- package/scripts/run/run-status-cli.ts +3 -16
- package/scripts/run/run-step-cli.ts +21 -14
- package/scripts/run/run-step.ts +1 -1
- package/scripts/run/run-stop-notify.ts +1 -3
- package/scripts/run/run-stranded-cli.ts +0 -4
- package/scripts/run/run-stranded-detect.ts +2 -2
- package/scripts/run/run-tail-cli.ts +17 -11
- package/scripts/run/run-tail.ts +2 -0
- package/scripts/run/run-tidy-lanes.ts +6 -2
- package/scripts/run/run-tidy-stashes.ts +12 -12
- package/scripts/run/run-wake-cli.ts +4 -13
- package/scripts/run/run-wake-loop.ts +2 -2
- package/scripts/run/run-wake.ts +2 -4
- package/scripts/run/run-watcher-guard-cli.ts +0 -4
- package/scripts/run/run-watcher-guard.ts +18 -12
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/safe-chain/ci-installer-pin.ts +11 -5
- package/scripts/safe-chain/pack-hook.ts +92 -0
- package/scripts/safe-chain/project-config.ts +1 -1
- package/scripts/security/security-audit-provision-logic.ts +0 -1
- package/scripts/security/security-audit-provision.ts +3 -2
- package/scripts/security/security-audit.ts +0 -1
- package/scripts/security/security-updates-logic.ts +0 -1
- package/scripts/sonar/sonar-hotspots-cli.ts +10 -52
- package/scripts/sonar/sonar-hotspots.ts +0 -1
- package/scripts/sonar/sonar-new-code-cli.ts +150 -0
- package/scripts/sonar/sonar-new-code.ts +79 -0
- package/scripts/sonar/sonar-project.ts +97 -0
- package/scripts/split/split-assess-cli.ts +1 -1
- package/scripts/split/split-assess.ts +2 -5
- package/scripts/sync/managed-config-scope-cli.ts +1 -1
- package/scripts/sync/skill-migration.ts +0 -1
- package/scripts/sync/sync-ai-files.ts +4 -0
- package/scripts/sync/sync-hook-safety.ts +2 -3
- package/scripts/sync/sync.ts +1 -8
- package/scripts/sync/template-source-logic.ts +0 -1
- package/scripts/sync/workflow-pin-logic.ts +16 -28
- package/scripts/test/test-declared-changed.ts +13 -16
- package/scripts/test/test-declared-logic.ts +0 -1
- package/scripts/test/test-declared.ts +3 -9
- package/scripts/test/test-red-commit.ts +2 -2
- package/scripts/test/test-red.ts +14 -7
- package/scripts/test/test-type-logic.ts +1 -1
- package/scripts/test/unit-worker-share.ts +7 -16
- package/scripts/time-runtime/time-batch-guard.ts +1 -2
- package/scripts/time-runtime/time-bundle-call.ts +0 -1
- package/scripts/time-runtime/time-bundles.ts +0 -4
- package/scripts/time-runtime/time-density-hook.ts +0 -2
- package/scripts/time-runtime/time-followup-stage.ts +1 -1
- package/scripts/time-runtime/time-format.ts +0 -73
- package/scripts/time-runtime/time-round-trips.ts +0 -4
- package/scripts/time-runtime/time-single-check.ts +1 -1
- package/scripts/time-runtime/time-spans.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +1 -2
- package/scripts/ui/ui-routes-cli.ts +0 -5
- package/scripts/ui/ui-routes.ts +0 -5
- package/scripts/version/effective-upstream.ts +2 -2
- package/scripts/version/latest-corepack.ts +7 -9
- package/scripts/version/latest-scope-cli.ts +0 -2
- package/scripts/version/latest-update.ts +0 -2
- package/scripts/version/npm-registry.ts +2 -2
- package/scripts/version/publishable-range-check.ts +1 -1
- package/scripts/version/release-hold.ts +0 -4
- package/scripts/version/running-binary.ts +2 -2
- package/scripts/version/version-check-logic.ts +0 -2
- package/scripts/version/version-commands.ts +11 -1
- package/scripts/version/version-remote.ts +3 -3
- package/scripts/version/version-targets.ts +4 -4
- package/templates/CLAUDE.basic.md +6 -6
- package/templates/sonar-project.properties +28 -1
- package/templates/workflows/ci.yml +16 -82
- package/templates/workflows/dependabot-auto-merge.yml +6 -3
- package/templates/workflows/github-release.yml +79 -0
- package/tsconfig.sonar.json +4 -22
- package/prompts/collaboration-workflow/cross-repo-epic.md +0 -5
- package/prompts/collaboration-workflow/epic-audit.md +0 -5
- package/prompts/collaboration-workflow/epic-bundle.md +0 -5
- package/scripts/init/plugin-install-hint.ts +0 -47
- package/scripts/version/parse-json.ts +0 -13
- /package/scripts/{git → followup}/git-followup-stages.ts +0 -0
- /package/scripts/{git → gh}/git-gh-api-path.ts +0 -0
- /package/scripts/{git → gh}/git-gh-command.ts +0 -0
- /package/scripts/{git → gh}/git-gh-issue.ts +0 -0
- /package/scripts/{git → gh}/git-gh-pr-snapshot.ts +0 -0
- /package/scripts/{git → gh}/git-gh-reachability.ts +0 -0
- /package/scripts/{git → gh}/git-gh-repo.ts +0 -0
- /package/scripts/{git → gh}/git-gh-rest-state.ts +0 -0
- /package/scripts/{git → gh}/git-pr-checks-watch.ts +0 -0
- /package/scripts/{git → gh}/git-pr-error.ts +0 -0
- /package/scripts/{git → gh}/git-pr-messages.ts +0 -0
- /package/scripts/{git → gh}/github-issue-url.ts +0 -0
- /package/scripts/git/{stash-orphans.ts → stash/stash-orphans.ts} +0 -0
- /package/scripts/{git → issue}/issue-reference.ts +0 -0
- /package/scripts/{git → lib}/animation-helpers.ts +0 -0
- /package/scripts/{git → lib}/git-animation.ts +0 -0
- /package/scripts/{git → notify}/git-notify.ts +0 -0
- /package/scripts/{git → notify}/telegram-test-logic.ts +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { yaml_document } from '#scripts/lib/yaml-document'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
// The status checks kit's distributed workflows report, keyed by the workflow that reports them —
|
|
5
|
+
// the single source `josh ruleset:check` and `josh doctor` compare a repository's required status
|
|
6
|
+
// checks against (joshuafolkken/kit#3012). Each name is the job's `name:`, the string a ruleset's
|
|
7
|
+
// required check is matched by; `required-checks-logic.test.ts` reads it back off the workflows.
|
|
8
|
+
//
|
|
9
|
+
// `E2E` belongs here even though `josh followup` deliberately does not wait on it
|
|
10
|
+
// (joshuafolkken/kit#991): a ruleset counts a skipped job as passing, and the job is skipped when the
|
|
11
|
+
// project has no E2E suite, so requiring it never blocks a merge — it only refuses a red one.
|
|
12
|
+
interface WorkflowChecks {
|
|
13
|
+
workflow: string
|
|
14
|
+
checks: ReadonlyArray<string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const CI_WORKFLOW = '.github/workflows/ci.yml'
|
|
18
|
+
const SONAR_WORKFLOW = '.github/workflows/sonar-qube.yml'
|
|
19
|
+
const CLASSIFICATION_WORKFLOW = '.github/workflows/pr-classification.yml'
|
|
20
|
+
const REQUIRED_STATUS_CHECKS = 'required_status_checks'
|
|
21
|
+
const NOT_FOUND_STATUS = 404
|
|
22
|
+
|
|
23
|
+
const DISTRIBUTED_CHECKS: ReadonlyArray<WorkflowChecks> = [
|
|
24
|
+
{ workflow: CI_WORKFLOW, checks: ['Checks', 'Detect E2E', 'E2E', 'Security Audit'] },
|
|
25
|
+
{ workflow: SONAR_WORKFLOW, checks: ['SonarQube'] },
|
|
26
|
+
{ workflow: CLASSIFICATION_WORKFLOW, checks: ['Release classification'] },
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
// Where the repository's required checks live. A ruleset is the source `--apply` writes to; classic
|
|
30
|
+
// branch protection is read beside it, and is the source for a repository that never moved to rulesets.
|
|
31
|
+
type RequiredSource =
|
|
32
|
+
| { kind: 'ruleset'; ruleset_id: number; contexts: ReadonlyArray<string> }
|
|
33
|
+
| { kind: 'protection'; contexts: ReadonlyArray<string> }
|
|
34
|
+
| { kind: 'none' }
|
|
35
|
+
| { kind: 'unreadable' }
|
|
36
|
+
|
|
37
|
+
const status_check_schema = z.looseObject({ context: z.string() })
|
|
38
|
+
const branch_rule_schema = z.looseObject({
|
|
39
|
+
type: z.literal(REQUIRED_STATUS_CHECKS),
|
|
40
|
+
ruleset_id: z.number(),
|
|
41
|
+
parameters: z.looseObject({ required_status_checks: z.array(status_check_schema) }),
|
|
42
|
+
})
|
|
43
|
+
const protection_schema = z.looseObject({ contexts: z.array(z.string()) })
|
|
44
|
+
const ruleset_rule_schema = z.looseObject({
|
|
45
|
+
type: z.string(),
|
|
46
|
+
parameters: z
|
|
47
|
+
.looseObject({ required_status_checks: z.array(status_check_schema).optional() })
|
|
48
|
+
.optional(),
|
|
49
|
+
})
|
|
50
|
+
const ruleset_schema = z.looseObject({ rules: z.array(ruleset_rule_schema) })
|
|
51
|
+
const workflow_job_schema = z.looseObject({ name: z.string().optional() })
|
|
52
|
+
const workflow_schema = z.looseObject({ jobs: z.record(z.string(), workflow_job_schema) })
|
|
53
|
+
|
|
54
|
+
// One `gh api` read: the body it answered, or the HTTP status it failed with (`undefined` when
|
|
55
|
+
// nothing answered at all).
|
|
56
|
+
type ApiRead = { kind: 'body'; stdout: string } | { kind: 'failed'; status: number | undefined }
|
|
57
|
+
|
|
58
|
+
type BranchRule = z.infer<typeof branch_rule_schema>
|
|
59
|
+
type RulesetRule = z.infer<typeof ruleset_rule_schema>
|
|
60
|
+
|
|
61
|
+
function parse_yaml_safe(content: string): unknown {
|
|
62
|
+
try {
|
|
63
|
+
return yaml_document.parse_yaml(content)
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The job `name:`s a workflow defines — the contexts it reports. A file that does not parse, or has
|
|
70
|
+
// no `jobs` mapping, reports nothing.
|
|
71
|
+
function workflow_job_names(content: string): Array<string> {
|
|
72
|
+
const parsed = workflow_schema.safeParse(parse_yaml_safe(content))
|
|
73
|
+
if (!parsed.success) return []
|
|
74
|
+
|
|
75
|
+
return Object.values(parsed.data.jobs).flatMap((job) =>
|
|
76
|
+
job.name === undefined ? [] : [job.name],
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The checks this repository should require: only those a workflow it has actually reports. A file
|
|
81
|
+
// of the same name is not enough — a project's own `ci.yml` with a `build` job is never told to
|
|
82
|
+
// require `Checks`, which nothing there would ever report.
|
|
83
|
+
function expected_checks(read_workflow: (workflow: string) => string | undefined): Array<string> {
|
|
84
|
+
return DISTRIBUTED_CHECKS.flatMap((entry) => {
|
|
85
|
+
const content = read_workflow(entry.workflow)
|
|
86
|
+
if (content === undefined) return []
|
|
87
|
+
const reported = workflow_job_names(content)
|
|
88
|
+
|
|
89
|
+
return entry.checks.filter((check) => reported.includes(check))
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parse_json(stdout: string): unknown {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(stdout)
|
|
96
|
+
} catch {
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function status_check_rules(payload: unknown): Array<BranchRule> {
|
|
102
|
+
if (!Array.isArray(payload)) return []
|
|
103
|
+
|
|
104
|
+
return payload.flatMap((entry: unknown) => {
|
|
105
|
+
const parsed = branch_rule_schema.safeParse(entry)
|
|
106
|
+
|
|
107
|
+
return parsed.success ? [parsed.data] : []
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// `GET repos/{owner}/{repo}/rules/branches/{branch}` — every rule in force on the branch, across
|
|
112
|
+
// rulesets. The contexts of all of them are combined, since each is enforced; the first ruleset is the
|
|
113
|
+
// one `--apply` writes to. No `required_status_checks` rule at all reads as `none`.
|
|
114
|
+
function parse_branch_rules(stdout: string | undefined): RequiredSource {
|
|
115
|
+
const payload = stdout === undefined ? undefined : parse_json(stdout)
|
|
116
|
+
if (!Array.isArray(payload)) return { kind: 'unreadable' }
|
|
117
|
+
const rules = status_check_rules(payload)
|
|
118
|
+
const [first] = rules
|
|
119
|
+
if (first === undefined) return { kind: 'none' }
|
|
120
|
+
const contexts = rules.flatMap((rule) =>
|
|
121
|
+
rule.parameters.required_status_checks.map((check) => check.context),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return { kind: 'ruleset', ruleset_id: first.ruleset_id, contexts }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// `GET …/branches/{branch}/protection/required_status_checks`. GitHub answers a branch without
|
|
128
|
+
// protection (or without required checks) with a 404, which alone reads as `none`. Any other
|
|
129
|
+
// failure — a 403 for a caller without admin access, a 5xx, a timeout — or a body that is not the
|
|
130
|
+
// expected document reads as `unreadable`, so a check that is in fact required is never reported
|
|
131
|
+
// missing.
|
|
132
|
+
function parse_protection(read: ApiRead): RequiredSource {
|
|
133
|
+
if (read.kind === 'failed') {
|
|
134
|
+
return { kind: read.status === NOT_FOUND_STATUS ? 'none' : 'unreadable' }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const parsed = protection_schema.safeParse(parse_json(read.stdout))
|
|
138
|
+
|
|
139
|
+
return parsed.success
|
|
140
|
+
? { kind: 'protection', contexts: parsed.data.contexts }
|
|
141
|
+
: { kind: 'unreadable' }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// GitHub enforces rulesets and classic protection together, so a check either one requires is
|
|
145
|
+
// required. The ruleset stays the source `--apply` writes to; a protection read that failed for any
|
|
146
|
+
// reason but a 404 leaves the whole answer `unreadable`, as a lone protection read would.
|
|
147
|
+
function combine_sources(rules: RequiredSource, protection: RequiredSource): RequiredSource {
|
|
148
|
+
if (rules.kind !== 'ruleset') return protection
|
|
149
|
+
if (protection.kind === 'none') return rules
|
|
150
|
+
if (protection.kind !== 'protection') return { kind: 'unreadable' }
|
|
151
|
+
|
|
152
|
+
return { ...rules, contexts: [...new Set([...rules.contexts, ...protection.contexts])] }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function missing_checks(
|
|
156
|
+
expected: ReadonlyArray<string>,
|
|
157
|
+
contexts: ReadonlyArray<string>,
|
|
158
|
+
): Array<string> {
|
|
159
|
+
return expected.filter((check) => !contexts.includes(check))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function with_added_checks(rule: RulesetRule, missing: ReadonlyArray<string>): RulesetRule {
|
|
163
|
+
if (rule.type !== REQUIRED_STATUS_CHECKS || rule.parameters === undefined) return rule
|
|
164
|
+
const current = rule.parameters.required_status_checks ?? []
|
|
165
|
+
const added = missing.map((context) => ({ context }))
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...rule,
|
|
169
|
+
parameters: { ...rule.parameters, required_status_checks: [...current, ...added] },
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The `PUT repos/{owner}/{repo}/rulesets/{id}` body: the ruleset's own rules, unchanged but for the
|
|
174
|
+
// missing contexts appended to its required-checks rule. `rules` replaces the whole list, so every
|
|
175
|
+
// other rule is carried over as read rather than dropped.
|
|
176
|
+
function ruleset_update_body(
|
|
177
|
+
ruleset_stdout: string,
|
|
178
|
+
missing: ReadonlyArray<string>,
|
|
179
|
+
): string | undefined {
|
|
180
|
+
const parsed = ruleset_schema.safeParse(parse_json(ruleset_stdout))
|
|
181
|
+
if (!parsed.success) return undefined
|
|
182
|
+
|
|
183
|
+
return JSON.stringify({
|
|
184
|
+
rules: parsed.data.rules.map((rule) => with_added_checks(rule, missing)),
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The `POST …/protection/required_status_checks/contexts` body, which appends rather than replaces.
|
|
189
|
+
function protection_update_body(missing: ReadonlyArray<string>): string {
|
|
190
|
+
return JSON.stringify({ contexts: missing })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const required_checks_logic = {
|
|
194
|
+
DISTRIBUTED_CHECKS,
|
|
195
|
+
workflow_job_names,
|
|
196
|
+
expected_checks,
|
|
197
|
+
parse_branch_rules,
|
|
198
|
+
parse_protection,
|
|
199
|
+
combine_sources,
|
|
200
|
+
missing_checks,
|
|
201
|
+
ruleset_update_body,
|
|
202
|
+
protection_update_body,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type { ApiRead, RequiredSource, WorkflowChecks }
|
|
206
|
+
export { required_checks_logic }
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { repo_setting } from './repo-setting'
|
|
2
|
+
import type { RequiredSource } from './required-checks-logic'
|
|
3
|
+
|
|
4
|
+
// What one read of a repository's required status checks found, against what kit's distributed
|
|
5
|
+
// workflows report (joshuafolkken/kit#3012).
|
|
6
|
+
interface RequiredChecksReport {
|
|
7
|
+
repo: string | undefined
|
|
8
|
+
branch: string | undefined
|
|
9
|
+
source: RequiredSource
|
|
10
|
+
expected: ReadonlyArray<string>
|
|
11
|
+
missing: ReadonlyArray<string>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const LABEL = 'Required status checks'
|
|
15
|
+
const APPLY_COMMAND = 'pnpm josh ruleset:check --apply'
|
|
16
|
+
|
|
17
|
+
function source_label(source: RequiredSource): string {
|
|
18
|
+
return source.kind === 'ruleset' ? `ruleset ${String(source.ruleset_id)}` : 'branch protection'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// An unreadable answer is never reported as a missing one — the same rule every repository-setting
|
|
22
|
+
// report follows — so it names the possible causes and no remediation.
|
|
23
|
+
function format_unreadable(report: RequiredChecksReport): Array<string> {
|
|
24
|
+
return [
|
|
25
|
+
` ⚠ ${LABEL}: could not be read (${repo_setting.report_target(report.repo)}) — not checked, not necessarily missing`,
|
|
26
|
+
' `gh` is missing or unauthenticated, or the request failed (offline, timed out).',
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// No ruleset or protection requires anything. `--apply` adds to an existing rule and never creates
|
|
31
|
+
// one — which branches a ruleset targets and who may bypass it are the maintainer's choices.
|
|
32
|
+
function format_none(report: RequiredChecksReport, target: string): Array<string> {
|
|
33
|
+
return [
|
|
34
|
+
` ⚠ ${LABEL}: none required on ${String(report.branch)} (${target})`,
|
|
35
|
+
` Expected: ${report.expected.join(', ')}`,
|
|
36
|
+
' Create a branch ruleset (Settings → Rules → Rulesets) that requires them.',
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function format_found(report: RequiredChecksReport, target: string): Array<string> {
|
|
41
|
+
const where = `${String(report.branch)} (${target}, ${source_label(report.source)})`
|
|
42
|
+
|
|
43
|
+
if (report.missing.length === 0) {
|
|
44
|
+
return [` ✔ ${LABEL}: all ${String(report.expected.length)} kit checks required on ${where}`]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
` ⚠ ${LABEL}: ${report.missing.join(', ')} not required on ${where}`,
|
|
49
|
+
` Add: ${APPLY_COMMAND}`,
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function format_report(report: RequiredChecksReport): Array<string> {
|
|
54
|
+
const target = repo_setting.report_target(report.repo)
|
|
55
|
+
if (report.source.kind === 'unreadable') return format_unreadable(report)
|
|
56
|
+
if (report.source.kind === 'none') return format_none(report, target)
|
|
57
|
+
|
|
58
|
+
return format_found(report, target)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Whether nothing is left to do: the checks were read and every expected one is required.
|
|
62
|
+
function is_complete(report: RequiredChecksReport): boolean {
|
|
63
|
+
const is_read = report.source.kind === 'ruleset' || report.source.kind === 'protection'
|
|
64
|
+
|
|
65
|
+
return is_read && report.missing.length === 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const required_checks_report = { format_report, is_complete }
|
|
69
|
+
|
|
70
|
+
export type { RequiredChecksReport }
|
|
71
|
+
export { required_checks_report }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { gh_spawn } from '#scripts/gh/gh-spawn'
|
|
4
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
|
+
import { repo_setting } from './repo-setting'
|
|
6
|
+
import { required_checks_report, type RequiredChecksReport } from './required-checks-report'
|
|
7
|
+
import { ruleset_check } from './ruleset-check'
|
|
8
|
+
|
|
9
|
+
// `josh ruleset:check [--apply]` — say whether the default branch requires every status check kit's
|
|
10
|
+
// distributed workflows report, and with `--apply` add the missing ones (joshuafolkken/kit#3012).
|
|
11
|
+
// Changing a repository setting is outward-facing, so the write happens only on the flag a person
|
|
12
|
+
// typed; `josh sync` and `josh doctor` never pass it. Exits non-zero while anything is left to do.
|
|
13
|
+
|
|
14
|
+
const ARGV_OFFSET = 2
|
|
15
|
+
const APPLY_FLAG = '--apply'
|
|
16
|
+
const FAILURE_EXIT_CODE = 1
|
|
17
|
+
|
|
18
|
+
function exit_code_for(report: RequiredChecksReport): number {
|
|
19
|
+
return required_checks_report.is_complete(report) ? 0 : FAILURE_EXIT_CODE
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function apply(report: RequiredChecksReport): number {
|
|
23
|
+
try {
|
|
24
|
+
if (!ruleset_check.apply_missing(report)) return FAILURE_EXIT_CODE
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error(`Could not add ${report.missing.join(', ')}: ${String(error)}`)
|
|
27
|
+
|
|
28
|
+
return FAILURE_EXIT_CODE
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.info(` ✔ Added: ${report.missing.join(', ')}`)
|
|
32
|
+
|
|
33
|
+
return 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function run(argv: ReadonlyArray<string>): number {
|
|
37
|
+
const report = ruleset_check.inspect(gh_spawn.get_repo_name_with_owner(), PROJECT_ROOT)
|
|
38
|
+
|
|
39
|
+
repo_setting.print_section(required_checks_report.format_report(report))
|
|
40
|
+
const is_writable = argv.includes(APPLY_FLAG) && report.missing.length > 0
|
|
41
|
+
|
|
42
|
+
return is_writable ? apply(report) : exit_code_for(report)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function main(argv: ReadonlyArray<string>): void {
|
|
46
|
+
process.exitCode = run(argv)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ruleset_check_cli = { APPLY_FLAG, run }
|
|
50
|
+
|
|
51
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET))
|
|
52
|
+
|
|
53
|
+
export { ruleset_check_cli }
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
3
|
+
import { gh_failure } from '#scripts/gh/git-gh-failure'
|
|
4
|
+
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
|
+
import { file_reader } from '#scripts/lib/read-file'
|
|
6
|
+
import { repo_setting } from './repo-setting'
|
|
7
|
+
import { required_checks_logic, type ApiRead, type RequiredSource } from './required-checks-logic'
|
|
8
|
+
import { required_checks_report, type RequiredChecksReport } from './required-checks-report'
|
|
9
|
+
|
|
10
|
+
// Read a repository's required status checks on its default branch and compare them with the checks
|
|
11
|
+
// kit's distributed workflows report (joshuafolkken/kit#3012). Every read degrades to `unreadable`
|
|
12
|
+
// rather than throwing; the one write, `apply_missing`, runs only on `josh ruleset:check --apply`.
|
|
13
|
+
|
|
14
|
+
function read_api(api_path: string): string | undefined {
|
|
15
|
+
return git_gh_exec.read_gh_api_sync({
|
|
16
|
+
path: api_path,
|
|
17
|
+
cwd: PROJECT_ROOT,
|
|
18
|
+
timeout_ms: repo_setting.GH_TIMEOUT_MS,
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function read_default_branch(repo: string): string | undefined {
|
|
23
|
+
const branch = git_gh_exec
|
|
24
|
+
.read_gh_api_sync({
|
|
25
|
+
path: `repos/${repo}`,
|
|
26
|
+
jq_filter: '.default_branch',
|
|
27
|
+
cwd: PROJECT_ROOT,
|
|
28
|
+
timeout_ms: repo_setting.GH_TIMEOUT_MS,
|
|
29
|
+
})
|
|
30
|
+
?.trim()
|
|
31
|
+
|
|
32
|
+
return branch === '' ? undefined : branch
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Unlike `read_api`, keeps the failure's HTTP status: a 404 here means "no protection", which is an
|
|
36
|
+
// answer, while any other failure is not.
|
|
37
|
+
function read_protection(repo: string, branch: string): ApiRead {
|
|
38
|
+
try {
|
|
39
|
+
const stdout = git_gh_exec.exec_gh_api_sync({
|
|
40
|
+
path: `repos/${repo}/branches/${branch}/protection/required_status_checks`,
|
|
41
|
+
cwd: PROJECT_ROOT,
|
|
42
|
+
timeout_ms: repo_setting.GH_TIMEOUT_MS,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return { kind: 'body', stdout }
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return { kind: 'failed', status: gh_failure.failure_of(error)?.status }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Rulesets and classic branch protection both, since GitHub enforces both; unreadable rules stop
|
|
52
|
+
// there rather than letting protection alone pass for the whole answer.
|
|
53
|
+
function read_source(repo: string, branch: string): RequiredSource {
|
|
54
|
+
const from_rules = required_checks_logic.parse_branch_rules(
|
|
55
|
+
read_api(`repos/${repo}/rules/branches/${branch}`),
|
|
56
|
+
)
|
|
57
|
+
if (from_rules.kind === 'unreadable') return from_rules
|
|
58
|
+
const from_protection = required_checks_logic.parse_protection(read_protection(repo, branch))
|
|
59
|
+
|
|
60
|
+
return required_checks_logic.combine_sources(from_rules, from_protection)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The checks `root` should require, decided by the jobs its distributed workflows define on disk.
|
|
64
|
+
function expected_in(root: string): Array<string> {
|
|
65
|
+
return required_checks_logic.expected_checks((workflow) =>
|
|
66
|
+
file_reader.read_if_readable(path.join(root, workflow)),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function contexts_of(source: RequiredSource): ReadonlyArray<string> {
|
|
71
|
+
return source.kind === 'ruleset' || source.kind === 'protection' ? source.contexts : []
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function inspect(repo: string | undefined, root: string): RequiredChecksReport {
|
|
75
|
+
const expected = expected_in(root)
|
|
76
|
+
const branch = repo === undefined ? undefined : read_default_branch(repo)
|
|
77
|
+
const source: RequiredSource =
|
|
78
|
+
repo === undefined || branch === undefined ? { kind: 'unreadable' } : read_source(repo, branch)
|
|
79
|
+
const missing =
|
|
80
|
+
source.kind === 'unreadable'
|
|
81
|
+
? []
|
|
82
|
+
: required_checks_logic.missing_checks(expected, contexts_of(source))
|
|
83
|
+
|
|
84
|
+
return { repo, branch, source, expected, missing }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function apply_to_ruleset(repo: string, ruleset_id: number, missing: ReadonlyArray<string>): void {
|
|
88
|
+
const api_path = `repos/${repo}/rulesets/${String(ruleset_id)}`
|
|
89
|
+
const current = git_gh_exec.exec_gh_api_sync({ path: api_path, cwd: PROJECT_ROOT })
|
|
90
|
+
const body = required_checks_logic.ruleset_update_body(current, missing)
|
|
91
|
+
if (body === undefined) throw new Error(`Unexpected ruleset response from ${api_path}`)
|
|
92
|
+
|
|
93
|
+
git_gh_exec.exec_gh_api_sync({ path: api_path, method: 'PUT', body, cwd: PROJECT_ROOT })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function apply_to_protection(repo: string, branch: string, missing: ReadonlyArray<string>): void {
|
|
97
|
+
git_gh_exec.exec_gh_api_sync({
|
|
98
|
+
path: `repos/${repo}/branches/${branch}/protection/required_status_checks/contexts`,
|
|
99
|
+
method: 'POST',
|
|
100
|
+
body: required_checks_logic.protection_update_body(missing),
|
|
101
|
+
cwd: PROJECT_ROOT,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function apply_to_source(
|
|
106
|
+
repo: string,
|
|
107
|
+
branch: string,
|
|
108
|
+
source: RequiredSource,
|
|
109
|
+
missing: ReadonlyArray<string>,
|
|
110
|
+
): boolean {
|
|
111
|
+
if (source.kind === 'ruleset') {
|
|
112
|
+
apply_to_ruleset(repo, source.ruleset_id, missing)
|
|
113
|
+
|
|
114
|
+
return true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (source.kind !== 'protection') return false
|
|
118
|
+
apply_to_protection(repo, branch, missing)
|
|
119
|
+
|
|
120
|
+
return true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The one write: append the missing checks to the rule that already requires checks. A repository
|
|
124
|
+
// with neither a ruleset rule nor protection gets nothing created for it, which `format_report`
|
|
125
|
+
// explains. Throws on a failed request so the caller can report it; `false` when nothing was written.
|
|
126
|
+
function apply_missing(report: RequiredChecksReport): boolean {
|
|
127
|
+
const { repo, branch, source, missing } = report
|
|
128
|
+
if (repo === undefined || branch === undefined || missing.length === 0) return false
|
|
129
|
+
|
|
130
|
+
return apply_to_source(repo, branch, source, missing)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// The `josh doctor` section: read, never write.
|
|
134
|
+
function report_required_checks_section(
|
|
135
|
+
repo: string | undefined,
|
|
136
|
+
root: string,
|
|
137
|
+
): RequiredChecksReport {
|
|
138
|
+
const report = inspect(repo, root)
|
|
139
|
+
|
|
140
|
+
repo_setting.print_section(required_checks_report.format_report(report))
|
|
141
|
+
|
|
142
|
+
return report
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const ruleset_check = { expected_in, inspect, apply_missing, report_required_checks_section }
|
|
146
|
+
|
|
147
|
+
export { ruleset_check }
|
|
@@ -37,7 +37,7 @@ function main(argv: ReadonlyArray<string>): void {
|
|
|
37
37
|
process.exitCode = run(argv)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const disposition_cli = { USAGE,
|
|
40
|
+
const disposition_cli = { USAGE, reason_line, run }
|
|
41
41
|
|
|
42
42
|
if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET))
|
|
43
43
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
1
2
|
import { git_command } from '#scripts/git/git-command'
|
|
2
|
-
import { git_gh_command } from '#scripts/git/git-gh-command'
|
|
3
3
|
import { reproduction_measure } from '#scripts/issue/reproduction-measure'
|
|
4
4
|
import { test_declared_logic, type Verdict } from '#scripts/test/test-declared-logic'
|
|
5
5
|
|
|
@@ -101,13 +101,7 @@ async function main(argv: ReadonlyArray<string>): Promise<void> {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
const review_attest_cli = {
|
|
104
|
-
CHECK_FLAG,
|
|
105
|
-
main,
|
|
106
|
-
NOT_REQUIRED_LINE,
|
|
107
104
|
run,
|
|
108
|
-
run_check,
|
|
109
|
-
UNKNOWN_NONCE_LINE,
|
|
110
|
-
USAGE,
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|
|
@@ -3,6 +3,7 @@ import { lstatSync } from 'node:fs'
|
|
|
3
3
|
import { git_command } from '#scripts/git/git-command'
|
|
4
4
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
5
5
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import { json_value } from '#scripts/lib/json-value'
|
|
6
7
|
import { review_checkout, type ReviewCheckout } from './review-checkout'
|
|
7
8
|
|
|
8
9
|
// The record that says which checkout a `/code-review` actually read (joshuafolkken/kit#1522).
|
|
@@ -81,17 +82,7 @@ function attest_path(nonce: string): string {
|
|
|
81
82
|
function read_json(source: string): unknown {
|
|
82
83
|
const raw = stamp_file.read_stamp_text(source)
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
return JSON.parse(raw)
|
|
88
|
-
} catch {
|
|
89
|
-
return undefined
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function is_record(value: unknown): value is Record<string, unknown> {
|
|
94
|
-
return typeof value === 'object' && value !== null
|
|
85
|
+
return raw === undefined ? undefined : json_value.parse_or_undefined(raw)
|
|
95
86
|
}
|
|
96
87
|
|
|
97
88
|
function string_field(value: Record<string, unknown>, key: string): string | undefined {
|
|
@@ -101,7 +92,7 @@ function string_field(value: Record<string, unknown>, key: string): string | und
|
|
|
101
92
|
}
|
|
102
93
|
|
|
103
94
|
function to_checkout(value: unknown): ReviewCheckout | undefined {
|
|
104
|
-
if (!is_record(value)) return undefined
|
|
95
|
+
if (!json_value.is_record(value)) return undefined
|
|
105
96
|
|
|
106
97
|
const root = string_field(value, 'root')
|
|
107
98
|
const branch = string_field(value, 'branch')
|
|
@@ -113,7 +104,7 @@ function to_checkout(value: unknown): ReviewCheckout | undefined {
|
|
|
113
104
|
}
|
|
114
105
|
|
|
115
106
|
function to_pointer(value: unknown): PointerRecord | undefined {
|
|
116
|
-
if (!is_record(value)) return undefined
|
|
107
|
+
if (!json_value.is_record(value)) return undefined
|
|
117
108
|
|
|
118
109
|
const nonce = string_field(value, 'nonce')
|
|
119
110
|
const taken_at = string_field(value, 'taken_at')
|
|
@@ -336,20 +327,17 @@ function refusal_message(verdict: AttestVerdict): string {
|
|
|
336
327
|
}
|
|
337
328
|
|
|
338
329
|
const review_attest = {
|
|
339
|
-
ATTEST_PREFIX,
|
|
340
330
|
attest,
|
|
341
331
|
attest_path,
|
|
342
332
|
check,
|
|
343
333
|
check_here,
|
|
344
334
|
clear,
|
|
345
335
|
clear_here,
|
|
346
|
-
EXPECT_PREFIX,
|
|
347
336
|
expect_path,
|
|
348
337
|
is_briefed_since,
|
|
349
338
|
is_reviewed_on,
|
|
350
339
|
MISMATCH_VERDICT,
|
|
351
340
|
MISSING_REASON,
|
|
352
|
-
POINTER_PREFIX,
|
|
353
341
|
pointer_path,
|
|
354
342
|
read_attestation,
|
|
355
343
|
read_expectation,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
5
|
+
import { bash_output_cap_reader } from '#scripts/document/bash-output-cap'
|
|
5
6
|
import { scoped_green } from '#scripts/gate/scoped-green'
|
|
6
7
|
import { change_base } from '#scripts/git/change-base'
|
|
7
8
|
import { changed_paths } from '#scripts/git/changed-paths'
|
|
@@ -16,6 +17,8 @@ import { run_ship_review } from '#scripts/run/run-ship-review'
|
|
|
16
17
|
import { review_attest } from './review-attest'
|
|
17
18
|
import { review_brief } from './review-brief'
|
|
18
19
|
import { review_checkout, type ReviewCheckout } from './review-checkout'
|
|
20
|
+
import type { DiffParts } from './review-diff-parts'
|
|
21
|
+
import { review_diff_write } from './review-diff-write'
|
|
19
22
|
import { review_level, type ReviewLevel } from './review-level'
|
|
20
23
|
import { review_stamps } from './review-stamps'
|
|
21
24
|
import { review_tree } from './review-tree'
|
|
@@ -197,8 +200,26 @@ interface ComposeRequest {
|
|
|
197
200
|
profile: AgentProfile
|
|
198
201
|
}
|
|
199
202
|
|
|
203
|
+
// **Swallowed, and the brief keeps its target line** (joshuafolkken/kit#2963): parts that could not be
|
|
204
|
+
// written leave the reviewer the whole `git diff` to read, which costs truncated output rather than
|
|
205
|
+
// scope — the direction every failure here falls.
|
|
206
|
+
async function read_diff(
|
|
207
|
+
root: string,
|
|
208
|
+
base: string,
|
|
209
|
+
paths: ReadonlyArray<string>,
|
|
210
|
+
): Promise<{ parts: DiffParts; cap: number } | undefined> {
|
|
211
|
+
const cap = bash_output_cap_reader.bash_output_cap(root)
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
return { parts: await review_diff_write.write_parts({ root, base, paths, cap }), cap }
|
|
215
|
+
} catch {
|
|
216
|
+
return undefined
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
200
220
|
async function compose_brief(request: ComposeRequest): Promise<string> {
|
|
201
221
|
const { round, paths, tree, base, profile } = request
|
|
222
|
+
const contract = await open_contract()
|
|
202
223
|
const stamps = {
|
|
203
224
|
gate: review_stamps.gate_stamp.read(),
|
|
204
225
|
in_flight: review_stamps.in_flight_stamp.read(),
|
|
@@ -218,7 +239,8 @@ async function compose_brief(request: ComposeRequest): Promise<string> {
|
|
|
218
239
|
// the map is stored against is the same one the brief printed (joshuafolkken/kit#1537).
|
|
219
240
|
base,
|
|
220
241
|
rubric_path: RUBRIC_PATH,
|
|
221
|
-
...
|
|
242
|
+
...contract,
|
|
243
|
+
diff: await read_diff(contract.checkout.root, base, paths),
|
|
222
244
|
})
|
|
223
245
|
}
|
|
224
246
|
|
|
@@ -321,15 +343,10 @@ const review_brief_cli = {
|
|
|
321
343
|
FIRST_ROUND,
|
|
322
344
|
format_reason,
|
|
323
345
|
KEPT_NOTE_PREFIX,
|
|
324
|
-
kept_note,
|
|
325
|
-
LEVEL_ONLY_FLAG,
|
|
326
|
-
main,
|
|
327
346
|
parse_round,
|
|
328
347
|
record_round_one,
|
|
329
348
|
report_error,
|
|
330
349
|
run,
|
|
331
|
-
run_level,
|
|
332
|
-
USAGE,
|
|
333
350
|
}
|
|
334
351
|
|
|
335
352
|
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET))
|