@joshuafolkken/kit 1.1178.0 → 1.1229.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -2
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -2
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +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 +5 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +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 +48 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +26 -19
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-55HHG5PX.js} +1210 -711
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-5VMKCCC4.js} +25 -10
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-6HJJXRBX.js} +4 -2
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-F56MFS4B.js} +3 -3
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-FIVDHQGT.js} +7 -6
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-JUDKDCDP.js} +496 -378
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-NV2JRZZO.js} +718 -681
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-RNWHN5BP.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-TQNPDZNH.js} +4 -4
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-VC3PZPZO.js} +1 -1
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +217 -195
- 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 -58
- package/dist/josh.js +868 -74
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +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-budget.ts +69 -1
- package/scripts/gate/gate-plan.ts +47 -7
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +27 -8
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-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 +5 -15
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +1 -1
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +45 -18
- package/scripts/issue/issue-file.ts +24 -1
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +11 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +10 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-lane.ts +22 -0
- package/scripts/josh/josh-commands-maintenance.ts +4 -4
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +33 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +3 -3
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +1 -1
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +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} +2 -2
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +16 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +2 -2
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +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} +23 -8
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +3 -3
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +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} +8 -2
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +1 -1
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/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 +17 -17
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +2 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/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-render.ts → event/run-event-render.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-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,139 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { git_spawn } from './git-spawn'
|
|
5
|
+
|
|
6
|
+
// The pre-push hook, run ahead of the push instead of inside it (joshuafolkken/kit#3300).
|
|
7
|
+
//
|
|
8
|
+
// **The push budget is a transfer budget, and a hook is not a transfer.** `PUSH_TIMEOUT_MS` was read
|
|
9
|
+
// off what moving objects costs here, but `git push` runs the pre-push hook first and the budget
|
|
10
|
+
// counted it: a hook that ran the whole unit suite — any push the gate's record does not cover, such
|
|
11
|
+
// as the observation ledger commit or a tree `ship` has just synced with main — took 130 to 196
|
|
12
|
+
// seconds and was killed at 120 on a push that was healthy. Killing `git` did not kill the hook
|
|
13
|
+
// either: lefthook and vitest outlived it, and the retry started a second suite in the same work tree,
|
|
14
|
+
// where each `test-state-guard` read the other's temporary files as a mutation and both failed.
|
|
15
|
+
//
|
|
16
|
+
// So the hook runs here, once, with no budget, and `git_command.push` spawns the bounded transfer that
|
|
17
|
+
// follows with `--no-verify`. The retry then repeats the transfer alone, and a hook that is still
|
|
18
|
+
// running cannot exist for it to overlap.
|
|
19
|
+
//
|
|
20
|
+
// **What git hands the hook is reproduced rather than dropped.** `git hook run` executes whatever
|
|
21
|
+
// hook is configured — lefthook's or anyone's — and the arguments and stdin are git's pre-push
|
|
22
|
+
// contract: the remote's name and URL, and one `<local ref> <local oid> <remote ref> <remote oid>`
|
|
23
|
+
// line per ref. A hook that reads them (`git lfs pre-push` uploads exactly the objects those lines
|
|
24
|
+
// name) would otherwise act on nothing. Both pushes `git_command.push` makes send the current branch to
|
|
25
|
+
// the same name on `origin`, so that is the one line written. Like git, an up-to-date branch runs no
|
|
26
|
+
// hook: the remote-tracking ref already naming `HEAD` is git's "Everything up-to-date".
|
|
27
|
+
|
|
28
|
+
const REMOTE = 'origin'
|
|
29
|
+
const HOOK_NAME = 'pre-push'
|
|
30
|
+
const PROBE_HOOK_NAME = 'kit-hook-run-probe'
|
|
31
|
+
const ALLOW_UNKNOWN_HOOK_NAME_FLAG = '--allow-unknown-hook-name'
|
|
32
|
+
const HOOK_RUN = 'run'
|
|
33
|
+
const STDIN_FILE = 'refs'
|
|
34
|
+
const TEMPORARY_PREFIX = 'kit-pre-push-'
|
|
35
|
+
const ZERO_DIGIT = '0'
|
|
36
|
+
|
|
37
|
+
interface PushReferences {
|
|
38
|
+
branch: string
|
|
39
|
+
local_oid: string
|
|
40
|
+
remote_oid: string | undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// `undefined` is a branch the remote has never seen, which git reports with an all-zero object id.
|
|
44
|
+
async function read_remote_oid(branch: string): Promise<string | undefined> {
|
|
45
|
+
try {
|
|
46
|
+
return await git_spawn.read([
|
|
47
|
+
'rev-parse',
|
|
48
|
+
'--verify',
|
|
49
|
+
'--quiet',
|
|
50
|
+
`refs/remotes/${REMOTE}/${branch}`,
|
|
51
|
+
])
|
|
52
|
+
} catch {
|
|
53
|
+
return undefined
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function read_push_references(): Promise<PushReferences> {
|
|
58
|
+
const [branch, local_oid] = await Promise.all([
|
|
59
|
+
git_spawn.read(['rev-parse', '--abbrev-ref', 'HEAD']),
|
|
60
|
+
git_spawn.read(['rev-parse', 'HEAD']),
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
return { branch, local_oid, remote_oid: await read_remote_oid(branch) }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The zero id takes the local id's length, so a SHA-256 repository gets 64 digits rather than 40.
|
|
67
|
+
function to_stdin_line(references: PushReferences): string {
|
|
68
|
+
const reference = `refs/heads/${references.branch}`
|
|
69
|
+
const remote_oid = references.remote_oid ?? ZERO_DIGIT.repeat(references.local_oid.length)
|
|
70
|
+
|
|
71
|
+
return `${reference} ${references.local_oid} ${reference} ${remote_oid}\n`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function to_hook_run_arguments(stdin_path: string, hook_name: string): Array<string> {
|
|
75
|
+
return [HOOK_RUN, '--ignore-missing', `--to-stdin=${stdin_path}`, hook_name]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// **A git that cannot run the hook this way leaves it to the push** — `git hook run` is Git 2.36, and
|
|
79
|
+
// `--to-stdin` later still; an older git exits 129 on either. It is asked with a hook name nothing
|
|
80
|
+
// configures, which `--ignore-missing` turns into a clean exit on any git that understands the call,
|
|
81
|
+
// so the answer never depends on what the real hook does. Git 2.54 refuses a name that is not a
|
|
82
|
+
// native hook unless `--allow-unknown-hook-name` says otherwise, and a git before it refuses that flag,
|
|
83
|
+
// so the probe is asked both ways.
|
|
84
|
+
async function can_probe(probe_arguments: ReadonlyArray<string>): Promise<boolean> {
|
|
85
|
+
try {
|
|
86
|
+
await git_spawn.read(['hook', ...probe_arguments])
|
|
87
|
+
|
|
88
|
+
return true
|
|
89
|
+
} catch {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function can_run_hooks(stdin_path: string): Promise<boolean> {
|
|
95
|
+
const [, ...options] = to_hook_run_arguments(stdin_path, PROBE_HOOK_NAME)
|
|
96
|
+
|
|
97
|
+
if (await can_probe([HOOK_RUN, ALLOW_UNKNOWN_HOOK_NAME_FLAG, ...options])) return true
|
|
98
|
+
|
|
99
|
+
return await can_probe([HOOK_RUN, ...options])
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function run_with_stdin(stdin_line: string): Promise<boolean> {
|
|
103
|
+
const directory = await mkdtemp(path.join(tmpdir(), TEMPORARY_PREFIX))
|
|
104
|
+
const stdin_path = path.join(directory, STDIN_FILE)
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await writeFile(stdin_path, stdin_line)
|
|
108
|
+
if (!(await can_run_hooks(stdin_path))) return false
|
|
109
|
+
const url = await git_spawn.read(['remote', 'get-url', REMOTE])
|
|
110
|
+
|
|
111
|
+
await git_spawn.with_output('hook', [
|
|
112
|
+
...to_hook_run_arguments(stdin_path, HOOK_NAME),
|
|
113
|
+
'--',
|
|
114
|
+
REMOTE,
|
|
115
|
+
url,
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
return true
|
|
119
|
+
} finally {
|
|
120
|
+
await rm(directory, { force: true, recursive: true })
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// `true` when the hook is settled here — run, or nothing to push — so the push skips it; `false` when
|
|
125
|
+
// this git cannot run it, so the push runs it as it always did. A hook that fails throws
|
|
126
|
+
// `git hook exited with code <n>`, and the push is never attempted.
|
|
127
|
+
async function run(): Promise<boolean> {
|
|
128
|
+
const references = await read_push_references()
|
|
129
|
+
|
|
130
|
+
if (references.local_oid === references.remote_oid) return true
|
|
131
|
+
|
|
132
|
+
return await run_with_stdin(to_stdin_line(references))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const git_pre_push_hook = {
|
|
136
|
+
run,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { git_pre_push_hook }
|
|
@@ -62,9 +62,7 @@ function create_prompt(): Interface | undefined {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function handle_prompt_fallback<T>(fallback_value?: T): T {
|
|
65
|
-
if (fallback_value !== undefined)
|
|
66
|
-
return fallback_value
|
|
67
|
-
}
|
|
65
|
+
if (fallback_value !== undefined) return fallback_value
|
|
68
66
|
|
|
69
67
|
throw new Error('TTY not available')
|
|
70
68
|
}
|
|
@@ -72,9 +70,7 @@ function handle_prompt_fallback<T>(fallback_value?: T): T {
|
|
|
72
70
|
async function with_prompt<T>(callback: PromptCallback<T>, fallback_value?: T): Promise<T> {
|
|
73
71
|
const prompt = create_prompt()
|
|
74
72
|
|
|
75
|
-
if (prompt === undefined)
|
|
76
|
-
return handle_prompt_fallback(fallback_value)
|
|
77
|
-
}
|
|
73
|
+
if (prompt === undefined) return handle_prompt_fallback(fallback_value)
|
|
78
74
|
|
|
79
75
|
try {
|
|
80
76
|
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { read_spawn_stderr, read_spawn_stdout } from '#scripts/lib/spawn-exit'
|
|
2
|
+
import { GIT_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
2
3
|
import { execaSync } from 'execa'
|
|
3
4
|
import { git_utilities } from './constants'
|
|
4
5
|
|
|
@@ -7,7 +8,6 @@ import { git_utilities } from './constants'
|
|
|
7
8
|
// non-zero exit is returned as a result, and every call is bounded: a git that never answers is an
|
|
8
9
|
// unknown the caller already handles, never a hang.
|
|
9
10
|
|
|
10
|
-
const GIT_TIMEOUT_MS = 10_000
|
|
11
11
|
const SUCCESS_EXIT_CODE = 0
|
|
12
12
|
// git translates its messages, so a caller matching stderr would fail under any non-English locale.
|
|
13
13
|
const C_LOCALE: Record<string, string> = { LC_ALL: 'C', LANGUAGE: 'C' }
|
|
@@ -16,6 +16,8 @@ interface GitSyncOptions {
|
|
|
16
16
|
cwd?: string
|
|
17
17
|
timeout?: number
|
|
18
18
|
env?: Record<string, string | undefined>
|
|
19
|
+
// A step a person watches (`josh start`) shows git's own progress and hook output as it runs.
|
|
20
|
+
stdio?: 'inherit'
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
interface GitSyncResult {
|
package/scripts/git/git-spawn.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { git_ssh_keepalive } from './git-ssh-keepalive'
|
|
|
20
20
|
// spawns git itself. The timeout it carries is shared here by `read_remote` / `with_output_remote`. Those two files are the spawn sites of the ordinary command
|
|
21
21
|
// modules — the claim stops there. A synchronous caller goes through `git-spawn-sync.ts`, the same
|
|
22
22
|
// binary resolution with a result instead of an exception (joshuafolkken/kit#3065). Other parts of
|
|
23
|
-
// this package spawn git for their own purposes (`scripts/run/run-progress-clock.ts`,
|
|
23
|
+
// this package spawn git for their own purposes (`scripts/run/progress/run-progress-clock.ts`,
|
|
24
24
|
// `scripts/git/git-fixture-workspace.ts`), and an audit of how the git binary is resolved has to read
|
|
25
25
|
// those too.
|
|
26
26
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
LEDGER_DIRECTORIES,
|
|
2
3
|
LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
3
4
|
observation_ledger,
|
|
4
|
-
OBSERVATION_LEDGER_DIRECTORY,
|
|
5
5
|
} from '#scripts/observations/observation-ledger'
|
|
6
6
|
import { observation_ledger_prepare } from '#scripts/observations/observation-ledger-prepare'
|
|
7
7
|
import { git_command } from './git-command'
|
|
@@ -29,9 +29,7 @@ async function confirm_package_json_staged(should_force = false): Promise<boolea
|
|
|
29
29
|
async function confirm_package_json_version(should_force = false): Promise<void> {
|
|
30
30
|
const is_version_updated = await git_status.check_package_json_version()
|
|
31
31
|
|
|
32
|
-
if (is_version_updated)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
32
|
+
if (is_version_updated) return
|
|
35
33
|
|
|
36
34
|
if (should_force) {
|
|
37
35
|
console.info('💡 Skipping package.json version check (force).')
|
|
@@ -83,7 +81,7 @@ async function stage_untracked_files(files: ReadonlyArray<string>): Promise<void
|
|
|
83
81
|
// own lines** (joshuafolkken/kit#2919): its writers append to its own tree's issue file, so this is
|
|
84
82
|
// the step that takes them to the default branch.
|
|
85
83
|
const LEDGER_PATHSPECS: ReadonlyArray<string> = [
|
|
86
|
-
|
|
84
|
+
...LEDGER_DIRECTORIES,
|
|
87
85
|
...LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
88
86
|
]
|
|
89
87
|
|
|
@@ -108,7 +106,7 @@ function is_stageable(file_path: string, excluded: ReadonlyArray<string>): boole
|
|
|
108
106
|
// step dies on git's empty index with `Failed to commit changes` — a message naming nothing that
|
|
109
107
|
// caused it. This line is what turns that into a diagnosis.
|
|
110
108
|
//
|
|
111
|
-
// **The paths are parsed rather than matched as substrings**:
|
|
109
|
+
// **The paths are parsed rather than matched as substrings**: `.josh/observations-old/x.md`
|
|
112
110
|
// contains the ledger's path, and a hint naming a file the exclusion never touched is a hint that
|
|
113
111
|
// teaches the reader to ignore it.
|
|
114
112
|
function report_excluded_paths(status_output: string, excluded: ReadonlyArray<string>): void {
|
|
@@ -31,9 +31,7 @@ function has_unstaged_files(status_output: string): boolean {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function is_staged_file(line: string): boolean {
|
|
34
|
-
if (line.length < REQUIRED_STATUS_LENGTH || is_untracked_file(line))
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
34
|
+
if (line.length < REQUIRED_STATUS_LENGTH || is_untracked_file(line)) return false
|
|
37
35
|
|
|
38
36
|
const [staged_status] = line
|
|
39
37
|
|
|
@@ -47,9 +45,7 @@ function has_unstaged_changes(line: string): boolean {
|
|
|
47
45
|
function has_all_files_staged(status_output: string): boolean {
|
|
48
46
|
const lines = parse_status_lines(status_output)
|
|
49
47
|
|
|
50
|
-
if (lines.length === 0)
|
|
51
|
-
return true
|
|
52
|
-
}
|
|
48
|
+
if (lines.length === 0) return true
|
|
53
49
|
|
|
54
50
|
return lines.every((line) => !has_unstaged_changes(line))
|
|
55
51
|
}
|
|
@@ -62,9 +58,7 @@ function is_package_json_staged(status_output: string): boolean {
|
|
|
62
58
|
const lines = parse_status_lines(status_output)
|
|
63
59
|
|
|
64
60
|
return lines.some((line) => {
|
|
65
|
-
if (!is_staged_file(line))
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
61
|
+
if (!is_staged_file(line)) return false
|
|
68
62
|
|
|
69
63
|
const filename = extract_filename(line)
|
|
70
64
|
|
|
@@ -49,7 +49,7 @@ function paths_matching(command: string, pattern: RegExp): Array<string> {
|
|
|
49
49
|
return [...command.matchAll(pattern)].flatMap((match) => {
|
|
50
50
|
const file_path = match[1]?.trim()
|
|
51
51
|
|
|
52
|
-
return file_path === undefined || file_path === '' ? [] :
|
|
52
|
+
return file_path === undefined || file_path === '' ? [] : file_path
|
|
53
53
|
})
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { HOOK_PROCESS_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
3
4
|
import { execa } from 'execa'
|
|
4
5
|
|
|
5
6
|
// The spell half of the edit hook (joshuafolkken/kit#2296). joshuafolkken/kit#2275 gave the hook a way
|
|
@@ -47,9 +48,6 @@ const CSPELL_ARGS: ReadonlyArray<string> = [
|
|
|
47
48
|
'--no-summary',
|
|
48
49
|
]
|
|
49
50
|
const PNPM = 'pnpm'
|
|
50
|
-
// A spell check that hangs would hold the edit's turn open; the bound is generous because reaching it
|
|
51
|
-
// means something is already wrong, exactly as the formatter spawns in `format-edited-file.ts`.
|
|
52
|
-
const PROCESS_TIMEOUT_MS = 15_000
|
|
53
51
|
|
|
54
52
|
const HEADER =
|
|
55
53
|
'The edit hook found words cspell does not know; fix them or add them to `cspell.config.yaml` now rather than at the gate:'
|
|
@@ -79,7 +77,7 @@ async function run_cspell(file_path: string, project_root: string): Promise<stri
|
|
|
79
77
|
cwd: project_root,
|
|
80
78
|
stdout: 'pipe',
|
|
81
79
|
stderr: 'ignore',
|
|
82
|
-
timeout:
|
|
80
|
+
timeout: HOOK_PROCESS_TIMEOUT_MS,
|
|
83
81
|
})
|
|
84
82
|
|
|
85
83
|
return result.stdout
|
|
@@ -4,6 +4,7 @@ import path from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { resolve_local_bin, resolve_package_bin } from '#scripts/build/local-bin'
|
|
6
6
|
import { ESLINT_EDIT_CACHE_FLAGS } from '#scripts/josh/josh-command-types'
|
|
7
|
+
import { HOOK_PROCESS_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
7
8
|
import { time_density_hook } from '#scripts/time-runtime/time-density-hook'
|
|
8
9
|
import { execa } from 'execa'
|
|
9
10
|
import { z } from 'zod'
|
|
@@ -87,15 +88,6 @@ const DAEMON_RESTART = 'restart'
|
|
|
87
88
|
// `eslint.config.*` entry, and the rule modules under `eslint/` that it imports.
|
|
88
89
|
const ESLINT_CONFIG_DIR = 'eslint'
|
|
89
90
|
const ESLINT_CONFIG_ENTRY = /^eslint\.config\.[cm]?[jt]s$/u
|
|
90
|
-
// A formatter that hangs would hold the edit's turn open, so each spawn is bounded. The bound has
|
|
91
|
-
// to be read against the *worst-case run* rather than one spawn: an edit to a config input plans
|
|
92
|
-
// eslint, prettier and `eslint_d restart`, and the first and last each have a second route behind
|
|
93
|
-
// the daemon — five spawns. The hook entry in `.claude/settings.json` declares 90 seconds, so the
|
|
94
|
-
// per-spawn bound must leave five of them under it, or the harness kill lands first and lands at a
|
|
95
|
-
// moment this file did not choose — possibly inside `prettier --write`, which rewrites in place and
|
|
96
|
-
// can leave the file truncated. 15s × 5 = 75s. It is still two orders of magnitude beyond the 0.84s
|
|
97
|
-
// a warm run takes, so reaching it means something is already wrong.
|
|
98
|
-
const PROCESS_TIMEOUT_MS = 15_000
|
|
99
91
|
|
|
100
92
|
// The problems eslint could not auto-fix are handed to the model verbatim from eslint's own stdout —
|
|
101
93
|
// its default formatter already prints the file, the `line:col`, and the rule name the issue asks
|
|
@@ -315,7 +307,7 @@ async function spawn_invocation(
|
|
|
315
307
|
env: { ESLINT_D_MISS: 'fail' },
|
|
316
308
|
stdout: 'pipe',
|
|
317
309
|
stderr: 'ignore',
|
|
318
|
-
timeout:
|
|
310
|
+
timeout: HOOK_PROCESS_TIMEOUT_MS,
|
|
319
311
|
})
|
|
320
312
|
|
|
321
313
|
return { exit_code: result.exitCode ?? 1, stdout: result.stdout }
|
|
@@ -469,7 +461,7 @@ function report_no_payload(): void {
|
|
|
469
461
|
// have (joshuafolkken/kit#2314). The rewrite, lint and spell blocks are composed together first, then
|
|
470
462
|
// joined to the density line — one envelope, since a `PostToolUse` hook's stdout carries only one
|
|
471
463
|
// (joshuafolkken/kit#2296). A hook killed at its 15s timeout is the one case this loses the density
|
|
472
|
-
// line to — a run already gone pathologically wrong, per
|
|
464
|
+
// line to — a run already gone pathologically wrong, per HOOK_PROCESS_TIMEOUT_MS.
|
|
473
465
|
async function run_hook(payload: string): Promise<void> {
|
|
474
466
|
const notice = time_density_hook.density_notice(payload)
|
|
475
467
|
const file_path = parse_edited_path(payload)
|
|
@@ -500,6 +492,5 @@ export {
|
|
|
500
492
|
select_invocation,
|
|
501
493
|
ESLINT_DAEMON,
|
|
502
494
|
MAX_DIAGNOSTIC_CHARS,
|
|
503
|
-
PROCESS_TIMEOUT_MS,
|
|
504
495
|
}
|
|
505
496
|
export type { CommandRunner, FormatCommand }
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import { gate_skip } from '#scripts/gate/gate-skip'
|
|
3
|
+
import { gate_skip, type GateReuse } from '#scripts/gate/gate-skip'
|
|
4
4
|
import { gate_tree, type GateTree } from '#scripts/gate/gate-tree'
|
|
5
5
|
import { hook_gate_reuse } from '#scripts/gate/hook-gate-reuse'
|
|
6
|
-
import type { FileMapStamp } from '#scripts/josh/file-map-stamp'
|
|
7
6
|
import { test_unit_guard } from '#scripts/test/test-unit-guard'
|
|
8
7
|
|
|
9
8
|
// The pre-push hook's unit run, which no longer re-runs a suite a green gate already covers
|
|
@@ -42,6 +41,15 @@ const FORCE_ENV = 'JOSH_PRE_PUSH_FORCE'
|
|
|
42
41
|
// `gate-tree.ts` rather than read a second way here — plus the one this hook adds.
|
|
43
42
|
interface PushTree extends GateTree {
|
|
44
43
|
is_clean: boolean
|
|
44
|
+
// What a dirty tree reports as the reason the record was not reused (joshuafolkken/kit#3307) — the
|
|
45
|
+
// paths name a lock file a hook's own `pnpm install` rewrote, the case that went unexplained.
|
|
46
|
+
carry_miss: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function carry_miss(lines: ReadonlyArray<string> | undefined): string {
|
|
50
|
+
if (lines === undefined) return 'git status could not be read'
|
|
51
|
+
|
|
52
|
+
return `the working tree differs from HEAD (${lines.join('; ')})`
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
// `git status --porcelain` prints one line per difference from HEAD, untracked files included — so an
|
|
@@ -57,7 +65,11 @@ async function read_push_tree(): Promise<PushTree> {
|
|
|
57
65
|
hook_gate_reuse.read_status_lines(),
|
|
58
66
|
])
|
|
59
67
|
|
|
60
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
...tree,
|
|
70
|
+
is_clean: hook_gate_reuse.is_worktree_clean(lines),
|
|
71
|
+
carry_miss: carry_miss(lines),
|
|
72
|
+
}
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
// `--force` is the gate's own flag, reused rather than respelled: it and `JOSH_PRE_PUSH_FORCE` are
|
|
@@ -74,14 +86,15 @@ function forwarded_arguments(extra_arguments: ReadonlyArray<string>): ReadonlyAr
|
|
|
74
86
|
// **Any argument at all refuses the reuse**, not only `--force`. Everything else is forwarded to
|
|
75
87
|
// vitest, and a caller who narrowed the run to one spec asked for that run rather than for a
|
|
76
88
|
// recorded result about a whole tree. The hook's own line passes none, so this costs it nothing.
|
|
77
|
-
function
|
|
89
|
+
function push_reuse(
|
|
78
90
|
tree: PushTree,
|
|
79
91
|
extra_arguments: ReadonlyArray<string>,
|
|
80
92
|
source?: string,
|
|
81
|
-
):
|
|
82
|
-
return hook_gate_reuse.
|
|
93
|
+
): GateReuse {
|
|
94
|
+
return hook_gate_reuse.hook_reuse({
|
|
83
95
|
tree,
|
|
84
96
|
is_tree_carried: tree.is_clean,
|
|
97
|
+
carry_miss: tree.carry_miss,
|
|
85
98
|
extra_arguments,
|
|
86
99
|
force_env: FORCE_ENV,
|
|
87
100
|
source,
|
|
@@ -107,20 +120,29 @@ function format_skip(taken_at: string): string {
|
|
|
107
120
|
// other commands already have. **A project that has vitest and no test file at all fails the push
|
|
108
121
|
// instead** (joshuafolkken/kit#1224): the guard treats that half as a broken state rather than a
|
|
109
122
|
// young project, and the hook returns what the guard returns.
|
|
123
|
+
// **A fall back to the whole suite says why, in one line** (joshuafolkken/kit#3307): under a crowded
|
|
124
|
+
// machine the suite costs minutes, and a silent miss left a moved merge base, a dirty tree and a
|
|
125
|
+
// mismatched record indistinguishable from one another.
|
|
126
|
+
function format_miss(reason: string): string {
|
|
127
|
+
return `↻ running the whole unit suite — the green gate record does not cover this push: ${reason}.`
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
async function run_pre_push_unit(
|
|
111
131
|
extra_arguments: ReadonlyArray<string> = [],
|
|
112
132
|
source?: string,
|
|
113
133
|
): Promise<number> {
|
|
114
|
-
const
|
|
134
|
+
const reuse = push_reuse(await read_push_tree(), extra_arguments, source)
|
|
135
|
+
|
|
136
|
+
if ('miss' in reuse) {
|
|
137
|
+
process.stdout.write(`${format_miss(reuse.miss)}\n`)
|
|
115
138
|
|
|
116
|
-
if (reusable === undefined) {
|
|
117
139
|
return await test_unit_guard.run_guarded_unit(
|
|
118
140
|
process.cwd(),
|
|
119
141
|
forwarded_arguments(extra_arguments),
|
|
120
142
|
)
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
process.stdout.write(`${format_skip(
|
|
145
|
+
process.stdout.write(`${format_skip(reuse.stamp.taken_at)}\n`)
|
|
124
146
|
|
|
125
147
|
return 0
|
|
126
148
|
}
|
|
@@ -54,8 +54,8 @@ function parse_arguments(argv: ReadonlyArray<string>): ReservedCommand | undefin
|
|
|
54
54
|
// command becomes an exit code the caller returns rather than a throw — the reservation is released by
|
|
55
55
|
// `with_core_reservation`'s `finally` on either path.
|
|
56
56
|
async function run_command(command: ReadonlyArray<string>): Promise<number> {
|
|
57
|
-
const [file, ...args] = command
|
|
58
|
-
const result = await execa(file
|
|
57
|
+
const [file = '', ...args] = command
|
|
58
|
+
const result = await execa(file, args, { stdio: 'inherit', reject: false })
|
|
59
59
|
|
|
60
60
|
return result.exitCode ?? FAIL_EXIT_CODE
|
|
61
61
|
}
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
# runs through josh, whose command is the name with its first `-` turned into `:`
|
|
13
13
|
# (`pretool-guard` → `pretool:guard`).
|
|
14
14
|
# - In kit's own checkout the gate is `hook-bundle-ready.ts`, which rebuilds stale bundles
|
|
15
|
-
# (joshuafolkken/kit#2984)
|
|
16
|
-
#
|
|
17
|
-
#
|
|
15
|
+
# (joshuafolkken/kit#2984) and then runs the bundle — or the josh fallback — itself, so a hook call
|
|
16
|
+
# starts node once (joshuafolkken/kit#3327). In an installed package it is a presence check: the
|
|
17
|
+
# published bundles cannot be stale against a source the consumer never edits, and Node strips no
|
|
18
|
+
# types under `node_modules`.
|
|
18
19
|
# - An `if`/`else`, never `&&`/`||`: a guard's refusal is a non-zero exit, and a chain would read it as
|
|
19
20
|
# a missing bundle and run the hook twice.
|
|
20
21
|
|
|
@@ -36,6 +37,6 @@ case $0 in
|
|
|
36
37
|
if [ -d "$package_dir/dist/hooks" ]; then node "$bundle" "$@"; else node "$package_dir/dist/josh.js" "$josh_command" "$@"; fi
|
|
37
38
|
;;
|
|
38
39
|
*)
|
|
39
|
-
|
|
40
|
+
node --disable-warning=ExperimentalWarning "$hooks_dir/hook-bundle-ready.ts" "$hook_name" "$josh_command" "$@"
|
|
40
41
|
;;
|
|
41
42
|
esac
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { text } from 'node:stream/consumers'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { agent_headless } from '#scripts/agent/agent-headless'
|
|
4
5
|
import { backlog_ready } from '#scripts/backlog/backlog-ready'
|
|
5
6
|
import { backlog_stalled_detect } from '#scripts/backlog/backlog-stalled-detect'
|
|
6
7
|
import { repo_party } from '#scripts/discovery/repo-party'
|
|
@@ -13,11 +14,11 @@ import { lane_background } from '#scripts/rules/lane-background'
|
|
|
13
14
|
import { lane_park } from '#scripts/rules/lane-park'
|
|
14
15
|
import { last_prompt } from '#scripts/rules/last-prompt'
|
|
15
16
|
import { stop_rules, type StopContext, type StopOutcome } from '#scripts/rules/stop-rules'
|
|
16
|
-
import { run_carry } from '#scripts/run/run-carry'
|
|
17
|
-
import { run_cut } from '#scripts/run/run-cut'
|
|
17
|
+
import { run_carry } from '#scripts/run/carry/run-carry'
|
|
18
|
+
import { run_cut } from '#scripts/run/cut/run-cut'
|
|
19
|
+
import { run_hold } from '#scripts/run/hold/run-hold'
|
|
18
20
|
import { run_halfrun_resume } from '#scripts/run/run-halfrun-resume'
|
|
19
21
|
import { run_headless } from '#scripts/run/run-headless'
|
|
20
|
-
import { run_hold } from '#scripts/run/run-hold'
|
|
21
22
|
import { run_stranded_detect } from '#scripts/run/run-stranded-detect'
|
|
22
23
|
import { time_density_hook } from '#scripts/time-runtime/time-density-hook'
|
|
23
24
|
import { time_hook_transcript } from '#scripts/time-runtime/time-hook-transcript'
|
|
@@ -87,6 +88,7 @@ async function build_context(
|
|
|
87
88
|
agent_pending: lane_background.pending_agent_ids(tail).length > 0,
|
|
88
89
|
handed_off: lane_handoff.is_handed_off(process.cwd()),
|
|
89
90
|
session_lang: session_lang(),
|
|
91
|
+
headless_agent: agent_headless.is_headless(),
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -217,11 +217,6 @@ function build_file_actions(shape?: ProjectShape): ReadonlyArray<FileAction> {
|
|
|
217
217
|
|
|
218
218
|
return [
|
|
219
219
|
...(shape?.has_git === false ? [] : [build_gitignore_action()]),
|
|
220
|
-
build_action(
|
|
221
|
-
'.npmrc',
|
|
222
|
-
() => init_logic.generate_npmrc(),
|
|
223
|
-
(existing) => init_logic.merge_npmrc(existing),
|
|
224
|
-
),
|
|
225
220
|
build_eslint_action(),
|
|
226
221
|
build_action(
|
|
227
222
|
PRETTIER_CONFIG_JS,
|
|
@@ -104,8 +104,16 @@ function did_skip_copy_if_absent(
|
|
|
104
104
|
|
|
105
105
|
// The merged file is written back, and reported as `updated`, only when the merge changed it — a
|
|
106
106
|
// second `josh init` over an already-merged file reports `unchanged` (joshuafolkken/kit#2873).
|
|
107
|
+
// The workspace template with the project's own `.npmrc` values carried over: once the file holds a
|
|
108
|
+
// key, the merge keeps it, so this is the one chance to move the value (joshuafolkken/kit#3267).
|
|
109
|
+
function read_workspace_template(source_path: string, destination_path: string): string {
|
|
110
|
+
const npmrc = file_reader.read_file_or_empty(path.join(path.dirname(destination_path), '.npmrc'))
|
|
111
|
+
|
|
112
|
+
return init_logic.carry_npmrc_settings(readFileSync(source_path, 'utf8'), npmrc)
|
|
113
|
+
}
|
|
114
|
+
|
|
107
115
|
function merge_existing_workspace_yaml(source_path: string, destination_path: string): void {
|
|
108
|
-
const template =
|
|
116
|
+
const template = read_workspace_template(source_path, destination_path)
|
|
109
117
|
const existing = readFileSync(destination_path, 'utf8')
|
|
110
118
|
const merged = init_logic.merge_workspace_yaml(existing, template)
|
|
111
119
|
|
|
@@ -126,7 +134,9 @@ function did_skip_workspace_yaml_copy(source_path: string, destination_path: str
|
|
|
126
134
|
return false
|
|
127
135
|
}
|
|
128
136
|
|
|
129
|
-
|
|
137
|
+
const template = read_workspace_template(source_path, destination_path)
|
|
138
|
+
|
|
139
|
+
writeFileSync(destination_path, transform_copied_content(destination_path, template))
|
|
130
140
|
console.info(` ✔ created ${WORKSPACE_YAML}`)
|
|
131
141
|
|
|
132
142
|
return false
|
|
@@ -385,6 +395,7 @@ const init_ai_copy = {
|
|
|
385
395
|
ai_file_source,
|
|
386
396
|
ai_file_mappings,
|
|
387
397
|
ensure_basic_claude_md,
|
|
398
|
+
read_workspace_template,
|
|
388
399
|
}
|
|
389
400
|
|
|
390
401
|
export { init_ai_copy, CLAUDE_MD_FILENAME }
|
|
@@ -3,6 +3,7 @@ import { json_format } from '#scripts/config-merge/json-format'
|
|
|
3
3
|
import { parse_jsonc } from '#scripts/config-merge/parse-jsonc'
|
|
4
4
|
import { patch_json_key } from '#scripts/config-merge/patch-json-key'
|
|
5
5
|
import { json_object_schema, string_array_schema, string_record_schema } from '#scripts/lib/schemas'
|
|
6
|
+
import { safe_chain_preinstall } from '#scripts/safe-chain/preinstall-command'
|
|
6
7
|
import { apply_jf_migrations, remove_retired_scripts } from './init-logic-migrate'
|
|
7
8
|
import { PACKAGE_JSON_KEY_ORDER } from './init-logic-package-key-order'
|
|
8
9
|
import { kit_base_preset } from './kit-base-preset'
|
|
@@ -216,6 +217,23 @@ function merge_package_scripts(content: string, scripts: Record<string, string>)
|
|
|
216
217
|
return serialize_package_json({ ...parsed, scripts: { ...prepend, ...migrated, ...append } })
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
// Replace only a safe-chain `preinstall` an earlier kit wrote, for a consumer that never re-runs
|
|
221
|
+
// `josh init`; every other key and script is kept (joshuafolkken/kit#3269).
|
|
222
|
+
function upgrade_safe_chain_preinstall(content: string): string {
|
|
223
|
+
const parsed = parse_jsonc(content)
|
|
224
|
+
const raw = parsed['scripts']
|
|
225
|
+
if (raw === undefined) return content
|
|
226
|
+
|
|
227
|
+
const scripts = string_record_schema.parse(raw)
|
|
228
|
+
const current = scripts['preinstall']
|
|
229
|
+
if (current === undefined) return content
|
|
230
|
+
|
|
231
|
+
const migrated = safe_chain_preinstall.migrate_preinstall(current)
|
|
232
|
+
if (migrated === current) return content
|
|
233
|
+
|
|
234
|
+
return serialize_package_json({ ...parsed, scripts: { ...scripts, preinstall: migrated } })
|
|
235
|
+
}
|
|
236
|
+
|
|
219
237
|
function merge_development_dependencies(
|
|
220
238
|
content: string,
|
|
221
239
|
additions: Record<string, string>,
|
|
@@ -327,6 +345,7 @@ const init_logic_json_merge = {
|
|
|
327
345
|
merge_json_array_field,
|
|
328
346
|
merge_json_object,
|
|
329
347
|
merge_package_scripts,
|
|
348
|
+
upgrade_safe_chain_preinstall,
|
|
330
349
|
merge_package_script_suffix,
|
|
331
350
|
replace_in_package_script,
|
|
332
351
|
remove_script_with_marker,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { init_logic_json_merge } from './init-logic-json-merge'
|
|
2
|
+
import { kit_development_versions } from './kit-development-versions'
|
|
2
3
|
|
|
3
4
|
const SECRETLINT_CONFIG_FILENAME = '.secretlintrc.json'
|
|
4
5
|
const SECRETLINT_RULE_PRESET = '@secretlint/secretlint-rule-preset-recommend'
|
|
@@ -6,12 +7,8 @@ const SECRETLINT_RULE_PRESET = '@secretlint/secretlint-rule-preset-recommend'
|
|
|
6
7
|
// secretlint resolves both its CLI and every rule package from the project it runs in, not
|
|
7
8
|
// transitively through the kit — the same constraint that forces the prettier plugins into
|
|
8
9
|
// consumer devDependencies (as kit peers, see init.ts). Omitting either
|
|
9
|
-
// entry makes the pre-commit hook fail with "Cannot find module".
|
|
10
|
-
|
|
11
|
-
const SECRETLINT_DEV_DEPS: Record<string, string> = {
|
|
12
|
-
secretlint: '^13.0.2',
|
|
13
|
-
[SECRETLINT_RULE_PRESET]: '^13.0.2',
|
|
14
|
-
}
|
|
10
|
+
// entry makes the pre-commit hook fail with "Cannot find module". The versions are kit's own.
|
|
11
|
+
const SECRETLINT_PACKAGES = ['secretlint', SECRETLINT_RULE_PRESET]
|
|
15
12
|
|
|
16
13
|
function generate_secretlint_config(): string {
|
|
17
14
|
const config = { rules: [{ id: SECRETLINT_RULE_PRESET }] }
|
|
@@ -24,7 +21,10 @@ function get_secretlint_config_filename(): string {
|
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
function merge_secretlint_development_deps(content: string): string {
|
|
27
|
-
return init_logic_json_merge.merge_development_dependencies(
|
|
24
|
+
return init_logic_json_merge.merge_development_dependencies(
|
|
25
|
+
content,
|
|
26
|
+
kit_development_versions.versions_of(SECRETLINT_PACKAGES),
|
|
27
|
+
)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const init_logic_secretlint = {
|