@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
package/scripts/epic/epic-cli.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cli_body } from '#scripts/josh/cli-body'
|
|
2
2
|
import type { InsertKind, InsertPosition } from './epic-chains'
|
|
3
|
-
import { epic_cli_argv, ISSUE_NUMBER_PATTERN } from './epic-cli-argv'
|
|
3
|
+
import { epic_cli_argv, ISSUE_NUMBER_PATTERN, type EpicArgv, type FormFlags } from './epic-cli-argv'
|
|
4
4
|
import { epic_cli_remove } from './epic-cli-remove'
|
|
5
5
|
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
6
6
|
|
|
@@ -27,25 +27,22 @@ const ORDER_AFTER_FLAG = '--order-after'
|
|
|
27
27
|
// from a file, or from stdin as `-`. The text is a judgement, so the caller writes it; what the command
|
|
28
28
|
// contributes is placing it in the epic's `## Decisions` and on each child (joshuafolkken/kit#1350).
|
|
29
29
|
const DECISION_FLAG = '--decision-file'
|
|
30
|
-
// Which flags consume the argument after them — per
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
]
|
|
39
|
-
const
|
|
40
|
-
ADD_FLAG,
|
|
41
|
-
BEFORE_FLAG,
|
|
42
|
-
|
|
43
|
-
ORDER_BEFORE_FLAG,
|
|
44
|
-
ORDER_AFTER_FLAG,
|
|
45
|
-
DECISION_FLAG,
|
|
46
|
-
])
|
|
30
|
+
// Which flags each form knows, and which of them consume the argument after them — per form, for the
|
|
31
|
+
// reason `epic-cli-argv.ts` gives.
|
|
32
|
+
const CREATE_FORM: FormFlags = {
|
|
33
|
+
switches: [ORDERED_FLAG],
|
|
34
|
+
value_flags: [RATIONALE_FLAG, ORIGIN_FLAG],
|
|
35
|
+
}
|
|
36
|
+
const PROMOTE_FORM: FormFlags = { ...CREATE_FORM, switches: [PROMOTE_FLAG, ORDERED_FLAG] }
|
|
37
|
+
const RECONCILE_FORM: FormFlags = { switches: [RECONCILE_FLAG] }
|
|
38
|
+
const CHECK_FORM: FormFlags = { switches: [] }
|
|
39
|
+
const ADD_FORM: FormFlags = {
|
|
40
|
+
switches: [ADD_FLAG],
|
|
41
|
+
value_flags: [BEFORE_FLAG, AFTER_FLAG, ORDER_BEFORE_FLAG, ORDER_AFTER_FLAG, DECISION_FLAG],
|
|
42
|
+
}
|
|
47
43
|
|
|
48
|
-
const { count_flag, is_value_unusable, read_flag_value } =
|
|
44
|
+
const { count_flag, is_switch_set, is_value_unusable, positionals_of, read_flag_value, read_form } =
|
|
45
|
+
epic_cli_argv
|
|
49
46
|
|
|
50
47
|
interface CreateArguments {
|
|
51
48
|
title: string
|
|
@@ -65,13 +62,6 @@ interface PromoteArguments {
|
|
|
65
62
|
origin?: string | undefined
|
|
66
63
|
}
|
|
67
64
|
|
|
68
|
-
function to_positional_arguments(
|
|
69
|
-
argv: ReadonlyArray<string>,
|
|
70
|
-
value_flags: ReadonlySet<string> = VALUE_FLAGS,
|
|
71
|
-
): Array<string> {
|
|
72
|
-
return epic_cli_argv.to_positional_arguments(argv, value_flags)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
65
|
// Deduplicated because a repeated number would render a duplicate task-list row, and with
|
|
76
66
|
// `--ordered` would ask GitHub to make an issue block itself.
|
|
77
67
|
function to_child_numbers(raw_children: ReadonlyArray<string>): Array<number> {
|
|
@@ -83,22 +73,33 @@ function to_child_numbers(raw_children: ReadonlyArray<string>): Array<number> {
|
|
|
83
73
|
return [...new Set(numbers)]
|
|
84
74
|
}
|
|
85
75
|
|
|
86
|
-
// The
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
// The flags a creation and a promotion share, read the same way for both.
|
|
77
|
+
function read_batch_flags(
|
|
78
|
+
parsed: EpicArgv,
|
|
79
|
+
): Pick<CreateArguments, 'is_ordered' | 'rationale_path' | 'origin'> {
|
|
80
|
+
return {
|
|
81
|
+
is_ordered: is_switch_set(parsed, ORDERED_FLAG),
|
|
82
|
+
rationale_path: read_flag_value(parsed, RATIONALE_FLAG),
|
|
83
|
+
origin: read_flag_value(parsed, ORIGIN_FLAG),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function to_create_arguments(parsed: EpicArgv): CreateArguments | undefined {
|
|
88
|
+
const [title, ...raw_children] = parsed.positionals
|
|
90
89
|
if (title === undefined || title.length === 0) return undefined
|
|
91
90
|
|
|
92
91
|
const children = to_child_numbers(raw_children)
|
|
93
92
|
if (children.length === 0) return undefined
|
|
94
93
|
|
|
95
|
-
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
return { title, children, ...read_batch_flags(parsed) }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// The title comes first so the children can stay a bare list of numbers, which is how they are
|
|
98
|
+
// written down when the split is made.
|
|
99
|
+
function parse_create_arguments(argv: ReadonlyArray<string>): CreateArguments | undefined {
|
|
100
|
+
const parsed = read_form(argv, CREATE_FORM)
|
|
101
|
+
|
|
102
|
+
return parsed === undefined ? undefined : to_create_arguments(parsed)
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
// Whether the invocation is a promotion rather than a creation.
|
|
@@ -118,7 +119,7 @@ interface ReconcileArguments {
|
|
|
118
119
|
// The epic to reconcile. The number after `--reconcile` is the only subject; there are no children
|
|
119
120
|
// and no position — the repair reads what the epic already records rather than being told an order.
|
|
120
121
|
function parse_reconcile_arguments(argv: ReadonlyArray<string>): ReconcileArguments | undefined {
|
|
121
|
-
const [raw_epic] =
|
|
122
|
+
const [raw_epic] = positionals_of(argv, RECONCILE_FORM)
|
|
122
123
|
if (raw_epic === undefined || !ISSUE_NUMBER_PATTERN.test(raw_epic)) return undefined
|
|
123
124
|
|
|
124
125
|
return { epic_number: Number(raw_epic) }
|
|
@@ -127,20 +128,20 @@ function parse_reconcile_arguments(argv: ReadonlyArray<string>): ReconcileArgume
|
|
|
127
128
|
// The promoted issue and its children. The number after `--promote` is the epic; everything else
|
|
128
129
|
// positional is a child. Refused when the epic would also be listed as its own child, which would
|
|
129
130
|
// have it block itself under `--ordered`.
|
|
130
|
-
function
|
|
131
|
-
const [raw_epic, ...raw_children] =
|
|
131
|
+
function to_promote_arguments(parsed: EpicArgv): PromoteArguments | undefined {
|
|
132
|
+
const [raw_epic, ...raw_children] = parsed.positionals
|
|
132
133
|
if (raw_epic === undefined || !ISSUE_NUMBER_PATTERN.test(raw_epic)) return undefined
|
|
133
134
|
const epic_number = Number(raw_epic)
|
|
134
135
|
const children = to_child_numbers(raw_children).filter((child) => child !== epic_number)
|
|
135
136
|
if (children.length === 0) return undefined
|
|
136
137
|
|
|
137
|
-
return {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
return { epic_number, children, ...read_batch_flags(parsed) }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parse_promote_arguments(argv: ReadonlyArray<string>): PromoteArguments | undefined {
|
|
142
|
+
const parsed = read_form(argv, PROMOTE_FORM)
|
|
143
|
+
|
|
144
|
+
return parsed === undefined ? undefined : to_promote_arguments(parsed)
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
// `--add <E> <N...> [--before <M> | --after <M> | --order-before <M> | --order-after <M>]`: the epic
|
|
@@ -195,36 +196,33 @@ const POSITION_FLAGS: ReadonlyArray<PositionFlag> = [
|
|
|
195
196
|
// of each direction, or a dependency-writing flag beside an order-only one. `read_flag_value` would
|
|
196
197
|
// answer with the first, which is a silent choice rather than a refusal — and here the silent choice
|
|
197
198
|
// would decide whether a `blocked-by` is written at all.
|
|
198
|
-
function
|
|
199
|
-
return POSITION_FLAGS.reduce((total, entry) => total + count_flag(
|
|
199
|
+
function count_position_flags(parsed: EpicArgv): number {
|
|
200
|
+
return POSITION_FLAGS.reduce((total, entry) => total + count_flag(parsed, entry.flag), 0)
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
interface PositionTarget extends PositionFlag {
|
|
203
204
|
raw: string
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
function to_position_target(
|
|
207
|
-
|
|
208
|
-
entry: PositionFlag,
|
|
209
|
-
): PositionTarget | undefined {
|
|
210
|
-
const raw = read_flag_value(argv, entry.flag)
|
|
207
|
+
function to_position_target(parsed: EpicArgv, entry: PositionFlag): PositionTarget | undefined {
|
|
208
|
+
const raw = read_flag_value(parsed, entry.flag)
|
|
211
209
|
|
|
212
210
|
return raw === undefined ? undefined : { ...entry, raw }
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
function read_position_target(
|
|
216
|
-
if (
|
|
213
|
+
function read_position_target(parsed: EpicArgv): PositionTarget | undefined {
|
|
214
|
+
if (count_position_flags(parsed) > 1) return undefined
|
|
217
215
|
|
|
218
|
-
return POSITION_FLAGS.map((entry) => to_position_target(
|
|
216
|
+
return POSITION_FLAGS.map((entry) => to_position_target(parsed, entry)).find(
|
|
219
217
|
(found) => found !== undefined,
|
|
220
218
|
)
|
|
221
219
|
}
|
|
222
220
|
|
|
223
221
|
// A target that is not an issue number is refused for the same reason two flags are: guessing would
|
|
224
222
|
// insert somewhere.
|
|
225
|
-
function parse_position(
|
|
226
|
-
const has_flag =
|
|
227
|
-
const target = read_position_target(
|
|
223
|
+
function parse_position(parsed: EpicArgv): PositionOutcome {
|
|
224
|
+
const has_flag = count_position_flags(parsed) > 0
|
|
225
|
+
const target = read_position_target(parsed)
|
|
228
226
|
if (target === undefined) return has_flag ? REFUSED_POSITION : NO_POSITION
|
|
229
227
|
if (!ISSUE_NUMBER_PATTERN.test(target.raw)) return REFUSED_POSITION
|
|
230
228
|
|
|
@@ -235,10 +233,6 @@ function parse_position(argv: ReadonlyArray<string>): PositionOutcome {
|
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
235
|
|
|
238
|
-
function has_unknown_flag(argv: ReadonlyArray<string>): boolean {
|
|
239
|
-
return epic_cli_argv.has_unknown_flag(argv, ADD_KNOWN_FLAGS)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
236
|
// Whether *this* is why the insertion could not be read, so the refusal can say so. Without it the
|
|
243
237
|
// caller prints the generic requirements line, and a shell that expanded `--decision-file $REC` to
|
|
244
238
|
// nothing is answered with "an epic number and at least one child issue number are required" — advice
|
|
@@ -248,9 +242,9 @@ function is_decision_path_unusable(argv: ReadonlyArray<string>): boolean {
|
|
|
248
242
|
}
|
|
249
243
|
|
|
250
244
|
function read_add_subject(
|
|
251
|
-
|
|
245
|
+
parsed: EpicArgv,
|
|
252
246
|
): { epic_number: number; children: Array<number> } | undefined {
|
|
253
|
-
const [raw_epic, ...raw_children] =
|
|
247
|
+
const [raw_epic, ...raw_children] = parsed.positionals
|
|
254
248
|
if (raw_epic === undefined || !ISSUE_NUMBER_PATTERN.test(raw_epic)) return undefined
|
|
255
249
|
const epic_number = Number(raw_epic)
|
|
256
250
|
const children = to_child_numbers(raw_children).filter((child) => child !== epic_number)
|
|
@@ -258,21 +252,28 @@ function read_add_subject(
|
|
|
258
252
|
return children.length === 0 ? undefined : { epic_number, children }
|
|
259
253
|
}
|
|
260
254
|
|
|
261
|
-
function
|
|
262
|
-
|
|
263
|
-
const subject = read_add_subject(argv)
|
|
255
|
+
function to_add_arguments(parsed: EpicArgv): AddArguments | undefined {
|
|
256
|
+
const subject = read_add_subject(parsed)
|
|
264
257
|
if (subject === undefined) return undefined
|
|
265
|
-
const outcome = parse_position(
|
|
258
|
+
const outcome = parse_position(parsed)
|
|
266
259
|
if (outcome.is_refused) return undefined
|
|
267
260
|
|
|
268
261
|
return {
|
|
269
262
|
...subject,
|
|
270
263
|
position: outcome.position,
|
|
271
264
|
is_order_only: outcome.is_order_only,
|
|
272
|
-
decision_path: read_flag_value(
|
|
265
|
+
decision_path: read_flag_value(parsed, DECISION_FLAG),
|
|
273
266
|
}
|
|
274
267
|
}
|
|
275
268
|
|
|
269
|
+
// An unknown flag is refused by the strict read: a mistyped positioning flag would otherwise leave its
|
|
270
|
+
// value positional, so it becomes a child and the edit lands somewhere else (joshuafolkken/kit#890).
|
|
271
|
+
function parse_add_arguments(argv: ReadonlyArray<string>): AddArguments | undefined {
|
|
272
|
+
const parsed = is_decision_path_unusable(argv) ? undefined : read_form(argv, ADD_FORM)
|
|
273
|
+
|
|
274
|
+
return parsed === undefined ? undefined : to_add_arguments(parsed)
|
|
275
|
+
}
|
|
276
|
+
|
|
276
277
|
// The one refusal `--add` has to explain rather than merely report. `into owner/repo#N` is a legal
|
|
277
278
|
// thing for a person to type, and this command cannot serve it: it reads and edits issues in the
|
|
278
279
|
// repository it is run from. Falling through to the usage line would read as "that form does not
|
|
@@ -288,7 +289,7 @@ interface CrossRepoAddTarget {
|
|
|
288
289
|
// problem, and
|
|
289
290
|
// a suggestion built from it would drop or mangle what the person typed.
|
|
290
291
|
function find_cross_repo_add_target(argv: ReadonlyArray<string>): CrossRepoAddTarget | undefined {
|
|
291
|
-
const [raw_epic] =
|
|
292
|
+
const [raw_epic] = positionals_of(argv, ADD_FORM)
|
|
292
293
|
if (raw_epic === undefined) return undefined
|
|
293
294
|
const epic = epic_parse.parse_external_reference(raw_epic)
|
|
294
295
|
if (epic === undefined) return undefined
|
|
@@ -351,7 +352,7 @@ function format_cross_repo_refusal(found: CrossRepoAddTarget): string {
|
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
function parse_check_argument(argv: ReadonlyArray<string>): number | undefined {
|
|
354
|
-
const [raw] =
|
|
355
|
+
const [raw] = positionals_of(argv, CHECK_FORM)
|
|
355
356
|
if (raw === undefined || !ISSUE_NUMBER_PATTERN.test(raw)) return undefined
|
|
356
357
|
|
|
357
358
|
return Number(raw)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { rest_comment_schema, type RestCommentData } from '#scripts/git/git-schemas'
|
|
2
3
|
import { read_json_listing } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { rest_comment_schema, type RestCommentData } from '#scripts/git/schemas'
|
|
4
4
|
import { epic_parse, type ExternalChild } from './epic-parse'
|
|
5
5
|
|
|
6
6
|
// The comment the epic auto-close announces itself with, and how a run recognizes the one a previous
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/git-schemas'
|
|
2
3
|
import { cutoff_cause, cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
3
4
|
import {
|
|
4
5
|
parse_json_array_or_undefined,
|
|
5
6
|
parse_json_object_safe,
|
|
6
7
|
} from '#scripts/git/parse-json-array'
|
|
7
|
-
import { epic_child_schema, epic_issue_schema, type EpicChildData } from '#scripts/git/schemas'
|
|
8
8
|
import { EPIC_LABEL } from '#scripts/issue/issue-labels'
|
|
9
9
|
import { error_text } from '#scripts/lib/error-message'
|
|
10
10
|
import { epic_close_comment } from './epic-close-comment'
|
|
@@ -2,6 +2,7 @@ import { repo_map_logic } from '#scripts/discovery/repo-map-logic'
|
|
|
2
2
|
import { repo_origin } from '#scripts/discovery/repo-origin'
|
|
3
3
|
import { git_gh_api_path } from '#scripts/gh/git-gh-api-path'
|
|
4
4
|
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
5
|
+
import { GH_API_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
5
6
|
import { propagate_publish } from '#scripts/propagate/propagate-publish'
|
|
6
7
|
import { derive_versions_endpoint } from '#scripts/version/version-command-config'
|
|
7
8
|
import { load } from 'js-yaml'
|
|
@@ -69,7 +70,6 @@ function is_published(repo: string, version: string): boolean {
|
|
|
69
70
|
return propagate_publish.is_version_published(versions, version)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
const GH_TIMEOUT_MS = 20_000
|
|
73
73
|
// The manifest arrives base64-encoded in the API's `content` field; `jq` decodes and reads it.
|
|
74
74
|
const VERSION_JQ = '.content | @base64d | fromjson | {version, private, workspaces} | tojson'
|
|
75
75
|
const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'
|
|
@@ -145,7 +145,7 @@ function pnpm_workspace_path(repo: string): string {
|
|
|
145
145
|
|
|
146
146
|
// A read that failed for any reason other than a missing file leaves the layout unknown.
|
|
147
147
|
function classify_workspace_failure(repo: string): WorkspaceCheck {
|
|
148
|
-
const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo),
|
|
148
|
+
const status = git_gh_exec.exec_gh_api_status_sync(pnpm_workspace_path(repo), GH_API_TIMEOUT_MS)
|
|
149
149
|
|
|
150
150
|
return status === NOT_FOUND_STATUS ? 'single' : 'unreadable'
|
|
151
151
|
}
|
|
@@ -154,7 +154,7 @@ function read_pnpm_workspace(repo: string): WorkspaceCheck {
|
|
|
154
154
|
const stdout = git_gh_exec.read_gh_api_sync({
|
|
155
155
|
path: pnpm_workspace_path(repo),
|
|
156
156
|
jq_filter: WORKSPACE_JQ,
|
|
157
|
-
timeout_ms:
|
|
157
|
+
timeout_ms: GH_API_TIMEOUT_MS,
|
|
158
158
|
})
|
|
159
159
|
if (stdout === undefined) return classify_workspace_failure(repo)
|
|
160
160
|
|
|
@@ -229,7 +229,7 @@ function to_manifest(repo: string, stdout: string): ManifestAnswer {
|
|
|
229
229
|
// here is trustworthy in a way a registry 404 is not: the repository's issues are already being read,
|
|
230
230
|
// so access is established and what is missing is the file.
|
|
231
231
|
function classify_manifest_failure(repo: string): ManifestAnswer {
|
|
232
|
-
const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo),
|
|
232
|
+
const status = git_gh_exec.exec_gh_api_status_sync(manifest_path(repo), GH_API_TIMEOUT_MS)
|
|
233
233
|
|
|
234
234
|
return { kind: status === NOT_FOUND_STATUS ? 'absent' : 'unreadable' }
|
|
235
235
|
}
|
|
@@ -238,7 +238,7 @@ function fetch_manifest(repo: string): ManifestAnswer {
|
|
|
238
238
|
const stdout = git_gh_exec.read_gh_api_sync({
|
|
239
239
|
path: manifest_path(repo),
|
|
240
240
|
jq_filter: VERSION_JQ,
|
|
241
|
-
timeout_ms:
|
|
241
|
+
timeout_ms: GH_API_TIMEOUT_MS,
|
|
242
242
|
})
|
|
243
243
|
|
|
244
244
|
return stdout === undefined ? classify_manifest_failure(repo) : to_manifest(repo, stdout)
|
|
@@ -121,7 +121,7 @@ function find_stuck_children(children: ReadonlyArray<EpicChild>): Array<string>
|
|
|
121
121
|
const index = index_children(children)
|
|
122
122
|
const remaining = new Set(children.map((child) => key_of(child)))
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
function peel(): number {
|
|
125
125
|
const ready = [...remaining].filter((node) => !has_remaining_blocker(index, node, remaining))
|
|
126
126
|
|
|
127
127
|
for (const node of ready) remaining.delete(node)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
2
3
|
import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
3
4
|
import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
|
|
4
|
-
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
5
5
|
import { EPIC_LABEL, has_any_label } from '#scripts/issue/issue-labels'
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
import { epic_parse } from './epic-parse'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_issue_rest } from '#scripts/gh/git-gh-issue-rest'
|
|
2
|
+
import { blocked_by_schema } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { blocked_by_schema } from '#scripts/git/schemas'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import type { IssueReference } from './epic-reference'
|
|
6
6
|
|
|
@@ -29,11 +29,7 @@ const PULL_REQUEST_SEGMENT = '/pull/'
|
|
|
29
29
|
const epic_issue_schema = z.object({
|
|
30
30
|
number: z.number(),
|
|
31
31
|
title: z.string().default(''),
|
|
32
|
-
body: z
|
|
33
|
-
.string()
|
|
34
|
-
.nullable()
|
|
35
|
-
.default('')
|
|
36
|
-
.transform((value) => value ?? ''),
|
|
32
|
+
body: z.union([z.string(), z.null().transform(() => '')]).default(''),
|
|
37
33
|
state: z.string().default(UNKNOWN_STATE),
|
|
38
34
|
// The issue endpoint answers for a pull request as readily as for an issue, and nothing in the
|
|
39
35
|
// other fields separates them — an open PR reports `state: OPEN`, a merged one `MERGED`. The URL
|
|
@@ -187,10 +187,10 @@ function solo_offer(
|
|
|
187
187
|
read: BusyRead,
|
|
188
188
|
request: LaneRequest,
|
|
189
189
|
): LaneOffer {
|
|
190
|
-
const { offered, notice } = epic_solo.select(answer.children, read, request.repo)
|
|
190
|
+
const { offered, notice = '' } = epic_solo.select(answer.children, read, request.repo)
|
|
191
191
|
|
|
192
192
|
if (offered.length === NO_LANES && answer.children.length > NO_LANES) {
|
|
193
|
-
return { children: [], verdict: WAIT_VERDICT, notice
|
|
193
|
+
return { children: [], verdict: WAIT_VERDICT, notice }
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
return { ...answer, children: offered, notice: offered_notice(offered, read, request) }
|
|
@@ -5,6 +5,7 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
|
5
5
|
import { PROJECT_ROOT } from '#scripts/init/init-paths'
|
|
6
6
|
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
7
7
|
import { lane_capacity } from '#scripts/lane/lane-capacity'
|
|
8
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
8
9
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
9
10
|
import { epic_classify } from './epic-classify'
|
|
10
11
|
import { epic_cross_repo } from './epic-cross-repo'
|
|
@@ -31,12 +32,10 @@ import { epic_report, type EpicNextResult } from './epic-report'
|
|
|
31
32
|
const SUCCESS_EXIT_CODE = 0
|
|
32
33
|
const FAILURE_EXIT_CODE = 1
|
|
33
34
|
const ARGV_OFFSET = 2
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
// changed
|
|
37
|
-
|
|
38
|
-
const LANES_FLAG = '--lanes'
|
|
39
|
-
const FLAG_PREFIX = '--'
|
|
35
|
+
// Asking for every free lane (`--lanes`) is opt-in, so the answer stays one token for a caller that
|
|
36
|
+
// has not been changed. `--repo` alone still prints exactly one number — what joshuafolkken/kit#1491
|
|
37
|
+
// changed for it is *when* that number appears, not how many arrive (joshuafolkken/kit#1491).
|
|
38
|
+
const OPTIONS = { repo: { type: 'string' }, lanes: { type: 'boolean' } } as const
|
|
40
39
|
const USAGE =
|
|
41
40
|
'Usage: josh epic:next <epic-number|owner/repo#number>... [--repo <owner/repo>] [--lanes]'
|
|
42
41
|
// A repository that could not be read is refused rather than stood in for. Since
|
|
@@ -66,30 +65,6 @@ interface NextOptions {
|
|
|
66
65
|
usage?: string
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
// A value that is itself a flag is no value at all. `--repo --lanes` used to narrow to a repository
|
|
70
|
-
// literally named `--lanes` and report `No runnable child in --lanes` with exit 0; refused here, it
|
|
71
|
-
// reaches the usage line instead — the caller meant a repository and named none.
|
|
72
|
-
function parse_repo(rest: ReadonlyArray<string>): string | undefined {
|
|
73
|
-
const flag_index = rest.indexOf(REPO_FLAG)
|
|
74
|
-
const value = flag_index === -1 ? undefined : rest[flag_index + 1]
|
|
75
|
-
|
|
76
|
-
return value?.startsWith(FLAG_PREFIX) === true ? undefined : value
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// The leading arguments are epic references and everything from the first flag on is options. Split
|
|
80
|
-
// on the flag rather than taking a fixed count, so `epic:next 858 909 --repo X` reads two epics and
|
|
81
|
-
// `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493).
|
|
82
|
-
function split_at_flag(argv: ReadonlyArray<string>): {
|
|
83
|
-
head: ReadonlyArray<string>
|
|
84
|
-
rest: ReadonlyArray<string>
|
|
85
|
-
} {
|
|
86
|
-
const flag_at = argv.findIndex((entry) => entry.startsWith(FLAG_PREFIX))
|
|
87
|
-
|
|
88
|
-
return flag_at === -1
|
|
89
|
-
? { head: argv, rest: [] }
|
|
90
|
-
: { head: argv.slice(0, flag_at), rest: argv.slice(flag_at) }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
68
|
// Every leading entry, or nothing. One entry that does not parse fails the whole read rather than
|
|
94
69
|
// being dropped: a mistyped second epic would otherwise run the first one alone and report nothing
|
|
95
70
|
// at all about the one that was missed — an unattended run silently doing half of what was asked.
|
|
@@ -106,21 +81,34 @@ function parse_references(head: ReadonlyArray<string>): ReadonlyArray<EpicRefere
|
|
|
106
81
|
return references.length === 0 ? undefined : references
|
|
107
82
|
}
|
|
108
83
|
|
|
84
|
+
function with_repo(
|
|
85
|
+
references: ReadonlyArray<EpicReference>,
|
|
86
|
+
values: { repo?: string; lanes?: boolean },
|
|
87
|
+
): NextOptions {
|
|
88
|
+
const { repo, lanes: is_all_lanes = false } = values
|
|
89
|
+
|
|
90
|
+
if (repo === undefined) return is_all_lanes ? { usage: USAGE } : { references }
|
|
91
|
+
|
|
92
|
+
return { references, repo, is_all_lanes }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The positional arguments are epic references and the flags are options, so `epic:next 858 909 --repo
|
|
96
|
+
// X` reads two epics and `epic:next 858 --repo X` still reads one (joshuafolkken/kit#1493). The read is
|
|
97
|
+
// strict: an unknown flag reaches the usage line rather than being ignored, and a value that is itself
|
|
98
|
+
// a flag is no value at all — `--repo --lanes` used to narrow to a repository literally named
|
|
99
|
+
// `--lanes` and report `No runnable child in --lanes` with exit 0.
|
|
100
|
+
//
|
|
109
101
|
// `--lanes` without `--repo` is refused rather than ignored: the lane count is a property of one
|
|
110
102
|
// repository, so the aggregate listing has nothing to apply it to, and a flag that silently does
|
|
111
|
-
// nothing is a caller believing it asked for something.
|
|
103
|
+
// nothing is a caller believing it asked for something. An empty name (`--repo=` from an unset
|
|
104
|
+
// variable) is no repository either: narrowed to it, the answer is `No runnable child in .` and exit 0.
|
|
112
105
|
function parse_options(argv: ReadonlyArray<string>): NextOptions {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const is_all_lanes = rest.includes(LANES_FLAG)
|
|
118
|
-
|
|
119
|
-
if (repo === undefined) {
|
|
120
|
-
return is_all_lanes || rest.includes(REPO_FLAG) ? { usage: USAGE } : { references }
|
|
121
|
-
}
|
|
106
|
+
const parsed = cli_flags.arguments_of(argv, OPTIONS)
|
|
107
|
+
if (parsed === undefined) return { usage: USAGE }
|
|
108
|
+
const references = parse_references(parsed.positionals)
|
|
109
|
+
if (references === undefined || parsed.values.repo === '') return { usage: USAGE }
|
|
122
110
|
|
|
123
|
-
return
|
|
111
|
+
return with_repo(references, parsed.values)
|
|
124
112
|
}
|
|
125
113
|
|
|
126
114
|
// The answer for one epic, from an already-fetched snapshot. Split from the fetch so the whole
|
|
@@ -446,7 +434,6 @@ const epic_next = {
|
|
|
446
434
|
is_order_declared,
|
|
447
435
|
repo_verdict,
|
|
448
436
|
UNCHECKED_EXCLUSION,
|
|
449
|
-
split_at_flag,
|
|
450
437
|
parse_options,
|
|
451
438
|
refuse_reads,
|
|
452
439
|
views_of,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
2
|
-
import type { OpenIssueData } from '#scripts/git/schemas'
|
|
2
|
+
import type { OpenIssueData } from '#scripts/git/git-schemas'
|
|
3
3
|
import { has_any_label, IN_PROGRESS_LABEL, RUN_SOLO_LABEL } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { lane_await } from '#scripts/lane/lane-await'
|
|
5
5
|
import { lane_registry } from '#scripts/lane/lane-registry'
|
|
@@ -20,12 +20,16 @@ type TriageVerdict = 'triage'
|
|
|
20
20
|
|
|
21
21
|
const TRIAGE_VERDICT: TriageVerdict = 'triage'
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
// Read off a bare label list too, so `josh issue:file` refuses an untriaged `auto-ok` filing by the
|
|
24
|
+
// same answer the offer paths withhold it by (joshuafolkken/kit#3313).
|
|
25
|
+
function has_triage_label(labels: ReadonlyArray<string>): boolean {
|
|
26
26
|
return has_label_name(labels, RUN_SOLO_LABEL) || has_label_name(labels, RUN_LANE_LABEL)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function is_triaged(child: EpicChild): boolean {
|
|
30
|
+
return has_triage_label(child.labels)
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
// The candidates nobody has judged, in the order they were given.
|
|
30
34
|
function untriaged(children: ReadonlyArray<EpicChild>): ReadonlyArray<EpicChild> {
|
|
31
35
|
return children.filter((child) => !is_triaged(child))
|
|
@@ -41,7 +45,7 @@ function message(children: ReadonlyArray<EpicChild>, repo: string): string {
|
|
|
41
45
|
)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
const epic_triage = { TRIAGE_VERDICT, is_triaged, message, untriaged }
|
|
48
|
+
const epic_triage = { TRIAGE_VERDICT, has_triage_label, is_triaged, message, untriaged }
|
|
45
49
|
|
|
46
50
|
export type { TriageVerdict }
|
|
47
51
|
export { epic_triage }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { epic_subject_schema } from '#scripts/git/git-schemas'
|
|
1
2
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
2
|
-
import { epic_subject_schema } from '#scripts/git/schemas'
|
|
3
3
|
import { EPIC_LABEL, has_label_name } from '#scripts/issue/issue-labels'
|
|
4
4
|
import { epic_parse, type DeclarationState } from './epic-parse'
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { git_next_issues } from '#scripts/issue/git-next-issues'
|
|
2
2
|
import { review_attest } from '#scripts/review/review-attest'
|
|
3
3
|
import { review_stamps } from '#scripts/review/review-stamps'
|
|
4
|
-
import { run_hold } from '#scripts/run/run-hold'
|
|
5
|
-
import { run_progress_clock } from '#scripts/run/run-progress-clock'
|
|
4
|
+
import { run_hold } from '#scripts/run/hold/run-hold'
|
|
5
|
+
import { run_progress_clock } from '#scripts/run/progress/run-progress-clock'
|
|
6
6
|
import { parse_completed_issue_number } from './followup-issue-number'
|
|
7
7
|
import { git_followup_cleanup, type CleanupStep } from './git-followup-cleanup'
|
|
8
8
|
import { git_followup_pending } from './git-followup-pending'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
2
|
+
import { issue_label_schema } from '#scripts/git/git-schemas'
|
|
2
3
|
import { parse_json_object_safe } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { issue_label_schema } from '#scripts/git/schemas'
|
|
4
4
|
import { IN_PROGRESS_LABEL, label_name_of } from '#scripts/issue/issue-labels'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ import { z } from 'zod'
|
|
|
19
19
|
// No procedure covered the ordinary ending: merged, closed, and still marked as running.
|
|
20
20
|
|
|
21
21
|
// The `number,labels,body` read answers labels as objects, and this is the one field of it this
|
|
22
|
-
// step needs. The element shape is `schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
22
|
+
// step needs. The element shape is `git-schemas.ts`'s rather than a second spelling of it; the wrapper
|
|
23
23
|
// is local because a read's *field list* is what multiplies helpers when it is shared
|
|
24
24
|
// (`git-gh-issue-read.ts`).
|
|
25
25
|
const labels_read_schema = z.object({ labels: z.array(issue_label_schema).optional() })
|
|
@@ -66,8 +66,8 @@ function is_posted_since(comment: IssueComment, merged_at: string): boolean {
|
|
|
66
66
|
return Date.parse(comment.created_at) >= Date.parse(merged_at)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function parse_comments(comments_json: string | undefined): Array<IssueComment> {
|
|
70
|
-
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json
|
|
69
|
+
function parse_comments(comments_json: string | undefined = ''): Array<IssueComment> {
|
|
70
|
+
const parsed = COMMENTS_SCHEMA.safeParse(json_value.parse_or_undefined(comments_json))
|
|
71
71
|
|
|
72
72
|
return parsed.success ? parsed.data : []
|
|
73
73
|
}
|