agent-roast 0.1.0 โ 0.1.2
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 +43 -54
- package/dist/{chunk-RFCXDN22.js โ chunk-EPPMO4JE.js} +55 -50
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,79 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
|
|
5
|
+
<img src="docs/assets/logo.svg" alt="agent-roast" width="340">
|
|
6
|
+
</picture>
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
### Audit your git history for AI coding agent infractions and panic loops
|
|
9
|
+
|
|
10
|
+
[](https://www.npmjs.com/package/agent-roast)
|
|
11
|
+
[-1f9d55)](#privacy)
|
|
7
12
|
[](LICENSE)
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
</div>
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
`agent-roast` scans recorded git diffs for shortcuts taken by AI coding assistants (Claude Code, Cursor, Aider). It checks for skipped tests, compiler type escapes (`as any`), swallowed errors, and panic revert loops, then outputs a discipline score and line proofs. 100% offline, zero API keys, zero telemetry.
|
|
12
17
|
|
|
13
18
|
```bash
|
|
14
|
-
npx agent-roast
|
|
19
|
+
npx agent-roast # scan AI-attributed commits (last 90 days)
|
|
20
|
+
npx agent-roast --all # scan entire repository history
|
|
21
|
+
npx agent-roast --verbose # show commit SHAs and line proofs
|
|
15
22
|
```
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### What it looks like:
|
|
24
|
+
What a report looks like (a real repository audit, trimmed):
|
|
20
25
|
|
|
21
26
|
```text
|
|
22
|
-
|
|
23
|
-
โ ๐ค AGENT ROAST DOSSIER โ
|
|
24
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
25
|
-
|
|
26
|
-
Repository: my-app
|
|
27
|
-
Commits: 18 AI-attributed / 42 total
|
|
28
|
-
Code Added: 4,210 lines (AI) / 12,480 lines (total)
|
|
29
|
-
|
|
30
|
-
AGENT ARCHETYPE: The Any Architect
|
|
31
|
-
Leans on compiler bypasses (`as any`, `@ts-ignore`) rather than strict type modeling.
|
|
32
|
-
|
|
33
|
-
DISCIPLINE SCORE: 68 / 100
|
|
34
|
-
(100 = spotless discipline, 0 = complete shortcut addiction)
|
|
35
|
-
|
|
36
|
-
๐ INFRACTIONS BREAKDOWN:
|
|
37
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
38
|
-
โ Skipped tests: 4 (it.skip, describe.skip, xit)
|
|
39
|
-
โฒ Type escapes: 16 (as any, @ts-ignore, eslint-disable)
|
|
40
|
-
โ Swallowed errors: 3 (empty catch {}, except: pass)
|
|
41
|
-
โป Panic fix loops: 2 (quick fixes < 15m apart)
|
|
42
|
-
|
|
43
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
44
|
-
๐ข Share your score on X: https://twitter.com/intent/tweet?text=...
|
|
45
|
-
Tip: Run with --verbose to see exact commit SHAs and lines
|
|
46
|
-
```
|
|
27
|
+
agent-roast โ git discipline audit (304 AI commits in Soup)
|
|
47
28
|
|
|
48
|
-
|
|
29
|
+
SCORE: 55 / 100 ยท The Panic Looper
|
|
30
|
+
Prone to rapid-fire fix and revert thrashing cycles when wrestling stubborn bugs.
|
|
49
31
|
|
|
50
|
-
|
|
32
|
+
INFRACTIONS DETECTED (42)
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
3. **Swallowed Errors:** Empty `catch {}` or `except: pass` blocks hiding production bugs.
|
|
55
|
-
4. **Panic Loops:** Consecutive quickfix/revert commits within 15 minutes by the agent.
|
|
34
|
+
โ panic-loop (15) โ consecutive quick fixes < 15m apart
|
|
35
|
+
latest: 3df29a1 -> a910f21 (3m apart)
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
โ type-escape (19) โ compiler type bypasses in added lines
|
|
38
|
+
e.g. src/client.ts:42 โ response.body as any
|
|
58
39
|
|
|
59
|
-
|
|
40
|
+
โ test-skip (6) โ disabled or skipped test suites
|
|
41
|
+
e.g. tests/test_outbound.py:18 โ pytest.skip(...)
|
|
60
42
|
|
|
61
|
-
|
|
62
|
-
npx agent-roast # Audit AI-attributed commits (Co-Authored-By: Claude, Cursor, Aider, etc.)
|
|
63
|
-
npx agent-roast --all # Audit ALL commits across the repository
|
|
64
|
-
npx agent-roast --verbose # Print exact commit SHAs and code lines as proof
|
|
65
|
-
npx agent-roast --since 30d # Audit last 30 days of git history
|
|
66
|
-
npx agent-roast --json # Output raw JSON metrics for CI pipelines
|
|
43
|
+
โ swallowed-error (2) โ empty catch blocks or except: pass
|
|
67
44
|
```
|
|
68
45
|
|
|
46
|
+
<img src="docs/assets/case-study.gif" alt="agent-roast git audit session" width="100%">
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### What it checks
|
|
51
|
+
|
|
52
|
+
Only **added lines** (`+`) in source files are scanned โ file moves and refactoring never trigger false alarms.
|
|
53
|
+
|
|
54
|
+
- **Skipped tests:** `it.skip`, `describe.skip`, `xit`, `pytest.skip`, `test.todo` added when tests fail.
|
|
55
|
+
- **Type escapes:** `as any`, `// @ts-ignore`, `// @ts-expect-error`, `# type: ignore`, `eslint-disable`.
|
|
56
|
+
- **Swallowed errors:** Empty `catch {}` or `except: pass` blocks hiding production errors.
|
|
57
|
+
- **Panic fix loops:** Rapid-fire fix and revert commits by the same author within 15 minutes.
|
|
58
|
+
|
|
69
59
|
---
|
|
70
60
|
|
|
71
|
-
### Privacy
|
|
61
|
+
### Privacy
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
- **
|
|
76
|
-
- Your source code never leaves your terminal.
|
|
63
|
+
- **100% Local:** Runs streaming `git log` on your machine in <200ms.
|
|
64
|
+
- **Zero Telemetry:** No cloud dependencies, no LLM API calls, no network traffic.
|
|
65
|
+
- **Hermetic:** Source code never leaves your computer.
|
|
77
66
|
|
|
78
67
|
---
|
|
79
68
|
|
|
@@ -425,71 +425,81 @@ var MetricsCollector = class {
|
|
|
425
425
|
// src/renderer.ts
|
|
426
426
|
import pc from "picocolors";
|
|
427
427
|
function generateShareUrl(summary) {
|
|
428
|
-
const text = `My AI coding agent scored ${summary.score}/100 on
|
|
429
|
-
|
|
430
|
-
Archetype: "${summary.archetype}"
|
|
428
|
+
const text = `My AI coding agent scored ${summary.score}/100 on agent-roast.
|
|
431
429
|
|
|
430
|
+
Archetype: ${summary.archetype}
|
|
432
431
|
Infractions:
|
|
433
432
|
\u2022 ${summary.testSkips.length} skipped tests
|
|
434
433
|
\u2022 ${summary.typeEscapes.length} type escapes
|
|
435
434
|
\u2022 ${summary.panicLoops.length} panic revert loops
|
|
436
435
|
|
|
437
|
-
Audit your repo
|
|
436
|
+
Audit your repo: npx agent-roast`;
|
|
438
437
|
return `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}`;
|
|
439
438
|
}
|
|
440
439
|
function formatTerminalCard(summary, options) {
|
|
441
440
|
const lines = [];
|
|
442
|
-
const boxW = 60;
|
|
443
|
-
const hLine = "\u2550".repeat(boxW - 2);
|
|
444
|
-
lines.push(pc.cyan(`\u2554${hLine}\u2557`));
|
|
445
|
-
lines.push(
|
|
446
|
-
pc.cyan("\u2551") + pc.bold(pc.white(" \u{1F916} AGENT ROAST DOSSIER ")) + pc.cyan("\u2551")
|
|
447
|
-
);
|
|
448
|
-
lines.push(pc.cyan(`\u255A${hLine}\u255D`));
|
|
449
441
|
lines.push("");
|
|
450
|
-
lines.push(` ${pc.dim("Repository:")} ${pc.bold(pc.white(options.repoName))}`);
|
|
451
442
|
lines.push(
|
|
452
|
-
|
|
443
|
+
`${pc.bold(pc.cyan("agent-roast"))} ${pc.dim("\u2014 git discipline audit")} ${pc.white(options.repoName)}`
|
|
453
444
|
);
|
|
454
445
|
lines.push(
|
|
455
|
-
` ${pc.dim("
|
|
446
|
+
` ${pc.dim("commits:")} ${pc.yellow(summary.aiCommits)} AI / ${summary.totalCommits} total ${pc.dim("lines added:")} ${pc.green(summary.aiLinesAdded)} AI / ${summary.totalLinesAdded} total`
|
|
456
447
|
);
|
|
457
448
|
lines.push("");
|
|
458
449
|
if (!summary.isSufficientData) {
|
|
459
|
-
lines.push(pc.yellow(`
|
|
460
|
-
lines.push(pc.dim("
|
|
450
|
+
lines.push(pc.yellow(` insufficient data (< 200 lines added by AI)`));
|
|
451
|
+
lines.push(pc.dim(" commit more code and run `npx agent-roast` again."));
|
|
461
452
|
lines.push("");
|
|
462
453
|
return lines.join("\n");
|
|
463
454
|
}
|
|
464
455
|
let scoreColor = pc.green;
|
|
465
456
|
if (summary.score < 60) scoreColor = pc.red;
|
|
466
457
|
else if (summary.score < 80) scoreColor = pc.yellow;
|
|
467
|
-
lines.push(` ${pc.bold("AGENT ARCHETYPE:")} ${pc.magenta(pc.bold(summary.archetype))}`);
|
|
468
|
-
lines.push(` ${pc.italic(summary.archetypeDescription)}`);
|
|
469
|
-
lines.push("");
|
|
470
|
-
lines.push(` ${pc.bold("DISCIPLINE SCORE:")} ${scoreColor(pc.bold(`${summary.score} / 100`))}`);
|
|
471
458
|
lines.push(
|
|
472
|
-
pc.
|
|
459
|
+
` ${pc.bold("SCORE:")} ${scoreColor(pc.bold(`${summary.score} / 100`))} ${pc.dim("\xB7")} ${pc.magenta(pc.bold(summary.archetype))}`
|
|
473
460
|
);
|
|
461
|
+
lines.push(` ${pc.dim(summary.archetypeDescription)}`);
|
|
474
462
|
lines.push("");
|
|
475
|
-
|
|
476
|
-
lines.push(pc.
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
463
|
+
const totalInfractions = summary.testSkips.length + summary.typeEscapes.length + summary.swallowedErrors.length + summary.panicLoops.length;
|
|
464
|
+
lines.push(pc.bold(` INFRACTIONS (${totalInfractions})`));
|
|
465
|
+
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"));
|
|
466
|
+
if (summary.panicLoops.length > 0) {
|
|
467
|
+
lines.push(
|
|
468
|
+
` ${pc.magenta("\u25CF")} ${pc.bold("panic-loop")} (${summary.panicLoops.length}) ${pc.dim("consecutive quick fixes < 15m apart")}`
|
|
469
|
+
);
|
|
470
|
+
const sample = summary.panicLoops[0];
|
|
471
|
+
lines.push(` ${pc.dim(sample.reason)}`);
|
|
472
|
+
}
|
|
473
|
+
if (summary.typeEscapes.length > 0) {
|
|
474
|
+
lines.push(
|
|
475
|
+
` ${pc.yellow("\u25CF")} ${pc.bold("type-escape")} (${summary.typeEscapes.length}) ${pc.dim("compiler bypasses (as any, @ts-ignore)")}`
|
|
476
|
+
);
|
|
477
|
+
const sample = summary.typeEscapes[0];
|
|
478
|
+
if (sample.file) {
|
|
479
|
+
lines.push(` ${pc.dim(`${sample.file}: ${sample.lineSnippet || ""}`)}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (summary.testSkips.length > 0) {
|
|
483
|
+
lines.push(
|
|
484
|
+
` ${pc.red("\u25CF")} ${pc.bold("test-skip")} (${summary.testSkips.length}) ${pc.dim("skipped or disabled tests")}`
|
|
485
|
+
);
|
|
486
|
+
const sample = summary.testSkips[0];
|
|
487
|
+
if (sample.file) {
|
|
488
|
+
lines.push(` ${pc.dim(`${sample.file}: ${sample.lineSnippet || ""}`)}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (summary.swallowedErrors.length > 0) {
|
|
492
|
+
lines.push(
|
|
493
|
+
` ${pc.red("\u25CF")} ${pc.bold("swallowed-error")} (${summary.swallowedErrors.length}) ${pc.dim("empty catch blocks or except: pass")}`
|
|
494
|
+
);
|
|
495
|
+
const sample = summary.swallowedErrors[0];
|
|
496
|
+
if (sample.file) {
|
|
497
|
+
lines.push(` ${pc.dim(`${sample.file}: ${sample.lineSnippet || ""}`)}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (totalInfractions === 0) {
|
|
501
|
+
lines.push(` ${pc.green("\u25CF")} ${pc.dim("zero infractions detected across all inspected lines.")}`);
|
|
502
|
+
}
|
|
493
503
|
lines.push("");
|
|
494
504
|
if (options.verbose) {
|
|
495
505
|
const allInfractions = [
|
|
@@ -499,27 +509,22 @@ function formatTerminalCard(summary, options) {
|
|
|
499
509
|
...summary.panicLoops
|
|
500
510
|
];
|
|
501
511
|
if (allInfractions.length > 0) {
|
|
502
|
-
lines.push(pc.bold("
|
|
503
|
-
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"));
|
|
512
|
+
lines.push(pc.bold(" EVIDENCE (First 10 occurrences):"));
|
|
513
|
+
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"));
|
|
504
514
|
for (const inf of allInfractions.slice(0, 10)) {
|
|
505
|
-
const fileLoc = inf.file ? `${inf.file}` : "
|
|
515
|
+
const fileLoc = inf.file ? `${inf.file}` : "commit history";
|
|
506
516
|
lines.push(
|
|
507
|
-
` \
|
|
517
|
+
` \u25CF ${pc.cyan(inf.commitHash.slice(0, 7))} [${inf.type}] ${pc.white(fileLoc)}`
|
|
508
518
|
);
|
|
509
519
|
if (inf.lineSnippet) {
|
|
510
|
-
lines.push(`
|
|
520
|
+
lines.push(` ${pc.dim(`\u2514\u2500 ${inf.lineSnippet}`)}`);
|
|
511
521
|
}
|
|
512
522
|
}
|
|
513
523
|
lines.push("");
|
|
514
524
|
}
|
|
515
525
|
}
|
|
516
|
-
const shareUrl = generateShareUrl(summary);
|
|
517
|
-
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"));
|
|
518
|
-
lines.push(
|
|
519
|
-
` ${pc.cyan("\u{1F4E2} Share your score on X:")} ${pc.underline(pc.cyan(shareUrl))}`
|
|
520
|
-
);
|
|
521
526
|
lines.push(
|
|
522
|
-
` ${pc.dim("
|
|
527
|
+
` ${pc.dim("Share score:")} ${pc.cyan(generateShareUrl(summary))}`
|
|
523
528
|
);
|
|
524
529
|
lines.push("");
|
|
525
530
|
return lines.join("\n");
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-roast",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Zero-telemetry CLI scorecard analyzing git history for AI coding agent infractions and panic loops",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
"picocolors": "^1.1.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
+
"@napi-rs/canvas": "^1.0.10",
|
|
47
48
|
"@types/node": "^20.0.0",
|
|
49
|
+
"gifenc": "^1.0.3",
|
|
48
50
|
"tsup": "^8.0.0",
|
|
49
51
|
"typescript": "^5.0.0",
|
|
50
52
|
"vitest": "^3.0.0"
|