@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
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALREADY_DONE_LABEL,
|
|
3
|
-
BREAKING_CHANGE_LABEL,
|
|
4
|
-
BUGFIX_LABEL,
|
|
5
3
|
CONTEXT_CUT_THRESHOLD,
|
|
6
|
-
ENHANCEMENT_LABEL,
|
|
7
|
-
FULL_PAGE_QUERY,
|
|
8
4
|
IN_PROGRESS_LABEL,
|
|
9
5
|
NEEDS_DECISION_LABEL,
|
|
10
6
|
NEEDS_HUMAN_REVIEW_LABEL,
|
|
7
|
+
PORCELAIN_FLAG,
|
|
11
8
|
RECENT_REQUEST_WINDOW,
|
|
12
|
-
RELEASE_CLASSIFICATION_NAMES,
|
|
13
9
|
agent_role_profile,
|
|
14
10
|
agent_session_environment,
|
|
15
11
|
bash_triggers,
|
|
12
|
+
cli_flags,
|
|
16
13
|
error_text,
|
|
17
14
|
filing_cap,
|
|
18
|
-
gh_failure,
|
|
19
15
|
git_command,
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
git_common_directory,
|
|
17
|
+
git_gh_command,
|
|
18
|
+
git_gh_issue_read,
|
|
19
|
+
git_gh_pr_read,
|
|
20
|
+
git_spawn_sync,
|
|
22
21
|
has_any_label,
|
|
23
|
-
has_stderr_field,
|
|
24
22
|
issue_cite,
|
|
25
23
|
lane_background,
|
|
26
24
|
lane_park,
|
|
27
25
|
lane_registry,
|
|
28
|
-
|
|
26
|
+
open_issue_schema,
|
|
29
27
|
parse_json_object_safe,
|
|
30
28
|
read_json_listing,
|
|
31
29
|
repo_origin,
|
|
@@ -34,36 +32,34 @@ import {
|
|
|
34
32
|
run_cut,
|
|
35
33
|
run_headless,
|
|
36
34
|
run_hold,
|
|
37
|
-
run_progress_clock,
|
|
38
35
|
run_tail,
|
|
39
|
-
run_watcher_guard,
|
|
40
36
|
shell_segments,
|
|
41
37
|
tail_commands
|
|
42
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-AIOIOMPX.js";
|
|
43
39
|
import {
|
|
44
40
|
find_local_bin_upwards
|
|
45
41
|
} from "./chunk-3V37IID6.js";
|
|
46
42
|
import {
|
|
47
43
|
batch_outcome
|
|
48
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-XYUIQOW3.js";
|
|
49
45
|
import {
|
|
50
46
|
investigation_refusal
|
|
51
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-7HVB7SZG.js";
|
|
52
48
|
import {
|
|
53
49
|
duplicate_read_outcome
|
|
54
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-5SD2YM7Z.js";
|
|
55
51
|
import {
|
|
56
52
|
investigation_reads
|
|
57
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-NXYMNPUQ.js";
|
|
58
54
|
import {
|
|
59
55
|
lane_guard_policy,
|
|
60
56
|
time_batch_guard
|
|
61
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-QVLT52OV.js";
|
|
62
58
|
import {
|
|
63
59
|
hook_decision,
|
|
64
60
|
lane_child_marker,
|
|
65
61
|
run_issue_number
|
|
66
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-MKVGBQPA.js";
|
|
67
63
|
import {
|
|
68
64
|
PLATFORM_TEMP_ROOT,
|
|
69
65
|
cost_attribute,
|
|
@@ -75,10 +71,10 @@ import {
|
|
|
75
71
|
lane_paths,
|
|
76
72
|
stamp_file,
|
|
77
73
|
time_shell
|
|
78
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-4JZDP5X5.js";
|
|
79
75
|
|
|
80
76
|
// scripts/rules/direct-filing.ts
|
|
81
|
-
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
|
|
77
|
+
var DIRECT_FILING_REASON = '\u26D4 direct filing: an Issue is filed with `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2> [--route <route>] [--label <name>] [--repo <owner/repo>]`, never with `gh api \u2026/issues` or `gh issue create`. The command runs every filing step in order \u2014 the duplicate scout, the body lint and its classification labels, the depth and `route:` labels, the `## Origin` check when filing into another repository, and `epic:bundle` after the create \u2014 so none of them is a step to remember. When the scout prints candidates it holds the filing: read each, then reissue with `--distinct <N,\u2026>` for the ones that are separate deliverables. Reference: `docs/josh-commands-automation.md` \u2192 "`josh issue:file`". **This rule fires on every occurrence, not once per run.**';
|
|
82
78
|
function is_any_filing(command) {
|
|
83
79
|
return bash_triggers.is_direct_filing(command) || bash_triggers.is_issue_filing(command);
|
|
84
80
|
}
|
|
@@ -204,7 +200,6 @@ var run_progress = {
|
|
|
204
200
|
format_line,
|
|
205
201
|
format_next_line,
|
|
206
202
|
format_next_report,
|
|
207
|
-
format_record,
|
|
208
203
|
format_report,
|
|
209
204
|
is_due,
|
|
210
205
|
minutes_from,
|
|
@@ -212,16 +207,140 @@ var run_progress = {
|
|
|
212
207
|
observation_key
|
|
213
208
|
};
|
|
214
209
|
|
|
210
|
+
// scripts/run/run-progress-clock.ts
|
|
211
|
+
import { z } from "zod";
|
|
212
|
+
var PROGRESS_PREFIX = "josh-run-progress-";
|
|
213
|
+
var LIFE_PREFIX = "josh-run-progress-life-";
|
|
214
|
+
var LOG_SUFFIX = ".log";
|
|
215
|
+
var LOG_KEEP = 12;
|
|
216
|
+
var MS_PER_MINUTE2 = 6e4;
|
|
217
|
+
var LOG_STALE_MINUTES = 90;
|
|
218
|
+
var LOG_STALE_MS = LOG_STALE_MINUTES * MS_PER_MINUTE2;
|
|
219
|
+
var BLOCK_SEPARATOR = "\n\n";
|
|
220
|
+
var report_stamp_schema = z.object({ reported_at: z.string(), line: z.string().optional() });
|
|
221
|
+
function parse_stamp(raw) {
|
|
222
|
+
const parsed = report_stamp_schema.safeParse(JSON.parse(raw));
|
|
223
|
+
if (!parsed.success) return void 0;
|
|
224
|
+
const reported_at = Date.parse(parsed.data.reported_at);
|
|
225
|
+
return Number.isNaN(reported_at) ? void 0 : reported_at;
|
|
226
|
+
}
|
|
227
|
+
function read_last_line(target) {
|
|
228
|
+
const raw = stamp_file.read_stamp_text(target);
|
|
229
|
+
if (raw === void 0) return void 0;
|
|
230
|
+
try {
|
|
231
|
+
return report_stamp_schema.safeParse(JSON.parse(raw)).data?.line;
|
|
232
|
+
} catch {
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function read_last_report(target) {
|
|
237
|
+
const raw = stamp_file.read_stamp_text(target);
|
|
238
|
+
if (raw === void 0) return void 0;
|
|
239
|
+
try {
|
|
240
|
+
return parse_stamp(raw);
|
|
241
|
+
} catch {
|
|
242
|
+
return void 0;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function log_path_of(target) {
|
|
246
|
+
return `${target}${LOG_SUFFIX}`;
|
|
247
|
+
}
|
|
248
|
+
function is_log_stale(target, now_ms) {
|
|
249
|
+
const previous_ms = read_last_report(target);
|
|
250
|
+
return previous_ms === void 0 || now_ms - previous_ms > LOG_STALE_MS;
|
|
251
|
+
}
|
|
252
|
+
function recent_blocks(log_target2, is_fresh) {
|
|
253
|
+
if (is_fresh) return [];
|
|
254
|
+
const raw = stamp_file.read_stamp_text(log_target2);
|
|
255
|
+
if (raw === void 0) return [];
|
|
256
|
+
return raw.split(BLOCK_SEPARATOR).map((block) => block.trimEnd()).filter((block) => block.length > 0);
|
|
257
|
+
}
|
|
258
|
+
function append_line(log_target2, reported_at, line, is_fresh) {
|
|
259
|
+
const block = `${reported_at}
|
|
260
|
+
${line}`;
|
|
261
|
+
const blocks = [...recent_blocks(log_target2, is_fresh), block].slice(-LOG_KEEP);
|
|
262
|
+
stamp_file.write_text_stamp(log_target2, `${blocks.join(BLOCK_SEPARATOR)}
|
|
263
|
+
`);
|
|
264
|
+
}
|
|
265
|
+
function mark(target, now_ms, line) {
|
|
266
|
+
const reported_at = new Date(now_ms).toISOString();
|
|
267
|
+
const kept = line ?? read_last_line(target);
|
|
268
|
+
if (line !== void 0) {
|
|
269
|
+
append_line(log_path_of(target), reported_at, line, is_log_stale(target, now_ms));
|
|
270
|
+
}
|
|
271
|
+
stamp_file.write_stamp(target, kept === void 0 ? { reported_at } : { reported_at, line: kept });
|
|
272
|
+
}
|
|
273
|
+
function stamp_target_of(git_directory) {
|
|
274
|
+
return stamp_file.stamp_path(PROGRESS_PREFIX, git_directory);
|
|
275
|
+
}
|
|
276
|
+
function life_target_of(git_directory) {
|
|
277
|
+
return stamp_file.stamp_path(LIFE_PREFIX, git_directory);
|
|
278
|
+
}
|
|
279
|
+
var life_schema = z.object({ alive: z.literal(true), pinged_at: z.string().optional() });
|
|
280
|
+
function begin_life(target) {
|
|
281
|
+
stamp_file.write_stamp(target, { alive: true, pinged_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
282
|
+
}
|
|
283
|
+
function ping_life(target) {
|
|
284
|
+
begin_life(target);
|
|
285
|
+
}
|
|
286
|
+
function parse_pinged_at(raw) {
|
|
287
|
+
const parsed = life_schema.safeParse(JSON.parse(raw));
|
|
288
|
+
if (!parsed.success || parsed.data.pinged_at === void 0) return void 0;
|
|
289
|
+
const pinged_ms = Date.parse(parsed.data.pinged_at);
|
|
290
|
+
return Number.isNaN(pinged_ms) ? void 0 : pinged_ms;
|
|
291
|
+
}
|
|
292
|
+
function is_life_fresh(target, threshold_ms) {
|
|
293
|
+
const raw = stamp_file.read_stamp_text(target);
|
|
294
|
+
if (raw === void 0) return false;
|
|
295
|
+
try {
|
|
296
|
+
const pinged_ms = parse_pinged_at(raw);
|
|
297
|
+
return pinged_ms !== void 0 && Date.now() - pinged_ms < threshold_ms;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
error_text.trace_swallowed("run_progress_clock.is_life_fresh", error);
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function is_life_ended(target) {
|
|
304
|
+
return stamp_file.read_stamp_text(target) === void 0;
|
|
305
|
+
}
|
|
306
|
+
function end_life(target) {
|
|
307
|
+
stamp_file.remove_stamp(target);
|
|
308
|
+
}
|
|
309
|
+
function stamp_target_sync() {
|
|
310
|
+
const directory = git_common_directory.own();
|
|
311
|
+
return directory === void 0 ? void 0 : stamp_target_of(directory);
|
|
312
|
+
}
|
|
313
|
+
function read_last_report_sync() {
|
|
314
|
+
const target = stamp_target_sync();
|
|
315
|
+
return target === void 0 ? void 0 : read_last_report(target);
|
|
316
|
+
}
|
|
317
|
+
var run_progress_clock = {
|
|
318
|
+
LIFE_PREFIX,
|
|
319
|
+
LOG_KEEP,
|
|
320
|
+
LOG_STALE_MS,
|
|
321
|
+
begin_life,
|
|
322
|
+
end_life,
|
|
323
|
+
is_life_ended,
|
|
324
|
+
is_life_fresh,
|
|
325
|
+
life_target_of,
|
|
326
|
+
log_path_of,
|
|
327
|
+
mark,
|
|
328
|
+
parse_stamp,
|
|
329
|
+
ping_life,
|
|
330
|
+
read_last_line,
|
|
331
|
+
read_last_report,
|
|
332
|
+
read_last_report_sync,
|
|
333
|
+
stamp_target_of
|
|
334
|
+
};
|
|
335
|
+
|
|
215
336
|
// scripts/run/run-progress-config.ts
|
|
216
337
|
import { readFileSync } from "node:fs";
|
|
217
338
|
import path from "node:path";
|
|
218
|
-
import { z } from "zod";
|
|
339
|
+
import { z as z2 } from "zod";
|
|
219
340
|
var PACKAGE_FILE = "package.json";
|
|
220
|
-
var CONFIG_FIELD = "josh";
|
|
221
|
-
var INTERVAL_FIELD = "progress_interval_minutes";
|
|
222
341
|
var MAX_DEPTH = 10;
|
|
223
|
-
var package_schema =
|
|
224
|
-
josh:
|
|
342
|
+
var package_schema = z2.object({
|
|
343
|
+
josh: z2.object({ progress_interval_minutes: z2.number().positive() }).optional()
|
|
225
344
|
});
|
|
226
345
|
function ancestors_of(start) {
|
|
227
346
|
const directories = [];
|
|
@@ -258,8 +377,6 @@ function configured_interval_ms(directory = process.cwd()) {
|
|
|
258
377
|
return resolve_interval_ms(void 0, directory);
|
|
259
378
|
}
|
|
260
379
|
var run_progress_config = {
|
|
261
|
-
CONFIG_FIELD,
|
|
262
|
-
INTERVAL_FIELD,
|
|
263
380
|
PACKAGE_FILE,
|
|
264
381
|
configured_interval_ms,
|
|
265
382
|
read_package_minutes,
|
|
@@ -323,7 +440,7 @@ function decide(call, run2, can_record) {
|
|
|
323
440
|
if (can_record) ARM_RECORD.record(target, lands_ms);
|
|
324
441
|
return false;
|
|
325
442
|
}
|
|
326
|
-
var EARLY_HEARTBEAT_REASON =
|
|
443
|
+
var EARLY_HEARTBEAT_REASON = "\u26D4 early heartbeat: this call arms a wait timer of its own, and the progress clock is not yours to keep. `pnpm josh run:progress --wait` keeps it for you (`JOSH_PROGRESS_INTERVAL_MINUTES`, or `josh.progress_interval_minutes` in `package.json`, default 20 minutes): start it once in the background and it reports the five labelled lines to the run's event stream on its own, without waking you \u2014 relay nothing, and start it again only after it exits, which it does when newly runnable work arrives. Two timers armed at once is what produced reports 3\u20135 minutes apart on a 15-minute setting (joshuafolkken/kit#1570): a new one was armed on the turn a timer fired and again on the turn a child's completion woke the run, and `--mark` recorded each report without anything refusing the early ones. **An explicit ask is not a heartbeat**: if the person asked for progress now, run `pnpm josh run:progress --once`, which prints the five labelled lines whatever the clock says. If you are waiting on something rather than on a clock, wait on the thing itself \u2014 `pnpm josh followup` waits for CI. The procedure is `.claude/skills/workflow-commands/backlogrun.md` \u2192 \"Progress while the run is quiet\". **This rule fires on every early arm, not once per run**, so reissuing the same sleep will be refused again.";
|
|
327
444
|
var PROGRESS_NAMES = /* @__PURE__ */ new Set(["run:progress"]);
|
|
328
445
|
var WATCH_FLAG = /(?:^|\s)--(?:wait|once)(?=\s|$)/u;
|
|
329
446
|
function is_progress_watch_segment(segment) {
|
|
@@ -406,10 +523,7 @@ var ROW2 = {
|
|
|
406
523
|
var file_body = {
|
|
407
524
|
FILE_BODY_REASON,
|
|
408
525
|
ROW: ROW2,
|
|
409
|
-
carries_a_file_body
|
|
410
|
-
is_in_place_perl,
|
|
411
|
-
is_interpreter_write,
|
|
412
|
-
writes_existing_file
|
|
526
|
+
carries_a_file_body
|
|
413
527
|
};
|
|
414
528
|
|
|
415
529
|
// scripts/rules/gh-api.ts
|
|
@@ -424,8 +538,11 @@ var REPO_GROUP = 2;
|
|
|
424
538
|
function is_gh_api(segment) {
|
|
425
539
|
return GH_API_COMMAND.test(segment);
|
|
426
540
|
}
|
|
541
|
+
function method_of(segment) {
|
|
542
|
+
return API_METHOD.exec(segment)?.[1]?.toUpperCase();
|
|
543
|
+
}
|
|
427
544
|
function is_read(segment) {
|
|
428
|
-
const method =
|
|
545
|
+
const method = method_of(segment);
|
|
429
546
|
if (method !== void 0) return method.toUpperCase() === READ_METHOD;
|
|
430
547
|
return !API_FIELD.test(segment);
|
|
431
548
|
}
|
|
@@ -437,7 +554,7 @@ function repo_target(segment) {
|
|
|
437
554
|
if (match === null) return void 0;
|
|
438
555
|
return { owner: match[OWNER_GROUP] ?? "", repo: match[REPO_GROUP] ?? "" };
|
|
439
556
|
}
|
|
440
|
-
var gh_api = {
|
|
557
|
+
var gh_api = { GH_FLAGS, is_gh_api, is_read, is_write, method_of, repo_target };
|
|
441
558
|
|
|
442
559
|
// scripts/rules/git-argv.ts
|
|
443
560
|
var ENV_ASSIGNMENT = /^[A-Za-z_]\w*=/u;
|
|
@@ -447,7 +564,8 @@ var WRAPPER_WORDS = /* @__PURE__ */ new Set([
|
|
|
447
564
|
"exec",
|
|
448
565
|
"run",
|
|
449
566
|
"sudo",
|
|
450
|
-
"command"
|
|
567
|
+
"command",
|
|
568
|
+
"env"
|
|
451
569
|
]);
|
|
452
570
|
var SUBSHELL_OPEN = "(";
|
|
453
571
|
var VALUE_TAKING_GLOBALS = /* @__PURE__ */ new Set([
|
|
@@ -487,11 +605,15 @@ function after_globals(words, start) {
|
|
|
487
605
|
}
|
|
488
606
|
return index;
|
|
489
607
|
}
|
|
490
|
-
function
|
|
608
|
+
function arguments_of(segment, command) {
|
|
491
609
|
const words = words_of(segment);
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
610
|
+
const command_index = after_wrapper(words);
|
|
611
|
+
return words[command_index] === command ? words.slice(command_index + NEXT) : void 0;
|
|
612
|
+
}
|
|
613
|
+
function parse(segment) {
|
|
614
|
+
const words = arguments_of(segment, GIT_COMMAND);
|
|
615
|
+
if (words === void 0) return void 0;
|
|
616
|
+
const subcommand_index = after_globals(words, 0);
|
|
495
617
|
return { subcommand: words[subcommand_index], args: words.slice(subcommand_index + NEXT) };
|
|
496
618
|
}
|
|
497
619
|
var SHORT_CLUSTER = /^-[A-Za-z]+$/u;
|
|
@@ -503,7 +625,7 @@ function short_cluster_has(token, letters) {
|
|
|
503
625
|
}
|
|
504
626
|
return false;
|
|
505
627
|
}
|
|
506
|
-
var git_argv = { parse, short_cluster_has
|
|
628
|
+
var git_argv = { arguments_of, parse, short_cluster_has };
|
|
507
629
|
|
|
508
630
|
// scripts/rules/git-force.ts
|
|
509
631
|
var FORCE_LONG_PREFIX = "--force";
|
|
@@ -542,42 +664,32 @@ var ROW3 = {
|
|
|
542
664
|
var git_force = {
|
|
543
665
|
GIT_FORCE_REASON,
|
|
544
666
|
ROW: ROW3,
|
|
545
|
-
|
|
546
|
-
is_force_or_delete,
|
|
547
|
-
is_force_or_delete_push
|
|
667
|
+
is_force_or_delete
|
|
548
668
|
};
|
|
549
669
|
|
|
550
670
|
// scripts/cost-runtime/cost-cli.ts
|
|
551
671
|
import { fileURLToPath } from "node:url";
|
|
552
|
-
import { parseArgs } from "node:util";
|
|
553
672
|
|
|
554
673
|
// scripts/cost-runtime/codex-usage.ts
|
|
555
674
|
import { globSync, readFileSync as readFileSync2 } from "node:fs";
|
|
556
675
|
import path2 from "node:path";
|
|
557
|
-
import { z as
|
|
676
|
+
import { z as z3 } from "zod";
|
|
558
677
|
var THREAD_ID_PATTERN = /^[A-Za-z0-9-]+$/u;
|
|
559
|
-
var SESSION_META_SCHEMA =
|
|
560
|
-
type:
|
|
561
|
-
payload:
|
|
678
|
+
var SESSION_META_SCHEMA = z3.looseObject({
|
|
679
|
+
type: z3.literal("session_meta"),
|
|
680
|
+
payload: z3.looseObject({ id: z3.string(), cwd: z3.string() })
|
|
562
681
|
});
|
|
563
|
-
var TOTAL_USAGE_SCHEMA =
|
|
564
|
-
var TOKEN_COUNT_SCHEMA =
|
|
565
|
-
type:
|
|
566
|
-
payload:
|
|
567
|
-
type:
|
|
568
|
-
info:
|
|
682
|
+
var TOTAL_USAGE_SCHEMA = z3.looseObject({ input_tokens: z3.number().nonnegative() });
|
|
683
|
+
var TOKEN_COUNT_SCHEMA = z3.looseObject({
|
|
684
|
+
type: z3.literal("event_msg"),
|
|
685
|
+
payload: z3.looseObject({
|
|
686
|
+
type: z3.literal("token_count"),
|
|
687
|
+
info: z3.looseObject({
|
|
569
688
|
total_token_usage: TOTAL_USAGE_SCHEMA,
|
|
570
689
|
last_token_usage: TOTAL_USAGE_SCHEMA
|
|
571
690
|
})
|
|
572
691
|
})
|
|
573
692
|
});
|
|
574
|
-
function decode(line) {
|
|
575
|
-
try {
|
|
576
|
-
return JSON.parse(line);
|
|
577
|
-
} catch {
|
|
578
|
-
return void 0;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
693
|
function same_project(cwd, target) {
|
|
582
694
|
return cost_transcript.session_cwd(cwd) === cost_transcript.session_cwd(target);
|
|
583
695
|
}
|
|
@@ -585,7 +697,7 @@ function billed_per_request(usages) {
|
|
|
585
697
|
return usages.map((usage) => usage.last_token_usage.input_tokens);
|
|
586
698
|
}
|
|
587
699
|
function measurement_from(content, target, thread_id) {
|
|
588
|
-
const values = content.split("\n").map((line) =>
|
|
700
|
+
const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
|
|
589
701
|
const metadata = values.flatMap((value) => {
|
|
590
702
|
const parsed = SESSION_META_SCHEMA.safeParse(value);
|
|
591
703
|
return parsed.success ? [parsed.data] : [];
|
|
@@ -736,11 +848,9 @@ function mainline_records(pairs) {
|
|
|
736
848
|
return [...groups.values()];
|
|
737
849
|
}
|
|
738
850
|
var cost_corpus = {
|
|
739
|
-
accumulate_missing,
|
|
740
851
|
load_corpus,
|
|
741
852
|
attribute_corpus,
|
|
742
853
|
floor_for_issue,
|
|
743
|
-
dedupe_across_sessions,
|
|
744
854
|
attributed,
|
|
745
855
|
mainline_records
|
|
746
856
|
};
|
|
@@ -830,8 +940,8 @@ function selected_threshold(values) {
|
|
|
830
940
|
function threshold_option(over) {
|
|
831
941
|
return over === void 0 ? {} : { over };
|
|
832
942
|
}
|
|
833
|
-
function path_option(
|
|
834
|
-
return
|
|
943
|
+
function path_option(path10) {
|
|
944
|
+
return path10 === void 0 ? {} : { path: path10 };
|
|
835
945
|
}
|
|
836
946
|
function to_options(values) {
|
|
837
947
|
if (values.cut === true && values.over !== void 0) return void 0;
|
|
@@ -845,12 +955,8 @@ var PARSE_ARGS_OPTIONS = {
|
|
|
845
955
|
path: { type: "string" }
|
|
846
956
|
};
|
|
847
957
|
function parse_options(argv) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return to_options(values);
|
|
851
|
-
} catch {
|
|
852
|
-
return void 0;
|
|
853
|
-
}
|
|
958
|
+
const values = cli_flags.values_of(argv, PARSE_ARGS_OPTIONS);
|
|
959
|
+
return values === void 0 ? void 0 : to_options(values);
|
|
854
960
|
}
|
|
855
961
|
function report_empty(cwd, session_id) {
|
|
856
962
|
const searched = cost_transcript.searched_directories(cost_transcript.transcript_directories(cwd));
|
|
@@ -947,11 +1053,8 @@ var cost_cli = {
|
|
|
947
1053
|
USAGE,
|
|
948
1054
|
parse_options,
|
|
949
1055
|
to_threshold,
|
|
950
|
-
load_corpus: cost_corpus.load_corpus,
|
|
951
|
-
attributed: cost_corpus.attributed,
|
|
952
1056
|
run,
|
|
953
|
-
session_verdict
|
|
954
|
-
main
|
|
1057
|
+
session_verdict
|
|
955
1058
|
};
|
|
956
1059
|
if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
|
|
957
1060
|
|
|
@@ -970,16 +1073,13 @@ function format_share(part, whole) {
|
|
|
970
1073
|
return `${(part / whole * PERCENT_SCALE).toFixed(PERCENT_DECIMALS)}%`;
|
|
971
1074
|
}
|
|
972
1075
|
var cost_format = {
|
|
973
|
-
USD_DECIMALS,
|
|
974
|
-
PERCENT_SCALE,
|
|
975
|
-
PERCENT_DECIMALS,
|
|
976
1076
|
format_usd,
|
|
977
1077
|
format_tokens,
|
|
978
1078
|
format_share
|
|
979
1079
|
};
|
|
980
1080
|
|
|
981
1081
|
// scripts/rules/implementation-cut-verdict.ts
|
|
982
|
-
import { z as
|
|
1082
|
+
import { z as z4 } from "zod";
|
|
983
1083
|
var VERDICT_CACHE_PREFIX = "josh-impl-cut-verdict-";
|
|
984
1084
|
var VERDICT_REUSE_MS = 5e3;
|
|
985
1085
|
var VERDICT_VALUES = [
|
|
@@ -987,7 +1087,7 @@ var VERDICT_VALUES = [
|
|
|
987
1087
|
"under",
|
|
988
1088
|
"unmeasurable"
|
|
989
1089
|
];
|
|
990
|
-
var cache_schema =
|
|
1090
|
+
var cache_schema = z4.object({ read_at_ms: z4.number(), verdict: z4.enum(VERDICT_VALUES) });
|
|
991
1091
|
function cache_path(directory) {
|
|
992
1092
|
return stamp_file.stamp_path(VERDICT_CACHE_PREFIX, directory);
|
|
993
1093
|
}
|
|
@@ -1083,7 +1183,6 @@ var implementation_cut = {
|
|
|
1083
1183
|
REISSUE_WINDOW_MS,
|
|
1084
1184
|
ROW: ROW4,
|
|
1085
1185
|
decide: decide2,
|
|
1086
|
-
is_edit_tool,
|
|
1087
1186
|
is_over_threshold_edit,
|
|
1088
1187
|
is_reissued_refusal,
|
|
1089
1188
|
takes_the_impl_cut,
|
|
@@ -1159,20 +1258,20 @@ var lane_carry_conflict = {
|
|
|
1159
1258
|
};
|
|
1160
1259
|
|
|
1161
1260
|
// scripts/agent/interactive-ask.ts
|
|
1162
|
-
import { z as
|
|
1261
|
+
import { z as z5 } from "zod";
|
|
1163
1262
|
var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
1164
1263
|
function is_interactive_tool(name) {
|
|
1165
1264
|
return INTERACTIVE_TOOLS.has(name);
|
|
1166
1265
|
}
|
|
1167
|
-
var OPTION_SCHEMA =
|
|
1168
|
-
var QUESTION_SCHEMA =
|
|
1169
|
-
question:
|
|
1170
|
-
options:
|
|
1266
|
+
var OPTION_SCHEMA = z5.looseObject({ label: z5.string().optional() });
|
|
1267
|
+
var QUESTION_SCHEMA = z5.looseObject({
|
|
1268
|
+
question: z5.string().optional(),
|
|
1269
|
+
options: z5.array(OPTION_SCHEMA).optional()
|
|
1171
1270
|
});
|
|
1172
|
-
var ASK_INPUT_SCHEMA =
|
|
1173
|
-
var DENIAL_SCHEMA =
|
|
1174
|
-
tool_name:
|
|
1175
|
-
tool_input:
|
|
1271
|
+
var ASK_INPUT_SCHEMA = z5.looseObject({ questions: z5.array(QUESTION_SCHEMA).optional() });
|
|
1272
|
+
var DENIAL_SCHEMA = z5.looseObject({
|
|
1273
|
+
tool_name: z5.string().optional(),
|
|
1274
|
+
tool_input: z5.unknown().optional()
|
|
1176
1275
|
});
|
|
1177
1276
|
var UNKNOWN_QUESTION = "(question text unavailable)";
|
|
1178
1277
|
var OPTION_SEPARATOR = " / ";
|
|
@@ -1198,7 +1297,7 @@ function refused_ask_of(permission_denials) {
|
|
|
1198
1297
|
if (questions.length === 0) return void 0;
|
|
1199
1298
|
return questions.map((question) => render_question(question)).join(QUESTION_SEPARATOR);
|
|
1200
1299
|
}
|
|
1201
|
-
var interactive_ask = {
|
|
1300
|
+
var interactive_ask = { is_interactive_tool, refused_ask_of };
|
|
1202
1301
|
|
|
1203
1302
|
// scripts/rules/lane-interactive-ask.ts
|
|
1204
1303
|
function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(process.cwd())) {
|
|
@@ -1257,6 +1356,108 @@ var lane_switch_main = {
|
|
|
1257
1356
|
switch_targets
|
|
1258
1357
|
};
|
|
1259
1358
|
|
|
1359
|
+
// scripts/run/run-stage.ts
|
|
1360
|
+
var KICKOFF = "kickoff";
|
|
1361
|
+
var HALFRUN = "halfrun";
|
|
1362
|
+
var PRRUN = "prrun";
|
|
1363
|
+
var FULLRUN = "fullrun";
|
|
1364
|
+
var COMMANDS = [KICKOFF, HALFRUN, PRRUN, FULLRUN];
|
|
1365
|
+
var DEFAULT_COMMAND = FULLRUN;
|
|
1366
|
+
var FRESH = "fresh";
|
|
1367
|
+
var PLANNED = "planned";
|
|
1368
|
+
var HALFRUN_STOPPED = "halfrun-stopped";
|
|
1369
|
+
var PRRUN_STOPPED = "prrun-stopped";
|
|
1370
|
+
var MERGED = "merged";
|
|
1371
|
+
var STATES = [FRESH, PLANNED, HALFRUN_STOPPED, PRRUN_STOPPED, MERGED];
|
|
1372
|
+
var REACHED = "reached";
|
|
1373
|
+
var STARTS = {
|
|
1374
|
+
[FRESH]: "plan",
|
|
1375
|
+
[PLANNED]: "implement",
|
|
1376
|
+
[HALFRUN_STOPPED]: "gate",
|
|
1377
|
+
[PRRUN_STOPPED]: "followup",
|
|
1378
|
+
[MERGED]: REACHED
|
|
1379
|
+
};
|
|
1380
|
+
var START_TOKENS = [...new Set(Object.values(STARTS))];
|
|
1381
|
+
var ENDS = {
|
|
1382
|
+
[KICKOFF]: "plan posted",
|
|
1383
|
+
[HALFRUN]: "gated, before the commit",
|
|
1384
|
+
[PRRUN]: "green, mergeable PR",
|
|
1385
|
+
[FULLRUN]: "merged"
|
|
1386
|
+
};
|
|
1387
|
+
var NEXT_PREFIX = "Next: ";
|
|
1388
|
+
var NEXT_SEPARATOR = " | ";
|
|
1389
|
+
var STATE_HEADER = "Issue state";
|
|
1390
|
+
var CELL_ARROW = " \u2192 ";
|
|
1391
|
+
var REACHED_CELL = "reached \u2014 report and stop";
|
|
1392
|
+
function to_command(value) {
|
|
1393
|
+
return COMMANDS.find((command) => command === value);
|
|
1394
|
+
}
|
|
1395
|
+
function state_of(facts) {
|
|
1396
|
+
if (facts.is_closed) return MERGED;
|
|
1397
|
+
if (facts.is_prrun_stopped) return PRRUN_STOPPED;
|
|
1398
|
+
if (facts.is_halfrun_stopped) return HALFRUN_STOPPED;
|
|
1399
|
+
return facts.is_planned ? PLANNED : FRESH;
|
|
1400
|
+
}
|
|
1401
|
+
function decide3(state, command) {
|
|
1402
|
+
const is_reached = STATES.indexOf(state) > COMMANDS.indexOf(command);
|
|
1403
|
+
const start = is_reached ? REACHED : STARTS[state];
|
|
1404
|
+
return { state, command, start, end: ENDS[command], is_reached };
|
|
1405
|
+
}
|
|
1406
|
+
function next_commands(command) {
|
|
1407
|
+
return COMMANDS.slice(COMMANDS.indexOf(command) + 1);
|
|
1408
|
+
}
|
|
1409
|
+
function next_line(command, issue) {
|
|
1410
|
+
const commands = next_commands(command).map((next) => `${next} #${issue}`);
|
|
1411
|
+
return `${NEXT_PREFIX}${commands.join(NEXT_SEPARATOR)}`;
|
|
1412
|
+
}
|
|
1413
|
+
function format_decision(issue, decision) {
|
|
1414
|
+
const fields = [`at: ${decision.state}`, `to: ${decision.command}`, `start: ${decision.start}`];
|
|
1415
|
+
return `stage #${issue} \u2014 ${fields.join(" \xB7 ")}`;
|
|
1416
|
+
}
|
|
1417
|
+
function cell(state, command) {
|
|
1418
|
+
const decision = decide3(state, command);
|
|
1419
|
+
return decision.is_reached ? REACHED_CELL : `${decision.start}${CELL_ARROW}${decision.end}`;
|
|
1420
|
+
}
|
|
1421
|
+
function table_rows() {
|
|
1422
|
+
const header = [STATE_HEADER, ...COMMANDS.map((command) => `\`${command}\``)];
|
|
1423
|
+
const rows = STATES.map((state) => [
|
|
1424
|
+
`\`${state}\``,
|
|
1425
|
+
...COMMANDS.map((command) => cell(state, command))
|
|
1426
|
+
]);
|
|
1427
|
+
return [header, ...rows];
|
|
1428
|
+
}
|
|
1429
|
+
var run_stage = {
|
|
1430
|
+
COMMANDS,
|
|
1431
|
+
DEFAULT_COMMAND,
|
|
1432
|
+
FRESH,
|
|
1433
|
+
HALFRUN,
|
|
1434
|
+
HALFRUN_STOPPED,
|
|
1435
|
+
KICKOFF,
|
|
1436
|
+
MERGED,
|
|
1437
|
+
PLANNED,
|
|
1438
|
+
PRRUN,
|
|
1439
|
+
PRRUN_STOPPED,
|
|
1440
|
+
REACHED,
|
|
1441
|
+
START_TOKENS,
|
|
1442
|
+
STATES,
|
|
1443
|
+
decide: decide3,
|
|
1444
|
+
format_decision,
|
|
1445
|
+
next_commands,
|
|
1446
|
+
next_line,
|
|
1447
|
+
state_of,
|
|
1448
|
+
table_rows,
|
|
1449
|
+
to_command
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
// scripts/rules/decision-oracle-stage.ts
|
|
1453
|
+
var RUN_ENTRY_ORACLE = {
|
|
1454
|
+
name: "run:entry",
|
|
1455
|
+
decision: "Where a run on an issue starts, and whether the typed command has already been reached",
|
|
1456
|
+
args: "<N> [--to kickoff|halfrun|prrun|fullrun]",
|
|
1457
|
+
vocabulary: run_stage.START_TOKENS,
|
|
1458
|
+
single_source: ".claude/skills/workflow-commands/SKILL.md \u2192 \xA72k"
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1260
1461
|
// scripts/rules/decision-oracle.ts
|
|
1261
1462
|
var CHAIN_RULE_MD = ".claude/skills/workflow-commands/chain-rule.md";
|
|
1262
1463
|
var BACKLOGRUN_PROGRESS_MD = ".claude/skills/workflow-commands/backlogrun-progress.md";
|
|
@@ -1264,11 +1465,11 @@ var SKILL_2E = ".claude/skills/workflow-commands/SKILL.md \u2192 \xA72e";
|
|
|
1264
1465
|
var SPLIT_ASSESSMENT_QUESTION = ".claude/skills/workflow-commands/split-assessment.md \u2192 The question";
|
|
1265
1466
|
var PRE_GATE_CUT_MD = ".claude/skills/workflow-commands/pre-gate-cut.md";
|
|
1266
1467
|
var BACKLOGRUN_MD = ".claude/skills/workflow-commands/backlogrun.md";
|
|
1267
|
-
var RUN_MERGE_REFERENCE = "docs/josh-commands.md \u2192 `josh run:merge`";
|
|
1268
|
-
var EPIC_NEXT_REFERENCE = "docs/josh-commands.md \u2192 `josh epic:next`";
|
|
1269
|
-
var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands.md \u2192 `josh backlog:budget`";
|
|
1270
|
-
var RUN_LIVENESS_REFERENCE = "docs/josh-commands.md \u2192 `josh run:liveness`";
|
|
1271
|
-
var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands.md \u2192 `josh auto-ok:next`";
|
|
1468
|
+
var RUN_MERGE_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:merge`";
|
|
1469
|
+
var EPIC_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh epic:next`";
|
|
1470
|
+
var BACKLOG_BUDGET_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh backlog:budget`";
|
|
1471
|
+
var RUN_LIVENESS_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh run:liveness`";
|
|
1472
|
+
var AUTO_OK_NEXT_REFERENCE = "docs/josh-commands-automation.md \u2192 `josh auto-ok:next`";
|
|
1272
1473
|
var RUN_CUT_CMD = "run:cut";
|
|
1273
1474
|
var REQUIRED = "required";
|
|
1274
1475
|
var SKIP = "skip";
|
|
@@ -1520,15 +1721,16 @@ var DECISION_ORACLES = [
|
|
|
1520
1721
|
decision: "Whether an epic's declaration matches its recorded relations, and the repair when it does not",
|
|
1521
1722
|
args: "--reconcile <E>",
|
|
1522
1723
|
vocabulary: [RECONCILED, NOTHING_TO_RECONCILE],
|
|
1523
|
-
single_source: "docs/josh-commands.md \u2192 `josh epic --reconcile`"
|
|
1724
|
+
single_source: "docs/josh-commands-automation.md \u2192 `josh epic --reconcile`"
|
|
1524
1725
|
},
|
|
1525
1726
|
{
|
|
1526
1727
|
name: "lane:list",
|
|
1527
1728
|
decision: "Whether each in-progress issue holds a live lane, and the two-directional difference",
|
|
1528
1729
|
args: "",
|
|
1529
1730
|
vocabulary: [LIVE, STOPPED, UNKNOWN],
|
|
1530
|
-
single_source: "docs/josh-commands.md \u2192 The `in-progress` / lane difference"
|
|
1531
|
-
}
|
|
1731
|
+
single_source: "docs/josh-commands-automation.md \u2192 The `in-progress` / lane difference"
|
|
1732
|
+
},
|
|
1733
|
+
RUN_ENTRY_ORACLE
|
|
1532
1734
|
];
|
|
1533
1735
|
function get_command(oracle) {
|
|
1534
1736
|
return oracle.command ?? oracle.name;
|
|
@@ -1591,6 +1793,7 @@ var NOT_NAMED = /* @__PURE__ */ new Map([
|
|
|
1591
1793
|
["epic:next", READ],
|
|
1592
1794
|
["run:merge", PHASE],
|
|
1593
1795
|
["run:step", SELF],
|
|
1796
|
+
["run:entry", SELF],
|
|
1594
1797
|
["refactor:scan", PHASE],
|
|
1595
1798
|
["split:assess", PHASE],
|
|
1596
1799
|
["sonar:hotspots", READ],
|
|
@@ -1643,7 +1846,330 @@ function row_for(oracle, firing_point) {
|
|
|
1643
1846
|
};
|
|
1644
1847
|
}
|
|
1645
1848
|
var ROWS = oracle_firing.firing_oracles().map(({ oracle, firing_point }) => row_for(oracle, firing_point));
|
|
1646
|
-
var oracle_consulted = { ROWS,
|
|
1849
|
+
var oracle_consulted = { ROWS, reason_for };
|
|
1850
|
+
|
|
1851
|
+
// scripts/rules/destructive-command.ts
|
|
1852
|
+
var RM_COMMAND = "rm";
|
|
1853
|
+
var RECURSIVE_LONG = "--recursive";
|
|
1854
|
+
var RECURSIVE_LETTERS = "rR";
|
|
1855
|
+
var FORCE_LONG = "--force";
|
|
1856
|
+
var FORCE_LETTER = "f";
|
|
1857
|
+
function has_flag(args, long, letters) {
|
|
1858
|
+
return args.some((argument) => argument === long || git_argv.short_cluster_has(argument, letters));
|
|
1859
|
+
}
|
|
1860
|
+
function is_recursive_forced_rm(segment) {
|
|
1861
|
+
const args = git_argv.arguments_of(segment, RM_COMMAND);
|
|
1862
|
+
if (args === void 0) return false;
|
|
1863
|
+
return has_flag(args, RECURSIVE_LONG, RECURSIVE_LETTERS) && has_flag(args, FORCE_LONG, FORCE_LETTER);
|
|
1864
|
+
}
|
|
1865
|
+
var GH_COMMAND = "gh";
|
|
1866
|
+
var REPO_GROUP2 = "repo";
|
|
1867
|
+
var REPO_REMOVALS = /* @__PURE__ */ new Set(["delete", "archive"]);
|
|
1868
|
+
var REPO_EDIT = "edit";
|
|
1869
|
+
var VISIBILITY_FLAG = "--visibility";
|
|
1870
|
+
var PR_GROUP = "pr";
|
|
1871
|
+
var PR_CLOSE = "close";
|
|
1872
|
+
function is_destructive_repo_action(action, args) {
|
|
1873
|
+
if (REPO_REMOVALS.has(action)) return true;
|
|
1874
|
+
return action === REPO_EDIT && args.some((argument) => argument.startsWith(VISIBILITY_FLAG));
|
|
1875
|
+
}
|
|
1876
|
+
function is_destructive_gh_command(args) {
|
|
1877
|
+
const [group, action = ""] = args;
|
|
1878
|
+
if (group === PR_GROUP) return action === PR_CLOSE;
|
|
1879
|
+
return group === REPO_GROUP2 && is_destructive_repo_action(action, args);
|
|
1880
|
+
}
|
|
1881
|
+
var DELETE_METHOD = "DELETE";
|
|
1882
|
+
var REPOSITORY_PATH = "repos/";
|
|
1883
|
+
var ISSUE_LABEL_PATH = /^['"]?\/?repos\/[^/\s'"]+\/[^/\s'"]+\/issues\/\d+\/labels\/[^/\s'"]+['"]?$/u;
|
|
1884
|
+
function is_destructive_api_call(args) {
|
|
1885
|
+
const call = [GH_COMMAND, ...args].join(" ");
|
|
1886
|
+
if (!gh_api.is_gh_api(call) || gh_api.method_of(call) !== DELETE_METHOD) return false;
|
|
1887
|
+
return args.some(
|
|
1888
|
+
(argument) => argument.includes(REPOSITORY_PATH) && !ISSUE_LABEL_PATH.test(argument)
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
function is_destructive_gh(segment) {
|
|
1892
|
+
const args = git_argv.arguments_of(segment, GH_COMMAND);
|
|
1893
|
+
if (args === void 0) return false;
|
|
1894
|
+
return is_destructive_gh_command(args) || is_destructive_api_call(args);
|
|
1895
|
+
}
|
|
1896
|
+
function is_destructive_segment2(segment) {
|
|
1897
|
+
return is_recursive_forced_rm(segment) || is_destructive_gh(segment);
|
|
1898
|
+
}
|
|
1899
|
+
function is_destructive_command(command) {
|
|
1900
|
+
return shell_segments.segments_of(command).some((segment) => is_destructive_segment2(segment));
|
|
1901
|
+
}
|
|
1902
|
+
var DESTRUCTIVE_COMMAND_REASON = "\u26D4 destructive command: a recursive forced `rm` (`rm -rf`, `rm -fr`, `rm -r -f`, `rm --recursive --force`), `gh repo delete` / `archive`, `gh repo edit --visibility`, `gh pr close`, or a `gh api -X DELETE repos/\u2026` call deletes or closes something that cannot be restored, which is Tier C and needs explicit current-turn user instruction (`prompts/collaboration-workflow/operating-rules.md`). The `.claude/settings.json` deny list matches prefix globs, so it misses these spellings; this row reads the argv instead. Remove a file or directory one path at a time without `-f`, or ask the user to run the command in their own terminal. `gh issue close` and removing a label from an Issue (`DELETE repos/<o>/<r>/issues/<N>/labels/<name>`) are not refused. **This rule fires on every occurrence, not once per run.**";
|
|
1903
|
+
var ROW10 = {
|
|
1904
|
+
id: "destructive-command",
|
|
1905
|
+
is_trigger: bash_triggers.on_bash_command(is_destructive_command),
|
|
1906
|
+
reason: DESTRUCTIVE_COMMAND_REASON,
|
|
1907
|
+
decide: () => true
|
|
1908
|
+
};
|
|
1909
|
+
var destructive_command = { DESTRUCTIVE_COMMAND_REASON, ROW: ROW10, is_destructive_command };
|
|
1910
|
+
|
|
1911
|
+
// scripts/rules/worktree-guard.ts
|
|
1912
|
+
var CHECKOUT_SUBCOMMAND = "checkout";
|
|
1913
|
+
var RESTORE_SUBCOMMAND = "restore";
|
|
1914
|
+
var STASH_SUBCOMMAND = "stash";
|
|
1915
|
+
var PATHSPEC_SEPARATOR = "--";
|
|
1916
|
+
function is_worktree_checkout(args) {
|
|
1917
|
+
return args.includes(PATHSPEC_SEPARATOR);
|
|
1918
|
+
}
|
|
1919
|
+
var STAGED_LONG = "--staged";
|
|
1920
|
+
var STAGED_LETTER = "S";
|
|
1921
|
+
var WORKTREE_LONG = "--worktree";
|
|
1922
|
+
var WORKTREE_LETTER = "W";
|
|
1923
|
+
function touches_worktree(args) {
|
|
1924
|
+
const is_staged = args.some(
|
|
1925
|
+
(argument) => argument === STAGED_LONG || git_argv.short_cluster_has(argument, STAGED_LETTER)
|
|
1926
|
+
);
|
|
1927
|
+
const is_worktree = args.some(
|
|
1928
|
+
(argument) => argument === WORKTREE_LONG || git_argv.short_cluster_has(argument, WORKTREE_LETTER)
|
|
1929
|
+
);
|
|
1930
|
+
return !is_staged || is_worktree;
|
|
1931
|
+
}
|
|
1932
|
+
var PUSH_STASH = "push";
|
|
1933
|
+
var MESSAGE_LONG = "--message";
|
|
1934
|
+
var MESSAGE_SHORT_LETTER = "m";
|
|
1935
|
+
var READ_ONLY_STASH = /* @__PURE__ */ new Set(["list", "show"]);
|
|
1936
|
+
function has_message(args) {
|
|
1937
|
+
return args.some(
|
|
1938
|
+
(argument) => argument === MESSAGE_LONG || argument.startsWith(`${MESSAGE_LONG}=`) || git_argv.short_cluster_has(argument, MESSAGE_SHORT_LETTER)
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
function is_unauthorized_stash(args) {
|
|
1942
|
+
const [stash_subcommand, ...rest] = args;
|
|
1943
|
+
if (stash_subcommand === void 0) return true;
|
|
1944
|
+
if (READ_ONLY_STASH.has(stash_subcommand)) return false;
|
|
1945
|
+
if (stash_subcommand === PUSH_STASH) return !has_message(rest);
|
|
1946
|
+
return true;
|
|
1947
|
+
}
|
|
1948
|
+
var CLEAN_SUBCOMMAND = "clean";
|
|
1949
|
+
var FORCE_LONG2 = "--force";
|
|
1950
|
+
var FORCE_LETTER2 = "f";
|
|
1951
|
+
function is_forced_clean(args) {
|
|
1952
|
+
return args.some(
|
|
1953
|
+
(argument) => argument === FORCE_LONG2 || git_argv.short_cluster_has(argument, FORCE_LETTER2)
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
var JUDGED_SUBCOMMANDS = /* @__PURE__ */ new Map([
|
|
1957
|
+
[CHECKOUT_SUBCOMMAND, is_worktree_checkout],
|
|
1958
|
+
[RESTORE_SUBCOMMAND, touches_worktree],
|
|
1959
|
+
[CLEAN_SUBCOMMAND, is_forced_clean],
|
|
1960
|
+
[STASH_SUBCOMMAND, is_unauthorized_stash]
|
|
1961
|
+
]);
|
|
1962
|
+
function is_unauthorized_segment(segment) {
|
|
1963
|
+
const call = git_argv.parse(segment);
|
|
1964
|
+
if (call === void 0) return false;
|
|
1965
|
+
return JUDGED_SUBCOMMANDS.get(call.subcommand ?? "")?.(call.args) === true;
|
|
1966
|
+
}
|
|
1967
|
+
function is_unauthorized_worktree_change(command) {
|
|
1968
|
+
return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
|
|
1969
|
+
}
|
|
1970
|
+
var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>`, a forced `git clean` and an unauthorized `git stash` discard work or mutate the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
|
|
1971
|
+
var ROW11 = {
|
|
1972
|
+
id: "worktree-mutation",
|
|
1973
|
+
is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
|
|
1974
|
+
reason: WORKTREE_MUTATION_REASON,
|
|
1975
|
+
decide: () => true
|
|
1976
|
+
};
|
|
1977
|
+
var worktree_guard = {
|
|
1978
|
+
ROW: ROW11,
|
|
1979
|
+
WORKTREE_MUTATION_REASON,
|
|
1980
|
+
is_unauthorized_worktree_change,
|
|
1981
|
+
touches_worktree
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
// scripts/rules/index-guard.ts
|
|
1985
|
+
var INDEX_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
1986
|
+
"add",
|
|
1987
|
+
"commit",
|
|
1988
|
+
"mv",
|
|
1989
|
+
"reset",
|
|
1990
|
+
"rm",
|
|
1991
|
+
"stage"
|
|
1992
|
+
]);
|
|
1993
|
+
var RESTORE_SUBCOMMAND2 = "restore";
|
|
1994
|
+
function is_index_restore(args) {
|
|
1995
|
+
return !worktree_guard.touches_worktree(args);
|
|
1996
|
+
}
|
|
1997
|
+
function is_index_segment(segment) {
|
|
1998
|
+
const call = git_argv.parse(segment);
|
|
1999
|
+
if (call === void 0) return false;
|
|
2000
|
+
if (INDEX_SUBCOMMANDS.has(call.subcommand ?? "")) return true;
|
|
2001
|
+
return call.subcommand === RESTORE_SUBCOMMAND2 && is_index_restore(call.args);
|
|
2002
|
+
}
|
|
2003
|
+
function is_index_mutation(command) {
|
|
2004
|
+
return shell_segments.segments_of(command).some((segment) => is_index_segment(segment));
|
|
2005
|
+
}
|
|
2006
|
+
var INDEX_MUTATION_REASON = '\u26D4 git index mutation: `git add` / `stage` / `commit` / `reset` / `rm` / `mv` and `git restore --staged` overwrite the user\'s staged snapshot, which `CLAUDE.md` \u2192 Git Rules forbids on your own judgement (`prompts/collaboration-workflow/operating-rules.md` \u2192 "git index \u3092\u52DD\u624B\u306B\u5909\u66F4\u3057\u306A\u3044"). The `.claude/settings.json` deny list matches prefix globs, so it misses `git -C . commit`, `git -c k=v commit` and `env git commit`; this row reads the argv instead, so the spelling does not matter. A commit goes through `pnpm josh git -y`; any other staging needs explicit current-turn user instruction, run by the user in their own terminal. **This rule fires on every occurrence, not once per run.**';
|
|
2007
|
+
var ROW12 = {
|
|
2008
|
+
id: "index-mutation",
|
|
2009
|
+
is_trigger: bash_triggers.on_bash_command(is_index_mutation),
|
|
2010
|
+
reason: INDEX_MUTATION_REASON,
|
|
2011
|
+
decide: () => true
|
|
2012
|
+
};
|
|
2013
|
+
var index_guard = { INDEX_MUTATION_REASON, ROW: ROW12, is_index_mutation };
|
|
2014
|
+
|
|
2015
|
+
// scripts/rules/protected-files.ts
|
|
2016
|
+
import path5 from "node:path";
|
|
2017
|
+
|
|
2018
|
+
// scripts/doctor/doctor-consumer.ts
|
|
2019
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2020
|
+
import path4 from "node:path";
|
|
2021
|
+
|
|
2022
|
+
// scripts/version/kit-descriptor.ts
|
|
2023
|
+
var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
|
|
2024
|
+
|
|
2025
|
+
// scripts/doctor/doctor-consumer.ts
|
|
2026
|
+
import { z as z6 } from "zod";
|
|
2027
|
+
var NODE_MODULES = "node_modules";
|
|
2028
|
+
var SETTINGS_PATH = path4.join(".claude", "settings.json");
|
|
2029
|
+
var CLAUDE_MD = "CLAUDE.md";
|
|
2030
|
+
var PLUGIN_ID = "kit@kit";
|
|
2031
|
+
var CLAUDE_MD_POINTER = `@${NODE_MODULES}/${KIT_PACKAGE_NAME}/dist/${CLAUDE_MD}`;
|
|
2032
|
+
var SECRETLINT_BIN = path4.join(NODE_MODULES, ".bin", "secretlint");
|
|
2033
|
+
var GIT_TIMEOUT_MS = 2e3;
|
|
2034
|
+
var manifest_schema = z6.object({
|
|
2035
|
+
name: z6.string().optional(),
|
|
2036
|
+
dependencies: z6.record(z6.string(), z6.string()).optional(),
|
|
2037
|
+
devDependencies: z6.record(z6.string(), z6.string()).optional()
|
|
2038
|
+
});
|
|
2039
|
+
var settings_schema = z6.object({
|
|
2040
|
+
enabledPlugins: z6.record(z6.string(), z6.boolean()).optional()
|
|
2041
|
+
});
|
|
2042
|
+
var STATE_OK = "ok";
|
|
2043
|
+
var STATE_TARGET_MISSING = "target-missing";
|
|
2044
|
+
var STATE_MISSING = "missing";
|
|
2045
|
+
function parse_manifest(root) {
|
|
2046
|
+
const content = file_reader.read_if_readable(path4.join(root, "package.json"));
|
|
2047
|
+
if (content === void 0) return void 0;
|
|
2048
|
+
const parsed = manifest_schema.safeParse(json_value.parse_or_undefined(content));
|
|
2049
|
+
return parsed.success ? parsed.data : void 0;
|
|
2050
|
+
}
|
|
2051
|
+
function lists_kit_dependency(manifest) {
|
|
2052
|
+
if (manifest === void 0) return false;
|
|
2053
|
+
return Object.hasOwn(manifest.dependencies ?? {}, KIT_PACKAGE_NAME) || Object.hasOwn(manifest.devDependencies ?? {}, KIT_PACKAGE_NAME);
|
|
2054
|
+
}
|
|
2055
|
+
function is_kit_consumer(root) {
|
|
2056
|
+
const manifest = parse_manifest(root);
|
|
2057
|
+
if (manifest?.name === KIT_PACKAGE_NAME) return false;
|
|
2058
|
+
return lists_kit_dependency(manifest) || existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME));
|
|
2059
|
+
}
|
|
2060
|
+
function is_plugin_declared(root) {
|
|
2061
|
+
const content = file_reader.read_if_readable(path4.join(root, SETTINGS_PATH));
|
|
2062
|
+
if (content === void 0) return false;
|
|
2063
|
+
const parsed = settings_schema.safeParse(json_value.parse_or_undefined(content));
|
|
2064
|
+
return parsed.success && parsed.data.enabledPlugins?.[PLUGIN_ID] === true;
|
|
2065
|
+
}
|
|
2066
|
+
function configured_hooks_path(root) {
|
|
2067
|
+
const value = git_spawn_sync.read(["-C", root, "config", "--get", "core.hooksPath"], {
|
|
2068
|
+
timeout: GIT_TIMEOUT_MS
|
|
2069
|
+
});
|
|
2070
|
+
return value === "" ? void 0 : value;
|
|
2071
|
+
}
|
|
2072
|
+
function claude_md_state(root) {
|
|
2073
|
+
const content = file_reader.read_if_readable(path4.join(root, CLAUDE_MD));
|
|
2074
|
+
if (content === void 0) return STATE_MISSING;
|
|
2075
|
+
if (!content.includes(CLAUDE_MD_POINTER)) return STATE_OK;
|
|
2076
|
+
return existsSync2(path4.join(root, NODE_MODULES, KIT_PACKAGE_NAME, "dist", CLAUDE_MD)) ? STATE_OK : STATE_TARGET_MISSING;
|
|
2077
|
+
}
|
|
2078
|
+
function is_secretlint_runnable(root) {
|
|
2079
|
+
return existsSync2(path4.join(root, SECRETLINT_BIN));
|
|
2080
|
+
}
|
|
2081
|
+
function status_line(is_ok, ok_text, warn_text) {
|
|
2082
|
+
return is_ok ? ` \u2713 ${ok_text}` : ` \u26A0 ${warn_text}`;
|
|
2083
|
+
}
|
|
2084
|
+
function plugin_line(is_declared2) {
|
|
2085
|
+
return status_line(
|
|
2086
|
+
is_declared2,
|
|
2087
|
+
"kit plugin declared (skills load in a trusted workspace; headless from its second session)",
|
|
2088
|
+
"kit plugin not declared in .claude/settings.json \u2014 run `pnpm josh sync`"
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
function hooks_path_line(hooks_path) {
|
|
2092
|
+
return status_line(
|
|
2093
|
+
hooks_path === void 0,
|
|
2094
|
+
"git hooks install normally (core.hooksPath unset)",
|
|
2095
|
+
`core.hooksPath is set (${hooks_path ?? ""}) \u2014 lefthook installs no git hooks; unset it`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
function claude_md_line(state) {
|
|
2099
|
+
if (state === STATE_OK) return " \u2713 CLAUDE.md resolves to the installed kit rules";
|
|
2100
|
+
if (state === STATE_TARGET_MISSING) {
|
|
2101
|
+
return " \u26A0 CLAUDE.md points at rules that are not installed \u2014 run `pnpm install`";
|
|
2102
|
+
}
|
|
2103
|
+
return " \u26A0 CLAUDE.md is missing \u2014 run `pnpm josh init`";
|
|
2104
|
+
}
|
|
2105
|
+
function secretlint_line(is_runnable) {
|
|
2106
|
+
return status_line(
|
|
2107
|
+
is_runnable,
|
|
2108
|
+
"secretlint is runnable",
|
|
2109
|
+
"secretlint is not installed \u2014 run `pnpm install`"
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
function consumer_setup_lines(root) {
|
|
2113
|
+
return [
|
|
2114
|
+
plugin_line(is_plugin_declared(root)),
|
|
2115
|
+
hooks_path_line(configured_hooks_path(root)),
|
|
2116
|
+
claude_md_line(claude_md_state(root)),
|
|
2117
|
+
secretlint_line(is_secretlint_runnable(root))
|
|
2118
|
+
];
|
|
2119
|
+
}
|
|
2120
|
+
function report_consumer_setup(root) {
|
|
2121
|
+
if (!is_kit_consumer(root)) return;
|
|
2122
|
+
console.info("");
|
|
2123
|
+
console.info("Consumer setup:");
|
|
2124
|
+
for (const line of consumer_setup_lines(root)) console.info(line);
|
|
2125
|
+
}
|
|
2126
|
+
var doctor_consumer = {
|
|
2127
|
+
claude_md_line,
|
|
2128
|
+
claude_md_state,
|
|
2129
|
+
configured_hooks_path,
|
|
2130
|
+
hooks_path_line,
|
|
2131
|
+
is_kit_consumer,
|
|
2132
|
+
is_plugin_declared,
|
|
2133
|
+
is_secretlint_runnable,
|
|
2134
|
+
plugin_line,
|
|
2135
|
+
report_consumer_setup,
|
|
2136
|
+
secretlint_line
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
// scripts/rules/protected-files.ts
|
|
2140
|
+
var READ_TOOL = "Read";
|
|
2141
|
+
var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write"]);
|
|
2142
|
+
var ENVIRONMENT_FILE = ".env";
|
|
2143
|
+
var SETTINGS_FILE = path5.join(".claude", "settings.json");
|
|
2144
|
+
function file_path_of(call) {
|
|
2145
|
+
if (!json_value.is_record(call.input)) return "";
|
|
2146
|
+
const value = call.input["file_path"];
|
|
2147
|
+
return typeof value === "string" ? value : "";
|
|
2148
|
+
}
|
|
2149
|
+
function is_environment_read(call) {
|
|
2150
|
+
return call.name === READ_TOOL && path5.basename(file_path_of(call)) === ENVIRONMENT_FILE;
|
|
2151
|
+
}
|
|
2152
|
+
function is_consumer_settings_edit(call, is_kit_consumer2) {
|
|
2153
|
+
if (!EDIT_TOOLS2.has(call.name)) return false;
|
|
2154
|
+
const file_path = path5.resolve(file_path_of(call));
|
|
2155
|
+
if (!file_path.endsWith(`${path5.sep}${SETTINGS_FILE}`)) return false;
|
|
2156
|
+
return is_kit_consumer2(path5.dirname(path5.dirname(file_path)));
|
|
2157
|
+
}
|
|
2158
|
+
function is_protected_file_call(call, is_kit_consumer2 = doctor_consumer.is_kit_consumer) {
|
|
2159
|
+
return is_environment_read(call) || is_consumer_settings_edit(call, is_kit_consumer2);
|
|
2160
|
+
}
|
|
2161
|
+
var PROTECTED_FILE_REASON = '\u26D4 protected file: `.env` holds the Telegram bot token, so reading it puts a secret in the transcript \u2014 the scripts that need it load it in their own process. `.claude/settings.json` in a consumer repository is the deny list and the hooks every other rule rests on, so a run must not rewrite it; `josh sync` owns it, and a change belongs upstream in kit (`CLAUDE.md` \u2192 "Route distributed-doc / config changes upstream to kit"). Leave both files alone, or ask the user. **This rule fires on every occurrence, not once per run.**';
|
|
2162
|
+
var ROW13 = {
|
|
2163
|
+
id: "protected-file",
|
|
2164
|
+
is_trigger: is_protected_file_call,
|
|
2165
|
+
reason: PROTECTED_FILE_REASON,
|
|
2166
|
+
decide: () => true
|
|
2167
|
+
};
|
|
2168
|
+
var protected_files = { ROW: ROW13, is_protected_file_call };
|
|
2169
|
+
|
|
2170
|
+
// scripts/rules/permission-guards.ts
|
|
2171
|
+
var ROWS2 = [index_guard.ROW, destructive_command.ROW, protected_files.ROW];
|
|
2172
|
+
var permission_guards = { ROWS: ROWS2 };
|
|
1647
2173
|
|
|
1648
2174
|
// scripts/rules/piped-verification.ts
|
|
1649
2175
|
var VERIFICATION_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -1713,7 +2239,7 @@ function is_poll_loop(command) {
|
|
|
1713
2239
|
return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
|
|
1714
2240
|
}
|
|
1715
2241
|
var POLL_LOOP_REASON = '\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command\'s output, a marker file or a process \u2014 with a `sleep` or, worse, without one \u2014 and the loop does not know when the command finished. A regex that never matches waits to the harness block limit; one that matches a mid-run line exits too early \u2014 either way the command exit is not what ends the wait, and each pass spends a full-context round trip (joshuafolkken/kit#2371 measured about 45 minutes, 13% of six lanes, lost to exactly this). Do not poll the output file. A command known to take minutes \u2014 `pnpm josh gate`, `pnpm josh followup`, `pnpm josh format` \u2014 is started detached from the first with `run_in_background` set, and the harness re-invokes you when it exits: its completion notification is what ends the wait, on the task exit rather than a regex or the block limit. So issue the long command in the background and end the turn \u2014 do not arm a loop; the wake-up brings you back. To glance at interim output, `Read` the output file once, never in a sleep loop. If you are waiting on CI, `pnpm josh followup` waits on it for you. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every occurrence, not once per run.**';
|
|
1716
|
-
var
|
|
2242
|
+
var ROW14 = {
|
|
1717
2243
|
id: "poll-loop",
|
|
1718
2244
|
is_trigger: bash_triggers.on_bash_command(is_poll_loop),
|
|
1719
2245
|
reason: POLL_LOOP_REASON,
|
|
@@ -1721,7 +2247,7 @@ var ROW10 = {
|
|
|
1721
2247
|
};
|
|
1722
2248
|
var poll_loop = {
|
|
1723
2249
|
POLL_LOOP_REASON,
|
|
1724
|
-
ROW:
|
|
2250
|
+
ROW: ROW14,
|
|
1725
2251
|
is_poll_loop
|
|
1726
2252
|
};
|
|
1727
2253
|
|
|
@@ -1841,13 +2367,13 @@ function posts_at_path_literally(command) {
|
|
|
1841
2367
|
return RAW_FIELD_BODY_AT.test(command);
|
|
1842
2368
|
}
|
|
1843
2369
|
var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which sends the value verbatim \u2014 the string `@<path>` is posted as the comment, not the file it points at. `-F` / `--field` is the flag that reads the file, and the two spellings differ by one character while `gh` exits 0 and returns a comment URL either way, so the break is invisible until a person reads the issue (joshuafolkken/kit#2304 lost two park comments this way). Post an issue or PR comment with `pnpm josh issue:comment <N> --body-file <path>` \u2014 the one spelling, which cannot take the wrong flag. The rule and the safe forms are in `prompts/collaboration-workflow/shell-body.md`. This rule fires on every occurrence, not once per run.";
|
|
1844
|
-
var
|
|
2370
|
+
var ROW15 = {
|
|
1845
2371
|
id: "raw-field-body",
|
|
1846
2372
|
is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
|
|
1847
2373
|
reason: RAW_FIELD_BODY_REASON,
|
|
1848
2374
|
decide: () => true
|
|
1849
2375
|
};
|
|
1850
|
-
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW:
|
|
2376
|
+
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW15, posts_at_path_literally };
|
|
1851
2377
|
|
|
1852
2378
|
// scripts/rules/rule-body-guard.ts
|
|
1853
2379
|
var EDIT_TOOL = "Edit";
|
|
@@ -1918,7 +2444,7 @@ function runs_placement_oracle(command) {
|
|
|
1918
2444
|
(names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
|
|
1919
2445
|
);
|
|
1920
2446
|
}
|
|
1921
|
-
var
|
|
2447
|
+
var ROW16 = {
|
|
1922
2448
|
id: "rule-body",
|
|
1923
2449
|
is_trigger: writes_rule_prose,
|
|
1924
2450
|
reason: RULE_BODY_REASON,
|
|
@@ -1927,12 +2453,9 @@ var ROW12 = {
|
|
|
1927
2453
|
};
|
|
1928
2454
|
var rule_body_guard = {
|
|
1929
2455
|
RULE_BODY_REASON,
|
|
1930
|
-
ROW:
|
|
1931
|
-
adds_rule_prose,
|
|
2456
|
+
ROW: ROW16,
|
|
1932
2457
|
answered_placement,
|
|
1933
2458
|
is_rule_document,
|
|
1934
|
-
prose_of,
|
|
1935
|
-
runs_placement_oracle,
|
|
1936
2459
|
writes_rule_prose
|
|
1937
2460
|
};
|
|
1938
2461
|
|
|
@@ -2002,27 +2525,17 @@ var SHELL_BODY_REASON = "\u26D4 shell-evaluated body: this command carries a bod
|
|
|
2002
2525
|
var shell_body_trigger = {
|
|
2003
2526
|
SHELL_BODY_REASON,
|
|
2004
2527
|
carries_a_body,
|
|
2005
|
-
is_safe_body_form,
|
|
2006
2528
|
is_shell_evaluated_body,
|
|
2007
2529
|
keeps_body_safe
|
|
2008
2530
|
};
|
|
2009
2531
|
|
|
2010
|
-
// scripts/test/test-declared-changed.ts
|
|
2011
|
-
import { execFileSync } from "node:child_process";
|
|
2012
|
-
|
|
2013
2532
|
// scripts/gate/project-checks.ts
|
|
2014
|
-
import { existsSync as
|
|
2015
|
-
import
|
|
2016
|
-
|
|
2017
|
-
// scripts/init/project-profile.ts
|
|
2018
|
-
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3 } from "node:fs";
|
|
2019
|
-
import path4 from "node:path";
|
|
2020
|
-
|
|
2021
|
-
// scripts/version/kit-descriptor.ts
|
|
2022
|
-
var KIT_PACKAGE_NAME = "@joshuafolkken/kit";
|
|
2533
|
+
import { existsSync as existsSync4, globSync as globSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
2534
|
+
import path7 from "node:path";
|
|
2023
2535
|
|
|
2024
2536
|
// scripts/init/project-profile.ts
|
|
2025
|
-
import {
|
|
2537
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "node:fs";
|
|
2538
|
+
import path6 from "node:path";
|
|
2026
2539
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".svelte-kit"]);
|
|
2027
2540
|
var WEB_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".css", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
2028
2541
|
var TYPESCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx"]);
|
|
@@ -2054,26 +2567,21 @@ function requested_profile(args, usage = "josh init [--profile basic|full]") {
|
|
|
2054
2567
|
throw new Error("Profile must be basic or full");
|
|
2055
2568
|
}
|
|
2056
2569
|
function read_manifest(root) {
|
|
2057
|
-
const filename =
|
|
2058
|
-
if (!
|
|
2570
|
+
const filename = path6.join(root, "package.json");
|
|
2571
|
+
if (!existsSync3(filename)) return void 0;
|
|
2059
2572
|
const parsed = JSON.parse(readFileSync3(filename, "utf8"));
|
|
2060
|
-
if (
|
|
2061
|
-
throw new Error("package.json must contain an object");
|
|
2062
|
-
}
|
|
2573
|
+
if (!json_value.is_record(parsed)) throw new Error("package.json must contain an object");
|
|
2063
2574
|
return parsed;
|
|
2064
2575
|
}
|
|
2065
|
-
function is_record(value) {
|
|
2066
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2067
|
-
}
|
|
2068
2576
|
function recorded_profile(manifest) {
|
|
2069
2577
|
const { josh } = manifest;
|
|
2070
|
-
return is_record(josh) ? parse_profile(josh["profile"]) : void 0;
|
|
2578
|
+
return json_value.is_record(josh) ? parse_profile(josh["profile"]) : void 0;
|
|
2071
2579
|
}
|
|
2072
2580
|
function has_build_script(value) {
|
|
2073
|
-
return is_record(value) && ("build" in value || "dev" in value);
|
|
2581
|
+
return json_value.is_record(value) && ("build" in value || "dev" in value);
|
|
2074
2582
|
}
|
|
2075
2583
|
function has_project_dependencies(value) {
|
|
2076
|
-
if (!is_record(value)) return false;
|
|
2584
|
+
if (!json_value.is_record(value)) return false;
|
|
2077
2585
|
return Object.keys(value).some((name) => name !== KIT_PACKAGE_NAME);
|
|
2078
2586
|
}
|
|
2079
2587
|
function inferred_profile(manifest) {
|
|
@@ -2099,9 +2607,9 @@ function should_visit(entry) {
|
|
|
2099
2607
|
function scan_directory(directory, pending) {
|
|
2100
2608
|
const entries = readdirSync(directory, { withFileTypes: true });
|
|
2101
2609
|
pending.push(
|
|
2102
|
-
...entries.filter((entry) => should_visit(entry)).map((entry) =>
|
|
2610
|
+
...entries.filter((entry) => should_visit(entry)).map((entry) => path6.join(directory, entry.name))
|
|
2103
2611
|
);
|
|
2104
|
-
return entries.filter((entry) => entry.isFile()).map((entry) =>
|
|
2612
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => path6.extname(entry.name));
|
|
2105
2613
|
}
|
|
2106
2614
|
function file_extensions(root) {
|
|
2107
2615
|
const pending = [root];
|
|
@@ -2118,16 +2626,13 @@ function scan_files(root) {
|
|
|
2118
2626
|
return { has_web, has_typescript };
|
|
2119
2627
|
}
|
|
2120
2628
|
function has_github_remote(root) {
|
|
2121
|
-
const
|
|
2122
|
-
|
|
2123
|
-
reject: false
|
|
2124
|
-
});
|
|
2125
|
-
return result.exitCode === 0 && repo_origin.parse_origin_url(result.stdout) !== void 0;
|
|
2629
|
+
const url = git_spawn_sync.origin_url(root);
|
|
2630
|
+
return url !== void 0 && repo_origin.parse_origin_url(url) !== void 0;
|
|
2126
2631
|
}
|
|
2127
2632
|
function inspect_project(root, requested) {
|
|
2128
2633
|
const profile = resolve_profile(root, requested);
|
|
2129
2634
|
const files = scan_files(root);
|
|
2130
|
-
const has_git =
|
|
2635
|
+
const has_git = existsSync3(path6.join(root, ".git"));
|
|
2131
2636
|
return { ...profile, ...files, has_git, has_github: has_git && has_github_remote(root) };
|
|
2132
2637
|
}
|
|
2133
2638
|
var project_profile = {
|
|
@@ -2142,7 +2647,7 @@ var project_profile = {
|
|
|
2142
2647
|
var SKIP_MARKER = "\u2014 skipping";
|
|
2143
2648
|
|
|
2144
2649
|
// scripts/gate/project-checks.ts
|
|
2145
|
-
import { z as
|
|
2650
|
+
import { z as z7 } from "zod";
|
|
2146
2651
|
var PACKAGE_JSON = "package.json";
|
|
2147
2652
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
2148
2653
|
"node_modules",
|
|
@@ -2177,11 +2682,11 @@ var CSPELL_CONFIGS = [
|
|
|
2177
2682
|
];
|
|
2178
2683
|
var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
|
|
2179
2684
|
var DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
|
|
2180
|
-
var object_schema =
|
|
2685
|
+
var object_schema = z7.record(z7.string(), z7.unknown());
|
|
2181
2686
|
function project_root(directory) {
|
|
2182
2687
|
let current = directory;
|
|
2183
|
-
while (!
|
|
2184
|
-
const parent =
|
|
2688
|
+
while (!existsSync4(path7.join(current, PACKAGE_JSON))) {
|
|
2689
|
+
const parent = path7.dirname(current);
|
|
2185
2690
|
if (parent === current) return directory;
|
|
2186
2691
|
current = parent;
|
|
2187
2692
|
}
|
|
@@ -2194,24 +2699,23 @@ function has_files(directory, pattern) {
|
|
|
2194
2699
|
return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
|
|
2195
2700
|
}
|
|
2196
2701
|
function has_config(directory, names) {
|
|
2197
|
-
return names.some((name) =>
|
|
2702
|
+
return names.some((name) => existsSync4(path7.join(project_root(directory), name)));
|
|
2198
2703
|
}
|
|
2199
2704
|
function has_bin(directory, bin_name) {
|
|
2200
2705
|
return find_local_bin_upwards(directory, bin_name) !== void 0;
|
|
2201
2706
|
}
|
|
2202
2707
|
function josh_settings(manifest) {
|
|
2203
|
-
|
|
2204
|
-
return manifest.josh;
|
|
2708
|
+
return json_value.is_record(manifest) ? manifest["josh"] : void 0;
|
|
2205
2709
|
}
|
|
2206
2710
|
function is_basic_manifest(manifest) {
|
|
2207
2711
|
const josh = josh_settings(manifest);
|
|
2208
|
-
if (
|
|
2209
|
-
return project_profile.parse_profile(josh
|
|
2712
|
+
if (!json_value.is_record(josh)) return false;
|
|
2713
|
+
return project_profile.parse_profile(josh["profile"]) === "basic";
|
|
2210
2714
|
}
|
|
2211
2715
|
function read_manifest2(directory) {
|
|
2212
2716
|
try {
|
|
2213
|
-
const manifest_path =
|
|
2214
|
-
return
|
|
2717
|
+
const manifest_path = path7.join(project_root(directory), PACKAGE_JSON);
|
|
2718
|
+
return json_value.parse_or_undefined(readFileSync4(manifest_path, "utf8"));
|
|
2215
2719
|
} catch {
|
|
2216
2720
|
return void 0;
|
|
2217
2721
|
}
|
|
@@ -2231,8 +2735,8 @@ function missing_tool_reason(directory, package_name, bin_name) {
|
|
|
2231
2735
|
if (has_bin(directory, bin_name) || is_declared(directory, package_name)) return void 0;
|
|
2232
2736
|
return `${package_name} is not installed`;
|
|
2233
2737
|
}
|
|
2234
|
-
function skip_notice(
|
|
2235
|
-
return `josh ${
|
|
2738
|
+
function skip_notice(check4, reason) {
|
|
2739
|
+
return `josh ${check4}: ${reason} ${SKIP_MARKER} ${check4}.`;
|
|
2236
2740
|
}
|
|
2237
2741
|
function type_check_skip_reason(directory) {
|
|
2238
2742
|
if (!is_basic(directory)) return void 0;
|
|
@@ -2254,8 +2758,6 @@ function eslint_skip_reason(directory) {
|
|
|
2254
2758
|
var project_checks = {
|
|
2255
2759
|
CSPELL_CONFIGS,
|
|
2256
2760
|
ESLINT_CONFIGS,
|
|
2257
|
-
SCRIPT_FILES,
|
|
2258
|
-
TYPE_CONFIGS,
|
|
2259
2761
|
TYPE_FILES,
|
|
2260
2762
|
WEB_FILES,
|
|
2261
2763
|
eslint_skip_reason,
|
|
@@ -2293,46 +2795,45 @@ var BASIC_REQUIRED_SUFFIXES = [
|
|
|
2293
2795
|
".toml"
|
|
2294
2796
|
];
|
|
2295
2797
|
function normalize(paths) {
|
|
2296
|
-
return paths.map((
|
|
2798
|
+
return paths.map((path10) => path10.trim()).filter((path10) => path10 !== "");
|
|
2297
2799
|
}
|
|
2298
|
-
function is_test_file(
|
|
2299
|
-
return TEST_SUFFIXES.some((suffix) =>
|
|
2800
|
+
function is_test_file(path10) {
|
|
2801
|
+
return TEST_SUFFIXES.some((suffix) => path10.endsWith(suffix));
|
|
2300
2802
|
}
|
|
2301
|
-
function is_basic_manual(
|
|
2302
|
-
const lower =
|
|
2803
|
+
function is_basic_manual(path10, is_basic2) {
|
|
2804
|
+
const lower = path10.toLowerCase();
|
|
2303
2805
|
return is_basic2 && BASIC_REQUIRED_SUFFIXES.every((suffix) => !lower.endsWith(suffix));
|
|
2304
2806
|
}
|
|
2305
|
-
function is_basic_visual(
|
|
2306
|
-
const lower =
|
|
2807
|
+
function is_basic_visual(path10) {
|
|
2808
|
+
const lower = path10.toLowerCase();
|
|
2307
2809
|
return BASIC_VISUAL_SUFFIXES.some((suffix) => lower.endsWith(suffix));
|
|
2308
2810
|
}
|
|
2309
|
-
function is_exempt(
|
|
2310
|
-
if (EXEMPT_PATHS.includes(
|
|
2311
|
-
return EXEMPT_SUFFIXES.some((suffix) =>
|
|
2811
|
+
function is_exempt(path10, is_basic2 = false) {
|
|
2812
|
+
if (EXEMPT_PATHS.includes(path10) || is_basic_manual(path10, is_basic2)) return true;
|
|
2813
|
+
return EXEMPT_SUFFIXES.some((suffix) => path10.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path10.startsWith(prefix));
|
|
2312
2814
|
}
|
|
2313
|
-
function is_runtime(
|
|
2314
|
-
return !is_test_file(
|
|
2815
|
+
function is_runtime(path10, is_basic2 = false) {
|
|
2816
|
+
return !is_test_file(path10) && !is_exempt(path10, is_basic2);
|
|
2315
2817
|
}
|
|
2316
2818
|
function verdict_for(paths, is_basic2 = false) {
|
|
2317
2819
|
const changed = normalize(paths);
|
|
2318
|
-
if (changed.some((
|
|
2319
|
-
if (changed.some((
|
|
2820
|
+
if (changed.some((path10) => is_test_file(path10))) return "satisfied";
|
|
2821
|
+
if (changed.some((path10) => is_runtime(path10, is_basic2))) return "required";
|
|
2320
2822
|
return "exempt";
|
|
2321
2823
|
}
|
|
2322
2824
|
function runtime_files(paths, is_basic2 = false) {
|
|
2323
|
-
return normalize(paths).filter((
|
|
2825
|
+
return normalize(paths).filter((path10) => is_runtime(path10, is_basic2));
|
|
2324
2826
|
}
|
|
2325
2827
|
function exempt_files(paths, is_basic2 = false) {
|
|
2326
|
-
return normalize(paths).filter((
|
|
2828
|
+
return normalize(paths).filter((path10) => is_exempt(path10, is_basic2));
|
|
2327
2829
|
}
|
|
2328
2830
|
function manual_check_files(paths, is_basic2) {
|
|
2329
|
-
return normalize(paths).filter((
|
|
2831
|
+
return normalize(paths).filter((path10) => is_exempt(path10, is_basic2) && !is_exempt(path10));
|
|
2330
2832
|
}
|
|
2331
2833
|
var test_declared_logic = {
|
|
2332
2834
|
EXEMPT_PATHS,
|
|
2333
2835
|
EXEMPT_PREFIXES,
|
|
2334
2836
|
EXEMPT_SUFFIXES,
|
|
2335
|
-
TEST_SUFFIXES,
|
|
2336
2837
|
exempt_files,
|
|
2337
2838
|
is_basic_visual,
|
|
2338
2839
|
is_exempt,
|
|
@@ -2352,15 +2853,9 @@ function path_of(line) {
|
|
|
2352
2853
|
return arrow === -1 ? body : body.slice(arrow + RENAME_ARROW.length);
|
|
2353
2854
|
}
|
|
2354
2855
|
function read_changed_paths_sync() {
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
2359
|
-
});
|
|
2360
|
-
return output.split("\n").filter((line) => line.length > STATUS_PREFIX_LENGTH).map((line) => path_of(line));
|
|
2361
|
-
} catch {
|
|
2362
|
-
return [];
|
|
2363
|
-
}
|
|
2856
|
+
const output = git_spawn_sync.run(["status", PORCELAIN_FLAG], { timeout: 0 });
|
|
2857
|
+
if (output.exit_code !== 0) return [];
|
|
2858
|
+
return output.stdout.split("\n").filter((line) => line.length > STATUS_PREFIX_LENGTH).map((line) => path_of(line));
|
|
2364
2859
|
}
|
|
2365
2860
|
var injected = {};
|
|
2366
2861
|
function source_paths() {
|
|
@@ -2393,8 +2888,8 @@ function is_untested_commit(call) {
|
|
|
2393
2888
|
return test_declared_changed.current_verdict() === "required";
|
|
2394
2889
|
}
|
|
2395
2890
|
var REASON = '\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads the working tree as `required` (a changed file that is neither a test \u2014 `*.test.ts` / `*.e2e.ts` \u2014 nor a mechanically-exempt non-runtime path). `CLAUDE.md` \u2192 Code Change Rules requires a test for ALL code changes: a bug fix gets a regression test, a refactor gets tests pinning behavior first, logic gets a unit test, an observable UI/timing change gets an E2E. Add the declared test and reissue this commit. **If the change genuinely touches no executable runtime path** it is the pre-approved non-runtime exception \u2014 declare it in your Step 0 work summary with the reason it is exempt (non-executable config, a cosmetic asset swap) and reissue; the exception is a presentation, not a stop. Run `pnpm josh test:declared` to see the files it names. The procedure is `prompts/collaboration-workflow/rule-delivery.md` \u2192 "\u914D\u9001\u3055\u308C\u3066\u3044\u308B\u898F\u5247". This fires once per run, so reissuing will not repeat on the call in hand.';
|
|
2396
|
-
var
|
|
2397
|
-
var test_declared_commit = { REASON, ROW:
|
|
2891
|
+
var ROW17 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
|
|
2892
|
+
var test_declared_commit = { REASON, ROW: ROW17 };
|
|
2398
2893
|
|
|
2399
2894
|
// scripts/rules/third-party-write.ts
|
|
2400
2895
|
function is_third_party_write_segment(segment, session_owner) {
|
|
@@ -2410,101 +2905,38 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner) {
|
|
|
2410
2905
|
return targets.some((segment) => is_third_party_write_segment(segment, session_owner));
|
|
2411
2906
|
}
|
|
2412
2907
|
var THIRD_PARTY_WRITE_REASON = '\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session \u2014 a tracker we do not own is Tier C, and every write there (Issue, comment, PR, Discussion, review) needs explicit current-turn user instruction; typing the target, or holding a correct diagnosis, is not that instruction (`CLAUDE.md` \u2192 "Third-party repositories are Tier C", `prompts/collaboration-workflow/upstream-interrupt.md`). Confirm the party with `pnpm josh repo:party <owner/repo>` \u2014 owner equality, computed not judged. A read (`-X GET`, or no field flag) passes untouched; only a write (`-f`/`-F`/`--field`/`--raw-field`/`--input`, or `-X POST|PATCH|PUT|DELETE`) is refused. Record the finding on our own Issue and draft the artifact for a person to post \u2014 do not publish it yourself. **This rule fires on every occurrence, not once per run.**';
|
|
2413
|
-
var
|
|
2908
|
+
var ROW18 = {
|
|
2414
2909
|
id: "third-party-write",
|
|
2415
2910
|
is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
|
|
2416
2911
|
reason: THIRD_PARTY_WRITE_REASON,
|
|
2417
2912
|
decide: () => true
|
|
2418
2913
|
};
|
|
2419
2914
|
var third_party_write = {
|
|
2420
|
-
ROW:
|
|
2915
|
+
ROW: ROW18,
|
|
2421
2916
|
THIRD_PARTY_WRITE_REASON,
|
|
2422
|
-
is_third_party_write_segment,
|
|
2423
2917
|
writes_third_party
|
|
2424
2918
|
};
|
|
2425
2919
|
|
|
2426
|
-
// scripts/rules/
|
|
2427
|
-
var
|
|
2428
|
-
var
|
|
2429
|
-
var
|
|
2430
|
-
var
|
|
2431
|
-
|
|
2432
|
-
|
|
2920
|
+
// scripts/rules/delivered-rules.ts
|
|
2921
|
+
var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
|
|
2922
|
+
var STAMP_PREFIX = "josh-rule-guard-";
|
|
2923
|
+
var { is_issue_filing, on_bash_command } = bash_triggers;
|
|
2924
|
+
var WIP_CAP = 30;
|
|
2925
|
+
var WIP_CAP_REASON = `\u26D4 backlog WIP cap: count the target repository's open Issues before filing. With more than ${String(WIP_CAP)} open, close one first; nothing honestly closable means do not file. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by, and an interrupt, decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. The count command and both procedures are in \`prompts/collaboration-workflow/wip-cap.md\`. Reissue this call once you have counted \u2014 it fires once per run and cannot repeat on the call in hand.`;
|
|
2926
|
+
var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
|
|
2927
|
+
var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
|
|
2928
|
+
var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
|
|
2929
|
+
var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
|
|
2930
|
+
var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
|
|
2931
|
+
var NAMES_THE_BODY = /\bbody\b/u;
|
|
2932
|
+
function projects_away_body(segment) {
|
|
2933
|
+
const projection = FIELD_PROJECTION.exec(segment);
|
|
2934
|
+
return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
|
|
2433
2935
|
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
function touches_worktree(args) {
|
|
2439
|
-
const is_staged = args.some(
|
|
2440
|
-
(argument) => argument === STAGED_LONG || git_argv.short_cluster_has(argument, STAGED_LETTER)
|
|
2441
|
-
);
|
|
2442
|
-
const is_worktree = args.some(
|
|
2443
|
-
(argument) => argument === WORKTREE_LONG || git_argv.short_cluster_has(argument, WORKTREE_LETTER)
|
|
2444
|
-
);
|
|
2445
|
-
return !is_staged || is_worktree;
|
|
2446
|
-
}
|
|
2447
|
-
var PUSH_STASH = "push";
|
|
2448
|
-
var MESSAGE_LONG = "--message";
|
|
2449
|
-
var MESSAGE_SHORT_LETTER = "m";
|
|
2450
|
-
var READ_ONLY_STASH = /* @__PURE__ */ new Set(["list", "show"]);
|
|
2451
|
-
function has_message(args) {
|
|
2452
|
-
return args.some(
|
|
2453
|
-
(argument) => argument === MESSAGE_LONG || argument.startsWith(`${MESSAGE_LONG}=`) || git_argv.short_cluster_has(argument, MESSAGE_SHORT_LETTER)
|
|
2454
|
-
);
|
|
2455
|
-
}
|
|
2456
|
-
function is_unauthorized_stash(args) {
|
|
2457
|
-
const [stash_subcommand, ...rest] = args;
|
|
2458
|
-
if (stash_subcommand === void 0) return true;
|
|
2459
|
-
if (READ_ONLY_STASH.has(stash_subcommand)) return false;
|
|
2460
|
-
if (stash_subcommand === PUSH_STASH) return !has_message(rest);
|
|
2461
|
-
return true;
|
|
2462
|
-
}
|
|
2463
|
-
function is_unauthorized_segment(segment) {
|
|
2464
|
-
const call = git_argv.parse(segment);
|
|
2465
|
-
if (call === void 0) return false;
|
|
2466
|
-
if (call.subcommand === CHECKOUT_SUBCOMMAND) return is_worktree_checkout(call.args);
|
|
2467
|
-
if (call.subcommand === RESTORE_SUBCOMMAND) return touches_worktree(call.args);
|
|
2468
|
-
return call.subcommand === STASH_SUBCOMMAND && is_unauthorized_stash(call.args);
|
|
2469
|
-
}
|
|
2470
|
-
function is_unauthorized_worktree_change(command) {
|
|
2471
|
-
return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
|
|
2472
|
-
}
|
|
2473
|
-
var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>` and an unauthorized `git stash` mutate the working tree or the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
|
|
2474
|
-
var ROW15 = {
|
|
2475
|
-
id: "worktree-mutation",
|
|
2476
|
-
is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
|
|
2477
|
-
reason: WORKTREE_MUTATION_REASON,
|
|
2478
|
-
decide: () => true
|
|
2479
|
-
};
|
|
2480
|
-
var worktree_guard = {
|
|
2481
|
-
ROW: ROW15,
|
|
2482
|
-
WORKTREE_MUTATION_REASON,
|
|
2483
|
-
is_unauthorized_stash,
|
|
2484
|
-
is_unauthorized_worktree_change,
|
|
2485
|
-
is_worktree_checkout,
|
|
2486
|
-
touches_worktree
|
|
2487
|
-
};
|
|
2488
|
-
|
|
2489
|
-
// scripts/rules/delivered-rules.ts
|
|
2490
|
-
var SWITCH_ENV_KEY = "JOSH_RULE_GUARD";
|
|
2491
|
-
var STAMP_PREFIX = "josh-rule-guard-";
|
|
2492
|
-
var { is_issue_filing, on_bash_command } = bash_triggers;
|
|
2493
|
-
var WIP_CAP_REASON = "\u26D4 backlog WIP cap: count the target repository's open Issues before filing. With more than 30 open, close one first; nothing honestly closable means do not file. Two filings are exempt and proceed while stating the overage \u2014 one the run is blocked by, and an interrupt, decided by three tests rather than judgement: a verification answers wrongly, a documented workflow cannot complete, or data is lost or written outside the repository. Meeting none of the three, the finding is discretionary and waits. The count command and both procedures are in `prompts/collaboration-workflow/wip-cap.md`. Reissue this call once you have counted \u2014 it fires once per run and cannot repeat on the call in hand.";
|
|
2494
|
-
var ISSUE_VIEW_COMMAND = new RegExp(String.raw`^gh\s+${gh_api.GH_FLAGS}issue\s+view\s`, "u");
|
|
2495
|
-
var ISSUE_BODY_PATH = /repos\/[^\s'"]*\/issues\/\d+(?=$|["'\s])/u;
|
|
2496
|
-
var FETCHES_COMMENTS = /--comments\b|--json\s[\w,]*\bcomments\b|\/comments\b/u;
|
|
2497
|
-
var ISSUES_PATH = /repos\/[^\s'"]*\/issues\//u;
|
|
2498
|
-
var FIELD_PROJECTION = /(?:--jq|--json)[= ]\s*('[^']*'|"[^"]*"|\S+)/u;
|
|
2499
|
-
var NAMES_THE_BODY = /\bbody\b/u;
|
|
2500
|
-
function projects_away_body(segment) {
|
|
2501
|
-
const projection = FIELD_PROJECTION.exec(segment);
|
|
2502
|
-
return projection !== null && !NAMES_THE_BODY.test(projection[1] ?? "");
|
|
2503
|
-
}
|
|
2504
|
-
function is_body_read_segment(segment) {
|
|
2505
|
-
if (projects_away_body(segment)) return false;
|
|
2506
|
-
if (ISSUE_VIEW_COMMAND.test(segment)) return true;
|
|
2507
|
-
return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
|
|
2936
|
+
function is_body_read_segment(segment) {
|
|
2937
|
+
if (projects_away_body(segment)) return false;
|
|
2938
|
+
if (ISSUE_VIEW_COMMAND.test(segment)) return true;
|
|
2939
|
+
return gh_api.is_gh_api(segment) && ISSUE_BODY_PATH.test(segment) && gh_api.is_read(segment);
|
|
2508
2940
|
}
|
|
2509
2941
|
function fetches_issue_comments(segment) {
|
|
2510
2942
|
if (!FETCHES_COMMENTS.test(segment)) return false;
|
|
@@ -2727,6 +3159,10 @@ var DELIVERED_RULES = [
|
|
|
2727
3159
|
git_force.ROW,
|
|
2728
3160
|
worktree_guard.ROW,
|
|
2729
3161
|
file_body.ROW,
|
|
3162
|
+
// **The deny-list bypasses closed by argv and path rather than glob** (joshuafolkken/kit#2983) — an
|
|
3163
|
+
// index mutation, a recursive forced `rm` or destructive `gh` call, and a protected file. Each
|
|
3164
|
+
// claims a command or file no row above does and refuses every occurrence (`permission-guards.ts`).
|
|
3165
|
+
...permission_guards.ROWS,
|
|
2730
3166
|
// **The hand-written wait loop a lane child improvises over a backgrounded command's output**
|
|
2731
3167
|
// (joshuafolkken/kit#2371). `early-heartbeat` refuses a bare `sleep` and stands down on any loop
|
|
2732
3168
|
// keyword; this is the loop it left — a `while`/`until` that sleeps between probes of an output
|
|
@@ -2819,12 +3255,12 @@ function is_first_delivery(tail, call, delivered_at_ms, run2) {
|
|
|
2819
3255
|
return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run2);
|
|
2820
3256
|
}
|
|
2821
3257
|
function delivery_decision(rule) {
|
|
2822
|
-
const { decide:
|
|
2823
|
-
if (
|
|
3258
|
+
const { decide: decide6, already_satisfied } = rule;
|
|
3259
|
+
if (decide6 === void 0 && already_satisfied === void 0) return is_first_delivery;
|
|
2824
3260
|
return function should_block(tail, call, delivered_at_ms, run2) {
|
|
2825
3261
|
if (already_satisfied?.(tail, call) === true) return false;
|
|
2826
|
-
if (
|
|
2827
|
-
return
|
|
3262
|
+
if (decide6 === void 0) return true;
|
|
3263
|
+
return decide6(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
|
|
2828
3264
|
};
|
|
2829
3265
|
}
|
|
2830
3266
|
function guard_of(rule) {
|
|
@@ -2870,6 +3306,7 @@ var delivered_rules = {
|
|
|
2870
3306
|
SHELL_BODY_REASON: SHELL_BODY_REASON2,
|
|
2871
3307
|
SWITCH_ENV_KEY,
|
|
2872
3308
|
THIRD_PARTY_WRITE_REASON: third_party_write.THIRD_PARTY_WRITE_REASON,
|
|
3309
|
+
WIP_CAP,
|
|
2873
3310
|
WIP_CAP_REASON,
|
|
2874
3311
|
WORKTREE_MUTATION_REASON: worktree_guard.WORKTREE_MUTATION_REASON,
|
|
2875
3312
|
delivery,
|
|
@@ -2880,1680 +3317,11 @@ var delivered_rules = {
|
|
|
2880
3317
|
};
|
|
2881
3318
|
|
|
2882
3319
|
// scripts/run/run-watcher-hook.ts
|
|
2883
|
-
import { z as
|
|
3320
|
+
import { z as z12 } from "zod";
|
|
2884
3321
|
|
|
2885
3322
|
// scripts/run/run-progress-read.ts
|
|
2886
3323
|
import { loadavg } from "node:os";
|
|
2887
3324
|
|
|
2888
|
-
// scripts/git/git-gh-helpers.ts
|
|
2889
|
-
function parse_pr_state_string(result) {
|
|
2890
|
-
const trimmed = result.trim();
|
|
2891
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
2892
|
-
}
|
|
2893
|
-
function is_pr_already_exists_message(error_message) {
|
|
2894
|
-
return error_message.toLowerCase().includes("already exists");
|
|
2895
|
-
}
|
|
2896
|
-
function get_error_message_with_stderr(error) {
|
|
2897
|
-
if (!(error instanceof Error)) return String(error);
|
|
2898
|
-
if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
|
|
2899
|
-
${error.stderr}`;
|
|
2900
|
-
return error.message;
|
|
2901
|
-
}
|
|
2902
|
-
function handle_pr_create_error(error) {
|
|
2903
|
-
const error_message = get_error_message_with_stderr(error);
|
|
2904
|
-
if (is_pr_already_exists_message(error_message)) {
|
|
2905
|
-
throw new Error("PR_ALREADY_EXISTS");
|
|
2906
|
-
}
|
|
2907
|
-
throw error;
|
|
2908
|
-
}
|
|
2909
|
-
var git_gh_helpers = {
|
|
2910
|
-
parse_pr_state_string,
|
|
2911
|
-
is_pr_already_exists_message,
|
|
2912
|
-
get_error_message_with_stderr,
|
|
2913
|
-
handle_pr_create_error
|
|
2914
|
-
};
|
|
2915
|
-
|
|
2916
|
-
// scripts/git/git-gh-issue-rest.ts
|
|
2917
|
-
import { z as z7 } from "zod";
|
|
2918
|
-
|
|
2919
|
-
// scripts/git/git-gh-rest-state.ts
|
|
2920
|
-
var MERGED_STATE = "MERGED";
|
|
2921
|
-
function to_gh_state(state) {
|
|
2922
|
-
return state?.toUpperCase();
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
// scripts/git/schemas.ts
|
|
2926
|
-
import { z as z6 } from "zod";
|
|
2927
|
-
var package_name_schema = z6.object({ name: z6.string().min(1) });
|
|
2928
|
-
var rollup_item_schema = z6.looseObject({
|
|
2929
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
|
|
2930
|
-
__typename: z6.string().optional(),
|
|
2931
|
-
state: z6.string().optional(),
|
|
2932
|
-
status: z6.string().optional(),
|
|
2933
|
-
conclusion: z6.string().optional(),
|
|
2934
|
-
name: z6.string().optional(),
|
|
2935
|
-
context: z6.string().optional()
|
|
2936
|
-
});
|
|
2937
|
-
var pr_raw_schema = z6.object({
|
|
2938
|
-
statusCheckRollup: z6.array(rollup_item_schema).optional(),
|
|
2939
|
-
mergeStateStatus: z6.string().optional(),
|
|
2940
|
-
reviewDecision: z6.string().optional()
|
|
2941
|
-
});
|
|
2942
|
-
var pull_comment_schema = z6.object({
|
|
2943
|
-
body: z6.string().optional(),
|
|
2944
|
-
html_url: z6.string().optional(),
|
|
2945
|
-
user: z6.object({ login: z6.string().optional() }).optional()
|
|
2946
|
-
});
|
|
2947
|
-
var ai_review_pull_comment_schema = z6.object({
|
|
2948
|
-
body: z6.string().optional(),
|
|
2949
|
-
url: z6.string().optional(),
|
|
2950
|
-
author: z6.object({ login: z6.string().optional() }).optional()
|
|
2951
|
-
});
|
|
2952
|
-
var pr_info_schema = z6.object({
|
|
2953
|
-
mergeable: z6.union([z6.boolean(), z6.string()]).nullable().optional(),
|
|
2954
|
-
mergeStateStatus: z6.string().nullable().optional(),
|
|
2955
|
-
state: z6.string().optional()
|
|
2956
|
-
});
|
|
2957
|
-
var epic_issue_schema = z6.object({
|
|
2958
|
-
number: z6.number(),
|
|
2959
|
-
body: z6.string().optional()
|
|
2960
|
-
});
|
|
2961
|
-
var blocking_issue_schema = z6.object({
|
|
2962
|
-
number: z6.number(),
|
|
2963
|
-
state: z6.string().optional(),
|
|
2964
|
-
repository_url: z6.string().optional()
|
|
2965
|
-
});
|
|
2966
|
-
var blocked_by_schema = z6.object({
|
|
2967
|
-
nodes: z6.array(blocking_issue_schema).default([]),
|
|
2968
|
-
totalCount: z6.number().optional()
|
|
2969
|
-
}).optional();
|
|
2970
|
-
var epic_child_schema = z6.object({
|
|
2971
|
-
state: z6.string().optional(),
|
|
2972
|
-
blockedBy: blocked_by_schema
|
|
2973
|
-
});
|
|
2974
|
-
var rest_comment_schema = z6.looseObject({
|
|
2975
|
-
body: z6.string().nullish(),
|
|
2976
|
-
html_url: z6.string().optional(),
|
|
2977
|
-
user: z6.looseObject({ login: z6.string().optional() }).nullish()
|
|
2978
|
-
});
|
|
2979
|
-
var issue_label_schema = z6.object({ name: z6.string() });
|
|
2980
|
-
var epic_subject_schema = z6.object({
|
|
2981
|
-
number: z6.number(),
|
|
2982
|
-
state: z6.string().optional(),
|
|
2983
|
-
labels: z6.array(issue_label_schema).optional(),
|
|
2984
|
-
body: z6.string().optional()
|
|
2985
|
-
});
|
|
2986
|
-
var open_issue_schema = z6.object({
|
|
2987
|
-
number: z6.number(),
|
|
2988
|
-
title: z6.string(),
|
|
2989
|
-
labels: z6.array(issue_label_schema).optional(),
|
|
2990
|
-
createdAt: z6.string(),
|
|
2991
|
-
blockedBy: blocked_by_schema
|
|
2992
|
-
});
|
|
2993
|
-
|
|
2994
|
-
// scripts/git/git-gh-issue-rest.ts
|
|
2995
|
-
var rest_issue_schema = z7.looseObject({
|
|
2996
|
-
number: z7.number(),
|
|
2997
|
-
issue_dependencies_summary: z7.looseObject({ total_blocked_by: z7.number().optional() }).nullish(),
|
|
2998
|
-
// Present only on a pull request, which this endpoint serves as readily as an issue.
|
|
2999
|
-
pull_request: z7.looseObject({ merged_at: z7.string().nullish() }).nullish()
|
|
3000
|
-
});
|
|
3001
|
-
var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
|
|
3002
|
-
var REPOSITORY_URL_GROUP = 1;
|
|
3003
|
-
var BLOCKED_BY_FIELD = "blockedBy";
|
|
3004
|
-
var BLOCKED_BY_COUNT_FIELD = "blocked_by_count";
|
|
3005
|
-
var STATE_FIELD = "state";
|
|
3006
|
-
var BODY_FIELD = "body";
|
|
3007
|
-
var FIELD_SEPARATOR = ",";
|
|
3008
|
-
var EMPTY_BODY = "";
|
|
3009
|
-
var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
|
|
3010
|
-
["url", "html_url"],
|
|
3011
|
-
["createdAt", "created_at"]
|
|
3012
|
-
]);
|
|
3013
|
-
var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
|
|
3014
|
-
var NOT_AN_ISSUE_LISTING_MESSAGE = "gh api answered something other than an issue listing";
|
|
3015
|
-
var NOT_A_BLOCKER_LISTING_MESSAGE = "gh api answered something other than a blocked-by listing";
|
|
3016
|
-
function rest_field_name(field) {
|
|
3017
|
-
return REST_FIELD_NAMES.get(field) ?? field;
|
|
3018
|
-
}
|
|
3019
|
-
function split_fields(fields) {
|
|
3020
|
-
return fields.split(FIELD_SEPARATOR).map((field) => field.trim()).filter((field) => field.length > 0);
|
|
3021
|
-
}
|
|
3022
|
-
function to_issue_state(rest, value) {
|
|
3023
|
-
if (typeof rest.pull_request?.merged_at === "string") return MERGED_STATE;
|
|
3024
|
-
return typeof value === "string" ? to_gh_state(value) : value;
|
|
3025
|
-
}
|
|
3026
|
-
function total_blocked_by(rest) {
|
|
3027
|
-
return rest.issue_dependencies_summary?.total_blocked_by;
|
|
3028
|
-
}
|
|
3029
|
-
function to_gh_field_value(field, rest) {
|
|
3030
|
-
if (field === BLOCKED_BY_COUNT_FIELD) return total_blocked_by(rest);
|
|
3031
|
-
const value = rest[rest_field_name(field)];
|
|
3032
|
-
if (field === STATE_FIELD) return to_issue_state(rest, value);
|
|
3033
|
-
return field === BODY_FIELD ? value ?? EMPTY_BODY : value;
|
|
3034
|
-
}
|
|
3035
|
-
function parse_rest_issue(rest_json) {
|
|
3036
|
-
const parsed = parse_json_object_safe(rest_json, rest_issue_schema);
|
|
3037
|
-
if (parsed === void 0) throw new Error(NOT_AN_ISSUE_MESSAGE);
|
|
3038
|
-
return parsed;
|
|
3039
|
-
}
|
|
3040
|
-
function is_pull_request(rest) {
|
|
3041
|
-
return rest.pull_request !== void 0 && rest.pull_request !== null;
|
|
3042
|
-
}
|
|
3043
|
-
function parse_rest_issues(rest_json) {
|
|
3044
|
-
const parsed = parse_json_array_or_undefined(rest_json, rest_issue_schema);
|
|
3045
|
-
if (parsed === void 0) throw new Error(NOT_AN_ISSUE_LISTING_MESSAGE);
|
|
3046
|
-
return parsed;
|
|
3047
|
-
}
|
|
3048
|
-
function empty_blocked_by() {
|
|
3049
|
-
return { nodes: [], totalCount: 0 };
|
|
3050
|
-
}
|
|
3051
|
-
function repo_of_url(repository_url) {
|
|
3052
|
-
const match = REPOSITORY_URL.exec(repository_url ?? "");
|
|
3053
|
-
return match?.[REPOSITORY_URL_GROUP];
|
|
3054
|
-
}
|
|
3055
|
-
function to_blocked_by(blockers_json, exact_total) {
|
|
3056
|
-
const parsed = parse_json_array_or_undefined(blockers_json, blocking_issue_schema);
|
|
3057
|
-
if (parsed === void 0) throw new Error(NOT_A_BLOCKER_LISTING_MESSAGE);
|
|
3058
|
-
const nodes = parsed.map((blocker) => ({ ...blocker, state: to_gh_state(blocker.state) }));
|
|
3059
|
-
return { nodes, totalCount: exact_total ?? nodes.length };
|
|
3060
|
-
}
|
|
3061
|
-
function to_gh_issue(rest, fields, blocked_by) {
|
|
3062
|
-
return Object.fromEntries(
|
|
3063
|
-
fields.map((field) => [
|
|
3064
|
-
field,
|
|
3065
|
-
field === BLOCKED_BY_FIELD ? blocked_by : to_gh_field_value(field, rest)
|
|
3066
|
-
])
|
|
3067
|
-
);
|
|
3068
|
-
}
|
|
3069
|
-
function to_field_text(value) {
|
|
3070
|
-
if (typeof value === "string") return value;
|
|
3071
|
-
return value === void 0 || value === null ? EMPTY_BODY : JSON.stringify(value);
|
|
3072
|
-
}
|
|
3073
|
-
var CLOSED = "CLOSED";
|
|
3074
|
-
function reference_of_blocker(node, fallback_repo) {
|
|
3075
|
-
const repo = repo_of_url(node.repository_url) ?? fallback_repo;
|
|
3076
|
-
if (node.state === void 0) return { repo, number: node.number };
|
|
3077
|
-
return { repo, number: node.number, state: node.state.toUpperCase() === CLOSED ? CLOSED : "OPEN" };
|
|
3078
|
-
}
|
|
3079
|
-
var git_gh_issue_rest = {
|
|
3080
|
-
empty_blocked_by,
|
|
3081
|
-
repo_of_url,
|
|
3082
|
-
reference_of_blocker,
|
|
3083
|
-
split_fields,
|
|
3084
|
-
is_pull_request,
|
|
3085
|
-
parse_rest_issue,
|
|
3086
|
-
parse_rest_issues,
|
|
3087
|
-
total_blocked_by,
|
|
3088
|
-
to_blocked_by,
|
|
3089
|
-
to_gh_issue,
|
|
3090
|
-
to_field_text
|
|
3091
|
-
};
|
|
3092
|
-
|
|
3093
|
-
// scripts/git/git-gh-reachability.ts
|
|
3094
|
-
var RATE_LIMITED_STATUS = 429;
|
|
3095
|
-
var SERVER_ERROR_FLOOR = 500;
|
|
3096
|
-
function is_retryable_status(status) {
|
|
3097
|
-
if (status >= SERVER_ERROR_FLOOR) return true;
|
|
3098
|
-
return status === RATE_LIMITED_STATUS;
|
|
3099
|
-
}
|
|
3100
|
-
function classify_status(status) {
|
|
3101
|
-
if (status === void 0) return "unreachable";
|
|
3102
|
-
return is_retryable_status(status) ? "unreachable" : "reachable";
|
|
3103
|
-
}
|
|
3104
|
-
var gh_reachability = { classify_status };
|
|
3105
|
-
|
|
3106
|
-
// scripts/git/git-gh-issue-read.ts
|
|
3107
|
-
async function read_blocked_by_listing(issue_number, repo, exact_total) {
|
|
3108
|
-
const json = await git_gh_exec.exec_gh_api({
|
|
3109
|
-
path: `${git_gh_api_path.blocked_by_api_path(issue_number, repo)}${git_gh_api_path.FULL_PAGE_QUERY}`
|
|
3110
|
-
});
|
|
3111
|
-
return git_gh_issue_rest.to_blocked_by(json, exact_total);
|
|
3112
|
-
}
|
|
3113
|
-
async function read_blocked_by(issue_number, rest, repo) {
|
|
3114
|
-
if (git_gh_issue_rest.is_pull_request(rest)) return git_gh_issue_rest.empty_blocked_by();
|
|
3115
|
-
const exact_total = git_gh_issue_rest.total_blocked_by(rest);
|
|
3116
|
-
if (exact_total === 0) return git_gh_issue_rest.empty_blocked_by();
|
|
3117
|
-
return await read_blocked_by_listing(issue_number, repo, exact_total);
|
|
3118
|
-
}
|
|
3119
|
-
function mark_followup(error) {
|
|
3120
|
-
if (!(error instanceof Error)) return error;
|
|
3121
|
-
const failure = gh_failure.failure_of(error);
|
|
3122
|
-
if (failure === void 0) return error;
|
|
3123
|
-
return gh_failure.attach(error, { ...failure, is_followup: true });
|
|
3124
|
-
}
|
|
3125
|
-
async function read_relations(issue_number, rest, repo) {
|
|
3126
|
-
try {
|
|
3127
|
-
return await read_blocked_by(issue_number, rest, repo);
|
|
3128
|
-
} catch (error) {
|
|
3129
|
-
throw mark_followup(error);
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
async function read_issue_fields(issue_number, fields, repo) {
|
|
3133
|
-
const rest = git_gh_issue_rest.parse_rest_issue(
|
|
3134
|
-
await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(issue_number, repo) })
|
|
3135
|
-
);
|
|
3136
|
-
const requested = git_gh_issue_rest.split_fields(fields);
|
|
3137
|
-
const blocked_by = requested.includes(BLOCKED_BY_FIELD) ? await read_relations(issue_number, rest, repo) : void 0;
|
|
3138
|
-
return git_gh_issue_rest.to_gh_issue(rest, requested, blocked_by);
|
|
3139
|
-
}
|
|
3140
|
-
var NOT_FOUND_STATUS = 404;
|
|
3141
|
-
function is_number_unresolved(failure) {
|
|
3142
|
-
return failure.status === NOT_FOUND_STATUS && failure.is_followup !== true;
|
|
3143
|
-
}
|
|
3144
|
-
function to_unreadable_reason(status) {
|
|
3145
|
-
return gh_reachability.classify_status(status) === "unreachable" ? "unreachable" : "rejected";
|
|
3146
|
-
}
|
|
3147
|
-
function to_read_failure(error) {
|
|
3148
|
-
const failure = gh_failure.failure_of(error);
|
|
3149
|
-
if (failure === void 0) return { kind: "unreadable", reason: "malformed", status: void 0 };
|
|
3150
|
-
if (is_number_unresolved(failure)) return { kind: "missing" };
|
|
3151
|
-
return {
|
|
3152
|
-
kind: "unreadable",
|
|
3153
|
-
reason: to_unreadable_reason(failure.status),
|
|
3154
|
-
status: failure.status
|
|
3155
|
-
};
|
|
3156
|
-
}
|
|
3157
|
-
async function issue_view_json_classified(issue_number, fields, repo) {
|
|
3158
|
-
try {
|
|
3159
|
-
const issue = await read_issue_fields(issue_number, fields, repo);
|
|
3160
|
-
return { kind: "read", json: JSON.stringify(issue) };
|
|
3161
|
-
} catch (error) {
|
|
3162
|
-
return to_read_failure(error);
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
async function issue_view_field_classified(issue_number, field, repo) {
|
|
3166
|
-
try {
|
|
3167
|
-
const issue = await read_issue_fields(issue_number, field, repo);
|
|
3168
|
-
return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
|
|
3169
|
-
} catch (error) {
|
|
3170
|
-
return to_read_failure(error);
|
|
3171
|
-
}
|
|
3172
|
-
}
|
|
3173
|
-
async function issue_view_field(issue_number, field, repo) {
|
|
3174
|
-
const read2 = await issue_view_field_classified(issue_number, field, repo);
|
|
3175
|
-
return read2.kind === "read" ? read2.text : void 0;
|
|
3176
|
-
}
|
|
3177
|
-
async function issue_get_title(issue_number, repo) {
|
|
3178
|
-
const result = await issue_view_field(issue_number, "title", repo);
|
|
3179
|
-
return result === void 0 ? void 0 : git_gh_helpers.parse_pr_state_string(result);
|
|
3180
|
-
}
|
|
3181
|
-
async function issue_get_body(issue_number, repo) {
|
|
3182
|
-
return await issue_view_field(issue_number, "body", repo);
|
|
3183
|
-
}
|
|
3184
|
-
async function issue_get_body_classified(issue_number, repo) {
|
|
3185
|
-
return await issue_view_field_classified(issue_number, "body", repo);
|
|
3186
|
-
}
|
|
3187
|
-
async function issue_view_json(issue_number, fields, repo) {
|
|
3188
|
-
const read2 = await issue_view_json_classified(issue_number, fields, repo);
|
|
3189
|
-
return read2.kind === "read" ? read2.json : void 0;
|
|
3190
|
-
}
|
|
3191
|
-
var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
|
|
3192
|
-
async function issue_get_state_and_relations(issue_number, repo) {
|
|
3193
|
-
return await issue_view_json(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
|
|
3194
|
-
}
|
|
3195
|
-
async function issue_get_state_and_relations_classified(issue_number, repo) {
|
|
3196
|
-
return await issue_view_json_classified(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
|
|
3197
|
-
}
|
|
3198
|
-
function is_unreachable_read(failure) {
|
|
3199
|
-
return failure.kind === "unreadable" && failure.reason === "unreachable";
|
|
3200
|
-
}
|
|
3201
|
-
var PLAN_FIELDS = "number,title,body,state,url,labels,blockedBy";
|
|
3202
|
-
async function issue_get_plan_fields(issue_number) {
|
|
3203
|
-
return await issue_view_json(issue_number, PLAN_FIELDS);
|
|
3204
|
-
}
|
|
3205
|
-
async function issue_get_plan_fields_classified(issue_number) {
|
|
3206
|
-
return await issue_view_json_classified(issue_number, PLAN_FIELDS);
|
|
3207
|
-
}
|
|
3208
|
-
async function issue_get_labels_and_body(issue_number) {
|
|
3209
|
-
return await issue_view_json(issue_number, "number,state,labels,body");
|
|
3210
|
-
}
|
|
3211
|
-
async function issue_list_comments(issue_number) {
|
|
3212
|
-
const path7 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
|
|
3213
|
-
try {
|
|
3214
|
-
return await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true });
|
|
3215
|
-
} catch {
|
|
3216
|
-
return void 0;
|
|
3217
|
-
}
|
|
3218
|
-
}
|
|
3219
|
-
async function has_no_relations_endpoint(issue_number, repo) {
|
|
3220
|
-
const status = await git_gh_exec.exec_gh_api_status(
|
|
3221
|
-
git_gh_api_path.blocked_by_api_path(issue_number, repo)
|
|
3222
|
-
);
|
|
3223
|
-
return status === NOT_FOUND_STATUS;
|
|
3224
|
-
}
|
|
3225
|
-
async function issue_blocked_by_references(issue_number, own_repo, repo) {
|
|
3226
|
-
try {
|
|
3227
|
-
const listing = await read_blocked_by_listing(issue_number, repo);
|
|
3228
|
-
return listing.nodes.map((blocker) => git_gh_issue_rest.reference_of_blocker(blocker, own_repo));
|
|
3229
|
-
} catch (error) {
|
|
3230
|
-
if (await has_no_relations_endpoint(issue_number, repo)) return [];
|
|
3231
|
-
throw error;
|
|
3232
|
-
}
|
|
3233
|
-
}
|
|
3234
|
-
async function issue_blocked_by_numbers(issue_number, repo) {
|
|
3235
|
-
const references = await issue_blocked_by_references(issue_number, "", repo);
|
|
3236
|
-
return references.map((blocker) => blocker.number);
|
|
3237
|
-
}
|
|
3238
|
-
var git_gh_issue_read = {
|
|
3239
|
-
issue_blocked_by_numbers,
|
|
3240
|
-
issue_blocked_by_references,
|
|
3241
|
-
issue_get_title,
|
|
3242
|
-
issue_get_body,
|
|
3243
|
-
issue_get_body_classified,
|
|
3244
|
-
issue_view_json,
|
|
3245
|
-
issue_view_json_classified,
|
|
3246
|
-
issue_get_state_and_relations,
|
|
3247
|
-
issue_get_state_and_relations_classified,
|
|
3248
|
-
is_unreachable_read,
|
|
3249
|
-
issue_get_plan_fields,
|
|
3250
|
-
issue_get_plan_fields_classified,
|
|
3251
|
-
issue_get_labels_and_body,
|
|
3252
|
-
issue_list_comments
|
|
3253
|
-
};
|
|
3254
|
-
|
|
3255
|
-
// scripts/git/git-gh-issue-list.ts
|
|
3256
|
-
var PER_PAGE = 100;
|
|
3257
|
-
var FIRST_PAGE = 1;
|
|
3258
|
-
var DEFAULT_STATE = "open";
|
|
3259
|
-
var DEFAULT_SORT = "created";
|
|
3260
|
-
var LISTING_DIRECTION = "direction=desc";
|
|
3261
|
-
function listing_query(request) {
|
|
3262
|
-
const state = request.state ?? DEFAULT_STATE;
|
|
3263
|
-
const sort = request.sort ?? DEFAULT_SORT;
|
|
3264
|
-
return `state=${state}&sort=${sort}&${LISTING_DIRECTION}`;
|
|
3265
|
-
}
|
|
3266
|
-
function listing_path(request, page) {
|
|
3267
|
-
const label = request.label === void 0 ? "" : `&labels=${encodeURIComponent(request.label)}`;
|
|
3268
|
-
const paging = `&per_page=${String(PER_PAGE)}&page=${String(page)}`;
|
|
3269
|
-
return `${git_gh_api_path.issues_api_path(request.repo)}?${listing_query(request)}${label}${paging}`;
|
|
3270
|
-
}
|
|
3271
|
-
var DIGIT_PATTERN = /\d/u;
|
|
3272
|
-
function has_token_occurrence(body, term) {
|
|
3273
|
-
let index = body.indexOf(term);
|
|
3274
|
-
while (index !== -1) {
|
|
3275
|
-
if (!DIGIT_PATTERN.test(body.charAt(index + term.length))) return true;
|
|
3276
|
-
index = body.indexOf(term, index + 1);
|
|
3277
|
-
}
|
|
3278
|
-
return false;
|
|
3279
|
-
}
|
|
3280
|
-
function matches_body(rest, term) {
|
|
3281
|
-
if (term === void 0) return true;
|
|
3282
|
-
const { body } = rest;
|
|
3283
|
-
return typeof body === "string" && has_token_occurrence(body, term);
|
|
3284
|
-
}
|
|
3285
|
-
function is_selected(rest, request) {
|
|
3286
|
-
return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
|
|
3287
|
-
}
|
|
3288
|
-
async function fetch_page(request, page) {
|
|
3289
|
-
const path7 = listing_path(request, page);
|
|
3290
|
-
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path7 }));
|
|
3291
|
-
return {
|
|
3292
|
-
selected: rows.filter((row) => is_selected(row, request)),
|
|
3293
|
-
is_full: rows.length >= PER_PAGE
|
|
3294
|
-
};
|
|
3295
|
-
}
|
|
3296
|
-
var MAX_PAGES = 5;
|
|
3297
|
-
var MAX_SCANNED = MAX_PAGES * PER_PAGE;
|
|
3298
|
-
function wants_more(has_more, selected_count, limit) {
|
|
3299
|
-
return has_more && selected_count < limit;
|
|
3300
|
-
}
|
|
3301
|
-
async function fetch_selected(request) {
|
|
3302
|
-
const selected = [];
|
|
3303
|
-
let page = FIRST_PAGE;
|
|
3304
|
-
let has_more = true;
|
|
3305
|
-
while (page <= MAX_PAGES && wants_more(has_more, selected.length, request.limit)) {
|
|
3306
|
-
const result = await fetch_page(request, page);
|
|
3307
|
-
selected.push(...result.selected);
|
|
3308
|
-
has_more = result.is_full;
|
|
3309
|
-
page += 1;
|
|
3310
|
-
}
|
|
3311
|
-
return {
|
|
3312
|
-
rows: selected.slice(0, request.limit),
|
|
3313
|
-
is_capped: wants_more(has_more, selected.length, request.limit)
|
|
3314
|
-
};
|
|
3315
|
-
}
|
|
3316
|
-
async function read_relations2(rows, repo) {
|
|
3317
|
-
const relations = [];
|
|
3318
|
-
for (const row of rows) {
|
|
3319
|
-
relations.push(await read_blocked_by(String(row.number), row, repo));
|
|
3320
|
-
}
|
|
3321
|
-
return relations;
|
|
3322
|
-
}
|
|
3323
|
-
async function issue_list(request) {
|
|
3324
|
-
try {
|
|
3325
|
-
const { rows, is_capped } = await fetch_selected(request);
|
|
3326
|
-
const fields = git_gh_issue_rest.split_fields(request.json_fields);
|
|
3327
|
-
const relations = fields.includes(BLOCKED_BY_FIELD) ? await read_relations2(rows, request.repo) : void 0;
|
|
3328
|
-
const mapped = rows.map(
|
|
3329
|
-
(row, index) => git_gh_issue_rest.to_gh_issue(row, fields, relations?.[index])
|
|
3330
|
-
);
|
|
3331
|
-
return { json: JSON.stringify(mapped), is_capped };
|
|
3332
|
-
} catch {
|
|
3333
|
-
return { json: void 0, is_capped: false };
|
|
3334
|
-
}
|
|
3335
|
-
}
|
|
3336
|
-
var git_gh_issue_list = {
|
|
3337
|
-
issue_list
|
|
3338
|
-
};
|
|
3339
|
-
|
|
3340
|
-
// scripts/git/git-gh-issue-write.ts
|
|
3341
|
-
var PATCH_METHOD = "PATCH";
|
|
3342
|
-
var DELETE_METHOD = "DELETE";
|
|
3343
|
-
var HTML_URL_FILTER = ".html_url";
|
|
3344
|
-
var ID_FILTER = ".id";
|
|
3345
|
-
var LABELS_SEGMENT = "/labels";
|
|
3346
|
-
var COMMENTS_SEGMENT = "/comments";
|
|
3347
|
-
var CLOSED_STATE = "closed";
|
|
3348
|
-
var COLOR_HASH = "#";
|
|
3349
|
-
async function did_write_succeed(write) {
|
|
3350
|
-
try {
|
|
3351
|
-
await write();
|
|
3352
|
-
return true;
|
|
3353
|
-
} catch {
|
|
3354
|
-
return false;
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
|
-
async function issue_edit_body(issue_number, body) {
|
|
3358
|
-
return await git_gh_exec.exec_gh_api({
|
|
3359
|
-
path: git_gh_api_path.issue_api_path(issue_number),
|
|
3360
|
-
method: PATCH_METHOD,
|
|
3361
|
-
body: JSON.stringify({ body }),
|
|
3362
|
-
jq_filter: HTML_URL_FILTER
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
|
-
async function issue_comment(issue_number, body) {
|
|
3366
|
-
return await git_gh_exec.exec_gh_api({
|
|
3367
|
-
path: `${git_gh_api_path.issue_api_path(issue_number)}${COMMENTS_SEGMENT}`,
|
|
3368
|
-
body: JSON.stringify({ body }),
|
|
3369
|
-
jq_filter: HTML_URL_FILTER
|
|
3370
|
-
});
|
|
3371
|
-
}
|
|
3372
|
-
async function issue_try_comment(issue_number, body) {
|
|
3373
|
-
return await did_write_succeed(async () => await issue_comment(issue_number, body));
|
|
3374
|
-
}
|
|
3375
|
-
async function issue_close(issue_number, comment) {
|
|
3376
|
-
return await did_write_succeed(async () => {
|
|
3377
|
-
if (comment !== void 0) await issue_comment(issue_number, comment);
|
|
3378
|
-
await git_gh_exec.exec_gh_api({
|
|
3379
|
-
path: git_gh_api_path.issue_api_path(issue_number),
|
|
3380
|
-
method: PATCH_METHOD,
|
|
3381
|
-
body: JSON.stringify({ state: CLOSED_STATE })
|
|
3382
|
-
});
|
|
3383
|
-
});
|
|
3384
|
-
}
|
|
3385
|
-
function to_label_color(color) {
|
|
3386
|
-
return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
|
|
3387
|
-
}
|
|
3388
|
-
async function label_ensure(input) {
|
|
3389
|
-
try {
|
|
3390
|
-
await git_gh_exec.exec_gh_api({
|
|
3391
|
-
path: `${git_gh_api_path.repo_api_path()}${LABELS_SEGMENT}`,
|
|
3392
|
-
body: JSON.stringify({
|
|
3393
|
-
name: input.name,
|
|
3394
|
-
color: to_label_color(input.color),
|
|
3395
|
-
description: input.description
|
|
3396
|
-
})
|
|
3397
|
-
});
|
|
3398
|
-
} catch {
|
|
3399
|
-
}
|
|
3400
|
-
}
|
|
3401
|
-
function issue_create_request(input) {
|
|
3402
|
-
const { labels } = input;
|
|
3403
|
-
return {
|
|
3404
|
-
path: git_gh_api_path.issues_api_path(input.repo),
|
|
3405
|
-
body: JSON.stringify({
|
|
3406
|
-
title: input.title,
|
|
3407
|
-
...labels !== void 0 && { labels },
|
|
3408
|
-
body: input.body
|
|
3409
|
-
}),
|
|
3410
|
-
jq_filter: HTML_URL_FILTER
|
|
3411
|
-
};
|
|
3412
|
-
}
|
|
3413
|
-
async function issue_create_with_label(input) {
|
|
3414
|
-
return await git_gh_exec.exec_gh_api(
|
|
3415
|
-
issue_create_request({ title: input.title, body: input.body, labels: [input.label] })
|
|
3416
|
-
);
|
|
3417
|
-
}
|
|
3418
|
-
async function issue_add_label(issue_number, label) {
|
|
3419
|
-
return await did_write_succeed(
|
|
3420
|
-
async () => await git_gh_exec.exec_gh_api({
|
|
3421
|
-
path: `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`,
|
|
3422
|
-
body: JSON.stringify({ labels: [label] })
|
|
3423
|
-
})
|
|
3424
|
-
);
|
|
3425
|
-
}
|
|
3426
|
-
async function issue_remove_label(issue_number, label) {
|
|
3427
|
-
const labels_path = `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`;
|
|
3428
|
-
await git_gh_exec.exec_gh_api({
|
|
3429
|
-
path: `${labels_path}/${encodeURIComponent(label)}`,
|
|
3430
|
-
method: DELETE_METHOD
|
|
3431
|
-
});
|
|
3432
|
-
}
|
|
3433
|
-
async function read_issue_id(issue_number) {
|
|
3434
|
-
const raw = await git_gh_exec.exec_gh_api({
|
|
3435
|
-
path: git_gh_api_path.issue_api_path(issue_number),
|
|
3436
|
-
jq_filter: ID_FILTER
|
|
3437
|
-
});
|
|
3438
|
-
const issue_id = Number(raw.trim());
|
|
3439
|
-
if (!Number.isSafeInteger(issue_id) || issue_id <= 0) {
|
|
3440
|
-
throw new Error(`gh api answered no database id for issue #${issue_number}`);
|
|
3441
|
-
}
|
|
3442
|
-
return issue_id;
|
|
3443
|
-
}
|
|
3444
|
-
async function issue_add_blocked_by(issue_number, blocker) {
|
|
3445
|
-
return await did_write_succeed(async () => {
|
|
3446
|
-
const issue_id = await read_issue_id(blocker);
|
|
3447
|
-
await git_gh_exec.exec_gh_api({
|
|
3448
|
-
path: git_gh_api_path.blocked_by_api_path(issue_number),
|
|
3449
|
-
body: JSON.stringify({ issue_id })
|
|
3450
|
-
});
|
|
3451
|
-
});
|
|
3452
|
-
}
|
|
3453
|
-
async function issue_remove_blocked_by(issue_number, blocker) {
|
|
3454
|
-
return await did_write_succeed(async () => {
|
|
3455
|
-
const issue_id = await read_issue_id(blocker);
|
|
3456
|
-
const relations_path = git_gh_api_path.blocked_by_api_path(issue_number);
|
|
3457
|
-
await git_gh_exec.exec_gh_api({
|
|
3458
|
-
path: `${relations_path}/${String(issue_id)}`,
|
|
3459
|
-
method: DELETE_METHOD
|
|
3460
|
-
});
|
|
3461
|
-
});
|
|
3462
|
-
}
|
|
3463
|
-
var git_gh_issue_write = {
|
|
3464
|
-
issue_edit_body,
|
|
3465
|
-
issue_comment,
|
|
3466
|
-
issue_try_comment,
|
|
3467
|
-
issue_close,
|
|
3468
|
-
label_ensure,
|
|
3469
|
-
issue_create_request,
|
|
3470
|
-
issue_create_with_label,
|
|
3471
|
-
issue_add_label,
|
|
3472
|
-
issue_remove_label,
|
|
3473
|
-
issue_add_blocked_by,
|
|
3474
|
-
issue_remove_blocked_by
|
|
3475
|
-
};
|
|
3476
|
-
|
|
3477
|
-
// scripts/git/git-gh-issue.ts
|
|
3478
|
-
var NUMBER_AND_BODY_FIELDS = "number,body";
|
|
3479
|
-
var BACKLOG_SCAN_FIELDS = `number,title,body,${BLOCKED_BY_COUNT_FIELD}`;
|
|
3480
|
-
var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
|
|
3481
|
-
var SUMMARY_FIELDS = "number,title,labels,createdAt";
|
|
3482
|
-
var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy`;
|
|
3483
|
-
var { issue_list: issue_list2 } = git_gh_issue_list;
|
|
3484
|
-
async function issue_list_recent(limit) {
|
|
3485
|
-
return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
|
|
3486
|
-
}
|
|
3487
|
-
async function issue_list_open_bodies(limit) {
|
|
3488
|
-
return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
|
|
3489
|
-
}
|
|
3490
|
-
async function issue_list_recently_closed(limit) {
|
|
3491
|
-
return await issue_list2({
|
|
3492
|
-
json_fields: NUMBER_TITLE_AND_LABELS_FIELDS,
|
|
3493
|
-
limit,
|
|
3494
|
-
state: "closed",
|
|
3495
|
-
sort: "updated"
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
async function issue_search_body(term, limit) {
|
|
3499
|
-
return await issue_list2({
|
|
3500
|
-
json_fields: NUMBER_AND_BODY_FIELDS,
|
|
3501
|
-
limit,
|
|
3502
|
-
body_term: term
|
|
3503
|
-
});
|
|
3504
|
-
}
|
|
3505
|
-
async function issue_list_by_label(label, limit) {
|
|
3506
|
-
return await issue_list2({ json_fields: NUMBER_AND_BODY_FIELDS, limit, label });
|
|
3507
|
-
}
|
|
3508
|
-
async function issue_list_by_label_summary(label, limit, options) {
|
|
3509
|
-
return await issue_list2({
|
|
3510
|
-
json_fields: options?.json_fields ?? PICKUP_FIELDS,
|
|
3511
|
-
limit,
|
|
3512
|
-
label
|
|
3513
|
-
});
|
|
3514
|
-
}
|
|
3515
|
-
async function issue_list_by_label_in_repo(label, limit, repo) {
|
|
3516
|
-
return await issue_list2({ json_fields: SUMMARY_FIELDS, limit, label, repo });
|
|
3517
|
-
}
|
|
3518
|
-
var git_gh_issue = {
|
|
3519
|
-
...git_gh_issue_read,
|
|
3520
|
-
...git_gh_issue_write,
|
|
3521
|
-
issue_list_recent,
|
|
3522
|
-
issue_list_by_label,
|
|
3523
|
-
issue_list_by_label_in_repo,
|
|
3524
|
-
issue_list_by_label_summary,
|
|
3525
|
-
issue_search_body,
|
|
3526
|
-
issue_list_open_bodies,
|
|
3527
|
-
issue_list_recently_closed
|
|
3528
|
-
};
|
|
3529
|
-
|
|
3530
|
-
// scripts/ci/pr-classification.ts
|
|
3531
|
-
import { readFileSync as readFileSync5 } from "node:fs";
|
|
3532
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3533
|
-
import { z as z8 } from "zod";
|
|
3534
|
-
var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
|
|
3535
|
-
var ISSUE_BUG_LABEL = "bug";
|
|
3536
|
-
var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
|
|
3537
|
-
var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
|
|
3538
|
-
var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
|
|
3539
|
-
var LABEL = z8.object({ name: z8.string() });
|
|
3540
|
-
var USER = z8.object({ login: z8.string() });
|
|
3541
|
-
var PULL_REQUEST = z8.object({ number: z8.number(), user: USER });
|
|
3542
|
-
var EVENT = z8.object({
|
|
3543
|
-
pull_request: PULL_REQUEST,
|
|
3544
|
-
repository: z8.object({ full_name: z8.string() })
|
|
3545
|
-
});
|
|
3546
|
-
function classification_error(labels, author) {
|
|
3547
|
-
const legacy = labels.filter((label) => LEGACY_LABELS.has(label.toLowerCase()));
|
|
3548
|
-
if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
|
|
3549
|
-
const selected = labels.filter((label) => CLASSIFICATION_SET.has(label.toLowerCase()));
|
|
3550
|
-
if (selected.length > 1) {
|
|
3551
|
-
return `Choose exactly one release classification; found: ${selected.join(", ")}`;
|
|
3552
|
-
}
|
|
3553
|
-
if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
|
|
3554
|
-
return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
|
|
3555
|
-
}
|
|
3556
|
-
function declaration(label) {
|
|
3557
|
-
return `${DECLARATION_PREFIX}${label}`;
|
|
3558
|
-
}
|
|
3559
|
-
function is_classification(value) {
|
|
3560
|
-
return CLASSIFICATION_SET.has(value);
|
|
3561
|
-
}
|
|
3562
|
-
function release_candidates(candidates) {
|
|
3563
|
-
if (!candidates.includes(BREAKING_CHANGE_LABEL)) return candidates;
|
|
3564
|
-
return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
|
|
3565
|
-
}
|
|
3566
|
-
function issue_candidates(issue_json) {
|
|
3567
|
-
const issue = z8.object({ labels: z8.array(LABEL), body: z8.string().nullable() }).parse(JSON.parse(issue_json));
|
|
3568
|
-
const names = issue.labels.map((label) => label.name.toLowerCase());
|
|
3569
|
-
const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
|
|
3570
|
-
const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
|
|
3571
|
-
const unknown = declared.filter((value) => !is_classification(value));
|
|
3572
|
-
if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
|
|
3573
|
-
if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING_CHANGE_LABEL)) {
|
|
3574
|
-
candidates.push(BUGFIX_LABEL);
|
|
3575
|
-
}
|
|
3576
|
-
return candidates;
|
|
3577
|
-
}
|
|
3578
|
-
function select_issue_classification(issue_json) {
|
|
3579
|
-
const candidates = issue_candidates(issue_json);
|
|
3580
|
-
const selected = [...new Set(candidates)];
|
|
3581
|
-
const [label] = selected;
|
|
3582
|
-
if (label === void 0 || selected.length > 1) {
|
|
3583
|
-
throw new Error(
|
|
3584
|
-
`Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
|
|
3585
|
-
);
|
|
3586
|
-
}
|
|
3587
|
-
return label;
|
|
3588
|
-
}
|
|
3589
|
-
function fetch_current_labels(repository, pull_number) {
|
|
3590
|
-
const path7 = `repos/${repository}/issues/${String(pull_number)}/labels`;
|
|
3591
|
-
try {
|
|
3592
|
-
const names = git_gh_exec.exec_gh_api_sync({ path: path7, jq_filter: ".[].name" });
|
|
3593
|
-
return names.split("\n").filter((name) => name !== "");
|
|
3594
|
-
} catch (error) {
|
|
3595
|
-
const detail = error_text.message_of(error);
|
|
3596
|
-
throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error });
|
|
3597
|
-
}
|
|
3598
|
-
}
|
|
3599
|
-
function check_event(event_path, read_labels = fetch_current_labels) {
|
|
3600
|
-
const event = EVENT.parse(JSON.parse(readFileSync5(event_path, "utf8")));
|
|
3601
|
-
const labels = read_labels(event.repository.full_name, event.pull_request.number);
|
|
3602
|
-
return classification_error(labels, event.pull_request.user.login);
|
|
3603
|
-
}
|
|
3604
|
-
if (process.argv[1] === fileURLToPath2(import.meta.url)) {
|
|
3605
|
-
const event_path = process.env["GITHUB_EVENT_PATH"];
|
|
3606
|
-
if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
|
|
3607
|
-
const error = check_event(event_path);
|
|
3608
|
-
if (error) {
|
|
3609
|
-
console.error(`::error::${error}`);
|
|
3610
|
-
process.exitCode = 1;
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
var pr_classification = {
|
|
3614
|
-
classification_error,
|
|
3615
|
-
check_event,
|
|
3616
|
-
fetch_current_labels,
|
|
3617
|
-
declaration,
|
|
3618
|
-
is_classification,
|
|
3619
|
-
select_issue_classification
|
|
3620
|
-
};
|
|
3621
|
-
|
|
3622
|
-
// scripts/lib/poll.ts
|
|
3623
|
-
async function sleep(duration_ms) {
|
|
3624
|
-
await new Promise((resolve2) => {
|
|
3625
|
-
setTimeout(resolve2, duration_ms);
|
|
3626
|
-
});
|
|
3627
|
-
}
|
|
3628
|
-
async function wait_between(wait, interval_ms, attempt, attempts) {
|
|
3629
|
-
if (attempt < attempts - 1) await wait(interval_ms);
|
|
3630
|
-
}
|
|
3631
|
-
async function poll_until(is_done, options) {
|
|
3632
|
-
const wait = options.sleeper ?? sleep;
|
|
3633
|
-
for (let attempt = 0; attempt < options.attempts; attempt += 1) {
|
|
3634
|
-
if (await is_done()) return true;
|
|
3635
|
-
await wait_between(wait, options.interval_ms, attempt, options.attempts);
|
|
3636
|
-
}
|
|
3637
|
-
return false;
|
|
3638
|
-
}
|
|
3639
|
-
var poll = { poll_until, sleep };
|
|
3640
|
-
|
|
3641
|
-
// scripts/git/git-gh-pr.ts
|
|
3642
|
-
import { z as z12 } from "zod";
|
|
3643
|
-
|
|
3644
|
-
// scripts/git/git-gh-pr-rest.ts
|
|
3645
|
-
import { z as z9 } from "zod";
|
|
3646
|
-
var rest_repo_schema = z9.looseObject({ full_name: z9.string().optional() }).nullish();
|
|
3647
|
-
var rest_pull_side_schema = z9.looseObject({ repo: rest_repo_schema });
|
|
3648
|
-
var rest_pull_schema = z9.looseObject({
|
|
3649
|
-
number: z9.number(),
|
|
3650
|
-
html_url: z9.string().optional(),
|
|
3651
|
-
body: z9.string().nullish(),
|
|
3652
|
-
state: z9.string().optional(),
|
|
3653
|
-
merged: z9.boolean().nullish(),
|
|
3654
|
-
merged_at: z9.string().nullish(),
|
|
3655
|
-
mergeable: z9.boolean().nullish(),
|
|
3656
|
-
mergeable_state: z9.string().optional(),
|
|
3657
|
-
// `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
|
|
3658
|
-
// REST hangs the check runs and the status contexts off the commit rather than off the pull
|
|
3659
|
-
// request (joshuafolkken/kit#1028).
|
|
3660
|
-
head: z9.looseObject({ ref: z9.string().optional(), sha: z9.string().optional(), repo: rest_repo_schema }).nullish(),
|
|
3661
|
-
base: rest_pull_side_schema.nullish(),
|
|
3662
|
-
// `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
|
|
3663
|
-
// is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
|
|
3664
|
-
// (joshuafolkken/kit#2497).
|
|
3665
|
-
node_id: z9.string().optional(),
|
|
3666
|
-
auto_merge: z9.looseObject({}).nullish()
|
|
3667
|
-
});
|
|
3668
|
-
var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
|
|
3669
|
-
var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
|
|
3670
|
-
var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
|
|
3671
|
-
var OPEN_STATE2 = "open";
|
|
3672
|
-
var EMPTY_BODY2 = "";
|
|
3673
|
-
function parse_rest_pull(rest_json) {
|
|
3674
|
-
const parsed = parse_json_object_safe(rest_json, rest_pull_schema);
|
|
3675
|
-
if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
|
|
3676
|
-
return parsed;
|
|
3677
|
-
}
|
|
3678
|
-
function parse_rest_pulls(rest_json) {
|
|
3679
|
-
const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
|
|
3680
|
-
if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
|
|
3681
|
-
return parsed;
|
|
3682
|
-
}
|
|
3683
|
-
function select_pull(pulls) {
|
|
3684
|
-
return pulls.find((pull) => pull.state === OPEN_STATE2) ?? pulls[0];
|
|
3685
|
-
}
|
|
3686
|
-
function is_merged(pull) {
|
|
3687
|
-
return pull.merged === true || typeof pull.merged_at === "string";
|
|
3688
|
-
}
|
|
3689
|
-
function to_pr_state(pull) {
|
|
3690
|
-
return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
|
|
3691
|
-
}
|
|
3692
|
-
function to_repo_name(side) {
|
|
3693
|
-
return side?.repo?.full_name;
|
|
3694
|
-
}
|
|
3695
|
-
function is_same_repository_head(pull) {
|
|
3696
|
-
const head_repo = to_repo_name(pull.head);
|
|
3697
|
-
return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
|
|
3698
|
-
}
|
|
3699
|
-
function to_pr_info(pull) {
|
|
3700
|
-
return {
|
|
3701
|
-
mergeable: pull.mergeable,
|
|
3702
|
-
mergeStateStatus: to_gh_state(pull.mergeable_state),
|
|
3703
|
-
state: to_pr_state(pull)
|
|
3704
|
-
};
|
|
3705
|
-
}
|
|
3706
|
-
function to_pr_comments(rest_json) {
|
|
3707
|
-
const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
|
|
3708
|
-
if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
|
|
3709
|
-
return parsed.map((comment) => ({
|
|
3710
|
-
body: comment.body ?? EMPTY_BODY2,
|
|
3711
|
-
url: comment.html_url,
|
|
3712
|
-
author: { login: comment.user?.login }
|
|
3713
|
-
}));
|
|
3714
|
-
}
|
|
3715
|
-
var git_gh_pr_rest = {
|
|
3716
|
-
parse_rest_pull,
|
|
3717
|
-
parse_rest_pulls,
|
|
3718
|
-
select_pull,
|
|
3719
|
-
is_same_repository_head,
|
|
3720
|
-
is_merged,
|
|
3721
|
-
to_pr_state,
|
|
3722
|
-
to_pr_info,
|
|
3723
|
-
to_pr_comments
|
|
3724
|
-
};
|
|
3725
|
-
|
|
3726
|
-
// scripts/git/git-gh-pr-read.ts
|
|
3727
|
-
var pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
3728
|
-
var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
|
|
3729
|
-
var COMMENTS_QUERY = FULL_PAGE_QUERY;
|
|
3730
|
-
var OWNER_PLACEHOLDER = "{owner}";
|
|
3731
|
-
var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
|
|
3732
|
-
var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
|
|
3733
|
-
function lookup_path(branch_name) {
|
|
3734
|
-
const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
|
|
3735
|
-
return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
|
|
3736
|
-
}
|
|
3737
|
-
async function fetch_pr_number(branch_name) {
|
|
3738
|
-
const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
|
|
3739
|
-
return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
|
|
3740
|
-
}
|
|
3741
|
-
var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
3742
|
-
async function fetch_pr_number_read(branch_name) {
|
|
3743
|
-
try {
|
|
3744
|
-
const pr_number = await fetch_pr_number(branch_name);
|
|
3745
|
-
if (pr_number === void 0) return { kind: "missing" };
|
|
3746
|
-
pr_number_by_branch.set(branch_name, pr_number);
|
|
3747
|
-
return { kind: "read", pr_number };
|
|
3748
|
-
} catch (error) {
|
|
3749
|
-
return { kind: "unreadable", cause: error };
|
|
3750
|
-
}
|
|
3751
|
-
}
|
|
3752
|
-
async function start_pr_number_read(branch_name) {
|
|
3753
|
-
const pending = pending_pr_number_by_branch.get(branch_name);
|
|
3754
|
-
if (pending !== void 0) return await pending;
|
|
3755
|
-
const lookup = fetch_pr_number_read(branch_name);
|
|
3756
|
-
pending_pr_number_by_branch.set(branch_name, lookup);
|
|
3757
|
-
try {
|
|
3758
|
-
return await lookup;
|
|
3759
|
-
} finally {
|
|
3760
|
-
pending_pr_number_by_branch.delete(branch_name);
|
|
3761
|
-
}
|
|
3762
|
-
}
|
|
3763
|
-
function forget_pr_numbers() {
|
|
3764
|
-
pr_number_by_branch.clear();
|
|
3765
|
-
pending_pr_number_by_branch.clear();
|
|
3766
|
-
}
|
|
3767
|
-
async function read_pr_number(branch_name) {
|
|
3768
|
-
const cached = pr_number_by_branch.get(branch_name);
|
|
3769
|
-
if (cached !== void 0) return { kind: "read", pr_number: cached };
|
|
3770
|
-
return await start_pr_number_read(branch_name);
|
|
3771
|
-
}
|
|
3772
|
-
async function resolve_pr_number(branch_name) {
|
|
3773
|
-
const read2 = await read_pr_number(branch_name);
|
|
3774
|
-
return read2.kind === "read" ? read2.pr_number : void 0;
|
|
3775
|
-
}
|
|
3776
|
-
function to_unreadable_error(branch_name, cause) {
|
|
3777
|
-
return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
|
|
3778
|
-
}
|
|
3779
|
-
async function require_pr_number(branch_name) {
|
|
3780
|
-
const read2 = await read_pr_number(branch_name);
|
|
3781
|
-
if (read2.kind === "read") return read2.pr_number;
|
|
3782
|
-
if (read2.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
|
|
3783
|
-
throw to_unreadable_error(branch_name, read2.cause);
|
|
3784
|
-
}
|
|
3785
|
-
async function read_pull(pr_number) {
|
|
3786
|
-
const json = await git_gh_exec.exec_gh_api({
|
|
3787
|
-
path: git_gh_api_path.pull_api_path(String(pr_number))
|
|
3788
|
-
});
|
|
3789
|
-
return git_gh_pr_rest.parse_rest_pull(json);
|
|
3790
|
-
}
|
|
3791
|
-
async function read_pull_of_branch(branch_name) {
|
|
3792
|
-
const pr_number = await resolve_pr_number(branch_name);
|
|
3793
|
-
if (pr_number === void 0) return void 0;
|
|
3794
|
-
try {
|
|
3795
|
-
return await read_pull(pr_number);
|
|
3796
|
-
} catch {
|
|
3797
|
-
return void 0;
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
async function pr_exists(branch_name) {
|
|
3801
|
-
const read2 = await read_pr_number(branch_name);
|
|
3802
|
-
if (read2.kind === "unreadable") throw to_unreadable_error(branch_name, read2.cause);
|
|
3803
|
-
return read2.kind === "read";
|
|
3804
|
-
}
|
|
3805
|
-
async function pr_get_number(branch_name) {
|
|
3806
|
-
return await resolve_pr_number(branch_name);
|
|
3807
|
-
}
|
|
3808
|
-
async function pr_get_url(branch_name) {
|
|
3809
|
-
const pull = await read_pull_of_branch(branch_name);
|
|
3810
|
-
if (pull?.html_url === void 0) return void 0;
|
|
3811
|
-
return git_gh_helpers.parse_pr_state_string(pull.html_url);
|
|
3812
|
-
}
|
|
3813
|
-
async function pr_get_body(branch_name) {
|
|
3814
|
-
const pull = await read_pull_of_branch(branch_name);
|
|
3815
|
-
const body = pull?.body;
|
|
3816
|
-
return typeof body === "string" && body.length > 0 ? body : void 0;
|
|
3817
|
-
}
|
|
3818
|
-
async function pr_view(branch_name) {
|
|
3819
|
-
const pull = await read_pull_of_branch(branch_name);
|
|
3820
|
-
if (pull === void 0) return "";
|
|
3821
|
-
return JSON.stringify(git_gh_pr_rest.to_pr_info(pull));
|
|
3822
|
-
}
|
|
3823
|
-
async function read_comments(branch_name, to_path, to_json) {
|
|
3824
|
-
const pr_number = await resolve_pr_number(branch_name);
|
|
3825
|
-
if (pr_number === void 0) return void 0;
|
|
3826
|
-
const path7 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
|
|
3827
|
-
try {
|
|
3828
|
-
return to_json(await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true }));
|
|
3829
|
-
} catch {
|
|
3830
|
-
return void 0;
|
|
3831
|
-
}
|
|
3832
|
-
}
|
|
3833
|
-
function to_comments_json(raw) {
|
|
3834
|
-
return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
|
|
3835
|
-
}
|
|
3836
|
-
function as_served(raw) {
|
|
3837
|
-
return raw;
|
|
3838
|
-
}
|
|
3839
|
-
async function pr_get_comments(branch_name) {
|
|
3840
|
-
return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
|
|
3841
|
-
}
|
|
3842
|
-
async function pr_get_review_comments(branch_name) {
|
|
3843
|
-
return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
|
|
3844
|
-
}
|
|
3845
|
-
var git_gh_pr_read = {
|
|
3846
|
-
pr_exists,
|
|
3847
|
-
pr_get_number,
|
|
3848
|
-
pr_get_url,
|
|
3849
|
-
pr_get_body,
|
|
3850
|
-
pr_view,
|
|
3851
|
-
pr_get_comments,
|
|
3852
|
-
pr_get_review_comments
|
|
3853
|
-
};
|
|
3854
|
-
|
|
3855
|
-
// scripts/git/git-gh-pr-review.ts
|
|
3856
|
-
import { z as z10 } from "zod";
|
|
3857
|
-
var REVIEW_APPROVED = "APPROVED";
|
|
3858
|
-
var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
|
|
3859
|
-
var REVIEW_NONE = "";
|
|
3860
|
-
var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
|
|
3861
|
-
var rest_review_schema = z10.looseObject({
|
|
3862
|
-
state: z10.string().optional(),
|
|
3863
|
-
user: z10.looseObject({ login: z10.string().optional() }).nullish()
|
|
3864
|
-
});
|
|
3865
|
-
var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
|
|
3866
|
-
function parse_rest_reviews(reviews_json) {
|
|
3867
|
-
const parsed = parse_json_array_or_undefined(reviews_json, rest_review_schema);
|
|
3868
|
-
if (parsed === void 0) throw new Error(NOT_A_REVIEW_LISTING);
|
|
3869
|
-
return parsed;
|
|
3870
|
-
}
|
|
3871
|
-
var UNATTRIBUTED_REVIEWER = "(no author)";
|
|
3872
|
-
function read_reviewer(review) {
|
|
3873
|
-
const login = review.user?.login;
|
|
3874
|
-
return typeof login === "string" && login.length > 0 ? login : UNATTRIBUTED_REVIEWER;
|
|
3875
|
-
}
|
|
3876
|
-
function read_state_changing(review) {
|
|
3877
|
-
const state = review.state?.toUpperCase();
|
|
3878
|
-
return state !== void 0 && STATE_CHANGING_REVIEWS.has(state) ? state : void 0;
|
|
3879
|
-
}
|
|
3880
|
-
function fold_latest_states(reviews) {
|
|
3881
|
-
const latest = /* @__PURE__ */ new Map();
|
|
3882
|
-
for (const review of reviews) {
|
|
3883
|
-
const state = read_state_changing(review);
|
|
3884
|
-
if (state !== void 0) latest.set(read_reviewer(review), state);
|
|
3885
|
-
}
|
|
3886
|
-
return latest;
|
|
3887
|
-
}
|
|
3888
|
-
function to_review_decision(reviews_json) {
|
|
3889
|
-
const latest = new Set(fold_latest_states(parse_rest_reviews(reviews_json)).values());
|
|
3890
|
-
if (latest.has(REVIEW_CHANGES_REQUESTED)) return REVIEW_CHANGES_REQUESTED;
|
|
3891
|
-
return latest.has(REVIEW_APPROVED) ? REVIEW_APPROVED : REVIEW_NONE;
|
|
3892
|
-
}
|
|
3893
|
-
var git_gh_pr_review = { to_review_decision, parse_rest_reviews };
|
|
3894
|
-
|
|
3895
|
-
// scripts/git/git-gh-pr-rollup.ts
|
|
3896
|
-
import { z as z11 } from "zod";
|
|
3897
|
-
var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
|
|
3898
|
-
var TYPE_NAME_KEY = "__typename";
|
|
3899
|
-
var rollup_element_schema = z11.looseObject({});
|
|
3900
|
-
var check_runs_page_schema = z11.looseObject({
|
|
3901
|
-
// Required, not optional: GitHub always names the key, and accepting a page without one would
|
|
3902
|
-
// answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
|
|
3903
|
-
check_runs: z11.array(rollup_element_schema)
|
|
3904
|
-
});
|
|
3905
|
-
var id_holder_schema = z11.object({ id: z11.number() });
|
|
3906
|
-
var status_page_schema = z11.looseObject({
|
|
3907
|
-
statuses: z11.array(rollup_element_schema)
|
|
3908
|
-
});
|
|
3909
|
-
var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
|
|
3910
|
-
var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
|
|
3911
|
-
function read_pages(raw_json, schema, message) {
|
|
3912
|
-
const pages = parse_json_array_or_undefined(raw_json, schema);
|
|
3913
|
-
if (pages === void 0) throw new Error(message);
|
|
3914
|
-
return pages;
|
|
3915
|
-
}
|
|
3916
|
-
var NO_SUITE = -1;
|
|
3917
|
-
function read_id(value) {
|
|
3918
|
-
const parsed = id_holder_schema.safeParse(value);
|
|
3919
|
-
return parsed.success ? parsed.data.id : void 0;
|
|
3920
|
-
}
|
|
3921
|
-
function check_run_group_key(run2) {
|
|
3922
|
-
const { name, app } = run2;
|
|
3923
|
-
if (typeof name !== "string") return void 0;
|
|
3924
|
-
return JSON.stringify([name, read_id(app)]);
|
|
3925
|
-
}
|
|
3926
|
-
function read_rank(run2) {
|
|
3927
|
-
const id = read_id(run2);
|
|
3928
|
-
if (id === void 0) return void 0;
|
|
3929
|
-
return { suite: read_id(run2["check_suite"]) ?? NO_SUITE, run: id };
|
|
3930
|
-
}
|
|
3931
|
-
function is_ranked_above(candidate, current) {
|
|
3932
|
-
if (candidate.suite !== current.suite) return candidate.suite > current.suite;
|
|
3933
|
-
return candidate.run > current.run;
|
|
3934
|
-
}
|
|
3935
|
-
function should_replace(previous, rank) {
|
|
3936
|
-
const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
|
|
3937
|
-
return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
|
|
3938
|
-
}
|
|
3939
|
-
function remember_newest_run(run2, newest) {
|
|
3940
|
-
const key = check_run_group_key(run2);
|
|
3941
|
-
const rank = read_rank(run2);
|
|
3942
|
-
if (key === void 0 || rank === void 0) return;
|
|
3943
|
-
if (should_replace(newest.get(key), rank)) newest.set(key, run2);
|
|
3944
|
-
}
|
|
3945
|
-
function is_newest_run(run2, newest) {
|
|
3946
|
-
const key = check_run_group_key(run2);
|
|
3947
|
-
if (key === void 0 || read_rank(run2) === void 0) return true;
|
|
3948
|
-
return newest.get(key) === run2;
|
|
3949
|
-
}
|
|
3950
|
-
function to_check_run_items(check_runs_json) {
|
|
3951
|
-
const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
|
|
3952
|
-
const runs = pages.flatMap((page) => page.check_runs);
|
|
3953
|
-
const newest = /* @__PURE__ */ new Map();
|
|
3954
|
-
for (const run2 of runs) remember_newest_run(run2, newest);
|
|
3955
|
-
return runs.filter((run2) => is_newest_run(run2, newest));
|
|
3956
|
-
}
|
|
3957
|
-
function to_status_context_items(status_json) {
|
|
3958
|
-
const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
|
|
3959
|
-
return pages.flatMap((page) => page.statuses).map((status) => ({ ...status, [TYPE_NAME_KEY]: STATUS_CONTEXT_TYPE_NAME }));
|
|
3960
|
-
}
|
|
3961
|
-
function to_status_check_rollup(input) {
|
|
3962
|
-
return [
|
|
3963
|
-
...to_check_run_items(input.check_runs_json),
|
|
3964
|
-
...to_status_context_items(input.status_json)
|
|
3965
|
-
];
|
|
3966
|
-
}
|
|
3967
|
-
var git_gh_pr_rollup = {
|
|
3968
|
-
to_check_run_items,
|
|
3969
|
-
to_status_context_items,
|
|
3970
|
-
to_status_check_rollup
|
|
3971
|
-
};
|
|
3972
|
-
|
|
3973
|
-
// scripts/git/git-gh-pr-snapshot.ts
|
|
3974
|
-
var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
|
|
3975
|
-
async function read_object_pages(path7) {
|
|
3976
|
-
return await git_gh_exec.exec_gh_api({
|
|
3977
|
-
path: `${path7}${FULL_PAGE_QUERY}`,
|
|
3978
|
-
should_paginate: true,
|
|
3979
|
-
should_slurp: true
|
|
3980
|
-
});
|
|
3981
|
-
}
|
|
3982
|
-
async function read_array_pages(path7) {
|
|
3983
|
-
return await git_gh_exec.exec_gh_api({
|
|
3984
|
-
path: `${path7}${FULL_PAGE_QUERY}`,
|
|
3985
|
-
should_paginate: true
|
|
3986
|
-
});
|
|
3987
|
-
}
|
|
3988
|
-
function read_head_sha(pull) {
|
|
3989
|
-
const sha = pull.head?.sha;
|
|
3990
|
-
if (sha === void 0) throw new Error(NO_HEAD_SHA_MESSAGE);
|
|
3991
|
-
return sha;
|
|
3992
|
-
}
|
|
3993
|
-
async function read_status_check_rollup(commit_sha) {
|
|
3994
|
-
const [check_runs_json, status_json] = await Promise.all([
|
|
3995
|
-
read_object_pages(git_gh_api_path.commit_check_runs_api_path(commit_sha)),
|
|
3996
|
-
read_object_pages(git_gh_api_path.commit_status_api_path(commit_sha))
|
|
3997
|
-
]);
|
|
3998
|
-
return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
|
|
3999
|
-
}
|
|
4000
|
-
async function pr_get_review_decision(pr_number) {
|
|
4001
|
-
const path7 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
|
|
4002
|
-
return git_gh_pr_review.to_review_decision(await read_array_pages(path7));
|
|
4003
|
-
}
|
|
4004
|
-
async function build_checks_fields(pull) {
|
|
4005
|
-
return {
|
|
4006
|
-
statusCheckRollup: await read_status_check_rollup(read_head_sha(pull)),
|
|
4007
|
-
mergeStateStatus: to_gh_state(pull.mergeable_state)
|
|
4008
|
-
};
|
|
4009
|
-
}
|
|
4010
|
-
async function resolve_pull(branch_name) {
|
|
4011
|
-
const pr_number = await require_pr_number(branch_name);
|
|
4012
|
-
return { pr_number, pull: await read_pull(pr_number) };
|
|
4013
|
-
}
|
|
4014
|
-
async function pr_get_checks_snapshot(branch_name) {
|
|
4015
|
-
const { pr_number, pull } = await resolve_pull(branch_name);
|
|
4016
|
-
return { pr_number, snapshot_json: JSON.stringify(await build_checks_fields(pull)) };
|
|
4017
|
-
}
|
|
4018
|
-
async function pr_get_state_snapshot(branch_name) {
|
|
4019
|
-
const { pr_number, pull } = await resolve_pull(branch_name);
|
|
4020
|
-
const [fields, review_decision] = await Promise.all([
|
|
4021
|
-
build_checks_fields(pull),
|
|
4022
|
-
pr_get_review_decision(pr_number)
|
|
4023
|
-
]);
|
|
4024
|
-
return JSON.stringify({ ...fields, reviewDecision: review_decision });
|
|
4025
|
-
}
|
|
4026
|
-
var git_gh_pr_snapshot = {
|
|
4027
|
-
pr_get_state_snapshot,
|
|
4028
|
-
pr_get_checks_snapshot,
|
|
4029
|
-
pr_get_review_decision
|
|
4030
|
-
};
|
|
4031
|
-
|
|
4032
|
-
// scripts/git/git-gh-pr.ts
|
|
4033
|
-
var HTML_URL_FILTER2 = ".html_url";
|
|
4034
|
-
var PUT_METHOD = "PUT";
|
|
4035
|
-
var PATCH_METHOD2 = "PATCH";
|
|
4036
|
-
var MERGE_COMMIT_METHOD = "merge";
|
|
4037
|
-
var MERGE_REQUEST_TIMEOUT_MS = 18e4;
|
|
4038
|
-
var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
|
|
4039
|
-
var MERGE_READ_BACK_ATTEMPTS = 3;
|
|
4040
|
-
var MERGE_READ_BACK_INTERVAL_MS = 1e3;
|
|
4041
|
-
var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
|
|
4042
|
-
var LABELS_SEGMENT2 = "/labels";
|
|
4043
|
-
var PR_LABELS = z12.array(z12.object({ name: z12.string() }));
|
|
4044
|
-
async function add_pr_classification(number, label) {
|
|
4045
|
-
await git_gh_exec.exec_gh_api({
|
|
4046
|
-
path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
|
|
4047
|
-
body: JSON.stringify({ labels: [label] })
|
|
4048
|
-
});
|
|
4049
|
-
}
|
|
4050
|
-
async function pr_get_classification(branch_name) {
|
|
4051
|
-
const number = await require_pr_number(branch_name);
|
|
4052
|
-
const response = await git_gh_exec.exec_gh_api({
|
|
4053
|
-
path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
|
|
4054
|
-
});
|
|
4055
|
-
const names = PR_LABELS.parse(JSON.parse(response)).map((label) => label.name.toLowerCase());
|
|
4056
|
-
const selected = names.filter((name) => pr_classification.is_classification(name));
|
|
4057
|
-
if (selected.length === 0) return void 0;
|
|
4058
|
-
const error = pr_classification.classification_error(names, "human");
|
|
4059
|
-
if (error !== void 0) throw new Error(error);
|
|
4060
|
-
return selected[0];
|
|
4061
|
-
}
|
|
4062
|
-
async function pr_ensure_classification(branch_name, label) {
|
|
4063
|
-
if (await pr_get_classification(branch_name) !== void 0) return;
|
|
4064
|
-
const number = await require_pr_number(branch_name);
|
|
4065
|
-
await add_pr_classification(String(number), label);
|
|
4066
|
-
}
|
|
4067
|
-
async function post_pull_request(title, body) {
|
|
4068
|
-
const base = await git_command.get_default_branch();
|
|
4069
|
-
const head = await git_command.branch();
|
|
4070
|
-
forget_pr_numbers();
|
|
4071
|
-
try {
|
|
4072
|
-
return await git_gh_exec.exec_gh_api({
|
|
4073
|
-
path: git_gh_api_path.pulls_api_path(),
|
|
4074
|
-
body: JSON.stringify({ title, body, head, base }),
|
|
4075
|
-
jq_filter: HTML_URL_FILTER2
|
|
4076
|
-
});
|
|
4077
|
-
} catch (error) {
|
|
4078
|
-
return git_gh_helpers.handle_pr_create_error(error);
|
|
4079
|
-
}
|
|
4080
|
-
}
|
|
4081
|
-
async function pr_create(title, body, label) {
|
|
4082
|
-
const classification_error2 = pr_classification.classification_error([label], "human");
|
|
4083
|
-
if (classification_error2 !== void 0) throw new Error(classification_error2);
|
|
4084
|
-
const url = await post_pull_request(title, body);
|
|
4085
|
-
const number = PR_NUMBER_PATTERN.exec(url)?.[1];
|
|
4086
|
-
if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
|
|
4087
|
-
await add_pr_classification(number, label);
|
|
4088
|
-
return url;
|
|
4089
|
-
}
|
|
4090
|
-
async function pr_comment(branch_name, body) {
|
|
4091
|
-
const pr_number = await require_pr_number(branch_name);
|
|
4092
|
-
return await git_gh_issue_write.issue_comment(String(pr_number), body);
|
|
4093
|
-
}
|
|
4094
|
-
async function pr_update_body(branch_name, body) {
|
|
4095
|
-
const pr_number = await require_pr_number(branch_name);
|
|
4096
|
-
await git_gh_exec.exec_gh_api({
|
|
4097
|
-
path: git_gh_api_path.pull_api_path(String(pr_number)),
|
|
4098
|
-
method: PATCH_METHOD2,
|
|
4099
|
-
body: JSON.stringify({ body })
|
|
4100
|
-
});
|
|
4101
|
-
}
|
|
4102
|
-
async function put_merge(pr_number) {
|
|
4103
|
-
await git_gh_exec.exec_gh_api({
|
|
4104
|
-
path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
|
|
4105
|
-
method: PUT_METHOD,
|
|
4106
|
-
body: JSON.stringify({ merge_method: MERGE_COMMIT_METHOD }),
|
|
4107
|
-
timeout_ms: MERGE_REQUEST_TIMEOUT_MS
|
|
4108
|
-
});
|
|
4109
|
-
}
|
|
4110
|
-
async function read_merge_state(pr_number) {
|
|
4111
|
-
try {
|
|
4112
|
-
return git_gh_pr_rest.is_merged(await read_pull(pr_number));
|
|
4113
|
-
} catch (error) {
|
|
4114
|
-
console.warn(`Could not read the pull request back: ${String(error)}`);
|
|
4115
|
-
return void 0;
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
async function sleep_between_read_backs(attempt) {
|
|
4119
|
-
if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
|
|
4120
|
-
}
|
|
4121
|
-
async function has_merge_landed(pr_number, merge_error) {
|
|
4122
|
-
for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
|
|
4123
|
-
const is_merged2 = await read_merge_state(pr_number);
|
|
4124
|
-
if (is_merged2 !== void 0) return is_merged2;
|
|
4125
|
-
await sleep_between_read_backs(attempt);
|
|
4126
|
-
}
|
|
4127
|
-
throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
|
|
4128
|
-
}
|
|
4129
|
-
async function pr_merge(branch_name) {
|
|
4130
|
-
const pr_number = await require_pr_number(branch_name);
|
|
4131
|
-
try {
|
|
4132
|
-
await put_merge(pr_number);
|
|
4133
|
-
} catch (error) {
|
|
4134
|
-
if (!await has_merge_landed(pr_number, error)) throw error;
|
|
4135
|
-
}
|
|
4136
|
-
}
|
|
4137
|
-
var git_gh_pr = {
|
|
4138
|
-
...git_gh_pr_read,
|
|
4139
|
-
...git_gh_pr_snapshot,
|
|
4140
|
-
pr_create,
|
|
4141
|
-
pr_get_classification,
|
|
4142
|
-
pr_ensure_classification,
|
|
4143
|
-
pr_comment,
|
|
4144
|
-
pr_merge,
|
|
4145
|
-
pr_update_body
|
|
4146
|
-
};
|
|
4147
|
-
|
|
4148
|
-
// scripts/git/git-gh-pr-auto-merge.ts
|
|
4149
|
-
var GRAPHQL_PATH = "graphql";
|
|
4150
|
-
var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
|
|
4151
|
-
var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
|
|
4152
|
-
var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }";
|
|
4153
|
-
async function pr_enable_auto_merge(branch_name) {
|
|
4154
|
-
const pull = await read_pull(await require_pr_number(branch_name));
|
|
4155
|
-
if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
|
|
4156
|
-
await git_gh_exec.exec_gh_api({
|
|
4157
|
-
path: GRAPHQL_PATH,
|
|
4158
|
-
body: JSON.stringify({ query: ENABLE_AUTO_MERGE_MUTATION, variables: { id: pull.node_id } })
|
|
4159
|
-
});
|
|
4160
|
-
}
|
|
4161
|
-
async function pr_is_merged(branch_name) {
|
|
4162
|
-
try {
|
|
4163
|
-
return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
|
|
4164
|
-
} catch {
|
|
4165
|
-
return false;
|
|
4166
|
-
}
|
|
4167
|
-
}
|
|
4168
|
-
function head_reference_of(pull) {
|
|
4169
|
-
return pull.head?.ref ?? "";
|
|
4170
|
-
}
|
|
4171
|
-
function has_head_prefix(pull, prefix) {
|
|
4172
|
-
return git_gh_pr_rest.is_same_repository_head(pull) && head_reference_of(pull).startsWith(prefix);
|
|
4173
|
-
}
|
|
4174
|
-
function to_open_pull(pull) {
|
|
4175
|
-
return {
|
|
4176
|
-
number: pull.number,
|
|
4177
|
-
head_ref: head_reference_of(pull),
|
|
4178
|
-
has_auto_merge: pull.auto_merge !== null && pull.auto_merge !== void 0
|
|
4179
|
-
};
|
|
4180
|
-
}
|
|
4181
|
-
async function pr_list_open_with_head_prefix(prefix) {
|
|
4182
|
-
const json = await git_gh_exec.exec_gh_api({
|
|
4183
|
-
path: `${git_gh_api_path.pulls_api_path()}${OPEN_PULLS_QUERY}`,
|
|
4184
|
-
should_paginate: true
|
|
4185
|
-
});
|
|
4186
|
-
return git_gh_pr_rest.parse_rest_pulls(json).filter((pull) => has_head_prefix(pull, prefix)).map((pull) => to_open_pull(pull));
|
|
4187
|
-
}
|
|
4188
|
-
var git_gh_pr_auto_merge = {
|
|
4189
|
-
pr_enable_auto_merge,
|
|
4190
|
-
pr_is_merged,
|
|
4191
|
-
pr_list_open_with_head_prefix
|
|
4192
|
-
};
|
|
4193
|
-
|
|
4194
|
-
// scripts/git/git-gh-repo.ts
|
|
4195
|
-
async function repo_get_name_with_owner() {
|
|
4196
|
-
try {
|
|
4197
|
-
const result = await git_gh_exec.exec_gh_api({
|
|
4198
|
-
path: git_gh_api_path.repo_api_path(),
|
|
4199
|
-
jq_filter: ".full_name"
|
|
4200
|
-
});
|
|
4201
|
-
return git_gh_helpers.parse_pr_state_string(result);
|
|
4202
|
-
} catch {
|
|
4203
|
-
return void 0;
|
|
4204
|
-
}
|
|
4205
|
-
}
|
|
4206
|
-
var git_gh_repo = {
|
|
4207
|
-
repo_get_name_with_owner
|
|
4208
|
-
};
|
|
4209
|
-
|
|
4210
|
-
// scripts/git/git-pr-checks-parse.ts
|
|
4211
|
-
var CHECK_STATUS_PASS = "pass";
|
|
4212
|
-
var CHECK_STATUS_PENDING = "pending";
|
|
4213
|
-
var CHECK_STATUS_FAIL = "fail";
|
|
4214
|
-
var CHECK_STATUS_MISSING = "missing";
|
|
4215
|
-
var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "skipped", "neutral"]);
|
|
4216
|
-
var KEY_TYPE_NAME = "__typename";
|
|
4217
|
-
var KEY_STATE = "state";
|
|
4218
|
-
var KEY_STATUS = "status";
|
|
4219
|
-
var KEY_CONCLUSION = "conclusion";
|
|
4220
|
-
var KEY_NAME = "name";
|
|
4221
|
-
var KEY_CONTEXT = "context";
|
|
4222
|
-
function read_string(value) {
|
|
4223
|
-
if (typeof value !== "string") return void 0;
|
|
4224
|
-
const trimmed = value.trim();
|
|
4225
|
-
if (trimmed.length === 0) return void 0;
|
|
4226
|
-
return trimmed;
|
|
4227
|
-
}
|
|
4228
|
-
var PENDING_STATUS_STATES = /* @__PURE__ */ new Set(["pending", "expected"]);
|
|
4229
|
-
function parse_status_context(item) {
|
|
4230
|
-
const state = read_string(item[KEY_STATE])?.toLowerCase();
|
|
4231
|
-
if (state === "success") return CHECK_STATUS_PASS;
|
|
4232
|
-
if (state !== void 0 && PENDING_STATUS_STATES.has(state)) return CHECK_STATUS_PENDING;
|
|
4233
|
-
return CHECK_STATUS_FAIL;
|
|
4234
|
-
}
|
|
4235
|
-
function is_passing_conclusion(item) {
|
|
4236
|
-
const conclusion = read_string(item[KEY_CONCLUSION])?.toLowerCase();
|
|
4237
|
-
return conclusion !== void 0 && PASSING_CONCLUSIONS.has(conclusion);
|
|
4238
|
-
}
|
|
4239
|
-
function parse_check_run(item) {
|
|
4240
|
-
const status = read_string(item[KEY_STATUS])?.toLowerCase();
|
|
4241
|
-
if (status !== "completed") return CHECK_STATUS_PENDING;
|
|
4242
|
-
return is_passing_conclusion(item) ? CHECK_STATUS_PASS : CHECK_STATUS_FAIL;
|
|
4243
|
-
}
|
|
4244
|
-
function parse_rollup_status(item) {
|
|
4245
|
-
const type_name = read_string(item[KEY_TYPE_NAME]);
|
|
4246
|
-
if (type_name === "StatusContext") return parse_status_context(item);
|
|
4247
|
-
return parse_check_run(item);
|
|
4248
|
-
}
|
|
4249
|
-
function read_rollup_array(parsed) {
|
|
4250
|
-
const result = pr_raw_schema.safeParse(parsed);
|
|
4251
|
-
if (!result.success) return [];
|
|
4252
|
-
return result.data.statusCheckRollup ?? [];
|
|
4253
|
-
}
|
|
4254
|
-
function parse_json_safe(raw_json) {
|
|
4255
|
-
try {
|
|
4256
|
-
return JSON.parse(raw_json);
|
|
4257
|
-
} catch {
|
|
4258
|
-
return void 0;
|
|
4259
|
-
}
|
|
4260
|
-
}
|
|
4261
|
-
function parse_rollup_item(item) {
|
|
4262
|
-
const name = read_string(item[KEY_NAME]) ?? read_string(item[KEY_CONTEXT]);
|
|
4263
|
-
if (name === void 0) return void 0;
|
|
4264
|
-
return { name, status: parse_rollup_status(item) };
|
|
4265
|
-
}
|
|
4266
|
-
function parse_rollup_checks(raw_json) {
|
|
4267
|
-
const parsed = parse_json_safe(raw_json);
|
|
4268
|
-
const rollup = read_rollup_array(parsed);
|
|
4269
|
-
const checks = [];
|
|
4270
|
-
for (const item of rollup) {
|
|
4271
|
-
const parsed_item = parse_rollup_item(item);
|
|
4272
|
-
if (parsed_item !== void 0) checks.push(parsed_item);
|
|
4273
|
-
}
|
|
4274
|
-
return checks;
|
|
4275
|
-
}
|
|
4276
|
-
function parse_pr_state_snapshot(raw_json) {
|
|
4277
|
-
const parsed = parse_json_safe(raw_json);
|
|
4278
|
-
const result = pr_raw_schema.safeParse(parsed);
|
|
4279
|
-
const data = result.success ? result.data : void 0;
|
|
4280
|
-
return {
|
|
4281
|
-
rollup: parse_rollup_checks(raw_json),
|
|
4282
|
-
merge_state_status: read_string(data?.mergeStateStatus),
|
|
4283
|
-
review_decision: read_string(data?.reviewDecision)
|
|
4284
|
-
};
|
|
4285
|
-
}
|
|
4286
|
-
|
|
4287
|
-
// scripts/git/git-pr-checks-eval.ts
|
|
4288
|
-
var DEFAULT_REQUIRED_CHECKS = ["SonarQube"];
|
|
4289
|
-
var CODERABBIT_CHECK_NAME = "CodeRabbit";
|
|
4290
|
-
var REQUIRED_CHECKS_ENV_VAR = "JOSH_REQUIRED_CHECKS";
|
|
4291
|
-
function parse_required_checks() {
|
|
4292
|
-
const raw = process.env[REQUIRED_CHECKS_ENV_VAR];
|
|
4293
|
-
if (!raw) return DEFAULT_REQUIRED_CHECKS;
|
|
4294
|
-
return raw.split(",").map((check3) => check3.trim()).filter((check3) => check3.length > 0);
|
|
4295
|
-
}
|
|
4296
|
-
var REQUIRED_CHECKS = parse_required_checks();
|
|
4297
|
-
var MERGE_STATE_CLEAN = "CLEAN";
|
|
4298
|
-
var MERGE_STATE_UNSTABLE = "UNSTABLE";
|
|
4299
|
-
var MERGE_STATE_DIRTY = "DIRTY";
|
|
4300
|
-
var REVIEW_CHANGES_REQUESTED2 = "CHANGES_REQUESTED";
|
|
4301
|
-
var CHECK_SUITE_SEPARATOR = " / ";
|
|
4302
|
-
var FAILURE_MESSAGE_PREFIX = "PR checks failed";
|
|
4303
|
-
var FAILURE_REASON_REVIEW = "review requested changes";
|
|
4304
|
-
var FAILURE_REASON_CHECKS = "failed checks";
|
|
4305
|
-
var FAILURE_REASON_CONFLICT = "merge conflict";
|
|
4306
|
-
function is_required_match(check_name, required_name) {
|
|
4307
|
-
if (check_name === required_name) return true;
|
|
4308
|
-
return check_name.startsWith(`${required_name}${CHECK_SUITE_SEPARATOR}`);
|
|
4309
|
-
}
|
|
4310
|
-
function find_required_check(checks, required_name) {
|
|
4311
|
-
return checks.find((check3) => is_required_match(check3.name, required_name));
|
|
4312
|
-
}
|
|
4313
|
-
function read_required_statuses(checks) {
|
|
4314
|
-
return REQUIRED_CHECKS.map(
|
|
4315
|
-
(name) => find_required_check(checks, name)?.status ?? CHECK_STATUS_MISSING
|
|
4316
|
-
);
|
|
4317
|
-
}
|
|
4318
|
-
function is_review_blocked(review_decision) {
|
|
4319
|
-
return review_decision === REVIEW_CHANGES_REQUESTED2;
|
|
4320
|
-
}
|
|
4321
|
-
function is_merge_conflict(merge_state_status) {
|
|
4322
|
-
return merge_state_status === MERGE_STATE_DIRTY;
|
|
4323
|
-
}
|
|
4324
|
-
function evaluate_failure_state(input) {
|
|
4325
|
-
if (is_merge_conflict(input.merge_state_status) || is_review_blocked(input.review_decision) || input.failed_checks.length > 0) {
|
|
4326
|
-
return "failure";
|
|
4327
|
-
}
|
|
4328
|
-
return void 0;
|
|
4329
|
-
}
|
|
4330
|
-
function is_merge_state_clean(merge_state_status) {
|
|
4331
|
-
return merge_state_status === MERGE_STATE_CLEAN;
|
|
4332
|
-
}
|
|
4333
|
-
function is_every_required_passing(statuses) {
|
|
4334
|
-
return statuses.every((status) => status === CHECK_STATUS_PASS);
|
|
4335
|
-
}
|
|
4336
|
-
function is_coderabbit_check(check_name) {
|
|
4337
|
-
return is_required_match(check_name, CODERABBIT_CHECK_NAME);
|
|
4338
|
-
}
|
|
4339
|
-
function is_unstable_only_from_coderabbit(snapshot) {
|
|
4340
|
-
if (snapshot.merge_state_status !== MERGE_STATE_UNSTABLE) return false;
|
|
4341
|
-
const non_passing = snapshot.rollup.filter((check3) => check3.status !== CHECK_STATUS_PASS);
|
|
4342
|
-
if (non_passing.length === 0) return false;
|
|
4343
|
-
return non_passing.every((check3) => is_coderabbit_check(check3.name));
|
|
4344
|
-
}
|
|
4345
|
-
function is_required_check(check_name) {
|
|
4346
|
-
return REQUIRED_CHECKS.some((required) => is_required_match(check_name, required));
|
|
4347
|
-
}
|
|
4348
|
-
function is_blocking_failure(check3) {
|
|
4349
|
-
if (check3.status !== CHECK_STATUS_FAIL) return false;
|
|
4350
|
-
return !is_coderabbit_check(check3.name) || is_required_check(check3.name);
|
|
4351
|
-
}
|
|
4352
|
-
function collect_blocking_failures(snapshot) {
|
|
4353
|
-
return snapshot.rollup.filter((check3) => is_blocking_failure(check3)).map((check3) => check3.name);
|
|
4354
|
-
}
|
|
4355
|
-
function is_mergeable_state(snapshot) {
|
|
4356
|
-
if (is_merge_state_clean(snapshot.merge_state_status)) return true;
|
|
4357
|
-
return is_unstable_only_from_coderabbit(snapshot);
|
|
4358
|
-
}
|
|
4359
|
-
function collect_failure_reasons(snapshot) {
|
|
4360
|
-
const reasons = [];
|
|
4361
|
-
if (is_merge_conflict(snapshot.merge_state_status)) reasons.push(FAILURE_REASON_CONFLICT);
|
|
4362
|
-
if (is_review_blocked(snapshot.review_decision)) reasons.push(FAILURE_REASON_REVIEW);
|
|
4363
|
-
const failed = collect_blocking_failures(snapshot);
|
|
4364
|
-
if (failed.length > 0) reasons.push(`${FAILURE_REASON_CHECKS}: ${failed.join(", ")}`);
|
|
4365
|
-
return reasons;
|
|
4366
|
-
}
|
|
4367
|
-
function describe_pr_failure(snapshot) {
|
|
4368
|
-
const reasons = collect_failure_reasons(snapshot);
|
|
4369
|
-
if (reasons.length === 0) return `${FAILURE_MESSAGE_PREFIX}.`;
|
|
4370
|
-
return `${FAILURE_MESSAGE_PREFIX} (${reasons.join("; ")}).`;
|
|
4371
|
-
}
|
|
4372
|
-
function evaluate_pr_state(snapshot) {
|
|
4373
|
-
const statuses = read_required_statuses(snapshot.rollup);
|
|
4374
|
-
const failure = evaluate_failure_state({
|
|
4375
|
-
review_decision: snapshot.review_decision,
|
|
4376
|
-
failed_checks: collect_blocking_failures(snapshot),
|
|
4377
|
-
merge_state_status: snapshot.merge_state_status
|
|
4378
|
-
});
|
|
4379
|
-
if (failure !== void 0) return failure;
|
|
4380
|
-
if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
|
|
4381
|
-
return "success";
|
|
4382
|
-
}
|
|
4383
|
-
return "pending";
|
|
4384
|
-
}
|
|
4385
|
-
function is_review_decision_decisive(snapshot) {
|
|
4386
|
-
return evaluate_pr_state(snapshot) !== "pending";
|
|
4387
|
-
}
|
|
4388
|
-
|
|
4389
|
-
// scripts/git/git-pr-checks.ts
|
|
4390
|
-
var SECONDS_TO_MS = 1e3;
|
|
4391
|
-
var CHECK_WAIT_INTERVAL_MS = 1e4;
|
|
4392
|
-
var DEFAULT_TIMEOUT_SECONDS = 1920;
|
|
4393
|
-
function compute_max_attempts(timeout_seconds, interval_ms) {
|
|
4394
|
-
return Math.ceil(timeout_seconds / (interval_ms / SECONDS_TO_MS)) + 1;
|
|
4395
|
-
}
|
|
4396
|
-
var DEFAULT_MAX_ATTEMPTS = compute_max_attempts(DEFAULT_TIMEOUT_SECONDS, CHECK_WAIT_INTERVAL_MS);
|
|
4397
|
-
function get_configured_max_attempts() {
|
|
4398
|
-
const environment_seconds = Number(process.env["JOSH_CI_TIMEOUT_SECONDS"]);
|
|
4399
|
-
return Number.isFinite(environment_seconds) && environment_seconds > 0 ? compute_max_attempts(environment_seconds, CHECK_WAIT_INTERVAL_MS) : DEFAULT_MAX_ATTEMPTS;
|
|
4400
|
-
}
|
|
4401
|
-
var CHECK_MAX_ATTEMPTS = get_configured_max_attempts();
|
|
4402
|
-
var DEFAULT_STABLE_READS = 2;
|
|
4403
|
-
var MAX_CONSECUTIVE_READ_FAILURES = 3;
|
|
4404
|
-
var PR_CHECKS_TIMEOUT_MESSAGE = "Timed out while waiting for PR checks to complete.";
|
|
4405
|
-
function is_pr_checks_timeout(error) {
|
|
4406
|
-
return error instanceof Error && error.message === PR_CHECKS_TIMEOUT_MESSAGE;
|
|
4407
|
-
}
|
|
4408
|
-
var SHOULD_ALWAYS_READ_REVIEW_DECISION = () => true;
|
|
4409
|
-
var MERGE_GATE_EVALUATOR = {
|
|
4410
|
-
evaluate: evaluate_pr_state,
|
|
4411
|
-
describe: describe_pr_failure,
|
|
4412
|
-
should_read_review_decision: is_review_decision_decisive
|
|
4413
|
-
};
|
|
4414
|
-
function advance_stable_count(previous, state) {
|
|
4415
|
-
return state === "success" ? previous + 1 : 0;
|
|
4416
|
-
}
|
|
4417
|
-
function classify_poll_result(input) {
|
|
4418
|
-
const state = input.evaluator.evaluate(input.snapshot);
|
|
4419
|
-
if (state === "failure") throw new Error(input.evaluator.describe(input.snapshot));
|
|
4420
|
-
const next_stable_count = advance_stable_count(input.stable_count, state);
|
|
4421
|
-
return {
|
|
4422
|
-
is_done: next_stable_count >= input.required_stable_reads,
|
|
4423
|
-
next_stable_count
|
|
4424
|
-
};
|
|
4425
|
-
}
|
|
4426
|
-
async function read_pr_state(input, evaluator) {
|
|
4427
|
-
try {
|
|
4428
|
-
const snapshot = await input.options.fetcher(
|
|
4429
|
-
input.options.branch_name,
|
|
4430
|
-
evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION
|
|
4431
|
-
);
|
|
4432
|
-
return { kind: "read", snapshot };
|
|
4433
|
-
} catch (error) {
|
|
4434
|
-
return { kind: "unreadable", cause: error };
|
|
4435
|
-
}
|
|
4436
|
-
}
|
|
4437
|
-
async function sleep_before_next_attempt(input) {
|
|
4438
|
-
if (input.attempt < input.options.max_attempts - 1) {
|
|
4439
|
-
await poll.sleep(input.options.interval_ms);
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
async function retry_after_unreadable(input, cause) {
|
|
4443
|
-
const next_failure_count = input.failure_count + 1;
|
|
4444
|
-
if (next_failure_count >= MAX_CONSECUTIVE_READ_FAILURES) throw cause;
|
|
4445
|
-
console.warn(
|
|
4446
|
-
`Could not read the PR state (${String(next_failure_count)}/${String(MAX_CONSECUTIVE_READ_FAILURES)}); asking again on the next poll: ${String(cause)}`
|
|
4447
|
-
);
|
|
4448
|
-
await sleep_before_next_attempt(input);
|
|
4449
|
-
return { next_stable_count: 0, next_failure_count, read_failure: cause };
|
|
4450
|
-
}
|
|
4451
|
-
function to_exhaustion_error(last_read_failure) {
|
|
4452
|
-
return last_read_failure ?? new Error(PR_CHECKS_TIMEOUT_MESSAGE);
|
|
4453
|
-
}
|
|
4454
|
-
async function attempt_pr_success_poll(input) {
|
|
4455
|
-
const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
|
|
4456
|
-
const read2 = await read_pr_state(input, evaluator);
|
|
4457
|
-
if (read2.kind === "unreadable") return await retry_after_unreadable(input, read2.cause);
|
|
4458
|
-
const classification = classify_poll_result({
|
|
4459
|
-
snapshot: read2.snapshot,
|
|
4460
|
-
stable_count: input.stable_count,
|
|
4461
|
-
required_stable_reads: input.options.required_stable_reads,
|
|
4462
|
-
evaluator
|
|
4463
|
-
});
|
|
4464
|
-
if (classification.is_done) {
|
|
4465
|
-
return { snapshot: read2.snapshot, next_stable_count: 0, next_failure_count: 0 };
|
|
4466
|
-
}
|
|
4467
|
-
await sleep_before_next_attempt(input);
|
|
4468
|
-
return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
|
|
4469
|
-
}
|
|
4470
|
-
async function wait_for_pr_success(options) {
|
|
4471
|
-
let carried = { next_stable_count: 0, next_failure_count: 0 };
|
|
4472
|
-
for (let attempt = 0; attempt < options.max_attempts; attempt += 1) {
|
|
4473
|
-
console.info(`Checking PR status\u2026 (${String(attempt + 1)}/${String(options.max_attempts)})`);
|
|
4474
|
-
carried = await attempt_pr_success_poll({
|
|
4475
|
-
options,
|
|
4476
|
-
stable_count: carried.next_stable_count,
|
|
4477
|
-
failure_count: carried.next_failure_count,
|
|
4478
|
-
attempt
|
|
4479
|
-
});
|
|
4480
|
-
if (carried.snapshot !== void 0) return carried.snapshot;
|
|
4481
|
-
}
|
|
4482
|
-
throw to_exhaustion_error(carried.read_failure);
|
|
4483
|
-
}
|
|
4484
|
-
async function default_fetch_pr_state(branch_name, should_read_review_decision = SHOULD_ALWAYS_READ_REVIEW_DECISION) {
|
|
4485
|
-
const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name);
|
|
4486
|
-
const snapshot = parse_pr_state_snapshot(checks.snapshot_json);
|
|
4487
|
-
if (!should_read_review_decision(snapshot)) return snapshot;
|
|
4488
|
-
const decision = await git_gh_pr_snapshot.pr_get_review_decision(checks.pr_number);
|
|
4489
|
-
return { ...snapshot, review_decision: read_string(decision) };
|
|
4490
|
-
}
|
|
4491
|
-
|
|
4492
|
-
// scripts/git/git-pr-checks-watch.ts
|
|
4493
|
-
var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
|
|
4494
|
-
var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
|
|
4495
|
-
var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
|
|
4496
|
-
var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
|
|
4497
|
-
function is_awaited_check(check3) {
|
|
4498
|
-
return !is_coderabbit_check(check3.name) || is_required_check(check3.name);
|
|
4499
|
-
}
|
|
4500
|
-
function evaluate_checks_settled(snapshot) {
|
|
4501
|
-
const awaited = snapshot.rollup.filter((check3) => is_awaited_check(check3));
|
|
4502
|
-
const considered = awaited.length === 0 ? snapshot.rollup : awaited;
|
|
4503
|
-
if (considered.length === 0 || considered.some((check3) => check3.status === CHECK_STATUS_PENDING)) {
|
|
4504
|
-
return "pending";
|
|
4505
|
-
}
|
|
4506
|
-
return snapshot.rollup.some((check3) => check3.status === CHECK_STATUS_FAIL) ? "failure" : "success";
|
|
4507
|
-
}
|
|
4508
|
-
function describe_checks_failure(snapshot) {
|
|
4509
|
-
const failed = snapshot.rollup.filter((check3) => check3.status === CHECK_STATUS_FAIL);
|
|
4510
|
-
return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check3) => check3.name).join(", ")}`;
|
|
4511
|
-
}
|
|
4512
|
-
var SHOULD_NEVER_READ_REVIEW_DECISION = () => false;
|
|
4513
|
-
var CHECKS_SETTLED_EVALUATOR = {
|
|
4514
|
-
evaluate: evaluate_checks_settled,
|
|
4515
|
-
describe: describe_checks_failure,
|
|
4516
|
-
should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION
|
|
4517
|
-
};
|
|
4518
|
-
async function watch_until_settled(branch_name) {
|
|
4519
|
-
await wait_for_pr_success({
|
|
4520
|
-
branch_name,
|
|
4521
|
-
fetcher: default_fetch_pr_state,
|
|
4522
|
-
interval_ms: CHECK_WAIT_INTERVAL_MS,
|
|
4523
|
-
max_attempts: compute_max_attempts(
|
|
4524
|
-
PR_CHECKS_WATCH_TIMEOUT_MS / SECONDS_TO_MS,
|
|
4525
|
-
CHECK_WAIT_INTERVAL_MS
|
|
4526
|
-
),
|
|
4527
|
-
required_stable_reads: DEFAULT_STABLE_READS,
|
|
4528
|
-
evaluator: CHECKS_SETTLED_EVALUATOR
|
|
4529
|
-
});
|
|
4530
|
-
}
|
|
4531
|
-
async function fail_when_no_checks(branch_name) {
|
|
4532
|
-
const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION);
|
|
4533
|
-
if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE);
|
|
4534
|
-
}
|
|
4535
|
-
async function pr_checks_watch(branch_name) {
|
|
4536
|
-
try {
|
|
4537
|
-
await watch_until_settled(branch_name);
|
|
4538
|
-
return { timed_out: false };
|
|
4539
|
-
} catch (error) {
|
|
4540
|
-
if (!is_pr_checks_timeout(error)) throw error;
|
|
4541
|
-
await fail_when_no_checks(branch_name);
|
|
4542
|
-
console.info(WATCH_TIMED_OUT_NOTE);
|
|
4543
|
-
return { timed_out: true };
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
var git_pr_checks_watch = { pr_checks_watch };
|
|
4547
|
-
|
|
4548
|
-
// scripts/git/git-gh-command.ts
|
|
4549
|
-
var git_gh_command = {
|
|
4550
|
-
...git_gh_pr,
|
|
4551
|
-
...git_gh_pr_auto_merge,
|
|
4552
|
-
pr_checks_watch: git_pr_checks_watch.pr_checks_watch,
|
|
4553
|
-
...git_gh_issue,
|
|
4554
|
-
...git_gh_repo
|
|
4555
|
-
};
|
|
4556
|
-
|
|
4557
3325
|
// scripts/epic/epic-busy.ts
|
|
4558
3326
|
var LISTING_LIMIT = 100;
|
|
4559
3327
|
var PARKED_LABELS = /* @__PURE__ */ new Set([NEEDS_DECISION_LABEL, ALREADY_DONE_LABEL]);
|
|
@@ -4610,37 +3378,36 @@ var epic_busy = {
|
|
|
4610
3378
|
unreadable_message,
|
|
4611
3379
|
truncated_message,
|
|
4612
3380
|
busy_reason,
|
|
4613
|
-
format_holders,
|
|
4614
3381
|
is_parked,
|
|
4615
3382
|
parse_listing,
|
|
4616
3383
|
read_repository
|
|
4617
3384
|
};
|
|
4618
3385
|
|
|
4619
3386
|
// scripts/run/run-liveness.ts
|
|
4620
|
-
import { readFileSync as
|
|
3387
|
+
import { readFileSync as readFileSync5, realpathSync, statSync } from "node:fs";
|
|
4621
3388
|
import { homedir, tmpdir } from "node:os";
|
|
4622
|
-
import
|
|
4623
|
-
import { setTimeout as
|
|
3389
|
+
import path8 from "node:path";
|
|
3390
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
4624
3391
|
|
|
4625
3392
|
// scripts/agent/agent-event.ts
|
|
4626
|
-
import { z as
|
|
3393
|
+
import { z as z9 } from "zod";
|
|
4627
3394
|
|
|
4628
3395
|
// scripts/agent/claude-result-event.ts
|
|
4629
|
-
import { z as
|
|
4630
|
-
var RESULT_EVENT_SCHEMA =
|
|
4631
|
-
type:
|
|
4632
|
-
is_error:
|
|
4633
|
-
subtype:
|
|
4634
|
-
num_turns:
|
|
4635
|
-
permission_denials:
|
|
4636
|
-
result:
|
|
4637
|
-
session_id:
|
|
4638
|
-
usage:
|
|
3396
|
+
import { z as z8 } from "zod";
|
|
3397
|
+
var RESULT_EVENT_SCHEMA = z8.looseObject({
|
|
3398
|
+
type: z8.literal("result"),
|
|
3399
|
+
is_error: z8.unknown().optional(),
|
|
3400
|
+
subtype: z8.unknown().optional(),
|
|
3401
|
+
num_turns: z8.unknown().optional(),
|
|
3402
|
+
permission_denials: z8.unknown().optional(),
|
|
3403
|
+
result: z8.unknown().optional(),
|
|
3404
|
+
session_id: z8.unknown().optional(),
|
|
3405
|
+
usage: z8.unknown().optional()
|
|
4639
3406
|
});
|
|
4640
|
-
var USAGE_SCHEMA =
|
|
4641
|
-
input_tokens:
|
|
4642
|
-
cache_read_input_tokens:
|
|
4643
|
-
output_tokens:
|
|
3407
|
+
var USAGE_SCHEMA = z8.looseObject({
|
|
3408
|
+
input_tokens: z8.unknown().optional(),
|
|
3409
|
+
cache_read_input_tokens: z8.unknown().optional(),
|
|
3410
|
+
output_tokens: z8.unknown().optional()
|
|
4644
3411
|
});
|
|
4645
3412
|
function usable_reason(value) {
|
|
4646
3413
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
@@ -4661,7 +3428,7 @@ function usage_of(value) {
|
|
|
4661
3428
|
};
|
|
4662
3429
|
return Object.values(usage).some((count) => count !== void 0) ? usage : void 0;
|
|
4663
3430
|
}
|
|
4664
|
-
function
|
|
3431
|
+
function decode(value) {
|
|
4665
3432
|
const parsed = RESULT_EVENT_SCHEMA.safeParse(value);
|
|
4666
3433
|
if (!parsed.success) return void 0;
|
|
4667
3434
|
const is_error = parsed.data.is_error === true;
|
|
@@ -4676,34 +3443,34 @@ function decode2(value) {
|
|
|
4676
3443
|
usage: usage_of(parsed.data.usage)
|
|
4677
3444
|
};
|
|
4678
3445
|
}
|
|
4679
|
-
var claude_result_event = { decode
|
|
3446
|
+
var claude_result_event = { decode };
|
|
4680
3447
|
|
|
4681
3448
|
// scripts/agent/agent-event.ts
|
|
4682
|
-
var TEXT_BLOCK_SCHEMA =
|
|
4683
|
-
var CLAUDE_MESSAGE_SCHEMA =
|
|
4684
|
-
var CLAUDE_INIT_SCHEMA =
|
|
4685
|
-
var CLAUDE_ASSISTANT_SCHEMA =
|
|
4686
|
-
type:
|
|
3449
|
+
var TEXT_BLOCK_SCHEMA = z9.looseObject({ type: z9.literal("text"), text: z9.string() });
|
|
3450
|
+
var CLAUDE_MESSAGE_SCHEMA = z9.looseObject({ content: z9.array(z9.unknown()).optional() });
|
|
3451
|
+
var CLAUDE_INIT_SCHEMA = z9.looseObject({ type: z9.literal("system"), subtype: z9.literal("init") });
|
|
3452
|
+
var CLAUDE_ASSISTANT_SCHEMA = z9.looseObject({
|
|
3453
|
+
type: z9.literal("assistant"),
|
|
4687
3454
|
message: CLAUDE_MESSAGE_SCHEMA
|
|
4688
3455
|
});
|
|
4689
|
-
var USAGE_SCHEMA2 =
|
|
4690
|
-
input_tokens:
|
|
4691
|
-
cached_input_tokens:
|
|
4692
|
-
output_tokens:
|
|
3456
|
+
var USAGE_SCHEMA2 = z9.looseObject({
|
|
3457
|
+
input_tokens: z9.number().optional(),
|
|
3458
|
+
cached_input_tokens: z9.number().optional(),
|
|
3459
|
+
output_tokens: z9.number().optional()
|
|
4693
3460
|
});
|
|
4694
|
-
var CODEX_START_SCHEMA =
|
|
4695
|
-
var CODEX_PROGRESS_SCHEMA =
|
|
4696
|
-
type:
|
|
4697
|
-
item:
|
|
3461
|
+
var CODEX_START_SCHEMA = z9.looseObject({ type: z9.enum(["thread.started", "turn.started"]) });
|
|
3462
|
+
var CODEX_PROGRESS_SCHEMA = z9.looseObject({
|
|
3463
|
+
type: z9.enum(["item.started", "item.updated", "item.completed"]),
|
|
3464
|
+
item: z9.looseObject({ type: z9.string(), text: z9.string().optional() }).optional()
|
|
4698
3465
|
});
|
|
4699
|
-
var CODEX_COMPLETE_SCHEMA =
|
|
4700
|
-
type:
|
|
3466
|
+
var CODEX_COMPLETE_SCHEMA = z9.looseObject({
|
|
3467
|
+
type: z9.literal("turn.completed"),
|
|
4701
3468
|
usage: USAGE_SCHEMA2.optional()
|
|
4702
3469
|
});
|
|
4703
|
-
var CODEX_FAILURE_SCHEMA =
|
|
4704
|
-
type:
|
|
4705
|
-
error:
|
|
4706
|
-
message:
|
|
3470
|
+
var CODEX_FAILURE_SCHEMA = z9.looseObject({
|
|
3471
|
+
type: z9.enum(["turn.failed", "error"]),
|
|
3472
|
+
error: z9.looseObject({ message: z9.string().optional() }).optional(),
|
|
3473
|
+
message: z9.string().optional()
|
|
4707
3474
|
});
|
|
4708
3475
|
var EMPTY_STATE = {
|
|
4709
3476
|
provider: void 0,
|
|
@@ -4794,7 +3561,7 @@ function describe(state) {
|
|
|
4794
3561
|
var agent_event = { describe, read };
|
|
4795
3562
|
|
|
4796
3563
|
// scripts/issue/issue-state.ts
|
|
4797
|
-
import { z as
|
|
3564
|
+
import { z as z10 } from "zod";
|
|
4798
3565
|
var NO_LABELS = "(none)";
|
|
4799
3566
|
var LABEL_SEPARATOR = ", ";
|
|
4800
3567
|
var STATE_LABEL = "state: ";
|
|
@@ -4804,9 +3571,9 @@ var YES = "yes";
|
|
|
4804
3571
|
var NO = "no";
|
|
4805
3572
|
var ISSUE_LABEL = "issue: ";
|
|
4806
3573
|
var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
|
|
4807
|
-
var label_schema =
|
|
4808
|
-
var issue_state_schema =
|
|
4809
|
-
state:
|
|
3574
|
+
var label_schema = z10.looseObject({ name: z10.string() });
|
|
3575
|
+
var issue_state_schema = z10.looseObject({
|
|
3576
|
+
state: z10.string(),
|
|
4810
3577
|
labels: label_schema.array().optional()
|
|
4811
3578
|
});
|
|
4812
3579
|
function parse_issue_state(json) {
|
|
@@ -4838,10 +3605,6 @@ ${format_issue_state(state)}`;
|
|
|
4838
3605
|
}
|
|
4839
3606
|
var issue_state = {
|
|
4840
3607
|
NO_LABELS,
|
|
4841
|
-
STATE_LABEL,
|
|
4842
|
-
LABELS_LABEL,
|
|
4843
|
-
HUMAN_REVIEW_LABEL_LINE,
|
|
4844
|
-
ISSUE_LABEL,
|
|
4845
3608
|
parse_issue_state,
|
|
4846
3609
|
format_issue_state,
|
|
4847
3610
|
format_attributed_issue_state
|
|
@@ -4856,9 +3619,9 @@ var PROCESS_ALIVE = "alive";
|
|
|
4856
3619
|
var PROCESS_UNKNOWN = "unknown";
|
|
4857
3620
|
var DEFAULT_SILENT_MINUTES = 30;
|
|
4858
3621
|
var DEFAULT_GAP_SECONDS = 5;
|
|
4859
|
-
var
|
|
3622
|
+
var MS_PER_MINUTE3 = 6e4;
|
|
4860
3623
|
var MS_PER_SECOND2 = 1e3;
|
|
4861
|
-
var
|
|
3624
|
+
var OPEN_STATE2 = "OPEN";
|
|
4862
3625
|
var STATE_FIELDS = "state,labels";
|
|
4863
3626
|
var REASONS = {
|
|
4864
3627
|
[ALIVE_VERDICT]: "The unit is still working: its output moved, or a process of the child is alive.",
|
|
@@ -4904,7 +3667,7 @@ function to_advice(verdict, has_work_to_stash) {
|
|
|
4904
3667
|
if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
|
|
4905
3668
|
return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
|
|
4906
3669
|
}
|
|
4907
|
-
function
|
|
3670
|
+
function decide4(traces) {
|
|
4908
3671
|
const verdict = to_verdict(traces);
|
|
4909
3672
|
const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
|
|
4910
3673
|
return {
|
|
@@ -4927,12 +3690,12 @@ var ALLOWED_ROOTS = [
|
|
|
4927
3690
|
)
|
|
4928
3691
|
];
|
|
4929
3692
|
function is_within(candidate, root) {
|
|
4930
|
-
const relative =
|
|
4931
|
-
return relative !== "" && !relative.startsWith("..") && !
|
|
3693
|
+
const relative = path8.relative(root, candidate);
|
|
3694
|
+
return relative !== "" && !relative.startsWith("..") && !path8.isAbsolute(relative);
|
|
4932
3695
|
}
|
|
4933
3696
|
function to_safe_path(output_path) {
|
|
4934
|
-
const normalized2 =
|
|
4935
|
-
if (!
|
|
3697
|
+
const normalized2 = path8.normalize(output_path);
|
|
3698
|
+
if (!path8.isAbsolute(normalized2)) return void 0;
|
|
4936
3699
|
return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
|
|
4937
3700
|
}
|
|
4938
3701
|
function sample_output(output_path) {
|
|
@@ -4946,7 +3709,7 @@ function read_agent_state(output_path) {
|
|
|
4946
3709
|
const safe_path = to_safe_path(output_path);
|
|
4947
3710
|
if (safe_path === void 0) return void 0;
|
|
4948
3711
|
try {
|
|
4949
|
-
const state = agent_event.read(
|
|
3712
|
+
const state = agent_event.read(readFileSync5(safe_path, "utf8"));
|
|
4950
3713
|
return state.provider === void 0 ? void 0 : state;
|
|
4951
3714
|
} catch {
|
|
4952
3715
|
return void 0;
|
|
@@ -4960,7 +3723,7 @@ async function read_output_frozen(request) {
|
|
|
4960
3723
|
const first = sample_output(request.output_path);
|
|
4961
3724
|
if (first === void 0) return void 0;
|
|
4962
3725
|
if (request.now_ms - first.mtime_ms < request.silent_window_ms) return false;
|
|
4963
|
-
await
|
|
3726
|
+
await sleep(request.gap_ms);
|
|
4964
3727
|
const second = sample_output(request.output_path);
|
|
4965
3728
|
if (second === void 0) return void 0;
|
|
4966
3729
|
return second.mtime_ms === first.mtime_ms && second.size === first.size;
|
|
@@ -4977,7 +3740,7 @@ async function read_child_settled(issue, repo) {
|
|
|
4977
3740
|
if (json === void 0) return void 0;
|
|
4978
3741
|
const state = issue_state.parse_issue_state(json);
|
|
4979
3742
|
if (state === void 0) return void 0;
|
|
4980
|
-
return state.state !==
|
|
3743
|
+
return state.state !== OPEN_STATE2 || carries_settled_label(state.labels);
|
|
4981
3744
|
}
|
|
4982
3745
|
async function read_traces(request) {
|
|
4983
3746
|
const [is_initial_child_settled, is_output_frozen, is_tree_dirty] = await Promise.all([
|
|
@@ -4986,7 +3749,7 @@ async function read_traces(request) {
|
|
|
4986
3749
|
gap_ms: request.gap_ms ?? DEFAULT_GAP_SECONDS * MS_PER_SECOND2,
|
|
4987
3750
|
now_ms: Date.now(),
|
|
4988
3751
|
output_path: request.output_path,
|
|
4989
|
-
silent_window_ms: request.silent_window_ms ?? DEFAULT_SILENT_MINUTES *
|
|
3752
|
+
silent_window_ms: request.silent_window_ms ?? DEFAULT_SILENT_MINUTES * MS_PER_MINUTE3
|
|
4990
3753
|
}),
|
|
4991
3754
|
run_hold.is_tree_dirty()
|
|
4992
3755
|
]);
|
|
@@ -5003,14 +3766,13 @@ async function read_traces(request) {
|
|
|
5003
3766
|
}
|
|
5004
3767
|
async function check(request) {
|
|
5005
3768
|
run_issue_number.require_issue_number(request.issue);
|
|
5006
|
-
return
|
|
3769
|
+
return decide4(await read_traces(request));
|
|
5007
3770
|
}
|
|
5008
3771
|
var run_liveness = {
|
|
5009
3772
|
check,
|
|
5010
|
-
decide:
|
|
3773
|
+
decide: decide4,
|
|
5011
3774
|
describe_agent_state,
|
|
5012
3775
|
read_child_settled,
|
|
5013
|
-
read_agent_state,
|
|
5014
3776
|
read_output_frozen,
|
|
5015
3777
|
sample_output,
|
|
5016
3778
|
to_safe_path
|
|
@@ -5018,14 +3780,14 @@ var run_liveness = {
|
|
|
5018
3780
|
|
|
5019
3781
|
// scripts/lane/lane-report.ts
|
|
5020
3782
|
var NO_LANES2 = "No lanes are open.";
|
|
5021
|
-
var
|
|
3783
|
+
var OPEN_STATE3 = "open";
|
|
5022
3784
|
var STRANDED_STATE = "stranded";
|
|
5023
3785
|
var UNREADABLE_STATE = "unreadable";
|
|
5024
3786
|
var UNKNOWN_VALUE = "-";
|
|
5025
3787
|
var COLUMN_SEPARATOR = " ";
|
|
5026
3788
|
function lane_state(lane) {
|
|
5027
3789
|
if (lane.is_stranded) return STRANDED_STATE;
|
|
5028
|
-
return lane.seat === void 0 ? UNREADABLE_STATE :
|
|
3790
|
+
return lane.seat === void 0 ? UNREADABLE_STATE : OPEN_STATE3;
|
|
5029
3791
|
}
|
|
5030
3792
|
function value_or_unknown(value) {
|
|
5031
3793
|
return value === void 0 ? UNKNOWN_VALUE : String(value);
|
|
@@ -5069,7 +3831,7 @@ function describe_opened(lane) {
|
|
|
5069
3831
|
}
|
|
5070
3832
|
var lane_report = {
|
|
5071
3833
|
NO_LANES: NO_LANES2,
|
|
5072
|
-
OPEN_STATE:
|
|
3834
|
+
OPEN_STATE: OPEN_STATE3,
|
|
5073
3835
|
STRANDED_STATE,
|
|
5074
3836
|
UNREADABLE_STATE,
|
|
5075
3837
|
describe_lane,
|
|
@@ -5079,7 +3841,7 @@ var lane_report = {
|
|
|
5079
3841
|
};
|
|
5080
3842
|
|
|
5081
3843
|
// scripts/run/run-preflight.ts
|
|
5082
|
-
import { z as
|
|
3844
|
+
import { z as z11 } from "zod";
|
|
5083
3845
|
var CLEAN_VERDICT = "clean";
|
|
5084
3846
|
var RECLAIM_VERDICT = "reclaim";
|
|
5085
3847
|
var RESUME_VERDICT = "resume";
|
|
@@ -5089,7 +3851,7 @@ var OPEN_PR = "open";
|
|
|
5089
3851
|
var MERGED_PR = "merged";
|
|
5090
3852
|
var CLOSED_PR = "closed";
|
|
5091
3853
|
var NO_CHILD_WORK = { branch_name: void 0, pr_state: NO_PR };
|
|
5092
|
-
var
|
|
3854
|
+
var pr_info_schema = z11.object({ state: z11.string().optional() });
|
|
5093
3855
|
var GH_PR_STATES = /* @__PURE__ */ new Map([
|
|
5094
3856
|
["CLOSED", CLOSED_PR],
|
|
5095
3857
|
["MERGED", MERGED_PR],
|
|
@@ -5100,7 +3862,7 @@ var CLEAN_ADVICE = "Start the child.";
|
|
|
5100
3862
|
var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
|
|
5101
3863
|
var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
|
|
5102
3864
|
var STASH_LABEL_PREFIX = "run:hold reclaimed before #";
|
|
5103
|
-
var {
|
|
3865
|
+
var { require_issue_number } = run_issue_number;
|
|
5104
3866
|
var UNREADABLE_PR_MESSAGE = "The pull request could not be read for branch ";
|
|
5105
3867
|
function needs_reclaim(tree) {
|
|
5106
3868
|
return tree.is_dirty || tree.branch !== tree.default_branch;
|
|
@@ -5158,7 +3920,7 @@ function to_advice2(verdict, issue, tree) {
|
|
|
5158
3920
|
if (verdict === PARK_VERDICT) return PARK_ADVICE;
|
|
5159
3921
|
return CLEAN_ADVICE;
|
|
5160
3922
|
}
|
|
5161
|
-
function
|
|
3923
|
+
function decide5(tree, child, issue) {
|
|
5162
3924
|
const verdict = to_verdict2(tree, child);
|
|
5163
3925
|
return {
|
|
5164
3926
|
advice: to_advice2(verdict, issue, tree),
|
|
@@ -5168,12 +3930,12 @@ function decide4(tree, child, issue) {
|
|
|
5168
3930
|
}
|
|
5169
3931
|
function read_pr_field(raw) {
|
|
5170
3932
|
try {
|
|
5171
|
-
return
|
|
3933
|
+
return pr_info_schema.parse(JSON.parse(raw)).state;
|
|
5172
3934
|
} catch {
|
|
5173
3935
|
return void 0;
|
|
5174
3936
|
}
|
|
5175
3937
|
}
|
|
5176
|
-
function
|
|
3938
|
+
function to_pr_state(raw) {
|
|
5177
3939
|
const state = read_pr_field(raw);
|
|
5178
3940
|
return (state === void 0 ? void 0 : GH_PR_STATES.get(state)) ?? NO_PR;
|
|
5179
3941
|
}
|
|
@@ -5185,17 +3947,17 @@ async function read_tree_state() {
|
|
|
5185
3947
|
]);
|
|
5186
3948
|
return { branch: current, default_branch, is_dirty };
|
|
5187
3949
|
}
|
|
5188
|
-
async function
|
|
3950
|
+
async function read_pr_state(branch_name) {
|
|
5189
3951
|
if (!await git_gh_pr_read.pr_exists(branch_name)) return NO_PR;
|
|
5190
3952
|
const raw = await git_gh_pr_read.pr_view(branch_name);
|
|
5191
3953
|
if (raw === "") throw new Error(`${UNREADABLE_PR_MESSAGE}${branch_name}`);
|
|
5192
|
-
return
|
|
3954
|
+
return to_pr_state(raw);
|
|
5193
3955
|
}
|
|
5194
3956
|
function is_pr_open(state) {
|
|
5195
3957
|
return state === OPEN_PR;
|
|
5196
3958
|
}
|
|
5197
3959
|
async function read_pr_of_branch(branch_name) {
|
|
5198
|
-
return { branch_name, pr_state: await
|
|
3960
|
+
return { branch_name, pr_state: await read_pr_state(branch_name) };
|
|
5199
3961
|
}
|
|
5200
3962
|
function pick_child_state(reads) {
|
|
5201
3963
|
const decided = reads.find((read2) => is_pr_decided(read2.pr_state));
|
|
@@ -5259,30 +4021,23 @@ async function read_child_state(issue) {
|
|
|
5259
4021
|
async function check2(issue) {
|
|
5260
4022
|
require_issue_number(issue);
|
|
5261
4023
|
const tree = await read_tree_state();
|
|
5262
|
-
if (needs_reclaim(tree)) return
|
|
5263
|
-
return
|
|
4024
|
+
if (needs_reclaim(tree)) return decide5(tree, NO_CHILD_WORK, issue);
|
|
4025
|
+
return decide5(tree, await read_child_state(issue), issue);
|
|
5264
4026
|
}
|
|
5265
4027
|
var run_preflight = {
|
|
5266
|
-
CLEAN_ADVICE,
|
|
5267
|
-
CLEAN_REASON,
|
|
5268
4028
|
CLEAN_VERDICT,
|
|
5269
|
-
ISSUE_NUMBER_PATTERN,
|
|
5270
|
-
PARK_ADVICE,
|
|
5271
|
-
PARK_VERDICT,
|
|
5272
|
-
RECLAIM_VERDICT,
|
|
5273
4029
|
RESUME_ADVICE,
|
|
5274
|
-
RESUME_VERDICT,
|
|
5275
4030
|
MERGED_PR,
|
|
5276
4031
|
STASH_LABEL_PREFIX,
|
|
5277
4032
|
check: check2,
|
|
5278
|
-
decide:
|
|
4033
|
+
decide: decide5,
|
|
5279
4034
|
read_child_state,
|
|
5280
|
-
read_pr_state
|
|
5281
|
-
to_pr_state
|
|
4035
|
+
read_pr_state,
|
|
4036
|
+
to_pr_state
|
|
5282
4037
|
};
|
|
5283
4038
|
|
|
5284
4039
|
// scripts/run/run-progress-read.ts
|
|
5285
|
-
var {
|
|
4040
|
+
var { mark: mark2, parse_stamp: parse_stamp2, read_last_report: read_last_report2 } = run_progress_clock;
|
|
5286
4041
|
var FIRST_LOAD_AVERAGE = 0;
|
|
5287
4042
|
var IDLE_READ = { kind: "idle" };
|
|
5288
4043
|
var UNREADABLE_READ = { kind: "unreadable" };
|
|
@@ -5359,24 +4114,46 @@ async function read_observations(request) {
|
|
|
5359
4114
|
return { kind: "observed", observations };
|
|
5360
4115
|
}
|
|
5361
4116
|
var run_progress_read = {
|
|
5362
|
-
PROGRESS_PREFIX,
|
|
5363
4117
|
has_run_started,
|
|
5364
4118
|
live_target,
|
|
5365
4119
|
log_target,
|
|
5366
|
-
mark,
|
|
5367
|
-
parse_stamp,
|
|
4120
|
+
mark: mark2,
|
|
4121
|
+
parse_stamp: parse_stamp2,
|
|
5368
4122
|
read_children,
|
|
5369
|
-
read_last_report,
|
|
4123
|
+
read_last_report: read_last_report2,
|
|
5370
4124
|
read_lanes,
|
|
5371
4125
|
read_observations,
|
|
5372
4126
|
read_record_age,
|
|
5373
4127
|
stamp_target
|
|
5374
4128
|
};
|
|
5375
4129
|
|
|
4130
|
+
// scripts/run/run-watcher-guard.ts
|
|
4131
|
+
var WATCHER_TICK_MS = 3e4;
|
|
4132
|
+
var TICK_MULTIPLIER = 3;
|
|
4133
|
+
var MS_PER_SECOND3 = 1e3;
|
|
4134
|
+
var STALE_THRESHOLD_MS = WATCHER_TICK_MS * TICK_MULTIPLIER;
|
|
4135
|
+
var STALE_THRESHOLD_S = STALE_THRESHOLD_MS / MS_PER_SECOND3;
|
|
4136
|
+
var STALE_NOTE = `Lane children are in-flight but the progress watcher has not pinged in the last ${String(STALE_THRESHOLD_S)}s \u2014 run \`pnpm josh run:progress --wait\` in the background before proceeding. It reports to the run event stream on its own and exits only when newly runnable work arrives, so start it once and relay nothing`;
|
|
4137
|
+
var OK_RESULT = { kind: "ok" };
|
|
4138
|
+
async function owes_watcher() {
|
|
4139
|
+
if (!await run_headless.is_backlog_parent()) return false;
|
|
4140
|
+
return await lane_registry.has_lanes_in_flight();
|
|
4141
|
+
}
|
|
4142
|
+
async function check3(life_target) {
|
|
4143
|
+
if (!await owes_watcher()) return OK_RESULT;
|
|
4144
|
+
if (run_progress_clock.is_life_fresh(life_target, STALE_THRESHOLD_MS)) return OK_RESULT;
|
|
4145
|
+
return { kind: "stale", note: STALE_NOTE };
|
|
4146
|
+
}
|
|
4147
|
+
var run_watcher_guard = {
|
|
4148
|
+
STALE_NOTE,
|
|
4149
|
+
STALE_THRESHOLD_MS,
|
|
4150
|
+
check: check3
|
|
4151
|
+
};
|
|
4152
|
+
|
|
5376
4153
|
// scripts/run/run-watcher-hook.ts
|
|
5377
4154
|
var SWITCH_ENV_KEY2 = "JOSH_WATCHER_GUARD";
|
|
5378
4155
|
var STAMP_PREFIX2 = "josh-watcher-guard-";
|
|
5379
|
-
var payload_schema =
|
|
4156
|
+
var payload_schema = z12.object({ transcript_path: z12.string().min(1) });
|
|
5380
4157
|
function transcript_of(raw_payload) {
|
|
5381
4158
|
try {
|
|
5382
4159
|
const parsed = payload_schema.safeParse(JSON.parse(raw_payload));
|
|
@@ -5405,7 +4182,7 @@ async function watcher_hook_reason(raw_payload, now_ms = Date.now()) {
|
|
|
5405
4182
|
if (transcript === void 0) return void 0;
|
|
5406
4183
|
return await stale_reason(transcript, now_ms);
|
|
5407
4184
|
}
|
|
5408
|
-
var run_watcher_hook = {
|
|
4185
|
+
var run_watcher_hook = { transcript_of, watcher_hook_reason };
|
|
5409
4186
|
|
|
5410
4187
|
// scripts/run/run-parent-cut-hook.ts
|
|
5411
4188
|
var SWITCH_ENV_KEY3 = "JOSH_PARENT_CUT_GUARD";
|
|
@@ -5437,7 +4214,8 @@ async function refusal_for(transcript, now_ms, state) {
|
|
|
5437
4214
|
function is_unit_call(raw_payload) {
|
|
5438
4215
|
try {
|
|
5439
4216
|
return (hook_decision.parse_hook_payload(raw_payload)?.agent_id ?? "") !== "";
|
|
5440
|
-
} catch {
|
|
4217
|
+
} catch (error) {
|
|
4218
|
+
error_text.trace_swallowed("run_parent_cut_hook.is_unit_call", error);
|
|
5441
4219
|
return false;
|
|
5442
4220
|
}
|
|
5443
4221
|
}
|
|
@@ -5453,11 +4231,48 @@ async function parent_cut_reason(raw_payload, now_ms = Date.now(), state = curre
|
|
|
5453
4231
|
var run_parent_cut_hook = {
|
|
5454
4232
|
PARENT_CUT_REASON,
|
|
5455
4233
|
QUIET_WINDOW_MS,
|
|
5456
|
-
STAMP_PREFIX: STAMP_PREFIX3,
|
|
5457
4234
|
SWITCH_ENV_KEY: SWITCH_ENV_KEY3,
|
|
5458
4235
|
parent_cut_reason
|
|
5459
4236
|
};
|
|
5460
4237
|
|
|
4238
|
+
// scripts/hooks/step-zero-notice.ts
|
|
4239
|
+
import path9 from "node:path";
|
|
4240
|
+
var STAMP_PREFIX4 = "josh-step-zero-notice-";
|
|
4241
|
+
var EDIT_TOOLS3 = /* @__PURE__ */ new Set(["Edit", "Write"]);
|
|
4242
|
+
var PARENT_PREFIX = "..";
|
|
4243
|
+
var NOTICE = "Before writing any implementation code: present the two-layer work summary (Now / Change / Check, then every change with its test) once per Issue. Full rule: Code Change Rules Step 0 in CLAUDE.md.";
|
|
4244
|
+
var STAMP = hook_decision.create_refusal_stamp(STAMP_PREFIX4);
|
|
4245
|
+
function edited_paths(tool_input) {
|
|
4246
|
+
if (!json_value.is_record(tool_input)) return [];
|
|
4247
|
+
const { file_path, file_paths } = tool_input;
|
|
4248
|
+
const listed = Array.isArray(file_paths) ? file_paths : [];
|
|
4249
|
+
return [file_path, ...listed].filter(
|
|
4250
|
+
(entry) => typeof entry === "string" && entry.length > 0
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
function is_runtime_file(file_path, root) {
|
|
4254
|
+
const relative = path9.relative(root, path9.resolve(root, file_path));
|
|
4255
|
+
if (relative.startsWith(PARENT_PREFIX) || path9.isAbsolute(relative)) return false;
|
|
4256
|
+
return !test_declared_logic.is_exempt(relative.split(path9.sep).join("/"));
|
|
4257
|
+
}
|
|
4258
|
+
function is_candidate(raw_payload, root) {
|
|
4259
|
+
const payload = hook_decision.parse_hook_payload(raw_payload);
|
|
4260
|
+
if (payload === void 0 || !EDIT_TOOLS3.has(payload.tool_name)) return void 0;
|
|
4261
|
+
const has_runtime_file = edited_paths(payload.tool_input).some(
|
|
4262
|
+
(file_path) => is_runtime_file(file_path, root)
|
|
4263
|
+
);
|
|
4264
|
+
return has_runtime_file ? payload.transcript_path : void 0;
|
|
4265
|
+
}
|
|
4266
|
+
function notice(raw_payload, root = process.cwd()) {
|
|
4267
|
+
const transcript = is_candidate(raw_payload, root);
|
|
4268
|
+
if (transcript === void 0) return void 0;
|
|
4269
|
+
const target = STAMP.path(transcript);
|
|
4270
|
+
if (STAMP.last_ms(target) !== hook_decision.NEVER_MS) return void 0;
|
|
4271
|
+
if (!STAMP.record(target, Date.now())) return void 0;
|
|
4272
|
+
return NOTICE;
|
|
4273
|
+
}
|
|
4274
|
+
var step_zero_notice = { NOTICE, notice, stamp_path: STAMP.path };
|
|
4275
|
+
|
|
5461
4276
|
// scripts/hooks/pretool-guard.ts
|
|
5462
4277
|
function combine_outcomes(batch, extra_reason) {
|
|
5463
4278
|
if (batch.reason !== void 0) return batch;
|
|
@@ -5469,11 +4284,16 @@ function combine_outcomes(batch, extra_reason) {
|
|
|
5469
4284
|
function is_clear(outcome) {
|
|
5470
4285
|
return outcome.reason === void 0 && outcome.notice === void 0 && outcome.fault === void 0;
|
|
5471
4286
|
}
|
|
5472
|
-
function with_duplicate_notice(combined,
|
|
5473
|
-
if (
|
|
5474
|
-
return { reason: void 0, notice, fault: void 0 };
|
|
4287
|
+
function with_duplicate_notice(combined, notice2) {
|
|
4288
|
+
if (notice2 === void 0 || !is_clear(combined)) return combined;
|
|
4289
|
+
return { reason: void 0, notice: notice2, fault: void 0 };
|
|
5475
4290
|
}
|
|
5476
|
-
function
|
|
4291
|
+
function with_step_zero_notice(outcome, raw_payload) {
|
|
4292
|
+
if (!is_clear(outcome)) return outcome;
|
|
4293
|
+
const notice2 = step_zero_notice.notice(raw_payload);
|
|
4294
|
+
return notice2 === void 0 ? outcome : { reason: void 0, notice: notice2, fault: void 0 };
|
|
4295
|
+
}
|
|
4296
|
+
function guard_outcome(raw_payload) {
|
|
5477
4297
|
const batch = batch_outcome(raw_payload);
|
|
5478
4298
|
const investigation = investigation_refusal(raw_payload);
|
|
5479
4299
|
const duplicate = duplicate_read_outcome(raw_payload);
|
|
@@ -5482,17 +4302,21 @@ function pretool_outcome(raw_payload) {
|
|
|
5482
4302
|
const combined = combine_outcomes(batch, investigation ?? duplicate_refusal ?? rule);
|
|
5483
4303
|
return with_duplicate_notice(combined, duplicate.notice);
|
|
5484
4304
|
}
|
|
4305
|
+
function pretool_outcome(raw_payload) {
|
|
4306
|
+
return with_step_zero_notice(guard_outcome(raw_payload), raw_payload);
|
|
4307
|
+
}
|
|
5485
4308
|
async function async_reason(raw_payload) {
|
|
5486
4309
|
const watcher = await run_watcher_hook.watcher_hook_reason(raw_payload);
|
|
5487
4310
|
return watcher ?? await run_parent_cut_hook.parent_cut_reason(raw_payload);
|
|
5488
4311
|
}
|
|
5489
4312
|
async function pretool_outcome_async(raw_payload) {
|
|
5490
|
-
const base =
|
|
4313
|
+
const base = guard_outcome(raw_payload);
|
|
5491
4314
|
if (!is_clear(base)) return base;
|
|
5492
4315
|
const reason = await async_reason(raw_payload);
|
|
5493
|
-
|
|
4316
|
+
if (reason !== void 0) return { reason, notice: void 0, fault: void 0 };
|
|
4317
|
+
return with_step_zero_notice(base, raw_payload);
|
|
5494
4318
|
}
|
|
5495
|
-
var pretool_guard = { combine_outcomes, pretool_outcome
|
|
4319
|
+
var pretool_guard = { combine_outcomes, pretool_outcome };
|
|
5496
4320
|
|
|
5497
4321
|
export {
|
|
5498
4322
|
pretool_outcome_async,
|