@joshuafolkken/kit 1.887.0 → 1.929.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/skills/workflow-commands/backlogrun-progress.md +13 -12
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -1
- package/.claude/skills/workflow-commands/fullrun-steps.md +4 -2
- package/.claude/skills/workflow-commands/halfrun.md +3 -1
- package/.claude/skills/workflow-commands/kickoff.md +5 -3
- package/.claude/skills/workflow-commands/observation-filing.md +3 -1
- package/.claude/skills/workflow-commands/prerequisite.md +3 -1
- package/.claude/skills/workflow-commands/target-repository.md +1 -1
- package/.github/pull_request_template.md +12 -0
- package/.github/release.yml +3 -0
- package/.github/workflows/ci.yml +8 -0
- package/.github/workflows/production.yml +10 -1
- package/.github/workflows/publish.yml +72 -2
- package/CLAUDE.md +2 -0
- package/README.md +2 -1
- package/dist/CLAUDE.md +2 -0
- package/dist/CLAUDE.static.md +8 -0
- package/dist/hooks/batch-guard.js +6 -6
- package/dist/hooks/{chunk-264QY6KS.js → chunk-2PGRQ62K.js} +25 -10
- package/dist/hooks/chunk-3V37IID6.js +82 -0
- package/dist/hooks/{chunk-36UE53LS.js → chunk-7XQ6TK3R.js} +2 -2
- package/dist/hooks/{chunk-PRDCNGA6.js → chunk-BNFG2IFK.js} +4 -4
- package/dist/hooks/{chunk-VGYSA2NT.js → chunk-GQ3EVWHY.js} +22 -3
- package/dist/hooks/{chunk-Y5OYCU2F.js → chunk-I3JP3D4A.js} +3 -3
- package/dist/hooks/{chunk-6MQCXEEA.js → chunk-LCJZE42I.js} +25 -88
- package/dist/hooks/{chunk-ZBK4XRAS.js → chunk-NJZSO7BK.js} +3 -3
- package/dist/hooks/{chunk-SSX23SJ2.js → chunk-RKUAD3I6.js} +1 -0
- package/dist/hooks/{chunk-FRJKVSK3.js → chunk-ROULPSQC.js} +2 -2
- package/dist/hooks/{chunk-FNFOLFFV.js → chunk-SXPTRFPG.js} +2 -2
- package/dist/hooks/{chunk-FXAKJKTB.js → chunk-WPALEIRX.js} +1 -1
- package/dist/hooks/{chunk-4OXFU3MK.js → chunk-YO43HEPY.js} +634 -156
- package/dist/hooks/codex-hook-adapter.js +13 -12
- package/dist/hooks/duplicate-read-guard.js +7 -7
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/investigation-guard.js +7 -7
- package/dist/hooks/pretool-guard.js +12 -11
- package/dist/hooks/session-lang.js +2 -2
- package/dist/hooks/stop-guard.js +5 -5
- package/dist/josh.js +156 -20
- package/eslint/base.js +4 -1
- package/package.json +72 -18
- package/playwright.config.ts +9 -17
- package/ports/index.d.ts +1 -0
- package/ports/index.js +1 -0
- package/ports/web-server.d.ts +5 -0
- package/ports/web-server.js +73 -0
- package/prettier/index.js +3 -8
- package/prettier/static.d.ts +3 -0
- package/prettier/static.js +10 -0
- package/prompts/collaboration-workflow/issue-template.md +11 -4
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +2 -2
- package/prompts/review.md +2 -2
- package/prompts/testing-guide.md +1 -0
- package/scripts/agent/agent-role-profile.ts +11 -0
- package/scripts/build/build-claude-md.ts +6 -1
- package/scripts/ci/pr-classification.ts +114 -0
- package/scripts/document/document-byte-budget.ts +3 -2
- package/scripts/gate/gate-report.ts +2 -0
- package/scripts/gate/gate-steps.ts +50 -0
- package/scripts/gate/project-checks.ts +145 -0
- package/scripts/gate/verification-gate.ts +14 -43
- package/scripts/git/git-common-directory.ts +9 -1
- package/scripts/git/git-gh-pr-rollup.ts +29 -1
- package/scripts/git/git-gh-pr.ts +61 -1
- package/scripts/git/git-pr.ts +62 -13
- package/scripts/git/issue-labels.ts +9 -0
- package/scripts/init/distributed-paths.ts +9 -5
- package/scripts/init/hook-command-bootstrap.ts +40 -8
- package/scripts/init/hook-command-rewrite.ts +7 -3
- package/scripts/init/init-actions.ts +94 -7
- package/scripts/init/init-ai-copy.ts +107 -19
- package/scripts/init/init-logic-workspace.ts +38 -5
- package/scripts/init/init-logic.ts +1 -0
- package/scripts/init/init-static.ts +55 -0
- package/scripts/init/init.ts +97 -30
- package/scripts/init/project-profile-cli.ts +14 -0
- package/scripts/init/project-profile.ts +158 -0
- package/scripts/init/start.ts +30 -0
- package/scripts/issue/issue-bug-label.ts +13 -0
- package/scripts/issue/issue-classification.ts +74 -0
- package/scripts/issue/issue-lint-cli.ts +10 -3
- package/scripts/issue/markdown-section.ts +38 -4
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +27 -1
- package/scripts/josh/josh-command-map.ts +3 -0
- package/scripts/josh/josh-commands-development.ts +8 -15
- package/scripts/josh/josh-commands-project.ts +14 -1
- package/scripts/josh/josh-logic.ts +13 -0
- package/scripts/josh/run-github-prerequisite.ts +32 -0
- package/scripts/lane/lane-await.ts +8 -1
- package/scripts/lane/openai-lane-ship.ts +30 -0
- package/scripts/lane/openai-lane-supervisor.ts +35 -8
- package/scripts/lane/openai-review-broker.ts +260 -0
- package/scripts/lint/cspell-cached.ts +21 -0
- package/scripts/lint/format.ts +68 -0
- package/scripts/lint/lint-parallel.ts +30 -1
- package/scripts/observations/observations-flush.ts +1 -1
- package/scripts/package/production-dependency-graph.ts +68 -0
- package/scripts/package/verify-optional-eslint-install.ts +62 -0
- package/scripts/release/github-release-cli.ts +11 -0
- package/scripts/release/github-release.ts +220 -0
- package/scripts/release/publish-tag-cli.ts +8 -0
- package/scripts/release/publish-tag.ts +11 -0
- package/scripts/release/release-publish.ts +7 -2
- package/scripts/rules/delivered-rules.ts +2 -0
- package/scripts/rules/issue-bug-label.ts +85 -0
- package/scripts/rules/issue-filing-args.ts +50 -0
- package/scripts/run/run-merge-cli.ts +19 -0
- package/scripts/run/run-merge.ts +9 -1
- package/scripts/run/run-ship-cli.ts +20 -0
- package/scripts/run/run-ship-detach.ts +180 -14
- package/scripts/run/run-ship-review-steps.ts +45 -25
- package/scripts/run/run-step-cli.ts +11 -1
- package/scripts/run/run-step.ts +36 -8
- package/scripts/run/run-wake-session.ts +3 -1
- package/scripts/test/pilot-files.ts +0 -2
- package/scripts/test/skip-marker.ts +3 -0
- package/scripts/test/test-declared-changed.ts +2 -1
- package/scripts/test/test-declared-logic.ts +15 -10
- package/scripts/test/test-declared.ts +20 -8
- package/scripts/test/test-red-logic.ts +3 -3
- package/scripts/test/test-unit-guard.ts +24 -12
- package/templates/CLAUDE.static.md +8 -0
- package/templates/cursorrules.static +1 -0
- package/templates/pnpm-workspace.static.yaml +4 -0
- package/templates/prettierignore.static +12 -0
- package/templates/workflows/ci.yml +5 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALREADY_DONE_LABEL,
|
|
3
|
+
BREAKING_CHANGE_LABEL,
|
|
4
|
+
BUG_LABEL,
|
|
3
5
|
CONTEXT_CUT_THRESHOLD,
|
|
6
|
+
ENHANCEMENT_LABEL,
|
|
4
7
|
FULL_PAGE_QUERY,
|
|
5
8
|
IN_PROGRESS_LABEL,
|
|
6
9
|
NEEDS_DECISION_LABEL,
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
git_gh_api_path,
|
|
16
19
|
git_gh_exec,
|
|
17
20
|
has_any_label,
|
|
21
|
+
has_label_name,
|
|
18
22
|
has_stderr_field,
|
|
19
23
|
lane_park,
|
|
20
24
|
lane_registry,
|
|
@@ -29,28 +33,31 @@ import {
|
|
|
29
33
|
run_watcher_guard,
|
|
30
34
|
shell_segments,
|
|
31
35
|
tail_commands
|
|
32
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-GQ3EVWHY.js";
|
|
37
|
+
import {
|
|
38
|
+
find_local_bin_upwards
|
|
39
|
+
} from "./chunk-3V37IID6.js";
|
|
33
40
|
import {
|
|
34
41
|
batch_outcome
|
|
35
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ROULPSQC.js";
|
|
36
43
|
import {
|
|
37
44
|
investigation_refusal
|
|
38
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-NJZSO7BK.js";
|
|
39
46
|
import {
|
|
40
47
|
duplicate_read_outcome
|
|
41
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-BNFG2IFK.js";
|
|
42
49
|
import {
|
|
43
50
|
investigation_reads
|
|
44
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-I3JP3D4A.js";
|
|
45
52
|
import {
|
|
46
53
|
lane_guard_policy,
|
|
47
54
|
time_batch_guard
|
|
48
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-7XQ6TK3R.js";
|
|
49
56
|
import {
|
|
50
57
|
hook_decision,
|
|
51
58
|
lane_child_marker,
|
|
52
59
|
run_issue_number
|
|
53
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-SXPTRFPG.js";
|
|
54
61
|
import {
|
|
55
62
|
PLATFORM_TEMP_ROOT,
|
|
56
63
|
cost_attribute,
|
|
@@ -60,12 +67,13 @@ import {
|
|
|
60
67
|
json_value,
|
|
61
68
|
lane_paths,
|
|
62
69
|
stamp_file,
|
|
70
|
+
time_density_hook,
|
|
63
71
|
time_shell
|
|
64
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-2PGRQ62K.js";
|
|
65
73
|
|
|
66
74
|
// scripts/hooks/pretool-guard.ts
|
|
67
75
|
import { text } from "node:stream/consumers";
|
|
68
|
-
import { fileURLToPath as
|
|
76
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
69
77
|
|
|
70
78
|
// scripts/run/run-progress.ts
|
|
71
79
|
var DEFAULT_INTERVAL_MINUTES = 20;
|
|
@@ -803,8 +811,8 @@ function selected_threshold(values) {
|
|
|
803
811
|
function threshold_option(over) {
|
|
804
812
|
return over === void 0 ? {} : { over };
|
|
805
813
|
}
|
|
806
|
-
function path_option(
|
|
807
|
-
return
|
|
814
|
+
function path_option(path7) {
|
|
815
|
+
return path7 === void 0 ? {} : { path: path7 };
|
|
808
816
|
}
|
|
809
817
|
function to_options(values) {
|
|
810
818
|
if (values.cut === true && values.over !== void 0) return void 0;
|
|
@@ -1050,6 +1058,201 @@ var implementation_cut = {
|
|
|
1050
1058
|
uncut_lane_child_issue
|
|
1051
1059
|
};
|
|
1052
1060
|
|
|
1061
|
+
// scripts/rules/issue-bug-label.ts
|
|
1062
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1063
|
+
import path4 from "node:path";
|
|
1064
|
+
|
|
1065
|
+
// scripts/issue/markdown-section.ts
|
|
1066
|
+
var HEADING_PREFIX = "## ";
|
|
1067
|
+
function is_heading(line) {
|
|
1068
|
+
return line.trimStart().startsWith(HEADING_PREFIX);
|
|
1069
|
+
}
|
|
1070
|
+
function is_the_heading(line, heading) {
|
|
1071
|
+
return line.trim() === heading;
|
|
1072
|
+
}
|
|
1073
|
+
function next_fence(line, fence) {
|
|
1074
|
+
const marker = /^ {0,3}(`{3,}|~{3,})/u.exec(line)?.[1];
|
|
1075
|
+
if (fence === "") return marker;
|
|
1076
|
+
const closing = new RegExp(
|
|
1077
|
+
String.raw`^ {0,3}${fence.charAt(0)}{${String(fence.length)},}\s*$`,
|
|
1078
|
+
"u"
|
|
1079
|
+
);
|
|
1080
|
+
if (closing.test(line)) return "";
|
|
1081
|
+
return void 0;
|
|
1082
|
+
}
|
|
1083
|
+
function unfenced_lines(body) {
|
|
1084
|
+
let fence = "";
|
|
1085
|
+
const visible = [];
|
|
1086
|
+
for (const line of body.split("\n")) {
|
|
1087
|
+
const next = next_fence(line, fence);
|
|
1088
|
+
const visible_line = line.replace(/^(?: {4}|\t).*/u, "");
|
|
1089
|
+
visible.push(next === void 0 && fence === "" ? visible_line : "");
|
|
1090
|
+
fence = next ?? fence;
|
|
1091
|
+
}
|
|
1092
|
+
return visible;
|
|
1093
|
+
}
|
|
1094
|
+
function has_unfenced_line(body, text2) {
|
|
1095
|
+
return unfenced_lines(body).some((line) => line.trim() === text2);
|
|
1096
|
+
}
|
|
1097
|
+
function has_line(body, text2) {
|
|
1098
|
+
return has_unfenced_line(body, text2);
|
|
1099
|
+
}
|
|
1100
|
+
function section_lines(body, heading) {
|
|
1101
|
+
const lines = body.split("\n");
|
|
1102
|
+
const visible = unfenced_lines(body);
|
|
1103
|
+
const start = visible.findIndex((line) => is_the_heading(line, heading));
|
|
1104
|
+
if (start === -1) return [];
|
|
1105
|
+
const rest = lines.slice(start + 1);
|
|
1106
|
+
const end = visible.slice(start + 1).findIndex((line) => is_heading(line));
|
|
1107
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
1108
|
+
}
|
|
1109
|
+
var markdown_section = { section_lines, is_heading, has_line, has_unfenced_line };
|
|
1110
|
+
|
|
1111
|
+
// scripts/issue/issue-bug-label.ts
|
|
1112
|
+
var BUG_DECLARATION_LINE = "- \u7A2E\u5225: \u4E0D\u5177\u5408";
|
|
1113
|
+
function is_bug_fix(body) {
|
|
1114
|
+
const background = markdown_section.section_lines(body, "## \u80CC\u666F").join("\n");
|
|
1115
|
+
return markdown_section.has_unfenced_line(background, BUG_DECLARATION_LINE);
|
|
1116
|
+
}
|
|
1117
|
+
var issue_bug_label = { BUG_DECLARATION_LINE, is_bug_fix };
|
|
1118
|
+
|
|
1119
|
+
// scripts/issue/issue-classification.ts
|
|
1120
|
+
var FEATURE_DECLARATIONS = ["- \u76EE\u7684: \u6A5F\u80FD\u8FFD\u52A0", "- \u76EE\u7684: \u6A5F\u80FD\u6539\u5584"];
|
|
1121
|
+
var BREAKING_DECLARATION = "- \u4E92\u63DB\u6027: \u7834\u58CA\u7684\u5909\u66F4";
|
|
1122
|
+
var NON_BUG_DECLARATION = "- \u7A2E\u5225: \u975E\u4E0D\u5177\u5408";
|
|
1123
|
+
var CLASSIFICATION_LABELS = [BUG_LABEL, ENHANCEMENT_LABEL, BREAKING_CHANGE_LABEL];
|
|
1124
|
+
var CONFLICT = "bug and enhancement declarations cannot be combined";
|
|
1125
|
+
var MISSING_BUG_CLASSIFICATION = "missing bug classification";
|
|
1126
|
+
var CONFLICTING_BUG_CLASSIFICATIONS = "conflicting bug classifications";
|
|
1127
|
+
function required_labels(body) {
|
|
1128
|
+
const labels = [];
|
|
1129
|
+
const background = markdown_section.section_lines(body, "## \u80CC\u666F").join("\n");
|
|
1130
|
+
if (issue_bug_label.is_bug_fix(body)) labels.push(BUG_LABEL);
|
|
1131
|
+
if (FEATURE_DECLARATIONS.some((line) => markdown_section.has_unfenced_line(background, line))) {
|
|
1132
|
+
labels.push(ENHANCEMENT_LABEL);
|
|
1133
|
+
}
|
|
1134
|
+
if (markdown_section.has_unfenced_line(background, BREAKING_DECLARATION)) {
|
|
1135
|
+
labels.push(BREAKING_CHANGE_LABEL);
|
|
1136
|
+
}
|
|
1137
|
+
return labels;
|
|
1138
|
+
}
|
|
1139
|
+
function labels_for(body, existing) {
|
|
1140
|
+
const missing = required_labels(body).filter((label) => !has_label_name(existing, label));
|
|
1141
|
+
return [...existing, ...missing];
|
|
1142
|
+
}
|
|
1143
|
+
function bug_classification_problem(body) {
|
|
1144
|
+
const background = markdown_section.section_lines(body, "## \u80CC\u666F").join("\n");
|
|
1145
|
+
const has_bug = issue_bug_label.is_bug_fix(body);
|
|
1146
|
+
const has_non_bug = markdown_section.has_unfenced_line(background, NON_BUG_DECLARATION);
|
|
1147
|
+
if (!has_bug && !has_non_bug) return MISSING_BUG_CLASSIFICATION;
|
|
1148
|
+
if (has_bug && has_non_bug) return CONFLICTING_BUG_CLASSIFICATIONS;
|
|
1149
|
+
return void 0;
|
|
1150
|
+
}
|
|
1151
|
+
function problems(body) {
|
|
1152
|
+
const classification_problem = bug_classification_problem(body);
|
|
1153
|
+
const found = [];
|
|
1154
|
+
if (classification_problem !== void 0) found.push(classification_problem);
|
|
1155
|
+
const labels = required_labels(body);
|
|
1156
|
+
if (labels.includes(BUG_LABEL) && labels.includes(ENHANCEMENT_LABEL)) found.push(CONFLICT);
|
|
1157
|
+
return found;
|
|
1158
|
+
}
|
|
1159
|
+
var issue_classification = {
|
|
1160
|
+
BREAKING_DECLARATION,
|
|
1161
|
+
CLASSIFICATION_LABELS,
|
|
1162
|
+
FEATURE_DECLARATIONS,
|
|
1163
|
+
NON_BUG_DECLARATION,
|
|
1164
|
+
labels_for,
|
|
1165
|
+
problems,
|
|
1166
|
+
required_labels
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
// scripts/rules/issue-filing-args.ts
|
|
1170
|
+
var SHELL_WORD = /(?:[^\s'"]|'[^']*'|"[^"]*")+/gu;
|
|
1171
|
+
var QUOTE = /['"]/gu;
|
|
1172
|
+
var RAW_FIELDS = /* @__PURE__ */ new Set(["-f", "--raw-field"]);
|
|
1173
|
+
var FILE_FIELDS = /* @__PURE__ */ new Set(["-F", "--field"]);
|
|
1174
|
+
var LABEL_OPTIONS = /* @__PURE__ */ new Set(["-l", "--label"]);
|
|
1175
|
+
var BODY_FILE_OPTIONS = /* @__PURE__ */ new Set(["--body-file"]);
|
|
1176
|
+
var INLINE_BODY_OPTIONS = /* @__PURE__ */ new Set(["-b", "--body"]);
|
|
1177
|
+
var BODY_FILE_PREFIX = "body=@";
|
|
1178
|
+
function words_of2(command) {
|
|
1179
|
+
return [...command.matchAll(SHELL_WORD)].map((match) => match[0].replaceAll(QUOTE, ""));
|
|
1180
|
+
}
|
|
1181
|
+
function values_after(words, flags) {
|
|
1182
|
+
return words.flatMap((word, index) => {
|
|
1183
|
+
const value = words[index + 1];
|
|
1184
|
+
return value !== void 0 && flags.has(word) ? [value] : [];
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
function has_inline_body(words) {
|
|
1188
|
+
const has_raw = values_after(words, RAW_FIELDS).some((value) => value.startsWith("body="));
|
|
1189
|
+
return has_raw || values_after(words, INLINE_BODY_OPTIONS).length > 0;
|
|
1190
|
+
}
|
|
1191
|
+
function body_file(command) {
|
|
1192
|
+
const words = words_of2(command);
|
|
1193
|
+
const fields = values_after(words, FILE_FIELDS).filter((value) => value.startsWith("body="));
|
|
1194
|
+
const options = values_after(words, BODY_FILE_OPTIONS);
|
|
1195
|
+
if (has_inline_body(words) || fields.length + options.length !== 1) return void 0;
|
|
1196
|
+
return fields[0]?.startsWith(BODY_FILE_PREFIX) ? fields[0].slice(BODY_FILE_PREFIX.length) : options[0];
|
|
1197
|
+
}
|
|
1198
|
+
function has_label(command, label) {
|
|
1199
|
+
const words = words_of2(command);
|
|
1200
|
+
const fields = [...values_after(words, RAW_FIELDS), ...values_after(words, FILE_FIELDS)];
|
|
1201
|
+
const options = values_after(words, LABEL_OPTIONS).flatMap((value) => value.split(","));
|
|
1202
|
+
return fields.includes(`labels[]=${label}`) || options.includes(label);
|
|
1203
|
+
}
|
|
1204
|
+
var issue_filing_args = { body_file, has_label, words_of: words_of2 };
|
|
1205
|
+
|
|
1206
|
+
// scripts/rules/issue-bug-label.ts
|
|
1207
|
+
var LINT_NAMES = /* @__PURE__ */ new Set(["issue:lint"]);
|
|
1208
|
+
var LINT_PATH = /\b(?:issue:lint|iln)\s+(?:'([^']+)'|"([^"]+)"|(\S+))/u;
|
|
1209
|
+
function lint_argument(segment) {
|
|
1210
|
+
const [, single_quoted, double_quoted, bare] = LINT_PATH.exec(segment) ?? [];
|
|
1211
|
+
return single_quoted ?? double_quoted ?? bare;
|
|
1212
|
+
}
|
|
1213
|
+
function latest_lint_path(tail) {
|
|
1214
|
+
const segments = tail_commands.prior_bash_commands(tail).flatMap((command) => shell_segments.segments_of(command));
|
|
1215
|
+
const lint = segments.findLast((segment) => shell_segments.is_josh_command(segment, LINT_NAMES));
|
|
1216
|
+
return lint === void 0 ? void 0 : lint_argument(lint);
|
|
1217
|
+
}
|
|
1218
|
+
function linted_body(body_path) {
|
|
1219
|
+
try {
|
|
1220
|
+
return readFileSync3(body_path, "utf8");
|
|
1221
|
+
} catch {
|
|
1222
|
+
return void 0;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function matches_linted_file(command, lint_path) {
|
|
1226
|
+
const filing_path = issue_filing_args.body_file(command);
|
|
1227
|
+
return filing_path !== void 0 && path4.resolve(filing_path) === path4.resolve(lint_path);
|
|
1228
|
+
}
|
|
1229
|
+
function needs_classification_labels(command, tail) {
|
|
1230
|
+
const lint_path = latest_lint_path(tail);
|
|
1231
|
+
if (lint_path === void 0) return false;
|
|
1232
|
+
const body = linted_body(lint_path);
|
|
1233
|
+
if (body === void 0) return false;
|
|
1234
|
+
if (!matches_linted_file(command, lint_path) || issue_classification.problems(body).length > 0) {
|
|
1235
|
+
return true;
|
|
1236
|
+
}
|
|
1237
|
+
const expected = issue_classification.required_labels(body);
|
|
1238
|
+
return issue_classification.CLASSIFICATION_LABELS.some(
|
|
1239
|
+
(label) => expected.includes(label) !== issue_filing_args.has_label(command, label)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
function decide3(call, run2) {
|
|
1243
|
+
const command = time_shell.bash_command(call.input);
|
|
1244
|
+
const tail = time_density_hook.read_tail(run2.transcript);
|
|
1245
|
+
return needs_classification_labels(command, tail);
|
|
1246
|
+
}
|
|
1247
|
+
var ISSUE_CLASSIFICATION_REASON = "\u26D4 issue classification mismatch: use the exact body file checked by `pnpm josh issue:lint` in this creation call (`-F body=@<path>` or `gh issue create --body-file <path>`) and apply exactly the `labels:` it reports (`bug`, `enhancement`, `breaking-change`). See `prompts/collaboration-workflow/issue-template.md`.";
|
|
1248
|
+
var ROW4 = {
|
|
1249
|
+
id: "issue-bug-label",
|
|
1250
|
+
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
1251
|
+
reason: ISSUE_CLASSIFICATION_REASON,
|
|
1252
|
+
decide: decide3
|
|
1253
|
+
};
|
|
1254
|
+
var issue_bug_label_rule = { ROW: ROW4, latest_lint_path, needs_classification_labels };
|
|
1255
|
+
|
|
1053
1256
|
// scripts/rules/issue-fold.ts
|
|
1054
1257
|
var FOLD_NAMES = /* @__PURE__ */ new Set(["issue:fold"]);
|
|
1055
1258
|
function runs_the_fold(command) {
|
|
@@ -1060,14 +1263,14 @@ function already_folded(tail, _call) {
|
|
|
1060
1263
|
return tail_commands.prior_bash_commands(tail).some((command) => runs_the_fold(command));
|
|
1061
1264
|
}
|
|
1062
1265
|
var ISSUE_FOLD_REASON = '\u26D4 issue fold: this run has filed an Issue before and is filing another with no `pnpm josh issue:fold` between them. Several findings from one session fold into one Issue by default \u2014 the filing-time counterpart to the split assessment, whose two questions it reuses: are the findings separable, and does the whole clearly exceed one verification gate (the size half is `split:assess`, called not recomputed). Reissue after folding: `pnpm josh issue:fold "<title>" "<title>" \u2026` \u2014 `fold` means file one Issue covering them, `separate` means file them apart (separable findings whose combined size clears the split guide), and the default is `fold`. Add `--not-separable` when they are really one deliverable. The procedure is `.claude/skills/workflow-commands/split-assessment.md` \u2192 "The question". It fires once per run and cannot repeat on the call in hand.';
|
|
1063
|
-
var
|
|
1266
|
+
var ROW5 = {
|
|
1064
1267
|
id: "issue-fold",
|
|
1065
1268
|
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
1066
1269
|
reason: ISSUE_FOLD_REASON,
|
|
1067
1270
|
already_satisfied: already_folded,
|
|
1068
1271
|
keeps: bash_triggers.on_bash_command(runs_the_fold)
|
|
1069
1272
|
};
|
|
1070
|
-
var issue_fold = { ISSUE_FOLD_REASON, ROW:
|
|
1273
|
+
var issue_fold = { ISSUE_FOLD_REASON, ROW: ROW5, already_folded, runs_the_fold };
|
|
1071
1274
|
|
|
1072
1275
|
// scripts/rules/issue-scout.ts
|
|
1073
1276
|
var SCOUT_NAMES = /* @__PURE__ */ new Set(["issue:scout"]);
|
|
@@ -1078,14 +1281,14 @@ function already_scouted(tail, _call) {
|
|
|
1078
1281
|
return tail_commands.prior_bash_commands(tail).some((command) => runs_the_scout(command));
|
|
1079
1282
|
}
|
|
1080
1283
|
var ISSUE_SCOUT_REASON = '\u26D4 issue scout: this files an Issue with no `pnpm josh issue:scout` earlier in the run. A duplicate is likeliest for the work that just finished, which is exactly where self-restraint is weakest. Reissue after scouting: `pnpm josh issue:scout "<title>"` (add `--body "<one-line summary, citing #N where the work follows one>"` so the epic half can answer), then read its `Duplicates:` \u2014 an **open** candidate covering the same work stops the run rather than filing a second Issue, and a `(closed)` one means the work is already done \u2014 before reissuing this filing. The procedure is `.claude/skills/workflow-commands/SKILL.md` \u2192 \xA72e. It fires once per run and cannot repeat on the call in hand.';
|
|
1081
|
-
var
|
|
1284
|
+
var ROW6 = {
|
|
1082
1285
|
id: "issue-scout",
|
|
1083
1286
|
is_trigger: bash_triggers.on_bash_command(bash_triggers.is_issue_filing),
|
|
1084
1287
|
reason: ISSUE_SCOUT_REASON,
|
|
1085
1288
|
already_satisfied: already_scouted,
|
|
1086
1289
|
keeps: bash_triggers.on_bash_command(runs_the_scout)
|
|
1087
1290
|
};
|
|
1088
|
-
var issue_scout = { ISSUE_SCOUT_REASON, ROW:
|
|
1291
|
+
var issue_scout = { ISSUE_SCOUT_REASON, ROW: ROW6, already_scouted, runs_the_scout };
|
|
1089
1292
|
|
|
1090
1293
|
// scripts/rules/josh-git-bare.ts
|
|
1091
1294
|
var GIT_COMMAND2 = /* @__PURE__ */ new Set(["git"]);
|
|
@@ -1101,7 +1304,7 @@ function is_bare_josh_git(command) {
|
|
|
1101
1304
|
return shell_segments.segments_of(command).some((segment) => is_bare_segment(segment));
|
|
1102
1305
|
}
|
|
1103
1306
|
var JOSH_GIT_BARE_REASON = '\u26D4 bare `pnpm josh git`: this prompts to confirm the staging, and with no TTY the prompt falls to its cancel branch \u2014 `Checking unstaged files... Found` then `Operation cancelled.` \u2014 so the call throws away the time it takes to fail (about 31 seconds in one measured lane) and you reissue it with `-y` anyway (joshuafolkken/kit#2297). Run it unattended from the start: `pnpm josh git -y "<title> #<N>"`. The recovery form that opens the PR for a push that already landed is `pnpm josh git -y --skip-commit --skip-push`. This rule fires on every occurrence, not once per run.';
|
|
1104
|
-
var
|
|
1307
|
+
var ROW7 = {
|
|
1105
1308
|
id: "josh-git-bare",
|
|
1106
1309
|
is_trigger: bash_triggers.on_bash_command(is_bare_josh_git),
|
|
1107
1310
|
reason: JOSH_GIT_BARE_REASON,
|
|
@@ -1109,7 +1312,7 @@ var ROW6 = {
|
|
|
1109
1312
|
};
|
|
1110
1313
|
var josh_git_bare = {
|
|
1111
1314
|
JOSH_GIT_BARE_REASON,
|
|
1112
|
-
ROW:
|
|
1315
|
+
ROW: ROW7,
|
|
1113
1316
|
is_bare_josh_git
|
|
1114
1317
|
};
|
|
1115
1318
|
|
|
@@ -1123,7 +1326,7 @@ function is_carry_conflict(command, is_lane_child = lane_child_marker.is_child_o
|
|
|
1123
1326
|
return is_lane_child;
|
|
1124
1327
|
}
|
|
1125
1328
|
var LANE_CARRY_CONFLICT_REASON = '\u26D4 lane child carry conflict: this session is a dispatched lane child, and `pnpm josh run:merge` / `pnpm josh run:carry` are the parent backlogrun budget commands \u2014 a child must never run them. The live carry record here belongs to the parent that launched this child, not to a competing run: the parent holds the one run budget for the repository, and every lane shares the common git directory it is keyed on, so a live owner a lane child reads is always its parent (joshuafolkken/kit#2267, measured 2026-09-21 in backlogrun #2252 \u2014 a child read run:merge `busy` as a competitor and parked #2258 unimplemented). Do not run this command, and do not treat it as a conflict: no `needs-decision`, no `confirmation` Telegram, no question to a person \u2014 a dispatched child cannot ask, and there is nothing to decide. Continue implementing this Issue; the parent owns the budget and counts the outcome at the return. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` and `backlogrun-progress.md` \u2192 "The hand-off". This rule fires on every occurrence, not once per run.';
|
|
1126
|
-
var
|
|
1329
|
+
var ROW8 = {
|
|
1127
1330
|
id: "lane-carry-conflict",
|
|
1128
1331
|
is_trigger: bash_triggers.on_bash_command(is_carry_conflict),
|
|
1129
1332
|
reason: LANE_CARRY_CONFLICT_REASON,
|
|
@@ -1131,7 +1334,7 @@ var ROW7 = {
|
|
|
1131
1334
|
};
|
|
1132
1335
|
var lane_carry_conflict = {
|
|
1133
1336
|
LANE_CARRY_CONFLICT_REASON,
|
|
1134
|
-
ROW:
|
|
1337
|
+
ROW: ROW8,
|
|
1135
1338
|
invokes_budget_command,
|
|
1136
1339
|
is_carry_conflict
|
|
1137
1340
|
};
|
|
@@ -1184,13 +1387,13 @@ function is_interactive_ask(call, is_lane_child = lane_child_marker.is_child_of(
|
|
|
1184
1387
|
return is_lane_child;
|
|
1185
1388
|
}
|
|
1186
1389
|
var LANE_INTERACTIVE_ASK_REASON = '\u26D4 lane child interactive ask: this session is a dispatched lane child and cannot ask a person, so this call is refused. Left to run it would end the turn with nothing on the Issue \u2014 the parent then finds an OPEN Issue with `in-progress` and no question (joshuafolkken/kit#2201; joshuafolkken/kit#2034 placed the same rule one tool-call too late). Do not ask: park the decision instead. Apply `needs-decision` and post a comment carrying the question, the options and whether work was stashed \u2014 `gh api repos/{owner}/{repo}/issues/<N>/labels -f \'labels[]=needs-decision\'` then `pnpm josh issue:comment <N> --body-file <path>` \u2014 then send the `confirmation` Telegram and stop. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "A lane child records its park before it stops", and the park itself is `backlogrun-park.md` \u2192 "park and continue". This rule fires on every occurrence, not once per run.';
|
|
1187
|
-
var
|
|
1390
|
+
var ROW9 = {
|
|
1188
1391
|
id: "lane-interactive-ask",
|
|
1189
1392
|
is_trigger: is_interactive_ask,
|
|
1190
1393
|
reason: LANE_INTERACTIVE_ASK_REASON,
|
|
1191
1394
|
decide: () => true
|
|
1192
1395
|
};
|
|
1193
|
-
var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW:
|
|
1396
|
+
var lane_interactive_ask = { LANE_INTERACTIVE_ASK_REASON, ROW: ROW9, is_interactive_ask };
|
|
1194
1397
|
|
|
1195
1398
|
// scripts/rules/lane-switch-main.ts
|
|
1196
1399
|
var SWITCH_SUBCOMMAND = "switch";
|
|
@@ -1221,7 +1424,7 @@ function is_switch_away_from_lane(command, is_lane_child = lane_child_marker.is_
|
|
|
1221
1424
|
return switch_targets(command).some((target) => target !== own_branch);
|
|
1222
1425
|
}
|
|
1223
1426
|
var LANE_SWITCH_MAIN_REASON = "\u26D4 lane child on `git switch main`: this session is a dispatched lane child, and switching to the default branch here always fails \u2014 a lane is a linked work tree, the primary checkout holds the default branch, and git allows one branch in one work tree at a time (`fatal: main is already used by worktree at <the primary checkout>`, measured 11 times in the 2026-09-21 backlogrun). Do not switch: the lane is already on its own issue branch, branched from a fresh default before it opened, and the latest default is brought in during the gate by `pnpm josh main:merge`. So skip this step and keep implementing on the current branch \u2014 no `git switch`, no `git pull`. `pnpm josh main:sync` (`josh ms`) is refused in a lane for the same reason, and a lane is finished with `pnpm josh lane:close <issue-number>`. The parent runs `git switch main && git pull` in the primary checkout before each lane opens, never the child \u2014 `backlogrun-lanes.md` and `backlogrun-child.md` are the single sources. This rule fires on every occurrence, not once per run.";
|
|
1224
|
-
var
|
|
1427
|
+
var ROW10 = {
|
|
1225
1428
|
id: "lane-switch-main",
|
|
1226
1429
|
is_trigger: bash_triggers.on_bash_command(is_switch_away_from_lane),
|
|
1227
1430
|
reason: LANE_SWITCH_MAIN_REASON,
|
|
@@ -1229,7 +1432,7 @@ var ROW9 = {
|
|
|
1229
1432
|
};
|
|
1230
1433
|
var lane_switch_main = {
|
|
1231
1434
|
LANE_SWITCH_MAIN_REASON,
|
|
1232
|
-
ROW:
|
|
1435
|
+
ROW: ROW10,
|
|
1233
1436
|
is_switch_away_from_lane,
|
|
1234
1437
|
switch_target,
|
|
1235
1438
|
switch_targets
|
|
@@ -1697,7 +1900,7 @@ function is_poll_loop(command) {
|
|
|
1697
1900
|
return LOOP_KEYWORD2.test(plain) && SLEEP_COMMAND.test(plain);
|
|
1698
1901
|
}
|
|
1699
1902
|
var POLL_LOOP_REASON = '\u26D4 output poll loop: this hand-writes a `while`/`until` wait loop over a command\'s output, a marker file or a process \u2014 with a `sleep` or, worse, without one \u2014 and the loop does not know when the command finished. A regex that never matches waits to the harness block limit; one that matches a mid-run line exits too early \u2014 either way the command exit is not what ends the wait, and each pass spends a full-context round trip (joshuafolkken/kit#2371 measured about 45 minutes, 13% of six lanes, lost to exactly this). Do not poll the output file. A command known to take minutes \u2014 `pnpm josh gate`, `pnpm josh followup`, `pnpm josh format` \u2014 is started detached from the first with `run_in_background` set, and the harness re-invokes you when it exits: its completion notification is what ends the wait, on the task exit rather than a regex or the block limit. So issue the long command in the background and end the turn \u2014 do not arm a loop; the wake-up brings you back. To glance at interim output, `Read` the output file once, never in a sleep loop. If you are waiting on CI, `pnpm josh followup` waits on it for you. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every occurrence, not once per run.**';
|
|
1700
|
-
var
|
|
1903
|
+
var ROW11 = {
|
|
1701
1904
|
id: "poll-loop",
|
|
1702
1905
|
is_trigger: bash_triggers.on_bash_command(is_poll_loop),
|
|
1703
1906
|
reason: POLL_LOOP_REASON,
|
|
@@ -1705,7 +1908,7 @@ var ROW10 = {
|
|
|
1705
1908
|
};
|
|
1706
1909
|
var poll_loop = {
|
|
1707
1910
|
POLL_LOOP_REASON,
|
|
1708
|
-
ROW:
|
|
1911
|
+
ROW: ROW11,
|
|
1709
1912
|
is_poll_loop
|
|
1710
1913
|
};
|
|
1711
1914
|
|
|
@@ -1825,16 +2028,16 @@ function posts_at_path_literally(command) {
|
|
|
1825
2028
|
return RAW_FIELD_BODY_AT.test(command);
|
|
1826
2029
|
}
|
|
1827
2030
|
var RAW_FIELD_BODY_REASON = "\u26D4 literal @path body: this passes `body=@<path>` to `gh api` with a raw field flag (`-f` / `--raw-field`), which sends the value verbatim \u2014 the string `@<path>` is posted as the comment, not the file it points at. `-F` / `--field` is the flag that reads the file, and the two spellings differ by one character while `gh` exits 0 and returns a comment URL either way, so the break is invisible until a person reads the issue (joshuafolkken/kit#2304 lost two park comments this way). Post an issue or PR comment with `pnpm josh issue:comment <N> --body-file <path>` \u2014 the one spelling, which cannot take the wrong flag. The rule and the safe forms are in `prompts/collaboration-workflow/shell-body.md`. This rule fires on every occurrence, not once per run.";
|
|
1828
|
-
var
|
|
2031
|
+
var ROW12 = {
|
|
1829
2032
|
id: "raw-field-body",
|
|
1830
2033
|
is_trigger: bash_triggers.on_bash_command(posts_at_path_literally),
|
|
1831
2034
|
reason: RAW_FIELD_BODY_REASON,
|
|
1832
2035
|
decide: () => true
|
|
1833
2036
|
};
|
|
1834
|
-
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW:
|
|
2037
|
+
var raw_field_body = { RAW_FIELD_BODY_REASON, ROW: ROW12, posts_at_path_literally };
|
|
1835
2038
|
|
|
1836
2039
|
// scripts/rules/rule-body-guard.ts
|
|
1837
|
-
import { existsSync as existsSync2, readFileSync as
|
|
2040
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
1838
2041
|
var EDIT_TOOL = "Edit";
|
|
1839
2042
|
var WRITE_TOOL = "Write";
|
|
1840
2043
|
var CLAUDE_DOCUMENT = /(?:^|\/)CLAUDE\.md$/u;
|
|
@@ -1857,7 +2060,7 @@ function string_field(input, key) {
|
|
|
1857
2060
|
return typeof value === "string" ? value : "";
|
|
1858
2061
|
}
|
|
1859
2062
|
function read_existing(file_path) {
|
|
1860
|
-
return existsSync2(file_path) ?
|
|
2063
|
+
return existsSync2(file_path) ? readFileSync4(file_path, "utf8") : "";
|
|
1861
2064
|
}
|
|
1862
2065
|
function texts_of(name, input, read2) {
|
|
1863
2066
|
if (name === WRITE_TOOL) {
|
|
@@ -1903,7 +2106,7 @@ function runs_placement_oracle(command) {
|
|
|
1903
2106
|
(names) => shell_segments.segments_of(command).some((segment) => shell_segments.is_josh_command(segment, names))
|
|
1904
2107
|
);
|
|
1905
2108
|
}
|
|
1906
|
-
var
|
|
2109
|
+
var ROW13 = {
|
|
1907
2110
|
id: "rule-body",
|
|
1908
2111
|
is_trigger: writes_rule_prose,
|
|
1909
2112
|
reason: RULE_BODY_REASON,
|
|
@@ -1912,7 +2115,7 @@ var ROW12 = {
|
|
|
1912
2115
|
};
|
|
1913
2116
|
var rule_body_guard = {
|
|
1914
2117
|
RULE_BODY_REASON,
|
|
1915
|
-
ROW:
|
|
2118
|
+
ROW: ROW13,
|
|
1916
2119
|
adds_rule_prose,
|
|
1917
2120
|
answered_placement,
|
|
1918
2121
|
is_rule_document,
|
|
@@ -1957,13 +2160,13 @@ function is_foreground_push_step(call) {
|
|
|
1957
2160
|
if (is_backgrounded(call.input)) return false;
|
|
1958
2161
|
return is_push_step_call(call);
|
|
1959
2162
|
}
|
|
1960
|
-
function
|
|
2163
|
+
function decide4() {
|
|
1961
2164
|
return true;
|
|
1962
2165
|
}
|
|
1963
2166
|
var RUN_TAIL_REASON = '\u26D4 run tail: this issues the commit-push-PR step in the foreground, where the harness decides when to stop waiting. Reissue it with `run_in_background` set. In `fullrun #1501` this call was given a 900-second timeout, the harness detached it at its own 600-second cap, and nothing read the output for a further 6m28s \u2014 45m03s of wall clock on about 17 minutes of work (joshuafolkken/kit#1510). **Never give a foreground call a timeout above the harness cap**: the cap decides, not the number. A backgrounded call re-invokes you when it exits, so the completion notification is what resumes the run. **When it arrives, the turn that reads it goes straight through any branch-2 filing and `pnpm josh epic:bundle` to `pnpm josh followup` \u2014 the turn never ends at the push.** CI went green 6m06s before the merge started in that run, and it started only because the person asked; joshuafolkken/kit#1333 had already settled that the merge is issued in the turn the review closes. While the push runs, do the work that writes nothing \u2014 drafting the completion body to a file, deciding the branch-2 disposition. `pnpm josh followup` itself stays in the **foreground** \u2014 nearly every step after it reads its result. But a tail does follow the merge, measured at 3.0 min and 5.9% of a run (joshuafolkken/kit#1462), so empty it beforehand: the steps that read the merge result stay after `followup`, and the rest are composed in the turn that issues it, bar the one exception background-commands.md names. The procedure is `.claude/skills/workflow-commands/background-commands.md`, "A command that can take minutes is issued in the background". **This rule fires on every foreground push, not once per run**, so reissuing the same call in the foreground will be refused again.';
|
|
1964
2167
|
var run_tail = {
|
|
1965
2168
|
RUN_TAIL_REASON,
|
|
1966
|
-
decide:
|
|
2169
|
+
decide: decide4,
|
|
1967
2170
|
is_backgrounded_push_step,
|
|
1968
2171
|
is_foreground_push_step,
|
|
1969
2172
|
is_push_step,
|
|
@@ -2044,35 +2247,160 @@ var shell_body_trigger = {
|
|
|
2044
2247
|
// scripts/test/test-declared-changed.ts
|
|
2045
2248
|
import { execFileSync } from "node:child_process";
|
|
2046
2249
|
|
|
2250
|
+
// scripts/gate/project-checks.ts
|
|
2251
|
+
import { existsSync as existsSync3, globSync as globSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
2252
|
+
import path5 from "node:path";
|
|
2253
|
+
|
|
2254
|
+
// scripts/test/skip-marker.ts
|
|
2255
|
+
var SKIP_MARKER = "\u2014 skipping";
|
|
2256
|
+
|
|
2257
|
+
// scripts/gate/project-checks.ts
|
|
2258
|
+
var PACKAGE_JSON = "package.json";
|
|
2259
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
2260
|
+
"node_modules",
|
|
2261
|
+
".git",
|
|
2262
|
+
".venv",
|
|
2263
|
+
"venv",
|
|
2264
|
+
"target",
|
|
2265
|
+
"dist",
|
|
2266
|
+
"build",
|
|
2267
|
+
".svelte-kit"
|
|
2268
|
+
]);
|
|
2269
|
+
var WEB_FILES = "**/*.{html,css,js,jsx,mjs,cjs}";
|
|
2270
|
+
var SCRIPT_FILES = "**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}";
|
|
2271
|
+
var TYPE_FILES = "**/*.{ts,tsx,mts,cts}";
|
|
2272
|
+
var ESLINT_CONFIGS = [
|
|
2273
|
+
"eslint.config.js",
|
|
2274
|
+
"eslint.config.mjs",
|
|
2275
|
+
"eslint.config.cjs",
|
|
2276
|
+
"eslint.config.ts",
|
|
2277
|
+
"eslint.config.mts",
|
|
2278
|
+
"eslint.config.cts"
|
|
2279
|
+
];
|
|
2280
|
+
var CSPELL_CONFIGS = [
|
|
2281
|
+
"cspell.config.yaml",
|
|
2282
|
+
"cspell.config.yml",
|
|
2283
|
+
"cspell.config.json",
|
|
2284
|
+
"cspell.config.js",
|
|
2285
|
+
"cspell.config.cjs",
|
|
2286
|
+
"cspell.config.mjs",
|
|
2287
|
+
"cspell.json",
|
|
2288
|
+
"cspell.yaml"
|
|
2289
|
+
];
|
|
2290
|
+
var TYPE_CONFIGS = ["tsconfig.json", "jsconfig.json"];
|
|
2291
|
+
function project_root(directory) {
|
|
2292
|
+
let current = directory;
|
|
2293
|
+
while (!existsSync3(path5.join(current, PACKAGE_JSON))) {
|
|
2294
|
+
const parent = path5.dirname(current);
|
|
2295
|
+
if (parent === current) return directory;
|
|
2296
|
+
current = parent;
|
|
2297
|
+
}
|
|
2298
|
+
return current;
|
|
2299
|
+
}
|
|
2300
|
+
function is_ignored(entry) {
|
|
2301
|
+
return entry.split(/[/\\]/u).some((part) => IGNORED_DIRS.has(part));
|
|
2302
|
+
}
|
|
2303
|
+
function has_files(directory, pattern) {
|
|
2304
|
+
return globSync2(pattern, { cwd: project_root(directory), exclude: is_ignored }).length > 0;
|
|
2305
|
+
}
|
|
2306
|
+
function has_config(directory, names) {
|
|
2307
|
+
return names.some((name) => existsSync3(path5.join(project_root(directory), name)));
|
|
2308
|
+
}
|
|
2309
|
+
function has_bin(directory, bin_name) {
|
|
2310
|
+
return find_local_bin_upwards(directory, bin_name) !== void 0;
|
|
2311
|
+
}
|
|
2312
|
+
function josh_settings(manifest) {
|
|
2313
|
+
if (typeof manifest !== "object" || manifest === null || !("josh" in manifest)) return void 0;
|
|
2314
|
+
return manifest.josh;
|
|
2315
|
+
}
|
|
2316
|
+
function is_static_manifest(manifest) {
|
|
2317
|
+
const josh = josh_settings(manifest);
|
|
2318
|
+
if (typeof josh !== "object" || josh === null || !("profile" in josh)) return false;
|
|
2319
|
+
return josh.profile === "static";
|
|
2320
|
+
}
|
|
2321
|
+
function is_static(directory) {
|
|
2322
|
+
try {
|
|
2323
|
+
const manifest_path = path5.join(project_root(directory), PACKAGE_JSON);
|
|
2324
|
+
const manifest = JSON.parse(readFileSync5(manifest_path, "utf8"));
|
|
2325
|
+
return is_static_manifest(manifest);
|
|
2326
|
+
} catch {
|
|
2327
|
+
return false;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
function skip_notice(check3, reason) {
|
|
2331
|
+
return `josh ${check3}: ${reason} ${SKIP_MARKER} ${check3}.`;
|
|
2332
|
+
}
|
|
2333
|
+
function type_check_skip_reason(directory) {
|
|
2334
|
+
if (!is_static(directory)) return void 0;
|
|
2335
|
+
if (!has_files(directory, TYPE_FILES)) return "no TypeScript files were found";
|
|
2336
|
+
if (!has_config(directory, TYPE_CONFIGS)) return "no TypeScript configuration was found";
|
|
2337
|
+
if (!has_bin(directory, "tsc")) return "typescript is not installed";
|
|
2338
|
+
return void 0;
|
|
2339
|
+
}
|
|
2340
|
+
function prettier_skip_reason(directory) {
|
|
2341
|
+
if (!is_static(directory)) return void 0;
|
|
2342
|
+
if (!has_files(directory, WEB_FILES)) return "no HTML, CSS or JavaScript files were found";
|
|
2343
|
+
if (!has_bin(directory, "prettier")) return "prettier is not installed";
|
|
2344
|
+
return void 0;
|
|
2345
|
+
}
|
|
2346
|
+
function eslint_skip_reason(directory) {
|
|
2347
|
+
if (!is_static(directory)) return void 0;
|
|
2348
|
+
if (!has_files(directory, SCRIPT_FILES)) return "no JavaScript or TypeScript files were found";
|
|
2349
|
+
if (!has_config(directory, ESLINT_CONFIGS)) return "no ESLint configuration was found";
|
|
2350
|
+
if (!has_bin(directory, "eslint")) return "eslint is not installed";
|
|
2351
|
+
return void 0;
|
|
2352
|
+
}
|
|
2353
|
+
var project_checks = {
|
|
2354
|
+
CSPELL_CONFIGS,
|
|
2355
|
+
ESLINT_CONFIGS,
|
|
2356
|
+
SCRIPT_FILES,
|
|
2357
|
+
TYPE_CONFIGS,
|
|
2358
|
+
TYPE_FILES,
|
|
2359
|
+
WEB_FILES,
|
|
2360
|
+
eslint_skip_reason,
|
|
2361
|
+
has_bin,
|
|
2362
|
+
has_config,
|
|
2363
|
+
has_files,
|
|
2364
|
+
is_static,
|
|
2365
|
+
prettier_skip_reason,
|
|
2366
|
+
project_root,
|
|
2367
|
+
skip_notice,
|
|
2368
|
+
type_check_skip_reason
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2047
2371
|
// scripts/test/test-declared-logic.ts
|
|
2048
2372
|
var TEST_SUFFIXES = [".e2e.ts", ".test.ts"];
|
|
2049
2373
|
var EXEMPT_PATHS = [".editorconfig"];
|
|
2050
2374
|
var EXEMPT_PREFIXES = [".idea/", ".vscode/", "prompts/"];
|
|
2051
2375
|
var EXEMPT_SUFFIXES = [".md"];
|
|
2376
|
+
var STATIC_VISUAL_SUFFIXES = [".html", ".css"];
|
|
2052
2377
|
function normalize(paths) {
|
|
2053
|
-
return paths.map((
|
|
2378
|
+
return paths.map((path7) => path7.trim()).filter((path7) => path7 !== "");
|
|
2379
|
+
}
|
|
2380
|
+
function is_test_file(path7) {
|
|
2381
|
+
return TEST_SUFFIXES.some((suffix) => path7.endsWith(suffix));
|
|
2054
2382
|
}
|
|
2055
|
-
function
|
|
2056
|
-
return
|
|
2383
|
+
function is_static_visual(path7, is_static2) {
|
|
2384
|
+
return is_static2 && STATIC_VISUAL_SUFFIXES.some((suffix) => path7.endsWith(suffix));
|
|
2057
2385
|
}
|
|
2058
|
-
function is_exempt(
|
|
2059
|
-
if (EXEMPT_PATHS.includes(
|
|
2060
|
-
return EXEMPT_SUFFIXES.some((suffix) =>
|
|
2386
|
+
function is_exempt(path7, is_static2 = false) {
|
|
2387
|
+
if (EXEMPT_PATHS.includes(path7) || is_static_visual(path7, is_static2)) return true;
|
|
2388
|
+
return EXEMPT_SUFFIXES.some((suffix) => path7.endsWith(suffix)) || EXEMPT_PREFIXES.some((prefix) => path7.startsWith(prefix));
|
|
2061
2389
|
}
|
|
2062
|
-
function is_runtime(
|
|
2063
|
-
return !is_test_file(
|
|
2390
|
+
function is_runtime(path7, is_static2 = false) {
|
|
2391
|
+
return !is_test_file(path7) && !is_exempt(path7, is_static2);
|
|
2064
2392
|
}
|
|
2065
|
-
function verdict_for(paths) {
|
|
2393
|
+
function verdict_for(paths, is_static2 = false) {
|
|
2066
2394
|
const changed = normalize(paths);
|
|
2067
|
-
if (changed.some((
|
|
2068
|
-
if (changed.some((
|
|
2395
|
+
if (changed.some((path7) => is_test_file(path7))) return "satisfied";
|
|
2396
|
+
if (changed.some((path7) => is_runtime(path7, is_static2))) return "required";
|
|
2069
2397
|
return "exempt";
|
|
2070
2398
|
}
|
|
2071
|
-
function runtime_files(paths) {
|
|
2072
|
-
return normalize(paths).filter((
|
|
2399
|
+
function runtime_files(paths, is_static2 = false) {
|
|
2400
|
+
return normalize(paths).filter((path7) => is_runtime(path7, is_static2));
|
|
2073
2401
|
}
|
|
2074
|
-
function exempt_files(paths) {
|
|
2075
|
-
return normalize(paths).filter((
|
|
2402
|
+
function exempt_files(paths, is_static2 = false) {
|
|
2403
|
+
return normalize(paths).filter((path7) => is_exempt(path7, is_static2));
|
|
2076
2404
|
}
|
|
2077
2405
|
var test_declared_logic = {
|
|
2078
2406
|
EXEMPT_PATHS,
|
|
@@ -2111,7 +2439,7 @@ function source_paths() {
|
|
|
2111
2439
|
return injected.paths ?? read_changed_paths_sync();
|
|
2112
2440
|
}
|
|
2113
2441
|
function current_verdict(paths = source_paths()) {
|
|
2114
|
-
return test_declared_logic.verdict_for(paths);
|
|
2442
|
+
return test_declared_logic.verdict_for(paths, project_checks.is_static(process.cwd()));
|
|
2115
2443
|
}
|
|
2116
2444
|
function restore_injection(previous) {
|
|
2117
2445
|
if (previous === void 0) {
|
|
@@ -2137,8 +2465,8 @@ function is_untested_commit(call) {
|
|
|
2137
2465
|
return test_declared_changed.current_verdict() === "required";
|
|
2138
2466
|
}
|
|
2139
2467
|
var REASON = '\u26D4 test declared: this commits a runtime code change with no test beside it \u2014 `pnpm josh test:declared` reads the working tree as `required` (a changed file that is neither a test \u2014 `*.test.ts` / `*.e2e.ts` \u2014 nor a mechanically-exempt non-runtime path). `CLAUDE.md` \u2192 Code Change Rules requires a test for ALL code changes: a bug fix gets a regression test, a refactor gets tests pinning behavior first, logic gets a unit test, an observable UI/timing change gets an E2E. Add the declared test and reissue this commit. **If the change genuinely touches no executable runtime path** it is the pre-approved non-runtime exception \u2014 declare it in your Step 0 work summary with the reason it is exempt (non-executable config, a cosmetic asset swap) and reissue; the exception is a presentation, not a stop. Run `pnpm josh test:declared` to see the files it names. The procedure is `prompts/collaboration-workflow/rule-delivery.md` \u2192 "\u914D\u9001\u3055\u308C\u3066\u3044\u308B\u898F\u5247". This fires once per run, so reissuing will not repeat on the call in hand.';
|
|
2140
|
-
var
|
|
2141
|
-
var test_declared_commit = { REASON, ROW:
|
|
2468
|
+
var ROW14 = { id: "test-declared", is_trigger: is_untested_commit, reason: REASON };
|
|
2469
|
+
var test_declared_commit = { REASON, ROW: ROW14, is_untested_commit };
|
|
2142
2470
|
|
|
2143
2471
|
// scripts/rules/third-party-write.ts
|
|
2144
2472
|
function is_third_party_write_segment(segment, session_owner) {
|
|
@@ -2154,14 +2482,14 @@ function writes_third_party(command, resolve_owner = repo_party.current_owner) {
|
|
|
2154
2482
|
return targets.some((segment) => is_third_party_write_segment(segment, session_owner));
|
|
2155
2483
|
}
|
|
2156
2484
|
var THIRD_PARTY_WRITE_REASON = '\u26D4 third-party write: this `gh api` call writes to a repository whose owner is not this session \u2014 a tracker we do not own is Tier C, and every write there (Issue, comment, PR, Discussion, review) needs explicit current-turn user instruction; typing the target, or holding a correct diagnosis, is not that instruction (`CLAUDE.md` \u2192 "Third-party repositories are Tier C", `prompts/collaboration-workflow/upstream-interrupt.md`). Confirm the party with `pnpm josh repo:party <owner/repo>` \u2014 owner equality, computed not judged. A read (`-X GET`, or no field flag) passes untouched; only a write (`-f`/`-F`/`--field`/`--raw-field`/`--input`, or `-X POST|PATCH|PUT|DELETE`) is refused. Record the finding on our own Issue and draft the artifact for a person to post \u2014 do not publish it yourself. **This rule fires on every occurrence, not once per run.**';
|
|
2157
|
-
var
|
|
2485
|
+
var ROW15 = {
|
|
2158
2486
|
id: "third-party-write",
|
|
2159
2487
|
is_trigger: bash_triggers.on_bash_command((command) => writes_third_party(command)),
|
|
2160
2488
|
reason: THIRD_PARTY_WRITE_REASON,
|
|
2161
2489
|
decide: () => true
|
|
2162
2490
|
};
|
|
2163
2491
|
var third_party_write = {
|
|
2164
|
-
ROW:
|
|
2492
|
+
ROW: ROW15,
|
|
2165
2493
|
THIRD_PARTY_WRITE_REASON,
|
|
2166
2494
|
is_third_party_write_segment,
|
|
2167
2495
|
writes_third_party
|
|
@@ -2215,14 +2543,14 @@ function is_unauthorized_worktree_change(command) {
|
|
|
2215
2543
|
return shell_segments.segments_of(command).some((segment) => is_unauthorized_segment(segment));
|
|
2216
2544
|
}
|
|
2217
2545
|
var WORKTREE_MUTATION_REASON = '\u26D4 unauthorized working-tree change: `git checkout -- <path>`, `git restore <path>` and an unauthorized `git stash` mutate the working tree or the repository-wide stash every work tree shares, and none is on the `.claude/settings.json` deny list (`operating-rules.md`). Do not run them on your own judgement. A commit goes through `pnpm josh git`; a stash pop goes through `pnpm josh stash:pop "<message>"`, never a positional `git stash pop` a shared stack lets another lane divert. A stash push is authorized only inside a documented flow and only with a findable message \u2014 `git stash push -u -m "<message>"` \u2014 which this row allows; a bare `git stash`, a message-less push, or `pop` / `apply` / `drop` is refused. **This rule fires on every occurrence, not once per run.**';
|
|
2218
|
-
var
|
|
2546
|
+
var ROW16 = {
|
|
2219
2547
|
id: "worktree-mutation",
|
|
2220
2548
|
is_trigger: bash_triggers.on_bash_command(is_unauthorized_worktree_change),
|
|
2221
2549
|
reason: WORKTREE_MUTATION_REASON,
|
|
2222
2550
|
decide: () => true
|
|
2223
2551
|
};
|
|
2224
2552
|
var worktree_guard = {
|
|
2225
|
-
ROW:
|
|
2553
|
+
ROW: ROW16,
|
|
2226
2554
|
WORKTREE_MUTATION_REASON,
|
|
2227
2555
|
is_unauthorized_stash,
|
|
2228
2556
|
is_unauthorized_worktree_change,
|
|
@@ -2525,7 +2853,8 @@ var DELIVERED_RULES = [
|
|
|
2525
2853
|
// (`issue:lint`) delivers on the reissue, after `wip-cap` / `issue-scout` / `filing-cap` /
|
|
2526
2854
|
// `issue-fold` have each had their turn. The non-overlapping firing point (`pkg:scout` on a package
|
|
2527
2855
|
// add) claims a command no row above matches.
|
|
2528
|
-
...oracle_consulted.ROWS
|
|
2856
|
+
...oracle_consulted.ROWS,
|
|
2857
|
+
issue_bug_label_rule.ROW
|
|
2529
2858
|
];
|
|
2530
2859
|
var ALONE_IN_TURN = 1;
|
|
2531
2860
|
function batches_the_turn(call, turn) {
|
|
@@ -2559,12 +2888,12 @@ function is_first_delivery(tail, call, delivered_at_ms, run2) {
|
|
|
2559
2888
|
return delivered_at_ms === hook_decision.NEVER_MS && !will_batch_guard_refuse(tail, call, run2);
|
|
2560
2889
|
}
|
|
2561
2890
|
function delivery_decision(rule) {
|
|
2562
|
-
const { decide:
|
|
2563
|
-
if (
|
|
2891
|
+
const { decide: decide7, already_satisfied } = rule;
|
|
2892
|
+
if (decide7 === void 0 && already_satisfied === void 0) return is_first_delivery;
|
|
2564
2893
|
return function should_block(tail, call, delivered_at_ms, run2) {
|
|
2565
2894
|
if (already_satisfied?.(tail, call) === true) return false;
|
|
2566
|
-
if (
|
|
2567
|
-
return
|
|
2895
|
+
if (decide7 === void 0) return is_first_delivery(tail, call, delivered_at_ms, run2);
|
|
2896
|
+
return decide7(call, run2, !will_batch_guard_refuse(tail, call, run2), delivered_at_ms);
|
|
2568
2897
|
};
|
|
2569
2898
|
}
|
|
2570
2899
|
function guard_of(rule) {
|
|
@@ -2620,7 +2949,7 @@ var delivered_rules = {
|
|
|
2620
2949
|
};
|
|
2621
2950
|
|
|
2622
2951
|
// scripts/run/run-watcher-hook.ts
|
|
2623
|
-
import { z as
|
|
2952
|
+
import { z as z16 } from "zod";
|
|
2624
2953
|
|
|
2625
2954
|
// scripts/run/run-progress-read.ts
|
|
2626
2955
|
import { loadavg } from "node:os";
|
|
@@ -2949,9 +3278,9 @@ async function issue_get_labels_and_body(issue_number) {
|
|
|
2949
3278
|
return await issue_view_json(issue_number, "number,state,labels,body");
|
|
2950
3279
|
}
|
|
2951
3280
|
async function issue_list_comments(issue_number) {
|
|
2952
|
-
const
|
|
3281
|
+
const path7 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
|
|
2953
3282
|
try {
|
|
2954
|
-
return await git_gh_exec.exec_gh_api({ path:
|
|
3283
|
+
return await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true });
|
|
2955
3284
|
} catch {
|
|
2956
3285
|
return void 0;
|
|
2957
3286
|
}
|
|
@@ -3026,8 +3355,8 @@ function is_selected(rest, request) {
|
|
|
3026
3355
|
return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
|
|
3027
3356
|
}
|
|
3028
3357
|
async function fetch_page(request, page) {
|
|
3029
|
-
const
|
|
3030
|
-
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path:
|
|
3358
|
+
const path7 = listing_path(request, page);
|
|
3359
|
+
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path7 }));
|
|
3031
3360
|
return {
|
|
3032
3361
|
selected: rows.filter((row) => is_selected(row, request)),
|
|
3033
3362
|
is_full: rows.length >= PER_PAGE
|
|
@@ -3267,6 +3596,92 @@ var git_gh_issue = {
|
|
|
3267
3596
|
issue_list_recently_closed
|
|
3268
3597
|
};
|
|
3269
3598
|
|
|
3599
|
+
// scripts/ci/pr-classification.ts
|
|
3600
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3601
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3602
|
+
import { z as z7 } from "zod";
|
|
3603
|
+
var BUGFIX = "bugfix";
|
|
3604
|
+
var BREAKING = "breaking-change";
|
|
3605
|
+
var CLASSIFICATION_LABELS2 = [
|
|
3606
|
+
BREAKING,
|
|
3607
|
+
"enhancement",
|
|
3608
|
+
BUGFIX,
|
|
3609
|
+
"other-change",
|
|
3610
|
+
"ignore-for-release"
|
|
3611
|
+
];
|
|
3612
|
+
var CLASSIFICATION_SET = new Set(CLASSIFICATION_LABELS2);
|
|
3613
|
+
var ISSUE_BUG_LABEL = "bug";
|
|
3614
|
+
var DECLARATION = /^- リリース分類: (.+)$/gmu;
|
|
3615
|
+
var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
|
|
3616
|
+
var LABEL = z7.object({ name: z7.string() });
|
|
3617
|
+
var USER = z7.object({ login: z7.string() });
|
|
3618
|
+
var PULL_REQUEST = z7.object({ user: USER, labels: z7.array(LABEL) });
|
|
3619
|
+
var EVENT = z7.object({
|
|
3620
|
+
pull_request: PULL_REQUEST
|
|
3621
|
+
});
|
|
3622
|
+
function classification_error(labels, author) {
|
|
3623
|
+
const legacy = labels.filter((label) => LEGACY_LABELS.has(label.toLowerCase()));
|
|
3624
|
+
if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
|
|
3625
|
+
const selected = labels.filter((label) => CLASSIFICATION_SET.has(label.toLowerCase()));
|
|
3626
|
+
if (selected.length > 1) {
|
|
3627
|
+
return `Choose exactly one release classification; found: ${selected.join(", ")}`;
|
|
3628
|
+
}
|
|
3629
|
+
if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
|
|
3630
|
+
return `Choose one release classification: ${CLASSIFICATION_LABELS2.join(", ")}`;
|
|
3631
|
+
}
|
|
3632
|
+
function is_classification(value) {
|
|
3633
|
+
return CLASSIFICATION_SET.has(value);
|
|
3634
|
+
}
|
|
3635
|
+
function release_candidates(candidates) {
|
|
3636
|
+
if (!candidates.includes(BREAKING)) return candidates;
|
|
3637
|
+
return candidates.filter((candidate) => candidate !== "enhancement");
|
|
3638
|
+
}
|
|
3639
|
+
function issue_candidates(issue_json) {
|
|
3640
|
+
const issue = z7.object({ labels: z7.array(LABEL), body: z7.string().nullable() }).parse(JSON.parse(issue_json));
|
|
3641
|
+
const names = issue.labels.map((label) => label.name.toLowerCase());
|
|
3642
|
+
const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
|
|
3643
|
+
const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
|
|
3644
|
+
const unknown = declared.filter((value) => !is_classification(value));
|
|
3645
|
+
if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
|
|
3646
|
+
if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING)) {
|
|
3647
|
+
candidates.push(BUGFIX);
|
|
3648
|
+
}
|
|
3649
|
+
return candidates;
|
|
3650
|
+
}
|
|
3651
|
+
function select_issue_classification(issue_json) {
|
|
3652
|
+
const candidates = issue_candidates(issue_json);
|
|
3653
|
+
const selected = [...new Set(candidates)];
|
|
3654
|
+
const [label] = selected;
|
|
3655
|
+
if (label === void 0 || selected.length > 1) {
|
|
3656
|
+
throw new Error(
|
|
3657
|
+
`Choose one release classification before opening the PR: ${CLASSIFICATION_LABELS2.join(", ")}; found: ${selected.join(", ") || "none"}. Add "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: <label>" to the Issue body.`
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3660
|
+
return label;
|
|
3661
|
+
}
|
|
3662
|
+
function check_event(event_path) {
|
|
3663
|
+
const event = EVENT.parse(JSON.parse(readFileSync6(event_path, "utf8")));
|
|
3664
|
+
return classification_error(
|
|
3665
|
+
event.pull_request.labels.map((label) => label.name),
|
|
3666
|
+
event.pull_request.user.login
|
|
3667
|
+
);
|
|
3668
|
+
}
|
|
3669
|
+
if (process.argv[1] === fileURLToPath2(import.meta.url)) {
|
|
3670
|
+
const event_path = process.env["GITHUB_EVENT_PATH"];
|
|
3671
|
+
if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
|
|
3672
|
+
const error = check_event(event_path);
|
|
3673
|
+
if (error) {
|
|
3674
|
+
console.error(`::error::${error}`);
|
|
3675
|
+
process.exitCode = 1;
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
var pr_classification = {
|
|
3679
|
+
classification_error,
|
|
3680
|
+
check_event,
|
|
3681
|
+
is_classification,
|
|
3682
|
+
select_issue_classification
|
|
3683
|
+
};
|
|
3684
|
+
|
|
3270
3685
|
// scripts/lib/poll.ts
|
|
3271
3686
|
async function sleep(duration_ms) {
|
|
3272
3687
|
await new Promise((resolve2) => {
|
|
@@ -3286,29 +3701,32 @@ async function poll_until(is_done, options) {
|
|
|
3286
3701
|
}
|
|
3287
3702
|
var poll = { poll_until, sleep };
|
|
3288
3703
|
|
|
3704
|
+
// scripts/git/git-gh-pr.ts
|
|
3705
|
+
import { z as z11 } from "zod";
|
|
3706
|
+
|
|
3289
3707
|
// scripts/git/git-gh-pr-rest.ts
|
|
3290
|
-
import { z as
|
|
3291
|
-
var rest_repo_schema =
|
|
3292
|
-
var rest_pull_side_schema =
|
|
3293
|
-
var rest_pull_schema =
|
|
3294
|
-
number:
|
|
3295
|
-
html_url:
|
|
3296
|
-
body:
|
|
3297
|
-
state:
|
|
3298
|
-
merged:
|
|
3299
|
-
merged_at:
|
|
3300
|
-
mergeable:
|
|
3301
|
-
mergeable_state:
|
|
3708
|
+
import { z as z8 } from "zod";
|
|
3709
|
+
var rest_repo_schema = z8.looseObject({ full_name: z8.string().optional() }).nullish();
|
|
3710
|
+
var rest_pull_side_schema = z8.looseObject({ repo: rest_repo_schema });
|
|
3711
|
+
var rest_pull_schema = z8.looseObject({
|
|
3712
|
+
number: z8.number(),
|
|
3713
|
+
html_url: z8.string().optional(),
|
|
3714
|
+
body: z8.string().nullish(),
|
|
3715
|
+
state: z8.string().optional(),
|
|
3716
|
+
merged: z8.boolean().nullish(),
|
|
3717
|
+
merged_at: z8.string().nullish(),
|
|
3718
|
+
mergeable: z8.boolean().nullish(),
|
|
3719
|
+
mergeable_state: z8.string().optional(),
|
|
3302
3720
|
// `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
|
|
3303
3721
|
// REST hangs the check runs and the status contexts off the commit rather than off the pull
|
|
3304
3722
|
// request (joshuafolkken/kit#1028).
|
|
3305
|
-
head:
|
|
3723
|
+
head: z8.looseObject({ ref: z8.string().optional(), sha: z8.string().optional(), repo: rest_repo_schema }).nullish(),
|
|
3306
3724
|
base: rest_pull_side_schema.nullish(),
|
|
3307
3725
|
// `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
|
|
3308
3726
|
// is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
|
|
3309
3727
|
// (joshuafolkken/kit#2497).
|
|
3310
|
-
node_id:
|
|
3311
|
-
auto_merge:
|
|
3728
|
+
node_id: z8.string().optional(),
|
|
3729
|
+
auto_merge: z8.looseObject({}).nullish()
|
|
3312
3730
|
});
|
|
3313
3731
|
var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
|
|
3314
3732
|
var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
|
|
@@ -3477,9 +3895,9 @@ async function pr_head_reference(pr_number) {
|
|
|
3477
3895
|
async function read_comments(branch_name, to_path, to_json) {
|
|
3478
3896
|
const pr_number = await resolve_pr_number(branch_name);
|
|
3479
3897
|
if (pr_number === void 0) return void 0;
|
|
3480
|
-
const
|
|
3898
|
+
const path7 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
|
|
3481
3899
|
try {
|
|
3482
|
-
return to_json(await git_gh_exec.exec_gh_api({ path:
|
|
3900
|
+
return to_json(await git_gh_exec.exec_gh_api({ path: path7, should_paginate: true }));
|
|
3483
3901
|
} catch {
|
|
3484
3902
|
return void 0;
|
|
3485
3903
|
}
|
|
@@ -3508,14 +3926,14 @@ var git_gh_pr_read = {
|
|
|
3508
3926
|
};
|
|
3509
3927
|
|
|
3510
3928
|
// scripts/git/git-gh-pr-review.ts
|
|
3511
|
-
import { z as
|
|
3929
|
+
import { z as z9 } from "zod";
|
|
3512
3930
|
var REVIEW_APPROVED = "APPROVED";
|
|
3513
3931
|
var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
|
|
3514
3932
|
var REVIEW_NONE = "";
|
|
3515
3933
|
var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
|
|
3516
|
-
var rest_review_schema =
|
|
3517
|
-
state:
|
|
3518
|
-
user:
|
|
3934
|
+
var rest_review_schema = z9.looseObject({
|
|
3935
|
+
state: z9.string().optional(),
|
|
3936
|
+
user: z9.looseObject({ login: z9.string().optional() }).nullish()
|
|
3519
3937
|
});
|
|
3520
3938
|
var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
|
|
3521
3939
|
function parse_rest_reviews(reviews_json) {
|
|
@@ -3548,17 +3966,18 @@ function to_review_decision(reviews_json) {
|
|
|
3548
3966
|
var git_gh_pr_review = { to_review_decision, parse_rest_reviews };
|
|
3549
3967
|
|
|
3550
3968
|
// scripts/git/git-gh-pr-rollup.ts
|
|
3551
|
-
import { z as
|
|
3969
|
+
import { z as z10 } from "zod";
|
|
3552
3970
|
var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
|
|
3553
3971
|
var TYPE_NAME_KEY = "__typename";
|
|
3554
|
-
var rollup_element_schema =
|
|
3555
|
-
var check_runs_page_schema =
|
|
3972
|
+
var rollup_element_schema = z10.looseObject({});
|
|
3973
|
+
var check_runs_page_schema = z10.looseObject({
|
|
3556
3974
|
// Required, not optional: GitHub always names the key, and accepting a page without one would
|
|
3557
3975
|
// answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
|
|
3558
|
-
check_runs:
|
|
3976
|
+
check_runs: z10.array(rollup_element_schema)
|
|
3559
3977
|
});
|
|
3560
|
-
var
|
|
3561
|
-
|
|
3978
|
+
var check_run_app_schema = z10.object({ id: z10.number() });
|
|
3979
|
+
var status_page_schema = z10.looseObject({
|
|
3980
|
+
statuses: z10.array(rollup_element_schema)
|
|
3562
3981
|
});
|
|
3563
3982
|
var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
|
|
3564
3983
|
var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
|
|
@@ -3567,9 +3986,31 @@ function read_pages(raw_json, schema, message) {
|
|
|
3567
3986
|
if (pages === void 0) throw new Error(message);
|
|
3568
3987
|
return pages;
|
|
3569
3988
|
}
|
|
3989
|
+
function check_run_group_key(run2) {
|
|
3990
|
+
const { name, app: raw_app } = run2;
|
|
3991
|
+
if (typeof name !== "string") return void 0;
|
|
3992
|
+
const app = check_run_app_schema.safeParse(raw_app);
|
|
3993
|
+
return JSON.stringify([name, app.success ? app.data.id : void 0]);
|
|
3994
|
+
}
|
|
3995
|
+
function remember_newest_run(run2, latest_ids) {
|
|
3996
|
+
const key = check_run_group_key(run2);
|
|
3997
|
+
const { id } = run2;
|
|
3998
|
+
if (key === void 0 || typeof id !== "number") return;
|
|
3999
|
+
const previous_id = latest_ids.get(key);
|
|
4000
|
+
if (previous_id === void 0 || id > previous_id) latest_ids.set(key, id);
|
|
4001
|
+
}
|
|
4002
|
+
function is_newest_run(run2, latest_ids) {
|
|
4003
|
+
const key = check_run_group_key(run2);
|
|
4004
|
+
const { id } = run2;
|
|
4005
|
+
if (key === void 0 || typeof id !== "number") return true;
|
|
4006
|
+
return id === latest_ids.get(key);
|
|
4007
|
+
}
|
|
3570
4008
|
function to_check_run_items(check_runs_json) {
|
|
3571
4009
|
const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
|
|
3572
|
-
|
|
4010
|
+
const runs = pages.flatMap((page) => page.check_runs);
|
|
4011
|
+
const latest_ids = /* @__PURE__ */ new Map();
|
|
4012
|
+
for (const run2 of runs) remember_newest_run(run2, latest_ids);
|
|
4013
|
+
return runs.filter((run2) => is_newest_run(run2, latest_ids));
|
|
3573
4014
|
}
|
|
3574
4015
|
function to_status_context_items(status_json) {
|
|
3575
4016
|
const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
|
|
@@ -3589,16 +4030,16 @@ var git_gh_pr_rollup = {
|
|
|
3589
4030
|
|
|
3590
4031
|
// scripts/git/git-gh-pr-snapshot.ts
|
|
3591
4032
|
var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
|
|
3592
|
-
async function read_object_pages(
|
|
4033
|
+
async function read_object_pages(path7) {
|
|
3593
4034
|
return await git_gh_exec.exec_gh_api({
|
|
3594
|
-
path: `${
|
|
4035
|
+
path: `${path7}${FULL_PAGE_QUERY}`,
|
|
3595
4036
|
should_paginate: true,
|
|
3596
4037
|
should_slurp: true
|
|
3597
4038
|
});
|
|
3598
4039
|
}
|
|
3599
|
-
async function read_array_pages(
|
|
4040
|
+
async function read_array_pages(path7) {
|
|
3600
4041
|
return await git_gh_exec.exec_gh_api({
|
|
3601
|
-
path: `${
|
|
4042
|
+
path: `${path7}${FULL_PAGE_QUERY}`,
|
|
3602
4043
|
should_paginate: true
|
|
3603
4044
|
});
|
|
3604
4045
|
}
|
|
@@ -3615,8 +4056,8 @@ async function read_status_check_rollup(commit_sha) {
|
|
|
3615
4056
|
return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
|
|
3616
4057
|
}
|
|
3617
4058
|
async function pr_get_review_decision(pr_number) {
|
|
3618
|
-
const
|
|
3619
|
-
return git_gh_pr_review.to_review_decision(await read_array_pages(
|
|
4059
|
+
const path7 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
|
|
4060
|
+
return git_gh_pr_review.to_review_decision(await read_array_pages(path7));
|
|
3620
4061
|
}
|
|
3621
4062
|
async function build_checks_fields(pull) {
|
|
3622
4063
|
return {
|
|
@@ -3655,7 +4096,33 @@ var MERGE_REQUEST_TIMEOUT_MS = 18e4;
|
|
|
3655
4096
|
var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
|
|
3656
4097
|
var MERGE_READ_BACK_ATTEMPTS = 3;
|
|
3657
4098
|
var MERGE_READ_BACK_INTERVAL_MS = 1e3;
|
|
3658
|
-
|
|
4099
|
+
var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
|
|
4100
|
+
var LABELS_SEGMENT2 = "/labels";
|
|
4101
|
+
var PR_LABELS = z11.array(z11.object({ name: z11.string() }));
|
|
4102
|
+
async function add_pr_classification(number, label) {
|
|
4103
|
+
await git_gh_exec.exec_gh_api({
|
|
4104
|
+
path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
|
|
4105
|
+
body: JSON.stringify({ labels: [label] })
|
|
4106
|
+
});
|
|
4107
|
+
}
|
|
4108
|
+
async function pr_get_classification(branch_name) {
|
|
4109
|
+
const number = await require_pr_number(branch_name);
|
|
4110
|
+
const response = await git_gh_exec.exec_gh_api({
|
|
4111
|
+
path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
|
|
4112
|
+
});
|
|
4113
|
+
const names = PR_LABELS.parse(JSON.parse(response)).map((label) => label.name.toLowerCase());
|
|
4114
|
+
const selected = names.filter((name) => pr_classification.is_classification(name));
|
|
4115
|
+
if (selected.length === 0) return void 0;
|
|
4116
|
+
const error = pr_classification.classification_error(names, "human");
|
|
4117
|
+
if (error !== void 0) throw new Error(error);
|
|
4118
|
+
return selected[0];
|
|
4119
|
+
}
|
|
4120
|
+
async function pr_ensure_classification(branch_name, label) {
|
|
4121
|
+
if (await pr_get_classification(branch_name) !== void 0) return;
|
|
4122
|
+
const number = await require_pr_number(branch_name);
|
|
4123
|
+
await add_pr_classification(String(number), label);
|
|
4124
|
+
}
|
|
4125
|
+
async function post_pull_request(title, body) {
|
|
3659
4126
|
const base = await git_command.get_default_branch();
|
|
3660
4127
|
const head = await git_command.branch();
|
|
3661
4128
|
forget_pr_numbers();
|
|
@@ -3669,6 +4136,15 @@ async function pr_create(title, body) {
|
|
|
3669
4136
|
return git_gh_helpers.handle_pr_create_error(error);
|
|
3670
4137
|
}
|
|
3671
4138
|
}
|
|
4139
|
+
async function pr_create(title, body, label) {
|
|
4140
|
+
const classification_error2 = pr_classification.classification_error([label], "human");
|
|
4141
|
+
if (classification_error2 !== void 0) throw new Error(classification_error2);
|
|
4142
|
+
const url = await post_pull_request(title, body);
|
|
4143
|
+
const number = PR_NUMBER_PATTERN.exec(url)?.[1];
|
|
4144
|
+
if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
|
|
4145
|
+
await add_pr_classification(number, label);
|
|
4146
|
+
return url;
|
|
4147
|
+
}
|
|
3672
4148
|
async function pr_checkout(pr_number) {
|
|
3673
4149
|
const branch_name = await git_gh_pr_read.pr_head_reference(pr_number);
|
|
3674
4150
|
await git_command.fetch_branch(branch_name);
|
|
@@ -3726,6 +4202,8 @@ var git_gh_pr = {
|
|
|
3726
4202
|
...git_gh_pr_read,
|
|
3727
4203
|
...git_gh_pr_snapshot,
|
|
3728
4204
|
pr_create,
|
|
4205
|
+
pr_get_classification,
|
|
4206
|
+
pr_ensure_classification,
|
|
3729
4207
|
pr_checkout,
|
|
3730
4208
|
pr_comment,
|
|
3731
4209
|
pr_merge,
|
|
@@ -4204,30 +4682,30 @@ var epic_busy = {
|
|
|
4204
4682
|
};
|
|
4205
4683
|
|
|
4206
4684
|
// scripts/run/run-liveness.ts
|
|
4207
|
-
import { readFileSync as
|
|
4685
|
+
import { readFileSync as readFileSync7, realpathSync, statSync } from "node:fs";
|
|
4208
4686
|
import { homedir, tmpdir } from "node:os";
|
|
4209
|
-
import
|
|
4687
|
+
import path6 from "node:path";
|
|
4210
4688
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
4211
4689
|
|
|
4212
4690
|
// scripts/agent/agent-event.ts
|
|
4213
|
-
import { z as
|
|
4691
|
+
import { z as z13 } from "zod";
|
|
4214
4692
|
|
|
4215
4693
|
// scripts/agent/claude-result-event.ts
|
|
4216
|
-
import { z as
|
|
4217
|
-
var RESULT_EVENT_SCHEMA =
|
|
4218
|
-
type:
|
|
4219
|
-
is_error:
|
|
4220
|
-
subtype:
|
|
4221
|
-
num_turns:
|
|
4222
|
-
permission_denials:
|
|
4223
|
-
result:
|
|
4224
|
-
session_id:
|
|
4225
|
-
usage:
|
|
4694
|
+
import { z as z12 } from "zod";
|
|
4695
|
+
var RESULT_EVENT_SCHEMA = z12.looseObject({
|
|
4696
|
+
type: z12.literal("result"),
|
|
4697
|
+
is_error: z12.unknown().optional(),
|
|
4698
|
+
subtype: z12.unknown().optional(),
|
|
4699
|
+
num_turns: z12.unknown().optional(),
|
|
4700
|
+
permission_denials: z12.unknown().optional(),
|
|
4701
|
+
result: z12.unknown().optional(),
|
|
4702
|
+
session_id: z12.unknown().optional(),
|
|
4703
|
+
usage: z12.unknown().optional()
|
|
4226
4704
|
});
|
|
4227
|
-
var USAGE_SCHEMA =
|
|
4228
|
-
input_tokens:
|
|
4229
|
-
cache_read_input_tokens:
|
|
4230
|
-
output_tokens:
|
|
4705
|
+
var USAGE_SCHEMA = z12.looseObject({
|
|
4706
|
+
input_tokens: z12.unknown().optional(),
|
|
4707
|
+
cache_read_input_tokens: z12.unknown().optional(),
|
|
4708
|
+
output_tokens: z12.unknown().optional()
|
|
4231
4709
|
});
|
|
4232
4710
|
function usable_reason(value) {
|
|
4233
4711
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
@@ -4266,31 +4744,31 @@ function decode2(value) {
|
|
|
4266
4744
|
var claude_result_event = { decode: decode2 };
|
|
4267
4745
|
|
|
4268
4746
|
// scripts/agent/agent-event.ts
|
|
4269
|
-
var TEXT_BLOCK_SCHEMA =
|
|
4270
|
-
var CLAUDE_MESSAGE_SCHEMA =
|
|
4271
|
-
var CLAUDE_INIT_SCHEMA =
|
|
4272
|
-
var CLAUDE_ASSISTANT_SCHEMA =
|
|
4273
|
-
type:
|
|
4747
|
+
var TEXT_BLOCK_SCHEMA = z13.looseObject({ type: z13.literal("text"), text: z13.string() });
|
|
4748
|
+
var CLAUDE_MESSAGE_SCHEMA = z13.looseObject({ content: z13.array(z13.unknown()).optional() });
|
|
4749
|
+
var CLAUDE_INIT_SCHEMA = z13.looseObject({ type: z13.literal("system"), subtype: z13.literal("init") });
|
|
4750
|
+
var CLAUDE_ASSISTANT_SCHEMA = z13.looseObject({
|
|
4751
|
+
type: z13.literal("assistant"),
|
|
4274
4752
|
message: CLAUDE_MESSAGE_SCHEMA
|
|
4275
4753
|
});
|
|
4276
|
-
var USAGE_SCHEMA2 =
|
|
4277
|
-
input_tokens:
|
|
4278
|
-
cached_input_tokens:
|
|
4279
|
-
output_tokens:
|
|
4754
|
+
var USAGE_SCHEMA2 = z13.looseObject({
|
|
4755
|
+
input_tokens: z13.number().optional(),
|
|
4756
|
+
cached_input_tokens: z13.number().optional(),
|
|
4757
|
+
output_tokens: z13.number().optional()
|
|
4280
4758
|
});
|
|
4281
|
-
var CODEX_START_SCHEMA =
|
|
4282
|
-
var CODEX_PROGRESS_SCHEMA =
|
|
4283
|
-
type:
|
|
4284
|
-
item:
|
|
4759
|
+
var CODEX_START_SCHEMA = z13.looseObject({ type: z13.enum(["thread.started", "turn.started"]) });
|
|
4760
|
+
var CODEX_PROGRESS_SCHEMA = z13.looseObject({
|
|
4761
|
+
type: z13.enum(["item.started", "item.updated", "item.completed"]),
|
|
4762
|
+
item: z13.looseObject({ type: z13.string(), text: z13.string().optional() }).optional()
|
|
4285
4763
|
});
|
|
4286
|
-
var CODEX_COMPLETE_SCHEMA =
|
|
4287
|
-
type:
|
|
4764
|
+
var CODEX_COMPLETE_SCHEMA = z13.looseObject({
|
|
4765
|
+
type: z13.literal("turn.completed"),
|
|
4288
4766
|
usage: USAGE_SCHEMA2.optional()
|
|
4289
4767
|
});
|
|
4290
|
-
var CODEX_FAILURE_SCHEMA =
|
|
4291
|
-
type:
|
|
4292
|
-
error:
|
|
4293
|
-
message:
|
|
4768
|
+
var CODEX_FAILURE_SCHEMA = z13.looseObject({
|
|
4769
|
+
type: z13.enum(["turn.failed", "error"]),
|
|
4770
|
+
error: z13.looseObject({ message: z13.string().optional() }).optional(),
|
|
4771
|
+
message: z13.string().optional()
|
|
4294
4772
|
});
|
|
4295
4773
|
var EMPTY_STATE = {
|
|
4296
4774
|
provider: void 0,
|
|
@@ -4388,7 +4866,7 @@ function describe(state) {
|
|
|
4388
4866
|
var agent_event = { describe, read };
|
|
4389
4867
|
|
|
4390
4868
|
// scripts/issue/issue-state.ts
|
|
4391
|
-
import { z as
|
|
4869
|
+
import { z as z14 } from "zod";
|
|
4392
4870
|
var NO_LABELS = "(none)";
|
|
4393
4871
|
var LABEL_SEPARATOR = ", ";
|
|
4394
4872
|
var STATE_LABEL = "state: ";
|
|
@@ -4398,9 +4876,9 @@ var YES = "yes";
|
|
|
4398
4876
|
var NO = "no";
|
|
4399
4877
|
var ISSUE_LABEL = "issue: ";
|
|
4400
4878
|
var HUMAN_REVIEW_LABELS = /* @__PURE__ */ new Set([NEEDS_HUMAN_REVIEW_LABEL]);
|
|
4401
|
-
var label_schema =
|
|
4402
|
-
var issue_state_schema =
|
|
4403
|
-
state:
|
|
4879
|
+
var label_schema = z14.looseObject({ name: z14.string() });
|
|
4880
|
+
var issue_state_schema = z14.looseObject({
|
|
4881
|
+
state: z14.string(),
|
|
4404
4882
|
labels: label_schema.array().optional()
|
|
4405
4883
|
});
|
|
4406
4884
|
function parse_issue_state(json) {
|
|
@@ -4498,7 +4976,7 @@ function to_advice(verdict, has_work_to_stash) {
|
|
|
4498
4976
|
if (verdict === UNDETERMINED_VERDICT) return UNDETERMINED_ADVICE;
|
|
4499
4977
|
return has_work_to_stash ? STOPPED_DIRTY_ADVICE : STOPPED_CLEAN_ADVICE;
|
|
4500
4978
|
}
|
|
4501
|
-
function
|
|
4979
|
+
function decide5(traces) {
|
|
4502
4980
|
const verdict = to_verdict(traces);
|
|
4503
4981
|
const has_work_to_stash = verdict === STOPPED_VERDICT && traces.is_tree_dirty;
|
|
4504
4982
|
return {
|
|
@@ -4521,12 +4999,12 @@ var ALLOWED_ROOTS = [
|
|
|
4521
4999
|
)
|
|
4522
5000
|
];
|
|
4523
5001
|
function is_within(candidate, root) {
|
|
4524
|
-
const relative =
|
|
4525
|
-
return relative !== "" && !relative.startsWith("..") && !
|
|
5002
|
+
const relative = path6.relative(root, candidate);
|
|
5003
|
+
return relative !== "" && !relative.startsWith("..") && !path6.isAbsolute(relative);
|
|
4526
5004
|
}
|
|
4527
5005
|
function to_safe_path(output_path) {
|
|
4528
|
-
const normalized2 =
|
|
4529
|
-
if (!
|
|
5006
|
+
const normalized2 = path6.normalize(output_path);
|
|
5007
|
+
if (!path6.isAbsolute(normalized2)) return void 0;
|
|
4530
5008
|
return ALLOWED_ROOTS.some((root) => is_within(normalized2, root)) ? normalized2 : void 0;
|
|
4531
5009
|
}
|
|
4532
5010
|
function sample_output(output_path) {
|
|
@@ -4540,7 +5018,7 @@ function read_agent_state(output_path) {
|
|
|
4540
5018
|
const safe_path = to_safe_path(output_path);
|
|
4541
5019
|
if (safe_path === void 0) return void 0;
|
|
4542
5020
|
try {
|
|
4543
|
-
const state = agent_event.read(
|
|
5021
|
+
const state = agent_event.read(readFileSync7(safe_path, "utf8"));
|
|
4544
5022
|
return state.provider === void 0 ? void 0 : state;
|
|
4545
5023
|
} catch {
|
|
4546
5024
|
return void 0;
|
|
@@ -4597,11 +5075,11 @@ async function read_traces(request) {
|
|
|
4597
5075
|
}
|
|
4598
5076
|
async function check(request) {
|
|
4599
5077
|
run_issue_number.require_issue_number(request.issue);
|
|
4600
|
-
return
|
|
5078
|
+
return decide5(await read_traces(request));
|
|
4601
5079
|
}
|
|
4602
5080
|
var run_liveness = {
|
|
4603
5081
|
check,
|
|
4604
|
-
decide:
|
|
5082
|
+
decide: decide5,
|
|
4605
5083
|
describe_agent_state,
|
|
4606
5084
|
read_child_settled,
|
|
4607
5085
|
read_agent_state,
|
|
@@ -4672,7 +5150,7 @@ var lane_report = {
|
|
|
4672
5150
|
};
|
|
4673
5151
|
|
|
4674
5152
|
// scripts/run/run-preflight.ts
|
|
4675
|
-
import { z as
|
|
5153
|
+
import { z as z15 } from "zod";
|
|
4676
5154
|
var CLEAN_VERDICT = "clean";
|
|
4677
5155
|
var RECLAIM_VERDICT = "reclaim";
|
|
4678
5156
|
var RESUME_VERDICT = "resume";
|
|
@@ -4682,7 +5160,7 @@ var OPEN_PR = "open";
|
|
|
4682
5160
|
var MERGED_PR = "merged";
|
|
4683
5161
|
var CLOSED_PR = "closed";
|
|
4684
5162
|
var NO_CHILD_WORK = { branch_name: void 0, pr_state: NO_PR };
|
|
4685
|
-
var pr_info_schema2 =
|
|
5163
|
+
var pr_info_schema2 = z15.object({ state: z15.string().optional() });
|
|
4686
5164
|
var GH_PR_STATES = /* @__PURE__ */ new Map([
|
|
4687
5165
|
["CLOSED", CLOSED_PR],
|
|
4688
5166
|
["MERGED", MERGED_PR],
|
|
@@ -4751,7 +5229,7 @@ function to_advice2(verdict, issue, tree) {
|
|
|
4751
5229
|
if (verdict === PARK_VERDICT) return PARK_ADVICE;
|
|
4752
5230
|
return CLEAN_ADVICE;
|
|
4753
5231
|
}
|
|
4754
|
-
function
|
|
5232
|
+
function decide6(tree, child, issue) {
|
|
4755
5233
|
const verdict = to_verdict2(tree, child);
|
|
4756
5234
|
return {
|
|
4757
5235
|
advice: to_advice2(verdict, issue, tree),
|
|
@@ -4820,8 +5298,8 @@ async function read_child_state(issue) {
|
|
|
4820
5298
|
async function check2(issue) {
|
|
4821
5299
|
require_issue_number(issue);
|
|
4822
5300
|
const tree = await read_tree_state();
|
|
4823
|
-
if (needs_reclaim(tree)) return
|
|
4824
|
-
return
|
|
5301
|
+
if (needs_reclaim(tree)) return decide6(tree, NO_CHILD_WORK, issue);
|
|
5302
|
+
return decide6(tree, await read_child_state(issue), issue);
|
|
4825
5303
|
}
|
|
4826
5304
|
var run_preflight = {
|
|
4827
5305
|
CLEAN_ADVICE,
|
|
@@ -4836,7 +5314,7 @@ var run_preflight = {
|
|
|
4836
5314
|
MERGED_PR,
|
|
4837
5315
|
STASH_LABEL_PREFIX,
|
|
4838
5316
|
check: check2,
|
|
4839
|
-
decide:
|
|
5317
|
+
decide: decide6,
|
|
4840
5318
|
read_child_state,
|
|
4841
5319
|
read_pr_state: read_pr_state2,
|
|
4842
5320
|
to_pr_state: to_pr_state2
|
|
@@ -4936,7 +5414,7 @@ var run_progress_read = {
|
|
|
4936
5414
|
// scripts/run/run-watcher-hook.ts
|
|
4937
5415
|
var SWITCH_ENV_KEY2 = "JOSH_WATCHER_GUARD";
|
|
4938
5416
|
var STAMP_PREFIX2 = "josh-watcher-guard-";
|
|
4939
|
-
var payload_schema =
|
|
5417
|
+
var payload_schema = z16.object({ transcript_path: z16.string().min(1) });
|
|
4940
5418
|
function transcript_of(raw_payload) {
|
|
4941
5419
|
try {
|
|
4942
5420
|
const parsed = payload_schema.safeParse(JSON.parse(raw_payload));
|
|
@@ -4998,7 +5476,7 @@ async function pretool_outcome_async(raw_payload) {
|
|
|
4998
5476
|
return reason === void 0 ? base : { reason, notice: void 0, fault: void 0 };
|
|
4999
5477
|
}
|
|
5000
5478
|
var pretool_guard = { combine_outcomes, pretool_outcome, pretool_outcome_async };
|
|
5001
|
-
if (process.argv[1] ===
|
|
5479
|
+
if (process.argv[1] === fileURLToPath3(import.meta.url)) {
|
|
5002
5480
|
if (process.stdin.isTTY) hook_decision.report_no_payload("pretool:guard");
|
|
5003
5481
|
else {
|
|
5004
5482
|
const raw_payload = await text(process.stdin);
|