agent-roast 0.1.3 → 0.2.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 +74 -15
- package/dist/chunk-FHRPVXCI.js +1298 -0
- package/dist/cli.js +70 -14
- package/dist/index.d.ts +114 -5
- package/dist/index.js +41 -3
- package/package.json +1 -1
- package/dist/chunk-EPPMO4JE.js +0 -560
package/dist/cli.js
CHANGED
|
@@ -1,31 +1,87 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
GitError,
|
|
4
|
+
GitResolutionError,
|
|
5
|
+
InvalidDetectorError,
|
|
6
|
+
auditPullRequest,
|
|
7
|
+
formatGithubStepSummary,
|
|
3
8
|
roastRepository
|
|
4
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-FHRPVXCI.js";
|
|
5
10
|
|
|
6
11
|
// src/cli.ts
|
|
7
12
|
import { Command } from "commander";
|
|
8
13
|
import { createRequire } from "module";
|
|
14
|
+
import * as fs from "fs";
|
|
9
15
|
var require2 = createRequire(import.meta.url);
|
|
10
16
|
var pkg = require2("../package.json");
|
|
11
17
|
var program = new Command();
|
|
12
|
-
program.name("agent-roast").description("Audit your git history for AI coding
|
|
18
|
+
program.name("agent-roast").description("Audit your git history or PR diffs for AI coding shortcuts and panic loops").version(pkg.version || "0.2.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("--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", "test-skip").option("--format <format>", "Output format: terminal, json, github", "terminal").option("--authors", "Display author names and emails in report", false).action(async (repoPath, options) => {
|
|
13
19
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
if (process.env.GITHUB_EVENT_NAME === "pull_request_target") {
|
|
21
|
+
console.warn(
|
|
22
|
+
"Warning: pull_request_target runs with elevated repository privileges. agent-roast recommends running on 'pull_request'."
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (options.base) {
|
|
26
|
+
const failOnRules = options.failOn ? options.failOn.split(",").map((s) => s.trim()).filter(Boolean) : ["test-skip"];
|
|
27
|
+
const chosenFormat = options.json ? "json" : options.format || "terminal";
|
|
28
|
+
const prResult = await auditPullRequest({
|
|
29
|
+
cwd: repoPath,
|
|
30
|
+
base: options.base,
|
|
31
|
+
head: options.head || "HEAD",
|
|
32
|
+
failOn: failOnRules,
|
|
33
|
+
format: chosenFormat,
|
|
34
|
+
verbose: options.verbose,
|
|
35
|
+
authors: options.authors
|
|
36
|
+
});
|
|
37
|
+
if (process.env.GITHUB_STEP_SUMMARY) {
|
|
38
|
+
try {
|
|
39
|
+
fs.appendFileSync(
|
|
40
|
+
process.env.GITHUB_STEP_SUMMARY,
|
|
41
|
+
formatGithubStepSummary(prResult) + "\n"
|
|
42
|
+
);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error(`Warning: Failed to write to GITHUB_STEP_SUMMARY: ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (process.env.GITHUB_OUTPUT) {
|
|
48
|
+
try {
|
|
49
|
+
fs.appendFileSync(
|
|
50
|
+
process.env.GITHUB_OUTPUT,
|
|
51
|
+
`passed=${prResult.passed}
|
|
52
|
+
infractions-count=${prResult.infractions.length}
|
|
53
|
+
has-supported-changes=${prResult.hasSupportedChanges}
|
|
54
|
+
`
|
|
55
|
+
);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error(`Warning: Failed to write to GITHUB_OUTPUT: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log(prResult.output);
|
|
61
|
+
process.exitCode = prResult.exitCode;
|
|
23
62
|
} else {
|
|
24
|
-
|
|
63
|
+
const result = await roastRepository({
|
|
64
|
+
cwd: repoPath,
|
|
65
|
+
since: options.since,
|
|
66
|
+
all: options.all,
|
|
67
|
+
verbose: options.verbose,
|
|
68
|
+
json: options.json
|
|
69
|
+
});
|
|
70
|
+
if (options.json) {
|
|
71
|
+
console.log(JSON.stringify(result.summary, null, 2));
|
|
72
|
+
} else {
|
|
73
|
+
console.log(result.output);
|
|
74
|
+
}
|
|
75
|
+
process.exitCode = 0;
|
|
25
76
|
}
|
|
26
77
|
} catch (err) {
|
|
27
|
-
|
|
28
|
-
|
|
78
|
+
if (err instanceof GitResolutionError || err instanceof InvalidDetectorError || err instanceof GitError || err.name === "GitError") {
|
|
79
|
+
console.error(`agent-roast error: ${err.message}`);
|
|
80
|
+
process.exitCode = 2;
|
|
81
|
+
} else {
|
|
82
|
+
console.error(`Error auditing repository: ${err.message}`);
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
}
|
|
29
85
|
}
|
|
30
86
|
});
|
|
31
87
|
program.parse(process.argv);
|
package/dist/index.d.ts
CHANGED
|
@@ -9,15 +9,19 @@ interface CommitInfo {
|
|
|
9
9
|
isAiAttributed: boolean;
|
|
10
10
|
aiSignatures: string[];
|
|
11
11
|
}
|
|
12
|
+
type DetectorType = "test-skip" | "type-escape" | "swallowed-error" | "panic-loop";
|
|
13
|
+
declare const VALID_DETECTORS: readonly DetectorType[];
|
|
12
14
|
interface Infraction {
|
|
13
|
-
type:
|
|
15
|
+
type: DetectorType;
|
|
14
16
|
file?: string;
|
|
17
|
+
lineNumber?: number;
|
|
15
18
|
lineSnippet?: string;
|
|
16
19
|
commitHash: string;
|
|
17
20
|
commitDate: string;
|
|
18
21
|
reason: string;
|
|
19
22
|
}
|
|
20
23
|
interface MetricSummary {
|
|
24
|
+
schemaVersion: string;
|
|
21
25
|
totalLinesAdded: number;
|
|
22
26
|
aiLinesAdded: number;
|
|
23
27
|
totalCommits: number;
|
|
@@ -27,10 +31,12 @@ interface MetricSummary {
|
|
|
27
31
|
swallowedErrors: Infraction[];
|
|
28
32
|
panicLoops: Infraction[];
|
|
29
33
|
rawScore: number;
|
|
30
|
-
score: number;
|
|
34
|
+
score: number | null;
|
|
31
35
|
isSufficientData: boolean;
|
|
32
36
|
archetype: string;
|
|
33
37
|
archetypeDescription: string;
|
|
38
|
+
skippedLongLines?: number;
|
|
39
|
+
skippedUnknownFiles?: number;
|
|
34
40
|
}
|
|
35
41
|
interface RoastOptions {
|
|
36
42
|
cwd?: string;
|
|
@@ -38,6 +44,33 @@ interface RoastOptions {
|
|
|
38
44
|
all?: boolean;
|
|
39
45
|
verbose?: boolean;
|
|
40
46
|
json?: boolean;
|
|
47
|
+
authors?: boolean;
|
|
48
|
+
}
|
|
49
|
+
interface PrAuditOptions {
|
|
50
|
+
cwd?: string;
|
|
51
|
+
base: string;
|
|
52
|
+
head: string;
|
|
53
|
+
failOn?: string[];
|
|
54
|
+
format?: "terminal" | "json" | "github";
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
authors?: boolean;
|
|
57
|
+
}
|
|
58
|
+
interface PrAuditResult {
|
|
59
|
+
schemaVersion: string;
|
|
60
|
+
baseSha: string;
|
|
61
|
+
headSha: string;
|
|
62
|
+
mergeBaseSha: string;
|
|
63
|
+
hasSupportedChanges: boolean;
|
|
64
|
+
filesInspected: number;
|
|
65
|
+
linesAdded: number;
|
|
66
|
+
defectDensityPer100Lines: number;
|
|
67
|
+
infractions: Infraction[];
|
|
68
|
+
failedRules: string[];
|
|
69
|
+
passed: boolean;
|
|
70
|
+
skippedLongLines: number;
|
|
71
|
+
skippedUnknownFiles: number;
|
|
72
|
+
output: string;
|
|
73
|
+
exitCode: number;
|
|
41
74
|
}
|
|
42
75
|
|
|
43
76
|
interface AiAttributionResult {
|
|
@@ -53,14 +86,69 @@ interface ParsedDiffLine {
|
|
|
53
86
|
interface ParsedFileDiff {
|
|
54
87
|
path: string;
|
|
55
88
|
addedLines: ParsedDiffLine[];
|
|
89
|
+
deleted?: boolean;
|
|
56
90
|
}
|
|
57
91
|
interface ParsedCommit extends CommitInfo {
|
|
58
92
|
files: ParsedFileDiff[];
|
|
59
93
|
totalAddedCount: number;
|
|
60
94
|
}
|
|
61
|
-
|
|
95
|
+
interface ParseDiffResult {
|
|
96
|
+
files: ParsedFileDiff[];
|
|
97
|
+
totalAdded: number;
|
|
98
|
+
skippedLongLines: number;
|
|
99
|
+
skippedUnknownFiles: number;
|
|
100
|
+
}
|
|
101
|
+
declare const RECORD_SEPARATOR = "\u001E__ROAST_REC__\u001E";
|
|
102
|
+
declare const FIELD_SEPARATOR = "\u001F__ROAST_FIELD__\u001F";
|
|
103
|
+
declare class GitError extends Error {
|
|
104
|
+
stderr: string;
|
|
105
|
+
code: number | null;
|
|
106
|
+
constructor(stderr: string, code: number | null);
|
|
107
|
+
}
|
|
108
|
+
declare const HERMETIC_GIT_FLAGS: readonly ["-c", "core.quotepath=false", "-c", "diff.noprefix=false", "-c", "diff.mnemonicPrefix=false", "--no-pager"];
|
|
109
|
+
declare function getHermeticGitEnv(extraEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
110
|
+
declare function parseGitLogStream(stream: Readable, onCommit: (commit: ParsedCommit, stats: {
|
|
111
|
+
skippedLongLines: number;
|
|
112
|
+
skippedUnknownFiles: number;
|
|
113
|
+
}) => void): Promise<{
|
|
114
|
+
totalSkippedLongLines: number;
|
|
115
|
+
totalSkippedUnknownFiles: number;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Unquotes git C-style escaped path (e.g. "b/foo\"bar.ts" or "b/\321\202\320\265\321\201\321\202.ts")
|
|
119
|
+
* decoding octal escape sequences and full Unicode code points safely without surrogate splitting.
|
|
120
|
+
*/
|
|
121
|
+
declare function unquoteGitPath(pathStr: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Parses raw git diff text into structured files and added lines.
|
|
124
|
+
* Deterministic, robust against desynchronization and minified files.
|
|
125
|
+
*/
|
|
126
|
+
declare function parseDiff(rawDiff: string): ParseDiffResult;
|
|
62
127
|
declare function streamGitLog(cwd?: string, since?: string): Readable;
|
|
128
|
+
declare function streamGitDiff(cwd: string, baseSha: string, headSha: string): Readable;
|
|
129
|
+
/**
|
|
130
|
+
* Validates and resolves a git ref to a full commit SHA safely using --end-of-options.
|
|
131
|
+
* Exits with code 2 on failure.
|
|
132
|
+
*/
|
|
133
|
+
declare function resolveCommitSha(cwd: string, ref: string, roleName?: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* Finds the merge-base between two commit SHAs safely using --end-of-options.
|
|
136
|
+
*/
|
|
137
|
+
declare function findMergeBase(cwd: string, baseSha: string, headSha: string): string;
|
|
138
|
+
declare class GitResolutionError extends Error {
|
|
139
|
+
constructor(message: string);
|
|
140
|
+
}
|
|
63
141
|
|
|
142
|
+
declare function isCodeFile(filePath: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Audits a collection of file diffs (e.g. for PR mode) against detector rules.
|
|
145
|
+
*/
|
|
146
|
+
declare function auditFileDiffs(files: ParsedFileDiff[], commitHash?: string, commitDate?: string): {
|
|
147
|
+
infractions: Infraction[];
|
|
148
|
+
hasSupportedChanges: boolean;
|
|
149
|
+
filesInspected: number;
|
|
150
|
+
linesAdded: number;
|
|
151
|
+
};
|
|
64
152
|
/**
|
|
65
153
|
* Strips quoted string literals and regex literals from a line so that
|
|
66
154
|
* test fixtures and string constants (e.g. { line: "it.skip" }) are not flagged.
|
|
@@ -75,11 +163,18 @@ declare class MetricsCollector {
|
|
|
75
163
|
private typeEscapes;
|
|
76
164
|
private swallowedErrors;
|
|
77
165
|
private panicLoops;
|
|
78
|
-
private
|
|
166
|
+
private skippedLongLines;
|
|
167
|
+
private skippedUnknownFiles;
|
|
168
|
+
private activePanicChain;
|
|
79
169
|
private includeAll;
|
|
80
170
|
constructor(options?: {
|
|
81
171
|
all?: boolean;
|
|
82
172
|
});
|
|
173
|
+
recordSkipped(stats: {
|
|
174
|
+
skippedLongLines?: number;
|
|
175
|
+
skippedUnknownFiles?: number;
|
|
176
|
+
}): void;
|
|
177
|
+
private flushPanicChain;
|
|
83
178
|
processCommit(commit: ParsedCommit): void;
|
|
84
179
|
getSummary(): MetricSummary;
|
|
85
180
|
}
|
|
@@ -89,10 +184,24 @@ declare function formatTerminalCard(summary: MetricSummary, options: {
|
|
|
89
184
|
repoName: string;
|
|
90
185
|
verbose?: boolean;
|
|
91
186
|
}): string;
|
|
187
|
+
declare function formatPrTerminalCard(result: PrAuditResult): string;
|
|
188
|
+
/**
|
|
189
|
+
* Generates GitHub Actions workflow commands (::error and ::warning)
|
|
190
|
+
* respecting GitHub's limits (max 10 annotations per level per step).
|
|
191
|
+
*/
|
|
192
|
+
declare function formatGithubAnnotations(result: PrAuditResult): string;
|
|
193
|
+
/**
|
|
194
|
+
* Generates GitHub Step Summary Markdown
|
|
195
|
+
*/
|
|
196
|
+
declare function formatGithubStepSummary(result: PrAuditResult): string;
|
|
92
197
|
|
|
198
|
+
declare class InvalidDetectorError extends Error {
|
|
199
|
+
constructor(message: string);
|
|
200
|
+
}
|
|
93
201
|
declare function roastRepository(options?: RoastOptions): Promise<{
|
|
94
202
|
summary: MetricSummary;
|
|
95
203
|
output: string;
|
|
96
204
|
}>;
|
|
205
|
+
declare function auditPullRequest(options: PrAuditOptions): Promise<PrAuditResult>;
|
|
97
206
|
|
|
98
|
-
export { type AiAttributionResult, type CommitInfo, type Infraction, type MetricSummary, MetricsCollector, type ParsedCommit, type ParsedDiffLine, type ParsedFileDiff, type RoastOptions, detectAiAttribution, formatTerminalCard, generateShareUrl, parseGitLogStream, roastRepository, streamGitLog, stripStringAndRegexLiterals };
|
|
207
|
+
export { type AiAttributionResult, type CommitInfo, type DetectorType, FIELD_SEPARATOR, GitError, GitResolutionError, HERMETIC_GIT_FLAGS, type Infraction, InvalidDetectorError, type MetricSummary, MetricsCollector, type ParseDiffResult, type ParsedCommit, type ParsedDiffLine, type ParsedFileDiff, type PrAuditOptions, type PrAuditResult, RECORD_SEPARATOR, type RoastOptions, VALID_DETECTORS, auditFileDiffs, auditPullRequest, detectAiAttribution, findMergeBase, formatGithubAnnotations, formatGithubStepSummary, formatPrTerminalCard, formatTerminalCard, generateShareUrl, getHermeticGitEnv, isCodeFile, parseDiff, parseGitLogStream, resolveCommitSha, roastRepository, streamGitDiff, streamGitLog, stripStringAndRegexLiterals, unquoteGitPath };
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
FIELD_SEPARATOR,
|
|
4
|
+
GitError,
|
|
5
|
+
GitResolutionError,
|
|
6
|
+
HERMETIC_GIT_FLAGS,
|
|
7
|
+
InvalidDetectorError,
|
|
3
8
|
MetricsCollector,
|
|
9
|
+
RECORD_SEPARATOR,
|
|
10
|
+
VALID_DETECTORS,
|
|
11
|
+
auditFileDiffs,
|
|
12
|
+
auditPullRequest,
|
|
4
13
|
detectAiAttribution,
|
|
14
|
+
findMergeBase,
|
|
15
|
+
formatGithubAnnotations,
|
|
16
|
+
formatGithubStepSummary,
|
|
17
|
+
formatPrTerminalCard,
|
|
5
18
|
formatTerminalCard,
|
|
6
19
|
generateShareUrl,
|
|
20
|
+
getHermeticGitEnv,
|
|
21
|
+
isCodeFile,
|
|
22
|
+
parseDiff,
|
|
7
23
|
parseGitLogStream,
|
|
24
|
+
resolveCommitSha,
|
|
8
25
|
roastRepository,
|
|
26
|
+
streamGitDiff,
|
|
9
27
|
streamGitLog,
|
|
10
|
-
stripStringAndRegexLiterals
|
|
11
|
-
|
|
28
|
+
stripStringAndRegexLiterals,
|
|
29
|
+
unquoteGitPath
|
|
30
|
+
} from "./chunk-FHRPVXCI.js";
|
|
12
31
|
export {
|
|
32
|
+
FIELD_SEPARATOR,
|
|
33
|
+
GitError,
|
|
34
|
+
GitResolutionError,
|
|
35
|
+
HERMETIC_GIT_FLAGS,
|
|
36
|
+
InvalidDetectorError,
|
|
13
37
|
MetricsCollector,
|
|
38
|
+
RECORD_SEPARATOR,
|
|
39
|
+
VALID_DETECTORS,
|
|
40
|
+
auditFileDiffs,
|
|
41
|
+
auditPullRequest,
|
|
14
42
|
detectAiAttribution,
|
|
43
|
+
findMergeBase,
|
|
44
|
+
formatGithubAnnotations,
|
|
45
|
+
formatGithubStepSummary,
|
|
46
|
+
formatPrTerminalCard,
|
|
15
47
|
formatTerminalCard,
|
|
16
48
|
generateShareUrl,
|
|
49
|
+
getHermeticGitEnv,
|
|
50
|
+
isCodeFile,
|
|
51
|
+
parseDiff,
|
|
17
52
|
parseGitLogStream,
|
|
53
|
+
resolveCommitSha,
|
|
18
54
|
roastRepository,
|
|
55
|
+
streamGitDiff,
|
|
19
56
|
streamGitLog,
|
|
20
|
-
stripStringAndRegexLiterals
|
|
57
|
+
stripStringAndRegexLiterals,
|
|
58
|
+
unquoteGitPath
|
|
21
59
|
};
|