@applesnort/crosscheck 0.3.0 → 0.6.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/README.md +138 -4
- package/bin/crosscheck.mjs +388 -25
- package/lib/cache.mjs +100 -0
- package/lib/comment.mjs +138 -0
- package/lib/config.mjs +30 -6
- package/lib/merge.mjs +7 -1
- package/lib/prompt.mjs +87 -1
- package/lib/run.mjs +106 -4
- package/lib/target.mjs +123 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -19,7 +19,42 @@ or your own wrapper. crosscheck owns prompt construction, routing, fan-out, dedu
|
|
|
19
19
|
and output; you own the model. `--dry-run` prints the roster and prompts without
|
|
20
20
|
spawning anything.
|
|
21
21
|
|
|
22
|
-
No dependencies, no install step,
|
|
22
|
+
No dependencies, no install step, 267 tests.
|
|
23
|
+
|
|
24
|
+
## Getting set up
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @applesnort/crosscheck init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Scaffolds `.crosscheckrc.json`, a `.crosscheck/lenses/` directory with a note on
|
|
31
|
+
writing lenses, and a `.github/workflows/crosscheck.yml` that reviews each pull
|
|
32
|
+
request, uploads SARIF to code scanning, and posts a summary comment. Existing
|
|
33
|
+
files are left alone unless you pass `--force`.
|
|
34
|
+
|
|
35
|
+
Set `exec` to whatever runs your model, then:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
crosscheck run --diff --dry-run
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## In CI
|
|
42
|
+
|
|
43
|
+
The scaffolded workflow reviews the diff against the base branch, and does two
|
|
44
|
+
things with the result:
|
|
45
|
+
|
|
46
|
+
- **SARIF to code scanning** — per-line annotations in the Files changed view,
|
|
47
|
+
which is where a reviewer is already looking.
|
|
48
|
+
- **A summary comment** via `--comment-file`, carrying what per-line annotations
|
|
49
|
+
cannot: which lenses were skipped and why, which died, what the baseline
|
|
50
|
+
suppressed, and how many findings verification refuted.
|
|
51
|
+
|
|
52
|
+
The comment embeds a marker so later runs **edit** it rather than stacking. A pull
|
|
53
|
+
request with eleven bot comments gets muted, and a muted reviewer finds nothing.
|
|
54
|
+
|
|
55
|
+
The workflow checks out with `fetch-depth: 0`, because reviewing a diff needs the
|
|
56
|
+
base commit. crosscheck itself never talks to a model — supply whatever your
|
|
57
|
+
`exec` command needs via the workflow's `env`.
|
|
23
58
|
|
|
24
59
|
## Configuration
|
|
25
60
|
|
|
@@ -50,7 +85,9 @@ and an unrecognised key is an error rather than a silent no-op, because a
|
|
|
50
85
|
misspelled `exec` that quietly does nothing is worse than a crash.
|
|
51
86
|
|
|
52
87
|
Accepted keys: `exec`, `lenses`, `concurrency`, `only`, `skip`, `mixed`, `out`,
|
|
53
|
-
`sarif`, `baseline`, `overlap
|
|
88
|
+
`sarif`, `baseline`, `overlap`, `preflight`, `context`, `verify`, `since`,
|
|
89
|
+
`max-dispatches`, `no-cache`, `cache-dir`, `comment-file`. `exec` accepts a string
|
|
90
|
+
or a per-lens map. Keys beginning `//` are treated as comments.
|
|
54
91
|
|
|
55
92
|
> **v0.x — the API is unstable.** The CLI commands and the `lib/` exports may
|
|
56
93
|
> change shape before 1.0. Pin an exact version if you depend on it.
|
|
@@ -59,6 +96,100 @@ Accepted keys: `exec`, `lenses`, `concurrency`, `only`, `skip`, `mixed`, `out`,
|
|
|
59
96
|
> similar to the (abandoned) `cross-check`. Installed, the command is
|
|
60
97
|
> `crosscheck`.
|
|
61
98
|
|
|
99
|
+
## Reviewing a change, not a directory
|
|
100
|
+
|
|
101
|
+
Nobody reviews a whole tree — they review a diff. Auditing paths makes cost scale
|
|
102
|
+
with repository size instead of change size, and re-reads code nobody touched.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
crosscheck run --diff # everything uncommitted
|
|
106
|
+
crosscheck run --staged # what you are about to commit
|
|
107
|
+
crosscheck run --since origin/main # the branch, for a PR
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each lens is told which lines moved:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
- src/a.js (changed lines: 5-27, 61-84)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Ranges are widened by `--context` lines (20 by default) so a lens sees the code
|
|
117
|
+
around a change, and the prompt is explicit that changed lines are the *priority*
|
|
118
|
+
rather than the boundary — a defect elsewhere that the change causes or depends on
|
|
119
|
+
is still worth reporting, while a pre-existing one it never touches is not what the
|
|
120
|
+
review is for. An empty diff exits cleanly rather than falling back to reviewing
|
|
121
|
+
everything.
|
|
122
|
+
|
|
123
|
+
## Verification is on by default
|
|
124
|
+
|
|
125
|
+
`BLOCK` findings are refuted before they are reported. Each one gets a skeptic that
|
|
126
|
+
is handed **the file** rather than the finding's account of it, told to default to
|
|
127
|
+
refuted when it cannot name a concrete trigger. Refuted findings are removed and
|
|
128
|
+
**the count is always printed, including zero** — a finding that vanished without a
|
|
129
|
+
number is indistinguishable from one that was never found.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
crosscheck: verifying 3 finding(s)
|
|
133
|
+
✓ confirmed src/session.js:44
|
|
134
|
+
✗ refuted src/cache.js:12
|
|
135
|
+
Refuted in verification: 1.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
False positives cost more than misses: a panel that cries wolf twice stops being
|
|
139
|
+
read, and its true findings go unread with the rest. `--verify` extends the pass to
|
|
140
|
+
every severity; `--no-verify` skips it, at that cost.
|
|
141
|
+
|
|
142
|
+
A verifier that fails to run is **not** treated as agreement — the finding stands
|
|
143
|
+
and the failure is reported.
|
|
144
|
+
|
|
145
|
+
## Your own gate, before anything is dispatched
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{ "preflight": "scripts/check-clean-worktree.sh" }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A non-zero exit aborts before a single model call. That lets a project enforce a
|
|
152
|
+
rule crosscheck knows nothing about — data classification, branch policy, a clean
|
|
153
|
+
worktree — without the rule having to exist upstream.
|
|
154
|
+
|
|
155
|
+
## Cost control
|
|
156
|
+
|
|
157
|
+
A tool that costs real money per run gets switched off, and a switched-off tool
|
|
158
|
+
finds nothing.
|
|
159
|
+
|
|
160
|
+
**A cheap lens should not pay for an expensive model.** `exec` may be a map:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"exec": {
|
|
165
|
+
"default": "claude -p",
|
|
166
|
+
"conventions": "llm -m claude-haiku-4-5"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Precedence is the lens's own `exec` in its frontmatter, then the map entry, then
|
|
172
|
+
`default`. A rostered lens with no command anywhere fails loudly and names itself
|
|
173
|
+
— it is never quietly skipped.
|
|
174
|
+
|
|
175
|
+
**Unchanged files are not re-reviewed.** Results are cached under
|
|
176
|
+
`.crosscheck/cache`, keyed on the lens definition, the files, and their contents.
|
|
177
|
+
The definition is part of the key deliberately: editing a lens must invalidate its
|
|
178
|
+
results, or you would be served answers from the previous prompt with no way to
|
|
179
|
+
tell. `--no-cache` disables it, `--cache-dir` relocates it. A failed lens is never
|
|
180
|
+
cached, so it is retried rather than permanently wrong.
|
|
181
|
+
|
|
182
|
+
**`--max-dispatches N` caps the run, and says what it dropped:**
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
crosscheck: BUDGET REACHED — 3 lens(es) not run: check, security-check, taint
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The unit is dispatches, not dollars. crosscheck cannot see tokens or cost —
|
|
189
|
+
`--exec` is an arbitrary command — so a monetary budget would be a number invented
|
|
190
|
+
from nothing. Truncation is always named, because a run that quietly stopped early
|
|
191
|
+
looks exactly like a run that found nothing.
|
|
192
|
+
|
|
62
193
|
## SARIF output
|
|
63
194
|
|
|
64
195
|
Findings are emitted as [SARIF 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html),
|
|
@@ -200,8 +331,11 @@ lib/
|
|
|
200
331
|
prompt.mjs lens prompt construction
|
|
201
332
|
run.mjs roster planning and bounded fan-out
|
|
202
333
|
config.mjs .crosscheckrc.json discovery and validation
|
|
203
|
-
|
|
204
|
-
|
|
334
|
+
target.mjs diff parsing, changed line ranges
|
|
335
|
+
cache.mjs content-addressed result cache
|
|
336
|
+
comment.mjs pull-request summary comment
|
|
337
|
+
bin/crosscheck.mjs CLI: init | run | lenses | report | sarif |
|
|
338
|
+
baseline | overlap | calibrate
|
|
205
339
|
fixtures/calibration/ planted defects, ground truth, and the calibration record
|
|
206
340
|
fixtures/deception/ 20 modules that look safe and are not, or the reverse
|
|
207
341
|
PROVENANCE.md where all of this came from
|