@jphutchins/code-review 0.1.0-alpha.6 → 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 +12 -10
- package/dist/index.js +445 -215
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/schema/VERSIONING.md +2 -1
- package/schema/findings.schema.json +23 -14
- package/templates/comment.eta +24 -6
- package/templates/inline.eta +16 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jphutchins/code-review",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "Deterministic commenter for agentic PR review — gather, render, inline, post, adapt, extract, cost, validate, print-schema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"eslint-config-prettier": "^10.1.8",
|
|
49
49
|
"prettier": "^3.9.4",
|
|
50
50
|
"tsup": "^8.3.5",
|
|
51
|
+
"tsx": "^4.23.0",
|
|
51
52
|
"typescript": "^5.7.3",
|
|
52
53
|
"typescript-eslint": "^8.62.1",
|
|
53
54
|
"vitest": "^3.0.4"
|
package/schema/VERSIONING.md
CHANGED
|
@@ -66,7 +66,8 @@ ignored); a version outside the supported set degrades to a §5.5 sticky notice.
|
|
|
66
66
|
|---|---|---|
|
|
67
67
|
| `v0.1.0` | superseded | Initial schema. Matches the proven camas reference implementation. |
|
|
68
68
|
| `v0.2.0` | superseded | Adds required `schema_version`; optional `code`/`code_url` finding fields; normative `suggestion` `""`/`null` semantics; abstract vendor-neutral envelope (see SPEC §6.1). |
|
|
69
|
-
| `v0.3.0` |
|
|
69
|
+
| `v0.3.0` | superseded | Adds optional `reasoning` finding field. |
|
|
70
|
+
| `v0.4.0` | **current** | Breaking: renames finding `body` → `description`; makes `reasoning` and `confidence` **required**; adds optional `recommendation` (prose fix); removes the free-text `suggestion` field (a `patch`, now `string \| null`, is the sole mechanical fix, projected into a suggestion by the commenter). The CLI supports only `0.4` — `0.2`/`0.3` documents cannot supply the now-required `reasoning`/`confidence`, so they degrade to a §5.5 unsupported-version notice rather than being upcast. |
|
|
70
71
|
|
|
71
72
|
### Price-map schema
|
|
72
73
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://raw.githubusercontent.com/JPHutchins/code-review/main/schema/findings.schema.json",
|
|
4
4
|
"title": "Code Review — findings",
|
|
5
|
-
"description": "Structured output of an agentic PR review. The review agent emits this object (e.g. via a coding-agent CLI's structured-output mode, such as Claude Code's `--json-schema`, whose result lands in `.structured_output
|
|
5
|
+
"description": "Structured output of an agentic PR code review. The review agent emits this object (e.g. via a coding-agent CLI's structured-output mode, such as Claude Code's `--json-schema`, whose result lands in `.structured_output`, or a file).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["schema_version", "summary", "verdict", "findings"],
|
|
@@ -10,24 +10,33 @@
|
|
|
10
10
|
"schema_version": {
|
|
11
11
|
"type": "string",
|
|
12
12
|
"pattern": "^(0|[1-9]\\d*)\\.(\\d+)\\.(\\d+)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$",
|
|
13
|
-
"description": "Semver version of this findings schema the object conforms to (e.g. \"0.1.0\").
|
|
13
|
+
"description": "Semver version of this findings schema the object conforms to (e.g. \"0.1.0\")."
|
|
14
14
|
},
|
|
15
15
|
"summary": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"description": "
|
|
17
|
+
"description": "1-4 sentence markdown walkthrough of the overall assessment."
|
|
18
18
|
},
|
|
19
19
|
"verdict": {
|
|
20
20
|
"type": "string",
|
|
21
21
|
"enum": ["approve", "comment", "changes"],
|
|
22
|
-
"description": "Overall stance.
|
|
22
|
+
"description": "Overall stance."
|
|
23
23
|
},
|
|
24
24
|
"findings": {
|
|
25
25
|
"type": "array",
|
|
26
|
-
"description": "Zero or more specific findings.
|
|
26
|
+
"description": "Zero or more specific findings.",
|
|
27
27
|
"items": {
|
|
28
28
|
"type": "object",
|
|
29
29
|
"additionalProperties": false,
|
|
30
|
-
"required": [
|
|
30
|
+
"required": [
|
|
31
|
+
"path",
|
|
32
|
+
"start_line",
|
|
33
|
+
"end_line",
|
|
34
|
+
"severity",
|
|
35
|
+
"title",
|
|
36
|
+
"description",
|
|
37
|
+
"reasoning",
|
|
38
|
+
"confidence"
|
|
39
|
+
],
|
|
31
40
|
"properties": {
|
|
32
41
|
"path": {
|
|
33
42
|
"type": "string",
|
|
@@ -67,27 +76,27 @@
|
|
|
67
76
|
"type": "string",
|
|
68
77
|
"description": "One-line summary of the finding."
|
|
69
78
|
},
|
|
70
|
-
"
|
|
79
|
+
"description": {
|
|
71
80
|
"type": "string",
|
|
72
|
-
"description": "
|
|
81
|
+
"description": "1-3 sentence markdown explanation of what is wrong. Details belong in `reasoning`."
|
|
73
82
|
},
|
|
74
|
-
"
|
|
75
|
-
"type":
|
|
76
|
-
"description": "
|
|
83
|
+
"recommendation": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "1-2 sentence markdown prose describing what to do about the finding — distinct from `patch`, which is a machine-applicable unified diff. Use this when the fix is best explained rather than mechanically applied, or to accompany a `patch` with rationale."
|
|
77
86
|
},
|
|
78
87
|
"confidence": {
|
|
79
88
|
"type": "number",
|
|
80
89
|
"minimum": 0,
|
|
81
90
|
"maximum": 1,
|
|
82
|
-
"description": "
|
|
91
|
+
"description": "0..1 confidence; a commenter MAY suppress findings below a configurable threshold to control noise, but MUST NOT suppress a critical-severity finding on confidence alone."
|
|
83
92
|
},
|
|
84
93
|
"reasoning": {
|
|
85
94
|
"type": "string",
|
|
86
|
-
"description": "
|
|
95
|
+
"description": "Rationale/evidence for why the finding holds, so a human or downstream agent can judge its soundness — distinct from `description`, which explains the finding to the reader."
|
|
87
96
|
},
|
|
88
97
|
"patch": {
|
|
89
98
|
"type": "string",
|
|
90
|
-
"description": "Optional single-hunk unified diff of
|
|
99
|
+
"description": "Optional single-hunk unified diff of one mechanical fix to this file, expressed against its current (post-change) content — e.g. the `git diff` of just that edit after you apply it. It MUST be exactly one hunk and one contiguous change, and its context and removed lines MUST match the file verbatim so it applies cleanly. Omit this field when there is no clean mechanical fix — never invent one; put prose guidance in `recommendation` instead."
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
}
|
package/templates/comment.eta
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<!-- code-review -->
|
|
2
2
|
<!-- reviewed-sha: <%= it.reviewedSha %> -->
|
|
3
|
-
<% if (it.
|
|
4
|
-
|
|
5
|
-
<% } else if (it.jsonUrl) { -%>
|
|
6
|
-
<!-- code-review:findings-json <%~ it.jsonUrl %> -->
|
|
3
|
+
<% if (it.findingsPointer) { -%>
|
|
4
|
+
<%~ it.findingsPointer %>
|
|
7
5
|
<% } -%>
|
|
8
6
|
|
|
9
7
|
### <%= it.verdictBadge(it.findings.verdict) %>
|
|
8
|
+
<% if (it.postedAt) { -%>
|
|
9
|
+
Reviewed `<%= it.reviewedSha.slice(0, 7) %>` · <%= it.postedAt %>
|
|
10
|
+
<% } -%>
|
|
10
11
|
|
|
11
12
|
<% const meta = [] %><% if (it.route) { meta.push('**Route:** ' + it.route) } %><% if (it.effort) { meta.push('**effort:** ' + it.effort) } %><% if (it.usageAvailable) { meta.push('**turns:** ' + it.envelope.turns); meta.push('**wall:** ' + it.formatDuration(it.envelope.duration_ms)); meta.push('**models:** ' + it.modelNames); meta.push('**cost:** ' + it.formatCost(it.costReport.totalCostUSD)) } else { meta.push('_usage unavailable_') } %><%~ meta.join(' · ') %>
|
|
12
13
|
|
|
@@ -36,8 +37,7 @@ _No findings — clean review._
|
|
|
36
37
|
These findings are on lines not in the diff, so they can't be anchored as inline comments:
|
|
37
38
|
|
|
38
39
|
<% it.strays.forEach(function(f) { -%>
|
|
39
|
-
- <%= it.severityEmoji(f.severity) %> `<%= f.path %>:<%= f.start_line %><% if (f.start_line !== f.end_line) { %>–<%= f.end_line %><% } %>` — <%= f.title
|
|
40
|
-
<% if (f.reasoning) { -%>
|
|
40
|
+
- <%= it.severityEmoji(f.severity) %> `<%= f.path %>:<%= f.start_line %><% if (f.start_line !== f.end_line) { %>–<%= f.end_line %><% } %>` — <%= f.title %> · confidence <%= it.formatConfidence(f.confidence) %>
|
|
41
41
|
<details><summary>Reasoning</summary>
|
|
42
42
|
|
|
43
43
|
<% f.reasoning.split("\n").forEach(function(rl) { -%>
|
|
@@ -45,6 +45,20 @@ These findings are on lines not in the diff, so they can't be anchored as inline
|
|
|
45
45
|
<% }) -%>
|
|
46
46
|
|
|
47
47
|
</details>
|
|
48
|
+
<% if (f.recommendation) { -%>
|
|
49
|
+
|
|
50
|
+
**Recommended fix:** <%~ f.recommendation %>
|
|
51
|
+
<% } -%>
|
|
52
|
+
<% if (f.patchProjection.kind === "suggestion") { -%>
|
|
53
|
+
|
|
54
|
+
```suggestion
|
|
55
|
+
<%~ f.patchProjection.text %>
|
|
56
|
+
```
|
|
57
|
+
<% } else if (f.patchProjection.kind === "patch") { -%>
|
|
58
|
+
|
|
59
|
+
```patch
|
|
60
|
+
<%~ f.patchProjection.raw %>
|
|
61
|
+
```
|
|
48
62
|
<% } -%>
|
|
49
63
|
<% }) %>
|
|
50
64
|
<% } %>
|
|
@@ -83,5 +97,9 @@ All <%= it.testReport.total %> tests passed.
|
|
|
83
97
|
<% } else { -%>
|
|
84
98
|
> **Usage/cost unavailable** — the result envelope for this run was missing or malformed.
|
|
85
99
|
<% } -%>
|
|
100
|
+
<% if (it.usageAvailable && !it.pricesProvided) { -%>
|
|
101
|
+
>
|
|
102
|
+
> _Cost shown as **N/A** — [No `.github/prices.json`](https://github.com/JPHutchins/code-review/blob/main/schema/prices.example.json) price map to recompute from._
|
|
103
|
+
<% } -%>
|
|
86
104
|
>
|
|
87
105
|
> _Generated by [code-review](https://github.com/JPHutchins/code-review)<% if (it.runUrl) { %> · [view the run & traces](<%~ it.runUrl %>)<% } %>._
|
package/templates/inline.eta
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
<% if (it.
|
|
2
|
-
|
|
1
|
+
<% if (it.findingsPointer) { -%>
|
|
2
|
+
<%~ it.findingsPointer %>
|
|
3
3
|
<% } -%>
|
|
4
|
-
<%= it.severityEmoji(it.severity) %>
|
|
4
|
+
<%= it.severityEmoji(it.severity) %> <%= it.severity.charAt(0).toUpperCase() + it.severity.slice(1) %>: **<%= it.title %>** · <%= it.formatConfidence(it.confidence) %> confidence
|
|
5
5
|
|
|
6
|
-
<%~ it.
|
|
7
|
-
<% if (it.
|
|
6
|
+
<%~ it.description %>
|
|
7
|
+
<% if (it.recommendation) { -%>
|
|
8
|
+
|
|
9
|
+
**Recommended fix:** <%~ it.recommendation %>
|
|
10
|
+
<% } -%>
|
|
11
|
+
<% if (it.patchProjection.kind === "suggestion") { -%>
|
|
8
12
|
|
|
9
13
|
```suggestion
|
|
10
|
-
<%~ it.
|
|
14
|
+
<%~ it.patchProjection.text %>
|
|
15
|
+
```
|
|
16
|
+
<% } else if (it.patchProjection.kind === "patch") { -%>
|
|
17
|
+
|
|
18
|
+
```patch
|
|
19
|
+
<%~ it.patchProjection.raw %>
|
|
11
20
|
```
|
|
12
21
|
<% } -%>
|
|
13
22
|
|
|
14
23
|
> [!TIP]
|
|
15
|
-
> Generated by <%~ it.modelsText
|
|
16
|
-
<% if (it.reasoning) { -%>
|
|
24
|
+
> Generated by <%~ it.modelsText %>.
|
|
17
25
|
> <details>
|
|
18
26
|
> <summary>Reasoning</summary>
|
|
19
27
|
>
|
|
@@ -22,4 +30,3 @@
|
|
|
22
30
|
<% }) -%>
|
|
23
31
|
>
|
|
24
32
|
> </details>
|
|
25
|
-
<% } -%>
|