@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
|
@@ -4,15 +4,17 @@ import path from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import type { AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
6
6
|
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
7
|
+
import { poll } from '#scripts/lib/poll'
|
|
7
8
|
import { telegram_notify } from '#scripts/notify/telegram-notify'
|
|
8
|
-
import { run_carry, type CarryRead } from '
|
|
9
|
-
import { run_event_stream } from '
|
|
10
|
-
import { run_headless } from '
|
|
9
|
+
import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
|
|
10
|
+
import { run_event_stream } from '#scripts/run/event/run-event-stream'
|
|
11
|
+
import { run_headless } from '#scripts/run/run-headless'
|
|
11
12
|
import { run_wake, type RunWake, type WakeStopReason, type WakeTidyResult } from './run-wake'
|
|
12
13
|
import { run_wake_describe, type WakeContext } from './run-wake-describe'
|
|
13
14
|
import { run_wake_driver } from './run-wake-driver'
|
|
14
15
|
import { run_wake_handoff } from './run-wake-handoff'
|
|
15
16
|
import { run_wake_loop, type LoopPorts, type LoopStop } from './run-wake-loop'
|
|
17
|
+
import { run_wake_restart, type RestartPorts } from './run-wake-restart'
|
|
16
18
|
import { run_wake_session, type LaunchResult } from './run-wake-session'
|
|
17
19
|
|
|
18
20
|
// `josh run:wake --start | --list | --stop | --loop` — the supervisor that continues a cut
|
|
@@ -209,10 +211,16 @@ function log_hint(context: WakeContext): string {
|
|
|
209
211
|
return `The output of every session this supervisor started is in ${context.log_target}.`
|
|
210
212
|
}
|
|
211
213
|
|
|
214
|
+
// The log hint goes ahead of the note: the Telegram length limit cuts the tail, and a long failure note
|
|
215
|
+
// (stdout tail plus stderr tail) would otherwise take the one line that says where to look next.
|
|
216
|
+
function warning_body(stop: LoopStop, body: string, context: WakeContext): string {
|
|
217
|
+
return [body, log_hint(context), stop.note].filter(Boolean).join('\n')
|
|
218
|
+
}
|
|
219
|
+
|
|
212
220
|
async function warn_of_stop(stop: LoopStop, body: string, context: WakeContext): Promise<void> {
|
|
213
221
|
await telegram_notify.warn({
|
|
214
222
|
issue_title: WARNING_TITLE,
|
|
215
|
-
body:
|
|
223
|
+
body: warning_body(stop, body, context),
|
|
216
224
|
recovery: WARNING_RECOVERY,
|
|
217
225
|
})
|
|
218
226
|
}
|
|
@@ -303,6 +311,22 @@ function tidy_up(target: string): void {
|
|
|
303
311
|
if (run_wake.tidy_own_wake(target) === SUPERSEDED_RESULT) note_to_stderr(SUPERSEDED_NOTE)
|
|
304
312
|
}
|
|
305
313
|
|
|
314
|
+
function restart_ports(context: WakeContext, ports: LoopPorts, interval_ms: number): RestartPorts {
|
|
315
|
+
return {
|
|
316
|
+
pass: async () => await run_wake_loop.run_loop(context.wake_target, ports, interval_ms),
|
|
317
|
+
is_resumable: () => run_carry.read_carry(context.carry_target).kind === 'carried',
|
|
318
|
+
pause: async () => {
|
|
319
|
+
await poll.sleep(interval_ms)
|
|
320
|
+
},
|
|
321
|
+
note: note_to_stderr,
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// A failed pass is restarted while the carry record is still resumable (`run-wake-restart.ts`), so
|
|
326
|
+
// only a failure past the restart bound reaches `finish` and its warning (joshuafolkken/kit#3332).
|
|
327
|
+
// **The record is claimed once and held across every restart**, so a `--stop` landing in the pause
|
|
328
|
+
// between two passes still finds it, signals this process, and the next pass reads the removal as the
|
|
329
|
+
// person's stop rather than claiming a fresh record and resuming.
|
|
306
330
|
async function loop(context: WakeContext, interval_ms: number): Promise<number> {
|
|
307
331
|
const read = run_carry.read_carry(context.carry_target)
|
|
308
332
|
|
|
@@ -315,11 +339,8 @@ async function loop(context: WakeContext, interval_ms: number): Promise<number>
|
|
|
315
339
|
|
|
316
340
|
if (claimed === undefined) return report(RUNNING_VERDICT)
|
|
317
341
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
ports_for(context, claimed.profile ?? profile),
|
|
321
|
-
interval_ms,
|
|
322
|
-
)
|
|
342
|
+
const ports = ports_for(context, claimed.profile ?? profile)
|
|
343
|
+
const stop = await run_wake_restart.supervise(restart_ports(context, ports, interval_ms))
|
|
323
344
|
|
|
324
345
|
tidy_up(context.wake_target)
|
|
325
346
|
|
|
@@ -408,6 +429,7 @@ const run_wake_cli = {
|
|
|
408
429
|
USAGE,
|
|
409
430
|
run,
|
|
410
431
|
stop_body,
|
|
432
|
+
warning_body,
|
|
411
433
|
}
|
|
412
434
|
|
|
413
435
|
if (process.argv[1] === SCRIPT_PATH) await main(process.argv.slice(ARGV_OFFSET))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { agent_role_profile } from '#scripts/agent/agent-role-profile'
|
|
2
2
|
import { gh_spawn } from '#scripts/gh/gh-spawn'
|
|
3
3
|
import { issue_citation } from '#scripts/rules/issue-citation'
|
|
4
|
-
import { run_carry, type CarryRead } from '
|
|
5
|
-
import { run_event_stream } from '
|
|
6
|
-
import { run_liveness } from '
|
|
4
|
+
import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
|
|
5
|
+
import { run_event_stream } from '#scripts/run/event/run-event-stream'
|
|
6
|
+
import { run_liveness } from '#scripts/run/run-liveness'
|
|
7
7
|
import { run_wake, type RunWake } from './run-wake'
|
|
8
8
|
|
|
9
9
|
// The `--list` presentation of a wake record (joshuafolkken/kit#2407 split it out of `run-wake-cli.ts`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { josh_command, type JoshResult } from '#scripts/josh/josh-run'
|
|
2
|
-
import { run_carry, type CarryRead } from '
|
|
3
|
-
import { run_invocation } from '
|
|
2
|
+
import { run_carry, type CarryRead } from '#scripts/run/carry/run-carry'
|
|
3
|
+
import { run_invocation } from '#scripts/run/run-invocation'
|
|
4
4
|
import type { DriveResult } from './run-wake-loop'
|
|
5
5
|
|
|
6
6
|
const SUCCESS = 0
|
|
@@ -8,6 +8,8 @@ const FIRST_LINE = 0
|
|
|
8
8
|
const RESUME_PREFIX = 'resume: '
|
|
9
9
|
const DETAILS_TAIL_LENGTH = 2048
|
|
10
10
|
const TRUNCATION_MARK = '... '
|
|
11
|
+
// The prefix `backlog-drive-cli.ts` gives the exception it catches.
|
|
12
|
+
const ERROR_PREFIX = 'error '
|
|
11
13
|
|
|
12
14
|
function driver_args(invocation: string): ReadonlyArray<string> | undefined {
|
|
13
15
|
const rebuilt = run_invocation.rebuild(invocation)
|
|
@@ -40,6 +42,11 @@ function details_tail(details: string): string {
|
|
|
40
42
|
// **The hand-off names its next move, so the woken session never searches the procedure for it**
|
|
41
43
|
// (joshuafolkken/kit#3156). Whatever the branch, the session acts on it and hands the loop back to the
|
|
42
44
|
// supervisor with a cut; only a person's stop ends the run instead.
|
|
45
|
+
//
|
|
46
|
+
// **The claim comes first** (joshuafolkken/kit#3238). The supervisor hands the record off before the
|
|
47
|
+
// wake, so until the session adopts it with `--resume` every count it writes — `--merged`, `--done`,
|
|
48
|
+
// the closing `--cut` — is refused as advancing a handed-off budget, and the supervisor, seeing no
|
|
49
|
+
// claim, ends the run as a failed wake.
|
|
43
50
|
const HAND_BACK = 'then hand the loop back with pnpm josh run:carry --cut --owner "$PPID"'
|
|
44
51
|
const NEXT_BY_VERDICT: ReadonlyArray<readonly [string, string]> = [
|
|
45
52
|
[
|
|
@@ -48,28 +55,38 @@ const NEXT_BY_VERDICT: ReadonlyArray<readonly [string, string]> = [
|
|
|
48
55
|
],
|
|
49
56
|
[
|
|
50
57
|
'merge human-review',
|
|
51
|
-
'the child stopped before its commit for a person
|
|
58
|
+
'stop the run, because the child stopped before its commit for a person (needs-human-review.md)',
|
|
52
59
|
],
|
|
53
60
|
['epic #', `follow backlogrun-steps.md → "Named issues run first, in order", ${HAND_BACK}`],
|
|
54
61
|
]
|
|
55
62
|
const NEXT_DEFAULT = `act on the result by backlogrun-steps.md → "The loop", ${HAND_BACK}`
|
|
56
63
|
|
|
57
|
-
function next_move(verdict: string): string {
|
|
64
|
+
function next_move(verdict: string, invocation: string): string {
|
|
58
65
|
const found = NEXT_BY_VERDICT.find(([prefix]) => verdict.startsWith(prefix))
|
|
59
66
|
|
|
60
|
-
return `\nNext: ${found?.[1] ?? NEXT_DEFAULT}`
|
|
67
|
+
return `\nNext: first claim the carry record with ${run_carry.claim_command(invocation)}, then ${found?.[1] ?? NEXT_DEFAULT}`
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
function handoff_material(
|
|
70
|
+
function handoff_material(
|
|
71
|
+
verdict: string,
|
|
72
|
+
resume: string,
|
|
73
|
+
invocation: string,
|
|
74
|
+
details: string,
|
|
75
|
+
): string {
|
|
64
76
|
const context = details === '' ? '' : `\nDetails: ${details_tail(details)}`
|
|
65
77
|
const epic = verdict.startsWith('epic #')
|
|
66
78
|
? '\nThe named item is an epic. Follow backlogrun-steps.md named epic procedure; do not launch the epic root as a fullrun child. After all children merge or park, record the root with pnpm josh run:carry --done <epic-number> --owner "$PPID" before continuing to the next named item.'
|
|
67
79
|
: ''
|
|
68
80
|
|
|
69
|
-
return `Driver result: ${verdict}\n${resume}${epic}${next_move(verdict)}${context}`
|
|
81
|
+
return `Driver result: ${verdict}\n${resume}${epic}${next_move(verdict, invocation)}${context}`
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
function driver_result(
|
|
84
|
+
function driver_result(
|
|
85
|
+
out: string,
|
|
86
|
+
read: CarryRead,
|
|
87
|
+
invocation: string,
|
|
88
|
+
details = '',
|
|
89
|
+
): DriveResult {
|
|
73
90
|
const lines = out.split('\n')
|
|
74
91
|
const verdict = lines[FIRST_LINE]
|
|
75
92
|
|
|
@@ -80,13 +97,32 @@ function driver_result(out: string, read: CarryRead, details = ''): DriveResult
|
|
|
80
97
|
return { kind: 'failed', note: `backlog:drive returned an incomplete result: ${out}` }
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
return { kind: 'judgment', material: handoff_material(verdict, resume, details) }
|
|
100
|
+
return { kind: 'judgment', material: handoff_material(verdict, resume, invocation, details) }
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
// **The cause is the driver's stdout `error …` line, so it leads the note** (joshuafolkken/kit#3242).
|
|
104
|
+
// `backlog:drive` prints an exception only to stdout, and its stderr is never empty — every command it
|
|
105
|
+
// ran is there — so reading `err ?? out` dropped the one line that said why. The stderr follows as its
|
|
106
|
+
// tail, for the same reason `details_tail` cuts a judgment's details.
|
|
107
|
+
function cause_of(out: string): string {
|
|
108
|
+
const errors = out.split('\n').filter((line) => line.startsWith(ERROR_PREFIX))
|
|
88
109
|
|
|
89
|
-
return
|
|
110
|
+
return errors.length > 0 ? errors.join('\n') : details_tail(out)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function failure_note(result: JoshResult): string {
|
|
114
|
+
return [cause_of(result.out), details_tail(result.err ?? '')].filter(Boolean).join('\n')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function read_result(
|
|
118
|
+
result: JoshResult | undefined,
|
|
119
|
+
target: string,
|
|
120
|
+
invocation: string,
|
|
121
|
+
): DriveResult {
|
|
122
|
+
if (result === undefined) return { kind: 'released' }
|
|
123
|
+
if (result.code !== SUCCESS) return { kind: 'failed', note: failure_note(result) }
|
|
124
|
+
|
|
125
|
+
return driver_result(result.out, run_carry.read_carry(target), invocation, result.err)
|
|
90
126
|
}
|
|
91
127
|
|
|
92
128
|
function claim_record(target: string, invocation: string): boolean {
|
|
@@ -97,7 +133,51 @@ function claim_record(target: string, invocation: string): boolean {
|
|
|
97
133
|
return run_carry.adopt_carry(target, current.carry, run_carry.owner_of(process.pid)) !== undefined
|
|
98
134
|
}
|
|
99
135
|
|
|
100
|
-
|
|
136
|
+
// **A verdict with nothing to judge is re-driven here, never handed to a session** (joshuafolkken/kit#3245).
|
|
137
|
+
// A woken session met with `window`, `merge busy` or `merge retry` only cut and handed the loop back, and
|
|
138
|
+
// the supervisor then ran the very same driver again — a model round trip, at a cache write each, that
|
|
139
|
+
// decided nothing. The record is already this process's, so the rerun needs no fresh claim; the limit
|
|
140
|
+
// keeps a driver that never settles from holding the supervisor past its own checks.
|
|
141
|
+
const RERUN_VERDICTS: ReadonlyArray<string> = ['window', 'merge busy', 'merge retry']
|
|
142
|
+
const RERUN_LIMIT = 10
|
|
143
|
+
const RERUN_PAUSE_MS = 60_000
|
|
144
|
+
|
|
145
|
+
type Pause = (ms: number) => Promise<void>
|
|
146
|
+
|
|
147
|
+
async function pause(ms: number): Promise<void> {
|
|
148
|
+
await new Promise((resolve) => {
|
|
149
|
+
setTimeout(resolve, ms)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function is_mechanical(result: JoshResult): boolean {
|
|
154
|
+
const verdict = result.out.split('\n', 1)[FIRST_LINE] ?? ''
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
result.code === SUCCESS &&
|
|
158
|
+
RERUN_VERDICTS.some((entry) => verdict === entry || verdict.startsWith(`${entry} `))
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// A record that expired or was removed during the pause answers `undefined`: its stale verdict must not
|
|
163
|
+
// reach a session, so the supervisor's own next pass re-reads the record and ends the run as `expired`
|
|
164
|
+
// or `stopped`; a rerun would only fail on it and report the run as `failed`.
|
|
165
|
+
async function run_driver(
|
|
166
|
+
target: string,
|
|
167
|
+
args: ReadonlyArray<string>,
|
|
168
|
+
wait: Pause,
|
|
169
|
+
remaining: number,
|
|
170
|
+
): Promise<JoshResult | undefined> {
|
|
171
|
+
const result = await josh_command.josh_run(args, true)
|
|
172
|
+
if (remaining === 0 || !is_mechanical(result)) return result
|
|
173
|
+
|
|
174
|
+
await wait(RERUN_PAUSE_MS)
|
|
175
|
+
if (run_carry.read_carry(target).kind !== 'carried') return undefined
|
|
176
|
+
|
|
177
|
+
return await run_driver(target, args, wait, remaining - 1)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function drive(target: string, wait: Pause = pause): Promise<DriveResult> {
|
|
101
181
|
const read = run_carry.read_carry(target)
|
|
102
182
|
if (read.kind !== 'carried') return { kind: 'failed', note: 'The carry record changed.' }
|
|
103
183
|
|
|
@@ -108,9 +188,11 @@ async function drive(target: string): Promise<DriveResult> {
|
|
|
108
188
|
return { kind: 'failed', note: 'The driver could not claim the carry record.' }
|
|
109
189
|
}
|
|
110
190
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
191
|
+
return read_result(
|
|
192
|
+
await run_driver(target, args, wait, RERUN_LIMIT),
|
|
193
|
+
target,
|
|
194
|
+
read.carry.invocation,
|
|
195
|
+
)
|
|
114
196
|
}
|
|
115
197
|
|
|
116
|
-
export const run_wake_driver = { drive, driver_args, driver_result }
|
|
198
|
+
export const run_wake_driver = { RERUN_LIMIT, drive, driver_args, driver_result }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
2
|
-
import { run_carry } from '
|
|
2
|
+
import { run_carry } from '#scripts/run/carry/run-carry'
|
|
3
3
|
|
|
4
4
|
// The supervisor's recovery hand-off (joshuafolkken/kit#2437). A session that claimed the carry record
|
|
5
5
|
// and then died without `run:carry --cut` leaves it `carried`, owned by a gone process, with no
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CarryRead } from '
|
|
1
|
+
import type { CarryRead } from '#scripts/run/carry/run-carry'
|
|
2
2
|
import {
|
|
3
3
|
run_wake,
|
|
4
4
|
type RunWake,
|
|
@@ -56,8 +56,13 @@ interface LoopPorts {
|
|
|
56
56
|
now: () => Date
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// `released`: the carry record ended while the driver paused, so there is nothing to hand on and the
|
|
60
|
+
// next pass decides from the record how the run ends.
|
|
59
61
|
type DriveResult =
|
|
60
|
-
|
|
62
|
+
| { kind: 'finished' }
|
|
63
|
+
| { kind: 'released' }
|
|
64
|
+
| { kind: 'judgment'; material: string }
|
|
65
|
+
| { kind: 'failed'; note: string }
|
|
61
66
|
|
|
62
67
|
interface LoopStop {
|
|
63
68
|
reason: WakeStopReason
|
|
@@ -89,6 +94,7 @@ async function wake_step(wake: RunWake, ports: LoopPorts): Promise<StepOutcome>
|
|
|
89
94
|
const driven = await ports.drive()
|
|
90
95
|
if (driven.kind === 'finished') return stopped('ended')
|
|
91
96
|
if (driven.kind === 'failed') return stopped(FAILED_REASON, driven.note)
|
|
97
|
+
if (driven.kind === 'released') return { kind: 'continue', wake }
|
|
92
98
|
|
|
93
99
|
const session_id = ports.new_session_id()
|
|
94
100
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { run_wake_loop, type LoopStop } from './run-wake-loop'
|
|
2
|
+
|
|
3
|
+
// **A supervisor that fails while the run is still resumable restarts itself** (joshuafolkken/kit#3332).
|
|
4
|
+
// A `backlog:drive` that exited on a transient `main:sync` failure ended the supervisor as `failed`,
|
|
5
|
+
// and the run then sat for forty minutes with a valid carry record, waiting for one `run:wake --start`
|
|
6
|
+
// that only a person was left to type — joshuafolkken/kit#3297's "resume it yourself" rule binds an AI
|
|
7
|
+
// session, and once the supervisor is gone there is none to read it.
|
|
8
|
+
//
|
|
9
|
+
// **Bounded, and only the bound reaches a person.** A failure that is not transient fails again on
|
|
10
|
+
// every pass, so the restarts stop at `RESTART_LIMIT` and the last failure is handed back for the
|
|
11
|
+
// ordinary warning, its note saying how many restarts went before it.
|
|
12
|
+
|
|
13
|
+
const RESTART_LIMIT = 3
|
|
14
|
+
|
|
15
|
+
interface RestartPorts {
|
|
16
|
+
// One run of the loop over the record the caller claimed once and holds across every restart, so a
|
|
17
|
+
// `--stop` landing in the pause is read by the next pass as the person's stop.
|
|
18
|
+
pass: () => Promise<LoopStop>
|
|
19
|
+
// Whether the carry record can still be continued — `carried`, neither `expired` nor gone.
|
|
20
|
+
is_resumable: () => boolean
|
|
21
|
+
pause: () => Promise<void>
|
|
22
|
+
note: (text: string) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function is_restartable(stop: LoopStop, restarts: number, ports: RestartPorts): boolean {
|
|
26
|
+
return (
|
|
27
|
+
stop.reason === run_wake_loop.FAILED_REASON && restarts < RESTART_LIMIT && ports.is_resumable()
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function restart_note(stop: LoopStop, restarts: number): string {
|
|
32
|
+
return `the supervisor failed (${stop.note ?? 'no note'}); restarting it, ${String(restarts + 1)} of ${String(RESTART_LIMIT)}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function with_restart_count(stop: LoopStop, restarts: number): LoopStop {
|
|
36
|
+
if (restarts === 0 || stop.reason !== run_wake_loop.FAILED_REASON) return stop
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...stop,
|
|
40
|
+
note: [stop.note, `after ${String(restarts)} automatic restarts`].filter(Boolean).join('\n'),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function supervise(ports: RestartPorts, restarts = 0): Promise<LoopStop> {
|
|
45
|
+
const stop = await ports.pass()
|
|
46
|
+
|
|
47
|
+
if (!is_restartable(stop, restarts, ports)) return with_restart_count(stop, restarts)
|
|
48
|
+
|
|
49
|
+
ports.note(restart_note(stop, restarts))
|
|
50
|
+
await ports.pause()
|
|
51
|
+
|
|
52
|
+
return await supervise(ports, restarts + 1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const run_wake_restart = { RESTART_LIMIT, supervise }
|
|
56
|
+
|
|
57
|
+
export { run_wake_restart }
|
|
58
|
+
export type { RestartPorts }
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { agent_argv, type AgentArgvResult } from '#scripts/agent/agent-argv'
|
|
2
2
|
import { agent_role_profile, type AgentProfile } from '#scripts/agent/agent-role-profile'
|
|
3
3
|
import { claude_agent_argv } from '#scripts/agent/claude-agent-argv'
|
|
4
|
-
import { detached_launch, type LaunchArgv } from '
|
|
5
|
-
import { run_invocation } from '
|
|
4
|
+
import { detached_launch, type LaunchArgv } from '#scripts/run/detached-launch'
|
|
5
|
+
import { run_invocation } from '#scripts/run/run-invocation'
|
|
6
6
|
|
|
7
7
|
// How the supervisor starts things: the next agent session, and — at `--start` — its own detached
|
|
8
8
|
// self. Both go through one `launch`, because the two differ only in what is being run
|
|
@@ -159,5 +159,5 @@ const run_wake_session = {
|
|
|
159
159
|
wake_argv,
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
export type { LaunchRequest, LaunchResult } from '
|
|
162
|
+
export type { LaunchRequest, LaunchResult } from '#scripts/run/detached-launch'
|
|
163
163
|
export { run_wake_session }
|
|
@@ -3,8 +3,8 @@ import { backlog_budget } from '#scripts/backlog/backlog-budget'
|
|
|
3
3
|
import { process_identity } from '#scripts/josh/process-identity'
|
|
4
4
|
import { process_owner_schema } from '#scripts/josh/process-owner'
|
|
5
5
|
import { stamp_file } from '#scripts/josh/stamp-file'
|
|
6
|
+
import type { CarryRead } from '#scripts/run/carry/run-carry'
|
|
6
7
|
import { z } from 'zod'
|
|
7
|
-
import type { CarryRead } from './run-carry'
|
|
8
8
|
|
|
9
9
|
// joshuafolkken/kit#1719. joshuafolkken/kit#1714 made a `backlogrun`'s budget survive the session cut
|
|
10
10
|
// and left out the one thing that starts the next session, so the record was carried and the
|
|
@@ -1,38 +1,37 @@
|
|
|
1
|
-
// `
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// The `preinstall` kit writes fetches nothing: it only checks that safe-chain is scanning the install
|
|
2
|
+
// and warns when it is not (joshuafolkken/kit#3269). It used to run `pnpm dlx @aikidosec/safe-chain
|
|
3
|
+
// setup-ci`, which downloaded an unverified package on every install and — `setup-ci` acting only on
|
|
4
|
+
// a CI runner, where kit installs with `--ignore-scripts` — protected nothing. safe-chain's shell
|
|
5
|
+
// integration runs pnpm behind its registry proxy and hands `GLOBAL_AGENT_HTTP_PROXY` to it, which a
|
|
6
|
+
// bare pnpm never sets — the one signal a lifecycle script can read (joshuafolkken/kit#2707).
|
|
7
|
+
const INSTALL_GUIDE_ANCHOR = 'installing-safe-chain'
|
|
8
|
+
const INSTALL_GUIDE_URL = `https://github.com/joshuafolkken/kit/blob/main/SECURITY.md#${INSTALL_GUIDE_ANCHOR}`
|
|
9
|
+
// `.pnpmfile.mjs` recognizes the command by the package name, so the hint names it.
|
|
7
10
|
const INTEGRATION_HINT =
|
|
8
|
-
'Warning: safe-chain is not scanning this install because its shell
|
|
9
|
-
|
|
11
|
+
'Warning: safe-chain (@aikidosec/safe-chain) is not scanning this install because its shell' +
|
|
12
|
+
` integration is not active. Install it with the hash-verified steps at ${INSTALL_GUIDE_URL}` +
|
|
10
13
|
' - then restart your terminal.'
|
|
11
14
|
// `preinstall` runs before any dependency is installed, kit included, so the check is plain Node with
|
|
12
15
|
// no imports. It never fails the install and stays silent on CI, where the runner is set up instead.
|
|
13
16
|
// The source avoids double quotes, `$` and backticks so the shell passes it to Node verbatim.
|
|
14
17
|
const LOCAL_INTEGRATION_CHECK_SOURCE = `if(!process.env.CI&&!process.env.GLOBAL_AGENT_HTTP_PROXY)console.warn('${INTEGRATION_HINT}')`
|
|
15
|
-
const
|
|
18
|
+
const SAFE_CHAIN_CMD = `node -e "${LOCAL_INTEGRATION_CHECK_SOURCE}"`
|
|
19
|
+
// Every `preinstall` an earlier kit wrote: `setup-ci` alone, pinned or not, or followed by the
|
|
20
|
+
// `node -e` check of any earlier wording.
|
|
21
|
+
const LEGACY_PREINSTALL_RE =
|
|
22
|
+
/^pnpm dlx @aikidosec\/safe-chain(?:@\S+)? setup-ci(?: && node -e "[^"]*")?$/u
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Upgrade a `preinstall` kit wrote before the check existed, pinned version included; any other
|
|
22
|
-
// value is the consumer's own and is left exactly as it is.
|
|
24
|
+
// Replace a `preinstall` kit wrote earlier; any other value is the consumer's own and is left exactly
|
|
25
|
+
// as it is.
|
|
23
26
|
function migrate_preinstall(value: string): string {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return with_local_check(value)
|
|
27
|
+
return LEGACY_PREINSTALL_RE.test(value) ? SAFE_CHAIN_CMD : value
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
const SAFE_CHAIN_CMD = with_local_check(SETUP_CI_CMD)
|
|
30
|
-
|
|
31
30
|
const safe_chain_preinstall = {
|
|
32
31
|
SAFE_CHAIN_CMD,
|
|
33
|
-
LOCAL_INTEGRATION_CHECK_CMD,
|
|
34
32
|
LOCAL_INTEGRATION_CHECK_SOURCE,
|
|
35
33
|
INTEGRATION_HINT,
|
|
34
|
+
INSTALL_GUIDE_ANCHOR,
|
|
36
35
|
migrate_preinstall,
|
|
37
36
|
}
|
|
38
37
|
|
|
@@ -8,7 +8,6 @@ import { z } from 'zod'
|
|
|
8
8
|
|
|
9
9
|
const WORKSPACE_FILE = 'pnpm-workspace.yaml'
|
|
10
10
|
const PROJECT_CONFIG_FILE = '.aikido'
|
|
11
|
-
const NPMRC_FILE = '.npmrc'
|
|
12
11
|
const EXCLUSIONS_FIELD = 'minimumPackageAgeExclusions'
|
|
13
12
|
const AGE_FIELD = 'minimumPackageAgeHours'
|
|
14
13
|
const MINUTES_PER_HOUR = 60
|
|
@@ -78,18 +77,22 @@ function replace_safe_chain_block(existing: string, safe_chain: Record<string, u
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
// Safe Chain enforces its own minimum package age (48 hours by default), so CI refused lockfiles pnpm
|
|
81
|
-
// had resolved under
|
|
82
|
-
// floored to whole hours so Safe Chain is never stricter than pnpm, and an
|
|
83
|
-
// nothing; an explicit `0` opt-out is written as 0 so an earlier
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
// had resolved under pnpm's shorter window (kit #2743). The workspace's `minimumReleaseAge` is the
|
|
81
|
+
// source: its minutes are floored to whole hours so Safe Chain is never stricter than pnpm, and an
|
|
82
|
+
// undeclared window writes nothing; an explicit `0` opt-out is written as 0 so an earlier
|
|
83
|
+
// synchronized age cannot outlive it.
|
|
84
|
+
function package_age_hours(workspace: string): number | undefined {
|
|
85
|
+
const minutes = release_age.parse_declared_minimum_release_age(workspace)
|
|
86
86
|
if (minutes === undefined) return undefined
|
|
87
87
|
|
|
88
88
|
return Math.floor(minutes / MINUTES_PER_HOUR)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
function merge_age(
|
|
92
|
-
|
|
91
|
+
function merge_age(
|
|
92
|
+
safe_chain: Record<string, unknown>,
|
|
93
|
+
workspace: string,
|
|
94
|
+
): Record<string, unknown> {
|
|
95
|
+
const hours = package_age_hours(workspace)
|
|
93
96
|
if (hours === undefined) return safe_chain
|
|
94
97
|
|
|
95
98
|
return { ...safe_chain, [AGE_FIELD]: hours }
|
|
@@ -107,22 +110,24 @@ function merge_exclusions(
|
|
|
107
110
|
function has_nothing_to_write(
|
|
108
111
|
existing: string,
|
|
109
112
|
exclusions: ReadonlyArray<string>,
|
|
110
|
-
|
|
113
|
+
workspace: string,
|
|
111
114
|
): boolean {
|
|
112
|
-
return
|
|
115
|
+
return (
|
|
116
|
+
existing.length === 0 && exclusions.length === 0 && package_age_hours(workspace) === undefined
|
|
117
|
+
)
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
function read_exclusions(workspace: string): ReadonlyArray<string> {
|
|
116
121
|
return workspace_schema.parse(yaml_document.parse_yaml(workspace)).minimumReleaseAgeExclude ?? []
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
function merge_project_config(existing: string, workspace: string
|
|
124
|
+
function merge_project_config(existing: string, workspace: string): string {
|
|
120
125
|
const exclusions = read_exclusions(workspace)
|
|
121
126
|
const aikido = yaml_document.parse_yaml(existing)
|
|
122
127
|
const safe_chain = read_mapping(aikido['safe-chain'], 'safe-chain')
|
|
123
|
-
const merged = merge_age(merge_exclusions(safe_chain, exclusions),
|
|
128
|
+
const merged = merge_age(merge_exclusions(safe_chain, exclusions), workspace)
|
|
124
129
|
if (JSON.stringify(merged) === JSON.stringify(safe_chain)) return existing
|
|
125
|
-
if (has_nothing_to_write(existing, exclusions,
|
|
130
|
+
if (has_nothing_to_write(existing, exclusions, workspace)) return existing
|
|
126
131
|
|
|
127
132
|
return replace_safe_chain_block(existing, merged)
|
|
128
133
|
}
|
|
@@ -133,8 +138,7 @@ function sync_project_config(root: string): boolean {
|
|
|
133
138
|
const config_path = path.join(root, PROJECT_CONFIG_FILE)
|
|
134
139
|
const existing = file_reader.read_file_or_empty(config_path)
|
|
135
140
|
const workspace = readFileSync(workspace_path, 'utf8')
|
|
136
|
-
const
|
|
137
|
-
const merged = merge_project_config(existing, workspace, npmrc)
|
|
141
|
+
const merged = merge_project_config(existing, workspace)
|
|
138
142
|
if (merged === existing) return false
|
|
139
143
|
writeFileSync(config_path, merged)
|
|
140
144
|
|
|
@@ -71,7 +71,7 @@ function sync_workspace_yaml(
|
|
|
71
71
|
destination_path: string,
|
|
72
72
|
is_force = false,
|
|
73
73
|
): boolean {
|
|
74
|
-
const template =
|
|
74
|
+
const template = init_ai_copy.read_workspace_template(template_path, destination_path)
|
|
75
75
|
const existing = is_force ? '' : file_reader.read_file_or_empty(destination_path)
|
|
76
76
|
const merged = init_logic.merge_workspace_yaml(existing, template)
|
|
77
77
|
|
|
@@ -29,7 +29,8 @@ function sync_with_merge(
|
|
|
29
29
|
console.info(` ✔ synced ${destination_name}`)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// Removes only the exact settings lines earlier kit releases wrote, which pnpm 12 ignores in
|
|
33
|
+
// `.npmrc` (joshuafolkken/kit#3267); every other line, a `_authToken` line included, is left alone.
|
|
33
34
|
// Sync used to remove the env-var form, which broke every project that had opted its project
|
|
34
35
|
// .npmrc in via `npmrcAuthFile` — there the line is the live credential, and the opt-in itself
|
|
35
36
|
// can live in a deploy platform's dashboard where sync cannot see it (joshuafolkken/kit#759).
|
package/scripts/sync/sync.ts
CHANGED
|
@@ -192,12 +192,23 @@ function sync_prepare_lefthook_warning(destination_path: string): void {
|
|
|
192
192
|
)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
// The `preinstall` earlier kits wrote fetched safe-chain on every install (joshuafolkken/kit#3269);
|
|
196
|
+
// `josh init` replaces it, and this covers everyone already initialized.
|
|
197
|
+
function sync_safe_chain_preinstall(destination_path: string): void {
|
|
198
|
+
sync_package_json_with(
|
|
199
|
+
destination_path,
|
|
200
|
+
(existing) => init_logic.upgrade_safe_chain_preinstall(existing),
|
|
201
|
+
' ✔ synced preinstall safe-chain check',
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
195
205
|
// Every migration an already-initialized consumer needs applied to its own manifest, run as one
|
|
196
206
|
// group so the next one is added here rather than at the call site.
|
|
197
207
|
function sync_package_json_migrations(destination_path: string): void {
|
|
198
208
|
sync_package_manager_version(destination_path)
|
|
199
209
|
sync_secretlint_development_deps(destination_path)
|
|
200
210
|
sync_prepare_lefthook_warning(destination_path)
|
|
211
|
+
sync_safe_chain_preinstall(destination_path)
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
// Two distributed artifacts each depend on a repository setting kit cannot write, and both reports
|
|
@@ -230,8 +241,8 @@ function sync_project_artifacts(is_force: boolean): void {
|
|
|
230
241
|
|
|
231
242
|
sync_sonar_with_template(name_with_owner, is_force)
|
|
232
243
|
sync_config_files()
|
|
233
|
-
// After `
|
|
234
|
-
// (joshuafolkken/kit#2743).
|
|
244
|
+
// After `sync_ai_copy_all`, which may add the `pnpm-workspace.yaml` window `.aikido`'s age is
|
|
245
|
+
// derived from (joshuafolkken/kit#2743, #3267).
|
|
235
246
|
project_config.sync_project_config(PROJECT_ROOT)
|
|
236
247
|
sync_package_json_migrations(path.join(PROJECT_ROOT, PACKAGE_JSON))
|
|
237
248
|
report_repository_settings(name_with_owner)
|
|
@@ -277,6 +288,8 @@ const sync = {
|
|
|
277
288
|
sync_package_manager_version,
|
|
278
289
|
sync_secretlint_development_deps,
|
|
279
290
|
sync_prepare_lefthook_warning,
|
|
291
|
+
sync_safe_chain_preinstall,
|
|
292
|
+
sync_package_json_migrations,
|
|
280
293
|
migrate_prettierrc: did_migrate_prettierrc,
|
|
281
294
|
}
|
|
282
295
|
|