@joshuafolkken/kit 1.1166.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 +9 -5
- package/.claude/skills/workflow-commands/backlogrun-park.md +29 -9
- 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 +24 -15
- 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 +10 -5
- 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 +47 -33
- 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-DYAVAM6S.js → chunk-55HHG5PX.js} +1405 -631
- package/dist/hooks/{chunk-A6P7PN7P.js → chunk-5VMKCCC4.js} +25 -10
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/{chunk-5KTCMCOT.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-ENA7P5FB.js → chunk-F56MFS4B.js} +3 -3
- package/dist/hooks/{chunk-ANJ2WGFD.js → chunk-FIVDHQGT.js} +7 -6
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-GU5MATVE.js → chunk-JUDKDCDP.js} +499 -389
- package/dist/hooks/{chunk-ZDZ555YH.js → chunk-NV2JRZZO.js} +743 -909
- package/dist/hooks/{chunk-V2BTOZHT.js → chunk-RNWHN5BP.js} +3 -3
- package/dist/hooks/{chunk-URJA6XY4.js → chunk-TQNPDZNH.js} +4 -4
- package/dist/hooks/{chunk-GYNGP7HI.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 +232 -210
- 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 +81 -59
- package/dist/josh.js +872 -75
- 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 +31 -9
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +7 -6
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-rank.ts +18 -9
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-restructure.ts +152 -0
- 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/backlog/backlog-waves.ts +15 -17
- 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/cost-runtime/context-cut-threshold.ts +1 -9
- package/scripts/cost-runtime/cost-verdict.ts +15 -28
- 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-followup-stages.ts +9 -0
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +5 -130
- 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-issue.ts +5 -3
- package/scripts/gh/git-gh-pr-read.ts +68 -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 +9 -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 +18 -6
- 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 +12 -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/{schemas.ts → git-schemas.ts} +2 -0
- 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/git/main-merge.ts +57 -21
- 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 +11 -1
- 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 +35 -3
- 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-notify.ts +26 -1
- 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 +3 -3
- 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} +12 -6
- 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} +3 -3
- package/scripts/run/{run-cut-report.ts → cut/run-cut-report.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} +29 -8
- 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-next.ts → ship/run-ship-next.ts} +13 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +66 -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} +10 -6
- 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-stage.ts → ship/run-ship-stage.ts} +13 -0
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +15 -8
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/ship/run-ship-sync.ts +128 -0
- package/scripts/run/{run-ship.ts → ship/run-ship.ts} +6 -0
- 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} +31 -9
- 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} +99 -17
- 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/test-red-logic.ts +17 -2
- package/scripts/test/test-red.ts +8 -7
- 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/time-runtime/time-transcript-line.ts +27 -1
- 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/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-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-review.ts → ship/run-ship-review.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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { issue_bug_label } from '#scripts/issue/issue-bug-label'
|
|
2
|
+
import { observation_ledger } from '#scripts/observations/observation-ledger'
|
|
2
3
|
import { z } from 'zod'
|
|
3
4
|
import { test_declared_logic } from './test-declared-logic'
|
|
4
5
|
|
|
@@ -11,7 +12,7 @@ import { test_declared_logic } from './test-declared-logic'
|
|
|
11
12
|
// that test catches anything — the added and changed tests run against the merge-base, and at least one
|
|
12
13
|
// of them has to fail there.
|
|
13
14
|
|
|
14
|
-
type RedVerdict = 'red' | 'green' | 'no-test'
|
|
15
|
+
type RedVerdict = 'red' | 'green' | 'no-test' | 'test-only'
|
|
15
16
|
|
|
16
17
|
// The declaration a bug-fix Issue carries under its background heading, in the same slot as the
|
|
17
18
|
// behavior-change declaration (`behavior-change-lint.ts`). Read from the declaration, never inferred,
|
|
@@ -33,6 +34,18 @@ function unit_test_files(paths: ReadonlyArray<string>): Array<string> {
|
|
|
33
34
|
.filter((path) => test_declared_logic.is_test_file(path) && path.endsWith(UNIT_TEST_SUFFIX))
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// **A change whose every path is a test file has no pre-fix code to run against**
|
|
38
|
+
// (joshuafolkken/kit#3214). The merge-base then runs the same code as HEAD, so its result equals HEAD's
|
|
39
|
+
// and says nothing about the fix. Exempt paths do not count: a document-rule test reads `*.md` and
|
|
40
|
+
// `prompts/` files, so a change to one of those is pre-fix content the test can be red against.
|
|
41
|
+
// Observation ledger paths do not count either way (joshuafolkken/kit#3230): a run commits its own
|
|
42
|
+
// ledger entry, and no test reads it as pre-fix content.
|
|
43
|
+
function is_test_only(paths: ReadonlyArray<string>): boolean {
|
|
44
|
+
const counted = paths.filter((path) => !observation_ledger.is_ledger_path(path))
|
|
45
|
+
|
|
46
|
+
return counted.length > 0 && counted.every((path) => test_declared_logic.is_test_file(path))
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
// Vitest's JSON report carries one `testResults` entry per file it loaded — a file that failed to load
|
|
37
50
|
// included — and none for a file its config excluded, so this is how many files actually ran. The
|
|
38
51
|
// changed-path count cannot stand in for it: a file the merge-base config does not include runs nothing
|
|
@@ -58,7 +71,8 @@ function verdict_for(test_count: number, is_failed: boolean): RedVerdict {
|
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
// Only `green` on a declared bug fix is refused. `no-test` is already `josh test:declared`'s refusal
|
|
61
|
-
// when a runtime file changed,
|
|
74
|
+
// when a runtime file changed, a declared fix that touched only docs has nothing to reproduce, and a
|
|
75
|
+
// `test-only` fix runs the same code on the merge-base as on HEAD.
|
|
62
76
|
function is_refused(body: string, verdict: RedVerdict): boolean {
|
|
63
77
|
return verdict === 'green' && is_bug_fix(body)
|
|
64
78
|
}
|
|
@@ -67,6 +81,7 @@ const test_red_logic = {
|
|
|
67
81
|
BUG_DECLARATION_LINE,
|
|
68
82
|
is_bug_fix,
|
|
69
83
|
is_refused,
|
|
84
|
+
is_test_only,
|
|
70
85
|
ran_file_count,
|
|
71
86
|
unit_test_files,
|
|
72
87
|
verdict_for,
|
package/scripts/test/test-red.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { execa } from 'execa'
|
|
|
11
11
|
import { test_red_logic, type RedVerdict } from './test-red-logic'
|
|
12
12
|
|
|
13
13
|
// `josh test:red` — run the added and changed Vitest files against the pre-fix tree and print `red`,
|
|
14
|
-
// `green` or `
|
|
14
|
+
// `green`, `no-test` or `test-only` (joshuafolkken/kit#2448).
|
|
15
15
|
//
|
|
16
16
|
// **The pre-fix tree is a detached worktree at the merge-base, never the caller's checkout.** The tests
|
|
17
17
|
// are copied in from the working tree, so what runs is the new test against the old code, and the
|
|
@@ -26,7 +26,7 @@ const REPORT_NAME = 'report.json'
|
|
|
26
26
|
const NODE_MODULES = 'node_modules'
|
|
27
27
|
const VITEST_BIN = path.join(NODE_MODULES, '.bin', 'vitest')
|
|
28
28
|
const USAGE =
|
|
29
|
-
'Usage: josh test:red — prints red / green / no-test for the changed tests on the merge-base'
|
|
29
|
+
'Usage: josh test:red — prints red / green / no-test / test-only for the changed tests on the merge-base'
|
|
30
30
|
|
|
31
31
|
interface RedRun {
|
|
32
32
|
verdict: RedVerdict
|
|
@@ -34,9 +34,7 @@ interface RedRun {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// The changed Vitest files that still exist — a deleted test has nothing to run.
|
|
37
|
-
|
|
38
|
-
const paths = await changed_paths.read_changed_paths(false)
|
|
39
|
-
|
|
37
|
+
function changed_tests(paths: ReadonlyArray<string>): Array<string> {
|
|
40
38
|
return test_red_logic.unit_test_files(paths).filter((file) => existsSync(file))
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -126,12 +124,15 @@ async function verdict_on(base: string, files: ReadonlyArray<string>): Promise<R
|
|
|
126
124
|
}
|
|
127
125
|
|
|
128
126
|
// The verdict over the current checkout. No merge-base (not a repository, no default branch) reads as
|
|
129
|
-
// `no-test` — there is no pre-fix tree to run against, and a guard fails open.
|
|
127
|
+
// `no-test` — there is no pre-fix tree to run against, and a guard fails open. A change whose every path
|
|
128
|
+
// is a test file is `test-only` without a run: the merge-base runs the same code as HEAD.
|
|
130
129
|
async function run(): Promise<RedRun> {
|
|
131
130
|
const base = await change_base.resolved()
|
|
132
|
-
const
|
|
131
|
+
const paths = await changed_paths.read_changed_paths(false)
|
|
132
|
+
const files = changed_tests(paths)
|
|
133
133
|
|
|
134
134
|
if (base === undefined || files.length === 0) return { verdict: 'no-test', files }
|
|
135
|
+
if (test_red_logic.is_test_only(paths)) return { verdict: 'test-only', files }
|
|
135
136
|
|
|
136
137
|
return { verdict: await verdict_on(base, files), files }
|
|
137
138
|
}
|
|
@@ -49,7 +49,11 @@ const NO_FINISH = 0
|
|
|
49
49
|
|
|
50
50
|
// The launch's own result body, which is the only place the harness writes the id it assigns. Read
|
|
51
51
|
// as a derived fact and the body discarded, exactly as `has_failure_line` and `followup_stages` are.
|
|
52
|
-
|
|
52
|
+
// **A foreground call that outran its timeout is a launch too** (joshuafolkken/kit#3304): the harness
|
|
53
|
+
// moves it to the background and says so as `moved to the background (ID: <id>)`. Missing that spelling
|
|
54
|
+
// let the #3273 lane child end its turn on a running task the stop hook never saw.
|
|
55
|
+
const LAUNCH_PATTERN =
|
|
56
|
+
/(?:running in background with ID:|moved to the background \(ID:)\s*([\w-]+)/iu
|
|
53
57
|
// A backgrounded subagent's launch result, which names its id on an `agentId:` line of its own.
|
|
54
58
|
const AGENT_LAUNCH_PATTERN = /^Async agent launched[\s\S]*?\bagentId:\s*([\w-]+)/u
|
|
55
59
|
// The notice the harness writes when a task it took into the background ends
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// clone `CLAUDE.md` prohibits, and the two copies would disagree the first time one of them learned
|
|
11
11
|
// to accept another format.
|
|
12
12
|
function parse_instant(raw: string | null | undefined): number | undefined {
|
|
13
|
-
|
|
13
|
+
if (raw === null || raw === undefined) return undefined
|
|
14
|
+
|
|
15
|
+
const parsed = Date.parse(raw)
|
|
14
16
|
|
|
15
17
|
return Number.isNaN(parsed) ? undefined : parsed
|
|
16
18
|
}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
|
|
2
2
|
import type { FollowupStage } from '#scripts/followup/git-followup-stages'
|
|
3
3
|
import { time_background } from './time-background'
|
|
4
|
-
import { time_bundle_call } from './time-bundle-call'
|
|
5
4
|
import { time_followup_stage } from './time-followup-stage'
|
|
6
|
-
import { time_markers, type PhaseMarker } from './time-markers'
|
|
7
5
|
import { time_reported_failure } from './time-reported-failure'
|
|
8
6
|
import { time_shell } from './time-shell'
|
|
9
|
-
import {
|
|
7
|
+
import { time_tool_call, type ToolCall } from './time-tool-call'
|
|
10
8
|
import { time_transcript_line, type Block, type TranscriptLine } from './time-transcript-line'
|
|
11
|
-
import { time_writes } from './time-writes'
|
|
12
9
|
|
|
13
10
|
const { NO_MESSAGE_ID, parse_line } = time_transcript_line
|
|
11
|
+
const { NO_CALL, UNKNOWN_CALL, UNKNOWN_TOOL, to_tool_call } = time_tool_call
|
|
14
12
|
|
|
15
13
|
// Turning a session transcript into timed spans (joshuafolkken/kit#1267).
|
|
16
14
|
//
|
|
@@ -24,6 +22,10 @@ const { NO_MESSAGE_ID, parse_line } = time_transcript_line
|
|
|
24
22
|
// the move changed no call site. `cost_blocks`' three type constants are still imported rather than
|
|
25
23
|
// restated.
|
|
26
24
|
//
|
|
25
|
+
// **Reading a `tool_use` block into a `ToolCall` is `time-tool-call.ts`'s** since
|
|
26
|
+
// joshuafolkken/kit#3264, when this file reached its length limit again. `UNKNOWN_TOOL` and
|
|
27
|
+
// `to_tool_call` are re-exported below, so that move changed no call site either.
|
|
28
|
+
//
|
|
27
29
|
// **The partition is by gap, not by pair.** Every span is the interval between two consecutive
|
|
28
30
|
// events, classified by the *later* one: a span ending at an assistant line is model wait, one
|
|
29
31
|
// ending at a tool result is that tool's execution, one ending at a user prompt is human wait.
|
|
@@ -34,7 +36,6 @@ const { NO_MESSAGE_ID, parse_line } = time_transcript_line
|
|
|
34
36
|
|
|
35
37
|
const ASSISTANT_TYPE = 'assistant'
|
|
36
38
|
const USER_TYPE = 'user'
|
|
37
|
-
const UNKNOWN_TOOL = 'unknown'
|
|
38
39
|
|
|
39
40
|
type SpanCategory = 'model' | 'tool' | 'human'
|
|
40
41
|
|
|
@@ -90,96 +91,6 @@ const NO_RESULT: ResultFacts = {
|
|
|
90
91
|
followup_stages: time_followup_stage.NO_STAGES,
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
// What a tool span is labelled with. `josh_command` is empty for everything that is not a
|
|
94
|
-
// `pnpm josh <cmd>` invocation, and the report drops empty labels rather than printing a bucket.
|
|
95
|
-
//
|
|
96
|
-
// `marker` names the workflow boundary the call is, for the phase breakdown that slices the same
|
|
97
|
-
// spans by stage (joshuafolkken/kit#1269). It is carried here rather than re-derived later because
|
|
98
|
-
// the tool's *input* is what decides it, and a span keeps no input — only the label read off it.
|
|
99
|
-
//
|
|
100
|
-
// `is_bundleable` and `targets` are carried for exactly that reason too (joshuafolkken/kit#1344).
|
|
101
|
-
// Whether a call could have gone out beside another, and what it names, are both read off the input —
|
|
102
|
-
// so a module asking about them after the fact would have nothing to read. `time-bundle-call.ts`
|
|
103
|
-
// decides both; the rule each one follows is stated there.
|
|
104
|
-
//
|
|
105
|
-
// `check_key` is the fourth, and the third time this reason has applied (joshuafolkken/kit#1383). Two
|
|
106
|
-
// runs of one verification check are the same call only if they named the same files, and the files
|
|
107
|
-
// are in the input — so `josh_command` alone cannot say, and nothing downstream could recover it.
|
|
108
|
-
// `time-single-check.ts` decides it.
|
|
109
|
-
//
|
|
110
|
-
// `message_id` is the fifth, and it is the one that says which *turn* a call belongs to
|
|
111
|
-
// (joshuafolkken/kit#1406). It is read off the line the `tool_use` block sat on rather than off the
|
|
112
|
-
// result that closes the span, because a `tool_result` line carries no message id at all — so a span
|
|
113
|
-
// that did not keep it here could never be attributed to the turn that issued it.
|
|
114
|
-
//
|
|
115
|
-
// `writes` is the sixth, and it is what `targets` could not say (joshuafolkken/kit#1472). `targets`
|
|
116
|
-
// names what a call mentioned; `marker` says a call was *an* edit but not of what, and for a tool
|
|
117
|
-
// outside `BUNDLEABLE_TOOLS` it arrived with no target at all. So a consumer subtracting edits from
|
|
118
|
-
// reads — `investigation-reads.ts` — had nothing to subtract for `MultiEdit` / `NotebookEdit`, and
|
|
119
|
-
// nothing at all to tell an in-place `sed -i` from the `sed -n` it shares a label with.
|
|
120
|
-
// `time-writes.ts` decides it, from the input this span is about to discard.
|
|
121
|
-
// `issue` is the seventh, and it is the one `branch` could not carry (joshuafolkken/kit#1617). Under
|
|
122
|
-
// lanes the session writing the transcript stays on the default branch while the work runs in a linked
|
|
123
|
-
// work tree, so `branch` names no issue on any line of the run; the `in-progress` label call does, and
|
|
124
|
-
// like every field above it that answer is read off the *input* a span is about to discard.
|
|
125
|
-
// `time_markers.NO_ISSUE` for every call that declares nothing, which is all but one call per run.
|
|
126
|
-
interface ToolCall {
|
|
127
|
-
label: string
|
|
128
|
-
josh_command: string
|
|
129
|
-
// Every josh subcommand the call ran, where `josh_command` is only the first (joshuafolkken/kit#1883).
|
|
130
|
-
// A chained `pnpm josh lint:related && pnpm josh test:related` carries both; the count tables expand
|
|
131
|
-
// it so the ones `josh_command` drops are still counted.
|
|
132
|
-
josh_commands: ReadonlyArray<string>
|
|
133
|
-
check_key: string
|
|
134
|
-
marker: PhaseMarker
|
|
135
|
-
is_bundleable: boolean
|
|
136
|
-
targets: ReadonlyArray<string>
|
|
137
|
-
// Carried beside `targets` because sharing a target means something different depending on it: a
|
|
138
|
-
// second write to one file is independent work, a read of a file just written is not
|
|
139
|
-
// (joshuafolkken/kit#1509).
|
|
140
|
-
is_writing: boolean
|
|
141
|
-
// The conservative write answer `is_read_only_call` asks of a live call, carried so a reader of the
|
|
142
|
-
// transcript asks the same read-only question as the guard (joshuafolkken/kit#3139).
|
|
143
|
-
may_write: boolean
|
|
144
|
-
// Whether this is a subagent launch whose prompt builds on an earlier launch's finding
|
|
145
|
-
// (joshuafolkken/kit#1854). Carried for the reason every field here is — the prompt it is read from
|
|
146
|
-
// is the input, and a span keeps none. `time-agent-bundles.ts` reads it.
|
|
147
|
-
has_prior_reference: boolean
|
|
148
|
-
writes: ReadonlyArray<string>
|
|
149
|
-
message_id: string
|
|
150
|
-
issue: number
|
|
151
|
-
// The background run this call reads the output of, and `''` for every other call
|
|
152
|
-
// (joshuafolkken/kit#1662). It is the eighth field carried for the reason the seven above are:
|
|
153
|
-
// the id sits in the command string, which a span does not keep, and it is the only thing that
|
|
154
|
-
// says a `tail` two turns later is the join of a command started minutes ago.
|
|
155
|
-
reads_background: string
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const NO_CALL: ToolCall = {
|
|
159
|
-
label: '',
|
|
160
|
-
josh_command: '',
|
|
161
|
-
josh_commands: [],
|
|
162
|
-
check_key: time_single_check.NO_CHECK,
|
|
163
|
-
marker: time_markers.NO_MARKER,
|
|
164
|
-
message_id: NO_MESSAGE_ID,
|
|
165
|
-
writes: [],
|
|
166
|
-
issue: time_markers.NO_ISSUE,
|
|
167
|
-
reads_background: time_background.NO_BACKGROUND,
|
|
168
|
-
...time_bundle_call.not_bundleable(),
|
|
169
|
-
}
|
|
170
|
-
const UNKNOWN_CALL: ToolCall = {
|
|
171
|
-
label: UNKNOWN_TOOL,
|
|
172
|
-
josh_command: '',
|
|
173
|
-
josh_commands: [],
|
|
174
|
-
check_key: time_single_check.NO_CHECK,
|
|
175
|
-
marker: time_markers.NO_MARKER,
|
|
176
|
-
message_id: NO_MESSAGE_ID,
|
|
177
|
-
writes: [],
|
|
178
|
-
issue: time_markers.NO_ISSUE,
|
|
179
|
-
reads_background: time_background.NO_BACKGROUND,
|
|
180
|
-
...time_bundle_call.not_bundleable(),
|
|
181
|
-
}
|
|
182
|
-
|
|
183
94
|
// `ended_ms` is the absolute instant the span closed, so `[ended_ms - duration_ms, ended_ms]` is the
|
|
184
95
|
// interval it occupied. A duration alone cannot say *when*, and two things need that: the CI wait,
|
|
185
96
|
// which is the part of the PR-open→merge window no span covers (joshuafolkken/kit#1268), and the
|
|
@@ -242,50 +153,6 @@ interface Timeline {
|
|
|
242
153
|
spans: Array<Span>
|
|
243
154
|
}
|
|
244
155
|
|
|
245
|
-
// Everything but Bash: the tool's own name is the label, and nothing it runs is a shell command to
|
|
246
|
-
// read a check key, a josh command or a declared issue off.
|
|
247
|
-
function non_bash_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
248
|
-
return {
|
|
249
|
-
label: name,
|
|
250
|
-
josh_command: '',
|
|
251
|
-
josh_commands: [],
|
|
252
|
-
check_key: time_single_check.NO_CHECK,
|
|
253
|
-
marker: time_markers.tool_marker(name, input),
|
|
254
|
-
message_id,
|
|
255
|
-
writes: time_writes.tool_writes(name, input),
|
|
256
|
-
issue: time_markers.NO_ISSUE,
|
|
257
|
-
// A non-Bash tool joins a backgrounded command too — `BashOutput` carries the shell id in a
|
|
258
|
-
// field of its own, `Monitor` names the same `…/tasks/<id>.output` path in a command — so the
|
|
259
|
-
// same reader is asked here rather than the field being left empty for every tool but `Bash`.
|
|
260
|
-
reads_background: time_background.read_id_of(input),
|
|
261
|
-
...time_bundle_call.tool_facts(name, input),
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function bash_call(input: unknown, message_id: string): ToolCall {
|
|
266
|
-
const command = time_shell.bash_command(input)
|
|
267
|
-
const josh_command = time_shell.josh_command_of(command)
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
label: time_shell.bash_label(command),
|
|
271
|
-
josh_command,
|
|
272
|
-
josh_commands: time_shell.josh_commands_of(command),
|
|
273
|
-
check_key: time_single_check.check_key(josh_command, command),
|
|
274
|
-
marker: time_markers.bash_marker(command),
|
|
275
|
-
message_id,
|
|
276
|
-
writes: time_writes.bash_writes(command),
|
|
277
|
-
issue: time_markers.bash_issue(command),
|
|
278
|
-
reads_background: time_background.read_id_of(input),
|
|
279
|
-
...time_bundle_call.bash_facts(command),
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function to_tool_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
284
|
-
if (name !== cost_blocks.BASH_TOOL) return non_bash_call(name, input, message_id)
|
|
285
|
-
|
|
286
|
-
return bash_call(input, message_id)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
156
|
// Identified calls only. A `tool_use` written without an `id` would otherwise be registered under
|
|
290
157
|
// the empty string, where the next result that carries no `tool_use_id` would match it and be
|
|
291
158
|
// labelled with an unrelated tool — a wrong name where `UNKNOWN_TOOL` is the honest one.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { cost_blocks } from '#scripts/cost-runtime/cost-blocks'
|
|
2
|
+
import { time_background } from './time-background'
|
|
3
|
+
import { time_bundle_call } from './time-bundle-call'
|
|
4
|
+
import { time_markers, type PhaseMarker } from './time-markers'
|
|
5
|
+
import { time_shell } from './time-shell'
|
|
6
|
+
import { time_single_check } from './time-single-check'
|
|
7
|
+
import { time_transcript_line } from './time-transcript-line'
|
|
8
|
+
import { time_writes } from './time-writes'
|
|
9
|
+
|
|
10
|
+
// Reading a `tool_use` block into the label a span carries — split out of `time-spans.ts` when it
|
|
11
|
+
// reached its line limit (joshuafolkken/kit#3264). `time_spans` re-exports `UNKNOWN_TOOL` and
|
|
12
|
+
// `to_tool_call` under the names they always had, so the move changed no call site.
|
|
13
|
+
|
|
14
|
+
const { NO_MESSAGE_ID } = time_transcript_line
|
|
15
|
+
|
|
16
|
+
const UNKNOWN_TOOL = 'unknown'
|
|
17
|
+
|
|
18
|
+
// What a tool span is labelled with. `josh_command` is empty for everything that is not a
|
|
19
|
+
// `pnpm josh <cmd>` invocation, and the report drops empty labels rather than printing a bucket.
|
|
20
|
+
//
|
|
21
|
+
// `marker` names the workflow boundary the call is, for the phase breakdown that slices the same
|
|
22
|
+
// spans by stage (joshuafolkken/kit#1269). It is carried here rather than re-derived later because
|
|
23
|
+
// the tool's *input* is what decides it, and a span keeps no input — only the label read off it.
|
|
24
|
+
//
|
|
25
|
+
// `is_bundleable` and `targets` are carried for exactly that reason too (joshuafolkken/kit#1344).
|
|
26
|
+
// Whether a call could have gone out beside another, and what it names, are both read off the input —
|
|
27
|
+
// so a module asking about them after the fact would have nothing to read. `time-bundle-call.ts`
|
|
28
|
+
// decides both; the rule each one follows is stated there.
|
|
29
|
+
//
|
|
30
|
+
// `check_key` is the fourth, and the third time this reason has applied (joshuafolkken/kit#1383). Two
|
|
31
|
+
// runs of one verification check are the same call only if they named the same files, and the files
|
|
32
|
+
// are in the input — so `josh_command` alone cannot say, and nothing downstream could recover it.
|
|
33
|
+
// `time-single-check.ts` decides it.
|
|
34
|
+
//
|
|
35
|
+
// `message_id` is the fifth, and it is the one that says which *turn* a call belongs to
|
|
36
|
+
// (joshuafolkken/kit#1406). It is read off the line the `tool_use` block sat on rather than off the
|
|
37
|
+
// result that closes the span, because a `tool_result` line carries no message id at all — so a span
|
|
38
|
+
// that did not keep it here could never be attributed to the turn that issued it.
|
|
39
|
+
//
|
|
40
|
+
// `writes` is the sixth, and it is what `targets` could not say (joshuafolkken/kit#1472). `targets`
|
|
41
|
+
// names what a call mentioned; `marker` says a call was *an* edit but not of what, and for a tool
|
|
42
|
+
// outside `BUNDLEABLE_TOOLS` it arrived with no target at all. So a consumer subtracting edits from
|
|
43
|
+
// reads — `investigation-reads.ts` — had nothing to subtract for `MultiEdit` / `NotebookEdit`, and
|
|
44
|
+
// nothing at all to tell an in-place `sed -i` from the `sed -n` it shares a label with.
|
|
45
|
+
// `time-writes.ts` decides it, from the input this span is about to discard.
|
|
46
|
+
// `issue` is the seventh, and it is the one `branch` could not carry (joshuafolkken/kit#1617). Under
|
|
47
|
+
// lanes the session writing the transcript stays on the default branch while the work runs in a linked
|
|
48
|
+
// work tree, so `branch` names no issue on any line of the run; the `in-progress` label call does, and
|
|
49
|
+
// like every field above it that answer is read off the *input* a span is about to discard.
|
|
50
|
+
// `time_markers.NO_ISSUE` for every call that declares nothing, which is all but one call per run.
|
|
51
|
+
interface ToolCall {
|
|
52
|
+
label: string
|
|
53
|
+
josh_command: string
|
|
54
|
+
// Every josh subcommand the call ran, where `josh_command` is only the first (joshuafolkken/kit#1883).
|
|
55
|
+
// A chained `pnpm josh lint:related && pnpm josh test:related` carries both; the count tables expand
|
|
56
|
+
// it so the ones `josh_command` drops are still counted.
|
|
57
|
+
josh_commands: ReadonlyArray<string>
|
|
58
|
+
check_key: string
|
|
59
|
+
marker: PhaseMarker
|
|
60
|
+
is_bundleable: boolean
|
|
61
|
+
targets: ReadonlyArray<string>
|
|
62
|
+
// Carried beside `targets` because sharing a target means something different depending on it: a
|
|
63
|
+
// second write to one file is independent work, a read of a file just written is not
|
|
64
|
+
// (joshuafolkken/kit#1509).
|
|
65
|
+
is_writing: boolean
|
|
66
|
+
// The conservative write answer `is_read_only_call` asks of a live call, carried so a reader of the
|
|
67
|
+
// transcript asks the same read-only question as the guard (joshuafolkken/kit#3139).
|
|
68
|
+
may_write: boolean
|
|
69
|
+
// Whether this is a subagent launch whose prompt builds on an earlier launch's finding
|
|
70
|
+
// (joshuafolkken/kit#1854). Carried for the reason every field here is — the prompt it is read from
|
|
71
|
+
// is the input, and a span keeps none. `time-agent-bundles.ts` reads it.
|
|
72
|
+
has_prior_reference: boolean
|
|
73
|
+
writes: ReadonlyArray<string>
|
|
74
|
+
message_id: string
|
|
75
|
+
issue: number
|
|
76
|
+
// The background run this call reads the output of, and `''` for every other call
|
|
77
|
+
// (joshuafolkken/kit#1662). It is the eighth field carried for the reason the seven above are:
|
|
78
|
+
// the id sits in the command string, which a span does not keep, and it is the only thing that
|
|
79
|
+
// says a `tail` two turns later is the join of a command started minutes ago.
|
|
80
|
+
reads_background: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const NO_CALL: ToolCall = {
|
|
84
|
+
label: '',
|
|
85
|
+
josh_command: '',
|
|
86
|
+
josh_commands: [],
|
|
87
|
+
check_key: time_single_check.NO_CHECK,
|
|
88
|
+
marker: time_markers.NO_MARKER,
|
|
89
|
+
message_id: NO_MESSAGE_ID,
|
|
90
|
+
writes: [],
|
|
91
|
+
issue: time_markers.NO_ISSUE,
|
|
92
|
+
reads_background: time_background.NO_BACKGROUND,
|
|
93
|
+
...time_bundle_call.not_bundleable(),
|
|
94
|
+
}
|
|
95
|
+
const UNKNOWN_CALL: ToolCall = {
|
|
96
|
+
label: UNKNOWN_TOOL,
|
|
97
|
+
josh_command: '',
|
|
98
|
+
josh_commands: [],
|
|
99
|
+
check_key: time_single_check.NO_CHECK,
|
|
100
|
+
marker: time_markers.NO_MARKER,
|
|
101
|
+
message_id: NO_MESSAGE_ID,
|
|
102
|
+
writes: [],
|
|
103
|
+
issue: time_markers.NO_ISSUE,
|
|
104
|
+
reads_background: time_background.NO_BACKGROUND,
|
|
105
|
+
...time_bundle_call.not_bundleable(),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Everything but Bash: the tool's own name is the label, and nothing it runs is a shell command to
|
|
109
|
+
// read a check key, a josh command or a declared issue off.
|
|
110
|
+
function non_bash_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
111
|
+
return {
|
|
112
|
+
label: name,
|
|
113
|
+
josh_command: '',
|
|
114
|
+
josh_commands: [],
|
|
115
|
+
check_key: time_single_check.NO_CHECK,
|
|
116
|
+
marker: time_markers.tool_marker(name, input),
|
|
117
|
+
message_id,
|
|
118
|
+
writes: time_writes.tool_writes(name, input),
|
|
119
|
+
issue: time_markers.NO_ISSUE,
|
|
120
|
+
// A non-Bash tool joins a backgrounded command too — `BashOutput` carries the shell id in a
|
|
121
|
+
// field of its own, `Monitor` names the same `…/tasks/<id>.output` path in a command — so the
|
|
122
|
+
// same reader is asked here rather than the field being left empty for every tool but `Bash`.
|
|
123
|
+
reads_background: time_background.read_id_of(input),
|
|
124
|
+
...time_bundle_call.tool_facts(name, input),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function bash_call(input: unknown, message_id: string): ToolCall {
|
|
129
|
+
const command = time_shell.bash_command(input)
|
|
130
|
+
const josh_command = time_shell.josh_command_of(command)
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
label: time_shell.bash_label(command),
|
|
134
|
+
josh_command,
|
|
135
|
+
josh_commands: time_shell.josh_commands_of(command),
|
|
136
|
+
check_key: time_single_check.check_key(josh_command, command),
|
|
137
|
+
marker: time_markers.bash_marker(command),
|
|
138
|
+
message_id,
|
|
139
|
+
writes: time_writes.bash_writes(command),
|
|
140
|
+
issue: time_markers.bash_issue(command),
|
|
141
|
+
reads_background: time_background.read_id_of(input),
|
|
142
|
+
...time_bundle_call.bash_facts(command),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function to_tool_call(name: string, input: unknown, message_id: string): ToolCall {
|
|
147
|
+
if (name !== cost_blocks.BASH_TOOL) return non_bash_call(name, input, message_id)
|
|
148
|
+
|
|
149
|
+
return bash_call(input, message_id)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const time_tool_call = {
|
|
153
|
+
UNKNOWN_TOOL,
|
|
154
|
+
NO_CALL,
|
|
155
|
+
UNKNOWN_CALL,
|
|
156
|
+
to_tool_call,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type { ToolCall }
|
|
160
|
+
export { time_tool_call }
|
|
@@ -79,6 +79,13 @@ const ERROR_TEXT_LIMIT = 256
|
|
|
79
79
|
// shared constant exists because each guard writes the literal in its own reason, so the one place a
|
|
80
80
|
// refusal is *detected* names it here.
|
|
81
81
|
const REFUSAL_MARKER = '⛔'
|
|
82
|
+
// The label the harness puts in front of a PreToolUse hook's deny reason, naming the hook and the tool.
|
|
83
|
+
const HOOK_ERROR_LABEL = /^PreToolUse:\S+ hook error: /u
|
|
84
|
+
// A line that is one bare verdict token — `cut`, `busy`, `not-a-lane` — the single word a josh verdict
|
|
85
|
+
// command prints on standard output (joshuafolkken/kit#3223). How many are kept bounds the field the
|
|
86
|
+
// way `ERROR_TEXT_LIMIT` bounds `error_text`.
|
|
87
|
+
const TOKEN_LINE = /^[a-z][a-z-]*$/u
|
|
88
|
+
const TOKEN_LINE_LIMIT = 8
|
|
82
89
|
|
|
83
90
|
interface Block {
|
|
84
91
|
type: string
|
|
@@ -111,6 +118,11 @@ interface Block {
|
|
|
111
118
|
// the parsed block rather than by re-scanning the body downstream. `''` where the result was not a
|
|
112
119
|
// `⛔` refusal, so a reader tells "no guard spoke" from "this guard spoke" without a second parse.
|
|
113
120
|
refusal_guard: string
|
|
121
|
+
// The bare-token lines of a result that did not fail, and `[]` for every other block
|
|
122
|
+
// (joshuafolkken/kit#3223). It obeys the same prohibition as `error_text` — what is kept is a few
|
|
123
|
+
// single words, never the body — and it exists so the verdict a josh command answered with
|
|
124
|
+
// (`run:cut`'s `cut`) is read from the parsed block rather than by searching the raw line.
|
|
125
|
+
token_lines: ReadonlyArray<string>
|
|
114
126
|
// The id the harness assigned to a command it took into the background, and `''` for every other
|
|
115
127
|
// block (joshuafolkken/kit#1662). It is the fourth field read off the body under the same
|
|
116
128
|
// prohibition as the three above — what is kept is the id, never the text it was read from — and
|
|
@@ -159,8 +171,12 @@ function block_names(
|
|
|
159
171
|
// before the first `:` — `⛔ pre-gate cut: …` is `pre-gate cut`. A reason with no colon (a headline
|
|
160
172
|
// like `⛔ scoped checks not green on this tree`) is taken whole. Reading the opening rather than
|
|
161
173
|
// searching the body is what stops a result that merely quoted a reason being read as a refusal.
|
|
174
|
+
//
|
|
175
|
+
// **The harness writes the reason behind its own label** — `PreToolUse:Edit hook error: ⛔ …` — so that
|
|
176
|
+
// label is stripped before the opening is read (joshuafolkken/kit#3223). Without it no live refusal
|
|
177
|
+
// carried a guard: the fixtures wrote the bare `⛔` body and passed while every recorded one read `''`.
|
|
162
178
|
function guard_from_refusal(text: string): string {
|
|
163
|
-
const trimmed = text.trimStart()
|
|
179
|
+
const trimmed = text.trimStart().replace(HOOK_ERROR_LABEL, '')
|
|
164
180
|
if (!trimmed.startsWith(REFUSAL_MARKER)) return ''
|
|
165
181
|
|
|
166
182
|
const headline = trimmed.slice(REFUSAL_MARKER.length).split('\n', 1)[0]?.trimStart() ?? ''
|
|
@@ -169,6 +185,14 @@ function guard_from_refusal(text: string): string {
|
|
|
169
185
|
return (colon === -1 ? headline : headline.slice(0, colon)).trim()
|
|
170
186
|
}
|
|
171
187
|
|
|
188
|
+
function token_lines_of(text: string): Array<string> {
|
|
189
|
+
return text
|
|
190
|
+
.split('\n')
|
|
191
|
+
.map((line) => line.trim())
|
|
192
|
+
.filter((line) => TOKEN_LINE.test(line))
|
|
193
|
+
.slice(0, TOKEN_LINE_LIMIT)
|
|
194
|
+
}
|
|
195
|
+
|
|
172
196
|
// **The body is flattened once and both readers are handed the text.** `result_text` is what turns a
|
|
173
197
|
// content field that may be a string or a list of blocks into one string, and it is idempotent on a
|
|
174
198
|
// string — so passing its own output back to `has_failure_line` reads exactly as passing the raw
|
|
@@ -185,6 +209,7 @@ function to_block(raw: z.infer<typeof BLOCK_SCHEMA>): Block {
|
|
|
185
209
|
followup_stages: time_followup_stage.read_stages(text),
|
|
186
210
|
error_text: is_error === true ? text.slice(0, ERROR_TEXT_LIMIT) : '',
|
|
187
211
|
refusal_guard: is_error === true ? guard_from_refusal(text) : '',
|
|
212
|
+
token_lines: is_error === true ? [] : token_lines_of(text),
|
|
188
213
|
background_id: time_background.launch_id(text),
|
|
189
214
|
agent_id: time_background.agent_launch_id(text),
|
|
190
215
|
}
|
|
@@ -248,6 +273,7 @@ function parse_line(line: string): TranscriptLine | undefined {
|
|
|
248
273
|
const time_transcript_line = {
|
|
249
274
|
ERROR_TEXT_LIMIT,
|
|
250
275
|
NO_MESSAGE_ID,
|
|
276
|
+
TOKEN_LINE_LIMIT,
|
|
251
277
|
guard_from_refusal,
|
|
252
278
|
parse_line,
|
|
253
279
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
+
import { ancestor_directories } from '#scripts/lib/ancestor-directories'
|
|
4
5
|
import { json_value } from '#scripts/lib/json-value'
|
|
5
6
|
import { package_named_version_schema } from '#scripts/lib/schemas'
|
|
6
7
|
|
|
7
8
|
const PACKAGE_JSON = 'package.json'
|
|
8
|
-
// Node module paths never nest anywhere near this deep; the bound only guards against an unexpected
|
|
9
|
-
// filesystem cycle so the walk-up cannot loop forever.
|
|
10
|
-
const MAX_WALK_DEPTH = 64
|
|
11
9
|
|
|
12
10
|
// Options for `resolve_effective_upstream_version`. `resolve_marker` is a subpath specifier used for
|
|
13
11
|
// `createRequire().resolve()` when the package root itself is not directly resolvable — e.g. kit does
|
|
@@ -31,26 +29,10 @@ function read_matching_version(manifest_path: string, package_name: string): str
|
|
|
31
29
|
return parsed.data.version
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
// The directory itself and each of its ancestors up to the filesystem root, nearest-first. Bounded
|
|
35
|
-
// by MAX_WALK_DEPTH so an unexpected filesystem cycle cannot produce an unbounded list.
|
|
36
|
-
function ancestor_directories(start_directory: string): Array<string> {
|
|
37
|
-
const directories: Array<string> = []
|
|
38
|
-
let current = start_directory
|
|
39
|
-
|
|
40
|
-
for (let depth = 0; depth < MAX_WALK_DEPTH; depth += 1) {
|
|
41
|
-
directories.push(current)
|
|
42
|
-
const parent = path.dirname(current)
|
|
43
|
-
if (parent === current) break
|
|
44
|
-
current = parent
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return directories
|
|
48
|
-
}
|
|
49
|
-
|
|
50
32
|
// Walk up from a resolved file's directory to the package root, returning the version of the first
|
|
51
33
|
// ancestor package.json whose `name` matches the target — or undefined when no matching root exists.
|
|
52
34
|
function walk_up_for_version(start_directory: string, package_name: string): string | undefined {
|
|
53
|
-
for (const directory of ancestor_directories(start_directory)) {
|
|
35
|
+
for (const directory of ancestor_directories.list(start_directory)) {
|
|
54
36
|
const version = read_matching_version(path.join(directory, PACKAGE_JSON), package_name)
|
|
55
37
|
if (version !== undefined) return version
|
|
56
38
|
}
|