@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
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
error_text
|
|
3
3
|
} from "./chunk-CDY4NFQG.js";
|
|
4
|
+
import {
|
|
5
|
+
agent_role_profile,
|
|
6
|
+
agent_session_role
|
|
7
|
+
} from "./chunk-UPUZF3AN.js";
|
|
4
8
|
import {
|
|
5
9
|
lane_child_marker
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LAOV7NLM.js";
|
|
7
11
|
import {
|
|
8
12
|
ENV_FILE_NAME,
|
|
9
13
|
GIT_LOCATION_VARIABLES,
|
|
@@ -11,20 +15,24 @@ import {
|
|
|
11
15
|
PORCELAIN_FLAG,
|
|
12
16
|
PORT_SEED_KEY,
|
|
13
17
|
agent_session_environment,
|
|
18
|
+
changed_paths,
|
|
14
19
|
file_reader,
|
|
15
20
|
git_command,
|
|
16
21
|
git_location_environment,
|
|
17
22
|
git_spawn,
|
|
18
23
|
git_utilities,
|
|
24
|
+
has_timed_out,
|
|
19
25
|
issue_number_shape,
|
|
20
26
|
json_value,
|
|
21
27
|
lane_paths,
|
|
22
28
|
ports,
|
|
23
29
|
process_identity,
|
|
24
30
|
process_owner_schema,
|
|
31
|
+
rest_comment_schema,
|
|
25
32
|
run_carry
|
|
26
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-APUO6U4J.js";
|
|
27
34
|
import {
|
|
35
|
+
COMMAND_TIMEOUT_MS,
|
|
28
36
|
INSTALL_TIMEOUT_MS,
|
|
29
37
|
PLATFORM_TEMP_ROOT,
|
|
30
38
|
PROBE_TIMEOUT_MS,
|
|
@@ -32,29 +40,238 @@ import {
|
|
|
32
40
|
stamp_file
|
|
33
41
|
} from "./chunk-DGT54IH5.js";
|
|
34
42
|
|
|
35
|
-
// scripts/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
// scripts/discovery/repo-discovery.ts
|
|
44
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
45
|
+
import path from "node:path";
|
|
46
|
+
|
|
47
|
+
// scripts/discovery/repo-origin.ts
|
|
48
|
+
var GITHUB_HOST = "github.com";
|
|
49
|
+
var SSH_ALIAS_PATTERN = /^github(?:-\w+)*$/u;
|
|
50
|
+
var GIT_SUFFIX = ".git";
|
|
51
|
+
var PATH_SEPARATOR = "/";
|
|
52
|
+
var USER_SEPARATOR = "@";
|
|
53
|
+
var HOST_SEPARATOR = ":";
|
|
54
|
+
var OWNER_INDEX = 0;
|
|
55
|
+
var REPO_INDEX = 1;
|
|
56
|
+
var REPO_PATH_SEGMENTS = 2;
|
|
57
|
+
var NOT_FOUND = -1;
|
|
58
|
+
var SECTION_PREFIX = "[";
|
|
59
|
+
var ORIGIN_SECTION = /^\[remote\s+"origin"\]$/u;
|
|
60
|
+
var REMOTE_SECTION = /^\[remote\s+"[^"]+"\]$/u;
|
|
61
|
+
var URL_KEY = "url";
|
|
62
|
+
function is_github_hostname(host) {
|
|
63
|
+
return host.toLowerCase() === GITHUB_HOST;
|
|
40
64
|
}
|
|
41
|
-
function
|
|
42
|
-
|
|
65
|
+
function is_github_ssh_host(host) {
|
|
66
|
+
const normalized = host.toLowerCase();
|
|
67
|
+
return is_github_hostname(normalized) || SSH_ALIAS_PATTERN.test(normalized);
|
|
43
68
|
}
|
|
44
|
-
|
|
69
|
+
function strip_git_suffix(name) {
|
|
70
|
+
return name.endsWith(GIT_SUFFIX) ? name.slice(0, -GIT_SUFFIX.length) : name;
|
|
71
|
+
}
|
|
72
|
+
function to_identity(repository_path) {
|
|
73
|
+
const segments = repository_path.split(PATH_SEPARATOR).filter((segment) => segment !== "");
|
|
74
|
+
if (segments.length !== REPO_PATH_SEGMENTS) return void 0;
|
|
75
|
+
const owner = segments[OWNER_INDEX] ?? "";
|
|
76
|
+
const repo = strip_git_suffix(segments[REPO_INDEX] ?? "");
|
|
77
|
+
return repo === "" ? void 0 : { owner, repo };
|
|
78
|
+
}
|
|
79
|
+
function parse_scp_like(url) {
|
|
80
|
+
const user_end = url.indexOf(USER_SEPARATOR);
|
|
81
|
+
const authority = user_end === NOT_FOUND ? url : url.slice(user_end + 1);
|
|
82
|
+
const host_end = authority.indexOf(HOST_SEPARATOR);
|
|
83
|
+
if (host_end === NOT_FOUND || !is_github_ssh_host(authority.slice(0, host_end))) return void 0;
|
|
84
|
+
return to_identity(authority.slice(host_end + 1));
|
|
85
|
+
}
|
|
86
|
+
function parse_url_like(url) {
|
|
87
|
+
try {
|
|
88
|
+
const parsed = new URL(url);
|
|
89
|
+
return is_github_hostname(parsed.hostname) ? to_identity(parsed.pathname) : void 0;
|
|
90
|
+
} catch {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function parse_origin_url(url) {
|
|
95
|
+
const trimmed = url.trim();
|
|
96
|
+
if (trimmed === "") return void 0;
|
|
97
|
+
return parse_url_like(trimmed) ?? parse_scp_like(trimmed);
|
|
98
|
+
}
|
|
99
|
+
function is_section_header(line) {
|
|
100
|
+
return line.startsWith(SECTION_PREFIX);
|
|
101
|
+
}
|
|
102
|
+
function origin_section_lines(lines) {
|
|
103
|
+
const start = lines.findIndex((line) => ORIGIN_SECTION.test(line));
|
|
104
|
+
if (start === NOT_FOUND) return [];
|
|
105
|
+
const rest = lines.slice(start + 1);
|
|
106
|
+
const end = rest.findIndex((line) => is_section_header(line));
|
|
107
|
+
return end === NOT_FOUND ? rest : rest.slice(0, end);
|
|
108
|
+
}
|
|
109
|
+
function url_value(line) {
|
|
110
|
+
const separator = line.indexOf("=");
|
|
111
|
+
if (separator === NOT_FOUND || line.slice(0, separator).trim() !== URL_KEY) return void 0;
|
|
112
|
+
return line.slice(separator + 1).trim();
|
|
113
|
+
}
|
|
114
|
+
function parse_origin_from_config(content) {
|
|
115
|
+
const lines = content.split("\n").map((line) => line.trim());
|
|
116
|
+
return origin_section_lines(lines).map((line) => url_value(line)).find((url) => url !== void 0);
|
|
117
|
+
}
|
|
118
|
+
function section_groups(lines) {
|
|
119
|
+
const groups = [];
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
if (is_section_header(line)) groups.push([line]);
|
|
122
|
+
else groups.at(-1)?.push(line);
|
|
123
|
+
}
|
|
124
|
+
return groups;
|
|
125
|
+
}
|
|
126
|
+
function parse_remote_urls_from_config(content) {
|
|
127
|
+
const lines = content.split("\n").map((line) => line.trim());
|
|
128
|
+
return section_groups(lines).filter((group) => REMOTE_SECTION.test(group[0] ?? "")).flatMap((group) => group.map((line) => url_value(line))).filter((url) => url !== void 0);
|
|
129
|
+
}
|
|
130
|
+
function format_identity(identity) {
|
|
131
|
+
return `${identity.owner}${PATH_SEPARATOR}${identity.repo}`.toLowerCase();
|
|
132
|
+
}
|
|
133
|
+
var repo_origin = {
|
|
134
|
+
GITHUB_HOST,
|
|
135
|
+
is_github_hostname,
|
|
136
|
+
is_github_ssh_host,
|
|
137
|
+
parse_origin_from_config,
|
|
138
|
+
parse_origin_url,
|
|
139
|
+
parse_remote_urls_from_config,
|
|
140
|
+
format_identity
|
|
141
|
+
};
|
|
45
142
|
|
|
46
|
-
// scripts/
|
|
47
|
-
var
|
|
48
|
-
var
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
143
|
+
// scripts/discovery/repo-map-logic.ts
|
|
144
|
+
var OVERRIDE_ENTRY_SEPARATOR = ",";
|
|
145
|
+
var OVERRIDE_PAIR_SEPARATOR = "=";
|
|
146
|
+
var OVERRIDE_PAIR_PARTS = 2;
|
|
147
|
+
var KEY_INDEX = 0;
|
|
148
|
+
var PATH_INDEX = 1;
|
|
149
|
+
function is_same_owner(identity, current_owner) {
|
|
150
|
+
return identity.owner.toLowerCase() === current_owner.toLowerCase();
|
|
53
151
|
}
|
|
54
|
-
|
|
152
|
+
function add_entry(target, origin_url, repository_path, current_owner) {
|
|
153
|
+
if (origin_url === void 0) return;
|
|
154
|
+
const identity = repo_origin.parse_origin_url(origin_url);
|
|
155
|
+
if (identity === void 0 || !is_same_owner(identity, current_owner)) return;
|
|
156
|
+
target.set(repo_origin.format_identity(identity), repository_path);
|
|
157
|
+
}
|
|
158
|
+
function parse_override_entry(entry) {
|
|
159
|
+
const parts = entry.split(OVERRIDE_PAIR_SEPARATOR).map((part) => part.trim());
|
|
160
|
+
if (parts.length !== OVERRIDE_PAIR_PARTS) return void 0;
|
|
161
|
+
const key = parts[KEY_INDEX] ?? "";
|
|
162
|
+
const repository_path = parts[PATH_INDEX] ?? "";
|
|
163
|
+
if (repository_path === "") return void 0;
|
|
164
|
+
return { path: repository_path, origin_url: `https://${repo_origin.GITHUB_HOST}/${key}` };
|
|
165
|
+
}
|
|
166
|
+
function parse_overrides(raw) {
|
|
167
|
+
if (raw === void 0) return [];
|
|
168
|
+
return raw.split(OVERRIDE_ENTRY_SEPARATOR).map((entry) => parse_override_entry(entry)).filter((entry) => entry !== void 0);
|
|
169
|
+
}
|
|
170
|
+
function build_repository_map(discovered, current_owner, override_value) {
|
|
171
|
+
const result = /* @__PURE__ */ new Map();
|
|
172
|
+
for (const entry of discovered) add_entry(result, entry.origin_url, entry.path, current_owner);
|
|
173
|
+
for (const entry of parse_overrides(override_value)) {
|
|
174
|
+
add_entry(result, entry.origin_url, entry.path, current_owner);
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
var repo_map_logic = {
|
|
179
|
+
is_same_owner,
|
|
180
|
+
build_repository_map
|
|
181
|
+
};
|
|
55
182
|
|
|
56
|
-
// scripts/
|
|
57
|
-
|
|
183
|
+
// scripts/discovery/repo-discovery.ts
|
|
184
|
+
var GIT_DIRECTORY = ".git";
|
|
185
|
+
var GIT_CONFIG = "config";
|
|
186
|
+
var GITDIR_PREFIX = "gitdir:";
|
|
187
|
+
var COMMON_DIRECTORY_FILE = "commondir";
|
|
188
|
+
var OVERRIDE_ENV_KEY = "JOSH_REPO_PATHS";
|
|
189
|
+
function resolve_common_directory(git_directory) {
|
|
190
|
+
const pointer = path.join(git_directory, COMMON_DIRECTORY_FILE);
|
|
191
|
+
if (!existsSync(pointer)) return git_directory;
|
|
192
|
+
try {
|
|
193
|
+
return path.resolve(git_directory, readFileSync(pointer, "utf8").trim());
|
|
194
|
+
} catch {
|
|
195
|
+
return git_directory;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function follow_git_file(repository_path, git_file) {
|
|
199
|
+
const pointer = readFileSync(git_file, "utf8").trim();
|
|
200
|
+
if (!pointer.startsWith(GITDIR_PREFIX)) return void 0;
|
|
201
|
+
return path.resolve(repository_path, pointer.slice(GITDIR_PREFIX.length).trim());
|
|
202
|
+
}
|
|
203
|
+
function resolve_git_directory(repository_path) {
|
|
204
|
+
const candidate = path.join(repository_path, GIT_DIRECTORY);
|
|
205
|
+
if (!existsSync(candidate)) return void 0;
|
|
206
|
+
try {
|
|
207
|
+
if (statSync(candidate).isDirectory()) return candidate;
|
|
208
|
+
const linked = follow_git_file(repository_path, candidate);
|
|
209
|
+
return linked === void 0 ? void 0 : resolve_common_directory(linked);
|
|
210
|
+
} catch {
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function read_config(repository_path) {
|
|
215
|
+
const git_directory = resolve_git_directory(repository_path);
|
|
216
|
+
if (git_directory === void 0) return void 0;
|
|
217
|
+
try {
|
|
218
|
+
return readFileSync(path.join(git_directory, GIT_CONFIG), "utf8");
|
|
219
|
+
} catch {
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function read_origin_url(repository_path) {
|
|
224
|
+
const config = read_config(repository_path);
|
|
225
|
+
return config === void 0 ? void 0 : repo_origin.parse_origin_from_config(config);
|
|
226
|
+
}
|
|
227
|
+
function read_remote_urls(repository_path) {
|
|
228
|
+
const config = read_config(repository_path);
|
|
229
|
+
return config === void 0 ? [] : repo_origin.parse_remote_urls_from_config(config);
|
|
230
|
+
}
|
|
231
|
+
function sibling_directories(parent) {
|
|
232
|
+
try {
|
|
233
|
+
return readdirSync(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(parent, entry.name)).toSorted((left, right) => left.localeCompare(right));
|
|
234
|
+
} catch {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function scan_siblings(parent) {
|
|
239
|
+
return sibling_directories(parent).map((repository_path) => ({
|
|
240
|
+
path: repository_path,
|
|
241
|
+
origin_url: read_origin_url(repository_path)
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
function resolve_current_owner(repository_path) {
|
|
245
|
+
const origin_url = read_origin_url(repository_path);
|
|
246
|
+
if (origin_url === void 0) return void 0;
|
|
247
|
+
return repo_origin.parse_origin_url(origin_url)?.owner;
|
|
248
|
+
}
|
|
249
|
+
function main_worktree(repository_path) {
|
|
250
|
+
const git_directory = resolve_git_directory(repository_path);
|
|
251
|
+
if (git_directory === void 0 || path.basename(git_directory) !== GIT_DIRECTORY) {
|
|
252
|
+
return repository_path;
|
|
253
|
+
}
|
|
254
|
+
return path.dirname(git_directory);
|
|
255
|
+
}
|
|
256
|
+
function discover_repositories(repository_path, environment3 = process.env) {
|
|
257
|
+
const main = main_worktree(repository_path);
|
|
258
|
+
const current_owner = resolve_current_owner(main);
|
|
259
|
+
if (current_owner === void 0) return /* @__PURE__ */ new Map();
|
|
260
|
+
return repo_map_logic.build_repository_map(
|
|
261
|
+
scan_siblings(path.dirname(main)),
|
|
262
|
+
current_owner,
|
|
263
|
+
environment3[OVERRIDE_ENV_KEY]
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
var repo_discovery = {
|
|
267
|
+
OVERRIDE_ENV_KEY,
|
|
268
|
+
main_worktree,
|
|
269
|
+
read_origin_url,
|
|
270
|
+
read_remote_urls,
|
|
271
|
+
resolve_current_owner,
|
|
272
|
+
scan_siblings,
|
|
273
|
+
discover_repositories
|
|
274
|
+
};
|
|
58
275
|
|
|
59
276
|
// scripts/issue/issue-cite.ts
|
|
60
277
|
var GITHUB_URL_PREFIX = "https://github.com/";
|
|
@@ -119,39 +336,394 @@ var issue_cite = {
|
|
|
119
336
|
unreadable_line
|
|
120
337
|
};
|
|
121
338
|
|
|
122
|
-
// scripts/
|
|
123
|
-
|
|
124
|
-
var
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
339
|
+
// scripts/rules/issue-citation.ts
|
|
340
|
+
var ISSUE_NUMBER = /#\d+/gu;
|
|
341
|
+
var REFERENCE_BOUNDARY = /[\s#()[\]<>|]/u;
|
|
342
|
+
var OWNER_REPO = /^[^\s/#]+\/[^\s/#]+$/u;
|
|
343
|
+
var MARKDOWN_LINK = /\[[^[\]]*\]\([^()]*\)/gu;
|
|
344
|
+
var NUMBER_CONTINUATION = /^(?:\w|-[a-z])/iu;
|
|
345
|
+
var CONTINUATION_WIDTH = 2;
|
|
346
|
+
var GLUED_LEAD = /[&./]/u;
|
|
347
|
+
var FENCE_MARKER = /^\s*(?:`{3,}|~{3,})/u;
|
|
348
|
+
var QUOTE_LINE = /^\s*>/u;
|
|
349
|
+
var PR_PREFIX = /\b(?:pr|pull request)\s*$/iu;
|
|
350
|
+
var BACKTICK_PAIR = 2;
|
|
351
|
+
function link_covers(link, index) {
|
|
352
|
+
const start = link.index ?? 0;
|
|
353
|
+
return index >= start && index < start + link[0].length;
|
|
354
|
+
}
|
|
355
|
+
function is_linked_at(line, index) {
|
|
356
|
+
for (const link of line.matchAll(MARKDOWN_LINK)) {
|
|
357
|
+
if (link_covers(link, index)) return true;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
function lead_token(message, hash_index) {
|
|
362
|
+
let start = hash_index;
|
|
363
|
+
while (start > 0 && !REFERENCE_BOUNDARY.test(message.charAt(start - 1))) start -= 1;
|
|
364
|
+
return message.slice(start, hash_index);
|
|
365
|
+
}
|
|
366
|
+
function repo_prefix(message, hash_index) {
|
|
367
|
+
const token = lead_token(message, hash_index);
|
|
368
|
+
return OWNER_REPO.test(token) ? token : "";
|
|
369
|
+
}
|
|
370
|
+
function is_inline_code_at(line, index) {
|
|
371
|
+
const backticks = line.slice(0, index).split("`").length - 1;
|
|
372
|
+
return backticks % BACKTICK_PAIR === 1;
|
|
373
|
+
}
|
|
374
|
+
function is_bare_at(line, index) {
|
|
375
|
+
if (is_linked_at(line, index) || is_inline_code_at(line, index)) return false;
|
|
376
|
+
return !PR_PREFIX.test(line.slice(0, index));
|
|
377
|
+
}
|
|
378
|
+
function is_glued(line, reference2) {
|
|
379
|
+
const end = reference2.index + reference2.text.length;
|
|
380
|
+
if (NUMBER_CONTINUATION.test(line.slice(end, end + CONTINUATION_WIDTH))) return true;
|
|
381
|
+
return reference2.prefix === "" && GLUED_LEAD.test(lead_token(line, reference2.index));
|
|
382
|
+
}
|
|
383
|
+
function line_references(line) {
|
|
384
|
+
const references = [];
|
|
385
|
+
for (const match of line.matchAll(ISSUE_NUMBER)) {
|
|
386
|
+
const reference2 = { index: match.index, prefix: repo_prefix(line, match.index), text: match[0] };
|
|
387
|
+
if (!is_glued(line, reference2) && is_bare_at(line, match.index)) references.push(reference2);
|
|
388
|
+
}
|
|
389
|
+
return references;
|
|
390
|
+
}
|
|
391
|
+
function collect_line_references(line, found) {
|
|
392
|
+
for (const reference2 of line_references(line)) found.push(`${reference2.prefix}${reference2.text}`);
|
|
393
|
+
}
|
|
394
|
+
function lines_outside_fences(message) {
|
|
395
|
+
const lines = [];
|
|
396
|
+
let is_in_fence = false;
|
|
397
|
+
for (const line of message.split("\n")) {
|
|
398
|
+
if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
|
|
399
|
+
else if (!is_in_fence) lines.push(line);
|
|
400
|
+
}
|
|
401
|
+
return lines;
|
|
402
|
+
}
|
|
403
|
+
function prose_lines(message) {
|
|
404
|
+
return lines_outside_fences(message).filter((line) => !QUOTE_LINE.test(line));
|
|
405
|
+
}
|
|
406
|
+
function bare_references(message) {
|
|
407
|
+
const found = [];
|
|
408
|
+
for (const line of prose_lines(message)) collect_line_references(line, found);
|
|
409
|
+
return [...new Set(found)];
|
|
410
|
+
}
|
|
411
|
+
function prompt_references(prompt) {
|
|
412
|
+
const found = /* @__PURE__ */ new Set();
|
|
413
|
+
for (const match of prompt.matchAll(ISSUE_NUMBER)) {
|
|
414
|
+
found.add(repo_prefix(prompt, match.index) + match[0]);
|
|
415
|
+
}
|
|
416
|
+
return found;
|
|
417
|
+
}
|
|
418
|
+
function unquoted_references(message, prompt) {
|
|
419
|
+
const quoted2 = prompt_references(prompt);
|
|
420
|
+
return bare_references(message).filter((reference2) => !quoted2.has(reference2));
|
|
421
|
+
}
|
|
422
|
+
function has_bare_reference(message) {
|
|
423
|
+
return bare_references(message).length > 0;
|
|
424
|
+
}
|
|
425
|
+
function cite_argument(reference2) {
|
|
426
|
+
return reference2.startsWith("#") ? reference2.slice(1) : reference2;
|
|
427
|
+
}
|
|
428
|
+
function cite_arguments(references) {
|
|
429
|
+
return references.map((reference2) => cite_argument(reference2));
|
|
430
|
+
}
|
|
431
|
+
function reference_link(reference2, default_slug) {
|
|
432
|
+
const slug = reference2.prefix === "" ? default_slug : reference2.prefix;
|
|
433
|
+
const number = reference2.text.slice(1);
|
|
434
|
+
return `[${reference2.prefix}${reference2.text}](${issue_cite.issue_url(slug, number)})`;
|
|
435
|
+
}
|
|
436
|
+
function linkify_line(line, slug) {
|
|
437
|
+
let result = "";
|
|
438
|
+
let cursor = 0;
|
|
439
|
+
for (const reference2 of line_references(line)) {
|
|
440
|
+
const start = reference2.index - reference2.prefix.length;
|
|
441
|
+
result += line.slice(cursor, start) + reference_link(reference2, slug);
|
|
442
|
+
cursor = reference2.index + reference2.text.length;
|
|
443
|
+
}
|
|
444
|
+
return result + line.slice(cursor);
|
|
445
|
+
}
|
|
446
|
+
function rewrite_line(line, slug, is_in_fence) {
|
|
447
|
+
if (is_in_fence || FENCE_MARKER.test(line) || QUOTE_LINE.test(line)) return line;
|
|
448
|
+
return linkify_line(line, slug);
|
|
449
|
+
}
|
|
450
|
+
function linkify(message, slug) {
|
|
451
|
+
const out = [];
|
|
452
|
+
let is_in_fence = false;
|
|
453
|
+
for (const line of message.split("\n")) {
|
|
454
|
+
if (FENCE_MARKER.test(line)) is_in_fence = !is_in_fence;
|
|
455
|
+
out.push(rewrite_line(line, slug, is_in_fence));
|
|
456
|
+
}
|
|
457
|
+
return out.join("\n");
|
|
458
|
+
}
|
|
459
|
+
var issue_citation = {
|
|
460
|
+
bare_references,
|
|
461
|
+
cite_arguments,
|
|
462
|
+
has_bare_reference,
|
|
463
|
+
linkify,
|
|
464
|
+
prose_lines,
|
|
465
|
+
unquoted_references
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// scripts/issue/session-cite.ts
|
|
469
|
+
function session_repo() {
|
|
470
|
+
const url = repo_discovery.read_origin_url(process.cwd());
|
|
471
|
+
const identity = url === void 0 ? void 0 : repo_origin.parse_origin_url(url);
|
|
472
|
+
return identity === void 0 ? void 0 : `${identity.owner}/${identity.repo}`;
|
|
473
|
+
}
|
|
474
|
+
function issue(number, title, repo = session_repo()) {
|
|
475
|
+
return issue_cite.reference(repo, String(number), title);
|
|
476
|
+
}
|
|
477
|
+
function text(message) {
|
|
478
|
+
const repo = session_repo();
|
|
479
|
+
return repo === void 0 ? message : issue_citation.linkify(message, repo);
|
|
480
|
+
}
|
|
481
|
+
var session_cite = {
|
|
482
|
+
issue,
|
|
483
|
+
session_repo,
|
|
484
|
+
text
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
// scripts/josh/stamp-record.ts
|
|
488
|
+
var NONE_READ = { kind: "none" };
|
|
489
|
+
var UNREADABLE_READ = { kind: "unreadable" };
|
|
490
|
+
function is_older_than(iso, max_age_ms, now) {
|
|
491
|
+
const at = Date.parse(iso);
|
|
492
|
+
if (Number.isNaN(at)) return true;
|
|
493
|
+
return now.getTime() - at > max_age_ms;
|
|
494
|
+
}
|
|
495
|
+
function classify(raw, spec, now) {
|
|
496
|
+
if (raw === void 0) return NONE_READ;
|
|
497
|
+
const record = spec.parse(raw);
|
|
498
|
+
if (record === void 0) return UNREADABLE_READ;
|
|
499
|
+
const is_expired2 = is_older_than(spec.timestamp_of(record), spec.max_age_ms, now);
|
|
500
|
+
return { kind: is_expired2 ? "expired" : "carried", record };
|
|
501
|
+
}
|
|
502
|
+
var stamp_record = { classify, is_older_than };
|
|
503
|
+
|
|
504
|
+
// scripts/run/hold/run-hold.ts
|
|
505
|
+
import { z } from "zod";
|
|
506
|
+
var HOLD_PREFIX = "josh-run-hold-";
|
|
507
|
+
var UNNUMBERED_ISSUE = "new";
|
|
508
|
+
var HOLD_MAX_AGE_HOURS = 8;
|
|
509
|
+
var MILLISECONDS_PER_HOUR = 36e5;
|
|
510
|
+
var HOLD_MAX_AGE_MS = HOLD_MAX_AGE_HOURS * MILLISECONDS_PER_HOUR;
|
|
511
|
+
var RELEASE_COMMAND = "pnpm josh run:release";
|
|
512
|
+
var FORCE_RELEASE_COMMAND = `${RELEASE_COMMAND} --force`;
|
|
513
|
+
var NOT_IDLE = 'that is not "nothing is running here"';
|
|
514
|
+
var FREE_READ = { kind: "free" };
|
|
515
|
+
var UNREADABLE_READ2 = { kind: "unreadable" };
|
|
516
|
+
function hold_path(git_directory) {
|
|
517
|
+
return stamp_file.stamp_path(HOLD_PREFIX, git_directory);
|
|
518
|
+
}
|
|
519
|
+
async function worktree_directory() {
|
|
520
|
+
const directories2 = await git_command.git_directories();
|
|
521
|
+
return directories2[0];
|
|
522
|
+
}
|
|
523
|
+
async function is_linked_worktree() {
|
|
524
|
+
const directories2 = await git_command.git_directories();
|
|
525
|
+
return directories2[0] !== void 0 && directories2[0] !== directories2[1];
|
|
526
|
+
}
|
|
527
|
+
var run_hold_schema = z.object({
|
|
528
|
+
issue: z.string(),
|
|
529
|
+
taken_at: z.string(),
|
|
530
|
+
pid: z.number(),
|
|
531
|
+
is_fullrun: z.boolean().optional(),
|
|
532
|
+
is_halfrun_stop: z.boolean().optional(),
|
|
533
|
+
prrun_stop_head: z.string().optional(),
|
|
534
|
+
owner_pid: z.number().optional(),
|
|
535
|
+
owner_start: z.string().optional(),
|
|
536
|
+
notified_at: z.string().optional()
|
|
537
|
+
});
|
|
538
|
+
function parse_hold(raw) {
|
|
539
|
+
return json_value.parse_with(raw, run_hold_schema);
|
|
540
|
+
}
|
|
541
|
+
function is_expired(hold, now) {
|
|
542
|
+
return stamp_record.is_older_than(hold.taken_at, HOLD_MAX_AGE_MS, now);
|
|
543
|
+
}
|
|
544
|
+
function is_stop_marked(hold) {
|
|
545
|
+
return hold.is_halfrun_stop === true || hold.prrun_stop_head !== void 0;
|
|
546
|
+
}
|
|
547
|
+
function is_owner_gone(hold) {
|
|
548
|
+
if (is_stop_marked(hold) || hold.owner_pid === void 0) return false;
|
|
549
|
+
return process_identity.is_same_process(hold.owner_pid, hold.owner_start) === false;
|
|
550
|
+
}
|
|
551
|
+
function is_stale(hold, now) {
|
|
552
|
+
return is_expired(hold, now) || is_owner_gone(hold);
|
|
553
|
+
}
|
|
554
|
+
function classify2(raw, now) {
|
|
555
|
+
if (raw === void 0) return FREE_READ;
|
|
556
|
+
const hold = parse_hold(raw);
|
|
557
|
+
if (hold === void 0) return UNREADABLE_READ2;
|
|
558
|
+
return { kind: is_stale(hold, now) ? "stale" : "held", hold };
|
|
559
|
+
}
|
|
560
|
+
function read_hold(target, now = /* @__PURE__ */ new Date()) {
|
|
561
|
+
return classify2(stamp_file.read_stamp_text(target), now);
|
|
562
|
+
}
|
|
563
|
+
function session_owner(environment3 = process.env) {
|
|
564
|
+
const pid = Number(environment3[agent_session_environment.AGENT_PID_KEY]);
|
|
565
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return {};
|
|
566
|
+
return { owner_pid: pid, owner_start: process_identity.read_start(pid) };
|
|
567
|
+
}
|
|
568
|
+
function build_hold(issue2, now, is_fullrun) {
|
|
569
|
+
return { issue: issue2, taken_at: now.toISOString(), pid: process.pid, is_fullrun, ...session_owner() };
|
|
570
|
+
}
|
|
571
|
+
function write_hold(target, issue2, now = /* @__PURE__ */ new Date()) {
|
|
572
|
+
const hold = build_hold(issue2, now);
|
|
573
|
+
stamp_file.write_stamp(target, hold);
|
|
574
|
+
return hold;
|
|
575
|
+
}
|
|
576
|
+
function create_hold(target, issue2, now = /* @__PURE__ */ new Date(), is_fullrun) {
|
|
577
|
+
return stamp_file.create_stamp(target, build_hold(issue2, now, is_fullrun));
|
|
578
|
+
}
|
|
579
|
+
var HALFRUN_STOP_FIELDS = { is_halfrun_stop: true };
|
|
580
|
+
function create_stop_hold(target, issue2, mark, now = /* @__PURE__ */ new Date()) {
|
|
581
|
+
return stamp_file.create_stamp(target, { ...build_hold(issue2, now), ...mark });
|
|
582
|
+
}
|
|
583
|
+
function create_halfrun_stop_hold(target, issue2, now = /* @__PURE__ */ new Date()) {
|
|
584
|
+
return create_stop_hold(target, issue2, HALFRUN_STOP_FIELDS, now);
|
|
585
|
+
}
|
|
586
|
+
async function is_tree_dirty(directory) {
|
|
587
|
+
try {
|
|
588
|
+
const status = await git_command.status(directory);
|
|
589
|
+
return status.trim() !== "";
|
|
590
|
+
} catch (error) {
|
|
591
|
+
error_text.trace_swallowed("run_hold.is_tree_dirty", error);
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function release_hold(target) {
|
|
596
|
+
stamp_file.remove_stamp(target);
|
|
597
|
+
}
|
|
598
|
+
function describe_holder(hold) {
|
|
599
|
+
const holder = hold.issue === UNNUMBERED_ISSUE ? "an unnumbered run" : session_cite.issue(hold.issue);
|
|
600
|
+
return `${holder}, recorded ${hold.taken_at} (pid ${String(hold.owner_pid ?? hold.pid)})`;
|
|
601
|
+
}
|
|
602
|
+
function own_release_command(issue2) {
|
|
603
|
+
return issue2 === UNNUMBERED_ISSUE ? RELEASE_COMMAND : `${RELEASE_COMMAND} ${issue2}`;
|
|
604
|
+
}
|
|
605
|
+
function is_own_hold(hold, claimant) {
|
|
606
|
+
return hold.issue === claimant;
|
|
607
|
+
}
|
|
608
|
+
function is_reentry(hold, claimant) {
|
|
609
|
+
return claimant !== UNNUMBERED_ISSUE && is_own_hold(hold, claimant);
|
|
610
|
+
}
|
|
611
|
+
function held_message(hold) {
|
|
612
|
+
return `This working tree is already held by ${describe_holder(hold)}. One run at a time per working tree: a second run here commits its files onto the other run's branch. If that record is stale, run \`${FORCE_RELEASE_COMMAND}\` in this working tree \u2014 it removes a record this run did not write, so be sure the run that did has ended \u2014 and ask again.`;
|
|
613
|
+
}
|
|
614
|
+
function foreign_release_message(hold) {
|
|
615
|
+
return `This working tree is held by ${describe_holder(hold)}, and this release did not claim it \u2014 nothing was removed. Release it as that run with \`${own_release_command(hold.issue)}\`, or, once you are sure it has ended, remove the record with \`${FORCE_RELEASE_COMMAND}\`.`;
|
|
616
|
+
}
|
|
617
|
+
function forced_release_message(hold) {
|
|
618
|
+
return `Forced: removed the run record held by ${describe_holder(hold)}.`;
|
|
619
|
+
}
|
|
620
|
+
function unreadable_message() {
|
|
621
|
+
return `This working tree's run record could not be read \u2014 ${NOT_IDLE}. Run \`${FORCE_RELEASE_COMMAND}\` in this working tree to clear it, then ask again.`;
|
|
622
|
+
}
|
|
623
|
+
function stale_message(hold) {
|
|
624
|
+
const why = is_owner_gone(hold) ? "the session that wrote it has ended" : `older than ${String(HOLD_MAX_AGE_HOURS)} hours`;
|
|
625
|
+
return `Replaced a stale run record held by ${describe_holder(hold)}: ${why}, so the run that wrote it has ended without releasing it.`;
|
|
626
|
+
}
|
|
627
|
+
function uncommitted_message(hold) {
|
|
628
|
+
return `This working tree still has uncommitted changes, and its run record \u2014 held by ${describe_holder(hold)} \u2014 has expired or outlived its session. That is a run which stopped for a person rather than one that finished, so the tree is not free: commit or stash the work, or run \`${own_release_command(hold.issue)}\` once you are done with it, then ask again.`;
|
|
629
|
+
}
|
|
630
|
+
function race_message(read, target) {
|
|
631
|
+
if (read.kind === "held") return held_message(read.hold);
|
|
632
|
+
return `A record already exists at ${target} and this run did not write it \u2014 ${NOT_IDLE}. Either another run claimed the tree at the same moment, or the record belongs to another account. Run \`${FORCE_RELEASE_COMMAND}\`, and remove that file by hand if it is still there.`;
|
|
633
|
+
}
|
|
634
|
+
function unknown_message() {
|
|
635
|
+
return `Could not read this working tree's git directory \u2014 ${NOT_IDLE}. Run this from inside the checkout the run will edit.`;
|
|
636
|
+
}
|
|
637
|
+
var run_hold = {
|
|
638
|
+
is_stop_marked,
|
|
639
|
+
FORCE_RELEASE_COMMAND,
|
|
640
|
+
HOLD_MAX_AGE_HOURS,
|
|
641
|
+
HOLD_MAX_AGE_MS,
|
|
642
|
+
RELEASE_COMMAND,
|
|
643
|
+
UNNUMBERED_ISSUE,
|
|
644
|
+
classify: classify2,
|
|
645
|
+
create_halfrun_stop_hold,
|
|
646
|
+
create_stop_hold,
|
|
647
|
+
HALFRUN_STOP_FIELDS,
|
|
648
|
+
create_hold,
|
|
649
|
+
foreign_release_message,
|
|
650
|
+
forced_release_message,
|
|
651
|
+
held_message,
|
|
652
|
+
hold_path,
|
|
653
|
+
is_linked_worktree,
|
|
654
|
+
is_own_hold,
|
|
655
|
+
is_reentry,
|
|
656
|
+
is_tree_dirty,
|
|
657
|
+
own_release_command,
|
|
658
|
+
race_message,
|
|
659
|
+
read_hold,
|
|
660
|
+
release_hold,
|
|
661
|
+
session_owner,
|
|
662
|
+
stale_message,
|
|
663
|
+
uncommitted_message,
|
|
664
|
+
unknown_message,
|
|
665
|
+
unreadable_message,
|
|
666
|
+
worktree_directory,
|
|
667
|
+
write_hold
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// scripts/agent/agent-headless.ts
|
|
671
|
+
var KEY = "JOSH_AGENT_HEADLESS";
|
|
672
|
+
var VALUE = "1";
|
|
673
|
+
function environment() {
|
|
674
|
+
return { [KEY]: VALUE };
|
|
675
|
+
}
|
|
676
|
+
function is_headless(source = process.env) {
|
|
677
|
+
return source[KEY] === VALUE;
|
|
678
|
+
}
|
|
679
|
+
var agent_headless = { KEY, environment, is_headless };
|
|
680
|
+
|
|
681
|
+
// scripts/josh/session-language.ts
|
|
682
|
+
var ENV_KEY = "JOSH_SESSION_LANG";
|
|
683
|
+
var DEFAULT_SESSION_LANG = "ja";
|
|
684
|
+
function resolve_session_lang() {
|
|
685
|
+
const value = process.env[ENV_KEY]?.trim();
|
|
686
|
+
if (value) return { lang: value, is_default: false };
|
|
687
|
+
return { lang: DEFAULT_SESSION_LANG, is_default: true };
|
|
688
|
+
}
|
|
689
|
+
var session_language = { DEFAULT_SESSION_LANG, ENV_KEY, resolve_session_lang };
|
|
690
|
+
|
|
691
|
+
// scripts/lane/lane-reap.ts
|
|
692
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
693
|
+
|
|
694
|
+
// scripts/run/ship/run-ship-stage.ts
|
|
695
|
+
import { z as z2 } from "zod";
|
|
696
|
+
var STAGE = {
|
|
697
|
+
PREFLIGHT: "preflight",
|
|
698
|
+
REVIEW: "review",
|
|
699
|
+
GATE: "gate",
|
|
700
|
+
SYNC: "sync",
|
|
701
|
+
COMMIT: "commit",
|
|
702
|
+
ROUND_TWO: "round-2",
|
|
703
|
+
FOLLOWUP: "followup",
|
|
704
|
+
REPORT: "report"
|
|
705
|
+
};
|
|
706
|
+
var PHASE = {
|
|
707
|
+
START: "start",
|
|
708
|
+
DONE: "done",
|
|
709
|
+
FAILED: "failed",
|
|
710
|
+
SKIPPED: "skipped"
|
|
711
|
+
};
|
|
712
|
+
var RECORD_PREFIX = "josh-ship-stages-";
|
|
713
|
+
var RECORD_SEPARATOR = "-";
|
|
714
|
+
var SKIP_COMMIT_FLAG = "--skip-commit";
|
|
715
|
+
var SKIP_PUSH_FLAG = "--skip-push";
|
|
716
|
+
var WORD_SEPARATOR = " ";
|
|
145
717
|
var STARTED_SEPARATOR = ",";
|
|
146
718
|
var STARTED_INDEX = 3;
|
|
147
719
|
var WORD_COUNT = STARTED_INDEX + 1;
|
|
148
|
-
var record_schema =
|
|
149
|
-
done:
|
|
150
|
-
started:
|
|
720
|
+
var record_schema = z2.object({
|
|
721
|
+
done: z2.array(z2.string()),
|
|
722
|
+
started: z2.array(z2.string()).default([])
|
|
151
723
|
});
|
|
152
724
|
var EMPTY_RECORD = { done: [], started: [] };
|
|
153
|
-
function record_path(repository2,
|
|
154
|
-
return stamp_file.stamp_path(`${RECORD_PREFIX}${
|
|
725
|
+
function record_path(repository2, issue2) {
|
|
726
|
+
return stamp_file.stamp_path(`${RECORD_PREFIX}${issue2}${RECORD_SEPARATOR}`, repository2);
|
|
155
727
|
}
|
|
156
728
|
function parse_record(raw) {
|
|
157
729
|
return json_value.parse_with(raw, record_schema) ?? EMPTY_RECORD;
|
|
@@ -206,471 +778,104 @@ var DONE_BY = {
|
|
|
206
778
|
};
|
|
207
779
|
function is_done(stage, done, state, has_body = false) {
|
|
208
780
|
if (has_body && stage === STAGE.COMMIT && !state.is_merged) return false;
|
|
209
|
-
return DONE_BY[stage](done, state);
|
|
210
|
-
}
|
|
211
|
-
function commit_flags(state) {
|
|
212
|
-
if (!state.is_committed) return [];
|
|
213
|
-
return state.is_pushed ? [SKIP_COMMIT_FLAG, SKIP_PUSH_FLAG] : [SKIP_COMMIT_FLAG];
|
|
214
|
-
}
|
|
215
|
-
function event_text(issue, stage, phase, started = []) {
|
|
216
|
-
const words = [issue_cite.plain(issue), stage, phase];
|
|
217
|
-
const listed = [...new Set(started)].join(STARTED_SEPARATOR);
|
|
218
|
-
const full = started.length > 0 ? [...words, listed] : words;
|
|
219
|
-
return full.join(WORD_SEPARATOR);
|
|
220
|
-
}
|
|
221
|
-
function started_of(text) {
|
|
222
|
-
return text.split(WORD_SEPARATOR, WORD_COUNT)[STARTED_INDEX]?.split(STARTED_SEPARATOR);
|
|
223
|
-
}
|
|
224
|
-
var CONFLICT = "conflict";
|
|
225
|
-
function stop_reason(stage, conflicts = []) {
|
|
226
|
-
return conflicts.length > 0 ? CONFLICT : stage;
|
|
227
|
-
}
|
|
228
|
-
var run_ship_stage = {
|
|
229
|
-
CONFLICT,
|
|
230
|
-
PHASE,
|
|
231
|
-
STAGE,
|
|
232
|
-
clear,
|
|
233
|
-
commit_flags,
|
|
234
|
-
end_attempt,
|
|
235
|
-
event_text,
|
|
236
|
-
is_done,
|
|
237
|
-
mark_done,
|
|
238
|
-
mark_started,
|
|
239
|
-
read_done,
|
|
240
|
-
read_started,
|
|
241
|
-
record_path,
|
|
242
|
-
started_of,
|
|
243
|
-
stop_reason
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// scripts/run/ship/run-ship-next.ts
|
|
247
|
-
var { STAGE: STAGE2 } = run_ship_stage;
|
|
248
|
-
var COMMAND = {
|
|
249
|
-
LOG: "pnpm josh ship --log",
|
|
250
|
-
DETACH: "pnpm josh ship --detach",
|
|
251
|
-
DETACH_REVIEW: "pnpm josh ship --detach --review"
|
|
252
|
-
};
|
|
253
|
-
var ESCAPED_QUOTE = String.raw`'\''`;
|
|
254
|
-
var BARE_WORD = /^[\w%+,./:=@-]+$/u;
|
|
255
|
-
function quoted(title) {
|
|
256
|
-
return `'${title.split("'").join(ESCAPED_QUOTE)}'`;
|
|
257
|
-
}
|
|
258
|
-
function shell_word(token) {
|
|
259
|
-
return BARE_WORD.test(token) ? token : quoted(token);
|
|
260
|
-
}
|
|
261
|
-
function resume_of(args) {
|
|
262
|
-
const cites = args.cites.flatMap((cite) => ["--cite", cite]);
|
|
263
|
-
return {
|
|
264
|
-
title: args.title,
|
|
265
|
-
flags: [...args.notify, ...args.body, ...cites],
|
|
266
|
-
is_review: args.is_review
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function ship_command(command, resume) {
|
|
270
|
-
const words = [...resume.flags, resume.title].map((token) => shell_word(token));
|
|
271
|
-
return [command, ...words].join(" ");
|
|
272
|
-
}
|
|
273
|
-
function detach_again(resume) {
|
|
274
|
-
return `leave the fix uncommitted and run \`${ship_command(COMMAND.DETACH, resume)}\`.`;
|
|
275
|
-
}
|
|
276
|
-
function detach_as_started(resume) {
|
|
277
|
-
const command = resume.is_review ? COMMAND.DETACH_REVIEW : COMMAND.DETACH;
|
|
278
|
-
return `leave the fix uncommitted and run \`${ship_command(command, resume)}\`.`;
|
|
279
|
-
}
|
|
280
|
-
var AFTER_FIX = {
|
|
281
|
-
[STAGE2.PREFLIGHT]: detach_as_started,
|
|
282
|
-
[STAGE2.REVIEW]: detach_as_started,
|
|
283
|
-
[STAGE2.GATE]: detach_as_started,
|
|
284
|
-
[STAGE2.SYNC]: detach_as_started,
|
|
285
|
-
[STAGE2.COMMIT]: detach_as_started,
|
|
286
|
-
[STAGE2.ROUND_TWO]: detach_again,
|
|
287
|
-
[STAGE2.FOLLOWUP]: detach_again,
|
|
288
|
-
[STAGE2.REPORT]: detach_again
|
|
289
|
-
};
|
|
290
|
-
function conflict_step(conflicts) {
|
|
291
|
-
return `Resolve the conflict with origin/main in ${conflicts.join(", ")} (the merge is left in progress: remove the conflict markers in those files), then`;
|
|
292
|
-
}
|
|
293
|
-
function next_step(issue, stage, resume) {
|
|
294
|
-
const conflicts = resume.conflicts ?? [];
|
|
295
|
-
if (conflicts.length > 0) return `${conflict_step(conflicts)} ${AFTER_FIX[stage](resume)}`;
|
|
296
|
-
return `Read the stopped report with \`${COMMAND.LOG} ${issue}\`, fix the ${stage} failure, then ${AFTER_FIX[stage](resume)}`;
|
|
297
|
-
}
|
|
298
|
-
var run_ship_next = { COMMAND, next_step, quoted, resume_of };
|
|
299
|
-
|
|
300
|
-
// scripts/lane/lane-child-invocation.ts
|
|
301
|
-
var CHILD_INVOCATION = "fullrun";
|
|
302
|
-
var REVIEW_INVOCATION = "the reviewer of lane";
|
|
303
|
-
var RESUME_GUIDE = ".claude/skills/workflow-commands/pre-gate-cut.md";
|
|
304
|
-
var SHIP_PROCESS = String.raw`(josh(\.[jt]s)? ship|run-ship-cli\.ts)`;
|
|
305
|
-
function child_invocation(issue) {
|
|
306
|
-
issue_number_shape.require_issue_number(issue);
|
|
307
|
-
return `${CHILD_INVOCATION} ${issue_cite.plain(issue)}`;
|
|
308
|
-
}
|
|
309
|
-
function process_pattern(issue) {
|
|
310
|
-
issue_number_shape.require_issue_number(issue);
|
|
311
|
-
return `(${CHILD_INVOCATION}|${REVIEW_INVOCATION}|${SHIP_PROCESS} .*) ${issue_cite.plain(issue)}$`;
|
|
312
|
-
}
|
|
313
|
-
function review_invocation(issue) {
|
|
314
|
-
issue_number_shape.require_issue_number(issue);
|
|
315
|
-
return `${REVIEW_INVOCATION} ${issue_cite.plain(issue)}`;
|
|
316
|
-
}
|
|
317
|
-
function resume_invocation(issue) {
|
|
318
|
-
const preamble = `Resuming the lane child for issue ${issue_cite.plain(issue)} \u2014 do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md). Run \`pnpm josh run:cut --resume ${issue}\` before anything else and follow the matching verdict in ${RESUME_GUIDE}: \`resume\` goes to the gate, \`resume-impl\` continues implementation. Only on \`fresh\` proceed as an ordinary`;
|
|
319
|
-
return `${preamble} ${child_invocation(issue)}`;
|
|
320
|
-
}
|
|
321
|
-
function outage_resume_invocation(issue) {
|
|
322
|
-
const preamble = `Resuming the lane child for issue ${issue_cite.plain(issue)} after an API disconnection \u2014 your session was restored with its full context, so do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md). Continue the run from where it stopped: run \`pnpm josh run:step ${issue}\` to find the next action, redoing only the last step if it did not complete.`;
|
|
323
|
-
return `${preamble} ${child_invocation(issue)}`;
|
|
324
|
-
}
|
|
325
|
-
function ship_stop_invocation(issue, stop) {
|
|
326
|
-
const opening = `Resuming the lane child for issue ${issue_cite.plain(issue)} after its detached ship supervisor stopped at a failed stage \u2014 do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md).`;
|
|
327
|
-
const next = stop === void 0 ? `Run \`pnpm josh run:step ${issue}\` and read the stopped report it names, fix what stopped the ship, then hand the region back per chain-rule.md.` : run_ship_next.next_step(issue, stop.stage, stop);
|
|
328
|
-
return `${opening} ${next} Do not re-plan or re-implement what is already on the branch. ${child_invocation(issue)}`;
|
|
329
|
-
}
|
|
330
|
-
var lane_child_invocation = {
|
|
331
|
-
child_invocation,
|
|
332
|
-
outage_resume_invocation,
|
|
333
|
-
process_pattern,
|
|
334
|
-
resume_invocation,
|
|
335
|
-
review_invocation,
|
|
336
|
-
ship_stop_invocation
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
// scripts/lane/process-tools.ts
|
|
340
|
-
import path from "node:path";
|
|
341
|
-
var NOT_FOUND_CODE = "ENOENT";
|
|
342
|
-
var PACKAGE_HINT = "install the package that provides it (procps on Debian/Ubuntu: `apt-get install procps`)";
|
|
343
|
-
function is_not_found(error) {
|
|
344
|
-
return error instanceof Error && "code" in error && error.code === NOT_FOUND_CODE;
|
|
345
|
-
}
|
|
346
|
-
function missing_notice(command) {
|
|
347
|
-
return `\`${path.basename(command)}\` is not installed \u2014 kit's lane commands read processes with ps and pgrep; ${PACKAGE_HINT}`;
|
|
348
|
-
}
|
|
349
|
-
function throw_if_missing(command, error) {
|
|
350
|
-
if (is_not_found(error)) throw new Error(missing_notice(command), { cause: error });
|
|
351
|
-
}
|
|
352
|
-
var process_tools = { missing_notice, throw_if_missing };
|
|
353
|
-
|
|
354
|
-
// scripts/lane/lane-reap.ts
|
|
355
|
-
var PGREP_COMMAND = "pgrep";
|
|
356
|
-
var MATCH_FULL_COMMAND_LINE = "-f";
|
|
357
|
-
var CHILDREN_OF_FLAG = "-P";
|
|
358
|
-
var PS_COMMAND = "/bin/ps";
|
|
359
|
-
var PARENT_FORMAT = ["-o", "ppid=", "-p"];
|
|
360
|
-
var TERMINATE_SIGNAL = "SIGTERM";
|
|
361
|
-
var LOWEST_CHILD_PID = 2;
|
|
362
|
-
function parse_pids(printed) {
|
|
363
|
-
return printed.split("\n").map((line) => Number(line.trim())).filter((pid) => Number.isSafeInteger(pid) && pid >= LOWEST_CHILD_PID);
|
|
364
|
-
}
|
|
365
|
-
function run_probe(command, args) {
|
|
366
|
-
try {
|
|
367
|
-
return execFileSync(command, [...args], {
|
|
368
|
-
encoding: "utf8",
|
|
369
|
-
timeout: PROBE_TIMEOUT_MS,
|
|
370
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
371
|
-
});
|
|
372
|
-
} catch (error) {
|
|
373
|
-
process_tools.throw_if_missing(command, error);
|
|
374
|
-
return "";
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
function parent_of(pid) {
|
|
378
|
-
return parse_pids(run_probe(PS_COMMAND, [...PARENT_FORMAT, String(pid)]))[0];
|
|
379
|
-
}
|
|
380
|
-
function terminate(pid) {
|
|
381
|
-
try {
|
|
382
|
-
process.kill(pid, TERMINATE_SIGNAL);
|
|
383
|
-
return true;
|
|
384
|
-
} catch {
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
var SYSTEM_PROBES = {
|
|
389
|
-
matching: (pattern) => parse_pids(run_probe(PGREP_COMMAND, [MATCH_FULL_COMMAND_LINE, pattern])),
|
|
390
|
-
children_of: (pid) => parse_pids(run_probe(PGREP_COMMAND, [CHILDREN_OF_FLAG, String(pid)])),
|
|
391
|
-
parent_of,
|
|
392
|
-
terminate
|
|
393
|
-
};
|
|
394
|
-
function ancestry_of(pid, probes) {
|
|
395
|
-
const ancestry = /* @__PURE__ */ new Set([pid]);
|
|
396
|
-
let current = probes.parent_of(pid);
|
|
397
|
-
while (current !== void 0 && !ancestry.has(current)) {
|
|
398
|
-
ancestry.add(current);
|
|
399
|
-
current = probes.parent_of(current);
|
|
400
|
-
}
|
|
401
|
-
return ancestry;
|
|
402
|
-
}
|
|
403
|
-
function own_ancestry(probes = SYSTEM_PROBES) {
|
|
404
|
-
return ancestry_of(process.pid, probes);
|
|
405
|
-
}
|
|
406
|
-
function tree_of(roots, probes) {
|
|
407
|
-
const tree = [];
|
|
408
|
-
const pending = [...roots];
|
|
409
|
-
let next = pending.shift();
|
|
410
|
-
while (next !== void 0) {
|
|
411
|
-
if (!tree.includes(next)) {
|
|
412
|
-
tree.push(next);
|
|
413
|
-
pending.push(...probes.children_of(next));
|
|
414
|
-
}
|
|
415
|
-
next = pending.shift();
|
|
416
|
-
}
|
|
417
|
-
return tree;
|
|
418
|
-
}
|
|
419
|
-
function roots_of(issue, own2, probes) {
|
|
420
|
-
const pattern = lane_child_invocation.process_pattern(issue);
|
|
421
|
-
return probes.matching(pattern).filter((pid) => !own2.has(pid));
|
|
422
|
-
}
|
|
423
|
-
function reap_child(issue, probes = SYSTEM_PROBES) {
|
|
424
|
-
const own2 = own_ancestry(probes);
|
|
425
|
-
const doomed = tree_of(roots_of(issue, own2, probes), probes).filter((pid) => !own2.has(pid));
|
|
426
|
-
return doomed.filter((pid) => probes.terminate(pid));
|
|
427
|
-
}
|
|
428
|
-
function is_child_running(issue, probes = SYSTEM_PROBES) {
|
|
429
|
-
return roots_of(issue, own_ancestry(probes), probes).length > 0;
|
|
430
|
-
}
|
|
431
|
-
var lane_reap = {
|
|
432
|
-
is_child_running,
|
|
433
|
-
own_ancestry,
|
|
434
|
-
parse_pids,
|
|
435
|
-
reap_child
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
// scripts/lane/lane-registry.ts
|
|
439
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
440
|
-
import path3 from "node:path";
|
|
441
|
-
|
|
442
|
-
// scripts/discovery/repo-discovery.ts
|
|
443
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
444
|
-
import path2 from "node:path";
|
|
445
|
-
|
|
446
|
-
// scripts/discovery/repo-origin.ts
|
|
447
|
-
var GITHUB_HOST = "github.com";
|
|
448
|
-
var SSH_ALIAS_PATTERN = /^github(?:-\w+)*$/u;
|
|
449
|
-
var GIT_SUFFIX = ".git";
|
|
450
|
-
var PATH_SEPARATOR = "/";
|
|
451
|
-
var USER_SEPARATOR = "@";
|
|
452
|
-
var HOST_SEPARATOR = ":";
|
|
453
|
-
var OWNER_INDEX = 0;
|
|
454
|
-
var REPO_INDEX = 1;
|
|
455
|
-
var REPO_PATH_SEGMENTS = 2;
|
|
456
|
-
var NOT_FOUND = -1;
|
|
457
|
-
var SECTION_PREFIX = "[";
|
|
458
|
-
var ORIGIN_SECTION = /^\[remote\s+"origin"\]$/u;
|
|
459
|
-
var REMOTE_SECTION = /^\[remote\s+"[^"]+"\]$/u;
|
|
460
|
-
var URL_KEY = "url";
|
|
461
|
-
function is_github_hostname(host) {
|
|
462
|
-
return host.toLowerCase() === GITHUB_HOST;
|
|
463
|
-
}
|
|
464
|
-
function is_github_ssh_host(host) {
|
|
465
|
-
const normalized = host.toLowerCase();
|
|
466
|
-
return is_github_hostname(normalized) || SSH_ALIAS_PATTERN.test(normalized);
|
|
467
|
-
}
|
|
468
|
-
function strip_git_suffix(name) {
|
|
469
|
-
return name.endsWith(GIT_SUFFIX) ? name.slice(0, -GIT_SUFFIX.length) : name;
|
|
470
|
-
}
|
|
471
|
-
function to_identity(repository_path) {
|
|
472
|
-
const segments = repository_path.split(PATH_SEPARATOR).filter((segment) => segment !== "");
|
|
473
|
-
if (segments.length !== REPO_PATH_SEGMENTS) return void 0;
|
|
474
|
-
const owner = segments[OWNER_INDEX] ?? "";
|
|
475
|
-
const repo = strip_git_suffix(segments[REPO_INDEX] ?? "");
|
|
476
|
-
return repo === "" ? void 0 : { owner, repo };
|
|
477
|
-
}
|
|
478
|
-
function parse_scp_like(url) {
|
|
479
|
-
const user_end = url.indexOf(USER_SEPARATOR);
|
|
480
|
-
const authority = user_end === NOT_FOUND ? url : url.slice(user_end + 1);
|
|
481
|
-
const host_end = authority.indexOf(HOST_SEPARATOR);
|
|
482
|
-
if (host_end === NOT_FOUND || !is_github_ssh_host(authority.slice(0, host_end))) return void 0;
|
|
483
|
-
return to_identity(authority.slice(host_end + 1));
|
|
484
|
-
}
|
|
485
|
-
function parse_url_like(url) {
|
|
486
|
-
try {
|
|
487
|
-
const parsed = new URL(url);
|
|
488
|
-
return is_github_hostname(parsed.hostname) ? to_identity(parsed.pathname) : void 0;
|
|
489
|
-
} catch {
|
|
490
|
-
return void 0;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
function parse_origin_url(url) {
|
|
494
|
-
const trimmed2 = url.trim();
|
|
495
|
-
if (trimmed2 === "") return void 0;
|
|
496
|
-
return parse_url_like(trimmed2) ?? parse_scp_like(trimmed2);
|
|
497
|
-
}
|
|
498
|
-
function is_section_header(line) {
|
|
499
|
-
return line.startsWith(SECTION_PREFIX);
|
|
500
|
-
}
|
|
501
|
-
function origin_section_lines(lines) {
|
|
502
|
-
const start = lines.findIndex((line) => ORIGIN_SECTION.test(line));
|
|
503
|
-
if (start === NOT_FOUND) return [];
|
|
504
|
-
const rest = lines.slice(start + 1);
|
|
505
|
-
const end = rest.findIndex((line) => is_section_header(line));
|
|
506
|
-
return end === NOT_FOUND ? rest : rest.slice(0, end);
|
|
507
|
-
}
|
|
508
|
-
function url_value(line) {
|
|
509
|
-
const separator = line.indexOf("=");
|
|
510
|
-
if (separator === NOT_FOUND || line.slice(0, separator).trim() !== URL_KEY) return void 0;
|
|
511
|
-
return line.slice(separator + 1).trim();
|
|
512
|
-
}
|
|
513
|
-
function parse_origin_from_config(content) {
|
|
514
|
-
const lines = content.split("\n").map((line) => line.trim());
|
|
515
|
-
return origin_section_lines(lines).map((line) => url_value(line)).find((url) => url !== void 0);
|
|
516
|
-
}
|
|
517
|
-
function section_groups(lines) {
|
|
518
|
-
const groups = [];
|
|
519
|
-
for (const line of lines) {
|
|
520
|
-
if (is_section_header(line)) groups.push([line]);
|
|
521
|
-
else groups.at(-1)?.push(line);
|
|
522
|
-
}
|
|
523
|
-
return groups;
|
|
524
|
-
}
|
|
525
|
-
function parse_remote_urls_from_config(content) {
|
|
526
|
-
const lines = content.split("\n").map((line) => line.trim());
|
|
527
|
-
return section_groups(lines).filter((group) => REMOTE_SECTION.test(group[0] ?? "")).flatMap((group) => group.map((line) => url_value(line))).filter((url) => url !== void 0);
|
|
528
|
-
}
|
|
529
|
-
function format_identity(identity) {
|
|
530
|
-
return `${identity.owner}${PATH_SEPARATOR}${identity.repo}`.toLowerCase();
|
|
531
|
-
}
|
|
532
|
-
var repo_origin = {
|
|
533
|
-
GITHUB_HOST,
|
|
534
|
-
is_github_hostname,
|
|
535
|
-
is_github_ssh_host,
|
|
536
|
-
parse_origin_from_config,
|
|
537
|
-
parse_origin_url,
|
|
538
|
-
parse_remote_urls_from_config,
|
|
539
|
-
format_identity
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
// scripts/discovery/repo-map-logic.ts
|
|
543
|
-
var OVERRIDE_ENTRY_SEPARATOR = ",";
|
|
544
|
-
var OVERRIDE_PAIR_SEPARATOR = "=";
|
|
545
|
-
var OVERRIDE_PAIR_PARTS = 2;
|
|
546
|
-
var KEY_INDEX = 0;
|
|
547
|
-
var PATH_INDEX = 1;
|
|
548
|
-
function is_same_owner(identity, current_owner) {
|
|
549
|
-
return identity.owner.toLowerCase() === current_owner.toLowerCase();
|
|
550
|
-
}
|
|
551
|
-
function add_entry(target, origin_url, repository_path, current_owner) {
|
|
552
|
-
if (origin_url === void 0) return;
|
|
553
|
-
const identity = repo_origin.parse_origin_url(origin_url);
|
|
554
|
-
if (identity === void 0 || !is_same_owner(identity, current_owner)) return;
|
|
555
|
-
target.set(repo_origin.format_identity(identity), repository_path);
|
|
556
|
-
}
|
|
557
|
-
function parse_override_entry(entry) {
|
|
558
|
-
const parts = entry.split(OVERRIDE_PAIR_SEPARATOR).map((part) => part.trim());
|
|
559
|
-
if (parts.length !== OVERRIDE_PAIR_PARTS) return void 0;
|
|
560
|
-
const key = parts[KEY_INDEX] ?? "";
|
|
561
|
-
const repository_path = parts[PATH_INDEX] ?? "";
|
|
562
|
-
if (repository_path === "") return void 0;
|
|
563
|
-
return { path: repository_path, origin_url: `https://${repo_origin.GITHUB_HOST}/${key}` };
|
|
564
|
-
}
|
|
565
|
-
function parse_overrides(raw) {
|
|
566
|
-
if (raw === void 0) return [];
|
|
567
|
-
return raw.split(OVERRIDE_ENTRY_SEPARATOR).map((entry) => parse_override_entry(entry)).filter((entry) => entry !== void 0);
|
|
568
|
-
}
|
|
569
|
-
function build_repository_map(discovered, current_owner, override_value2) {
|
|
570
|
-
const result = /* @__PURE__ */ new Map();
|
|
571
|
-
for (const entry of discovered) add_entry(result, entry.origin_url, entry.path, current_owner);
|
|
572
|
-
for (const entry of parse_overrides(override_value2)) {
|
|
573
|
-
add_entry(result, entry.origin_url, entry.path, current_owner);
|
|
574
|
-
}
|
|
575
|
-
return result;
|
|
576
|
-
}
|
|
577
|
-
var repo_map_logic = {
|
|
578
|
-
is_same_owner,
|
|
579
|
-
build_repository_map
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
// scripts/discovery/repo-discovery.ts
|
|
583
|
-
var GIT_DIRECTORY = ".git";
|
|
584
|
-
var GIT_CONFIG = "config";
|
|
585
|
-
var GITDIR_PREFIX = "gitdir:";
|
|
586
|
-
var COMMON_DIRECTORY_FILE = "commondir";
|
|
587
|
-
var OVERRIDE_ENV_KEY = "JOSH_REPO_PATHS";
|
|
588
|
-
function resolve_common_directory(git_directory) {
|
|
589
|
-
const pointer = path2.join(git_directory, COMMON_DIRECTORY_FILE);
|
|
590
|
-
if (!existsSync(pointer)) return git_directory;
|
|
591
|
-
try {
|
|
592
|
-
return path2.resolve(git_directory, readFileSync(pointer, "utf8").trim());
|
|
593
|
-
} catch {
|
|
594
|
-
return git_directory;
|
|
595
|
-
}
|
|
781
|
+
return DONE_BY[stage](done, state);
|
|
596
782
|
}
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return path2.resolve(repository_path, pointer.slice(GITDIR_PREFIX.length).trim());
|
|
783
|
+
function commit_flags(state) {
|
|
784
|
+
if (!state.is_committed) return [];
|
|
785
|
+
return state.is_pushed ? [SKIP_COMMIT_FLAG, SKIP_PUSH_FLAG] : [SKIP_COMMIT_FLAG];
|
|
601
786
|
}
|
|
602
|
-
function
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const linked = follow_git_file(repository_path, candidate);
|
|
608
|
-
return linked === void 0 ? void 0 : resolve_common_directory(linked);
|
|
609
|
-
} catch {
|
|
610
|
-
return void 0;
|
|
611
|
-
}
|
|
787
|
+
function event_text(issue2, stage, phase, started = []) {
|
|
788
|
+
const words = [issue_cite.plain(issue2), stage, phase];
|
|
789
|
+
const listed = [...new Set(started)].join(STARTED_SEPARATOR);
|
|
790
|
+
const full = started.length > 0 ? [...words, listed] : words;
|
|
791
|
+
return full.join(WORD_SEPARATOR);
|
|
612
792
|
}
|
|
613
|
-
function
|
|
614
|
-
|
|
615
|
-
if (git_directory === void 0) return void 0;
|
|
616
|
-
try {
|
|
617
|
-
return readFileSync(path2.join(git_directory, GIT_CONFIG), "utf8");
|
|
618
|
-
} catch {
|
|
619
|
-
return void 0;
|
|
620
|
-
}
|
|
793
|
+
function started_of(text2) {
|
|
794
|
+
return text2.split(WORD_SEPARATOR, WORD_COUNT)[STARTED_INDEX]?.split(STARTED_SEPARATOR);
|
|
621
795
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
return
|
|
796
|
+
var CONFLICT = "conflict";
|
|
797
|
+
function stop_reason(stage, conflicts = []) {
|
|
798
|
+
return conflicts.length > 0 ? CONFLICT : stage;
|
|
625
799
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
800
|
+
var run_ship_stage = {
|
|
801
|
+
CONFLICT,
|
|
802
|
+
PHASE,
|
|
803
|
+
STAGE,
|
|
804
|
+
clear,
|
|
805
|
+
commit_flags,
|
|
806
|
+
end_attempt,
|
|
807
|
+
event_text,
|
|
808
|
+
is_done,
|
|
809
|
+
mark_done,
|
|
810
|
+
mark_started,
|
|
811
|
+
read_done,
|
|
812
|
+
read_started,
|
|
813
|
+
record_path,
|
|
814
|
+
started_of,
|
|
815
|
+
stop_reason
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
// scripts/run/ship/run-ship-next.ts
|
|
819
|
+
var { STAGE: STAGE2 } = run_ship_stage;
|
|
820
|
+
var COMMAND = {
|
|
821
|
+
LOG: "pnpm josh ship --log",
|
|
822
|
+
DETACH: "pnpm josh ship --detach",
|
|
823
|
+
DETACH_REVIEW: "pnpm josh ship --detach --review"
|
|
824
|
+
};
|
|
825
|
+
var ESCAPED_QUOTE = String.raw`'\''`;
|
|
826
|
+
var BARE_WORD = /^[\w%+,./:=@-]+$/u;
|
|
827
|
+
function quoted(title) {
|
|
828
|
+
return `'${title.split("'").join(ESCAPED_QUOTE)}'`;
|
|
629
829
|
}
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
return readdirSync(parent, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path2.join(parent, entry.name)).toSorted((left, right) => left.localeCompare(right));
|
|
633
|
-
} catch {
|
|
634
|
-
return [];
|
|
635
|
-
}
|
|
830
|
+
function shell_word(token) {
|
|
831
|
+
return BARE_WORD.test(token) ? token : quoted(token);
|
|
636
832
|
}
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
833
|
+
function resume_of(args) {
|
|
834
|
+
const cites = args.cites.flatMap((cite) => ["--cite", cite]);
|
|
835
|
+
return {
|
|
836
|
+
title: args.title,
|
|
837
|
+
flags: [...args.notify, ...args.body, ...cites],
|
|
838
|
+
is_review: args.is_review
|
|
839
|
+
};
|
|
642
840
|
}
|
|
643
|
-
function
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
return repo_origin.parse_origin_url(origin_url)?.owner;
|
|
841
|
+
function ship_command(command, resume) {
|
|
842
|
+
const words = [...resume.flags, resume.title].map((token) => shell_word(token));
|
|
843
|
+
return [command, ...words].join(" ");
|
|
647
844
|
}
|
|
648
|
-
function
|
|
649
|
-
|
|
650
|
-
if (git_directory === void 0 || path2.basename(git_directory) !== GIT_DIRECTORY) {
|
|
651
|
-
return repository_path;
|
|
652
|
-
}
|
|
653
|
-
return path2.dirname(git_directory);
|
|
845
|
+
function detach_again(resume) {
|
|
846
|
+
return `leave the fix uncommitted and run \`${ship_command(COMMAND.DETACH, resume)}\`.`;
|
|
654
847
|
}
|
|
655
|
-
function
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
if (current_owner === void 0) return /* @__PURE__ */ new Map();
|
|
659
|
-
return repo_map_logic.build_repository_map(
|
|
660
|
-
scan_siblings(path2.dirname(main)),
|
|
661
|
-
current_owner,
|
|
662
|
-
environment3[OVERRIDE_ENV_KEY]
|
|
663
|
-
);
|
|
848
|
+
function detach_as_started(resume) {
|
|
849
|
+
const command = resume.is_review ? COMMAND.DETACH_REVIEW : COMMAND.DETACH;
|
|
850
|
+
return `leave the fix uncommitted and run \`${ship_command(command, resume)}\`.`;
|
|
664
851
|
}
|
|
665
|
-
var
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
852
|
+
var AFTER_FIX = {
|
|
853
|
+
[STAGE2.PREFLIGHT]: detach_as_started,
|
|
854
|
+
[STAGE2.REVIEW]: detach_as_started,
|
|
855
|
+
[STAGE2.GATE]: detach_as_started,
|
|
856
|
+
[STAGE2.SYNC]: detach_as_started,
|
|
857
|
+
[STAGE2.COMMIT]: detach_as_started,
|
|
858
|
+
[STAGE2.ROUND_TWO]: detach_again,
|
|
859
|
+
[STAGE2.FOLLOWUP]: detach_again,
|
|
860
|
+
[STAGE2.REPORT]: detach_again
|
|
673
861
|
};
|
|
862
|
+
function conflict_step(conflicts) {
|
|
863
|
+
return `Resolve the conflict with origin/main in ${conflicts.join(", ")} (the merge is left in progress: remove the conflict markers in those files), then`;
|
|
864
|
+
}
|
|
865
|
+
function fix_step(issue2, stage, is_excerpted) {
|
|
866
|
+
if (is_excerpted) return `Fix the ${stage} failure shown in the output below`;
|
|
867
|
+
return `Read the stopped report with \`${COMMAND.LOG} ${issue2}\`, fix the ${stage} failure`;
|
|
868
|
+
}
|
|
869
|
+
function next_step(issue2, stage, resume, is_excerpted = false) {
|
|
870
|
+
const conflicts = resume.conflicts ?? [];
|
|
871
|
+
if (conflicts.length > 0) return `${conflict_step(conflicts)} ${AFTER_FIX[stage](resume)}`;
|
|
872
|
+
return `${fix_step(issue2, stage, is_excerpted)}, then ${AFTER_FIX[stage](resume)}`;
|
|
873
|
+
}
|
|
874
|
+
var run_ship_next = { COMMAND, next_step, quoted, resume_of };
|
|
875
|
+
|
|
876
|
+
// scripts/run/ship/run-ship-stop-context.ts
|
|
877
|
+
import { stripVTControlCharacters } from "node:util";
|
|
878
|
+
import { z as z6 } from "zod";
|
|
674
879
|
|
|
675
880
|
// scripts/git/git-ls-remote.ts
|
|
676
881
|
var LS_REMOTE_COMMAND = "ls-remote";
|
|
@@ -687,7 +892,7 @@ import { linkSync, renameSync, rmSync } from "node:fs";
|
|
|
687
892
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
688
893
|
|
|
689
894
|
// scripts/git/git-common-directory.ts
|
|
690
|
-
import { execFileSync
|
|
895
|
+
import { execFileSync } from "node:child_process";
|
|
691
896
|
var OWN_GIT_DIRECTORY = 0;
|
|
692
897
|
var COMMON_GIT_DIRECTORY = 1;
|
|
693
898
|
var PROBE_TIMEOUT_MS2 = 5e3;
|
|
@@ -703,7 +908,7 @@ function cache_key(cwd) {
|
|
|
703
908
|
}
|
|
704
909
|
function read_directories(cwd) {
|
|
705
910
|
try {
|
|
706
|
-
const output =
|
|
911
|
+
const output = execFileSync(
|
|
707
912
|
git_utilities.get_git_command_for_spawn(),
|
|
708
913
|
[...git_command.GIT_DIRECTORY_ARGUMENTS],
|
|
709
914
|
{
|
|
@@ -758,7 +963,7 @@ function parse_owner(raw) {
|
|
|
758
963
|
function read_owner(target) {
|
|
759
964
|
return parse_owner(stamp_file.read_stamp_text(target));
|
|
760
965
|
}
|
|
761
|
-
function
|
|
966
|
+
function is_owner_gone2(owner) {
|
|
762
967
|
return process_identity.is_same_process(owner.pid, owner.process_start) === false;
|
|
763
968
|
}
|
|
764
969
|
function restore(aside, target) {
|
|
@@ -781,7 +986,7 @@ function move_aside(target) {
|
|
|
781
986
|
return void 0;
|
|
782
987
|
}
|
|
783
988
|
}
|
|
784
|
-
function clear_stale(target, is_gone2 =
|
|
989
|
+
function clear_stale(target, is_gone2 = is_owner_gone2) {
|
|
785
990
|
const raw = stale_record(target, is_gone2);
|
|
786
991
|
if (raw === void 0) return;
|
|
787
992
|
const aside = move_aside(target);
|
|
@@ -852,241 +1057,607 @@ async function locked(arguments_, timeout_ms) {
|
|
|
852
1057
|
`git ${arguments_.join(" ")}: timed out waiting for the work-tree lock ${target}`
|
|
853
1058
|
);
|
|
854
1059
|
}
|
|
855
|
-
return output;
|
|
1060
|
+
return output;
|
|
1061
|
+
}
|
|
1062
|
+
async function worktree_list() {
|
|
1063
|
+
return await git_spawn.read([WORKTREE, "list", PORCELAIN_FLAG]);
|
|
1064
|
+
}
|
|
1065
|
+
async function worktree_add(directory, branch_name, start_point) {
|
|
1066
|
+
const flags = start_point === void 0 ? ["add"] : ["add", "--no-track", "-b", branch_name];
|
|
1067
|
+
return await locked([WORKTREE, ...flags, directory, start_point ?? branch_name], SUITE_TIMEOUT_MS);
|
|
1068
|
+
}
|
|
1069
|
+
async function worktree_add_detached(directory, commit) {
|
|
1070
|
+
return await locked([WORKTREE, "add", "--detach", directory, commit], SUITE_TIMEOUT_MS);
|
|
1071
|
+
}
|
|
1072
|
+
async function worktree_remove(directory) {
|
|
1073
|
+
return await locked([WORKTREE, "remove", "--force", directory], INSTALL_TIMEOUT_MS);
|
|
1074
|
+
}
|
|
1075
|
+
async function worktree_prune() {
|
|
1076
|
+
return await locked([WORKTREE, "prune"]);
|
|
1077
|
+
}
|
|
1078
|
+
async function ls_remote_branch(branch_name) {
|
|
1079
|
+
return await git_spawn.read_remote(ls_remote_branch_arguments(branch_name));
|
|
1080
|
+
}
|
|
1081
|
+
async function branch_delete(branch_name) {
|
|
1082
|
+
return await git_spawn.read(["branch", "-D", branch_name]);
|
|
1083
|
+
}
|
|
1084
|
+
var git_worktree = {
|
|
1085
|
+
branch_delete,
|
|
1086
|
+
ls_remote_branch,
|
|
1087
|
+
worktree_add,
|
|
1088
|
+
worktree_add_detached,
|
|
1089
|
+
worktree_list,
|
|
1090
|
+
worktree_prune,
|
|
1091
|
+
worktree_remove
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
// scripts/gh/git-gh-api-path.ts
|
|
1095
|
+
var FULL_PAGE_QUERY = "?per_page=100";
|
|
1096
|
+
function repo_api_path(repo = "{owner}/{repo}") {
|
|
1097
|
+
return `repos/${repo}`;
|
|
1098
|
+
}
|
|
1099
|
+
function issues_api_path(repo) {
|
|
1100
|
+
return `${repo_api_path(repo)}/issues`;
|
|
1101
|
+
}
|
|
1102
|
+
function issue_api_path(issue_number, repo) {
|
|
1103
|
+
return `${issues_api_path(repo)}/${issue_number}`;
|
|
1104
|
+
}
|
|
1105
|
+
function blocked_by_api_path(issue_number, repo) {
|
|
1106
|
+
return `${issue_api_path(issue_number, repo)}/dependencies/blocked_by`;
|
|
1107
|
+
}
|
|
1108
|
+
function issue_comments_api_path(issue_number, repo) {
|
|
1109
|
+
return `${issue_api_path(issue_number, repo)}/comments`;
|
|
1110
|
+
}
|
|
1111
|
+
function pulls_api_path(repo) {
|
|
1112
|
+
return `${repo_api_path(repo)}/pulls`;
|
|
1113
|
+
}
|
|
1114
|
+
function pull_api_path(pr_number, repo) {
|
|
1115
|
+
return `${pulls_api_path(repo)}/${pr_number}`;
|
|
1116
|
+
}
|
|
1117
|
+
function pull_merge_api_path(pr_number, repo) {
|
|
1118
|
+
return `${pull_api_path(pr_number, repo)}/merge`;
|
|
1119
|
+
}
|
|
1120
|
+
function pull_comments_api_path(pr_number, repo) {
|
|
1121
|
+
return `${pull_api_path(pr_number, repo)}/comments`;
|
|
1122
|
+
}
|
|
1123
|
+
function pull_reviews_api_path(pr_number, repo) {
|
|
1124
|
+
return `${pull_api_path(pr_number, repo)}/reviews`;
|
|
1125
|
+
}
|
|
1126
|
+
function commit_api_path(commit_sha, repo) {
|
|
1127
|
+
return `${repo_api_path(repo)}/commits/${commit_sha}`;
|
|
1128
|
+
}
|
|
1129
|
+
function commit_check_runs_api_path(commit_sha, repo) {
|
|
1130
|
+
return `${commit_api_path(commit_sha, repo)}/check-runs`;
|
|
1131
|
+
}
|
|
1132
|
+
function commit_status_api_path(commit_sha, repo) {
|
|
1133
|
+
return `${commit_api_path(commit_sha, repo)}/status`;
|
|
1134
|
+
}
|
|
1135
|
+
function tag_reference_api_path(tag, repo) {
|
|
1136
|
+
return `${repo_api_path(repo)}/git/ref/tags/${tag}`;
|
|
1137
|
+
}
|
|
1138
|
+
function release_by_tag_api_path(tag, repo) {
|
|
1139
|
+
return `${repo_api_path(repo)}/releases/tags/${tag}`;
|
|
1140
|
+
}
|
|
1141
|
+
var git_gh_api_path = {
|
|
1142
|
+
FULL_PAGE_QUERY,
|
|
1143
|
+
repo_api_path,
|
|
1144
|
+
tag_ref_api_path: tag_reference_api_path,
|
|
1145
|
+
release_by_tag_api_path,
|
|
1146
|
+
issues_api_path,
|
|
1147
|
+
issue_api_path,
|
|
1148
|
+
blocked_by_api_path,
|
|
1149
|
+
issue_comments_api_path,
|
|
1150
|
+
pulls_api_path,
|
|
1151
|
+
pull_api_path,
|
|
1152
|
+
pull_merge_api_path,
|
|
1153
|
+
pull_comments_api_path,
|
|
1154
|
+
pull_reviews_api_path,
|
|
1155
|
+
commit_api_path,
|
|
1156
|
+
commit_check_runs_api_path,
|
|
1157
|
+
commit_status_api_path
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// scripts/gh/git-gh-exec.ts
|
|
1161
|
+
import { execa as execa2, execaSync } from "execa";
|
|
1162
|
+
|
|
1163
|
+
// scripts/gh/git-gh-check.ts
|
|
1164
|
+
import { execa } from "execa";
|
|
1165
|
+
var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
|
|
1166
|
+
var gh_check = {};
|
|
1167
|
+
async function run_gh_check() {
|
|
1168
|
+
try {
|
|
1169
|
+
await execa("gh", ["--version"], { timeout: COMMAND_TIMEOUT_MS });
|
|
1170
|
+
} catch {
|
|
1171
|
+
delete gh_check.promise;
|
|
1172
|
+
throw new Error(GH_NOT_INSTALLED_MSG);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
async function check_gh_installed() {
|
|
1176
|
+
gh_check.promise ??= run_gh_check();
|
|
1177
|
+
await gh_check.promise;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// scripts/git/parse-json-array.ts
|
|
1181
|
+
function parse_json_loose(raw_json) {
|
|
1182
|
+
return json_value.parse_or_undefined(raw_json);
|
|
1183
|
+
}
|
|
1184
|
+
function parse_json_array_or_undefined(raw_json, element_schema) {
|
|
1185
|
+
const value = parse_json_loose(raw_json);
|
|
1186
|
+
if (!Array.isArray(value)) return void 0;
|
|
1187
|
+
return element_schema.array().parse(value);
|
|
1188
|
+
}
|
|
1189
|
+
function read_json_listing(raw_json, element_schema) {
|
|
1190
|
+
try {
|
|
1191
|
+
const rows = parse_json_array_or_undefined(raw_json, element_schema);
|
|
1192
|
+
return rows === void 0 ? { kind: "unreadable" } : { kind: "read", rows };
|
|
1193
|
+
} catch {
|
|
1194
|
+
return { kind: "unexpected_shape" };
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
function parse_json_object_or_undefined(raw_json, schema) {
|
|
1198
|
+
const value = parse_json_loose(raw_json);
|
|
1199
|
+
if (value === void 0) return void 0;
|
|
1200
|
+
return schema.parse(value);
|
|
1201
|
+
}
|
|
1202
|
+
var parse_json = {
|
|
1203
|
+
parse_json_object_or_undefined
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// scripts/gh/git-gh-failure.ts
|
|
1207
|
+
import { z as z3 } from "zod";
|
|
1208
|
+
var error_document_schema = z3.object({ status: z3.union([z3.string(), z3.number()]).optional() }).catch({});
|
|
1209
|
+
var GH_FAILURE_FIELD = "gh_failure";
|
|
1210
|
+
function has_gh_failure(error) {
|
|
1211
|
+
return error instanceof Error && Object.hasOwn(error, GH_FAILURE_FIELD);
|
|
1212
|
+
}
|
|
1213
|
+
function parse_response_status(stdout) {
|
|
1214
|
+
const document = parse_json.parse_json_object_or_undefined(stdout, error_document_schema);
|
|
1215
|
+
if (document?.status === void 0) return void 0;
|
|
1216
|
+
const status = Number(document.status);
|
|
1217
|
+
return Number.isSafeInteger(status) ? status : void 0;
|
|
1218
|
+
}
|
|
1219
|
+
function classify_stdout(stdout) {
|
|
1220
|
+
return { status: parse_response_status(stdout) };
|
|
1221
|
+
}
|
|
1222
|
+
function attach(error, failure) {
|
|
1223
|
+
Object.defineProperty(error, GH_FAILURE_FIELD, {
|
|
1224
|
+
value: failure,
|
|
1225
|
+
enumerable: false,
|
|
1226
|
+
configurable: true
|
|
1227
|
+
});
|
|
1228
|
+
return error;
|
|
1229
|
+
}
|
|
1230
|
+
function failure_of(error) {
|
|
1231
|
+
return has_gh_failure(error) ? error.gh_failure : void 0;
|
|
1232
|
+
}
|
|
1233
|
+
var gh_failure = { attach, classify_stdout, failure_of, parse_response_status };
|
|
1234
|
+
|
|
1235
|
+
// scripts/gh/git-gh-exec.ts
|
|
1236
|
+
var BODY_FROM_STDIN = "-";
|
|
1237
|
+
var GH_REQUEST_TIMEOUT_MS = 6e4;
|
|
1238
|
+
function to_timeout_option(timeout_ms) {
|
|
1239
|
+
return { timeout: timeout_ms ?? GH_REQUEST_TIMEOUT_MS };
|
|
1240
|
+
}
|
|
1241
|
+
function direct_environment() {
|
|
1242
|
+
const removed = agent_session_environment.removed_proxy_environment();
|
|
1243
|
+
return Object.keys(removed).length > 0 ? { env: removed } : {};
|
|
1244
|
+
}
|
|
1245
|
+
function to_spawn_options(timeout_ms, cwd) {
|
|
1246
|
+
return {
|
|
1247
|
+
...to_timeout_option(timeout_ms),
|
|
1248
|
+
...direct_environment(),
|
|
1249
|
+
...cwd !== void 0 && { cwd }
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
var GH_REQUEST_TIMEOUT_MESSAGE = "gh request timed out";
|
|
1253
|
+
function has_stderr_field(error) {
|
|
1254
|
+
return error instanceof Error && "stderr" in error && typeof error.stderr === "string";
|
|
1255
|
+
}
|
|
1256
|
+
function has_stdout_field(error) {
|
|
1257
|
+
return error instanceof Error && "stdout" in error && typeof error.stdout === "string";
|
|
1258
|
+
}
|
|
1259
|
+
function to_timeout_prefix(error) {
|
|
1260
|
+
return has_timed_out(error) ? `${GH_REQUEST_TIMEOUT_MESSAGE}: ` : "";
|
|
1261
|
+
}
|
|
1262
|
+
function to_gh_error(error) {
|
|
1263
|
+
const stderr = has_stderr_field(error) ? error.stderr.trim() : "";
|
|
1264
|
+
const stdout = has_stdout_field(error) ? error.stdout.trim() : "";
|
|
1265
|
+
const summary = stderr.length > 0 ? stderr : error_text.message_of(error);
|
|
1266
|
+
const detail = stdout.length > 0 ? `${summary}
|
|
1267
|
+
${stdout}` : summary;
|
|
1268
|
+
return gh_failure.attach(
|
|
1269
|
+
new Error(`${to_timeout_prefix(error)}${detail}`, { cause: error }),
|
|
1270
|
+
gh_failure.classify_stdout(stdout)
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
async function exec_gh_command(arguments_, timeout_ms, cwd) {
|
|
1274
|
+
await check_gh_installed();
|
|
1275
|
+
const options = to_spawn_options(timeout_ms, cwd);
|
|
1276
|
+
try {
|
|
1277
|
+
const { stdout } = await execa2("gh", arguments_, options);
|
|
1278
|
+
return stdout.trimEnd();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
throw to_gh_error(error);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
async function exec_gh_command_with_stdin(input) {
|
|
1284
|
+
await check_gh_installed();
|
|
1285
|
+
const options = { input: input.stdin_body, ...to_spawn_options(input.timeout_ms, input.cwd) };
|
|
1286
|
+
try {
|
|
1287
|
+
const { stdout } = await execa2("gh", input.args, options);
|
|
1288
|
+
return stdout.trimEnd();
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
throw to_gh_error(error);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
var STATUS_LINE_PATTERN = /^HTTP\/[\d.]+ (?<status>\d{3})\b/u;
|
|
1294
|
+
function parse_status_line(output) {
|
|
1295
|
+
const { status } = STATUS_LINE_PATTERN.exec(output)?.groups ?? {};
|
|
1296
|
+
return status === void 0 ? void 0 : Number(status);
|
|
1297
|
+
}
|
|
1298
|
+
function status_of_failure(error) {
|
|
1299
|
+
return has_stdout_field(error) ? parse_status_line(error.stdout) : void 0;
|
|
1300
|
+
}
|
|
1301
|
+
async function exec_gh_api_status(path6, timeout_ms) {
|
|
1302
|
+
const options = to_spawn_options(timeout_ms);
|
|
1303
|
+
try {
|
|
1304
|
+
await check_gh_installed();
|
|
1305
|
+
const { stdout } = await execa2("gh", ["api", "--include", "--silent", path6], options);
|
|
1306
|
+
return parse_status_line(stdout);
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
return status_of_failure(error);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function exec_gh_api_status_sync(path6, timeout_ms) {
|
|
1312
|
+
const options = to_spawn_options(timeout_ms);
|
|
1313
|
+
try {
|
|
1314
|
+
const { stdout } = execaSync("gh", ["api", "--include", "--silent", path6], options);
|
|
1315
|
+
return parse_status_line(stdout);
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
return status_of_failure(error);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
var METHOD_FLAG = "--method";
|
|
1321
|
+
var INPUT_FLAG = "--input";
|
|
1322
|
+
var PAGINATE_FLAG = "--paginate";
|
|
1323
|
+
var SLURP_FLAG = "--slurp";
|
|
1324
|
+
var JQ_FLAG = "--jq";
|
|
1325
|
+
function optional_flag(flag, value) {
|
|
1326
|
+
return value === void 0 ? [] : [flag, value];
|
|
1327
|
+
}
|
|
1328
|
+
function to_gh_api_args(request) {
|
|
1329
|
+
const body_args = request.body === void 0 ? [] : [INPUT_FLAG, BODY_FROM_STDIN];
|
|
1330
|
+
const paginate_args = request.should_paginate === true ? [PAGINATE_FLAG] : [];
|
|
1331
|
+
const slurp_args = request.should_slurp === true ? [SLURP_FLAG] : [];
|
|
1332
|
+
return [
|
|
1333
|
+
"api",
|
|
1334
|
+
request.path,
|
|
1335
|
+
...optional_flag(METHOD_FLAG, request.method),
|
|
1336
|
+
...body_args,
|
|
1337
|
+
...paginate_args,
|
|
1338
|
+
...slurp_args,
|
|
1339
|
+
...optional_flag(JQ_FLAG, request.jq_filter)
|
|
1340
|
+
];
|
|
1341
|
+
}
|
|
1342
|
+
async function exec_gh_api(request) {
|
|
1343
|
+
const args = to_gh_api_args(request);
|
|
1344
|
+
if (request.body === void 0) {
|
|
1345
|
+
return await exec_gh_command(args, request.timeout_ms, request.cwd);
|
|
1346
|
+
}
|
|
1347
|
+
return await exec_gh_command_with_stdin({
|
|
1348
|
+
args,
|
|
1349
|
+
stdin_body: request.body,
|
|
1350
|
+
timeout_ms: request.timeout_ms,
|
|
1351
|
+
cwd: request.cwd
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
function exec_gh_api_sync(request) {
|
|
1355
|
+
const body_option = request.body === void 0 ? {} : { input: request.body };
|
|
1356
|
+
const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
|
|
1357
|
+
try {
|
|
1358
|
+
const { stdout } = execaSync("gh", to_gh_api_args(request), options);
|
|
1359
|
+
return stdout.trimEnd();
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
throw to_gh_error(error);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function read_gh_api_sync(request) {
|
|
1365
|
+
try {
|
|
1366
|
+
return exec_gh_api_sync(request);
|
|
1367
|
+
} catch {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
var git_gh_exec = {
|
|
1372
|
+
direct_environment,
|
|
1373
|
+
exec_gh_command,
|
|
1374
|
+
exec_gh_command_with_stdin,
|
|
1375
|
+
exec_gh_api,
|
|
1376
|
+
exec_gh_api_sync,
|
|
1377
|
+
read_gh_api_sync,
|
|
1378
|
+
exec_gh_api_status,
|
|
1379
|
+
exec_gh_api_status_sync,
|
|
1380
|
+
parse_status_line
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// scripts/gh/git-gh-helpers.ts
|
|
1384
|
+
function parse_pr_state_string(result) {
|
|
1385
|
+
const trimmed = result.trim();
|
|
1386
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1387
|
+
}
|
|
1388
|
+
function is_pr_already_exists_message(error_message) {
|
|
1389
|
+
return error_message.toLowerCase().includes("already exists");
|
|
1390
|
+
}
|
|
1391
|
+
function get_error_message_with_stderr(error) {
|
|
1392
|
+
if (!(error instanceof Error)) return String(error);
|
|
1393
|
+
if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
|
|
1394
|
+
${error.stderr}`;
|
|
1395
|
+
return error.message;
|
|
1396
|
+
}
|
|
1397
|
+
function handle_pr_create_error(error) {
|
|
1398
|
+
const error_message = get_error_message_with_stderr(error);
|
|
1399
|
+
if (is_pr_already_exists_message(error_message)) {
|
|
1400
|
+
throw new Error("PR_ALREADY_EXISTS");
|
|
1401
|
+
}
|
|
1402
|
+
throw error;
|
|
1403
|
+
}
|
|
1404
|
+
var git_gh_helpers = {
|
|
1405
|
+
parse_pr_state_string,
|
|
1406
|
+
get_error_message_with_stderr,
|
|
1407
|
+
handle_pr_create_error
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// scripts/gh/git-gh-pr-rest.ts
|
|
1411
|
+
import { z as z4 } from "zod";
|
|
1412
|
+
|
|
1413
|
+
// scripts/gh/git-gh-rest-state.ts
|
|
1414
|
+
var MERGED_STATE = "MERGED";
|
|
1415
|
+
function to_gh_state(state) {
|
|
1416
|
+
return state?.toUpperCase();
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
// scripts/gh/git-gh-pr-rest.ts
|
|
1420
|
+
var rest_repo_schema = z4.looseObject({ full_name: z4.string().optional() }).nullish();
|
|
1421
|
+
var rest_pull_side_schema = z4.looseObject({ repo: rest_repo_schema });
|
|
1422
|
+
var rest_pull_schema = z4.looseObject({
|
|
1423
|
+
number: z4.number(),
|
|
1424
|
+
html_url: z4.string().optional(),
|
|
1425
|
+
body: z4.string().nullish(),
|
|
1426
|
+
state: z4.string().optional(),
|
|
1427
|
+
merged: z4.boolean().nullish(),
|
|
1428
|
+
merged_at: z4.string().nullish(),
|
|
1429
|
+
mergeable: z4.boolean().nullish(),
|
|
1430
|
+
mergeable_state: z4.string().optional(),
|
|
1431
|
+
// `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
|
|
1432
|
+
// REST hangs the check runs and the status contexts off the commit rather than off the pull
|
|
1433
|
+
// request.
|
|
1434
|
+
head: z4.looseObject({ ref: z4.string().optional(), sha: z4.string().optional(), repo: rest_repo_schema }).nullish(),
|
|
1435
|
+
base: rest_pull_side_schema.nullish(),
|
|
1436
|
+
// `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
|
|
1437
|
+
// is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`.
|
|
1438
|
+
node_id: z4.string().optional(),
|
|
1439
|
+
auto_merge: z4.looseObject({}).nullish()
|
|
1440
|
+
});
|
|
1441
|
+
var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
|
|
1442
|
+
var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
|
|
1443
|
+
var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
|
|
1444
|
+
var OPEN_STATE = "open";
|
|
1445
|
+
var EMPTY_BODY = "";
|
|
1446
|
+
function parse_rest_pull(rest_json) {
|
|
1447
|
+
const parsed = parse_json_object_or_undefined(rest_json, rest_pull_schema);
|
|
1448
|
+
if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
|
|
1449
|
+
return parsed;
|
|
1450
|
+
}
|
|
1451
|
+
function parse_rest_pulls(rest_json) {
|
|
1452
|
+
const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
|
|
1453
|
+
if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
|
|
1454
|
+
return parsed;
|
|
1455
|
+
}
|
|
1456
|
+
function select_pull(pulls) {
|
|
1457
|
+
return pulls.find((pull) => pull.state === OPEN_STATE) ?? pulls[0];
|
|
1458
|
+
}
|
|
1459
|
+
function is_merged(pull) {
|
|
1460
|
+
return pull.merged === true || typeof pull.merged_at === "string";
|
|
1461
|
+
}
|
|
1462
|
+
function to_pr_state(pull) {
|
|
1463
|
+
return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
|
|
1464
|
+
}
|
|
1465
|
+
function to_repo_name(side) {
|
|
1466
|
+
return side?.repo?.full_name;
|
|
1467
|
+
}
|
|
1468
|
+
function is_same_repository_head(pull) {
|
|
1469
|
+
const head_repo = to_repo_name(pull.head);
|
|
1470
|
+
return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
|
|
1471
|
+
}
|
|
1472
|
+
function to_pr_info(pull) {
|
|
1473
|
+
return {
|
|
1474
|
+
mergeable: pull.mergeable,
|
|
1475
|
+
mergeStateStatus: to_gh_state(pull.mergeable_state),
|
|
1476
|
+
state: to_pr_state(pull)
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function to_pr_comments(rest_json) {
|
|
1480
|
+
const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
|
|
1481
|
+
if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
|
|
1482
|
+
return parsed.map((comment) => ({
|
|
1483
|
+
body: comment.body ?? EMPTY_BODY,
|
|
1484
|
+
url: comment.html_url,
|
|
1485
|
+
author: { login: comment.user?.login }
|
|
1486
|
+
}));
|
|
1487
|
+
}
|
|
1488
|
+
var git_gh_pr_rest = {
|
|
1489
|
+
parse_rest_pull,
|
|
1490
|
+
parse_rest_pulls,
|
|
1491
|
+
select_pull,
|
|
1492
|
+
is_same_repository_head,
|
|
1493
|
+
is_merged,
|
|
1494
|
+
to_pr_state,
|
|
1495
|
+
to_pr_info,
|
|
1496
|
+
to_pr_comments
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
// scripts/gh/git-gh-pr-read.ts
|
|
1500
|
+
var pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
1501
|
+
var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
|
|
1502
|
+
var COMMENTS_QUERY = FULL_PAGE_QUERY;
|
|
1503
|
+
var OWNER_PLACEHOLDER = "{owner}";
|
|
1504
|
+
var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
|
|
1505
|
+
var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
|
|
1506
|
+
function lookup_path(branch_name) {
|
|
1507
|
+
const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
|
|
1508
|
+
return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
|
|
1509
|
+
}
|
|
1510
|
+
async function fetch_pr_number(branch_name) {
|
|
1511
|
+
const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
|
|
1512
|
+
return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
|
|
1513
|
+
}
|
|
1514
|
+
var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
1515
|
+
var pull_by_number = /* @__PURE__ */ new Map();
|
|
1516
|
+
async function fetch_pr_number_read(branch_name) {
|
|
1517
|
+
try {
|
|
1518
|
+
const pr_number = await fetch_pr_number(branch_name);
|
|
1519
|
+
if (pr_number === void 0) return { kind: "missing" };
|
|
1520
|
+
pr_number_by_branch.set(branch_name, pr_number);
|
|
1521
|
+
return { kind: "read", pr_number };
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
return { kind: "unreadable", cause: error };
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
async function start_pr_number_read(branch_name) {
|
|
1527
|
+
const pending = pending_pr_number_by_branch.get(branch_name);
|
|
1528
|
+
if (pending !== void 0) return await pending;
|
|
1529
|
+
const lookup = fetch_pr_number_read(branch_name);
|
|
1530
|
+
pending_pr_number_by_branch.set(branch_name, lookup);
|
|
1531
|
+
try {
|
|
1532
|
+
return await lookup;
|
|
1533
|
+
} finally {
|
|
1534
|
+
pending_pr_number_by_branch.delete(branch_name);
|
|
1535
|
+
}
|
|
856
1536
|
}
|
|
857
|
-
|
|
858
|
-
|
|
1537
|
+
function forget_pr_numbers() {
|
|
1538
|
+
pr_number_by_branch.clear();
|
|
1539
|
+
pending_pr_number_by_branch.clear();
|
|
1540
|
+
pull_by_number.clear();
|
|
859
1541
|
}
|
|
860
|
-
async function
|
|
861
|
-
const
|
|
862
|
-
|
|
1542
|
+
async function read_pr_number(branch_name) {
|
|
1543
|
+
const cached = pr_number_by_branch.get(branch_name);
|
|
1544
|
+
if (cached !== void 0) return { kind: "read", pr_number: cached };
|
|
1545
|
+
return await start_pr_number_read(branch_name);
|
|
863
1546
|
}
|
|
864
|
-
async function
|
|
865
|
-
|
|
1547
|
+
async function resolve_pr_number(branch_name) {
|
|
1548
|
+
const read = await read_pr_number(branch_name);
|
|
1549
|
+
return read.kind === "read" ? read.pr_number : void 0;
|
|
866
1550
|
}
|
|
867
|
-
|
|
868
|
-
return
|
|
1551
|
+
function to_unreadable_error(branch_name, cause) {
|
|
1552
|
+
return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
|
|
869
1553
|
}
|
|
870
|
-
async function
|
|
871
|
-
|
|
1554
|
+
async function require_pr_number(branch_name) {
|
|
1555
|
+
const read = await read_pr_number(branch_name);
|
|
1556
|
+
if (read.kind === "read") return read.pr_number;
|
|
1557
|
+
if (read.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
|
|
1558
|
+
throw to_unreadable_error(branch_name, read.cause);
|
|
872
1559
|
}
|
|
873
|
-
async function
|
|
874
|
-
|
|
1560
|
+
async function read_pull(pr_number) {
|
|
1561
|
+
const json = await git_gh_exec.exec_gh_api({
|
|
1562
|
+
path: git_gh_api_path.pull_api_path(String(pr_number))
|
|
1563
|
+
});
|
|
1564
|
+
return git_gh_pr_rest.parse_rest_pull(json);
|
|
875
1565
|
}
|
|
876
|
-
async function
|
|
877
|
-
|
|
1566
|
+
async function read_remembered_pull(pr_number) {
|
|
1567
|
+
const pending = pull_by_number.get(pr_number) ?? read_pull(pr_number);
|
|
1568
|
+
pull_by_number.set(pr_number, pending);
|
|
1569
|
+
try {
|
|
1570
|
+
return await pending;
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
if (pull_by_number.get(pr_number) === pending) pull_by_number.delete(pr_number);
|
|
1573
|
+
throw error;
|
|
1574
|
+
}
|
|
878
1575
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
worktree_remove
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
// scripts/agent/agent-role-profile.ts
|
|
890
|
-
import { z as z2 } from "zod";
|
|
891
|
-
var FIRST_PRINTABLE_CODE = 32;
|
|
892
|
-
var DELETE_CODE = 127;
|
|
893
|
-
var FIRST_CODE_POINT = 0;
|
|
894
|
-
var EMPTY_LENGTH = 0;
|
|
895
|
-
var MAX_VALUE_LENGTH = 4096;
|
|
896
|
-
var ROLE_SCHEMA = z2.enum(["scheduler", "worker", "reviewer"]);
|
|
897
|
-
var PROVIDER_SCHEMA = z2.enum(["anthropic", "openai"]);
|
|
898
|
-
var EFFORT_SCHEMA = z2.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
899
|
-
var PHASE_SCHEMA = z2.enum(["implementation", "pre-gate"]);
|
|
900
|
-
var PROFILE_SCHEMA = z2.object({
|
|
901
|
-
provider: PROVIDER_SCHEMA.default("anthropic"),
|
|
902
|
-
role: ROLE_SCHEMA,
|
|
903
|
-
model: z2.string(),
|
|
904
|
-
effort: EFFORT_SCHEMA
|
|
905
|
-
});
|
|
906
|
-
var SCHEDULER = "scheduler";
|
|
907
|
-
var WORKER = "worker";
|
|
908
|
-
var REVIEWER = "reviewer";
|
|
909
|
-
var IMPLEMENTATION_PHASE = "implementation";
|
|
910
|
-
var PRE_GATE_PHASE = "pre-gate";
|
|
911
|
-
var ANTHROPIC_PROVIDER = "anthropic";
|
|
912
|
-
var OPENAI_PROVIDER = "openai";
|
|
913
|
-
var OPENAI_MODEL = "gpt-6.1-sol";
|
|
914
|
-
var ANTHROPIC_MODEL = "claude-opus-5-5";
|
|
915
|
-
var CODEX_SESSION_KEY = "CODEX_THREAD_ID";
|
|
916
|
-
var DEFAULT_PROFILES = {
|
|
917
|
-
scheduler: {
|
|
918
|
-
provider: ANTHROPIC_PROVIDER,
|
|
919
|
-
role: SCHEDULER,
|
|
920
|
-
model: ANTHROPIC_MODEL,
|
|
921
|
-
effort: "medium"
|
|
922
|
-
},
|
|
923
|
-
worker: { provider: ANTHROPIC_PROVIDER, role: WORKER, model: ANTHROPIC_MODEL, effort: "medium" },
|
|
924
|
-
reviewer: {
|
|
925
|
-
provider: ANTHROPIC_PROVIDER,
|
|
926
|
-
role: REVIEWER,
|
|
927
|
-
model: ANTHROPIC_MODEL,
|
|
928
|
-
effort: "high"
|
|
1576
|
+
async function read_pull_of_branch(branch_name, read = read_remembered_pull) {
|
|
1577
|
+
const number_read = await read_pr_number(branch_name);
|
|
1578
|
+
if (number_read.kind !== "read") return number_read;
|
|
1579
|
+
try {
|
|
1580
|
+
return { kind: "read", pull: await read(number_read.pr_number) };
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
return { kind: "unreadable", cause: error };
|
|
929
1583
|
}
|
|
930
|
-
};
|
|
931
|
-
var OPENAI_PROFILES = {
|
|
932
|
-
scheduler: { provider: OPENAI_PROVIDER, role: SCHEDULER, model: OPENAI_MODEL, effort: "medium" },
|
|
933
|
-
worker: { provider: OPENAI_PROVIDER, role: WORKER, model: OPENAI_MODEL, effort: "medium" },
|
|
934
|
-
reviewer: { provider: OPENAI_PROVIDER, role: REVIEWER, model: OPENAI_MODEL, effort: "high" }
|
|
935
|
-
};
|
|
936
|
-
var PROVIDER_PROFILES = { anthropic: DEFAULT_PROFILES, openai: OPENAI_PROFILES };
|
|
937
|
-
var SHIP_EFFORT = "low";
|
|
938
|
-
var PHASE_EFFORT = {
|
|
939
|
-
worker: { [PRE_GATE_PHASE]: SHIP_EFFORT }
|
|
940
|
-
};
|
|
941
|
-
var ENV_KEYS = {
|
|
942
|
-
scheduler: { model: "JOSH_SCHEDULER_MODEL", effort: "JOSH_SCHEDULER_EFFORT" },
|
|
943
|
-
worker: { model: "JOSH_WORKER_MODEL", effort: "JOSH_WORKER_EFFORT" },
|
|
944
|
-
reviewer: { model: "JOSH_REVIEWER_MODEL", effort: "JOSH_REVIEWER_EFFORT" }
|
|
945
|
-
};
|
|
946
|
-
var LEGACY_WORKER_KEYS = { model: "JOSH_LANE_MODEL", effort: "JOSH_LANE_EFFORT" };
|
|
947
|
-
function is_control_character(character) {
|
|
948
|
-
const code = character.codePointAt(FIRST_CODE_POINT) ?? FIRST_PRINTABLE_CODE;
|
|
949
|
-
return code < FIRST_PRINTABLE_CODE || code === DELETE_CODE;
|
|
950
|
-
}
|
|
951
|
-
function is_safe_value(value) {
|
|
952
|
-
if (value.length === EMPTY_LENGTH || value.length > MAX_VALUE_LENGTH) return false;
|
|
953
|
-
for (const character of value) if (is_control_character(character)) return false;
|
|
954
|
-
return true;
|
|
955
1584
|
}
|
|
956
|
-
function
|
|
957
|
-
const
|
|
958
|
-
return
|
|
1585
|
+
async function read_pull_or_undefined(branch_name) {
|
|
1586
|
+
const read = await read_pull_of_branch(branch_name);
|
|
1587
|
+
return read.kind === "read" ? read.pull : void 0;
|
|
959
1588
|
}
|
|
960
|
-
function
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if (role !== WORKER) return { key, value: void 0 };
|
|
965
|
-
const legacy = LEGACY_WORKER_KEYS[field];
|
|
966
|
-
const legacy_value = trimmed(environment3[legacy]);
|
|
967
|
-
return legacy_value === void 0 ? { key, value: void 0 } : { key: legacy, value: legacy_value };
|
|
1589
|
+
async function pr_exists(branch_name) {
|
|
1590
|
+
const read = await read_pr_number(branch_name);
|
|
1591
|
+
if (read.kind === "unreadable") throw to_unreadable_error(branch_name, read.cause);
|
|
1592
|
+
return read.kind === "read";
|
|
968
1593
|
}
|
|
969
|
-
function
|
|
970
|
-
|
|
971
|
-
return provider === OPENAI_PROVIDER ? { key, value: void 0 } : override_value(role, "model", environment3);
|
|
1594
|
+
async function pr_get_number(branch_name) {
|
|
1595
|
+
return await resolve_pr_number(branch_name);
|
|
972
1596
|
}
|
|
973
|
-
function
|
|
974
|
-
|
|
1597
|
+
async function pr_get_url(branch_name) {
|
|
1598
|
+
const pull = await read_pull_or_undefined(branch_name);
|
|
1599
|
+
if (pull?.html_url === void 0) return void 0;
|
|
1600
|
+
return git_gh_helpers.parse_pr_state_string(pull.html_url);
|
|
975
1601
|
}
|
|
976
|
-
function
|
|
977
|
-
const
|
|
978
|
-
|
|
1602
|
+
async function pr_get_merge_state(branch_name) {
|
|
1603
|
+
const read = await read_pull_of_branch(branch_name, read_pull);
|
|
1604
|
+
if (read.kind !== "read") return void 0;
|
|
1605
|
+
const { pull } = read;
|
|
1606
|
+
return {
|
|
1607
|
+
is_merged: git_gh_pr_rest.is_merged(pull),
|
|
1608
|
+
merged_at: pull.merged_at ?? void 0,
|
|
1609
|
+
head_sha: pull.head?.sha,
|
|
1610
|
+
merge_state_status: to_gh_state(pull.mergeable_state)
|
|
1611
|
+
};
|
|
979
1612
|
}
|
|
980
|
-
function
|
|
981
|
-
|
|
1613
|
+
async function pr_get_body(branch_name) {
|
|
1614
|
+
const pull = await read_pull_or_undefined(branch_name);
|
|
1615
|
+
const body = pull?.body;
|
|
1616
|
+
return typeof body === "string" && body.length > 0 ? body : void 0;
|
|
1617
|
+
}
|
|
1618
|
+
async function pr_view(branch_name) {
|
|
1619
|
+
const read = await read_pull_of_branch(branch_name, read_pull);
|
|
1620
|
+
if (read.kind === "unreadable") throw to_unreadable_error(branch_name, read.cause);
|
|
1621
|
+
if (read.kind === "missing") return "";
|
|
1622
|
+
return JSON.stringify(git_gh_pr_rest.to_pr_info(read.pull));
|
|
1623
|
+
}
|
|
1624
|
+
async function read_comments(branch_name, to_path, to_json) {
|
|
1625
|
+
const pr_number = await resolve_pr_number(branch_name);
|
|
1626
|
+
if (pr_number === void 0) return void 0;
|
|
1627
|
+
const path6 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
|
|
1628
|
+
try {
|
|
1629
|
+
return to_json(await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true }));
|
|
1630
|
+
} catch {
|
|
1631
|
+
return void 0;
|
|
1632
|
+
}
|
|
982
1633
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
return { kind: "provider", provider: has_codex ? OPENAI_PROVIDER : ANTHROPIC_PROVIDER };
|
|
1005
|
-
}
|
|
1006
|
-
function resolve_provider(environment3 = process.env) {
|
|
1007
|
-
return detected_provider(environment3) ?? handed_provider(environment3);
|
|
1008
|
-
}
|
|
1009
|
-
function default_notice(environment3 = process.env) {
|
|
1010
|
-
const selected = resolve_provider(environment3);
|
|
1011
|
-
return selected.kind === "provider" && selected.is_default === true ? DEFAULT_NOTICE : "";
|
|
1012
|
-
}
|
|
1013
|
-
function warn_of_default(environment3 = process.env) {
|
|
1014
|
-
const notice = default_notice(environment3).trim();
|
|
1015
|
-
if (notice !== "") console.error(notice);
|
|
1016
|
-
}
|
|
1017
|
-
function has_completion_callback(environment3 = process.env) {
|
|
1018
|
-
const selected = resolve_provider(environment3);
|
|
1019
|
-
return selected.kind !== "provider" || selected.provider !== OPENAI_PROVIDER;
|
|
1020
|
-
}
|
|
1021
|
-
function handoff_environment(environment3 = process.env) {
|
|
1022
|
-
const selected = resolve_provider(environment3);
|
|
1023
|
-
return selected.kind === "provider" ? { [HANDED_PROVIDER_KEY]: selected.provider } : {};
|
|
1024
|
-
}
|
|
1025
|
-
function validate(profile, model_key) {
|
|
1026
|
-
if (!is_safe_value(profile.model)) {
|
|
1027
|
-
return rejection(model_key, profile.model, "is not a safe model");
|
|
1028
|
-
}
|
|
1029
|
-
return { kind: "profile", profile };
|
|
1030
|
-
}
|
|
1031
|
-
function phase_effort(role, phase, fallback) {
|
|
1032
|
-
const parsed = PHASE_SCHEMA.safeParse(phase);
|
|
1033
|
-
if (!parsed.success) return fallback;
|
|
1034
|
-
return PHASE_EFFORT[role]?.[parsed.data] ?? fallback;
|
|
1035
|
-
}
|
|
1036
|
-
function overridden_effort(profile, phase, environment3) {
|
|
1037
|
-
const parsed = EFFORT_SCHEMA.safeParse(override_value(profile.role, "effort", environment3).value);
|
|
1038
|
-
return parsed.success ? parsed.data : phase_effort(profile.role, phase, profile.effort);
|
|
1039
|
-
}
|
|
1040
|
-
function with_phase_effort(profile, phase, environment3 = process.env) {
|
|
1041
|
-
return { ...profile, effort: overridden_effort(profile, phase, environment3) };
|
|
1042
|
-
}
|
|
1043
|
-
function resolve2(role, environment3 = process.env, phase) {
|
|
1044
|
-
const selected = resolve_provider(environment3);
|
|
1045
|
-
if (selected.kind === "rejected") return selected;
|
|
1046
|
-
const defaults = PROVIDER_PROFILES[selected.provider][role];
|
|
1047
|
-
const model = model_override(role, environment3, selected.provider);
|
|
1048
|
-
const effort = override_value(role, "effort", environment3);
|
|
1049
|
-
const resolved = resolved_effort(effort, phase_effort(role, phase, defaults.effort));
|
|
1050
|
-
if (resolved.kind === "rejected") return resolved;
|
|
1051
|
-
const profile = {
|
|
1052
|
-
provider: selected.provider,
|
|
1053
|
-
role,
|
|
1054
|
-
model: model.value ?? defaults.model,
|
|
1055
|
-
effort: resolved.effort
|
|
1056
|
-
};
|
|
1057
|
-
return validate(profile, model.key);
|
|
1058
|
-
}
|
|
1059
|
-
function describe(profile) {
|
|
1060
|
-
return `provider=${profile.provider} role=${profile.role} model=${profile.model} effort=${profile.effort}`;
|
|
1061
|
-
}
|
|
1062
|
-
function parse(value) {
|
|
1063
|
-
const parsed = PROFILE_SCHEMA.safeParse(value);
|
|
1064
|
-
return parsed.success && is_safe_value(parsed.data.model) ? parsed.data : void 0;
|
|
1065
|
-
}
|
|
1066
|
-
var agent_role_profile = {
|
|
1067
|
-
CODEX_SESSION_KEY,
|
|
1068
|
-
DEFAULT_PROFILES,
|
|
1069
|
-
HANDED_PROVIDER_KEY,
|
|
1070
|
-
IMPLEMENTATION_PHASE,
|
|
1071
|
-
MAX_VALUE_LENGTH,
|
|
1072
|
-
OPENAI_PROFILES,
|
|
1073
|
-
PRE_GATE_PHASE,
|
|
1074
|
-
PROFILE_SCHEMA,
|
|
1075
|
-
REVIEWER,
|
|
1076
|
-
SCHEDULER,
|
|
1077
|
-
WORKER,
|
|
1078
|
-
default_notice,
|
|
1079
|
-
describe,
|
|
1080
|
-
handoff_environment,
|
|
1081
|
-
has_completion_callback,
|
|
1082
|
-
is_safe_value,
|
|
1083
|
-
parse,
|
|
1084
|
-
resolve: resolve2,
|
|
1085
|
-
resolve_provider,
|
|
1086
|
-
warn_of_default,
|
|
1087
|
-
with_phase_effort
|
|
1634
|
+
function to_comments_json(raw) {
|
|
1635
|
+
return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
|
|
1636
|
+
}
|
|
1637
|
+
function as_served(raw) {
|
|
1638
|
+
return raw;
|
|
1639
|
+
}
|
|
1640
|
+
async function pr_get_comments(branch_name) {
|
|
1641
|
+
return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
|
|
1642
|
+
}
|
|
1643
|
+
async function pr_get_review_comments(branch_name) {
|
|
1644
|
+
return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
|
|
1645
|
+
}
|
|
1646
|
+
var git_gh_pr_read = {
|
|
1647
|
+
pr_exists,
|
|
1648
|
+
pr_get_number,
|
|
1649
|
+
pr_get_url,
|
|
1650
|
+
pr_get_body,
|
|
1651
|
+
pr_get_merge_state,
|
|
1652
|
+
pr_view,
|
|
1653
|
+
pr_get_comments,
|
|
1654
|
+
pr_get_review_comments
|
|
1088
1655
|
};
|
|
1089
1656
|
|
|
1657
|
+
// scripts/lane/lane-registry.ts
|
|
1658
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "node:fs";
|
|
1659
|
+
import path2 from "node:path";
|
|
1660
|
+
|
|
1090
1661
|
// scripts/lane/lane-environment.ts
|
|
1091
1662
|
var LANE_OUTPUT_KEY = "JOSH_LANE_OUTPUT";
|
|
1092
1663
|
var LANE_PROVIDER_KEY = "JOSH_LANE_AGENT_PROVIDER";
|
|
@@ -1100,8 +1671,8 @@ var MIN_QUOTED_LENGTH = 2;
|
|
|
1100
1671
|
var FIRST_CHARACTER_END = 1;
|
|
1101
1672
|
var LAST_CHARACTER_START = -1;
|
|
1102
1673
|
function strip_export(line) {
|
|
1103
|
-
const
|
|
1104
|
-
return
|
|
1674
|
+
const trimmed = line.trimStart();
|
|
1675
|
+
return trimmed.startsWith(EXPORT_PREFIX) ? trimmed.slice(EXPORT_PREFIX.length).trimStart() : trimmed;
|
|
1105
1676
|
}
|
|
1106
1677
|
function is_key_line(line, key) {
|
|
1107
1678
|
const body = strip_export(line);
|
|
@@ -1115,8 +1686,8 @@ function is_output_line(line) {
|
|
|
1115
1686
|
return is_key_line(line, LANE_OUTPUT_KEY);
|
|
1116
1687
|
}
|
|
1117
1688
|
function split_lines(content) {
|
|
1118
|
-
const
|
|
1119
|
-
return
|
|
1689
|
+
const trimmed = content.trimEnd();
|
|
1690
|
+
return trimmed.length === 0 ? [] : trimmed.split("\n");
|
|
1120
1691
|
}
|
|
1121
1692
|
function is_quoted(raw) {
|
|
1122
1693
|
const first = raw.slice(0, FIRST_CHARACTER_END);
|
|
@@ -1223,23 +1794,26 @@ function held_seat(content) {
|
|
|
1223
1794
|
return seat_from_content(content) ?? EMPTY_SEAT;
|
|
1224
1795
|
}
|
|
1225
1796
|
function read_environment(directory) {
|
|
1226
|
-
return file_reader.read_if_readable(
|
|
1797
|
+
return file_reader.read_if_readable(path2.join(directory, ENV_FILE_NAME));
|
|
1798
|
+
}
|
|
1799
|
+
function dispatched_at(directory) {
|
|
1800
|
+
return statSync2(path2.join(directory, ENV_FILE_NAME), { throwIfNoEntry: false })?.mtimeMs;
|
|
1227
1801
|
}
|
|
1228
1802
|
function branch_issue(branch) {
|
|
1229
1803
|
if (!branch?.endsWith(lane_paths.LANE_BRANCH_SUFFIX)) return void 0;
|
|
1230
|
-
const
|
|
1231
|
-
return issue_number_shape.ISSUE_NUMBER_PATTERN.test(
|
|
1804
|
+
const issue2 = branch.slice(0, -lane_paths.LANE_BRANCH_SUFFIX.length);
|
|
1805
|
+
return issue_number_shape.ISSUE_NUMBER_PATTERN.test(issue2) ? issue2 : void 0;
|
|
1232
1806
|
}
|
|
1233
1807
|
function is_gone(directory) {
|
|
1234
1808
|
return !existsSync2(directory);
|
|
1235
1809
|
}
|
|
1236
|
-
function build_lane(
|
|
1810
|
+
function build_lane(issue2, branch, directory, is_stranded) {
|
|
1237
1811
|
const content = is_stranded ? void 0 : read_environment(directory);
|
|
1238
1812
|
const held = held_seat(content);
|
|
1239
1813
|
const output = content === void 0 ? void 0 : lane_environment.read_lane_output(content);
|
|
1240
1814
|
const profile = content === void 0 ? void 0 : lane_environment.read_lane_profile(content);
|
|
1241
1815
|
return {
|
|
1242
|
-
issue,
|
|
1816
|
+
issue: issue2,
|
|
1243
1817
|
branch,
|
|
1244
1818
|
directory,
|
|
1245
1819
|
seat: held.seat,
|
|
@@ -1247,21 +1821,22 @@ function build_lane(issue, branch, directory, is_stranded) {
|
|
|
1247
1821
|
preview_port: held.preview_port,
|
|
1248
1822
|
output,
|
|
1249
1823
|
profile,
|
|
1824
|
+
dispatched_ms: is_stranded ? void 0 : dispatched_at(directory),
|
|
1250
1825
|
is_stranded
|
|
1251
1826
|
};
|
|
1252
1827
|
}
|
|
1253
|
-
function is_lane_directory(root,
|
|
1254
|
-
return directory === lane_paths.lane_directory(root,
|
|
1828
|
+
function is_lane_directory(root, issue2, directory) {
|
|
1829
|
+
return directory === lane_paths.lane_directory(root, issue2);
|
|
1255
1830
|
}
|
|
1256
1831
|
function parse_block(block, root) {
|
|
1257
1832
|
const lines = block.split("\n");
|
|
1258
1833
|
const directory = line_value(lines, WORKTREE_PREFIX);
|
|
1259
1834
|
const branch = line_value(lines, BRANCH_PREFIX);
|
|
1260
|
-
const
|
|
1261
|
-
if (directory === void 0 || branch === void 0 ||
|
|
1835
|
+
const issue2 = branch_issue(branch);
|
|
1836
|
+
if (directory === void 0 || branch === void 0 || issue2 === void 0 || !is_lane_directory(root, issue2, directory)) {
|
|
1262
1837
|
return void 0;
|
|
1263
1838
|
}
|
|
1264
|
-
return build_lane(
|
|
1839
|
+
return build_lane(issue2, branch, directory, is_gone(directory));
|
|
1265
1840
|
}
|
|
1266
1841
|
function is_lane(lane) {
|
|
1267
1842
|
return lane !== void 0;
|
|
@@ -1287,27 +1862,23 @@ async function has_lanes_in_flight() {
|
|
|
1287
1862
|
const lanes = await list_lanes();
|
|
1288
1863
|
return lanes.some((lane) => !lane.is_stranded);
|
|
1289
1864
|
}
|
|
1290
|
-
function live_count(lanes) {
|
|
1291
|
-
return lanes.filter((lane) => !lane.is_stranded).length;
|
|
1292
|
-
}
|
|
1293
1865
|
function used_seats(lanes) {
|
|
1294
1866
|
return lanes.map((lane) => lane.seat).filter((seat) => seat !== void 0);
|
|
1295
1867
|
}
|
|
1296
1868
|
function unreadable_lanes(lanes) {
|
|
1297
1869
|
return lanes.filter((lane) => !lane.is_stranded && lane.seat === void 0);
|
|
1298
1870
|
}
|
|
1299
|
-
function find_lane(lanes,
|
|
1300
|
-
return lanes.find((lane) => lane.issue ===
|
|
1871
|
+
function find_lane(lanes, issue2) {
|
|
1872
|
+
return lanes.find((lane) => lane.issue === issue2);
|
|
1301
1873
|
}
|
|
1302
|
-
async function find_open_lane(
|
|
1303
|
-
return find_lane(await list_lanes(),
|
|
1874
|
+
async function find_open_lane(issue2) {
|
|
1875
|
+
return find_lane(await list_lanes(), issue2);
|
|
1304
1876
|
}
|
|
1305
1877
|
var lane_registry = {
|
|
1306
1878
|
find_lane,
|
|
1307
1879
|
find_open_lane,
|
|
1308
1880
|
has_lanes_in_flight,
|
|
1309
1881
|
list_lanes,
|
|
1310
|
-
live_count,
|
|
1311
1882
|
main_repository_root,
|
|
1312
1883
|
parse_block,
|
|
1313
1884
|
read_environment,
|
|
@@ -1316,6 +1887,522 @@ var lane_registry = {
|
|
|
1316
1887
|
used_seats
|
|
1317
1888
|
};
|
|
1318
1889
|
|
|
1890
|
+
// scripts/run/run-preflight.ts
|
|
1891
|
+
import { z as z5 } from "zod";
|
|
1892
|
+
var CLEAN_VERDICT = "clean";
|
|
1893
|
+
var RECLAIM_VERDICT = "reclaim";
|
|
1894
|
+
var RESUME_VERDICT = "resume";
|
|
1895
|
+
var PARK_VERDICT = "park";
|
|
1896
|
+
var NO_PR = "none";
|
|
1897
|
+
var OPEN_PR = "open";
|
|
1898
|
+
var MERGED_PR = "merged";
|
|
1899
|
+
var CLOSED_PR = "closed";
|
|
1900
|
+
var NO_CHILD_WORK = { branch_name: void 0, pr_state: NO_PR };
|
|
1901
|
+
var pr_info_schema = z5.object({ state: z5.string().optional() });
|
|
1902
|
+
var GH_PR_STATES = /* @__PURE__ */ new Map([
|
|
1903
|
+
["CLOSED", CLOSED_PR],
|
|
1904
|
+
["MERGED", MERGED_PR],
|
|
1905
|
+
["OPEN", OPEN_PR]
|
|
1906
|
+
]);
|
|
1907
|
+
var CLEAN_REASON = "Nothing was left behind: the tree is clean, HEAD is on the default branch, and this issue has no pull request and no branch holding work.";
|
|
1908
|
+
var CLEAN_ADVICE = "Start the child.";
|
|
1909
|
+
var RESUME_ADVICE = "Reuse the branch and run the whole verification gate from the start \u2014 refactor, `pnpm josh gate`, `/code-review` \u2014 never only the part the interrupted run had not reached. Nobody verified what it already committed.";
|
|
1910
|
+
var PARK_ADVICE = "Park the child: add `needs-decision` and comment what was found. Do not delete the branch, do not reopen the pull request, and do not commit on top of it.";
|
|
1911
|
+
var STASH_LABEL_PREFIX = "run:hold reclaimed before #";
|
|
1912
|
+
var { require_issue_number } = issue_number_shape;
|
|
1913
|
+
var UNREADABLE_PR_MESSAGE = "The pull request could not be read for branch ";
|
|
1914
|
+
function needs_reclaim(tree) {
|
|
1915
|
+
return tree.is_dirty || tree.branch !== tree.default_branch;
|
|
1916
|
+
}
|
|
1917
|
+
function is_pr_decided(state) {
|
|
1918
|
+
return state === MERGED_PR || state === CLOSED_PR;
|
|
1919
|
+
}
|
|
1920
|
+
function has_leftover_work(child) {
|
|
1921
|
+
return child.branch_name !== void 0 || child.pr_state === OPEN_PR;
|
|
1922
|
+
}
|
|
1923
|
+
function to_verdict(tree, child) {
|
|
1924
|
+
if (needs_reclaim(tree)) return RECLAIM_VERDICT;
|
|
1925
|
+
if (is_pr_decided(child.pr_state)) return PARK_VERDICT;
|
|
1926
|
+
if (has_leftover_work(child)) return RESUME_VERDICT;
|
|
1927
|
+
return CLEAN_VERDICT;
|
|
1928
|
+
}
|
|
1929
|
+
function reclaim_reason(tree) {
|
|
1930
|
+
const findings = [
|
|
1931
|
+
tree.is_dirty ? "uncommitted changes" : void 0,
|
|
1932
|
+
tree.branch === tree.default_branch ? void 0 : `HEAD on ${tree.branch}`
|
|
1933
|
+
].filter((entry) => entry !== void 0);
|
|
1934
|
+
return `An earlier run left ${findings.join(" and ")} in this working tree.`;
|
|
1935
|
+
}
|
|
1936
|
+
function park_reason(state) {
|
|
1937
|
+
return `The pull request for this issue is ${state}, so whether any work is still owed is a person's call.`;
|
|
1938
|
+
}
|
|
1939
|
+
function resume_reason(child) {
|
|
1940
|
+
const branch_part = child.branch_name === void 0 ? "" : ` on branch ${child.branch_name}`;
|
|
1941
|
+
const pr_part = child.pr_state === OPEN_PR ? ", and its pull request is still open" : "";
|
|
1942
|
+
return `An earlier run left a partial implementation${branch_part}${pr_part}.`;
|
|
1943
|
+
}
|
|
1944
|
+
function to_reason(verdict, tree, child) {
|
|
1945
|
+
if (verdict === RECLAIM_VERDICT) return reclaim_reason(tree);
|
|
1946
|
+
if (verdict === PARK_VERDICT) return park_reason(child.pr_state);
|
|
1947
|
+
if (verdict === RESUME_VERDICT) return resume_reason(child);
|
|
1948
|
+
return CLEAN_REASON;
|
|
1949
|
+
}
|
|
1950
|
+
function reclaim_steps(tree, issue2) {
|
|
1951
|
+
if (!tree.is_dirty) return [];
|
|
1952
|
+
return [
|
|
1953
|
+
`git stash push -u -m "${STASH_LABEL_PREFIX}${issue2}"`,
|
|
1954
|
+
`Record the stash on ${session_cite.issue(issue2)} \u2014 the comment is what gets it popped; nothing pops it for you.`
|
|
1955
|
+
];
|
|
1956
|
+
}
|
|
1957
|
+
function reclaim_advice(tree, issue2) {
|
|
1958
|
+
return [
|
|
1959
|
+
...reclaim_steps(tree, issue2),
|
|
1960
|
+
"pnpm josh ms",
|
|
1961
|
+
"Then ask this command again: it is re-askable, and the clean tree gets its own answer."
|
|
1962
|
+
].join("\n");
|
|
1963
|
+
}
|
|
1964
|
+
function to_advice(verdict, issue2, tree) {
|
|
1965
|
+
if (verdict === RECLAIM_VERDICT) return reclaim_advice(tree, issue2);
|
|
1966
|
+
if (verdict === RESUME_VERDICT) return RESUME_ADVICE;
|
|
1967
|
+
if (verdict === PARK_VERDICT) return PARK_ADVICE;
|
|
1968
|
+
return CLEAN_ADVICE;
|
|
1969
|
+
}
|
|
1970
|
+
function decide(tree, child, issue2) {
|
|
1971
|
+
const verdict = to_verdict(tree, child);
|
|
1972
|
+
return {
|
|
1973
|
+
advice: to_advice(verdict, issue2, tree),
|
|
1974
|
+
reason: to_reason(verdict, tree, child),
|
|
1975
|
+
verdict
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
function read_pr_field(raw) {
|
|
1979
|
+
return json_value.parse_with(raw, pr_info_schema)?.state;
|
|
1980
|
+
}
|
|
1981
|
+
function to_pr_state2(raw) {
|
|
1982
|
+
const state = read_pr_field(raw);
|
|
1983
|
+
return (state === void 0 ? void 0 : GH_PR_STATES.get(state)) ?? NO_PR;
|
|
1984
|
+
}
|
|
1985
|
+
async function read_tree_state() {
|
|
1986
|
+
const [is_dirty, current, default_branch] = await Promise.all([
|
|
1987
|
+
run_hold.is_tree_dirty(),
|
|
1988
|
+
git_command.branch(),
|
|
1989
|
+
git_command.get_default_branch()
|
|
1990
|
+
]);
|
|
1991
|
+
return { branch: current, default_branch, is_dirty };
|
|
1992
|
+
}
|
|
1993
|
+
async function read_pr_state(branch_name) {
|
|
1994
|
+
if (!await git_gh_pr_read.pr_exists(branch_name)) return NO_PR;
|
|
1995
|
+
const raw = await git_gh_pr_read.pr_view(branch_name);
|
|
1996
|
+
if (raw === "") throw new Error(`${UNREADABLE_PR_MESSAGE}${branch_name}`);
|
|
1997
|
+
return to_pr_state2(raw);
|
|
1998
|
+
}
|
|
1999
|
+
function is_pr_open(state) {
|
|
2000
|
+
return state === OPEN_PR;
|
|
2001
|
+
}
|
|
2002
|
+
async function read_pr_of_branch(branch_name) {
|
|
2003
|
+
return { branch_name, pr_state: await read_pr_state(branch_name) };
|
|
2004
|
+
}
|
|
2005
|
+
function pick_child_state(reads) {
|
|
2006
|
+
const decided = reads.find((read) => is_pr_decided(read.pr_state));
|
|
2007
|
+
const open = reads.find((read) => is_pr_open(read.pr_state));
|
|
2008
|
+
return decided ?? open ?? { branch_name: reads[0]?.branch_name, pr_state: NO_PR };
|
|
2009
|
+
}
|
|
2010
|
+
var ISSUE_BRANCH_SUFFIX = "-*";
|
|
2011
|
+
var ANY_REMOTE_PREFIX = "*/";
|
|
2012
|
+
var REMOTE_SEPARATOR = "/";
|
|
2013
|
+
var NOT_FOUND_INDEX = -1;
|
|
2014
|
+
function to_head_branch(name) {
|
|
2015
|
+
const separator_index = name.indexOf(REMOTE_SEPARATOR);
|
|
2016
|
+
return separator_index === NOT_FOUND_INDEX ? name : name.slice(separator_index + 1);
|
|
2017
|
+
}
|
|
2018
|
+
function group_by_head(pairs) {
|
|
2019
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2020
|
+
for (const [name, reference2] of pairs) groups.set(name, [...groups.get(name) ?? [], reference2]);
|
|
2021
|
+
return groups;
|
|
2022
|
+
}
|
|
2023
|
+
async function read_branch_candidates(issue2) {
|
|
2024
|
+
const pattern = `${issue2}${ISSUE_BRANCH_SUFFIX}`;
|
|
2025
|
+
const [local, remote] = await Promise.all([
|
|
2026
|
+
git_command.branch_names(pattern),
|
|
2027
|
+
git_command.branch_names_remote(`${ANY_REMOTE_PREFIX}${pattern}`)
|
|
2028
|
+
]);
|
|
2029
|
+
return group_by_head([
|
|
2030
|
+
...local.map((name) => [name, name]),
|
|
2031
|
+
...remote.map((reference2) => [to_head_branch(reference2), reference2])
|
|
2032
|
+
]);
|
|
2033
|
+
}
|
|
2034
|
+
async function has_commits_beyond(base, references) {
|
|
2035
|
+
const counts = await Promise.all(
|
|
2036
|
+
references.map(async (reference2) => await git_command.commit_count_beyond(base, reference2))
|
|
2037
|
+
);
|
|
2038
|
+
return counts.some((count) => count > 0);
|
|
2039
|
+
}
|
|
2040
|
+
async function is_held_dirty(name, lane) {
|
|
2041
|
+
if (lane?.branch !== name || lane.is_stranded) return false;
|
|
2042
|
+
return await run_hold.is_tree_dirty(lane.directory);
|
|
2043
|
+
}
|
|
2044
|
+
async function read_candidate(name, references, context) {
|
|
2045
|
+
const state = await read_pr_of_branch(name);
|
|
2046
|
+
if (state.pr_state !== NO_PR || await has_commits_beyond(context.base, references)) return state;
|
|
2047
|
+
return await is_held_dirty(name, context.lane) ? state : void 0;
|
|
2048
|
+
}
|
|
2049
|
+
async function read_child_state(issue2) {
|
|
2050
|
+
const candidates = await read_branch_candidates(issue2);
|
|
2051
|
+
if (candidates.size === 0) return NO_CHILD_WORK;
|
|
2052
|
+
const [base, lane] = await Promise.all([
|
|
2053
|
+
git_command.default_branch_reference(),
|
|
2054
|
+
lane_registry.find_open_lane(issue2)
|
|
2055
|
+
]);
|
|
2056
|
+
const reads = await Promise.all(
|
|
2057
|
+
[...candidates].map(
|
|
2058
|
+
async ([name, references]) => await read_candidate(name, references, { base, lane })
|
|
2059
|
+
)
|
|
2060
|
+
);
|
|
2061
|
+
return pick_child_state(reads.filter((read) => read !== void 0));
|
|
2062
|
+
}
|
|
2063
|
+
async function check(issue2) {
|
|
2064
|
+
require_issue_number(issue2);
|
|
2065
|
+
const tree = await read_tree_state();
|
|
2066
|
+
if (needs_reclaim(tree)) return decide(tree, NO_CHILD_WORK, issue2);
|
|
2067
|
+
return decide(tree, await read_child_state(issue2), issue2);
|
|
2068
|
+
}
|
|
2069
|
+
var run_preflight = {
|
|
2070
|
+
CLEAN_VERDICT,
|
|
2071
|
+
RESUME_ADVICE,
|
|
2072
|
+
MERGED_PR,
|
|
2073
|
+
STASH_LABEL_PREFIX,
|
|
2074
|
+
check,
|
|
2075
|
+
decide,
|
|
2076
|
+
read_child_state,
|
|
2077
|
+
read_pr_state,
|
|
2078
|
+
to_pr_state: to_pr_state2
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
// scripts/run/ship/run-ship-probe.ts
|
|
2082
|
+
var REPOSITORY_DIRECTORY_INDEX = 1;
|
|
2083
|
+
var REMOTE_SHA_INDEX = 0;
|
|
2084
|
+
var REMOTE_FIELD_SEPARATOR = /\s/u;
|
|
2085
|
+
var HEAD_REFERENCE = "HEAD";
|
|
2086
|
+
async function or_false(is_true) {
|
|
2087
|
+
try {
|
|
2088
|
+
return await is_true();
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
error_text.trace_swallowed("run_ship_probe.or_false", error);
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
async function read_committed() {
|
|
2095
|
+
if (await run_hold.is_tree_dirty()) return false;
|
|
2096
|
+
const base = await git_command.default_branch_reference();
|
|
2097
|
+
return await git_command.commit_count_beyond(base, HEAD_REFERENCE) > 0;
|
|
2098
|
+
}
|
|
2099
|
+
async function read_pushed(branch_name) {
|
|
2100
|
+
const [remote, head] = await Promise.all([
|
|
2101
|
+
git_worktree.ls_remote_branch(branch_name),
|
|
2102
|
+
git_command.head_commit()
|
|
2103
|
+
]);
|
|
2104
|
+
return remote.split(REMOTE_FIELD_SEPARATOR)[REMOTE_SHA_INDEX] === head;
|
|
2105
|
+
}
|
|
2106
|
+
async function read_merged(branch_name) {
|
|
2107
|
+
if (await run_preflight.read_pr_state(branch_name) !== run_preflight.MERGED_PR) return false;
|
|
2108
|
+
const base = await git_command.default_branch_reference();
|
|
2109
|
+
return await git_command.commit_count_beyond(base, HEAD_REFERENCE) === 0;
|
|
2110
|
+
}
|
|
2111
|
+
var NOTHING_DONE = { is_committed: false, is_pushed: false, is_merged: false };
|
|
2112
|
+
async function read_branch_state(branch_name) {
|
|
2113
|
+
const [is_committed, is_pushed, is_merged2] = await Promise.all([
|
|
2114
|
+
or_false(read_committed),
|
|
2115
|
+
or_false(async () => await read_pushed(branch_name)),
|
|
2116
|
+
or_false(async () => await read_merged(branch_name))
|
|
2117
|
+
]);
|
|
2118
|
+
return { is_committed, is_pushed, is_merged: is_merged2 };
|
|
2119
|
+
}
|
|
2120
|
+
async function read_state() {
|
|
2121
|
+
try {
|
|
2122
|
+
return await read_branch_state(await git_command.branch());
|
|
2123
|
+
} catch {
|
|
2124
|
+
return NOTHING_DONE;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async function repository_directory() {
|
|
2128
|
+
try {
|
|
2129
|
+
const directories2 = await git_command.git_directories();
|
|
2130
|
+
return directories2[REPOSITORY_DIRECTORY_INDEX];
|
|
2131
|
+
} catch {
|
|
2132
|
+
return void 0;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
async function record_target(issue2) {
|
|
2136
|
+
const repository2 = await repository_directory();
|
|
2137
|
+
return repository2 === void 0 ? void 0 : run_ship_stage.record_path(repository2, issue2);
|
|
2138
|
+
}
|
|
2139
|
+
var run_ship_probe = { read_state, record_target, repository_directory };
|
|
2140
|
+
|
|
2141
|
+
// scripts/run/ship/run-ship-stop-context.ts
|
|
2142
|
+
var EXCERPT_LINE_LIMIT = 40;
|
|
2143
|
+
var EXCERPT_LINE_WIDTH = 240;
|
|
2144
|
+
var CHANGED_PATH_LIMIT = 30;
|
|
2145
|
+
var OUTPUT_PREFIX = "josh-ship-stop-";
|
|
2146
|
+
var OUTPUT_SUFFIX = ".log";
|
|
2147
|
+
var KEY_SEPARATOR = "-";
|
|
2148
|
+
var LINE_BREAK = "\n";
|
|
2149
|
+
var CARRIAGE_RETURN = "\r";
|
|
2150
|
+
var omitted_schema = z6.object({ path: z6.string().optional(), last_line: z6.number().int() }).strict();
|
|
2151
|
+
var stop_context_schema = z6.object({
|
|
2152
|
+
excerpt: z6.array(z6.string()).readonly(),
|
|
2153
|
+
omitted: omitted_schema.optional(),
|
|
2154
|
+
changed: z6.array(z6.string()).readonly().optional(),
|
|
2155
|
+
more_changed: z6.number().int()
|
|
2156
|
+
}).strict();
|
|
2157
|
+
function shown_frame(line) {
|
|
2158
|
+
return line.trimEnd().split(CARRIAGE_RETURN).at(-1) ?? "";
|
|
2159
|
+
}
|
|
2160
|
+
function output_lines(output) {
|
|
2161
|
+
return stripVTControlCharacters(output).trimEnd().split(LINE_BREAK).map((line) => agent_role_profile.printable(shown_frame(line)));
|
|
2162
|
+
}
|
|
2163
|
+
async function keep_full_output(issue2, output) {
|
|
2164
|
+
const repository2 = await run_ship_probe.repository_directory();
|
|
2165
|
+
if (repository2 === void 0) return void 0;
|
|
2166
|
+
const target = stamp_file.stamp_path(
|
|
2167
|
+
`${OUTPUT_PREFIX}${issue2}${KEY_SEPARATOR}`,
|
|
2168
|
+
repository2,
|
|
2169
|
+
OUTPUT_SUFFIX
|
|
2170
|
+
);
|
|
2171
|
+
try {
|
|
2172
|
+
return stamp_file.replace_text_stamp(target, output);
|
|
2173
|
+
} catch {
|
|
2174
|
+
return void 0;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
async function omitted_range(issue2, lines) {
|
|
2178
|
+
const path6 = await keep_full_output(issue2, lines.join(LINE_BREAK));
|
|
2179
|
+
const last_line = lines.length - EXCERPT_LINE_LIMIT;
|
|
2180
|
+
return path6 === void 0 ? { last_line } : { path: path6, last_line };
|
|
2181
|
+
}
|
|
2182
|
+
function clipped(lines) {
|
|
2183
|
+
return lines.map((line) => line.slice(0, EXCERPT_LINE_WIDTH));
|
|
2184
|
+
}
|
|
2185
|
+
async function excerpt_of(issue2, output) {
|
|
2186
|
+
const lines = output.trim() === "" ? [] : output_lines(output);
|
|
2187
|
+
if (lines.length <= EXCERPT_LINE_LIMIT) return { excerpt: clipped(lines) };
|
|
2188
|
+
return {
|
|
2189
|
+
excerpt: clipped(lines.slice(-EXCERPT_LINE_LIMIT)),
|
|
2190
|
+
omitted: await omitted_range(issue2, lines)
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
async function read_paths() {
|
|
2194
|
+
try {
|
|
2195
|
+
return await changed_paths.read_changed_paths(false);
|
|
2196
|
+
} catch {
|
|
2197
|
+
return void 0;
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
async function changed_of() {
|
|
2201
|
+
const paths = await read_paths();
|
|
2202
|
+
if (paths === void 0) return { more_changed: 0 };
|
|
2203
|
+
return {
|
|
2204
|
+
changed: paths.slice(0, CHANGED_PATH_LIMIT),
|
|
2205
|
+
more_changed: Math.max(0, paths.length - CHANGED_PATH_LIMIT)
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
async function build(issue2, output) {
|
|
2209
|
+
return { ...await excerpt_of(issue2, output), ...await changed_of() };
|
|
2210
|
+
}
|
|
2211
|
+
function has_excerpt(context) {
|
|
2212
|
+
return context !== void 0 && context.excerpt.length > 0;
|
|
2213
|
+
}
|
|
2214
|
+
function excerpt_heading(issue2, omitted, shown) {
|
|
2215
|
+
if (omitted === void 0) return "The failed stage output:";
|
|
2216
|
+
const source = omitted.path ?? `the stopped report (\`${run_ship_next.COMMAND.LOG} ${issue2}\`)`;
|
|
2217
|
+
const range = `lines 1-${String(omitted.last_line)} are in ${source}`;
|
|
2218
|
+
return `The failed stage output (its last ${String(shown)} lines; ${range}):`;
|
|
2219
|
+
}
|
|
2220
|
+
function with_dropped(omitted, dropped) {
|
|
2221
|
+
if (dropped === 0) return omitted;
|
|
2222
|
+
return { ...omitted, last_line: (omitted?.last_line ?? 0) + dropped };
|
|
2223
|
+
}
|
|
2224
|
+
function excerpt_block(issue2, context, changed, dropped) {
|
|
2225
|
+
const shown = context.excerpt.slice(dropped);
|
|
2226
|
+
const heading = excerpt_heading(issue2, with_dropped(context.omitted, dropped), shown.length);
|
|
2227
|
+
return [heading, ...shown, changed].join(LINE_BREAK);
|
|
2228
|
+
}
|
|
2229
|
+
function changed_text(context) {
|
|
2230
|
+
if (context.changed === void 0) return "The changed files could not be read from git.";
|
|
2231
|
+
if (context.changed.length === 0) return "No files changed against the default branch.";
|
|
2232
|
+
const more = context.more_changed > 0 ? ` (and ${String(context.more_changed)} more)` : "";
|
|
2233
|
+
return `Changed files on the branch: ${context.changed.join(", ")}${more}.`;
|
|
2234
|
+
}
|
|
2235
|
+
function describe(issue2, context, budget = agent_role_profile.MAX_VALUE_LENGTH) {
|
|
2236
|
+
const changed = changed_text(context);
|
|
2237
|
+
if (!has_excerpt(context)) return changed;
|
|
2238
|
+
let dropped = 0;
|
|
2239
|
+
let block = excerpt_block(issue2, context, changed, dropped);
|
|
2240
|
+
while (block.length > budget && dropped < context.excerpt.length - 1) {
|
|
2241
|
+
dropped += 1;
|
|
2242
|
+
block = excerpt_block(issue2, context, changed, dropped);
|
|
2243
|
+
}
|
|
2244
|
+
return block;
|
|
2245
|
+
}
|
|
2246
|
+
var run_ship_stop_context = {
|
|
2247
|
+
CHANGED_PATH_LIMIT,
|
|
2248
|
+
EXCERPT_LINE_LIMIT,
|
|
2249
|
+
build,
|
|
2250
|
+
describe,
|
|
2251
|
+
has_excerpt,
|
|
2252
|
+
stop_context_schema
|
|
2253
|
+
};
|
|
2254
|
+
|
|
2255
|
+
// scripts/lane/lane-child-invocation.ts
|
|
2256
|
+
var CHILD_INVOCATION = "fullrun";
|
|
2257
|
+
var LINE_BREAK2 = "\n";
|
|
2258
|
+
var REVIEW_INVOCATION = "the reviewer of lane";
|
|
2259
|
+
var RESUME_GUIDE = ".claude/skills/workflow-commands/pre-gate-cut.md";
|
|
2260
|
+
var SHIP_PROCESS = String.raw`(josh(\.[jt]s)? ship|run-ship-cli\.ts)`;
|
|
2261
|
+
function child_invocation(issue2) {
|
|
2262
|
+
issue_number_shape.require_issue_number(issue2);
|
|
2263
|
+
return `${CHILD_INVOCATION} ${issue_cite.plain(issue2)}`;
|
|
2264
|
+
}
|
|
2265
|
+
function process_pattern(issue2) {
|
|
2266
|
+
issue_number_shape.require_issue_number(issue2);
|
|
2267
|
+
return `(${CHILD_INVOCATION}|${REVIEW_INVOCATION}|${SHIP_PROCESS} .*) ${issue_cite.plain(issue2)}$`;
|
|
2268
|
+
}
|
|
2269
|
+
function review_invocation(issue2) {
|
|
2270
|
+
issue_number_shape.require_issue_number(issue2);
|
|
2271
|
+
return `${REVIEW_INVOCATION} ${issue_cite.plain(issue2)}`;
|
|
2272
|
+
}
|
|
2273
|
+
function resume_invocation(issue2) {
|
|
2274
|
+
const preamble = `Resuming the lane child for issue ${issue_cite.plain(issue2)} \u2014 do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md). Run \`pnpm josh run:cut --resume ${issue2}\` before anything else and follow the matching verdict in ${RESUME_GUIDE}: \`resume\` goes to the gate, \`resume-impl\` continues implementation. Only on \`fresh\` proceed as an ordinary`;
|
|
2275
|
+
return `${preamble} ${child_invocation(issue2)}`;
|
|
2276
|
+
}
|
|
2277
|
+
function outage_resume_invocation(issue2) {
|
|
2278
|
+
const preamble = `Resuming the lane child for issue ${issue_cite.plain(issue2)} after an API disconnection \u2014 your session was restored with its full context, so do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md). Continue the run from where it stopped: run \`pnpm josh run:step ${issue2}\` to find the next action, redoing only the last step if it did not complete.`;
|
|
2279
|
+
return `${preamble} ${child_invocation(issue2)}`;
|
|
2280
|
+
}
|
|
2281
|
+
function known_stop_lines(issue2, stop, frame) {
|
|
2282
|
+
const { context } = stop;
|
|
2283
|
+
const is_excerpted = run_ship_stop_context.has_excerpt(context);
|
|
2284
|
+
const next = run_ship_next.next_step(issue2, stop.stage, stop, is_excerpted);
|
|
2285
|
+
if (context === void 0) return [next];
|
|
2286
|
+
const fixed = [...frame, next].join(LINE_BREAK2).length + LINE_BREAK2.length;
|
|
2287
|
+
const budget = agent_role_profile.MAX_VALUE_LENGTH - fixed;
|
|
2288
|
+
return [next, run_ship_stop_context.describe(issue2, context, budget)];
|
|
2289
|
+
}
|
|
2290
|
+
function ship_stop_invocation(issue2, stop) {
|
|
2291
|
+
const opening = `Resuming the lane child for issue ${issue_cite.plain(issue2)} after its detached ship supervisor stopped at a failed stage \u2014 do not re-read the workflow-commands entry documents (SKILL.md, fullrun.md).`;
|
|
2292
|
+
const closing = `Do not re-plan or re-implement what is already on the branch. ${child_invocation(issue2)}`;
|
|
2293
|
+
const middle = stop === void 0 ? [
|
|
2294
|
+
`Run \`pnpm josh run:step ${issue2}\` and read the stopped report it names, fix what stopped the ship, then hand the region back per chain-rule.md.`
|
|
2295
|
+
] : known_stop_lines(issue2, stop, [opening, closing]);
|
|
2296
|
+
return [opening, ...middle, closing].join(LINE_BREAK2);
|
|
2297
|
+
}
|
|
2298
|
+
var lane_child_invocation = {
|
|
2299
|
+
child_invocation,
|
|
2300
|
+
outage_resume_invocation,
|
|
2301
|
+
process_pattern,
|
|
2302
|
+
resume_invocation,
|
|
2303
|
+
review_invocation,
|
|
2304
|
+
ship_stop_invocation
|
|
2305
|
+
};
|
|
2306
|
+
|
|
2307
|
+
// scripts/lane/process-tools.ts
|
|
2308
|
+
import path3 from "node:path";
|
|
2309
|
+
var NOT_FOUND_CODE = "ENOENT";
|
|
2310
|
+
var PACKAGE_HINT = "install the package that provides it (procps on Debian/Ubuntu: `apt-get install procps`)";
|
|
2311
|
+
function is_not_found(error) {
|
|
2312
|
+
return error instanceof Error && "code" in error && error.code === NOT_FOUND_CODE;
|
|
2313
|
+
}
|
|
2314
|
+
function missing_notice(command) {
|
|
2315
|
+
return `\`${path3.basename(command)}\` is not installed \u2014 kit's lane commands read processes with ps and pgrep; ${PACKAGE_HINT}`;
|
|
2316
|
+
}
|
|
2317
|
+
function throw_if_missing(command, error) {
|
|
2318
|
+
if (is_not_found(error)) throw new Error(missing_notice(command), { cause: error });
|
|
2319
|
+
}
|
|
2320
|
+
var process_tools = { missing_notice, throw_if_missing };
|
|
2321
|
+
|
|
2322
|
+
// scripts/lane/lane-reap.ts
|
|
2323
|
+
var PGREP_COMMAND = "pgrep";
|
|
2324
|
+
var MATCH_FULL_COMMAND_LINE = "-f";
|
|
2325
|
+
var CHILDREN_OF_FLAG = "-P";
|
|
2326
|
+
var PS_COMMAND = "/bin/ps";
|
|
2327
|
+
var PARENT_FORMAT = ["-o", "ppid=", "-p"];
|
|
2328
|
+
var TERMINATE_SIGNAL = "SIGTERM";
|
|
2329
|
+
var LOWEST_CHILD_PID = 2;
|
|
2330
|
+
function parse_pids(printed) {
|
|
2331
|
+
return printed.split("\n").map((line) => Number(line.trim())).filter((pid) => Number.isSafeInteger(pid) && pid >= LOWEST_CHILD_PID);
|
|
2332
|
+
}
|
|
2333
|
+
function run_probe(command, args) {
|
|
2334
|
+
try {
|
|
2335
|
+
return execFileSync2(command, [...args], {
|
|
2336
|
+
encoding: "utf8",
|
|
2337
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
2338
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2339
|
+
});
|
|
2340
|
+
} catch (error) {
|
|
2341
|
+
process_tools.throw_if_missing(command, error);
|
|
2342
|
+
return "";
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
function parent_of(pid) {
|
|
2346
|
+
return parse_pids(run_probe(PS_COMMAND, [...PARENT_FORMAT, String(pid)]))[0];
|
|
2347
|
+
}
|
|
2348
|
+
function terminate(pid) {
|
|
2349
|
+
try {
|
|
2350
|
+
process.kill(pid, TERMINATE_SIGNAL);
|
|
2351
|
+
return true;
|
|
2352
|
+
} catch {
|
|
2353
|
+
return false;
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
var SYSTEM_PROBES = {
|
|
2357
|
+
matching: (pattern) => parse_pids(run_probe(PGREP_COMMAND, [MATCH_FULL_COMMAND_LINE, pattern])),
|
|
2358
|
+
children_of: (pid) => parse_pids(run_probe(PGREP_COMMAND, [CHILDREN_OF_FLAG, String(pid)])),
|
|
2359
|
+
parent_of,
|
|
2360
|
+
terminate
|
|
2361
|
+
};
|
|
2362
|
+
function ancestry_of(pid, probes) {
|
|
2363
|
+
const ancestry = /* @__PURE__ */ new Set([pid]);
|
|
2364
|
+
let current = probes.parent_of(pid);
|
|
2365
|
+
while (current !== void 0 && !ancestry.has(current)) {
|
|
2366
|
+
ancestry.add(current);
|
|
2367
|
+
current = probes.parent_of(current);
|
|
2368
|
+
}
|
|
2369
|
+
return ancestry;
|
|
2370
|
+
}
|
|
2371
|
+
function own_ancestry(probes = SYSTEM_PROBES) {
|
|
2372
|
+
return ancestry_of(process.pid, probes);
|
|
2373
|
+
}
|
|
2374
|
+
function tree_of(roots, probes) {
|
|
2375
|
+
const tree = [];
|
|
2376
|
+
const pending = [...roots];
|
|
2377
|
+
let next = pending.shift();
|
|
2378
|
+
while (next !== void 0) {
|
|
2379
|
+
if (!tree.includes(next)) {
|
|
2380
|
+
tree.push(next);
|
|
2381
|
+
pending.push(...probes.children_of(next));
|
|
2382
|
+
}
|
|
2383
|
+
next = pending.shift();
|
|
2384
|
+
}
|
|
2385
|
+
return tree;
|
|
2386
|
+
}
|
|
2387
|
+
function roots_of(issue2, own2, probes) {
|
|
2388
|
+
const pattern = lane_child_invocation.process_pattern(issue2);
|
|
2389
|
+
return probes.matching(pattern).filter((pid) => !own2.has(pid));
|
|
2390
|
+
}
|
|
2391
|
+
function reap_child(issue2, probes = SYSTEM_PROBES) {
|
|
2392
|
+
const own2 = own_ancestry(probes);
|
|
2393
|
+
const doomed = tree_of(roots_of(issue2, own2, probes), probes).filter((pid) => !own2.has(pid));
|
|
2394
|
+
return doomed.filter((pid) => probes.terminate(pid));
|
|
2395
|
+
}
|
|
2396
|
+
function is_child_running(issue2, probes = SYSTEM_PROBES) {
|
|
2397
|
+
return roots_of(issue2, own_ancestry(probes), probes).length > 0;
|
|
2398
|
+
}
|
|
2399
|
+
var lane_reap = {
|
|
2400
|
+
is_child_running,
|
|
2401
|
+
own_ancestry,
|
|
2402
|
+
parse_pids,
|
|
2403
|
+
reap_child
|
|
2404
|
+
};
|
|
2405
|
+
|
|
1319
2406
|
// scripts/run/run-headless.ts
|
|
1320
2407
|
var HEADLESS_ENV_KEY = "JOSH_RUN_HEADLESS";
|
|
1321
2408
|
var HEADLESS_VALUE = "1";
|
|
@@ -1375,7 +2462,7 @@ var run_headless = {
|
|
|
1375
2462
|
};
|
|
1376
2463
|
|
|
1377
2464
|
// scripts/run/event/run-event-stream.ts
|
|
1378
|
-
import { z as
|
|
2465
|
+
import { z as z7 } from "zod";
|
|
1379
2466
|
var EVENT_PREFIX = "josh-run-events-";
|
|
1380
2467
|
var EVENT_SUFFIX = ".jsonl";
|
|
1381
2468
|
var EVENT_CAP = 500;
|
|
@@ -1402,6 +2489,10 @@ var EVENT_KIND = {
|
|
|
1402
2489
|
// (`lane:restart`). It follows the fresh dispatch's `CHILD_LAUNCH`, so it is a launch to the parent and
|
|
1403
2490
|
// the board, and — like that launch — a batch record a lane child never reads as its own position.
|
|
1404
2491
|
RESTART: "restart",
|
|
2492
|
+
// A person withdrew a lane child — stopped it and closed its issue as not planned (`lane:withdraw`).
|
|
2493
|
+
// It is written before the stop, so `run:merge` settles the child as withdrawn whatever GitHub says
|
|
2494
|
+
// by the time the parent collects it, and counts it as neither a merge, a failure nor an outage.
|
|
2495
|
+
WITHDRAW: "withdraw",
|
|
1405
2496
|
// The backlog emptied while nothing of the run's own was in flight — the drain. It marks the position
|
|
1406
2497
|
// at which the end-of-run retrospective is owed, *before* the idle watch opens, so `run:step` fires
|
|
1407
2498
|
// the retrospective at the drain rather than after the watch runs its course. It is not itself a
|
|
@@ -1496,11 +2587,11 @@ var DISPOSABLE_KINDS = /* @__PURE__ */ new Set([
|
|
|
1496
2587
|
EVENT_KIND.SETUP,
|
|
1497
2588
|
EVENT_KIND.PR_CHECKS
|
|
1498
2589
|
]);
|
|
1499
|
-
var event_schema =
|
|
1500
|
-
pos:
|
|
1501
|
-
at:
|
|
1502
|
-
kind:
|
|
1503
|
-
text:
|
|
2590
|
+
var event_schema = z7.object({
|
|
2591
|
+
pos: z7.number(),
|
|
2592
|
+
at: z7.string(),
|
|
2593
|
+
kind: z7.string(),
|
|
2594
|
+
text: z7.string()
|
|
1504
2595
|
});
|
|
1505
2596
|
function is_event_kind(value) {
|
|
1506
2597
|
return EVENT_KINDS.includes(value);
|
|
@@ -1570,11 +2661,11 @@ function append_held(target, event) {
|
|
|
1570
2661
|
stamp_file.replace_text_stamp(target, serialize(events));
|
|
1571
2662
|
return position;
|
|
1572
2663
|
}
|
|
1573
|
-
function append(target, kind,
|
|
2664
|
+
function append(target, kind, text2, at) {
|
|
1574
2665
|
if (!is_event_kind(kind)) return { appended: false, position: FIRST_POSITION };
|
|
1575
2666
|
const lock = `${target}${LOCK_SUFFIX}`;
|
|
1576
2667
|
const position = repository_lock.with_lock_sync(
|
|
1577
|
-
() => append_held(target, { at, kind, text }),
|
|
2668
|
+
() => append_held(target, { at, kind, text: text2 }),
|
|
1578
2669
|
lock,
|
|
1579
2670
|
LOCK_WAIT_MS
|
|
1580
2671
|
);
|
|
@@ -1654,58 +2745,69 @@ var KIND = run_event_stream.EVENT_KIND;
|
|
|
1654
2745
|
var BATCH_RECORD_KINDS = /* @__PURE__ */ new Set([
|
|
1655
2746
|
KIND.CHILD_LAUNCH,
|
|
1656
2747
|
KIND.RESTART,
|
|
2748
|
+
KIND.WITHDRAW,
|
|
1657
2749
|
KIND.PARK,
|
|
1658
2750
|
KIND.SPLIT,
|
|
1659
2751
|
KIND.RETROSPECTIVE,
|
|
1660
2752
|
KIND.STRANDED
|
|
1661
2753
|
]);
|
|
1662
2754
|
var ISSUE_REFERENCE = /#(\d+)/u;
|
|
1663
|
-
function is_foreign_ship_event(event,
|
|
1664
|
-
return SHIP_POSITION_KINDS.has(event.kind) && !event.text.startsWith(`${issue_cite.plain(
|
|
2755
|
+
function is_foreign_ship_event(event, issue2) {
|
|
2756
|
+
return SHIP_POSITION_KINDS.has(event.kind) && !event.text.startsWith(`${issue_cite.plain(issue2)} `);
|
|
1665
2757
|
}
|
|
1666
|
-
function is_own_ship_event(event,
|
|
1667
|
-
return SHIP_POSITION_KINDS.has(event.kind) && !is_foreign_ship_event(event,
|
|
2758
|
+
function is_own_ship_event(event, issue2) {
|
|
2759
|
+
return SHIP_POSITION_KINDS.has(event.kind) && !is_foreign_ship_event(event, issue2);
|
|
1668
2760
|
}
|
|
1669
2761
|
function issue_named(event) {
|
|
1670
2762
|
return ISSUE_REFERENCE.exec(event.text)?.[1];
|
|
1671
2763
|
}
|
|
1672
|
-
function names_issue(event,
|
|
1673
|
-
return issue_named(event) ===
|
|
2764
|
+
function names_issue(event, issue2) {
|
|
2765
|
+
return issue_named(event) === issue2;
|
|
1674
2766
|
}
|
|
1675
|
-
function is_lane_position(event,
|
|
1676
|
-
return !BATCH_RECORD_KINDS.has(event.kind) && names_issue(event,
|
|
2767
|
+
function is_lane_position(event, issue2) {
|
|
2768
|
+
return !BATCH_RECORD_KINDS.has(event.kind) && names_issue(event, issue2);
|
|
1677
2769
|
}
|
|
1678
|
-
function since_own_launch(events,
|
|
2770
|
+
function since_own_launch(events, issue2) {
|
|
1679
2771
|
const launch2 = events.findLastIndex(
|
|
1680
|
-
(event) => event.kind === KIND.CHILD_LAUNCH && names_issue(event,
|
|
2772
|
+
(event) => event.kind === KIND.CHILD_LAUNCH && names_issue(event, issue2)
|
|
1681
2773
|
);
|
|
1682
2774
|
return events.slice(launch2 + 1);
|
|
1683
2775
|
}
|
|
1684
|
-
function is_foreign_event(event,
|
|
1685
|
-
if (is_lane_child) return !is_lane_position(event,
|
|
1686
|
-
return is_foreign_ship_event(event,
|
|
2776
|
+
function is_foreign_event(event, issue2, is_lane_child) {
|
|
2777
|
+
if (is_lane_child) return !is_lane_position(event, issue2);
|
|
2778
|
+
return is_foreign_ship_event(event, issue2);
|
|
1687
2779
|
}
|
|
1688
|
-
function last_issue_event(events, scope,
|
|
1689
|
-
const attempt = is_lane_child ? since_own_launch(events,
|
|
1690
|
-
const own2 = attempt.filter((event) => !is_foreign_event(event,
|
|
2780
|
+
function last_issue_event(events, scope, issue2, is_lane_child) {
|
|
2781
|
+
const attempt = is_lane_child ? since_own_launch(events, issue2) : events;
|
|
2782
|
+
const own2 = attempt.filter((event) => !is_foreign_event(event, issue2, is_lane_child));
|
|
1691
2783
|
if (scope.kind === SINCE_SCOPE) return last_scoped_event(own2, scope);
|
|
1692
|
-
return own2.findLast((event) => is_own_ship_event(event,
|
|
2784
|
+
return own2.findLast((event) => is_own_ship_event(event, issue2));
|
|
1693
2785
|
}
|
|
1694
2786
|
var LAUNCH_OR_SETTLE_KINDS = /* @__PURE__ */ new Set([
|
|
1695
2787
|
KIND.CHILD_LAUNCH,
|
|
1696
2788
|
KIND.MERGE,
|
|
1697
2789
|
KIND.PARK,
|
|
1698
|
-
KIND.SPLIT
|
|
2790
|
+
KIND.SPLIT,
|
|
2791
|
+
KIND.WITHDRAW
|
|
1699
2792
|
]);
|
|
1700
|
-
function is_merge_owed(events, scope,
|
|
2793
|
+
function is_merge_owed(events, scope, issue2) {
|
|
1701
2794
|
const newest = (scoped_events(events, scope) ?? []).findLast(
|
|
1702
|
-
(event) => LAUNCH_OR_SETTLE_KINDS.has(event.kind) && names_issue(event,
|
|
2795
|
+
(event) => LAUNCH_OR_SETTLE_KINDS.has(event.kind) && names_issue(event, issue2)
|
|
1703
2796
|
);
|
|
1704
2797
|
return newest?.kind === KIND.CHILD_LAUNCH;
|
|
1705
2798
|
}
|
|
2799
|
+
function is_launch_or_withdrawal(event, issue2) {
|
|
2800
|
+
const is_kind = event.kind === KIND.CHILD_LAUNCH || event.kind === KIND.WITHDRAW;
|
|
2801
|
+
return is_kind && names_issue(event, issue2);
|
|
2802
|
+
}
|
|
2803
|
+
function is_withdrawn(events, issue2) {
|
|
2804
|
+
const newest = events.findLast((event) => is_launch_or_withdrawal(event, issue2));
|
|
2805
|
+
return newest?.kind === KIND.WITHDRAW;
|
|
2806
|
+
}
|
|
1706
2807
|
var run_event_scope = {
|
|
1707
2808
|
UNKNOWN_EVENT_SCOPE,
|
|
1708
2809
|
is_merge_owed,
|
|
2810
|
+
is_withdrawn,
|
|
1709
2811
|
issue_named,
|
|
1710
2812
|
last_issue_event,
|
|
1711
2813
|
last_scoped_event,
|
|
@@ -1723,19 +2825,19 @@ async function stream_target() {
|
|
|
1723
2825
|
const repository2 = await run_carry.repository_directory();
|
|
1724
2826
|
return repository2 === void 0 ? void 0 : run_event_stream.target_of(repository2);
|
|
1725
2827
|
}
|
|
1726
|
-
async function emit(kind,
|
|
2828
|
+
async function emit(kind, text2) {
|
|
1727
2829
|
try {
|
|
1728
2830
|
const target = await stream_target();
|
|
1729
2831
|
if (target === void 0) return;
|
|
1730
|
-
run_event_stream.append(target, kind,
|
|
2832
|
+
run_event_stream.append(target, kind, text2, now_iso());
|
|
1731
2833
|
} catch {
|
|
1732
2834
|
}
|
|
1733
2835
|
}
|
|
1734
|
-
async function emit_once(kind,
|
|
2836
|
+
async function emit_once(kind, text2) {
|
|
1735
2837
|
try {
|
|
1736
2838
|
const target = await stream_target();
|
|
1737
2839
|
if (target === void 0 || run_event_stream.read_last(target)?.kind === kind) return false;
|
|
1738
|
-
return run_event_stream.append(target, kind,
|
|
2840
|
+
return run_event_stream.append(target, kind, text2, now_iso()).appended;
|
|
1739
2841
|
} catch (error) {
|
|
1740
2842
|
error_text.trace_swallowed("run_event_stream_emit.emit_once", error);
|
|
1741
2843
|
return false;
|
|
@@ -1746,7 +2848,7 @@ function invocation_events(repository2, target) {
|
|
|
1746
2848
|
const scope = run_event_scope.scope_of(run_carry.read_carry(run_carry.carry_path(repository2)));
|
|
1747
2849
|
return run_event_scope.scoped_events(events, scope) ?? events;
|
|
1748
2850
|
}
|
|
1749
|
-
async function emit_once_since(kind,
|
|
2851
|
+
async function emit_once_since(kind, text2, reset_kind) {
|
|
1750
2852
|
try {
|
|
1751
2853
|
const repository2 = await run_carry.repository_directory();
|
|
1752
2854
|
if (repository2 === void 0) return false;
|
|
@@ -1754,7 +2856,7 @@ async function emit_once_since(kind, text, reset_kind) {
|
|
|
1754
2856
|
if (run_event_stream.has_since(invocation_events(repository2, target), kind, reset_kind)) {
|
|
1755
2857
|
return false;
|
|
1756
2858
|
}
|
|
1757
|
-
return run_event_stream.append(target, kind,
|
|
2859
|
+
return run_event_stream.append(target, kind, text2, now_iso()).appended;
|
|
1758
2860
|
} catch (error) {
|
|
1759
2861
|
error_text.trace_swallowed("run_event_stream_emit.emit_once_since", error);
|
|
1760
2862
|
return false;
|
|
@@ -1763,18 +2865,21 @@ async function emit_once_since(kind, text, reset_kind) {
|
|
|
1763
2865
|
async function emit_heartbeat(line) {
|
|
1764
2866
|
await emit(run_event_stream.EVENT_KIND.HEARTBEAT, line);
|
|
1765
2867
|
}
|
|
1766
|
-
async function
|
|
2868
|
+
async function scoped_current_events() {
|
|
1767
2869
|
try {
|
|
1768
2870
|
const repository2 = await run_carry.repository_directory();
|
|
1769
2871
|
if (repository2 === void 0) return [];
|
|
1770
2872
|
const read = run_carry.read_carry(run_carry.carry_path(repository2));
|
|
1771
2873
|
const scope = run_event_scope.scope_of(read);
|
|
1772
2874
|
const events = run_event_stream.read_events(run_event_stream.target_of(repository2));
|
|
1773
|
-
return run_event_scope.scoped_events(events, scope)
|
|
2875
|
+
return run_event_scope.scoped_events(events, scope);
|
|
1774
2876
|
} catch {
|
|
1775
|
-
return
|
|
2877
|
+
return void 0;
|
|
1776
2878
|
}
|
|
1777
2879
|
}
|
|
2880
|
+
async function current_events() {
|
|
2881
|
+
return await scoped_current_events() ?? [];
|
|
2882
|
+
}
|
|
1778
2883
|
async function invocation_or_all_events() {
|
|
1779
2884
|
try {
|
|
1780
2885
|
const repository2 = await run_carry.repository_directory();
|
|
@@ -1799,6 +2904,7 @@ var run_event_stream_emit = {
|
|
|
1799
2904
|
emit_once,
|
|
1800
2905
|
emit_once_since,
|
|
1801
2906
|
invocation_or_all_events,
|
|
2907
|
+
scoped_current_events,
|
|
1802
2908
|
stream_target
|
|
1803
2909
|
};
|
|
1804
2910
|
|
|
@@ -1811,11 +2917,11 @@ import { existsSync as existsSync3, lstatSync, mkdirSync, readlinkSync, symlinkS
|
|
|
1811
2917
|
import path5 from "node:path";
|
|
1812
2918
|
|
|
1813
2919
|
// scripts/gh/gh-cli-token.ts
|
|
1814
|
-
import { execaSync } from "execa";
|
|
2920
|
+
import { execaSync as execaSync2 } from "execa";
|
|
1815
2921
|
var GH_CLI_TIMEOUT_MS = 5e3;
|
|
1816
2922
|
function get() {
|
|
1817
2923
|
try {
|
|
1818
|
-
const { stdout } =
|
|
2924
|
+
const { stdout } = execaSync2("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
|
|
1819
2925
|
const token = stdout.trim();
|
|
1820
2926
|
return token.length > 0 ? token : void 0;
|
|
1821
2927
|
} catch {
|
|
@@ -1824,16 +2930,6 @@ function get() {
|
|
|
1824
2930
|
}
|
|
1825
2931
|
var gh_cli_token = { get };
|
|
1826
2932
|
|
|
1827
|
-
// scripts/agent/agent-session-role.ts
|
|
1828
|
-
var KEY2 = "JOSH_AGENT_ROLE";
|
|
1829
|
-
function environment_for(role) {
|
|
1830
|
-
return { [KEY2]: role };
|
|
1831
|
-
}
|
|
1832
|
-
function is_reviewer(source = process.env) {
|
|
1833
|
-
return source[KEY2] === agent_role_profile.REVIEWER;
|
|
1834
|
-
}
|
|
1835
|
-
var agent_session_role = { KEY: KEY2, env_for: environment_for, is_reviewer };
|
|
1836
|
-
|
|
1837
2933
|
// scripts/agent/codex-home-source.ts
|
|
1838
2934
|
import os from "node:os";
|
|
1839
2935
|
import path4 from "node:path";
|
|
@@ -1843,7 +2939,7 @@ function default_home() {
|
|
|
1843
2939
|
var codex_home_source = { default_home };
|
|
1844
2940
|
|
|
1845
2941
|
// scripts/agent/agent-launch-environment.ts
|
|
1846
|
-
var
|
|
2942
|
+
var OPENAI_PROVIDER = "openai";
|
|
1847
2943
|
var REVIEWER_HOME = ["node_modules", ".cache", "josh", "openai", "reviewer-home"];
|
|
1848
2944
|
var HOME_FILES = ["auth.json", "config.toml"];
|
|
1849
2945
|
var PRIVATE_DIRECTORY_MODE = 448;
|
|
@@ -1871,25 +2967,45 @@ function reviewer_home(cwd, environment3) {
|
|
|
1871
2967
|
for (const name of HOME_FILES) link_home_file(source, target, name);
|
|
1872
2968
|
return target;
|
|
1873
2969
|
}
|
|
1874
|
-
function
|
|
2970
|
+
function build2(cwd, profile, environment3 = {}) {
|
|
1875
2971
|
if (profile === void 0) return environment3;
|
|
1876
2972
|
const marked = {
|
|
1877
2973
|
...agent_headless.environment(),
|
|
1878
2974
|
...agent_session_role.env_for(profile.role),
|
|
1879
2975
|
...environment3
|
|
1880
2976
|
};
|
|
1881
|
-
if (profile.provider !==
|
|
2977
|
+
if (profile.provider !== OPENAI_PROVIDER) return marked;
|
|
1882
2978
|
const base = { ...openai_environment(), ...marked };
|
|
1883
2979
|
return profile.role === agent_role_profile.REVIEWER ? { ...base, CODEX_HOME: reviewer_home(cwd, environment3) } : base;
|
|
1884
2980
|
}
|
|
1885
|
-
var agent_launch_environment = { build };
|
|
2981
|
+
var agent_launch_environment = { build: build2 };
|
|
2982
|
+
|
|
2983
|
+
// scripts/run/launch-priority.ts
|
|
2984
|
+
import { setPriority } from "node:os";
|
|
2985
|
+
var LANE_NICENESS = 10;
|
|
2986
|
+
function lower(pid, set_priority = setPriority) {
|
|
2987
|
+
if (pid === void 0) return;
|
|
2988
|
+
try {
|
|
2989
|
+
set_priority(pid, LANE_NICENESS);
|
|
2990
|
+
} catch {
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
var launch_priority = {
|
|
2994
|
+
LANE_NICENESS,
|
|
2995
|
+
lower
|
|
2996
|
+
};
|
|
1886
2997
|
|
|
1887
2998
|
// scripts/run/detached-launch.ts
|
|
1888
2999
|
var NO_PID_NOTE = "the process started without a pid";
|
|
1889
3000
|
var UNSAFE_NOTE = "the wake command or its arguments contain characters that are not safe to execute";
|
|
1890
|
-
var { is_safe_value
|
|
3001
|
+
var { is_safe_prompt, is_safe_value } = agent_role_profile;
|
|
3002
|
+
var LAST = -1;
|
|
3003
|
+
function is_safe_element(argv, value, index) {
|
|
3004
|
+
const is_prompt = argv.ends_with_prompt === true && index === argv.args.length + LAST;
|
|
3005
|
+
return is_prompt ? is_safe_prompt(value) : is_safe_value(value);
|
|
3006
|
+
}
|
|
1891
3007
|
function is_safe_argv(argv) {
|
|
1892
|
-
return
|
|
3008
|
+
return is_safe_value(argv.command) && argv.args.every((value, index) => is_safe_element(argv, value, index));
|
|
1893
3009
|
}
|
|
1894
3010
|
function note_of(error) {
|
|
1895
3011
|
return error instanceof Error ? error.message : JSON.stringify(error);
|
|
@@ -1955,7 +3071,7 @@ function notify_spawn(pid, on_spawn, on_error) {
|
|
|
1955
3071
|
}
|
|
1956
3072
|
}
|
|
1957
3073
|
async function attached_spawn(request, log, on_error, on_spawn) {
|
|
1958
|
-
return await new Promise((
|
|
3074
|
+
return await new Promise((resolve2) => {
|
|
1959
3075
|
const environment3 = agent_launch_environment.build(request.cwd, request.profile, request.env);
|
|
1960
3076
|
const child = spawn(
|
|
1961
3077
|
request.argv.command,
|
|
@@ -1964,11 +3080,12 @@ async function attached_spawn(request, log, on_error, on_spawn) {
|
|
|
1964
3080
|
);
|
|
1965
3081
|
child.once("error", (error) => {
|
|
1966
3082
|
on_error(note_of(error));
|
|
1967
|
-
|
|
3083
|
+
resolve2({ kind: "failed", note: note_of(error) });
|
|
1968
3084
|
});
|
|
1969
3085
|
child.once("exit", (exit_code) => {
|
|
1970
|
-
|
|
3086
|
+
resolve2({ kind: "completed", pid: child.pid ?? 0, exit_code });
|
|
1971
3087
|
});
|
|
3088
|
+
launch_priority.lower(child.pid);
|
|
1972
3089
|
notify_spawn(child.pid, on_spawn, on_error);
|
|
1973
3090
|
});
|
|
1974
3091
|
}
|
|
@@ -1982,6 +3099,7 @@ function spawned(request, log, on_error) {
|
|
|
1982
3099
|
on_error(note_of(error));
|
|
1983
3100
|
});
|
|
1984
3101
|
child.unref();
|
|
3102
|
+
launch_priority.lower(child.pid);
|
|
1985
3103
|
return child.pid === void 0 ? { kind: "failed", note: NO_PID_NOTE } : launched(child.pid);
|
|
1986
3104
|
}
|
|
1987
3105
|
function launch(request, on_error) {
|
|
@@ -2012,7 +3130,7 @@ var detached_launch = {
|
|
|
2012
3130
|
UNSAFE_NOTE,
|
|
2013
3131
|
ensure_log,
|
|
2014
3132
|
is_safe_argv,
|
|
2015
|
-
is_safe_value
|
|
3133
|
+
is_safe_value,
|
|
2016
3134
|
launch,
|
|
2017
3135
|
launch_attached,
|
|
2018
3136
|
log_header,
|
|
@@ -2020,19 +3138,43 @@ var detached_launch = {
|
|
|
2020
3138
|
};
|
|
2021
3139
|
|
|
2022
3140
|
export {
|
|
3141
|
+
FULL_PAGE_QUERY,
|
|
3142
|
+
git_gh_api_path,
|
|
3143
|
+
parse_json_array_or_undefined,
|
|
3144
|
+
read_json_listing,
|
|
3145
|
+
parse_json_object_or_undefined,
|
|
3146
|
+
gh_failure,
|
|
3147
|
+
has_stderr_field,
|
|
3148
|
+
has_stdout_field,
|
|
3149
|
+
git_gh_exec,
|
|
3150
|
+
git_gh_helpers,
|
|
3151
|
+
MERGED_STATE,
|
|
3152
|
+
to_gh_state,
|
|
2023
3153
|
repo_origin,
|
|
2024
3154
|
repo_discovery,
|
|
2025
3155
|
issue_cite,
|
|
3156
|
+
issue_citation,
|
|
3157
|
+
session_cite,
|
|
2026
3158
|
git_common_directory,
|
|
3159
|
+
repository_lock,
|
|
2027
3160
|
run_event_stream,
|
|
2028
3161
|
run_event_scope,
|
|
2029
3162
|
run_event_stream_emit,
|
|
2030
|
-
|
|
2031
|
-
|
|
3163
|
+
stamp_record,
|
|
3164
|
+
git_worktree,
|
|
3165
|
+
run_hold,
|
|
3166
|
+
git_gh_pr_rest,
|
|
3167
|
+
forget_pr_numbers,
|
|
3168
|
+
require_pr_number,
|
|
3169
|
+
read_pull,
|
|
3170
|
+
git_gh_pr_read,
|
|
3171
|
+
lane_registry,
|
|
3172
|
+
run_preflight,
|
|
2032
3173
|
lane_child_invocation,
|
|
2033
3174
|
agent_headless,
|
|
2034
3175
|
detached_launch,
|
|
2035
3176
|
session_language,
|
|
2036
|
-
|
|
3177
|
+
process_tools,
|
|
3178
|
+
lane_reap,
|
|
2037
3179
|
run_headless
|
|
2038
3180
|
};
|