@joshuafolkken/kit 1.1784.0 → 1.2114.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/agents/reviewer-verification.md +19 -0
- package/.claude/agents/reviewer.md +19 -0
- package/.claude/settings.json +5 -6
- package/.claude/skills/dependency-update/SKILL.md +9 -10
- package/.claude/skills/workflow-commands/SKILL.md +6 -6
- package/.claude/skills/workflow-commands/background-commands.md +8 -5
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -1
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-park.md +5 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +2 -3
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +1 -1
- package/.claude/skills/workflow-commands/backlogrun-steps.md +3 -2
- package/.claude/skills/workflow-commands/backlogrun.md +1 -1
- package/.claude/skills/workflow-commands/chain-rule.md +41 -32
- package/.claude/skills/workflow-commands/delegation.md +6 -6
- package/.claude/skills/workflow-commands/entry-sequence.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +7 -5
- package/.claude/skills/workflow-commands/fullrun-steps.md +6 -6
- package/.claude/skills/workflow-commands/fullrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-comments.md +9 -4
- package/.claude/skills/workflow-commands/issue-fold-existing.md +2 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -2
- package/.claude/skills/workflow-commands/observation-filing.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +6 -4
- package/.claude/skills/workflow-commands/pre-gate-cut.md +9 -7
- package/.claude/skills/workflow-commands/prerequisite.md +3 -2
- package/.claude/skills/workflow-commands/retrospective.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +7 -5
- package/.codex/hooks.json +5 -5
- package/.github/actions/setup-pnpm/action.yml +9 -16
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/auto-tag.yml +45 -0
- package/.github/workflows/ci.yml +155 -15
- package/.github/workflows/dependabot-auto-merge.yml +63 -4
- package/.github/workflows/production.yml +8 -0
- package/.github/workflows/publish.yml +59 -45
- package/.vscode/settings.json +19 -0
- package/CLAUDE.md +5 -12
- package/SECURITY.md +2 -2
- package/dist/CLAUDE.md +5 -12
- package/dist/commands/document-read-cli.js +2 -1
- package/dist/commands/document-section-main.js +2 -1
- package/dist/commands/read-set-cli.js +194 -134
- package/dist/commands/run-prep-main.js +9694 -10173
- package/dist/commands/run-step-cli.js +22138 -18285
- package/dist/hooks/audit-provision.js +23 -5
- package/dist/hooks/chunk-3GKE653K.js +6204 -0
- package/dist/hooks/chunk-45CJJOO7.js +1243 -0
- package/dist/hooks/{chunk-CWMBGKYW.js → chunk-5PM2JRGV.js} +2153 -856
- package/dist/hooks/{chunk-WR2KQAYZ.js → chunk-7SIUNQXK.js} +1207 -1470
- package/dist/hooks/{chunk-O2YBIDSI.js → chunk-CQD4LJE6.js} +1682 -1130
- package/dist/hooks/{chunk-VWWCFGWK.js → chunk-OPSRGWFU.js} +1 -1
- package/dist/hooks/{chunk-XGBNSWEI.js → chunk-UJ3FPMMM.js} +132 -49
- package/dist/hooks/chunk-VVNYJWEI.js +30 -0
- package/dist/hooks/chunk-Z6DQJKH3.js +2806 -0
- package/dist/hooks/codex-hook-adapter.js +8 -8
- package/dist/hooks/format-edited.js +4 -5
- package/dist/hooks/inputs.json +415 -389
- package/dist/hooks/pretool-guard.js +7 -6
- package/dist/hooks/session-lang.js +101 -258
- package/dist/hooks/stop-guard.js +1744 -2576
- package/dist/josh.js +7133 -6368
- package/dist/version/index.js +161 -1
- package/eslint/rules/comment-why-only.js +108 -0
- package/package.json +15 -8
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +14 -11
- package/prompts/collaboration-workflow/issue-citation.md +0 -4
- package/prompts/collaboration-workflow/issue-template.md +40 -21
- package/prompts/collaboration-workflow/lane-instructions.md +3 -1
- package/prompts/collaboration-workflow/measurement.md +8 -1
- package/prompts/collaboration-workflow/operating-rules.md +11 -2
- package/prompts/collaboration-workflow/output-bounds.md +6 -2
- package/prompts/collaboration-workflow/plan-comment.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow/residency.md +3 -2
- package/prompts/collaboration-workflow/rule-delivery.md +13 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +3 -1
- package/prompts/review-rubric.md +8 -0
- package/prompts/review.md +31 -22
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-argv.ts +14 -16
- package/scripts/agent/agent-provider.ts +138 -0
- package/scripts/agent/agent-role-profile.ts +108 -156
- package/scripts/agent/agent-safe-text.ts +20 -5
- package/scripts/agent/api-usage-limit.ts +65 -0
- package/scripts/agent/claude-agent-argv.ts +19 -9
- package/scripts/agent/codex-agent-argv.ts +118 -22
- package/scripts/agent/usage-limit-reset.ts +179 -0
- package/scripts/auth/claude-token-output.ts +2 -2
- package/scripts/auto-ok/auto-ok-author.ts +53 -0
- package/scripts/auto-ok/auto-ok-cli.ts +20 -9
- package/scripts/backlog/backlog-defect-priority.ts +1 -1
- package/scripts/backlog/backlog-drive-cli.ts +6 -1
- package/scripts/backlog/backlog-drive-hold.ts +3 -3
- package/scripts/backlog/backlog-drive-idle.ts +10 -0
- package/scripts/backlog/backlog-drive-launch-pass.ts +90 -0
- package/scripts/backlog/backlog-drive-launch.ts +8 -2
- package/scripts/backlog/backlog-drive-named-offer.ts +73 -30
- package/scripts/backlog/backlog-drive-named.ts +28 -7
- package/scripts/backlog/backlog-drive-paused.ts +5 -4
- package/scripts/backlog/backlog-drive-restore.ts +11 -3
- package/scripts/backlog/backlog-drive.ts +21 -60
- package/scripts/backlog/backlog-gate-scope.ts +61 -0
- package/scripts/backlog/backlog-measure-close.ts +112 -0
- package/scripts/backlog/backlog-next.ts +39 -49
- package/scripts/backlog/backlog-occupied.ts +2 -2
- package/scripts/backlog/backlog-overlap.ts +42 -8
- package/scripts/backlog/backlog-plan-cli.ts +4 -1
- package/scripts/backlog/backlog-plan-comment.ts +81 -0
- package/scripts/backlog/backlog-plan-read.ts +19 -6
- package/scripts/backlog/backlog-pool.ts +50 -12
- package/scripts/backlog/backlog-rank.ts +60 -13
- package/scripts/backlog/backlog-stalled-detect.ts +2 -1
- package/scripts/backlog/backlog-waves.ts +17 -2
- package/scripts/backlog/backlogrun-launch.ts +7 -4
- package/scripts/cost-runtime/context-cut-payback.ts +27 -25
- package/scripts/cost-runtime/cost-cli.ts +37 -16
- package/scripts/cost-runtime/cost-pricing.ts +9 -0
- package/scripts/cost-runtime/cost-verdict.ts +4 -1
- package/scripts/delegation/delegation-policy.ts +2 -2
- package/scripts/delegation/duplicate-read-guard.ts +1 -0
- package/scripts/delegation/investigation-guard.ts +14 -2
- package/scripts/delegation/investigation-reads.ts +45 -23
- package/scripts/dependabot/dependabot-cooldown.ts +3 -2
- package/scripts/discovery/repo-party.ts +80 -20
- package/scripts/discovery/session-owner-pin.ts +51 -0
- package/scripts/document/capped-print.ts +1 -1
- package/scripts/document/command-reference-line-budget.ts +42 -0
- package/scripts/document/document-byte-budget.ts +17 -7
- package/scripts/document/document-checks.ts +20 -0
- package/scripts/document/document-ratchet-check.ts +101 -0
- package/scripts/document/document-section.ts +1 -0
- package/scripts/document/entry-read-budget.ts +7 -3
- package/scripts/document/issue-citation-budget.ts +94 -0
- package/scripts/epic/epic-cli-argv.ts +2 -2
- package/scripts/epic/epic-fetch.ts +4 -2
- package/scripts/epic/epic-graph.ts +7 -2
- package/scripts/epic/epic-issue.ts +3 -1
- package/scripts/epic/epic-rank.ts +5 -0
- package/scripts/exports/unused-members-check.ts +62 -0
- package/scripts/exports/unused-members-cli.ts +6 -51
- package/scripts/followup/followup-ci-path.ts +67 -0
- package/scripts/followup/followup-coderabbit-retrigger.ts +261 -0
- package/scripts/followup/followup-coderabbit-wait.ts +13 -10
- package/scripts/followup/followup-failed-tests.ts +160 -0
- package/scripts/followup/followup-job-needs.ts +100 -0
- package/scripts/followup/followup-job-rerun.ts +303 -0
- package/scripts/followup/followup-main-move.ts +113 -0
- package/scripts/followup/git-followup-finish.ts +43 -11
- package/scripts/followup/git-followup-pending.ts +3 -3
- package/scripts/followup/git-followup-stages.ts +41 -11
- package/scripts/followup/git-pr-followup-checks.ts +55 -16
- package/scripts/followup/git-pr-followup-wrapup.ts +24 -5
- package/scripts/followup/git-pr-followup.ts +62 -27
- package/scripts/fresh-machine/fresh-machine-script.ts +5 -9
- package/scripts/fresh-machine/specs/kit-test-backlogrun-python.json +2 -2
- package/scripts/fresh-machine/specs/kit-test-backlogrun.json +2 -2
- package/scripts/gate/busy-average.ts +34 -2
- package/scripts/gate/check-related-pinned.ts +43 -0
- package/scripts/gate/check-related-reuse.ts +64 -0
- package/scripts/gate/check-related-section.ts +57 -0
- package/scripts/gate/check-related-unplanned.ts +34 -0
- package/scripts/gate/check-related.ts +265 -0
- package/scripts/gate/command-memory.ts +1 -1
- package/scripts/gate/core-admission.ts +24 -6
- package/scripts/gate/core-budget-timing.ts +20 -6
- package/scripts/gate/core-budget.ts +40 -11
- package/scripts/gate/core-wait-record.ts +3 -2
- package/scripts/gate/core-wait.ts +25 -2
- package/scripts/gate/core-weights.ts +15 -7
- package/scripts/gate/gate-labels.ts +46 -0
- package/scripts/gate/gate-ledger.ts +14 -6
- package/scripts/gate/gate-plan.ts +103 -58
- package/scripts/gate/gate-report.ts +9 -7
- package/scripts/gate/gate-run-steps.ts +26 -3
- package/scripts/gate/gate-skip.ts +45 -6
- package/scripts/gate/gate-static-group.ts +60 -0
- package/scripts/gate/gate-steps.ts +2 -1
- package/scripts/gate/gate-tree.ts +4 -2
- package/scripts/gate/hook-gate-reuse.ts +19 -2
- package/scripts/gate/metrics-totals.ts +59 -0
- package/scripts/gate/scoped-green.ts +68 -15
- package/scripts/gate/since-green.ts +14 -6
- package/scripts/gate/structural-checks.ts +96 -0
- package/scripts/gate/verification-gate.ts +51 -16
- package/scripts/gate/verification-inputs.ts +21 -6
- package/scripts/gate/verification-tail.ts +52 -0
- package/scripts/gh/gh-author-association.ts +23 -2
- package/scripts/gh/git-ai-review-scan.ts +1 -1
- package/scripts/gh/git-gh-api-path.ts +12 -3
- package/scripts/gh/git-gh-comments-unreadable.ts +36 -0
- package/scripts/gh/git-gh-exec.ts +32 -2
- package/scripts/gh/git-gh-issue-read.ts +4 -2
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +8 -2
- package/scripts/gh/git-gh-issue.ts +12 -5
- package/scripts/gh/git-gh-pr-auto-merge.ts +13 -6
- package/scripts/gh/git-gh-pr-graphql-state.ts +56 -0
- package/scripts/gh/git-gh-pr-read.ts +74 -13
- package/scripts/gh/git-gh-pr-rest.ts +3 -1
- package/scripts/gh/git-gh-pr-snapshot.ts +22 -7
- package/scripts/gh/git-gh-pr-too-large.ts +20 -0
- package/scripts/gh/git-gh-pr.ts +21 -7
- package/scripts/gh/git-gh-retry.ts +141 -0
- package/scripts/gh/git-pr-ai-review.ts +15 -6
- package/scripts/gh/git-pr-checks-eval.ts +3 -0
- package/scripts/gh/git-pr-checks.ts +5 -5
- package/scripts/git/base-tree.ts +30 -0
- package/scripts/git/change-base.ts +4 -10
- package/scripts/git/changed-paths-sync.ts +63 -0
- package/scripts/git/changed-paths.ts +16 -1
- package/scripts/git/git-common-directory.ts +11 -1
- package/scripts/git/git-diff-reads.ts +66 -20
- package/scripts/git/git-error.ts +9 -2
- package/scripts/git/git-schemas.ts +13 -0
- package/scripts/git/git-worktree.ts +13 -0
- package/scripts/git/gone-branch.ts +4 -1
- package/scripts/git/main-merge-budget-conflict.ts +89 -0
- package/scripts/git/main-merge-guard.ts +55 -18
- package/scripts/git/main-merge-regenerate.ts +59 -25
- package/scripts/git/main-merge.ts +53 -5
- package/scripts/git/no-base-checkout.ts +127 -0
- package/scripts/git/stash/git-stash.ts +11 -2
- package/scripts/git/stash/stash-push-cli.ts +10 -0
- package/scripts/hooks/batch-guard.ts +40 -21
- package/scripts/hooks/edited-cspell.ts +37 -11
- package/scripts/hooks/format-edited-file.ts +22 -2
- package/scripts/hooks/lane-plan-guard.ts +50 -0
- package/scripts/hooks/pre-commit-lint-switch.ts +9 -0
- package/scripts/hooks/pre-commit-lint.ts +14 -1
- package/scripts/hooks/pre-push-related-switch.ts +10 -0
- package/scripts/hooks/pre-push-unit.ts +76 -10
- package/scripts/hooks/pretool-guard.ts +2 -0
- package/scripts/hooks/reserved-run.ts +14 -1
- package/scripts/hooks/run-hook.sh +23 -8
- package/scripts/hooks/step-zero-notice.ts +6 -1
- package/scripts/hooks/stop-actions.ts +3 -15
- package/scripts/hooks/stop-guard.ts +7 -6
- package/scripts/init/distributed-paths.ts +1 -0
- package/scripts/init/hook-command-rewrite.ts +5 -1
- package/scripts/init/hook-launch.ts +4 -2
- package/scripts/issue/aggregation-split.ts +52 -0
- package/scripts/issue/defect-origin-line.ts +19 -1
- package/scripts/issue/defect-rate-cli.ts +5 -102
- package/scripts/issue/defect-report.ts +4 -4
- package/scripts/issue/effect-audit-cli.ts +178 -0
- package/scripts/issue/effect-audit.ts +185 -0
- package/scripts/issue/expected-effect-lint.ts +64 -9
- package/scripts/issue/git-next-issues.ts +5 -0
- package/scripts/issue/issue-body-or-comment.ts +38 -4
- package/scripts/issue/issue-cite-cli.ts +1 -3
- package/scripts/issue/issue-classification.ts +1 -0
- package/scripts/issue/issue-closing-pr.ts +8 -1
- package/scripts/issue/issue-evidence-exit.ts +4 -2
- package/scripts/issue/issue-file-cap.ts +58 -0
- package/scripts/issue/issue-file-cli.ts +111 -38
- package/scripts/issue/issue-file-deferral.ts +52 -0
- package/scripts/issue/issue-file-fold.ts +29 -10
- package/scripts/issue/issue-file-interrupt.ts +85 -0
- package/scripts/issue/issue-file-lane-hint.ts +30 -0
- package/scripts/issue/issue-file.ts +43 -6
- package/scripts/issue/issue-labels.ts +19 -2
- package/scripts/issue/issue-lint-cli.ts +27 -2
- package/scripts/issue/issue-measured-cli.ts +13 -35
- package/scripts/issue/issue-measured-close.ts +58 -0
- package/scripts/issue/issue-not-before-label.ts +2 -2
- package/scripts/issue/issue-not-before.ts +4 -1
- package/scripts/issue/issue-priority-cli.ts +60 -0
- package/scripts/issue/issue-priority-raise.ts +55 -0
- package/scripts/issue/issue-priority.ts +174 -43
- package/scripts/issue/issue-rank.ts +21 -4
- package/scripts/issue/issue-read-cli.ts +32 -6
- package/scripts/issue/issue-read.ts +37 -12
- package/scripts/issue/issue-sample-gate.ts +95 -74
- package/scripts/issue/issue-sample-prime.ts +127 -0
- package/scripts/issue/issue-sample-required.ts +57 -0
- package/scripts/issue/issue-sample-source.ts +12 -11
- package/scripts/issue/issue-search.ts +124 -0
- package/scripts/issue/issue-state.ts +13 -1
- package/scripts/issue/issue-template-section.ts +43 -0
- package/scripts/issue/issue-watch-diff.ts +31 -1
- package/scripts/issue/issue-watch-hook.ts +11 -2
- package/scripts/issue/issue-watch.ts +55 -12
- package/scripts/issue/markdown-section.ts +1 -0
- package/scripts/issue/measure-declaration.ts +121 -0
- package/scripts/issue/measure-rerun-cli.ts +7 -70
- package/scripts/issue/plan-consumers-section.ts +22 -1
- package/scripts/issue/reproduction-measure.ts +3 -1
- package/scripts/issue/size-estimate.ts +90 -0
- package/scripts/issue/trusted-measure.ts +84 -0
- package/scripts/josh/command-usage.ts +3 -2
- package/scripts/josh/durable-state-migrate.ts +105 -0
- package/scripts/josh/durable-state.ts +112 -0
- package/scripts/josh/josh-aliases.ts +95 -0
- package/scripts/josh/josh-command-map.ts +1 -82
- package/scripts/josh/josh-commands-agent-hooks.ts +6 -6
- package/scripts/josh/josh-commands-ai.ts +5 -5
- package/scripts/josh/josh-commands-development.ts +59 -58
- package/scripts/josh/josh-commands-issue-write.ts +23 -4
- package/scripts/josh/josh-commands-issue.ts +32 -0
- package/scripts/josh/josh-commands-lane.ts +87 -4
- package/scripts/josh/josh-commands-lint.ts +16 -3
- package/scripts/josh/josh-commands-maintenance.ts +15 -0
- package/scripts/josh/josh-commands-review.ts +41 -6
- package/scripts/josh/josh-commands-ship.ts +56 -8
- package/scripts/josh/josh-commands-test-check.ts +80 -0
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/josh-environment-file.ts +43 -3
- package/scripts/josh/josh-in-process.ts +2 -2
- package/scripts/josh/josh-logic.ts +5 -3
- package/scripts/josh/josh-run.ts +19 -1
- package/scripts/josh/run-record-path.ts +15 -0
- package/scripts/josh/stamp-file.ts +11 -2
- package/scripts/lane/lane-adopt-cli.ts +98 -0
- package/scripts/lane/lane-adopt.ts +208 -0
- package/scripts/lane/lane-await.ts +5 -8
- package/scripts/lane/lane-baseline.ts +123 -0
- package/scripts/lane/lane-breakdown-cli.ts +64 -0
- package/scripts/lane/lane-breakdown-implement.ts +170 -0
- package/scripts/lane/lane-breakdown-laps.ts +107 -0
- package/scripts/lane/lane-breakdown-pinned.ts +76 -0
- package/scripts/lane/lane-breakdown-text.ts +145 -0
- package/scripts/lane/lane-breakdown.ts +239 -0
- package/scripts/lane/lane-check-stats.ts +29 -8
- package/scripts/lane/lane-child-invocation.ts +9 -2
- package/scripts/lane/lane-child-mark-keys.ts +12 -0
- package/scripts/lane/lane-child-marker.ts +20 -1
- package/scripts/lane/lane-ci-path-stats.ts +73 -0
- package/scripts/lane/lane-claim-stats.ts +84 -0
- package/scripts/lane/lane-close.ts +8 -3
- package/scripts/lane/lane-dispatch-log.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +2 -2
- package/scripts/lane/lane-gap-stats.ts +7 -6
- package/scripts/lane/lane-guard-policy.ts +7 -1
- package/scripts/lane/lane-implement-stats.ts +207 -0
- package/scripts/lane/lane-implement-subagents.ts +90 -0
- package/scripts/lane/lane-investigation-switch.ts +16 -0
- package/scripts/lane/lane-ledger-kind.ts +31 -0
- package/scripts/lane/lane-ledger-schema-ship.ts +49 -0
- package/scripts/lane/lane-ledger-schema.ts +119 -36
- package/scripts/lane/lane-ledger-write.ts +257 -0
- package/scripts/lane/lane-ledger.ts +31 -177
- package/scripts/lane/lane-load.ts +1 -1
- package/scripts/lane/lane-measure-cli.ts +176 -59
- package/scripts/lane/lane-net-read.ts +44 -0
- package/scripts/lane/lane-net-time.ts +212 -0
- package/scripts/lane/lane-open.ts +16 -7
- package/scripts/lane/lane-output.ts +2 -4
- package/scripts/lane/lane-overrun-prompt.ts +147 -0
- package/scripts/lane/lane-overrun.ts +274 -0
- package/scripts/lane/lane-paths.ts +24 -1
- package/scripts/lane/lane-pause-cli.ts +4 -1
- package/scripts/lane/lane-paused-spans.ts +97 -0
- package/scripts/lane/lane-plan-check.ts +207 -0
- package/scripts/lane/lane-plan-cli.ts +41 -0
- package/scripts/lane/lane-plan.ts +144 -0
- package/scripts/lane/lane-registry.ts +14 -1
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-round-one-stats.ts +51 -0
- package/scripts/lane/lane-sampler.ts +23 -4
- package/scripts/lane/lane-session-action.ts +93 -18
- package/scripts/lane/lane-session-broker.ts +49 -21
- package/scripts/lane/lane-session-reread-main.ts +14 -0
- package/scripts/lane/lane-session-reread.ts +51 -0
- package/scripts/lane/lane-ship-lap-stats.ts +74 -0
- package/scripts/lane/lane-sleep.ts +58 -0
- package/scripts/lane/lane-spent.ts +1 -1
- package/scripts/lane/lane-split-stats.ts +28 -0
- package/scripts/lane/lane-stage-stats.ts +13 -1
- package/scripts/lane/lane-state.ts +41 -9
- package/scripts/lane/lane-stats-tables.ts +58 -0
- package/scripts/lane/lane-stats.ts +21 -5
- package/scripts/lane/lane-stop-stats.ts +51 -19
- package/scripts/lane/lane-tail-stats.ts +32 -0
- package/scripts/lane/lane-transcript-calls.ts +67 -10
- package/scripts/lane/lane-transcript-stats.ts +44 -12
- package/scripts/lane/lane-value-cell.ts +72 -0
- package/scripts/lane/lane-value.ts +118 -0
- package/scripts/lane/lane-withdraw-closed.ts +124 -0
- package/scripts/lane/lane-withdraw.ts +108 -31
- package/scripts/lane/lane-yield.ts +85 -0
- package/scripts/lane/openai-lane-ship.ts +12 -11
- package/scripts/lane/openai-lane-supervisor-decision.ts +6 -8
- package/scripts/lane/openai-lane-supervisor-owner.ts +5 -8
- package/scripts/lane/openai-lane-supervisor.ts +5 -4
- package/scripts/lib/poll.ts +38 -9
- package/scripts/lib/settled-together.ts +10 -0
- package/scripts/lib/shell-quote.ts +21 -0
- package/scripts/lib/token-shape.ts +111 -0
- package/scripts/lint/eslint-daemon-sources.ts +79 -0
- package/scripts/lint/eslint-daemon.ts +45 -6
- package/scripts/lint/eslint-warm-cli.ts +7 -0
- package/scripts/lint/eslint-warm.ts +66 -0
- package/scripts/lint/import-graph.ts +141 -11
- package/scripts/lint/lint-gate-scope.ts +2 -2
- package/scripts/lint/lint-parallel.ts +54 -6
- package/scripts/lint/lint-related.ts +154 -33
- package/scripts/lint/rename-cli.ts +179 -0
- package/scripts/lint/rename-identifier.ts +192 -0
- package/scripts/lint/type-only-imports.ts +58 -0
- package/scripts/notify/telegram-notify.ts +12 -3
- package/scripts/observations/observation-record.ts +42 -16
- package/scripts/observations/observations-flush.ts +32 -9
- package/scripts/overrides/overrides-check.ts +9 -1
- package/scripts/release/release-approval-prune.ts +7 -1
- package/scripts/repo/required-checks-logic.ts +4 -1
- package/scripts/repo/ruleset-check.ts +4 -3
- package/scripts/report/markdown-shape.ts +28 -0
- package/scripts/report/report-format-reference.ts +15 -1
- package/scripts/report/report-labels.ts +12 -1
- package/scripts/report/report-lint-child-summary.ts +94 -0
- package/scripts/report/report-lint-cli.ts +26 -7
- package/scripts/report/report-lint.ts +2 -20
- package/scripts/review/prose-stale-cli.ts +88 -0
- package/scripts/review/prose-stale-scan.ts +152 -0
- package/scripts/review/review-attest.ts +73 -23
- package/scripts/review/review-brief-cli.ts +68 -19
- package/scripts/review/review-brief-paths.ts +22 -0
- package/scripts/review/review-brief.ts +14 -7
- package/scripts/review/review-comment-carry.ts +47 -0
- package/scripts/review/review-comment-round.ts +85 -22
- package/scripts/review/review-finding-ledger.ts +109 -7
- package/scripts/review/review-finding-text.ts +43 -0
- package/scripts/review/review-interaction.ts +11 -0
- package/scripts/review/review-lane-handover.ts +68 -0
- package/scripts/review/review-lint-cli.ts +57 -0
- package/scripts/review/review-lint-findings.ts +92 -0
- package/scripts/review/review-lint-sections.ts +80 -0
- package/scripts/review/review-lint.ts +221 -0
- package/scripts/review/review-prior-findings.ts +102 -0
- package/scripts/review/review-readers-scan.ts +4 -2
- package/scripts/review/review-record-cli.ts +177 -54
- package/scripts/review/review-record-code-round.ts +55 -0
- package/scripts/review/review-record-comment.ts +60 -0
- package/scripts/review/review-record-interaction.ts +35 -0
- package/scripts/review/review-record-pinned.ts +108 -0
- package/scripts/review/review-record.ts +25 -1
- package/scripts/review/review-reviewed-tree.ts +307 -0
- package/scripts/review/review-round-one-closure.ts +62 -0
- package/scripts/review/review-round-reuse.ts +106 -0
- package/scripts/review/review-round2-cli.ts +91 -29
- package/scripts/review/review-round2-skip-comment.ts +55 -0
- package/scripts/review/review-round2.ts +24 -8
- package/scripts/review/review-stamps.ts +7 -3
- package/scripts/review/review-stop-log.ts +20 -6
- package/scripts/review/review-stops-cli.ts +2 -1
- package/scripts/review/review-submit-cli.ts +101 -0
- package/scripts/rules/agent-model.ts +1 -1
- package/scripts/rules/bash-triggers.ts +3 -3
- package/scripts/rules/concise-reply.ts +88 -0
- package/scripts/rules/credential-body.ts +77 -0
- package/scripts/rules/decision-oracle.ts +1 -1
- package/scripts/rules/delivered-rules.ts +36 -17
- package/scripts/rules/destructive-command.ts +3 -11
- package/scripts/rules/direct-issue-comment.ts +34 -12
- package/scripts/rules/direct-issue-read.ts +191 -0
- package/scripts/rules/direct-pr-create.ts +4 -24
- package/scripts/rules/early-heartbeat.ts +2 -0
- package/scripts/rules/edit-tool.ts +27 -0
- package/scripts/rules/file-edit-rows.ts +17 -0
- package/scripts/rules/file-rewrite.ts +146 -0
- package/scripts/rules/firing-point.ts +3 -2
- package/scripts/rules/gh-api.ts +128 -7
- package/scripts/rules/gh-repo-selector.ts +84 -0
- package/scripts/rules/git-force.ts +2 -2
- package/scripts/rules/guard-remedy.ts +14 -5
- package/scripts/rules/implementation-cut.ts +61 -28
- package/scripts/rules/investigator-foreground.ts +231 -0
- package/scripts/rules/issue-citation.ts +15 -60
- package/scripts/rules/issue-comments.ts +9 -4
- package/scripts/rules/josh-git-bare.ts +1 -1
- package/scripts/rules/lane-background.ts +41 -13
- package/scripts/rules/lane-direct-tool.ts +107 -9
- package/scripts/rules/lane-interactive-ask.ts +4 -1
- package/scripts/rules/lane-park.ts +4 -2
- package/scripts/rules/lane-spilled-read.ts +90 -0
- package/scripts/rules/lane-split-park.ts +1 -1
- package/scripts/rules/lane-switch-main.ts +4 -4
- package/scripts/rules/last-prompt.ts +20 -47
- package/scripts/rules/oracle-consulted.ts +1 -1
- package/scripts/rules/permission-guards.ts +5 -1
- package/scripts/rules/piped-verification-shapes.ts +110 -0
- package/scripts/rules/piped-verification.ts +67 -17
- package/scripts/rules/poll-loop.ts +72 -9
- package/scripts/rules/pr-merge.ts +5 -5
- package/scripts/rules/pre-gate-cut.ts +3 -3
- package/scripts/rules/protected-files.ts +88 -24
- package/scripts/rules/reply-language.ts +3 -17
- package/scripts/rules/reply-prose.ts +34 -0
- package/scripts/rules/repo-admin.ts +132 -0
- package/scripts/rules/reviewer-foreground.ts +88 -0
- package/scripts/rules/round-one-check-related.ts +120 -0
- package/scripts/rules/rule-body-guard.ts +5 -15
- package/scripts/rules/rule-value-cache.ts +53 -5
- package/scripts/rules/rule-value-candidates.ts +84 -0
- package/scripts/rules/rule-value-cli.ts +59 -11
- package/scripts/rules/rule-value.ts +17 -4
- package/scripts/rules/run-tail-rule.ts +1 -1
- package/scripts/rules/sed-in-place.ts +196 -0
- package/scripts/rules/shell-segments.ts +10 -2
- package/scripts/rules/stop-rule-rows.ts +1 -13
- package/scripts/rules/stop-rules.ts +33 -58
- package/scripts/rules/test-declared-commit.ts +1 -1
- package/scripts/rules/third-party-write.ts +110 -23
- package/scripts/rules/timed-rerun.ts +1 -1
- package/scripts/rules/turn-filings.ts +90 -0
- package/scripts/rules/worktree-guard.ts +22 -7
- package/scripts/run/board/run-board-ending-reason.ts +6 -2
- package/scripts/run/board/run-board-fit.ts +24 -1
- package/scripts/run/board/run-board-found.ts +134 -0
- package/scripts/run/board/run-board-header.ts +8 -0
- package/scripts/run/board/run-board-labels.ts +11 -1
- package/scripts/run/board/run-board-lane-time.ts +1 -0
- package/scripts/run/board/run-board-link.ts +15 -2
- package/scripts/run/board/run-board-notes.ts +7 -1
- package/scripts/run/board/run-board-operation.ts +4 -2
- package/scripts/run/board/run-board-phase.ts +2 -5
- package/scripts/run/board/run-board-plan.ts +1 -1
- package/scripts/run/board/run-board-read.ts +46 -1
- package/scripts/run/board/run-board-reason.ts +20 -8
- package/scripts/run/board/run-board-render-checks.ts +3 -2
- package/scripts/run/board/run-board-render-drawn.ts +11 -0
- package/scripts/run/board/run-board-render-found.ts +157 -0
- package/scripts/run/board/run-board-render-lead.ts +9 -0
- package/scripts/run/board/run-board-render-legend.ts +19 -0
- package/scripts/run/board/run-board-render-tail.ts +1 -1
- package/scripts/run/board/run-board-render.ts +82 -18
- package/scripts/run/board/run-board-spin.ts +1 -3
- package/scripts/run/board/run-board-status.ts +2 -0
- package/scripts/run/board/run-board-tick.ts +39 -10
- package/scripts/run/carry/run-carry-ended.ts +3 -2
- package/scripts/run/carry/run-carry.ts +3 -2
- package/scripts/run/cut/run-cut-cli.ts +33 -133
- package/scripts/run/cut/run-cut-digest.ts +152 -0
- package/scripts/run/cut/run-cut-handoff.ts +52 -8
- package/scripts/run/cut/run-cut-report.ts +24 -3
- package/scripts/run/cut/run-cut-resume.ts +129 -0
- package/scripts/run/cut/run-cut-review.ts +105 -0
- package/scripts/run/cut/run-cut.ts +18 -7
- package/scripts/run/detached-launch.ts +1 -1
- package/scripts/run/entry/run-entry-cli.ts +88 -19
- package/scripts/run/entry/run-entry-spill.ts +83 -0
- package/scripts/run/entry/run-entry-stop.ts +5 -1
- package/scripts/run/entry/run-entry-trust.ts +91 -0
- package/scripts/run/event/run-event-baseline.ts +56 -0
- package/scripts/run/event/run-event-filed.ts +9 -3
- package/scripts/run/event/run-event-overrun.ts +197 -0
- package/scripts/run/event/run-event-render.ts +4 -0
- package/scripts/run/event/run-event-stream.ts +25 -3
- package/scripts/run/hold/run-stop-notify.ts +3 -8
- package/scripts/run/merge/run-merge-cap.ts +57 -0
- package/scripts/run/merge/run-merge-cli.ts +66 -23
- package/scripts/run/merge/run-merge-outcome.ts +2 -0
- package/scripts/run/merge/run-merge-ship.ts +13 -3
- package/scripts/run/merge/run-merge-signals.ts +40 -23
- package/scripts/run/merge/run-merge-steps.ts +56 -36
- package/scripts/run/merge/run-merge-usage-limit.ts +53 -0
- package/scripts/run/merge/run-merge-withdrawn.ts +53 -0
- package/scripts/run/merge/run-merge.ts +41 -14
- package/scripts/run/review/run-review-cli.ts +306 -20
- package/scripts/run/review/run-review-early-push.ts +121 -0
- package/scripts/run/review/run-review-join-read.ts +94 -0
- package/scripts/run/review/run-review-pinned-accepted.ts +126 -0
- package/scripts/run/review/run-review-pinned-join.ts +216 -0
- package/scripts/run/review/run-review-pinned-steps.ts +383 -0
- package/scripts/run/review/run-review-pinned-tree.ts +37 -0
- package/scripts/run/review/run-review-pinned.ts +53 -0
- package/scripts/run/review/run-review-steps.ts +70 -34
- package/scripts/run/review/run-review.ts +26 -6
- package/scripts/run/run-parent-cut-hook.ts +3 -4
- package/scripts/run/run-prep.ts +1 -0
- package/scripts/run/run-stage-read.ts +2 -6
- package/scripts/run/run-step-cli.ts +17 -5
- package/scripts/run/run-step-lane-plan.ts +27 -0
- package/scripts/run/run-step-section.ts +26 -7
- package/scripts/run/run-step.ts +5 -2
- package/scripts/run/ship/run-ship-args.ts +19 -0
- package/scripts/run/ship/run-ship-brokered-review.ts +106 -0
- package/scripts/run/ship/run-ship-cli.ts +131 -39
- package/scripts/run/ship/run-ship-conflict-probe.ts +59 -0
- package/scripts/run/ship/run-ship-detach.ts +10 -4
- package/scripts/run/ship/run-ship-handover.ts +106 -0
- package/scripts/run/ship/run-ship-infra-retry.ts +16 -1
- package/scripts/run/ship/run-ship-lap.ts +63 -0
- package/scripts/run/ship/run-ship-next.ts +27 -26
- package/scripts/run/ship/run-ship-overlap.ts +8 -3
- package/scripts/run/ship/run-ship-place.ts +9 -2
- package/scripts/run/ship/run-ship-planned.ts +92 -0
- package/scripts/run/ship/run-ship-pre-detach-pinned.ts +197 -0
- package/scripts/run/ship/run-ship-pre-detach-timed.ts +7 -8
- package/scripts/run/ship/run-ship-pre-detach.ts +77 -23
- package/scripts/run/ship/run-ship-preflight.ts +16 -4
- package/scripts/run/ship/run-ship-probe.ts +23 -9
- package/scripts/run/ship/run-ship-relaunch-cap.ts +43 -7
- package/scripts/run/ship/run-ship-relaunch.ts +51 -0
- package/scripts/run/ship/run-ship-return.ts +103 -29
- package/scripts/run/ship/run-ship-review-reason.ts +24 -0
- package/scripts/run/ship/run-ship-review-steps.ts +230 -59
- package/scripts/run/ship/run-ship-review.ts +171 -51
- package/scripts/run/ship/run-ship-reviewer.ts +138 -42
- package/scripts/run/ship/run-ship-scoped.ts +39 -10
- package/scripts/run/ship/run-ship-stage.ts +49 -13
- package/scripts/run/ship/run-ship-steps.ts +24 -4
- package/scripts/run/ship/run-ship-stop-cause.ts +43 -2
- package/scripts/run/ship/run-ship-stop-context.ts +50 -13
- package/scripts/run/ship/run-ship-sync.ts +171 -30
- package/scripts/run/ship/run-ship-synced-scope.ts +178 -0
- package/scripts/run/ship/run-ship-wait-cli.ts +145 -0
- package/scripts/run/ship/run-ship-waiter.ts +114 -0
- package/scripts/run/ship/run-ship.ts +16 -2
- package/scripts/run/wake/run-wake-caffeinate.ts +48 -0
- package/scripts/run/wake/run-wake-cli.ts +6 -113
- package/scripts/run/wake/run-wake-code.ts +53 -11
- package/scripts/run/wake/run-wake-driver.ts +5 -3
- package/scripts/run/wake/run-wake-finish.ts +137 -0
- package/scripts/run/wake/run-wake-loop.ts +5 -3
- package/scripts/run/wake/run-wake-ports.ts +11 -3
- package/scripts/run/wake/run-wake-session.ts +2 -0
- package/scripts/run/wake/run-wake.ts +3 -2
- package/scripts/safe-chain/ci-installer-pin.ts +49 -8
- package/scripts/safe-chain/project-config.ts +3 -2
- package/scripts/security/secretlint-scan.ts +42 -9
- package/scripts/security/security-audit-provision-main.ts +9 -1
- package/scripts/security/security-audit-provision.ts +1 -1
- package/scripts/security/security-audit.ts +21 -1
- package/scripts/security/sonar-file.ts +10 -5
- package/scripts/split/split-assess.ts +12 -3
- package/scripts/split/split-early-notice.ts +116 -0
- package/scripts/split/split-measure.ts +20 -2
- package/scripts/split/split-resolution.ts +107 -0
- package/scripts/test/direct-importers.ts +185 -0
- package/scripts/test/pure-files-entries.ts +83 -0
- package/scripts/test/related-heavy-tests.ts +81 -0
- package/scripts/test/test-no-base.ts +88 -0
- package/scripts/test/test-pinned-cache-logic.ts +363 -0
- package/scripts/test/test-pinned-cache.ts +337 -0
- package/scripts/test/test-pinned-emit.ts +49 -0
- package/scripts/test/test-pinned-functions.ts +116 -0
- package/scripts/test/test-pinned-ledger.ts +80 -0
- package/scripts/test/test-pinned-logic.ts +208 -20
- package/scripts/test/test-pinned-main.ts +84 -18
- package/scripts/test/test-pinned-pool.ts +73 -0
- package/scripts/test/test-pinned-probe.ts +254 -0
- package/scripts/test/test-pinned-prune-logic.ts +118 -0
- package/scripts/test/test-pinned-reach.ts +194 -0
- package/scripts/test/test-pinned-record.ts +196 -0
- package/scripts/test/test-pinned-rewire.ts +148 -0
- package/scripts/test/test-pinned-spawn.ts +81 -0
- package/scripts/test/test-pinned-stage.ts +70 -0
- package/scripts/test/test-pinned.ts +258 -47
- package/scripts/test/test-reach-cli.ts +61 -0
- package/scripts/test/test-read-map.ts +35 -11
- package/scripts/test/test-read-recording.ts +17 -5
- package/scripts/test/test-read-trace.ts +26 -8
- package/scripts/test/test-red-logic.ts +46 -7
- package/scripts/test/test-red.ts +34 -14
- package/scripts/test/test-related-claim.ts +37 -0
- package/scripts/test/test-related-main.ts +4 -1
- package/scripts/test/test-related-moved.ts +91 -0
- package/scripts/test/test-related-record.ts +43 -23
- package/scripts/test/test-related-scope.ts +99 -34
- package/scripts/test/test-related-synced.ts +135 -0
- package/scripts/test/test-related-touched.ts +84 -0
- package/scripts/test/test-related.ts +167 -83
- package/scripts/test/test-unit-guard.ts +28 -5
- package/scripts/test/unit-durations.ts +41 -0
- package/scripts/test/unit-worker-share.ts +5 -4
- package/scripts/test/vitest-cache-directory.ts +19 -0
- package/scripts/test/vitest-cli-excludes.ts +14 -0
- package/scripts/test/vitest-failure-summary.ts +111 -0
- package/scripts/test/whole-suite-files.ts +12 -1
- package/scripts/time-runtime/time-batch-guard.ts +42 -4
- package/scripts/time-runtime/time-bundle-call.ts +1 -1
- package/scripts/time-runtime/time-bundles.ts +36 -5
- package/scripts/time-runtime/time-markers.ts +2 -2
- package/scripts/time-runtime/time-search-call.ts +38 -0
- package/scripts/time-runtime/time-shell.ts +1 -1
- package/scripts/time-runtime/time-transcript-line.ts +17 -5
- package/scripts/version/development-engines-verdict.ts +124 -0
- package/scripts/version/latest-corepack.ts +2 -0
- package/templates/gitignore +2 -0
- package/templates/workflows/ci.yml +41 -0
- package/dist/hooks/chunk-AZX4FGQ3.js +0 -207
- package/dist/hooks/chunk-PT5R6UJD.js +0 -7131
- package/dist/hooks/chunk-TUIFS5E7.js +0 -72
- package/dist/hooks/chunk-YCDOIR3I.js +0 -3456
- package/scripts/cost-runtime/context-cut-threshold.ts +0 -11
- package/scripts/rules/filing-cap.ts +0 -139
|
@@ -1,64 +1,89 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
1
3
|
import { SUITE_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
2
4
|
import { execa } from 'execa'
|
|
3
5
|
import { git_command } from './git-command'
|
|
4
6
|
import { git_spawn } from './git-spawn'
|
|
7
|
+
import { main_merge_budget_conflict } from './main-merge-budget-conflict'
|
|
5
8
|
|
|
6
|
-
// A merge of the default branch that stops only on
|
|
7
|
-
// resumed session
|
|
8
|
-
//
|
|
9
|
+
// A merge of the default branch that stops only on the files below is finished here rather than by a
|
|
10
|
+
// resumed session, with no conflict marker for anyone to read. A generated file is the output of a
|
|
11
|
+
// command, so running that command on the merged tree writes the one correct resolution. A rewritten
|
|
12
|
+
// file is resolved by a rule that keeps both sides of the merge, and throws on any hunk it cannot.
|
|
9
13
|
//
|
|
10
|
-
// **This table is the single list of what is resolved that way** — a file a person writes
|
|
11
|
-
//
|
|
12
|
-
// path not listed here still stops as `conflict`. A file
|
|
13
|
-
// is exactly what its command writes: `tools/test/pure-files.ts` is not, so
|
|
14
|
-
// rewrite far more than the conflict.
|
|
14
|
+
// **This table is the single list of what is resolved that way** — a file a person writes belongs in
|
|
15
|
+
// it only with a rewrite rule, because regenerating would discard that person's side of the merge. A
|
|
16
|
+
// conflict on any path not listed here still stops as `conflict`. A file is regenerated only once its
|
|
17
|
+
// committed content is exactly what its command writes: `tools/test/pure-files.ts` is not, so
|
|
18
|
+
// regenerating it would rewrite far more than the conflict.
|
|
15
19
|
|
|
16
20
|
interface RegeneratedFile {
|
|
17
21
|
path: string
|
|
18
22
|
command: ReadonlyArray<string>
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
interface RewrittenFile {
|
|
26
|
+
path: string
|
|
27
|
+
rewrite: (content: string) => string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ResolvedFile = RegeneratedFile | RewrittenFile
|
|
31
|
+
|
|
32
|
+
const RESOLVED_FILES: ReadonlyArray<ResolvedFile> = [
|
|
22
33
|
{
|
|
23
34
|
path: 'docs/josh-command-catalog.md',
|
|
24
|
-
command: ['pnpm', 'exec', 'tsx', 'tools/document/generate-catalog.ts'],
|
|
35
|
+
command: ['pnpm', 'exec', 'tsx', 'tools/document/generate-catalog-main.ts'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
path: main_merge_budget_conflict.BUDGET_TABLE_PATH,
|
|
39
|
+
rewrite: main_merge_budget_conflict.resolve,
|
|
25
40
|
},
|
|
26
41
|
]
|
|
27
42
|
|
|
28
|
-
function
|
|
43
|
+
function resolved_for(
|
|
29
44
|
files: ReadonlyArray<string>,
|
|
30
|
-
table: ReadonlyArray<
|
|
31
|
-
): ReadonlyArray<
|
|
45
|
+
table: ReadonlyArray<ResolvedFile>,
|
|
46
|
+
): ReadonlyArray<ResolvedFile> {
|
|
32
47
|
const entries = files.map((file) => table.find((entry) => entry.path === file))
|
|
33
48
|
|
|
34
49
|
return entries.every((entry) => entry !== undefined) ? entries : []
|
|
35
50
|
}
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
async function rewrite_file(entry: RewrittenFile, cwd: string): Promise<void> {
|
|
53
|
+
const file = path.join(cwd, entry.path)
|
|
54
|
+
|
|
55
|
+
await writeFile(file, entry.rewrite(await readFile(file, 'utf8')))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function regenerate_file(entry: RegeneratedFile, cwd: string): Promise<void> {
|
|
59
|
+
const [binary = '', ...command_arguments] = entry.command
|
|
60
|
+
|
|
61
|
+
await execa(binary, command_arguments, { cwd })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Each entry writes only its own file, so they run side by side.
|
|
65
|
+
async function regenerate(entries: ReadonlyArray<ResolvedFile>, cwd: string): Promise<void> {
|
|
39
66
|
await Promise.all(
|
|
40
67
|
entries.map(async (entry) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
await execa(binary, command_arguments, { cwd })
|
|
68
|
+
await ('rewrite' in entry ? rewrite_file(entry, cwd) : regenerate_file(entry, cwd))
|
|
44
69
|
}),
|
|
45
70
|
)
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
// `--no-edit` keeps the message the merge was started with — the one carrying the issue number the
|
|
49
74
|
// `commit-msg` hook asks for.
|
|
50
|
-
async function conclude_merge(entries: ReadonlyArray<
|
|
75
|
+
async function conclude_merge(entries: ReadonlyArray<ResolvedFile>): Promise<void> {
|
|
51
76
|
await git_spawn.with_output('add', ['--', ...entries.map((entry) => entry.path)])
|
|
52
77
|
await git_spawn.with_output('commit', ['--no-edit'], { timeout_ms: SUITE_TIMEOUT_MS })
|
|
53
78
|
}
|
|
54
79
|
|
|
55
|
-
// Whether the merge that stopped on `files` was finished. A command that fails
|
|
56
|
-
// progress and answers `false`, so the stop is the `conflict` it would have been without this table.
|
|
80
|
+
// Whether the merge that stopped on `files` was finished. A command that fails, or a rewrite that
|
|
81
|
+
// refuses a hunk, leaves the merge in progress and answers `false`, so the stop is the `conflict` it would have been without this table.
|
|
57
82
|
async function resolve(
|
|
58
83
|
files: ReadonlyArray<string>,
|
|
59
|
-
table: ReadonlyArray<
|
|
84
|
+
table: ReadonlyArray<ResolvedFile> = RESOLVED_FILES,
|
|
60
85
|
): Promise<boolean> {
|
|
61
|
-
const entries =
|
|
86
|
+
const entries = resolved_for(files, table)
|
|
62
87
|
|
|
63
88
|
if (entries.length === 0) return false
|
|
64
89
|
|
|
@@ -72,7 +97,16 @@ async function resolve(
|
|
|
72
97
|
return true
|
|
73
98
|
}
|
|
74
99
|
|
|
75
|
-
|
|
100
|
+
// Whether a merge stopping on exactly `files` is one `resolve` would try to finish — asked before any
|
|
101
|
+
// merge, by `josh ship`'s conflict probe, so it never stops on a conflict the sync resolves.
|
|
102
|
+
function is_resolvable(
|
|
103
|
+
files: ReadonlyArray<string>,
|
|
104
|
+
table: ReadonlyArray<ResolvedFile> = RESOLVED_FILES,
|
|
105
|
+
): boolean {
|
|
106
|
+
return resolved_for(files, table).length > 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const main_merge_regenerate = { RESOLVED_FILES, is_resolvable, resolve }
|
|
76
110
|
|
|
77
|
-
export type { RegeneratedFile }
|
|
111
|
+
export type { RegeneratedFile, ResolvedFile, RewrittenFile }
|
|
78
112
|
export { main_merge_regenerate }
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { extract_issue_number } from '#scripts/hooks/check-commit-message'
|
|
2
2
|
import { issue_cite } from '#scripts/issue/issue-cite'
|
|
3
3
|
import { composite_arguments, USAGE_ERROR_EXIT_CODE } from '#scripts/josh/josh-composite-arguments'
|
|
4
|
+
import { lane_install } from '#scripts/lane/lane-install'
|
|
4
5
|
import { error_text } from '#scripts/lib/error-message'
|
|
5
6
|
import { split_assess } from '#scripts/split/split-assess'
|
|
7
|
+
import { split_early_notice } from '#scripts/split/split-early-notice'
|
|
6
8
|
import { split_measure } from '#scripts/split/split-measure'
|
|
7
9
|
import { git_command } from './git-command'
|
|
8
10
|
import { main_merge_guard } from './main-merge-guard'
|
|
@@ -33,6 +35,9 @@ const FAILURE_EXIT_CODE = 1
|
|
|
33
35
|
const COMMAND_NAME = 'main:merge'
|
|
34
36
|
const SPLIT_UNMEASURED =
|
|
35
37
|
'split notice: the diff could not be measured — run `pnpm josh split:assess`'
|
|
38
|
+
const LOCKFILE = 'pnpm-lock.yaml'
|
|
39
|
+
const INSTALL_OWED =
|
|
40
|
+
'the merge brought in a lockfile change, but installing it failed — run `pnpm install --frozen-lockfile`'
|
|
36
41
|
|
|
37
42
|
// git's default merge message carries no `#N`, so on an issue branch the `commit-msg` hook refuses
|
|
38
43
|
// it and leaves the index mid-merge. The number is read with the hook's
|
|
@@ -47,7 +52,7 @@ function merge_message(default_branch: string, current_branch: string): string |
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
// What one merge of the default branch came to. `josh ship` merges it again
|
|
50
|
-
// before the
|
|
55
|
+
// before the gate, before the push and on a followup failed on its checks, and branches on the answer rather than on an
|
|
51
56
|
// exit code: `current` brought nothing in, so the tree is unchanged; `merged` changed it; `refused`
|
|
52
57
|
// is the guard's answer, nothing attempted; `conflict` names the paths git left unmerged, so a resumed
|
|
53
58
|
// session goes straight to them instead of asking git again.
|
|
@@ -65,7 +70,7 @@ interface MergeTarget {
|
|
|
65
70
|
|
|
66
71
|
// A failure that left no unmerged path — a remote that cannot be reached, a hook that refused — is not
|
|
67
72
|
// a conflict, so it is rethrown rather than reported as one with nothing to resolve. A conflict only on
|
|
68
|
-
//
|
|
73
|
+
// the files `main-merge-regenerate.ts` resolves is resolved and committed, so it answers `undefined`
|
|
69
74
|
// and the merge reads as finished.
|
|
70
75
|
async function unresolved_conflict(error: unknown): Promise<MergeOutcome | undefined> {
|
|
71
76
|
const files = main_merge_guard.unmerged_paths(await git_command.status())
|
|
@@ -89,6 +94,32 @@ async function merge_into(target: MergeTarget): Promise<MergeOutcome> {
|
|
|
89
94
|
return { kind: target.is_current ? 'current' : 'merged', branch: default_branch }
|
|
90
95
|
}
|
|
91
96
|
|
|
97
|
+
// joshuafolkken/kit#4777: a merge that brings in a lockfile change leaves `node_modules` on the old
|
|
98
|
+
// one, so the next check fails on files this branch never touched. Only a finished merge installs — a
|
|
99
|
+
// conflict or a refusal changed no dependency yet. A failed install does not fail the merge; it says
|
|
100
|
+
// the install is still owed, with pnpm's output. A throw is caught for the same reason: the merge is
|
|
101
|
+
// already committed, so `josh ship` must not read it as refused.
|
|
102
|
+
async function install_failure(): Promise<string | undefined> {
|
|
103
|
+
try {
|
|
104
|
+
const result = await lane_install.install_dependencies(await git_command.repository_root())
|
|
105
|
+
|
|
106
|
+
return result.is_installed ? undefined : result.output
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return error_text.message_of(error)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function install_merged_lockfile(
|
|
113
|
+
outcome: MergeOutcome,
|
|
114
|
+
incoming: ReadonlyArray<string>,
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
if (outcome.kind !== 'merged' || !incoming.includes(LOCKFILE)) return
|
|
117
|
+
|
|
118
|
+
const failure = await install_failure()
|
|
119
|
+
|
|
120
|
+
if (failure !== undefined) console.warn(`${INSTALL_OWED}\n${failure}`)
|
|
121
|
+
}
|
|
122
|
+
|
|
92
123
|
// The fetch is what makes `origin/<default>` current before the merge reads it. `git pull` did the
|
|
93
124
|
// two together, which is the only thing it was doing here that is worth keeping. The refusal is read
|
|
94
125
|
// after the fetch, so the incoming paths are the ones the merge would bring in.
|
|
@@ -104,14 +135,22 @@ async function merge(): Promise<MergeOutcome> {
|
|
|
104
135
|
|
|
105
136
|
if (message !== undefined) return { kind: 'refused', message }
|
|
106
137
|
|
|
107
|
-
|
|
138
|
+
const outcome = await merge_into({
|
|
139
|
+
default_branch,
|
|
140
|
+
current_branch,
|
|
141
|
+
is_current: incoming.length === 0,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
await install_merged_lockfile(outcome, incoming)
|
|
145
|
+
|
|
146
|
+
return outcome
|
|
108
147
|
}
|
|
109
148
|
|
|
110
149
|
// A merge that stops on a conflict keeps git's report and adds how to finish it, so the next step is
|
|
111
150
|
// the sanctioned commit rather than a request for `git add`.
|
|
112
151
|
function failure_text(outcome: MergeOutcome): string | undefined {
|
|
113
152
|
if (outcome.kind === 'refused') return outcome.message
|
|
114
|
-
if (outcome.kind === 'conflict') return main_merge_guard.
|
|
153
|
+
if (outcome.kind === 'conflict') return main_merge_guard.conflict_hint()
|
|
115
154
|
|
|
116
155
|
return undefined
|
|
117
156
|
}
|
|
@@ -121,9 +160,18 @@ function failure_text(outcome: MergeOutcome): string | undefined {
|
|
|
121
160
|
// advisory: a finished merge never fails on it, and an unreadable diff says so rather than reading as
|
|
122
161
|
// under the guide. It sits on the command, not on `merge()`, because `josh ship` merges again past the
|
|
123
162
|
// point a run can act on it.
|
|
163
|
+
async function measured_notice(): Promise<string | undefined> {
|
|
164
|
+
const measurement = await split_measure.read_branch()
|
|
165
|
+
const notice = split_assess.notice(measurement)
|
|
166
|
+
|
|
167
|
+
if (notice !== undefined) await split_early_notice.record(measurement)
|
|
168
|
+
|
|
169
|
+
return notice
|
|
170
|
+
}
|
|
171
|
+
|
|
124
172
|
async function split_notice(): Promise<string | undefined> {
|
|
125
173
|
try {
|
|
126
|
-
return
|
|
174
|
+
return await measured_notice()
|
|
127
175
|
} catch (error) {
|
|
128
176
|
return `${SPLIT_UNMEASURED}: ${error_text.message_of(error)}`
|
|
129
177
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
statSync,
|
|
8
|
+
symlinkSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from 'node:fs'
|
|
11
|
+
import { tmpdir } from 'node:os'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import { repo_discovery } from '#scripts/discovery/repo-discovery'
|
|
14
|
+
import { execa } from 'execa'
|
|
15
|
+
import { changed_paths } from './changed-paths'
|
|
16
|
+
import { git_command } from './git-command'
|
|
17
|
+
import { git_location_environment } from './git-location-environment'
|
|
18
|
+
|
|
19
|
+
// **A checkout of this tree as CI's `actions/checkout` hands it over** — a detached, depth-one `HEAD`
|
|
20
|
+
// with no local default branch and no remote-tracking ref (joshuafolkken/kit#4710). Code that passes
|
|
21
|
+
// `main` to git reads the local branch every lane has and passes here, then fails in CI with
|
|
22
|
+
// `bad revision 'main'`; run inside this checkout, it fails here too. **The `origin` remote is kept**,
|
|
23
|
+
// as CI keeps it: code that reads the repository's owner from it reads it here too
|
|
24
|
+
// (joshuafolkken/kit#4738).
|
|
25
|
+
//
|
|
26
|
+
// **No object is copied**: the objects are borrowed through `objects/info/alternates`, and only the
|
|
27
|
+
// tree is checked out. The work in progress is copied over it, and `node_modules` is linked, so the
|
|
28
|
+
// tests read what the lane's own run reads.
|
|
29
|
+
|
|
30
|
+
const CHECKOUT_PREFIX = 'josh-no-base-'
|
|
31
|
+
const NODE_MODULES = 'node_modules'
|
|
32
|
+
const ORIGIN_URL_KEY = 'remote.origin.url'
|
|
33
|
+
|
|
34
|
+
// Cleared rather than inherited: under a git hook `GIT_DIR` beats `cwd`, and every command here must
|
|
35
|
+
// act on the new checkout.
|
|
36
|
+
async function git(cwd: string, arguments_: ReadonlyArray<string>): Promise<string> {
|
|
37
|
+
const { stdout } = await execa('git', arguments_, {
|
|
38
|
+
cwd,
|
|
39
|
+
env: git_location_environment.location_free_environment(),
|
|
40
|
+
extendEnv: true,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return stdout.trim()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A file the work deleted is deleted from the checkout too. A directory git names as one path — a
|
|
47
|
+
// nested repository, a submodule — is not the work's to copy.
|
|
48
|
+
function copy_work(root: string, directory: string, file: string): void {
|
|
49
|
+
const source = path.join(root, file)
|
|
50
|
+
const target = path.join(directory, file)
|
|
51
|
+
|
|
52
|
+
if (!existsSync(source)) {
|
|
53
|
+
rmSync(target, { force: true })
|
|
54
|
+
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (statSync(source).isDirectory()) return
|
|
59
|
+
|
|
60
|
+
mkdirSync(path.dirname(target), { recursive: true })
|
|
61
|
+
copyFileSync(source, target)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function borrow_objects(root: string, directory: string): Promise<void> {
|
|
65
|
+
const common = await git(root, ['rev-parse', '--path-format=absolute', '--git-common-dir'])
|
|
66
|
+
const alternates = path.join(directory, '.git', 'objects', 'info', 'alternates')
|
|
67
|
+
|
|
68
|
+
writeFileSync(alternates, `${path.join(common, 'objects')}\n`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The remote is declared and never fetched, so it names the repository without bringing a
|
|
72
|
+
// remote-tracking ref along. A repository with no `origin` hands none over. Written with `git config`
|
|
73
|
+
// rather than `git remote add`, which the unit suite's network guard refuses whole.
|
|
74
|
+
async function add_origin(root: string, directory: string): Promise<void> {
|
|
75
|
+
const origin_url = repo_discovery.read_origin_url(root)
|
|
76
|
+
|
|
77
|
+
if (origin_url !== undefined) await git(directory, ['config', ORIGIN_URL_KEY, origin_url])
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function link_dependencies(root: string, directory: string): void {
|
|
81
|
+
const source = path.join(root, NODE_MODULES)
|
|
82
|
+
|
|
83
|
+
if (existsSync(source)) symlinkSync(source, path.join(directory, NODE_MODULES))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// `root` is the repository the checkout is cut from; the work in progress is read from the process's
|
|
87
|
+
// own repository, which is the same one wherever `josh test:related` calls this.
|
|
88
|
+
async function check_out(root: string, directory: string, head: string): Promise<void> {
|
|
89
|
+
await git(directory, ['init', '--quiet'])
|
|
90
|
+
await add_origin(root, directory)
|
|
91
|
+
await borrow_objects(root, directory)
|
|
92
|
+
await git(directory, ['checkout', '--quiet', '--detach', head])
|
|
93
|
+
writeFileSync(path.join(directory, '.git', 'shallow'), `${head}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function fill(root: string, directory: string): Promise<void> {
|
|
97
|
+
const head = await git_command.head_commit()
|
|
98
|
+
|
|
99
|
+
await check_out(root, directory, head)
|
|
100
|
+
link_dependencies(root, directory)
|
|
101
|
+
|
|
102
|
+
const work = await changed_paths.read_changed_since(head)
|
|
103
|
+
|
|
104
|
+
for (const file of work) copy_work(root, directory, file)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function remove(directory: string): void {
|
|
108
|
+
rmSync(directory, { force: true, recursive: true })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// A checkout that could not be finished is removed before the failure is passed on.
|
|
112
|
+
async function create(root: string): Promise<string> {
|
|
113
|
+
const directory = mkdtempSync(path.join(tmpdir(), CHECKOUT_PREFIX))
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await fill(root, directory)
|
|
117
|
+
} catch (error) {
|
|
118
|
+
remove(directory)
|
|
119
|
+
throw error
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return directory
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const no_base_checkout = { create, remove }
|
|
126
|
+
|
|
127
|
+
export { no_base_checkout }
|
|
@@ -88,12 +88,20 @@ async function pop(selector: string, directory?: string): Promise<void> {
|
|
|
88
88
|
// — the resume path documented in `backlogrun-park.md` ("expect to resolve conflicts"). The non-zero
|
|
89
89
|
// exit is indistinguishable from a real failure by its code alone, so a conflicted pop is told apart
|
|
90
90
|
// by the unmerged paths it leaves: `--diff-filter=U` lists exactly those, empty on any other failure.
|
|
91
|
-
|
|
91
|
+
// Read from git rather than parsed from a status string (`main_merge_guard.unmerged_paths`), since a
|
|
92
|
+
// stash caller holds a directory, not a status.
|
|
93
|
+
async function unmerged_paths(directory?: string): Promise<Array<string>> {
|
|
92
94
|
const unmerged = await git_spawn.read(
|
|
93
95
|
git_args(directory, ['diff', NAME_ONLY_FLAG, '--diff-filter=U']),
|
|
94
96
|
)
|
|
95
97
|
|
|
96
|
-
return unmerged !== ''
|
|
98
|
+
return unmerged.split('\n').filter((line) => line !== '')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function has_conflict(directory?: string): Promise<boolean> {
|
|
102
|
+
const unmerged = await unmerged_paths(directory)
|
|
103
|
+
|
|
104
|
+
return unmerged.length > 0
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
// Uncommitted work is anything `git status --porcelain` lists — untracked files included, which is what
|
|
@@ -211,6 +219,7 @@ const git_stash = {
|
|
|
211
219
|
push,
|
|
212
220
|
select,
|
|
213
221
|
STASHABLE_PATHSPEC,
|
|
222
|
+
unmerged_paths,
|
|
214
223
|
work_message,
|
|
215
224
|
}
|
|
216
225
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { main_merge_guard } from '#scripts/git/main-merge-guard'
|
|
1
2
|
import { error_text } from '#scripts/lib/error-message'
|
|
2
3
|
import { git_stash } from './git-stash'
|
|
3
4
|
import { stash_args, type Request } from './stash-args'
|
|
@@ -16,6 +17,7 @@ import { stash_args, type Request } from './stash-args'
|
|
|
16
17
|
// can call it unconditionally and pop only after `stashed`.
|
|
17
18
|
|
|
18
19
|
const COMMAND = 'stash:push'
|
|
20
|
+
const STASH_RERUN = 'the command that stashed (`run:pause`, `stash:push`, `latest:if-required`)'
|
|
19
21
|
|
|
20
22
|
const STASHED_VERDICT = 'stashed'
|
|
21
23
|
const CLEAN_VERDICT = 'clean'
|
|
@@ -40,7 +42,15 @@ type PushVerdict = typeof STASHED_VERDICT | typeof CLEAN_VERDICT
|
|
|
40
42
|
|
|
41
43
|
// The decision and the push without the printing, so a caller that chains a pop on `stashed`
|
|
42
44
|
// (`josh latest:if-required`) reads the verdict instead of parsing stdout.
|
|
45
|
+
// An index with unresolved paths is refused before the push: `git stash` cannot set them aside, and
|
|
46
|
+
// the way out is closing the merge, not a stash (joshuafolkken/kit#4769).
|
|
43
47
|
async function set_aside(request: Request): Promise<PushVerdict> {
|
|
48
|
+
const unmerged = await git_stash.unmerged_paths(request.dir)
|
|
49
|
+
|
|
50
|
+
if (unmerged.length > 0) {
|
|
51
|
+
throw new Error(main_merge_guard.unresolved_message(COMMAND, unmerged, STASH_RERUN))
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
if (!(await git_stash.has_stashable_changes(request.dir))) return CLEAN_VERDICT
|
|
45
55
|
|
|
46
56
|
await git_stash.push(request.message, request.dir, git_stash.STASHABLE_PATHSPEC)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { will_refuse as will_investigation_refuse } from '#scripts/delegation/investigation-guard'
|
|
2
|
+
import { hook_decision, type GuardRun } from '#scripts/josh/hook-decision'
|
|
2
3
|
import { lane_guard_policy, type LaneGuardMode } from '#scripts/lane/lane-guard-policy'
|
|
3
4
|
import { time_batch_guard, type GuardedCall } from '#scripts/time-runtime/time-batch-guard'
|
|
4
5
|
|
|
@@ -40,29 +41,39 @@ function is_batch_candidate(call: GuardedCall): boolean {
|
|
|
40
41
|
|
|
41
42
|
// **How the batching guard behaves for the session in hand, decided from the one-place enumeration**.
|
|
42
43
|
// Outside a lane child the mode is `refuse` and the guard is exactly what it always was; in a dispatched
|
|
43
|
-
// lane child
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// change beyond the enumeration. The enumeration reads `is_child_of` — the dispatch mark against this
|
|
48
|
-
// checkout's own issue — only for a guard whose mode it would actually change, so a person working in a
|
|
49
|
-
// lane sees the guard unchanged.
|
|
44
|
+
// lane child the general rule is `off` and only the `search-batching` row still refuses — a chain of
|
|
45
|
+
// searches issued one per turn, the largest share of the child's single-call pairs (kit#5342). The
|
|
46
|
+
// enumeration reads `is_child_of` — the dispatch mark against this checkout's own issue — only for a
|
|
47
|
+
// guard whose mode it would actually change, so a person working in a lane sees the guard unchanged.
|
|
50
48
|
function batching_mode(): LaneGuardMode {
|
|
51
49
|
return lane_guard_policy.mode_here('batching')
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
function is_search_refused_here(): boolean {
|
|
53
|
+
return lane_guard_policy.mode_here('search-batching') === 'refuse'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Where the general rule is `off`, only a search the narrower refusal judges is read for. The
|
|
57
|
+
// lane-child modes and their reasons live in `lane-guard-policy.ts`; on the main line both are `refuse`.
|
|
56
58
|
function is_candidate(call: GuardedCall): boolean {
|
|
57
|
-
|
|
59
|
+
if (batching_mode() !== 'off') return is_batch_candidate(call)
|
|
60
|
+
|
|
61
|
+
return is_search_refused_here() && time_batch_guard.is_search_chain_call(call)
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
// A refusable call is refused only where the mode is `refuse
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
// A refusable call is refused only where the mode is `refuse`; elsewhere a search chain alone is, under
|
|
65
|
+
// the `search-batching` row, standing aside for the investigation guard's refusal of the same call.
|
|
66
|
+
// Why a lane child takes each mode is its `because` in `lane-guard-policy.ts`.
|
|
67
|
+
function should_block_here(
|
|
68
|
+
tail: string,
|
|
69
|
+
call: GuardedCall,
|
|
70
|
+
refused_at_ms: number,
|
|
71
|
+
run: GuardRun,
|
|
72
|
+
): boolean {
|
|
73
|
+
if (batching_mode() === 'refuse') return time_batch_guard.should_block(tail, call, refused_at_ms)
|
|
74
|
+
if (!is_search_refused_here() || will_investigation_refuse(tail, call, run)) return false
|
|
64
75
|
|
|
65
|
-
return time_batch_guard.
|
|
76
|
+
return time_batch_guard.should_block_search_chain(tail, call, refused_at_ms)
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
// The whole-file write earns a notice on every run. The second branch turns a
|
|
@@ -81,9 +92,8 @@ function should_notify_here(tail: string, call: GuardedCall, notified_at_ms: num
|
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
// The wording each notice carries, with the concrete calls the run just issued one-per-turn appended.
|
|
84
|
-
// The whole-file write keeps its own base text; the lane notice is reached
|
|
85
|
-
//
|
|
86
|
-
// child is shown *which* recent calls it could have batched rather than only told to batch.
|
|
95
|
+
// The whole-file write keeps its own base text; the lane notice is reached only under a `notice`-mode
|
|
96
|
+
// batching row, so a headless child would be shown *which* recent calls it could have batched.
|
|
87
97
|
function notice_text(call: GuardedCall, tail: string): string {
|
|
88
98
|
const base = time_batch_guard.is_notice_call(call)
|
|
89
99
|
? time_batch_guard.NOTICE
|
|
@@ -99,8 +109,7 @@ const GUARD = hook_decision.create_transcript_guard({
|
|
|
99
109
|
should_block: should_block_here,
|
|
100
110
|
reason: time_batch_guard.REASON,
|
|
101
111
|
// The whole-file write is notified rather than refused, on a record of its own so it never spends
|
|
102
|
-
// the refusal's stamp; the
|
|
103
|
-
// here fires again since kit#2276 restored batching to `notice` in a lane child.
|
|
112
|
+
// the refusal's stamp; the refusable-call notice fires only under a `notice`-mode batching row.
|
|
104
113
|
notify: {
|
|
105
114
|
prefix: time_batch_guard.NOTICE_STAMP_PREFIX,
|
|
106
115
|
should_notify: should_notify_here,
|
|
@@ -117,6 +126,15 @@ const notice_refusal_path = hook_decision.create_refusal_stamp(
|
|
|
117
126
|
time_batch_guard.NOTICE_STAMP_PREFIX,
|
|
118
127
|
).path
|
|
119
128
|
const { deny_envelope, load_environment_file, DISABLED_VALUES } = hook_decision
|
|
129
|
+
const STAMP = hook_decision.create_refusal_stamp(time_batch_guard.STAMP_PREFIX)
|
|
130
|
+
|
|
131
|
+
// Whether this guard refuses the call in hand, asked with its real record — the rule guard stands
|
|
132
|
+
// aside for it, so the two never decide apart.
|
|
133
|
+
function will_refuse(tail: string, call: GuardedCall, run: GuardRun): boolean {
|
|
134
|
+
if (!is_enabled() || !is_candidate(call)) return false
|
|
135
|
+
|
|
136
|
+
return should_block_here(tail, call, STAMP.last_ms(STAMP.path(run.transcript)), run)
|
|
137
|
+
}
|
|
120
138
|
|
|
121
139
|
export {
|
|
122
140
|
batch_outcome,
|
|
@@ -126,6 +144,7 @@ export {
|
|
|
126
144
|
load_environment_file,
|
|
127
145
|
notice_refusal_path,
|
|
128
146
|
refusal_path,
|
|
147
|
+
will_refuse,
|
|
129
148
|
DISABLED_VALUES,
|
|
130
149
|
SWITCH_ENV_KEY,
|
|
131
150
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { resolve_local_bin } from '#scripts/build/local-bin'
|
|
3
4
|
import { HOOK_PROCESS_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
4
5
|
import { execa } from 'execa'
|
|
5
6
|
|
|
@@ -38,15 +39,28 @@ const CSPELL_EXTENSIONS: ReadonlySet<string> = new Set([
|
|
|
38
39
|
// exit code is ignored: cspell exits non-zero precisely when it found words, which is the case this
|
|
39
40
|
// exists for.
|
|
40
41
|
const CSPELL_ARGS: ReadonlyArray<string> = [
|
|
41
|
-
'exec',
|
|
42
|
-
'cspell',
|
|
43
42
|
'lint',
|
|
44
43
|
'--words-only',
|
|
45
44
|
'--unique',
|
|
46
45
|
'--no-progress',
|
|
47
46
|
'--no-summary',
|
|
48
47
|
]
|
|
48
|
+
const CSPELL_BIN = 'cspell'
|
|
49
49
|
const PNPM = 'pnpm'
|
|
50
|
+
// The text arrives on stdin under the edited file's own name, so the project config — its ignore
|
|
51
|
+
// list, its language per extension, its overrides — applies exactly as it does to the path. Reading
|
|
52
|
+
// stdin rather than the file is what lets the check run beside the formatters
|
|
53
|
+
// (joshuafolkken/kit#4596): they rewrite the file while it runs, and a read caught mid-write would
|
|
54
|
+
// report on a truncated file.
|
|
55
|
+
const STDIN_SCHEME = 'stdin://'
|
|
56
|
+
|
|
57
|
+
interface CspellRequest {
|
|
58
|
+
file_path: string
|
|
59
|
+
project_root: string
|
|
60
|
+
text: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type CspellRunner = (request: CspellRequest) => Promise<string>
|
|
50
64
|
|
|
51
65
|
const HEADER =
|
|
52
66
|
'The edit hook found words cspell does not know; fix them or add them to `cspell.config.yaml` now rather than at the gate:'
|
|
@@ -54,8 +68,6 @@ const HEADER =
|
|
|
54
68
|
const MAX_DIAGNOSTIC_CHARS = 4000
|
|
55
69
|
const TRUNCATION_NOTICE = '\n…(unknown words truncated)'
|
|
56
70
|
|
|
57
|
-
type CspellRunner = (file_path: string, project_root: string) => Promise<string>
|
|
58
|
-
|
|
59
71
|
// One question before a process starts: is this a file under the project that spell-checking has
|
|
60
72
|
// anything to say about?
|
|
61
73
|
function is_checkable(file_path: string, project_root: string): boolean {
|
|
@@ -68,12 +80,24 @@ function is_checkable(file_path: string, project_root: string): boolean {
|
|
|
68
80
|
return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
// The project's own shim, spawned directly: `pnpm exec` costs a second process start on every edit,
|
|
84
|
+
// and this check was the largest step of the edit hook (joshuafolkken/kit#4596). `pnpm exec` stays as
|
|
85
|
+
// the route for a project whose binaries live somewhere other than the root's `node_modules/.bin`.
|
|
86
|
+
function cspell_command(project_root: string): ReadonlyArray<string> {
|
|
87
|
+
const shim = resolve_local_bin(project_root, CSPELL_BIN)
|
|
88
|
+
|
|
89
|
+
return existsSync(shim) ? [shim] : [PNPM, 'exec', CSPELL_BIN]
|
|
90
|
+
}
|
|
91
|
+
|
|
71
92
|
// The live runner. `reject: false` because a non-zero exit is the ordinary way cspell reports words,
|
|
72
93
|
// and stderr is ignored so a config note never reaches the model as if it were an unknown word.
|
|
73
|
-
async function run_cspell(
|
|
74
|
-
const
|
|
94
|
+
async function run_cspell(request: CspellRequest): Promise<string> {
|
|
95
|
+
const [bin = PNPM, ...prefix] = cspell_command(request.project_root)
|
|
96
|
+
const target = `${STDIN_SCHEME}${path.resolve(request.file_path)}`
|
|
97
|
+
const result = await execa(bin, [...prefix, ...CSPELL_ARGS, target], {
|
|
75
98
|
reject: false,
|
|
76
|
-
cwd: project_root,
|
|
99
|
+
cwd: request.project_root,
|
|
100
|
+
input: request.text,
|
|
77
101
|
stdout: 'pipe',
|
|
78
102
|
stderr: 'ignore',
|
|
79
103
|
timeout: HOOK_PROCESS_TIMEOUT_MS,
|
|
@@ -104,22 +128,24 @@ function format_unknown_words(stdout: string): string | undefined {
|
|
|
104
128
|
|
|
105
129
|
// Never turns a successful edit into a failure: a runner that throws — cspell missing, a timeout — is
|
|
106
130
|
// swallowed and reported as nothing to say, exactly as the lint half swallows a formatter that could
|
|
107
|
-
// not start.
|
|
131
|
+
// not start. `text` is the file's content as the caller read it, so the check does not depend on when
|
|
132
|
+
// it runs against the formatters.
|
|
108
133
|
async function spelling_diagnostics(
|
|
109
134
|
file_path: string | undefined,
|
|
110
135
|
project_root: string,
|
|
136
|
+
text: string,
|
|
111
137
|
runner: CspellRunner = run_cspell,
|
|
112
138
|
): Promise<string | undefined> {
|
|
113
139
|
if (file_path === undefined || !is_checkable(file_path, project_root)) return undefined
|
|
114
140
|
|
|
115
141
|
try {
|
|
116
|
-
return format_unknown_words(await runner(file_path, project_root))
|
|
142
|
+
return format_unknown_words(await runner({ file_path, project_root, text }))
|
|
117
143
|
} catch {
|
|
118
144
|
return undefined
|
|
119
145
|
}
|
|
120
146
|
}
|
|
121
147
|
|
|
122
|
-
const edited_cspell = { format_unknown_words, spelling_diagnostics }
|
|
148
|
+
const edited_cspell = { cspell_command, format_unknown_words, run_cspell, spelling_diagnostics }
|
|
123
149
|
|
|
124
|
-
export type { CspellRunner }
|
|
150
|
+
export type { CspellRequest, CspellRunner }
|
|
125
151
|
export { edited_cspell, MAX_DIAGNOSTIC_CHARS }
|