@jphutchins/code-review 0.1.0-alpha.7 → 0.1.0-alpha.9
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/README.md +11 -9
- package/dist/index.js +133 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/schema/findings.schema.json +9 -9
- package/templates/comment.eta +5 -2
- package/templates/inline.eta +2 -2
package/README.md
CHANGED
|
@@ -25,8 +25,9 @@ the security boundary, and the cost.
|
|
|
25
25
|
## The CLI
|
|
26
26
|
|
|
27
27
|
The npm package is the **deterministic commenter** — the presentation and posting layer that no
|
|
28
|
-
model should do, plus the adapter glue between an agent CLI's native output and the
|
|
29
|
-
envelope
|
|
28
|
+
model should do, plus the adapter glue between an agent CLI's native output and the abstract result
|
|
29
|
+
envelope defined in [`src/schema.ts`](src/schema.ts) (the deliverable of
|
|
30
|
+
[SPEC §3.2](SPEC.md#32-the-deliverable)).
|
|
30
31
|
|
|
31
32
|
```sh
|
|
32
33
|
npm install -g @jphutchins/code-review
|
|
@@ -40,7 +41,7 @@ npx @jphutchins/code-review <subcommand>
|
|
|
40
41
|
| `gather` | Resolve the PR from the CI head SHA and gather the review inputs (diff with git-diff fallback, PR context, prior bot review, failing-job logs) into the workspace for the agent |
|
|
41
42
|
| `render` | Render the sticky-comment markdown from findings + usage + prices |
|
|
42
43
|
| `inline` | Build the GitHub reviews `comments[]` payload from findings + diff (in-diff validation; strays demote to the summary) |
|
|
43
|
-
| `adapt` | Map a native agent-CLI result envelope onto the abstract
|
|
44
|
+
| `adapt` | Map a native agent-CLI result envelope onto the abstract result envelope (`src/schema.ts`) |
|
|
44
45
|
| `extract` | Recover findings/triage JSON from a native envelope via the deterministic extraction ladder |
|
|
45
46
|
| `validate-patches` | Validate each finding's `patch` against the real PR-head file, aligning the finding's line range to it and keeping the patch (projected into a suggestion at render time), or dropping the patch |
|
|
46
47
|
| `cost` | Recompute USD cost from the envelope's per-model token counts + a price map |
|
|
@@ -63,16 +64,17 @@ in [templates/](templates/). See [docs/adapters.md](docs/adapters.md) for the ad
|
|
|
63
64
|
letting the CLI pick where your key gets sent.
|
|
64
65
|
3. Commit `.github/prices.json` (fork [schema/prices.example.json](schema/prices.example.json) and
|
|
65
66
|
fill in your provider's per-token rates) — without it the cost footer renders **$0**
|
|
66
|
-
([SPEC §
|
|
67
|
+
([SPEC §4.4](SPEC.md#44-required-controls-conformance)).
|
|
67
68
|
4. Merge to your default branch first — `workflow_run` only fires from the default branch, so the
|
|
68
69
|
introducing PR won't review itself — then open a test PR.
|
|
69
70
|
5. First run: consider `egress-policy: audit` to discover the real allowlist, then switch to `block`
|
|
70
|
-
([SPEC
|
|
71
|
+
([SPEC Appendix A](SPEC.md#appendix-a--reference-realization-github-actions-non-normative)).
|
|
71
72
|
|
|
72
73
|
Every model knob is committed step `env` on the workflow's triage and review steps — models,
|
|
73
74
|
efforts, the subagent model, and the tier aliases, right where each is consumed — edited and
|
|
74
|
-
PR-reviewed like the rest of the file (
|
|
75
|
-
|
|
75
|
+
PR-reviewed like the rest of the file (implementation detail the spec deliberately leaves to this
|
|
76
|
+
repo — see [SPEC Appendix A](SPEC.md#appendix-a--reference-realization-github-actions-non-normative)).
|
|
77
|
+
Only the backend endpoint is a per-repo **Actions variable** (`API_BASE_URL`, required, no default); pointing it at
|
|
76
78
|
another provider requires adding that provider's API host to the workflow's egress allowlist in the
|
|
77
79
|
same reviewed PR.
|
|
78
80
|
|
|
@@ -87,8 +89,8 @@ links to:
|
|
|
87
89
|
downstream tool) SHOULD base64-decode and parse that marker rather than parse the comment's prose.
|
|
88
90
|
Embedding in the comment (rather than only linking the artifact) keeps the pointer from expiring
|
|
89
91
|
with artifact retention; when the encoded findings are too large to embed, the sticky falls back to
|
|
90
|
-
a `<!-- code-review:findings-json <url> -->` link marker instead
|
|
91
|
-
|
|
92
|
+
a `<!-- code-review:findings-json <url> -->` link marker instead — the shared serializer is
|
|
93
|
+
[`src/surface.ts`](src/surface.ts).
|
|
92
94
|
- **`code-review-transcript`** — the full Claude Code session transcripts for the triage and review
|
|
93
95
|
phases. This is advisory/auditability only: it is never read by the comment job and never affects
|
|
94
96
|
what gets posted.
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,10 @@ var drop = (reason) => ({
|
|
|
89
89
|
kind: "drop",
|
|
90
90
|
reason
|
|
91
91
|
});
|
|
92
|
+
var keep = (reason) => ({
|
|
93
|
+
kind: "keep",
|
|
94
|
+
reason
|
|
95
|
+
});
|
|
92
96
|
var parseHunk = (patch) => {
|
|
93
97
|
const rawLines = patch.split("\n");
|
|
94
98
|
const lines = rawLines.length > 0 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
|
|
@@ -128,10 +132,12 @@ var validatePatch = (patch, fileLines) => {
|
|
|
128
132
|
const removedCount = body.filter((l) => l.kind === "removed").length;
|
|
129
133
|
const addedCount = body.filter((l) => l.kind === "added").length;
|
|
130
134
|
if (removedCount === 0 && addedCount === 0) return drop("hunk contains no changes");
|
|
131
|
-
if (removedCount === 0)
|
|
135
|
+
if (removedCount === 0) {
|
|
136
|
+
return keep("pure insertion applies cleanly but has no removed range to anchor a suggestion");
|
|
137
|
+
}
|
|
132
138
|
const { firstRemoved, lastRemoved } = removedRange(body, oldStart);
|
|
133
139
|
if (firstRemoved === null || lastRemoved === null) return drop("malformed hunk body");
|
|
134
|
-
return { startLine: firstRemoved, endLine: lastRemoved };
|
|
140
|
+
return { kind: "anchored", startLine: firstRemoved, endLine: lastRemoved };
|
|
135
141
|
};
|
|
136
142
|
var patchToSuggestion = (patch) => {
|
|
137
143
|
const parsed = parseHunk(patch);
|
|
@@ -164,18 +170,28 @@ var severityEmoji = (s) => {
|
|
|
164
170
|
};
|
|
165
171
|
var EMBED_LIMIT = 4e4;
|
|
166
172
|
var AGENTS_STOP_DIRECTIVE = "<!-- AGENTS: STOP \u2014 do not parse the prose below; decode this findings JSON and read schema_version first. -->";
|
|
167
|
-
var
|
|
168
|
-
const b64 = Buffer.from(JSON.stringify(
|
|
173
|
+
var encodeMarker = (document, jsonUrl, limit) => {
|
|
174
|
+
const b64 = Buffer.from(JSON.stringify(document), "utf-8").toString("base64");
|
|
169
175
|
const marker = b64.length <= limit ? `<!-- code-review:findings-json;base64 ${b64} -->` : jsonUrl ? `<!-- code-review:findings-json ${jsonUrl} -->` : "";
|
|
170
176
|
return marker ? `${AGENTS_STOP_DIRECTIVE}
|
|
171
177
|
${marker}` : "";
|
|
172
178
|
};
|
|
179
|
+
var findingsPointer = (findings, jsonUrl, limit = EMBED_LIMIT) => encodeMarker(findings, jsonUrl, limit);
|
|
180
|
+
var findingPointer = (finding, schemaVersion, jsonUrl, limit = EMBED_LIMIT) => encodeMarker({ schema_version: schemaVersion, findings: [finding] }, jsonUrl, limit);
|
|
173
181
|
var escapeFence = (text) => text.replace(/```/g, "`` ` ``");
|
|
174
182
|
var projectPatch = (patch) => {
|
|
175
|
-
if (patch ===
|
|
183
|
+
if (patch === void 0) return { kind: "none" };
|
|
176
184
|
const lowered = patchToSuggestion(patch);
|
|
177
185
|
return typeof lowered === "string" ? { kind: "suggestion", text: escapeFence(lowered) } : { kind: "patch", raw: escapeFence(patch) };
|
|
178
186
|
};
|
|
187
|
+
var formatConfidence = (n) => n.toFixed(2);
|
|
188
|
+
var reviewBodyPointer = (headSha, stickyUrl, marker) => {
|
|
189
|
+
const sha7 = headSha.slice(0, 7);
|
|
190
|
+
const linkLine = stickyUrl ? `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the [summary comment](${stickyUrl}) for the verdict, walkthrough, and cost.` : `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the summary comment for the verdict, walkthrough, and cost.`;
|
|
191
|
+
return marker ? `${marker}
|
|
192
|
+
|
|
193
|
+
${linkLine}` : linkLine;
|
|
194
|
+
};
|
|
179
195
|
|
|
180
196
|
// src/render.ts
|
|
181
197
|
var escapePipes = (text) => text.replace(/\|/g, "\\|");
|
|
@@ -215,6 +231,7 @@ var render = (input) => {
|
|
|
215
231
|
modelNames,
|
|
216
232
|
testReport: input.testReport ?? null,
|
|
217
233
|
reviewedSha: input.reviewedSha ?? "0000000000000000000000000000000000000000",
|
|
234
|
+
postedAt: input.postedAt ?? "",
|
|
218
235
|
severityCounts: input.severityCounts ?? computeSeverityCounts(input.findings.findings),
|
|
219
236
|
strays: (input.strays ?? []).map(sanitizeFinding),
|
|
220
237
|
inlineDisposition: input.inlineDisposition ?? null,
|
|
@@ -243,7 +260,8 @@ var render = (input) => {
|
|
|
243
260
|
return `\u2753 ${v}`;
|
|
244
261
|
}
|
|
245
262
|
},
|
|
246
|
-
severityEmoji
|
|
263
|
+
severityEmoji,
|
|
264
|
+
formatConfidence
|
|
247
265
|
});
|
|
248
266
|
};
|
|
249
267
|
var key = (path, line) => `${path}:${String(line)}`;
|
|
@@ -308,6 +326,7 @@ var renderCommentBody = (f, eta, template, modelsText, jsonUrl, pointer) => (
|
|
|
308
326
|
...f,
|
|
309
327
|
patchProjection: projectPatch(f.patch),
|
|
310
328
|
severityEmoji,
|
|
329
|
+
formatConfidence,
|
|
311
330
|
modelsText,
|
|
312
331
|
jsonUrl: jsonUrl ?? null,
|
|
313
332
|
findingsPointer: pointer
|
|
@@ -319,8 +338,8 @@ var buildInlineComments = (findings, diff, context) => {
|
|
|
319
338
|
const { inDiff, strays } = partitionFindings(findings, index);
|
|
320
339
|
const eta = new Eta({ autoTrim: false });
|
|
321
340
|
const modelsText = formatModels(models);
|
|
322
|
-
const pointer = context.findingsPointer ?? (fullFindings ? findingsPointer(fullFindings, jsonUrl) : "");
|
|
323
341
|
const comments = inDiff.map((f) => {
|
|
342
|
+
const pointer = fullFindings ? findingPointer(f, fullFindings.schema_version, jsonUrl) : "";
|
|
324
343
|
const comment = {
|
|
325
344
|
path: f.path,
|
|
326
345
|
line: f.end_line,
|
|
@@ -388,7 +407,7 @@ var FindingShape = t.intersection([
|
|
|
388
407
|
code: t.string,
|
|
389
408
|
code_url: t.string,
|
|
390
409
|
recommendation: t.string,
|
|
391
|
-
patch: t.
|
|
410
|
+
patch: t.string
|
|
392
411
|
})
|
|
393
412
|
]);
|
|
394
413
|
var EndGeStart = t.refinement(
|
|
@@ -528,6 +547,8 @@ var formatMarkdown = (md) => {
|
|
|
528
547
|
return `${lines.join("\n").replace(/\n+$/, "")}
|
|
529
548
|
`;
|
|
530
549
|
};
|
|
550
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
551
|
+
var formatUtc = (d) => `${String(d.getUTCFullYear())}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())} UTC`;
|
|
531
552
|
var identity = (decoded) => decoded;
|
|
532
553
|
var findingsTable = [
|
|
533
554
|
{
|
|
@@ -745,11 +766,7 @@ var parseHtmlUrl = (raw) => {
|
|
|
745
766
|
}
|
|
746
767
|
};
|
|
747
768
|
var postInlineReview = async (repo, prNumber, headSha, comments, stickyUrl, marker, ghApi) => {
|
|
748
|
-
const
|
|
749
|
-
const linkLine = stickyUrl ? `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the [summary comment](${stickyUrl}) for the verdict, walkthrough, and cost.` : `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the summary comment for the verdict, walkthrough, and cost.`;
|
|
750
|
-
const pointer = marker ? `${marker}
|
|
751
|
-
|
|
752
|
-
${linkLine}` : linkLine;
|
|
769
|
+
const pointer = reviewBodyPointer(headSha, stickyUrl, marker);
|
|
753
770
|
const body = JSON.stringify({
|
|
754
771
|
body: pointer,
|
|
755
772
|
commit_id: headSha,
|
|
@@ -859,6 +876,84 @@ var dismissReviews = async (repo, prNumber, ids, ghApi) => {
|
|
|
859
876
|
}
|
|
860
877
|
}
|
|
861
878
|
};
|
|
879
|
+
var REVIEW_THREAD_COMMENTS_QUERY = "query($owner:String!,$name:String!,$pr:Int!){repository(owner:$owner,name:$name){pullRequest(number:$pr){reviewThreads(first:100){pageInfo{hasNextPage}nodes{comments(first:100){nodes{id isMinimized author{login} originalCommit{oid}}}}}}}}";
|
|
880
|
+
var MINIMIZE_COMMENT_MUTATION = "mutation($id:ID!){minimizeComment(input:{subjectId:$id,classifier:OUTDATED}){minimizedComment{isMinimized}}}";
|
|
881
|
+
var supersededCommentId = (c, headSha, logins) => {
|
|
882
|
+
if (typeof c !== "object" || c === null) return null;
|
|
883
|
+
const o = c;
|
|
884
|
+
const login = o.author?.login;
|
|
885
|
+
const oid = o.originalCommit?.oid;
|
|
886
|
+
return typeof o.id === "string" && o.isMinimized !== true && typeof login === "string" && logins.includes(login) && typeof oid === "string" && oid !== headSha ? o.id : null;
|
|
887
|
+
};
|
|
888
|
+
var supersededBotCommentIds = (raw, headSha, botLogin) => {
|
|
889
|
+
let parsed;
|
|
890
|
+
try {
|
|
891
|
+
parsed = JSON.parse(raw);
|
|
892
|
+
} catch {
|
|
893
|
+
return { ids: [], truncated: false };
|
|
894
|
+
}
|
|
895
|
+
const conn = parsed.data?.repository?.pullRequest?.reviewThreads;
|
|
896
|
+
const truncated = conn?.pageInfo?.hasNextPage === true;
|
|
897
|
+
const nodes = conn?.nodes;
|
|
898
|
+
if (!Array.isArray(nodes)) return { ids: [], truncated };
|
|
899
|
+
const logins = [botLogin.replace(/\[bot\]$/, ""), botLogin];
|
|
900
|
+
const ids = nodes.flatMap((t4) => {
|
|
901
|
+
const cnodes = t4.comments?.nodes;
|
|
902
|
+
return Array.isArray(cnodes) ? cnodes.map((c) => supersededCommentId(c, headSha, logins)).filter((id) => id !== null) : [];
|
|
903
|
+
});
|
|
904
|
+
return { ids, truncated };
|
|
905
|
+
};
|
|
906
|
+
var minimizeSupersededComments = async (repo, prNumber, headSha, botLogin, ghApi) => {
|
|
907
|
+
const slash = repo.indexOf("/");
|
|
908
|
+
if (slash <= 0) return;
|
|
909
|
+
const owner = repo.slice(0, slash);
|
|
910
|
+
const name = repo.slice(slash + 1);
|
|
911
|
+
let raw;
|
|
912
|
+
try {
|
|
913
|
+
raw = await ghApi([
|
|
914
|
+
"graphql",
|
|
915
|
+
"-f",
|
|
916
|
+
`query=${REVIEW_THREAD_COMMENTS_QUERY}`,
|
|
917
|
+
"-f",
|
|
918
|
+
`owner=${owner}`,
|
|
919
|
+
"-f",
|
|
920
|
+
`name=${name}`,
|
|
921
|
+
"-F",
|
|
922
|
+
`pr=${String(prNumber)}`
|
|
923
|
+
]);
|
|
924
|
+
} catch (err) {
|
|
925
|
+
process.stderr.write(
|
|
926
|
+
`Warning: could not list review threads to minimize stale comments on PR #${String(prNumber)}: ${err instanceof Error ? err.message : String(err)}
|
|
927
|
+
`
|
|
928
|
+
);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
const { ids, truncated } = supersededBotCommentIds(raw, headSha, botLogin);
|
|
932
|
+
if (truncated) {
|
|
933
|
+
process.stderr.write(
|
|
934
|
+
`Note: PR #${String(prNumber)} has more than 100 review threads \u2014 only the first 100 were scanned for stale bot comments
|
|
935
|
+
`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
let minimized = 0;
|
|
939
|
+
for (const id of ids) {
|
|
940
|
+
try {
|
|
941
|
+
await ghApi(["graphql", "-f", `query=${MINIMIZE_COMMENT_MUTATION}`, "-f", `id=${id}`]);
|
|
942
|
+
minimized += 1;
|
|
943
|
+
} catch (err) {
|
|
944
|
+
process.stderr.write(
|
|
945
|
+
`Warning: failed to minimize a stale review comment on PR #${String(prNumber)}: ${err instanceof Error ? err.message : String(err)}
|
|
946
|
+
`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
if (minimized > 0) {
|
|
951
|
+
process.stderr.write(
|
|
952
|
+
`Minimized ${String(minimized)} stale inline comment(s) from superseded reviews on PR #${String(prNumber)}
|
|
953
|
+
`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
};
|
|
862
957
|
var post = async (input, ghApi = runGhApi) => {
|
|
863
958
|
const candidates = await fetchPrCandidates(input.repo, input.headSha, ghApi);
|
|
864
959
|
const resolution = resolvePr(candidates, input.headBranch);
|
|
@@ -901,7 +996,8 @@ var post = async (input, ghApi = runGhApi) => {
|
|
|
901
996
|
reviewedSha: input.headSha,
|
|
902
997
|
effort: input.effort,
|
|
903
998
|
runUrl: input.runUrl,
|
|
904
|
-
jsonUrl: input.jsonUrl
|
|
999
|
+
jsonUrl: input.jsonUrl,
|
|
1000
|
+
postedAt: input.postedAt
|
|
905
1001
|
})
|
|
906
1002
|
);
|
|
907
1003
|
if (isEmptyDiff(diff)) {
|
|
@@ -942,7 +1038,8 @@ var post = async (input, ghApi = runGhApi) => {
|
|
|
942
1038
|
testReport,
|
|
943
1039
|
inlineDisposition: { kind: "no-envelope" },
|
|
944
1040
|
runUrl: input.runUrl,
|
|
945
|
-
jsonUrl: input.jsonUrl
|
|
1041
|
+
jsonUrl: input.jsonUrl,
|
|
1042
|
+
postedAt: input.postedAt
|
|
946
1043
|
})
|
|
947
1044
|
);
|
|
948
1045
|
await upsertSticky(input.repo, prNumber, existingSticky, body, ghApi);
|
|
@@ -955,7 +1052,8 @@ var post = async (input, ghApi = runGhApi) => {
|
|
|
955
1052
|
const { comments: rawComments, strays } = buildInlineComments(findings.findings, diff, {
|
|
956
1053
|
inlineTemplate,
|
|
957
1054
|
models: envelope.models.map((m) => m.model),
|
|
958
|
-
|
|
1055
|
+
findings,
|
|
1056
|
+
jsonUrl: input.jsonUrl
|
|
959
1057
|
});
|
|
960
1058
|
const { comments, longFiles } = checkLongSuggestions(rawComments);
|
|
961
1059
|
for (const wf of longFiles) {
|
|
@@ -981,7 +1079,8 @@ var post = async (input, ghApi = runGhApi) => {
|
|
|
981
1079
|
strays,
|
|
982
1080
|
runUrl: input.runUrl,
|
|
983
1081
|
jsonUrl: input.jsonUrl,
|
|
984
|
-
findingsPointer: findingsMarker
|
|
1082
|
+
findingsPointer: findingsMarker,
|
|
1083
|
+
postedAt: input.postedAt
|
|
985
1084
|
};
|
|
986
1085
|
const longFilesNote = longFiles.length > 0 ? `
|
|
987
1086
|
|
|
@@ -1022,6 +1121,7 @@ var post = async (input, ghApi = runGhApi) => {
|
|
|
1022
1121
|
`Posted ${String(comments.length)} inline comments on PR #${String(prNumber)}
|
|
1023
1122
|
`
|
|
1024
1123
|
);
|
|
1124
|
+
await minimizeSupersededComments(input.repo, prNumber, input.headSha, input.botLogin, ghApi);
|
|
1025
1125
|
if (stickyRef !== null) {
|
|
1026
1126
|
const confirmedDisposition = {
|
|
1027
1127
|
kind: "posted",
|
|
@@ -1597,7 +1697,8 @@ var renderCmd = defineCommand({
|
|
|
1597
1697
|
reviewedSha: args["reviewed-sha"],
|
|
1598
1698
|
route: args.route,
|
|
1599
1699
|
effort: args.effort,
|
|
1600
|
-
testReport
|
|
1700
|
+
testReport,
|
|
1701
|
+
postedAt: formatUtc(/* @__PURE__ */ new Date())
|
|
1601
1702
|
});
|
|
1602
1703
|
process.stdout.write(output);
|
|
1603
1704
|
}
|
|
@@ -1813,7 +1914,7 @@ var readFileLines = (path) => {
|
|
|
1813
1914
|
}
|
|
1814
1915
|
};
|
|
1815
1916
|
var validateFinding = (finding, repoRoot) => {
|
|
1816
|
-
if (finding.patch === void 0
|
|
1917
|
+
if (finding.patch === void 0) return finding;
|
|
1817
1918
|
const lines = readFileLines(resolve$1(repoRoot, finding.path));
|
|
1818
1919
|
if (lines === null) {
|
|
1819
1920
|
process.stderr.write(
|
|
@@ -1823,19 +1924,23 @@ var validateFinding = (finding, repoRoot) => {
|
|
|
1823
1924
|
return withoutPatch(finding);
|
|
1824
1925
|
}
|
|
1825
1926
|
const result = validatePatch(finding.patch, lines);
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1927
|
+
switch (result.kind) {
|
|
1928
|
+
case "anchored":
|
|
1929
|
+
return { ...finding, start_line: result.startLine, end_line: result.endLine };
|
|
1930
|
+
case "keep":
|
|
1931
|
+
return finding;
|
|
1932
|
+
case "drop":
|
|
1933
|
+
process.stderr.write(
|
|
1934
|
+
`validate-patches: ${finding.path}:${String(finding.start_line)}: ${result.reason} \u2014 dropping patch
|
|
1829
1935
|
`
|
|
1830
|
-
|
|
1831
|
-
|
|
1936
|
+
);
|
|
1937
|
+
return withoutPatch(finding);
|
|
1832
1938
|
}
|
|
1833
|
-
return { ...finding, start_line: result.startLine, end_line: result.endLine };
|
|
1834
1939
|
};
|
|
1835
1940
|
var validatePatchesCmd = defineCommand({
|
|
1836
1941
|
meta: {
|
|
1837
1942
|
name: "validate-patches",
|
|
1838
|
-
description: "Validate each finding's patch against the real PR-head tree, aligning the finding's range
|
|
1943
|
+
description: "Validate each finding's patch against the real PR-head tree, aligning the finding's range and keeping the patch when it anchors, keeping it unaligned when it's a pure insertion, or dropping it when it doesn't apply (issue #10)"
|
|
1839
1944
|
},
|
|
1840
1945
|
args: {
|
|
1841
1946
|
findings: {
|
|
@@ -2128,7 +2233,8 @@ var postCmd = defineCommand({
|
|
|
2128
2233
|
effort: args.effort,
|
|
2129
2234
|
testReportPath: args["test-report"],
|
|
2130
2235
|
runUrl: args["run-url"],
|
|
2131
|
-
jsonUrl: args["json-url"]
|
|
2236
|
+
jsonUrl: args["json-url"],
|
|
2237
|
+
postedAt: formatUtc(/* @__PURE__ */ new Date())
|
|
2132
2238
|
});
|
|
2133
2239
|
}
|
|
2134
2240
|
});
|