@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
package/scripts/init/init.ts
CHANGED
|
@@ -23,7 +23,9 @@ import { init_file_action } from './init-file-action'
|
|
|
23
23
|
import { init_install } from './init-install'
|
|
24
24
|
import { init_logic } from './init-logic'
|
|
25
25
|
import { PACKAGE_DIR, PROJECT_ROOT } from './init-paths'
|
|
26
|
+
import { kit_development_versions } from './kit-development-versions'
|
|
26
27
|
import { kit_setup_state } from './kit-setup-state'
|
|
28
|
+
import { package_manager_pin } from './package-manager-pin'
|
|
27
29
|
import { project_profile, type ProjectProfile, type ProjectShape } from './project-profile'
|
|
28
30
|
|
|
29
31
|
const PACKAGE_JSON = 'package.json'
|
|
@@ -51,7 +53,10 @@ function resolve_kit_package_manager(manifest: unknown): string | undefined {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
function get_kit_package_manager(): string | undefined {
|
|
54
|
-
return
|
|
56
|
+
return package_manager_pin.choose(
|
|
57
|
+
resolve_kit_package_manager(init_actions.read_package_json(PACKAGE_JSON)),
|
|
58
|
+
process.env['npm_config_user_agent'],
|
|
59
|
+
)
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
function get_kit_development_engines(): Record<string, unknown> {
|
|
@@ -81,21 +86,10 @@ function tool_dependency_names(has_git: boolean): ReadonlyArray<string> {
|
|
|
81
86
|
|
|
82
87
|
// Versions mirror kit's own devDependencies, so the consumer runs the toolchain kit is verified with.
|
|
83
88
|
function get_toolchain_development_dependencies(has_git: boolean): Record<string, string> {
|
|
84
|
-
|
|
85
|
-
.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
})
|
|
89
|
-
.parse(init_actions.read_package_json(PACKAGE_JSON))
|
|
90
|
-
const names = [...Object.keys(manifest.peerDependencies), ...tool_dependency_names(has_git)]
|
|
91
|
-
const entries = names.map((name): [string, string] => {
|
|
92
|
-
const version = manifest.devDependencies[name]
|
|
93
|
-
if (version === undefined) throw new Error(`Missing development version for ${name}`)
|
|
94
|
-
|
|
95
|
-
return [name, version]
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
return Object.fromEntries(entries)
|
|
89
|
+
return kit_development_versions.versions_of([
|
|
90
|
+
...kit_development_versions.peer_names(),
|
|
91
|
+
...tool_dependency_names(has_git),
|
|
92
|
+
])
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
function apply_dependency_merges(content: string, has_git = true): string {
|
|
@@ -6,17 +6,17 @@ import { start_exec } from './start-exec'
|
|
|
6
6
|
const DEFAULT_BRANCH = 'main'
|
|
7
7
|
|
|
8
8
|
function create_repository(root: string): void {
|
|
9
|
-
start_exec.
|
|
9
|
+
start_exec.git_run(['init', `--initial-branch=${DEFAULT_BRANCH}`], root)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function commit_all(root: string): void {
|
|
13
|
-
start_exec.
|
|
13
|
+
start_exec.git_run(['add', '--all'], root)
|
|
14
14
|
// The first commit of a new repository has no branch to come from, so the hook that keeps commits
|
|
15
15
|
// off main — installed by the initialize step in a full project — would refuse the only commit
|
|
16
16
|
// that has to land there.
|
|
17
|
-
start_exec.
|
|
17
|
+
start_exec.git_run(['commit', '--no-verify', '--message', 'Initial commit'], root)
|
|
18
18
|
// A repository created by an older `git init` may name its unborn branch `master`.
|
|
19
|
-
start_exec.
|
|
19
|
+
start_exec.git_run(['branch', '--move', '--force', DEFAULT_BRANCH], root)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const initial_commit = { create_repository, commit_all }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { package_path } from './init-paths'
|
|
4
|
+
|
|
5
|
+
const PACKAGE_JSON = 'package.json'
|
|
6
|
+
|
|
7
|
+
const kit_manifest_schema = z.object({
|
|
8
|
+
peerDependencies: z.record(z.string(), z.string()).default({}),
|
|
9
|
+
devDependencies: z.record(z.string(), z.string()),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
type KitManifest = z.infer<typeof kit_manifest_schema>
|
|
13
|
+
|
|
14
|
+
function read_kit_manifest(): KitManifest {
|
|
15
|
+
const content = readFileSync(package_path(PACKAGE_JSON), 'utf8')
|
|
16
|
+
|
|
17
|
+
return kit_manifest_schema.parse(JSON.parse(content))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The version kit's own devDependencies carry for each name, so a consumer runs the toolchain kit
|
|
21
|
+
// is verified with rather than a copy of it that drifts.
|
|
22
|
+
function versions_of(names: ReadonlyArray<string>): Record<string, string> {
|
|
23
|
+
const { devDependencies: versions } = read_kit_manifest()
|
|
24
|
+
const entries = names.map((name): [string, string] => {
|
|
25
|
+
const version = versions[name]
|
|
26
|
+
if (version === undefined) throw new Error(`Missing development version for ${name}`)
|
|
27
|
+
|
|
28
|
+
return [name, version]
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return Object.fromEntries(entries)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function peer_names(): ReadonlyArray<string> {
|
|
35
|
+
return Object.keys(read_kit_manifest().peerDependencies)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const kit_development_versions = { peer_names, versions_of }
|
|
39
|
+
|
|
40
|
+
export { kit_development_versions }
|
|
@@ -8,13 +8,13 @@ const START_HINT =
|
|
|
8
8
|
// reached the branch, so `josh start` has no setup pull request to open (joshuafolkken/kit#2816).
|
|
9
9
|
function is_kit_committed(root: string): boolean {
|
|
10
10
|
// `./` resolves the path from `root` rather than from the repository's top level.
|
|
11
|
-
const manifest = start_exec.
|
|
11
|
+
const manifest = start_exec.git_read(['show', 'HEAD:./package.json'], root)
|
|
12
12
|
|
|
13
13
|
return manifest?.includes(`"${KIT_PACKAGE_NAME}"`) ?? false
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function is_git_work_tree(root: string): boolean {
|
|
17
|
-
return start_exec.
|
|
17
|
+
return start_exec.git_succeeds(['rev-parse', '--is-inside-work-tree'], root)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// The line `josh init` ends with in a Git repository whose checkout does not hold kit yet: the setup is
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { package_manager_version } from '#scripts/version/package-manager-version'
|
|
2
|
+
import semver from 'semver'
|
|
3
|
+
|
|
4
|
+
// pnpm sets `npm_config_user_agent` to `pnpm/<version> npm/? node/<version> <platform> <arch>` for
|
|
5
|
+
// every script and `pnpm exec` it runs, so the pnpm that invoked `josh init` is readable from it.
|
|
6
|
+
const PNPM_USER_AGENT_REGEX = /^pnpm\/(\S+)/u
|
|
7
|
+
|
|
8
|
+
function running_pnpm_version(user_agent = ''): string | undefined {
|
|
9
|
+
const version = PNPM_USER_AGENT_REGEX.exec(user_agent)?.[1]
|
|
10
|
+
|
|
11
|
+
return version === undefined ? undefined : (semver.valid(version) ?? undefined)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function is_older_than(kit_pin: string, version: string): boolean {
|
|
15
|
+
const kit_version = semver.valid(package_manager_version.extract_pnpm_pin(kit_pin) ?? '')
|
|
16
|
+
|
|
17
|
+
return kit_version !== null && semver.lt(kit_version, version)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Never pin a project below the pnpm installing it: the lockfile that pnpm just wrote can name a
|
|
21
|
+
// version an older pnpm fails to resolve, so `pnpm install` under kit's older pin breaks the
|
|
22
|
+
// first `josh init` (joshuafolkken/kit#3367). The kit pin keeps its integrity suffix when it wins.
|
|
23
|
+
function choose(kit_pin: string | undefined, user_agent: string | undefined): string | undefined {
|
|
24
|
+
const running = running_pnpm_version(user_agent)
|
|
25
|
+
if (kit_pin === undefined || running === undefined) return kit_pin
|
|
26
|
+
|
|
27
|
+
return is_older_than(kit_pin, running) ? `pnpm@${running}` : kit_pin
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const package_manager_pin = {
|
|
31
|
+
running_pnpm_version,
|
|
32
|
+
choose,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { package_manager_pin }
|
|
@@ -1,39 +1,80 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
2
2
|
import { git_location_environment } from '#scripts/git/git-location-environment'
|
|
3
|
+
import { git_spawn_sync } from '#scripts/git/git-spawn-sync'
|
|
4
|
+
import { COMMAND_TIMEOUT_MS, SUITE_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
3
5
|
import { execaSync } from 'execa'
|
|
6
|
+
import type { Visibility } from './start-plan'
|
|
4
7
|
|
|
5
|
-
// The
|
|
6
|
-
//
|
|
8
|
+
// The spawn shapes every `josh start` step uses, shared by `start-steps.ts`, the setup pull request
|
|
9
|
+
// step and the `josh init` hint so none of them keeps a copy of its own.
|
|
7
10
|
//
|
|
8
|
-
// The
|
|
11
|
+
// The git shapes below act on `root`, so `cwd` has to mean it: an inherited `GIT_DIR` and friends beat `cwd`
|
|
9
12
|
// and would carry a `git init` or commit into whichever repository a calling hook was firing in
|
|
10
13
|
// (joshuafolkken/kit#3151).
|
|
14
|
+
//
|
|
15
|
+
// **Every git and gh call here is bounded** (joshuafolkken/kit#3262). A read asks the local
|
|
16
|
+
// repository and takes git's own budget. A write — and `gh repo create --push` — can fire the
|
|
17
|
+
// project's own commit and push hooks, which may run its whole suite, so it takes the suite budget:
|
|
18
|
+
// long enough for that, short enough that a push that never answers ends the run.
|
|
19
|
+
const SUCCESS_EXIT_CODE = 0
|
|
20
|
+
|
|
11
21
|
function root_environment(): Record<string, string | undefined> {
|
|
12
22
|
return git_location_environment.location_free_environment()
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
function
|
|
16
|
-
return (
|
|
17
|
-
execaSync(command, args, { cwd: root, env: root_environment(), reject: false }).exitCode === 0
|
|
18
|
-
)
|
|
25
|
+
function git_succeeds(args: ReadonlyArray<string>, root: string): boolean {
|
|
26
|
+
return git_spawn_sync.run(args, { cwd: root, env: root_environment() }).exit_code === 0
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
29
|
+
function git_read(args: ReadonlyArray<string>, root: string): string | undefined {
|
|
30
|
+
return git_spawn_sync.read(args, { cwd: root, env: root_environment() })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function git_run(args: ReadonlyArray<string>, root: string): void {
|
|
34
|
+
const options = { cwd: root, env: root_environment(), timeout: SUITE_TIMEOUT_MS }
|
|
35
|
+
const { exit_code } = git_spawn_sync.run(args, { ...options, stdio: 'inherit' })
|
|
36
|
+
|
|
37
|
+
if (exit_code === SUCCESS_EXIT_CODE) return
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
const outcome = exit_code === undefined ? 'did not finish' : `exited with ${String(exit_code)}`
|
|
40
|
+
|
|
41
|
+
throw new Error(`git ${args.join(' ')} ${outcome}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Every gh spawn here dials GitHub directly, past a scanner's loopback proxy, the same as every
|
|
45
|
+
// other `gh` spawn under scripts/ (joshuafolkken/kit#2436). Each argument list is written inline so
|
|
46
|
+
// `gh-subcommand-guard.ts` reads its subcommand rather than `<dynamic>`.
|
|
47
|
+
interface GhProbeOptions {
|
|
48
|
+
cwd: string
|
|
49
|
+
env: Record<string, string | undefined>
|
|
50
|
+
timeout: number
|
|
51
|
+
reject: false
|
|
29
52
|
}
|
|
30
53
|
|
|
31
|
-
|
|
32
|
-
// other `gh` spawn under scripts/ (joshuafolkken/kit#2436); a local `git` step is unaffected by it.
|
|
33
|
-
function run(command: string, args: ReadonlyArray<string>, root: string): void {
|
|
54
|
+
function gh_probe_options(root: string): GhProbeOptions {
|
|
34
55
|
const environment = { ...root_environment(), ...git_gh_exec.direct_environment().env }
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
return { cwd: root, env: environment, timeout: COMMAND_TIMEOUT_MS, reject: false }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function is_gh_installed(root: string): boolean {
|
|
61
|
+
const result = execaSync('gh', ['--version'], gh_probe_options(root)) // NOSONAR S8705: execa array args (no shell), trusted dev CLI tooling
|
|
62
|
+
|
|
63
|
+
return result.exitCode === SUCCESS_EXIT_CODE
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function is_gh_signed_in(root: string): boolean {
|
|
67
|
+
const result = execaSync('gh', ['auth', 'status'], gh_probe_options(root)) // NOSONAR S8705: execa array args (no shell), trusted dev CLI tooling
|
|
68
|
+
|
|
69
|
+
return result.exitCode === SUCCESS_EXIT_CODE
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function create_github_repository(name: string, visibility: Visibility, root: string): void {
|
|
73
|
+
const source = ['--source', '.', '--remote', 'origin', '--push']
|
|
74
|
+
const { env } = gh_probe_options(root)
|
|
75
|
+
const options = { cwd: root, env, timeout: SUITE_TIMEOUT_MS, stdio: 'inherit' } as const
|
|
76
|
+
|
|
77
|
+
execaSync('gh', ['repo', 'create', name, `--${visibility}`, ...source], options) // NOSONAR S8705: execa array args (no shell), trusted dev CLI tooling
|
|
37
78
|
}
|
|
38
79
|
|
|
39
80
|
// A caller's own initialize command stands in for the in-process `josh init`, so it keeps the
|
|
@@ -42,6 +83,14 @@ function run_local(command: string, args: ReadonlyArray<string>, root: string):
|
|
|
42
83
|
execaSync(command, args, { cwd: root, stdio: 'inherit' })
|
|
43
84
|
}
|
|
44
85
|
|
|
45
|
-
const start_exec = {
|
|
86
|
+
const start_exec = {
|
|
87
|
+
git_succeeds,
|
|
88
|
+
git_read,
|
|
89
|
+
git_run,
|
|
90
|
+
is_gh_installed,
|
|
91
|
+
is_gh_signed_in,
|
|
92
|
+
create_github_repository,
|
|
93
|
+
run_local,
|
|
94
|
+
}
|
|
46
95
|
|
|
47
96
|
export { start_exec }
|
|
@@ -22,12 +22,12 @@ const FIRST_SETUP_ISSUE_FILTER = `[.[] | select(.title == "${SETUP_TITLE}") | .n
|
|
|
22
22
|
|
|
23
23
|
// Listings are read NUL-separated (`-z`), so a path is never quoted — a name a caller's initialize
|
|
24
24
|
// command wrote need not be ASCII the way kit's own are (#2872).
|
|
25
|
-
function paths_of(output: string | undefined): Array<string> {
|
|
26
|
-
return
|
|
25
|
+
function paths_of(output: string | undefined = ''): Array<string> {
|
|
26
|
+
return output.split('\0').filter((listed) => listed.length > 0)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function listing(args: ReadonlyArray<string>, root: string): Array<string> {
|
|
30
|
-
return paths_of(start_exec.
|
|
30
|
+
return paths_of(start_exec.git_read([...args, '-z'], root))
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function setup_issue(issue_number: string): IssueInfo {
|
|
@@ -103,13 +103,13 @@ function switch_to(branch: string, current: string | undefined, root: string): v
|
|
|
103
103
|
|
|
104
104
|
const reference = `refs/heads/${branch}`
|
|
105
105
|
|
|
106
|
-
if (start_exec.
|
|
106
|
+
if (start_exec.git_succeeds(['rev-parse', '--verify', '--quiet', reference], root)) {
|
|
107
107
|
throw new Error(
|
|
108
108
|
`The setup branch ${branch} already exists. Run git switch ${branch}, then josh start again.`,
|
|
109
109
|
)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
start_exec.
|
|
112
|
+
start_exec.git_run(['switch', '--create', branch], root)
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// The paths are named on the commit as well as on the add, so anything the user had staged before
|
|
@@ -117,8 +117,8 @@ function switch_to(branch: string, current: string | undefined, root: string): v
|
|
|
117
117
|
function commit_kit_paths(paths: ReadonlyArray<string>, issue: IssueInfo, root: string): void {
|
|
118
118
|
if (paths.length === 0) return
|
|
119
119
|
|
|
120
|
-
start_exec.
|
|
121
|
-
start_exec.
|
|
120
|
+
start_exec.git_run(['add', '--', ...paths], root)
|
|
121
|
+
start_exec.git_run(['commit', '--message', issue.commit_message, '--', ...paths], root)
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// A caller's initialize command writes files kit cannot name, so with a `baseline` — what was already
|
|
@@ -143,7 +143,7 @@ function setup_paths(
|
|
|
143
143
|
// Issue → branch → only the setup's files committed → push → pull request. The merge is left to a
|
|
144
144
|
// person: it is the one step that changes main.
|
|
145
145
|
async function open(root: string, baseline?: ReadonlyArray<string>): Promise<void> {
|
|
146
|
-
const current = start_exec.
|
|
146
|
+
const current = start_exec.git_read(['symbolic-ref', '--short', 'HEAD'], root)
|
|
147
147
|
const paths = setup_paths(root, baseline, current)
|
|
148
148
|
|
|
149
149
|
if (paths.length === 0) {
|
|
@@ -156,7 +156,7 @@ async function open(root: string, baseline?: ReadonlyArray<string>): Promise<voi
|
|
|
156
156
|
|
|
157
157
|
switch_to(issue.branch_name, current, root)
|
|
158
158
|
commit_kit_paths(paths, issue, root)
|
|
159
|
-
start_exec.
|
|
159
|
+
start_exec.git_run(['push', '--set-upstream', 'origin', issue.branch_name], root)
|
|
160
160
|
await git_pr.create_with_issue_info(issue)
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -29,7 +29,7 @@ const GH_INSTALL_HINT =
|
|
|
29
29
|
const GH_AUTH_HINT =
|
|
30
30
|
'GitHub CLI is not signed in. Run gh auth login, then run josh start again. Nothing was changed.'
|
|
31
31
|
|
|
32
|
-
const {
|
|
32
|
+
const { git_succeeds, git_read, run_local } = start_exec
|
|
33
33
|
|
|
34
34
|
function read_git_state(root: string, shape: ProjectShape): GitState {
|
|
35
35
|
const { has_git, has_github } = shape
|
|
@@ -46,8 +46,8 @@ function read_git_state(root: string, shape: ProjectShape): GitState {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const has_origin = git_spawn_sync.origin_url(root) !== undefined
|
|
49
|
-
const branch =
|
|
50
|
-
const has_commits =
|
|
49
|
+
const branch = git_read(['symbolic-ref', '--short', 'HEAD'], root)
|
|
50
|
+
const has_commits = git_succeeds(['rev-parse', '--verify', '--quiet', 'HEAD'], root)
|
|
51
51
|
const has_kit_committed = kit_setup_state.is_kit_committed(root)
|
|
52
52
|
|
|
53
53
|
return { has_git, has_github, has_origin, branch, has_commits, has_kit_committed }
|
|
@@ -56,8 +56,8 @@ function read_git_state(root: string, shape: ProjectShape): GitState {
|
|
|
56
56
|
// Every step talks to GitHub, so the CLI is checked before the first write rather than at the step
|
|
57
57
|
// that needs it — a missing `gh` then leaves the directory exactly as it was.
|
|
58
58
|
function github_cli_refusal(root: string): string | undefined {
|
|
59
|
-
if (!
|
|
60
|
-
if (!
|
|
59
|
+
if (!start_exec.is_gh_installed(root)) return GH_INSTALL_HINT
|
|
60
|
+
if (!start_exec.is_gh_signed_in(root)) return GH_AUTH_HINT
|
|
61
61
|
|
|
62
62
|
return undefined
|
|
63
63
|
}
|
|
@@ -94,11 +94,7 @@ function commit_all(context: StepContext): void {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function create_github_repository(context: StepContext): void {
|
|
97
|
-
|
|
98
|
-
const visibility = `--${context.visibility}`
|
|
99
|
-
const source = ['--source', '.', '--remote', 'origin', '--push']
|
|
100
|
-
|
|
101
|
-
run('gh', ['repo', 'create', name, visibility, ...source], context.root)
|
|
97
|
+
start_exec.create_github_repository(path.basename(context.root), context.visibility, context.root)
|
|
102
98
|
}
|
|
103
99
|
|
|
104
100
|
// The repository was created by the step before, so `gh` resolves the placeholder from the
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
4
4
|
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
5
|
+
import { issue_label_schema } from '#scripts/git/git-schemas'
|
|
5
6
|
import { parse_json } from '#scripts/git/parse-json-array'
|
|
6
|
-
import { issue_label_schema } from '#scripts/git/schemas'
|
|
7
7
|
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
8
8
|
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
9
9
|
import { z } from 'zod'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { open_issue_schema, type OpenIssueData } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_array_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { open_issue_schema, type OpenIssueData } from '#scripts/git/schemas'
|
|
4
4
|
import { has_any_label, NOT_DIRECTLY_RUNNABLE_LABELS } from './issue-labels'
|
|
5
5
|
import { issue_rank, type DependencyRow } from './issue-rank'
|
|
6
6
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { git_branch } from '#scripts/git/git-branch'
|
|
2
2
|
import { git_command } from '#scripts/git/git-command'
|
|
3
|
-
import { run_carry } from '#scripts/run/run-carry'
|
|
3
|
+
import { run_carry } from '#scripts/run/carry/run-carry'
|
|
4
4
|
import { issue_file } from './issue-file'
|
|
5
|
-
import { AUTO_OK_LABEL, has_label_name } from './issue-labels'
|
|
5
|
+
import { AUTO_OK_LABEL, has_label_name, RELEASE_LABEL } from './issue-labels'
|
|
6
6
|
import { issue_state_cli } from './issue-state-cli'
|
|
7
7
|
|
|
8
8
|
// Whether `josh issue:file` applies `auto-ok` to the Issue it files (joshuafolkken/kit#3213). Work
|
|
@@ -12,9 +12,13 @@ import { issue_state_cli } from './issue-state-cli'
|
|
|
12
12
|
// default. `--no-auto-ok` is the one exception, declared for an Issue that needs a person's judgement
|
|
13
13
|
// (a Tier B toss-up or a Tier C action). A filing to another repository is never opted in: that
|
|
14
14
|
// repository's own `backlogrun` would implement and merge it though nobody opted that repository in.
|
|
15
|
+
// A `release` Issue is never opted in either (joshuafolkken/kit#3360): a release is Tier C, so only a
|
|
16
|
+
// person applies `auto-ok` to it, whatever the carry record or the branch's Issue says.
|
|
15
17
|
|
|
16
18
|
interface AutoOkSignals {
|
|
17
19
|
is_opted_out: boolean
|
|
20
|
+
// The filing carries the `release` label.
|
|
21
|
+
is_release: boolean
|
|
18
22
|
is_carried: boolean
|
|
19
23
|
// The labels of the Issue the current branch names; `undefined` when the branch names none or the
|
|
20
24
|
// Issue could not be read.
|
|
@@ -31,6 +35,10 @@ const CROSS_REPOSITORY: AutoOkDecision = {
|
|
|
31
35
|
is_applied: false,
|
|
32
36
|
reason: 'the Issue is filed to another repository',
|
|
33
37
|
}
|
|
38
|
+
const RELEASE: AutoOkDecision = {
|
|
39
|
+
is_applied: false,
|
|
40
|
+
reason: `a ${RELEASE_LABEL} Issue is opted in by a person only`,
|
|
41
|
+
}
|
|
34
42
|
const CARRIED: AutoOkDecision = { is_applied: true, reason: 'a backlogrun carry record is live' }
|
|
35
43
|
const BRANCH_OPTED_IN: AutoOkDecision = {
|
|
36
44
|
is_applied: true,
|
|
@@ -41,9 +49,22 @@ const NOT_OPTED_IN: AutoOkDecision = {
|
|
|
41
49
|
reason: 'no backlogrun carry record is live and the branch names no auto-ok Issue',
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
type Withholding = Pick<AutoOkSignals, 'is_opted_out' | 'is_release'>
|
|
53
|
+
|
|
54
|
+
// The two signals that withhold `auto-ok` before any other is read, or `undefined` when neither holds.
|
|
55
|
+
function withheld(signals: Withholding): AutoOkDecision | undefined {
|
|
46
56
|
if (signals.is_opted_out) return OPTED_OUT
|
|
57
|
+
if (signals.is_release) return RELEASE
|
|
58
|
+
|
|
59
|
+
return undefined
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The opt-out and the `release` label win over every signal, then the carry record, then the branch's
|
|
63
|
+
// Issue.
|
|
64
|
+
function decide(signals: AutoOkSignals): AutoOkDecision {
|
|
65
|
+
const decision = withheld(signals)
|
|
66
|
+
|
|
67
|
+
if (decision !== undefined) return decision
|
|
47
68
|
if (signals.is_carried) return CARRIED
|
|
48
69
|
if (has_label_name(signals.branch_labels ?? [], AUTO_OK_LABEL)) return BRANCH_OPTED_IN
|
|
49
70
|
|
|
@@ -76,17 +97,21 @@ async function branch_labels(current: string): Promise<ReadonlyArray<string> | u
|
|
|
76
97
|
}
|
|
77
98
|
|
|
78
99
|
// Each read is skipped once an earlier signal has already decided the answer; a cross-repository
|
|
79
|
-
// filing is refused before any signal is read.
|
|
100
|
+
// filing is refused before any signal is read. `labels` are the ones the filing declares.
|
|
80
101
|
async function resolve(
|
|
81
102
|
is_opted_out: boolean,
|
|
82
103
|
target: string,
|
|
83
104
|
current: string,
|
|
105
|
+
labels: ReadonlyArray<string> = [],
|
|
84
106
|
): Promise<AutoOkDecision> {
|
|
85
|
-
|
|
107
|
+
const withholding = { is_opted_out, is_release: has_label_name(labels, RELEASE_LABEL) }
|
|
108
|
+
const decision = withheld(withholding)
|
|
109
|
+
|
|
110
|
+
if (decision !== undefined) return decision
|
|
86
111
|
if (!issue_file.is_same_repository(target, current)) return CROSS_REPOSITORY
|
|
87
112
|
if (await is_carried()) return CARRIED
|
|
88
113
|
|
|
89
|
-
return decide({
|
|
114
|
+
return decide({ ...withholding, is_carried: false, branch_labels: await branch_labels(current) })
|
|
90
115
|
}
|
|
91
116
|
|
|
92
117
|
const issue_auto_ok = { decide, line_of, resolve }
|
|
@@ -34,8 +34,8 @@ async function read_upstream(reference: string): Promise<UpstreamEntry | undefin
|
|
|
34
34
|
const match = REF_PATTERN.exec(reference)
|
|
35
35
|
if (match === null) return undefined
|
|
36
36
|
|
|
37
|
-
const [, repo, number] = match
|
|
38
|
-
const body = await read_body(number
|
|
37
|
+
const [, repo, number = ''] = match
|
|
38
|
+
const body = await read_body(number, repo)
|
|
39
39
|
|
|
40
40
|
return body === undefined ? undefined : { ref: reference, body }
|
|
41
41
|
}
|
|
@@ -33,11 +33,11 @@ function has_heading(body: string, heading: string): boolean {
|
|
|
33
33
|
return markdown_section.has_line(body, heading)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// The lines under
|
|
37
|
-
function
|
|
38
|
-
if (!has_heading(body,
|
|
36
|
+
// The lines under the upstream heading, up to the next `##` heading; `undefined` when it is absent.
|
|
37
|
+
function upstream_section_lines(body: string): ReadonlyArray<string> | undefined {
|
|
38
|
+
if (!has_heading(body, UPSTREAM_ISSUES_HEADING)) return undefined
|
|
39
39
|
|
|
40
|
-
return markdown_section.section_lines(body,
|
|
40
|
+
return markdown_section.section_lines(body, UPSTREAM_ISSUES_HEADING)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// A heading that looks like the upstream one but is not exactly it (`## Upstream`, `## Upstreams`,
|
|
@@ -59,8 +59,8 @@ function tokens_of(section: ReadonlyArray<string>): ReadonlyArray<string> {
|
|
|
59
59
|
.filter((token) => token.length > 0)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
function has_two_segments(path: string | undefined): boolean {
|
|
63
|
-
const segments =
|
|
62
|
+
function has_two_segments(path: string | undefined = ''): boolean {
|
|
63
|
+
const segments = path.split(PATH_SEPARATOR)
|
|
64
64
|
|
|
65
65
|
return segments.length === PATH_SEGMENT_COUNT && segments.every((segment) => segment.length > 0)
|
|
66
66
|
}
|
|
@@ -115,7 +115,7 @@ function classify_backlinks(
|
|
|
115
115
|
): BacklinkVerdict {
|
|
116
116
|
if (has_wrong_upstream_heading(origin_body)) return WRONG_HEADING
|
|
117
117
|
|
|
118
|
-
const section =
|
|
118
|
+
const section = upstream_section_lines(origin_body)
|
|
119
119
|
|
|
120
120
|
return section === undefined
|
|
121
121
|
? verdict_without_section(origin_body)
|
|
@@ -125,7 +125,7 @@ function classify_backlinks(
|
|
|
125
125
|
// The `owner/repo#N` references the upstream section lists, so the CLI knows which upstream bodies to
|
|
126
126
|
// fetch and check for `## Origin`.
|
|
127
127
|
function upstream_references(origin_body: string): ReadonlyArray<string> {
|
|
128
|
-
const section =
|
|
128
|
+
const section = upstream_section_lines(origin_body)
|
|
129
129
|
if (section === undefined) return []
|
|
130
130
|
|
|
131
131
|
return tokens_of(section).filter((token) => is_qualified_reference(token))
|
|
@@ -138,7 +138,7 @@ function upstream_references(origin_body: string): ReadonlyArray<string> {
|
|
|
138
138
|
function needs_upstreams(origin_body: string): boolean {
|
|
139
139
|
if (has_wrong_upstream_heading(origin_body)) return false
|
|
140
140
|
|
|
141
|
-
const section =
|
|
141
|
+
const section = upstream_section_lines(origin_body)
|
|
142
142
|
if (section === undefined) return false
|
|
143
143
|
|
|
144
144
|
return !is_malformed_upstream_section(section)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
4
4
|
import { cli_body } from '#scripts/josh/cli-body'
|
|
5
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
5
6
|
import { error_text } from '#scripts/lib/error-message'
|
|
6
7
|
|
|
7
8
|
// `josh issue:comment <N> --body <text> | --body-file <path>` — post one comment to an issue (a PR
|
|
@@ -28,25 +29,30 @@ interface CommentRequest {
|
|
|
28
29
|
body: string
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
// The
|
|
32
|
-
// both-flags-at-once refusal and the file read
|
|
33
|
-
|
|
34
|
-
const index = argv.indexOf(flag)
|
|
32
|
+
// The read is strict (joshuafolkken/kit#3261): an unknown flag refuses the call rather than being
|
|
33
|
+
// ignored. `cli_body.resolve` owns the both-flags-at-once refusal and the file read.
|
|
34
|
+
const OPTIONS = { body: { type: 'string' }, 'body-file': { type: 'string' } } as const
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// The number is the one positional, and it must be a real issue number: a missing, malformed or
|
|
37
|
+
// second one refuses the whole call rather than posting to some other issue.
|
|
38
|
+
function single_issue_number(positionals: ReadonlyArray<string>): string | undefined {
|
|
39
|
+
const [issue_number, ...rest] = positionals
|
|
40
|
+
const is_single = rest.length === 0 && issue_number !== undefined
|
|
41
|
+
|
|
42
|
+
return is_single && ISSUE_NUMBER_PATTERN.test(issue_number) ? issue_number : undefined
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// flags are given, which `run` reports — the message names the two flags a caller must pick between.
|
|
45
|
+
// `resolve` may throw when both body flags are given, which `run` reports — the message names the two
|
|
46
|
+
// flags a caller must pick between.
|
|
42
47
|
function parse_request(argv: ReadonlyArray<string>): CommentRequest | undefined {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
const parsed = cli_flags.arguments_of(argv, OPTIONS)
|
|
49
|
+
if (parsed === undefined) return undefined
|
|
50
|
+
const issue_number = single_issue_number(parsed.positionals)
|
|
51
|
+
if (issue_number === undefined) return undefined
|
|
46
52
|
|
|
47
53
|
const body = cli_body.resolve({
|
|
48
|
-
inline:
|
|
49
|
-
file_path:
|
|
54
|
+
inline: parsed.values.body,
|
|
55
|
+
file_path: parsed.values['body-file'],
|
|
50
56
|
inline_flag: BODY_FLAG,
|
|
51
57
|
file_flag: BODY_FILE_FLAG,
|
|
52
58
|
})
|