@jphutchins/code-review 0.1.0-alpha.32 → 0.1.0-alpha.34
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/dist/index.js +293 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -773,24 +773,35 @@ var DEFAULT_RESERVE = {
|
|
|
773
773
|
frac: 0.15,
|
|
774
774
|
growth: 0.25,
|
|
775
775
|
flatUsd: 0.02,
|
|
776
|
-
flatMs: 12e4
|
|
776
|
+
flatMs: 12e4,
|
|
777
|
+
flatMem: 2 * 1024 * 1024 * 1024
|
|
777
778
|
};
|
|
778
779
|
var SOFT_MULTIPLE = 2;
|
|
779
|
-
var
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
780
|
+
var growingReserve = (used, limit, flat, r) => {
|
|
781
|
+
const usedFrac = Math.min(1, Math.max(0, used / limit));
|
|
782
|
+
return Math.max(flat, (r.frac + r.growth * usedFrac) * limit);
|
|
783
|
+
};
|
|
784
|
+
var costAxis = (i) => i.spentUsd !== null && i.budgetUsd !== null && i.budgetUsd > 0 ? {
|
|
785
|
+
used: i.spentUsd,
|
|
786
|
+
limit: i.budgetUsd,
|
|
787
|
+
hardReserve: growingReserve(i.spentUsd, i.budgetUsd, i.reserve.flatUsd, i.reserve)
|
|
788
|
+
} : null;
|
|
789
|
+
var timeAxis = (i) => i.elapsedMs !== null && i.wallMs !== null && i.wallMs > 0 ? {
|
|
790
|
+
used: i.elapsedMs,
|
|
791
|
+
limit: i.wallMs,
|
|
792
|
+
hardReserve: growingReserve(i.elapsedMs, i.wallMs, i.reserve.flatMs, i.reserve)
|
|
793
|
+
} : null;
|
|
794
|
+
var axisSeverity = (a) => {
|
|
785
795
|
const remaining = a.limit - a.used;
|
|
786
|
-
if (remaining <= hardReserve) return 2;
|
|
787
|
-
if (remaining <= SOFT_MULTIPLE * hardReserve) return 1;
|
|
796
|
+
if (remaining <= a.hardReserve) return 2;
|
|
797
|
+
if (remaining <= SOFT_MULTIPLE * a.hardReserve) return 1;
|
|
788
798
|
return 0;
|
|
789
799
|
};
|
|
790
800
|
var decideBudget = (i) => {
|
|
791
|
-
const worst = [costAxis(i), timeAxis(i)].filter((a) => a !== null).reduce((max, a) => Math.max(max, axisSeverity(a
|
|
801
|
+
const worst = [costAxis(i), timeAxis(i)].filter((a) => a !== null).reduce((max, a) => Math.max(max, axisSeverity(a)), 0);
|
|
792
802
|
return worst === 2 ? { kind: "hard" } : worst === 1 ? { kind: "soft" } : { kind: "ok" };
|
|
793
803
|
};
|
|
804
|
+
var memoryCritical = (availMemBytes, totalMemBytes, floorBytes) => availMemBytes !== null && totalMemBytes !== null && totalMemBytes > 0 && availMemBytes <= floorBytes;
|
|
794
805
|
var pct = (n) => `${String(Math.round(n * 100))}%`;
|
|
795
806
|
var money = (n) => `$${n.toFixed(2)}`;
|
|
796
807
|
var mins = (ms) => `${(ms / 6e4).toFixed(1)}m`;
|
|
@@ -845,6 +856,7 @@ var lastValidPath = (draftPath) => {
|
|
|
845
856
|
};
|
|
846
857
|
var mainHasWrittenDraft = (draftMtimeMs, seedMarkerMtimeMs) => draftMtimeMs !== null && (seedMarkerMtimeMs === null || draftMtimeMs > seedMarkerMtimeMs);
|
|
847
858
|
var spawnFloorMessage = (draftPath) => `Write your own first-pass findings to ${draftPath} before spawning subagents \u2014 a review must never depend on subagents alone, and a pre-seeded draft does not count until you have revised it yourself this run. Write ${draftPath} from what you have read so far (preliminary findings are fine), run \`code-review validate ${draftPath} --explain\` until it passes, then fan out; your subagents run in the background, so keep refining the draft as their reports arrive.`;
|
|
859
|
+
var memoryPressureMessage = (draftPath) => `System memory is critically low right now, so another subagent can't be spawned \u2014 a fresh process is the fastest way to tip the runner into an out-of-memory kill that would lose the whole review. Don't wind down: keep reading code directly and keep folding the reports from subagents already running into ${draftPath}, then try spawning again in a moment \u2014 this clears as soon as running subagents finish and free their memory.`;
|
|
848
860
|
var forceBackgroundSpawn = (toolInput) => ({
|
|
849
861
|
hookSpecificOutput: {
|
|
850
862
|
hookEventName: "PreToolUse",
|
|
@@ -890,6 +902,8 @@ var evaluateBudgetHook = (input, params) => {
|
|
|
890
902
|
if (phase.kind === "hard" && blockedDuringConvergence(toolName, rec["tool_input"]))
|
|
891
903
|
return denyPreTool(budgetMessage(inputs, phase, params.draftPath, isSubagent));
|
|
892
904
|
if (SPAWN_TOOLS.has(toolName)) {
|
|
905
|
+
if (memoryCritical(params.availMemBytes, params.totalMemBytes, params.reserve.flatMem))
|
|
906
|
+
return denyPreTool(memoryPressureMessage(params.draftPath));
|
|
893
907
|
if (!isSubagent && !params.mainDraftWritten)
|
|
894
908
|
return denyPreTool(spawnFloorMessage(params.draftPath));
|
|
895
909
|
return forceBackgroundSpawn(rec["tool_input"]);
|
|
@@ -936,6 +950,21 @@ var parseFraction = (raw, fallback) => {
|
|
|
936
950
|
const n = Number.parseFloat(raw);
|
|
937
951
|
return Number.isFinite(n) && n >= 0 && n <= 1 ? n : fallback;
|
|
938
952
|
};
|
|
953
|
+
var BYTE_UNIT = {
|
|
954
|
+
"": 1,
|
|
955
|
+
k: 1024,
|
|
956
|
+
m: 1024 * 1024,
|
|
957
|
+
g: 1024 * 1024 * 1024,
|
|
958
|
+
t: 1024 * 1024 * 1024 * 1024
|
|
959
|
+
};
|
|
960
|
+
var parseByteSize = (raw) => {
|
|
961
|
+
const m = /^(\d+(?:\.\d+)?)\s*([kmgt])?(?:i?b)?$/i.exec(raw.trim());
|
|
962
|
+
if (m === null) return null;
|
|
963
|
+
const [, num = "", unit = ""] = m;
|
|
964
|
+
const n = Number.parseFloat(num);
|
|
965
|
+
const mult = BYTE_UNIT[unit.toLowerCase()];
|
|
966
|
+
return Number.isFinite(n) && mult !== void 0 ? n * mult : null;
|
|
967
|
+
};
|
|
939
968
|
var budgetHookCommand = (draftPath, opts) => [
|
|
940
969
|
"code-review budget-hook --draft",
|
|
941
970
|
shellQuote(draftPath),
|
|
@@ -945,7 +974,8 @@ var budgetHookCommand = (draftPath, opts) => [
|
|
|
945
974
|
...opts.reserveFrac ? ["--reserve-frac", shellQuote(opts.reserveFrac)] : [],
|
|
946
975
|
...opts.reserveGrowth ? ["--reserve-growth", shellQuote(opts.reserveGrowth)] : [],
|
|
947
976
|
...opts.reserveUsd ? ["--reserve-usd", shellQuote(opts.reserveUsd)] : [],
|
|
948
|
-
...opts.reserveWall ? ["--reserve-wall", shellQuote(opts.reserveWall)] : []
|
|
977
|
+
...opts.reserveWall ? ["--reserve-wall", shellQuote(opts.reserveWall)] : [],
|
|
978
|
+
...opts.reserveMem ? ["--reserve-mem", shellQuote(opts.reserveMem)] : []
|
|
949
979
|
].join(" ");
|
|
950
980
|
|
|
951
981
|
// src/format.ts
|
|
@@ -1068,9 +1098,9 @@ var formatErrors = (errors) => errors.map(describeValidationError);
|
|
|
1068
1098
|
var declaredVersion = (raw) => typeof raw === "object" && raw !== null && "schema_version" in raw ? typeof raw.schema_version === "string" ? raw.schema_version : void 0 : void 0;
|
|
1069
1099
|
var supportedVersions = (kind) => tableFor(kind).map((entry) => entry.minor);
|
|
1070
1100
|
var defaultVersion = (kind) => {
|
|
1071
|
-
const
|
|
1072
|
-
if (!
|
|
1073
|
-
return
|
|
1101
|
+
const latest2 = tableFor(kind).find((entry) => entry.latest);
|
|
1102
|
+
if (!latest2) throw new Error(`Registry invariant violated \u2014 no latest entry for "${kind}"`);
|
|
1103
|
+
return latest2.defaultVersion;
|
|
1074
1104
|
};
|
|
1075
1105
|
var bundledSchemaPath = (relativePath) => resolve$1(import.meta.dirname, "..", "schema", relativePath);
|
|
1076
1106
|
var schemaPathFor = (kind, version) => {
|
|
@@ -1759,6 +1789,134 @@ var announce = async (input, ghApi = runGhApi) => {
|
|
|
1759
1789
|
ghApi
|
|
1760
1790
|
);
|
|
1761
1791
|
};
|
|
1792
|
+
var incompleteBody = (headSha, runUrl, existingBody) => {
|
|
1793
|
+
const notice = `${DEFAULT_MARKER}
|
|
1794
|
+
|
|
1795
|
+
\u26A0\uFE0F **Code review did not complete** for \`${headSha.slice(0, 7)}\` \u2014 the review job failed ([run](${runUrl})). Re-request the review; do not treat this round as spent.`;
|
|
1796
|
+
const carried = existingBody ? carryForwardMarkers(existingBody) : "";
|
|
1797
|
+
return carried ? `${notice}
|
|
1798
|
+
|
|
1799
|
+
${carried}` : notice;
|
|
1800
|
+
};
|
|
1801
|
+
var reportIncomplete = async (input, ghApi = runGhApi) => {
|
|
1802
|
+
const candidates = await fetchPrCandidates(input.repo, input.headSha, ghApi);
|
|
1803
|
+
const resolution = resolvePr(candidates, input.headBranch);
|
|
1804
|
+
if (resolution.kind !== "open") {
|
|
1805
|
+
process.stderr.write(
|
|
1806
|
+
`No open PR for ${input.headSha} \u2014 nothing to report (${resolution.kind})
|
|
1807
|
+
`
|
|
1808
|
+
);
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
const existing = await findBotComment(
|
|
1812
|
+
input.repo,
|
|
1813
|
+
resolution.prNumber,
|
|
1814
|
+
input.botLogin,
|
|
1815
|
+
DEFAULT_MARKER,
|
|
1816
|
+
ghApi
|
|
1817
|
+
);
|
|
1818
|
+
if (existing !== null && parseReviewComplete(existing.body)) {
|
|
1819
|
+
process.stderr.write(`Sticky already reflects a completed review \u2014 leaving it in place
|
|
1820
|
+
`);
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
if (existing !== null && !existing.body.includes(input.runUrl)) {
|
|
1824
|
+
process.stderr.write(`Sticky belongs to another run \u2014 leaving it in place
|
|
1825
|
+
`);
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
await upsertSticky(
|
|
1829
|
+
input.repo,
|
|
1830
|
+
resolution.prNumber,
|
|
1831
|
+
existing,
|
|
1832
|
+
incompleteBody(input.headSha, input.runUrl, existing?.body),
|
|
1833
|
+
ghApi
|
|
1834
|
+
);
|
|
1835
|
+
};
|
|
1836
|
+
|
|
1837
|
+
// src/checkrun.ts
|
|
1838
|
+
var CHECK_RUN_NAME = "Code review";
|
|
1839
|
+
var latest = (checks) => checks.reduce(
|
|
1840
|
+
(best, c) => best === null || c.id > best.id ? c : best,
|
|
1841
|
+
null
|
|
1842
|
+
);
|
|
1843
|
+
var isOpen = (status) => status === "in_progress" || status === "queued";
|
|
1844
|
+
var settled = /* @__PURE__ */ new Set(["success", "neutral", "skipped"]);
|
|
1845
|
+
var decideCheckAction = (checks, intent) => {
|
|
1846
|
+
const head = latest(checks);
|
|
1847
|
+
switch (intent) {
|
|
1848
|
+
case "in_progress":
|
|
1849
|
+
return head !== null && isOpen(head.status) ? { kind: "noop", reason: "a check is already in progress for this head" } : { kind: "create", status: "in_progress" };
|
|
1850
|
+
case "neutral":
|
|
1851
|
+
if (head === null) return { kind: "create", status: "completed", conclusion: "neutral" };
|
|
1852
|
+
return head.status === "completed" && head.conclusion === "neutral" ? { kind: "noop", reason: "the check already records this completed review" } : { kind: "patch", id: head.id, status: "completed", conclusion: "neutral" };
|
|
1853
|
+
case "failure":
|
|
1854
|
+
if (head === null) return { kind: "create", status: "completed", conclusion: "failure" };
|
|
1855
|
+
if (head.status === "completed" && head.conclusion !== null && settled.has(head.conclusion))
|
|
1856
|
+
return { kind: "noop", reason: "a completed review already recorded this head" };
|
|
1857
|
+
return head.status === "completed" && head.conclusion === "failure" ? { kind: "noop", reason: "the check already records this failure" } : { kind: "patch", id: head.id, status: "completed", conclusion: "failure" };
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
var CHECK_JQ = ".check_runs[] | {id: .id, status: .status, conclusion: .conclusion}";
|
|
1861
|
+
var fetchChecks = async (repo, headSha, ghApi) => parseJsonl(
|
|
1862
|
+
await ghApi([
|
|
1863
|
+
`repos/${repo}/commits/${headSha}/check-runs?check_name=${encodeURIComponent(CHECK_RUN_NAME)}&per_page=100`,
|
|
1864
|
+
"--paginate",
|
|
1865
|
+
"--jq",
|
|
1866
|
+
CHECK_JQ
|
|
1867
|
+
])
|
|
1868
|
+
);
|
|
1869
|
+
var output = (intent, runUrl) => {
|
|
1870
|
+
switch (intent) {
|
|
1871
|
+
case "in_progress":
|
|
1872
|
+
return {
|
|
1873
|
+
title: "Code review in progress",
|
|
1874
|
+
summary: `The review is running \u2014 [see the run](${runUrl}).`
|
|
1875
|
+
};
|
|
1876
|
+
case "neutral":
|
|
1877
|
+
return {
|
|
1878
|
+
title: "Code review complete",
|
|
1879
|
+
summary: `The review was posted \u2014 [see the run](${runUrl}).`
|
|
1880
|
+
};
|
|
1881
|
+
case "failure":
|
|
1882
|
+
return {
|
|
1883
|
+
title: "Code review did not complete",
|
|
1884
|
+
summary: `The review job failed \u2014 [see the run](${runUrl}). Re-request the review; do not treat this round as spent.`
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
var checkRun = async (input, ghApi = runGhApi) => {
|
|
1889
|
+
const action = decideCheckAction(
|
|
1890
|
+
await fetchChecks(input.repo, input.headSha, ghApi),
|
|
1891
|
+
input.intent
|
|
1892
|
+
);
|
|
1893
|
+
if (action.kind === "noop") {
|
|
1894
|
+
process.stderr.write(`code-review check-run: ${action.reason} \u2014 leaving it
|
|
1895
|
+
`);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
const body = action.kind === "create" ? {
|
|
1899
|
+
name: CHECK_RUN_NAME,
|
|
1900
|
+
head_sha: input.headSha,
|
|
1901
|
+
status: action.status,
|
|
1902
|
+
details_url: input.runUrl,
|
|
1903
|
+
...action.conclusion ? { conclusion: action.conclusion } : {},
|
|
1904
|
+
output: output(input.intent, input.runUrl)
|
|
1905
|
+
} : {
|
|
1906
|
+
status: action.status,
|
|
1907
|
+
conclusion: action.conclusion,
|
|
1908
|
+
details_url: input.runUrl,
|
|
1909
|
+
output: output(input.intent, input.runUrl)
|
|
1910
|
+
};
|
|
1911
|
+
const endpoint = action.kind === "create" ? [`--method`, `POST`, `repos/${input.repo}/check-runs`, `--input`, `-`] : [
|
|
1912
|
+
`--method`,
|
|
1913
|
+
`PATCH`,
|
|
1914
|
+
`repos/${input.repo}/check-runs/${String(action.id)}`,
|
|
1915
|
+
`--input`,
|
|
1916
|
+
`-`
|
|
1917
|
+
];
|
|
1918
|
+
await ghApi(endpoint, JSON.stringify(body));
|
|
1919
|
+
};
|
|
1762
1920
|
var DURATION_RE = /^(\d+)(h|m|s)$/;
|
|
1763
1921
|
var USD_RE = /^\$(\d+(?:\.\d+)?)$/;
|
|
1764
1922
|
var toSeconds = (n, unit) => unit === "h" ? n * 3600 : unit === "m" ? n * 60 : n;
|
|
@@ -1965,12 +2123,12 @@ var resolveCiRun = async (repo, headSha, workflowName, ghApi) => {
|
|
|
1965
2123
|
`
|
|
1966
2124
|
);
|
|
1967
2125
|
}
|
|
1968
|
-
const
|
|
2126
|
+
const latest2 = runs.filter((r) => r.name === workflowName).reduce(
|
|
1969
2127
|
(best, r) => best === null || r.run_number > best.run_number ? r : best,
|
|
1970
2128
|
null
|
|
1971
2129
|
);
|
|
1972
2130
|
return {
|
|
1973
|
-
run:
|
|
2131
|
+
run: latest2 === null ? null : { id: latest2.id, status: latest2.status ?? "unknown", conclusion: latest2.conclusion },
|
|
1974
2132
|
seenNames: [...new Set(runs.flatMap((r) => r.name === null ? [] : [r.name]))]
|
|
1975
2133
|
};
|
|
1976
2134
|
};
|
|
@@ -2722,7 +2880,7 @@ var renderCmd = defineCommand({
|
|
|
2722
2880
|
const prices = decode(PriceMapCodec.decode(readJSON(priceResolution.path)), "prices");
|
|
2723
2881
|
const template = readFileSync(templatePath, "utf-8");
|
|
2724
2882
|
const testReport = args["test-report"] ? decode(TestSummaryCodec.decode(readJSON(args["test-report"])), "test report") : void 0;
|
|
2725
|
-
const
|
|
2883
|
+
const output2 = render({
|
|
2726
2884
|
findings,
|
|
2727
2885
|
envelope,
|
|
2728
2886
|
prices,
|
|
@@ -2734,7 +2892,7 @@ var renderCmd = defineCommand({
|
|
|
2734
2892
|
testReport,
|
|
2735
2893
|
postedAt: formatUtc(/* @__PURE__ */ new Date())
|
|
2736
2894
|
});
|
|
2737
|
-
process.stdout.write(
|
|
2895
|
+
process.stdout.write(output2);
|
|
2738
2896
|
}
|
|
2739
2897
|
});
|
|
2740
2898
|
var inlineCmd = defineCommand({
|
|
@@ -2874,6 +3032,18 @@ var snapshotIfValid = (draftPath) => {
|
|
|
2874
3032
|
);
|
|
2875
3033
|
}
|
|
2876
3034
|
};
|
|
3035
|
+
var readMemInfo = () => {
|
|
3036
|
+
try {
|
|
3037
|
+
const text = readFileSync("/proc/meminfo", "utf8");
|
|
3038
|
+
const kb = (key2) => {
|
|
3039
|
+
const m = new RegExp(`^${key2}:\\s+(\\d+)\\s+kB`, "m").exec(text);
|
|
3040
|
+
return m?.[1] !== void 0 ? Number(m[1]) * 1024 : null;
|
|
3041
|
+
};
|
|
3042
|
+
return { availBytes: kb("MemAvailable"), totalBytes: kb("MemTotal") };
|
|
3043
|
+
} catch {
|
|
3044
|
+
return { availBytes: null, totalBytes: null };
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
2877
3047
|
var budgetHookCmd = defineCommand({
|
|
2878
3048
|
meta: {
|
|
2879
3049
|
name: "budget-hook",
|
|
@@ -2912,19 +3082,24 @@ var budgetHookCmd = defineCommand({
|
|
|
2912
3082
|
"reserve-wall": {
|
|
2913
3083
|
type: "string",
|
|
2914
3084
|
description: "Flat wall-clock wind-down floor (e.g. 2m, 120s), whichever is larger with --reserve-frac (default: 2m)"
|
|
3085
|
+
},
|
|
3086
|
+
"reserve-mem": {
|
|
3087
|
+
type: "string",
|
|
3088
|
+
description: "Free-RAM floor (e.g. 2g, 1536m) below which new subagent spawns are denied until memory recovers; not a convergence axis (default: 2g)"
|
|
2915
3089
|
}
|
|
2916
3090
|
},
|
|
2917
3091
|
run: async ({ args }) => {
|
|
2918
3092
|
try {
|
|
2919
3093
|
const draftPath = resolve$1(args.draft);
|
|
2920
3094
|
const input = readStdinJSON();
|
|
3095
|
+
const mem = readMemInfo();
|
|
2921
3096
|
const transcriptPath = transcriptPathOf(input);
|
|
2922
3097
|
const tree = transcriptPath ? readTranscriptTree(resolve$1(transcriptPath)) : void 0;
|
|
2923
3098
|
const usage = tree ? sumTranscriptUsage(tree.entries) : void 0;
|
|
2924
3099
|
const prices = args.prices ? tryReadPrices(args.prices) : null;
|
|
2925
3100
|
const spentUsd = prices !== null && usage ? computeCost(usage.models, prices).totalCostUSD : null;
|
|
2926
3101
|
const wallMs = args.wall ? parseWallMs(args.wall) : null;
|
|
2927
|
-
const
|
|
3102
|
+
const output2 = evaluateBudgetHook(input, {
|
|
2928
3103
|
spentUsd,
|
|
2929
3104
|
budgetUsd: parseBudgetUsd(args["budget-usd"]),
|
|
2930
3105
|
elapsedMs: anchoredElapsedMs({
|
|
@@ -2934,11 +3109,14 @@ var budgetHookCmd = defineCommand({
|
|
|
2934
3109
|
nowMs: Date.now()
|
|
2935
3110
|
}),
|
|
2936
3111
|
wallMs,
|
|
3112
|
+
availMemBytes: mem.availBytes,
|
|
3113
|
+
totalMemBytes: mem.totalBytes,
|
|
2937
3114
|
reserve: {
|
|
2938
3115
|
frac: parseFraction(args["reserve-frac"], DEFAULT_RESERVE.frac),
|
|
2939
3116
|
growth: parseFraction(args["reserve-growth"], DEFAULT_RESERVE.growth),
|
|
2940
3117
|
flatUsd: parseBudgetUsd(args["reserve-usd"]) ?? DEFAULT_RESERVE.flatUsd,
|
|
2941
|
-
flatMs: args["reserve-wall"] ? parseWallMs(args["reserve-wall"]) ?? DEFAULT_RESERVE.flatMs : DEFAULT_RESERVE.flatMs
|
|
3118
|
+
flatMs: args["reserve-wall"] ? parseWallMs(args["reserve-wall"]) ?? DEFAULT_RESERVE.flatMs : DEFAULT_RESERVE.flatMs,
|
|
3119
|
+
flatMem: args["reserve-mem"] ? parseByteSize(args["reserve-mem"]) ?? DEFAULT_RESERVE.flatMem : DEFAULT_RESERVE.flatMem
|
|
2942
3120
|
},
|
|
2943
3121
|
draftPath,
|
|
2944
3122
|
mainDraftWritten: mainHasWrittenDraft(
|
|
@@ -2948,7 +3126,7 @@ var budgetHookCmd = defineCommand({
|
|
|
2948
3126
|
});
|
|
2949
3127
|
if (asRecord(input)?.["hook_event_name"] === "PostToolBatch" && !isSubagentHookInput(input))
|
|
2950
3128
|
snapshotIfValid(draftPath);
|
|
2951
|
-
process.stdout.write(`${JSON.stringify(
|
|
3129
|
+
process.stdout.write(`${JSON.stringify(output2)}
|
|
2952
3130
|
`);
|
|
2953
3131
|
} catch (err) {
|
|
2954
3132
|
process.stderr.write(`code-review budget-hook: degrading to no-op \u2014 ${errMsg(err)}
|
|
@@ -3015,6 +3193,10 @@ var printSettingsCmd = defineCommand({
|
|
|
3015
3193
|
"reserve-wall": {
|
|
3016
3194
|
type: "string",
|
|
3017
3195
|
description: "Flat wall-clock wind-down floor (e.g. 2m), whichever is larger with --reserve-frac (default: 2m)"
|
|
3196
|
+
},
|
|
3197
|
+
"reserve-mem": {
|
|
3198
|
+
type: "string",
|
|
3199
|
+
description: "Free-RAM floor (e.g. 2g) below which new subagent spawns are denied until memory recovers (default: 2g)"
|
|
3018
3200
|
}
|
|
3019
3201
|
},
|
|
3020
3202
|
run: async ({ args }) => {
|
|
@@ -3036,7 +3218,8 @@ var printSettingsCmd = defineCommand({
|
|
|
3036
3218
|
reserveFrac: args["reserve-frac"],
|
|
3037
3219
|
reserveGrowth: args["reserve-growth"],
|
|
3038
3220
|
reserveUsd: args["reserve-usd"],
|
|
3039
|
-
reserveWall: args["reserve-wall"]
|
|
3221
|
+
reserveWall: args["reserve-wall"],
|
|
3222
|
+
reserveMem: args["reserve-mem"]
|
|
3040
3223
|
}
|
|
3041
3224
|
});
|
|
3042
3225
|
process.stdout.write(`${JSON.stringify(settings)}
|
|
@@ -3762,6 +3945,92 @@ var announceCmd = defineCommand({
|
|
|
3762
3945
|
);
|
|
3763
3946
|
}
|
|
3764
3947
|
});
|
|
3948
|
+
var isCheckIntent = (s) => s === "in_progress" || s === "neutral" || s === "failure";
|
|
3949
|
+
var checkRunCmd = defineCommand({
|
|
3950
|
+
meta: {
|
|
3951
|
+
name: "check-run",
|
|
3952
|
+
description: "Upsert the native 'Code review' check-run on the head SHA \u2014 the attribution surface that appears in the PR's own checks list and (writing to the base repo) works for fork PRs too. `in_progress` at review start, `neutral` when the review completes, `failure` when it didn't. Forward-only: `failure` never overwrites a completed review."
|
|
3953
|
+
},
|
|
3954
|
+
args: {
|
|
3955
|
+
repo: { type: "string", description: "Repository (owner/name)", required: true },
|
|
3956
|
+
"head-sha": {
|
|
3957
|
+
type: "string",
|
|
3958
|
+
description: "Head SHA the check-run is anchored to",
|
|
3959
|
+
required: true
|
|
3960
|
+
},
|
|
3961
|
+
status: {
|
|
3962
|
+
type: "positional",
|
|
3963
|
+
description: "One of: in_progress, neutral, failure",
|
|
3964
|
+
required: true
|
|
3965
|
+
},
|
|
3966
|
+
"run-url": {
|
|
3967
|
+
type: "string",
|
|
3968
|
+
description: "Workflow run URL the check-run's details link to",
|
|
3969
|
+
required: true
|
|
3970
|
+
}
|
|
3971
|
+
},
|
|
3972
|
+
run: async ({ args }) => {
|
|
3973
|
+
if (!isCheckIntent(args.status)) {
|
|
3974
|
+
process.stderr.write(
|
|
3975
|
+
`::warning::code-review check-run: unrecognized status "${annotationSafe(args.status)}" \u2014 expected in_progress, neutral, or failure; skipping
|
|
3976
|
+
`
|
|
3977
|
+
);
|
|
3978
|
+
return;
|
|
3979
|
+
}
|
|
3980
|
+
await checkRun({
|
|
3981
|
+
repo: args.repo,
|
|
3982
|
+
headSha: args["head-sha"],
|
|
3983
|
+
intent: args.status,
|
|
3984
|
+
runUrl: args["run-url"]
|
|
3985
|
+
}).catch(
|
|
3986
|
+
(err) => process.stderr.write(
|
|
3987
|
+
`::warning::code-review check-run: could not upsert the check-run (${annotationSafe(errMsg(err))}) \u2014 continuing (attribution aid)
|
|
3988
|
+
`
|
|
3989
|
+
)
|
|
3990
|
+
);
|
|
3991
|
+
}
|
|
3992
|
+
});
|
|
3993
|
+
var reportIncompleteCmd = defineCommand({
|
|
3994
|
+
meta: {
|
|
3995
|
+
name: "report-incomplete",
|
|
3996
|
+
description: "Post (or update) the sticky when a review job hard-failed and posted nothing \u2014 an attributed 'did not complete' notice linking the run, telling the reader to re-request. Never buries a completed review, and never overwrites a superseding run's live in-progress placeholder. (A cancelled review is left to the superseding run that took over.)"
|
|
3997
|
+
},
|
|
3998
|
+
args: {
|
|
3999
|
+
repo: { type: "string", description: "Repository (owner/name)", required: true },
|
|
4000
|
+
"head-sha": {
|
|
4001
|
+
type: "string",
|
|
4002
|
+
description: "Trusted head SHA to resolve the PR (from workflow_run.head_sha)",
|
|
4003
|
+
required: true
|
|
4004
|
+
},
|
|
4005
|
+
"run-url": {
|
|
4006
|
+
type: "string",
|
|
4007
|
+
description: "Workflow run URL the notice links to",
|
|
4008
|
+
required: true
|
|
4009
|
+
},
|
|
4010
|
+
"bot-login": {
|
|
4011
|
+
type: "string",
|
|
4012
|
+
description: "Bot login to trust for the sticky comment upsert (default: github-actions[bot])"
|
|
4013
|
+
},
|
|
4014
|
+
"head-branch": {
|
|
4015
|
+
type: "string",
|
|
4016
|
+
description: "Head branch to disambiguate the PR when multiple share a commit"
|
|
4017
|
+
}
|
|
4018
|
+
},
|
|
4019
|
+
run: async ({ args }) => {
|
|
4020
|
+
await reportIncomplete({
|
|
4021
|
+
repo: args.repo,
|
|
4022
|
+
headSha: args["head-sha"],
|
|
4023
|
+
botLogin: args["bot-login"] || "github-actions[bot]",
|
|
4024
|
+
runUrl: args["run-url"],
|
|
4025
|
+
headBranch: args["head-branch"]
|
|
4026
|
+
}).catch(
|
|
4027
|
+
(err) => process.stderr.write(
|
|
4028
|
+
`::warning::code-review report-incomplete: could not post the failure notice (${annotationSafe(errMsg(err))}) \u2014 continuing
|
|
4029
|
+
`
|
|
4030
|
+
)
|
|
4031
|
+
);
|
|
4032
|
+
}
|
|
4033
|
+
});
|
|
3765
4034
|
var requireCeilingSec = (raw) => {
|
|
3766
4035
|
if (raw === void 0) return null;
|
|
3767
4036
|
const ms = parseWallMs(raw);
|
|
@@ -3935,6 +4204,8 @@ var main = defineCommand({
|
|
|
3935
4204
|
inline: inlineCmd,
|
|
3936
4205
|
post: postCmd,
|
|
3937
4206
|
announce: announceCmd,
|
|
4207
|
+
"check-run": checkRunCmd,
|
|
4208
|
+
"report-incomplete": reportIncompleteCmd,
|
|
3938
4209
|
cost: costCmd,
|
|
3939
4210
|
"check-cost": checkCostCmd,
|
|
3940
4211
|
validate: validateCmd,
|