strikethroo 3.17.1 → 3.18.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/dist-web/assets/{arc-bFI_bykL.js → arc-BlSNa2QY.js} +1 -1
- package/dist-web/assets/{architectureDiagram-3BPJPVTR-eduQ3NL0.js → architectureDiagram-3BPJPVTR-CPcCF2a4.js} +1 -1
- package/dist-web/assets/{blockDiagram-GPEHLZMM-Cdc4VCdU.js → blockDiagram-GPEHLZMM-ECDR0edZ.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-DK5FM3c1.js → c4Diagram-AAUBKEIU-oM3VnEAQ.js} +1 -1
- package/dist-web/assets/channel-CmU4mBgT.js +1 -0
- package/dist-web/assets/{chunk-2J33WTMH-7biHL6be.js → chunk-2J33WTMH-DHk5SJLz.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-9gARU0Ar.js → chunk-4BX2VUAB-CjBwgkMu.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-C9jTAQT8.js → chunk-55IACEB6-D95zqyiV.js} +1 -1
- package/dist-web/assets/{chunk-727SXJPM-Bqe00t7X.js → chunk-727SXJPM-DCCPRUfv.js} +1 -1
- package/dist-web/assets/{chunk-AQP2D5EJ-DZ3Zz5Bq.js → chunk-AQP2D5EJ-qur7KQyD.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-FHFhRSUs.js → chunk-FMBD7UC4-D46Su7Fv.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-603eoTKu.js → chunk-ND2GUHAM-BioGsqxt.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-Cv5n9Aq_.js → chunk-QZHKN3VN-C8LdDiqn.js} +1 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-D_FMKQsB.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-D_FMKQsB.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-D65G_bqC.js → cose-bilkent-S5V4N54A-ccY03BRh.js} +1 -1
- package/dist-web/assets/{dagre-BM42HDAG-p8to9lbM.js → dagre-BM42HDAG-BYLmCViB.js} +1 -1
- package/dist-web/assets/{diagram-2AECGRRQ-BX87SeKm.js → diagram-2AECGRRQ-DTvenvXR.js} +1 -1
- package/dist-web/assets/{diagram-5GNKFQAL-BrOUDHCI.js → diagram-5GNKFQAL-CCzsYDmr.js} +1 -1
- package/dist-web/assets/{diagram-KO2AKTUF-xu_psi3A.js → diagram-KO2AKTUF-QSff7J_V.js} +1 -1
- package/dist-web/assets/{diagram-LMA3HP47-DaZ4RA3L.js → diagram-LMA3HP47-CVqlEmu2.js} +1 -1
- package/dist-web/assets/{diagram-OG6HWLK6-Cau5YU2v.js → diagram-OG6HWLK6-dBVz-I46.js} +1 -1
- package/dist-web/assets/{erDiagram-TEJ5UH35-DzipiTQg.js → erDiagram-TEJ5UH35-B_y4ftNu.js} +1 -1
- package/dist-web/assets/{flowDiagram-I6XJVG4X-Dtv_2Tvk.js → flowDiagram-I6XJVG4X-M410hT_n.js} +1 -1
- package/dist-web/assets/{ganttDiagram-6RSMTGT7-EEtWY2A7.js → ganttDiagram-6RSMTGT7-qiri2OJa.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-DaHmpGdB.js → gitGraphDiagram-PVQCEYII-FqGGBobG.js} +1 -1
- package/dist-web/assets/{index-MkSYPUmR.js → index-1SV04K_c.js} +1 -1
- package/dist-web/assets/{index-DToSQu_H.js → index-BaFSLlgl.js} +1 -1
- package/dist-web/assets/{index-QGKq5NNb.js → index-DrUIu0u6.js} +4 -4
- package/dist-web/assets/{infoDiagram-5YYISTIA-Du5rxJD7.js → infoDiagram-5YYISTIA-CczHqRr1.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-I_k-NwEa.js → ishikawaDiagram-YF4QCWOH-ty1DRy6n.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-JvRwnn5D.js → journeyDiagram-JHISSGLW-D_JYvt6i.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-B5E49Jo8.js → kanban-definition-UN3LZRKU-CT67nUTK.js} +1 -1
- package/dist-web/assets/{linear-DzGLUPw9.js → linear-B1HaE_t1.js} +1 -1
- package/dist-web/assets/{mermaid.core-CYkrjFmg.js → mermaid.core-Dumx_iKV.js} +4 -4
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-CDKHMTiZ.js → mindmap-definition-RKZ34NQL-CVulJo63.js} +1 -1
- package/dist-web/assets/{pieDiagram-4H26LBE5-jYQa4N-h.js → pieDiagram-4H26LBE5-C1yD5ntZ.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-h3o6CS5Z.js → quadrantDiagram-W4KKPZXB-B7PoiM8c.js} +1 -1
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-DuQXVvtG.js → requirementDiagram-4Y6WPE33-COvHb3Mv.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-5OEKKPKP-DdllUSJD.js → sankeyDiagram-5OEKKPKP-CAnwm5-J.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-3UESZ5HK-B4S3zZhB.js → sequenceDiagram-3UESZ5HK-C2KFLIUq.js} +1 -1
- package/dist-web/assets/{stateDiagram-AJRCARHV-ChScLWsf.js → stateDiagram-AJRCARHV-CqmhS5P6.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-C3Kw8ZDR.js +1 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-CFlA4lVE.js → timeline-definition-PNZ67QCA-DvPre9M_.js} +1 -1
- package/dist-web/assets/{vennDiagram-CIIHVFJN-DnolTal7.js → vennDiagram-CIIHVFJN-Top6i7KH.js} +1 -1
- package/dist-web/assets/{wardley-L42UT6IY-DqKa7n4S.js → wardley-L42UT6IY-_jVLsWqf.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-DsvDFtn8.js → wardleyDiagram-YWT4CUSO-BV4e5mP6.js} +1 -1
- package/dist-web/assets/{xychartDiagram-2RQKCTM6-CEJa0Ikx.js → xychartDiagram-2RQKCTM6-Bn1hw4Jk.js} +1 -1
- package/dist-web/index.html +1 -1
- package/package.json +1 -1
- package/templates/harness/skills/st-code-review/SKILL.md +47 -282
- package/templates/harness/skills/st-code-review/scripts/code-review.cjs +128 -345
- package/templates/harness/skills/st-execute-blueprint/SKILL.md +15 -18
- package/templates/harness/skills/st-full-workflow/SKILL.md +15 -18
- package/templates/strikethroo/config/hooks/CODE_REVIEW.md +19 -29
- package/dist-web/assets/channel-DMrO6cG1.js +0 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-BU-3r6Z2.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-BU-3r6Z2.js +0 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-YsYUZlN9.js +0 -1
|
@@ -34,20 +34,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
34
34
|
// src/skill-scripts/code-review.ts
|
|
35
35
|
var code_review_exports = {};
|
|
36
36
|
__export(code_review_exports, {
|
|
37
|
-
MAX_REVIEW_ROUNDS: () => MAX_REVIEW_ROUNDS,
|
|
38
|
-
_decideRound: () => _decideRound,
|
|
39
37
|
_exitCodeFor: () => _exitCodeFor,
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
_extractReviewDocument: () => _extractReviewDocument,
|
|
39
|
+
_makeDeliveryToken: () => _makeDeliveryToken,
|
|
42
40
|
_readBaseCommit: () => _readBaseCommit,
|
|
43
41
|
_readCumulativeDiff: () => _readCumulativeDiff,
|
|
44
|
-
|
|
42
|
+
_verdictFor: () => _verdictFor,
|
|
45
43
|
buildReviewerPrompt: () => buildReviewerPrompt,
|
|
46
44
|
createFindingsGate: () => createFindingsGate,
|
|
47
45
|
main: () => main,
|
|
48
|
-
|
|
49
|
-
runBoundedReviewRound: () => runBoundedReviewRound,
|
|
50
|
-
runReviewRound: () => runReviewRound
|
|
46
|
+
runReview: () => runReview
|
|
51
47
|
});
|
|
52
48
|
module.exports = __toCommonJS(code_review_exports);
|
|
53
49
|
var crypto = __toESM(require("crypto"));
|
|
@@ -730,61 +726,11 @@ var discoverHarnesses = async (request, overrides = {}) => {
|
|
|
730
726
|
|
|
731
727
|
// src/skill-scripts/shared/review-findings.ts
|
|
732
728
|
var import_child_process4 = require("child_process");
|
|
733
|
-
var
|
|
734
|
-
var
|
|
735
|
-
var MAX_REVIEW_ROUNDS = 3;
|
|
736
|
-
var DEFAULT_SEVERITY_FLOOR = "major";
|
|
737
|
-
var DEFAULT_CONFIDENCE_FLOOR = "high";
|
|
729
|
+
var SEVERITIES = ["critical", "major", "minor", "info"];
|
|
730
|
+
var CONFIDENCES = ["high", "medium", "low"];
|
|
738
731
|
var XMLLINT_TIMEOUT_MS = 3e4;
|
|
739
|
-
var isSeverity = (value) => value
|
|
740
|
-
var isConfidence = (value) => value
|
|
741
|
-
var SEVERITY_FLOOR_RE = /^[ \t]*#{0,6}[ \t]*severity floor[ \t]*:[ \t]*`?([a-z]+)`?/im;
|
|
742
|
-
var CONFIDENCE_FLOOR_RE = /^[ \t]*#{0,6}[ \t]*confidence floor[ \t]*:[ \t]*`?([a-z]+)`?/im;
|
|
743
|
-
var ROUND_BUDGET_RE = /^[ \t]*#{0,6}[ \t]*round budget[ \t]*:[ \t]*`?(-?\d{1,9})`?/im;
|
|
744
|
-
var parseReviewMandate = (hookContent) => {
|
|
745
|
-
const notes = [];
|
|
746
|
-
const severityMatch = SEVERITY_FLOOR_RE.exec(hookContent);
|
|
747
|
-
const statedSeverity = (severityMatch?.[1] ?? "").toLowerCase();
|
|
748
|
-
let severityFloor = DEFAULT_SEVERITY_FLOOR;
|
|
749
|
-
if (isSeverity(statedSeverity)) {
|
|
750
|
-
severityFloor = statedSeverity;
|
|
751
|
-
} else {
|
|
752
|
-
notes.push(
|
|
753
|
-
`The hook states no recognised severity floor, so the compiled default \`${DEFAULT_SEVERITY_FLOOR}\` applies.`
|
|
754
|
-
);
|
|
755
|
-
}
|
|
756
|
-
const confidenceMatch = CONFIDENCE_FLOOR_RE.exec(hookContent);
|
|
757
|
-
const statedConfidence = (confidenceMatch?.[1] ?? "").toLowerCase();
|
|
758
|
-
let confidenceFloor = DEFAULT_CONFIDENCE_FLOOR;
|
|
759
|
-
if (isConfidence(statedConfidence)) {
|
|
760
|
-
confidenceFloor = statedConfidence;
|
|
761
|
-
} else {
|
|
762
|
-
notes.push(
|
|
763
|
-
`The hook states no recognised confidence floor, so the compiled default \`${DEFAULT_CONFIDENCE_FLOOR}\` applies.`
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
const budgetMatch = ROUND_BUDGET_RE.exec(hookContent);
|
|
767
|
-
let roundBudget = MAX_REVIEW_ROUNDS;
|
|
768
|
-
if (budgetMatch === null) {
|
|
769
|
-
notes.push(
|
|
770
|
-
`The hook states no round budget, so the compiled ceiling of ${MAX_REVIEW_ROUNDS} rounds applies.`
|
|
771
|
-
);
|
|
772
|
-
} else {
|
|
773
|
-
const stated = Number(budgetMatch[1]);
|
|
774
|
-
if (!Number.isInteger(stated) || stated < 1) {
|
|
775
|
-
notes.push(
|
|
776
|
-
`The hook states a round budget of "${budgetMatch[1]}", which is not a positive whole number of rounds, so the compiled ceiling of ${MAX_REVIEW_ROUNDS} applies.`
|
|
777
|
-
);
|
|
778
|
-
} else if (stated > MAX_REVIEW_ROUNDS) {
|
|
779
|
-
notes.push(
|
|
780
|
-
`The hook states a round budget of ${stated}, above the compiled ceiling of ${MAX_REVIEW_ROUNDS}. The ceiling is enforced in code and cannot be raised by editing the hook, so ${MAX_REVIEW_ROUNDS} rounds apply.`
|
|
781
|
-
);
|
|
782
|
-
} else {
|
|
783
|
-
roundBudget = stated;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
return { severityFloor, confidenceFloor, roundBudget, notes };
|
|
787
|
-
};
|
|
732
|
+
var isSeverity = (value) => SEVERITIES.includes(value);
|
|
733
|
+
var isConfidence = (value) => CONFIDENCES.includes(value);
|
|
788
734
|
var validateAgainstSchema = (xsdFile, xmlFile, timeoutMs = XMLLINT_TIMEOUT_MS) => new Promise((resolve3) => {
|
|
789
735
|
let settled = false;
|
|
790
736
|
let diagnostics = "";
|
|
@@ -943,12 +889,11 @@ var parseReviewFindings = (xml) => {
|
|
|
943
889
|
comment = {
|
|
944
890
|
file: file ?? "",
|
|
945
891
|
location: lineRange(attributes),
|
|
946
|
-
// Absent, empty, and unrecognised all become null
|
|
947
|
-
//
|
|
892
|
+
// Absent, empty, and unrecognised all become null. The label is
|
|
893
|
+
// advisory, so an unreadable one is dropped rather than guessed at.
|
|
948
894
|
severity: isSeverity(severity) ? severity : null,
|
|
949
895
|
confidence: isConfidence(confidence) ? confidence : null,
|
|
950
896
|
category: null,
|
|
951
|
-
hasSuggestion: false,
|
|
952
897
|
summary: ""
|
|
953
898
|
};
|
|
954
899
|
capture = null;
|
|
@@ -965,55 +910,24 @@ var parseReviewFindings = (xml) => {
|
|
|
965
910
|
} else if (name === "category") {
|
|
966
911
|
capture = "category";
|
|
967
912
|
buffer = "";
|
|
968
|
-
} else if (name === "suggestion") {
|
|
969
|
-
comment.hasSuggestion = true;
|
|
970
913
|
}
|
|
971
914
|
}
|
|
972
915
|
return findings;
|
|
973
916
|
};
|
|
974
|
-
var
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
917
|
+
var countFindings = (findings) => {
|
|
918
|
+
const counts = {
|
|
919
|
+
total: findings.length,
|
|
920
|
+
critical: 0,
|
|
921
|
+
major: 0,
|
|
922
|
+
minor: 0,
|
|
923
|
+
info: 0,
|
|
924
|
+
unlabelled: 0
|
|
925
|
+
};
|
|
979
926
|
for (const finding of findings) {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
reasons.push("severity-absent");
|
|
983
|
-
} else if (SEVERITY_RANK[finding.severity] < SEVERITY_RANK[severityFloor]) {
|
|
984
|
-
reasons.push("severity-below-floor");
|
|
985
|
-
}
|
|
986
|
-
if (finding.confidence === null) {
|
|
987
|
-
reasons.push("confidence-absent");
|
|
988
|
-
} else if (CONFIDENCE_RANK[finding.confidence] < CONFIDENCE_RANK[confidenceFloor]) {
|
|
989
|
-
reasons.push("confidence-below-floor");
|
|
990
|
-
}
|
|
991
|
-
const clearsFloors = reasons.length === 0;
|
|
992
|
-
if (clearsFloors) aboveFloor += 1;
|
|
993
|
-
if (!finding.hasSuggestion) {
|
|
994
|
-
reasons.push("no-suggestion");
|
|
995
|
-
if (clearsFloors) aboveFloorWithoutSuggestion += 1;
|
|
996
|
-
}
|
|
997
|
-
if (reasons.length === 0) {
|
|
998
|
-
actionable.push(finding);
|
|
999
|
-
} else {
|
|
1000
|
-
recorded.push({ ...finding, reasons });
|
|
1001
|
-
}
|
|
927
|
+
if (finding.severity === null) counts.unlabelled += 1;
|
|
928
|
+
else counts[finding.severity] += 1;
|
|
1002
929
|
}
|
|
1003
|
-
return
|
|
1004
|
-
severityFloor,
|
|
1005
|
-
confidenceFloor,
|
|
1006
|
-
actionable,
|
|
1007
|
-
recorded,
|
|
1008
|
-
counts: {
|
|
1009
|
-
total: findings.length,
|
|
1010
|
-
aboveFloor,
|
|
1011
|
-
belowFloor: findings.length - aboveFloor,
|
|
1012
|
-
actionable: actionable.length,
|
|
1013
|
-
recorded: recorded.length,
|
|
1014
|
-
aboveFloorWithoutSuggestion
|
|
1015
|
-
}
|
|
1016
|
-
};
|
|
930
|
+
return counts;
|
|
1017
931
|
};
|
|
1018
932
|
|
|
1019
933
|
// src/skill-scripts/code-review.ts
|
|
@@ -1023,6 +937,7 @@ var REVIEW_DIR_NAME = "review";
|
|
|
1023
937
|
var BASE_COMMIT_FILE_NAME = "base-commit.json";
|
|
1024
938
|
var REVIEW_FILE_NAME = "review.xml";
|
|
1025
939
|
var FINDINGS_FILE_NAME = "findings.json";
|
|
940
|
+
var TRANSCRIPT_FILE_NAME = "reviewer-output.txt";
|
|
1026
941
|
var SHA_RE = /^[0-9a-f]{40}$/i;
|
|
1027
942
|
var errorMessage2 = (error) => error instanceof Error ? error.message : String(error);
|
|
1028
943
|
var readFileOrNull = (filePath) => {
|
|
@@ -1032,47 +947,47 @@ var readFileOrNull = (filePath) => {
|
|
|
1032
947
|
return null;
|
|
1033
948
|
}
|
|
1034
949
|
};
|
|
1035
|
-
var
|
|
1036
|
-
|
|
1037
|
-
|
|
950
|
+
var writeTranscript = (reviewDir, transcript) => {
|
|
951
|
+
if (transcript === void 0 || transcript === "") return;
|
|
952
|
+
try {
|
|
953
|
+
fs6.mkdirSync(reviewDir, { recursive: true });
|
|
954
|
+
fs6.writeFileSync(path6.join(reviewDir, TRANSCRIPT_FILE_NAME), transcript, "utf8");
|
|
955
|
+
} catch {
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
var createFindingsGate = () => async (context) => {
|
|
959
|
+
const reviewDir = path6.dirname(context.reviewFile);
|
|
960
|
+
const findingsFile = path6.join(reviewDir, FINDINGS_FILE_NAME);
|
|
1038
961
|
const record = (payload) => {
|
|
1039
962
|
try {
|
|
1040
|
-
fs6.mkdirSync(
|
|
963
|
+
fs6.mkdirSync(reviewDir, { recursive: true });
|
|
1041
964
|
fs6.writeFileSync(findingsFile, `${JSON.stringify(payload, null, 2)}
|
|
1042
965
|
`, "utf8");
|
|
1043
966
|
} catch (error) {
|
|
1044
967
|
throw new Error(
|
|
1045
|
-
`The review
|
|
968
|
+
`The review findings could not be written to ${findingsFile}: ${errorMessage2(error)}`
|
|
1046
969
|
);
|
|
1047
970
|
}
|
|
1048
971
|
};
|
|
1049
|
-
const base = {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
return { kind: "findings-absent", detail };
|
|
1062
|
-
}
|
|
1063
|
-
try {
|
|
1064
|
-
fs6.mkdirSync(roundDir, { recursive: true });
|
|
1065
|
-
fs6.writeFileSync(
|
|
1066
|
-
context.reviewFile,
|
|
1067
|
-
recovered.endsWith("\n") ? recovered : `${recovered}
|
|
972
|
+
const base = { reviewFile: context.reviewFile, xsdFile: context.xsdFile };
|
|
973
|
+
const delivered = _extractReviewDocument(context.reviewerStdout, context.deliveryToken);
|
|
974
|
+
if (delivered === null) {
|
|
975
|
+
const detail = "The reviewer printed no complete findings document between this dispatch's delimiters. A round with no findings document cannot be read as a round with no findings.";
|
|
976
|
+
record({ ...base, status: "findings-absent", detail, findings: [] });
|
|
977
|
+
return { kind: "findings-absent", detail };
|
|
978
|
+
}
|
|
979
|
+
try {
|
|
980
|
+
fs6.mkdirSync(reviewDir, { recursive: true });
|
|
981
|
+
fs6.writeFileSync(
|
|
982
|
+
context.reviewFile,
|
|
983
|
+
delivered.endsWith("\n") ? delivered : `${delivered}
|
|
1068
984
|
`,
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
985
|
+
"utf8"
|
|
986
|
+
);
|
|
987
|
+
} catch (error) {
|
|
988
|
+
throw new Error(
|
|
989
|
+
`The delivered findings document could not be written to ${context.reviewFile}: ` + errorMessage2(error)
|
|
990
|
+
);
|
|
1076
991
|
}
|
|
1077
992
|
const validation = await validateAgainstSchema(context.xsdFile, context.reviewFile);
|
|
1078
993
|
if (validation.kind === "validator-unavailable") {
|
|
@@ -1080,46 +995,25 @@ var createFindingsGate = (mandate) => async (context) => {
|
|
|
1080
995
|
...base,
|
|
1081
996
|
status: "validator-unavailable",
|
|
1082
997
|
detail: validation.detail,
|
|
1083
|
-
|
|
1084
|
-
recorded: []
|
|
998
|
+
findings: []
|
|
1085
999
|
});
|
|
1086
1000
|
return { kind: "validator-unavailable", detail: validation.detail };
|
|
1087
1001
|
}
|
|
1088
1002
|
if (validation.kind === "invalid") {
|
|
1089
|
-
const detail = `${context.reviewFile} does not validate against ${context.xsdFile}, so its findings
|
|
1090
|
-
record({ ...base, status: "schema-invalid", detail,
|
|
1003
|
+
const detail = `${context.reviewFile} does not validate against ${context.xsdFile}, so its findings could not be certified and none of them was recorded. xmllint reported: ${validation.detail}`;
|
|
1004
|
+
record({ ...base, status: "schema-invalid", detail, findings: [] });
|
|
1091
1005
|
return { kind: "schema-invalid", detail };
|
|
1092
1006
|
}
|
|
1093
1007
|
const xml = readFileOrNull(context.reviewFile);
|
|
1094
1008
|
if (xml === null) {
|
|
1095
1009
|
const detail = `${context.reviewFile} validated but could not then be read.`;
|
|
1096
|
-
record({ ...base, status: "findings-absent", detail,
|
|
1010
|
+
record({ ...base, status: "findings-absent", detail, findings: [] });
|
|
1097
1011
|
return { kind: "findings-absent", detail };
|
|
1098
1012
|
}
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
);
|
|
1104
|
-
record({
|
|
1105
|
-
...base,
|
|
1106
|
-
status: "evaluated",
|
|
1107
|
-
counts: partition.counts,
|
|
1108
|
-
actionable: partition.actionable,
|
|
1109
|
-
recorded: partition.recorded
|
|
1110
|
-
});
|
|
1111
|
-
return {
|
|
1112
|
-
kind: "evaluated",
|
|
1113
|
-
aboveFloor: partition.counts.aboveFloor,
|
|
1114
|
-
belowFloor: partition.counts.belowFloor,
|
|
1115
|
-
actionable: partition.counts.actionable,
|
|
1116
|
-
recorded: partition.counts.recorded,
|
|
1117
|
-
total: partition.counts.total,
|
|
1118
|
-
aboveFloorWithoutSuggestion: partition.counts.aboveFloorWithoutSuggestion,
|
|
1119
|
-
severityFloor: mandate.severityFloor,
|
|
1120
|
-
confidenceFloor: mandate.confidenceFloor,
|
|
1121
|
-
findingsFile
|
|
1122
|
-
};
|
|
1013
|
+
const findings = parseReviewFindings(xml);
|
|
1014
|
+
const counts = countFindings(findings);
|
|
1015
|
+
record({ ...base, status: "evaluated", counts, findings });
|
|
1016
|
+
return { kind: "evaluated", counts, findingsFile };
|
|
1123
1017
|
};
|
|
1124
1018
|
var _readBaseCommit = (filePath) => {
|
|
1125
1019
|
const raw = readFileOrNull(filePath);
|
|
@@ -1191,27 +1085,14 @@ var readReviewerSkill = () => {
|
|
|
1191
1085
|
const content = readFileOrNull(skillFile);
|
|
1192
1086
|
return content === null ? "Load the `st-code-review` skill and follow its Operating Procedure. If that skill is not installed on this harness, follow the mandate below exactly." : content;
|
|
1193
1087
|
};
|
|
1194
|
-
var
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
}
|
|
1198
|
-
return findings.map((finding) => {
|
|
1199
|
-
const attributes = [
|
|
1200
|
-
finding.severity === void 0 ? null : `severity=${finding.severity}`,
|
|
1201
|
-
finding.confidence === void 0 ? null : `confidence=${finding.confidence}`
|
|
1202
|
-
].filter((part) => part !== null).join(" ");
|
|
1203
|
-
const where = finding.location === void 0 ? finding.file : `${finding.file}:${finding.location}`;
|
|
1204
|
-
return `- [${finding.disposition}] ${where}${attributes ? ` (${attributes})` : ""} \u2014 ${finding.summary}`;
|
|
1205
|
-
}).join("\n");
|
|
1206
|
-
};
|
|
1207
|
-
var _makeFallbackToken = () => crypto.randomBytes(6).toString("hex");
|
|
1208
|
-
var fallbackBeginMarker = (token) => `<<<BEGIN REVIEW XML ${token}>>>`;
|
|
1209
|
-
var fallbackEndMarker = (token) => `<<<END REVIEW XML ${token}>>>`;
|
|
1088
|
+
var _makeDeliveryToken = () => crypto.randomBytes(6).toString("hex");
|
|
1089
|
+
var beginMarker = (token) => `<<<BEGIN REVIEW XML ${token}>>>`;
|
|
1090
|
+
var endMarker = (token) => `<<<END REVIEW XML ${token}>>>`;
|
|
1210
1091
|
var ANSI_PATTERN = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
1211
|
-
var
|
|
1092
|
+
var _extractReviewDocument = (stdout, token) => {
|
|
1212
1093
|
const clean = stdout.replace(ANSI_PATTERN, "");
|
|
1213
|
-
const begin =
|
|
1214
|
-
const end =
|
|
1094
|
+
const begin = beginMarker(token);
|
|
1095
|
+
const end = endMarker(token);
|
|
1215
1096
|
let searchFrom = clean.length;
|
|
1216
1097
|
while (searchFrom >= 0) {
|
|
1217
1098
|
const endIndex = clean.lastIndexOf(end, searchFrom);
|
|
@@ -1225,12 +1106,12 @@ var _extractFallbackXml = (stdout, token) => {
|
|
|
1225
1106
|
return null;
|
|
1226
1107
|
};
|
|
1227
1108
|
var buildReviewerPrompt = (input) => [
|
|
1228
|
-
`Strikethroo code review gate \u2014 Plan ${input.planId}
|
|
1109
|
+
`Strikethroo code review gate \u2014 Plan ${input.planId}.`,
|
|
1229
1110
|
"",
|
|
1230
1111
|
"You are the independent reviewer, running on a different harness than the one",
|
|
1231
1112
|
"that wrote this code. You detect; you never fix. Do not edit, create, or delete",
|
|
1232
1113
|
"source files. Do not run formatters. Do not commit. Your entire output is one",
|
|
1233
|
-
"
|
|
1114
|
+
"findings document, printed as described below, plus a short report of the counts.",
|
|
1234
1115
|
"",
|
|
1235
1116
|
`Repository / workspace root: ${input.workspace}`,
|
|
1236
1117
|
`Strikethroo workspace root: ${input.strikethrooRoot}`,
|
|
@@ -1238,29 +1119,28 @@ var buildReviewerPrompt = (input) => [
|
|
|
1238
1119
|
`Review mandate hook: ${input.hookFile}`,
|
|
1239
1120
|
`Findings schema to validate against: ${input.xsdFile}`,
|
|
1240
1121
|
`Base commit anchoring this plan's scope: ${input.baseCommit}`,
|
|
1241
|
-
`Round: ${input.round}`,
|
|
1242
|
-
`Write your findings to: ${input.reviewFile}`,
|
|
1243
1122
|
"",
|
|
1244
|
-
"##
|
|
1123
|
+
"## How to deliver your findings",
|
|
1245
1124
|
"",
|
|
1246
|
-
"
|
|
1247
|
-
"
|
|
1248
|
-
"findings document as the final thing you print, between these exact lines:",
|
|
1125
|
+
"Print the complete findings document as the final thing you print, between these",
|
|
1126
|
+
"exact lines:",
|
|
1249
1127
|
"",
|
|
1250
|
-
|
|
1128
|
+
beginMarker(input.deliveryToken),
|
|
1251
1129
|
// The placeholder deliberately does not begin with `<?xml` or `<review`.
|
|
1252
|
-
// `
|
|
1130
|
+
// `_extractReviewDocument` rejects a region on exactly that test, which is what
|
|
1253
1131
|
// stops a reviewer that echoes these instructions back from being read as a
|
|
1254
|
-
//
|
|
1132
|
+
// delivered document. A placeholder shaped like a real document would defeat
|
|
1255
1133
|
// it — keep this line prose, here and in any mirror of it.
|
|
1256
1134
|
"... the complete findings document, beginning with its XML declaration ...",
|
|
1257
|
-
|
|
1135
|
+
endMarker(input.deliveryToken),
|
|
1258
1136
|
"",
|
|
1259
|
-
"
|
|
1260
|
-
"
|
|
1261
|
-
"
|
|
1262
|
-
"
|
|
1263
|
-
"
|
|
1137
|
+
"Copy those two lines from this dispatch; never invent a token. Print nothing after",
|
|
1138
|
+
"the closing line. Do not write the document to a file \u2014 this channel is the only",
|
|
1139
|
+
"one that is read. The document is validated against the schema named above, so an",
|
|
1140
|
+
"incomplete or invented document fails the review. Being unable to read the",
|
|
1141
|
+
"repository is not a reason to emit this block: a review you could not perform is a",
|
|
1142
|
+
"failed review, and emitting well-formed XML instead of reporting that failure is a",
|
|
1143
|
+
"worse outcome than the failure.",
|
|
1264
1144
|
"",
|
|
1265
1145
|
"## Review mandate (authoritative \u2014 it overrides the reviewer instructions below)",
|
|
1266
1146
|
"",
|
|
@@ -1270,10 +1150,6 @@ var buildReviewerPrompt = (input) => [
|
|
|
1270
1150
|
"",
|
|
1271
1151
|
input.skillInstructions.trim(),
|
|
1272
1152
|
"",
|
|
1273
|
-
"## Prior adjudicated findings \u2014 do not re-litigate these",
|
|
1274
|
-
"",
|
|
1275
|
-
renderAdjudicated(input.adjudicatedFindings),
|
|
1276
|
-
"",
|
|
1277
1153
|
"## Cumulative diff",
|
|
1278
1154
|
"",
|
|
1279
1155
|
`Produced with \`git diff ${input.baseCommit} --\` in ${input.workspace}: the recorded`,
|
|
@@ -1285,48 +1161,6 @@ ${input.diff}
|
|
|
1285
1161
|
<<<END CUMULATIVE DIFF>>>`,
|
|
1286
1162
|
""
|
|
1287
1163
|
].join("\n");
|
|
1288
|
-
var asAdjudicated = (value, disposition) => {
|
|
1289
|
-
if (!Array.isArray(value)) return [];
|
|
1290
|
-
const adjudicated = [];
|
|
1291
|
-
for (const entry of value) {
|
|
1292
|
-
if (entry === null || typeof entry !== "object") continue;
|
|
1293
|
-
const finding = entry;
|
|
1294
|
-
if (typeof finding["file"] !== "string") continue;
|
|
1295
|
-
adjudicated.push({
|
|
1296
|
-
file: finding["file"],
|
|
1297
|
-
...typeof finding["location"] === "string" ? { location: finding["location"] } : {},
|
|
1298
|
-
...typeof finding["severity"] === "string" ? { severity: finding["severity"] } : {},
|
|
1299
|
-
...typeof finding["confidence"] === "string" ? { confidence: finding["confidence"] } : {},
|
|
1300
|
-
summary: typeof finding["summary"] === "string" ? finding["summary"] : "",
|
|
1301
|
-
disposition
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
return adjudicated;
|
|
1305
|
-
};
|
|
1306
|
-
var _readPriorAdjudicated = (planDir, round) => {
|
|
1307
|
-
const carried = /* @__PURE__ */ new Map();
|
|
1308
|
-
for (let prior = 1; prior < round; prior += 1) {
|
|
1309
|
-
const raw = readFileOrNull(
|
|
1310
|
-
path6.join(planDir, REVIEW_DIR_NAME, `round-${prior}`, FINDINGS_FILE_NAME)
|
|
1311
|
-
);
|
|
1312
|
-
if (raw === null) continue;
|
|
1313
|
-
try {
|
|
1314
|
-
const parsed = JSON.parse(raw);
|
|
1315
|
-
if (parsed === null || typeof parsed !== "object") continue;
|
|
1316
|
-
const partition = parsed;
|
|
1317
|
-
const roundFindings = [
|
|
1318
|
-
...asAdjudicated(partition["actionable"], "applied"),
|
|
1319
|
-
...asAdjudicated(partition["recorded"], "recorded-below-floor")
|
|
1320
|
-
];
|
|
1321
|
-
for (const finding of roundFindings) {
|
|
1322
|
-
carried.set(`${finding.file}|${finding.location ?? ""}|${finding.summary}`, finding);
|
|
1323
|
-
}
|
|
1324
|
-
} catch {
|
|
1325
|
-
continue;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
return [...carried.values()];
|
|
1329
|
-
};
|
|
1330
1164
|
var skip = (reason, detail) => ({
|
|
1331
1165
|
kind: "skipped",
|
|
1332
1166
|
reason,
|
|
@@ -1389,17 +1223,16 @@ var resolveReviewContext = (startPath, validatorAvailable = () => executableOnPa
|
|
|
1389
1223
|
workspace: path6.dirname(path6.dirname(strikethrooRoot)),
|
|
1390
1224
|
hookFile,
|
|
1391
1225
|
hookContent,
|
|
1392
|
-
xsdFile
|
|
1393
|
-
mandate: parseReviewMandate(hookContent)
|
|
1226
|
+
xsdFile
|
|
1394
1227
|
}
|
|
1395
1228
|
};
|
|
1396
1229
|
};
|
|
1397
|
-
var
|
|
1230
|
+
var runReview = async (request, overrides = {}) => {
|
|
1398
1231
|
const dependencies2 = { ...defaultDependencies2, ...overrides };
|
|
1399
1232
|
const startPath = request.startPath ?? process.cwd();
|
|
1400
1233
|
const resolution = resolveReviewContext(startPath, dependencies2.validatorAvailable);
|
|
1401
1234
|
if (resolution.kind === "ended") return resolution.result;
|
|
1402
|
-
const { strikethrooRoot, workspace, hookFile, hookContent, xsdFile
|
|
1235
|
+
const { strikethrooRoot, workspace, hookFile, hookContent, xsdFile } = resolution.context;
|
|
1403
1236
|
const resolved = resolvePlan(request.plan, startPath);
|
|
1404
1237
|
if (!resolved) {
|
|
1405
1238
|
return {
|
|
@@ -1441,25 +1274,28 @@ var runReviewRound = async (request, overrides = {}) => {
|
|
|
1441
1274
|
`The diff from ${baseCommit} to the working tree in ${workspace} is empty, so there was nothing to review. No reviewer was dispatched, and no round was certified.`
|
|
1442
1275
|
);
|
|
1443
1276
|
}
|
|
1444
|
-
const
|
|
1445
|
-
const reviewFile = path6.join(
|
|
1277
|
+
const reviewDir = path6.join(planDir, REVIEW_DIR_NAME);
|
|
1278
|
+
const reviewFile = path6.join(reviewDir, REVIEW_FILE_NAME);
|
|
1446
1279
|
try {
|
|
1447
|
-
fs6.mkdirSync(
|
|
1280
|
+
fs6.mkdirSync(reviewDir, { recursive: true });
|
|
1448
1281
|
} catch (error) {
|
|
1449
1282
|
return {
|
|
1450
1283
|
kind: "infrastructure-failure",
|
|
1451
|
-
detail: `Could not create the review
|
|
1284
|
+
detail: `Could not create the review directory ${reviewDir}: ${errorMessage2(error)}`
|
|
1452
1285
|
};
|
|
1453
1286
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1287
|
+
const staleArtifacts = [reviewFile, path6.join(reviewDir, TRANSCRIPT_FILE_NAME)];
|
|
1288
|
+
for (const stale of staleArtifacts) {
|
|
1289
|
+
try {
|
|
1290
|
+
fs6.rmSync(stale, { force: true });
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
return {
|
|
1293
|
+
kind: "infrastructure-failure",
|
|
1294
|
+
detail: `Could not remove the stale review artifact ${stale}: ${errorMessage2(error)}`
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1461
1297
|
}
|
|
1462
|
-
const
|
|
1298
|
+
const deliveryToken = _makeDeliveryToken();
|
|
1463
1299
|
const prompt = buildReviewerPrompt({
|
|
1464
1300
|
planId,
|
|
1465
1301
|
planFile,
|
|
@@ -1469,12 +1305,9 @@ var runReviewRound = async (request, overrides = {}) => {
|
|
|
1469
1305
|
hookContent,
|
|
1470
1306
|
xsdFile,
|
|
1471
1307
|
baseCommit,
|
|
1472
|
-
round: request.round,
|
|
1473
|
-
reviewFile,
|
|
1474
1308
|
diff,
|
|
1475
|
-
adjudicatedFindings: request.adjudicatedFindings ?? _readPriorAdjudicated(planDir, request.round),
|
|
1476
1309
|
skillInstructions: readReviewerSkill(),
|
|
1477
|
-
|
|
1310
|
+
deliveryToken
|
|
1478
1311
|
});
|
|
1479
1312
|
const dispatched = await dependencies2.dispatch({ harness, workspace, prompt });
|
|
1480
1313
|
if (dispatched.kind === "infrastructure-failure") {
|
|
@@ -1484,91 +1317,56 @@ var runReviewRound = async (request, overrides = {}) => {
|
|
|
1484
1317
|
return {
|
|
1485
1318
|
kind: "fallback",
|
|
1486
1319
|
harness,
|
|
1487
|
-
round: request.round,
|
|
1488
1320
|
reason: dispatched.reason,
|
|
1489
1321
|
detail: dispatched.detail
|
|
1490
1322
|
};
|
|
1491
1323
|
}
|
|
1492
1324
|
if (dispatched.kind === "launched-failure") {
|
|
1325
|
+
writeTranscript(reviewDir, dispatched.stdout);
|
|
1493
1326
|
return {
|
|
1494
1327
|
kind: "launched-failure",
|
|
1495
1328
|
harness,
|
|
1496
|
-
round: request.round,
|
|
1497
1329
|
reviewFile,
|
|
1498
1330
|
exitCode: dispatched.exitCode,
|
|
1499
1331
|
detail: `The ${harness} reviewer exited ${dispatched.exitCode}.`
|
|
1500
1332
|
};
|
|
1501
1333
|
}
|
|
1502
|
-
const evaluate = dependencies2.evaluateFindings ?? createFindingsGate(
|
|
1334
|
+
const evaluate = dependencies2.evaluateFindings ?? createFindingsGate();
|
|
1503
1335
|
const findingsGate = await evaluate({
|
|
1504
1336
|
reviewFile,
|
|
1505
1337
|
xsdFile,
|
|
1506
1338
|
planDir,
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
fallbackToken
|
|
1339
|
+
reviewerStdout: dispatched.stdout ?? "",
|
|
1340
|
+
deliveryToken
|
|
1510
1341
|
});
|
|
1342
|
+
if (findingsGate.kind !== "evaluated") {
|
|
1343
|
+
writeTranscript(reviewDir, dispatched.stdout);
|
|
1344
|
+
}
|
|
1511
1345
|
return {
|
|
1512
1346
|
kind: "reviewed",
|
|
1513
1347
|
harness,
|
|
1514
|
-
round: request.round,
|
|
1515
1348
|
baseCommit,
|
|
1516
1349
|
reviewFile,
|
|
1517
1350
|
reviewFilePresent: fs6.existsSync(reviewFile),
|
|
1351
|
+
verdict: _verdictFor(findingsGate),
|
|
1518
1352
|
findingsGate
|
|
1519
1353
|
};
|
|
1520
1354
|
};
|
|
1521
|
-
var
|
|
1355
|
+
var _verdictFor = (outcome) => {
|
|
1522
1356
|
if (outcome.kind !== "evaluated") {
|
|
1523
|
-
return { kind: "
|
|
1524
|
-
}
|
|
1525
|
-
const recorded = `${outcome.recorded} finding(s) were recorded without being applied, of which ${outcome.aboveFloorWithoutSuggestion} cleared both floors but carried no local fix. See ${outcome.findingsFile}.`;
|
|
1526
|
-
if (outcome.actionable === 0) {
|
|
1527
|
-
return {
|
|
1528
|
-
kind: "gate-passed",
|
|
1529
|
-
detail: `No finding cleared the \`${outcome.severityFloor}\` severity floor and the \`${outcome.confidenceFloor}\` confidence floor with a local fix attached, so the review gate passed on round ${round}. ${recorded}`
|
|
1530
|
-
};
|
|
1531
|
-
}
|
|
1532
|
-
if (round >= roundBudget) {
|
|
1533
|
-
return {
|
|
1534
|
-
kind: "budget-exhausted",
|
|
1535
|
-
actionable: outcome.actionable,
|
|
1536
|
-
detail: `Round ${round} of an enforced ${roundBudget}-round budget still reports ${outcome.actionable} actionable finding(s), so the review gate halts with the budget exhausted. The plan stays in \`plans/\` and every round's findings are recorded under its \`review/\` directory. ${recorded}`
|
|
1537
|
-
};
|
|
1357
|
+
return { kind: "review-failed", detail: outcome.detail };
|
|
1538
1358
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
nextRound: round + 1,
|
|
1542
|
-
actionable: outcome.actionable,
|
|
1543
|
-
detail: `${outcome.actionable} finding(s) clear both floors and carry a local fix. Dispatch them on the implementer route, re-run POST_EXECUTION in full, then run round ${round + 1} of ${roundBudget}. ${recorded}`
|
|
1544
|
-
};
|
|
1545
|
-
};
|
|
1546
|
-
var runBoundedReviewRound = async (request, overrides = {}) => {
|
|
1547
|
-
const startPath = request.startPath ?? process.cwd();
|
|
1548
|
-
const resolution = resolveReviewContext(
|
|
1549
|
-
startPath,
|
|
1550
|
-
{ ...defaultDependencies2, ...overrides }.validatorAvailable
|
|
1551
|
-
);
|
|
1552
|
-
if (resolution.kind === "ended") return resolution.result;
|
|
1553
|
-
const { mandate } = resolution.context;
|
|
1554
|
-
if (request.round > mandate.roundBudget) {
|
|
1359
|
+
const { counts, findingsFile } = outcome;
|
|
1360
|
+
if (counts.total === 0) {
|
|
1555
1361
|
return {
|
|
1556
|
-
kind: "
|
|
1557
|
-
|
|
1558
|
-
roundBudget: mandate.roundBudget,
|
|
1559
|
-
roundCeiling: MAX_REVIEW_ROUNDS,
|
|
1560
|
-
mandateNotes: mandate.notes,
|
|
1561
|
-
detail: `Round ${request.round} was requested, but the review gate enforces a ${mandate.roundBudget}-round budget (compiled ceiling ${MAX_REVIEW_ROUNDS}). No reviewer was dispatched. The plan stays in \`plans/\` and the rounds already run are recorded under its \`review/\` directory.`
|
|
1362
|
+
kind: "review-recorded",
|
|
1363
|
+
detail: `The reviewer raised no findings. See ${findingsFile}.`
|
|
1562
1364
|
};
|
|
1563
1365
|
}
|
|
1564
|
-
const
|
|
1565
|
-
if (result.kind !== "reviewed") return result;
|
|
1366
|
+
const byLabel = SEVERITIES.filter((label) => counts[label] > 0).map((label) => `${counts[label]} ${label}`).concat(counts.unlabelled > 0 ? [`${counts.unlabelled} unlabelled`] : []).join(", ");
|
|
1566
1367
|
return {
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
roundBudget: mandate.roundBudget,
|
|
1570
|
-
roundCeiling: MAX_REVIEW_ROUNDS,
|
|
1571
|
-
mandateNotes: mandate.notes
|
|
1368
|
+
kind: "review-recorded",
|
|
1369
|
+
detail: `The reviewer raised ${counts.total} finding(s) (${byLabel}). They are recorded, not applied: read them and decide which to act on. See ${findingsFile}.`
|
|
1572
1370
|
};
|
|
1573
1371
|
};
|
|
1574
1372
|
var emit = (result, exitCode) => {
|
|
@@ -1579,34 +1377,23 @@ var emit = (result, exitCode) => {
|
|
|
1579
1377
|
var _exitCodeFor = (result) => {
|
|
1580
1378
|
if (result.kind === "infrastructure-failure") return 2;
|
|
1581
1379
|
if (result.kind === "launched-failure") return 1;
|
|
1582
|
-
if (result.kind === "
|
|
1583
|
-
if (result.kind === "reviewed" && result.decision !== void 0) {
|
|
1584
|
-
return result.decision.kind === "budget-exhausted" || result.decision.kind === "round-failed" ? 1 : 0;
|
|
1585
|
-
}
|
|
1380
|
+
if (result.kind === "reviewed") return result.verdict.kind === "review-failed" ? 1 : 0;
|
|
1586
1381
|
return 0;
|
|
1587
1382
|
};
|
|
1588
1383
|
var main = async (startPath = process.cwd()) => {
|
|
1589
|
-
const [planArg, harnessArg
|
|
1384
|
+
const [planArg, harnessArg] = process.argv.slice(2);
|
|
1590
1385
|
if (!planArg || !harnessArg || !SUPPORTED_HARNESSES.includes(harnessArg)) {
|
|
1591
1386
|
emit(
|
|
1592
1387
|
{
|
|
1593
1388
|
kind: "infrastructure-failure",
|
|
1594
|
-
detail: `Usage: code-review.cjs <plan-id-or-path> <current-harness
|
|
1389
|
+
detail: `Usage: code-review.cjs <plan-id-or-path> <current-harness>. <current-harness> is one of: ${SUPPORTED_HARNESSES.join(", ")}.`
|
|
1595
1390
|
},
|
|
1596
1391
|
2
|
|
1597
1392
|
);
|
|
1598
1393
|
}
|
|
1599
|
-
const
|
|
1600
|
-
if (!Number.isInteger(round) || round < 1) {
|
|
1601
|
-
emit(
|
|
1602
|
-
{ kind: "infrastructure-failure", detail: `Round "${roundArg}" is not a positive integer.` },
|
|
1603
|
-
2
|
|
1604
|
-
);
|
|
1605
|
-
}
|
|
1606
|
-
const result = await runBoundedReviewRound({
|
|
1394
|
+
const result = await runReview({
|
|
1607
1395
|
plan: planArg,
|
|
1608
1396
|
currentHarness: harnessArg,
|
|
1609
|
-
round,
|
|
1610
1397
|
startPath
|
|
1611
1398
|
});
|
|
1612
1399
|
emit(result, _exitCodeFor(result));
|
|
@@ -1624,18 +1411,14 @@ if (require.main === module) {
|
|
|
1624
1411
|
}
|
|
1625
1412
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1626
1413
|
0 && (module.exports = {
|
|
1627
|
-
MAX_REVIEW_ROUNDS,
|
|
1628
|
-
_decideRound,
|
|
1629
1414
|
_exitCodeFor,
|
|
1630
|
-
|
|
1631
|
-
|
|
1415
|
+
_extractReviewDocument,
|
|
1416
|
+
_makeDeliveryToken,
|
|
1632
1417
|
_readBaseCommit,
|
|
1633
1418
|
_readCumulativeDiff,
|
|
1634
|
-
|
|
1419
|
+
_verdictFor,
|
|
1635
1420
|
buildReviewerPrompt,
|
|
1636
1421
|
createFindingsGate,
|
|
1637
1422
|
main,
|
|
1638
|
-
|
|
1639
|
-
runBoundedReviewRound,
|
|
1640
|
-
runReviewRound
|
|
1423
|
+
runReview
|
|
1641
1424
|
});
|