@james-wall/codegov 0.1.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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +335 -0
- package/dist/cli.js.map +1 -0
- package/dist/db/queries.d.ts +69 -0
- package/dist/db/queries.js +109 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.d.ts +3 -0
- package/dist/db/schema.js +84 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/detect/engine.d.ts +18 -0
- package/dist/detect/engine.js +214 -0
- package/dist/detect/engine.js.map +1 -0
- package/dist/export/formats.d.ts +26 -0
- package/dist/export/formats.js +273 -0
- package/dist/export/formats.js.map +1 -0
- package/dist/git/hooks.d.ts +4 -0
- package/dist/git/hooks.js +48 -0
- package/dist/git/hooks.js.map +1 -0
- package/dist/git/parser.d.ts +15 -0
- package/dist/git/parser.js +114 -0
- package/dist/git/parser.js.map +1 -0
- package/dist/scan/index.d.ts +4 -0
- package/dist/scan/index.js +92 -0
- package/dist/scan/index.js.map +1 -0
- package/dist/scan/store.d.ts +10 -0
- package/dist/scan/store.js +87 -0
- package/dist/scan/store.js.map +1 -0
- package/dist/server/dashboard.d.ts +1 -0
- package/dist/server/dashboard.js +115 -0
- package/dist/server/dashboard.js.map +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +44 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/otel.d.ts +2 -0
- package/dist/server/otel.js +67 -0
- package/dist/server/otel.js.map +1 -0
- package/dist/server/webhook.d.ts +2 -0
- package/dist/server/webhook.js +22 -0
- package/dist/server/webhook.js.map +1 -0
- package/dist/types.d.ts +86 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const SCHEMA = `
|
|
4
|
+
CREATE TABLE IF NOT EXISTS tool_events (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
tool TEXT NOT NULL,
|
|
7
|
+
developer TEXT NOT NULL,
|
|
8
|
+
timestamp TEXT NOT NULL,
|
|
9
|
+
event_type TEXT NOT NULL,
|
|
10
|
+
file_path TEXT,
|
|
11
|
+
lines_added INTEGER DEFAULT 0,
|
|
12
|
+
lines_removed INTEGER DEFAULT 0,
|
|
13
|
+
model TEXT,
|
|
14
|
+
tokens_in INTEGER DEFAULT 0,
|
|
15
|
+
tokens_out INTEGER DEFAULT 0,
|
|
16
|
+
cost_usd REAL DEFAULT 0,
|
|
17
|
+
session_id TEXT,
|
|
18
|
+
raw_span TEXT,
|
|
19
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS commits (
|
|
23
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
24
|
+
hash TEXT UNIQUE NOT NULL,
|
|
25
|
+
author TEXT NOT NULL,
|
|
26
|
+
timestamp TEXT NOT NULL,
|
|
27
|
+
repo TEXT,
|
|
28
|
+
branch TEXT,
|
|
29
|
+
message TEXT,
|
|
30
|
+
total_additions INTEGER DEFAULT 0,
|
|
31
|
+
total_deletions INTEGER DEFAULT 0,
|
|
32
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS commit_files (
|
|
36
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
+
commit_hash TEXT NOT NULL,
|
|
38
|
+
file_path TEXT NOT NULL,
|
|
39
|
+
additions INTEGER DEFAULT 0,
|
|
40
|
+
deletions INTEGER DEFAULT 0,
|
|
41
|
+
FOREIGN KEY (commit_hash) REFERENCES commits(hash)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS attributions (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
commit_hash TEXT NOT NULL,
|
|
47
|
+
tool_event_id INTEGER NOT NULL,
|
|
48
|
+
tool TEXT NOT NULL,
|
|
49
|
+
developer TEXT NOT NULL,
|
|
50
|
+
file_path TEXT,
|
|
51
|
+
confidence TEXT NOT NULL DEFAULT 'medium',
|
|
52
|
+
method TEXT NOT NULL,
|
|
53
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
54
|
+
FOREIGN KEY (commit_hash) REFERENCES commits(hash),
|
|
55
|
+
FOREIGN KEY (tool_event_id) REFERENCES tool_events(id)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_developer ON tool_events(developer);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_timestamp ON tool_events(timestamp);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_file ON tool_events(file_path);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_commits_author ON commits(author);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_commits_timestamp ON commits(timestamp);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_commit_files_hash ON commit_files(commit_hash);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_commit_files_path ON commit_files(file_path);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_attributions_commit ON attributions(commit_hash);
|
|
66
|
+
`;
|
|
67
|
+
let db = null;
|
|
68
|
+
export function getDb(dbPath) {
|
|
69
|
+
if (db)
|
|
70
|
+
return db;
|
|
71
|
+
const resolvedPath = dbPath ?? path.join(process.cwd(), "codegov.db");
|
|
72
|
+
db = new Database(resolvedPath);
|
|
73
|
+
db.pragma("journal_mode = WAL");
|
|
74
|
+
db.pragma("foreign_keys = ON");
|
|
75
|
+
db.exec(SCHEMA);
|
|
76
|
+
return db;
|
|
77
|
+
}
|
|
78
|
+
export function closeDb() {
|
|
79
|
+
if (db) {
|
|
80
|
+
db.close();
|
|
81
|
+
db = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Dd,CAAC;AAEF,IAAI,EAAE,GAA6B,IAAI,CAAC;AAExC,MAAM,UAAU,KAAK,CAAC,MAAe;IACnC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAElB,MAAM,YAAY,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;IACtE,EAAE,GAAG,IAAI,QAAQ,CAAC,YAAY,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,EAAE,EAAE,CAAC;QACP,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AgentId } from "../types.js";
|
|
2
|
+
interface DetectionResult {
|
|
3
|
+
agentId: AgentId;
|
|
4
|
+
confidence: number;
|
|
5
|
+
modelVersion: string | null;
|
|
6
|
+
promptSummary: string | null;
|
|
7
|
+
signals: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface CommitInfo {
|
|
10
|
+
hash: string;
|
|
11
|
+
message: string;
|
|
12
|
+
body: string;
|
|
13
|
+
author: string;
|
|
14
|
+
email: string;
|
|
15
|
+
trailers: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function detectAgent(commit: CommitInfo): DetectionResult;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
export function detectAgent(commit) {
|
|
2
|
+
const signals = [];
|
|
3
|
+
let agentId = "human";
|
|
4
|
+
let confidence = 0;
|
|
5
|
+
let modelVersion = null;
|
|
6
|
+
let promptSummary = null;
|
|
7
|
+
const detectors = [
|
|
8
|
+
detectClaudeCode,
|
|
9
|
+
detectCursor,
|
|
10
|
+
detectCopilot,
|
|
11
|
+
detectDevin,
|
|
12
|
+
detectAider,
|
|
13
|
+
detectGenericAI,
|
|
14
|
+
];
|
|
15
|
+
for (const detector of detectors) {
|
|
16
|
+
const result = detector(commit);
|
|
17
|
+
if (result.matched && result.confidence > confidence) {
|
|
18
|
+
agentId = result.agentId;
|
|
19
|
+
confidence = result.confidence;
|
|
20
|
+
modelVersion = result.modelVersion;
|
|
21
|
+
if (result.promptSummary)
|
|
22
|
+
promptSummary = result.promptSummary;
|
|
23
|
+
signals.push(...result.signals);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { agentId, confidence, modelVersion, promptSummary, signals };
|
|
27
|
+
}
|
|
28
|
+
function detectClaudeCode(commit) {
|
|
29
|
+
const signals = [];
|
|
30
|
+
let confidence = 0;
|
|
31
|
+
let modelVersion = null;
|
|
32
|
+
const fullText = commit.trailers + "\n" + commit.body;
|
|
33
|
+
const coAuthorMatch = fullText.match(/Co-[Aa]uthored-[Bb]y:\s*[Cc]laude[^<]*<(?:claude@anthropic\.com|noreply@anthropic\.com)>/i);
|
|
34
|
+
if (coAuthorMatch) {
|
|
35
|
+
signals.push("co-authored-by-claude-trailer");
|
|
36
|
+
confidence = 0.95;
|
|
37
|
+
const versionMatch = coAuthorMatch[0].match(/[Cc]laude\s+((?:Opus|Sonnet|Haiku)\s*[\d.]+(?:\s*\([^)]+\))?)/);
|
|
38
|
+
if (versionMatch) {
|
|
39
|
+
modelVersion = `Claude ${versionMatch[1].trim()}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (commit.body.includes("Generated by Claude Code") ||
|
|
43
|
+
commit.body.includes("Generated with Claude Code")) {
|
|
44
|
+
signals.push("claude-code-generation-marker");
|
|
45
|
+
confidence = Math.max(confidence, 0.95);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
matched: signals.length > 0,
|
|
49
|
+
agentId: "claude-code",
|
|
50
|
+
confidence,
|
|
51
|
+
modelVersion,
|
|
52
|
+
promptSummary: null,
|
|
53
|
+
signals,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function detectCursor(commit) {
|
|
57
|
+
const signals = [];
|
|
58
|
+
let confidence = 0;
|
|
59
|
+
const fullText = commit.trailers + "\n" + commit.body;
|
|
60
|
+
if (commit.author === "cursor[bot]" || commit.email.includes("cursor[bot]")) {
|
|
61
|
+
signals.push("cursor-bot-author");
|
|
62
|
+
confidence = 0.95;
|
|
63
|
+
}
|
|
64
|
+
// Direct authorship: Cursor Agent <cursoragent@cursor.com>
|
|
65
|
+
if (commit.email === "cursoragent@cursor.com" || commit.author === "Cursor Agent") {
|
|
66
|
+
signals.push("cursor-agent-author");
|
|
67
|
+
confidence = Math.max(confidence, 0.95);
|
|
68
|
+
}
|
|
69
|
+
// Co-author patterns (multiple formats seen in the wild)
|
|
70
|
+
if (fullText.match(/Co-[Aa]uthored-[Bb]y:\s*(?:Cursor|Cursor Agent)\s*<cursoragent@cursor\.com>/i)) {
|
|
71
|
+
signals.push("cursor-agent-co-author");
|
|
72
|
+
confidence = Math.max(confidence, 0.95);
|
|
73
|
+
}
|
|
74
|
+
if (fullText.match(/Co-[Aa]uthored-[Bb]y:\s*cursor-agent\s*<cursor-agent@cursor\.sh>/i)) {
|
|
75
|
+
signals.push("cursor-agent-co-author");
|
|
76
|
+
confidence = Math.max(confidence, 0.95);
|
|
77
|
+
}
|
|
78
|
+
if (commit.body.includes("Applied via @cursor push command")) {
|
|
79
|
+
signals.push("cursor-push-command");
|
|
80
|
+
confidence = Math.max(confidence, 0.9);
|
|
81
|
+
}
|
|
82
|
+
if (commit.trailers.match(/Made-with:\s*Cursor/i)) {
|
|
83
|
+
signals.push("made-with-cursor-trailer");
|
|
84
|
+
confidence = Math.max(confidence, 0.85);
|
|
85
|
+
}
|
|
86
|
+
if (commit.body.includes("Generated by Cursor")) {
|
|
87
|
+
signals.push("cursor-generation-marker");
|
|
88
|
+
confidence = Math.max(confidence, 0.9);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
matched: signals.length > 0,
|
|
92
|
+
agentId: "cursor",
|
|
93
|
+
confidence,
|
|
94
|
+
modelVersion: null,
|
|
95
|
+
promptSummary: null,
|
|
96
|
+
signals,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function detectCopilot(commit) {
|
|
100
|
+
const signals = [];
|
|
101
|
+
let confidence = 0;
|
|
102
|
+
const fullText = commit.trailers + "\n" + commit.body;
|
|
103
|
+
const copilotEmails = [
|
|
104
|
+
"copilot@github.com",
|
|
105
|
+
"175728472+Copilot@users.noreply.github.com",
|
|
106
|
+
"223556219+Copilot@users.noreply.github.com",
|
|
107
|
+
];
|
|
108
|
+
for (const email of copilotEmails) {
|
|
109
|
+
if (fullText.includes(email)) {
|
|
110
|
+
signals.push("copilot-co-authored-trailer");
|
|
111
|
+
confidence = 0.95;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (commit.author === "copilot-swe-agent[bot]" ||
|
|
116
|
+
commit.email.includes("198982749+Copilot@users.noreply.github.com")) {
|
|
117
|
+
signals.push("copilot-swe-agent-author");
|
|
118
|
+
confidence = Math.max(confidence, 0.95);
|
|
119
|
+
}
|
|
120
|
+
if (commit.trailers.match(/Agent-Logs-Url:/i)) {
|
|
121
|
+
signals.push("copilot-agent-logs-trailer");
|
|
122
|
+
confidence = Math.max(confidence, 0.9);
|
|
123
|
+
}
|
|
124
|
+
if (commit.body.includes("copilot-workspace")) {
|
|
125
|
+
signals.push("copilot-workspace-marker");
|
|
126
|
+
confidence = Math.max(confidence, 0.8);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
matched: signals.length > 0,
|
|
130
|
+
agentId: "copilot",
|
|
131
|
+
confidence,
|
|
132
|
+
modelVersion: null,
|
|
133
|
+
promptSummary: null,
|
|
134
|
+
signals,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function detectDevin(commit) {
|
|
138
|
+
const signals = [];
|
|
139
|
+
let confidence = 0;
|
|
140
|
+
if (commit.email === "158243242+devin-ai-integration[bot]@users.noreply.github.com" ||
|
|
141
|
+
commit.email.includes("devin-ai-integration[bot]")) {
|
|
142
|
+
signals.push("devin-bot-author");
|
|
143
|
+
confidence = 0.95;
|
|
144
|
+
}
|
|
145
|
+
if (commit.author === "Devin AI") {
|
|
146
|
+
signals.push("devin-author-name");
|
|
147
|
+
confidence = Math.max(confidence, 0.95);
|
|
148
|
+
}
|
|
149
|
+
if (commit.body.includes("devin.ai") || commit.body.includes("Devin AI")) {
|
|
150
|
+
signals.push("devin-body-marker");
|
|
151
|
+
confidence = Math.max(confidence, 0.8);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
matched: signals.length > 0,
|
|
155
|
+
agentId: "devin",
|
|
156
|
+
confidence,
|
|
157
|
+
modelVersion: null,
|
|
158
|
+
promptSummary: null,
|
|
159
|
+
signals,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function detectAider(commit) {
|
|
163
|
+
const signals = [];
|
|
164
|
+
let confidence = 0;
|
|
165
|
+
let modelVersion = null;
|
|
166
|
+
const fullText = commit.trailers + "\n" + commit.body;
|
|
167
|
+
const aiderMatch = fullText.match(/Co-[Aa]uthored-[Bb]y:\s*aider\s*\(([^)]+)\)\s*<aider@aider\.chat>/i);
|
|
168
|
+
if (aiderMatch) {
|
|
169
|
+
signals.push("aider-co-authored-trailer");
|
|
170
|
+
confidence = 0.95;
|
|
171
|
+
modelVersion = aiderMatch[1].trim();
|
|
172
|
+
}
|
|
173
|
+
if (!aiderMatch && fullText.includes("aider@aider.chat")) {
|
|
174
|
+
signals.push("aider-email");
|
|
175
|
+
confidence = Math.max(confidence, 0.85);
|
|
176
|
+
}
|
|
177
|
+
if (commit.message.match(/^aider:/i)) {
|
|
178
|
+
signals.push("aider-message-prefix");
|
|
179
|
+
confidence = Math.max(confidence, 0.9);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
matched: signals.length > 0,
|
|
183
|
+
agentId: "aider",
|
|
184
|
+
confidence,
|
|
185
|
+
modelVersion,
|
|
186
|
+
promptSummary: null,
|
|
187
|
+
signals,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function detectGenericAI(commit) {
|
|
191
|
+
const signals = [];
|
|
192
|
+
let confidence = 0;
|
|
193
|
+
const fullText = commit.trailers + "\n" + commit.body;
|
|
194
|
+
const nonAiBots = ["dependabot", "renovate", "greenkeeper", "snyk-bot", "mergify"];
|
|
195
|
+
const isNonAiBot = nonAiBots.some((bot) => fullText.toLowerCase().includes(bot));
|
|
196
|
+
if (!isNonAiBot &&
|
|
197
|
+
fullText.match(/Co-[Aa]uthored-[Bb]y:.*(ai|assistant|agent|llm)/i)) {
|
|
198
|
+
signals.push("generic-ai-co-author");
|
|
199
|
+
confidence = 0.6;
|
|
200
|
+
}
|
|
201
|
+
if (commit.body.match(/generated\s+(by|with|using)\s+(an?\s+)?(ai|llm|assistant|agent)/i)) {
|
|
202
|
+
signals.push("ai-generation-mention-in-body");
|
|
203
|
+
confidence = Math.max(confidence, 0.5);
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
matched: signals.length > 0,
|
|
207
|
+
agentId: "unknown-ai",
|
|
208
|
+
confidence,
|
|
209
|
+
modelVersion: null,
|
|
210
|
+
promptSummary: null,
|
|
211
|
+
signals,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/detect/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ProvenanceRecord } from "../types.js";
|
|
2
|
+
export interface AuditReport {
|
|
3
|
+
generatedAt: string;
|
|
4
|
+
repository: string;
|
|
5
|
+
period: {
|
|
6
|
+
from: string;
|
|
7
|
+
to: string;
|
|
8
|
+
};
|
|
9
|
+
summary: {
|
|
10
|
+
totalCommits: number;
|
|
11
|
+
aiAuthoredCommits: number;
|
|
12
|
+
aiAuthoredPercentage: number;
|
|
13
|
+
agentBreakdown: Record<string, number>;
|
|
14
|
+
reviewCoverage: {
|
|
15
|
+
reviewed: number;
|
|
16
|
+
unreviewed: number;
|
|
17
|
+
modified: number;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
records: ProvenanceRecord[];
|
|
21
|
+
}
|
|
22
|
+
export declare function generateAuditReport(since?: string): AuditReport;
|
|
23
|
+
export declare function exportJson(report: AuditReport): string;
|
|
24
|
+
export declare function exportCsv(report: AuditReport): string;
|
|
25
|
+
export declare function exportSbom(report: AuditReport): string;
|
|
26
|
+
export declare function exportHtml(report: AuditReport): string;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { readRecords } from "../scan/store.js";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
export function generateAuditReport(since) {
|
|
4
|
+
let records = readRecords();
|
|
5
|
+
let fromDate = records.length > 0 ? records[records.length - 1].timestamp : new Date().toISOString();
|
|
6
|
+
const toDate = new Date().toISOString();
|
|
7
|
+
if (since) {
|
|
8
|
+
const sinceDate = parseSince(since);
|
|
9
|
+
if (sinceDate) {
|
|
10
|
+
fromDate = sinceDate.toISOString();
|
|
11
|
+
records = records.filter((r) => new Date(r.timestamp) >= sinceDate);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const agentBreakdown = {};
|
|
15
|
+
const reviewCoverage = { reviewed: 0, unreviewed: 0, modified: 0 };
|
|
16
|
+
for (const r of records) {
|
|
17
|
+
agentBreakdown[r.agentId] = (agentBreakdown[r.agentId] || 0) + 1;
|
|
18
|
+
if (r.reviewStatus === "approved")
|
|
19
|
+
reviewCoverage.reviewed++;
|
|
20
|
+
else if (r.reviewStatus === "modified")
|
|
21
|
+
reviewCoverage.modified++;
|
|
22
|
+
else
|
|
23
|
+
reviewCoverage.unreviewed++;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
generatedAt: new Date().toISOString(),
|
|
27
|
+
repository: getRepoName(),
|
|
28
|
+
period: { from: fromDate, to: toDate },
|
|
29
|
+
summary: {
|
|
30
|
+
totalCommits: getTotalCommitCount(since),
|
|
31
|
+
aiAuthoredCommits: records.length,
|
|
32
|
+
aiAuthoredPercentage: getTotalCommitCount(since) > 0
|
|
33
|
+
? (records.length / getTotalCommitCount(since)) * 100
|
|
34
|
+
: 0,
|
|
35
|
+
agentBreakdown,
|
|
36
|
+
reviewCoverage,
|
|
37
|
+
},
|
|
38
|
+
records,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function exportJson(report) {
|
|
42
|
+
return JSON.stringify(report, null, 2);
|
|
43
|
+
}
|
|
44
|
+
export function exportCsv(report) {
|
|
45
|
+
const headers = [
|
|
46
|
+
"commit_hash", "timestamp", "agent_id", "model_version",
|
|
47
|
+
"confidence", "review_status", "reviewer",
|
|
48
|
+
"files_changed", "lines_added", "lines_removed", "signals",
|
|
49
|
+
];
|
|
50
|
+
const rows = report.records.map((r) => [
|
|
51
|
+
r.commitHash, r.timestamp, r.agentId,
|
|
52
|
+
r.modelVersion || "", r.confidence.toFixed(2),
|
|
53
|
+
r.reviewStatus, r.reviewer || "",
|
|
54
|
+
r.filesChanged.length.toString(),
|
|
55
|
+
r.linesAdded.toString(), r.linesRemoved.toString(),
|
|
56
|
+
r.signals.join(";"),
|
|
57
|
+
]);
|
|
58
|
+
return [
|
|
59
|
+
headers.join(","),
|
|
60
|
+
...rows.map((row) => row.map(csvEscape).join(",")),
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
export function exportSbom(report) {
|
|
64
|
+
const doc = {
|
|
65
|
+
spdxVersion: "SPDX-2.3",
|
|
66
|
+
dataLicense: "CC0-1.0",
|
|
67
|
+
SPDXID: "SPDXRef-DOCUMENT",
|
|
68
|
+
name: `ai-provenance-${report.repository}`,
|
|
69
|
+
documentNamespace: `https://codegov.dev/spdx/${report.repository}/${Date.now()}`,
|
|
70
|
+
creationInfo: {
|
|
71
|
+
created: report.generatedAt,
|
|
72
|
+
creators: ["Tool: codegov-0.1.0"],
|
|
73
|
+
},
|
|
74
|
+
packages: [
|
|
75
|
+
{
|
|
76
|
+
SPDXID: "SPDXRef-Package",
|
|
77
|
+
name: report.repository,
|
|
78
|
+
downloadLocation: "NOASSERTION",
|
|
79
|
+
annotation: report.records.map((r) => ({
|
|
80
|
+
annotationType: "REVIEW",
|
|
81
|
+
annotator: `Tool: ${r.agentId}${r.modelVersion ? ` (${r.modelVersion})` : ""}`,
|
|
82
|
+
annotationDate: r.timestamp,
|
|
83
|
+
annotationComment: [
|
|
84
|
+
`Commit: ${r.commitHash}`,
|
|
85
|
+
`Confidence: ${(r.confidence * 100).toFixed(0)}%`,
|
|
86
|
+
`Review: ${r.reviewStatus}`,
|
|
87
|
+
`Files: ${r.filesChanged.join(", ")}`,
|
|
88
|
+
`Signals: ${r.signals.join(", ")}`,
|
|
89
|
+
].join(" | "),
|
|
90
|
+
})),
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
return JSON.stringify(doc, null, 2);
|
|
95
|
+
}
|
|
96
|
+
export function exportHtml(report) {
|
|
97
|
+
const s = report.summary;
|
|
98
|
+
const agentRows = Object.entries(s.agentBreakdown)
|
|
99
|
+
.sort((a, b) => b[1] - a[1])
|
|
100
|
+
.map(([agent, count]) => {
|
|
101
|
+
const pct = s.totalCommits > 0 ? ((count / s.totalCommits) * 100).toFixed(1) : "0";
|
|
102
|
+
return `<tr><td>${esc(agent)}</td><td>${count}</td><td>${pct}%</td></tr>`;
|
|
103
|
+
})
|
|
104
|
+
.join("\n ");
|
|
105
|
+
const commitRows = report.records
|
|
106
|
+
.map((r) => {
|
|
107
|
+
const hash = r.commitHash.slice(0, 7);
|
|
108
|
+
const date = new Date(r.timestamp).toLocaleDateString();
|
|
109
|
+
const conf = Math.round(r.confidence * 100);
|
|
110
|
+
return `<tr>
|
|
111
|
+
<td><code>${hash}</code></td>
|
|
112
|
+
<td>${date}</td>
|
|
113
|
+
<td><span class="agent agent-${esc(r.agentId)}">${esc(r.agentId)}</span></td>
|
|
114
|
+
<td>${esc(r.modelVersion || "—")}</td>
|
|
115
|
+
<td>${conf}%</td>
|
|
116
|
+
<td>${r.filesChanged.length}</td>
|
|
117
|
+
<td class="additions">+${r.linesAdded}</td>
|
|
118
|
+
<td class="deletions">-${r.linesRemoved}</td>
|
|
119
|
+
<td><span class="status status-${r.reviewStatus}">${r.reviewStatus}</span></td>
|
|
120
|
+
</tr>`;
|
|
121
|
+
})
|
|
122
|
+
.join("\n ");
|
|
123
|
+
const reviewedPct = s.aiAuthoredCommits > 0
|
|
124
|
+
? Math.round((s.reviewCoverage.reviewed / s.aiAuthoredCommits) * 100)
|
|
125
|
+
: 0;
|
|
126
|
+
return `<!DOCTYPE html>
|
|
127
|
+
<html lang="en">
|
|
128
|
+
<head>
|
|
129
|
+
<meta charset="UTF-8">
|
|
130
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
131
|
+
<title>CodeGov Report — ${esc(report.repository)}</title>
|
|
132
|
+
<style>
|
|
133
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
134
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0d1117; color: #c9d1d9; padding: 2rem; max-width: 1200px; margin: 0 auto; }
|
|
135
|
+
h1 { font-size: 1.5rem; margin-bottom: 0.25rem; }
|
|
136
|
+
.subtitle { color: #8b949e; margin-bottom: 2rem; }
|
|
137
|
+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
|
|
138
|
+
.card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1.25rem; }
|
|
139
|
+
.card .value { font-size: 2rem; font-weight: 700; color: #f0f6fc; }
|
|
140
|
+
.card .label { color: #8b949e; font-size: 0.85rem; margin-top: 0.25rem; }
|
|
141
|
+
.card .value.highlight { color: #58a6ff; }
|
|
142
|
+
table { width: 100%; border-collapse: collapse; background: #161b22; border: 1px solid #30363d; border-radius: 8px; overflow: hidden; margin-bottom: 2rem; }
|
|
143
|
+
th { text-align: left; padding: 0.75rem 1rem; background: #21262d; color: #8b949e; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid #30363d; }
|
|
144
|
+
td { padding: 0.6rem 1rem; border-bottom: 1px solid #21262d; font-size: 0.9rem; }
|
|
145
|
+
tr:last-child td { border-bottom: none; }
|
|
146
|
+
code { background: #21262d; padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.85rem; }
|
|
147
|
+
.agent { padding: 0.15rem 0.5rem; border-radius: 12px; font-size: 0.8rem; font-weight: 500; }
|
|
148
|
+
.agent-claude-code { background: #1a1a2e; color: #d4a574; }
|
|
149
|
+
.agent-cursor { background: #1a2e1a; color: #7bc97b; }
|
|
150
|
+
.agent-copilot { background: #1a2e2e; color: #79c0ff; }
|
|
151
|
+
.agent-devin { background: #2e1a2e; color: #d2a8ff; }
|
|
152
|
+
.agent-aider { background: #2e2e1a; color: #e3b341; }
|
|
153
|
+
.additions { color: #3fb950; }
|
|
154
|
+
.deletions { color: #f85149; }
|
|
155
|
+
.status { padding: 0.15rem 0.5rem; border-radius: 12px; font-size: 0.75rem; }
|
|
156
|
+
.status-unreviewed { background: #2e1a1a; color: #f85149; }
|
|
157
|
+
.status-approved { background: #1a2e1a; color: #3fb950; }
|
|
158
|
+
.status-modified { background: #2e2e1a; color: #e3b341; }
|
|
159
|
+
.section-title { font-size: 1.1rem; margin-bottom: 1rem; color: #f0f6fc; }
|
|
160
|
+
.footer { text-align: center; color: #484f58; font-size: 0.8rem; margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #21262d; }
|
|
161
|
+
</style>
|
|
162
|
+
</head>
|
|
163
|
+
<body>
|
|
164
|
+
<h1>CodeGov Provenance Report</h1>
|
|
165
|
+
<p class="subtitle">${esc(report.repository)} — generated ${new Date(report.generatedAt).toLocaleString()}</p>
|
|
166
|
+
|
|
167
|
+
<div class="cards">
|
|
168
|
+
<div class="card">
|
|
169
|
+
<div class="value">${s.totalCommits}</div>
|
|
170
|
+
<div class="label">Total Commits</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="card">
|
|
173
|
+
<div class="value highlight">${s.aiAuthoredCommits}</div>
|
|
174
|
+
<div class="label">AI-Authored Commits</div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="card">
|
|
177
|
+
<div class="value highlight">${s.aiAuthoredPercentage.toFixed(1)}%</div>
|
|
178
|
+
<div class="label">AI-Authored</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="card">
|
|
181
|
+
<div class="value">${reviewedPct}%</div>
|
|
182
|
+
<div class="label">Review Coverage</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<h2 class="section-title">By Agent</h2>
|
|
187
|
+
<table>
|
|
188
|
+
<thead><tr><th>Agent</th><th>Commits</th><th>Share</th></tr></thead>
|
|
189
|
+
<tbody>
|
|
190
|
+
${agentRows}
|
|
191
|
+
</tbody>
|
|
192
|
+
</table>
|
|
193
|
+
|
|
194
|
+
<h2 class="section-title">AI-Authored Commits</h2>
|
|
195
|
+
<table>
|
|
196
|
+
<thead>
|
|
197
|
+
<tr><th>Commit</th><th>Date</th><th>Agent</th><th>Model</th><th>Confidence</th><th>Files</th><th>Added</th><th>Removed</th><th>Review</th></tr>
|
|
198
|
+
</thead>
|
|
199
|
+
<tbody>
|
|
200
|
+
${commitRows}
|
|
201
|
+
</tbody>
|
|
202
|
+
</table>
|
|
203
|
+
|
|
204
|
+
<div class="footer">Generated by CodeGov v0.1.0</div>
|
|
205
|
+
</body>
|
|
206
|
+
</html>`;
|
|
207
|
+
}
|
|
208
|
+
function esc(s) {
|
|
209
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
210
|
+
}
|
|
211
|
+
function getRepoName() {
|
|
212
|
+
try {
|
|
213
|
+
const url = execFileSync("git", ["remote", "get-url", "origin"], {
|
|
214
|
+
encoding: "utf-8",
|
|
215
|
+
}).trim();
|
|
216
|
+
const match = url.match(/\/([^/]+?)(?:\.git)?$/);
|
|
217
|
+
return match ? match[1] : "unknown";
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
try {
|
|
221
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
222
|
+
encoding: "utf-8",
|
|
223
|
+
})
|
|
224
|
+
.trim()
|
|
225
|
+
.split("/")
|
|
226
|
+
.pop();
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return "unknown";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function getTotalCommitCount(since) {
|
|
234
|
+
const args = ["rev-list", "--count", "HEAD"];
|
|
235
|
+
if (since) {
|
|
236
|
+
args.push(`--since=${since}`);
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
return parseInt(execFileSync("git", args, { encoding: "utf-8" }).trim());
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function parseSince(since) {
|
|
246
|
+
const match = since.match(/^(\d+)([dhm])$/);
|
|
247
|
+
if (!match) {
|
|
248
|
+
const date = new Date(since);
|
|
249
|
+
return isNaN(date.getTime()) ? null : date;
|
|
250
|
+
}
|
|
251
|
+
const value = parseInt(match[1]);
|
|
252
|
+
const unit = match[2];
|
|
253
|
+
const now = new Date();
|
|
254
|
+
switch (unit) {
|
|
255
|
+
case "d":
|
|
256
|
+
now.setDate(now.getDate() - value);
|
|
257
|
+
break;
|
|
258
|
+
case "h":
|
|
259
|
+
now.setHours(now.getHours() - value);
|
|
260
|
+
break;
|
|
261
|
+
case "m":
|
|
262
|
+
now.setMonth(now.getMonth() - value);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
return now;
|
|
266
|
+
}
|
|
267
|
+
function csvEscape(value) {
|
|
268
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
269
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
270
|
+
}
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=formats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.js","sourceRoot":"","sources":["../../src/export/formats.ts"],"names":[],"mappings":"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"}
|