@joshuafolkken/kit 1.1178.0 → 1.1240.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +10 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -1
- package/.claude/skills/workflow-commands/latest-gate.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +32 -31
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +50 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +44 -38
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +203 -179
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +66 -72
- package/dist/josh.js +1088 -78
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +1 -0
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +16 -5
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +4 -129
- package/scripts/gate/core-admission.ts +162 -0
- package/scripts/gate/core-budget.ts +190 -68
- package/scripts/gate/gate-plan.ts +80 -7
- package/scripts/gate/gate-report.ts +28 -9
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/machine-capacity.ts +175 -0
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +34 -10
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-pr-read.ts +64 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +1 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +7 -5
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +2 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +10 -16
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/package-manager-pin.ts +35 -0
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +32 -7
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +72 -22
- package/scripts/issue/issue-file.ts +29 -1
- package/scripts/issue/issue-labels.ts +13 -0
- package/scripts/issue/issue-release-cli.ts +155 -0
- package/scripts/issue/issue-release.ts +58 -0
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +14 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +18 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +8 -1
- package/scripts/josh/josh-commands-lane.ts +24 -1
- package/scripts/josh/josh-commands-maintenance.ts +6 -6
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +34 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +12 -4
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +44 -30
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-open.ts +5 -5
- package/scripts/lane/lane-paths.ts +13 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-registry.ts +11 -0
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-stray.ts +140 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release-runs.ts +115 -0
- package/scripts/release/github-release.ts +13 -67
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +1 -1
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +7 -4
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +124 -93
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +6 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
- /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
- /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
|
|
2
|
+
import { time_background } from './time-background'
|
|
3
|
+
import { time_bundle_call } from './time-bundle-call'
|
|
4
|
+
import { time_markers, type PhaseMarker } from './time-markers'
|
|
5
|
+
import { time_shell } from './time-shell'
|
|
6
|
+
import { time_single_check } from './time-single-check'
|
|
7
|
+
import { time_transcript_line } from './time-transcript-line'
|
|
8
|
+
import { time_writes } from './time-writes'
|
|
9
|
+
|
|
10
|
+
// Reading a `tool_use` block into the label a span carries — split out of `time-spans.ts` when it
|
|
11
|
+
// reached its line limit (joshuafolkken/kit#3264). `time_spans` re-exports `UNKNOWN_TOOL` and
|
|
12
|
+
// `to_tool_call` under the names they always had, so the move changed no call site.
|
|
13
|
+
|
|
14
|
+
const { NO_MESSAGE_ID } = time_transcript_line
|
|
15
|
+
|
|
16
|
+
const UNKNOWN_TOOL = 'unknown'
|
|
17
|
+
|
|
18
|
+
// What a tool span is labelled with. `josh_command` is empty for everything that is not a
|
|
19
|
+
// `pnpm josh <cmd>` invocation, and the report drops empty labels rather than printing a bucket.
|
|
20
|
+
//
|
|
21
|
+
// `marker` names the workflow boundary the call is, for the phase breakdown that slices the same
|
|
22
|
+
// spans by stage (joshuafolkken/kit#1269). It is carried here rather than re-derived later because
|
|
23
|
+
// the tool's *input* is what decides it, and a span keeps no input — only the label read off it.
|
|
24
|
+
//
|
|
25
|
+
// `is_bundleable` and `targets` are carried for exactly that reason too (joshuafolkken/kit#1344).
|
|
26
|
+
// Whether a call could have gone out beside another, and what it names, are both read off the input —
|
|
27
|
+
// so a module asking about them after the fact would have nothing to read. `time-bundle-call.ts`
|
|
28
|
+
// decides both; the rule each one follows is stated there.
|
|
29
|
+
//
|
|
30
|
+
// `check_key` is the fourth, and the third time this reason has applied (joshuafolkken/kit#1383). Two
|
|
31
|
+
// runs of one verification check are the same call only if they named the same files, and the files
|
|
32
|
+
// are in the input — so `josh_command` alone cannot say, and nothing downstream could recover it.
|
|
33
|
+
// `time-single-check.ts` decides it.
|
|
34
|
+
//
|
|
35
|
+
// `message_id` is the fifth, and it is the one that says which *turn* a call belongs to
|
|
36
|
+
// (joshuafolkken/kit#1406). It is read off the line the `tool_use` block sat on rather than off the
|
|
37
|
+
// result that closes the span, because a `tool_result` line carries no message id at all — so a span
|
|
38
|
+
// that did not keep it here could never be attributed to the turn that issued it.
|
|
39
|
+
//
|
|
40
|
+
// `writes` is the sixth, and it is what `targets` could not say (joshuafolkken/kit#1472). `targets`
|
|
41
|
+
// names what a call mentioned; `marker` says a call was *an* edit but not of what, and for a tool
|
|
42
|
+
// outside `BUNDLEABLE_TOOLS` it arrived with no target at all. So a consumer subtracting edits from
|
|
43
|
+
// reads — `investigation-reads.ts` — had nothing to subtract for `MultiEdit` / `NotebookEdit`, and
|
|
44
|
+
// nothing at all to tell an in-place `sed -i` from the `sed -n` it shares a label with.
|
|
45
|
+
// `time-writes.ts` decides it, from the input this span is about to discard.
|
|
46
|
+
// `issue` is the seventh, and it is the one `branch` could not carry (joshuafolkken/kit#1617). Under
|
|
47
|
+
// lanes the session writing the transcript stays on the default branch while the work runs in a linked
|
|
48
|
+
// work tree, so `branch` names no issue on any line of the run; the `in-progress` label call does, and
|
|
49
|
+
// like every field above it that answer is read off the *input* a span is about to discard.
|
|
50
|
+
// `time_markers.NO_ISSUE` for every call that declares nothing, which is all but one call per run.
|
|
51
|
+
interface ToolCall {
|
|
52
|
+
label: string
|
|
53
|
+
josh_command: string
|
|
54
|
+
// Every josh subcommand the call ran, where `josh_command` is only the first (joshuafolkken/kit#1883).
|
|
55
|
+
// A chained `pnpm josh lint:related && pnpm josh test:related` carries both; the count tables expand
|
|
56
|
+
// it so the ones `josh_command` drops are still counted.
|
|
57
|
+
josh_commands: ReadonlyArray<string>
|
|
58
|
+
check_key: string
|
|
59
|
+
marker: PhaseMarker
|
|
60
|
+
is_bundleable: boolean
|
|
61
|
+
targets: ReadonlyArray<string>
|
|
62
|
+
// Carried beside `targets` because sharing a target means something different depending on it: a
|
|
63
|
+
// second write to one file is independent work, a read of a file just written is not
|
|
64
|
+
// (joshuafolkken/kit#1509).
|
|
65
|
+
is_writing: boolean
|
|
66
|
+
// The conservative write answer `is_read_only_call` asks of a live call, carried so a reader of the
|
|
67
|
+
// transcript asks the same read-only question as the guard (joshuafolkken/kit#3139).
|
|
68
|
+
may_write: boolean
|
|
69
|
+
// Whether this is a subagent launch whose prompt builds on an earlier launch's finding
|
|
70
|
+
// (joshuafolkken/kit#1854). Carried for the reason every field here is — the prompt it is read from
|
|
71
|
+
// is the input, and a span keeps none. `time-agent-bundles.ts` reads it.
|
|
72
|
+
has_prior_reference: boolean
|
|
73
|
+
writes: ReadonlyArray<string>
|
|
74
|
+
message_id: string
|
|
75
|
+
issue: number
|
|
76
|
+
// The background run this call reads the output of, and `''` for every other call
|
|
77
|
+
// (joshuafolkken/kit#1662). It is the eighth field carried for the reason the seven above are:
|
|
78
|
+
// the id sits in the command string, which a span does not keep, and it is the only thing that
|
|
79
|
+
// says a `tail` two turns later is the join of a command started minutes ago.
|
|
80
|
+
reads_background: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const NO_CALL: ToolCall = {
|
|
84
|
+
label: '',
|
|
85
|
+
josh_command: '',
|
|
86
|
+
josh_commands: [],
|
|
87
|
+
check_key: time_single_check.NO_CHECK,
|
|
88
|
+
marker: time_markers.NO_MARKER,
|
|
89
|
+
message_id: NO_MESSAGE_ID,
|
|
90
|
+
writes: [],
|
|
91
|
+
issue: time_markers.NO_ISSUE,
|
|
92
|
+
reads_background: time_background.NO_BACKGROUND,
|
|
93
|
+
...time_bundle_call.not_bundleable(),
|
|
94
|
+
}
|
|
95
|
+
const UNKNOWN_CALL: ToolCall = {
|
|
96
|
+
label: UNKNOWN_TOOL,
|
|
97
|
+
josh_command: '',
|
|
98
|
+
josh_commands: [],
|
|
99
|
+
check_key: time_single_check.NO_CHECK,
|
|
100
|
+
marker: time_markers.NO_MARKER,
|
|
101
|
+
message_id: NO_MESSAGE_ID,
|
|
102
|
+
writes: [],
|
|
103
|
+
issue: time_markers.NO_ISSUE,
|
|
104
|
+
reads_background: time_background.NO_BACKGROUND,
|
|
105
|
+
...time_bundle_call.not_bundleable(),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Everything but Bash: the tool's own name is the label, and nothing it runs is a shell command to
|
|
109
|
+
// read a check key, a josh command or a declared issue off.
|
|
110
|
+
function non_bash_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
111
|
+
return {
|
|
112
|
+
label: name,
|
|
113
|
+
josh_command: '',
|
|
114
|
+
josh_commands: [],
|
|
115
|
+
check_key: time_single_check.NO_CHECK,
|
|
116
|
+
marker: time_markers.tool_marker(name, input),
|
|
117
|
+
message_id,
|
|
118
|
+
writes: time_writes.tool_writes(name, input),
|
|
119
|
+
issue: time_markers.NO_ISSUE,
|
|
120
|
+
// A non-Bash tool joins a backgrounded command too — `BashOutput` carries the shell id in a
|
|
121
|
+
// field of its own, `Monitor` names the same `…/tasks/<id>.output` path in a command — so the
|
|
122
|
+
// same reader is asked here rather than the field being left empty for every tool but `Bash`.
|
|
123
|
+
reads_background: time_background.read_id_of(input),
|
|
124
|
+
...time_bundle_call.tool_facts(name, input),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function bash_call(input: unknown, message_id: string): ToolCall {
|
|
129
|
+
const command = time_shell.bash_command(input)
|
|
130
|
+
const josh_command = time_shell.josh_command_of(command)
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
label: time_shell.bash_label(command),
|
|
134
|
+
josh_command,
|
|
135
|
+
josh_commands: time_shell.josh_commands_of(command),
|
|
136
|
+
check_key: time_single_check.check_key(josh_command, command),
|
|
137
|
+
marker: time_markers.bash_marker(command),
|
|
138
|
+
message_id,
|
|
139
|
+
writes: time_writes.bash_writes(command),
|
|
140
|
+
issue: time_markers.bash_issue(command),
|
|
141
|
+
reads_background: time_background.read_id_of(input),
|
|
142
|
+
...time_bundle_call.bash_facts(command),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function to_tool_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
147
|
+
if (name !== cost_blocks.BASH_TOOL) return non_bash_call(name, input, message_id)
|
|
148
|
+
|
|
149
|
+
return bash_call(input, message_id)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const time_tool_call = {
|
|
153
|
+
UNKNOWN_TOOL,
|
|
154
|
+
NO_CALL,
|
|
155
|
+
UNKNOWN_CALL,
|
|
156
|
+
to_tool_call,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type { ToolCall }
|
|
160
|
+
export { time_tool_call }
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
+
import { ancestor_directories } from '#scripts/lib/ancestor-directories'
|
|
4
5
|
import { json_value } from '#scripts/lib/json-value'
|
|
5
6
|
import { package_named_version_schema } from '#scripts/lib/schemas'
|
|
6
7
|
|
|
7
8
|
const PACKAGE_JSON = 'package.json'
|
|
8
|
-
// Node module paths never nest anywhere near this deep; the bound only guards against an unexpected
|
|
9
|
-
// filesystem cycle so the walk-up cannot loop forever.
|
|
10
|
-
const MAX_WALK_DEPTH = 64
|
|
11
9
|
|
|
12
10
|
// Options for `resolve_effective_upstream_version`. `resolve_marker` is a subpath specifier used for
|
|
13
11
|
// `createRequire().resolve()` when the package root itself is not directly resolvable — e.g. kit does
|
|
@@ -31,26 +29,10 @@ function read_matching_version(manifest_path: string, package_name: string): str
|
|
|
31
29
|
return parsed.data.version
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
// The directory itself and each of its ancestors up to the filesystem root, nearest-first. Bounded
|
|
35
|
-
// by MAX_WALK_DEPTH so an unexpected filesystem cycle cannot produce an unbounded list.
|
|
36
|
-
function ancestor_directories(start_directory: string): Array<string> {
|
|
37
|
-
const directories: Array<string> = []
|
|
38
|
-
let current = start_directory
|
|
39
|
-
|
|
40
|
-
for (let depth = 0; depth < MAX_WALK_DEPTH; depth += 1) {
|
|
41
|
-
directories.push(current)
|
|
42
|
-
const parent = path.dirname(current)
|
|
43
|
-
if (parent === current) break
|
|
44
|
-
current = parent
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return directories
|
|
48
|
-
}
|
|
49
|
-
|
|
50
32
|
// Walk up from a resolved file's directory to the package root, returning the version of the first
|
|
51
33
|
// ancestor package.json whose `name` matches the target — or undefined when no matching root exists.
|
|
52
34
|
function walk_up_for_version(start_directory: string, package_name: string): string | undefined {
|
|
53
|
-
for (const directory of ancestor_directories(start_directory)) {
|
|
35
|
+
for (const directory of ancestor_directories.list(start_directory)) {
|
|
54
36
|
const version = read_matching_version(path.join(directory, PACKAGE_JSON), package_name)
|
|
55
37
|
if (version !== undefined) return version
|
|
56
38
|
}
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* major equals the `packageManager` pin keeps both invariants: never below the adopted
|
|
12
12
|
* major, and advancing while that major is the current one.
|
|
13
13
|
*
|
|
14
|
-
* The `
|
|
15
|
-
* safe-chain filters the registry only when the process tree was launched through one
|
|
16
|
-
* its wrapped shell commands, so `josh latest` and `pnpm josh latest` used to resolve
|
|
14
|
+
* The `minimumReleaseAge` quarantine is applied natively from `pnpm-workspace.yaml` (kit#768,
|
|
15
|
+
* #3267). safe-chain filters the registry only when the process tree was launched through one
|
|
16
|
+
* of its wrapped shell commands, so `josh latest` and `pnpm josh latest` used to resolve
|
|
17
17
|
* different answers and the pin oscillated. Reading the window from the repo-managed
|
|
18
|
-
*
|
|
19
|
-
* invocation context; right after a pnpm release the answer is simply the previous
|
|
18
|
+
* `pnpm-workspace.yaml` and filtering by publish timestamp makes the resolution identical in
|
|
19
|
+
* every invocation context; right after a pnpm release the answer is simply the previous
|
|
20
20
|
* release, exactly as the filtered view behaved.
|
|
21
21
|
*
|
|
22
22
|
* The resolved version is floored at the current `packageManager` pin: a filtered registry
|
|
@@ -30,14 +30,19 @@
|
|
|
30
30
|
* manifest that arrived with the two fields out of step kept the pnpm dual-declaration
|
|
31
31
|
* warning forever. The alignment is idempotent, so running it unconditionally is free.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* A failed bump exits non-zero with its cause, the manifest restored (#3361). It used to be
|
|
34
|
+
* swallowed as a skip, so `latest:scope --record` marked the run fresh and a pnpm that could not
|
|
35
|
+
* self-update (the Corepack shim refuses it) stayed pinned silently. `josh latest` runs this step
|
|
36
|
+
* last, right before the record, so the dependency update and the audit still finish and only the
|
|
37
|
+
* record is withheld. A registry answer not newer than the pin, or nothing aged past the
|
|
38
|
+
* quarantine window yet, remains a non-fatal skip.
|
|
35
39
|
*
|
|
36
40
|
* Usage: tsx scripts/version/latest-corepack.ts
|
|
37
41
|
*/
|
|
38
42
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
39
43
|
import { fileURLToPath } from 'node:url'
|
|
40
44
|
import { json_value } from '#scripts/lib/json-value'
|
|
45
|
+
import { COMMAND_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
41
46
|
import { execaSync } from 'execa'
|
|
42
47
|
import semver from 'semver'
|
|
43
48
|
import { z } from 'zod'
|
|
@@ -49,14 +54,23 @@ const PACKAGE_MANAGER_RE = /"packageManager"\s*:\s*"pnpm@(\d+)(?:[^\d]|$)/u
|
|
|
49
54
|
const PINNED_VERSION_RE = /"packageManager"\s*:\s*"pnpm@([^"+]+)/u
|
|
50
55
|
const TARGET_PREFIX = 'pnpm@'
|
|
51
56
|
const FAILURE_EXIT_CODE = 1
|
|
52
|
-
const
|
|
53
|
-
const VIEW_TIMEOUT_MS = 30_000
|
|
57
|
+
const WORKSPACE_PATH = 'pnpm-workspace.yaml'
|
|
54
58
|
const SHA512_BYTES = 64
|
|
55
59
|
const INTEGRITY_RE = /^sha512-([A-Za-z0-9+/]+={0,2})$/u
|
|
56
60
|
const PACKAGE_MANAGER_VALUE_RE = /("packageManager"\s*:\s*")pnpm@[^"]+(")/u
|
|
61
|
+
|
|
62
|
+
// `target` is undefined both when the registry did not answer and when it answered but nothing has
|
|
63
|
+
// aged past the quarantine window yet; only the first is a failure (#3361).
|
|
64
|
+
interface TargetResolution {
|
|
65
|
+
target: string | undefined
|
|
66
|
+
is_registry_reachable: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
const PNPM_DEVELOPMENT_MANAGER_SCHEMA = z.object({ name: z.literal('pnpm'), version: z.string() })
|
|
58
70
|
const DEV_ENGINES_SCHEMA = z.object({ packageManager: PNPM_DEVELOPMENT_MANAGER_SCHEMA })
|
|
59
71
|
const DEV_ENGINES_PNPM_SCHEMA = z.object({ devEngines: DEV_ENGINES_SCHEMA.optional() })
|
|
72
|
+
const COREPACK_REMEDY =
|
|
73
|
+
'pnpm runs through the Corepack shim, where pnpm 11+ refuses self-update (ERR_PNPM_CANT_SELF_UPDATE_IN_COREPACK). Run `corepack disable pnpm`, install a standalone pnpm (https://pnpm.io/installation), then rerun josh latest.'
|
|
60
74
|
|
|
61
75
|
function extract_development_engines_version(package_json_content: string): string | undefined {
|
|
62
76
|
const parsed = DEV_ENGINES_PNPM_SCHEMA.safeParse(
|
|
@@ -121,29 +135,40 @@ function extract_times_json(stdout: string): Record<string, string> | undefined
|
|
|
121
135
|
function query_release_times(): Record<string, string> | undefined {
|
|
122
136
|
const result = execaSync('pnpm', ['view', 'pnpm', 'time', '--json'], {
|
|
123
137
|
reject: false,
|
|
124
|
-
timeout:
|
|
138
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
125
139
|
})
|
|
126
140
|
if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
|
|
127
141
|
|
|
128
142
|
return extract_times_json(result.stdout)
|
|
129
143
|
}
|
|
130
144
|
|
|
145
|
+
function to_resolution(
|
|
146
|
+
version: string | undefined,
|
|
147
|
+
is_registry_reachable: boolean,
|
|
148
|
+
): TargetResolution {
|
|
149
|
+
const target = version === undefined ? undefined : `${TARGET_PREFIX}${version}`
|
|
150
|
+
|
|
151
|
+
return { target, is_registry_reachable }
|
|
152
|
+
}
|
|
153
|
+
|
|
131
154
|
// Ask the registry for the newest pnpm release on the pinned major that has aged past the
|
|
132
|
-
// quarantine window.
|
|
133
|
-
function
|
|
155
|
+
// quarantine window.
|
|
156
|
+
function resolve_major_target(major: string): TargetResolution {
|
|
134
157
|
const times = query_release_times()
|
|
135
|
-
if (times === undefined) return undefined
|
|
158
|
+
if (times === undefined) return to_resolution(undefined, false)
|
|
136
159
|
|
|
137
|
-
// The project's own
|
|
138
|
-
// latest` reads `package.json` and writes the pnpm pin relative to the working
|
|
139
|
-
// has no subdirectory case — and honouring
|
|
140
|
-
// project that declares none (joshuafolkken/kit#808).
|
|
141
|
-
|
|
160
|
+
// The project's own `pnpm-workspace.yaml`, deliberately not the upward walk the version check
|
|
161
|
+
// uses: `josh latest` reads `package.json` and writes the pnpm pin relative to the working
|
|
162
|
+
// directory, so it has no subdirectory case — and honouring an ancestor's policy here could freeze
|
|
163
|
+
// pnpm bumps in a project that declares none (joshuafolkken/kit#808).
|
|
164
|
+
const version = release_age.select_aged_version(
|
|
142
165
|
times,
|
|
143
166
|
major,
|
|
144
|
-
release_age.read_minimum_release_age(
|
|
167
|
+
release_age.read_minimum_release_age(WORKSPACE_PATH),
|
|
145
168
|
Date.now(),
|
|
146
169
|
)
|
|
170
|
+
|
|
171
|
+
return to_resolution(version, true)
|
|
147
172
|
}
|
|
148
173
|
|
|
149
174
|
function parse_latest_version(stdout: string): string | undefined {
|
|
@@ -156,31 +181,47 @@ function parse_latest_version(stdout: string): string | undefined {
|
|
|
156
181
|
function query_latest_version(): string | undefined {
|
|
157
182
|
const result = execaSync('pnpm', ['view', 'pnpm', 'version', '--json'], {
|
|
158
183
|
reject: false,
|
|
159
|
-
timeout:
|
|
184
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
160
185
|
})
|
|
161
186
|
if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
|
|
162
187
|
|
|
163
188
|
return parse_latest_version(result.stdout)
|
|
164
189
|
}
|
|
165
190
|
|
|
166
|
-
// The
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
// The target handed to pnpm self-update: an exact registry-resolved version on the pinned major, or
|
|
192
|
+
// the latest exact version when no major can be read from package.json. The latest-version query
|
|
193
|
+
// has no quarantine selection, so resolving nothing there always means the registry did not answer.
|
|
194
|
+
function resolve_target(major: string | undefined): TargetResolution {
|
|
195
|
+
if (major !== undefined) return resolve_major_target(major)
|
|
196
|
+
const version = query_latest_version()
|
|
197
|
+
|
|
198
|
+
return to_resolution(version, version !== undefined)
|
|
199
|
+
}
|
|
172
200
|
|
|
173
|
-
|
|
201
|
+
function resolve_corepack_target(major: string | undefined): string | undefined {
|
|
202
|
+
return resolve_target(major).target
|
|
174
203
|
}
|
|
175
204
|
|
|
176
|
-
// Non-fatal skip message
|
|
177
|
-
// (latest:update, audit) running instead of aborting on a bump failure.
|
|
205
|
+
// Non-fatal skip message for a registry answer with nothing to adopt — nothing to bump, nothing failed.
|
|
178
206
|
function warn_skip(reason: string): void {
|
|
179
|
-
console.warn(`⚠ Skipped pnpm bump (${reason})
|
|
207
|
+
console.warn(`⚠ Skipped pnpm bump (${reason}).`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Every failure path ends here, so the exit status the `josh latest` chain stops on is one value.
|
|
211
|
+
function fail_bump(reason: string): number {
|
|
212
|
+
console.error(
|
|
213
|
+
`✖ pnpm bump failed (${reason}); the run is not recorded, so the next josh latest retries it.`,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
return FAILURE_EXIT_CODE
|
|
180
217
|
}
|
|
181
218
|
|
|
182
|
-
|
|
183
|
-
|
|
219
|
+
// Corepack exports COREPACK_ROOT to the package manager it launches, and pnpm 11+ refuses
|
|
220
|
+
// `self-update` under it (ERR_PNPM_CANT_SELF_UPDATE_IN_COREPACK), so the remedy is named there.
|
|
221
|
+
function fail_self_update(status: number, environment: NodeJS.ProcessEnv = process.env): number {
|
|
222
|
+
if (environment['COREPACK_ROOT'] !== undefined) console.error(COREPACK_REMEDY)
|
|
223
|
+
|
|
224
|
+
return fail_bump(`pnpm self-update exited ${String(status)}`)
|
|
184
225
|
}
|
|
185
226
|
|
|
186
227
|
function run_pnpm_update(target: string): number {
|
|
@@ -193,17 +234,6 @@ function run_pnpm_update(target: string): number {
|
|
|
193
234
|
return result.exitCode ?? FAILURE_EXIT_CODE
|
|
194
235
|
}
|
|
195
236
|
|
|
196
|
-
// The target is an already-resolved exact version, so a non-zero status here is a
|
|
197
|
-
// genuine pnpm or network failure; a later run retries. Returns whether a skip
|
|
198
|
-
// happened.
|
|
199
|
-
function did_warn_skip(status: number): boolean {
|
|
200
|
-
if (status === 0) return false
|
|
201
|
-
|
|
202
|
-
warn_skip(`pnpm self-update exited ${String(status)}`)
|
|
203
|
-
|
|
204
|
-
return true
|
|
205
|
-
}
|
|
206
|
-
|
|
207
237
|
// Realign `devEngines.packageManager.version` with the `packageManager` pin so the two
|
|
208
238
|
// fields keep matching (pnpm suppresses the dual-declaration warning only on an exact
|
|
209
239
|
// match). Runs on every path, not just after a successful bump: a repository that arrives
|
|
@@ -224,7 +254,7 @@ function restore_package_json(
|
|
|
224
254
|
package_json_path: string = PACKAGE_JSON_PATH,
|
|
225
255
|
): void {
|
|
226
256
|
writeFileSync(package_json_path, content)
|
|
227
|
-
console.info('✔ Restored package.json pin (pnpm bump
|
|
257
|
+
console.info('✔ Restored package.json pin (pnpm bump failed)')
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
function decode_integrity(encoded: string): string | undefined {
|
|
@@ -247,7 +277,7 @@ function extract_encoded_integrity(stdout: string): string | undefined {
|
|
|
247
277
|
function query_integrity(target: string): string | undefined {
|
|
248
278
|
const result = execaSync('pnpm', ['view', target, 'dist.integrity', '--json'], {
|
|
249
279
|
reject: false,
|
|
250
|
-
timeout:
|
|
280
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
251
281
|
})
|
|
252
282
|
if ((result.exitCode ?? FAILURE_EXIT_CODE) !== 0) return undefined
|
|
253
283
|
|
|
@@ -292,35 +322,19 @@ function notify_skipped_bump(target: string, pinned_version: string): void {
|
|
|
292
322
|
warn_skip(`registry answered ${target}, below the pinned ${pinned_version}`)
|
|
293
323
|
}
|
|
294
324
|
|
|
295
|
-
|
|
296
|
-
// skipped and the reason has already been logged.
|
|
297
|
-
function resolve_floored_target(original: string, major: string | undefined): string | undefined {
|
|
298
|
-
const target = resolve_corepack_target(major)
|
|
299
|
-
|
|
300
|
-
if (target === undefined) {
|
|
301
|
-
warn_unresolved(major)
|
|
302
|
-
|
|
303
|
-
return undefined
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const pinned_version = extract_pinned_version(original)
|
|
307
|
-
if (!is_target_not_newer_than_pin(target, pinned_version)) return target
|
|
308
|
-
|
|
309
|
-
notify_skipped_bump(target, pinned_version ?? '')
|
|
310
|
-
|
|
311
|
-
return undefined
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function restore_after_update(original: string, target: string, integrity: string): void {
|
|
325
|
+
function restore_after_update(original: string, target: string, integrity: string): number {
|
|
315
326
|
try {
|
|
316
327
|
restore_integrity(target, integrity)
|
|
328
|
+
|
|
329
|
+
return 0
|
|
317
330
|
} catch {
|
|
318
331
|
restore_package_json(original)
|
|
319
|
-
|
|
332
|
+
|
|
333
|
+
return fail_bump('pnpm self-update wrote an unexpected pin')
|
|
320
334
|
}
|
|
321
335
|
}
|
|
322
336
|
|
|
323
|
-
function pin_unpinned_manifest(original: string, target: string, integrity: string):
|
|
337
|
+
function pin_unpinned_manifest(original: string, target: string, integrity: string): number {
|
|
324
338
|
const manifest = z.record(z.string(), z.unknown()).parse(json_value.parse_or_undefined(original))
|
|
325
339
|
const pinned = JSON.stringify(
|
|
326
340
|
{ ...manifest, packageManager: `${target}+${integrity}` },
|
|
@@ -334,53 +348,71 @@ function pin_unpinned_manifest(original: string, target: string, integrity: stri
|
|
|
334
348
|
)
|
|
335
349
|
|
|
336
350
|
const result = execaSync('pnpm', ['--version'], { reject: false })
|
|
337
|
-
if (result.exitCode === 0 && result.stdout.trim() === target.slice(TARGET_PREFIX.length)) return
|
|
351
|
+
if (result.exitCode === 0 && result.stdout.trim() === target.slice(TARGET_PREFIX.length)) return 0
|
|
338
352
|
|
|
339
353
|
restore_package_json(original)
|
|
340
|
-
warn_skip('the newly pinned pnpm version could not start')
|
|
341
|
-
}
|
|
342
354
|
|
|
343
|
-
|
|
344
|
-
|
|
355
|
+
return fail_bump('the newly pinned pnpm version could not start')
|
|
356
|
+
}
|
|
345
357
|
|
|
346
|
-
|
|
347
|
-
|
|
358
|
+
// The target is an already-resolved exact version, so a non-zero status here is a genuine
|
|
359
|
+
// pnpm, Corepack or network failure.
|
|
360
|
+
function update_pinned_manifest(original: string, target: string, integrity: string): number {
|
|
361
|
+
const status = run_pnpm_update(target)
|
|
362
|
+
if (status === 0) return restore_after_update(original, target, integrity)
|
|
348
363
|
|
|
349
|
-
|
|
350
|
-
}
|
|
364
|
+
restore_package_json(original)
|
|
351
365
|
|
|
352
|
-
|
|
366
|
+
return fail_self_update(status)
|
|
353
367
|
}
|
|
354
368
|
|
|
355
369
|
// Query integrity before mutating the manifest; self-update writes both pins without it.
|
|
356
|
-
function bump_package_manager(original: string, target: string):
|
|
370
|
+
function bump_package_manager(original: string, target: string): number {
|
|
357
371
|
const integrity = query_integrity(target)
|
|
372
|
+
if (integrity === undefined) return fail_bump(`no integrity for ${target}`)
|
|
373
|
+
if (!PACKAGE_MANAGER_RE.test(original)) return pin_unpinned_manifest(original, target, integrity)
|
|
358
374
|
|
|
359
|
-
|
|
360
|
-
|
|
375
|
+
return update_pinned_manifest(original, target, integrity)
|
|
376
|
+
}
|
|
361
377
|
|
|
362
|
-
|
|
363
|
-
|
|
378
|
+
// An unreachable registry fails, so the unrecorded run is retried. A reachable one with nothing
|
|
379
|
+
// aged past the quarantine window is the kit#768 skip — like a not-newer answer, there is nothing
|
|
380
|
+
// to adopt yet, and failing would stall every run for up to the whole window.
|
|
381
|
+
function report_unresolved(major: string | undefined, is_registry_reachable: boolean): number {
|
|
382
|
+
const label = `pnpm ${major ?? ''}`.trimEnd()
|
|
383
|
+
if (!is_registry_reachable) return fail_bump(`the registry did not answer for ${label}`)
|
|
364
384
|
|
|
365
|
-
|
|
366
|
-
pin_unpinned_manifest(original, target, integrity)
|
|
385
|
+
warn_skip(`no ${label} release has aged past the quarantine window yet`)
|
|
367
386
|
|
|
368
|
-
|
|
387
|
+
return 0
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Resolve the pnpm target with the pin floor applied, then bump.
|
|
391
|
+
function bump_to_registry_target(original: string, major: string | undefined): number {
|
|
392
|
+
const { target, is_registry_reachable } = resolve_target(major)
|
|
393
|
+
if (target === undefined) return report_unresolved(major, is_registry_reachable)
|
|
394
|
+
|
|
395
|
+
const pinned_version = extract_pinned_version(original)
|
|
396
|
+
|
|
397
|
+
if (!is_target_not_newer_than_pin(target, pinned_version)) {
|
|
398
|
+
return bump_package_manager(original, target)
|
|
369
399
|
}
|
|
370
400
|
|
|
371
|
-
|
|
401
|
+
notify_skipped_bump(target, pinned_version ?? '')
|
|
402
|
+
|
|
403
|
+
return 0
|
|
372
404
|
}
|
|
373
405
|
|
|
374
|
-
function main():
|
|
406
|
+
function main(): number {
|
|
375
407
|
const original = readFileSync(PACKAGE_JSON_PATH, 'utf8')
|
|
376
|
-
const
|
|
377
|
-
const target = resolve_floored_target(original, major)
|
|
378
|
-
if (target !== undefined) bump_package_manager(original, target)
|
|
408
|
+
const status = bump_to_registry_target(original, extract_pnpm_major(original))
|
|
379
409
|
|
|
380
410
|
sync_development_engines()
|
|
411
|
+
|
|
412
|
+
return status
|
|
381
413
|
}
|
|
382
414
|
|
|
383
|
-
if (process.argv[1] === fileURLToPath(import.meta.url)) main()
|
|
415
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) process.exitCode = main()
|
|
384
416
|
|
|
385
417
|
const latest_corepack = {
|
|
386
418
|
extract_pnpm_major,
|
|
@@ -388,12 +420,11 @@ const latest_corepack = {
|
|
|
388
420
|
is_target_not_newer_than_pin,
|
|
389
421
|
notify_skipped_bump,
|
|
390
422
|
extract_times_json,
|
|
391
|
-
query_major_latest_version,
|
|
392
423
|
resolve_corepack_target,
|
|
393
424
|
run_pnpm_update,
|
|
394
425
|
query_integrity,
|
|
395
426
|
restore_integrity,
|
|
396
|
-
|
|
427
|
+
fail_self_update,
|
|
397
428
|
sync_development_engines,
|
|
398
429
|
restore_package_json,
|
|
399
430
|
main,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { package_with_deps_schema } from '#scripts/overrides/schemas'
|
|
1
|
+
import { package_with_deps_schema } from '#scripts/overrides/overrides-schemas'
|
|
2
2
|
import semver from 'semver'
|
|
3
3
|
|
|
4
4
|
// `pnpm update --latest` sets each range to whatever the registry reports as newest. When a
|
|
@@ -13,7 +13,7 @@ import { overrides_check } from '#scripts/overrides/overrides-logic'
|
|
|
13
13
|
import { execaSync } from 'execa'
|
|
14
14
|
import { fresh_metadata_cache } from './fresh-metadata-cache'
|
|
15
15
|
import { latest_regression, type VersionRegression } from './latest-regression'
|
|
16
|
-
import {
|
|
16
|
+
import { safe_chain_version_update } from './safe-chain-version-update'
|
|
17
17
|
|
|
18
18
|
const PACKAGE_JSON_PATH = 'package.json'
|
|
19
19
|
const LOCKFILE_PATH = 'pnpm-lock.yaml'
|
|
@@ -175,11 +175,11 @@ function main(): void {
|
|
|
175
175
|
report_overrides(overrides)
|
|
176
176
|
report_lockfile_overrides()
|
|
177
177
|
|
|
178
|
-
// Skipped after a rollback so the tree really is left exactly as it was found — this sync
|
|
179
|
-
//
|
|
178
|
+
// Skipped after a rollback so the tree really is left exactly as it was found — this sync rewrites
|
|
179
|
+
// the workflows' pinned safe-chain installer, which would contradict the notice that
|
|
180
180
|
// nothing changed.
|
|
181
181
|
if (outcome.status === 0 && !outcome.is_rolled_back) {
|
|
182
|
-
|
|
182
|
+
safe_chain_version_update.sync()
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { json_value } from '#scripts/lib/json-value'
|
|
2
|
+
import { FETCH_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
2
3
|
import { migrate_logic } from '#scripts/registry-migration/migrate-logic'
|
|
3
4
|
import { execaSync } from 'execa'
|
|
5
|
+
import semver from 'semver'
|
|
4
6
|
import { z } from 'zod'
|
|
5
7
|
import { release_age } from './release-age'
|
|
6
8
|
|
|
@@ -12,7 +14,6 @@ const PUBLIC_REGISTRY = 'https://registry.npmjs.org'
|
|
|
12
14
|
// The abbreviated packument carries `dist-tags` without every version's full manifest.
|
|
13
15
|
const ABBREVIATED_ACCEPT = 'application/vnd.npm.install-v1+json'
|
|
14
16
|
const FULL_ACCEPT = 'application/json'
|
|
15
|
-
const REQUEST_TIMEOUT_MS = 10_000
|
|
16
17
|
const SCOPE_SEPARATOR = '/'
|
|
17
18
|
const ENCODED_SCOPE_SEPARATOR = '%2f'
|
|
18
19
|
const SCOPE_PATTERN = /^@[^/]+(?=\/)/u
|
|
@@ -29,7 +30,10 @@ const FETCH_SCRIPT = [
|
|
|
29
30
|
'process.stdout.write(await response.text())',
|
|
30
31
|
].join('\n')
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
// A registry-supplied version is used only when it is exactly a semver version — it ends up inside an
|
|
34
|
+
// install command, so `1.0.0;curl…|sh` or any other text is refused at the source, not downstream.
|
|
35
|
+
const version_schema = z.string().refine((value) => semver.valid(value) === value)
|
|
36
|
+
const latest_schema = z.looseObject({ 'dist-tags': z.looseObject({ latest: version_schema }) })
|
|
33
37
|
const times_schema = z.looseObject({ time: release_age.release_times_schema })
|
|
34
38
|
|
|
35
39
|
// The packument URL for a package name; a scoped name keeps its `@` and encodes the separator, the
|
|
@@ -42,7 +46,7 @@ function read_packument(package_name: string, accept: string): unknown {
|
|
|
42
46
|
const result = execaSync(
|
|
43
47
|
process.execPath,
|
|
44
48
|
['--input-type=module', '-e', FETCH_SCRIPT, packument_url(package_name), accept],
|
|
45
|
-
{ reject: false, timeout:
|
|
49
|
+
{ reject: false, timeout: FETCH_TIMEOUT_MS },
|
|
46
50
|
)
|
|
47
51
|
if (result.exitCode !== 0) return undefined
|
|
48
52
|
|
|
@@ -59,7 +63,7 @@ function is_github_scope(scope: string): boolean {
|
|
|
59
63
|
if (is_cached_github !== undefined) return is_cached_github
|
|
60
64
|
const result = execaSync('pnpm', ['config', 'get', `${scope}${SCOPE_REGISTRY_SUFFIX}`], {
|
|
61
65
|
reject: false,
|
|
62
|
-
timeout:
|
|
66
|
+
timeout: FETCH_TIMEOUT_MS,
|
|
63
67
|
})
|
|
64
68
|
const is_github = result.exitCode === 0 && migrate_logic.is_github_tarball(result.stdout.trim())
|
|
65
69
|
|
|
@@ -102,6 +106,17 @@ function has_public_version(package_name: string, version: string): boolean {
|
|
|
102
106
|
return parsed.success && parsed.data.time[version] !== undefined
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
// The version itself, or nothing when it is not exactly a semver version.
|
|
110
|
+
function valid_version(value: string): string | undefined {
|
|
111
|
+
return version_schema.safeParse(value).success ? value : undefined
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const npm_registry = {
|
|
115
|
+
has_public_version,
|
|
116
|
+
packument_url,
|
|
117
|
+
read_latest,
|
|
118
|
+
read_release_times,
|
|
119
|
+
valid_version,
|
|
120
|
+
}
|
|
106
121
|
|
|
107
122
|
export { npm_registry }
|