agent-roast 0.6.0 → 0.7.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 +35 -15
- package/dist/{chunk-RU5OYHLR.js → chunk-YGHNTWHX.js} +143 -23
- package/dist/cli.js +47 -35
- package/dist/index.d.ts +7 -2
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ npx agent-roast check --baseline .agentroast-baseline.json
|
|
|
38
38
|
npx agent-roast --staged
|
|
39
39
|
|
|
40
40
|
# Audit pull request diffs (CI gate)
|
|
41
|
-
npx agent-roast --base origin/main --head HEAD --format github
|
|
41
|
+
npx agent-roast review --base origin/main --head HEAD --format github
|
|
42
42
|
|
|
43
43
|
# Optional historical audit
|
|
44
44
|
npx agent-roast --all --verbose
|
|
@@ -106,7 +106,7 @@ regressions) may appear in the report/summary rather than as an inline PR annota
|
|
|
106
106
|
Use a separate SARIF category for introduced-findings review; it does not represent
|
|
107
107
|
a full scan of all outstanding alerts.
|
|
108
108
|
|
|
109
|
-
Install
|
|
109
|
+
Install 0.7.0: `npx agent-roast@0.7.0 review --base origin/main`. For local development,
|
|
110
110
|
run `npm run build` and use `node dist/cli.js`.
|
|
111
111
|
|
|
112
112
|
### Baseline adoption
|
|
@@ -175,7 +175,6 @@ Configure custom ignore paths, rule severities, and CI thresholds with a `.agent
|
|
|
175
175
|
|
|
176
176
|
```json
|
|
177
177
|
{
|
|
178
|
-
"$schema": "https://raw.githubusercontent.com/Nari-Ab/agent-roast/main/schema.json",
|
|
179
178
|
"ignorePaths": [
|
|
180
179
|
"**/*.min.js",
|
|
181
180
|
"**/vendor/**",
|
|
@@ -183,11 +182,10 @@ Configure custom ignore paths, rule severities, and CI thresholds with a `.agent
|
|
|
183
182
|
"**/fixtures/**"
|
|
184
183
|
],
|
|
185
184
|
"rules": {
|
|
186
|
-
"
|
|
187
|
-
"
|
|
185
|
+
"test-focus": "error",
|
|
186
|
+
"swallowed-error": "warn",
|
|
188
187
|
"trivial-assert": "warn",
|
|
189
|
-
"stub-abandonment": "warn"
|
|
190
|
-
"mock-leak": "warn"
|
|
188
|
+
"stub-abandonment": "warn"
|
|
191
189
|
},
|
|
192
190
|
"scoreThreshold": 70
|
|
193
191
|
}
|
|
@@ -203,9 +201,22 @@ npx agent-roast --staged
|
|
|
203
201
|
npx agent-roast init --hook
|
|
204
202
|
```
|
|
205
203
|
|
|
206
|
-
`--staged`
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
Since 0.7.0, `--staged` (also `review --staged`) compares the **full source**
|
|
205
|
+
of changed files in HEAD and the Git index. It detects deletion-only empty handlers
|
|
206
|
+
and retains comment context. Existing findings do not block; additional occurrences
|
|
207
|
+
do. Unstaged edits, untracked files and `git add -N` intent-to-add entries are excluded.
|
|
208
|
+
The previous released 0.6.0 `--staged` command checks only added index lines.
|
|
209
|
+
|
|
210
|
+
Policy comes from HEAD. Staged/unstaged config edits cannot weaken this check and
|
|
211
|
+
are reported; adopt policy changes in a separately reviewed commit. With no HEAD,
|
|
212
|
+
the first commit uses built-in defaults; use `--fail-on` for an explicit override.
|
|
213
|
+
For example: `node dist/cli.js review --staged --fail-on test-skip,test-focus,swallowed-error`.
|
|
214
|
+
Source is pinned by Git blob IDs. If HEAD or the staged inventory changes before the
|
|
215
|
+
scan finishes, it exits 2 and asks for a rerun. The report's `source` is `index`;
|
|
216
|
+
`headSha` is an `INDEX:` snapshot digest, not a commit SHA, and an unborn base is `EMPTY`.
|
|
217
|
+
The JSON schema version for context reviews is now `1.1.0`.
|
|
218
|
+
|
|
219
|
+
The generated hook uses the locally installed CLI (`--no-install`) and blocks on configured error rules instead of scoring
|
|
209
220
|
old commits. It preserves an existing pre-commit hook.
|
|
210
221
|
|
|
211
222
|
### Configure what blocks a change
|
|
@@ -216,6 +227,7 @@ Create `.agentroastrc.json` (or run `npx agent-roast init`):
|
|
|
216
227
|
{
|
|
217
228
|
"rules": {
|
|
218
229
|
"test-skip": "error",
|
|
230
|
+
"test-focus": "error",
|
|
219
231
|
"trivial-assert": "error",
|
|
220
232
|
"swallowed-error": "warn",
|
|
221
233
|
"type-escape": "off"
|
|
@@ -227,7 +239,7 @@ Create `.agentroastrc.json` (or run `npx agent-roast init`):
|
|
|
227
239
|
```
|
|
228
240
|
|
|
229
241
|
`off` removes findings and score penalties; `warn` reports without blocking a diff;
|
|
230
|
-
`error` blocks PR/staged checks. An explicit `failOn` array overrides which enabled
|
|
242
|
+
`error` blocks PR/staged checks, including `test-focus` by default in 0.7.0. An explicit `failOn` array overrides which enabled
|
|
231
243
|
rules block (`[]` makes the audit advisory). CLI `--fail-on` takes precedence over
|
|
232
244
|
that array. History and badge audits honor disabled rules and the panic-loop window.
|
|
233
245
|
History exit status is controlled by `scoreThreshold` or `--fail-under`.
|
|
@@ -264,7 +276,6 @@ jobs:
|
|
|
264
276
|
- name: Run agent-roast audit
|
|
265
277
|
uses: Nari-Ab/agent-roast@v1
|
|
266
278
|
with:
|
|
267
|
-
fail-on: test-skip,trivial-assert,stub-abandonment
|
|
268
279
|
format: github
|
|
269
280
|
```
|
|
270
281
|
|
|
@@ -274,8 +285,12 @@ jobs:
|
|
|
274
285
|
|
|
275
286
|
### What it checks
|
|
276
287
|
|
|
277
|
-
|
|
288
|
+
`check` scans current source. `review` scans both full versions of changed source;
|
|
289
|
+
the 0.7.0 `--staged` command does the same for HEAD and index. Legacy
|
|
290
|
+
`--base` and history audits inspect added lines. Comment/literal masking reduces
|
|
291
|
+
noise; these are heuristic checks and can have false positives and missed findings.
|
|
278
292
|
|
|
293
|
+
- **Focused tests (`test-focus`):** Exclusive `test.only`, `it.only`, `describe.only`, `suite.only`, Playwright `test.describe.only` and common modifier chains in JS/TS-family sources. `fit` / `fdescribe` aliases are recognized in conventional test paths. Supports contiguous multiline chains and ignores literal/comment examples. Default blocking rule; an existing explicit `failOn` list must include it to block. This does not resolve imports or custom aliases; configure your runner's native forbid-only option too when available ([Playwright](https://playwright.dev/docs/api/class-testconfig#test-config-forbid-only), [Jest API](https://jestjs.io/docs/api#testonlyname-fn-timeout)).
|
|
279
294
|
- **Skipped tests (`test-skip`):** `it.skip`, `describe.skip`, `xit`, `pytest.skip`, `test.todo` added to silence failing tests. (Default blocking gate).
|
|
280
295
|
- **Trivial assertions (`trivial-assert`):** Tautological or dummy assertions added to fabricate test green checks (`expect(true).toBe(true)`, `expect(1).toBe(1)`, `assert True`, `self.assertTrue(True)`).
|
|
281
296
|
- **Stub abandonment (`stub-abandonment`):** Dummy stubs left unimplemented in committed code (`throw new Error("Not implemented")`, `raise NotImplementedError`, Rust `todo!()` / `unimplemented!()`).
|
|
@@ -310,7 +325,8 @@ stubs, alongside a compiler, test runner and language-aware linter. Findings ide
|
|
|
310
325
|
patterns, not proof of AI authorship or developer intent. Deliberate skips and abstract
|
|
311
326
|
methods can be legitimate. Contiguous block comments, Python docstrings and multi-line empty handlers are
|
|
312
327
|
recognized. Template interpolation, complex language syntax and context outside
|
|
313
|
-
the added diff are not fully parsed.
|
|
328
|
+
the added diff in legacy audit modes are not fully parsed. Custom test API aliases,
|
|
329
|
+
computed property calls and type/import resolution are not handled. Panic loops are an observational history heuristic.
|
|
314
330
|
The discipline score summarizes added history; it does not certify current code quality.
|
|
315
331
|
`check` reports actionable findings without assigning a personality or quality score.
|
|
316
332
|
|
|
@@ -323,7 +339,11 @@ npm test
|
|
|
323
339
|
```
|
|
324
340
|
|
|
325
341
|
`npm test` builds the distribution first so CLI integration and bundle checks run
|
|
326
|
-
against current code. Regression tests use disposable Git repositories.
|
|
342
|
+
against current code. Regression tests use disposable Git repositories. The programmatic
|
|
343
|
+
`auditPullRequest({ staged: true })` API retains the legacy added-lines contract;
|
|
344
|
+
use `reviewStagedChanges()` for the new context comparison and `ReviewResult`.
|
|
345
|
+
The composite Action installs exactly 0.7.0. For local development, use
|
|
346
|
+
`node dist/cli.js --staged` after building.
|
|
327
347
|
|
|
328
348
|
### Author & Support
|
|
329
349
|
|
|
@@ -640,6 +640,12 @@ var SourceMasker = class {
|
|
|
640
640
|
|
|
641
641
|
// src/rules.ts
|
|
642
642
|
var RULE_GUIDES = {
|
|
643
|
+
"test-focus": {
|
|
644
|
+
title: "Focused test narrows the test run",
|
|
645
|
+
why: "An exclusive test or suite can leave other tests unexecuted while the selected tests pass.",
|
|
646
|
+
suggestion: "Remove .only (or replace fit/fdescribe with it/describe) and run the full suite. Enable your test runner's forbid-only setting in CI when available.",
|
|
647
|
+
example: 'test.only("auth", ...) \u2192 test("auth", ...), then run the full suite'
|
|
648
|
+
},
|
|
643
649
|
"test-skip": {
|
|
644
650
|
title: "Skipped or disabled test",
|
|
645
651
|
why: "The test runner may report success without executing this behavior check.",
|
|
@@ -689,12 +695,13 @@ var VALID_DETECTORS = [
|
|
|
689
695
|
"swallowed-error",
|
|
690
696
|
"panic-loop",
|
|
691
697
|
"trivial-assert",
|
|
692
|
-
"stub-abandonment"
|
|
698
|
+
"stub-abandonment",
|
|
699
|
+
"test-focus"
|
|
693
700
|
];
|
|
694
701
|
|
|
695
702
|
// src/config.ts
|
|
696
703
|
var DEFAULT_CONFIG = {
|
|
697
|
-
failOn: ["test-skip", "trivial-assert"],
|
|
704
|
+
failOn: ["test-skip", "trivial-assert", "test-focus"],
|
|
698
705
|
ignorePaths: [
|
|
699
706
|
"**/fixtures/**",
|
|
700
707
|
"**/__fixtures__/**",
|
|
@@ -709,6 +716,7 @@ var DEFAULT_CONFIG = {
|
|
|
709
716
|
rules: {
|
|
710
717
|
"test-skip": "error",
|
|
711
718
|
"trivial-assert": "error",
|
|
719
|
+
"test-focus": "error",
|
|
712
720
|
"swallowed-error": "warn",
|
|
713
721
|
"stub-abandonment": "warn",
|
|
714
722
|
"type-escape": "warn"
|
|
@@ -840,6 +848,7 @@ var TEST_SKIP_PATTERNS = [
|
|
|
840
848
|
{ regex: /\bpytest\.skip\(/, name: "imperative pytest skip (pytest.skip)" },
|
|
841
849
|
{ regex: /\btest\.todo\(/, name: "todo placeholder test" }
|
|
842
850
|
];
|
|
851
|
+
var FOCUSED_TEST = /(?<![\w$.])(?:it|test|describe|suite)(?:\s*\.\s*(?:concurrent|serial|parallel|describe))*\s*\.\s*only\b(?=\s*(?:\(|\.\s*(?:each|failing|concurrent|serial|parallel)\b))|(?<![\w$.])(?:fit|fdescribe)\b(?=\s*(?:\(|\.\s*each\b))/g;
|
|
843
852
|
var COMMENT_DIRECTIVE_PATTERNS = [
|
|
844
853
|
{ regex: /^\s*\/\/\s*@ts-ignore\b/, name: "compiler ignore (@ts-ignore)" },
|
|
845
854
|
{ regex: /^\s*\/\/\s*@ts-expect-error\b/, name: "compiler suppression (@ts-expect-error)" },
|
|
@@ -879,6 +888,8 @@ var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
879
888
|
".jsx",
|
|
880
889
|
".mjs",
|
|
881
890
|
".cjs",
|
|
891
|
+
".mts",
|
|
892
|
+
".cts",
|
|
882
893
|
".py",
|
|
883
894
|
".go",
|
|
884
895
|
".rs",
|
|
@@ -1024,6 +1035,35 @@ function auditFileDiffs(files, commitHash = "HEAD", commitDate = (/* @__PURE__ *
|
|
|
1024
1035
|
}
|
|
1025
1036
|
}
|
|
1026
1037
|
}
|
|
1038
|
+
if (/\.(?:[cm]?[jt]sx?|vue|svelte)$/i.test(file.path)) {
|
|
1039
|
+
let source = "";
|
|
1040
|
+
const offsets = [];
|
|
1041
|
+
for (let i = 0; i < maskedLines.length; i++) {
|
|
1042
|
+
if (i) source += maskedLines[i].number === maskedLines[i - 1].number + 1 ? "\n" : "\0";
|
|
1043
|
+
offsets.push(source.length);
|
|
1044
|
+
source += maskedLines[i].code;
|
|
1045
|
+
}
|
|
1046
|
+
let row = 0;
|
|
1047
|
+
for (const match of source.matchAll(FOCUSED_TEST)) {
|
|
1048
|
+
let previous = match.index - 1;
|
|
1049
|
+
while (previous >= 0 && /\s/.test(source[previous])) previous--;
|
|
1050
|
+
if (source[previous] === ".") continue;
|
|
1051
|
+
if (/^(?:fit|fdescribe)$/.test(match[0]) && !isTestFile(file.path)) continue;
|
|
1052
|
+
if (/\bfunction$/.test(source.slice(Math.max(0, previous - 8), previous + 1))) continue;
|
|
1053
|
+
while (row + 1 < offsets.length && offsets[row + 1] <= match.index) row++;
|
|
1054
|
+
let end = row;
|
|
1055
|
+
while (end + 1 < offsets.length && offsets[end + 1] < match.index + match[0].length) end++;
|
|
1056
|
+
infractions.push({
|
|
1057
|
+
type: "test-focus",
|
|
1058
|
+
file: file.path,
|
|
1059
|
+
lineNumber: maskedLines[row].number,
|
|
1060
|
+
lineSnippet: maskedLines.slice(row, end + 1).map((line) => line.raw.trim()).join(" "),
|
|
1061
|
+
commitHash,
|
|
1062
|
+
commitDate,
|
|
1063
|
+
reason: "focused test or suite excludes other tests from the run"
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1027
1067
|
for (let index = 0; index < maskedLines.length; index++) {
|
|
1028
1068
|
const start = maskedLines[index];
|
|
1029
1069
|
const isCatch = /\bcatch\s*(?:\([^)]*\))?\s*\{\s*$/.test(start.code);
|
|
@@ -1067,6 +1107,7 @@ var MetricsCollector = class {
|
|
|
1067
1107
|
totalCommits = 0;
|
|
1068
1108
|
aiCommits = 0;
|
|
1069
1109
|
testSkips = [];
|
|
1110
|
+
focusedTests = [];
|
|
1070
1111
|
typeEscapes = [];
|
|
1071
1112
|
swallowedErrors = [];
|
|
1072
1113
|
panicLoops = [];
|
|
@@ -1173,6 +1214,7 @@ var MetricsCollector = class {
|
|
|
1173
1214
|
});
|
|
1174
1215
|
const buckets = {
|
|
1175
1216
|
"test-skip": this.testSkips,
|
|
1217
|
+
"test-focus": this.focusedTests,
|
|
1176
1218
|
"type-escape": this.typeEscapes,
|
|
1177
1219
|
"swallowed-error": this.swallowedErrors,
|
|
1178
1220
|
"trivial-assert": this.trivialAsserts,
|
|
@@ -1190,7 +1232,7 @@ var MetricsCollector = class {
|
|
|
1190
1232
|
this.flushPanicChain();
|
|
1191
1233
|
const linesSample = this.includeAll ? this.totalLinesAdded : this.aiLinesAdded;
|
|
1192
1234
|
const isSufficientData = linesSample >= MIN_SAMPLE_LOC;
|
|
1193
|
-
const codeInfractionPoints = this.testSkips.length * 15 + this.trivialAsserts.length * 15 + this.swallowedErrors.length * 10 + this.stubAbandonments.length * 8 + this.typeEscapes.length * 3;
|
|
1235
|
+
const codeInfractionPoints = this.testSkips.length * 15 + this.focusedTests.length * 15 + this.trivialAsserts.length * 15 + this.swallowedErrors.length * 10 + this.stubAbandonments.length * 8 + this.typeEscapes.length * 3;
|
|
1194
1236
|
let panicInfractionPoints = 0;
|
|
1195
1237
|
for (const loop of this.panicLoops) {
|
|
1196
1238
|
const countMatch = loop.reason.match(/chain of (\d+) quick fixes/);
|
|
@@ -1259,6 +1301,7 @@ var MetricsCollector = class {
|
|
|
1259
1301
|
totalCommits: this.totalCommits,
|
|
1260
1302
|
aiCommits: this.aiCommits,
|
|
1261
1303
|
testSkips: this.testSkips,
|
|
1304
|
+
focusedTests: this.focusedTests,
|
|
1262
1305
|
typeEscapes: this.typeEscapes,
|
|
1263
1306
|
swallowedErrors: this.swallowedErrors,
|
|
1264
1307
|
panicLoops: this.panicLoops,
|
|
@@ -1315,7 +1358,7 @@ function formatTerminalCard(summary, options) {
|
|
|
1315
1358
|
lines.push(` ${pc.dim(summary.archetypeDescription)}`);
|
|
1316
1359
|
lines.push("");
|
|
1317
1360
|
}
|
|
1318
|
-
const totalInfractions = (summary.testSkips?.length || 0) + (summary.typeEscapes?.length || 0) + (summary.swallowedErrors?.length || 0) + (summary.panicLoops?.length || 0) + (summary.trivialAsserts?.length || 0) + (summary.stubAbandonments?.length || 0);
|
|
1361
|
+
const totalInfractions = (summary.testSkips?.length || 0) + (summary.focusedTests?.length || 0) + (summary.typeEscapes?.length || 0) + (summary.swallowedErrors?.length || 0) + (summary.panicLoops?.length || 0) + (summary.trivialAsserts?.length || 0) + (summary.stubAbandonments?.length || 0);
|
|
1319
1362
|
lines.push(pc.bold(` INFRACTIONS (${totalInfractions})`));
|
|
1320
1363
|
lines.push(pc.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1321
1364
|
if (summary.panicLoops && summary.panicLoops.length > 0) {
|
|
@@ -1334,6 +1377,11 @@ function formatTerminalCard(summary, options) {
|
|
|
1334
1377
|
lines.push(` ${pc.dim(`${sample.file}: ${sample.lineSnippet || ""}`)}`);
|
|
1335
1378
|
}
|
|
1336
1379
|
}
|
|
1380
|
+
if (summary.focusedTests?.length) {
|
|
1381
|
+
lines.push(` ${pc.red("\u25CF")} ${pc.bold("test-focus")} (${summary.focusedTests.length}) ${pc.dim("exclusive tests narrow the test run")}`);
|
|
1382
|
+
const sample = summary.focusedTests[0];
|
|
1383
|
+
if (sample.file) lines.push(` ${pc.dim(`${terminalText(sample.file)}: ${terminalText(sample.lineSnippet || "")}`)}`);
|
|
1384
|
+
}
|
|
1337
1385
|
if (summary.testSkips && summary.testSkips.length > 0) {
|
|
1338
1386
|
lines.push(
|
|
1339
1387
|
` ${pc.red("\u25CF")} ${pc.bold("test-skip")} (${summary.testSkips.length}) ${pc.dim("skipped or disabled tests")}`
|
|
@@ -1388,6 +1436,7 @@ function formatTerminalCard(summary, options) {
|
|
|
1388
1436
|
}
|
|
1389
1437
|
const allInfractions = [
|
|
1390
1438
|
...summary.testSkips || [],
|
|
1439
|
+
...summary.focusedTests || [],
|
|
1391
1440
|
...summary.trivialAsserts || [],
|
|
1392
1441
|
...summary.swallowedErrors || [],
|
|
1393
1442
|
...summary.stubAbandonments || [],
|
|
@@ -1631,8 +1680,8 @@ function formatReviewTerminal(result) {
|
|
|
1631
1680
|
const status = !result.complete ? "INCOMPLETE" : result.passed ? "PASSED" : "BLOCKED";
|
|
1632
1681
|
const lines = [
|
|
1633
1682
|
"",
|
|
1634
|
-
pc.bold(`agent-roast \u2014 context review: ${status}`),
|
|
1635
|
-
` ${result.mergeBaseSha.slice(0, 7)} \u2192 ${result.headSha.slice(0, 7)} \xB7 policy
|
|
1683
|
+
pc.bold(`agent-roast \u2014 ${result.source === "index" ? "staged context review" : "context review"}: ${status}`),
|
|
1684
|
+
` ${result.mergeBaseSha.slice(0, 7)} \u2192 ${result.headSha.slice(0, result.source === "index" ? 18 : 7)} \xB7 policy: ${result.policySha === "EMPTY" ? "built-in first-commit defaults" : "base " + result.policySha.slice(0, 7)}`,
|
|
1636
1685
|
` ${result.filesInspected} changed files compared \xB7 ${result.linesInspected} source lines inspected across both versions`,
|
|
1637
1686
|
` ${result.infractions.length} new \xB7 ${result.unchangedCount} existing \xB7 ${result.removedFindings.length} no longer detected`
|
|
1638
1687
|
];
|
|
@@ -1655,9 +1704,9 @@ function formatReviewTerminal(result) {
|
|
|
1655
1704
|
function formatReviewMarkdown(result) {
|
|
1656
1705
|
const status = !result.complete ? "INCOMPLETE" : result.passed ? "PASSED" : "BLOCKED";
|
|
1657
1706
|
const lines = [
|
|
1658
|
-
`## agent-roast context review: ${status}`,
|
|
1707
|
+
`## agent-roast ${result.source === "index" ? "staged context review" : "context review"}: ${status}`,
|
|
1659
1708
|
"",
|
|
1660
|
-
`Compared ${result.filesInspected} changed files at \`${result.mergeBaseSha.slice(0, 7)}\` \u2192 \`${result.headSha.slice(0, 7)}\`. Policy: \`${result.policySha.slice(0, 7)}\`.`,
|
|
1709
|
+
`Compared ${result.filesInspected} changed files at \`${result.mergeBaseSha.slice(0, 7)}\` \u2192 \`${result.headSha.slice(0, result.source === "index" ? 18 : 7)}\`. Policy: \`${result.policySha === "EMPTY" ? "built-in first-commit defaults" : result.policySha.slice(0, 7)}\`.`,
|
|
1661
1710
|
"",
|
|
1662
1711
|
`**${result.infractions.length} new** \xB7 ${result.unchangedCount} existing \xB7 ${result.removedFindings.length} no longer detected.`,
|
|
1663
1712
|
""
|
|
@@ -1985,6 +2034,7 @@ Example: ${RULE_GUIDES[rule].example}` },
|
|
|
1985
2034
|
results,
|
|
1986
2035
|
properties: report.mode === "review" ? {
|
|
1987
2036
|
scope: "introduced findings in changed files",
|
|
2037
|
+
source: report.source ?? "commits",
|
|
1988
2038
|
baseSha: report.baseSha,
|
|
1989
2039
|
headSha: report.headSha,
|
|
1990
2040
|
mergeBaseSha: report.mergeBaseSha,
|
|
@@ -2063,7 +2113,7 @@ async function checkProject(options = {}) {
|
|
|
2063
2113
|
const cwd = path4.resolve(options.cwd || process.cwd());
|
|
2064
2114
|
const root = await fs4.realpath(git(cwd, ["rev-parse", "--show-toplevel"]).trim());
|
|
2065
2115
|
const config = loadConfig(root);
|
|
2066
|
-
const failOn = options.failOn ?? config.failOn ?? ["test-skip", "trivial-assert"];
|
|
2116
|
+
const failOn = options.failOn ?? config.failOn ?? ["test-skip", "trivial-assert", "test-focus"];
|
|
2067
2117
|
for (const rule of failOn) if (!VALID_DETECTORS.includes(rule)) throw new ConfigError(`Unknown rule '${rule}'.`);
|
|
2068
2118
|
const format = options.format || "terminal";
|
|
2069
2119
|
if (!["terminal", "json", "github", "markdown", "sarif"].includes(format)) throw new ConfigError("Unsupported check output format.");
|
|
@@ -2131,6 +2181,7 @@ async function checkProject(options = {}) {
|
|
|
2131
2181
|
import path5 from "path";
|
|
2132
2182
|
|
|
2133
2183
|
// src/snapshot.ts
|
|
2184
|
+
import { createHash as createHash2 } from "crypto";
|
|
2134
2185
|
import { spawn as spawn2 } from "child_process";
|
|
2135
2186
|
function assertNotAborted(signal) {
|
|
2136
2187
|
if (signal?.aborted) throw new ConfigError("Review cancelled; no complete result was produced.");
|
|
@@ -2177,6 +2228,16 @@ async function readGit(cwd, args, signal, limit = 8 * 1024 * 1024) {
|
|
|
2177
2228
|
if (signal?.aborted) abort();
|
|
2178
2229
|
});
|
|
2179
2230
|
}
|
|
2231
|
+
async function requireLocalObjects(cwd, signal) {
|
|
2232
|
+
try {
|
|
2233
|
+
const promises = (await readGit(cwd, ["config", "--type=bool", "--get-regexp", "^remote\\..*\\.promisor$"], signal, 64 * 1024)).toString();
|
|
2234
|
+
if (promises.split("\n").some((line) => /\s+(?:true|yes|on|1)\s*$/i.test(line))) {
|
|
2235
|
+
throw new ConfigError("Offline review requires a non-partial clone with local objects; promisor remotes are unsupported.");
|
|
2236
|
+
}
|
|
2237
|
+
} catch (error) {
|
|
2238
|
+
if (!(error instanceof GitError && error.code === 1)) throw error;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2180
2241
|
async function reviewRefs(cwd, base, head, signal) {
|
|
2181
2242
|
const resolve2 = async (ref) => {
|
|
2182
2243
|
if (!ref.trim()) throw new ConfigError("Review refs must not be empty.");
|
|
@@ -2187,14 +2248,7 @@ async function reviewRefs(cwd, base, head, signal) {
|
|
|
2187
2248
|
throw new ConfigError(`Cannot resolve review ref '${ref}'. Use valid commits and fetch complete history (actions/checkout fetch-depth: 0). ${error instanceof Error ? error.message : String(error)}`);
|
|
2188
2249
|
}
|
|
2189
2250
|
};
|
|
2190
|
-
|
|
2191
|
-
const promises = (await readGit(cwd, ["config", "--type=bool", "--get-regexp", "^remote\\..*\\.promisor$"], signal, 64 * 1024)).toString();
|
|
2192
|
-
if (promises.split("\n").some((line) => /\s+(?:true|yes|on|1)\s*$/i.test(line))) {
|
|
2193
|
-
throw new ConfigError("Offline review requires a non-partial clone with local objects; promisor remotes are unsupported.");
|
|
2194
|
-
}
|
|
2195
|
-
} catch (error) {
|
|
2196
|
-
if (!(error instanceof GitError && error.code === 1)) throw error;
|
|
2197
|
-
}
|
|
2251
|
+
await requireLocalObjects(cwd, signal);
|
|
2198
2252
|
const baseSha = await resolve2(base);
|
|
2199
2253
|
const headSha = await resolve2(head);
|
|
2200
2254
|
let ancestors;
|
|
@@ -2209,6 +2263,9 @@ async function reviewRefs(cwd, base, head, signal) {
|
|
|
2209
2263
|
}
|
|
2210
2264
|
async function changedTreeFiles(cwd, before, after, signal) {
|
|
2211
2265
|
const buffer = await readGit(cwd, ["diff", "--raw", "-z", "--no-relative", "--no-abbrev", "--no-ext-diff", "--no-textconv", "--find-renames=50%", "-l1000", "--end-of-options", before, after, "--"], signal);
|
|
2266
|
+
return parseTreeChanges(buffer);
|
|
2267
|
+
}
|
|
2268
|
+
function parseTreeChanges(buffer) {
|
|
2212
2269
|
const tokens = new TextDecoder("utf-8", { fatal: true }).decode(buffer).split("\0");
|
|
2213
2270
|
if (tokens.pop() !== "") throw new ConfigError("Truncated Git change inventory.");
|
|
2214
2271
|
const changes = [];
|
|
@@ -2245,6 +2302,59 @@ async function reviewPolicy(cwd, baseSha, signal) {
|
|
|
2245
2302
|
}
|
|
2246
2303
|
return { config: configFromText("{}", "default review policy"), filename: null };
|
|
2247
2304
|
}
|
|
2305
|
+
async function stagedSnapshot(cwd, signal) {
|
|
2306
|
+
await requireLocalObjects(cwd, signal);
|
|
2307
|
+
const head = async () => {
|
|
2308
|
+
try {
|
|
2309
|
+
return (await readGit(cwd, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], signal, 1024)).toString().trim();
|
|
2310
|
+
} catch (error) {
|
|
2311
|
+
if (!(error instanceof GitError && error.code === 1)) throw error;
|
|
2312
|
+
const ref = (await readGit(cwd, ["symbolic-ref", "--quiet", "HEAD"], signal, 1024)).toString().trim();
|
|
2313
|
+
try {
|
|
2314
|
+
await readGit(cwd, ["show-ref", "--verify", "--quiet", ref], signal, 1024);
|
|
2315
|
+
} catch (missing) {
|
|
2316
|
+
if (missing instanceof GitError && missing.code === 1) return "EMPTY";
|
|
2317
|
+
throw missing;
|
|
2318
|
+
}
|
|
2319
|
+
throw new ConfigError("HEAD exists but cannot be resolved to a commit.");
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
const baseSha = await head();
|
|
2323
|
+
const inventory = () => readGit(cwd, [
|
|
2324
|
+
"diff",
|
|
2325
|
+
"--cached",
|
|
2326
|
+
"--raw",
|
|
2327
|
+
"-z",
|
|
2328
|
+
"--no-relative",
|
|
2329
|
+
"--no-abbrev",
|
|
2330
|
+
"--no-ext-diff",
|
|
2331
|
+
"--no-textconv",
|
|
2332
|
+
"--ignore-submodules=none",
|
|
2333
|
+
"--ita-invisible-in-index",
|
|
2334
|
+
"--find-renames=50%",
|
|
2335
|
+
"-l1000",
|
|
2336
|
+
"--end-of-options",
|
|
2337
|
+
...baseSha === "EMPTY" ? [] : [baseSha],
|
|
2338
|
+
"--"
|
|
2339
|
+
], signal);
|
|
2340
|
+
const conflicts = await readGit(cwd, ["ls-files", "--unmerged", "-z", "--full-name"], signal);
|
|
2341
|
+
if (conflicts.length) throw new ConfigError("Resolve unmerged index entries before reviewing staged changes.");
|
|
2342
|
+
const pinned = await inventory();
|
|
2343
|
+
const changes = parseTreeChanges(pinned);
|
|
2344
|
+
const headSha = "INDEX:" + createHash2("sha256").update(baseSha).update(pinned).digest("hex");
|
|
2345
|
+
return {
|
|
2346
|
+
baseSha,
|
|
2347
|
+
headSha,
|
|
2348
|
+
mergeBaseSha: baseSha,
|
|
2349
|
+
changes,
|
|
2350
|
+
verify: async () => {
|
|
2351
|
+
assertNotAborted(signal);
|
|
2352
|
+
if (await head() !== baseSha || !(await inventory()).equals(pinned)) {
|
|
2353
|
+
throw new ConfigError("HEAD or staged changes changed during review; rerun before committing. No complete result was produced.");
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2248
2358
|
|
|
2249
2359
|
// src/review.ts
|
|
2250
2360
|
var GENERATED_PATHS2 = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.min.js", "**/*.map"];
|
|
@@ -2268,21 +2378,29 @@ function compareFindings(before, after, newPath) {
|
|
|
2268
2378
|
}
|
|
2269
2379
|
return { introduced, removed: [...old.values()].flatMap((bucket) => bucket.findings.slice(bucket.matched)), unchanged };
|
|
2270
2380
|
}
|
|
2271
|
-
|
|
2381
|
+
function reviewChanges(options) {
|
|
2382
|
+
return compareSourceChanges({ ...options, staged: false });
|
|
2383
|
+
}
|
|
2384
|
+
function reviewStagedChanges(options = {}) {
|
|
2385
|
+
return compareSourceChanges({ ...options, staged: true });
|
|
2386
|
+
}
|
|
2387
|
+
async function compareSourceChanges(options) {
|
|
2272
2388
|
assertNotAborted(options.signal);
|
|
2273
2389
|
const cwd = path5.resolve(options.cwd || process.cwd());
|
|
2274
|
-
const
|
|
2275
|
-
const
|
|
2390
|
+
const staged = options.staged ? await stagedSnapshot(cwd, options.signal) : void 0;
|
|
2391
|
+
const refs = options.staged ? { baseSha: staged.baseSha, headSha: staged.headSha, mergeBaseSha: staged.mergeBaseSha } : await reviewRefs(cwd, options.base, options.head ?? "HEAD", options.signal);
|
|
2392
|
+
const policy = refs.baseSha === "EMPTY" ? { config: configFromText("{}", "default first-commit policy"), filename: null } : await reviewPolicy(cwd, refs.baseSha, options.signal);
|
|
2276
2393
|
const format = options.format ?? "terminal";
|
|
2277
2394
|
if (!["terminal", "json", "github", "markdown", "sarif"].includes(format)) throw new ConfigError("Unsupported review format.");
|
|
2278
|
-
const failOn = options.failOn ?? policy.config.failOn ?? ["test-skip", "trivial-assert"];
|
|
2395
|
+
const failOn = options.failOn ?? policy.config.failOn ?? ["test-skip", "trivial-assert", "test-focus"];
|
|
2279
2396
|
for (const rule of failOn) if (!VALID_DETECTORS.includes(rule)) throw new ConfigError(`Unknown review rule '${rule}'.`);
|
|
2280
|
-
const changes = await changedTreeFiles(cwd, refs.mergeBaseSha, refs.headSha, options.signal);
|
|
2397
|
+
const changes = staged?.changes ?? await changedTreeFiles(cwd, refs.mergeBaseSha, refs.headSha, options.signal);
|
|
2281
2398
|
const policyChanges = changes.filter((change) => CONFIG_FILENAMES.includes(change.oldPath) || CONFIG_FILENAMES.includes(change.newPath)).map((change) => change.newPath);
|
|
2282
2399
|
const ignores = [...GENERATED_PATHS2, ...policy.config.ignorePaths || []];
|
|
2283
2400
|
const result = {
|
|
2284
|
-
schemaVersion: "1.
|
|
2401
|
+
schemaVersion: "1.1.0",
|
|
2285
2402
|
mode: "review",
|
|
2403
|
+
source: staged ? "index" : "commits",
|
|
2286
2404
|
...refs,
|
|
2287
2405
|
policySha: refs.baseSha,
|
|
2288
2406
|
policyFile: policy.filename,
|
|
@@ -2355,6 +2473,7 @@ async function reviewChanges(options) {
|
|
|
2355
2473
|
}
|
|
2356
2474
|
}
|
|
2357
2475
|
assertNotAborted(options.signal);
|
|
2476
|
+
await staged?.verify();
|
|
2358
2477
|
result.complete = result.skippedFiles.length === 0;
|
|
2359
2478
|
result.failedRules = [...new Set(result.infractions.filter((finding) => failOn.includes(finding.type)).map((finding) => finding.type))];
|
|
2360
2479
|
result.passed = result.complete && result.failedRules.length === 0;
|
|
@@ -2514,6 +2633,7 @@ export {
|
|
|
2514
2633
|
checkProject,
|
|
2515
2634
|
compareFindings,
|
|
2516
2635
|
reviewChanges,
|
|
2636
|
+
reviewStagedChanges,
|
|
2517
2637
|
InvalidDetectorError,
|
|
2518
2638
|
roastRepository,
|
|
2519
2639
|
auditPullRequest
|
package/dist/cli.js
CHANGED
|
@@ -16,9 +16,10 @@ import {
|
|
|
16
16
|
initProject,
|
|
17
17
|
loadConfig,
|
|
18
18
|
reviewChanges,
|
|
19
|
+
reviewStagedChanges,
|
|
19
20
|
roastRepository,
|
|
20
21
|
writeBaseline
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-YGHNTWHX.js";
|
|
22
23
|
|
|
23
24
|
// src/cli.ts
|
|
24
25
|
import { Command } from "commander";
|
|
@@ -27,8 +28,39 @@ import * as fs from "fs";
|
|
|
27
28
|
import * as path from "path";
|
|
28
29
|
var require2 = createRequire(import.meta.url);
|
|
29
30
|
var pkg = require2("../package.json");
|
|
31
|
+
async function runContextReview(repoPath, options, staged) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const abort = () => controller.abort();
|
|
34
|
+
process.once("SIGINT", abort);
|
|
35
|
+
process.once("SIGTERM", abort);
|
|
36
|
+
try {
|
|
37
|
+
const common = {
|
|
38
|
+
cwd: repoPath,
|
|
39
|
+
failOn: options.failOn !== void 0 ? options.failOn.split(",").map((rule) => rule.trim()).filter(Boolean) : void 0,
|
|
40
|
+
format: options.json ? "json" : options.format,
|
|
41
|
+
signal: controller.signal
|
|
42
|
+
};
|
|
43
|
+
const result = staged ? await reviewStagedChanges(common) : await reviewChanges({ ...common, base: options.base, head: options.head });
|
|
44
|
+
if (process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, formatReviewMarkdown(result) + "\n");
|
|
45
|
+
if (process.env.GITHUB_OUTPUT) fs.appendFileSync(
|
|
46
|
+
process.env.GITHUB_OUTPUT,
|
|
47
|
+
`passed=${result.passed}
|
|
48
|
+
infractions-count=${result.infractions.length}
|
|
49
|
+
has-supported-changes=${result.filesInspected > 0}
|
|
50
|
+
complete=${result.complete}
|
|
51
|
+
existing-count=${result.unchangedCount}
|
|
52
|
+
removed-count=${result.removedFindings.length}
|
|
53
|
+
`
|
|
54
|
+
);
|
|
55
|
+
console.log(result.output);
|
|
56
|
+
process.exitCode = result.exitCode;
|
|
57
|
+
} finally {
|
|
58
|
+
process.removeListener("SIGINT", abort);
|
|
59
|
+
process.removeListener("SIGTERM", abort);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
30
62
|
var program = new Command();
|
|
31
|
-
program.name("agent-roast").description("Audit your git history or PR diffs for AI coding shortcuts and panic loops").version(pkg.version || "0.4.0").argument("[path]", "Path to git repository (default: current directory)", ".").option("-a, --all", "Audit ALL commits regardless of AI attribution tags", false).option("-s, --since <time>", "Time window for git analysis", "90 days ago").option("-v, --verbose", "Show commit SHAs and line snippets for all infractions", false).option("--json", "Output results as raw JSON", false).option("--staged", "
|
|
63
|
+
program.name("agent-roast").description("Audit your git history or PR diffs for AI coding shortcuts and panic loops").version(pkg.version || "0.4.0").argument("[path]", "Path to git repository (default: current directory)", ".").option("-a, --all", "Audit ALL commits regardless of AI attribution tags", false).option("-s, --since <time>", "Time window for git analysis", "90 days ago").option("-v, --verbose", "Show commit SHAs and line snippets for all infractions", false).option("--json", "Output results as raw JSON", false).option("--staged", "Compare full HEAD/index source and gate new staged findings (including the first commit)", false).option("--base <ref>", "Base git ref/branch for PR mode audit (e.g. origin/main)").option("--head <ref>", "Head git ref/SHA for PR mode audit (default: HEAD)", "HEAD").option("--fail-on <rules>", "Comma-separated detectors that trigger CI failure").option("--fail-under <score>", "Fail CI if agent score is below threshold (0-100)").option("--format <format>", "Output format: terminal, json, github, markdown (check/review also support sarif)", "terminal").option("--authors", "Display author names and emails in report", false).action(async (repoPath, options) => {
|
|
32
64
|
try {
|
|
33
65
|
if (process.env.GITHUB_EVENT_NAME === "pull_request_target") {
|
|
34
66
|
console.warn(
|
|
@@ -36,6 +68,13 @@ program.name("agent-roast").description("Audit your git history or PR diffs for
|
|
|
36
68
|
);
|
|
37
69
|
}
|
|
38
70
|
if (options.staged && options.base) throw new ConfigError("--staged cannot be combined with --base.");
|
|
71
|
+
if (options.staged) {
|
|
72
|
+
if (options.all || options.failUnder !== void 0 || program.getOptionValueSource("since") === "cli" || program.getOptionValueSource("head") === "cli") {
|
|
73
|
+
throw new ConfigError("--staged compares HEAD and index; --all, --since, --head and --fail-under do not apply.");
|
|
74
|
+
}
|
|
75
|
+
await runContextReview(repoPath, options, true);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
39
78
|
if (!["terminal", "json", "github", "markdown"].includes(options.format)) {
|
|
40
79
|
throw new ConfigError("--format must be terminal, json, github, or markdown.");
|
|
41
80
|
}
|
|
@@ -122,43 +161,16 @@ has-supported-changes=${prResult.hasSupportedChanges}
|
|
|
122
161
|
}
|
|
123
162
|
}
|
|
124
163
|
});
|
|
125
|
-
program.command("review").description("Compare full changed source at merge-base and head; gate only new findings").argument("[path]", "Path to the Git repository", ".").addHelpText("after", "\nRequired: --base <trusted-ref> Optional: --head <ref> (default HEAD)\nOutput: --format terminal|json|github|markdown|sarif, --json, --fail-on <rules>\nExample: agent-roast review --base origin/main --format markdown").action(async (repoPath) => {
|
|
164
|
+
program.command("review").description("Compare full changed source at merge-base and head; gate only new findings").argument("[path]", "Path to the Git repository", ".").addHelpText("after", "\nRequired: --base <trusted-ref> Optional: --head <ref> (default HEAD), or use --staged for HEAD/index\nOutput: --format terminal|json|github|markdown|sarif, --json, --fail-on <rules>\nExample: agent-roast review --base origin/main --format markdown").action(async (repoPath) => {
|
|
126
165
|
try {
|
|
127
166
|
const options = program.opts();
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
throw new ConfigError("review compares committed refs; --staged, --all, --since and --fail-under do not apply.");
|
|
167
|
+
if (options.staged ? options.base || program.getOptionValueSource("head") === "cli" : !options.base) {
|
|
168
|
+
throw new ConfigError("Use review --base <trusted-ref> or review --staged; --base/--head cannot be combined with --staged.");
|
|
131
169
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
process.once("SIGINT", abort);
|
|
135
|
-
process.once("SIGTERM", abort);
|
|
136
|
-
try {
|
|
137
|
-
const result = await reviewChanges({
|
|
138
|
-
cwd: repoPath,
|
|
139
|
-
base: options.base,
|
|
140
|
-
head: options.head,
|
|
141
|
-
failOn: options.failOn !== void 0 ? options.failOn.split(",").map((rule) => rule.trim()).filter(Boolean) : void 0,
|
|
142
|
-
format: options.json ? "json" : options.format,
|
|
143
|
-
signal: controller.signal
|
|
144
|
-
});
|
|
145
|
-
if (process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, formatReviewMarkdown(result) + "\n");
|
|
146
|
-
if (process.env.GITHUB_OUTPUT) fs.appendFileSync(
|
|
147
|
-
process.env.GITHUB_OUTPUT,
|
|
148
|
-
`passed=${result.passed}
|
|
149
|
-
infractions-count=${result.infractions.length}
|
|
150
|
-
has-supported-changes=${result.filesInspected > 0}
|
|
151
|
-
complete=${result.complete}
|
|
152
|
-
existing-count=${result.unchangedCount}
|
|
153
|
-
removed-count=${result.removedFindings.length}
|
|
154
|
-
`
|
|
155
|
-
);
|
|
156
|
-
console.log(result.output);
|
|
157
|
-
process.exitCode = result.exitCode;
|
|
158
|
-
} finally {
|
|
159
|
-
process.removeListener("SIGINT", abort);
|
|
160
|
-
process.removeListener("SIGTERM", abort);
|
|
170
|
+
if (options.all || options.failUnder !== void 0 || program.getOptionValueSource("since") === "cli") {
|
|
171
|
+
throw new ConfigError("review compares source snapshots; --all, --since and --fail-under do not apply.");
|
|
161
172
|
}
|
|
173
|
+
await runContextReview(repoPath, options, options.staged);
|
|
162
174
|
} catch (error) {
|
|
163
175
|
console.error(`agent-roast review error: ${error instanceof Error ? error.message : String(error)}`);
|
|
164
176
|
process.exitCode = 2;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ interface CommitInfo {
|
|
|
9
9
|
isAiAttributed: boolean;
|
|
10
10
|
aiSignatures: string[];
|
|
11
11
|
}
|
|
12
|
-
type DetectorType = "test-skip" | "type-escape" | "swallowed-error" | "panic-loop" | "trivial-assert" | "stub-abandonment";
|
|
12
|
+
type DetectorType = "test-skip" | "type-escape" | "swallowed-error" | "panic-loop" | "trivial-assert" | "stub-abandonment" | "test-focus";
|
|
13
13
|
declare const VALID_DETECTORS: readonly DetectorType[];
|
|
14
14
|
interface Infraction {
|
|
15
15
|
type: DetectorType;
|
|
@@ -28,6 +28,7 @@ interface MetricSummary {
|
|
|
28
28
|
totalCommits: number;
|
|
29
29
|
aiCommits: number;
|
|
30
30
|
testSkips: Infraction[];
|
|
31
|
+
focusedTests?: Infraction[];
|
|
31
32
|
typeEscapes: Infraction[];
|
|
32
33
|
swallowedErrors: Infraction[];
|
|
33
34
|
panicLoops: Infraction[];
|
|
@@ -118,9 +119,11 @@ interface ReviewOptions {
|
|
|
118
119
|
format?: ReviewFormat;
|
|
119
120
|
signal?: AbortSignal;
|
|
120
121
|
}
|
|
122
|
+
type StagedReviewOptions = Omit<ReviewOptions, "base" | "head">;
|
|
121
123
|
interface ReviewResult {
|
|
122
124
|
schemaVersion: string;
|
|
123
125
|
mode: "review";
|
|
126
|
+
source?: "commits" | "index";
|
|
124
127
|
baseSha: string;
|
|
125
128
|
headSha: string;
|
|
126
129
|
mergeBaseSha: string;
|
|
@@ -240,6 +243,7 @@ declare class MetricsCollector {
|
|
|
240
243
|
private totalCommits;
|
|
241
244
|
private aiCommits;
|
|
242
245
|
private testSkips;
|
|
246
|
+
private focusedTests;
|
|
243
247
|
private typeEscapes;
|
|
244
248
|
private swallowedErrors;
|
|
245
249
|
private panicLoops;
|
|
@@ -361,6 +365,7 @@ declare function compareFindings(before: Infraction[], after: Infraction[], newP
|
|
|
361
365
|
};
|
|
362
366
|
/** Compare full source blobs in changed files without executing or checking out the candidate. */
|
|
363
367
|
declare function reviewChanges(options: ReviewOptions): Promise<ReviewResult>;
|
|
368
|
+
declare function reviewStagedChanges(options?: StagedReviewOptions): Promise<ReviewResult>;
|
|
364
369
|
|
|
365
370
|
/** Offline SARIF 2.1.0, with relative URIs and explicit incomplete-run status.
|
|
366
371
|
* Snippets are omitted to avoid embedding source code in integration artifacts. */
|
|
@@ -404,4 +409,4 @@ declare function roastRepository(options?: RoastOptions): Promise<{
|
|
|
404
409
|
}>;
|
|
405
410
|
declare function auditPullRequest(options: PrAuditOptions): Promise<PrAuditResult>;
|
|
406
411
|
|
|
407
|
-
export { type AgentRoastConfig, type AiAttributionResult, CONFIG_FILENAMES, type CheckOptions, type CheckResult, type CommitInfo, ConfigError, DEFAULT_CONFIG, DEFAULT_CONFIG_TEMPLATE, type DetectorType, FIELD_SEPARATOR, type FindingBaseline, GITHUB_WORKFLOW_TEMPLATE, GitError, GitResolutionError, HERMETIC_GIT_FLAGS, type Infraction, type InitOptions, type InitResult, InvalidDetectorError, type MetricSummary, MetricsCollector, PRE_COMMIT_HOOK_TEMPLATE, type ParseDiffResult, type ParsedCommit, type ParsedDiffLine, type ParsedFileDiff, type PrAuditOptions, type PrAuditResult, RECORD_SEPARATOR, RULE_GUIDES, type ReviewFormat, type ReviewOptions, type ReviewResult, type RoastOptions, type RuleGuide, type ShieldsEndpointResponse, VALID_DETECTORS, applyBaseline, auditFileDiffs, auditPullRequest, checkProject, compareFindings, configFromText, createBaseline, detectAiAttribution, findMergeBase, findingFingerprint, formatCheckMarkdown, formatCheckTerminal, formatGithubAnnotations, formatGithubStepSummary, formatPrMarkdownComment, formatPrTerminalCard, formatReviewMarkdown, formatReviewTerminal, formatSarif, formatTerminalCard, generateShareUrl, generateShieldsJson, generateShieldsMarkdown, generateSvgBadge, getHermeticGitEnv, getScoreColor, initProject, isCodeFile, isTestFile, loadBaseline, loadConfig, matchIgnorePath, parseDiff, parseGitLogStream, resolveCommitSha, reviewChanges, roastRepository, streamGitDiff, streamGitLog, stripStringAndRegexLiterals, unquoteGitPath, writeBaseline };
|
|
412
|
+
export { type AgentRoastConfig, type AiAttributionResult, CONFIG_FILENAMES, type CheckOptions, type CheckResult, type CommitInfo, ConfigError, DEFAULT_CONFIG, DEFAULT_CONFIG_TEMPLATE, type DetectorType, FIELD_SEPARATOR, type FindingBaseline, GITHUB_WORKFLOW_TEMPLATE, GitError, GitResolutionError, HERMETIC_GIT_FLAGS, type Infraction, type InitOptions, type InitResult, InvalidDetectorError, type MetricSummary, MetricsCollector, PRE_COMMIT_HOOK_TEMPLATE, type ParseDiffResult, type ParsedCommit, type ParsedDiffLine, type ParsedFileDiff, type PrAuditOptions, type PrAuditResult, RECORD_SEPARATOR, RULE_GUIDES, type ReviewFormat, type ReviewOptions, type ReviewResult, type RoastOptions, type RuleGuide, type ShieldsEndpointResponse, type StagedReviewOptions, VALID_DETECTORS, applyBaseline, auditFileDiffs, auditPullRequest, checkProject, compareFindings, configFromText, createBaseline, detectAiAttribution, findMergeBase, findingFingerprint, formatCheckMarkdown, formatCheckTerminal, formatGithubAnnotations, formatGithubStepSummary, formatPrMarkdownComment, formatPrTerminalCard, formatReviewMarkdown, formatReviewTerminal, formatSarif, formatTerminalCard, generateShareUrl, generateShieldsJson, generateShieldsMarkdown, generateSvgBadge, getHermeticGitEnv, getScoreColor, initProject, isCodeFile, isTestFile, loadBaseline, loadConfig, matchIgnorePath, parseDiff, parseGitLogStream, resolveCommitSha, reviewChanges, reviewStagedChanges, roastRepository, streamGitDiff, streamGitLog, stripStringAndRegexLiterals, unquoteGitPath, writeBaseline };
|
package/dist/index.js
CHANGED
|
@@ -51,13 +51,14 @@ import {
|
|
|
51
51
|
parseGitLogStream,
|
|
52
52
|
resolveCommitSha,
|
|
53
53
|
reviewChanges,
|
|
54
|
+
reviewStagedChanges,
|
|
54
55
|
roastRepository,
|
|
55
56
|
streamGitDiff,
|
|
56
57
|
streamGitLog,
|
|
57
58
|
stripStringAndRegexLiterals,
|
|
58
59
|
unquoteGitPath,
|
|
59
60
|
writeBaseline
|
|
60
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-YGHNTWHX.js";
|
|
61
62
|
export {
|
|
62
63
|
CONFIG_FILENAMES,
|
|
63
64
|
ConfigError,
|
|
@@ -110,6 +111,7 @@ export {
|
|
|
110
111
|
parseGitLogStream,
|
|
111
112
|
resolveCommitSha,
|
|
112
113
|
reviewChanges,
|
|
114
|
+
reviewStagedChanges,
|
|
113
115
|
roastRepository,
|
|
114
116
|
streamGitDiff,
|
|
115
117
|
streamGitLog,
|