@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
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// moved to "read it later" is a rule that measurably never fires, and this moves nothing to later.
|
|
14
14
|
|
|
15
15
|
import { file_reader } from '#scripts/lib/read-file'
|
|
16
|
+
import { document_scan } from './document-scan'
|
|
16
17
|
|
|
17
18
|
// `#` to `######`, one space, then the title. Written with a literal space rather than `\s+` and a
|
|
18
19
|
// lazy title so it cannot backtrack: `sonarjs/super-linear-regex` rejects the `\s+…+?\s*$` shape,
|
|
@@ -21,6 +22,17 @@ const HEADING_PATTERN = /^(?<hashes>#{1,6}) (?<title>.*)$/u
|
|
|
21
22
|
const FENCE = '```'
|
|
22
23
|
const SINGLE_MATCH = 1
|
|
23
24
|
|
|
25
|
+
// Any bold span is a citable anchor: the documents point at `- **Cross-package problems → …**` at a
|
|
26
|
+
// line start and at a `**…**` emphasis mid-paragraph alike.
|
|
27
|
+
const BOLD_LABEL_PATTERN = /\*\*([^*]+?)\*\*/gu
|
|
28
|
+
const BOLD_DELIMITER = '**'
|
|
29
|
+
// A one-line inline code span: a double-backtick span (which may quote a backtick) or a single one.
|
|
30
|
+
const CODE_SPAN_PATTERN = /``[^\n]*?``|`[^`\n]*`/gu
|
|
31
|
+
// A label ranks below every heading, so a heading section never ends at one.
|
|
32
|
+
const LABEL_LEVEL = 7
|
|
33
|
+
// A list item opens with `-`, `*` or a number, after any indentation.
|
|
34
|
+
const LIST_ITEM_PATTERN = /^(?<indent>\s*)(?:[-*]|\d+\.) /u
|
|
35
|
+
|
|
24
36
|
interface Heading {
|
|
25
37
|
level: number
|
|
26
38
|
title: string
|
|
@@ -58,13 +70,27 @@ function push_heading(found: Array<Heading>, line: string, index: number): void
|
|
|
58
70
|
// **Fenced blocks are skipped, and that is not defensive tidying.** These documents quote shell, and
|
|
59
71
|
// a shell comment at the start of a line is a `#` in column one. Counted as a heading it would end
|
|
60
72
|
// the section above it early, which is a silent wrong answer rather than a visible failure.
|
|
73
|
+
// One flag per line — `true` for a fence delimiter and every line between a pair of them — so the
|
|
74
|
+
// heading scan and the label scan cannot disagree on what is quoted code.
|
|
75
|
+
function fenced_flags(lines: ReadonlyArray<string>): Array<boolean> {
|
|
76
|
+
let is_inside_fence = false
|
|
77
|
+
|
|
78
|
+
return lines.map(function is_fenced(line: string): boolean {
|
|
79
|
+
if (!line.startsWith(FENCE)) return is_inside_fence
|
|
80
|
+
|
|
81
|
+
is_inside_fence = !is_inside_fence
|
|
82
|
+
|
|
83
|
+
return true
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
61
87
|
function headings(markdown: string): Array<Heading> {
|
|
88
|
+
const lines = markdown.split('\n')
|
|
89
|
+
const fenced = fenced_flags(lines)
|
|
62
90
|
const found: Array<Heading> = []
|
|
63
|
-
let is_inside_fence = false
|
|
64
91
|
|
|
65
|
-
for (const [index, line] of
|
|
66
|
-
if (
|
|
67
|
-
else if (!is_inside_fence) push_heading(found, line, index)
|
|
92
|
+
for (const [index, line] of lines.entries()) {
|
|
93
|
+
if (fenced[index] !== true) push_heading(found, line, index)
|
|
68
94
|
}
|
|
69
95
|
|
|
70
96
|
return found
|
|
@@ -74,24 +100,77 @@ function titles(markdown: string): Array<string> {
|
|
|
74
100
|
return headings(markdown).map((heading) => heading.title)
|
|
75
101
|
}
|
|
76
102
|
|
|
103
|
+
// The zero-based line each character offset sits on, read as the number of line starts at or before it.
|
|
104
|
+
function line_starts(markdown: string): Array<number> {
|
|
105
|
+
return [...markdown.matchAll(/\n/gu)].map((match) => match.index + SINGLE_MATCH)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function line_of(starts: ReadonlyArray<number>, offset: number): number {
|
|
109
|
+
return starts.filter((start) => start <= offset).length
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// **A bold label is an anchor too** (joshuafolkken/kit#3248). The documents cite
|
|
113
|
+
// `- **Output language follows …**` exactly as they cite a heading, and the reference tests accepted
|
|
114
|
+
// both while this reader found only headings — so a green suite promised a section the command then
|
|
115
|
+
// refused. A label may wrap across lines, so the scan runs over the whole text, not line by line.
|
|
116
|
+
//
|
|
117
|
+
// Fenced lines are blanked before the scan rather than filtered after it: a `docs/**` glob in a
|
|
118
|
+
// fence would otherwise take a delimiter and pair every later `**` with the wrong partner. Blanking
|
|
119
|
+
// keeps the line count, so a label's line number still indexes the original document.
|
|
120
|
+
function unfenced_text(markdown: string): string {
|
|
121
|
+
const lines = markdown.split('\n')
|
|
122
|
+
const fenced = fenced_flags(lines)
|
|
123
|
+
|
|
124
|
+
return lines.map((line, index) => (fenced[index] === true ? '' : line)).join('\n')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// An inline code span gets the same treatment one level down: `prompts/**` mid-paragraph would take a
|
|
128
|
+
// delimiter just as a fenced glob does. Its `*` become spaces, so every offset still indexes the
|
|
129
|
+
// unmasked text — which is where the title is read from, keeping a label that quotes code intact.
|
|
130
|
+
function code_masked(text: string): string {
|
|
131
|
+
return text.replaceAll(CODE_SPAN_PATTERN, (span) => span.replaceAll('*', ' '))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function labels(markdown: string): Array<Heading> {
|
|
135
|
+
const text = unfenced_text(markdown)
|
|
136
|
+
const starts = line_starts(text)
|
|
137
|
+
|
|
138
|
+
return [...code_masked(text).matchAll(BOLD_LABEL_PATTERN)].map((match) => ({
|
|
139
|
+
level: LABEL_LEVEL,
|
|
140
|
+
title: text.slice(
|
|
141
|
+
match.index + BOLD_DELIMITER.length,
|
|
142
|
+
match.index + match[0].length - BOLD_DELIMITER.length,
|
|
143
|
+
),
|
|
144
|
+
line: line_of(starts, match.index),
|
|
145
|
+
}))
|
|
146
|
+
}
|
|
147
|
+
|
|
77
148
|
// **Prefix, because a reference cites the name and the heading carries the name plus its gloss.**
|
|
78
149
|
// `` → "The hand-off" `` points at `## The hand-off — one session does not have to run the whole
|
|
79
150
|
// epic`, and `` → "The check is asked at every merge" `` at a heading that continues with a comma.
|
|
80
151
|
// Requiring the whole heading would make every such reference unresolvable, and citing the whole
|
|
81
|
-
// heading in the prose would put the gloss into every pointer.
|
|
152
|
+
// heading in the prose would put the gloss into every pointer. Both sides are compared in the forms
|
|
153
|
+
// `document_scan.reference_forms` gives, so a reference that wraps or drops a heading's number lands.
|
|
82
154
|
function is_match(heading: Heading, wanted: string): boolean {
|
|
83
|
-
return heading.title.startsWith(wanted)
|
|
155
|
+
return document_scan.reference_forms(heading.title).some((form) => form.startsWith(wanted))
|
|
84
156
|
}
|
|
85
157
|
|
|
86
158
|
function prefixed(found: ReadonlyArray<Heading>, wanted: string): Array<Heading> {
|
|
87
|
-
|
|
159
|
+
const normalized = document_scan.normalize_reference(wanted)
|
|
160
|
+
|
|
161
|
+
return found.filter((heading) => is_match(heading, normalized))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function is_exact(heading: Heading, wanted: string): boolean {
|
|
165
|
+
return document_scan.reference_forms(heading.title).includes(wanted)
|
|
88
166
|
}
|
|
89
167
|
|
|
90
168
|
// **Exact wins, and an ambiguous prefix is refused rather than ranked.** Two headings that both
|
|
91
169
|
// start with the cited text is a document whose reference is genuinely ambiguous; picking the first
|
|
92
170
|
// would hand back a section nobody named, which is a failure a whole-file read does not have.
|
|
93
171
|
function select(found: ReadonlyArray<Heading>, wanted: string): Heading | undefined {
|
|
94
|
-
const
|
|
172
|
+
const normalized = document_scan.normalize_reference(wanted)
|
|
173
|
+
const exact = found.find((heading) => is_exact(heading, normalized))
|
|
95
174
|
|
|
96
175
|
if (exact !== undefined) return exact
|
|
97
176
|
|
|
@@ -103,34 +182,82 @@ function select(found: ReadonlyArray<Heading>, wanted: string): Heading | undefi
|
|
|
103
182
|
// A section ends where the next heading of the same or higher rank begins, so a `##` section carries
|
|
104
183
|
// its `###` subsections with it — which is what a pointer at a `##` heading means.
|
|
105
184
|
function last_line_of(found: ReadonlyArray<Heading>, chosen: Heading, total_lines: number): number {
|
|
106
|
-
const
|
|
107
|
-
const next = found.slice(index + SINGLE_MATCH).find((heading) => heading.level <= chosen.level)
|
|
185
|
+
const next = found.find((heading) => heading.line > chosen.line && heading.level <= chosen.level)
|
|
108
186
|
|
|
109
187
|
return (next === undefined ? total_lines : next.line) - SINGLE_MATCH
|
|
110
188
|
}
|
|
111
189
|
|
|
112
|
-
function
|
|
113
|
-
|
|
190
|
+
function indent_of(line: string): number {
|
|
191
|
+
return line.length - line.trimStart().length
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Where the block a label sits in stops: a blank line, a heading, or a list item at the label line's
|
|
195
|
+
// own depth or shallower — so a label's nested sub-items come with it and its next sibling does not.
|
|
196
|
+
function is_sibling_item(line: string, depth: number): boolean {
|
|
197
|
+
const { indent } = LIST_ITEM_PATTERN.exec(line)?.groups ?? {}
|
|
198
|
+
|
|
199
|
+
return indent !== undefined && indent.length <= depth
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ends_block(line: string, depth: number): boolean {
|
|
203
|
+
if (line.trim() === '' || HEADING_PATTERN.test(line)) return true
|
|
204
|
+
|
|
205
|
+
return is_sibling_item(line, depth)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A label's section is the paragraph or list item it opens, not the rest of the document.
|
|
209
|
+
function label_last_line(lines: ReadonlyArray<string>, label: Heading): number {
|
|
210
|
+
const depth = indent_of(lines[label.line] ?? '')
|
|
211
|
+
const end = lines.findIndex((line, index) => index > label.line && ends_block(line, depth))
|
|
212
|
+
|
|
213
|
+
return (end === -1 ? lines.length : end) - SINGLE_MATCH
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// **A heading answers before any label does.** A label is consulted only when no heading even
|
|
217
|
+
// starts with the cited text, so an ambiguous heading reference is refused rather than rescued by a
|
|
218
|
+
// bold span that happens to share its prefix.
|
|
219
|
+
function choose(markdown: string, wanted: string): Heading | undefined {
|
|
114
220
|
const found = headings(markdown)
|
|
115
|
-
|
|
221
|
+
|
|
222
|
+
if (prefixed(found, wanted).length > 0) return select(found, wanted)
|
|
223
|
+
|
|
224
|
+
return select(labels(markdown), wanted)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function extent_of(markdown: string, chosen: Heading): number {
|
|
228
|
+
const lines = markdown.split('\n')
|
|
229
|
+
|
|
230
|
+
if (chosen.level === LABEL_LEVEL) return label_last_line(lines, chosen)
|
|
231
|
+
|
|
232
|
+
return last_line_of(headings(markdown), chosen, lines.length)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function section(markdown: string, wanted: string): Section | undefined {
|
|
236
|
+
const chosen = choose(markdown, wanted)
|
|
116
237
|
|
|
117
238
|
if (chosen === undefined) return undefined
|
|
118
239
|
|
|
119
|
-
const last_line =
|
|
240
|
+
const last_line = extent_of(markdown, chosen)
|
|
241
|
+
const lines = markdown.split('\n').slice(chosen.line, last_line + SINGLE_MATCH)
|
|
120
242
|
|
|
121
243
|
return {
|
|
122
244
|
title: chosen.title,
|
|
123
245
|
level: chosen.level,
|
|
124
246
|
first_line: chosen.line,
|
|
125
247
|
last_line,
|
|
126
|
-
text: lines.
|
|
248
|
+
text: lines.join('\n'),
|
|
127
249
|
}
|
|
128
250
|
}
|
|
129
251
|
|
|
130
252
|
// Ambiguity and absence are different answers, and the caller reports them differently: one names
|
|
131
253
|
// the candidates, the other lists every heading the file has.
|
|
254
|
+
// Candidates follow `choose`'s order — headings, and labels only when no heading matches — so a
|
|
255
|
+
// refusal never suggests a label that citing it could not select.
|
|
132
256
|
function candidates(markdown: string, wanted: string): Array<string> {
|
|
133
|
-
|
|
257
|
+
const near = prefixed(headings(markdown), wanted)
|
|
258
|
+
const listed = near.length > 0 ? near : prefixed(labels(markdown), wanted)
|
|
259
|
+
|
|
260
|
+
return listed.map((heading) => heading.title)
|
|
134
261
|
}
|
|
135
262
|
|
|
136
263
|
function read_optional(file_path: string): string | undefined {
|
|
@@ -70,7 +70,7 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
70
70
|
// list, so its ~48KB read crossed a block downward and the recorded ceiling drops to the block multiple
|
|
71
71
|
// the stale-ratchet message named. The other entries still read it, so their rows hold.
|
|
72
72
|
// joshuafolkken/kit#2345 added the `implementation-unit` delegation row (full fan-out procedure in
|
|
73
|
-
// `docs/josh-commands-
|
|
73
|
+
// `docs/josh-commands-backlog.md` → "`josh fanout`"); `delegation.md` gained only a one-clause mention and a
|
|
74
74
|
// pointer, folded into the existing enumeration and offset by tightening `delegation.md` prose, so every entry
|
|
75
75
|
// stayed within its block (kickoff has no block of headroom — its pre-#2294 ceiling is the next
|
|
76
76
|
// multiple, which the downward ratchet holds shut).
|
|
@@ -101,12 +101,14 @@ const ENTRY_READ_BUDGET: ReadonlyArray<EntryBudget> = [
|
|
|
101
101
|
// provider prose moved to `docs/josh-commands-automation.md`, and the failure-only sections moved to
|
|
102
102
|
// `backlogrun-recovery.md`, which the parent's reach still charges whole.
|
|
103
103
|
// joshuafolkken/kit#3171 cut `CLAUDE.md` to triggers and pointers (~12 KB), lowering every entry.
|
|
104
|
-
|
|
104
|
+
// joshuafolkken/kit#3256 moved `CLAUDE.md`'s residency prose to `residency.md`, lowering kickoff and
|
|
105
|
+
// halfrun one block each.
|
|
106
|
+
{ entry: 'kickoff', bytes: 40_960 },
|
|
105
107
|
// Raised in joshuafolkken/kit#3177: #3173, #3181 and #3183 merged in parallel and together tipped
|
|
106
108
|
// fullrun 12 bytes over its block; none of #3177's documents are in its read set.
|
|
107
109
|
// Lowered back in joshuafolkken/kit#3180: the history and lint-settled proofs left its read set.
|
|
108
110
|
{ entry: 'fullrun', bytes: 57_344 },
|
|
109
|
-
{ entry: 'halfrun', bytes:
|
|
111
|
+
{ entry: 'halfrun', bytes: 53_248 },
|
|
110
112
|
{ entry: 'prrun', bytes: 61_440 },
|
|
111
113
|
{ entry: 'backlogrun', bytes: 176_128 },
|
|
112
114
|
{ entry: LANE_CHILD, bytes: 57_344 },
|
|
@@ -14,20 +14,20 @@
|
|
|
14
14
|
|
|
15
15
|
import { existsSync } from 'node:fs'
|
|
16
16
|
import path from 'node:path'
|
|
17
|
-
import { cost_tokens } from '#scripts/cost-runtime/cost-tokens'
|
|
18
17
|
import { PACKAGE_DIR } from '#scripts/init/init-paths'
|
|
19
18
|
import { bash_output_cap_reader } from './bash-output-cap'
|
|
20
|
-
import { document_section
|
|
19
|
+
import { document_section } from './document-section'
|
|
20
|
+
import { read_set_cost, type Cost } from './read-set-cost'
|
|
21
21
|
import { read_set_point_of_use } from './read-set-point-of-use'
|
|
22
22
|
|
|
23
23
|
const { POINT_OF_USE_FILES, POINT_OF_USE_BY_ENTRY, is_point_of_use, point_of_use_files } =
|
|
24
24
|
read_set_point_of_use
|
|
25
|
+
const { cost_of, scoped_file_cost, total } = read_set_cost
|
|
25
26
|
|
|
26
27
|
const SKILL_DIRECTORY = path.join('.claude', 'skills', 'workflow-commands')
|
|
27
28
|
const SKILL_FILE = 'SKILL.md'
|
|
28
29
|
const TABLE_SECTION = '1. Which file to read'
|
|
29
30
|
const NOTHING = 0
|
|
30
|
-
const ONE_LINE = 1
|
|
31
31
|
|
|
32
32
|
// The point-of-use classification — which documents an entry names but reads only at a later step —
|
|
33
33
|
// is its own concern, split into `read-set-point-of-use.ts` in joshuafolkken/kit#2289. This file
|
|
@@ -54,11 +54,6 @@ interface SectionReference {
|
|
|
54
54
|
heading: string
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
interface Cost {
|
|
58
|
-
bytes: number
|
|
59
|
-
tokens: number
|
|
60
|
-
}
|
|
61
|
-
|
|
62
57
|
interface SectionCost extends SectionReference {
|
|
63
58
|
cost: Cost
|
|
64
59
|
is_resolved: boolean
|
|
@@ -297,21 +292,6 @@ function read_set(root: string, entry: string): ReadSet {
|
|
|
297
292
|
return { entry, files, sections: sections_for(root, entry, files) }
|
|
298
293
|
}
|
|
299
294
|
|
|
300
|
-
function cost_of(text: string): Cost {
|
|
301
|
-
return { bytes: Buffer.byteLength(text, 'utf8'), tokens: cost_tokens.estimate(text) }
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function total(costs: ReadonlyArray<Cost>): Cost {
|
|
305
|
-
const running = { bytes: NOTHING, tokens: NOTHING }
|
|
306
|
-
|
|
307
|
-
for (const cost of costs) {
|
|
308
|
-
running.bytes += cost.bytes
|
|
309
|
-
running.tokens += cost.tokens
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return running
|
|
313
|
-
}
|
|
314
|
-
|
|
315
295
|
function section_cost(root: string, reference: SectionReference): SectionCost {
|
|
316
296
|
const found = document_section.section(read_document(root, reference.file), reference.heading)
|
|
317
297
|
|
|
@@ -334,74 +314,13 @@ function headings_for(sections: ReadonlyArray<SectionReference>, file: string):
|
|
|
334
314
|
return sections.filter((reference) => reference.file === file).map((one) => one.heading)
|
|
335
315
|
}
|
|
336
316
|
|
|
337
|
-
|
|
338
|
-
for (let index = found.first_line; index <= found.last_line; index += ONE_LINE) charged.add(index)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// `undefined` the moment one heading does not resolve, because that reference is charged at its whole
|
|
342
|
-
// file and there is then nothing to union.
|
|
343
|
-
function charged_lines(markdown: string, headings: ReadonlyArray<string>): Set<number> | undefined {
|
|
344
|
-
const charged = new Set<number>()
|
|
345
|
-
|
|
346
|
-
for (const heading of headings) {
|
|
347
|
-
const found = document_section.section(markdown, heading)
|
|
348
|
-
|
|
349
|
-
if (found === undefined) return undefined
|
|
350
|
-
|
|
351
|
-
add_range(charged, found)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return charged
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// **The union's charged lines are split into contiguous runs, and each run is costed on its own
|
|
358
|
-
// natural text.** Joining non-adjacent lines with `\n` into one string fabricates a token boundary at
|
|
359
|
-
// every gap the union skipped, so the estimate could drift a token *above* the per-reference sum even
|
|
360
|
-
// with no real overlap — a false negative saving the measurement must never print
|
|
361
|
-
// (joshuafolkken/kit#1934). A run is a maximal stretch of consecutive charged lines, which is exactly
|
|
362
|
-
// the contiguous text a reader of that section actually reads, so summing the runs both mirrors the
|
|
363
|
-
// real read and keeps `scoped` at or below the per-reference sum by construction.
|
|
364
|
-
function extend_or_start(runs: Array<Array<number>>, index: number): void {
|
|
365
|
-
const current = runs.at(-1)
|
|
366
|
-
|
|
367
|
-
if (current?.at(-1) === index - ONE_LINE) current.push(index)
|
|
368
|
-
else runs.push([index])
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function contiguous_runs(charged: ReadonlySet<number>): Array<Array<number>> {
|
|
372
|
-
const runs: Array<Array<number>> = []
|
|
373
|
-
const ascending = [...charged].toSorted((left, right) => left - right)
|
|
374
|
-
|
|
375
|
-
for (const index of ascending) extend_or_start(runs, index)
|
|
376
|
-
|
|
377
|
-
return runs
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function run_text(lines: ReadonlyArray<string>, run: ReadonlyArray<number>): string {
|
|
381
|
-
return run.map((index) => lines[index] ?? '').join('\n')
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// **Charged per file over the union of the lines its references cover, never per reference.**
|
|
385
|
-
// Summing the references instead double-counts the two ways one file can be cited twice — two
|
|
386
|
-
// unresolved headings each charged at the whole file, and a `##` section cited beside one of its own
|
|
387
|
-
// `###` children, which `section()` already returns inside the parent. Either one could push the
|
|
388
|
-
// scoped figure above the whole one and print a *negative* saving, which is the single direction this
|
|
389
|
-
// measurement must not be able to move (joshuafolkken/kit#1776 review round 1).
|
|
390
|
-
function file_scoped_cost(root: string, file: string, headings: ReadonlyArray<string>): Cost {
|
|
391
|
-
const markdown = read_document(root, file)
|
|
392
|
-
const charged = charged_lines(markdown, headings)
|
|
393
|
-
|
|
394
|
-
if (charged === undefined) return cost_of(markdown)
|
|
395
|
-
|
|
396
|
-
const lines = markdown.split('\n')
|
|
397
|
-
|
|
398
|
-
return total(contiguous_runs(charged).map((run) => cost_of(run_text(lines, run))))
|
|
399
|
-
}
|
|
400
|
-
|
|
317
|
+
// The per-file union that keeps `scoped` at or below `whole` lives in `read-set-cost.ts`.
|
|
401
318
|
function scoped_cost(root: string, sections: ReadonlyArray<SectionReference>): Cost {
|
|
402
319
|
const files = unique(sections.map((reference) => reference.file))
|
|
403
320
|
|
|
404
|
-
return total(
|
|
321
|
+
return total(
|
|
322
|
+
files.map((file) => scoped_file_cost(read_document(root, file), headings_for(sections, file))),
|
|
323
|
+
)
|
|
405
324
|
}
|
|
406
325
|
|
|
407
326
|
function file_costs(root: string, names: ReadonlyArray<string>): Array<FileCost> {
|
|
@@ -491,5 +410,7 @@ const entry_read_set = {
|
|
|
491
410
|
total,
|
|
492
411
|
}
|
|
493
412
|
|
|
494
|
-
export type {
|
|
413
|
+
export type { FileCost, ReadSet, ReadSetCost, SectionCost, SectionReference }
|
|
495
414
|
export { entry_read_set }
|
|
415
|
+
|
|
416
|
+
export { type Cost } from './read-set-cost'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cost_tokens } from '#scripts/cost-runtime/cost-tokens'
|
|
2
|
+
import { document_section, type Section } from './document-section'
|
|
3
|
+
|
|
4
|
+
// What a read costs, and the scoped union that charges one file's cited sections once — split out
|
|
5
|
+
// of `entry-read-set.ts` when it reached its line limit (joshuafolkken/kit#3264). `entry_read_set`
|
|
6
|
+
// re-exports `cost_of`, `total` and `Cost` under the names they always had, so the move changed no
|
|
7
|
+
// call site.
|
|
8
|
+
|
|
9
|
+
const NOTHING = 0
|
|
10
|
+
const ONE_LINE = 1
|
|
11
|
+
|
|
12
|
+
interface Cost {
|
|
13
|
+
bytes: number
|
|
14
|
+
tokens: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function cost_of(text: string): Cost {
|
|
18
|
+
return { bytes: Buffer.byteLength(text, 'utf8'), tokens: cost_tokens.estimate(text) }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function total(costs: ReadonlyArray<Cost>): Cost {
|
|
22
|
+
const running = { bytes: NOTHING, tokens: NOTHING }
|
|
23
|
+
|
|
24
|
+
for (const cost of costs) {
|
|
25
|
+
running.bytes += cost.bytes
|
|
26
|
+
running.tokens += cost.tokens
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return running
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function add_range(charged: Set<number>, found: Section): void {
|
|
33
|
+
for (let index = found.first_line; index <= found.last_line; index += ONE_LINE) charged.add(index)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `undefined` the moment one heading does not resolve, because that reference is charged at its whole
|
|
37
|
+
// file and there is then nothing to union.
|
|
38
|
+
function charged_lines(markdown: string, headings: ReadonlyArray<string>): Set<number> | undefined {
|
|
39
|
+
const charged = new Set<number>()
|
|
40
|
+
|
|
41
|
+
for (const heading of headings) {
|
|
42
|
+
const found = document_section.section(markdown, heading)
|
|
43
|
+
|
|
44
|
+
if (found === undefined) return undefined
|
|
45
|
+
|
|
46
|
+
add_range(charged, found)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return charged
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// **The union's charged lines are split into contiguous runs, and each run is costed on its own
|
|
53
|
+
// natural text.** Joining non-adjacent lines with `\n` into one string fabricates a token boundary at
|
|
54
|
+
// every gap the union skipped, so the estimate could drift a token *above* the per-reference sum even
|
|
55
|
+
// with no real overlap — a false negative saving the measurement must never print
|
|
56
|
+
// (joshuafolkken/kit#1934). A run is a maximal stretch of consecutive charged lines, which is exactly
|
|
57
|
+
// the contiguous text a reader of that section actually reads, so summing the runs both mirrors the
|
|
58
|
+
// real read and keeps `scoped` at or below the per-reference sum by construction.
|
|
59
|
+
function extend_or_start(runs: Array<Array<number>>, index: number): void {
|
|
60
|
+
const current = runs.at(-1)
|
|
61
|
+
|
|
62
|
+
if (current?.at(-1) === index - ONE_LINE) current.push(index)
|
|
63
|
+
else runs.push([index])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function contiguous_runs(charged: ReadonlySet<number>): Array<Array<number>> {
|
|
67
|
+
const runs: Array<Array<number>> = []
|
|
68
|
+
const ascending = [...charged].toSorted((left, right) => left - right)
|
|
69
|
+
|
|
70
|
+
for (const index of ascending) extend_or_start(runs, index)
|
|
71
|
+
|
|
72
|
+
return runs
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function run_text(lines: ReadonlyArray<string>, run: ReadonlyArray<number>): string {
|
|
76
|
+
return run.map((index) => lines[index] ?? '').join('\n')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// **Charged per file over the union of the lines its references cover, never per reference.**
|
|
80
|
+
// Summing the references instead double-counts the two ways one file can be cited twice — two
|
|
81
|
+
// unresolved headings each charged at the whole file, and a `##` section cited beside one of its own
|
|
82
|
+
// `###` children, which `section()` already returns inside the parent. Either one could push the
|
|
83
|
+
// scoped figure above the whole one and print a *negative* saving, which is the single direction this
|
|
84
|
+
// measurement must not be able to move (joshuafolkken/kit#1776 review round 1).
|
|
85
|
+
function scoped_file_cost(markdown: string, headings: ReadonlyArray<string>): Cost {
|
|
86
|
+
const charged = charged_lines(markdown, headings)
|
|
87
|
+
|
|
88
|
+
if (charged === undefined) return cost_of(markdown)
|
|
89
|
+
|
|
90
|
+
const lines = markdown.split('\n')
|
|
91
|
+
|
|
92
|
+
return total(contiguous_runs(charged).map((run) => cost_of(run_text(lines, run))))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const read_set_cost = { cost_of, scoped_file_cost, total }
|
|
96
|
+
|
|
97
|
+
export type { Cost }
|
|
98
|
+
export { read_set_cost }
|
|
@@ -147,6 +147,7 @@ function splice_declaration(
|
|
|
147
147
|
if (index === input.first) return [...input.rendered]
|
|
148
148
|
if (input.dropped.has(index)) return []
|
|
149
149
|
|
|
150
|
+
// eslint-disable-next-line unicorn/no-unnecessary-array-flat-map -- one return type (sonarjs/function-return-type)
|
|
150
151
|
return [line]
|
|
151
152
|
})
|
|
152
153
|
}
|
|
@@ -301,7 +301,7 @@ function reported_references(
|
|
|
301
301
|
.flatMap((reference) => {
|
|
302
302
|
const state = reported_state(states, reference)
|
|
303
303
|
|
|
304
|
-
return state === undefined ? [] :
|
|
304
|
+
return state === undefined ? [] : { reference, state }
|
|
305
305
|
})
|
|
306
306
|
}
|
|
307
307
|
|
|
@@ -73,9 +73,10 @@ function order_pairs(
|
|
|
73
73
|
const blocked = find_child(children, { repo: epic_repo, number: link.blocked })
|
|
74
74
|
if (blocker === undefined || blocked === undefined) return []
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
if (!is_open_local(blocker, current_repo) || !is_open_local(blocked, current_repo)) return []
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line unicorn/no-unnecessary-array-flat-map -- one return type (sonarjs/function-return-type)
|
|
79
|
+
return [{ blocker, blocked }]
|
|
79
80
|
})
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -4,6 +4,7 @@ import { git_gh_command } from '#scripts/gh/git-gh-command'
|
|
|
4
4
|
import { cutoff_of, type ScanCutoff } from '#scripts/git/listing-cutoff'
|
|
5
5
|
import { parse_json_array_or_undefined } from '#scripts/git/parse-json-array'
|
|
6
6
|
import { bounded_pool } from '#scripts/lib/bounded-pool'
|
|
7
|
+
import type { PollOptions } from '#scripts/lib/poll'
|
|
7
8
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
8
9
|
import { z } from 'zod'
|
|
9
10
|
import {
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
} from './epic-bundle'
|
|
15
16
|
import { epic_bundle_evidence } from './epic-bundle-evidence'
|
|
16
17
|
import { epic_bundle_gaps } from './epic-bundle-gaps'
|
|
18
|
+
import { epic_bundle_poll } from './epic-bundle-poll'
|
|
17
19
|
import { epic_bundle_referenced, type ReferencedContext } from './epic-bundle-referenced'
|
|
18
20
|
import { epic_index, epic_schema, type FetchedEpics } from './epic-index'
|
|
19
21
|
import { epic_issue } from './epic-issue'
|
|
@@ -393,9 +395,18 @@ async function report_widened(
|
|
|
393
395
|
return report_decision(subject, widened, repo)
|
|
394
396
|
}
|
|
395
397
|
|
|
396
|
-
// The recommendation for one issue, from the open backlog around it.
|
|
397
|
-
|
|
398
|
-
|
|
398
|
+
// The recommendation for one issue, from the open backlog around it. `options` is the poll a caller
|
|
399
|
+
// holding a just-filed issue passes, since the listing trails the create call (`epic-bundle-poll.ts`).
|
|
400
|
+
async function report_for(
|
|
401
|
+
issue_number: number,
|
|
402
|
+
repo: string,
|
|
403
|
+
options?: PollOptions,
|
|
404
|
+
): Promise<number> {
|
|
405
|
+
const backlog = await epic_bundle_poll.read_until_listed(
|
|
406
|
+
async () => await read_backlog(repo),
|
|
407
|
+
issue_number,
|
|
408
|
+
options,
|
|
409
|
+
)
|
|
399
410
|
|
|
400
411
|
if (!backlog.is_readable) {
|
|
401
412
|
console.error(unreadable_backlog_message(backlog))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { poll, type PollOptions } from '#scripts/lib/poll'
|
|
2
|
+
|
|
3
|
+
// **The open listing trails the create call** (joshuafolkken/kit#3332): `issue:file` asks
|
|
4
|
+
// `epic:bundle` about the issue it has just filed, and the list endpoint had not shown it yet on every
|
|
5
|
+
// filing of one run, so the placement was skipped each time. A caller that knows the issue is fresh
|
|
6
|
+
// passes a poll and the listing is read again until it shows the issue. An unreadable listing is
|
|
7
|
+
// final, since reading it again is not what would change it.
|
|
8
|
+
|
|
9
|
+
// A number typed at the command line names an issue that exists already, so it is read once.
|
|
10
|
+
const SINGLE_READ: PollOptions = { attempts: 1, interval_ms: 0 }
|
|
11
|
+
|
|
12
|
+
interface Listing {
|
|
13
|
+
is_readable: boolean
|
|
14
|
+
issues: ReadonlyArray<{ number: number }>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function is_settled(listing: Listing, issue_number: number): boolean {
|
|
18
|
+
return !listing.is_readable || listing.issues.some((issue) => issue.number === issue_number)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function read_until_listed<T extends Listing>(
|
|
22
|
+
read: () => Promise<T>,
|
|
23
|
+
issue_number: number,
|
|
24
|
+
options: PollOptions = SINGLE_READ,
|
|
25
|
+
): Promise<T> {
|
|
26
|
+
const reads: Array<T> = []
|
|
27
|
+
|
|
28
|
+
await poll.poll_until(async () => {
|
|
29
|
+
const listing = await read()
|
|
30
|
+
|
|
31
|
+
reads.push(listing)
|
|
32
|
+
|
|
33
|
+
return is_settled(listing, issue_number)
|
|
34
|
+
}, options)
|
|
35
|
+
|
|
36
|
+
return reads.at(-1) ?? (await read())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const epic_bundle_poll = { read_until_listed }
|
|
40
|
+
|
|
41
|
+
export { epic_bundle_poll }
|
|
@@ -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 { read_json_listing } from '#scripts/git/parse-json-array'
|
|
3
|
-
import { open_issue_schema, type OpenIssueData } from '#scripts/git/schemas'
|
|
4
4
|
import {
|
|
5
5
|
ALREADY_DONE_LABEL,
|
|
6
6
|
has_any_label,
|
|
@@ -70,11 +70,12 @@ function is_same_blockers(
|
|
|
70
70
|
left: ReadonlyArray<IssueReference>,
|
|
71
71
|
right: ReadonlyArray<IssueReference>,
|
|
72
72
|
): boolean {
|
|
73
|
-
|
|
74
|
-
blockers
|
|
73
|
+
function sorted(blockers: ReadonlyArray<IssueReference>): string {
|
|
74
|
+
return blockers
|
|
75
75
|
.map((blocker) => epic_graph.key_of(blocker))
|
|
76
76
|
.toSorted((first, second) => first.localeCompare(second))
|
|
77
77
|
.join(',')
|
|
78
|
+
}
|
|
78
79
|
|
|
79
80
|
return sorted(left) === sorted(right)
|
|
80
81
|
}
|
|
@@ -217,7 +217,7 @@ function is_ready(node: string, context: ClassifyContext): boolean {
|
|
|
217
217
|
function fill_memo(children: ReadonlyArray<EpicChild>, context: ClassifyContext): void {
|
|
218
218
|
const pending = new Set(children.map((child) => epic_graph.key_of(child)))
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
function peel(): number {
|
|
221
221
|
const ready = [...pending].filter((node) => is_ready(node, context))
|
|
222
222
|
|
|
223
223
|
for (const node of ready) {
|