@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
package/dist/hooks/stop-guard.js
CHANGED
|
@@ -1,60 +1,105 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import {
|
|
3
|
+
IN_PROGRESS_LABEL,
|
|
4
|
+
PROCESS_ALIVE,
|
|
5
|
+
PROCESS_NONE,
|
|
6
|
+
PROCESS_UNKNOWN,
|
|
3
7
|
RUN_LANE_LABEL,
|
|
4
8
|
RUN_SOLO_LABEL,
|
|
9
|
+
bounded_pool,
|
|
5
10
|
filing_cap,
|
|
6
11
|
gh_spawn,
|
|
7
12
|
git_gh_command,
|
|
13
|
+
git_gh_issue_read,
|
|
14
|
+
git_gh_repo,
|
|
8
15
|
has_label_name,
|
|
9
|
-
|
|
16
|
+
issue_state,
|
|
17
|
+
label_name_of,
|
|
10
18
|
lane_background,
|
|
11
19
|
lane_handoff,
|
|
12
20
|
lane_park,
|
|
21
|
+
pre_gate_cut,
|
|
13
22
|
repo_party,
|
|
14
23
|
run_cut,
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
run_cut_handoff,
|
|
25
|
+
run_liveness,
|
|
26
|
+
tail_commands
|
|
27
|
+
} from "./chunk-SYOTDYCI.js";
|
|
28
|
+
import {
|
|
29
|
+
LEDGER_FILE_EXTENSION,
|
|
30
|
+
LEGACY_LEDGER_FILE,
|
|
31
|
+
LEGACY_OBSERVATION_LEDGER_DIRECTORY,
|
|
32
|
+
LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
33
|
+
MIGRATION_CLAIM_SUFFIX,
|
|
34
|
+
OBSERVATION_LEDGER_DIRECTORY,
|
|
35
|
+
observation_ledger
|
|
36
|
+
} from "./chunk-4KKD2UTK.js";
|
|
17
37
|
import {
|
|
18
38
|
agent_headless,
|
|
19
|
-
|
|
39
|
+
git_gh_api_path,
|
|
40
|
+
git_gh_exec,
|
|
41
|
+
git_worktree,
|
|
42
|
+
has_stderr_field,
|
|
43
|
+
issue_citation,
|
|
20
44
|
issue_cite,
|
|
45
|
+
lane_child_invocation,
|
|
46
|
+
lane_reap,
|
|
21
47
|
lane_registry,
|
|
48
|
+
parse_json_object_or_undefined,
|
|
49
|
+
process_tools,
|
|
50
|
+
repository_lock,
|
|
22
51
|
run_event_stream,
|
|
23
52
|
run_event_stream_emit,
|
|
24
53
|
run_headless,
|
|
54
|
+
run_hold,
|
|
55
|
+
run_preflight,
|
|
56
|
+
session_cite,
|
|
25
57
|
session_language
|
|
26
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-WK6SMBIC.js";
|
|
27
59
|
import {
|
|
28
60
|
timed_fetch
|
|
29
61
|
} from "./chunk-723HKZO5.js";
|
|
30
|
-
import
|
|
62
|
+
import {
|
|
63
|
+
error_text
|
|
64
|
+
} from "./chunk-CDY4NFQG.js";
|
|
65
|
+
import {
|
|
66
|
+
agent_role_profile
|
|
67
|
+
} from "./chunk-UPUZF3AN.js";
|
|
31
68
|
import {
|
|
32
69
|
hook_decision
|
|
33
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-6TS2JD6D.js";
|
|
34
71
|
import {
|
|
35
72
|
time_density_hook,
|
|
36
73
|
time_hook_transcript,
|
|
37
74
|
time_reported_failure
|
|
38
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-TNUMY6NL.js";
|
|
39
76
|
import {
|
|
77
|
+
josh_environment_file,
|
|
40
78
|
lane_child_marker
|
|
41
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-LAOV7NLM.js";
|
|
42
80
|
import {
|
|
43
81
|
backlog_budget,
|
|
82
|
+
cli_flags,
|
|
83
|
+
file_reader,
|
|
84
|
+
git_command,
|
|
85
|
+
git_spawn,
|
|
86
|
+
issue_label_schema,
|
|
44
87
|
issue_number_shape,
|
|
45
88
|
json_value,
|
|
89
|
+
lane_paths,
|
|
46
90
|
process_identity,
|
|
47
91
|
process_owner_schema,
|
|
48
92
|
run_carry,
|
|
49
93
|
run_invocation
|
|
50
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-APUO6U4J.js";
|
|
51
95
|
import {
|
|
96
|
+
PROBE_TIMEOUT_MS,
|
|
52
97
|
stamp_file
|
|
53
98
|
} from "./chunk-DGT54IH5.js";
|
|
54
99
|
|
|
55
100
|
// scripts/hooks/stop-guard.ts
|
|
56
101
|
import { text } from "node:stream/consumers";
|
|
57
|
-
import { fileURLToPath } from "node:url";
|
|
102
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
58
103
|
|
|
59
104
|
// scripts/josh/josh-run.ts
|
|
60
105
|
import { execa } from "execa";
|
|
@@ -83,8 +128,8 @@ async function carry_target() {
|
|
|
83
128
|
return repository === void 0 ? void 0 : run_carry.carry_path(repository);
|
|
84
129
|
}
|
|
85
130
|
function live_carry(target) {
|
|
86
|
-
const
|
|
87
|
-
return
|
|
131
|
+
const read2 = run_carry.read_carry(target);
|
|
132
|
+
return read2.kind === "carried" ? read2.carry : void 0;
|
|
88
133
|
}
|
|
89
134
|
async function read_override() {
|
|
90
135
|
const target = await carry_target();
|
|
@@ -152,8 +197,10 @@ async function lane_limit(environment = process.env, read_override2 = lane_limit
|
|
|
152
197
|
function seated_limit(limit) {
|
|
153
198
|
return Math.min(limit, lane_seed_policy.LAST_LANE_SEAT);
|
|
154
199
|
}
|
|
155
|
-
function free_lanes(limit, occupied) {
|
|
156
|
-
|
|
200
|
+
function free_lanes(limit, occupied, seated = occupied) {
|
|
201
|
+
const by_limit = seated_limit(limit) - occupied;
|
|
202
|
+
const by_seats = lane_seed_policy.LAST_LANE_SEAT - seated;
|
|
203
|
+
return Math.max(NO_LANES, Math.min(by_limit, by_seats));
|
|
157
204
|
}
|
|
158
205
|
var lane_capacity = {
|
|
159
206
|
LANE_LIMIT_KEY,
|
|
@@ -163,6 +210,304 @@ var lane_capacity = {
|
|
|
163
210
|
seated_limit
|
|
164
211
|
};
|
|
165
212
|
|
|
213
|
+
// scripts/backlog/backlog-drive-restore.ts
|
|
214
|
+
var ISSUE_IN_EVENT = /#([1-9]\d*)\b/u;
|
|
215
|
+
var SETTLED_KINDS = /* @__PURE__ */ new Set([
|
|
216
|
+
run_event_stream.EVENT_KIND.MERGE,
|
|
217
|
+
run_event_stream.EVENT_KIND.PARK,
|
|
218
|
+
run_event_stream.EVENT_KIND.SPLIT,
|
|
219
|
+
run_event_stream.EVENT_KIND.WITHDRAW,
|
|
220
|
+
run_event_stream.EVENT_KIND.OUTAGE
|
|
221
|
+
]);
|
|
222
|
+
var DONE_KINDS = /* @__PURE__ */ new Set([
|
|
223
|
+
run_event_stream.EVENT_KIND.MERGE,
|
|
224
|
+
run_event_stream.EVENT_KIND.PARK,
|
|
225
|
+
run_event_stream.EVENT_KIND.SPLIT,
|
|
226
|
+
run_event_stream.EVENT_KIND.WITHDRAW
|
|
227
|
+
]);
|
|
228
|
+
function issue_of(event) {
|
|
229
|
+
return ISSUE_IN_EVENT.exec(event.text)?.[1];
|
|
230
|
+
}
|
|
231
|
+
function update_active(active, event) {
|
|
232
|
+
const issue = issue_of(event);
|
|
233
|
+
if (issue === void 0) return;
|
|
234
|
+
if (event.kind === run_event_stream.EVENT_KIND.CHILD_LAUNCH) active.add(issue);
|
|
235
|
+
if (SETTLED_KINDS.has(event.kind)) active.delete(issue);
|
|
236
|
+
}
|
|
237
|
+
function active_issues(events) {
|
|
238
|
+
const active = /* @__PURE__ */ new Set();
|
|
239
|
+
for (const event of events) update_active(active, event);
|
|
240
|
+
return active;
|
|
241
|
+
}
|
|
242
|
+
function settled_issues(events) {
|
|
243
|
+
const settled = events.filter((event) => DONE_KINDS.has(event.kind));
|
|
244
|
+
return new Set(
|
|
245
|
+
settled.map((event) => issue_of(event)).filter((issue) => issue !== void 0).map(Number)
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
function parked_issues(events) {
|
|
249
|
+
return new Set(
|
|
250
|
+
events.filter((event) => event.kind === run_event_stream.EVENT_KIND.PARK).map((event) => issue_of(event)).filter((issue) => issue !== void 0).map(Number)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
function mentioned_issues(events) {
|
|
254
|
+
const tracked = events.filter(
|
|
255
|
+
(event) => event.kind === run_event_stream.EVENT_KIND.CHILD_LAUNCH || SETTLED_KINDS.has(event.kind)
|
|
256
|
+
);
|
|
257
|
+
return new Set(tracked.map((event) => issue_of(event)).filter((issue) => issue !== void 0));
|
|
258
|
+
}
|
|
259
|
+
function restore(lanes, events, merged_issues, dispatched = []) {
|
|
260
|
+
const active = active_issues(events);
|
|
261
|
+
const mentioned = mentioned_issues(events);
|
|
262
|
+
const carried2 = new Set(dispatched.filter((issue) => !mentioned.has(issue)));
|
|
263
|
+
return lanes.filter(
|
|
264
|
+
(issue) => (active.has(issue) || carried2.has(issue)) && !merged_issues.includes(Number(issue))
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
var backlog_drive_restore = {
|
|
268
|
+
active_issues,
|
|
269
|
+
mentioned_issues,
|
|
270
|
+
restore,
|
|
271
|
+
settled_issues,
|
|
272
|
+
parked_issues
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// scripts/lane/lane-await.ts
|
|
276
|
+
import { spawnSync } from "node:child_process";
|
|
277
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
278
|
+
import { parseArgs } from "node:util";
|
|
279
|
+
var RECONFIRM_MS = 15e3;
|
|
280
|
+
var DEFAULT_POLL_MS = 5e3;
|
|
281
|
+
var NEVER_APPEARED_TIMEOUT_MS = 6e5;
|
|
282
|
+
var MS_PER_SECOND = 1e3;
|
|
283
|
+
var PROCESS_FOUND = 0;
|
|
284
|
+
var PROCESS_NOT_FOUND = 1;
|
|
285
|
+
var PGREP_COMMAND = "pgrep";
|
|
286
|
+
function process_trace_default(issue) {
|
|
287
|
+
const pattern = lane_child_invocation.process_pattern(issue);
|
|
288
|
+
const result = spawnSync(PGREP_COMMAND, ["-f", pattern], {
|
|
289
|
+
encoding: "utf8",
|
|
290
|
+
timeout: PROBE_TIMEOUT_MS
|
|
291
|
+
});
|
|
292
|
+
process_tools.throw_if_missing(PGREP_COMMAND, result.error);
|
|
293
|
+
if (result.status === PROCESS_FOUND || lane_handoff.is_ship_running(issue, process.cwd())) {
|
|
294
|
+
return PROCESS_ALIVE;
|
|
295
|
+
}
|
|
296
|
+
return result.status === PROCESS_NOT_FOUND ? PROCESS_NONE : PROCESS_UNKNOWN;
|
|
297
|
+
}
|
|
298
|
+
function is_process_running_default(issue) {
|
|
299
|
+
return process_trace_default(issue) === PROCESS_ALIVE;
|
|
300
|
+
}
|
|
301
|
+
async function is_settled_default(issue) {
|
|
302
|
+
return await run_liveness.read_child_settled(issue) === true;
|
|
303
|
+
}
|
|
304
|
+
function read_await_args(rest) {
|
|
305
|
+
try {
|
|
306
|
+
return parseArgs({
|
|
307
|
+
args: [...rest],
|
|
308
|
+
allowPositionals: true,
|
|
309
|
+
strict: true,
|
|
310
|
+
options: { owner: { type: "string" } }
|
|
311
|
+
});
|
|
312
|
+
} catch {
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function is_valid(raw) {
|
|
317
|
+
const { owner } = raw.values;
|
|
318
|
+
const numbers = owner === void 0 ? raw.positionals : [...raw.positionals, owner];
|
|
319
|
+
return raw.positionals.length > 0 && numbers.every((value) => issue_number_shape.is_issue_number(value));
|
|
320
|
+
}
|
|
321
|
+
function parse_arguments(rest) {
|
|
322
|
+
const raw = read_await_args(rest);
|
|
323
|
+
if (raw === void 0 || !is_valid(raw)) return void 0;
|
|
324
|
+
const { owner } = raw.values;
|
|
325
|
+
return { issues: raw.positionals, owner: owner === void 0 ? void 0 : Number(owner) };
|
|
326
|
+
}
|
|
327
|
+
function make_initial_state(is_settled) {
|
|
328
|
+
return { appeared: false, disappeared_at: void 0, first_polled_at: void 0, is_settled };
|
|
329
|
+
}
|
|
330
|
+
async function make_states(issues, is_settled) {
|
|
331
|
+
const settled = await Promise.all(issues.map(async (issue) => await is_settled(issue)));
|
|
332
|
+
return new Map(issues.map((issue, index) => [issue, make_initial_state(settled[index] === true)]));
|
|
333
|
+
}
|
|
334
|
+
function resolve_timing(options) {
|
|
335
|
+
return {
|
|
336
|
+
poll_ms: options.poll_ms ?? DEFAULT_POLL_MS,
|
|
337
|
+
reconfirm_ms: options.reconfirm_ms ?? RECONFIRM_MS,
|
|
338
|
+
never_appeared_timeout_ms: options.never_appeared_timeout_ms ?? NEVER_APPEARED_TIMEOUT_MS
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function resolve_options(options) {
|
|
342
|
+
return {
|
|
343
|
+
is_running: options.is_running ?? is_process_running_default,
|
|
344
|
+
is_settled: options.is_settled ?? is_settled_default,
|
|
345
|
+
now: options.now ?? Date.now,
|
|
346
|
+
sleep: options.sleep ?? sleep,
|
|
347
|
+
...resolve_timing(options)
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function update_liveness(state, is_live) {
|
|
351
|
+
if (!is_live) return;
|
|
352
|
+
state.appeared = true;
|
|
353
|
+
state.disappeared_at = void 0;
|
|
354
|
+
}
|
|
355
|
+
function check_not_appeared(issue, state, now_ms, timeout_ms) {
|
|
356
|
+
if (state.is_settled === true) return issue;
|
|
357
|
+
state.first_polled_at ??= now_ms;
|
|
358
|
+
const elapsed_ms = now_ms - state.first_polled_at;
|
|
359
|
+
if (elapsed_ms >= timeout_ms) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`lane child ${issue} never appeared within ${String(timeout_ms / MS_PER_SECOND)}s`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
function check_issue(issue, state, now_ms, config) {
|
|
367
|
+
const is_live = config.is_running(issue);
|
|
368
|
+
update_liveness(state, is_live);
|
|
369
|
+
if (is_live) return void 0;
|
|
370
|
+
if (!state.appeared) {
|
|
371
|
+
return check_not_appeared(issue, state, now_ms, config.never_appeared_timeout_ms);
|
|
372
|
+
}
|
|
373
|
+
if (state.disappeared_at === void 0) {
|
|
374
|
+
state.disappeared_at = now_ms;
|
|
375
|
+
return void 0;
|
|
376
|
+
}
|
|
377
|
+
return now_ms - state.disappeared_at >= config.reconfirm_ms ? issue : void 0;
|
|
378
|
+
}
|
|
379
|
+
function check_any(states, now_ms, config) {
|
|
380
|
+
for (const [issue, state] of states) {
|
|
381
|
+
const result = check_issue(issue, state, now_ms, config);
|
|
382
|
+
if (result !== void 0) return result;
|
|
383
|
+
}
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
async function wait_for_any(issues, options = {}) {
|
|
387
|
+
const config = resolve_options(options);
|
|
388
|
+
const states = await make_states(issues, config.is_settled);
|
|
389
|
+
for (; ; ) {
|
|
390
|
+
const completed = check_any(states, config.now(), config);
|
|
391
|
+
if (completed !== void 0) return completed;
|
|
392
|
+
await config.sleep(config.poll_ms);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
var lane_await = {
|
|
396
|
+
NEVER_APPEARED_TIMEOUT_MS,
|
|
397
|
+
RECONFIRM_MS,
|
|
398
|
+
check_issue,
|
|
399
|
+
is_process_running_default,
|
|
400
|
+
parse_arguments,
|
|
401
|
+
process_trace_default,
|
|
402
|
+
wait_for_any
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// scripts/lane/lane-spent.ts
|
|
406
|
+
var STATE_FIELD = "state";
|
|
407
|
+
var CLOSED_STATE = "CLOSED";
|
|
408
|
+
async function read_issue_state(issue) {
|
|
409
|
+
const json = await git_gh_issue_read.issue_view_json(issue, STATE_FIELD);
|
|
410
|
+
return json === void 0 ? void 0 : issue_state.parse_issue_state(json)?.state;
|
|
411
|
+
}
|
|
412
|
+
async function read_unpushed_commits(branch) {
|
|
413
|
+
try {
|
|
414
|
+
return await git_command.commit_count_unpushed(branch);
|
|
415
|
+
} catch {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function read_in_flight() {
|
|
420
|
+
return backlog_drive_restore.active_issues(await run_event_stream_emit.current_events());
|
|
421
|
+
}
|
|
422
|
+
var LIVE_PROBES = {
|
|
423
|
+
process_trace: lane_await.process_trace_default,
|
|
424
|
+
issue_state: read_issue_state,
|
|
425
|
+
in_flight: read_in_flight,
|
|
426
|
+
is_tree_dirty: run_hold.is_tree_dirty,
|
|
427
|
+
unpushed_commits: read_unpushed_commits
|
|
428
|
+
};
|
|
429
|
+
function trace_of(lane, probes) {
|
|
430
|
+
return lane.is_stranded ? PROCESS_NONE : probes.process_trace(lane.issue);
|
|
431
|
+
}
|
|
432
|
+
async function child_reason(lane, probes) {
|
|
433
|
+
const trace = probes.process_trace(lane.issue);
|
|
434
|
+
if (trace === PROCESS_NONE) return void 0;
|
|
435
|
+
return trace === PROCESS_ALIVE ? "a child is still working in it" : "whether a child is working in it could not be read";
|
|
436
|
+
}
|
|
437
|
+
async function issue_reason(lane, probes) {
|
|
438
|
+
const state = await probes.issue_state(lane.issue);
|
|
439
|
+
if (state === CLOSED_STATE) return void 0;
|
|
440
|
+
return state === void 0 ? "its issue could not be read" : "its issue is still open";
|
|
441
|
+
}
|
|
442
|
+
async function run_reason(lane, probes) {
|
|
443
|
+
const in_flight = await probes.in_flight();
|
|
444
|
+
return in_flight.has(lane.issue) ? "the running backlogrun has not collected it yet (`pnpm josh run:merge` does)" : void 0;
|
|
445
|
+
}
|
|
446
|
+
async function tree_reason(lane, probes) {
|
|
447
|
+
return await probes.is_tree_dirty(lane.directory) ? "it has uncommitted changes" : void 0;
|
|
448
|
+
}
|
|
449
|
+
async function commit_reason(lane, probes) {
|
|
450
|
+
const count = await probes.unpushed_commits(lane.branch);
|
|
451
|
+
if (count === 0) return void 0;
|
|
452
|
+
return count === void 0 ? "its commits could not be counted" : `it has ${String(count)} commit(s) that are on no remote`;
|
|
453
|
+
}
|
|
454
|
+
var CHECKS = [
|
|
455
|
+
child_reason,
|
|
456
|
+
issue_reason,
|
|
457
|
+
run_reason,
|
|
458
|
+
tree_reason,
|
|
459
|
+
commit_reason
|
|
460
|
+
];
|
|
461
|
+
async function kept_reason(lane, probes = LIVE_PROBES) {
|
|
462
|
+
const reasons = await Promise.all(CHECKS.map(async (check) => await check(lane, probes)));
|
|
463
|
+
return reasons.find((reason) => reason !== void 0);
|
|
464
|
+
}
|
|
465
|
+
async function sort_lanes(lanes, probes = LIVE_PROBES) {
|
|
466
|
+
const live = lanes.filter((lane) => !lane.is_stranded);
|
|
467
|
+
const reasons = await Promise.all(live.map(async (lane) => await kept_reason(lane, probes)));
|
|
468
|
+
const sorted = { spent: [], kept: [] };
|
|
469
|
+
for (const [index, lane] of live.entries()) {
|
|
470
|
+
const reason = reasons[index];
|
|
471
|
+
if (reason === void 0) sorted.spent.push(lane);
|
|
472
|
+
else sorted.kept.push({ issue: lane.issue, reason });
|
|
473
|
+
}
|
|
474
|
+
return sorted;
|
|
475
|
+
}
|
|
476
|
+
function issues_traced(traced, trace) {
|
|
477
|
+
return traced.filter(([, answer2]) => answer2 === trace).map(([issue]) => issue);
|
|
478
|
+
}
|
|
479
|
+
function traced_of(lanes, probes) {
|
|
480
|
+
return lanes.map((lane) => [lane.issue, trace_of(lane, probes)]);
|
|
481
|
+
}
|
|
482
|
+
async function lanes_in_use(probes = LIVE_PROBES) {
|
|
483
|
+
const traced = traced_of(await lane_registry.list_lanes(), probes);
|
|
484
|
+
return {
|
|
485
|
+
working: issues_traced(traced, PROCESS_ALIVE),
|
|
486
|
+
unread: issues_traced(traced, PROCESS_UNKNOWN)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function idle_lanes(lanes, probes = LIVE_PROBES) {
|
|
490
|
+
return issues_traced(traced_of(lanes, probes), PROCESS_NONE);
|
|
491
|
+
}
|
|
492
|
+
var lane_spent = { idle_lanes, kept_reason, lanes_in_use, sort_lanes };
|
|
493
|
+
|
|
494
|
+
// scripts/backlog/backlog-occupied.ts
|
|
495
|
+
function owned_issues(read2) {
|
|
496
|
+
const { invocation, events } = read2;
|
|
497
|
+
const is_only = invocation !== void 0 && run_invocation.has_only(invocation);
|
|
498
|
+
if (!is_only || events === void 0) return void 0;
|
|
499
|
+
const named = (run_invocation.issue_numbers(invocation) ?? []).map(String);
|
|
500
|
+
return /* @__PURE__ */ new Set([...named, ...backlog_drive_restore.mentioned_issues(events)]);
|
|
501
|
+
}
|
|
502
|
+
function occupied_lanes(lanes, read2) {
|
|
503
|
+
const live = lanes.filter((lane) => !lane.is_stranded);
|
|
504
|
+
const owned = owned_issues(read2);
|
|
505
|
+
if (owned === void 0) return live;
|
|
506
|
+
const idle = new Set(read2.idle(live));
|
|
507
|
+
return live.filter((lane) => owned.has(lane.issue) || !idle.has(lane.issue));
|
|
508
|
+
}
|
|
509
|
+
var backlog_occupied = { occupied_lanes };
|
|
510
|
+
|
|
166
511
|
// scripts/epic/epic-triage.ts
|
|
167
512
|
var TRIAGE_VERDICT = "triage";
|
|
168
513
|
function has_triage_label(labels) {
|
|
@@ -240,16 +585,36 @@ var backlog_stalled = {
|
|
|
240
585
|
// scripts/backlog/backlog-ready.ts
|
|
241
586
|
var NO_FREE = 0;
|
|
242
587
|
var NONE2 = 0;
|
|
243
|
-
async function
|
|
244
|
-
|
|
588
|
+
async function occupied_read() {
|
|
589
|
+
const carry = await run_headless.current_carry();
|
|
590
|
+
return {
|
|
591
|
+
invocation: carry?.invocation,
|
|
592
|
+
events: carry === void 0 ? [] : await run_event_stream_emit.scoped_current_events(),
|
|
593
|
+
idle: lane_spent.idle_lanes
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
async function occupied_lanes2(lanes) {
|
|
597
|
+
const open = lanes ?? await lane_registry.list_lanes();
|
|
598
|
+
return backlog_occupied.occupied_lanes(open, await occupied_read());
|
|
599
|
+
}
|
|
600
|
+
async function lane_use(lanes) {
|
|
601
|
+
const open = lanes ?? await lane_registry.list_lanes();
|
|
602
|
+
const occupied = await occupied_lanes2(open);
|
|
603
|
+
return { occupied: occupied.length, seated: open.filter((lane) => !lane.is_stranded).length };
|
|
604
|
+
}
|
|
605
|
+
function free_of(limit, use) {
|
|
606
|
+
return lane_capacity.free_lanes(limit, use.occupied, use.seated);
|
|
607
|
+
}
|
|
608
|
+
async function free_lanes_beside(lanes) {
|
|
609
|
+
const limit = await lane_capacity.lane_limit();
|
|
610
|
+
if (limit.kind !== "limit") return void 0;
|
|
611
|
+
return free_of(limit.limit, await lane_use(lanes));
|
|
245
612
|
}
|
|
246
613
|
async function free_under(limit) {
|
|
247
|
-
return
|
|
614
|
+
return free_of(limit, await lane_use());
|
|
248
615
|
}
|
|
249
616
|
async function free_lane_count() {
|
|
250
|
-
|
|
251
|
-
if (limit.kind !== "limit") return NO_FREE;
|
|
252
|
-
return await free_under(limit.limit);
|
|
617
|
+
return await free_lanes_beside(await lane_registry.list_lanes()) ?? NO_FREE;
|
|
253
618
|
}
|
|
254
619
|
async function drive_free_lane_count() {
|
|
255
620
|
const limit = await lane_capacity.lane_limit();
|
|
@@ -275,8 +640,8 @@ async function read_ready(ports = DEFAULT_PORTS) {
|
|
|
275
640
|
}
|
|
276
641
|
function ready_line(reading, slug) {
|
|
277
642
|
if (reading.issues.length === NONE2 || reading.free_lanes <= NO_FREE) return void 0;
|
|
278
|
-
const
|
|
279
|
-
const issues = reading.issues.map((issue) =>
|
|
643
|
+
const cite3 = issue_cite.citer(slug, /* @__PURE__ */ new Map());
|
|
644
|
+
const issues = reading.issues.map((issue) => cite3(issue)).join(" ");
|
|
280
645
|
return `ready ${issues} \xB7 free lanes ${String(reading.free_lanes)}`;
|
|
281
646
|
}
|
|
282
647
|
async function parent_ready_line(ports, is_parent, read_repo) {
|
|
@@ -306,7 +671,9 @@ var backlog_ready = {
|
|
|
306
671
|
read_backlog_next,
|
|
307
672
|
print_offer_hint,
|
|
308
673
|
free_lane_count,
|
|
309
|
-
|
|
674
|
+
free_lanes_beside,
|
|
675
|
+
lane_use,
|
|
676
|
+
occupied_lanes: occupied_lanes2,
|
|
310
677
|
drive_free_lane_count,
|
|
311
678
|
drains_pool,
|
|
312
679
|
print_ready_line,
|
|
@@ -659,46 +1026,400 @@ async function run_stall_check(ready = backlog_ready.DEFAULT_PORTS) {
|
|
|
659
1026
|
}
|
|
660
1027
|
var backlog_stalled_detect = { detect_and_report, run_stall_check };
|
|
661
1028
|
|
|
662
|
-
// scripts/
|
|
1029
|
+
// scripts/issue/issue-cite-cli.ts
|
|
1030
|
+
import { fileURLToPath } from "node:url";
|
|
663
1031
|
import { z as z2 } from "zod";
|
|
664
|
-
var
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
]
|
|
671
|
-
var
|
|
672
|
-
var
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1032
|
+
var SUCCESS_EXIT_CODE = 0;
|
|
1033
|
+
var FAILURE_EXIT_CODE = 1;
|
|
1034
|
+
var ARGV_OFFSET = 2;
|
|
1035
|
+
var READ_CONCURRENCY = 8;
|
|
1036
|
+
var TITLE_FIELD = "title";
|
|
1037
|
+
var LINE_SEPARATOR = "\n";
|
|
1038
|
+
var USAGE = "Usage: josh issue:cite <issue-number|owner/repo#N> [ ... ] [--repo <owner/repo>]";
|
|
1039
|
+
var OPTIONS = { repo: { type: "string" } };
|
|
1040
|
+
var title_schema = z2.looseObject({ title: z2.string() });
|
|
1041
|
+
function parse_title(json) {
|
|
1042
|
+
const title = json_value.parse_with(json, title_schema)?.title.trim();
|
|
1043
|
+
return title === "" ? void 0 : title;
|
|
1044
|
+
}
|
|
1045
|
+
function to_line(target, slug, read2) {
|
|
1046
|
+
if (read2.kind === "missing") return { kind: "fail", line: issue_cite.missing_line(target) };
|
|
1047
|
+
if (read2.kind !== "read") return { kind: "fail", line: issue_cite.unreadable_line(target) };
|
|
1048
|
+
const title = parse_title(read2.json);
|
|
1049
|
+
if (title === void 0) return { kind: "fail", line: issue_cite.unreadable_line(target) };
|
|
1050
|
+
return { kind: "ok", line: issue_cite.citation_line(slug, target.number, title) };
|
|
1051
|
+
}
|
|
1052
|
+
async function cite_target(target, local_slug) {
|
|
1053
|
+
const slug = target.repo ?? local_slug;
|
|
1054
|
+
if (slug === void 0) return { kind: "fail", line: issue_cite.no_repo_line(target) };
|
|
1055
|
+
const read2 = await git_gh_command.issue_view_json_classified(
|
|
1056
|
+
target.number,
|
|
1057
|
+
TITLE_FIELD,
|
|
1058
|
+
target.repo
|
|
1059
|
+
);
|
|
1060
|
+
return to_line(target, slug, read2);
|
|
1061
|
+
}
|
|
1062
|
+
function parse_targets(positionals, default_repo) {
|
|
1063
|
+
const targets = [];
|
|
1064
|
+
for (const token of positionals) {
|
|
1065
|
+
const target = issue_cite.parse_target(token, default_repo);
|
|
1066
|
+
if (target === void 0) return void 0;
|
|
1067
|
+
targets.push(target);
|
|
1068
|
+
}
|
|
1069
|
+
return targets.length === 0 ? void 0 : targets;
|
|
682
1070
|
}
|
|
683
|
-
function
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
return time_reported_failure.result_text(parsed.data.message.content);
|
|
1071
|
+
async function local_slug_for(targets) {
|
|
1072
|
+
const has_bare_target = targets.some((target) => target.repo === void 0);
|
|
1073
|
+
return has_bare_target ? await git_gh_command.repo_get_name_with_owner() : void 0;
|
|
687
1074
|
}
|
|
688
|
-
function
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
(
|
|
1075
|
+
function split_results(results) {
|
|
1076
|
+
return {
|
|
1077
|
+
lines: results.filter((result) => result.kind === "ok").map((result) => result.line),
|
|
1078
|
+
failures: results.filter((result) => result.kind === "fail").map((result) => result.line)
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function report2(cited) {
|
|
1082
|
+
if (cited.lines.length > 0) console.info(cited.lines.join(LINE_SEPARATOR));
|
|
1083
|
+
for (const failure of cited.failures) console.error(failure);
|
|
1084
|
+
return cited.failures.length === 0 ? SUCCESS_EXIT_CODE : FAILURE_EXIT_CODE;
|
|
1085
|
+
}
|
|
1086
|
+
function read_targets(args) {
|
|
1087
|
+
const parsed = cli_flags.arguments_of(args, OPTIONS);
|
|
1088
|
+
if (parsed === void 0) return void 0;
|
|
1089
|
+
return parse_targets(parsed.positionals, parsed.values.repo);
|
|
1090
|
+
}
|
|
1091
|
+
async function cite(args) {
|
|
1092
|
+
const targets = read_targets(args);
|
|
1093
|
+
if (targets === void 0) return void 0;
|
|
1094
|
+
const local_slug = await local_slug_for(targets);
|
|
1095
|
+
const results = await bounded_pool.bounded_map(
|
|
1096
|
+
targets,
|
|
1097
|
+
READ_CONCURRENCY,
|
|
1098
|
+
async (target) => await cite_target(target, local_slug)
|
|
692
1099
|
);
|
|
1100
|
+
return split_results(results);
|
|
693
1101
|
}
|
|
694
|
-
function
|
|
695
|
-
|
|
1102
|
+
async function run(args) {
|
|
1103
|
+
const cited = await cite(args);
|
|
1104
|
+
if (cited === void 0) {
|
|
1105
|
+
console.error(USAGE);
|
|
1106
|
+
return FAILURE_EXIT_CODE;
|
|
1107
|
+
}
|
|
1108
|
+
return report2(cited);
|
|
696
1109
|
}
|
|
697
|
-
function
|
|
698
|
-
|
|
699
|
-
|
|
1110
|
+
async function main(args) {
|
|
1111
|
+
process.exitCode = await run(args);
|
|
1112
|
+
}
|
|
1113
|
+
var issue_cite_cli = {
|
|
1114
|
+
USAGE,
|
|
1115
|
+
cite,
|
|
1116
|
+
parse_targets,
|
|
1117
|
+
run
|
|
1118
|
+
};
|
|
1119
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) await main(process.argv.slice(ARGV_OFFSET));
|
|
1120
|
+
|
|
1121
|
+
// scripts/notify/telegram-test.ts
|
|
1122
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1123
|
+
|
|
1124
|
+
// scripts/gh/github-issue-url.ts
|
|
1125
|
+
var NAME_WITH_OWNER_SEPARATOR = "/";
|
|
1126
|
+
var GITHUB_URL_PREFIX = "https://github.com/";
|
|
1127
|
+
var GITHUB_URL_PREFIX_SOURCE = GITHUB_URL_PREFIX.replaceAll(".", String.raw`\.`);
|
|
1128
|
+
var REPOSITORY_SOURCE = `^${GITHUB_URL_PREFIX_SOURCE}([^/]+)/([^/]+)`;
|
|
1129
|
+
var ISSUE_URL_PATTERN = new RegExp(String.raw`${REPOSITORY_SOURCE}/issues/(\d+)\b`, "u");
|
|
1130
|
+
var PULL_URL_PATTERN = new RegExp(String.raw`${REPOSITORY_SOURCE}/pull/(\d+)\b`, "u");
|
|
1131
|
+
function build_target(match) {
|
|
1132
|
+
const [, owner = "", repo = "", number = ""] = match;
|
|
1133
|
+
const name_with_owner = `${owner}${NAME_WITH_OWNER_SEPARATOR}${repo}`;
|
|
1134
|
+
return {
|
|
1135
|
+
target: {
|
|
1136
|
+
owner,
|
|
1137
|
+
repo,
|
|
1138
|
+
name_with_owner,
|
|
1139
|
+
base_url: `${GITHUB_URL_PREFIX}${name_with_owner}`
|
|
1140
|
+
},
|
|
1141
|
+
number
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
function parse_with(pattern, url = "") {
|
|
1145
|
+
const match = pattern.exec(url);
|
|
1146
|
+
if (match === null) return void 0;
|
|
1147
|
+
return build_target(match);
|
|
1148
|
+
}
|
|
1149
|
+
function parse(issue_url) {
|
|
1150
|
+
const parsed = parse_with(ISSUE_URL_PATTERN, issue_url);
|
|
1151
|
+
if (parsed === void 0) return void 0;
|
|
1152
|
+
return { ...parsed.target, issue_number: parsed.number };
|
|
1153
|
+
}
|
|
1154
|
+
function parse_pull(pull_url) {
|
|
1155
|
+
const parsed = parse_with(PULL_URL_PATTERN, pull_url);
|
|
1156
|
+
if (parsed === void 0) return void 0;
|
|
1157
|
+
return { ...parsed.target, pull_number: parsed.number };
|
|
1158
|
+
}
|
|
1159
|
+
var github_issue_url = { parse, parse_pull };
|
|
1160
|
+
|
|
1161
|
+
// scripts/git/git-error.ts
|
|
1162
|
+
function get_stderr_from_error(cause) {
|
|
1163
|
+
if (!has_stderr_field(cause)) return void 0;
|
|
1164
|
+
const stderr = cause.stderr.trim();
|
|
1165
|
+
return stderr.length > 0 ? stderr : void 0;
|
|
1166
|
+
}
|
|
1167
|
+
function get_error_message_from_cause(cause) {
|
|
1168
|
+
const message3 = cause.message.trim();
|
|
1169
|
+
if (message3.length > 0) return message3;
|
|
1170
|
+
return get_stderr_from_error(cause);
|
|
1171
|
+
}
|
|
1172
|
+
function get_cause_message(cause) {
|
|
1173
|
+
if (cause instanceof Error) return get_error_message_from_cause(cause);
|
|
1174
|
+
if (typeof cause === "string") return cause.trim();
|
|
1175
|
+
return void 0;
|
|
1176
|
+
}
|
|
1177
|
+
function display_error_details(cause) {
|
|
1178
|
+
const cause_message = get_cause_message(cause);
|
|
1179
|
+
if (cause_message === void 0 || cause_message.length === 0) return;
|
|
1180
|
+
console.error("");
|
|
1181
|
+
console.error("\u{1F4A1} Details:", cause_message);
|
|
1182
|
+
}
|
|
1183
|
+
var FAILURE_EXIT_CODE2 = 1;
|
|
1184
|
+
var OPERATION_CANCELLED_MESSAGE = "\u{1F4A1} Operation cancelled.";
|
|
1185
|
+
var BranchMismatchError = class extends Error {
|
|
1186
|
+
current_branch;
|
|
1187
|
+
target_branch_name;
|
|
1188
|
+
constructor(mismatch, options) {
|
|
1189
|
+
super(
|
|
1190
|
+
`Branch mismatch: on ${mismatch.current_branch}, expected ${mismatch.target_branch_name}`,
|
|
1191
|
+
options
|
|
1192
|
+
);
|
|
1193
|
+
this.name = "BranchMismatchError";
|
|
1194
|
+
this.current_branch = mismatch.current_branch;
|
|
1195
|
+
this.target_branch_name = mismatch.target_branch_name;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
var OperationCancelledError = class extends Error {
|
|
1199
|
+
constructor() {
|
|
1200
|
+
super("Operation cancelled");
|
|
1201
|
+
this.name = "OperationCancelledError";
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
function display_failure(error) {
|
|
1205
|
+
const error_message = error_text.message_of(error);
|
|
1206
|
+
console.error("");
|
|
1207
|
+
console.error("\u274C Error:", error_message);
|
|
1208
|
+
if (error instanceof Error && error.cause !== void 0) {
|
|
1209
|
+
display_error_details(error.cause);
|
|
1210
|
+
}
|
|
1211
|
+
console.error("");
|
|
1212
|
+
}
|
|
1213
|
+
function display_branch_mismatch(error) {
|
|
1214
|
+
console.error("");
|
|
1215
|
+
console.error("\u274C Branch mismatch detected");
|
|
1216
|
+
console.error("");
|
|
1217
|
+
console.error(`Current branch: ${error.current_branch}`);
|
|
1218
|
+
console.error(`Expected branch: ${error.target_branch_name}`);
|
|
1219
|
+
console.error("");
|
|
1220
|
+
console.error("\u{1F4A1} Please update main branch to the latest and try again.");
|
|
1221
|
+
}
|
|
1222
|
+
function display_cancelled() {
|
|
1223
|
+
console.info(OPERATION_CANCELLED_MESSAGE);
|
|
1224
|
+
console.info("");
|
|
1225
|
+
}
|
|
1226
|
+
function display(error) {
|
|
1227
|
+
if (error instanceof BranchMismatchError) {
|
|
1228
|
+
display_branch_mismatch(error);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (error instanceof OperationCancelledError) {
|
|
1232
|
+
display_cancelled();
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
display_failure(error);
|
|
1236
|
+
}
|
|
1237
|
+
function handle(error) {
|
|
1238
|
+
display(error);
|
|
1239
|
+
process.exitCode = FAILURE_EXIT_CODE2;
|
|
1240
|
+
}
|
|
1241
|
+
var git_error = {
|
|
1242
|
+
handle
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
// scripts/josh/cli-body.ts
|
|
1246
|
+
import { readFileSync, statSync } from "node:fs";
|
|
1247
|
+
import path from "node:path";
|
|
1248
|
+
var STDIN_PATH = "-";
|
|
1249
|
+
var STDIN_FD = 0;
|
|
1250
|
+
var ESCAPED_NEWLINE = String.raw`\n`;
|
|
1251
|
+
function read_body_file(raw_path) {
|
|
1252
|
+
const resolved = path.resolve(raw_path);
|
|
1253
|
+
const stats = statSync(resolved, { throwIfNoEntry: false });
|
|
1254
|
+
if (stats === void 0 || stats.isDirectory()) {
|
|
1255
|
+
throw new Error(`Not a readable file: ${resolved}`);
|
|
1256
|
+
}
|
|
1257
|
+
return readFileSync(resolved, "utf8");
|
|
1258
|
+
}
|
|
1259
|
+
function read_file_or_stdin(raw_path) {
|
|
1260
|
+
return raw_path === STDIN_PATH ? readFileSync(STDIN_FD, "utf8") : read_body_file(raw_path);
|
|
1261
|
+
}
|
|
1262
|
+
function expand_escaped_newlines(raw) {
|
|
1263
|
+
return raw.replaceAll(ESCAPED_NEWLINE, "\n");
|
|
1264
|
+
}
|
|
1265
|
+
function has_value(raw) {
|
|
1266
|
+
return raw !== void 0 && raw.length > 0;
|
|
1267
|
+
}
|
|
1268
|
+
function resolve_inline(inline) {
|
|
1269
|
+
return inline === void 0 ? void 0 : expand_escaped_newlines(inline.trim());
|
|
1270
|
+
}
|
|
1271
|
+
function resolve(input) {
|
|
1272
|
+
const { inline, file_path: file_path2 } = input;
|
|
1273
|
+
if (file_path2 === void 0 || file_path2.length === 0) return resolve_inline(inline);
|
|
1274
|
+
if (has_value(inline)) {
|
|
1275
|
+
throw new Error(`Pass ${input.inline_flag} or ${input.file_flag}, not both.`);
|
|
1276
|
+
}
|
|
1277
|
+
return read_file_or_stdin(file_path2);
|
|
1278
|
+
}
|
|
1279
|
+
var cli_body = { read_file_or_stdin, has_value, resolve };
|
|
1280
|
+
|
|
1281
|
+
// scripts/notify/telegram-test-logic.ts
|
|
1282
|
+
var VALID_TASK_TYPES = [
|
|
1283
|
+
"planning",
|
|
1284
|
+
"completion",
|
|
1285
|
+
"failure",
|
|
1286
|
+
"warning",
|
|
1287
|
+
"kickoff_retry",
|
|
1288
|
+
"confirmation",
|
|
1289
|
+
"stalled"
|
|
1290
|
+
];
|
|
1291
|
+
var DEFAULT_TASK_TYPE = "planning";
|
|
1292
|
+
function parse_task_type(raw) {
|
|
1293
|
+
if (raw === void 0) return DEFAULT_TASK_TYPE;
|
|
1294
|
+
const matched = VALID_TASK_TYPES.find((candidate) => candidate === raw);
|
|
1295
|
+
if (matched === void 0) {
|
|
1296
|
+
throw new Error(`Invalid --task-type: ${raw}. Expected one of ${VALID_TASK_TYPES.join(", ")}.`);
|
|
1297
|
+
}
|
|
1298
|
+
return matched;
|
|
1299
|
+
}
|
|
1300
|
+
var has_flag_value = cli_body.has_value;
|
|
1301
|
+
function coalesce(primary, fallback) {
|
|
1302
|
+
if (has_flag_value(primary)) return primary;
|
|
1303
|
+
return fallback;
|
|
1304
|
+
}
|
|
1305
|
+
function normalize_body(values) {
|
|
1306
|
+
return cli_body.resolve({
|
|
1307
|
+
inline: values.body,
|
|
1308
|
+
file_path: values["body-file"],
|
|
1309
|
+
inline_flag: "--body",
|
|
1310
|
+
file_flag: "--body-file"
|
|
1311
|
+
});
|
|
700
1312
|
}
|
|
701
|
-
|
|
1313
|
+
function require_recommended(values) {
|
|
1314
|
+
const recommended = values.recommended?.trim() ?? "";
|
|
1315
|
+
if (recommended.length === 0) {
|
|
1316
|
+
throw new Error('--task-type confirmation needs --recommended "<the recommended choice>".');
|
|
1317
|
+
}
|
|
1318
|
+
return recommended;
|
|
1319
|
+
}
|
|
1320
|
+
function given_alternatives(values) {
|
|
1321
|
+
return (values.alternative ?? []).filter((alternative) => alternative.trim().length > 0);
|
|
1322
|
+
}
|
|
1323
|
+
function build_choices(values, task_type) {
|
|
1324
|
+
if (task_type !== "confirmation") return void 0;
|
|
1325
|
+
return confirmation_options.block({
|
|
1326
|
+
recommended: require_recommended(values),
|
|
1327
|
+
alternatives: given_alternatives(values)
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
function build_input(input) {
|
|
1331
|
+
const task_type = parse_task_type(input.values["task-type"]);
|
|
1332
|
+
return {
|
|
1333
|
+
task_type,
|
|
1334
|
+
repo_name: coalesce(input.values["repo-name"], input.context.repo_name),
|
|
1335
|
+
issue_title: coalesce(input.values["issue-title"], input.context.issue_title),
|
|
1336
|
+
body: normalize_body(input.values),
|
|
1337
|
+
choices: build_choices(input.values, task_type),
|
|
1338
|
+
issue_url: input.values["issue-url"],
|
|
1339
|
+
pr_url: input.values["pr-url"]
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
var telegram_test_logic = {
|
|
1343
|
+
build_input,
|
|
1344
|
+
parse_task_type,
|
|
1345
|
+
has_flag_value
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
// scripts/notify/telegram-test.ts
|
|
1349
|
+
var REPO_NAME_SEPARATOR = "/";
|
|
1350
|
+
var ARGV_OFFSET2 = 2;
|
|
1351
|
+
var FAILURE_EXIT_CODE3 = 1;
|
|
1352
|
+
var USAGE2 = "Usage: josh notify [--task-type <type>] [--body <text> | --body-file <path>] [--issue-url <url>] [--pr-url <url>] [--issue-title <text>] [--repo-name <name>] [--recommended <text>] [--alternative <text>]...";
|
|
1353
|
+
var FREE_TEXT_FLAGS = ["--body", "--issue-title", "--recommended", "--alternative"];
|
|
1354
|
+
var OPTIONS2 = {
|
|
1355
|
+
"task-type": { type: "string" },
|
|
1356
|
+
"repo-name": { type: "string" },
|
|
1357
|
+
"issue-title": { type: "string" },
|
|
1358
|
+
body: { type: "string" },
|
|
1359
|
+
"body-file": { type: "string" },
|
|
1360
|
+
"issue-url": { type: "string" },
|
|
1361
|
+
"pr-url": { type: "string" },
|
|
1362
|
+
recommended: { type: "string" },
|
|
1363
|
+
alternative: { type: "string", multiple: true }
|
|
1364
|
+
};
|
|
1365
|
+
function parse_cli_arguments() {
|
|
1366
|
+
return cli_flags.values_of(
|
|
1367
|
+
cli_flags.attach_values(process.argv.slice(ARGV_OFFSET2), FREE_TEXT_FLAGS),
|
|
1368
|
+
OPTIONS2
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
function refuse_usage() {
|
|
1372
|
+
console.error(USAGE2);
|
|
1373
|
+
process.exitCode = FAILURE_EXIT_CODE3;
|
|
1374
|
+
}
|
|
1375
|
+
async function fetch_repo_name() {
|
|
1376
|
+
const name_with_owner = await git_gh_repo.repo_get_name_with_owner();
|
|
1377
|
+
if (name_with_owner === void 0) return void 0;
|
|
1378
|
+
return name_with_owner.split(REPO_NAME_SEPARATOR).at(-1);
|
|
1379
|
+
}
|
|
1380
|
+
async function fetch_issue_title(target) {
|
|
1381
|
+
if (target === void 0) return void 0;
|
|
1382
|
+
const title = await git_gh_issue_read.issue_get_title(target.issue_number, target.name_with_owner);
|
|
1383
|
+
if (title === void 0) {
|
|
1384
|
+
console.warn(
|
|
1385
|
+
`\u26A0\uFE0F Could not read ${session_cite.issue(target.issue_number, void 0, target.name_with_owner)}.`
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
return title;
|
|
1389
|
+
}
|
|
1390
|
+
async function resolve_issue_title(values, target) {
|
|
1391
|
+
if (telegram_test_logic.has_flag_value(values["issue-title"])) return void 0;
|
|
1392
|
+
return await fetch_issue_title(target);
|
|
1393
|
+
}
|
|
1394
|
+
async function resolve_context(values) {
|
|
1395
|
+
const target = github_issue_url.parse(values["issue-url"]);
|
|
1396
|
+
const pull_target = github_issue_url.parse_pull(values["pr-url"]);
|
|
1397
|
+
const repo_name = target?.repo ?? pull_target?.repo ?? await fetch_repo_name();
|
|
1398
|
+
const issue_title = await resolve_issue_title(values, target);
|
|
1399
|
+
return { repo_name, issue_title };
|
|
1400
|
+
}
|
|
1401
|
+
async function send_values(values) {
|
|
1402
|
+
const context = await resolve_context(values);
|
|
1403
|
+
await telegram_notify.send(telegram_test_logic.build_input({ values, context }));
|
|
1404
|
+
}
|
|
1405
|
+
async function main2() {
|
|
1406
|
+
josh_environment_file.load_environment_file();
|
|
1407
|
+
const values = parse_cli_arguments();
|
|
1408
|
+
if (values === void 0) {
|
|
1409
|
+
refuse_usage();
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
await send_values(values);
|
|
1413
|
+
}
|
|
1414
|
+
async function run_cli() {
|
|
1415
|
+
try {
|
|
1416
|
+
await main2();
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
git_error.handle(error);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (process.argv[1] === fileURLToPath2(import.meta.url)) await run_cli();
|
|
1422
|
+
var telegram_test = { fetch_repo_name, fetch_issue_title, resolve_context, run_cli, send_values };
|
|
702
1423
|
|
|
703
1424
|
// scripts/rules/stop-rules.ts
|
|
704
1425
|
import { z as z3 } from "zod";
|
|
@@ -717,21 +1438,21 @@ var OWNER_SEPARATOR = "/";
|
|
|
717
1438
|
function is_offer_line(line) {
|
|
718
1439
|
return OFFER_PATTERNS.some((pattern) => pattern.test(line));
|
|
719
1440
|
}
|
|
720
|
-
function offers_filing(
|
|
721
|
-
return issue_citation.prose_lines(
|
|
1441
|
+
function offers_filing(message3) {
|
|
1442
|
+
return issue_citation.prose_lines(message3).some((line) => is_offer_line(line));
|
|
722
1443
|
}
|
|
723
|
-
function url_owners(
|
|
724
|
-
return [...
|
|
1444
|
+
function url_owners(message3) {
|
|
1445
|
+
return [...message3.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
|
|
725
1446
|
}
|
|
726
|
-
function qualified_owners(
|
|
727
|
-
return issue_citation.bare_references(
|
|
1447
|
+
function qualified_owners(message3) {
|
|
1448
|
+
return issue_citation.bare_references(message3).filter((reference) => reference.includes(OWNER_SEPARATOR)).map((reference) => reference.split(OWNER_SEPARATOR)[0] ?? "");
|
|
728
1449
|
}
|
|
729
|
-
function mentioned_owners(
|
|
730
|
-
return [...url_owners(
|
|
1450
|
+
function mentioned_owners(message3) {
|
|
1451
|
+
return [...url_owners(message3), ...qualified_owners(message3)];
|
|
731
1452
|
}
|
|
732
|
-
function is_first_party_target(
|
|
1453
|
+
function is_first_party_target(message3, session_owner) {
|
|
733
1454
|
if (session_owner === void 0) return false;
|
|
734
|
-
return mentioned_owners(
|
|
1455
|
+
return mentioned_owners(message3).every(
|
|
735
1456
|
(owner) => repo_party.classify(session_owner, owner) === repo_party.FIRST_PARTY
|
|
736
1457
|
);
|
|
737
1458
|
}
|
|
@@ -773,10 +1494,10 @@ function letter_pattern(script) {
|
|
|
773
1494
|
return void 0;
|
|
774
1495
|
}
|
|
775
1496
|
}
|
|
776
|
-
function prose_of(
|
|
777
|
-
let text2 =
|
|
1497
|
+
function prose_of(message3) {
|
|
1498
|
+
let text2 = message3;
|
|
778
1499
|
for (const pattern of CODE_PATTERNS) text2 = text2.replaceAll(pattern, " ");
|
|
779
|
-
return text2.replaceAll(ASCII_RUN_PATTERN, (
|
|
1500
|
+
return text2.replaceAll(ASCII_RUN_PATTERN, (run3) => run3.includes("/") ? " " : run3);
|
|
780
1501
|
}
|
|
781
1502
|
function letter_count(text2) {
|
|
782
1503
|
return text2.match(LETTER_PATTERN)?.length ?? 0;
|
|
@@ -795,17 +1516,46 @@ function matches_share(prose, script, pattern) {
|
|
|
795
1516
|
const matched = lines.filter((line) => is_line_in_script(line, script, pattern));
|
|
796
1517
|
return matched.length / lines.length;
|
|
797
1518
|
}
|
|
798
|
-
function is_mismatch(
|
|
1519
|
+
function is_mismatch(message3, lang) {
|
|
799
1520
|
const script = script_of(lang);
|
|
800
1521
|
if (script === void 0) return false;
|
|
801
1522
|
const pattern = letter_pattern(script);
|
|
802
1523
|
if (pattern === void 0) return false;
|
|
803
|
-
const prose = prose_of(
|
|
1524
|
+
const prose = prose_of(message3);
|
|
804
1525
|
if (letter_count(prose) < MIN_PROSE_LETTERS) return false;
|
|
805
1526
|
return matches_share(prose, script, pattern) < MATCH_SHARE_FLOOR;
|
|
806
1527
|
}
|
|
807
1528
|
var reply_language = { is_mismatch, script_of };
|
|
808
1529
|
|
|
1530
|
+
// scripts/rules/stop-decision.ts
|
|
1531
|
+
var RECOMMENDED_LABEL = /^recommended[*_]*:/iu;
|
|
1532
|
+
var ALTERNATIVE_LABEL = /^alternative[*_]*:/iu;
|
|
1533
|
+
var LEADING_EMPHASIS = /^[*_]+/u;
|
|
1534
|
+
var TRAILING_EMPHASIS = /(?<![*_])[*_]+$/u;
|
|
1535
|
+
var BULLET = /^[-*]\s+/u;
|
|
1536
|
+
var QUESTION_ENDING = /(?:[??]|か|かな|かね)[\s。.]*$/u;
|
|
1537
|
+
function unwrapped(text2) {
|
|
1538
|
+
return text2.trim().replace(LEADING_EMPHASIS, "").replace(TRAILING_EMPHASIS, "").trim();
|
|
1539
|
+
}
|
|
1540
|
+
function bare_line(line) {
|
|
1541
|
+
return unwrapped(line.trim().replace(BULLET, ""));
|
|
1542
|
+
}
|
|
1543
|
+
function choice_of(message3, label) {
|
|
1544
|
+
const line = message3.split("\n").map((one) => bare_line(one)).find((one) => label.test(one));
|
|
1545
|
+
const choice = line === void 0 ? void 0 : unwrapped(line.replace(label, ""));
|
|
1546
|
+
return choice === "" ? void 0 : choice;
|
|
1547
|
+
}
|
|
1548
|
+
function decision_of(message3) {
|
|
1549
|
+
const recommended = choice_of(message3, RECOMMENDED_LABEL);
|
|
1550
|
+
if (recommended === void 0) return void 0;
|
|
1551
|
+
return { recommended, alternative: choice_of(message3, ALTERNATIVE_LABEL) };
|
|
1552
|
+
}
|
|
1553
|
+
function is_question(prompt) {
|
|
1554
|
+
return QUESTION_ENDING.test(prompt.trim());
|
|
1555
|
+
}
|
|
1556
|
+
var DECISION_REASON = "\u26D4 stop decision: this working tree is held by your run and your reply names no decision. If the run waits on a person, end the reply with a `Recommended: <choice>` line and, where there is one, an `Alternative: <choice>` line \u2014 the hook sends the `confirmation` Telegram from them. If it waits on nobody, continue the run, or say in one line that nothing is awaited. Do not repeat your previous reply.";
|
|
1557
|
+
var stop_decision = { DECISION_REASON, decision_of, is_question };
|
|
1558
|
+
|
|
809
1559
|
// scripts/rules/stop-rules.ts
|
|
810
1560
|
var SWITCH_ENV_KEY = "JOSH_STOP_GUARD";
|
|
811
1561
|
var BLOCK_DECISION = "block";
|
|
@@ -814,26 +1564,26 @@ var stop_payload_schema = z3.object({
|
|
|
814
1564
|
stop_hook_active: z3.boolean().nullish(),
|
|
815
1565
|
last_assistant_message: z3.string().nullish()
|
|
816
1566
|
});
|
|
817
|
-
var NO_OUTCOME = { reason: void 0 };
|
|
818
|
-
var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn
|
|
819
|
-
var HOLD_RELEASE_REASON = "\u26D4 working-tree hold not released: this working tree is clean but its `run:hold` record is still in place
|
|
1567
|
+
var NO_OUTCOME = { reason: void 0, actions: [], blocks_on_failure: false };
|
|
1568
|
+
var STOP_NOTIFY_REASON = '\u26D4 mid-workflow stop notification: this working tree is still held by a run and no `confirmation` Telegram was sent this turn. Send `pnpm josh notify --task-type confirmation --issue-url "<url>" --body=$\'<reason>\' --recommended "<choice>" --alternative "<choice>"` (`CLAUDE.md` \u2192 "Mid-workflow stop notification"), then confirm in one line that it was sent \u2014 do not repeat your previous reply.';
|
|
1569
|
+
var HOLD_RELEASE_REASON = "\u26D4 working-tree hold not released: this working tree is clean but its `run:hold` record is still in place. Release it with `pnpm josh run:release <N>` (bare for a `new` entry; `working-tree-hold.md`), then confirm in one line that it was released \u2014 do not repeat your previous reply.";
|
|
820
1570
|
var ISSUE_CITATION_REASON = "Session-facing output cites an Issue as a number-link so the reader can click through and see which repository it is (`CLAUDE.md`, `prompts/collaboration-workflow/issue-citation.md`).";
|
|
821
1571
|
function build_citation_reason(references) {
|
|
822
1572
|
const numbers = references.join(", ");
|
|
823
1573
|
const command = `pnpm josh issue:cite ${issue_citation.cite_arguments(references).join(" ")}`;
|
|
824
1574
|
return `\u26D4 issue citation: your reply names an Issue as a bare \`#N\` (${numbers}). Run \`${command}\` to print the paste-ready citation lines, then print the corrected citation lines alone \u2014 do not repeat your previous reply. ${ISSUE_CITATION_REASON}`;
|
|
825
1575
|
}
|
|
826
|
-
var FILING_OFFER_REASON = '\u26D4 filing offer: your reply offers to file an Issue instead of filing it, and this run is unattended \u2014 nobody is there to answer.
|
|
1576
|
+
var FILING_OFFER_REASON = '\u26D4 filing offer: your reply offers to file an Issue instead of filing it, and this run is unattended \u2014 nobody is there to answer. Run `pnpm josh issue:file "<title>" --body-file <path> --depth <0|1|2>` (Tier A, `observation-filing.md`), then end with the one-line citation of what was filed; do not repeat your previous reply. If it is not worth filing after all, say so in one line instead.';
|
|
827
1577
|
var HEADLESS_WAIT_MARKER = "\u26D4 headless parent:";
|
|
828
1578
|
var HEADLESS_REFUSAL_CAP = 3;
|
|
829
1579
|
var HEADLESS_WAIT_CALLS = ['"command":"pnpm josh lane:await'];
|
|
830
|
-
var HEADLESS_WAIT_BODY = ' this is a `claude -p` `backlogrun` parent
|
|
1580
|
+
var HEADLESS_WAIT_BODY = ' this is a `claude -p` `backlogrun` parent with lanes in flight, and ending this turn kills the process and its background waits. Hand the loop back with `pnpm josh run:carry --cut --owner "$PPID"` (or `--end` when the run is over), then end the turn (`backlogrun-progress.md`).';
|
|
831
1581
|
var HEADLESS_WAIT_REASON = `${HEADLESS_WAIT_MARKER}${HEADLESS_WAIT_BODY}`;
|
|
832
1582
|
var INDEX_COMMAND_PATTERN = /git (?:add|stage|rm --cached|restore --staged|restore -S)\b/u;
|
|
833
1583
|
var PERMISSION_PATTERN = /許可|承認|permission|approve|approval|authorization/iu;
|
|
834
|
-
var LANE_INDEX_REASON =
|
|
835
|
-
function asks_index_permission(
|
|
836
|
-
return INDEX_COMMAND_PATTERN.test(
|
|
1584
|
+
var LANE_INDEX_REASON = "\u26D4 lane index permission: this is a dispatched lane child (`JOSH_LANE_CHILD`) and your reply asks a person for permission to change the git index. That is Tier A here (`chain-rule.md`): remove any conflict markers, run `pnpm josh git -y` (it stages and commits, a conflicted merge included) and continue the run \u2014 do not repeat your previous reply.";
|
|
1585
|
+
function asks_index_permission(message3) {
|
|
1586
|
+
return INDEX_COMMAND_PATTERN.test(message3) && PERMISSION_PATTERN.test(message3);
|
|
837
1587
|
}
|
|
838
1588
|
function needs_index_route(context) {
|
|
839
1589
|
return context.lane_child && asks_index_permission(context.message);
|
|
@@ -856,26 +1606,29 @@ function needs_filing(context) {
|
|
|
856
1606
|
return filing_offer.offers_filing(context.message) && filing_offer.is_first_party_target(context.message, context.session_owner);
|
|
857
1607
|
}
|
|
858
1608
|
function is_held_idle(context) {
|
|
859
|
-
|
|
1609
|
+
if (context.hold_issue === void 0 || !context.hold_owned) return false;
|
|
1610
|
+
return !context.agent_pending && !context.handed_off;
|
|
860
1611
|
}
|
|
861
1612
|
function needs_notify(context) {
|
|
862
|
-
return is_held_idle(context) && !context.notified;
|
|
1613
|
+
return is_held_idle(context) && !context.notified && !context.answers_question;
|
|
1614
|
+
}
|
|
1615
|
+
function needs_decision(context) {
|
|
1616
|
+
return needs_notify(context) && stop_decision.decision_of(context.message) === void 0;
|
|
863
1617
|
}
|
|
864
1618
|
function needs_release(context) {
|
|
865
1619
|
return is_held_idle(context) && context.tree_clean && !context.prrun_stopped;
|
|
866
1620
|
}
|
|
867
|
-
function
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
return build_citation_reason(references);
|
|
871
|
-
}
|
|
872
|
-
function wording_reason(context) {
|
|
873
|
-
if (context.headless_agent) return void 0;
|
|
874
|
-
return citation_reason(context) ?? language_reason(context);
|
|
1621
|
+
function bare_references(context) {
|
|
1622
|
+
if (context.headless_agent) return [];
|
|
1623
|
+
return issue_citation.unquoted_references(context.message, context.prompt);
|
|
875
1624
|
}
|
|
876
1625
|
function reply_reason(context) {
|
|
877
1626
|
if (needs_filing(context)) return FILING_OFFER_REASON;
|
|
878
|
-
|
|
1627
|
+
if (context.headless_agent) return void 0;
|
|
1628
|
+
return language_reason(context);
|
|
1629
|
+
}
|
|
1630
|
+
function decision_reason(context) {
|
|
1631
|
+
return needs_decision(context) ? stop_decision.DECISION_REASON : void 0;
|
|
879
1632
|
}
|
|
880
1633
|
function is_headless_held(context) {
|
|
881
1634
|
if (!context.headless_waiting) return false;
|
|
@@ -888,23 +1641,61 @@ function count_headless_refusals(tail) {
|
|
|
888
1641
|
function needs_background_wait(context) {
|
|
889
1642
|
return context.lane_child && context.background_pending;
|
|
890
1643
|
}
|
|
891
|
-
function
|
|
1644
|
+
function lane_reason(context) {
|
|
892
1645
|
if (needs_index_route(context)) return LANE_INDEX_REASON;
|
|
893
1646
|
if (needs_background_wait(context)) return lane_background.LANE_BACKGROUND_STOP_REASON;
|
|
894
|
-
if (needs_notify(context)) return STOP_NOTIFY_REASON;
|
|
895
|
-
if (needs_release(context)) return HOLD_RELEASE_REASON;
|
|
896
1647
|
return void 0;
|
|
897
1648
|
}
|
|
1649
|
+
function model_reason(context) {
|
|
1650
|
+
const lane = lane_reason(context);
|
|
1651
|
+
if (lane !== void 0) return lane;
|
|
1652
|
+
const reasons = [reply_reason(context), decision_reason(context)].filter(
|
|
1653
|
+
(reason) => reason !== void 0
|
|
1654
|
+
);
|
|
1655
|
+
return reasons.length === 0 ? void 0 : reasons.join("\n\n");
|
|
1656
|
+
}
|
|
1657
|
+
function notify_actions(context, issue) {
|
|
1658
|
+
const decision = stop_decision.decision_of(context.message);
|
|
1659
|
+
if (decision === void 0 || !needs_notify(context)) return [];
|
|
1660
|
+
return [{ kind: "notify", issue, message: context.message, decision }];
|
|
1661
|
+
}
|
|
1662
|
+
function hold_actions(context) {
|
|
1663
|
+
const issue = context.hold_issue;
|
|
1664
|
+
if (issue === void 0) return [];
|
|
1665
|
+
const notify2 = notify_actions(context, issue);
|
|
1666
|
+
return needs_release(context) ? [...notify2, { kind: "release", issue }] : notify2;
|
|
1667
|
+
}
|
|
1668
|
+
function stop_actions(context) {
|
|
1669
|
+
const references = bare_references(context);
|
|
1670
|
+
const cite3 = references.length > 0 ? [{ kind: "cite", references }] : [];
|
|
1671
|
+
return [...hold_actions(context), ...cite3];
|
|
1672
|
+
}
|
|
898
1673
|
function block_reason(context) {
|
|
899
1674
|
if (is_headless_held(context)) return HEADLESS_WAIT_REASON;
|
|
900
1675
|
if (context.stop_hook_active || context.cut_pending) return void 0;
|
|
901
|
-
return
|
|
1676
|
+
return model_reason(context);
|
|
1677
|
+
}
|
|
1678
|
+
function runs_actions(context, reason) {
|
|
1679
|
+
return reason === void 0 && !context.cut_pending && lane_reason(context) === void 0;
|
|
902
1680
|
}
|
|
903
1681
|
function stop_outcome(context) {
|
|
904
|
-
|
|
1682
|
+
const reason = block_reason(context);
|
|
1683
|
+
if (!runs_actions(context, reason)) return { ...NO_OUTCOME, reason };
|
|
1684
|
+
const actions = stop_actions(context);
|
|
1685
|
+
if (actions.length === 0) return NO_OUTCOME;
|
|
1686
|
+
return { reason, actions, blocks_on_failure: !context.stop_hook_active };
|
|
905
1687
|
}
|
|
906
|
-
function
|
|
907
|
-
|
|
1688
|
+
function fallback_reason(action) {
|
|
1689
|
+
if (action.kind === "notify") return STOP_NOTIFY_REASON;
|
|
1690
|
+
if (action.kind === "release") return HOLD_RELEASE_REASON;
|
|
1691
|
+
return build_citation_reason(action.references);
|
|
1692
|
+
}
|
|
1693
|
+
function block_envelope(reason, lang, notice) {
|
|
1694
|
+
const refusal = { decision: BLOCK_DECISION, reason: `${reason} ${language_note(lang)}` };
|
|
1695
|
+
return JSON.stringify(notice === void 0 ? refusal : { ...refusal, systemMessage: notice });
|
|
1696
|
+
}
|
|
1697
|
+
function notice_envelope(notice) {
|
|
1698
|
+
return JSON.stringify({ systemMessage: notice });
|
|
908
1699
|
}
|
|
909
1700
|
function parse_stop_payload(raw_payload) {
|
|
910
1701
|
const parsed = stop_payload_schema.safeParse(JSON.parse(raw_payload));
|
|
@@ -926,32 +1717,51 @@ var stop_rules = {
|
|
|
926
1717
|
block_envelope,
|
|
927
1718
|
build_language_reason,
|
|
928
1719
|
count_headless_refusals,
|
|
1720
|
+
fallback_reason,
|
|
929
1721
|
is_enabled,
|
|
1722
|
+
notice_envelope,
|
|
930
1723
|
parse_stop_payload,
|
|
931
1724
|
stop_outcome
|
|
932
1725
|
};
|
|
933
1726
|
|
|
1727
|
+
// scripts/run/hold/run-hold-cli.ts
|
|
1728
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
1729
|
+
|
|
1730
|
+
// scripts/run/run-cli-fault.ts
|
|
1731
|
+
async function directory_of(where, read2) {
|
|
1732
|
+
try {
|
|
1733
|
+
return await read2();
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
error_text.trace_swallowed(where, error);
|
|
1736
|
+
return void 0;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
function message2(command, error) {
|
|
1740
|
+
return `${command} failed and established nothing: ${error_text.message_of(error)}`;
|
|
1741
|
+
}
|
|
1742
|
+
var run_cli_fault = { directory_of, message: message2 };
|
|
1743
|
+
|
|
934
1744
|
// scripts/run/run-halfrun-resume.ts
|
|
935
1745
|
var MARKED = "marked";
|
|
936
1746
|
var FOREIGN = "foreign";
|
|
937
1747
|
var UNREADABLE2 = "unreadable";
|
|
938
|
-
function hold_of(
|
|
939
|
-
return
|
|
1748
|
+
function hold_of(read2) {
|
|
1749
|
+
return read2.kind === "held" || read2.kind === "stale" ? read2.hold : void 0;
|
|
940
1750
|
}
|
|
941
|
-
function is_halfrun_stop(
|
|
942
|
-
const hold = hold_of(
|
|
1751
|
+
function is_halfrun_stop(read2, issue, is_dirty) {
|
|
1752
|
+
const hold = hold_of(read2);
|
|
943
1753
|
return is_dirty && hold?.issue === issue && hold.is_halfrun_stop === true;
|
|
944
1754
|
}
|
|
945
|
-
function is_own_record(
|
|
946
|
-
const hold = hold_of(
|
|
1755
|
+
function is_own_record(read2, issue) {
|
|
1756
|
+
const hold = hold_of(read2);
|
|
947
1757
|
return hold === void 0 || hold.issue === issue || hold.issue === run_hold.UNNUMBERED_ISSUE;
|
|
948
1758
|
}
|
|
949
|
-
function mark_stop_at(target, issue,
|
|
950
|
-
const
|
|
951
|
-
if (
|
|
952
|
-
if (!is_own_record(
|
|
1759
|
+
function mark_stop_at(target, issue, mark2 = run_hold.HALFRUN_STOP_FIELDS) {
|
|
1760
|
+
const read2 = run_hold.read_hold(target);
|
|
1761
|
+
if (read2.kind === "unreadable") return UNREADABLE2;
|
|
1762
|
+
if (!is_own_record(read2, issue)) return FOREIGN;
|
|
953
1763
|
run_hold.release_hold(target);
|
|
954
|
-
return run_hold.create_stop_hold(target, issue,
|
|
1764
|
+
return run_hold.create_stop_hold(target, issue, mark2) ? MARKED : FOREIGN;
|
|
955
1765
|
}
|
|
956
1766
|
function take_over(target, issue) {
|
|
957
1767
|
run_hold.release_hold(target);
|
|
@@ -992,59 +1802,1632 @@ var run_halfrun_resume = {
|
|
|
992
1802
|
take_over
|
|
993
1803
|
};
|
|
994
1804
|
|
|
995
|
-
// scripts/
|
|
996
|
-
import path from "node:path";
|
|
1805
|
+
// scripts/followup/git-followup-label.ts
|
|
997
1806
|
import { z as z4 } from "zod";
|
|
998
|
-
var
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
ended_at: z4.string(),
|
|
1004
|
-
stopped: z4.string().optional(),
|
|
1005
|
-
session: z4.string().optional()
|
|
1006
|
-
});
|
|
1007
|
-
function ended_path(git_directory) {
|
|
1008
|
-
return stamp_file.stamp_path(ENDED_PREFIX, git_directory);
|
|
1009
|
-
}
|
|
1010
|
-
function session_of(transcript) {
|
|
1011
|
-
return transcript === void 0 ? void 0 : path.basename(transcript, TRANSCRIPT_SUFFIX);
|
|
1807
|
+
var labels_read_schema = z4.object({ labels: z4.array(issue_label_schema).optional() });
|
|
1808
|
+
function unreadable_error(issue_number) {
|
|
1809
|
+
return new Error(
|
|
1810
|
+
`gh api answered no readable labels for issue ${session_cite.issue(issue_number)}`
|
|
1811
|
+
);
|
|
1012
1812
|
}
|
|
1013
|
-
function
|
|
1014
|
-
|
|
1813
|
+
async function read_label_names(issue_number) {
|
|
1814
|
+
const raw = await git_gh_command.issue_get_labels_and_body(issue_number);
|
|
1815
|
+
if (raw === void 0) throw unreadable_error(issue_number);
|
|
1816
|
+
const parsed = parse_json_object_or_undefined(raw, labels_read_schema);
|
|
1817
|
+
if (parsed === void 0) throw unreadable_error(issue_number);
|
|
1818
|
+
return (parsed.labels ?? []).map((label) => label.name);
|
|
1819
|
+
}
|
|
1820
|
+
async function strip_in_progress(issue_number) {
|
|
1821
|
+
if (issue_number === void 0) return;
|
|
1822
|
+
const stored = label_name_of(await read_label_names(issue_number), IN_PROGRESS_LABEL);
|
|
1823
|
+
if (stored === void 0) return;
|
|
1824
|
+
await git_gh_command.issue_remove_label(issue_number, stored);
|
|
1825
|
+
}
|
|
1826
|
+
var git_followup_label = {
|
|
1827
|
+
strip_in_progress
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
// scripts/run/run-label.ts
|
|
1831
|
+
var MARK_RECOVERY = "gh api repos/{owner}/{repo}/issues/<N>/labels -f 'labels[]=in-progress'";
|
|
1832
|
+
var UNMARK_RECOVERY = "gh api -X DELETE repos/{owner}/{repo}/issues/<N>/labels/in-progress";
|
|
1833
|
+
function warn2(action, issue, recovery) {
|
|
1834
|
+
console.warn(
|
|
1835
|
+
`\u26A0 could not ${action} \`in-progress\` on ${session_cite.issue(issue)} \u2014 run by hand: ${recovery}`
|
|
1836
|
+
);
|
|
1015
1837
|
}
|
|
1016
|
-
function
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
stamp_file.replace_stamp(target, {
|
|
1021
|
-
invocation,
|
|
1022
|
-
started_at,
|
|
1023
|
-
ended_at,
|
|
1024
|
-
...stop_of(owner_transcript, stopped)
|
|
1025
|
-
});
|
|
1838
|
+
async function mark(issue) {
|
|
1839
|
+
const is_marked = await git_gh_command.issue_add_label(issue, IN_PROGRESS_LABEL);
|
|
1840
|
+
if (!is_marked) warn2("apply", issue, MARK_RECOVERY);
|
|
1841
|
+
return is_marked;
|
|
1026
1842
|
}
|
|
1027
|
-
function
|
|
1028
|
-
|
|
1843
|
+
async function unmark(issue) {
|
|
1844
|
+
try {
|
|
1845
|
+
await git_followup_label.strip_in_progress(issue);
|
|
1846
|
+
return true;
|
|
1847
|
+
} catch {
|
|
1848
|
+
warn2("remove", issue, UNMARK_RECOVERY);
|
|
1849
|
+
return false;
|
|
1850
|
+
}
|
|
1029
1851
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1852
|
+
var run_label = {
|
|
1853
|
+
mark,
|
|
1854
|
+
unmark
|
|
1855
|
+
};
|
|
1856
|
+
|
|
1857
|
+
// scripts/lib/git-animation.ts
|
|
1858
|
+
import ora from "ora";
|
|
1859
|
+
var SUCCESS_ICON = "\u2705";
|
|
1860
|
+
function create_stop_function(spinner, message3) {
|
|
1861
|
+
return (result, icon) => {
|
|
1862
|
+
if (result === void 0) {
|
|
1863
|
+
spinner.stop();
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
const symbol = (icon ?? SUCCESS_ICON).trimEnd();
|
|
1867
|
+
spinner.stopAndPersist({ symbol, text: `${message3} ${result}` });
|
|
1868
|
+
};
|
|
1033
1869
|
}
|
|
1034
|
-
function
|
|
1035
|
-
const
|
|
1036
|
-
return
|
|
1870
|
+
function create_animation(message3) {
|
|
1871
|
+
const spinner = ora({ text: message3 }).start();
|
|
1872
|
+
return {
|
|
1873
|
+
stop: create_stop_function(spinner, message3),
|
|
1874
|
+
pause: () => {
|
|
1875
|
+
spinner.stop();
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1037
1878
|
}
|
|
1038
|
-
var
|
|
1879
|
+
var git_animation = {
|
|
1880
|
+
create_animation
|
|
1881
|
+
};
|
|
1039
1882
|
|
|
1040
|
-
// scripts/
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1883
|
+
// scripts/lib/animation-helpers.ts
|
|
1884
|
+
function get_error_message(error_message = "Operation failed") {
|
|
1885
|
+
return error_message;
|
|
1886
|
+
}
|
|
1887
|
+
function get_display_result(result, formatter) {
|
|
1888
|
+
return formatter?.(result) ?? String(result);
|
|
1889
|
+
}
|
|
1890
|
+
function handle_animation_success(animation, result, options) {
|
|
1891
|
+
const icon = options?.icon_selector?.(result);
|
|
1892
|
+
const display_result = get_display_result(result, options?.result_formatter);
|
|
1893
|
+
animation.stop(display_result, icon);
|
|
1894
|
+
}
|
|
1895
|
+
function handle_animation_error(animation, error, error_message) {
|
|
1896
|
+
animation.stop();
|
|
1897
|
+
if (error_message === void 0 || error_message.length === 0) {
|
|
1898
|
+
if (error instanceof Error) {
|
|
1899
|
+
throw error;
|
|
1900
|
+
}
|
|
1901
|
+
throw new Error(String(error), { cause: error });
|
|
1902
|
+
}
|
|
1903
|
+
throw new Error(get_error_message(error_message), { cause: error });
|
|
1904
|
+
}
|
|
1905
|
+
async function execute_with_animation(message3, command_executor, options) {
|
|
1906
|
+
const animation = git_animation.create_animation(message3);
|
|
1907
|
+
try {
|
|
1908
|
+
const result = await command_executor(() => {
|
|
1909
|
+
animation.pause();
|
|
1910
|
+
});
|
|
1911
|
+
handle_animation_success(animation, result, options);
|
|
1912
|
+
return result;
|
|
1913
|
+
} catch (error) {
|
|
1914
|
+
return handle_animation_error(animation, error, options?.error_message);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
function create_git_operation_config(error_message) {
|
|
1918
|
+
return {
|
|
1919
|
+
error_message,
|
|
1920
|
+
icon_selector: () => "\u2705",
|
|
1921
|
+
result_formatter: (message3) => message3
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
var animation_helpers = {
|
|
1925
|
+
execute_with_animation
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
// scripts/git/git-branch.ts
|
|
1929
|
+
async function current() {
|
|
1930
|
+
const config = {
|
|
1931
|
+
error_message: "Failed to get current branch"
|
|
1932
|
+
};
|
|
1933
|
+
return await animation_helpers.execute_with_animation(
|
|
1934
|
+
"Getting current branch...",
|
|
1935
|
+
git_command.branch,
|
|
1936
|
+
config
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
async function create(branch_name) {
|
|
1940
|
+
const config = create_git_operation_config("Failed to create branch");
|
|
1941
|
+
await animation_helpers.execute_with_animation(
|
|
1942
|
+
`Creating branch: ${branch_name}...`,
|
|
1943
|
+
async () => {
|
|
1944
|
+
await git_command.checkout_b(branch_name);
|
|
1945
|
+
return `Branch created: ${branch_name}`;
|
|
1946
|
+
},
|
|
1947
|
+
config
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
async function switch_to(branch_name) {
|
|
1951
|
+
const config = create_git_operation_config("Failed to switch branch");
|
|
1952
|
+
await animation_helpers.execute_with_animation(
|
|
1953
|
+
`Switching to branch: ${branch_name}...`,
|
|
1954
|
+
async () => {
|
|
1955
|
+
await git_command.checkout(branch_name);
|
|
1956
|
+
return `Switched to branch: ${branch_name}`;
|
|
1957
|
+
},
|
|
1958
|
+
config
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
async function exists(branch_name) {
|
|
1962
|
+
const config = {
|
|
1963
|
+
error_message: "Failed to check branch existence",
|
|
1964
|
+
result_formatter: (is_exists) => is_exists ? "Exists" : "Not found"
|
|
1965
|
+
};
|
|
1966
|
+
return await animation_helpers.execute_with_animation(
|
|
1967
|
+
"Checking if branch exists...",
|
|
1968
|
+
async () => {
|
|
1969
|
+
return await git_command.branch_exists(branch_name);
|
|
1970
|
+
},
|
|
1971
|
+
config
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
async function pull_latest() {
|
|
1975
|
+
const config = create_git_operation_config("Failed to pull latest from remote");
|
|
1976
|
+
await animation_helpers.execute_with_animation(
|
|
1977
|
+
"Pulling latest from remote...",
|
|
1978
|
+
async () => {
|
|
1979
|
+
await git_command.pull_fast_forward();
|
|
1980
|
+
return "Pulled latest from remote";
|
|
1981
|
+
},
|
|
1982
|
+
config
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
var ISSUE_PREFIX_PATTERN = /^(\d+)-/u;
|
|
1986
|
+
function has_same_issue_prefix(branch_a, branch_b) {
|
|
1987
|
+
const prefix_a = ISSUE_PREFIX_PATTERN.exec(branch_a)?.[0];
|
|
1988
|
+
const prefix_b = ISSUE_PREFIX_PATTERN.exec(branch_b)?.[0];
|
|
1989
|
+
return prefix_a !== void 0 && prefix_a === prefix_b;
|
|
1990
|
+
}
|
|
1991
|
+
function issue_from_branch(branch) {
|
|
1992
|
+
const digits = ISSUE_PREFIX_PATTERN.exec(branch)?.[1];
|
|
1993
|
+
return digits === void 0 ? void 0 : Number(digits);
|
|
1994
|
+
}
|
|
1995
|
+
function is_mismatch2(current_branch2, target_branch_name, default_branch) {
|
|
1996
|
+
if (current_branch2 === default_branch || current_branch2 === target_branch_name) return false;
|
|
1997
|
+
return !has_same_issue_prefix(current_branch2, target_branch_name);
|
|
1998
|
+
}
|
|
1999
|
+
async function handle_default_branch(target_branch_name) {
|
|
2000
|
+
await pull_latest();
|
|
2001
|
+
const is_branch_exists = await exists(target_branch_name);
|
|
2002
|
+
await (is_branch_exists ? switch_to : create)(target_branch_name);
|
|
2003
|
+
}
|
|
2004
|
+
async function check_and_create_branch(current_branch2, target_branch_name) {
|
|
2005
|
+
const default_branch = await git_command.get_default_branch();
|
|
2006
|
+
if (current_branch2 === default_branch) {
|
|
2007
|
+
await handle_default_branch(target_branch_name);
|
|
2008
|
+
return target_branch_name;
|
|
2009
|
+
}
|
|
2010
|
+
if (is_mismatch2(current_branch2, target_branch_name, default_branch)) {
|
|
2011
|
+
throw new BranchMismatchError({ current_branch: current_branch2, target_branch_name });
|
|
2012
|
+
}
|
|
2013
|
+
return current_branch2;
|
|
2014
|
+
}
|
|
2015
|
+
var git_branch = {
|
|
2016
|
+
current,
|
|
2017
|
+
check_and_create_branch,
|
|
2018
|
+
is_mismatch: is_mismatch2,
|
|
2019
|
+
// Exported so a lane's branch name can be checked against the real matcher rather than against a
|
|
2020
|
+
// second copy of the pattern. A copy is what let `lane/<N>` ship: nothing
|
|
2021
|
+
// asserted that the name `lane:open` writes is one this function accepts.
|
|
2022
|
+
has_same_issue_prefix,
|
|
2023
|
+
issue_from_branch
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
// scripts/run/run-prrun-resume.ts
|
|
2027
|
+
var PRRUN_MERGED = "prrun-merged";
|
|
2028
|
+
var PRRUN_MERGE = "prrun-merge";
|
|
2029
|
+
var PRRUN_GATE = "prrun-gate";
|
|
2030
|
+
function is_untouched(facts) {
|
|
2031
|
+
return facts.head === facts.stop_head && facts.pr_head === facts.stop_head && !facts.is_dirty;
|
|
2032
|
+
}
|
|
2033
|
+
function decide(facts) {
|
|
2034
|
+
if (facts.is_merged) return PRRUN_MERGED;
|
|
2035
|
+
return is_untouched(facts) ? PRRUN_MERGE : PRRUN_GATE;
|
|
2036
|
+
}
|
|
2037
|
+
function stop_head_of(read2, issue) {
|
|
2038
|
+
const hold = run_halfrun_resume.hold_of(read2);
|
|
2039
|
+
return hold?.issue === issue ? hold.prrun_stop_head : void 0;
|
|
2040
|
+
}
|
|
2041
|
+
async function read_merge_state() {
|
|
2042
|
+
return await git_gh_command.pr_get_merge_state(await git_branch.current());
|
|
2043
|
+
}
|
|
2044
|
+
async function read_facts(stop_head) {
|
|
2045
|
+
const [state, head, is_dirty] = await Promise.all([
|
|
2046
|
+
read_merge_state(),
|
|
2047
|
+
git_command.head_commit(),
|
|
2048
|
+
run_hold.is_tree_dirty()
|
|
2049
|
+
]);
|
|
2050
|
+
return {
|
|
2051
|
+
is_merged: state?.is_merged === true,
|
|
2052
|
+
stop_head,
|
|
2053
|
+
head,
|
|
2054
|
+
pr_head: state?.head_sha,
|
|
2055
|
+
is_dirty
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
async function resume_token(issue) {
|
|
2059
|
+
const target = await run_halfrun_resume.hold_target();
|
|
2060
|
+
if (target === void 0) return void 0;
|
|
2061
|
+
const stop_head = stop_head_of(run_hold.read_hold(target), issue);
|
|
2062
|
+
return stop_head === void 0 ? void 0 : decide(await read_facts(stop_head));
|
|
2063
|
+
}
|
|
2064
|
+
async function adopt2(issue) {
|
|
2065
|
+
const target = await run_halfrun_resume.hold_target();
|
|
2066
|
+
if (target === void 0 || stop_head_of(run_hold.read_hold(target), issue) === void 0) {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
return run_halfrun_resume.take_over(target, issue);
|
|
2070
|
+
}
|
|
2071
|
+
async function mark_stop_at2(target, issue) {
|
|
2072
|
+
const head = await git_command.head_commit();
|
|
2073
|
+
return run_halfrun_resume.mark_stop_at(target, issue, { prrun_stop_head: head });
|
|
2074
|
+
}
|
|
2075
|
+
var run_prrun_resume = {
|
|
2076
|
+
PRRUN_GATE,
|
|
2077
|
+
PRRUN_MERGE,
|
|
2078
|
+
PRRUN_MERGED,
|
|
2079
|
+
adopt: adopt2,
|
|
2080
|
+
decide,
|
|
2081
|
+
mark_stop_at: mark_stop_at2,
|
|
2082
|
+
resume_token,
|
|
2083
|
+
stop_head_of
|
|
2084
|
+
};
|
|
2085
|
+
|
|
2086
|
+
// scripts/run/tidy/run-tidy-cli.ts
|
|
2087
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2088
|
+
|
|
2089
|
+
// scripts/issue/issue-merged.ts
|
|
2090
|
+
var CLOSED_EVENT = "closed";
|
|
2091
|
+
var REOPENED_EVENT = "reopened";
|
|
2092
|
+
var MERGED_REFERENCE = "merged-reference";
|
|
2093
|
+
var COMPLETED_REASONS = /* @__PURE__ */ new Set(["", "completed"]);
|
|
2094
|
+
var FIELD_SEPARATOR = " ";
|
|
2095
|
+
var LINE_SEPARATOR2 = "\n";
|
|
2096
|
+
var TIMELINE_FILTER = [
|
|
2097
|
+
".[]",
|
|
2098
|
+
'| if .event == "cross-referenced" and .source.issue.pull_request.merged_at != null',
|
|
2099
|
+
String.raw`then "${MERGED_REFERENCE}\t"`,
|
|
2100
|
+
`elif .event == "${CLOSED_EVENT}" or .event == "${REOPENED_EVENT}"`,
|
|
2101
|
+
String.raw`then "\(.event)\t\(.state_reason // "")"`,
|
|
2102
|
+
"else empty end"
|
|
2103
|
+
].join(" ");
|
|
2104
|
+
function parse_rows(raw) {
|
|
2105
|
+
return raw.split(LINE_SEPARATOR2).filter((line) => line !== "").map((line) => {
|
|
2106
|
+
const [event = "", reason = ""] = line.split(FIELD_SEPARATOR);
|
|
2107
|
+
return { event, reason };
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
function is_merged_close(rows) {
|
|
2111
|
+
const last = rows.findLast((row) => row.event === CLOSED_EVENT || row.event === REOPENED_EVENT);
|
|
2112
|
+
if (last?.event !== CLOSED_EVENT || !COMPLETED_REASONS.has(last.reason)) return false;
|
|
2113
|
+
return rows.some((row) => row.event === MERGED_REFERENCE);
|
|
2114
|
+
}
|
|
2115
|
+
async function read_merge_state2(issue) {
|
|
2116
|
+
try {
|
|
2117
|
+
const raw = await git_gh_exec.exec_gh_api({
|
|
2118
|
+
path: `${git_gh_api_path.issue_api_path(issue)}/timeline?per_page=100`,
|
|
2119
|
+
should_paginate: true,
|
|
2120
|
+
jq_filter: TIMELINE_FILTER
|
|
2121
|
+
});
|
|
2122
|
+
return is_merged_close(parse_rows(raw));
|
|
2123
|
+
} catch {
|
|
2124
|
+
return void 0;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async function read_merged(issue) {
|
|
2128
|
+
return await read_merge_state2(issue) ?? false;
|
|
2129
|
+
}
|
|
2130
|
+
var issue_merged = { is_merged_close, parse_rows, read_merge_state: read_merge_state2, read_merged };
|
|
2131
|
+
|
|
2132
|
+
// scripts/observations/observation-ledger-line.ts
|
|
2133
|
+
var KEY_PREFIX = "- k:";
|
|
2134
|
+
var FIELD_SEPARATOR2 = " | ";
|
|
2135
|
+
var FIELD_COUNT = 5;
|
|
2136
|
+
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
2137
|
+
var DEPTH_PATTERN = /^d[1-9]\d*$/u;
|
|
2138
|
+
var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
2139
|
+
var SLUG_REASON = "the slug must be lowercase words joined by `-`";
|
|
2140
|
+
var DEPTH_REASON = "the depth must be `d1` or higher (never `d0`)";
|
|
2141
|
+
var DATE_REASON = "the date must be `YYYY-MM-DD`";
|
|
2142
|
+
var WHERE_REASON = "the `where` field is empty";
|
|
2143
|
+
var WHAT_REASON = "the `what` field is empty";
|
|
2144
|
+
function is_ledger_entry_line(line) {
|
|
2145
|
+
return line.trimStart().startsWith(KEY_PREFIX);
|
|
2146
|
+
}
|
|
2147
|
+
function slug_of(first_field) {
|
|
2148
|
+
return first_field.trimStart().slice(KEY_PREFIX.length);
|
|
2149
|
+
}
|
|
2150
|
+
function is_present(field = "") {
|
|
2151
|
+
return field.trim().length > 0;
|
|
2152
|
+
}
|
|
2153
|
+
function matches(pattern, field = "") {
|
|
2154
|
+
return pattern.test(field);
|
|
2155
|
+
}
|
|
2156
|
+
function slug_ok(key_field = "") {
|
|
2157
|
+
return SLUG_PATTERN.test(slug_of(key_field));
|
|
2158
|
+
}
|
|
2159
|
+
function field_reason(fields) {
|
|
2160
|
+
const [key_field, depth, date, where, what] = fields;
|
|
2161
|
+
const checks = [
|
|
2162
|
+
[slug_ok(key_field), SLUG_REASON],
|
|
2163
|
+
[matches(DEPTH_PATTERN, depth), DEPTH_REASON],
|
|
2164
|
+
[matches(DATE_PATTERN, date), DATE_REASON],
|
|
2165
|
+
[is_present(where), WHERE_REASON],
|
|
2166
|
+
[is_present(what), WHAT_REASON]
|
|
2167
|
+
];
|
|
2168
|
+
return checks.find(([is_valid2]) => !is_valid2)?.[1];
|
|
2169
|
+
}
|
|
2170
|
+
function line_reason(line) {
|
|
2171
|
+
const fields = line.split(FIELD_SEPARATOR2);
|
|
2172
|
+
if (fields.length !== FIELD_COUNT) {
|
|
2173
|
+
return `expected ${String(FIELD_COUNT)} fields separated by " | " (a \`|\` in the last field is not allowed)`;
|
|
2174
|
+
}
|
|
2175
|
+
return field_reason(fields);
|
|
2176
|
+
}
|
|
2177
|
+
function broken_ledger_lines(content) {
|
|
2178
|
+
return content.split("\n").filter((line) => is_ledger_entry_line(line)).map((line) => ({ line, reason: line_reason(line) })).filter((entry) => entry.reason !== void 0);
|
|
2179
|
+
}
|
|
2180
|
+
function missing_lines(candidates, ledger) {
|
|
2181
|
+
const present = new Set(ledger.split("\n"));
|
|
2182
|
+
return candidates.filter((line) => !present.has(line));
|
|
2183
|
+
}
|
|
2184
|
+
var observation_ledger_line = {
|
|
2185
|
+
broken_ledger_lines,
|
|
2186
|
+
is_ledger_entry_line,
|
|
2187
|
+
line_reason,
|
|
2188
|
+
missing_lines
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
// scripts/run/tidy/run-tidy.ts
|
|
2192
|
+
var LINE_SEPARATOR3 = "\n";
|
|
2193
|
+
var CLEANED_HEADER = "run:tidy \u2014 cleaned:";
|
|
2194
|
+
var KEPT_HEADER = "run:tidy \u2014 kept:";
|
|
2195
|
+
var CHANGES_REASON = "uncommitted changes";
|
|
2196
|
+
var UNPUSHED_REASON = "commits no remote has";
|
|
2197
|
+
var RUNNING_REASON = "held by a live run";
|
|
2198
|
+
var CLEAN = { kind: "clean" };
|
|
2199
|
+
function keep(reason) {
|
|
2200
|
+
return { kind: "keep", reason };
|
|
2201
|
+
}
|
|
2202
|
+
function lane_verdict(facts) {
|
|
2203
|
+
if (!facts.is_merged) return void 0;
|
|
2204
|
+
if (facts.has_changes) return keep(CHANGES_REASON);
|
|
2205
|
+
if (facts.has_unpushed) return keep(UNPUSHED_REASON);
|
|
2206
|
+
return facts.is_running ? keep(RUNNING_REASON) : CLEAN;
|
|
2207
|
+
}
|
|
2208
|
+
function unmerged_reason(unmerged) {
|
|
2209
|
+
const named = unmerged.map((issue) => session_cite.issue(issue)).join(", ");
|
|
2210
|
+
return `${named} not merged`;
|
|
2211
|
+
}
|
|
2212
|
+
function stash_verdict(facts, merged) {
|
|
2213
|
+
const unmerged = facts.issues.filter((issue) => !merged.has(issue));
|
|
2214
|
+
if (unmerged.length === facts.issues.length) return void 0;
|
|
2215
|
+
return unmerged.length === 0 ? CLEAN : keep(unmerged_reason(unmerged));
|
|
2216
|
+
}
|
|
2217
|
+
function ledger_carry(added, ledger) {
|
|
2218
|
+
const entries = added.filter((line) => observation_ledger_line.is_ledger_entry_line(line));
|
|
2219
|
+
return observation_ledger_line.missing_lines([...new Set(entries)], ledger);
|
|
2220
|
+
}
|
|
2221
|
+
function format_outcome(outcome) {
|
|
2222
|
+
const suffix = outcome.verdict.kind === "keep" ? ` \u2014 ${outcome.verdict.reason}` : "";
|
|
2223
|
+
return ` ${outcome.target}${suffix}`;
|
|
2224
|
+
}
|
|
2225
|
+
function section(header, outcomes) {
|
|
2226
|
+
return outcomes.length === 0 ? [] : [header, ...outcomes.map((item) => format_outcome(item))];
|
|
2227
|
+
}
|
|
2228
|
+
function format_report(outcomes) {
|
|
2229
|
+
const cleaned = outcomes.filter((outcome) => outcome.verdict.kind === "clean");
|
|
2230
|
+
const kept = outcomes.filter((outcome) => outcome.verdict.kind === "keep");
|
|
2231
|
+
const lines = [...section(CLEANED_HEADER, cleaned), ...section(KEPT_HEADER, kept)];
|
|
2232
|
+
return lines.length === 0 ? void 0 : lines.join(LINE_SEPARATOR3);
|
|
2233
|
+
}
|
|
2234
|
+
var run_tidy = { CLEAN, format_report, keep, lane_verdict, ledger_carry, stash_verdict };
|
|
2235
|
+
|
|
2236
|
+
// scripts/git/stash/git-stash.ts
|
|
2237
|
+
var ENTRY_SEPARATOR = "\n";
|
|
2238
|
+
var FIELD_SEPARATOR3 = "\0";
|
|
2239
|
+
var LIST_FORMAT = "--format=%gd%x00%gs";
|
|
2240
|
+
var SUBJECT_MESSAGE_SEPARATOR = ": ";
|
|
2241
|
+
var HASH_FORMAT = "--format=%H%x00%gs";
|
|
2242
|
+
var HASH_POSITION_FORMAT = "--format=%H%x00%gd";
|
|
2243
|
+
var NAME_ONLY_FLAG = "--name-only";
|
|
2244
|
+
var ADDED_PREFIX = "+";
|
|
2245
|
+
var FILE_HEADER_PREFIX = "+++";
|
|
2246
|
+
function to_entry(line) {
|
|
2247
|
+
const [selector, subject] = line.split(FIELD_SEPARATOR3);
|
|
2248
|
+
if (selector === void 0 || subject === void 0) return void 0;
|
|
2249
|
+
return { selector, subject };
|
|
2250
|
+
}
|
|
2251
|
+
function parse_entries(raw) {
|
|
2252
|
+
if (raw === "") return [];
|
|
2253
|
+
return raw.split(ENTRY_SEPARATOR).map((line) => to_entry(line)).filter((entry) => entry !== void 0);
|
|
2254
|
+
}
|
|
2255
|
+
function matches2(subject, message3) {
|
|
2256
|
+
return subject === message3 || subject.endsWith(`${SUBJECT_MESSAGE_SEPARATOR}${message3}`);
|
|
2257
|
+
}
|
|
2258
|
+
function select(entries, message3) {
|
|
2259
|
+
const found = entries.filter((entry) => matches2(entry.subject, message3));
|
|
2260
|
+
const [first] = found;
|
|
2261
|
+
if (first === void 0) return { kind: "none" };
|
|
2262
|
+
if (found.length > 1) {
|
|
2263
|
+
return { kind: "ambiguous", selectors: found.map((entry) => entry.selector) };
|
|
2264
|
+
}
|
|
2265
|
+
return { kind: "match", selector: first.selector };
|
|
2266
|
+
}
|
|
2267
|
+
function git_args(directory, args) {
|
|
2268
|
+
return directory === void 0 ? args : ["-C", directory, ...args];
|
|
2269
|
+
}
|
|
2270
|
+
async function list(directory) {
|
|
2271
|
+
return parse_entries(await git_spawn.read(git_args(directory, ["stash", "list", LIST_FORMAT])));
|
|
2272
|
+
}
|
|
2273
|
+
async function pop(selector, directory) {
|
|
2274
|
+
await git_spawn.read(git_args(directory, ["stash", "pop", selector]));
|
|
2275
|
+
}
|
|
2276
|
+
async function has_conflict(directory) {
|
|
2277
|
+
const unmerged = await git_spawn.read(
|
|
2278
|
+
git_args(directory, ["diff", NAME_ONLY_FLAG, "--diff-filter=U"])
|
|
2279
|
+
);
|
|
2280
|
+
return unmerged !== "";
|
|
2281
|
+
}
|
|
2282
|
+
async function has_changes(directory) {
|
|
2283
|
+
return await git_spawn.read(git_args(directory, ["status", "--porcelain"])) !== "";
|
|
2284
|
+
}
|
|
2285
|
+
async function push(message3, directory) {
|
|
2286
|
+
await git_spawn.read(git_args(directory, ["stash", "push", "-u", "-m", message3]));
|
|
2287
|
+
}
|
|
2288
|
+
async function list_by_hash(directory) {
|
|
2289
|
+
return parse_entries(await git_spawn.read(git_args(directory, ["stash", "list", HASH_FORMAT])));
|
|
2290
|
+
}
|
|
2291
|
+
async function changed_paths(hash, directory) {
|
|
2292
|
+
const raw = await git_spawn.read(
|
|
2293
|
+
git_args(directory, ["stash", "show", "--include-untracked", NAME_ONLY_FLAG, hash])
|
|
2294
|
+
);
|
|
2295
|
+
return raw === "" ? [] : raw.split(ENTRY_SEPARATOR);
|
|
2296
|
+
}
|
|
2297
|
+
async function untracked_lines(hash, file, directory) {
|
|
2298
|
+
try {
|
|
2299
|
+
const raw = await git_spawn.read(git_args(directory, ["show", `${hash}^3:${file}`]));
|
|
2300
|
+
return raw === "" ? [] : raw.split(ENTRY_SEPARATOR);
|
|
2301
|
+
} catch {
|
|
2302
|
+
return [];
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
async function added_lines(hash, file, directory) {
|
|
2306
|
+
const diff = await git_spawn.read(git_args(directory, ["diff", `${hash}^1`, hash, "--", file]));
|
|
2307
|
+
if (diff === "") return await untracked_lines(hash, file, directory);
|
|
2308
|
+
return diff.split(ENTRY_SEPARATOR).filter((line) => line.startsWith(ADDED_PREFIX) && !line.startsWith(FILE_HEADER_PREFIX)).map((line) => line.slice(ADDED_PREFIX.length));
|
|
2309
|
+
}
|
|
2310
|
+
async function drop_by_hash(hash, directory) {
|
|
2311
|
+
const raw = await git_spawn.read(git_args(directory, ["stash", "list", HASH_POSITION_FORMAT]));
|
|
2312
|
+
const entry = parse_entries(raw).find((candidate) => candidate.selector === hash);
|
|
2313
|
+
if (entry === void 0) return false;
|
|
2314
|
+
await git_spawn.read(git_args(directory, ["stash", "drop", entry.subject]));
|
|
2315
|
+
return true;
|
|
2316
|
+
}
|
|
2317
|
+
function work_message(issue, occasion) {
|
|
2318
|
+
return `${issue}: uncommitted work at ${occasion}`;
|
|
2319
|
+
}
|
|
2320
|
+
var git_stash = {
|
|
2321
|
+
added_lines,
|
|
2322
|
+
changed_paths,
|
|
2323
|
+
drop_by_hash,
|
|
2324
|
+
has_changes,
|
|
2325
|
+
has_conflict,
|
|
2326
|
+
list,
|
|
2327
|
+
list_by_hash,
|
|
2328
|
+
matches: matches2,
|
|
2329
|
+
parse_entries,
|
|
2330
|
+
pop,
|
|
2331
|
+
push,
|
|
2332
|
+
select,
|
|
2333
|
+
work_message
|
|
2334
|
+
};
|
|
2335
|
+
|
|
2336
|
+
// scripts/lane/lane-close.ts
|
|
2337
|
+
import { existsSync, rmSync } from "node:fs";
|
|
2338
|
+
async function ignore_failure(action) {
|
|
2339
|
+
try {
|
|
2340
|
+
await action();
|
|
2341
|
+
} catch {
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
function lane_targets(root, issue, lane) {
|
|
2345
|
+
return {
|
|
2346
|
+
directory: lane?.directory ?? lane_paths.lane_directory(root, issue),
|
|
2347
|
+
branch: lane?.branch ?? lane_paths.lane_branch(issue)
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
function remove_directory(directory) {
|
|
2351
|
+
try {
|
|
2352
|
+
rmSync(directory, { force: true, recursive: true });
|
|
2353
|
+
} catch {
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
async function remove_lane(targets) {
|
|
2357
|
+
await ignore_failure(async () => await git_worktree.worktree_remove(targets.directory));
|
|
2358
|
+
remove_directory(targets.directory);
|
|
2359
|
+
await ignore_failure(async () => await git_worktree.worktree_prune());
|
|
2360
|
+
await ignore_failure(async () => await git_worktree.branch_delete(targets.branch));
|
|
2361
|
+
}
|
|
2362
|
+
async function residue(targets) {
|
|
2363
|
+
const left = [];
|
|
2364
|
+
if (existsSync(targets.directory)) left.push(targets.directory);
|
|
2365
|
+
if (await git_command.branch_exists(targets.branch)) left.push(targets.branch);
|
|
2366
|
+
return left;
|
|
2367
|
+
}
|
|
2368
|
+
function close_kind(did_exist, left_behind) {
|
|
2369
|
+
if (left_behind.length > 0) return "incomplete";
|
|
2370
|
+
return did_exist ? "closed" : "none";
|
|
2371
|
+
}
|
|
2372
|
+
async function lane_root_directory() {
|
|
2373
|
+
return lane_paths.lane_root(await lane_registry.main_repository_root());
|
|
2374
|
+
}
|
|
2375
|
+
async function resolve_lane(issue) {
|
|
2376
|
+
const root = await lane_root_directory();
|
|
2377
|
+
const existing = lane_registry.find_lane(await lane_registry.list_lanes(), issue);
|
|
2378
|
+
return { existing, targets: lane_targets(root, issue, existing) };
|
|
2379
|
+
}
|
|
2380
|
+
async function close_lane(issue) {
|
|
2381
|
+
const { existing, targets } = await resolve_lane(issue);
|
|
2382
|
+
const did_exist = existing !== void 0 || existsSync(targets.directory);
|
|
2383
|
+
const reaped = lane_reap.reap_child(issue);
|
|
2384
|
+
await remove_lane(targets);
|
|
2385
|
+
const left_behind = await residue(targets);
|
|
2386
|
+
return { issue, kind: close_kind(did_exist, left_behind), left_behind, reaped };
|
|
2387
|
+
}
|
|
2388
|
+
async function try_close(issue) {
|
|
2389
|
+
try {
|
|
2390
|
+
const outcome = await close_lane(issue);
|
|
2391
|
+
return outcome.kind !== "incomplete";
|
|
2392
|
+
} catch (error) {
|
|
2393
|
+
error_text.trace_swallowed("lane_close.try_close", error);
|
|
2394
|
+
return false;
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
async function close_each(issues) {
|
|
2398
|
+
const sweep2 = { closed: [], failed: [] };
|
|
2399
|
+
for (const issue of issues) {
|
|
2400
|
+
const bucket = await try_close(issue) ? sweep2.closed : sweep2.failed;
|
|
2401
|
+
bucket.push(issue);
|
|
2402
|
+
}
|
|
2403
|
+
return sweep2;
|
|
2404
|
+
}
|
|
2405
|
+
async function prune_lanes() {
|
|
2406
|
+
const lanes = await lane_registry.list_lanes();
|
|
2407
|
+
const { spent, kept } = await lane_spent.sort_lanes(lanes);
|
|
2408
|
+
const doomed = lanes.filter((lane) => lane.is_stranded || spent.includes(lane));
|
|
2409
|
+
const sweep2 = await close_each(doomed.map((lane) => lane.issue));
|
|
2410
|
+
return { ...sweep2, kept };
|
|
2411
|
+
}
|
|
2412
|
+
async function close_all_lanes() {
|
|
2413
|
+
const lanes = await lane_registry.list_lanes();
|
|
2414
|
+
return await close_each(lanes.map((lane) => lane.issue));
|
|
2415
|
+
}
|
|
2416
|
+
var lane_close = {
|
|
2417
|
+
close_all_lanes,
|
|
2418
|
+
close_lane,
|
|
2419
|
+
prune_lanes,
|
|
2420
|
+
resolve_lane
|
|
2421
|
+
};
|
|
2422
|
+
|
|
2423
|
+
// scripts/run/tidy/run-tidy-lanes.ts
|
|
2424
|
+
var CLOSED_KIND = "closed";
|
|
2425
|
+
var INCOMPLETE_REASON = "the close left files behind";
|
|
2426
|
+
var NOT_MERGED_REASON = "not merged";
|
|
2427
|
+
async function has_unpushed(directory) {
|
|
2428
|
+
try {
|
|
2429
|
+
const count = await git_spawn.read([
|
|
2430
|
+
"-C",
|
|
2431
|
+
directory,
|
|
2432
|
+
"rev-list",
|
|
2433
|
+
"--count",
|
|
2434
|
+
"HEAD",
|
|
2435
|
+
"--not",
|
|
2436
|
+
"--remotes"
|
|
2437
|
+
]);
|
|
2438
|
+
return count !== "0";
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
error_text.trace_swallowed("run_tidy_lanes.has_unpushed", error);
|
|
2441
|
+
return true;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
async function hold_path_of(directory) {
|
|
2445
|
+
const git_directory = await git_spawn.read(["-C", directory, "rev-parse", "--absolute-git-dir"]);
|
|
2446
|
+
return run_hold.hold_path(git_directory);
|
|
2447
|
+
}
|
|
2448
|
+
function is_running(hold_path) {
|
|
2449
|
+
const read2 = run_hold.read_hold(hold_path);
|
|
2450
|
+
return read2.kind === "held" || read2.kind === "unreadable";
|
|
2451
|
+
}
|
|
2452
|
+
async function lane_verdict2(lane, hold_path) {
|
|
2453
|
+
const verdict = run_tidy.lane_verdict({
|
|
2454
|
+
issue: lane.issue,
|
|
2455
|
+
is_merged: true,
|
|
2456
|
+
has_changes: await git_stash.has_changes(lane.directory),
|
|
2457
|
+
has_unpushed: await has_unpushed(lane.directory),
|
|
2458
|
+
is_running: is_running(hold_path)
|
|
2459
|
+
});
|
|
2460
|
+
return verdict ?? run_tidy.keep(NOT_MERGED_REASON);
|
|
2461
|
+
}
|
|
2462
|
+
async function close(issue, hold_path) {
|
|
2463
|
+
const outcome = await lane_close.close_lane(issue);
|
|
2464
|
+
if (outcome.kind !== CLOSED_KIND) return run_tidy.keep(INCOMPLETE_REASON);
|
|
2465
|
+
run_hold.release_hold(hold_path);
|
|
2466
|
+
return run_tidy.CLEAN;
|
|
2467
|
+
}
|
|
2468
|
+
async function settle(lane) {
|
|
2469
|
+
try {
|
|
2470
|
+
const hold_path = await hold_path_of(lane.directory);
|
|
2471
|
+
const verdict = await lane_verdict2(lane, hold_path);
|
|
2472
|
+
return verdict.kind === "clean" ? await close(lane.issue, hold_path) : verdict;
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
return run_tidy.keep(String(error));
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
async function tidy_lane(lane, is_merged, in_flight) {
|
|
2478
|
+
if (in_flight.has(lane.issue) || lane.is_stranded || !await is_merged(lane.issue)) {
|
|
2479
|
+
return void 0;
|
|
2480
|
+
}
|
|
2481
|
+
return { target: `lane ${session_cite.issue(lane.issue)}`, verdict: await settle(lane) };
|
|
2482
|
+
}
|
|
2483
|
+
async function tidy_lanes(is_merged, in_flight = /* @__PURE__ */ new Set()) {
|
|
2484
|
+
const outcomes = [];
|
|
2485
|
+
const lanes = await lane_registry.list_lanes();
|
|
2486
|
+
for (const lane of lanes) {
|
|
2487
|
+
const outcome = await tidy_lane(lane, is_merged, in_flight);
|
|
2488
|
+
if (outcome !== void 0) outcomes.push(outcome);
|
|
2489
|
+
}
|
|
2490
|
+
return outcomes;
|
|
2491
|
+
}
|
|
2492
|
+
var run_tidy_lanes = { has_unpushed, tidy_lanes };
|
|
2493
|
+
|
|
2494
|
+
// scripts/git/stash/stash-orphans.ts
|
|
2495
|
+
var HASH_PATTERN = /#(?<issue>\d+)\b/u;
|
|
2496
|
+
var LEADING_PATTERN = /(?:^|: )(?<issue>\d+): /u;
|
|
2497
|
+
var MESSAGE_PATTERNS = [HASH_PATTERN, LEADING_PATTERN];
|
|
2498
|
+
var ALL_ISSUE_PATTERN = new RegExp(HASH_PATTERN.source, "gu");
|
|
2499
|
+
var BRANCH_PATTERN = /^(?:WIP on|On) (?<issue>\d+)-lane: /u;
|
|
2500
|
+
var WIP_PREFIX = "WIP on ";
|
|
2501
|
+
var NO_PATTERNS = [];
|
|
2502
|
+
var HEADER = "Stashes whose issue is closed, or whose owner cannot be read \u2014 decide for each whether to revive or discard it (none is dropped automatically):";
|
|
2503
|
+
var FOOTER = 'Revive: `pnpm josh stash:pop "<message>"` \xB7 Discard: `git stash drop <selector>` \u2014 re-read the selector first, the stack is shared by every lane.';
|
|
2504
|
+
var UNKNOWN_OWNER = "owner unknown";
|
|
2505
|
+
var COLUMN_SEPARATOR = " ";
|
|
2506
|
+
var LINE_SEPARATOR4 = "\n";
|
|
2507
|
+
function patterns_for(subject) {
|
|
2508
|
+
if (subject.includes(run_preflight.STASH_LABEL_PREFIX)) return NO_PATTERNS;
|
|
2509
|
+
return subject.startsWith(WIP_PREFIX) ? [BRANCH_PATTERN] : [...MESSAGE_PATTERNS, BRANCH_PATTERN];
|
|
2510
|
+
}
|
|
2511
|
+
function issue_of2(subject) {
|
|
2512
|
+
for (const pattern of patterns_for(subject)) {
|
|
2513
|
+
const issue = pattern.exec(subject)?.groups?.["issue"];
|
|
2514
|
+
if (issue !== void 0) return issue;
|
|
2515
|
+
}
|
|
2516
|
+
return void 0;
|
|
2517
|
+
}
|
|
2518
|
+
function issues_of(entries) {
|
|
2519
|
+
const issues = entries.map((entry) => issue_of2(entry.subject));
|
|
2520
|
+
return [...new Set(issues.filter((issue) => issue !== void 0))];
|
|
2521
|
+
}
|
|
2522
|
+
function message_issues(subject) {
|
|
2523
|
+
const hashed = [...subject.matchAll(ALL_ISSUE_PATTERN)].map((match) => match.groups?.["issue"]);
|
|
2524
|
+
return [...hashed, LEADING_PATTERN.exec(subject)?.groups?.["issue"]];
|
|
2525
|
+
}
|
|
2526
|
+
function issues_named(subject) {
|
|
2527
|
+
const branch = BRANCH_PATTERN.exec(subject)?.groups?.["issue"];
|
|
2528
|
+
const named = subject.startsWith(WIP_PREFIX) ? [] : message_issues(subject);
|
|
2529
|
+
const found = [...named, branch].filter((issue) => issue !== void 0);
|
|
2530
|
+
return [...new Set(found)];
|
|
2531
|
+
}
|
|
2532
|
+
function orphans(entries, closed) {
|
|
2533
|
+
return entries.map((entry) => ({ ...entry, issue: issue_of2(entry.subject) })).filter((entry) => entry.issue === void 0 || closed.has(entry.issue));
|
|
2534
|
+
}
|
|
2535
|
+
function format_line(orphan) {
|
|
2536
|
+
const owner = orphan.issue === void 0 ? UNKNOWN_OWNER : `${session_cite.issue(orphan.issue)} closed`;
|
|
2537
|
+
return ` ${[orphan.selector, owner, orphan.subject].join(COLUMN_SEPARATOR)}`;
|
|
2538
|
+
}
|
|
2539
|
+
function format_report2(found) {
|
|
2540
|
+
if (found.length === 0) return void 0;
|
|
2541
|
+
return [HEADER, ...found.map((orphan) => format_line(orphan)), FOOTER].join(LINE_SEPARATOR4);
|
|
2542
|
+
}
|
|
2543
|
+
var stash_orphans = { format_report: format_report2, issue_of: issue_of2, issues_named, issues_of, orphans };
|
|
2544
|
+
|
|
2545
|
+
// scripts/git/stash/stash-sweep-lock.ts
|
|
2546
|
+
var LOCK_PREFIX = "josh-stash-sweep-lock-";
|
|
2547
|
+
var MAX_WAIT_MS = 3e4;
|
|
2548
|
+
function lock_path(cwd) {
|
|
2549
|
+
return repository_lock.lock_path(LOCK_PREFIX, cwd);
|
|
2550
|
+
}
|
|
2551
|
+
async function with_lock(work, target = lock_path(), max_wait_ms = MAX_WAIT_MS) {
|
|
2552
|
+
return await repository_lock.with_lock(work, target, max_wait_ms);
|
|
2553
|
+
}
|
|
2554
|
+
var stash_sweep_lock = { clear_stale: repository_lock.clear_stale, with_lock };
|
|
2555
|
+
|
|
2556
|
+
// scripts/observations/observation-ledger-home.ts
|
|
2557
|
+
import { appendFile, mkdir, readdir, readFile } from "node:fs/promises";
|
|
2558
|
+
import path3 from "node:path";
|
|
2559
|
+
|
|
2560
|
+
// scripts/observations/observation-ledger-migrate.ts
|
|
2561
|
+
import {
|
|
2562
|
+
appendFileSync,
|
|
2563
|
+
mkdirSync,
|
|
2564
|
+
readdirSync,
|
|
2565
|
+
readFileSync as readFileSync2,
|
|
2566
|
+
renameSync,
|
|
2567
|
+
rmdirSync,
|
|
2568
|
+
rmSync as rmSync2
|
|
2569
|
+
} from "node:fs";
|
|
2570
|
+
import path2 from "node:path";
|
|
2571
|
+
var NEWLINE = "\n";
|
|
2572
|
+
function claim_path(legacy) {
|
|
2573
|
+
return `${legacy}.${String(process.pid)}${MIGRATION_CLAIM_SUFFIX}`;
|
|
2574
|
+
}
|
|
2575
|
+
function claim(from, to) {
|
|
2576
|
+
try {
|
|
2577
|
+
renameSync(from, to);
|
|
2578
|
+
return true;
|
|
2579
|
+
} catch {
|
|
2580
|
+
return false;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function is_stale_claim(name, legacy_name) {
|
|
2584
|
+
const prefix = `${legacy_name}.`;
|
|
2585
|
+
if (!name.startsWith(prefix) || !name.endsWith(MIGRATION_CLAIM_SUFFIX)) return false;
|
|
2586
|
+
const pid = Number(name.slice(prefix.length, -MIGRATION_CLAIM_SUFFIX.length));
|
|
2587
|
+
return Number.isSafeInteger(pid) && !process_identity.is_present_pid(pid);
|
|
2588
|
+
}
|
|
2589
|
+
function stale_claims(legacy) {
|
|
2590
|
+
const directory = path2.dirname(legacy);
|
|
2591
|
+
try {
|
|
2592
|
+
return readdirSync(directory).filter((name) => is_stale_claim(name, path2.basename(legacy))).map((name) => path2.join(directory, name));
|
|
2593
|
+
} catch {
|
|
2594
|
+
return [];
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
function terminated(content) {
|
|
2598
|
+
return content.length === 0 || content.endsWith(NEWLINE) ? content : `${content}${NEWLINE}`;
|
|
2599
|
+
}
|
|
2600
|
+
function carried(content, ledger) {
|
|
2601
|
+
if (ledger === void 0) return terminated(content);
|
|
2602
|
+
const lines = content.split(NEWLINE).filter((line) => line.length > 0);
|
|
2603
|
+
const missing = observation_ledger_line.missing_lines(lines, ledger);
|
|
2604
|
+
return missing.length === 0 ? "" : `${missing.join(NEWLINE)}${NEWLINE}`;
|
|
2605
|
+
}
|
|
2606
|
+
function absorb(claimed, target) {
|
|
2607
|
+
mkdirSync(path2.dirname(target), { recursive: true });
|
|
2608
|
+
appendFileSync(
|
|
2609
|
+
target,
|
|
2610
|
+
carried(readFileSync2(claimed, "utf8"), file_reader.read_if_readable(target)),
|
|
2611
|
+
"utf8"
|
|
2612
|
+
);
|
|
2613
|
+
rmSync2(claimed);
|
|
2614
|
+
}
|
|
2615
|
+
function absorb_stale(legacy, target) {
|
|
2616
|
+
let count = 0;
|
|
2617
|
+
for (const stale of stale_claims(legacy)) {
|
|
2618
|
+
const claimed = claim_path(legacy);
|
|
2619
|
+
if (!claim(stale, claimed)) continue;
|
|
2620
|
+
absorb(claimed, target);
|
|
2621
|
+
count += 1;
|
|
2622
|
+
}
|
|
2623
|
+
return count;
|
|
2624
|
+
}
|
|
2625
|
+
function migrate_one(legacy, target) {
|
|
2626
|
+
const recovered = absorb_stale(legacy, target);
|
|
2627
|
+
const claimed = claim_path(legacy);
|
|
2628
|
+
if (!claim(legacy, claimed)) return recovered > 0;
|
|
2629
|
+
absorb(claimed, target);
|
|
2630
|
+
return true;
|
|
2631
|
+
}
|
|
2632
|
+
function source_name(name) {
|
|
2633
|
+
const end = name.indexOf(LEDGER_FILE_EXTENSION);
|
|
2634
|
+
return end === -1 ? void 0 : name.slice(0, end + LEDGER_FILE_EXTENSION.length);
|
|
2635
|
+
}
|
|
2636
|
+
function legacy_directory_names(directory) {
|
|
2637
|
+
try {
|
|
2638
|
+
const names = readdirSync(directory).map((name) => source_name(name));
|
|
2639
|
+
return [...new Set(names.filter((name) => name !== void 0))];
|
|
2640
|
+
} catch {
|
|
2641
|
+
return [];
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function migrate_directory(root) {
|
|
2645
|
+
const legacy_directory = path2.join(root, LEGACY_OBSERVATION_LEDGER_DIRECTORY);
|
|
2646
|
+
const did_move = legacy_directory_names(legacy_directory).map(
|
|
2647
|
+
(name) => migrate_one(
|
|
2648
|
+
path2.join(legacy_directory, name),
|
|
2649
|
+
path2.join(root, OBSERVATION_LEDGER_DIRECTORY, name)
|
|
2650
|
+
)
|
|
2651
|
+
).some(Boolean);
|
|
2652
|
+
try {
|
|
2653
|
+
rmdirSync(legacy_directory);
|
|
2654
|
+
} catch {
|
|
2655
|
+
}
|
|
2656
|
+
return did_move;
|
|
2657
|
+
}
|
|
2658
|
+
function migrate(root) {
|
|
2659
|
+
const target = path2.join(root, OBSERVATION_LEDGER_DIRECTORY, LEGACY_LEDGER_FILE);
|
|
2660
|
+
const moved_files = LEGACY_OBSERVATION_LEDGER_PATHS.map(
|
|
2661
|
+
(legacy) => migrate_one(path2.join(root, legacy), target)
|
|
2662
|
+
);
|
|
2663
|
+
return [...moved_files, migrate_directory(root)].some(Boolean);
|
|
2664
|
+
}
|
|
2665
|
+
var observation_ledger_migrate = { migrate };
|
|
2666
|
+
|
|
2667
|
+
// scripts/observations/observation-ledger-home.ts
|
|
2668
|
+
var DATE_END = 10;
|
|
2669
|
+
function ledger_root(cwd = process.cwd()) {
|
|
2670
|
+
return cwd;
|
|
2671
|
+
}
|
|
2672
|
+
function ledger_directory(cwd = process.cwd()) {
|
|
2673
|
+
const root = ledger_root(cwd);
|
|
2674
|
+
observation_ledger_migrate.migrate(root);
|
|
2675
|
+
return path3.join(root, OBSERVATION_LEDGER_DIRECTORY);
|
|
2676
|
+
}
|
|
2677
|
+
function file_path(name, cwd) {
|
|
2678
|
+
ledger_directory(cwd);
|
|
2679
|
+
return path3.join(ledger_root(cwd), observation_ledger.ledger_file(name));
|
|
2680
|
+
}
|
|
2681
|
+
function issue_path(issue, cwd = process.cwd()) {
|
|
2682
|
+
return file_path(issue, cwd);
|
|
2683
|
+
}
|
|
2684
|
+
async function current_branch(cwd) {
|
|
2685
|
+
try {
|
|
2686
|
+
return await git_spawn.read(["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
2687
|
+
} catch {
|
|
2688
|
+
return "";
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
function ledger_date(now) {
|
|
2692
|
+
return now.toISOString().slice(0, DATE_END);
|
|
2693
|
+
}
|
|
2694
|
+
async function writer_path(now, cwd = process.cwd()) {
|
|
2695
|
+
const issue = git_branch.issue_from_branch(await current_branch(cwd));
|
|
2696
|
+
return file_path(issue ?? ledger_date(now), cwd);
|
|
2697
|
+
}
|
|
2698
|
+
async function read_file(target) {
|
|
2699
|
+
try {
|
|
2700
|
+
const content = await readFile(target, "utf8");
|
|
2701
|
+
return content.length === 0 || content.endsWith("\n") ? content : `${content}
|
|
2702
|
+
`;
|
|
2703
|
+
} catch {
|
|
2704
|
+
return "";
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
async function ledger_files(directory) {
|
|
2708
|
+
try {
|
|
2709
|
+
const names = await readdir(directory);
|
|
2710
|
+
return names.filter((name) => observation_ledger.is_ledger_file_name(name)).toSorted((left, right) => left.localeCompare(right));
|
|
2711
|
+
} catch {
|
|
2712
|
+
return void 0;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
async function read(cwd = process.cwd()) {
|
|
2716
|
+
const directory = ledger_directory(cwd);
|
|
2717
|
+
const files = await ledger_files(directory);
|
|
2718
|
+
if (files === void 0) return void 0;
|
|
2719
|
+
const contents = await Promise.all(
|
|
2720
|
+
files.map(async (name) => await read_file(path3.join(directory, name)))
|
|
2721
|
+
);
|
|
2722
|
+
return contents.join("");
|
|
2723
|
+
}
|
|
2724
|
+
async function append(target, lines) {
|
|
2725
|
+
if (lines.length === 0) return;
|
|
2726
|
+
await mkdir(path3.dirname(target), { recursive: true });
|
|
2727
|
+
await appendFile(target, `${lines.join("\n")}
|
|
2728
|
+
`, "utf8");
|
|
2729
|
+
}
|
|
2730
|
+
var observation_ledger_home = {
|
|
2731
|
+
append,
|
|
2732
|
+
file_path,
|
|
2733
|
+
issue_path,
|
|
2734
|
+
ledger_date,
|
|
2735
|
+
ledger_root,
|
|
2736
|
+
read,
|
|
2737
|
+
writer_path
|
|
2738
|
+
};
|
|
2739
|
+
|
|
2740
|
+
// scripts/run/tidy/run-tidy-stashes.ts
|
|
2741
|
+
var READ_CONCURRENCY2 = 8;
|
|
2742
|
+
var GONE_REASON = "already dropped by another run";
|
|
2743
|
+
var BUSY_REASON = "another run held the stash sweep lock";
|
|
2744
|
+
async function added_ledger_lines(hash, ledger_paths) {
|
|
2745
|
+
const added = await bounded_pool.bounded_map(
|
|
2746
|
+
ledger_paths,
|
|
2747
|
+
READ_CONCURRENCY2,
|
|
2748
|
+
async (ledger_path) => await git_stash.added_lines(hash, ledger_path)
|
|
2749
|
+
);
|
|
2750
|
+
return added.flat();
|
|
2751
|
+
}
|
|
2752
|
+
async function carry_ledger(hash) {
|
|
2753
|
+
const changed = await git_stash.changed_paths(hash);
|
|
2754
|
+
const ledger_paths = changed.filter((file_path2) => observation_ledger.is_ledger_path(file_path2));
|
|
2755
|
+
if (ledger_paths.length === 0) return 0;
|
|
2756
|
+
const ledger_path = await observation_ledger_home.writer_path(/* @__PURE__ */ new Date());
|
|
2757
|
+
const added = await added_ledger_lines(hash, ledger_paths);
|
|
2758
|
+
const carried2 = run_tidy.ledger_carry(added, await observation_ledger_home.read() ?? "");
|
|
2759
|
+
await observation_ledger_home.append(ledger_path, carried2);
|
|
2760
|
+
return carried2.length;
|
|
2761
|
+
}
|
|
2762
|
+
function carried_note(count) {
|
|
2763
|
+
return count === 0 ? "" : ` (${String(count)} observation line(s) carried to the ledger)`;
|
|
2764
|
+
}
|
|
2765
|
+
async function drop(entry) {
|
|
2766
|
+
const carried2 = await carry_ledger(entry.selector);
|
|
2767
|
+
const target = `stash "${entry.subject}"${carried_note(carried2)}`;
|
|
2768
|
+
const is_dropped = await git_stash.drop_by_hash(entry.selector);
|
|
2769
|
+
return { target, verdict: is_dropped ? run_tidy.CLEAN : run_tidy.keep(GONE_REASON) };
|
|
2770
|
+
}
|
|
2771
|
+
async function merged_set(issues, is_merged) {
|
|
2772
|
+
const answers = await bounded_pool.bounded_map(
|
|
2773
|
+
issues,
|
|
2774
|
+
READ_CONCURRENCY2,
|
|
2775
|
+
async (issue) => await is_merged(issue)
|
|
2776
|
+
);
|
|
2777
|
+
return new Set(issues.filter((_issue, index) => answers[index] === true));
|
|
2778
|
+
}
|
|
2779
|
+
function entry_verdict(entry, merged) {
|
|
2780
|
+
const issues = stash_orphans.issues_named(entry.subject);
|
|
2781
|
+
return run_tidy.stash_verdict({ subject: entry.subject, issues }, merged);
|
|
2782
|
+
}
|
|
2783
|
+
async function tidy_entry(entry, merged) {
|
|
2784
|
+
const verdict = entry_verdict(entry, merged);
|
|
2785
|
+
if (verdict === void 0) return void 0;
|
|
2786
|
+
if (verdict.kind === "keep") return { target: `stash "${entry.subject}"`, verdict };
|
|
2787
|
+
try {
|
|
2788
|
+
return await drop(entry);
|
|
2789
|
+
} catch (error) {
|
|
2790
|
+
return { target: `stash "${entry.subject}"`, verdict: run_tidy.keep(String(error)) };
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
async function tidy_entries(entries, merged) {
|
|
2794
|
+
const outcomes = [];
|
|
2795
|
+
for (const entry of entries) {
|
|
2796
|
+
const outcome = await tidy_entry(entry, merged);
|
|
2797
|
+
if (outcome !== void 0) outcomes.push(outcome);
|
|
2798
|
+
}
|
|
2799
|
+
return outcomes;
|
|
2800
|
+
}
|
|
2801
|
+
function busy_outcome(entry, merged) {
|
|
2802
|
+
const verdict = entry_verdict(entry, merged);
|
|
2803
|
+
if (verdict === void 0) return void 0;
|
|
2804
|
+
return {
|
|
2805
|
+
target: `stash "${entry.subject}"`,
|
|
2806
|
+
verdict: verdict.kind === "keep" ? verdict : run_tidy.keep(BUSY_REASON)
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
function busy_outcomes(entries, merged) {
|
|
2810
|
+
return entries.map((entry) => busy_outcome(entry, merged)).filter((outcome) => outcome !== void 0);
|
|
2811
|
+
}
|
|
2812
|
+
async function tidy_stashes(is_merged) {
|
|
2813
|
+
const entries = await git_stash.list_by_hash();
|
|
2814
|
+
const issues = [...new Set(entries.flatMap((entry) => stash_orphans.issues_named(entry.subject)))];
|
|
2815
|
+
const merged = await merged_set(issues, is_merged);
|
|
2816
|
+
const outcomes = await stash_sweep_lock.with_lock(async () => await tidy_entries(entries, merged));
|
|
2817
|
+
return outcomes ?? busy_outcomes(entries, merged);
|
|
2818
|
+
}
|
|
2819
|
+
var run_tidy_stashes = { tidy_stashes };
|
|
2820
|
+
|
|
2821
|
+
// scripts/run/tidy/run-tidy-cli.ts
|
|
2822
|
+
var FAILURE_NOTE = "run:tidy could not finish; merged lanes and stashes may remain:";
|
|
2823
|
+
function memoized(read2) {
|
|
2824
|
+
const answers = /* @__PURE__ */ new Map();
|
|
2825
|
+
return async function read_once(issue) {
|
|
2826
|
+
const cached = answers.get(issue) ?? read2(issue);
|
|
2827
|
+
answers.set(issue, cached);
|
|
2828
|
+
return await cached;
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
async function sweep() {
|
|
2832
|
+
try {
|
|
2833
|
+
const is_merged = memoized(issue_merged.read_merged);
|
|
2834
|
+
const in_flight = backlog_drive_restore.active_issues(
|
|
2835
|
+
await run_event_stream_emit.current_events()
|
|
2836
|
+
);
|
|
2837
|
+
const lanes = await run_tidy_lanes.tidy_lanes(is_merged, in_flight);
|
|
2838
|
+
const stashes = await run_tidy_stashes.tidy_stashes(is_merged);
|
|
2839
|
+
const report6 = run_tidy.format_report([...lanes, ...stashes]);
|
|
2840
|
+
if (report6 !== void 0) console.error(report6);
|
|
2841
|
+
} catch (error) {
|
|
2842
|
+
console.error(FAILURE_NOTE, error);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
var run_tidy_cli = { sweep };
|
|
2846
|
+
if (process.argv[1] === fileURLToPath3(import.meta.url)) await sweep();
|
|
2847
|
+
|
|
2848
|
+
// scripts/run/hold/run-hold-cli.ts
|
|
2849
|
+
var SUCCESS_EXIT_CODE2 = 0;
|
|
2850
|
+
var FAILURE_EXIT_CODE4 = 1;
|
|
2851
|
+
var ARGV_OFFSET3 = 2;
|
|
2852
|
+
var RELEASE_FLAG = "--release";
|
|
2853
|
+
var FORCE_FLAG = "--force";
|
|
2854
|
+
var FULLRUN_FLAG = "--fullrun";
|
|
2855
|
+
var HALFRUN_STOP_FLAG = "--halfrun-stop";
|
|
2856
|
+
var PRRUN_STOP_FLAG = "--prrun-stop";
|
|
2857
|
+
var MAX_CLAIM_ARGUMENTS = 2;
|
|
2858
|
+
var USAGE3 = "Usage: josh run:hold [<issue-number> [--fullrun | --halfrun-stop | --prrun-stop]] | josh run:release [<issue-number> | --force]";
|
|
2859
|
+
var COMMAND = "run:hold";
|
|
2860
|
+
var HOLD_VERDICT = "hold";
|
|
2861
|
+
var BUSY_VERDICT = "busy";
|
|
2862
|
+
var UNKNOWN_VERDICT = "unknown";
|
|
2863
|
+
var RELEASED_VERDICT = "released";
|
|
2864
|
+
var NONE_VERDICT = "none";
|
|
2865
|
+
var HELD_VERDICT = "held";
|
|
2866
|
+
var FORCE_RELEASE_KIND = "force-release";
|
|
2867
|
+
var HALFRUN_STOP_KIND = "halfrun-stop";
|
|
2868
|
+
var PRRUN_STOP_KIND = "prrun-stop";
|
|
2869
|
+
function is_stop_request(request) {
|
|
2870
|
+
return request.kind === HALFRUN_STOP_KIND || request.kind === PRRUN_STOP_KIND;
|
|
2871
|
+
}
|
|
2872
|
+
var FORCE_RELEASE_REQUEST = { kind: FORCE_RELEASE_KIND };
|
|
2873
|
+
function release_request(claimant) {
|
|
2874
|
+
return { kind: "release", claimant };
|
|
2875
|
+
}
|
|
2876
|
+
var STOP_KIND_BY_FLAG = /* @__PURE__ */ new Map([
|
|
2877
|
+
[HALFRUN_STOP_FLAG, HALFRUN_STOP_KIND],
|
|
2878
|
+
[PRRUN_STOP_FLAG, PRRUN_STOP_KIND]
|
|
2879
|
+
]);
|
|
2880
|
+
function numbered_claim(issue, flag) {
|
|
2881
|
+
if (flag === void 0) return { kind: "claim", issue };
|
|
2882
|
+
const stop_kind = STOP_KIND_BY_FLAG.get(flag);
|
|
2883
|
+
if (stop_kind !== void 0) return { kind: stop_kind, issue };
|
|
2884
|
+
return flag === FULLRUN_FLAG ? { kind: "claim", issue, is_fullrun: true } : void 0;
|
|
2885
|
+
}
|
|
2886
|
+
function parse_claim(argv) {
|
|
2887
|
+
const [first, flag] = argv;
|
|
2888
|
+
if (first === void 0) return { kind: "claim", issue: run_hold.UNNUMBERED_ISSUE };
|
|
2889
|
+
if (argv.length > MAX_CLAIM_ARGUMENTS || !issue_number_shape.ISSUE_NUMBER_PATTERN.test(first)) {
|
|
2890
|
+
return void 0;
|
|
2891
|
+
}
|
|
2892
|
+
return numbered_claim(first, flag);
|
|
2893
|
+
}
|
|
2894
|
+
function parse_release_argument(first) {
|
|
2895
|
+
if (first === FORCE_FLAG) return FORCE_RELEASE_REQUEST;
|
|
2896
|
+
return issue_number_shape.ISSUE_NUMBER_PATTERN.test(first) ? release_request(first) : void 0;
|
|
2897
|
+
}
|
|
2898
|
+
function parse_release(argv) {
|
|
2899
|
+
const [first] = argv;
|
|
2900
|
+
if (first === void 0) return release_request(run_hold.UNNUMBERED_ISSUE);
|
|
2901
|
+
return argv.length > 1 ? void 0 : parse_release_argument(first);
|
|
2902
|
+
}
|
|
2903
|
+
function parse_request(argv) {
|
|
2904
|
+
if (argv[0] === RELEASE_FLAG) return parse_release(argv.slice(1));
|
|
2905
|
+
return parse_claim(argv);
|
|
2906
|
+
}
|
|
2907
|
+
function report3(message3, verdict, code) {
|
|
2908
|
+
console.error(message3);
|
|
2909
|
+
console.info(verdict);
|
|
2910
|
+
return code;
|
|
2911
|
+
}
|
|
2912
|
+
function report_busy(message3) {
|
|
2913
|
+
return report3(message3, BUSY_VERDICT, SUCCESS_EXIT_CODE2);
|
|
2914
|
+
}
|
|
2915
|
+
function report_hold() {
|
|
2916
|
+
console.info(HOLD_VERDICT);
|
|
2917
|
+
return SUCCESS_EXIT_CODE2;
|
|
2918
|
+
}
|
|
2919
|
+
function held_block(hold, issue) {
|
|
2920
|
+
return run_hold.is_reentry(hold, issue) ? void 0 : run_hold.held_message(hold);
|
|
2921
|
+
}
|
|
2922
|
+
function is_own_unstopped(hold, issue) {
|
|
2923
|
+
return run_hold.is_reentry(hold, issue) && !run_hold.is_stop_marked(hold);
|
|
2924
|
+
}
|
|
2925
|
+
async function stale_block(hold, issue) {
|
|
2926
|
+
if (is_own_unstopped(hold, issue)) return void 0;
|
|
2927
|
+
return await run_hold.is_tree_dirty() ? run_hold.uncommitted_message(hold) : void 0;
|
|
2928
|
+
}
|
|
2929
|
+
async function blocking_message(read2, issue) {
|
|
2930
|
+
if (read2.kind === "held") return held_block(read2.hold, issue);
|
|
2931
|
+
if (read2.kind === "unreadable") return run_hold.unreadable_message();
|
|
2932
|
+
return read2.kind === "stale" ? await stale_block(read2.hold, issue) : void 0;
|
|
2933
|
+
}
|
|
2934
|
+
function take_free_tree(target, request) {
|
|
2935
|
+
const is_taken = run_hold.create_hold(target, request.issue, /* @__PURE__ */ new Date(), request.is_fullrun);
|
|
2936
|
+
if (is_taken) return report_hold();
|
|
2937
|
+
return report_busy(run_hold.race_message(run_hold.read_hold(target), target));
|
|
2938
|
+
}
|
|
2939
|
+
function replace_stale(target, request, hold) {
|
|
2940
|
+
console.error(run_hold.stale_message(hold));
|
|
2941
|
+
run_hold.release_hold(target);
|
|
2942
|
+
return take_free_tree(target, request);
|
|
2943
|
+
}
|
|
2944
|
+
function take_tree(target, request, read2) {
|
|
2945
|
+
if (read2.kind === "held") return report_hold();
|
|
2946
|
+
return read2.kind === "stale" ? replace_stale(target, request, read2.hold) : take_free_tree(target, request);
|
|
2947
|
+
}
|
|
2948
|
+
function report_preflight(decision) {
|
|
2949
|
+
console.error(`${decision.reason}
|
|
2950
|
+
${decision.advice}`);
|
|
2951
|
+
console.info(decision.verdict);
|
|
2952
|
+
return SUCCESS_EXIT_CODE2;
|
|
2953
|
+
}
|
|
2954
|
+
async function preflight_gate(issue, is_linked) {
|
|
2955
|
+
if (is_linked || issue === run_hold.UNNUMBERED_ISSUE) return void 0;
|
|
2956
|
+
const decision = await run_preflight.check(issue);
|
|
2957
|
+
if (decision.verdict === run_preflight.CLEAN_VERDICT) return void 0;
|
|
2958
|
+
return report_preflight(decision);
|
|
2959
|
+
}
|
|
2960
|
+
async function claim2(target, request, is_linked) {
|
|
2961
|
+
const gate = await preflight_gate(request.issue, is_linked);
|
|
2962
|
+
if (gate !== void 0) return gate;
|
|
2963
|
+
const read2 = run_hold.read_hold(target);
|
|
2964
|
+
const blocked = await blocking_message(read2, request.issue);
|
|
2965
|
+
if (blocked !== void 0) return report_busy(blocked);
|
|
2966
|
+
const code = take_tree(target, request, read2);
|
|
2967
|
+
if (code === SUCCESS_EXIT_CODE2) await run_tidy_cli.sweep();
|
|
2968
|
+
return code;
|
|
2969
|
+
}
|
|
2970
|
+
function remove_record(target, was_present) {
|
|
2971
|
+
run_hold.release_hold(target);
|
|
2972
|
+
console.info(was_present ? RELEASED_VERDICT : NONE_VERDICT);
|
|
2973
|
+
return SUCCESS_EXIT_CODE2;
|
|
2974
|
+
}
|
|
2975
|
+
function report_held(hold) {
|
|
2976
|
+
return report3(run_hold.foreign_release_message(hold), HELD_VERDICT, FAILURE_EXIT_CODE4);
|
|
2977
|
+
}
|
|
2978
|
+
function report_unreadable_record() {
|
|
2979
|
+
return report3(run_hold.unreadable_message(), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
|
|
2980
|
+
}
|
|
2981
|
+
function release_record(target, claimant) {
|
|
2982
|
+
const read2 = run_hold.read_hold(target);
|
|
2983
|
+
if (read2.kind === "free") return remove_record(target, false);
|
|
2984
|
+
if (read2.kind === "unreadable") return report_unreadable_record();
|
|
2985
|
+
if (!run_hold.is_own_hold(read2.hold, claimant)) return report_held(read2.hold);
|
|
2986
|
+
return remove_record(target, true);
|
|
2987
|
+
}
|
|
2988
|
+
async function release(target, claimant) {
|
|
2989
|
+
const code = release_record(target, claimant);
|
|
2990
|
+
const is_own_end = code === SUCCESS_EXIT_CODE2 && claimant !== run_hold.UNNUMBERED_ISSUE;
|
|
2991
|
+
if (is_own_end) await run_label.unmark(claimant);
|
|
2992
|
+
return code;
|
|
2993
|
+
}
|
|
2994
|
+
function force_release(target) {
|
|
2995
|
+
const read2 = run_hold.read_hold(target);
|
|
2996
|
+
if (read2.kind === "held" || read2.kind === "stale") {
|
|
2997
|
+
console.error(run_hold.forced_release_message(read2.hold));
|
|
2998
|
+
}
|
|
2999
|
+
return remove_record(target, read2.kind !== "free");
|
|
3000
|
+
}
|
|
3001
|
+
function report_unknown() {
|
|
3002
|
+
return report3(run_hold.unknown_message(), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
|
|
3003
|
+
}
|
|
3004
|
+
function report_usage() {
|
|
3005
|
+
console.error(USAGE3);
|
|
3006
|
+
return FAILURE_EXIT_CODE4;
|
|
3007
|
+
}
|
|
3008
|
+
async function mark_stop(target, request) {
|
|
3009
|
+
const mark2 = request.kind === PRRUN_STOP_KIND ? await run_prrun_resume.mark_stop_at(target, request.issue) : run_halfrun_resume.mark_stop_at(target, request.issue);
|
|
3010
|
+
if (mark2 === run_halfrun_resume.MARKED) return report_hold();
|
|
3011
|
+
if (mark2 === run_halfrun_resume.UNREADABLE) return report_unreadable_record();
|
|
3012
|
+
return report_busy(run_hold.race_message(run_hold.read_hold(target), target));
|
|
3013
|
+
}
|
|
3014
|
+
async function dispatch(request, target, is_linked) {
|
|
3015
|
+
if (request.kind === "claim") return await claim2(target, request, is_linked);
|
|
3016
|
+
if (is_stop_request(request)) return await mark_stop(target, request);
|
|
3017
|
+
if (request.kind === FORCE_RELEASE_KIND) return force_release(target);
|
|
3018
|
+
return await release(target, request.claimant);
|
|
3019
|
+
}
|
|
3020
|
+
async function answer(request) {
|
|
3021
|
+
const directory = await run_cli_fault.directory_of(COMMAND, run_hold.worktree_directory);
|
|
3022
|
+
if (directory === void 0) return report_unknown();
|
|
3023
|
+
const is_linked = await run_hold.is_linked_worktree();
|
|
3024
|
+
return await dispatch(request, run_hold.hold_path(directory), is_linked);
|
|
3025
|
+
}
|
|
3026
|
+
async function run2(argv) {
|
|
3027
|
+
const request = parse_request(argv);
|
|
3028
|
+
if (request === void 0) return report_usage();
|
|
3029
|
+
try {
|
|
3030
|
+
return await answer(request);
|
|
3031
|
+
} catch (error) {
|
|
3032
|
+
return report3(run_cli_fault.message(COMMAND, error), UNKNOWN_VERDICT, FAILURE_EXIT_CODE4);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
async function main3(argv) {
|
|
3036
|
+
process.exitCode = await run2(argv);
|
|
3037
|
+
}
|
|
3038
|
+
var run_hold_cli = {
|
|
3039
|
+
BUSY_VERDICT,
|
|
3040
|
+
HELD_VERDICT,
|
|
3041
|
+
HOLD_VERDICT,
|
|
3042
|
+
NONE_VERDICT,
|
|
3043
|
+
RELEASED_VERDICT,
|
|
3044
|
+
UNKNOWN_VERDICT,
|
|
3045
|
+
USAGE: USAGE3,
|
|
3046
|
+
parse_request,
|
|
3047
|
+
run: run2
|
|
3048
|
+
};
|
|
3049
|
+
if (process.argv[1] === fileURLToPath4(import.meta.url)) await main3(process.argv.slice(ARGV_OFFSET3));
|
|
3050
|
+
|
|
3051
|
+
// scripts/run/hold/run-hold-session.ts
|
|
3052
|
+
function is_same_start(recorded, own) {
|
|
3053
|
+
return recorded === void 0 || own === void 0 || recorded === own;
|
|
3054
|
+
}
|
|
3055
|
+
function is_session_hold(hold, owner = run_hold.session_owner()) {
|
|
3056
|
+
if (hold.owner_pid === void 0 || owner.owner_pid === void 0) return true;
|
|
3057
|
+
return hold.owner_pid === owner.owner_pid && is_same_start(hold.owner_start, owner.owner_start);
|
|
3058
|
+
}
|
|
3059
|
+
function is_notified_since(hold, pause_start2) {
|
|
3060
|
+
if (pause_start2 === void 0 || hold.notified_at === void 0) return false;
|
|
3061
|
+
return Date.parse(hold.notified_at) >= Date.parse(pause_start2);
|
|
3062
|
+
}
|
|
3063
|
+
function mark_notified(target, now = /* @__PURE__ */ new Date()) {
|
|
3064
|
+
const hold = run_halfrun_resume.hold_of(run_hold.read_hold(target, now));
|
|
3065
|
+
if (hold === void 0) return;
|
|
3066
|
+
stamp_file.write_stamp(target, { ...hold, notified_at: now.toISOString() });
|
|
3067
|
+
}
|
|
3068
|
+
var run_hold_session = { is_notified_since, is_session_hold, mark_notified };
|
|
3069
|
+
|
|
3070
|
+
// scripts/hooks/stop-actions.ts
|
|
3071
|
+
var SUCCESS_EXIT_CODE3 = 0;
|
|
3072
|
+
var NOTICE_SEPARATOR = "\n";
|
|
3073
|
+
var SUMMARY_CHARS = 600;
|
|
3074
|
+
var TOKEN_CHAR = /^[!-~]$/u;
|
|
3075
|
+
var EMPTY_SUMMARY = "The run stopped and is waiting for you.";
|
|
3076
|
+
var SEQUENTIAL = 1;
|
|
3077
|
+
function token_start(text2) {
|
|
3078
|
+
let index = text2.length;
|
|
3079
|
+
while (index > 0 && TOKEN_CHAR.test(text2.charAt(index - 1))) index -= 1;
|
|
3080
|
+
return index;
|
|
3081
|
+
}
|
|
3082
|
+
function summary_of(message3) {
|
|
3083
|
+
const trimmed = message3.trim();
|
|
3084
|
+
if (trimmed === "") return EMPTY_SUMMARY;
|
|
3085
|
+
if (trimmed.length <= SUMMARY_CHARS) return trimmed;
|
|
3086
|
+
const overshoot = trimmed.slice(0, SUMMARY_CHARS + 1);
|
|
3087
|
+
const cut = overshoot.slice(0, Math.min(token_start(overshoot), SUMMARY_CHARS));
|
|
3088
|
+
return cut === "" ? trimmed.slice(0, SUMMARY_CHARS) : cut;
|
|
3089
|
+
}
|
|
3090
|
+
async function issue_url_of(issue) {
|
|
3091
|
+
if (issue === run_hold.UNNUMBERED_ISSUE) return void 0;
|
|
3092
|
+
const slug = await git_gh_repo.repo_get_name_with_owner();
|
|
3093
|
+
return slug === void 0 ? void 0 : issue_cite.issue_url(slug, issue);
|
|
3094
|
+
}
|
|
3095
|
+
function notify_values(issue_url, action) {
|
|
3096
|
+
const { recommended, alternative } = action.decision;
|
|
3097
|
+
const values = {
|
|
3098
|
+
"task-type": "confirmation",
|
|
3099
|
+
body: session_cite.text(summary_of(action.message)),
|
|
3100
|
+
recommended,
|
|
3101
|
+
...alternative !== void 0 && { alternative: [alternative] }
|
|
3102
|
+
};
|
|
3103
|
+
return issue_url === void 0 ? values : { ...values, "issue-url": issue_url };
|
|
3104
|
+
}
|
|
3105
|
+
async function mark_notified2() {
|
|
3106
|
+
try {
|
|
3107
|
+
const directory = await run_hold.worktree_directory();
|
|
3108
|
+
if (directory !== void 0) run_hold_session.mark_notified(run_hold.hold_path(directory));
|
|
3109
|
+
} catch (error) {
|
|
3110
|
+
console.warn(`stop:guard could not stamp the notify on the hold: ${String(error)}`);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
async function notify(action) {
|
|
3114
|
+
const issue_url = await issue_url_of(action.issue);
|
|
3115
|
+
await telegram_test.send_values(notify_values(issue_url, action));
|
|
3116
|
+
await mark_notified2();
|
|
3117
|
+
return `\u{1F4F1} stop:guard sent the confirmation notify (${issue_url ?? action.issue}).`;
|
|
3118
|
+
}
|
|
3119
|
+
async function release2(issue) {
|
|
3120
|
+
const argv = issue === run_hold.UNNUMBERED_ISSUE ? ["--release"] : ["--release", issue];
|
|
3121
|
+
const code = await run_hold_cli.run(argv);
|
|
3122
|
+
return code === SUCCESS_EXIT_CODE3 ? "\u{1F513} stop:guard released the run:hold record." : void 0;
|
|
3123
|
+
}
|
|
3124
|
+
async function cite2(references) {
|
|
3125
|
+
const cited = await issue_cite_cli.cite(issue_citation.cite_arguments(references));
|
|
3126
|
+
if (cited === void 0 || cited.failures.length > 0) return void 0;
|
|
3127
|
+
return ["Issue citations:", ...cited.lines].join(NOTICE_SEPARATOR);
|
|
3128
|
+
}
|
|
3129
|
+
async function run_action(action) {
|
|
3130
|
+
if (action.kind === "notify") return await notify(action);
|
|
3131
|
+
if (action.kind === "release") return await release2(action.issue);
|
|
3132
|
+
return await cite2(action.references);
|
|
3133
|
+
}
|
|
3134
|
+
function set_stdout_write(stream_write) {
|
|
3135
|
+
process.stdout.write = stream_write;
|
|
3136
|
+
}
|
|
3137
|
+
async function on_stderr(work) {
|
|
3138
|
+
const saved = { write: process.stdout.write.bind(process.stdout) };
|
|
3139
|
+
set_stdout_write(process.stderr.write.bind(process.stderr));
|
|
3140
|
+
try {
|
|
3141
|
+
return await work();
|
|
3142
|
+
} finally {
|
|
3143
|
+
set_stdout_write(saved.write);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
async function attempt(action) {
|
|
3147
|
+
try {
|
|
3148
|
+
return await run_action(action);
|
|
3149
|
+
} catch {
|
|
3150
|
+
return void 0;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
function is_notice(notice) {
|
|
3154
|
+
return notice !== void 0;
|
|
3155
|
+
}
|
|
3156
|
+
async function settle2(actions) {
|
|
3157
|
+
const notices = await on_stderr(
|
|
3158
|
+
async () => await bounded_pool.bounded_map(actions, SEQUENTIAL, attempt)
|
|
3159
|
+
);
|
|
3160
|
+
const failed = actions.find((_action, index) => notices[index] === void 0);
|
|
3161
|
+
return {
|
|
3162
|
+
notices: notices.filter((notice) => is_notice(notice)),
|
|
3163
|
+
failure: failed === void 0 ? void 0 : stop_rules.fallback_reason(failed)
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
function joined(notices) {
|
|
3167
|
+
return notices.length === 0 ? void 0 : notices.join(NOTICE_SEPARATOR);
|
|
3168
|
+
}
|
|
3169
|
+
function settled_envelope(settled, is_blocking, lang) {
|
|
3170
|
+
const notice = joined(settled.notices);
|
|
3171
|
+
if (is_blocking && settled.failure !== void 0) {
|
|
3172
|
+
return stop_rules.block_envelope(settled.failure, lang, notice);
|
|
3173
|
+
}
|
|
3174
|
+
return notice === void 0 ? void 0 : stop_rules.notice_envelope(notice);
|
|
3175
|
+
}
|
|
3176
|
+
async function envelope_of(outcome, lang) {
|
|
3177
|
+
if (outcome.reason !== void 0) return stop_rules.block_envelope(outcome.reason, lang);
|
|
3178
|
+
const settled = await settle2(outcome.actions);
|
|
3179
|
+
return settled_envelope(settled, outcome.blocks_on_failure, lang);
|
|
3180
|
+
}
|
|
3181
|
+
var stop_actions2 = { envelope_of, summary_of };
|
|
3182
|
+
|
|
3183
|
+
// scripts/rules/last-prompt.ts
|
|
3184
|
+
import { z as z5 } from "zod";
|
|
3185
|
+
var WORKFLOW_KEYWORDS = /* @__PURE__ */ new Set([
|
|
3186
|
+
"kickoff",
|
|
3187
|
+
"fullrun",
|
|
3188
|
+
"halfrun",
|
|
3189
|
+
"prrun",
|
|
3190
|
+
"backlogrun"
|
|
3191
|
+
]);
|
|
3192
|
+
var NO_PROMPT = "";
|
|
3193
|
+
var HUMAN_ORIGIN = "human";
|
|
3194
|
+
var KEYWORD_BEFORE_ARGUMENT = -2;
|
|
3195
|
+
var PROMPT_LINE_SCHEMA = z5.object({
|
|
3196
|
+
isMeta: z5.boolean().nullish(),
|
|
3197
|
+
timestamp: z5.string().nullish(),
|
|
3198
|
+
origin: z5.object({ kind: z5.string().nullish() }).nullish(),
|
|
3199
|
+
message: z5.object({ content: z5.unknown() })
|
|
3200
|
+
});
|
|
3201
|
+
var TIMED_LINE_SCHEMA = z5.object({ timestamp: z5.string() });
|
|
3202
|
+
function is_person(line) {
|
|
3203
|
+
if (line.isMeta === true) return false;
|
|
3204
|
+
return line.origin === null || line.origin === void 0 || line.origin.kind === HUMAN_ORIGIN;
|
|
3205
|
+
}
|
|
3206
|
+
function person_line(line) {
|
|
3207
|
+
const parsed = PROMPT_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line));
|
|
3208
|
+
return parsed.success && is_person(parsed.data) ? parsed.data : void 0;
|
|
3209
|
+
}
|
|
3210
|
+
function person_text(line) {
|
|
3211
|
+
const person = person_line(line);
|
|
3212
|
+
if (person === void 0) return void 0;
|
|
3213
|
+
return time_reported_failure.result_text(person.message.content);
|
|
3214
|
+
}
|
|
3215
|
+
function is_invocation(prompt) {
|
|
3216
|
+
const words = prompt.trim().split(/\s+/u);
|
|
3217
|
+
return [words.at(0), words.at(KEYWORD_BEFORE_ARGUMENT)].some(
|
|
3218
|
+
(word) => WORKFLOW_KEYWORDS.has(word ?? NO_PROMPT)
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
function prompt_line_text(line) {
|
|
3222
|
+
return filing_cap.is_prompt_line(line) ? person_text(line) : void 0;
|
|
3223
|
+
}
|
|
3224
|
+
function prompt_text(tail) {
|
|
3225
|
+
const text2 = tail.split("\n").map((line) => prompt_line_text(line)).findLast((one) => one !== void 0);
|
|
3226
|
+
return text2 === void 0 || is_invocation(text2) ? NO_PROMPT : text2;
|
|
3227
|
+
}
|
|
3228
|
+
function turn_prompt(tail) {
|
|
3229
|
+
const opener = tail.split("\n").findLast((line) => filing_cap.is_prompt_line(line));
|
|
3230
|
+
if (opener === void 0) return NO_PROMPT;
|
|
3231
|
+
return person_text(opener) ?? NO_PROMPT;
|
|
3232
|
+
}
|
|
3233
|
+
function prompt_time(tail) {
|
|
3234
|
+
return tail.split("\n").filter((line) => filing_cap.is_prompt_line(line)).map((line) => person_line(line)?.timestamp ?? void 0).findLast((time) => time !== void 0);
|
|
3235
|
+
}
|
|
3236
|
+
function tail_time(tail) {
|
|
3237
|
+
return tail.split("\n").map((line) => TIMED_LINE_SCHEMA.safeParse(json_value.parse_or_undefined(line)).data?.timestamp).find((time) => time !== void 0);
|
|
3238
|
+
}
|
|
3239
|
+
function pause_start(tail) {
|
|
3240
|
+
return prompt_time(tail) ?? tail_time(tail);
|
|
3241
|
+
}
|
|
3242
|
+
var last_prompt = { pause_start, prompt_text, turn_prompt };
|
|
3243
|
+
|
|
3244
|
+
// scripts/run/cut/run-cut-report.ts
|
|
3245
|
+
var SUCCESS_EXIT_CODE4 = 0;
|
|
3246
|
+
var FAILURE_EXIT_CODE5 = 1;
|
|
3247
|
+
var CUT_VERDICT = "cut";
|
|
3248
|
+
var RESUME_VERDICT = "resume";
|
|
3249
|
+
var RESUME_IMPL_VERDICT = "resume-impl";
|
|
3250
|
+
var FRESH_VERDICT = "fresh";
|
|
3251
|
+
var UNDER_THRESHOLD_VERDICT = "under-threshold";
|
|
3252
|
+
var STALE_VERDICT = "stale";
|
|
3253
|
+
var INCOMPLETE_VERDICT = "incomplete";
|
|
3254
|
+
var BAD_HANDOFF_VERDICT = "bad-handoff";
|
|
3255
|
+
var HANDED_OFF_VERDICT = "handed-off";
|
|
3256
|
+
var BUSY_VERDICT2 = "busy";
|
|
3257
|
+
var NOT_A_LANE_VERDICT = "not-a-lane";
|
|
3258
|
+
var UNREADY_VERDICT = "unready";
|
|
3259
|
+
var FAILED_VERDICT = "failed";
|
|
3260
|
+
var ENDED_VERDICT = "ended";
|
|
3261
|
+
var UNREADABLE_VERDICT = "unreadable";
|
|
3262
|
+
var UNKNOWN_VERDICT2 = "unknown";
|
|
3263
|
+
var OVER_VERDICT = "over";
|
|
3264
|
+
function report4(verdict, code) {
|
|
3265
|
+
console.info(verdict);
|
|
3266
|
+
return code;
|
|
3267
|
+
}
|
|
3268
|
+
function report_stale(cut_record) {
|
|
3269
|
+
console.error(run_cut.stale_message(cut_record));
|
|
3270
|
+
return report4(STALE_VERDICT, FAILURE_EXIT_CODE5);
|
|
3271
|
+
}
|
|
3272
|
+
function report_busy2(cut_record) {
|
|
3273
|
+
console.error(run_cut.busy_message(cut_record));
|
|
3274
|
+
return report4(BUSY_VERDICT2, FAILURE_EXIT_CODE5);
|
|
3275
|
+
}
|
|
3276
|
+
function report_incomplete(cut_record) {
|
|
3277
|
+
console.error(run_cut.incomplete_message(cut_record));
|
|
3278
|
+
return report4(INCOMPLETE_VERDICT, FAILURE_EXIT_CODE5);
|
|
3279
|
+
}
|
|
3280
|
+
var HANDOFF_OVERFLOW_NOTE = "--handoff <path> would grow the cut record past its byte bound";
|
|
3281
|
+
var HANDOFF_MISSING_NOTE = "this cut resumes into implementation and needs --handoff <path>";
|
|
3282
|
+
function report_bad_handoff(note) {
|
|
3283
|
+
console.error(
|
|
3284
|
+
`${note}. Nothing was cut; write the handoff file at ${run_cut_handoff.HANDOFF_PATH} and reissue.`
|
|
3285
|
+
);
|
|
3286
|
+
return report4(BAD_HANDOFF_VERDICT, FAILURE_EXIT_CODE5);
|
|
3287
|
+
}
|
|
3288
|
+
function report_handed_off(cut_record) {
|
|
3289
|
+
console.error(run_cut.handed_off_message(cut_record));
|
|
3290
|
+
return report4(HANDED_OFF_VERDICT, SUCCESS_EXIT_CODE4);
|
|
3291
|
+
}
|
|
3292
|
+
function report_unreadable() {
|
|
3293
|
+
console.error(run_cut.unreadable_message());
|
|
3294
|
+
return report4(UNREADABLE_VERDICT, FAILURE_EXIT_CODE5);
|
|
3295
|
+
}
|
|
3296
|
+
function report_fault(message3) {
|
|
3297
|
+
console.error(message3);
|
|
3298
|
+
return report4(UNKNOWN_VERDICT2, FAILURE_EXIT_CODE5);
|
|
3299
|
+
}
|
|
3300
|
+
function report_unknown2() {
|
|
3301
|
+
return report_fault(run_cut.unknown_message());
|
|
3302
|
+
}
|
|
3303
|
+
function refuse_unready(state) {
|
|
3304
|
+
console.error(
|
|
3305
|
+
`Not ready to cut on ${state.branch} (dirty: ${String(state.is_dirty)}); a cut needs an uncommitted lane branch. Nothing was cut.`
|
|
3306
|
+
);
|
|
3307
|
+
return report4(UNREADY_VERDICT, FAILURE_EXIT_CODE5);
|
|
3308
|
+
}
|
|
3309
|
+
function report_under_threshold() {
|
|
3310
|
+
console.error(
|
|
3311
|
+
"The current context is under the shared cut threshold, so nothing was cut; a resume would cost more than the accumulation it would drop. Continue to the gate."
|
|
3312
|
+
);
|
|
3313
|
+
return report4(UNDER_THRESHOLD_VERDICT, SUCCESS_EXIT_CODE4);
|
|
3314
|
+
}
|
|
3315
|
+
function report_reviewer() {
|
|
3316
|
+
console.error(
|
|
3317
|
+
"This session is a ship reviewer, which its supervisor is waiting on, so nothing was cut and nothing was relaunched. Write the findings file and finish the review in this process."
|
|
3318
|
+
);
|
|
3319
|
+
return report4(NOT_A_LANE_VERDICT, SUCCESS_EXIT_CODE4);
|
|
3320
|
+
}
|
|
3321
|
+
function report_held_cut(issue) {
|
|
3322
|
+
console.error(
|
|
3323
|
+
`The cut is recorded for ${session_cite.issue(issue)} outside a lane, so nothing was relaunched. Keep the hold, send a \`confirmation\` Telegram whose body names the resume command \`fullrun ${issue_cite.plain(issue)}\`, and end the turn; the fresh session's \`pnpm josh run:cut --resume ${issue}\` answers \`resume-impl\` with the handoff.`
|
|
3324
|
+
);
|
|
3325
|
+
return report4(CUT_VERDICT, SUCCESS_EXIT_CODE4);
|
|
3326
|
+
}
|
|
3327
|
+
var RELAUNCHED_CUT_MARKER = "cut-relaunched";
|
|
3328
|
+
function report_relaunched_cut() {
|
|
3329
|
+
console.error(RELAUNCHED_CUT_MARKER);
|
|
3330
|
+
return report4(CUT_VERDICT, SUCCESS_EXIT_CODE4);
|
|
3331
|
+
}
|
|
3332
|
+
function report_over() {
|
|
3333
|
+
console.error(
|
|
3334
|
+
"This session is over the shared context-cut threshold (`pnpm josh cost --cut` answers `over`), so the implementation cut was not resumed here and its record is kept. End this conversation and type the invocation again in a fresh session."
|
|
3335
|
+
);
|
|
3336
|
+
return report4(OVER_VERDICT, FAILURE_EXIT_CODE5);
|
|
3337
|
+
}
|
|
3338
|
+
var run_cut_report = {
|
|
3339
|
+
BAD_HANDOFF_VERDICT,
|
|
3340
|
+
BUSY_VERDICT: BUSY_VERDICT2,
|
|
3341
|
+
CUT_VERDICT,
|
|
3342
|
+
ENDED_VERDICT,
|
|
3343
|
+
FAILED_VERDICT,
|
|
3344
|
+
FAILURE_EXIT_CODE: FAILURE_EXIT_CODE5,
|
|
3345
|
+
FRESH_VERDICT,
|
|
3346
|
+
HANDED_OFF_VERDICT,
|
|
3347
|
+
HANDOFF_MISSING_NOTE,
|
|
3348
|
+
HANDOFF_OVERFLOW_NOTE,
|
|
3349
|
+
INCOMPLETE_VERDICT,
|
|
3350
|
+
NOT_A_LANE_VERDICT,
|
|
3351
|
+
OVER_VERDICT,
|
|
3352
|
+
RELAUNCHED_CUT_MARKER,
|
|
3353
|
+
RESUME_IMPL_VERDICT,
|
|
3354
|
+
RESUME_VERDICT,
|
|
3355
|
+
STALE_VERDICT,
|
|
3356
|
+
SUCCESS_EXIT_CODE: SUCCESS_EXIT_CODE4,
|
|
3357
|
+
UNDER_THRESHOLD_VERDICT,
|
|
3358
|
+
UNKNOWN_VERDICT: UNKNOWN_VERDICT2,
|
|
3359
|
+
UNREADABLE_VERDICT,
|
|
3360
|
+
UNREADY_VERDICT,
|
|
3361
|
+
refuse_unready,
|
|
3362
|
+
report: report4,
|
|
3363
|
+
report_bad_handoff,
|
|
3364
|
+
report_busy: report_busy2,
|
|
3365
|
+
report_fault,
|
|
3366
|
+
report_handed_off,
|
|
3367
|
+
report_held_cut,
|
|
3368
|
+
report_incomplete,
|
|
3369
|
+
report_over,
|
|
3370
|
+
report_relaunched_cut,
|
|
3371
|
+
report_reviewer,
|
|
3372
|
+
report_stale,
|
|
3373
|
+
report_under_threshold,
|
|
3374
|
+
report_unknown: report_unknown2,
|
|
3375
|
+
report_unreadable
|
|
3376
|
+
};
|
|
3377
|
+
|
|
3378
|
+
// scripts/run/carry/run-carry-ended.ts
|
|
3379
|
+
import path4 from "node:path";
|
|
3380
|
+
import { z as z6 } from "zod";
|
|
3381
|
+
var ENDED_PREFIX = "josh-run-ended-";
|
|
3382
|
+
var TRANSCRIPT_SUFFIX = ".jsonl";
|
|
3383
|
+
var ended_run_schema = z6.object({
|
|
3384
|
+
invocation: z6.string(),
|
|
3385
|
+
started_at: z6.string(),
|
|
3386
|
+
ended_at: z6.string(),
|
|
3387
|
+
stopped: z6.string().optional(),
|
|
3388
|
+
session: z6.string().optional()
|
|
3389
|
+
});
|
|
3390
|
+
function ended_path(git_directory) {
|
|
3391
|
+
return stamp_file.stamp_path(ENDED_PREFIX, git_directory);
|
|
3392
|
+
}
|
|
3393
|
+
function session_of(transcript) {
|
|
3394
|
+
return transcript === void 0 ? void 0 : path4.basename(transcript, TRANSCRIPT_SUFFIX);
|
|
3395
|
+
}
|
|
3396
|
+
function stop_of(transcript, stopped) {
|
|
3397
|
+
return stopped === void 0 ? {} : { stopped, session: session_of(transcript) };
|
|
3398
|
+
}
|
|
3399
|
+
function record_ended(target, read2, now = /* @__PURE__ */ new Date(), stopped) {
|
|
3400
|
+
if (read2.kind !== "carried") return;
|
|
3401
|
+
const { invocation, started_at, owner_transcript } = read2.carry;
|
|
3402
|
+
const ended_at = now.toISOString();
|
|
3403
|
+
stamp_file.replace_stamp(target, {
|
|
3404
|
+
invocation,
|
|
3405
|
+
started_at,
|
|
3406
|
+
ended_at,
|
|
3407
|
+
...stop_of(owner_transcript, stopped)
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3410
|
+
function parse_ended(raw) {
|
|
3411
|
+
return json_value.parse_with(raw, ended_run_schema);
|
|
3412
|
+
}
|
|
3413
|
+
function read_ended(target) {
|
|
3414
|
+
const raw = stamp_file.read_stamp_text(target);
|
|
3415
|
+
return raw === void 0 ? void 0 : parse_ended(raw);
|
|
3416
|
+
}
|
|
3417
|
+
function has_ended(target, started_at) {
|
|
3418
|
+
const ended = read_ended(target);
|
|
3419
|
+
return ended !== void 0 && ended.started_at === started_at;
|
|
3420
|
+
}
|
|
3421
|
+
var run_carry_ended = { ended_path, has_ended, read_ended, record_ended };
|
|
3422
|
+
|
|
3423
|
+
// scripts/run/wake/run-wake.ts
|
|
3424
|
+
import { z as z7 } from "zod";
|
|
3425
|
+
var WAKE_PREFIX = "josh-run-wake-";
|
|
3426
|
+
var WAKE_LOG_PREFIX = "josh-run-wake-log-";
|
|
3427
|
+
var WAKE_LOG_SUFFIX = ".log";
|
|
3428
|
+
var WAKE_GRACE_MS = 6e5;
|
|
3429
|
+
var MAX_WAKE_ATTEMPTS = 3;
|
|
3430
|
+
var IDLE_CEILING_MS = backlog_budget.DEFAULT_IDLE_MS;
|
|
1048
3431
|
var NO_WAKES = 0;
|
|
1049
3432
|
var ONE_WAKE = 1;
|
|
1050
3433
|
var WAKE_DECISION = { kind: "wake" };
|
|
@@ -1057,15 +3440,15 @@ var STOP_REASONS = {
|
|
|
1057
3440
|
unreadable: "unreadable"
|
|
1058
3441
|
};
|
|
1059
3442
|
var run_wake_schema = process_owner_schema.extend({
|
|
1060
|
-
invocation:
|
|
1061
|
-
started_at:
|
|
1062
|
-
woke:
|
|
1063
|
-
woke_at:
|
|
3443
|
+
invocation: z7.string(),
|
|
3444
|
+
started_at: z7.string(),
|
|
3445
|
+
woke: z7.number(),
|
|
3446
|
+
woke_at: z7.string().optional(),
|
|
1064
3447
|
// Every optional `RunWake` field must be listed: `z.object` strips undeclared ones on the round trip.
|
|
1065
|
-
attempts:
|
|
1066
|
-
woke_pid:
|
|
1067
|
-
spawned:
|
|
1068
|
-
idle_since:
|
|
3448
|
+
attempts: z7.number().optional(),
|
|
3449
|
+
woke_pid: z7.number().optional(),
|
|
3450
|
+
spawned: z7.array(z7.string()).optional(),
|
|
3451
|
+
idle_since: z7.string().optional(),
|
|
1069
3452
|
profile: agent_role_profile.PROFILE_SCHEMA.optional()
|
|
1070
3453
|
});
|
|
1071
3454
|
function is_overdue(marked_at, now, limit_ms = WAKE_GRACE_MS) {
|
|
@@ -1087,7 +3470,7 @@ function decide_not_handed_off(input) {
|
|
|
1087
3470
|
if (input.is_owner_live) return WAIT_DECISION;
|
|
1088
3471
|
return decide_launch(input);
|
|
1089
3472
|
}
|
|
1090
|
-
function
|
|
3473
|
+
function decide2(input) {
|
|
1091
3474
|
if (input.read.kind !== "carried") return { kind: "stop", reason: STOP_REASONS[input.read.kind] };
|
|
1092
3475
|
if (input.read.carry.is_handed_off !== true) return decide_not_handed_off(input);
|
|
1093
3476
|
return decide_launch(input);
|
|
@@ -1143,7 +3526,7 @@ function reclaim(target, wake) {
|
|
|
1143
3526
|
remove_wake(target);
|
|
1144
3527
|
return stamp_file.create_stamp(target, wake) ? wake : void 0;
|
|
1145
3528
|
}
|
|
1146
|
-
function
|
|
3529
|
+
function claim3(target, invocation, now, profile) {
|
|
1147
3530
|
const existing = read_wake(target);
|
|
1148
3531
|
if (is_held_by_live_supervisor(existing)) return void 0;
|
|
1149
3532
|
const wake = { ...fresh_wake(invocation, now, profile), ...carried_state(existing, invocation) };
|
|
@@ -1198,10 +3581,10 @@ var run_wake = {
|
|
|
1198
3581
|
IDLE_CEILING_MS,
|
|
1199
3582
|
MAX_WAKE_ATTEMPTS,
|
|
1200
3583
|
WAKE_GRACE_MS,
|
|
1201
|
-
claim,
|
|
3584
|
+
claim: claim3,
|
|
1202
3585
|
count_claim,
|
|
1203
3586
|
count_wake,
|
|
1204
|
-
decide,
|
|
3587
|
+
decide: decide2,
|
|
1205
3588
|
fresh_wake,
|
|
1206
3589
|
is_supervisor_live,
|
|
1207
3590
|
mark_idle,
|
|
@@ -1283,11 +3666,11 @@ var DEFAULT_PORTS3 = {
|
|
|
1283
3666
|
notify_stranded: real_notify_stranded
|
|
1284
3667
|
};
|
|
1285
3668
|
function input_at(ports, targets) {
|
|
1286
|
-
const
|
|
1287
|
-
if (
|
|
3669
|
+
const read2 = ports.read_carry(targets.carry);
|
|
3670
|
+
if (read2.kind !== "carried") return void 0;
|
|
1288
3671
|
return {
|
|
1289
|
-
read,
|
|
1290
|
-
is_owner_live: ports.is_owner_live(
|
|
3672
|
+
read: read2,
|
|
3673
|
+
is_owner_live: ports.is_owner_live(read2.carry),
|
|
1291
3674
|
supervisor: ports.supervisor_liveness(ports.read_wake(targets.wake))
|
|
1292
3675
|
};
|
|
1293
3676
|
}
|
|
@@ -1295,7 +3678,7 @@ async function gather2(ports) {
|
|
|
1295
3678
|
const targets = await ports.resolve_targets();
|
|
1296
3679
|
return targets === void 0 ? void 0 : input_at(ports, targets);
|
|
1297
3680
|
}
|
|
1298
|
-
async function
|
|
3681
|
+
async function report5(ports, input) {
|
|
1299
3682
|
if (input.read.kind !== "carried") return;
|
|
1300
3683
|
const text2 = run_stranded.describe(input.read.carry);
|
|
1301
3684
|
if (await ports.emit_stranded(text2)) await ports.notify_stranded(text2);
|
|
@@ -1304,7 +3687,7 @@ async function detect_and_report2(ports = DEFAULT_PORTS3) {
|
|
|
1304
3687
|
const input = await gather2(ports);
|
|
1305
3688
|
if (input === void 0) return run_stranded.OK;
|
|
1306
3689
|
const is_run_stranded = run_stranded.is_stranded(input);
|
|
1307
|
-
if (is_run_stranded) await
|
|
3690
|
+
if (is_run_stranded) await report5(ports, input);
|
|
1308
3691
|
return run_stranded.verdict_of(is_run_stranded);
|
|
1309
3692
|
}
|
|
1310
3693
|
async function run_stranded_check() {
|
|
@@ -1317,12 +3700,12 @@ function is_stranded_at(directory, ports = DEFAULT_PORTS3) {
|
|
|
1317
3700
|
const input = input_at(ports, targets_of(directory));
|
|
1318
3701
|
return input !== void 0 && run_stranded.is_stranded(input);
|
|
1319
3702
|
}
|
|
1320
|
-
function is_left_stranded(
|
|
1321
|
-
const
|
|
1322
|
-
if (
|
|
3703
|
+
function is_left_stranded(run3, ports = DEFAULT_PORTS3) {
|
|
3704
|
+
const read2 = ports.read_carry(run3.carry_target);
|
|
3705
|
+
if (read2.kind !== "carried") return !ports.has_ended(run3.ended_target, run3.started_at);
|
|
1323
3706
|
return run_stranded.is_stranded({
|
|
1324
|
-
read,
|
|
1325
|
-
is_owner_live: run_carry.is_held_by_another(
|
|
3707
|
+
read: read2,
|
|
3708
|
+
is_owner_live: run_carry.is_held_by_another(read2.carry, ports.is_owner_live),
|
|
1326
3709
|
supervisor: run_stranded.GONE
|
|
1327
3710
|
});
|
|
1328
3711
|
}
|
|
@@ -1334,10 +3717,23 @@ var run_stranded_detect = {
|
|
|
1334
3717
|
};
|
|
1335
3718
|
|
|
1336
3719
|
// scripts/hooks/stop-guard.ts
|
|
1337
|
-
async function
|
|
3720
|
+
async function held_record() {
|
|
1338
3721
|
const directory = await run_hold.worktree_directory();
|
|
1339
|
-
|
|
1340
|
-
return
|
|
3722
|
+
if (directory === void 0) return void 0;
|
|
3723
|
+
return run_halfrun_resume.hold_of(run_hold.read_hold(run_hold.hold_path(directory)));
|
|
3724
|
+
}
|
|
3725
|
+
function was_notified(hold, tail) {
|
|
3726
|
+
if (lane_park.mentions_confirmation_notify(tail)) return true;
|
|
3727
|
+
return hold !== void 0 && run_hold_session.is_notified_since(hold, last_prompt.pause_start(tail));
|
|
3728
|
+
}
|
|
3729
|
+
async function hold_facts(tail) {
|
|
3730
|
+
const hold = await held_record();
|
|
3731
|
+
return {
|
|
3732
|
+
hold_issue: hold?.issue,
|
|
3733
|
+
hold_owned: hold === void 0 || run_hold_session.is_session_hold(hold),
|
|
3734
|
+
notified: was_notified(hold, tail),
|
|
3735
|
+
prrun_stopped: hold?.prrun_stop_head !== void 0
|
|
3736
|
+
};
|
|
1341
3737
|
}
|
|
1342
3738
|
function transcript_tail(transcript_path) {
|
|
1343
3739
|
const transcript = time_hook_transcript.transcript_of(transcript_path, void 0);
|
|
@@ -1346,6 +3742,14 @@ function transcript_tail(transcript_path) {
|
|
|
1346
3742
|
function was_filed(tail) {
|
|
1347
3743
|
return filing_cap.turn_filing_count(tail) > 0;
|
|
1348
3744
|
}
|
|
3745
|
+
function is_cut_stop(tail) {
|
|
3746
|
+
if (run_cut.carried_cut_sync() !== void 0) return true;
|
|
3747
|
+
return tail_commands.answered(
|
|
3748
|
+
filing_cap.current_turn(tail),
|
|
3749
|
+
pre_gate_cut.takes_the_cut,
|
|
3750
|
+
run_cut_report.RELAUNCHED_CUT_MARKER
|
|
3751
|
+
);
|
|
3752
|
+
}
|
|
1349
3753
|
function session_lang() {
|
|
1350
3754
|
return session_language.resolve_session_lang().lang;
|
|
1351
3755
|
}
|
|
@@ -1360,13 +3764,13 @@ async function session_role_facts() {
|
|
|
1360
3764
|
async function build_context(transcript_path, payload_tail) {
|
|
1361
3765
|
const tail = transcript_tail(transcript_path);
|
|
1362
3766
|
return {
|
|
1363
|
-
...await hold_facts(),
|
|
3767
|
+
...await hold_facts(tail),
|
|
1364
3768
|
tree_clean: !await run_hold.is_tree_dirty(),
|
|
1365
|
-
notified: lane_park.mentions_confirmation_notify(tail),
|
|
1366
3769
|
message: payload_tail.message,
|
|
1367
3770
|
prompt: last_prompt.prompt_text(tail),
|
|
3771
|
+
answers_question: stop_decision.is_question(last_prompt.turn_prompt(tail)),
|
|
1368
3772
|
stop_hook_active: payload_tail.stop_hook_active,
|
|
1369
|
-
cut_pending:
|
|
3773
|
+
cut_pending: is_cut_stop(tail),
|
|
1370
3774
|
filed: was_filed(tail),
|
|
1371
3775
|
session_owner: repo_party.current_owner(),
|
|
1372
3776
|
...await session_role_facts(),
|
|
@@ -1397,8 +3801,8 @@ async function carried_owner_live() {
|
|
|
1397
3801
|
try {
|
|
1398
3802
|
const directory = await run_carry.repository_directory();
|
|
1399
3803
|
if (directory === void 0) return void 0;
|
|
1400
|
-
const
|
|
1401
|
-
return
|
|
3804
|
+
const read2 = run_carry.read_carry(run_carry.carry_path(directory));
|
|
3805
|
+
return read2.kind === "carried" ? run_carry.is_owner_live(read2.carry) : void 0;
|
|
1402
3806
|
} catch {
|
|
1403
3807
|
return void 0;
|
|
1404
3808
|
}
|
|
@@ -1412,13 +3816,13 @@ async function run_report_checks() {
|
|
|
1412
3816
|
async function write_stop_decision(raw_payload) {
|
|
1413
3817
|
hook_decision.load_environment_file();
|
|
1414
3818
|
await run_report_checks();
|
|
1415
|
-
const
|
|
1416
|
-
if (
|
|
1417
|
-
process.stdout.write(`${
|
|
3819
|
+
const envelope = await stop_actions2.envelope_of(await outcome_of(raw_payload), session_lang());
|
|
3820
|
+
if (envelope === void 0) return;
|
|
3821
|
+
process.stdout.write(`${envelope}
|
|
1418
3822
|
`);
|
|
1419
3823
|
}
|
|
1420
3824
|
var stop_guard = { outcome_of, stop_outcome_for_payload };
|
|
1421
|
-
if (process.argv[1] ===
|
|
3825
|
+
if (process.argv[1] === fileURLToPath5(import.meta.url)) {
|
|
1422
3826
|
if (process.stdin.isTTY) hook_decision.report_no_payload("stop:guard");
|
|
1423
3827
|
else await write_stop_decision(await text(process.stdin));
|
|
1424
3828
|
}
|