@jphutchins/code-review 0.1.0-alpha.7 → 0.1.0-alpha.8

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 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 spec's abstract
29
- envelope ([SPEC §6.1](SPEC.md#61-result-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 SPEC §6.1 envelope |
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 §6.2](SPEC.md#62-price-map)).
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 §8.4](SPEC.md#84-egress-allowlist)).
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 ([SPEC §8.5](SPEC.md#85-model-backend-env)). Only the backend
75
- endpoint is a per-repo **Actions variable** (`API_BASE_URL`, required, no default); pointing it at
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
- ([SPEC §5.1 item 7](SPEC.md#51-sticky-summary-comment)).
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
@@ -172,10 +172,18 @@ ${marker}` : "";
172
172
  };
173
173
  var escapeFence = (text) => text.replace(/```/g, "`` ` ``");
174
174
  var projectPatch = (patch) => {
175
- if (patch === null || patch === void 0) return { kind: "none" };
175
+ if (patch === void 0) return { kind: "none" };
176
176
  const lowered = patchToSuggestion(patch);
177
177
  return typeof lowered === "string" ? { kind: "suggestion", text: escapeFence(lowered) } : { kind: "patch", raw: escapeFence(patch) };
178
178
  };
179
+ var formatConfidence = (n) => n.toFixed(2);
180
+ var reviewBodyPointer = (headSha, stickyUrl, marker) => {
181
+ const sha7 = headSha.slice(0, 7);
182
+ 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.`;
183
+ return marker ? `${marker}
184
+
185
+ ${linkLine}` : linkLine;
186
+ };
179
187
 
180
188
  // src/render.ts
181
189
  var escapePipes = (text) => text.replace(/\|/g, "\\|");
@@ -215,6 +223,7 @@ var render = (input) => {
215
223
  modelNames,
216
224
  testReport: input.testReport ?? null,
217
225
  reviewedSha: input.reviewedSha ?? "0000000000000000000000000000000000000000",
226
+ postedAt: input.postedAt ?? "",
218
227
  severityCounts: input.severityCounts ?? computeSeverityCounts(input.findings.findings),
219
228
  strays: (input.strays ?? []).map(sanitizeFinding),
220
229
  inlineDisposition: input.inlineDisposition ?? null,
@@ -243,7 +252,8 @@ var render = (input) => {
243
252
  return `\u2753 ${v}`;
244
253
  }
245
254
  },
246
- severityEmoji
255
+ severityEmoji,
256
+ formatConfidence
247
257
  });
248
258
  };
249
259
  var key = (path, line) => `${path}:${String(line)}`;
@@ -308,6 +318,7 @@ var renderCommentBody = (f, eta, template, modelsText, jsonUrl, pointer) => (
308
318
  ...f,
309
319
  patchProjection: projectPatch(f.patch),
310
320
  severityEmoji,
321
+ formatConfidence,
311
322
  modelsText,
312
323
  jsonUrl: jsonUrl ?? null,
313
324
  findingsPointer: pointer
@@ -388,7 +399,7 @@ var FindingShape = t.intersection([
388
399
  code: t.string,
389
400
  code_url: t.string,
390
401
  recommendation: t.string,
391
- patch: t.union([t.string, t.null])
402
+ patch: t.string
392
403
  })
393
404
  ]);
394
405
  var EndGeStart = t.refinement(
@@ -528,6 +539,8 @@ var formatMarkdown = (md) => {
528
539
  return `${lines.join("\n").replace(/\n+$/, "")}
529
540
  `;
530
541
  };
542
+ var pad2 = (n) => String(n).padStart(2, "0");
543
+ var formatUtc = (d) => `${String(d.getUTCFullYear())}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())} UTC`;
531
544
  var identity = (decoded) => decoded;
532
545
  var findingsTable = [
533
546
  {
@@ -745,11 +758,7 @@ var parseHtmlUrl = (raw) => {
745
758
  }
746
759
  };
747
760
  var postInlineReview = async (repo, prNumber, headSha, comments, stickyUrl, marker, ghApi) => {
748
- const sha7 = headSha.slice(0, 7);
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;
761
+ const pointer = reviewBodyPointer(headSha, stickyUrl, marker);
753
762
  const body = JSON.stringify({
754
763
  body: pointer,
755
764
  commit_id: headSha,
@@ -901,7 +910,8 @@ var post = async (input, ghApi = runGhApi) => {
901
910
  reviewedSha: input.headSha,
902
911
  effort: input.effort,
903
912
  runUrl: input.runUrl,
904
- jsonUrl: input.jsonUrl
913
+ jsonUrl: input.jsonUrl,
914
+ postedAt: input.postedAt
905
915
  })
906
916
  );
907
917
  if (isEmptyDiff(diff)) {
@@ -942,7 +952,8 @@ var post = async (input, ghApi = runGhApi) => {
942
952
  testReport,
943
953
  inlineDisposition: { kind: "no-envelope" },
944
954
  runUrl: input.runUrl,
945
- jsonUrl: input.jsonUrl
955
+ jsonUrl: input.jsonUrl,
956
+ postedAt: input.postedAt
946
957
  })
947
958
  );
948
959
  await upsertSticky(input.repo, prNumber, existingSticky, body, ghApi);
@@ -981,7 +992,8 @@ var post = async (input, ghApi = runGhApi) => {
981
992
  strays,
982
993
  runUrl: input.runUrl,
983
994
  jsonUrl: input.jsonUrl,
984
- findingsPointer: findingsMarker
995
+ findingsPointer: findingsMarker,
996
+ postedAt: input.postedAt
985
997
  };
986
998
  const longFilesNote = longFiles.length > 0 ? `
987
999
 
@@ -1597,7 +1609,8 @@ var renderCmd = defineCommand({
1597
1609
  reviewedSha: args["reviewed-sha"],
1598
1610
  route: args.route,
1599
1611
  effort: args.effort,
1600
- testReport
1612
+ testReport,
1613
+ postedAt: formatUtc(/* @__PURE__ */ new Date())
1601
1614
  });
1602
1615
  process.stdout.write(output);
1603
1616
  }
@@ -1813,7 +1826,7 @@ var readFileLines = (path) => {
1813
1826
  }
1814
1827
  };
1815
1828
  var validateFinding = (finding, repoRoot) => {
1816
- if (finding.patch === void 0 || finding.patch === null) return finding;
1829
+ if (finding.patch === void 0) return finding;
1817
1830
  const lines = readFileLines(resolve$1(repoRoot, finding.path));
1818
1831
  if (lines === null) {
1819
1832
  process.stderr.write(
@@ -2128,7 +2141,8 @@ var postCmd = defineCommand({
2128
2141
  effort: args.effort,
2129
2142
  testReportPath: args["test-report"],
2130
2143
  runUrl: args["run-url"],
2131
- jsonUrl: args["json-url"]
2144
+ jsonUrl: args["json-url"],
2145
+ postedAt: formatUtc(/* @__PURE__ */ new Date())
2132
2146
  });
2133
2147
  }
2134
2148
  });