@joshuafolkken/kit 1.1471.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/epic-commands/SKILL.md +2 -14
- package/.claude/skills/epic-commands/epic-bundle.md +9 -5
- package/.claude/skills/epic-commands/execution-waves.md +1 -2
- package/.claude/skills/verify-ui/SKILL.md +1 -2
- package/.claude/skills/workflow-commands/SKILL.md +7 -9
- package/.claude/skills/workflow-commands/background-commands.md +3 -7
- package/.claude/skills/workflow-commands/backlogrun-child.md +3 -10
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +6 -15
- package/.claude/skills/workflow-commands/backlogrun-park.md +2 -8
- package/.claude/skills/workflow-commands/backlogrun-progress.md +10 -19
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +8 -8
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -10
- package/.claude/skills/workflow-commands/backlogrun.md +31 -50
- package/.claude/skills/workflow-commands/chain-rule.md +7 -6
- 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 +3 -6
- 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 +3 -3
- package/.claude/skills/workflow-commands/issue-scout.md +3 -2
- 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 +0 -14
- package/.claude/skills/workflow-commands/observation-ledger.md +4 -9
- package/.claude/skills/workflow-commands/pre-gate-cut.md +11 -16
- package/.claude/skills/workflow-commands/prerequisite.md +1 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +2 -7
- package/.claude/skills/workflow-commands/prrun.md +3 -5
- package/.claude/skills/workflow-commands/retrospective.md +4 -4
- package/.claude/skills/workflow-commands/split-assessment.md +1 -3
- package/.claude/skills/workflow-commands/target-repository.md +1 -2
- package/.claude/skills/workflow-commands/working-tree-hold.md +2 -4
- 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 +1 -1
- package/cspell/index.yaml +7 -0
- package/dist/CLAUDE.md +1 -1
- 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-J47HU5H5.js → chunk-6TS2JD6D.js} +3 -3
- package/dist/hooks/chunk-7DSBJVFH.js +50 -0
- package/dist/hooks/{chunk-YHBZAJSV.js → chunk-A65Y4YOR.js} +3 -3
- package/dist/hooks/{chunk-E7OIKX5Z.js → chunk-APUO6U4J.js} +85 -7
- package/dist/hooks/{chunk-IX6TTTI6.js → chunk-EFXE6C37.js} +8 -22
- package/dist/hooks/chunk-GP4IEC4O.js +20 -0
- package/dist/hooks/{chunk-SHGPANFB.js → chunk-JP45SOCJ.js} +3 -3
- package/dist/hooks/{chunk-K4EOONYM.js → chunk-LAOV7NLM.js} +1 -1
- package/dist/hooks/chunk-M4HGZV7U.js +83 -0
- package/dist/hooks/{chunk-KC5AK2GF.js → chunk-O6GDRNJB.js} +3 -3
- package/dist/hooks/{chunk-YYPI7SJZ.js → chunk-PF2NZL2A.js} +5 -5
- package/dist/hooks/{chunk-H5HVWYXK.js → chunk-PTICX4OL.js} +3 -4
- package/dist/hooks/{chunk-IIGFYXKC.js → chunk-RT27QRJW.js} +581 -1438
- package/dist/hooks/{chunk-43ZHT22P.js → chunk-SYOTDYCI.js} +1127 -1089
- package/dist/hooks/{chunk-AS74OU3D.js → chunk-TNUMY6NL.js} +1507 -1336
- package/dist/hooks/chunk-UPUZF3AN.js +279 -0
- package/dist/hooks/{chunk-EUMACUK2.js → chunk-WK6SMBIC.js} +1948 -806
- 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 +317 -264
- package/dist/hooks/investigation-guard.js +8 -11
- package/dist/hooks/pretool-guard.js +16 -13
- package/dist/hooks/session-lang.js +370 -25
- package/dist/hooks/stop-guard.js +2576 -176
- package/dist/josh.js +2124 -277
- package/lefthook/base.yml +5 -3
- package/package.json +12 -10
- package/prompts/collaboration-workflow/file-edits.md +1 -3
- package/prompts/collaboration-workflow/gh-rest.md +1 -2
- package/prompts/collaboration-workflow/issue-citation.md +2 -4
- package/prompts/collaboration-workflow/issue-template.md +10 -2
- package/prompts/collaboration-workflow/lane-instructions.md +35 -0
- package/prompts/collaboration-workflow/operating-rules.md +2 -6
- package/prompts/collaboration-workflow/output-bounds.md +3 -3
- package/prompts/collaboration-workflow/overview.md +1 -1
- package/prompts/collaboration-workflow/principles.md +14 -8
- package/prompts/collaboration-workflow/report-format.md +9 -0
- package/prompts/collaboration-workflow/residency.md +12 -8
- package/prompts/collaboration-workflow/rule-delivery.md +3 -11
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +5 -5
- package/prompts/collaboration-workflow/upstream-interrupt.md +2 -2
- package/prompts/collaboration-workflow/wip-cap.md +6 -6
- package/prompts/collaboration-workflow.md +1 -0
- package/prompts/review.md +4 -5
- package/prompts/testing-guide.md +2 -2
- package/scripts/agent/agent-argv.ts +20 -4
- 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 +2 -0
- package/scripts/agent/lane-instructions.ts +144 -0
- package/scripts/backlog/backlog-budget.ts +4 -4
- package/scripts/backlog/backlog-drive-named.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +9 -1
- package/scripts/backlog/backlog-occupied.ts +56 -0
- package/scripts/backlog/backlog-ready.ts +47 -10
- package/scripts/delegation/duplicate-read-guard.ts +2 -2
- package/scripts/delegation/investigation-guard.ts +15 -65
- package/scripts/delegation/investigation-reads.ts +0 -1
- package/scripts/document/document-byte-budget.ts +6 -5
- package/scripts/document/document-section-cli.ts +17 -5
- package/scripts/document/entry-read-budget.ts +2 -2
- package/scripts/document/read-set-cli.ts +4 -1
- package/scripts/epic/epic-audit-orphans.ts +5 -8
- package/scripts/epic/epic-audit.ts +52 -1
- 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/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 +58 -125
- 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 +13 -1
- 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/pre-commit-lint.ts +91 -0
- package/scripts/hooks/pre-push-unit.ts +1 -1
- package/scripts/hooks/pretool-guard.ts +27 -52
- package/scripts/hooks/reserved-run.ts +1 -0
- package/scripts/hooks/run-hook.sh +10 -12
- package/scripts/hooks/stop-actions.ts +204 -0
- package/scripts/hooks/stop-guard.ts +62 -21
- package/scripts/init/hook-launch.ts +12 -7
- 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 +19 -14
- package/scripts/issue/markdown-section.ts +19 -5
- package/scripts/josh/command-usage.ts +110 -0
- 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-capacity.ts +8 -2
- package/scripts/lane/lane-check-stats.ts +56 -0
- package/scripts/lane/lane-child-invocation.ts +35 -4
- package/scripts/lane/lane-dispatch.ts +3 -3
- package/scripts/lane/lane-guard-policy.ts +2 -2
- package/scripts/lane/lane-handoff.ts +8 -1
- package/scripts/lane/lane-install.ts +12 -4
- package/scripts/lane/lane-ledger.ts +43 -1
- package/scripts/lane/lane-limit-cli.ts +8 -9
- package/scripts/lane/lane-measure-cli.ts +2 -0
- package/scripts/lane/lane-preserve.ts +29 -1
- package/scripts/lane/lane-registry.ts +9 -7
- package/scripts/lane/lane-restart.ts +5 -26
- package/scripts/lane/lane-session-action.ts +146 -0
- package/scripts/lane/lane-session-broker.ts +57 -138
- 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/notify/telegram-test.ts +10 -5
- package/scripts/report/report-format-reference.ts +2 -0
- 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/rules/agent-model.ts +59 -0
- package/scripts/rules/delivered-rules.ts +11 -2
- package/scripts/rules/filing-cap.ts +1 -0
- package/scripts/rules/firing-point.ts +6 -5
- package/scripts/rules/guard-remedy.ts +117 -0
- package/scripts/rules/issue-comments.ts +67 -6
- package/scripts/rules/last-prompt.ts +50 -5
- package/scripts/rules/oracle-list-cli.ts +7 -0
- package/scripts/rules/piped-verification.ts +3 -1
- 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/shell-segments.ts +15 -0
- package/scripts/rules/stop-decision.ts +77 -0
- package/scripts/rules/stop-rules.ts +147 -47
- package/scripts/rules/tail-commands.ts +15 -1
- 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 +9 -1
- package/scripts/run/board/run-board-header.ts +4 -1
- 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 +16 -0
- package/scripts/run/board/run-board-reason.ts +10 -1
- 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 +5 -0
- package/scripts/run/board/run-board-status.ts +72 -21
- package/scripts/run/board/run-board-tick.ts +5 -1
- package/scripts/run/cut/run-cut-cli.ts +3 -2
- package/scripts/run/cut/run-cut-report.ts +16 -0
- 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-session.ts +46 -0
- package/scripts/run/hold/run-hold.ts +7 -1
- package/scripts/run/launch-priority.ts +32 -0
- package/scripts/run/merge/run-merge-cli.ts +16 -65
- 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 +21 -9
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-poll-hook.ts +157 -0
- package/scripts/run/run-step-cli.ts +22 -15
- package/scripts/run/run-step-section.ts +85 -0
- 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 +12 -10
- 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/test/test-e2e-guard.ts +30 -5
- package/scripts/test/test-unit-guard.ts +16 -1
- package/scripts/test/unit-worker-share.ts +26 -4
- package/scripts/time-runtime/time-shell.ts +29 -4
- package/scripts/time-runtime/time-spans.ts +1 -0
- package/scripts/time-runtime/time-tool-call.ts +7 -0
- package/templates/gitignore +6 -2
- package/dist/hooks/chunk-K4LMBQC2.js +0 -68
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,33 @@ 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);
|
|
245
607
|
}
|
|
246
608
|
async function free_lanes_beside(lanes) {
|
|
247
609
|
const limit = await lane_capacity.lane_limit();
|
|
248
610
|
if (limit.kind !== "limit") return void 0;
|
|
249
|
-
return
|
|
611
|
+
return free_of(limit.limit, await lane_use(lanes));
|
|
250
612
|
}
|
|
251
613
|
async function free_under(limit) {
|
|
252
|
-
return
|
|
614
|
+
return free_of(limit, await lane_use());
|
|
253
615
|
}
|
|
254
616
|
async function free_lane_count() {
|
|
255
617
|
return await free_lanes_beside(await lane_registry.list_lanes()) ?? NO_FREE;
|
|
@@ -278,8 +640,8 @@ async function read_ready(ports = DEFAULT_PORTS) {
|
|
|
278
640
|
}
|
|
279
641
|
function ready_line(reading, slug) {
|
|
280
642
|
if (reading.issues.length === NONE2 || reading.free_lanes <= NO_FREE) return void 0;
|
|
281
|
-
const
|
|
282
|
-
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(" ");
|
|
283
645
|
return `ready ${issues} \xB7 free lanes ${String(reading.free_lanes)}`;
|
|
284
646
|
}
|
|
285
647
|
async function parent_ready_line(ports, is_parent, read_repo) {
|
|
@@ -310,7 +672,8 @@ var backlog_ready = {
|
|
|
310
672
|
print_offer_hint,
|
|
311
673
|
free_lane_count,
|
|
312
674
|
free_lanes_beside,
|
|
313
|
-
|
|
675
|
+
lane_use,
|
|
676
|
+
occupied_lanes: occupied_lanes2,
|
|
314
677
|
drive_free_lane_count,
|
|
315
678
|
drains_pool,
|
|
316
679
|
print_ready_line,
|
|
@@ -663,46 +1026,400 @@ async function run_stall_check(ready = backlog_ready.DEFAULT_PORTS) {
|
|
|
663
1026
|
}
|
|
664
1027
|
var backlog_stalled_detect = { detect_and_report, run_stall_check };
|
|
665
1028
|
|
|
666
|
-
// scripts/
|
|
1029
|
+
// scripts/issue/issue-cite-cli.ts
|
|
1030
|
+
import { fileURLToPath } from "node:url";
|
|
667
1031
|
import { z as z2 } from "zod";
|
|
668
|
-
var
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
]
|
|
675
|
-
var
|
|
676
|
-
var
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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;
|
|
686
1070
|
}
|
|
687
|
-
function
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
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;
|
|
691
1074
|
}
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
(
|
|
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)
|
|
696
1099
|
);
|
|
1100
|
+
return split_results(results);
|
|
697
1101
|
}
|
|
698
|
-
function
|
|
699
|
-
|
|
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);
|
|
700
1109
|
}
|
|
701
|
-
function
|
|
702
|
-
|
|
703
|
-
|
|
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
|
+
});
|
|
1312
|
+
}
|
|
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);
|
|
704
1413
|
}
|
|
705
|
-
|
|
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 };
|
|
706
1423
|
|
|
707
1424
|
// scripts/rules/stop-rules.ts
|
|
708
1425
|
import { z as z3 } from "zod";
|
|
@@ -721,21 +1438,21 @@ var OWNER_SEPARATOR = "/";
|
|
|
721
1438
|
function is_offer_line(line) {
|
|
722
1439
|
return OFFER_PATTERNS.some((pattern) => pattern.test(line));
|
|
723
1440
|
}
|
|
724
|
-
function offers_filing(
|
|
725
|
-
return issue_citation.prose_lines(
|
|
1441
|
+
function offers_filing(message3) {
|
|
1442
|
+
return issue_citation.prose_lines(message3).some((line) => is_offer_line(line));
|
|
726
1443
|
}
|
|
727
|
-
function url_owners(
|
|
728
|
-
return [...
|
|
1444
|
+
function url_owners(message3) {
|
|
1445
|
+
return [...message3.matchAll(GITHUB_URL_OWNER)].map((match) => match[1] ?? "");
|
|
729
1446
|
}
|
|
730
|
-
function qualified_owners(
|
|
731
|
-
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] ?? "");
|
|
732
1449
|
}
|
|
733
|
-
function mentioned_owners(
|
|
734
|
-
return [...url_owners(
|
|
1450
|
+
function mentioned_owners(message3) {
|
|
1451
|
+
return [...url_owners(message3), ...qualified_owners(message3)];
|
|
735
1452
|
}
|
|
736
|
-
function is_first_party_target(
|
|
1453
|
+
function is_first_party_target(message3, session_owner) {
|
|
737
1454
|
if (session_owner === void 0) return false;
|
|
738
|
-
return mentioned_owners(
|
|
1455
|
+
return mentioned_owners(message3).every(
|
|
739
1456
|
(owner) => repo_party.classify(session_owner, owner) === repo_party.FIRST_PARTY
|
|
740
1457
|
);
|
|
741
1458
|
}
|
|
@@ -777,10 +1494,10 @@ function letter_pattern(script) {
|
|
|
777
1494
|
return void 0;
|
|
778
1495
|
}
|
|
779
1496
|
}
|
|
780
|
-
function prose_of(
|
|
781
|
-
let text2 =
|
|
1497
|
+
function prose_of(message3) {
|
|
1498
|
+
let text2 = message3;
|
|
782
1499
|
for (const pattern of CODE_PATTERNS) text2 = text2.replaceAll(pattern, " ");
|
|
783
|
-
return text2.replaceAll(ASCII_RUN_PATTERN, (
|
|
1500
|
+
return text2.replaceAll(ASCII_RUN_PATTERN, (run3) => run3.includes("/") ? " " : run3);
|
|
784
1501
|
}
|
|
785
1502
|
function letter_count(text2) {
|
|
786
1503
|
return text2.match(LETTER_PATTERN)?.length ?? 0;
|
|
@@ -799,17 +1516,46 @@ function matches_share(prose, script, pattern) {
|
|
|
799
1516
|
const matched = lines.filter((line) => is_line_in_script(line, script, pattern));
|
|
800
1517
|
return matched.length / lines.length;
|
|
801
1518
|
}
|
|
802
|
-
function is_mismatch(
|
|
1519
|
+
function is_mismatch(message3, lang) {
|
|
803
1520
|
const script = script_of(lang);
|
|
804
1521
|
if (script === void 0) return false;
|
|
805
1522
|
const pattern = letter_pattern(script);
|
|
806
1523
|
if (pattern === void 0) return false;
|
|
807
|
-
const prose = prose_of(
|
|
1524
|
+
const prose = prose_of(message3);
|
|
808
1525
|
if (letter_count(prose) < MIN_PROSE_LETTERS) return false;
|
|
809
1526
|
return matches_share(prose, script, pattern) < MATCH_SHARE_FLOOR;
|
|
810
1527
|
}
|
|
811
1528
|
var reply_language = { is_mismatch, script_of };
|
|
812
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
|
+
|
|
813
1559
|
// scripts/rules/stop-rules.ts
|
|
814
1560
|
var SWITCH_ENV_KEY = "JOSH_STOP_GUARD";
|
|
815
1561
|
var BLOCK_DECISION = "block";
|
|
@@ -818,7 +1564,7 @@ var stop_payload_schema = z3.object({
|
|
|
818
1564
|
stop_hook_active: z3.boolean().nullish(),
|
|
819
1565
|
last_assistant_message: z3.string().nullish()
|
|
820
1566
|
});
|
|
821
|
-
var NO_OUTCOME = { reason: void 0 };
|
|
1567
|
+
var NO_OUTCOME = { reason: void 0, actions: [], blocks_on_failure: false };
|
|
822
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.';
|
|
823
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.";
|
|
824
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`).";
|
|
@@ -836,8 +1582,8 @@ var HEADLESS_WAIT_REASON = `${HEADLESS_WAIT_MARKER}${HEADLESS_WAIT_BODY}`;
|
|
|
836
1582
|
var INDEX_COMMAND_PATTERN = /git (?:add|stage|rm --cached|restore --staged|restore -S)\b/u;
|
|
837
1583
|
var PERMISSION_PATTERN = /許可|承認|permission|approve|approval|authorization/iu;
|
|
838
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.";
|
|
839
|
-
function asks_index_permission(
|
|
840
|
-
return INDEX_COMMAND_PATTERN.test(
|
|
1585
|
+
function asks_index_permission(message3) {
|
|
1586
|
+
return INDEX_COMMAND_PATTERN.test(message3) && PERMISSION_PATTERN.test(message3);
|
|
841
1587
|
}
|
|
842
1588
|
function needs_index_route(context) {
|
|
843
1589
|
return context.lane_child && asks_index_permission(context.message);
|
|
@@ -860,26 +1606,29 @@ function needs_filing(context) {
|
|
|
860
1606
|
return filing_offer.offers_filing(context.message) && filing_offer.is_first_party_target(context.message, context.session_owner);
|
|
861
1607
|
}
|
|
862
1608
|
function is_held_idle(context) {
|
|
863
|
-
|
|
1609
|
+
if (context.hold_issue === void 0 || !context.hold_owned) return false;
|
|
1610
|
+
return !context.agent_pending && !context.handed_off;
|
|
864
1611
|
}
|
|
865
1612
|
function needs_notify(context) {
|
|
866
|
-
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;
|
|
867
1617
|
}
|
|
868
1618
|
function needs_release(context) {
|
|
869
1619
|
return is_held_idle(context) && context.tree_clean && !context.prrun_stopped;
|
|
870
1620
|
}
|
|
871
|
-
function
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
return build_citation_reason(references);
|
|
875
|
-
}
|
|
876
|
-
function wording_reason(context) {
|
|
877
|
-
if (context.headless_agent) return void 0;
|
|
878
|
-
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);
|
|
879
1624
|
}
|
|
880
1625
|
function reply_reason(context) {
|
|
881
1626
|
if (needs_filing(context)) return FILING_OFFER_REASON;
|
|
882
|
-
|
|
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;
|
|
883
1632
|
}
|
|
884
1633
|
function is_headless_held(context) {
|
|
885
1634
|
if (!context.headless_waiting) return false;
|
|
@@ -892,23 +1641,61 @@ function count_headless_refusals(tail) {
|
|
|
892
1641
|
function needs_background_wait(context) {
|
|
893
1642
|
return context.lane_child && context.background_pending;
|
|
894
1643
|
}
|
|
895
|
-
function
|
|
1644
|
+
function lane_reason(context) {
|
|
896
1645
|
if (needs_index_route(context)) return LANE_INDEX_REASON;
|
|
897
1646
|
if (needs_background_wait(context)) return lane_background.LANE_BACKGROUND_STOP_REASON;
|
|
898
|
-
if (needs_notify(context)) return STOP_NOTIFY_REASON;
|
|
899
|
-
if (needs_release(context)) return HOLD_RELEASE_REASON;
|
|
900
1647
|
return void 0;
|
|
901
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
|
+
}
|
|
902
1673
|
function block_reason(context) {
|
|
903
1674
|
if (is_headless_held(context)) return HEADLESS_WAIT_REASON;
|
|
904
1675
|
if (context.stop_hook_active || context.cut_pending) return void 0;
|
|
905
|
-
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;
|
|
906
1680
|
}
|
|
907
1681
|
function stop_outcome(context) {
|
|
908
|
-
|
|
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 };
|
|
909
1687
|
}
|
|
910
|
-
function
|
|
911
|
-
|
|
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 });
|
|
912
1699
|
}
|
|
913
1700
|
function parse_stop_payload(raw_payload) {
|
|
914
1701
|
const parsed = stop_payload_schema.safeParse(JSON.parse(raw_payload));
|
|
@@ -930,32 +1717,51 @@ var stop_rules = {
|
|
|
930
1717
|
block_envelope,
|
|
931
1718
|
build_language_reason,
|
|
932
1719
|
count_headless_refusals,
|
|
1720
|
+
fallback_reason,
|
|
933
1721
|
is_enabled,
|
|
1722
|
+
notice_envelope,
|
|
934
1723
|
parse_stop_payload,
|
|
935
1724
|
stop_outcome
|
|
936
1725
|
};
|
|
937
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
|
+
|
|
938
1744
|
// scripts/run/run-halfrun-resume.ts
|
|
939
1745
|
var MARKED = "marked";
|
|
940
1746
|
var FOREIGN = "foreign";
|
|
941
1747
|
var UNREADABLE2 = "unreadable";
|
|
942
|
-
function hold_of(
|
|
943
|
-
return
|
|
1748
|
+
function hold_of(read2) {
|
|
1749
|
+
return read2.kind === "held" || read2.kind === "stale" ? read2.hold : void 0;
|
|
944
1750
|
}
|
|
945
|
-
function is_halfrun_stop(
|
|
946
|
-
const hold = hold_of(
|
|
1751
|
+
function is_halfrun_stop(read2, issue, is_dirty) {
|
|
1752
|
+
const hold = hold_of(read2);
|
|
947
1753
|
return is_dirty && hold?.issue === issue && hold.is_halfrun_stop === true;
|
|
948
1754
|
}
|
|
949
|
-
function is_own_record(
|
|
950
|
-
const hold = hold_of(
|
|
1755
|
+
function is_own_record(read2, issue) {
|
|
1756
|
+
const hold = hold_of(read2);
|
|
951
1757
|
return hold === void 0 || hold.issue === issue || hold.issue === run_hold.UNNUMBERED_ISSUE;
|
|
952
1758
|
}
|
|
953
|
-
function mark_stop_at(target, issue,
|
|
954
|
-
const
|
|
955
|
-
if (
|
|
956
|
-
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;
|
|
957
1763
|
run_hold.release_hold(target);
|
|
958
|
-
return run_hold.create_stop_hold(target, issue,
|
|
1764
|
+
return run_hold.create_stop_hold(target, issue, mark2) ? MARKED : FOREIGN;
|
|
959
1765
|
}
|
|
960
1766
|
function take_over(target, issue) {
|
|
961
1767
|
run_hold.release_hold(target);
|
|
@@ -996,54 +1802,1627 @@ var run_halfrun_resume = {
|
|
|
996
1802
|
take_over
|
|
997
1803
|
};
|
|
998
1804
|
|
|
999
|
-
// scripts/
|
|
1000
|
-
import path from "node:path";
|
|
1805
|
+
// scripts/followup/git-followup-label.ts
|
|
1001
1806
|
import { z as z4 } from "zod";
|
|
1002
|
-
var
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
ended_at: z4.string(),
|
|
1008
|
-
stopped: z4.string().optional(),
|
|
1009
|
-
session: z4.string().optional()
|
|
1010
|
-
});
|
|
1011
|
-
function ended_path(git_directory) {
|
|
1012
|
-
return stamp_file.stamp_path(ENDED_PREFIX, git_directory);
|
|
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
|
+
);
|
|
1013
1812
|
}
|
|
1014
|
-
function
|
|
1015
|
-
|
|
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
|
+
);
|
|
1016
1837
|
}
|
|
1017
|
-
function
|
|
1018
|
-
|
|
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;
|
|
1019
1842
|
}
|
|
1020
|
-
function
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
...stop_of(owner_transcript, stopped)
|
|
1029
|
-
});
|
|
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
|
+
}
|
|
1030
1851
|
}
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1879
|
+
var git_animation = {
|
|
1880
|
+
create_animation
|
|
1881
|
+
};
|
|
1882
|
+
|
|
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 });
|
|
1041
1904
|
}
|
|
1042
|
-
|
|
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
|
+
};
|
|
1043
1927
|
|
|
1044
|
-
// scripts/
|
|
1045
|
-
|
|
1046
|
-
|
|
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-";
|
|
1047
3426
|
var WAKE_LOG_PREFIX = "josh-run-wake-log-";
|
|
1048
3427
|
var WAKE_LOG_SUFFIX = ".log";
|
|
1049
3428
|
var WAKE_GRACE_MS = 6e5;
|
|
@@ -1061,15 +3440,15 @@ var STOP_REASONS = {
|
|
|
1061
3440
|
unreadable: "unreadable"
|
|
1062
3441
|
};
|
|
1063
3442
|
var run_wake_schema = process_owner_schema.extend({
|
|
1064
|
-
invocation:
|
|
1065
|
-
started_at:
|
|
1066
|
-
woke:
|
|
1067
|
-
woke_at:
|
|
3443
|
+
invocation: z7.string(),
|
|
3444
|
+
started_at: z7.string(),
|
|
3445
|
+
woke: z7.number(),
|
|
3446
|
+
woke_at: z7.string().optional(),
|
|
1068
3447
|
// Every optional `RunWake` field must be listed: `z.object` strips undeclared ones on the round trip.
|
|
1069
|
-
attempts:
|
|
1070
|
-
woke_pid:
|
|
1071
|
-
spawned:
|
|
1072
|
-
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(),
|
|
1073
3452
|
profile: agent_role_profile.PROFILE_SCHEMA.optional()
|
|
1074
3453
|
});
|
|
1075
3454
|
function is_overdue(marked_at, now, limit_ms = WAKE_GRACE_MS) {
|
|
@@ -1091,7 +3470,7 @@ function decide_not_handed_off(input) {
|
|
|
1091
3470
|
if (input.is_owner_live) return WAIT_DECISION;
|
|
1092
3471
|
return decide_launch(input);
|
|
1093
3472
|
}
|
|
1094
|
-
function
|
|
3473
|
+
function decide2(input) {
|
|
1095
3474
|
if (input.read.kind !== "carried") return { kind: "stop", reason: STOP_REASONS[input.read.kind] };
|
|
1096
3475
|
if (input.read.carry.is_handed_off !== true) return decide_not_handed_off(input);
|
|
1097
3476
|
return decide_launch(input);
|
|
@@ -1147,7 +3526,7 @@ function reclaim(target, wake) {
|
|
|
1147
3526
|
remove_wake(target);
|
|
1148
3527
|
return stamp_file.create_stamp(target, wake) ? wake : void 0;
|
|
1149
3528
|
}
|
|
1150
|
-
function
|
|
3529
|
+
function claim3(target, invocation, now, profile) {
|
|
1151
3530
|
const existing = read_wake(target);
|
|
1152
3531
|
if (is_held_by_live_supervisor(existing)) return void 0;
|
|
1153
3532
|
const wake = { ...fresh_wake(invocation, now, profile), ...carried_state(existing, invocation) };
|
|
@@ -1202,10 +3581,10 @@ var run_wake = {
|
|
|
1202
3581
|
IDLE_CEILING_MS,
|
|
1203
3582
|
MAX_WAKE_ATTEMPTS,
|
|
1204
3583
|
WAKE_GRACE_MS,
|
|
1205
|
-
claim,
|
|
3584
|
+
claim: claim3,
|
|
1206
3585
|
count_claim,
|
|
1207
3586
|
count_wake,
|
|
1208
|
-
decide,
|
|
3587
|
+
decide: decide2,
|
|
1209
3588
|
fresh_wake,
|
|
1210
3589
|
is_supervisor_live,
|
|
1211
3590
|
mark_idle,
|
|
@@ -1287,11 +3666,11 @@ var DEFAULT_PORTS3 = {
|
|
|
1287
3666
|
notify_stranded: real_notify_stranded
|
|
1288
3667
|
};
|
|
1289
3668
|
function input_at(ports, targets) {
|
|
1290
|
-
const
|
|
1291
|
-
if (
|
|
3669
|
+
const read2 = ports.read_carry(targets.carry);
|
|
3670
|
+
if (read2.kind !== "carried") return void 0;
|
|
1292
3671
|
return {
|
|
1293
|
-
read,
|
|
1294
|
-
is_owner_live: ports.is_owner_live(
|
|
3672
|
+
read: read2,
|
|
3673
|
+
is_owner_live: ports.is_owner_live(read2.carry),
|
|
1295
3674
|
supervisor: ports.supervisor_liveness(ports.read_wake(targets.wake))
|
|
1296
3675
|
};
|
|
1297
3676
|
}
|
|
@@ -1299,7 +3678,7 @@ async function gather2(ports) {
|
|
|
1299
3678
|
const targets = await ports.resolve_targets();
|
|
1300
3679
|
return targets === void 0 ? void 0 : input_at(ports, targets);
|
|
1301
3680
|
}
|
|
1302
|
-
async function
|
|
3681
|
+
async function report5(ports, input) {
|
|
1303
3682
|
if (input.read.kind !== "carried") return;
|
|
1304
3683
|
const text2 = run_stranded.describe(input.read.carry);
|
|
1305
3684
|
if (await ports.emit_stranded(text2)) await ports.notify_stranded(text2);
|
|
@@ -1308,7 +3687,7 @@ async function detect_and_report2(ports = DEFAULT_PORTS3) {
|
|
|
1308
3687
|
const input = await gather2(ports);
|
|
1309
3688
|
if (input === void 0) return run_stranded.OK;
|
|
1310
3689
|
const is_run_stranded = run_stranded.is_stranded(input);
|
|
1311
|
-
if (is_run_stranded) await
|
|
3690
|
+
if (is_run_stranded) await report5(ports, input);
|
|
1312
3691
|
return run_stranded.verdict_of(is_run_stranded);
|
|
1313
3692
|
}
|
|
1314
3693
|
async function run_stranded_check() {
|
|
@@ -1321,12 +3700,12 @@ function is_stranded_at(directory, ports = DEFAULT_PORTS3) {
|
|
|
1321
3700
|
const input = input_at(ports, targets_of(directory));
|
|
1322
3701
|
return input !== void 0 && run_stranded.is_stranded(input);
|
|
1323
3702
|
}
|
|
1324
|
-
function is_left_stranded(
|
|
1325
|
-
const
|
|
1326
|
-
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);
|
|
1327
3706
|
return run_stranded.is_stranded({
|
|
1328
|
-
read,
|
|
1329
|
-
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),
|
|
1330
3709
|
supervisor: run_stranded.GONE
|
|
1331
3710
|
});
|
|
1332
3711
|
}
|
|
@@ -1338,10 +3717,23 @@ var run_stranded_detect = {
|
|
|
1338
3717
|
};
|
|
1339
3718
|
|
|
1340
3719
|
// scripts/hooks/stop-guard.ts
|
|
1341
|
-
async function
|
|
3720
|
+
async function held_record() {
|
|
1342
3721
|
const directory = await run_hold.worktree_directory();
|
|
1343
|
-
|
|
1344
|
-
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
|
+
};
|
|
1345
3737
|
}
|
|
1346
3738
|
function transcript_tail(transcript_path) {
|
|
1347
3739
|
const transcript = time_hook_transcript.transcript_of(transcript_path, void 0);
|
|
@@ -1350,6 +3742,14 @@ function transcript_tail(transcript_path) {
|
|
|
1350
3742
|
function was_filed(tail) {
|
|
1351
3743
|
return filing_cap.turn_filing_count(tail) > 0;
|
|
1352
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
|
+
}
|
|
1353
3753
|
function session_lang() {
|
|
1354
3754
|
return session_language.resolve_session_lang().lang;
|
|
1355
3755
|
}
|
|
@@ -1364,13 +3764,13 @@ async function session_role_facts() {
|
|
|
1364
3764
|
async function build_context(transcript_path, payload_tail) {
|
|
1365
3765
|
const tail = transcript_tail(transcript_path);
|
|
1366
3766
|
return {
|
|
1367
|
-
...await hold_facts(),
|
|
3767
|
+
...await hold_facts(tail),
|
|
1368
3768
|
tree_clean: !await run_hold.is_tree_dirty(),
|
|
1369
|
-
notified: lane_park.mentions_confirmation_notify(tail),
|
|
1370
3769
|
message: payload_tail.message,
|
|
1371
3770
|
prompt: last_prompt.prompt_text(tail),
|
|
3771
|
+
answers_question: stop_decision.is_question(last_prompt.turn_prompt(tail)),
|
|
1372
3772
|
stop_hook_active: payload_tail.stop_hook_active,
|
|
1373
|
-
cut_pending:
|
|
3773
|
+
cut_pending: is_cut_stop(tail),
|
|
1374
3774
|
filed: was_filed(tail),
|
|
1375
3775
|
session_owner: repo_party.current_owner(),
|
|
1376
3776
|
...await session_role_facts(),
|
|
@@ -1401,8 +3801,8 @@ async function carried_owner_live() {
|
|
|
1401
3801
|
try {
|
|
1402
3802
|
const directory = await run_carry.repository_directory();
|
|
1403
3803
|
if (directory === void 0) return void 0;
|
|
1404
|
-
const
|
|
1405
|
-
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;
|
|
1406
3806
|
} catch {
|
|
1407
3807
|
return void 0;
|
|
1408
3808
|
}
|
|
@@ -1416,13 +3816,13 @@ async function run_report_checks() {
|
|
|
1416
3816
|
async function write_stop_decision(raw_payload) {
|
|
1417
3817
|
hook_decision.load_environment_file();
|
|
1418
3818
|
await run_report_checks();
|
|
1419
|
-
const
|
|
1420
|
-
if (
|
|
1421
|
-
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}
|
|
1422
3822
|
`);
|
|
1423
3823
|
}
|
|
1424
3824
|
var stop_guard = { outcome_of, stop_outcome_for_payload };
|
|
1425
|
-
if (process.argv[1] ===
|
|
3825
|
+
if (process.argv[1] === fileURLToPath5(import.meta.url)) {
|
|
1426
3826
|
if (process.stdin.isTTY) hook_decision.report_no_payload("stop:guard");
|
|
1427
3827
|
else await write_stop_decision(await text(process.stdin));
|
|
1428
3828
|
}
|