@joshuafolkken/kit 1.1455.0 → 1.1514.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/settings.json +6 -6
- package/.claude/skills/dependency-update/SKILL.md +26 -55
- package/.claude/skills/epic-commands/SKILL.md +83 -156
- package/.claude/skills/epic-commands/epic-bundle.md +51 -79
- package/.claude/skills/epic-commands/execution-waves.md +43 -81
- package/.claude/skills/verify-ui/SKILL.md +24 -50
- package/.claude/skills/workflow-commands/SKILL.md +7 -9
- package/.claude/skills/workflow-commands/background-commands.md +31 -46
- package/.claude/skills/workflow-commands/backlogrun-child.md +65 -85
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +86 -125
- package/.claude/skills/workflow-commands/backlogrun-park.md +68 -118
- package/.claude/skills/workflow-commands/backlogrun-progress.md +17 -28
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +17 -26
- package/.claude/skills/workflow-commands/backlogrun-steps.md +35 -52
- package/.claude/skills/workflow-commands/backlogrun.md +31 -50
- package/.claude/skills/workflow-commands/chain-rule.md +31 -40
- package/.claude/skills/workflow-commands/delegation.md +12 -8
- package/.claude/skills/workflow-commands/entry-sequence.md +1 -3
- package/.claude/skills/workflow-commands/followup.md +55 -77
- package/.claude/skills/workflow-commands/fullrun-steps.md +3 -5
- package/.claude/skills/workflow-commands/fullrun.md +4 -6
- package/.claude/skills/workflow-commands/halfrun.md +3 -7
- package/.claude/skills/workflow-commands/into-target.md +1 -2
- package/.claude/skills/workflow-commands/issue-comments.md +43 -60
- package/.claude/skills/workflow-commands/issue-scout.md +3 -2
- package/.claude/skills/workflow-commands/kickoff.md +65 -74
- package/.claude/skills/workflow-commands/latest-gate.md +2 -6
- package/.claude/skills/workflow-commands/needs-human-review.md +2 -5
- package/.claude/skills/workflow-commands/observation-filing.md +59 -118
- package/.claude/skills/workflow-commands/observation-ledger.md +52 -112
- package/.claude/skills/workflow-commands/pre-gate-cut.md +88 -142
- package/.claude/skills/workflow-commands/prerequisite.md +11 -17
- package/.claude/skills/workflow-commands/progress-watcher.md +23 -33
- package/.claude/skills/workflow-commands/prrun.md +3 -5
- package/.claude/skills/workflow-commands/retrospective.md +35 -54
- package/.claude/skills/workflow-commands/split-assessment.md +32 -58
- package/.claude/skills/workflow-commands/target-repository.md +1 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +54 -73
- package/.codex/hooks.json +6 -6
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish.yml +2 -2
- package/CLAUDE.md +7 -6
- package/cspell/index.yaml +7 -0
- package/dist/CLAUDE.md +7 -6
- package/dist/commands/document-section-cli.js +67 -34
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/chunk-4KKD2UTK.js +66 -0
- package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-6TS2JD6D.js} +14 -3
- package/dist/hooks/chunk-7DSBJVFH.js +50 -0
- package/dist/hooks/{chunk-5H4SSDSD.js → chunk-A65Y4YOR.js} +3 -3
- package/dist/hooks/{chunk-3C4XRYG6.js → chunk-APUO6U4J.js} +102 -19
- package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
- package/dist/hooks/chunk-GP4IEC4O.js +20 -0
- package/dist/hooks/chunk-JP45SOCJ.js +56 -0
- package/dist/hooks/{chunk-OLMO72ON.js → chunk-LAOV7NLM.js} +1 -1
- package/dist/hooks/chunk-M4HGZV7U.js +83 -0
- package/dist/hooks/{chunk-TBJFTSUY.js → chunk-O6GDRNJB.js} +9 -7
- package/dist/hooks/{chunk-HFGWYGUV.js → chunk-PF2NZL2A.js} +5 -5
- package/dist/hooks/{chunk-WNLVGT7E.js → chunk-PTICX4OL.js} +4 -5
- package/dist/hooks/{chunk-HHI6FO3R.js → chunk-RT27QRJW.js} +763 -1515
- package/dist/hooks/{chunk-7WQBPLUN.js → chunk-SYOTDYCI.js} +1149 -1095
- package/dist/hooks/{chunk-LIAFCXL7.js → chunk-TNUMY6NL.js} +1519 -1332
- package/dist/hooks/chunk-UPUZF3AN.js +279 -0
- package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-WK6SMBIC.js} +1932 -784
- package/dist/hooks/codex-hook-adapter.js +17 -14
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +5 -4
- package/dist/hooks/inputs.json +302 -247
- package/dist/hooks/investigation-guard.js +8 -11
- package/dist/hooks/pretool-guard.js +16 -13
- package/dist/hooks/session-lang.js +372 -26
- package/dist/hooks/stop-guard.js +2593 -189
- package/dist/josh.js +2128 -282
- package/dist/managed-marker/index.js +1 -2
- package/dist/version/index.js +15 -12
- package/eslint/base.js +2 -1
- package/eslint/rules/unicorn.js +16 -0
- package/lefthook/base.yml +5 -3
- package/package.json +14 -12
- package/prompts/coding-standards.md +12 -22
- package/prompts/collaboration-workflow/file-edits.md +25 -45
- package/prompts/collaboration-workflow/gh-rest.md +19 -36
- package/prompts/collaboration-workflow/glossary.md +5 -7
- package/prompts/collaboration-workflow/issue-citation.md +6 -14
- package/prompts/collaboration-workflow/issue-template.md +40 -32
- package/prompts/collaboration-workflow/lane-instructions.md +35 -0
- package/prompts/collaboration-workflow/operating-rules.md +33 -46
- package/prompts/collaboration-workflow/output-bounds.md +15 -15
- package/prompts/collaboration-workflow/overview.md +14 -19
- package/prompts/collaboration-workflow/plan-comment.md +13 -13
- package/prompts/collaboration-workflow/principles.md +57 -54
- package/prompts/collaboration-workflow/proposal-request.md +1 -9
- package/prompts/collaboration-workflow/report-format.md +41 -49
- package/prompts/collaboration-workflow/residency.md +15 -11
- package/prompts/collaboration-workflow/rule-delivery.md +7 -15
- package/prompts/collaboration-workflow/shell-body.md +10 -20
- package/prompts/collaboration-workflow/turn-batching.md +8 -14
- package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
- package/prompts/collaboration-workflow/wip-cap.md +26 -32
- package/prompts/collaboration-workflow.md +25 -25
- package/prompts/refactoring.md +33 -43
- package/prompts/review-rubric.md +84 -129
- package/prompts/review.md +110 -141
- package/prompts/sonar-hotspot-handling.md +22 -50
- package/prompts/testing-guide.md +48 -99
- package/scripts/adopt/adopt-toolkits.ts +2 -4
- package/scripts/agent/agent-argv.ts +20 -4
- package/scripts/agent/agent-exit-record.ts +23 -1
- package/scripts/agent/agent-role-profile.ts +101 -45
- package/scripts/agent/agent-safe-text.ts +60 -0
- package/scripts/agent/claude-agent-argv.ts +14 -0
- package/scripts/agent/codex-agent-argv.ts +9 -2
- package/scripts/agent/lane-instructions.ts +144 -0
- package/scripts/backlog/backlog-budget-cli.ts +1 -2
- package/scripts/backlog/backlog-budget.ts +4 -4
- package/scripts/backlog/backlog-drive-carried.ts +26 -0
- package/scripts/backlog/backlog-drive-cli.ts +10 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -5
- package/scripts/backlog/backlog-drive-restore.ts +28 -2
- package/scripts/backlog/backlog-drive-seats.ts +27 -0
- package/scripts/backlog/backlog-drive.ts +11 -6
- package/scripts/backlog/backlog-occupied.ts +56 -0
- package/scripts/backlog/backlog-ready.ts +59 -15
- package/scripts/behavior/behavior-rules.ts +3 -2
- package/scripts/cost-runtime/codex-usage.ts +4 -2
- package/scripts/cost-runtime/cost-cli.ts +1 -2
- package/scripts/delegation/duplicate-read-guard.ts +2 -2
- package/scripts/delegation/investigation-guard.ts +15 -65
- package/scripts/delegation/investigation-reads.ts +1 -2
- package/scripts/discovery/repo-discovery.ts +4 -2
- package/scripts/discovery/repo-origin.ts +2 -4
- package/scripts/doctor/doctor-logic.ts +6 -3
- package/scripts/document/document-byte-budget.ts +28 -27
- package/scripts/document/document-byte-check.ts +1 -2
- package/scripts/document/document-section-cli.ts +17 -5
- package/scripts/document/entry-read-budget.ts +3 -3
- package/scripts/document/read-set-cli.ts +4 -1
- package/scripts/epic/epic-audit-orphans.ts +5 -8
- package/scripts/epic/epic-audit-rationale.ts +8 -2
- package/scripts/epic/epic-audit.ts +52 -1
- package/scripts/epic/epic-bundle-cli.ts +1 -2
- package/scripts/epic/epic-bundle-evidence.ts +49 -26
- package/scripts/epic/epic-bundle-referenced.ts +14 -3
- package/scripts/epic/epic-bundle.ts +17 -9
- package/scripts/epic/epic-chains.ts +6 -2
- package/scripts/epic/epic-close-comment.ts +1 -2
- package/scripts/followup/git-followup-workflow.ts +19 -1
- package/scripts/gate/check-inputs.ts +129 -0
- package/scripts/gate/check-reuse.ts +162 -0
- package/scripts/gate/core-budget.ts +93 -44
- package/scripts/gate/core-wait.ts +130 -0
- package/scripts/gate/free-cores.ts +49 -0
- package/scripts/gate/gate-ledger.ts +17 -3
- package/scripts/gate/gate-plan.ts +23 -1
- package/scripts/gate/gate-report.ts +15 -2
- package/scripts/gate/gate-run-steps.ts +150 -0
- package/scripts/gate/held-mark.ts +55 -0
- package/scripts/gate/machine-capacity.ts +70 -11
- package/scripts/gate/pre-commit-type-check.ts +14 -1
- package/scripts/gate/verification-gate.ts +65 -127
- package/scripts/gh/git-gh-issue-rest.ts +1 -0
- package/scripts/gh/git-gh-issue-write.ts +24 -2
- package/scripts/gh/git-pr.ts +19 -3
- package/scripts/git/git-common-directory.ts +3 -3
- package/scripts/git/git-diff-reads.ts +16 -9
- package/scripts/git/git-execa-error.ts +1 -2
- package/scripts/git/main-merge-regenerate.ts +78 -0
- package/scripts/git/main-merge.ts +14 -5
- package/scripts/hooks/batch-guard.ts +5 -8
- package/scripts/hooks/format-edited-file.ts +10 -4
- package/scripts/hooks/pre-commit-lint.ts +91 -0
- package/scripts/hooks/pre-push-unit.ts +15 -4
- package/scripts/hooks/pretool-guard.ts +48 -79
- package/scripts/hooks/reserved-run.ts +1 -0
- package/scripts/hooks/run-hook.sh +10 -12
- package/scripts/hooks/step-zero-notice.ts +3 -2
- package/scripts/hooks/stop-actions.ts +204 -0
- package/scripts/hooks/stop-guard.ts +62 -21
- package/scripts/init/hook-command-rewrite.ts +6 -2
- package/scripts/init/hook-launch.ts +12 -7
- package/scripts/init/init-logic-deploy-vps.ts +4 -2
- package/scripts/issue/behavior-change-lint.ts +12 -6
- package/scripts/issue/expected-effect-lint.ts +59 -0
- package/scripts/issue/issue-cite-cli.ts +35 -15
- package/scripts/issue/issue-classification.ts +1 -1
- package/scripts/issue/issue-lint-cli.ts +5 -3
- package/scripts/issue/issue-scout-cli.ts +22 -16
- package/scripts/issue/markdown-section.ts +19 -5
- package/scripts/josh/command-usage.ts +110 -0
- package/scripts/josh/file-map-stamp.ts +1 -3
- package/scripts/josh/hook-decision.ts +17 -2
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +55 -9
- package/scripts/josh/josh-commands-ai.ts +13 -0
- package/scripts/josh/josh-commands-development.ts +1 -1
- package/scripts/josh/josh-commands-hooks.ts +6 -0
- package/scripts/josh/josh-commands-lane.ts +10 -1
- package/scripts/josh/josh-commands-workflow.ts +5 -0
- package/scripts/josh/josh-logic.ts +6 -15
- package/scripts/josh/josh-usage-automation.ts +121 -0
- package/scripts/josh/josh-usage-backlog.ts +180 -0
- package/scripts/josh/josh-usage-developer.ts +230 -0
- package/scripts/josh/josh-usage-hooks.ts +38 -0
- package/scripts/josh/josh-usage-issue.ts +205 -0
- package/scripts/josh/josh-usage-review.ts +101 -0
- package/scripts/josh/josh-usage-run.ts +252 -0
- package/scripts/josh/josh-usage-session.ts +238 -0
- package/scripts/josh/josh-usage-workflow.ts +212 -0
- package/scripts/josh/josh.ts +14 -5
- package/scripts/josh/package-directory.ts +20 -0
- package/scripts/lane/lane-await.ts +3 -2
- package/scripts/lane/lane-capacity.ts +8 -2
- package/scripts/lane/lane-check-stats.ts +56 -0
- package/scripts/lane/lane-child-invocation.ts +49 -5
- package/scripts/lane/lane-dispatch.ts +3 -3
- package/scripts/lane/lane-guard-policy.ts +5 -5
- package/scripts/lane/lane-handoff.ts +8 -1
- package/scripts/lane/lane-install.ts +12 -4
- package/scripts/lane/lane-ledger.ts +52 -1
- package/scripts/lane/lane-limit-cli.ts +8 -9
- package/scripts/lane/lane-load.ts +9 -2
- package/scripts/lane/lane-measure-cli.ts +4 -0
- package/scripts/lane/lane-parked.ts +18 -0
- package/scripts/lane/lane-preserve.ts +29 -1
- package/scripts/lane/lane-process-load.ts +125 -0
- package/scripts/lane/lane-process-stats.ts +93 -0
- package/scripts/lane/lane-registry.ts +17 -10
- package/scripts/lane/lane-restart.ts +5 -26
- package/scripts/lane/lane-session-action.ts +146 -0
- package/scripts/lane/lane-session-broker.ts +68 -125
- package/scripts/lane/lane-spent.ts +14 -3
- package/scripts/lane/lane-stats.ts +1 -0
- package/scripts/lane/lane-withdraw-cli.ts +71 -0
- package/scripts/lane/lane-withdraw.ts +182 -0
- package/scripts/lint/format.ts +4 -4
- package/scripts/lint/lint-parallel.ts +10 -3
- package/scripts/lint/lint-related-scope.ts +4 -1
- package/scripts/managed-marker/managed-marker-logic.ts +1 -2
- package/scripts/notify/telegram-test.ts +10 -5
- package/scripts/overrides/lockfile-overrides.ts +1 -3
- package/scripts/propagate/propagate.ts +1 -2
- package/scripts/release/release-progress.ts +3 -2
- package/scripts/report/report-format-reference.ts +2 -0
- package/scripts/report/report-lint.ts +7 -4
- package/scripts/review/acceptance-map-check.ts +61 -0
- package/scripts/review/acceptance-map.ts +205 -0
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-record-cli.ts +6 -2
- package/scripts/rules/agent-model.ts +59 -0
- package/scripts/rules/delivered-rules.ts +11 -2
- package/scripts/rules/destructive-command.ts +6 -9
- package/scripts/rules/direct-filing.ts +3 -7
- package/scripts/rules/early-heartbeat.ts +7 -18
- package/scripts/rules/file-body.ts +6 -13
- package/scripts/rules/filing-cap.ts +4 -6
- package/scripts/rules/firing-point.ts +6 -5
- package/scripts/rules/git-force.ts +5 -9
- package/scripts/rules/guard-remedy.ts +117 -0
- package/scripts/rules/implementation-cut.ts +37 -35
- package/scripts/rules/index-guard.ts +4 -7
- package/scripts/rules/issue-comments.ts +67 -16
- package/scripts/rules/josh-git-bare.ts +3 -6
- package/scripts/rules/lane-background.ts +10 -21
- package/scripts/rules/lane-carry-conflict.ts +7 -17
- package/scripts/rules/lane-interactive-ask.ts +6 -11
- package/scripts/rules/lane-park.ts +5 -13
- package/scripts/rules/lane-split-park.ts +2 -7
- package/scripts/rules/lane-switch-main.ts +9 -12
- package/scripts/rules/last-prompt.ts +50 -5
- package/scripts/rules/oracle-consulted.ts +4 -5
- package/scripts/rules/oracle-list-cli.ts +7 -0
- package/scripts/rules/piped-verification.ts +84 -32
- package/scripts/rules/poll-loop.ts +7 -17
- package/scripts/rules/pre-gate-cut.ts +7 -21
- package/scripts/rules/prior-comment-read.ts +7 -5
- package/scripts/rules/protected-files.ts +5 -9
- package/scripts/rules/raw-field-body.ts +3 -7
- package/scripts/rules/rule-body-guard.ts +4 -11
- package/scripts/rules/rule-list.ts +4 -1
- package/scripts/rules/rule-registry-rows.ts +10 -0
- package/scripts/rules/rule-registry.ts +7 -4
- package/scripts/rules/rule-rewrite.ts +114 -0
- package/scripts/rules/rule-value.ts +7 -1
- package/scripts/rules/run-tail-rule.ts +6 -22
- package/scripts/rules/shell-body-trigger.ts +4 -6
- package/scripts/rules/shell-segments.ts +22 -0
- package/scripts/rules/stop-decision.ts +77 -0
- package/scripts/rules/stop-rules.ts +164 -77
- package/scripts/rules/tail-commands.ts +15 -1
- package/scripts/rules/test-declared-commit.ts +4 -10
- package/scripts/rules/third-party-write.ts +5 -10
- package/scripts/rules/worktree-guard.ts +5 -12
- package/scripts/run/board/run-board-cli.ts +2 -0
- package/scripts/run/board/run-board-closed.ts +8 -2
- package/scripts/run/board/run-board-github.ts +19 -3
- package/scripts/run/board/run-board-header.ts +4 -1
- package/scripts/run/board/run-board-idle.ts +31 -0
- package/scripts/run/board/run-board-labels.ts +2 -0
- package/scripts/run/board/run-board-layout.ts +1 -1
- package/scripts/run/board/run-board-read.ts +25 -12
- package/scripts/run/board/run-board-reason.ts +17 -4
- package/scripts/run/board/run-board-render-legend.ts +1 -0
- package/scripts/run/board/run-board-render-slot.ts +1 -1
- package/scripts/run/board/run-board-render-waits.ts +41 -0
- package/scripts/run/board/run-board-render.ts +6 -1
- package/scripts/run/board/run-board-row-yield.ts +1 -0
- package/scripts/run/board/run-board-state.ts +9 -0
- package/scripts/run/board/run-board-status.ts +89 -23
- package/scripts/run/board/run-board-thanks.ts +7 -2
- package/scripts/run/board/run-board-tick.ts +37 -8
- package/scripts/run/carry/run-carry-args.ts +1 -3
- package/scripts/run/carry/run-carry-conversation.ts +7 -2
- package/scripts/run/cut/run-cut-cli.ts +18 -22
- package/scripts/run/cut/run-cut-decline.ts +72 -0
- package/scripts/run/cut/run-cut-ready.ts +74 -0
- package/scripts/run/cut/run-cut-report.ts +29 -0
- package/scripts/run/cut/run-cut.ts +10 -1
- package/scripts/run/detached-launch.ts +23 -4
- package/scripts/run/entry/run-entry-cli.ts +8 -1
- package/scripts/run/event/run-event-render.ts +1 -0
- package/scripts/run/event/run-event-scope.ts +19 -1
- package/scripts/run/event/run-event-stream-emit.ts +13 -6
- package/scripts/run/event/run-event-stream.ts +4 -0
- package/scripts/run/hold/run-hold-cli.ts +14 -3
- package/scripts/run/hold/run-hold-session.ts +46 -0
- package/scripts/run/hold/run-hold.ts +13 -4
- package/scripts/run/launch-priority.ts +32 -0
- package/scripts/run/merge/run-merge-cli.ts +16 -42
- package/scripts/run/merge/run-merge-ship.ts +59 -1
- package/scripts/run/merge/run-merge-signals.ts +99 -0
- package/scripts/run/merge/run-merge-steps.ts +11 -1
- package/scripts/run/merge/run-merge.ts +22 -9
- package/scripts/run/run-ending.ts +31 -3
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +5 -10
- package/scripts/run/run-poll-hook.ts +157 -0
- package/scripts/run/run-preflight.ts +1 -2
- package/scripts/run/run-step-cli.ts +22 -15
- package/scripts/run/run-step-section.ts +85 -0
- package/scripts/run/run-stop-notify.ts +1 -2
- package/scripts/run/run-watcher-guard.ts +6 -3
- package/scripts/run/ship/run-ship-cli.ts +5 -12
- package/scripts/run/ship/run-ship-detach.ts +9 -0
- package/scripts/run/ship/run-ship-next.ts +10 -2
- package/scripts/run/ship/run-ship-overlap.ts +195 -0
- package/scripts/run/ship/run-ship-pre-detach.ts +33 -15
- package/scripts/run/ship/run-ship-preflight.ts +28 -2
- package/scripts/run/ship/run-ship-return.ts +31 -7
- package/scripts/run/ship/run-ship-review-steps.ts +25 -14
- package/scripts/run/ship/run-ship-review.ts +15 -2
- package/scripts/run/ship/run-ship-steps.ts +1 -1
- package/scripts/run/ship/run-ship-stop-context.ts +206 -0
- package/scripts/run/ship/run-ship-sync.ts +14 -6
- package/scripts/run/tidy/run-tidy-cli.ts +3 -3
- package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
- package/scripts/safe-chain/project-config.ts +7 -2
- package/scripts/sync/sync-hook-safety.ts +8 -2
- package/scripts/test/test-e2e-guard.ts +30 -5
- package/scripts/test/test-unit-guard.ts +16 -1
- package/scripts/test/unit-worker-share.ts +26 -3
- package/scripts/time-runtime/time-batch-guard.ts +3 -3
- package/scripts/time-runtime/time-hook-transcript.ts +3 -2
- package/scripts/time-runtime/time-shell.ts +39 -4
- package/scripts/time-runtime/time-spans.ts +1 -0
- package/scripts/time-runtime/time-tool-call.ts +7 -0
- package/scripts/version/effective-upstream.ts +3 -4
- package/scripts/version/kit-version-config.ts +8 -2
- package/scripts/version/latest-corepack.ts +1 -2
- package/scripts/version/release-hold.ts +11 -4
- package/scripts/version/version-commands.ts +12 -4
- package/templates/gitignore +6 -2
- package/dist/hooks/chunk-D5QBTOCZ.js +0 -68
- package/dist/hooks/chunk-UT7YC3N2.js +0 -56
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
FULL_PAGE_QUERY,
|
|
3
|
+
MERGED_STATE,
|
|
3
4
|
detached_launch,
|
|
5
|
+
forget_pr_numbers,
|
|
6
|
+
gh_failure,
|
|
4
7
|
git_common_directory,
|
|
8
|
+
git_gh_api_path,
|
|
9
|
+
git_gh_exec,
|
|
10
|
+
git_gh_helpers,
|
|
11
|
+
git_gh_pr_read,
|
|
12
|
+
git_gh_pr_rest,
|
|
5
13
|
issue_cite,
|
|
6
14
|
lane_child_invocation,
|
|
15
|
+
parse_json_array_or_undefined,
|
|
16
|
+
parse_json_object_or_undefined,
|
|
17
|
+
read_json_listing,
|
|
18
|
+
read_pull,
|
|
7
19
|
repo_discovery,
|
|
8
20
|
repo_origin,
|
|
21
|
+
require_pr_number,
|
|
9
22
|
run_event_stream,
|
|
10
|
-
run_event_stream_emit
|
|
11
|
-
|
|
23
|
+
run_event_stream_emit,
|
|
24
|
+
run_hold,
|
|
25
|
+
session_cite,
|
|
26
|
+
stamp_record,
|
|
27
|
+
to_gh_state
|
|
28
|
+
} from "./chunk-WK6SMBIC.js";
|
|
12
29
|
import {
|
|
13
30
|
error_text
|
|
14
31
|
} from "./chunk-CDY4NFQG.js";
|
|
32
|
+
import {
|
|
33
|
+
agent_role_profile,
|
|
34
|
+
agent_session_role
|
|
35
|
+
} from "./chunk-UPUZF3AN.js";
|
|
15
36
|
import {
|
|
16
37
|
CHECK_STATUS_FAIL,
|
|
17
38
|
CHECK_STATUS_PENDING,
|
|
18
39
|
PrConflictError,
|
|
19
|
-
|
|
40
|
+
cost_attribute,
|
|
20
41
|
cost_blocks,
|
|
21
42
|
describe_pr_failure,
|
|
22
43
|
evaluate_pr_state,
|
|
@@ -26,25 +47,28 @@ import {
|
|
|
26
47
|
is_review_decision_decisive,
|
|
27
48
|
parse_pr_state_snapshot,
|
|
28
49
|
read_string,
|
|
29
|
-
rest_comment_schema,
|
|
30
50
|
time_density_hook,
|
|
31
51
|
time_shell,
|
|
32
52
|
time_transcript_line
|
|
33
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-TNUMY6NL.js";
|
|
34
54
|
import {
|
|
35
55
|
lane_child_marker
|
|
36
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-LAOV7NLM.js";
|
|
37
57
|
import {
|
|
38
|
-
agent_session_environment,
|
|
39
58
|
backlog_budget,
|
|
59
|
+
blocking_issue_schema,
|
|
60
|
+
cli_flags,
|
|
61
|
+
cost_transcript,
|
|
40
62
|
cost_usage,
|
|
41
63
|
git_command,
|
|
42
|
-
|
|
64
|
+
issue_number_shape,
|
|
43
65
|
json_value,
|
|
66
|
+
lane_paths,
|
|
67
|
+
own_session,
|
|
44
68
|
process_identity
|
|
45
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-APUO6U4J.js";
|
|
46
70
|
import {
|
|
47
|
-
|
|
71
|
+
PLATFORM_TEMP_ROOT,
|
|
48
72
|
PROJECT_ROOT,
|
|
49
73
|
stamp_file
|
|
50
74
|
} from "./chunk-DGT54IH5.js";
|
|
@@ -84,18 +108,51 @@ async function bounded_map(items, limit, worker) {
|
|
|
84
108
|
}
|
|
85
109
|
var bounded_pool = { bounded_map, pool_width };
|
|
86
110
|
|
|
111
|
+
// scripts/rules/tail-commands.ts
|
|
112
|
+
function is_bash_use(block) {
|
|
113
|
+
return block.type === cost_blocks.TOOL_USE_TYPE && block.name === cost_blocks.BASH_TOOL;
|
|
114
|
+
}
|
|
115
|
+
function bash_commands_of(line) {
|
|
116
|
+
const parsed = time_transcript_line.parse_line(line);
|
|
117
|
+
if (parsed === void 0) return [];
|
|
118
|
+
return parsed.blocks.filter((block) => is_bash_use(block)).map((block) => time_shell.bash_command(block.input));
|
|
119
|
+
}
|
|
120
|
+
function prior_bash_commands(tail) {
|
|
121
|
+
return tail.split("\n").flatMap((line) => bash_commands_of(line));
|
|
122
|
+
}
|
|
123
|
+
function answered(text, is_call, token) {
|
|
124
|
+
const blocks = time_transcript_line.parse_text(text).flatMap((line) => line.blocks);
|
|
125
|
+
const call_ids = new Set(
|
|
126
|
+
blocks.filter((block) => is_call(time_shell.bash_command(block.input))).map((block) => block.id)
|
|
127
|
+
);
|
|
128
|
+
return blocks.some((block) => call_ids.has(block.result_id) && block.token_lines.includes(token));
|
|
129
|
+
}
|
|
130
|
+
var tail_commands = { answered, is_bash_use, prior_bash_commands };
|
|
131
|
+
|
|
87
132
|
// scripts/rules/shell-segments.ts
|
|
88
133
|
var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
|
|
134
|
+
var SEGMENT_PARTS = new RegExp(`(${SEGMENT_SEPARATOR.source})`, "u");
|
|
89
135
|
function segments_of(command) {
|
|
90
136
|
return command.split(SEGMENT_SEPARATOR).map((segment) => segment.trim());
|
|
91
137
|
}
|
|
138
|
+
function parts_of(command) {
|
|
139
|
+
return command.split(SEGMENT_PARTS);
|
|
140
|
+
}
|
|
141
|
+
function first_of(command) {
|
|
142
|
+
const plain = time_shell.blanked(command);
|
|
143
|
+
if (plain === void 0) return void 0;
|
|
144
|
+
const cut = plain.search(SEGMENT_SEPARATOR);
|
|
145
|
+
return (cut === -1 ? command : command.slice(0, cut)).trim();
|
|
146
|
+
}
|
|
92
147
|
function is_josh_command(segment, names) {
|
|
93
148
|
const named = time_shell.josh_command_of(segment);
|
|
94
149
|
if (!named.startsWith(time_shell.JOSH_PREFIX)) return false;
|
|
95
150
|
return names.has(named.slice(time_shell.JOSH_PREFIX.length));
|
|
96
151
|
}
|
|
97
152
|
var shell_segments = {
|
|
153
|
+
first_of,
|
|
98
154
|
is_josh_command,
|
|
155
|
+
parts_of,
|
|
99
156
|
segments_of
|
|
100
157
|
};
|
|
101
158
|
|
|
@@ -119,20 +176,6 @@ function is_issue_filing(command) {
|
|
|
119
176
|
}
|
|
120
177
|
var bash_triggers = { is_direct_filing, is_issue_filing, on_bash_command };
|
|
121
178
|
|
|
122
|
-
// scripts/rules/tail-commands.ts
|
|
123
|
-
function is_bash_use(block) {
|
|
124
|
-
return block.type === cost_blocks.TOOL_USE_TYPE && block.name === cost_blocks.BASH_TOOL;
|
|
125
|
-
}
|
|
126
|
-
function bash_commands_of(line) {
|
|
127
|
-
const parsed = time_transcript_line.parse_line(line);
|
|
128
|
-
if (parsed === void 0) return [];
|
|
129
|
-
return parsed.blocks.filter((block) => is_bash_use(block)).map((block) => time_shell.bash_command(block.input));
|
|
130
|
-
}
|
|
131
|
-
function prior_bash_commands(tail) {
|
|
132
|
-
return tail.split("\n").flatMap((line) => bash_commands_of(line));
|
|
133
|
-
}
|
|
134
|
-
var tail_commands = { is_bash_use, prior_bash_commands };
|
|
135
|
-
|
|
136
179
|
// scripts/rules/filing-cap.ts
|
|
137
180
|
var FILING_CAP = 10;
|
|
138
181
|
var CAP_REACHED = FILING_CAP;
|
|
@@ -171,10 +214,10 @@ function current_turn(tail) {
|
|
|
171
214
|
function turn_filing_count(tail) {
|
|
172
215
|
return prior_filing_count(current_turn(tail));
|
|
173
216
|
}
|
|
174
|
-
function decide(_call,
|
|
175
|
-
return prior_filing_count(time_density_hook.read_tail(
|
|
217
|
+
function decide(_call, run2) {
|
|
218
|
+
return prior_filing_count(time_density_hook.read_tail(run2.transcript)) >= CAP_REACHED;
|
|
176
219
|
}
|
|
177
|
-
var FILING_CAP_REASON = `\u26D4 filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling
|
|
220
|
+
var FILING_CAP_REASON = `\u26D4 filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling. Stop filing and report; a further Issue is a person's call. It fires on every occurrence (\`.claude/skills/workflow-commands/observation-filing.md\`).`;
|
|
178
221
|
var ROW = {
|
|
179
222
|
id: "filing-cap",
|
|
180
223
|
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
@@ -185,337 +228,12 @@ var filing_cap = {
|
|
|
185
228
|
FILING_CAP,
|
|
186
229
|
FILING_CAP_REASON,
|
|
187
230
|
ROW,
|
|
231
|
+
current_turn,
|
|
188
232
|
is_prompt_line,
|
|
189
233
|
prior_filing_count,
|
|
190
234
|
turn_filing_count
|
|
191
235
|
};
|
|
192
236
|
|
|
193
|
-
// scripts/rules/issue-citation.ts
|
|
194
|
-
var ISSUE_NUMBER = /#\d+/gu;
|
|
195
|
-
var REFERENCE_BOUNDARY = /[\s#()[\]<>|]/u;
|
|
196
|
-
var OWNER_REPO = /^[^\s/#]+\/[^\s/#]+$/u;
|
|
197
|
-
var MARKDOWN_LINK = /\[[^[\]]*\]\([^()]*\)/gu;
|
|
198
|
-
var NUMBER_CONTINUATION = /^(?:\w|-[a-z])/iu;
|
|
199
|
-
var CONTINUATION_WIDTH = 2;
|
|
200
|
-
var GLUED_LEAD = /[&./]/u;
|
|
201
|
-
var FENCE_MARKER = /^\s*(?:`{3,}|~{3,})/u;
|
|
202
|
-
var QUOTE_LINE = /^\s*>/u;
|
|
203
|
-
var PR_PREFIX = /\b(?:pr|pull request)\s*$/iu;
|
|
204
|
-
var BACKTICK_PAIR = 2;
|
|
205
|
-
function link_covers(link, index) {
|
|
206
|
-
const start = link.index ?? 0;
|
|
207
|
-
return index >= start && index < start + link[0].length;
|
|
208
|
-
}
|
|
209
|
-
function is_linked_at(line, index) {
|
|
210
|
-
for (const link of line.matchAll(MARKDOWN_LINK)) {
|
|
211
|
-
if (link_covers(link, index)) return true;
|
|
212
|
-
}
|
|
213
|
-
return false;
|
|
214
|
-
}
|
|
215
|
-
function lead_token(message, hash_index) {
|
|
216
|
-
let start = hash_index;
|
|
217
|
-
while (start > 0 && !REFERENCE_BOUNDARY.test(message.charAt(start - 1))) start -= 1;
|
|
218
|
-
return message.slice(start, hash_index);
|
|
219
|
-
}
|
|
220
|
-
function repo_prefix(message, hash_index) {
|
|
221
|
-
const token = lead_token(message, hash_index);
|
|
222
|
-
return OWNER_REPO.test(token) ? token : "";
|
|
223
|
-
}
|
|
224
|
-
function is_inline_code_at(line, index) {
|
|
225
|
-
const backticks = line.slice(0, index).split("`").length - 1;
|
|
226
|
-
return backticks % BACKTICK_PAIR === 1;
|
|
227
|
-
}
|
|
228
|
-
function is_bare_at(line, index) {
|
|
229
|
-
if (is_linked_at(line, index) || is_inline_code_at(line, index)) return false;
|
|
230
|
-
return !PR_PREFIX.test(line.slice(0, index));
|
|
231
|
-
}
|
|
232
|
-
function is_glued(line, reference) {
|
|
233
|
-
const end = reference.index + reference.text.length;
|
|
234
|
-
if (NUMBER_CONTINUATION.test(line.slice(end, end + CONTINUATION_WIDTH))) return true;
|
|
235
|
-
return reference.prefix === "" && GLUED_LEAD.test(lead_token(line, reference.index));
|
|
236
|
-
}
|
|
237
|
-
function line_references(line) {
|
|
238
|
-
const references = [];
|
|
239
|
-
for (const match of line.matchAll(ISSUE_NUMBER)) {
|
|
240
|
-
const reference = { index: match.index, prefix: repo_prefix(line, match.index), text: match[0] };
|
|
241
|
-
if (!is_glued(line, reference) && is_bare_at(line, match.index)) references.push(reference);
|
|
242
|
-
}
|
|
243
|
-
return references;
|
|
244
|
-
}
|
|
245
|
-
function collect_line_references(line, found) {
|
|
246
|
-
for (const reference of line_references(line)) found.push(`${reference.prefix}${reference.text}`);
|
|
247
|
-
}
|
|
248
|
-
function lines_outside_fences(message) {
|
|
249
|
-
const lines = [];
|
|
250
|
-
let is_in_fence = false;
|
|
251
|
-
for (const line of message.split("\n")) {
|
|
252
|
-
if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
|
|
253
|
-
else if (!is_in_fence) lines.push(line);
|
|
254
|
-
}
|
|
255
|
-
return lines;
|
|
256
|
-
}
|
|
257
|
-
function prose_lines(message) {
|
|
258
|
-
return lines_outside_fences(message).filter((line) => !QUOTE_LINE.test(line));
|
|
259
|
-
}
|
|
260
|
-
function bare_references(message) {
|
|
261
|
-
const found = [];
|
|
262
|
-
for (const line of prose_lines(message)) collect_line_references(line, found);
|
|
263
|
-
return [...new Set(found)];
|
|
264
|
-
}
|
|
265
|
-
function prompt_references(prompt) {
|
|
266
|
-
const found = /* @__PURE__ */ new Set();
|
|
267
|
-
for (const match of prompt.matchAll(ISSUE_NUMBER)) {
|
|
268
|
-
found.add(repo_prefix(prompt, match.index) + match[0]);
|
|
269
|
-
}
|
|
270
|
-
return found;
|
|
271
|
-
}
|
|
272
|
-
function unquoted_references(message, prompt) {
|
|
273
|
-
const quoted = prompt_references(prompt);
|
|
274
|
-
return bare_references(message).filter((reference) => !quoted.has(reference));
|
|
275
|
-
}
|
|
276
|
-
function has_bare_reference(message) {
|
|
277
|
-
return bare_references(message).length > 0;
|
|
278
|
-
}
|
|
279
|
-
function cite_argument(reference) {
|
|
280
|
-
return reference.startsWith("#") ? reference.slice(1) : reference;
|
|
281
|
-
}
|
|
282
|
-
function cite_arguments(references) {
|
|
283
|
-
return references.map((reference) => cite_argument(reference));
|
|
284
|
-
}
|
|
285
|
-
function reference_link(reference, default_slug) {
|
|
286
|
-
const slug = reference.prefix === "" ? default_slug : reference.prefix;
|
|
287
|
-
const number = reference.text.slice(1);
|
|
288
|
-
return `[${reference.prefix}${reference.text}](${issue_cite.issue_url(slug, number)})`;
|
|
289
|
-
}
|
|
290
|
-
function linkify_line(line, slug) {
|
|
291
|
-
let result = "";
|
|
292
|
-
let cursor = 0;
|
|
293
|
-
for (const reference of line_references(line)) {
|
|
294
|
-
const start = reference.index - reference.prefix.length;
|
|
295
|
-
result += line.slice(cursor, start) + reference_link(reference, slug);
|
|
296
|
-
cursor = reference.index + reference.text.length;
|
|
297
|
-
}
|
|
298
|
-
return result + line.slice(cursor);
|
|
299
|
-
}
|
|
300
|
-
function rewrite_line(line, slug, is_in_fence) {
|
|
301
|
-
if (is_in_fence || FENCE_MARKER.test(line) || QUOTE_LINE.test(line)) return line;
|
|
302
|
-
return linkify_line(line, slug);
|
|
303
|
-
}
|
|
304
|
-
function linkify(message, slug) {
|
|
305
|
-
const out = [];
|
|
306
|
-
let is_in_fence = false;
|
|
307
|
-
for (const line of message.split("\n")) {
|
|
308
|
-
if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
|
|
309
|
-
out.push(rewrite_line(line, slug, is_in_fence));
|
|
310
|
-
}
|
|
311
|
-
return out.join("\n");
|
|
312
|
-
}
|
|
313
|
-
var issue_citation = {
|
|
314
|
-
bare_references,
|
|
315
|
-
cite_arguments,
|
|
316
|
-
has_bare_reference,
|
|
317
|
-
linkify,
|
|
318
|
-
prose_lines,
|
|
319
|
-
unquoted_references
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
// scripts/issue/session-cite.ts
|
|
323
|
-
function session_repo() {
|
|
324
|
-
const url = repo_discovery.read_origin_url(process.cwd());
|
|
325
|
-
const identity = url === void 0 ? void 0 : repo_origin.parse_origin_url(url);
|
|
326
|
-
return identity === void 0 ? void 0 : `${identity.owner}/${identity.repo}`;
|
|
327
|
-
}
|
|
328
|
-
function issue(number, title, repo = session_repo()) {
|
|
329
|
-
return issue_cite.reference(repo, String(number), title);
|
|
330
|
-
}
|
|
331
|
-
function text(message) {
|
|
332
|
-
const repo = session_repo();
|
|
333
|
-
return repo === void 0 ? message : issue_citation.linkify(message, repo);
|
|
334
|
-
}
|
|
335
|
-
var session_cite = {
|
|
336
|
-
issue,
|
|
337
|
-
session_repo,
|
|
338
|
-
text
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// scripts/josh/stamp-record.ts
|
|
342
|
-
var NONE_READ = { kind: "none" };
|
|
343
|
-
var UNREADABLE_READ = { kind: "unreadable" };
|
|
344
|
-
function is_older_than(iso, max_age_ms, now) {
|
|
345
|
-
const at = Date.parse(iso);
|
|
346
|
-
if (Number.isNaN(at)) return true;
|
|
347
|
-
return now.getTime() - at > max_age_ms;
|
|
348
|
-
}
|
|
349
|
-
function classify(raw, spec, now) {
|
|
350
|
-
if (raw === void 0) return NONE_READ;
|
|
351
|
-
const record = spec.parse(raw);
|
|
352
|
-
if (record === void 0) return UNREADABLE_READ;
|
|
353
|
-
const is_expired3 = is_older_than(spec.timestamp_of(record), spec.max_age_ms, now);
|
|
354
|
-
return { kind: is_expired3 ? "expired" : "carried", record };
|
|
355
|
-
}
|
|
356
|
-
var stamp_record = { classify, is_older_than };
|
|
357
|
-
|
|
358
|
-
// scripts/run/hold/run-hold.ts
|
|
359
|
-
import { z } from "zod";
|
|
360
|
-
var HOLD_PREFIX = "josh-run-hold-";
|
|
361
|
-
var UNNUMBERED_ISSUE = "new";
|
|
362
|
-
var HOLD_MAX_AGE_HOURS = 8;
|
|
363
|
-
var MILLISECONDS_PER_HOUR = 36e5;
|
|
364
|
-
var HOLD_MAX_AGE_MS = HOLD_MAX_AGE_HOURS * MILLISECONDS_PER_HOUR;
|
|
365
|
-
var RELEASE_COMMAND = "pnpm josh run:release";
|
|
366
|
-
var FORCE_RELEASE_COMMAND = `${RELEASE_COMMAND} --force`;
|
|
367
|
-
var NOT_IDLE = 'that is not "nothing is running here"';
|
|
368
|
-
var FREE_READ = { kind: "free" };
|
|
369
|
-
var UNREADABLE_READ2 = { kind: "unreadable" };
|
|
370
|
-
function hold_path(git_directory) {
|
|
371
|
-
return stamp_file.stamp_path(HOLD_PREFIX, git_directory);
|
|
372
|
-
}
|
|
373
|
-
async function worktree_directory() {
|
|
374
|
-
const directories = await git_command.git_directories();
|
|
375
|
-
return directories[0];
|
|
376
|
-
}
|
|
377
|
-
async function is_linked_worktree() {
|
|
378
|
-
const directories = await git_command.git_directories();
|
|
379
|
-
return directories[0] !== void 0 && directories[0] !== directories[1];
|
|
380
|
-
}
|
|
381
|
-
var run_hold_schema = z.object({
|
|
382
|
-
issue: z.string(),
|
|
383
|
-
taken_at: z.string(),
|
|
384
|
-
pid: z.number(),
|
|
385
|
-
is_fullrun: z.boolean().optional(),
|
|
386
|
-
is_halfrun_stop: z.boolean().optional(),
|
|
387
|
-
prrun_stop_head: z.string().optional(),
|
|
388
|
-
owner_pid: z.number().optional(),
|
|
389
|
-
owner_start: z.string().optional()
|
|
390
|
-
});
|
|
391
|
-
function parse_hold(raw) {
|
|
392
|
-
return json_value.parse_with(raw, run_hold_schema);
|
|
393
|
-
}
|
|
394
|
-
function is_expired(hold, now) {
|
|
395
|
-
return stamp_record.is_older_than(hold.taken_at, HOLD_MAX_AGE_MS, now);
|
|
396
|
-
}
|
|
397
|
-
function is_owner_gone(hold) {
|
|
398
|
-
const is_stop_mark = hold.is_halfrun_stop === true || hold.prrun_stop_head !== void 0;
|
|
399
|
-
if (is_stop_mark || hold.owner_pid === void 0) return false;
|
|
400
|
-
return process_identity.is_same_process(hold.owner_pid, hold.owner_start) === false;
|
|
401
|
-
}
|
|
402
|
-
function is_stale(hold, now) {
|
|
403
|
-
return is_expired(hold, now) || is_owner_gone(hold);
|
|
404
|
-
}
|
|
405
|
-
function classify2(raw, now) {
|
|
406
|
-
if (raw === void 0) return FREE_READ;
|
|
407
|
-
const hold = parse_hold(raw);
|
|
408
|
-
if (hold === void 0) return UNREADABLE_READ2;
|
|
409
|
-
return { kind: is_stale(hold, now) ? "stale" : "held", hold };
|
|
410
|
-
}
|
|
411
|
-
function read_hold(target, now = /* @__PURE__ */ new Date()) {
|
|
412
|
-
return classify2(stamp_file.read_stamp_text(target), now);
|
|
413
|
-
}
|
|
414
|
-
function session_owner(environment = process.env) {
|
|
415
|
-
const pid = Number(environment[agent_session_environment.AGENT_PID_KEY]);
|
|
416
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return {};
|
|
417
|
-
return { owner_pid: pid, owner_start: process_identity.read_start(pid) };
|
|
418
|
-
}
|
|
419
|
-
function build_hold(issue2, now, is_fullrun) {
|
|
420
|
-
return { issue: issue2, taken_at: now.toISOString(), pid: process.pid, is_fullrun, ...session_owner() };
|
|
421
|
-
}
|
|
422
|
-
function write_hold(target, issue2, now = /* @__PURE__ */ new Date()) {
|
|
423
|
-
const hold = build_hold(issue2, now);
|
|
424
|
-
stamp_file.write_stamp(target, hold);
|
|
425
|
-
return hold;
|
|
426
|
-
}
|
|
427
|
-
function create_hold(target, issue2, now = /* @__PURE__ */ new Date(), is_fullrun) {
|
|
428
|
-
return stamp_file.create_stamp(target, build_hold(issue2, now, is_fullrun));
|
|
429
|
-
}
|
|
430
|
-
var HALFRUN_STOP_FIELDS = { is_halfrun_stop: true };
|
|
431
|
-
function create_stop_hold(target, issue2, mark, now = /* @__PURE__ */ new Date()) {
|
|
432
|
-
return stamp_file.create_stamp(target, { ...build_hold(issue2, now), ...mark });
|
|
433
|
-
}
|
|
434
|
-
function create_halfrun_stop_hold(target, issue2, now = /* @__PURE__ */ new Date()) {
|
|
435
|
-
return create_stop_hold(target, issue2, HALFRUN_STOP_FIELDS, now);
|
|
436
|
-
}
|
|
437
|
-
async function is_tree_dirty(directory) {
|
|
438
|
-
try {
|
|
439
|
-
const status = await git_command.status(directory);
|
|
440
|
-
return status.trim() !== "";
|
|
441
|
-
} catch (error) {
|
|
442
|
-
error_text.trace_swallowed("run_hold.is_tree_dirty", error);
|
|
443
|
-
return true;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
function release_hold(target) {
|
|
447
|
-
stamp_file.remove_stamp(target);
|
|
448
|
-
}
|
|
449
|
-
function describe_holder(hold) {
|
|
450
|
-
const holder = hold.issue === UNNUMBERED_ISSUE ? "an unnumbered run" : session_cite.issue(hold.issue);
|
|
451
|
-
return `${holder}, recorded ${hold.taken_at} (pid ${String(hold.owner_pid ?? hold.pid)})`;
|
|
452
|
-
}
|
|
453
|
-
function own_release_command(issue2) {
|
|
454
|
-
return issue2 === UNNUMBERED_ISSUE ? RELEASE_COMMAND : `${RELEASE_COMMAND} ${issue2}`;
|
|
455
|
-
}
|
|
456
|
-
function is_own_hold(hold, claimant) {
|
|
457
|
-
return hold.issue === claimant;
|
|
458
|
-
}
|
|
459
|
-
function is_reentry(hold, claimant) {
|
|
460
|
-
return claimant !== UNNUMBERED_ISSUE && is_own_hold(hold, claimant);
|
|
461
|
-
}
|
|
462
|
-
function held_message(hold) {
|
|
463
|
-
return `This working tree is already held by ${describe_holder(hold)}. One run at a time per working tree: a second run here commits its files onto the other run's branch. If that record is stale, run \`${FORCE_RELEASE_COMMAND}\` in this working tree \u2014 it removes a record this run did not write, so be sure the run that did has ended \u2014 and ask again.`;
|
|
464
|
-
}
|
|
465
|
-
function foreign_release_message(hold) {
|
|
466
|
-
return `This working tree is held by ${describe_holder(hold)}, and this release did not claim it \u2014 nothing was removed. Release it as that run with \`${own_release_command(hold.issue)}\`, or, once you are sure it has ended, remove the record with \`${FORCE_RELEASE_COMMAND}\`.`;
|
|
467
|
-
}
|
|
468
|
-
function forced_release_message(hold) {
|
|
469
|
-
return `Forced: removed the run record held by ${describe_holder(hold)}.`;
|
|
470
|
-
}
|
|
471
|
-
function unreadable_message() {
|
|
472
|
-
return `This working tree's run record could not be read \u2014 ${NOT_IDLE}. Run \`${FORCE_RELEASE_COMMAND}\` in this working tree to clear it, then ask again.`;
|
|
473
|
-
}
|
|
474
|
-
function stale_message(hold) {
|
|
475
|
-
const why = is_owner_gone(hold) ? "the session that wrote it has ended" : `older than ${String(HOLD_MAX_AGE_HOURS)} hours`;
|
|
476
|
-
return `Replaced a stale run record held by ${describe_holder(hold)}: ${why}, so the run that wrote it has ended without releasing it.`;
|
|
477
|
-
}
|
|
478
|
-
function uncommitted_message(hold) {
|
|
479
|
-
return `This working tree still has uncommitted changes, and its run record \u2014 held by ${describe_holder(hold)} \u2014 has expired or outlived its session. That is a run which stopped for a person rather than one that finished, so the tree is not free: commit or stash the work, or run \`${own_release_command(hold.issue)}\` once you are done with it, then ask again.`;
|
|
480
|
-
}
|
|
481
|
-
function race_message(read, target) {
|
|
482
|
-
if (read.kind === "held") return held_message(read.hold);
|
|
483
|
-
return `A record already exists at ${target} and this run did not write it \u2014 ${NOT_IDLE}. Either another run claimed the tree at the same moment, or the record belongs to another account. Run \`${FORCE_RELEASE_COMMAND}\`, and remove that file by hand if it is still there.`;
|
|
484
|
-
}
|
|
485
|
-
function unknown_message() {
|
|
486
|
-
return `Could not read this working tree's git directory \u2014 ${NOT_IDLE}. Run this from inside the checkout the run will edit.`;
|
|
487
|
-
}
|
|
488
|
-
var run_hold = {
|
|
489
|
-
FORCE_RELEASE_COMMAND,
|
|
490
|
-
HOLD_MAX_AGE_HOURS,
|
|
491
|
-
HOLD_MAX_AGE_MS,
|
|
492
|
-
RELEASE_COMMAND,
|
|
493
|
-
UNNUMBERED_ISSUE,
|
|
494
|
-
classify: classify2,
|
|
495
|
-
create_halfrun_stop_hold,
|
|
496
|
-
create_stop_hold,
|
|
497
|
-
HALFRUN_STOP_FIELDS,
|
|
498
|
-
create_hold,
|
|
499
|
-
foreign_release_message,
|
|
500
|
-
forced_release_message,
|
|
501
|
-
held_message,
|
|
502
|
-
hold_path,
|
|
503
|
-
is_linked_worktree,
|
|
504
|
-
is_own_hold,
|
|
505
|
-
is_reentry,
|
|
506
|
-
is_tree_dirty,
|
|
507
|
-
own_release_command,
|
|
508
|
-
race_message,
|
|
509
|
-
read_hold,
|
|
510
|
-
release_hold,
|
|
511
|
-
stale_message,
|
|
512
|
-
uncommitted_message,
|
|
513
|
-
unknown_message,
|
|
514
|
-
unreadable_message,
|
|
515
|
-
worktree_directory,
|
|
516
|
-
write_hold
|
|
517
|
-
};
|
|
518
|
-
|
|
519
237
|
// scripts/cost-runtime/cost-pricing.ts
|
|
520
238
|
var CACHE_WRITE_5M_MULTIPLIER = 1.25;
|
|
521
239
|
var CACHE_WRITE_1H_MULTIPLIER = 2;
|
|
@@ -660,16 +378,16 @@ var context_cut_payback = {
|
|
|
660
378
|
var CONTEXT_CUT_THRESHOLD = context_cut_payback.break_even_context();
|
|
661
379
|
|
|
662
380
|
// scripts/run/cut/run-cut.ts
|
|
663
|
-
import { z as
|
|
381
|
+
import { z as z2 } from "zod";
|
|
664
382
|
|
|
665
383
|
// scripts/run/cut/run-cut-handoff.ts
|
|
666
384
|
import { readFileSync } from "node:fs";
|
|
667
|
-
import { z
|
|
668
|
-
var handoff_schema =
|
|
669
|
-
instruction:
|
|
670
|
-
completed:
|
|
671
|
-
remaining:
|
|
672
|
-
untouched:
|
|
385
|
+
import { z } from "zod";
|
|
386
|
+
var handoff_schema = z.object({
|
|
387
|
+
instruction: z.string(),
|
|
388
|
+
completed: z.array(z.string()),
|
|
389
|
+
remaining: z.array(z.string()),
|
|
390
|
+
untouched: z.array(z.string())
|
|
673
391
|
});
|
|
674
392
|
var COMPLETED_LABEL = "Completed";
|
|
675
393
|
var REMAINING_LABEL = "Remaining";
|
|
@@ -677,7 +395,7 @@ var UNTOUCHED_LABEL = "Deliberately untouched";
|
|
|
677
395
|
var INSTRUCTION_LABEL = "Instruction (verbatim)";
|
|
678
396
|
var EMPTY_LIST_NOTE = "(none)";
|
|
679
397
|
function describe_key(key2) {
|
|
680
|
-
const value = handoff_schema.shape[key2] instanceof
|
|
398
|
+
const value = handoff_schema.shape[key2] instanceof z.ZodArray ? '["\u2026"]' : '"\u2026"';
|
|
681
399
|
return `"${key2}": ${value}`;
|
|
682
400
|
}
|
|
683
401
|
var HANDOFF_FORMAT = `a JSON object {${handoff_schema.keyof().options.map((key2) => describe_key(key2)).join(", ")}}`;
|
|
@@ -747,22 +465,22 @@ var CUT_MAX_AGE_HOURS = backlog_budget.IDLE_WAIT_HOURS;
|
|
|
747
465
|
var END_COMMAND = "pnpm josh run:cut --end";
|
|
748
466
|
var READ_COMMAND = "pnpm josh run:cut --json";
|
|
749
467
|
var MAX_HANDOFF_BYTES = 8192;
|
|
750
|
-
var run_cut_schema =
|
|
751
|
-
invocation:
|
|
752
|
-
issue:
|
|
753
|
-
branch:
|
|
754
|
-
phase:
|
|
755
|
-
cut_at:
|
|
756
|
-
is_handed_off:
|
|
468
|
+
var run_cut_schema = z2.object({
|
|
469
|
+
invocation: z2.string(),
|
|
470
|
+
issue: z2.string(),
|
|
471
|
+
branch: z2.string(),
|
|
472
|
+
phase: z2.string(),
|
|
473
|
+
cut_at: z2.string(),
|
|
474
|
+
is_handed_off: z2.boolean().optional(),
|
|
757
475
|
handoff: run_cut_handoff.handoff_schema.optional(),
|
|
758
|
-
is_merge_relaunched:
|
|
476
|
+
is_merge_relaunched: z2.boolean().optional()
|
|
759
477
|
});
|
|
760
478
|
function cut_path(git_directory) {
|
|
761
479
|
return stamp_file.stamp_path(CUT_PREFIX, git_directory);
|
|
762
480
|
}
|
|
763
|
-
var { worktree_directory
|
|
764
|
-
function invocation_for(
|
|
765
|
-
return lane_child_invocation.child_invocation(
|
|
481
|
+
var { worktree_directory } = run_hold;
|
|
482
|
+
function invocation_for(issue) {
|
|
483
|
+
return lane_child_invocation.child_invocation(issue);
|
|
766
484
|
}
|
|
767
485
|
function parse_cut(raw) {
|
|
768
486
|
return json_value.parse_with(raw, run_cut_schema);
|
|
@@ -775,24 +493,24 @@ var CUT_SPEC = {
|
|
|
775
493
|
timestamp_of: cut_at_of,
|
|
776
494
|
max_age_ms: CUT_MAX_AGE_MS
|
|
777
495
|
};
|
|
778
|
-
function
|
|
496
|
+
function is_expired(cut, now) {
|
|
779
497
|
return stamp_record.is_older_than(cut_at_of(cut), CUT_MAX_AGE_MS, now);
|
|
780
498
|
}
|
|
781
|
-
function
|
|
782
|
-
const
|
|
783
|
-
if (!("record" in
|
|
784
|
-
return { kind:
|
|
499
|
+
function classify(raw, now) {
|
|
500
|
+
const read2 = stamp_record.classify(raw, CUT_SPEC, now);
|
|
501
|
+
if (!("record" in read2)) return read2;
|
|
502
|
+
return { kind: read2.kind, cut: read2.record };
|
|
785
503
|
}
|
|
786
504
|
function read_cut(target, now = /* @__PURE__ */ new Date()) {
|
|
787
|
-
return
|
|
505
|
+
return classify(stamp_file.read_stamp_text(target), now);
|
|
788
506
|
}
|
|
789
507
|
function worktree_git_directory_sync() {
|
|
790
508
|
return git_common_directory.own();
|
|
791
509
|
}
|
|
792
510
|
function carried_cut_sync(now = /* @__PURE__ */ new Date(), directory = worktree_git_directory_sync()) {
|
|
793
511
|
if (directory === void 0) return void 0;
|
|
794
|
-
const
|
|
795
|
-
return
|
|
512
|
+
const read2 = read_cut(cut_path(directory), now);
|
|
513
|
+
return read2.kind === "carried" ? read2.cut : void 0;
|
|
796
514
|
}
|
|
797
515
|
function lane_cut_sync(lane_directory) {
|
|
798
516
|
const directory = git_common_directory.own(lane_directory);
|
|
@@ -825,20 +543,20 @@ function begin_cut(target, spec, now = /* @__PURE__ */ new Date()) {
|
|
|
825
543
|
if (!within_handoff_bound(cut)) return void 0;
|
|
826
544
|
return stamp_file.create_stamp(target, cut) ? cut : void 0;
|
|
827
545
|
}
|
|
828
|
-
function is_declared_cut(cut,
|
|
829
|
-
return cut.is_handed_off === true && cut.invocation === invocation_for(
|
|
546
|
+
function is_declared_cut(cut, issue) {
|
|
547
|
+
return cut.is_handed_off === true && cut.invocation === invocation_for(issue);
|
|
830
548
|
}
|
|
831
549
|
function matches_state(cut, request) {
|
|
832
550
|
return cut.branch === request.current_branch && cut.issue === request.issue && request.is_dirty && request.is_held;
|
|
833
551
|
}
|
|
834
|
-
function is_adopted_cut(cut,
|
|
835
|
-
return cut.is_handed_off === false && cut.invocation === invocation_for(
|
|
552
|
+
function is_adopted_cut(cut, issue) {
|
|
553
|
+
return cut.is_handed_off === false && cut.invocation === invocation_for(issue);
|
|
836
554
|
}
|
|
837
555
|
function has_required_handoff(cut) {
|
|
838
556
|
return !resumes_into_implementation(cut.phase) || run_cut_handoff.is_complete_handoff(cut.handoff);
|
|
839
557
|
}
|
|
840
|
-
function is_relaunchable(cut,
|
|
841
|
-
return is_declared_cut(cut,
|
|
558
|
+
function is_relaunchable(cut, issue) {
|
|
559
|
+
return is_declared_cut(cut, issue) && has_required_handoff(cut) && cut.is_merge_relaunched !== true;
|
|
842
560
|
}
|
|
843
561
|
function mark_merge_relaunched(target, cut) {
|
|
844
562
|
stamp_file.remove_stamp(target);
|
|
@@ -859,8 +577,8 @@ function end_cut(target) {
|
|
|
859
577
|
}
|
|
860
578
|
function hold_of(directory) {
|
|
861
579
|
if (directory === void 0) return void 0;
|
|
862
|
-
const
|
|
863
|
-
return
|
|
580
|
+
const read2 = run_hold.read_hold(run_hold.hold_path(directory));
|
|
581
|
+
return read2.kind === "held" || read2.kind === "stale" ? read2.hold : void 0;
|
|
864
582
|
}
|
|
865
583
|
function fullrun_issue(hold) {
|
|
866
584
|
return hold?.is_fullrun === true ? hold.issue : void 0;
|
|
@@ -871,8 +589,14 @@ function held_issue_sync() {
|
|
|
871
589
|
async function current_state() {
|
|
872
590
|
const branch = await git_command.branch();
|
|
873
591
|
const is_dirty = await run_hold.is_tree_dirty();
|
|
874
|
-
const hold = hold_of(await
|
|
875
|
-
return {
|
|
592
|
+
const hold = hold_of(await worktree_directory());
|
|
593
|
+
return {
|
|
594
|
+
branch,
|
|
595
|
+
is_dirty,
|
|
596
|
+
is_held: hold !== void 0,
|
|
597
|
+
held_issue: fullrun_issue(hold),
|
|
598
|
+
hold_taken_at: hold?.taken_at
|
|
599
|
+
};
|
|
876
600
|
}
|
|
877
601
|
function describe_cut(cut) {
|
|
878
602
|
return `${cut.invocation} cut at ${cut.cut_at} on ${cut.branch} (${cut.phase})`;
|
|
@@ -883,16 +607,16 @@ function busy_message(cut) {
|
|
|
883
607
|
function handed_off_message(cut) {
|
|
884
608
|
return `This cut was already handed off to a successor \u2014 ${describe_cut(cut)}. Nothing was resumed here; the run is being carried on by the process that adopted it, so this one has nothing to do.`;
|
|
885
609
|
}
|
|
886
|
-
function
|
|
610
|
+
function stale_message(cut) {
|
|
887
611
|
return `This cut does not match the current tree \u2014 ${describe_cut(cut)}. Resume failed; verify the branch and the uncommitted work, then clear it with \`${END_COMMAND}\` if the run is over.`;
|
|
888
612
|
}
|
|
889
613
|
function incomplete_message(cut) {
|
|
890
614
|
return `This cut carries no instruction to resume on \u2014 ${describe_cut(cut)}. Resume was refused rather than continuing without it; recover the run's instruction and work state before implementing, and clear the record with \`${END_COMMAND}\` if the run is over.`;
|
|
891
615
|
}
|
|
892
|
-
function
|
|
616
|
+
function unreadable_message() {
|
|
893
617
|
return `A cut record is here but could not be read; clear it with \`${END_COMMAND}\` once you know no run is using it. Read it with \`${READ_COMMAND}\`.`;
|
|
894
618
|
}
|
|
895
|
-
function
|
|
619
|
+
function unknown_message() {
|
|
896
620
|
return "This working tree\u2019s git directory could not be read, so no cut record was acted on.";
|
|
897
621
|
}
|
|
898
622
|
var run_cut = {
|
|
@@ -907,7 +631,7 @@ var run_cut = {
|
|
|
907
631
|
begin_cut,
|
|
908
632
|
busy_message,
|
|
909
633
|
carried_cut_sync,
|
|
910
|
-
classify
|
|
634
|
+
classify,
|
|
911
635
|
classify_resume,
|
|
912
636
|
current_state,
|
|
913
637
|
cut_path,
|
|
@@ -918,20 +642,21 @@ var run_cut = {
|
|
|
918
642
|
handed_off_message,
|
|
919
643
|
has_required_handoff,
|
|
920
644
|
held_issue_sync,
|
|
645
|
+
hold_of,
|
|
921
646
|
incomplete_message,
|
|
922
647
|
invocation_for,
|
|
923
|
-
is_expired
|
|
648
|
+
is_expired,
|
|
924
649
|
is_relaunchable,
|
|
925
650
|
lane_cut_sync,
|
|
926
651
|
mark_merge_relaunched,
|
|
927
652
|
read_cut,
|
|
928
653
|
record_within_bound,
|
|
929
654
|
resumes_into_implementation,
|
|
930
|
-
stale_message
|
|
931
|
-
unknown_message
|
|
932
|
-
unreadable_message
|
|
655
|
+
stale_message,
|
|
656
|
+
unknown_message,
|
|
657
|
+
unreadable_message,
|
|
933
658
|
within_handoff_bound,
|
|
934
|
-
worktree_directory
|
|
659
|
+
worktree_directory,
|
|
935
660
|
worktree_git_directory_sync
|
|
936
661
|
};
|
|
937
662
|
|
|
@@ -974,7 +699,7 @@ function is_foreground_push_step(call) {
|
|
|
974
699
|
function decide2() {
|
|
975
700
|
return true;
|
|
976
701
|
}
|
|
977
|
-
var RUN_TAIL_REASON =
|
|
702
|
+
var RUN_TAIL_REASON = "\u26D4 run tail: this issues the commit-push-PR step in the foreground, where the harness decides when to stop waiting. Reissue it with `run_in_background` set; when it exits, go straight on to `pnpm josh followup` in the foreground \u2014 the turn never ends at the push (`.claude/skills/workflow-commands/background-commands.md`). It fires on every foreground push, not once per run.";
|
|
978
703
|
var run_tail_rule = {
|
|
979
704
|
RUN_TAIL_REASON,
|
|
980
705
|
decide: decide2,
|
|
@@ -1021,11 +746,12 @@ function is_background_long_run(call, is_lane_child = () => lane_child_marker.is
|
|
|
1021
746
|
return is_candidate(call) && is_lane_child();
|
|
1022
747
|
}
|
|
1023
748
|
function foreground_input(call, is_lane_child = () => lane_child_marker.is_child_of(process.cwd())) {
|
|
1024
|
-
if (!is_backgrounded_ship(call) || !json_value.is_record(call.input)
|
|
1025
|
-
|
|
749
|
+
if (!is_backgrounded_ship(call) || !json_value.is_record(call.input) || !is_lane_child()) {
|
|
750
|
+
return void 0;
|
|
751
|
+
}
|
|
1026
752
|
return { ...call.input, [BACKGROUND_KEY2]: false, [TIMEOUT_KEY]: FOREGROUND_TIMEOUT_MS };
|
|
1027
753
|
}
|
|
1028
|
-
var FOREGROUND_NOTE = "lane background: this lane child backgrounded `josh ship`, which would die with the headless turn before its supervisor exists \u2014 so it was run in the foreground instead, where it detaches itself after the preflight. End the turn on `launched` / `busy
|
|
754
|
+
var FOREGROUND_NOTE = "lane background: this lane child backgrounded `josh ship`, which would die with the headless turn before its supervisor exists \u2014 so it was run in the foreground instead, where it detaches itself after the preflight. End the turn on `launched` / `busy`.";
|
|
1029
755
|
function unfinished_ids(tail, launched_ids) {
|
|
1030
756
|
const lines = tail.split("\n").map((line) => time_transcript_line.parse_line(line));
|
|
1031
757
|
const finished = new Set(lines.map((line) => line?.finished_background ?? ""));
|
|
@@ -1039,8 +765,8 @@ function pending_background_ids(tail) {
|
|
|
1039
765
|
function pending_agent_ids(tail) {
|
|
1040
766
|
return unfinished_ids(tail, (block) => [block.agent_id]);
|
|
1041
767
|
}
|
|
1042
|
-
var LANE_BACKGROUND_REASON = '\u26D4 lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless
|
|
1043
|
-
var LANE_BACKGROUND_STOP_REASON = "\u26D4 lane background stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a background task it launched has not finished
|
|
768
|
+
var LANE_BACKGROUND_REASON = '\u26D4 lane background: this session is a dispatched lane child (`JOSH_LANE_CHILD`), a headless process whose background tasks die when the turn ends. Hand over to `pnpm josh ship --detach --review "<title> #<N>"`, issued in the foreground, then end the turn on `launched` / `busy`. It fires on every occurrence (`.claude/skills/workflow-commands/chain-rule.md` step 0).';
|
|
769
|
+
var LANE_BACKGROUND_STOP_REASON = "\u26D4 lane background stop: this is a dispatched lane child (`JOSH_LANE_CHILD`) and a background task it launched has not finished, and ending this turn kills it. Wait for it in the foreground, or re-issue the work through `pnpm josh ship --detach` \u2014 do not notify, stop or repeat your reply.";
|
|
1044
770
|
var ROW2 = {
|
|
1045
771
|
id: "lane-background",
|
|
1046
772
|
is_trigger: (call) => is_background_long_run(call),
|
|
@@ -1061,7 +787,7 @@ var lane_background = {
|
|
|
1061
787
|
// scripts/run/ship/run-ship-detach.ts
|
|
1062
788
|
import { randomUUID } from "node:crypto";
|
|
1063
789
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
1064
|
-
import { z as
|
|
790
|
+
import { z as z3 } from "zod";
|
|
1065
791
|
var LOG_PREFIX = "josh-ship-log-";
|
|
1066
792
|
var PID_PREFIX = "josh-ship-pid-";
|
|
1067
793
|
var BODY_PREFIX = "josh-ship-notify-";
|
|
@@ -1082,14 +808,14 @@ var RESULT_PREFIX = "josh-ship-result-";
|
|
|
1082
808
|
var RECORD_POLL_MS = 25;
|
|
1083
809
|
var RECORD_WAIT_ATTEMPTS = 200;
|
|
1084
810
|
var CLAIM_WINDOW_MS = 12e4;
|
|
1085
|
-
var ship_record_schema =
|
|
1086
|
-
pid:
|
|
1087
|
-
process_start:
|
|
1088
|
-
launched_at:
|
|
1089
|
-
launcher_pid:
|
|
1090
|
-
launcher_start:
|
|
1091
|
-
launch_id:
|
|
1092
|
-
result:
|
|
811
|
+
var ship_record_schema = z3.object({
|
|
812
|
+
pid: z3.number().int().positive().optional(),
|
|
813
|
+
process_start: z3.string().optional(),
|
|
814
|
+
launched_at: z3.number().optional(),
|
|
815
|
+
launcher_pid: z3.number().int().positive().optional(),
|
|
816
|
+
launcher_start: z3.string().optional(),
|
|
817
|
+
launch_id: z3.string(),
|
|
818
|
+
result: z3.enum(["success", "failed"]).optional()
|
|
1093
819
|
});
|
|
1094
820
|
var LAUNCH_MARK = " \xB7 started by process ";
|
|
1095
821
|
var HEADER_START = "\n";
|
|
@@ -1152,6 +878,10 @@ function current_record(repository, number) {
|
|
|
1152
878
|
const record = read_record(repository, number);
|
|
1153
879
|
return record?.launch_id === process.env[LAUNCH_ID_KEY] ? record : void 0;
|
|
1154
880
|
}
|
|
881
|
+
function running_since(repository, number) {
|
|
882
|
+
const record = read_record(repository, number);
|
|
883
|
+
return record !== void 0 && result_of(record) === "running" ? record.launched_at : void 0;
|
|
884
|
+
}
|
|
1155
885
|
function is_pending(record) {
|
|
1156
886
|
return record !== void 0 && record.pid === void 0;
|
|
1157
887
|
}
|
|
@@ -1259,14 +989,14 @@ async function detach(request) {
|
|
|
1259
989
|
}
|
|
1260
990
|
return result;
|
|
1261
991
|
}
|
|
1262
|
-
function last_launch(
|
|
1263
|
-
const mark =
|
|
1264
|
-
if (mark === -1) return
|
|
1265
|
-
return
|
|
992
|
+
function last_launch(text) {
|
|
993
|
+
const mark = text.lastIndexOf(LAUNCH_MARK);
|
|
994
|
+
if (mark === -1) return text;
|
|
995
|
+
return text.slice(text.lastIndexOf(HEADER_START, mark) + HEADER_START.length);
|
|
1266
996
|
}
|
|
1267
997
|
function read_log(repository, number) {
|
|
1268
|
-
const
|
|
1269
|
-
return
|
|
998
|
+
const text = stamp_file.read_stamp_text(log_path(repository, number));
|
|
999
|
+
return text === void 0 ? void 0 : last_launch(text);
|
|
1270
1000
|
}
|
|
1271
1001
|
function is_supervised(source = process.env) {
|
|
1272
1002
|
return source[SUPERVISED_KEY] === SUPERVISED_VALUE;
|
|
@@ -1285,20 +1015,25 @@ var run_ship_detach = {
|
|
|
1285
1015
|
last_launch,
|
|
1286
1016
|
log_path,
|
|
1287
1017
|
read_log,
|
|
1018
|
+
running_since,
|
|
1288
1019
|
supervisor_argv
|
|
1289
1020
|
};
|
|
1290
1021
|
|
|
1291
1022
|
// scripts/lane/lane-handoff.ts
|
|
1292
|
-
function is_ship_running(
|
|
1023
|
+
function is_ship_running(issue, directory) {
|
|
1024
|
+
const repository = git_common_directory.repository(directory);
|
|
1025
|
+
return repository !== void 0 && run_ship_detach.read_result(repository, issue)?.result === "running";
|
|
1026
|
+
}
|
|
1027
|
+
function ship_running_since(issue, directory) {
|
|
1293
1028
|
const repository = git_common_directory.repository(directory);
|
|
1294
|
-
return repository
|
|
1029
|
+
return repository === void 0 ? void 0 : run_ship_detach.running_since(repository, issue);
|
|
1295
1030
|
}
|
|
1296
1031
|
function is_handed_off(directory, source = process.env) {
|
|
1297
|
-
const
|
|
1298
|
-
if (
|
|
1299
|
-
return is_ship_running(
|
|
1032
|
+
const issue = lane_child_marker.marked_issue(source);
|
|
1033
|
+
if (issue === void 0 || !lane_child_marker.is_child_of(directory, source)) return false;
|
|
1034
|
+
return is_ship_running(issue, directory);
|
|
1300
1035
|
}
|
|
1301
|
-
var lane_handoff = { is_handed_off, is_ship_running };
|
|
1036
|
+
var lane_handoff = { is_handed_off, is_ship_running, ship_running_since };
|
|
1302
1037
|
|
|
1303
1038
|
// scripts/issue/issue-labels.ts
|
|
1304
1039
|
var EPIC_LABEL = "epic";
|
|
@@ -1466,9 +1201,9 @@ function is_confirmation_notify(command) {
|
|
|
1466
1201
|
}
|
|
1467
1202
|
var NOTIFY_INVOCATION = /josh\s+(?:notify|nf)\b/u;
|
|
1468
1203
|
var COMMAND_NOTIFY_MARKER = "stop notified: a confirmation Telegram was sent by this command";
|
|
1469
|
-
function mentions_confirmation_notify(
|
|
1470
|
-
if (
|
|
1471
|
-
return NOTIFY_INVOCATION.test(
|
|
1204
|
+
function mentions_confirmation_notify(text) {
|
|
1205
|
+
if (text.includes(COMMAND_NOTIFY_MARKER)) return true;
|
|
1206
|
+
return NOTIFY_INVOCATION.test(text) && CONFIRMATION_TASK_TYPE.test(text);
|
|
1472
1207
|
}
|
|
1473
1208
|
function is_parking_child(directory = process.cwd()) {
|
|
1474
1209
|
return lane_child_marker.is_child_of(directory) && !lane_handoff.is_handed_off(directory);
|
|
@@ -1484,7 +1219,7 @@ var APPLIES_NEEDS_DECISION = new RegExp(
|
|
|
1484
1219
|
function records_the_park(command) {
|
|
1485
1220
|
return APPLIES_NEEDS_DECISION.test(command);
|
|
1486
1221
|
}
|
|
1487
|
-
var LANE_PARK_REASON =
|
|
1222
|
+
var LANE_PARK_REASON = "\u26D4 lane child park: this session is a dispatched lane child, so a stop for a person parks first: `gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=needs-decision'`; `pnpm josh issue:comment <N> --body-file <path>` (question, options, stash); a `needs-human-review` / `already-done` stop already is. Reissue this notify, once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
|
|
1488
1223
|
var ROW3 = {
|
|
1489
1224
|
id: "lane-park",
|
|
1490
1225
|
is_trigger: bash_triggers.on_bash_command(is_unparked_stop),
|
|
@@ -1502,373 +1237,494 @@ var lane_park = {
|
|
|
1502
1237
|
records_the_park
|
|
1503
1238
|
};
|
|
1504
1239
|
|
|
1505
|
-
// scripts/
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1240
|
+
// scripts/cost-runtime/cost-cli.ts
|
|
1241
|
+
import { fileURLToPath } from "node:url";
|
|
1242
|
+
|
|
1243
|
+
// scripts/cost-runtime/codex-usage.ts
|
|
1244
|
+
import { globSync, readFileSync as readFileSync2 } from "node:fs";
|
|
1245
|
+
import path from "node:path";
|
|
1246
|
+
import { z as z4 } from "zod";
|
|
1247
|
+
var THREAD_ID_PATTERN = /^[A-Za-z0-9-]+$/u;
|
|
1248
|
+
var SESSION_META_SCHEMA = z4.looseObject({
|
|
1249
|
+
type: z4.literal("session_meta"),
|
|
1250
|
+
payload: z4.looseObject({ id: z4.string(), cwd: z4.string() })
|
|
1251
|
+
});
|
|
1252
|
+
var TOTAL_USAGE_SCHEMA = z4.looseObject({ input_tokens: z4.number().nonnegative() });
|
|
1253
|
+
var TOKEN_COUNT_SCHEMA = z4.looseObject({
|
|
1254
|
+
type: z4.literal("event_msg"),
|
|
1255
|
+
payload: z4.looseObject({
|
|
1256
|
+
type: z4.literal("token_count"),
|
|
1257
|
+
info: z4.looseObject({
|
|
1258
|
+
total_token_usage: TOTAL_USAGE_SCHEMA,
|
|
1259
|
+
last_token_usage: TOTAL_USAGE_SCHEMA
|
|
1260
|
+
})
|
|
1261
|
+
})
|
|
1262
|
+
});
|
|
1263
|
+
function same_project(cwd, target) {
|
|
1264
|
+
return cost_transcript.session_cwd(cwd) === cost_transcript.session_cwd(target);
|
|
1517
1265
|
}
|
|
1518
|
-
function
|
|
1519
|
-
return
|
|
1266
|
+
function billed_per_request(usages) {
|
|
1267
|
+
return usages.map((usage) => usage.last_token_usage.input_tokens);
|
|
1520
1268
|
}
|
|
1521
|
-
function
|
|
1522
|
-
|
|
1269
|
+
function measurement_from(content, target, thread_id) {
|
|
1270
|
+
const values = content.split("\n").map((line) => json_value.parse_or_undefined(line));
|
|
1271
|
+
const metadata = values.flatMap((value) => {
|
|
1272
|
+
const parsed = SESSION_META_SCHEMA.safeParse(value);
|
|
1273
|
+
return parsed.success ? [parsed.data] : [];
|
|
1274
|
+
});
|
|
1275
|
+
const usages = values.flatMap((value) => {
|
|
1276
|
+
const parsed = TOKEN_COUNT_SCHEMA.safeParse(value);
|
|
1277
|
+
return parsed.success ? [parsed.data.payload.info] : [];
|
|
1278
|
+
});
|
|
1279
|
+
const session = metadata.find((entry) => entry.payload.id === thread_id);
|
|
1280
|
+
if (session === void 0 || usages.length === 0 || !same_project(session.payload.cwd, target)) {
|
|
1281
|
+
return void 0;
|
|
1282
|
+
}
|
|
1283
|
+
return { billed_input_per_request: billed_per_request(usages) };
|
|
1523
1284
|
}
|
|
1524
|
-
function
|
|
1525
|
-
|
|
1285
|
+
function rollout_files(home, thread_id) {
|
|
1286
|
+
if (!THREAD_ID_PATTERN.test(thread_id)) return [];
|
|
1287
|
+
return globSync(`sessions/**/*${thread_id}.jsonl`, { cwd: home }).map(
|
|
1288
|
+
(file) => path.join(home, file)
|
|
1289
|
+
);
|
|
1526
1290
|
}
|
|
1527
|
-
function
|
|
1528
|
-
|
|
1529
|
-
|
|
1291
|
+
function read_measurement(file, target, thread_id) {
|
|
1292
|
+
try {
|
|
1293
|
+
return measurement_from(readFileSync2(file, "utf8"), target, thread_id);
|
|
1294
|
+
} catch {
|
|
1295
|
+
return void 0;
|
|
1296
|
+
}
|
|
1530
1297
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
placeholder_owners,
|
|
1538
|
-
target_owner
|
|
1539
|
-
};
|
|
1540
|
-
|
|
1541
|
-
// scripts/gh/git-gh-api-path.ts
|
|
1542
|
-
var FULL_PAGE_QUERY = "?per_page=100";
|
|
1543
|
-
function repo_api_path(repo = "{owner}/{repo}") {
|
|
1544
|
-
return `repos/${repo}`;
|
|
1298
|
+
function latest_measurement(files, target, thread_id) {
|
|
1299
|
+
for (const file of files.toReversed()) {
|
|
1300
|
+
const measured = read_measurement(file, target, thread_id);
|
|
1301
|
+
if (measured !== void 0) return measured;
|
|
1302
|
+
}
|
|
1303
|
+
return void 0;
|
|
1545
1304
|
}
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1305
|
+
function measurement(target, default_home, environment = process.env) {
|
|
1306
|
+
const thread_id = environment["CODEX_THREAD_ID"];
|
|
1307
|
+
if (thread_id === void 0) return void 0;
|
|
1308
|
+
const home = environment["CODEX_HOME"] ?? path.join(default_home, ".codex");
|
|
1309
|
+
return latest_measurement(rollout_files(home, thread_id), target, thread_id);
|
|
1548
1310
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1311
|
+
var codex_usage = { measurement };
|
|
1312
|
+
|
|
1313
|
+
// scripts/cost-runtime/cost-corpus.ts
|
|
1314
|
+
function accumulate_missing(sessions) {
|
|
1315
|
+
return {
|
|
1316
|
+
no_usage_lines: sessions.reduce((sum, session) => sum + session.no_usage_lines, 0),
|
|
1317
|
+
malformed_lines: sessions.reduce((sum, session) => sum + session.malformed_lines, 0),
|
|
1318
|
+
unreadable_sessions: sessions.filter((session) => !session.is_readable).length,
|
|
1319
|
+
// Filled by the attribution, which is the only step that knows a unit could not follow its
|
|
1320
|
+
// parent; a per-session tally cannot see it, so it is zero here and set for an issue scope below.
|
|
1321
|
+
unattributed_sessions: 0
|
|
1322
|
+
};
|
|
1551
1323
|
}
|
|
1552
|
-
function
|
|
1553
|
-
|
|
1324
|
+
function load_corpus(cwd) {
|
|
1325
|
+
const files = cost_transcript.list_sessions_across(cost_transcript.transcript_directories(cwd));
|
|
1326
|
+
const sessions = files.map((file) => cost_transcript.read_session(file));
|
|
1327
|
+
return { sessions, files, missing: accumulate_missing(sessions) };
|
|
1554
1328
|
}
|
|
1555
|
-
function
|
|
1556
|
-
return
|
|
1329
|
+
function session_meta(corpus) {
|
|
1330
|
+
return new Map(
|
|
1331
|
+
corpus.files.map((file) => [
|
|
1332
|
+
file.session_id,
|
|
1333
|
+
{ is_delegated: file.is_delegated, owner: cost_transcript.owning_session_id(file) }
|
|
1334
|
+
])
|
|
1335
|
+
);
|
|
1557
1336
|
}
|
|
1558
|
-
function
|
|
1559
|
-
|
|
1337
|
+
function issue_set(records) {
|
|
1338
|
+
const issues = cost_attribute.group_by_issue(records).map((group) => group.issue).filter((issue) => issue !== cost_attribute.UNATTRIBUTED_KEY);
|
|
1339
|
+
return new Set(issues);
|
|
1560
1340
|
}
|
|
1561
|
-
function
|
|
1562
|
-
|
|
1341
|
+
function sole_of(issues) {
|
|
1342
|
+
if (issues.size !== 1) return cost_attribute.UNATTRIBUTED_KEY;
|
|
1343
|
+
return [...issues][0] ?? cost_attribute.UNATTRIBUTED_KEY;
|
|
1563
1344
|
}
|
|
1564
|
-
function
|
|
1565
|
-
|
|
1345
|
+
function owner_sets(corpus, meta) {
|
|
1346
|
+
const own = corpus.sessions.filter(
|
|
1347
|
+
(session) => meta.get(session.session_id)?.is_delegated !== true
|
|
1348
|
+
);
|
|
1349
|
+
return new Map(own.map((session) => [session.session_id, issue_set(session.records)]));
|
|
1350
|
+
}
|
|
1351
|
+
function pairs_with_inherited(session, inherited, is_delegated) {
|
|
1352
|
+
return cost_attribute.group_by_issue(session.records).flatMap(
|
|
1353
|
+
(group) => group.records.map((record) => ({
|
|
1354
|
+
record,
|
|
1355
|
+
issue: group.issue === cost_attribute.UNATTRIBUTED_KEY ? inherited : group.issue,
|
|
1356
|
+
baseline_tokens: session.baseline_tokens,
|
|
1357
|
+
session_id: session.session_id,
|
|
1358
|
+
is_delegated
|
|
1359
|
+
}))
|
|
1360
|
+
);
|
|
1566
1361
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1362
|
+
var NO_ISSUES = /* @__PURE__ */ new Set();
|
|
1363
|
+
function parent_issues(session, meta, owners) {
|
|
1364
|
+
const entry = meta.get(session.session_id);
|
|
1365
|
+
if (entry?.is_delegated !== true) return NO_ISSUES;
|
|
1366
|
+
return owners.get(entry.owner) ?? NO_ISSUES;
|
|
1367
|
+
}
|
|
1368
|
+
function pairs_of_session(session, meta, owners) {
|
|
1369
|
+
const is_delegated = meta.get(session.session_id)?.is_delegated === true;
|
|
1370
|
+
const candidates = parent_issues(session, meta, owners);
|
|
1371
|
+
const inherited = is_delegated ? sole_of(candidates) : cost_attribute.UNATTRIBUTED_KEY;
|
|
1372
|
+
const pairs = pairs_with_inherited(session, inherited, is_delegated);
|
|
1373
|
+
const has_unattributed = pairs.some((pair) => pair.issue === cost_attribute.UNATTRIBUTED_KEY);
|
|
1374
|
+
if (!is_delegated || !has_unattributed) return { pairs };
|
|
1375
|
+
return { pairs, unattributed_candidates: candidates };
|
|
1376
|
+
}
|
|
1377
|
+
function attribute_corpus(corpus) {
|
|
1378
|
+
const meta = session_meta(corpus);
|
|
1379
|
+
const owners = owner_sets(corpus, meta);
|
|
1380
|
+
const per_session = corpus.sessions.map((session) => pairs_of_session(session, meta, owners));
|
|
1381
|
+
return {
|
|
1382
|
+
pairs: per_session.flatMap((one) => one.pairs),
|
|
1383
|
+
unattributed: per_session.flatMap(
|
|
1384
|
+
(one) => one.unattributed_candidates === void 0 ? [] : [one.unattributed_candidates]
|
|
1385
|
+
)
|
|
1386
|
+
};
|
|
1569
1387
|
}
|
|
1570
|
-
function
|
|
1571
|
-
return
|
|
1388
|
+
function floor_for_issue(unattributed, issue_number) {
|
|
1389
|
+
return unattributed.filter((set) => set.size === 0 || set.has(issue_number)).length;
|
|
1572
1390
|
}
|
|
1573
|
-
function
|
|
1574
|
-
|
|
1391
|
+
function is_better(candidate, existing) {
|
|
1392
|
+
const { UNATTRIBUTED_KEY } = cost_attribute;
|
|
1393
|
+
return existing.issue === UNATTRIBUTED_KEY && candidate.issue !== UNATTRIBUTED_KEY;
|
|
1575
1394
|
}
|
|
1576
|
-
function
|
|
1577
|
-
|
|
1395
|
+
function values_of(best) {
|
|
1396
|
+
const collected = [];
|
|
1397
|
+
for (const [, pair] of best) collected.push(pair);
|
|
1398
|
+
return collected;
|
|
1578
1399
|
}
|
|
1579
|
-
function
|
|
1580
|
-
|
|
1400
|
+
function dedupe_across_sessions(pairs) {
|
|
1401
|
+
const best = /* @__PURE__ */ new Map();
|
|
1402
|
+
for (const pair of pairs) {
|
|
1403
|
+
const existing = best.get(pair.record.request_id);
|
|
1404
|
+
if (existing === void 0 || is_better(pair, existing)) {
|
|
1405
|
+
best.set(pair.record.request_id, pair);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return values_of(best);
|
|
1581
1409
|
}
|
|
1582
|
-
function
|
|
1583
|
-
return
|
|
1410
|
+
function attributed(corpus) {
|
|
1411
|
+
return dedupe_across_sessions(attribute_corpus(corpus).pairs);
|
|
1584
1412
|
}
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1413
|
+
function mainline_records(pairs) {
|
|
1414
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1415
|
+
for (const pair of pairs) {
|
|
1416
|
+
if (pair.is_delegated) continue;
|
|
1417
|
+
groups.set(pair.session_id, [...groups.get(pair.session_id) ?? [], pair.record]);
|
|
1418
|
+
}
|
|
1419
|
+
return [...groups.values()];
|
|
1587
1420
|
}
|
|
1588
|
-
var
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
issue_api_path,
|
|
1595
|
-
blocked_by_api_path,
|
|
1596
|
-
issue_comments_api_path,
|
|
1597
|
-
pulls_api_path,
|
|
1598
|
-
pull_api_path,
|
|
1599
|
-
pull_merge_api_path,
|
|
1600
|
-
pull_comments_api_path,
|
|
1601
|
-
pull_reviews_api_path,
|
|
1602
|
-
commit_api_path,
|
|
1603
|
-
commit_check_runs_api_path,
|
|
1604
|
-
commit_status_api_path
|
|
1421
|
+
var cost_corpus = {
|
|
1422
|
+
load_corpus,
|
|
1423
|
+
attribute_corpus,
|
|
1424
|
+
floor_for_issue,
|
|
1425
|
+
attributed,
|
|
1426
|
+
mainline_records
|
|
1605
1427
|
};
|
|
1606
1428
|
|
|
1607
|
-
// scripts/
|
|
1608
|
-
|
|
1429
|
+
// scripts/cost-runtime/cost-verdict.ts
|
|
1430
|
+
var FAILURE_EXIT_CODE = 1;
|
|
1431
|
+
var OVER_VERDICT = "over";
|
|
1432
|
+
var UNDER_VERDICT = "under";
|
|
1433
|
+
function per_request_cost(measurement2) {
|
|
1434
|
+
return measurement2.billed_input_per_request.at(-1) ?? 0;
|
|
1435
|
+
}
|
|
1436
|
+
function classify2(measurement2, limit) {
|
|
1437
|
+
return per_request_cost(measurement2) > limit ? OVER_VERDICT : UNDER_VERDICT;
|
|
1438
|
+
}
|
|
1439
|
+
function over_summary(measurement2, limit) {
|
|
1440
|
+
const total = measurement2.billed_input_per_request.length;
|
|
1441
|
+
return `${String(per_request_cost(measurement2))} billed input tokens per request, on the newest of ${String(total)} request(s); limit ${String(limit)}`;
|
|
1442
|
+
}
|
|
1443
|
+
function report_over(measurement2, limit) {
|
|
1444
|
+
if (measurement2 === void 0 || measurement2.billed_input_per_request.length === 0) {
|
|
1445
|
+
console.error("No requests in this session; there is nothing to hand off.");
|
|
1446
|
+
return FAILURE_EXIT_CODE;
|
|
1447
|
+
}
|
|
1448
|
+
console.info(classify2(measurement2, limit));
|
|
1449
|
+
console.error(over_summary(measurement2, limit));
|
|
1450
|
+
return 0;
|
|
1451
|
+
}
|
|
1452
|
+
var cost_verdict = {
|
|
1453
|
+
OVER_VERDICT,
|
|
1454
|
+
UNDER_VERDICT,
|
|
1455
|
+
classify: classify2,
|
|
1456
|
+
per_request_cost,
|
|
1457
|
+
report_over
|
|
1458
|
+
};
|
|
1609
1459
|
|
|
1610
|
-
// scripts/
|
|
1611
|
-
import
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
async function run_gh_check() {
|
|
1615
|
-
try {
|
|
1616
|
-
await execa("gh", ["--version"], { timeout: COMMAND_TIMEOUT_MS });
|
|
1617
|
-
} catch {
|
|
1618
|
-
delete gh_check.promise;
|
|
1619
|
-
throw new Error(GH_NOT_INSTALLED_MSG);
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
async function check_gh_installed() {
|
|
1623
|
-
gh_check.promise ??= run_gh_check();
|
|
1624
|
-
await gh_check.promise;
|
|
1460
|
+
// scripts/cost-runtime/transcript-cwd.ts
|
|
1461
|
+
import path2 from "node:path";
|
|
1462
|
+
function resolve(target, cwd) {
|
|
1463
|
+
return target === void 0 ? cwd : path2.resolve(target);
|
|
1625
1464
|
}
|
|
1465
|
+
var transcript_cwd = { resolve };
|
|
1626
1466
|
|
|
1627
|
-
// scripts/
|
|
1628
|
-
|
|
1629
|
-
|
|
1467
|
+
// scripts/cost-runtime/cost-cli.ts
|
|
1468
|
+
var ARGV_OFFSET = 2;
|
|
1469
|
+
var FAILURE_EXIT_CODE2 = 1;
|
|
1470
|
+
var USAGE = "Usage: josh cost (--cut | --over <tokens-per-request>) [--path <dir>]";
|
|
1471
|
+
var UNMEASURABLE_VERDICT = "unmeasurable";
|
|
1472
|
+
function to_threshold(raw) {
|
|
1473
|
+
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
1474
|
+
const parsed = Number(raw);
|
|
1475
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
1476
|
+
}
|
|
1477
|
+
function is_unparsed(raw, parsed) {
|
|
1478
|
+
return parsed === void 0 && raw !== void 0;
|
|
1479
|
+
}
|
|
1480
|
+
function selected_threshold(values) {
|
|
1481
|
+
if (values.cut === true) return CONTEXT_CUT_THRESHOLD;
|
|
1482
|
+
return to_threshold(values.over);
|
|
1483
|
+
}
|
|
1484
|
+
function threshold_option(over) {
|
|
1485
|
+
return over === void 0 ? {} : { over };
|
|
1486
|
+
}
|
|
1487
|
+
function path_option(path4) {
|
|
1488
|
+
return path4 === void 0 ? {} : { path: path4 };
|
|
1489
|
+
}
|
|
1490
|
+
function to_options(values) {
|
|
1491
|
+
if (values.cut === true && values.over !== void 0) return void 0;
|
|
1492
|
+
const over = selected_threshold(values);
|
|
1493
|
+
if (is_unparsed(values.over, over)) return void 0;
|
|
1494
|
+
return { ...threshold_option(over), ...path_option(values.path) };
|
|
1495
|
+
}
|
|
1496
|
+
var PARSE_ARGS_OPTIONS = {
|
|
1497
|
+
cut: { type: "boolean" },
|
|
1498
|
+
over: { type: "string" },
|
|
1499
|
+
path: { type: "string" }
|
|
1500
|
+
};
|
|
1501
|
+
function parse_options(argv) {
|
|
1502
|
+
const values = cli_flags.values_of(argv, PARSE_ARGS_OPTIONS);
|
|
1503
|
+
return values === void 0 ? void 0 : to_options(values);
|
|
1630
1504
|
}
|
|
1631
|
-
function
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
return
|
|
1505
|
+
function report_empty(cwd, session_id) {
|
|
1506
|
+
const searched = cost_transcript.searched_directories(cost_transcript.transcript_directories(cwd));
|
|
1507
|
+
for (const line of cost_transcript.missing_message(searched, session_id)) console.error(line);
|
|
1508
|
+
return FAILURE_EXIT_CODE2;
|
|
1635
1509
|
}
|
|
1636
|
-
function
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
} catch {
|
|
1641
|
-
return { kind: "unexpected_shape" };
|
|
1642
|
-
}
|
|
1510
|
+
function provider_of(environment) {
|
|
1511
|
+
const resolved = agent_role_profile.resolve_provider(environment);
|
|
1512
|
+
if (resolved.kind !== "provider" || resolved.is_default === true) return void 0;
|
|
1513
|
+
return resolved.provider;
|
|
1643
1514
|
}
|
|
1644
|
-
function
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
parse_json_object_or_undefined
|
|
1651
|
-
};
|
|
1652
|
-
|
|
1653
|
-
// scripts/gh/git-gh-failure.ts
|
|
1654
|
-
import { z as z5 } from "zod";
|
|
1655
|
-
var error_document_schema = z5.object({ status: z5.union([z5.string(), z5.number()]).optional() }).catch({});
|
|
1656
|
-
var GH_FAILURE_FIELD = "gh_failure";
|
|
1657
|
-
function has_gh_failure(error) {
|
|
1658
|
-
return error instanceof Error && Object.hasOwn(error, GH_FAILURE_FIELD);
|
|
1659
|
-
}
|
|
1660
|
-
function parse_response_status(stdout) {
|
|
1661
|
-
const document = parse_json.parse_json_object_or_undefined(stdout, error_document_schema);
|
|
1662
|
-
if (document?.status === void 0) return void 0;
|
|
1663
|
-
const status = Number(document.status);
|
|
1664
|
-
return Number.isSafeInteger(status) ? status : void 0;
|
|
1665
|
-
}
|
|
1666
|
-
function classify_stdout(stdout) {
|
|
1667
|
-
return { status: parse_response_status(stdout) };
|
|
1668
|
-
}
|
|
1669
|
-
function attach(error, failure) {
|
|
1670
|
-
Object.defineProperty(error, GH_FAILURE_FIELD, {
|
|
1671
|
-
value: failure,
|
|
1672
|
-
enumerable: false,
|
|
1673
|
-
configurable: true
|
|
1674
|
-
});
|
|
1675
|
-
return error;
|
|
1515
|
+
function measurement_of(session) {
|
|
1516
|
+
return {
|
|
1517
|
+
billed_input_per_request: session.records.map(
|
|
1518
|
+
(record) => cost_usage.billed_input(record.totals)
|
|
1519
|
+
)
|
|
1520
|
+
};
|
|
1676
1521
|
}
|
|
1677
|
-
function
|
|
1678
|
-
|
|
1522
|
+
function anthropic_own(target, environment) {
|
|
1523
|
+
const corpus = cost_corpus.load_corpus(target);
|
|
1524
|
+
const selection = own_session.select_own_session(corpus.files, environment);
|
|
1525
|
+
if (selection.kind === "absent") return { kind: "absent", session_id: selection.session_id };
|
|
1526
|
+
const session = corpus.sessions[selection.index];
|
|
1527
|
+
if (session === void 0) return { kind: "empty" };
|
|
1528
|
+
return { kind: "measured", measurement: measurement_of(session) };
|
|
1679
1529
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
var BODY_FROM_STDIN = "-";
|
|
1684
|
-
var GH_REQUEST_TIMEOUT_MS = 6e4;
|
|
1685
|
-
function to_timeout_option(timeout_ms) {
|
|
1686
|
-
return { timeout: timeout_ms ?? GH_REQUEST_TIMEOUT_MS };
|
|
1530
|
+
function anthropic_measurement(target, environment) {
|
|
1531
|
+
const own = anthropic_own(target, environment);
|
|
1532
|
+
return own.kind === "measured" ? own.measurement : void 0;
|
|
1687
1533
|
}
|
|
1688
|
-
function
|
|
1689
|
-
|
|
1690
|
-
return Object.keys(removed).length > 0 ? { env: removed } : {};
|
|
1534
|
+
function same_openai_project(target, cwd) {
|
|
1535
|
+
return cost_transcript.session_cwd(target) === cost_transcript.session_cwd(cwd);
|
|
1691
1536
|
}
|
|
1692
|
-
function
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1537
|
+
function reject_openai_cross_project(context, target, cwd) {
|
|
1538
|
+
if (context.provider !== "openai" || context.path === void 0 || same_openai_project(target, cwd)) {
|
|
1539
|
+
return false;
|
|
1540
|
+
}
|
|
1541
|
+
console.error(
|
|
1542
|
+
"OpenAI --path cannot select a thread from another project; run josh cost in that project."
|
|
1543
|
+
);
|
|
1544
|
+
return true;
|
|
1698
1545
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
return
|
|
1705
|
-
}
|
|
1706
|
-
function
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1546
|
+
function measure(target, provider, environment) {
|
|
1547
|
+
return provider === "openai" ? codex_usage.measurement(target, cost_transcript.home_directory(), environment) : anthropic_measurement(target, environment);
|
|
1548
|
+
}
|
|
1549
|
+
function run_over_anthropic(own, target, limit) {
|
|
1550
|
+
if (own.kind === "measured") return cost_verdict.report_over(own.measurement, limit);
|
|
1551
|
+
return report_empty(target, own.kind === "absent" ? own.session_id : void 0);
|
|
1552
|
+
}
|
|
1553
|
+
function run_over_codex(target, limit, environment) {
|
|
1554
|
+
const measurement2 = measure(target, "openai", environment);
|
|
1555
|
+
if (measurement2 !== void 0) return cost_verdict.report_over(measurement2, limit);
|
|
1556
|
+
console.error(`No Codex usage found for the current OpenAI thread under ${target}.`);
|
|
1557
|
+
return FAILURE_EXIT_CODE2;
|
|
1558
|
+
}
|
|
1559
|
+
function run_over(target, limit, provider, environment) {
|
|
1560
|
+
if (provider === "anthropic") {
|
|
1561
|
+
return run_over_anthropic(anthropic_own(target, environment), target, limit);
|
|
1562
|
+
}
|
|
1563
|
+
return run_over_codex(target, limit, environment);
|
|
1564
|
+
}
|
|
1565
|
+
function verdict_of(measurement2) {
|
|
1566
|
+
if (measurement2 === void 0 || measurement2.billed_input_per_request.length === 0) {
|
|
1567
|
+
return UNMEASURABLE_VERDICT;
|
|
1568
|
+
}
|
|
1569
|
+
return cost_verdict.classify(measurement2, CONTEXT_CUT_THRESHOLD);
|
|
1570
|
+
}
|
|
1571
|
+
function session_verdict(cwd = process.cwd(), environment = process.env) {
|
|
1572
|
+
const provider = provider_of(environment);
|
|
1573
|
+
if (provider === void 0) return UNMEASURABLE_VERDICT;
|
|
1574
|
+
return verdict_of(measure(cwd, provider, environment));
|
|
1575
|
+
}
|
|
1576
|
+
function run_context(argv, environment) {
|
|
1577
|
+
const options = parse_options(argv);
|
|
1578
|
+
if (options?.over === void 0) return void 0;
|
|
1579
|
+
const provider = provider_of(environment);
|
|
1580
|
+
if (provider === void 0) return void 0;
|
|
1581
|
+
return { over: options.over, path: options.path, provider };
|
|
1582
|
+
}
|
|
1583
|
+
function run(argv, cwd = process.cwd(), environment = process.env) {
|
|
1584
|
+
const context = run_context(argv, environment);
|
|
1585
|
+
if (context === void 0) {
|
|
1586
|
+
console.error(USAGE);
|
|
1587
|
+
return FAILURE_EXIT_CODE2;
|
|
1588
|
+
}
|
|
1589
|
+
const target = transcript_cwd.resolve(context.path, cwd);
|
|
1590
|
+
if (reject_openai_cross_project(context, target, cwd)) return FAILURE_EXIT_CODE2;
|
|
1591
|
+
return run_over(target, context.over, context.provider, environment);
|
|
1592
|
+
}
|
|
1593
|
+
function main(argv) {
|
|
1594
|
+
process.exitCode = run(argv);
|
|
1595
|
+
}
|
|
1596
|
+
var cost_cli = {
|
|
1597
|
+
UNMEASURABLE_VERDICT,
|
|
1598
|
+
USAGE,
|
|
1599
|
+
parse_options,
|
|
1600
|
+
to_threshold,
|
|
1601
|
+
run,
|
|
1602
|
+
session_verdict
|
|
1603
|
+
};
|
|
1604
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) main(process.argv.slice(ARGV_OFFSET));
|
|
1605
|
+
|
|
1606
|
+
// scripts/rules/pre-gate-cut.ts
|
|
1607
|
+
var GATE_COMMANDS = /* @__PURE__ */ new Set(["gate"]);
|
|
1608
|
+
var CUT_COMMANDS = /* @__PURE__ */ new Set(["run:cut"]);
|
|
1609
|
+
var CUT_MODE_FLAG = /(?:^|\s)--(?:resume|end|json)(?:[=\s]|$)/u;
|
|
1610
|
+
var HOLD_COMMANDS = /* @__PURE__ */ new Set(["run:hold"]);
|
|
1611
|
+
var HOLD_MODE_FLAG = /(?:^|\s)--release(?:[=\s]|$)/u;
|
|
1612
|
+
function invokes_josh(command, names, mode_flag) {
|
|
1613
|
+
return shell_segments.segments_of(command).some(
|
|
1614
|
+
(segment) => shell_segments.is_josh_command(segment, names) && mode_flag?.test(segment) !== true
|
|
1718
1615
|
);
|
|
1719
1616
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
const options = to_spawn_options(timeout_ms, cwd);
|
|
1723
|
-
try {
|
|
1724
|
-
const { stdout } = await execa2("gh", arguments_, options);
|
|
1725
|
-
return stdout.trimEnd();
|
|
1726
|
-
} catch (error) {
|
|
1727
|
-
throw to_gh_error(error);
|
|
1728
|
-
}
|
|
1617
|
+
function runs_the_gate(command) {
|
|
1618
|
+
return invokes_josh(command, GATE_COMMANDS);
|
|
1729
1619
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
const options = { input: input.stdin_body, ...to_spawn_options(input.timeout_ms, input.cwd) };
|
|
1733
|
-
try {
|
|
1734
|
-
const { stdout } = await execa2("gh", input.args, options);
|
|
1735
|
-
return stdout.trimEnd();
|
|
1736
|
-
} catch (error) {
|
|
1737
|
-
throw to_gh_error(error);
|
|
1738
|
-
}
|
|
1620
|
+
function takes_the_cut(command) {
|
|
1621
|
+
return invokes_josh(command, CUT_COMMANDS, CUT_MODE_FLAG);
|
|
1739
1622
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
const { status } = STATUS_LINE_PATTERN.exec(output)?.groups ?? {};
|
|
1743
|
-
return status === void 0 ? void 0 : Number(status);
|
|
1623
|
+
function claims_the_hold(command) {
|
|
1624
|
+
return invokes_josh(command, HOLD_COMMANDS, HOLD_MODE_FLAG);
|
|
1744
1625
|
}
|
|
1745
|
-
function
|
|
1746
|
-
return
|
|
1626
|
+
function asks_about_the_cut(command) {
|
|
1627
|
+
return invokes_josh(command, CUT_COMMANDS);
|
|
1747
1628
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
}
|
|
1629
|
+
function current_state2() {
|
|
1630
|
+
return {
|
|
1631
|
+
directory: process.cwd(),
|
|
1632
|
+
carried: run_cut.carried_cut_sync,
|
|
1633
|
+
marked_issue: lane_child_marker.marked_issue(),
|
|
1634
|
+
is_reviewer: agent_session_role.is_reviewer(),
|
|
1635
|
+
context_verdict: cost_cli.session_verdict
|
|
1636
|
+
};
|
|
1757
1637
|
}
|
|
1758
|
-
function
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1638
|
+
function dispatched_issue(state) {
|
|
1639
|
+
return state.is_reviewer ? void 0 : state.marked_issue;
|
|
1640
|
+
}
|
|
1641
|
+
function uncut_lane_issue(state) {
|
|
1642
|
+
const issue = lane_paths.lane_issue_of(state.directory);
|
|
1643
|
+
if (issue === void 0 || dispatched_issue(state) !== issue) return void 0;
|
|
1644
|
+
return state.carried()?.issue === issue ? void 0 : issue;
|
|
1645
|
+
}
|
|
1646
|
+
function warrants_the_cut(verdict) {
|
|
1647
|
+
return verdict !== cost_verdict.UNDER_VERDICT;
|
|
1648
|
+
}
|
|
1649
|
+
function is_uncut_gate(command, state = current_state2()) {
|
|
1650
|
+
if (!runs_the_gate(command) || uncut_lane_issue(state) === void 0) return false;
|
|
1651
|
+
return warrants_the_cut(state.context_verdict());
|
|
1652
|
+
}
|
|
1653
|
+
var PRE_GATE_CUT_REASON = "\u26D4 pre-gate cut: this checkout is a lane dispatched for this issue and the gate is the boundary to cut at. Issue `pnpm josh run:cut <N>`: on `cut`, end the turn immediately \u2014 no Telegram is owed; on any other verdict (`not-a-lane`, `busy`, \u2026), reissue the gate \u2014 this fires once per run (`.claude/skills/workflow-commands/pre-gate-cut.md`).";
|
|
1654
|
+
function reaches_the_pre_gate_boundary(call, _turn, run2) {
|
|
1655
|
+
if (!bash_triggers.on_bash_command(asks_about_the_cut)(call)) return false;
|
|
1656
|
+
return run2.some((issued) => bash_triggers.on_bash_command(claims_the_hold)(issued));
|
|
1657
|
+
}
|
|
1658
|
+
var pre_gate_cut = {
|
|
1659
|
+
PRE_GATE_CUT_REASON,
|
|
1660
|
+
asks_about_the_cut,
|
|
1661
|
+
claims_the_hold,
|
|
1662
|
+
is_uncut_gate,
|
|
1663
|
+
reaches_the_pre_gate_boundary,
|
|
1664
|
+
runs_the_gate,
|
|
1665
|
+
takes_the_cut,
|
|
1666
|
+
uncut_lane_issue
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
// scripts/discovery/repo-party.ts
|
|
1670
|
+
var FIRST_PARTY = "first-party";
|
|
1671
|
+
var THIRD_PARTY = "third-party";
|
|
1672
|
+
var UNKNOWN = "unknown";
|
|
1673
|
+
var GH_REPO_ENV_KEY = "GH_REPO";
|
|
1674
|
+
var GH_REPO_PATH_SEGMENTS = 2;
|
|
1675
|
+
function classify3(session_owner, target) {
|
|
1676
|
+
if (session_owner === void 0 || target === void 0) return UNKNOWN;
|
|
1677
|
+
return session_owner.toLowerCase() === target.toLowerCase() ? FIRST_PARTY : THIRD_PARTY;
|
|
1766
1678
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
var PAGINATE_FLAG = "--paginate";
|
|
1770
|
-
var SLURP_FLAG = "--slurp";
|
|
1771
|
-
var JQ_FLAG = "--jq";
|
|
1772
|
-
function optional_flag(flag, value) {
|
|
1773
|
-
return value === void 0 ? [] : [flag, value];
|
|
1774
|
-
}
|
|
1775
|
-
function to_gh_api_args(request) {
|
|
1776
|
-
const body_args = request.body === void 0 ? [] : [INPUT_FLAG, BODY_FROM_STDIN];
|
|
1777
|
-
const paginate_args = request.should_paginate === true ? [PAGINATE_FLAG] : [];
|
|
1778
|
-
const slurp_args = request.should_slurp === true ? [SLURP_FLAG] : [];
|
|
1779
|
-
return [
|
|
1780
|
-
"api",
|
|
1781
|
-
request.path,
|
|
1782
|
-
...optional_flag(METHOD_FLAG, request.method),
|
|
1783
|
-
...body_args,
|
|
1784
|
-
...paginate_args,
|
|
1785
|
-
...slurp_args,
|
|
1786
|
-
...optional_flag(JQ_FLAG, request.jq_filter)
|
|
1787
|
-
];
|
|
1679
|
+
function target_owner(owner_repo) {
|
|
1680
|
+
return repo_origin.parse_origin_url(`https://${repo_origin.GITHUB_HOST}/${owner_repo}`)?.owner;
|
|
1788
1681
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
if (request.body === void 0) {
|
|
1792
|
-
return await exec_gh_command(args, request.timeout_ms, request.cwd);
|
|
1793
|
-
}
|
|
1794
|
-
return await exec_gh_command_with_stdin({
|
|
1795
|
-
args,
|
|
1796
|
-
stdin_body: request.body,
|
|
1797
|
-
timeout_ms: request.timeout_ms,
|
|
1798
|
-
cwd: request.cwd
|
|
1799
|
-
});
|
|
1682
|
+
function current_owner(repository_path = process.cwd()) {
|
|
1683
|
+
return repo_discovery.resolve_current_owner(repository_path);
|
|
1800
1684
|
}
|
|
1801
|
-
function
|
|
1802
|
-
|
|
1803
|
-
const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
|
|
1804
|
-
try {
|
|
1805
|
-
const { stdout } = execaSync("gh", to_gh_api_args(request), options);
|
|
1806
|
-
return stdout.trimEnd();
|
|
1807
|
-
} catch (error) {
|
|
1808
|
-
throw to_gh_error(error);
|
|
1809
|
-
}
|
|
1685
|
+
function gh_repo_owners(gh_repo) {
|
|
1686
|
+
return [target_owner(gh_repo.split("/").slice(-GH_REPO_PATH_SEGMENTS).join("/"))];
|
|
1810
1687
|
}
|
|
1811
|
-
function
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1688
|
+
function remote_owners(repository_path) {
|
|
1689
|
+
return repo_discovery.read_remote_urls(repository_path).map((url) => repo_origin.parse_origin_url(url)?.owner);
|
|
1690
|
+
}
|
|
1691
|
+
function placeholder_owners(repository_path = process.cwd(), environment = process.env) {
|
|
1692
|
+
const gh_repo = environment[GH_REPO_ENV_KEY];
|
|
1693
|
+
return gh_repo === void 0 || gh_repo === "" ? remote_owners(repository_path) : gh_repo_owners(gh_repo);
|
|
1817
1694
|
}
|
|
1818
|
-
var
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
exec_gh_api_status_sync,
|
|
1827
|
-
parse_status_line
|
|
1695
|
+
var repo_party = {
|
|
1696
|
+
FIRST_PARTY,
|
|
1697
|
+
THIRD_PARTY,
|
|
1698
|
+
UNKNOWN,
|
|
1699
|
+
classify: classify3,
|
|
1700
|
+
current_owner,
|
|
1701
|
+
placeholder_owners,
|
|
1702
|
+
target_owner
|
|
1828
1703
|
};
|
|
1829
1704
|
|
|
1830
|
-
// scripts/gh/git-gh-
|
|
1831
|
-
function
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
|
|
1841
|
-
${error.stderr}`;
|
|
1842
|
-
return error.message;
|
|
1843
|
-
}
|
|
1844
|
-
function handle_pr_create_error(error) {
|
|
1845
|
-
const error_message = get_error_message_with_stderr(error);
|
|
1846
|
-
if (is_pr_already_exists_message(error_message)) {
|
|
1847
|
-
throw new Error("PR_ALREADY_EXISTS");
|
|
1705
|
+
// scripts/gh/git-gh-repo.ts
|
|
1706
|
+
async function repo_get_name_with_owner() {
|
|
1707
|
+
try {
|
|
1708
|
+
const result = await git_gh_exec.exec_gh_api({
|
|
1709
|
+
path: git_gh_api_path.repo_api_path(),
|
|
1710
|
+
jq_filter: ".full_name"
|
|
1711
|
+
});
|
|
1712
|
+
return git_gh_helpers.parse_pr_state_string(result);
|
|
1713
|
+
} catch {
|
|
1714
|
+
return void 0;
|
|
1848
1715
|
}
|
|
1849
|
-
throw error;
|
|
1850
1716
|
}
|
|
1851
|
-
var
|
|
1852
|
-
|
|
1853
|
-
get_error_message_with_stderr,
|
|
1854
|
-
handle_pr_create_error
|
|
1717
|
+
var git_gh_repo = {
|
|
1718
|
+
repo_get_name_with_owner
|
|
1855
1719
|
};
|
|
1856
1720
|
|
|
1857
1721
|
// scripts/gh/git-gh-issue-rest.ts
|
|
1858
|
-
import { z as
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
function to_gh_state(state) {
|
|
1863
|
-
return state?.toUpperCase();
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
// scripts/gh/git-gh-issue-rest.ts
|
|
1867
|
-
var rest_issue_schema = z6.looseObject({
|
|
1868
|
-
number: z6.number(),
|
|
1869
|
-
issue_dependencies_summary: z6.looseObject({ total_blocked_by: z6.number().optional() }).nullish(),
|
|
1722
|
+
import { z as z5 } from "zod";
|
|
1723
|
+
var rest_issue_schema = z5.looseObject({
|
|
1724
|
+
number: z5.number(),
|
|
1725
|
+
issue_dependencies_summary: z5.looseObject({ total_blocked_by: z5.number().optional() }).nullish(),
|
|
1870
1726
|
// Present only on a pull request, which this endpoint serves as readily as an issue.
|
|
1871
|
-
pull_request:
|
|
1727
|
+
pull_request: z5.looseObject({ merged_at: z5.string().nullish() }).nullish()
|
|
1872
1728
|
});
|
|
1873
1729
|
var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
|
|
1874
1730
|
var REPOSITORY_URL_GROUP = 1;
|
|
@@ -1882,6 +1738,7 @@ var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
|
|
|
1882
1738
|
["url", "html_url"],
|
|
1883
1739
|
["createdAt", "created_at"],
|
|
1884
1740
|
["closedAt", "closed_at"],
|
|
1741
|
+
["stateReason", "state_reason"],
|
|
1885
1742
|
["updatedAt", "updated_at"]
|
|
1886
1743
|
]);
|
|
1887
1744
|
var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
|
|
@@ -2030,23 +1887,23 @@ function to_read_failure(error) {
|
|
|
2030
1887
|
}
|
|
2031
1888
|
async function issue_view_json_classified(issue_number, fields, repo) {
|
|
2032
1889
|
try {
|
|
2033
|
-
const
|
|
2034
|
-
return { kind: "read", json: JSON.stringify(
|
|
1890
|
+
const issue = await read_issue_fields(issue_number, fields, repo);
|
|
1891
|
+
return { kind: "read", json: JSON.stringify(issue) };
|
|
2035
1892
|
} catch (error) {
|
|
2036
1893
|
return to_read_failure(error);
|
|
2037
1894
|
}
|
|
2038
1895
|
}
|
|
2039
1896
|
async function issue_view_field_classified(issue_number, field, repo) {
|
|
2040
1897
|
try {
|
|
2041
|
-
const
|
|
2042
|
-
return { kind: "read", text: git_gh_issue_rest.to_field_text(
|
|
1898
|
+
const issue = await read_issue_fields(issue_number, field, repo);
|
|
1899
|
+
return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
|
|
2043
1900
|
} catch (error) {
|
|
2044
1901
|
return to_read_failure(error);
|
|
2045
1902
|
}
|
|
2046
1903
|
}
|
|
2047
1904
|
async function issue_view_field(issue_number, field, repo) {
|
|
2048
|
-
const
|
|
2049
|
-
return
|
|
1905
|
+
const read2 = await issue_view_field_classified(issue_number, field, repo);
|
|
1906
|
+
return read2.kind === "read" ? read2.text : void 0;
|
|
2050
1907
|
}
|
|
2051
1908
|
async function issue_get_title(issue_number, repo) {
|
|
2052
1909
|
const result = await issue_view_field(issue_number, "title", repo);
|
|
@@ -2059,8 +1916,8 @@ async function issue_get_body_classified(issue_number, repo) {
|
|
|
2059
1916
|
return await issue_view_field_classified(issue_number, "body", repo);
|
|
2060
1917
|
}
|
|
2061
1918
|
async function issue_view_json(issue_number, fields, repo) {
|
|
2062
|
-
const
|
|
2063
|
-
return
|
|
1919
|
+
const read2 = await issue_view_json_classified(issue_number, fields, repo);
|
|
1920
|
+
return read2.kind === "read" ? read2.json : void 0;
|
|
2064
1921
|
}
|
|
2065
1922
|
var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
|
|
2066
1923
|
async function issue_get_state_and_relations(issue_number, repo) {
|
|
@@ -2083,9 +1940,9 @@ async function issue_get_labels_and_body(issue_number) {
|
|
|
2083
1940
|
return await issue_view_json(issue_number, "number,state,labels,body");
|
|
2084
1941
|
}
|
|
2085
1942
|
async function issue_list_comments(issue_number, timeout_ms) {
|
|
2086
|
-
const
|
|
1943
|
+
const path4 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
|
|
2087
1944
|
try {
|
|
2088
|
-
return await git_gh_exec.exec_gh_api({ path, should_paginate: true, timeout_ms });
|
|
1945
|
+
return await git_gh_exec.exec_gh_api({ path: path4, should_paginate: true, timeout_ms });
|
|
2089
1946
|
} catch {
|
|
2090
1947
|
return void 0;
|
|
2091
1948
|
}
|
|
@@ -2160,8 +2017,8 @@ function is_selected(rest, request) {
|
|
|
2160
2017
|
return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
|
|
2161
2018
|
}
|
|
2162
2019
|
async function fetch_page(request, page) {
|
|
2163
|
-
const
|
|
2164
|
-
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path }));
|
|
2020
|
+
const path4 = listing_path(request, page);
|
|
2021
|
+
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path4 }));
|
|
2165
2022
|
return {
|
|
2166
2023
|
selected: rows.filter((row) => is_selected(row, request)),
|
|
2167
2024
|
is_full: rows.length >= PER_PAGE
|
|
@@ -2213,7 +2070,7 @@ var git_gh_issue_list = {
|
|
|
2213
2070
|
};
|
|
2214
2071
|
|
|
2215
2072
|
// scripts/issue/issue-watch-diff.ts
|
|
2216
|
-
import { z as
|
|
2073
|
+
import { z as z6 } from "zod";
|
|
2217
2074
|
var RUN_MARKER = "<!-- josh:run -->";
|
|
2218
2075
|
var BOT_TYPE = "Bot";
|
|
2219
2076
|
var BOT_SUFFIX = "[bot]";
|
|
@@ -2228,11 +2085,11 @@ var AT = " at ";
|
|
|
2228
2085
|
var SECTION_SEPARATOR = "\n\n";
|
|
2229
2086
|
var LINE_SEPARATOR = "\n";
|
|
2230
2087
|
var CRLF = "\r\n";
|
|
2231
|
-
var comment_schema =
|
|
2232
|
-
id:
|
|
2233
|
-
user:
|
|
2234
|
-
created_at:
|
|
2235
|
-
body:
|
|
2088
|
+
var comment_schema = z6.looseObject({
|
|
2089
|
+
id: z6.number(),
|
|
2090
|
+
user: z6.looseObject({ login: z6.string(), type: z6.string().optional() }).nullish(),
|
|
2091
|
+
created_at: z6.string().optional(),
|
|
2092
|
+
body: z6.string().nullish()
|
|
2236
2093
|
});
|
|
2237
2094
|
function is_bot(user) {
|
|
2238
2095
|
return user?.type === BOT_TYPE || (user?.login.endsWith(BOT_SUFFIX) ?? false);
|
|
@@ -2304,6 +2161,7 @@ var LABEL_NAMES_FILTER = ".[].name";
|
|
|
2304
2161
|
var LABELS_SEGMENT = "/labels";
|
|
2305
2162
|
var COMMENTS_SEGMENT = "/comments";
|
|
2306
2163
|
var CLOSED_STATE = "closed";
|
|
2164
|
+
var NOT_PLANNED_REASON = "not_planned";
|
|
2307
2165
|
var COLOR_HASH = "#";
|
|
2308
2166
|
var UNPROCESSABLE_STATUS = 422;
|
|
2309
2167
|
var ALREADY_EXISTS_CODE = "already_exists";
|
|
@@ -2336,16 +2194,25 @@ ${issue_watch_diff.RUN_MARKER}` }),
|
|
|
2336
2194
|
async function issue_try_comment(issue_number, body) {
|
|
2337
2195
|
return await did_write_succeed(async () => await issue_comment(issue_number, body));
|
|
2338
2196
|
}
|
|
2339
|
-
async function
|
|
2197
|
+
async function close_with(issue_number, comment, state) {
|
|
2340
2198
|
return await did_write_succeed(async () => {
|
|
2341
2199
|
if (comment !== void 0) await issue_comment(issue_number, comment);
|
|
2342
2200
|
await git_gh_exec.exec_gh_api({
|
|
2343
2201
|
path: git_gh_api_path.issue_api_path(issue_number),
|
|
2344
2202
|
method: PATCH_METHOD,
|
|
2345
|
-
body: JSON.stringify(
|
|
2203
|
+
body: JSON.stringify(state)
|
|
2346
2204
|
});
|
|
2347
2205
|
});
|
|
2348
2206
|
}
|
|
2207
|
+
async function issue_close(issue_number, comment) {
|
|
2208
|
+
return await close_with(issue_number, comment, { state: CLOSED_STATE });
|
|
2209
|
+
}
|
|
2210
|
+
async function issue_close_not_planned(issue_number, comment) {
|
|
2211
|
+
return await close_with(issue_number, comment, {
|
|
2212
|
+
state: CLOSED_STATE,
|
|
2213
|
+
state_reason: NOT_PLANNED_REASON
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2349
2216
|
function to_label_color(color) {
|
|
2350
2217
|
return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
|
|
2351
2218
|
}
|
|
@@ -2458,6 +2325,7 @@ var git_gh_issue_write = {
|
|
|
2458
2325
|
issue_comment,
|
|
2459
2326
|
issue_try_comment,
|
|
2460
2327
|
issue_close,
|
|
2328
|
+
issue_close_not_planned,
|
|
2461
2329
|
label_ensure,
|
|
2462
2330
|
issue_create_request,
|
|
2463
2331
|
issue_create_with_label,
|
|
@@ -2527,20 +2395,20 @@ var git_gh_issue = {
|
|
|
2527
2395
|
};
|
|
2528
2396
|
|
|
2529
2397
|
// scripts/ci/pr-classification.ts
|
|
2530
|
-
import { readFileSync as
|
|
2531
|
-
import { fileURLToPath } from "node:url";
|
|
2532
|
-
import { z as
|
|
2398
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
2399
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2400
|
+
import { z as z7 } from "zod";
|
|
2533
2401
|
var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
|
|
2534
2402
|
var ISSUE_BUG_LABEL = "bug";
|
|
2535
2403
|
var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
|
|
2536
2404
|
var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
|
|
2537
2405
|
var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
|
|
2538
|
-
var LABEL =
|
|
2539
|
-
var USER =
|
|
2540
|
-
var PULL_REQUEST =
|
|
2541
|
-
var EVENT =
|
|
2406
|
+
var LABEL = z7.object({ name: z7.string() });
|
|
2407
|
+
var USER = z7.object({ login: z7.string() });
|
|
2408
|
+
var PULL_REQUEST = z7.object({ number: z7.number(), user: USER });
|
|
2409
|
+
var EVENT = z7.object({
|
|
2542
2410
|
pull_request: PULL_REQUEST,
|
|
2543
|
-
repository:
|
|
2411
|
+
repository: z7.object({ full_name: z7.string() })
|
|
2544
2412
|
});
|
|
2545
2413
|
function classification_error(labels, author) {
|
|
2546
2414
|
const legacy = labels.filter((label) => LEGACY_LABELS.has(label.toLowerCase()));
|
|
@@ -2563,9 +2431,9 @@ function release_candidates(candidates) {
|
|
|
2563
2431
|
return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
|
|
2564
2432
|
}
|
|
2565
2433
|
function issue_candidates(issue_json) {
|
|
2566
|
-
const
|
|
2567
|
-
const names =
|
|
2568
|
-
const declared = [...(
|
|
2434
|
+
const issue = z7.object({ labels: z7.array(LABEL), body: z7.string().nullable() }).parse(JSON.parse(issue_json));
|
|
2435
|
+
const names = issue.labels.map((label) => label.name.toLowerCase());
|
|
2436
|
+
const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
|
|
2569
2437
|
const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
|
|
2570
2438
|
const unknown = declared.filter((value) => !is_classification(value));
|
|
2571
2439
|
if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
|
|
@@ -2586,9 +2454,9 @@ function select_issue_classification(issue_json) {
|
|
|
2586
2454
|
return label;
|
|
2587
2455
|
}
|
|
2588
2456
|
function fetch_current_labels(repository, pull_number) {
|
|
2589
|
-
const
|
|
2457
|
+
const path4 = `repos/${repository}/issues/${String(pull_number)}/labels`;
|
|
2590
2458
|
try {
|
|
2591
|
-
const names = git_gh_exec.exec_gh_api_sync({ path, jq_filter: ".[].name" });
|
|
2459
|
+
const names = git_gh_exec.exec_gh_api_sync({ path: path4, jq_filter: ".[].name" });
|
|
2592
2460
|
return names.split("\n").filter((name) => name !== "");
|
|
2593
2461
|
} catch (error) {
|
|
2594
2462
|
const detail = error_text.message_of(error);
|
|
@@ -2596,11 +2464,11 @@ function fetch_current_labels(repository, pull_number) {
|
|
|
2596
2464
|
}
|
|
2597
2465
|
}
|
|
2598
2466
|
function check_event(event_path, read_labels = fetch_current_labels) {
|
|
2599
|
-
const event = EVENT.parse(JSON.parse(
|
|
2467
|
+
const event = EVENT.parse(JSON.parse(readFileSync3(event_path, "utf8")));
|
|
2600
2468
|
const labels = read_labels(event.repository.full_name, event.pull_request.number);
|
|
2601
2469
|
return classification_error(labels, event.pull_request.user.login);
|
|
2602
2470
|
}
|
|
2603
|
-
if (process.argv[1] ===
|
|
2471
|
+
if (process.argv[1] === fileURLToPath2(import.meta.url)) {
|
|
2604
2472
|
const event_path = process.env["GITHUB_EVENT_PATH"];
|
|
2605
2473
|
if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
|
|
2606
2474
|
const error = check_event(event_path);
|
|
@@ -2620,8 +2488,8 @@ var pr_classification = {
|
|
|
2620
2488
|
|
|
2621
2489
|
// scripts/lib/poll.ts
|
|
2622
2490
|
async function sleep2(duration_ms) {
|
|
2623
|
-
await new Promise((
|
|
2624
|
-
setTimeout(
|
|
2491
|
+
await new Promise((resolve2) => {
|
|
2492
|
+
setTimeout(resolve2, duration_ms);
|
|
2625
2493
|
});
|
|
2626
2494
|
}
|
|
2627
2495
|
async function wait_between(wait, interval_ms, attempt, attempts) {
|
|
@@ -2638,256 +2506,17 @@ async function poll_until(is_done, options) {
|
|
|
2638
2506
|
var poll = { poll_until, sleep: sleep2 };
|
|
2639
2507
|
|
|
2640
2508
|
// scripts/gh/git-gh-pr.ts
|
|
2641
|
-
import { z as
|
|
2642
|
-
|
|
2643
|
-
// scripts/gh/git-gh-pr-rest.ts
|
|
2644
|
-
import { z as z9 } from "zod";
|
|
2645
|
-
var rest_repo_schema = z9.looseObject({ full_name: z9.string().optional() }).nullish();
|
|
2646
|
-
var rest_pull_side_schema = z9.looseObject({ repo: rest_repo_schema });
|
|
2647
|
-
var rest_pull_schema = z9.looseObject({
|
|
2648
|
-
number: z9.number(),
|
|
2649
|
-
html_url: z9.string().optional(),
|
|
2650
|
-
body: z9.string().nullish(),
|
|
2651
|
-
state: z9.string().optional(),
|
|
2652
|
-
merged: z9.boolean().nullish(),
|
|
2653
|
-
merged_at: z9.string().nullish(),
|
|
2654
|
-
mergeable: z9.boolean().nullish(),
|
|
2655
|
-
mergeable_state: z9.string().optional(),
|
|
2656
|
-
// `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
|
|
2657
|
-
// REST hangs the check runs and the status contexts off the commit rather than off the pull
|
|
2658
|
-
// request.
|
|
2659
|
-
head: z9.looseObject({ ref: z9.string().optional(), sha: z9.string().optional(), repo: rest_repo_schema }).nullish(),
|
|
2660
|
-
base: rest_pull_side_schema.nullish(),
|
|
2661
|
-
// `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
|
|
2662
|
-
// is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`.
|
|
2663
|
-
node_id: z9.string().optional(),
|
|
2664
|
-
auto_merge: z9.looseObject({}).nullish()
|
|
2665
|
-
});
|
|
2666
|
-
var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
|
|
2667
|
-
var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
|
|
2668
|
-
var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
|
|
2669
|
-
var OPEN_STATE = "open";
|
|
2670
|
-
var EMPTY_BODY2 = "";
|
|
2671
|
-
function parse_rest_pull(rest_json) {
|
|
2672
|
-
const parsed = parse_json_object_or_undefined(rest_json, rest_pull_schema);
|
|
2673
|
-
if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
|
|
2674
|
-
return parsed;
|
|
2675
|
-
}
|
|
2676
|
-
function parse_rest_pulls(rest_json) {
|
|
2677
|
-
const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
|
|
2678
|
-
if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
|
|
2679
|
-
return parsed;
|
|
2680
|
-
}
|
|
2681
|
-
function select_pull(pulls) {
|
|
2682
|
-
return pulls.find((pull) => pull.state === OPEN_STATE) ?? pulls[0];
|
|
2683
|
-
}
|
|
2684
|
-
function is_merged(pull) {
|
|
2685
|
-
return pull.merged === true || typeof pull.merged_at === "string";
|
|
2686
|
-
}
|
|
2687
|
-
function to_pr_state(pull) {
|
|
2688
|
-
return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
|
|
2689
|
-
}
|
|
2690
|
-
function to_repo_name(side) {
|
|
2691
|
-
return side?.repo?.full_name;
|
|
2692
|
-
}
|
|
2693
|
-
function is_same_repository_head(pull) {
|
|
2694
|
-
const head_repo = to_repo_name(pull.head);
|
|
2695
|
-
return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
|
|
2696
|
-
}
|
|
2697
|
-
function to_pr_info(pull) {
|
|
2698
|
-
return {
|
|
2699
|
-
mergeable: pull.mergeable,
|
|
2700
|
-
mergeStateStatus: to_gh_state(pull.mergeable_state),
|
|
2701
|
-
state: to_pr_state(pull)
|
|
2702
|
-
};
|
|
2703
|
-
}
|
|
2704
|
-
function to_pr_comments(rest_json) {
|
|
2705
|
-
const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
|
|
2706
|
-
if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
|
|
2707
|
-
return parsed.map((comment) => ({
|
|
2708
|
-
body: comment.body ?? EMPTY_BODY2,
|
|
2709
|
-
url: comment.html_url,
|
|
2710
|
-
author: { login: comment.user?.login }
|
|
2711
|
-
}));
|
|
2712
|
-
}
|
|
2713
|
-
var git_gh_pr_rest = {
|
|
2714
|
-
parse_rest_pull,
|
|
2715
|
-
parse_rest_pulls,
|
|
2716
|
-
select_pull,
|
|
2717
|
-
is_same_repository_head,
|
|
2718
|
-
is_merged,
|
|
2719
|
-
to_pr_state,
|
|
2720
|
-
to_pr_info,
|
|
2721
|
-
to_pr_comments
|
|
2722
|
-
};
|
|
2723
|
-
|
|
2724
|
-
// scripts/gh/git-gh-pr-read.ts
|
|
2725
|
-
var pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
2726
|
-
var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
|
|
2727
|
-
var COMMENTS_QUERY = FULL_PAGE_QUERY;
|
|
2728
|
-
var OWNER_PLACEHOLDER = "{owner}";
|
|
2729
|
-
var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
|
|
2730
|
-
var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
|
|
2731
|
-
function lookup_path(branch_name) {
|
|
2732
|
-
const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
|
|
2733
|
-
return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
|
|
2734
|
-
}
|
|
2735
|
-
async function fetch_pr_number(branch_name) {
|
|
2736
|
-
const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
|
|
2737
|
-
return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
|
|
2738
|
-
}
|
|
2739
|
-
var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
2740
|
-
var pull_by_number = /* @__PURE__ */ new Map();
|
|
2741
|
-
async function fetch_pr_number_read(branch_name) {
|
|
2742
|
-
try {
|
|
2743
|
-
const pr_number = await fetch_pr_number(branch_name);
|
|
2744
|
-
if (pr_number === void 0) return { kind: "missing" };
|
|
2745
|
-
pr_number_by_branch.set(branch_name, pr_number);
|
|
2746
|
-
return { kind: "read", pr_number };
|
|
2747
|
-
} catch (error) {
|
|
2748
|
-
return { kind: "unreadable", cause: error };
|
|
2749
|
-
}
|
|
2750
|
-
}
|
|
2751
|
-
async function start_pr_number_read(branch_name) {
|
|
2752
|
-
const pending = pending_pr_number_by_branch.get(branch_name);
|
|
2753
|
-
if (pending !== void 0) return await pending;
|
|
2754
|
-
const lookup = fetch_pr_number_read(branch_name);
|
|
2755
|
-
pending_pr_number_by_branch.set(branch_name, lookup);
|
|
2756
|
-
try {
|
|
2757
|
-
return await lookup;
|
|
2758
|
-
} finally {
|
|
2759
|
-
pending_pr_number_by_branch.delete(branch_name);
|
|
2760
|
-
}
|
|
2761
|
-
}
|
|
2762
|
-
function forget_pr_numbers() {
|
|
2763
|
-
pr_number_by_branch.clear();
|
|
2764
|
-
pending_pr_number_by_branch.clear();
|
|
2765
|
-
pull_by_number.clear();
|
|
2766
|
-
}
|
|
2767
|
-
async function read_pr_number(branch_name) {
|
|
2768
|
-
const cached = pr_number_by_branch.get(branch_name);
|
|
2769
|
-
if (cached !== void 0) return { kind: "read", pr_number: cached };
|
|
2770
|
-
return await start_pr_number_read(branch_name);
|
|
2771
|
-
}
|
|
2772
|
-
async function resolve_pr_number(branch_name) {
|
|
2773
|
-
const read = await read_pr_number(branch_name);
|
|
2774
|
-
return read.kind === "read" ? read.pr_number : void 0;
|
|
2775
|
-
}
|
|
2776
|
-
function to_unreadable_error(branch_name, cause) {
|
|
2777
|
-
return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
|
|
2778
|
-
}
|
|
2779
|
-
async function require_pr_number(branch_name) {
|
|
2780
|
-
const read = await read_pr_number(branch_name);
|
|
2781
|
-
if (read.kind === "read") return read.pr_number;
|
|
2782
|
-
if (read.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
|
|
2783
|
-
throw to_unreadable_error(branch_name, read.cause);
|
|
2784
|
-
}
|
|
2785
|
-
async function read_pull(pr_number) {
|
|
2786
|
-
const json = await git_gh_exec.exec_gh_api({
|
|
2787
|
-
path: git_gh_api_path.pull_api_path(String(pr_number))
|
|
2788
|
-
});
|
|
2789
|
-
return git_gh_pr_rest.parse_rest_pull(json);
|
|
2790
|
-
}
|
|
2791
|
-
async function read_remembered_pull(pr_number) {
|
|
2792
|
-
const pending = pull_by_number.get(pr_number) ?? read_pull(pr_number);
|
|
2793
|
-
pull_by_number.set(pr_number, pending);
|
|
2794
|
-
try {
|
|
2795
|
-
return await pending;
|
|
2796
|
-
} catch (error) {
|
|
2797
|
-
if (pull_by_number.get(pr_number) === pending) pull_by_number.delete(pr_number);
|
|
2798
|
-
throw error;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
async function read_pull_of_branch(branch_name, read = read_remembered_pull) {
|
|
2802
|
-
const number_read = await read_pr_number(branch_name);
|
|
2803
|
-
if (number_read.kind !== "read") return number_read;
|
|
2804
|
-
try {
|
|
2805
|
-
return { kind: "read", pull: await read(number_read.pr_number) };
|
|
2806
|
-
} catch (error) {
|
|
2807
|
-
return { kind: "unreadable", cause: error };
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
async function read_pull_or_undefined(branch_name) {
|
|
2811
|
-
const read = await read_pull_of_branch(branch_name);
|
|
2812
|
-
return read.kind === "read" ? read.pull : void 0;
|
|
2813
|
-
}
|
|
2814
|
-
async function pr_exists(branch_name) {
|
|
2815
|
-
const read = await read_pr_number(branch_name);
|
|
2816
|
-
if (read.kind === "unreadable") throw to_unreadable_error(branch_name, read.cause);
|
|
2817
|
-
return read.kind === "read";
|
|
2818
|
-
}
|
|
2819
|
-
async function pr_get_number(branch_name) {
|
|
2820
|
-
return await resolve_pr_number(branch_name);
|
|
2821
|
-
}
|
|
2822
|
-
async function pr_get_url(branch_name) {
|
|
2823
|
-
const pull = await read_pull_or_undefined(branch_name);
|
|
2824
|
-
if (pull?.html_url === void 0) return void 0;
|
|
2825
|
-
return git_gh_helpers.parse_pr_state_string(pull.html_url);
|
|
2826
|
-
}
|
|
2827
|
-
async function pr_get_merge_state(branch_name) {
|
|
2828
|
-
const read = await read_pull_of_branch(branch_name, read_pull);
|
|
2829
|
-
if (read.kind !== "read") return void 0;
|
|
2830
|
-
const { pull } = read;
|
|
2831
|
-
return {
|
|
2832
|
-
is_merged: git_gh_pr_rest.is_merged(pull),
|
|
2833
|
-
merged_at: pull.merged_at ?? void 0,
|
|
2834
|
-
head_sha: pull.head?.sha,
|
|
2835
|
-
merge_state_status: to_gh_state(pull.mergeable_state)
|
|
2836
|
-
};
|
|
2837
|
-
}
|
|
2838
|
-
async function pr_get_body(branch_name) {
|
|
2839
|
-
const pull = await read_pull_or_undefined(branch_name);
|
|
2840
|
-
const body = pull?.body;
|
|
2841
|
-
return typeof body === "string" && body.length > 0 ? body : void 0;
|
|
2842
|
-
}
|
|
2843
|
-
async function pr_view(branch_name) {
|
|
2844
|
-
const read = await read_pull_of_branch(branch_name, read_pull);
|
|
2845
|
-
if (read.kind === "unreadable") throw to_unreadable_error(branch_name, read.cause);
|
|
2846
|
-
if (read.kind === "missing") return "";
|
|
2847
|
-
return JSON.stringify(git_gh_pr_rest.to_pr_info(read.pull));
|
|
2848
|
-
}
|
|
2849
|
-
async function read_comments(branch_name, to_path, to_json) {
|
|
2850
|
-
const pr_number = await resolve_pr_number(branch_name);
|
|
2851
|
-
if (pr_number === void 0) return void 0;
|
|
2852
|
-
const path = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
|
|
2853
|
-
try {
|
|
2854
|
-
return to_json(await git_gh_exec.exec_gh_api({ path, should_paginate: true }));
|
|
2855
|
-
} catch {
|
|
2856
|
-
return void 0;
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
function to_comments_json(raw) {
|
|
2860
|
-
return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
|
|
2861
|
-
}
|
|
2862
|
-
function as_served(raw) {
|
|
2863
|
-
return raw;
|
|
2864
|
-
}
|
|
2865
|
-
async function pr_get_comments(branch_name) {
|
|
2866
|
-
return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
|
|
2867
|
-
}
|
|
2868
|
-
async function pr_get_review_comments(branch_name) {
|
|
2869
|
-
return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
|
|
2870
|
-
}
|
|
2871
|
-
var git_gh_pr_read = {
|
|
2872
|
-
pr_exists,
|
|
2873
|
-
pr_get_number,
|
|
2874
|
-
pr_get_url,
|
|
2875
|
-
pr_get_body,
|
|
2876
|
-
pr_get_merge_state,
|
|
2877
|
-
pr_view,
|
|
2878
|
-
pr_get_comments,
|
|
2879
|
-
pr_get_review_comments
|
|
2880
|
-
};
|
|
2509
|
+
import { z as z10 } from "zod";
|
|
2881
2510
|
|
|
2882
2511
|
// scripts/gh/git-gh-pr-review.ts
|
|
2883
|
-
import { z as
|
|
2512
|
+
import { z as z8 } from "zod";
|
|
2884
2513
|
var REVIEW_APPROVED = "APPROVED";
|
|
2885
2514
|
var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
|
|
2886
2515
|
var REVIEW_NONE = "";
|
|
2887
2516
|
var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
|
|
2888
|
-
var rest_review_schema =
|
|
2889
|
-
state:
|
|
2890
|
-
user:
|
|
2517
|
+
var rest_review_schema = z8.looseObject({
|
|
2518
|
+
state: z8.string().optional(),
|
|
2519
|
+
user: z8.looseObject({ login: z8.string().optional() }).nullish()
|
|
2891
2520
|
});
|
|
2892
2521
|
var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
|
|
2893
2522
|
function parse_rest_reviews(reviews_json) {
|
|
@@ -2920,18 +2549,18 @@ function to_review_decision(reviews_json) {
|
|
|
2920
2549
|
var git_gh_pr_review = { to_review_decision };
|
|
2921
2550
|
|
|
2922
2551
|
// scripts/gh/git-gh-pr-rollup.ts
|
|
2923
|
-
import { z as
|
|
2552
|
+
import { z as z9 } from "zod";
|
|
2924
2553
|
var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
|
|
2925
2554
|
var TYPE_NAME_KEY = "__typename";
|
|
2926
|
-
var rollup_element_schema =
|
|
2927
|
-
var check_runs_page_schema =
|
|
2555
|
+
var rollup_element_schema = z9.looseObject({});
|
|
2556
|
+
var check_runs_page_schema = z9.looseObject({
|
|
2928
2557
|
// Required, not optional: GitHub always names the key, and accepting a page without one would
|
|
2929
2558
|
// answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
|
|
2930
|
-
check_runs:
|
|
2559
|
+
check_runs: z9.array(rollup_element_schema)
|
|
2931
2560
|
});
|
|
2932
|
-
var id_holder_schema =
|
|
2933
|
-
var status_page_schema =
|
|
2934
|
-
statuses:
|
|
2561
|
+
var id_holder_schema = z9.object({ id: z9.number() });
|
|
2562
|
+
var status_page_schema = z9.looseObject({
|
|
2563
|
+
statuses: z9.array(rollup_element_schema)
|
|
2935
2564
|
});
|
|
2936
2565
|
var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
|
|
2937
2566
|
var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
|
|
@@ -2945,15 +2574,15 @@ function read_id(value) {
|
|
|
2945
2574
|
const parsed = id_holder_schema.safeParse(value);
|
|
2946
2575
|
return parsed.success ? parsed.data.id : void 0;
|
|
2947
2576
|
}
|
|
2948
|
-
function check_run_group_key(
|
|
2949
|
-
const { name, app } =
|
|
2577
|
+
function check_run_group_key(run2) {
|
|
2578
|
+
const { name, app } = run2;
|
|
2950
2579
|
if (typeof name !== "string") return void 0;
|
|
2951
2580
|
return JSON.stringify([name, read_id(app)]);
|
|
2952
2581
|
}
|
|
2953
|
-
function read_rank(
|
|
2954
|
-
const id = read_id(
|
|
2582
|
+
function read_rank(run2) {
|
|
2583
|
+
const id = read_id(run2);
|
|
2955
2584
|
if (id === void 0) return void 0;
|
|
2956
|
-
return { suite: read_id(
|
|
2585
|
+
return { suite: read_id(run2["check_suite"]) ?? NO_SUITE, run: id };
|
|
2957
2586
|
}
|
|
2958
2587
|
function is_ranked_above(candidate, current) {
|
|
2959
2588
|
if (candidate.suite !== current.suite) return candidate.suite > current.suite;
|
|
@@ -2963,23 +2592,23 @@ function should_replace(previous, rank) {
|
|
|
2963
2592
|
const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
|
|
2964
2593
|
return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
|
|
2965
2594
|
}
|
|
2966
|
-
function remember_newest_run(
|
|
2967
|
-
const key2 = check_run_group_key(
|
|
2968
|
-
const rank = read_rank(
|
|
2595
|
+
function remember_newest_run(run2, newest) {
|
|
2596
|
+
const key2 = check_run_group_key(run2);
|
|
2597
|
+
const rank = read_rank(run2);
|
|
2969
2598
|
if (key2 === void 0 || rank === void 0) return;
|
|
2970
|
-
if (should_replace(newest.get(key2), rank)) newest.set(key2,
|
|
2599
|
+
if (should_replace(newest.get(key2), rank)) newest.set(key2, run2);
|
|
2971
2600
|
}
|
|
2972
|
-
function is_newest_run(
|
|
2973
|
-
const key2 = check_run_group_key(
|
|
2974
|
-
if (key2 === void 0 || read_rank(
|
|
2975
|
-
return newest.get(key2) ===
|
|
2601
|
+
function is_newest_run(run2, newest) {
|
|
2602
|
+
const key2 = check_run_group_key(run2);
|
|
2603
|
+
if (key2 === void 0 || read_rank(run2) === void 0) return true;
|
|
2604
|
+
return newest.get(key2) === run2;
|
|
2976
2605
|
}
|
|
2977
2606
|
function to_check_run_items(check_runs_json) {
|
|
2978
2607
|
const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
|
|
2979
2608
|
const runs = pages.flatMap((page) => page.check_runs);
|
|
2980
2609
|
const newest = /* @__PURE__ */ new Map();
|
|
2981
|
-
for (const
|
|
2982
|
-
return runs.filter((
|
|
2610
|
+
for (const run2 of runs) remember_newest_run(run2, newest);
|
|
2611
|
+
return runs.filter((run2) => is_newest_run(run2, newest));
|
|
2983
2612
|
}
|
|
2984
2613
|
function to_status_context_items(status_json) {
|
|
2985
2614
|
const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
|
|
@@ -2997,16 +2626,16 @@ var git_gh_pr_rollup = {
|
|
|
2997
2626
|
|
|
2998
2627
|
// scripts/gh/git-gh-pr-snapshot.ts
|
|
2999
2628
|
var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
|
|
3000
|
-
async function read_object_pages(
|
|
2629
|
+
async function read_object_pages(path4) {
|
|
3001
2630
|
return await git_gh_exec.exec_gh_api({
|
|
3002
|
-
path: `${
|
|
2631
|
+
path: `${path4}${FULL_PAGE_QUERY}`,
|
|
3003
2632
|
should_paginate: true,
|
|
3004
2633
|
should_slurp: true
|
|
3005
2634
|
});
|
|
3006
2635
|
}
|
|
3007
|
-
async function read_array_pages(
|
|
2636
|
+
async function read_array_pages(path4) {
|
|
3008
2637
|
return await git_gh_exec.exec_gh_api({
|
|
3009
|
-
path: `${
|
|
2638
|
+
path: `${path4}${FULL_PAGE_QUERY}`,
|
|
3010
2639
|
should_paginate: true
|
|
3011
2640
|
});
|
|
3012
2641
|
}
|
|
@@ -3023,8 +2652,8 @@ async function read_status_check_rollup(commit_sha) {
|
|
|
3023
2652
|
return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
|
|
3024
2653
|
}
|
|
3025
2654
|
async function pr_get_review_decision(pr_number) {
|
|
3026
|
-
const
|
|
3027
|
-
return git_gh_pr_review.to_review_decision(await read_array_pages(
|
|
2655
|
+
const path4 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
|
|
2656
|
+
return git_gh_pr_review.to_review_decision(await read_array_pages(path4));
|
|
3028
2657
|
}
|
|
3029
2658
|
async function build_checks_fields(pull) {
|
|
3030
2659
|
return {
|
|
@@ -3065,7 +2694,7 @@ var MERGE_READ_BACK_ATTEMPTS = 3;
|
|
|
3065
2694
|
var MERGE_READ_BACK_INTERVAL_MS = 1e3;
|
|
3066
2695
|
var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
|
|
3067
2696
|
var LABELS_SEGMENT2 = "/labels";
|
|
3068
|
-
var PR_LABELS =
|
|
2697
|
+
var PR_LABELS = z10.array(z10.object({ name: z10.string() }));
|
|
3069
2698
|
async function add_pr_classification(number, label) {
|
|
3070
2699
|
await git_gh_exec.exec_gh_api({
|
|
3071
2700
|
path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
|
|
@@ -3147,8 +2776,8 @@ async function sleep_between_read_backs(attempt) {
|
|
|
3147
2776
|
}
|
|
3148
2777
|
async function has_merge_landed(pr_number, merge_error) {
|
|
3149
2778
|
for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
|
|
3150
|
-
const
|
|
3151
|
-
if (
|
|
2779
|
+
const is_merged = await read_merge_state(pr_number);
|
|
2780
|
+
if (is_merged !== void 0) return is_merged;
|
|
3152
2781
|
await sleep_between_read_backs(attempt);
|
|
3153
2782
|
}
|
|
3154
2783
|
throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
|
|
@@ -3219,22 +2848,6 @@ var git_gh_pr_auto_merge = {
|
|
|
3219
2848
|
pr_list_open_with_head_prefix
|
|
3220
2849
|
};
|
|
3221
2850
|
|
|
3222
|
-
// scripts/gh/git-gh-repo.ts
|
|
3223
|
-
async function repo_get_name_with_owner() {
|
|
3224
|
-
try {
|
|
3225
|
-
const result = await git_gh_exec.exec_gh_api({
|
|
3226
|
-
path: git_gh_api_path.repo_api_path(),
|
|
3227
|
-
jq_filter: ".full_name"
|
|
3228
|
-
});
|
|
3229
|
-
return git_gh_helpers.parse_pr_state_string(result);
|
|
3230
|
-
} catch {
|
|
3231
|
-
return void 0;
|
|
3232
|
-
}
|
|
3233
|
-
}
|
|
3234
|
-
var git_gh_repo = {
|
|
3235
|
-
repo_get_name_with_owner
|
|
3236
|
-
};
|
|
3237
|
-
|
|
3238
2851
|
// scripts/gh/git-pr-checks.ts
|
|
3239
2852
|
var SECONDS_TO_MS = 1e3;
|
|
3240
2853
|
var CHECK_WAIT_INTERVAL_MS = 1e4;
|
|
@@ -3309,16 +2922,16 @@ function to_exhaustion_error(last_read_failure) {
|
|
|
3309
2922
|
}
|
|
3310
2923
|
async function attempt_pr_success_poll(input) {
|
|
3311
2924
|
const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
|
|
3312
|
-
const
|
|
3313
|
-
if (
|
|
2925
|
+
const read2 = await read_pr_state(input, evaluator);
|
|
2926
|
+
if (read2.kind === "unreadable") return await retry_after_unreadable(input, read2.cause);
|
|
3314
2927
|
const classification = classify_poll_result({
|
|
3315
|
-
snapshot:
|
|
2928
|
+
snapshot: read2.snapshot,
|
|
3316
2929
|
stable_count: input.stable_count,
|
|
3317
2930
|
required_stable_reads: input.options.required_stable_reads,
|
|
3318
2931
|
evaluator
|
|
3319
2932
|
});
|
|
3320
2933
|
if (classification.is_done) {
|
|
3321
|
-
return { snapshot:
|
|
2934
|
+
return { snapshot: read2.snapshot, next_stable_count: 0, next_failure_count: 0 };
|
|
3322
2935
|
}
|
|
3323
2936
|
await sleep_before_next_attempt(input);
|
|
3324
2937
|
return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
|
|
@@ -3359,20 +2972,20 @@ var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
|
|
|
3359
2972
|
var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
|
|
3360
2973
|
var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
|
|
3361
2974
|
var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
|
|
3362
|
-
function is_awaited_check(
|
|
3363
|
-
return !is_coderabbit_check(
|
|
2975
|
+
function is_awaited_check(check2) {
|
|
2976
|
+
return !is_coderabbit_check(check2.name) || is_required_check(check2.name);
|
|
3364
2977
|
}
|
|
3365
2978
|
function evaluate_checks_settled(snapshot) {
|
|
3366
|
-
const awaited = snapshot.rollup.filter((
|
|
2979
|
+
const awaited = snapshot.rollup.filter((check2) => is_awaited_check(check2));
|
|
3367
2980
|
const considered = awaited.length === 0 ? snapshot.rollup : awaited;
|
|
3368
|
-
if (considered.length === 0 || considered.some((
|
|
2981
|
+
if (considered.length === 0 || considered.some((check2) => check2.status === CHECK_STATUS_PENDING)) {
|
|
3369
2982
|
return "pending";
|
|
3370
2983
|
}
|
|
3371
|
-
return snapshot.rollup.some((
|
|
2984
|
+
return snapshot.rollup.some((check2) => check2.status === CHECK_STATUS_FAIL) ? "failure" : "success";
|
|
3372
2985
|
}
|
|
3373
2986
|
function describe_checks_failure(snapshot) {
|
|
3374
|
-
const failed = snapshot.rollup.filter((
|
|
3375
|
-
return `${CHECKS_FAILED_MESSAGE}: ${failed.map((
|
|
2987
|
+
const failed = snapshot.rollup.filter((check2) => check2.status === CHECK_STATUS_FAIL);
|
|
2988
|
+
return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check2) => check2.name).join(", ")}`;
|
|
3376
2989
|
}
|
|
3377
2990
|
function should_never_read_review_decision() {
|
|
3378
2991
|
return false;
|
|
@@ -3441,39 +3054,480 @@ function get_repo_name_with_owner_within(timeout_ms) {
|
|
|
3441
3054
|
}
|
|
3442
3055
|
var gh_spawn = { get_repo_name_with_owner, get_repo_name_with_owner_within };
|
|
3443
3056
|
|
|
3057
|
+
// scripts/issue/issue-state.ts
|
|
3058
|
+
import { z as z11 } from "zod";
|
|
3059
|
+
var NO_LABELS = "(none)";
|
|
3060
|
+
var LABEL_SEPARATOR = ", ";
|
|
3061
|
+
var STATE_LABEL = "state: ";
|
|
3062
|
+
var LABELS_LABEL = "labels: ";
|
|
3063
|
+
var HUMAN_REVIEW_LABEL_LINE = "human_review: ";
|
|
3064
|
+
var YES = "yes";
|
|
3065
|
+
var NO = "no";
|
|
3066
|
+
var ISSUE_LABEL = "issue: ";
|
|
3067
|
+
var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
|
|
3068
|
+
var label_schema = z11.looseObject({ name: z11.string() });
|
|
3069
|
+
var issue_state_schema = z11.looseObject({
|
|
3070
|
+
state: z11.string(),
|
|
3071
|
+
labels: label_schema.array().optional()
|
|
3072
|
+
});
|
|
3073
|
+
function parse_issue_state(json) {
|
|
3074
|
+
try {
|
|
3075
|
+
const parsed = parse_json_object_or_undefined(json, issue_state_schema);
|
|
3076
|
+
if (parsed === void 0) return void 0;
|
|
3077
|
+
const labels = parsed.labels ?? [];
|
|
3078
|
+
return {
|
|
3079
|
+
state: parsed.state,
|
|
3080
|
+
labels: labels.map((label) => label.name),
|
|
3081
|
+
is_human_review: has_any_label(labels, HUMAN_REVIEW_LABELS)
|
|
3082
|
+
};
|
|
3083
|
+
} catch {
|
|
3084
|
+
return void 0;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
function format_labels(labels) {
|
|
3088
|
+
return labels.length === 0 ? NO_LABELS : labels.join(LABEL_SEPARATOR);
|
|
3089
|
+
}
|
|
3090
|
+
function format_issue_state(state) {
|
|
3091
|
+
const human_review = `${HUMAN_REVIEW_LABEL_LINE}${state.is_human_review ? YES : NO}`;
|
|
3092
|
+
return `${STATE_LABEL}${state.state}
|
|
3093
|
+
${LABELS_LABEL}${format_labels(state.labels)}
|
|
3094
|
+
${human_review}`;
|
|
3095
|
+
}
|
|
3096
|
+
function format_attributed_issue_state(issue_number, state) {
|
|
3097
|
+
return `${ISSUE_LABEL}${issue_number}
|
|
3098
|
+
${format_issue_state(state)}`;
|
|
3099
|
+
}
|
|
3100
|
+
var issue_state = {
|
|
3101
|
+
NO_LABELS,
|
|
3102
|
+
parse_issue_state,
|
|
3103
|
+
format_issue_state,
|
|
3104
|
+
format_attributed_issue_state
|
|
3105
|
+
};
|
|
3106
|
+
|
|
3107
|
+
// scripts/run/run-liveness.ts
|
|
3108
|
+
import { readFileSync as readFileSync4, realpathSync, statSync } from "node:fs";
|
|
3109
|
+
import { homedir, tmpdir } from "node:os";
|
|
3110
|
+
import path3 from "node:path";
|
|
3111
|
+
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
3112
|
+
|
|
3113
|
+
// scripts/agent/agent-event.ts
|
|
3114
|
+
import { z as z14 } from "zod";
|
|
3115
|
+
|
|
3116
|
+
// scripts/agent/claude-result-event.ts
|
|
3117
|
+
import { z as z13 } from "zod";
|
|
3118
|
+
|
|
3119
|
+
// scripts/agent/interactive-ask.ts
|
|
3120
|
+
import { z as z12 } from "zod";
|
|
3121
|
+
var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
3122
|
+
function is_interactive_tool(name) {
|
|
3123
|
+
return INTERACTIVE_TOOLS.has(name);
|
|
3124
|
+
}
|
|
3125
|
+
var OPTION_SCHEMA = z12.looseObject({ label: z12.string().optional() });
|
|
3126
|
+
var QUESTION_SCHEMA = z12.looseObject({
|
|
3127
|
+
question: z12.string().optional(),
|
|
3128
|
+
options: z12.array(OPTION_SCHEMA).optional()
|
|
3129
|
+
});
|
|
3130
|
+
var ASK_INPUT_SCHEMA = z12.looseObject({ questions: z12.array(QUESTION_SCHEMA).optional() });
|
|
3131
|
+
var DENIAL_SCHEMA = z12.looseObject({
|
|
3132
|
+
tool_name: z12.string().optional(),
|
|
3133
|
+
tool_input: z12.unknown().optional()
|
|
3134
|
+
});
|
|
3135
|
+
var UNKNOWN_QUESTION = "(question text unavailable)";
|
|
3136
|
+
var OPTION_SEPARATOR = " / ";
|
|
3137
|
+
var QUESTION_SEPARATOR = "; ";
|
|
3138
|
+
function labels_of(question) {
|
|
3139
|
+
return (question.options ?? []).map((option) => option.label ?? "").filter((label) => label !== "");
|
|
3140
|
+
}
|
|
3141
|
+
function render_question(question) {
|
|
3142
|
+
const labels = labels_of(question);
|
|
3143
|
+
const text = question.question ?? UNKNOWN_QUESTION;
|
|
3144
|
+
return labels.length === 0 ? text : `${text} [${labels.join(OPTION_SEPARATOR)}]`;
|
|
3145
|
+
}
|
|
3146
|
+
function interactive_input(raw) {
|
|
3147
|
+
const denial = DENIAL_SCHEMA.safeParse(raw);
|
|
3148
|
+
if (!denial.success || !is_interactive_tool(denial.data.tool_name ?? "")) return void 0;
|
|
3149
|
+
const input = ASK_INPUT_SCHEMA.safeParse(denial.data.tool_input);
|
|
3150
|
+
return input.success ? input.data : void 0;
|
|
3151
|
+
}
|
|
3152
|
+
function refused_ask_of(permission_denials) {
|
|
3153
|
+
if (!Array.isArray(permission_denials)) return void 0;
|
|
3154
|
+
const input = permission_denials.map((raw) => interactive_input(raw)).find((one) => one !== void 0);
|
|
3155
|
+
const questions = input?.questions ?? [];
|
|
3156
|
+
if (questions.length === 0) return void 0;
|
|
3157
|
+
return questions.map((question) => render_question(question)).join(QUESTION_SEPARATOR);
|
|
3158
|
+
}
|
|
3159
|
+
var interactive_ask = { is_interactive_tool, refused_ask_of };
|
|
3160
|
+
|
|
3161
|
+
// scripts/agent/claude-result-event.ts
|
|
3162
|
+
var RESULT_EVENT_SCHEMA = z13.looseObject({
|
|
3163
|
+
type: z13.literal("result"),
|
|
3164
|
+
is_error: z13.unknown().optional(),
|
|
3165
|
+
subtype: z13.unknown().optional(),
|
|
3166
|
+
num_turns: z13.unknown().optional(),
|
|
3167
|
+
permission_denials: z13.unknown().optional(),
|
|
3168
|
+
result: z13.unknown().optional(),
|
|
3169
|
+
session_id: z13.unknown().optional(),
|
|
3170
|
+
usage: z13.unknown().optional()
|
|
3171
|
+
});
|
|
3172
|
+
var USAGE_SCHEMA = z13.looseObject({
|
|
3173
|
+
input_tokens: z13.unknown().optional(),
|
|
3174
|
+
cache_read_input_tokens: z13.unknown().optional(),
|
|
3175
|
+
output_tokens: z13.unknown().optional()
|
|
3176
|
+
});
|
|
3177
|
+
function usable_reason(value) {
|
|
3178
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3179
|
+
}
|
|
3180
|
+
function numeric_value(value) {
|
|
3181
|
+
return typeof value === "number" ? value : void 0;
|
|
3182
|
+
}
|
|
3183
|
+
function denial_count(value) {
|
|
3184
|
+
return Array.isArray(value) ? value.length : 0;
|
|
3185
|
+
}
|
|
3186
|
+
function usage_of(value) {
|
|
3187
|
+
const parsed = USAGE_SCHEMA.safeParse(value);
|
|
3188
|
+
if (!parsed.success) return void 0;
|
|
3189
|
+
const usage = {
|
|
3190
|
+
input_tokens: numeric_value(parsed.data.input_tokens),
|
|
3191
|
+
cached_input_tokens: numeric_value(parsed.data.cache_read_input_tokens),
|
|
3192
|
+
output_tokens: numeric_value(parsed.data.output_tokens)
|
|
3193
|
+
};
|
|
3194
|
+
return Object.values(usage).some((count) => count !== void 0) ? usage : void 0;
|
|
3195
|
+
}
|
|
3196
|
+
function decode(value) {
|
|
3197
|
+
const parsed = RESULT_EVENT_SCHEMA.safeParse(value);
|
|
3198
|
+
if (!parsed.success) return void 0;
|
|
3199
|
+
const is_error = parsed.data.is_error === true;
|
|
3200
|
+
return {
|
|
3201
|
+
is_error,
|
|
3202
|
+
subtype: usable_reason(parsed.data.subtype),
|
|
3203
|
+
num_turns: numeric_value(parsed.data.num_turns),
|
|
3204
|
+
permission_denials: denial_count(parsed.data.permission_denials),
|
|
3205
|
+
refused_ask: interactive_ask.refused_ask_of(parsed.data.permission_denials),
|
|
3206
|
+
reason: is_error ? usable_reason(parsed.data.result) ?? usable_reason(parsed.data.subtype) : void 0,
|
|
3207
|
+
session_id: usable_reason(parsed.data.session_id),
|
|
3208
|
+
usage: usage_of(parsed.data.usage)
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
var claude_result_event = { decode };
|
|
3212
|
+
|
|
3213
|
+
// scripts/agent/agent-event.ts
|
|
3214
|
+
var TEXT_BLOCK_SCHEMA = z14.looseObject({ type: z14.literal("text"), text: z14.string() });
|
|
3215
|
+
var CLAUDE_MESSAGE_SCHEMA = z14.looseObject({ content: z14.array(z14.unknown()).optional() });
|
|
3216
|
+
var CLAUDE_INIT_SCHEMA = z14.looseObject({ type: z14.literal("system"), subtype: z14.literal("init") });
|
|
3217
|
+
var CLAUDE_ASSISTANT_SCHEMA = z14.looseObject({
|
|
3218
|
+
type: z14.literal("assistant"),
|
|
3219
|
+
message: CLAUDE_MESSAGE_SCHEMA
|
|
3220
|
+
});
|
|
3221
|
+
var USAGE_SCHEMA2 = z14.looseObject({
|
|
3222
|
+
input_tokens: z14.number().optional(),
|
|
3223
|
+
cached_input_tokens: z14.number().optional(),
|
|
3224
|
+
output_tokens: z14.number().optional()
|
|
3225
|
+
});
|
|
3226
|
+
var CODEX_START_SCHEMA = z14.looseObject({ type: z14.enum(["thread.started", "turn.started"]) });
|
|
3227
|
+
var CODEX_PROGRESS_SCHEMA = z14.looseObject({
|
|
3228
|
+
type: z14.enum(["item.started", "item.updated", "item.completed"]),
|
|
3229
|
+
item: z14.looseObject({ type: z14.string(), text: z14.string().optional() }).optional()
|
|
3230
|
+
});
|
|
3231
|
+
var CODEX_COMPLETE_SCHEMA = z14.looseObject({
|
|
3232
|
+
type: z14.literal("turn.completed"),
|
|
3233
|
+
usage: USAGE_SCHEMA2.optional()
|
|
3234
|
+
});
|
|
3235
|
+
var CODEX_FAILURE_SCHEMA = z14.looseObject({
|
|
3236
|
+
type: z14.enum(["turn.failed", "error"]),
|
|
3237
|
+
error: z14.looseObject({ message: z14.string().optional() }).optional(),
|
|
3238
|
+
message: z14.string().optional()
|
|
3239
|
+
});
|
|
3240
|
+
var EMPTY_STATE = {
|
|
3241
|
+
provider: void 0,
|
|
3242
|
+
launch: "pending",
|
|
3243
|
+
progress: void 0,
|
|
3244
|
+
result: "running",
|
|
3245
|
+
reason: void 0,
|
|
3246
|
+
usage: void 0
|
|
3247
|
+
};
|
|
3248
|
+
var LAUNCH_HEADER_PREFIX = "=== ";
|
|
3249
|
+
var LAUNCH_HEADER_MARKER = " \xB7 started by process ";
|
|
3250
|
+
function usable_text(value) {
|
|
3251
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3252
|
+
}
|
|
3253
|
+
function claude_progress(content) {
|
|
3254
|
+
return content.map((block) => TEXT_BLOCK_SCHEMA.safeParse(block)).filter((parsed) => parsed.success).map((parsed) => parsed.data.text).findLast((text) => text.trim() !== "");
|
|
3255
|
+
}
|
|
3256
|
+
function claude_result(value, state) {
|
|
3257
|
+
const result = claude_result_event.decode(value);
|
|
3258
|
+
if (result === void 0) return void 0;
|
|
3259
|
+
return {
|
|
3260
|
+
...state,
|
|
3261
|
+
provider: "anthropic",
|
|
3262
|
+
result: result.is_error ? "failed" : "completed",
|
|
3263
|
+
reason: result.is_error ? result.reason ?? state.reason : state.reason,
|
|
3264
|
+
usage: result.usage
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
function claude_assistant(value, state) {
|
|
3268
|
+
const assistant = CLAUDE_ASSISTANT_SCHEMA.safeParse(value);
|
|
3269
|
+
if (!assistant.success) return void 0;
|
|
3270
|
+
const progress = claude_progress(assistant.data.message.content ?? []) ?? state.progress;
|
|
3271
|
+
return { ...state, provider: "anthropic", progress };
|
|
3272
|
+
}
|
|
3273
|
+
function claude_terminal(value, state) {
|
|
3274
|
+
return claude_result(value, state);
|
|
3275
|
+
}
|
|
3276
|
+
function claude_event(value, state) {
|
|
3277
|
+
if (CLAUDE_INIT_SCHEMA.safeParse(value).success) {
|
|
3278
|
+
return { ...state, provider: "anthropic", launch: "started" };
|
|
3279
|
+
}
|
|
3280
|
+
return claude_assistant(value, state) ?? claude_terminal(value, state);
|
|
3281
|
+
}
|
|
3282
|
+
function codex_progress(value, state) {
|
|
3283
|
+
const parsed = CODEX_PROGRESS_SCHEMA.safeParse(value);
|
|
3284
|
+
if (!parsed.success) return void 0;
|
|
3285
|
+
const progress = usable_text(parsed.data.item?.text) ?? state.progress;
|
|
3286
|
+
return { ...state, provider: "openai", progress };
|
|
3287
|
+
}
|
|
3288
|
+
function codex_completed(value, state) {
|
|
3289
|
+
const completed = CODEX_COMPLETE_SCHEMA.safeParse(value);
|
|
3290
|
+
if (!completed.success) return void 0;
|
|
3291
|
+
return { ...state, provider: "openai", result: "completed", usage: completed.data.usage };
|
|
3292
|
+
}
|
|
3293
|
+
function codex_failed(value, state) {
|
|
3294
|
+
const failed = CODEX_FAILURE_SCHEMA.safeParse(value);
|
|
3295
|
+
if (!failed.success) return void 0;
|
|
3296
|
+
const reason = usable_text(failed.data.error?.message) ?? usable_text(failed.data.message);
|
|
3297
|
+
return { ...state, provider: "openai", result: "failed", reason: reason ?? state.reason };
|
|
3298
|
+
}
|
|
3299
|
+
function codex_terminal(value, state) {
|
|
3300
|
+
return codex_completed(value, state) ?? codex_failed(value, state);
|
|
3301
|
+
}
|
|
3302
|
+
function codex_event(value, state) {
|
|
3303
|
+
if (CODEX_START_SCHEMA.safeParse(value).success) {
|
|
3304
|
+
return { ...state, provider: "openai", launch: "started" };
|
|
3305
|
+
}
|
|
3306
|
+
return codex_progress(value, state) ?? codex_terminal(value, state);
|
|
3307
|
+
}
|
|
3308
|
+
function normalized(value, state) {
|
|
3309
|
+
return claude_event(value, state) ?? codex_event(value, state) ?? state;
|
|
3310
|
+
}
|
|
3311
|
+
function next_state(line, state) {
|
|
3312
|
+
const is_header = line.startsWith(LAUNCH_HEADER_PREFIX) && line.includes(LAUNCH_HEADER_MARKER);
|
|
3313
|
+
if (is_header) return EMPTY_STATE;
|
|
3314
|
+
return normalized(json_value.parse_or_undefined(line), state);
|
|
3315
|
+
}
|
|
3316
|
+
function read(transcript) {
|
|
3317
|
+
let state = EMPTY_STATE;
|
|
3318
|
+
for (const line of transcript.split("\n")) state = next_state(line, state);
|
|
3319
|
+
return state;
|
|
3320
|
+
}
|
|
3321
|
+
function describe(state) {
|
|
3322
|
+
const progress = state.progress === void 0 ? "" : ` progress=${state.progress}`;
|
|
3323
|
+
const reason = state.reason === void 0 ? "" : ` reason=${state.reason}`;
|
|
3324
|
+
return `launch=${state.launch} result=${state.result}${progress}${reason}`;
|
|
3325
|
+
}
|
|
3326
|
+
var agent_event = { describe, read };
|
|
3327
|
+
|
|
3328
|
+
// scripts/run/run-liveness.ts
|
|
3329
|
+
var ALIVE_VERDICT = "alive";
|
|
3330
|
+
var SETTLED_VERDICT = "settled";
|
|
3331
|
+
var STOPPED_VERDICT = "stopped";
|
|
3332
|
+
var UNDETERMINED_VERDICT = "undetermined";
|
|
3333
|
+
var PROCESS_ALIVE = "alive";
|
|
3334
|
+
var PROCESS_NONE = "none";
|
|
3335
|
+
var PROCESS_UNKNOWN = "unknown";
|
|
3336
|
+
var DEFAULT_SILENT_MINUTES = 30;
|
|
3337
|
+
var DEFAULT_GAP_SECONDS = 5;
|
|
3338
|
+
var MS_PER_MINUTE = 6e4;
|
|
3339
|
+
var MS_PER_SECOND = 1e3;
|
|
3340
|
+
var OPEN_STATE = "OPEN";
|
|
3341
|
+
var STATE_FIELDS = "state,labels";
|
|
3342
|
+
var REASONS = {
|
|
3343
|
+
[ALIVE_VERDICT]: "The unit is still working: its output moved, or a process of the child is alive.",
|
|
3344
|
+
[SETTLED_VERDICT]: "The child is closed, parked with `needs-decision`, or marked `already-done`, so there is nothing here to recover.",
|
|
3345
|
+
[STOPPED_VERDICT]: "The output has been frozen past the silent window and no process of the child is alive in that checkout.",
|
|
3346
|
+
[UNDETERMINED_VERDICT]: "A trace could not be read, so this says nothing about the unit. A trace that could not be read answers `undetermined`, never `stopped`."
|
|
3347
|
+
};
|
|
3348
|
+
var ALIVE_ADVICE = "Keep waiting on `pnpm josh lane:await`; do not touch the child.";
|
|
3349
|
+
var SETTLED_ADVICE = "Read the child with `pnpm josh issue:state <N>` and take the branch its state says.";
|
|
3350
|
+
var STOPPED_CLEAN_ADVICE = 'The unit ended \u2014 classify how before booking it. Run `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it reads the child\'s exit record without waiting for the unit to return, re-dispatches a child that could not reach the API (counting the outage; a run of them trips the environment guard and stops the run), and parks an abandoned child with `needs-decision` against the consecutive-failure guard. The checkout is clean, so there is nothing to stash.';
|
|
3351
|
+
var STOPPED_DIRTY_ADVICE = 'Stash the half-finished work with `git stash push -u -m "backlogrun: stopped unit for #<N>"` and record it on the Issue, then classify how the unit ended with `pnpm josh run:merge <N> --output <path>` (add `--epic <E> --repo <owner/repo> --owner "$PPID"` for a named epic): it re-dispatches an API-outage child (counting the outage; a run of them trips the environment guard) and parks an abandoned child with `needs-decision` against the consecutive-failure guard, without waiting for the unit to return.';
|
|
3352
|
+
var UNDETERMINED_ADVICE = "Read the trace that failed and ask again. Where the process trace was given as `unknown`, leave `--process` out so the command reads it. Two of these in a row is a fault in the check itself rather than a slow unit: stop polling and report it.";
|
|
3353
|
+
function is_output_moving(traces) {
|
|
3354
|
+
return traces.is_output_frozen === false;
|
|
3355
|
+
}
|
|
3356
|
+
function is_agent_state_finished(state) {
|
|
3357
|
+
return state?.result === "completed" || state?.result === "failed";
|
|
3358
|
+
}
|
|
3359
|
+
function is_agent_finished(traces) {
|
|
3360
|
+
return is_agent_state_finished(traces.agent_state);
|
|
3361
|
+
}
|
|
3362
|
+
function definitive_verdict(traces) {
|
|
3363
|
+
if (traces.is_child_settled === true) return SETTLED_VERDICT;
|
|
3364
|
+
if (is_agent_finished(traces)) return STOPPED_VERDICT;
|
|
3365
|
+
if (is_output_moving(traces)) return ALIVE_VERDICT;
|
|
3366
|
+
return void 0;
|
|
3367
|
+
}
|
|
3368
|
+
function is_check_running(traces) {
|
|
3369
|
+
return traces.process_trace === PROCESS_ALIVE;
|
|
3370
|
+
}
|
|
3371
|
+
function has_unreadable_trace(traces) {
|
|
3372
|
+
return traces.is_child_settled === void 0 || traces.is_output_frozen === void 0 || traces.process_trace === PROCESS_UNKNOWN;
|
|
3373
|
+
}
|
|
3374
|
+
function to_verdict(traces) {
|
|
3375
|
+
const definitive = definitive_verdict(traces);
|
|
3376
|
+
if (definitive !== void 0) return definitive;
|
|
3377
|
+
if (has_unreadable_trace(traces)) return UNDETERMINED_VERDICT;
|
|
3378
|
+
return is_check_running(traces) ? ALIVE_VERDICT : STOPPED_VERDICT;
|
|
3379
|
+
}
|
|
3380
|
+
function to_advice(verdict, has_work_to_stash) {
|
|
3381
|
+
if (verdict === ALIVE_VERDICT) return ALIVE_ADVICE;
|
|
3382
|
+
if (verdict === SETTLED_VERDICT) return SETTLED_ADVICE;
|
|
3383
|
+
if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
|
|
3384
|
+
return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
|
|
3385
|
+
}
|
|
3386
|
+
function decide3(traces) {
|
|
3387
|
+
const verdict = to_verdict(traces);
|
|
3388
|
+
const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
|
|
3389
|
+
return {
|
|
3390
|
+
advice: to_advice(verdict, has_work_to_stash),
|
|
3391
|
+
has_work_to_stash,
|
|
3392
|
+
reason: traces.agent_state?.reason ?? REASONS[verdict],
|
|
3393
|
+
verdict
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
function to_resolved_root(root) {
|
|
3397
|
+
try {
|
|
3398
|
+
return realpathSync(root);
|
|
3399
|
+
} catch {
|
|
3400
|
+
return root;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
var ALLOWED_ROOTS = [
|
|
3404
|
+
...new Set(
|
|
3405
|
+
[homedir(), tmpdir(), PLATFORM_TEMP_ROOT].flatMap((root) => [root, to_resolved_root(root)])
|
|
3406
|
+
)
|
|
3407
|
+
];
|
|
3408
|
+
function is_within(candidate, root) {
|
|
3409
|
+
const relative = path3.relative(root, candidate);
|
|
3410
|
+
return relative !== "" && !relative.startsWith("..") && !path3.isAbsolute(relative);
|
|
3411
|
+
}
|
|
3412
|
+
function to_safe_path(output_path) {
|
|
3413
|
+
const normalized2 = path3.normalize(output_path);
|
|
3414
|
+
if (!path3.isAbsolute(normalized2)) return void 0;
|
|
3415
|
+
return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
|
|
3416
|
+
}
|
|
3417
|
+
function sample_output(output_path) {
|
|
3418
|
+
const safe_path = to_safe_path(output_path);
|
|
3419
|
+
if (safe_path === void 0) return void 0;
|
|
3420
|
+
const stats = statSync(safe_path, { throwIfNoEntry: false });
|
|
3421
|
+
if (!stats?.isFile()) return void 0;
|
|
3422
|
+
return { mtime_ms: stats.mtimeMs, size: stats.size };
|
|
3423
|
+
}
|
|
3424
|
+
function read_agent_state(output_path) {
|
|
3425
|
+
const safe_path = to_safe_path(output_path);
|
|
3426
|
+
if (safe_path === void 0) return void 0;
|
|
3427
|
+
try {
|
|
3428
|
+
const state = agent_event.read(readFileSync4(safe_path, "utf8"));
|
|
3429
|
+
return state.provider === void 0 ? void 0 : state;
|
|
3430
|
+
} catch {
|
|
3431
|
+
return void 0;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
function describe_agent_state(output_path) {
|
|
3435
|
+
const state = read_agent_state(output_path);
|
|
3436
|
+
return state === void 0 ? void 0 : `agent: ${state.provider ?? "unknown"} ${agent_event.describe(state)}`;
|
|
3437
|
+
}
|
|
3438
|
+
async function read_output_frozen(request) {
|
|
3439
|
+
const first = sample_output(request.output_path);
|
|
3440
|
+
if (first === void 0) return void 0;
|
|
3441
|
+
if (request.now_ms - first.mtime_ms < request.silent_window_ms) return false;
|
|
3442
|
+
await sleep3(request.gap_ms);
|
|
3443
|
+
const second = sample_output(request.output_path);
|
|
3444
|
+
if (second === void 0) return void 0;
|
|
3445
|
+
return second.mtime_ms === first.mtime_ms && second.size === first.size;
|
|
3446
|
+
}
|
|
3447
|
+
function carries_label(labels, wanted) {
|
|
3448
|
+
return labels.some((label) => label.toLowerCase() === wanted);
|
|
3449
|
+
}
|
|
3450
|
+
var SETTLED_LABELS = [NEEDS_DECISION_LABEL, ALREADY_DONE_LABEL];
|
|
3451
|
+
function carries_settled_label(labels) {
|
|
3452
|
+
return SETTLED_LABELS.some((label) => carries_label(labels, label));
|
|
3453
|
+
}
|
|
3454
|
+
async function read_child_settled(issue, repo) {
|
|
3455
|
+
const json = await git_gh_issue_read.issue_view_json(issue, STATE_FIELDS, repo);
|
|
3456
|
+
if (json === void 0) return void 0;
|
|
3457
|
+
const state = issue_state.parse_issue_state(json);
|
|
3458
|
+
if (state === void 0) return void 0;
|
|
3459
|
+
return state.state !== OPEN_STATE || carries_settled_label(state.labels);
|
|
3460
|
+
}
|
|
3461
|
+
async function read_traces(request) {
|
|
3462
|
+
const [is_initial_child_settled, is_output_frozen, is_tree_dirty] = await Promise.all([
|
|
3463
|
+
read_child_settled(request.issue, request.repo),
|
|
3464
|
+
read_output_frozen({
|
|
3465
|
+
gap_ms: request.gap_ms ?? DEFAULT_GAP_SECONDS * MS_PER_SECOND,
|
|
3466
|
+
now_ms: Date.now(),
|
|
3467
|
+
output_path: request.output_path,
|
|
3468
|
+
silent_window_ms: request.silent_window_ms ?? DEFAULT_SILENT_MINUTES * MS_PER_MINUTE
|
|
3469
|
+
}),
|
|
3470
|
+
run_hold.is_tree_dirty()
|
|
3471
|
+
]);
|
|
3472
|
+
const agent_state = read_agent_state(request.output_path);
|
|
3473
|
+
const is_latest_child_settled = is_agent_state_finished(agent_state) ? await read_child_settled(request.issue, request.repo) : void 0;
|
|
3474
|
+
const is_child_settled = is_latest_child_settled ?? is_initial_child_settled;
|
|
3475
|
+
return {
|
|
3476
|
+
agent_state,
|
|
3477
|
+
is_child_settled,
|
|
3478
|
+
is_output_frozen,
|
|
3479
|
+
is_tree_dirty,
|
|
3480
|
+
process_trace: request.process_trace
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
async function check(request) {
|
|
3484
|
+
issue_number_shape.require_issue_number(request.issue);
|
|
3485
|
+
return decide3(await read_traces(request));
|
|
3486
|
+
}
|
|
3487
|
+
var run_liveness = {
|
|
3488
|
+
check,
|
|
3489
|
+
decide: decide3,
|
|
3490
|
+
describe_agent_state,
|
|
3491
|
+
read_child_settled,
|
|
3492
|
+
read_output_frozen,
|
|
3493
|
+
sample_output,
|
|
3494
|
+
to_safe_path
|
|
3495
|
+
};
|
|
3496
|
+
|
|
3444
3497
|
export {
|
|
3445
3498
|
bounded_pool,
|
|
3446
|
-
git_gh_api_path,
|
|
3447
|
-
read_json_listing,
|
|
3448
|
-
parse_json_object_or_undefined,
|
|
3449
|
-
has_stdout_field,
|
|
3450
|
-
git_gh_exec,
|
|
3451
3499
|
git_gh_issue_read,
|
|
3452
3500
|
issue_watch_diff,
|
|
3453
|
-
issue_citation,
|
|
3454
|
-
session_cite,
|
|
3455
3501
|
shell_segments,
|
|
3456
3502
|
bash_triggers,
|
|
3457
3503
|
tail_commands,
|
|
3458
3504
|
filing_cap,
|
|
3459
3505
|
CONTEXT_CUT_THRESHOLD,
|
|
3460
|
-
|
|
3506
|
+
cost_verdict,
|
|
3507
|
+
cost_cli,
|
|
3461
3508
|
run_cut_handoff,
|
|
3462
3509
|
run_cut,
|
|
3463
3510
|
run_tail_rule,
|
|
3464
3511
|
lane_background,
|
|
3512
|
+
interactive_ask,
|
|
3465
3513
|
IN_PROGRESS_LABEL,
|
|
3466
3514
|
NEEDS_DECISION_LABEL,
|
|
3467
3515
|
RUN_SOLO_LABEL,
|
|
3468
3516
|
RUN_LANE_LABEL,
|
|
3469
|
-
NEEDS_HUMAN_REVIEW_LABEL,
|
|
3470
3517
|
ALREADY_DONE_LABEL,
|
|
3471
3518
|
has_any_label,
|
|
3519
|
+
label_name_of,
|
|
3472
3520
|
has_label_name,
|
|
3473
3521
|
lane_handoff,
|
|
3474
3522
|
lane_park,
|
|
3523
|
+
pre_gate_cut,
|
|
3475
3524
|
gh_spawn,
|
|
3476
3525
|
repo_party,
|
|
3477
|
-
|
|
3478
|
-
git_gh_command
|
|
3526
|
+
git_gh_repo,
|
|
3527
|
+
git_gh_command,
|
|
3528
|
+
issue_state,
|
|
3529
|
+
PROCESS_ALIVE,
|
|
3530
|
+
PROCESS_NONE,
|
|
3531
|
+
PROCESS_UNKNOWN,
|
|
3532
|
+
run_liveness
|
|
3479
3533
|
};
|