@mesadev/agentblame 0.2.11 → 3.0.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/dist/agentblame.js +3500 -0
- package/dist/blame.d.ts +4 -1
- package/dist/blame.js +280 -78
- package/dist/blame.js.map +1 -1
- package/dist/capture.d.ts +4 -7
- package/dist/capture.js +464 -486
- package/dist/capture.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +334 -72
- package/dist/index.js.map +1 -1
- package/dist/lib/analytics.d.ts +179 -0
- package/dist/lib/analytics.js +833 -0
- package/dist/lib/analytics.js.map +1 -0
- package/dist/lib/attribution.d.ts +54 -0
- package/dist/lib/attribution.js +266 -0
- package/dist/lib/attribution.js.map +1 -0
- package/dist/lib/checkpoint.d.ts +97 -0
- package/dist/lib/checkpoint.js +441 -0
- package/dist/lib/checkpoint.js.map +1 -0
- package/dist/lib/config.d.ts +46 -0
- package/dist/lib/config.js +123 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/database.d.ts +115 -85
- package/dist/lib/database.js +305 -325
- package/dist/lib/database.js.map +1 -1
- package/dist/lib/delta.d.ts +78 -0
- package/dist/lib/delta.js +309 -0
- package/dist/lib/delta.js.map +1 -0
- package/dist/lib/git/gitBlame.js +9 -4
- package/dist/lib/git/gitBlame.js.map +1 -1
- package/dist/lib/git/gitConfig.d.ts +5 -3
- package/dist/lib/git/gitConfig.js +41 -6
- package/dist/lib/git/gitConfig.js.map +1 -1
- package/dist/lib/git/gitDiff.d.ts +13 -1
- package/dist/lib/git/gitDiff.js +39 -7
- package/dist/lib/git/gitDiff.js.map +1 -1
- package/dist/lib/git/gitNotes.d.ts +30 -4
- package/dist/lib/git/gitNotes.js +140 -24
- package/dist/lib/git/gitNotes.js.map +1 -1
- package/dist/lib/hooks.d.ts +1 -0
- package/dist/lib/hooks.js +148 -27
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.js +13 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/storage.d.ts +163 -0
- package/dist/lib/storage.js +823 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/trace.d.ts +118 -0
- package/dist/lib/trace.js +499 -0
- package/dist/lib/trace.js.map +1 -0
- package/dist/lib/types.d.ts +322 -114
- package/dist/lib/types.js +2 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/util.d.ts +8 -8
- package/dist/lib/util.js +18 -22
- package/dist/lib/util.js.map +1 -1
- package/dist/lib/watcher.d.ts +104 -0
- package/dist/lib/watcher.js +398 -0
- package/dist/lib/watcher.js.map +1 -0
- package/dist/post-merge.js +460 -421
- package/dist/post-merge.js.map +1 -1
- package/dist/process.d.ts +6 -5
- package/dist/process.js +182 -158
- package/dist/process.js.map +1 -1
- package/dist/sync.js +172 -131
- package/dist/sync.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* Agent Blame CLI
|
|
4
|
+
* Agent Blame CLI v3
|
|
5
5
|
*
|
|
6
6
|
* Commands:
|
|
7
7
|
* agentblame init - Set up hooks and configuration
|
|
@@ -50,7 +50,13 @@ const blame_1 = require("./blame");
|
|
|
50
50
|
const sync_1 = require("./sync");
|
|
51
51
|
const process_1 = require("./process");
|
|
52
52
|
const capture_1 = require("./capture");
|
|
53
|
-
const
|
|
53
|
+
const hooks_1 = require("./lib/hooks");
|
|
54
|
+
const gitCli_1 = require("./lib/git/gitCli");
|
|
55
|
+
const gitConfig_1 = require("./lib/git/gitConfig");
|
|
56
|
+
const database_1 = require("./lib/database");
|
|
57
|
+
const storage_1 = require("./lib/storage");
|
|
58
|
+
const analytics_1 = require("./lib/analytics");
|
|
59
|
+
const config_1 = require("./lib/config");
|
|
54
60
|
const ANALYTICS_TAG = "agentblame-analytics-anchor";
|
|
55
61
|
/**
|
|
56
62
|
* Check if Bun is installed and available in PATH.
|
|
@@ -92,6 +98,15 @@ async function main() {
|
|
|
92
98
|
case "prune":
|
|
93
99
|
await runPrune();
|
|
94
100
|
break;
|
|
101
|
+
case "migrate":
|
|
102
|
+
await runMigrate();
|
|
103
|
+
break;
|
|
104
|
+
case "debug":
|
|
105
|
+
await runDebug();
|
|
106
|
+
break;
|
|
107
|
+
case "config":
|
|
108
|
+
await runConfig(args.slice(1));
|
|
109
|
+
break;
|
|
95
110
|
case "--version":
|
|
96
111
|
case "-v":
|
|
97
112
|
printVersion();
|
|
@@ -109,7 +124,7 @@ async function main() {
|
|
|
109
124
|
}
|
|
110
125
|
function printHelp() {
|
|
111
126
|
console.log(`
|
|
112
|
-
Agent Blame - Track AI-generated code in your commits
|
|
127
|
+
Agent Blame v3 - Track AI-generated code in your commits
|
|
113
128
|
|
|
114
129
|
Usage:
|
|
115
130
|
agentblame init Set up hooks for current repo
|
|
@@ -119,13 +134,24 @@ Usage:
|
|
|
119
134
|
agentblame blame <file> Show AI attribution for a file
|
|
120
135
|
agentblame blame --summary Show summary only
|
|
121
136
|
agentblame blame --json Output as JSON
|
|
122
|
-
agentblame
|
|
137
|
+
agentblame blame --verbose Show full prompts (not truncated)
|
|
138
|
+
agentblame status Show session and capture stats
|
|
123
139
|
agentblame sync Transfer notes after squash/rebase
|
|
140
|
+
agentblame config Show all configuration
|
|
141
|
+
agentblame config get <key> Get a config value
|
|
142
|
+
agentblame config set <key> <value> Set a config value
|
|
124
143
|
agentblame prune Remove old entries from database
|
|
144
|
+
agentblame migrate Migrate from v2 to v3 schema
|
|
145
|
+
agentblame debug Show detailed debug info
|
|
146
|
+
|
|
147
|
+
Configuration Keys:
|
|
148
|
+
storePromptContent Store actual prompt text (default: false)
|
|
149
|
+
Set to true to store full prompt content
|
|
125
150
|
|
|
126
151
|
Examples:
|
|
127
152
|
agentblame init
|
|
128
153
|
agentblame blame src/index.ts
|
|
154
|
+
agentblame config set storePromptContent false
|
|
129
155
|
`);
|
|
130
156
|
}
|
|
131
157
|
function printVersion() {
|
|
@@ -140,18 +166,14 @@ function printVersion() {
|
|
|
140
166
|
}
|
|
141
167
|
/**
|
|
142
168
|
* Create the analytics anchor tag on the root commit.
|
|
143
|
-
* This tag is used to store repository-wide analytics.
|
|
144
169
|
*/
|
|
145
170
|
async function createAnalyticsTag(repoRoot) {
|
|
146
171
|
try {
|
|
147
|
-
|
|
148
|
-
const existingTag = await (0, lib_1.runGit)(repoRoot, ["tag", "-l", ANALYTICS_TAG], 5000);
|
|
172
|
+
const existingTag = await (0, gitCli_1.runGit)(repoRoot, ["tag", "-l", ANALYTICS_TAG], 5000);
|
|
149
173
|
if (existingTag.stdout.trim()) {
|
|
150
|
-
// Tag already exists
|
|
151
174
|
return true;
|
|
152
175
|
}
|
|
153
|
-
|
|
154
|
-
const rootResult = await (0, lib_1.runGit)(repoRoot, ["rev-list", "--max-parents=0", "HEAD"], 10000);
|
|
176
|
+
const rootResult = await (0, gitCli_1.runGit)(repoRoot, ["rev-list", "--max-parents=0", "HEAD"], 10000);
|
|
155
177
|
if (rootResult.exitCode !== 0 || !rootResult.stdout.trim()) {
|
|
156
178
|
return false;
|
|
157
179
|
}
|
|
@@ -159,14 +181,9 @@ async function createAnalyticsTag(repoRoot) {
|
|
|
159
181
|
if (rootLines.length === 0) {
|
|
160
182
|
return false;
|
|
161
183
|
}
|
|
162
|
-
// Use the first root commit
|
|
163
184
|
const rootSha = rootLines[0];
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (tagResult.exitCode !== 0) {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
return true;
|
|
185
|
+
const tagResult = await (0, gitCli_1.runGit)(repoRoot, ["tag", ANALYTICS_TAG, rootSha], 5000);
|
|
186
|
+
return tagResult.exitCode === 0;
|
|
170
187
|
}
|
|
171
188
|
catch {
|
|
172
189
|
return false;
|
|
@@ -199,7 +216,8 @@ async function cleanupGlobalInstall() {
|
|
|
199
216
|
if (fs.existsSync(globalClaudeSettings)) {
|
|
200
217
|
const config = JSON.parse(await fs.promises.readFile(globalClaudeSettings, "utf8"));
|
|
201
218
|
if (config.hooks?.PostToolUse) {
|
|
202
|
-
config.hooks.PostToolUse = config.hooks.PostToolUse.filter((h) => !h?.hooks?.some((hh) => hh?.command?.includes("agentblame") ||
|
|
219
|
+
config.hooks.PostToolUse = config.hooks.PostToolUse.filter((h) => !h?.hooks?.some((hh) => hh?.command?.includes("agentblame") ||
|
|
220
|
+
hh?.command?.includes("capture")));
|
|
203
221
|
}
|
|
204
222
|
await fs.promises.writeFile(globalClaudeSettings, JSON.stringify(config, null, 2), "utf8");
|
|
205
223
|
results.claude = true;
|
|
@@ -208,7 +226,7 @@ async function cleanupGlobalInstall() {
|
|
|
208
226
|
catch {
|
|
209
227
|
// Ignore errors
|
|
210
228
|
}
|
|
211
|
-
// Clean up global database
|
|
229
|
+
// Clean up global database (old location)
|
|
212
230
|
const globalDb = path.join(home, ".agentblame");
|
|
213
231
|
try {
|
|
214
232
|
if (fs.existsSync(globalDb)) {
|
|
@@ -226,7 +244,7 @@ async function runInit(initArgs = []) {
|
|
|
226
244
|
// Check if Bun is installed (required for hooks)
|
|
227
245
|
if (!isBunInstalled()) {
|
|
228
246
|
const installCmd = process.platform === "win32"
|
|
229
|
-
?
|
|
247
|
+
? 'powershell -c "irm bun.sh/install.ps1 | iex"'
|
|
230
248
|
: "curl -fsSL https://bun.sh/install | bash";
|
|
231
249
|
console.log("");
|
|
232
250
|
console.log(" \x1b[31m✗\x1b[0m Bun is required but not installed");
|
|
@@ -241,7 +259,7 @@ async function runInit(initArgs = []) {
|
|
|
241
259
|
process.exit(1);
|
|
242
260
|
}
|
|
243
261
|
// Validate we're in a git repo
|
|
244
|
-
const repoRoot = await (0,
|
|
262
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
245
263
|
if (!repoRoot) {
|
|
246
264
|
console.log("");
|
|
247
265
|
console.log(" \x1b[31m✗\x1b[0m Not in a git repository");
|
|
@@ -252,7 +270,7 @@ async function runInit(initArgs = []) {
|
|
|
252
270
|
}
|
|
253
271
|
// Header
|
|
254
272
|
console.log("");
|
|
255
|
-
console.log(" \x1b[1m\x1b[35m◆\x1b[0m \x1b[1mAgent Blame\x1b[0m");
|
|
273
|
+
console.log(" \x1b[1m\x1b[35m◆\x1b[0m \x1b[1mAgent Blame v3\x1b[0m");
|
|
256
274
|
console.log(" \x1b[2mTrack AI-generated code in your commits\x1b[0m");
|
|
257
275
|
console.log("");
|
|
258
276
|
const repoName = path.basename(repoRoot);
|
|
@@ -275,17 +293,26 @@ async function runInit(initArgs = []) {
|
|
|
275
293
|
}
|
|
276
294
|
// Track results
|
|
277
295
|
const results = [];
|
|
278
|
-
// Create .agentblame directory and initialize SQLite database
|
|
296
|
+
// Create .git/agentblame directory and initialize SQLite database
|
|
279
297
|
try {
|
|
280
|
-
|
|
281
|
-
(0,
|
|
282
|
-
(0,
|
|
283
|
-
|
|
298
|
+
(0, storage_1.ensureAgentBlameDirs)(repoRoot);
|
|
299
|
+
const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
|
|
300
|
+
(0, database_1.setDatabasePath)(dbPath);
|
|
301
|
+
(0, database_1.initDatabase)();
|
|
302
|
+
results.push({ name: "Database (.git/agentblame/)", success: true });
|
|
284
303
|
}
|
|
285
304
|
catch (err) {
|
|
286
305
|
results.push({ name: "Database", success: false });
|
|
287
306
|
}
|
|
288
|
-
//
|
|
307
|
+
// Initialize analytics
|
|
308
|
+
try {
|
|
309
|
+
await (0, analytics_1.initAnalytics)(repoRoot);
|
|
310
|
+
results.push({ name: "Analytics", success: true });
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
results.push({ name: "Analytics", success: false });
|
|
314
|
+
}
|
|
315
|
+
// Add old .agentblame/ to .gitignore (for backwards compatibility)
|
|
289
316
|
try {
|
|
290
317
|
const gitignorePath = path.join(repoRoot, ".gitignore");
|
|
291
318
|
let gitignoreContent = "";
|
|
@@ -294,32 +321,33 @@ async function runInit(initArgs = []) {
|
|
|
294
321
|
}
|
|
295
322
|
let entriesToAdd = "";
|
|
296
323
|
if (!gitignoreContent.includes(".agentblame")) {
|
|
297
|
-
entriesToAdd += "\n# Agent Blame local database\n.agentblame/\n";
|
|
324
|
+
entriesToAdd += "\n# Agent Blame local database (legacy)\n.agentblame/\n";
|
|
298
325
|
}
|
|
299
326
|
if (!gitignoreContent.includes(".opencode")) {
|
|
300
|
-
entriesToAdd +=
|
|
327
|
+
entriesToAdd +=
|
|
328
|
+
"\n# OpenCode local plugin (installed by agentblame init)\n.opencode/\n";
|
|
301
329
|
}
|
|
302
330
|
if (entriesToAdd) {
|
|
303
331
|
await fs.promises.appendFile(gitignorePath, entriesToAdd);
|
|
304
332
|
}
|
|
305
333
|
results.push({ name: "Updated .gitignore", success: true });
|
|
306
334
|
}
|
|
307
|
-
catch
|
|
335
|
+
catch {
|
|
308
336
|
results.push({ name: "Updated .gitignore", success: false });
|
|
309
337
|
}
|
|
310
338
|
// Install editor hooks (repo-level)
|
|
311
|
-
const cursorSuccess = await (0,
|
|
339
|
+
const cursorSuccess = await (0, hooks_1.installCursorHooks)(repoRoot);
|
|
312
340
|
results.push({ name: "Cursor hooks", success: cursorSuccess });
|
|
313
|
-
const claudeSuccess = await (0,
|
|
341
|
+
const claudeSuccess = await (0, hooks_1.installClaudeHooks)(repoRoot);
|
|
314
342
|
results.push({ name: "Claude Code hooks", success: claudeSuccess });
|
|
315
|
-
const opencodeSuccess = await (0,
|
|
343
|
+
const opencodeSuccess = await (0, hooks_1.installOpenCodeHooks)(repoRoot);
|
|
316
344
|
results.push({ name: "OpenCode hooks", success: opencodeSuccess });
|
|
317
345
|
// Install repo hooks and workflow
|
|
318
|
-
const gitHookSuccess = await (0,
|
|
346
|
+
const gitHookSuccess = await (0, hooks_1.installGitHook)(repoRoot);
|
|
319
347
|
results.push({ name: "Git post-commit hook", success: gitHookSuccess });
|
|
320
|
-
const notesPushSuccess = await (0,
|
|
348
|
+
const notesPushSuccess = await (0, gitConfig_1.configureNotesSync)(repoRoot);
|
|
321
349
|
results.push({ name: "Notes auto-push", success: notesPushSuccess });
|
|
322
|
-
const githubActionSuccess = await (0,
|
|
350
|
+
const githubActionSuccess = await (0, hooks_1.installGitHubAction)(repoRoot);
|
|
323
351
|
results.push({ name: "GitHub Actions workflow", success: githubActionSuccess });
|
|
324
352
|
// Create analytics anchor tag
|
|
325
353
|
const analyticsTagSuccess = await createAnalyticsTag(repoRoot);
|
|
@@ -356,7 +384,7 @@ async function runInit(initArgs = []) {
|
|
|
356
384
|
async function runClean(uninstallArgs = []) {
|
|
357
385
|
const forceCleanup = uninstallArgs.includes("--force") || uninstallArgs.includes("-f");
|
|
358
386
|
// Validate we're in a git repo
|
|
359
|
-
const repoRoot = await (0,
|
|
387
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
360
388
|
if (!repoRoot) {
|
|
361
389
|
console.log("");
|
|
362
390
|
console.log(" \x1b[31m✗\x1b[0m Not in a git repository");
|
|
@@ -391,31 +419,42 @@ async function runClean(uninstallArgs = []) {
|
|
|
391
419
|
// Track results
|
|
392
420
|
const results = [];
|
|
393
421
|
// Remove editor hooks (repo-level)
|
|
394
|
-
const cursorSuccess = await (0,
|
|
422
|
+
const cursorSuccess = await (0, hooks_1.uninstallCursorHooks)(repoRoot);
|
|
395
423
|
results.push({ name: "Cursor hooks", success: cursorSuccess });
|
|
396
|
-
const claudeSuccess = await (0,
|
|
424
|
+
const claudeSuccess = await (0, hooks_1.uninstallClaudeHooks)(repoRoot);
|
|
397
425
|
results.push({ name: "Claude Code hooks", success: claudeSuccess });
|
|
398
|
-
const opencodeSuccess = await (0,
|
|
426
|
+
const opencodeSuccess = await (0, hooks_1.uninstallOpenCodeHooks)(repoRoot);
|
|
399
427
|
results.push({ name: "OpenCode hooks", success: opencodeSuccess });
|
|
400
428
|
// Remove repo hooks and workflow
|
|
401
|
-
const gitHookSuccess = await (0,
|
|
429
|
+
const gitHookSuccess = await (0, hooks_1.uninstallGitHook)(repoRoot);
|
|
402
430
|
results.push({ name: "Git post-commit hook", success: gitHookSuccess });
|
|
403
|
-
const notesPushSuccess = await (0,
|
|
431
|
+
const notesPushSuccess = await (0, gitConfig_1.removeNotesSync)(repoRoot);
|
|
404
432
|
results.push({ name: "Notes auto-push", success: notesPushSuccess });
|
|
405
|
-
const githubActionSuccess = await (0,
|
|
433
|
+
const githubActionSuccess = await (0, hooks_1.uninstallGitHubAction)(repoRoot);
|
|
406
434
|
results.push({ name: "GitHub Actions workflow", success: githubActionSuccess });
|
|
407
|
-
// Remove .agentblame directory (
|
|
408
|
-
const
|
|
435
|
+
// Remove .git/agentblame directory (v3 location)
|
|
436
|
+
const agentBlameGitDir = (0, storage_1.getAgentBlameGitDir)(repoRoot);
|
|
409
437
|
let dbSuccess = true;
|
|
410
438
|
try {
|
|
411
|
-
if (fs.existsSync(
|
|
412
|
-
await fs.promises.rm(
|
|
439
|
+
if (fs.existsSync(agentBlameGitDir)) {
|
|
440
|
+
await fs.promises.rm(agentBlameGitDir, { recursive: true });
|
|
413
441
|
}
|
|
414
442
|
}
|
|
415
443
|
catch {
|
|
416
444
|
dbSuccess = false;
|
|
417
445
|
}
|
|
418
|
-
results.push({ name: "Database", success: dbSuccess });
|
|
446
|
+
results.push({ name: "Database (.git/agentblame/)", success: dbSuccess });
|
|
447
|
+
// Also remove legacy .agentblame directory
|
|
448
|
+
const legacyDir = path.join(repoRoot, ".agentblame");
|
|
449
|
+
try {
|
|
450
|
+
if (fs.existsSync(legacyDir)) {
|
|
451
|
+
await fs.promises.rm(legacyDir, { recursive: true });
|
|
452
|
+
results.push({ name: "Legacy database (.agentblame/)", success: true });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// Ignore
|
|
457
|
+
}
|
|
419
458
|
// Print results
|
|
420
459
|
console.log(" \x1b[2m─────────────────────────────────────────\x1b[0m");
|
|
421
460
|
console.log("");
|
|
@@ -446,12 +485,18 @@ async function runBlame(args) {
|
|
|
446
485
|
else if (arg === "--summary") {
|
|
447
486
|
options.summary = true;
|
|
448
487
|
}
|
|
488
|
+
else if (arg === "--prompts" || arg === "-p") {
|
|
489
|
+
options.showPrompts = true;
|
|
490
|
+
}
|
|
491
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
492
|
+
options.verbose = true;
|
|
493
|
+
}
|
|
449
494
|
else if (!arg.startsWith("-")) {
|
|
450
495
|
filePath = arg;
|
|
451
496
|
}
|
|
452
497
|
}
|
|
453
498
|
if (!filePath) {
|
|
454
|
-
console.error("Usage: agentblame blame [--json|--summary] <file>");
|
|
499
|
+
console.error("Usage: agentblame blame [--json|--summary|--prompts|--verbose] <file>");
|
|
455
500
|
process.exit(1);
|
|
456
501
|
}
|
|
457
502
|
await (0, blame_1.blame)(filePath, options);
|
|
@@ -470,45 +515,262 @@ async function runSync(args) {
|
|
|
470
515
|
}
|
|
471
516
|
async function runStatus() {
|
|
472
517
|
// Find repo root and set database directory
|
|
473
|
-
const repoRoot = await (0,
|
|
518
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
474
519
|
if (!repoRoot) {
|
|
475
520
|
console.error("Not in a git repository");
|
|
476
521
|
process.exit(1);
|
|
477
522
|
}
|
|
478
|
-
const
|
|
479
|
-
(0,
|
|
480
|
-
console.log("\nAgent Blame Status\n");
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
console.log(
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
523
|
+
const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
|
|
524
|
+
(0, database_1.setDatabasePath)(dbPath);
|
|
525
|
+
console.log("\nAgent Blame v3 Status\n");
|
|
526
|
+
try {
|
|
527
|
+
const stats = (0, database_1.getStats)();
|
|
528
|
+
console.log(`Sessions: ${stats.sessions}`);
|
|
529
|
+
console.log(`Prompts: ${stats.prompts}`);
|
|
530
|
+
console.log(`Tool Calls: ${stats.toolCalls}`);
|
|
531
|
+
if (stats.sessions > 0) {
|
|
532
|
+
console.log("\nRecent sessions:");
|
|
533
|
+
const recent = (0, database_1.getRecentSessions)(5);
|
|
534
|
+
for (const session of recent) {
|
|
535
|
+
const time = new Date(session.createdAt).toLocaleTimeString();
|
|
536
|
+
const agent = session.agent;
|
|
537
|
+
const model = session.model || "";
|
|
538
|
+
const committed = session.firstCommitSha ? "committed" : "pending";
|
|
539
|
+
console.log(` [${agent}] ${session.id.slice(0, 8)} - ${model || "unknown"} (${committed}) at ${time}`);
|
|
540
|
+
}
|
|
541
|
+
if (stats.sessions > 5) {
|
|
542
|
+
console.log(` ... and ${stats.sessions - 5} more`);
|
|
543
|
+
}
|
|
493
544
|
}
|
|
494
545
|
}
|
|
546
|
+
catch (err) {
|
|
547
|
+
console.log(" No database found. Run 'agentblame init' first.");
|
|
548
|
+
}
|
|
495
549
|
console.log("");
|
|
496
550
|
}
|
|
551
|
+
async function runConfig(args) {
|
|
552
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
553
|
+
if (!repoRoot) {
|
|
554
|
+
console.error("Not in a git repository");
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}
|
|
557
|
+
const subcommand = args[0];
|
|
558
|
+
const key = args[1];
|
|
559
|
+
const value = args[2];
|
|
560
|
+
// No subcommand - list all config
|
|
561
|
+
if (!subcommand) {
|
|
562
|
+
console.log("\nAgent Blame Configuration\n");
|
|
563
|
+
const configs = await (0, config_1.listConfig)(repoRoot);
|
|
564
|
+
for (const cfg of configs) {
|
|
565
|
+
const status = cfg.isDefault ? "(default)" : "(custom)";
|
|
566
|
+
console.log(` ${cfg.key}: ${cfg.value} ${status}`);
|
|
567
|
+
}
|
|
568
|
+
console.log("\nUse 'agentblame config set <key> <value>' to change settings.");
|
|
569
|
+
console.log("");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Get a config value
|
|
573
|
+
if (subcommand === "get") {
|
|
574
|
+
if (!key) {
|
|
575
|
+
console.error("Usage: agentblame config get <key>");
|
|
576
|
+
console.error(`Valid keys: ${config_1.VALID_CONFIG_KEYS.join(", ")}`);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
if (!config_1.VALID_CONFIG_KEYS.includes(key)) {
|
|
580
|
+
console.error(`Unknown config key: ${key}`);
|
|
581
|
+
console.error(`Valid keys: ${config_1.VALID_CONFIG_KEYS.join(", ")}`);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
const currentValue = await (0, config_1.getConfig)(repoRoot, key);
|
|
585
|
+
console.log(String(currentValue));
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
// Set a config value
|
|
589
|
+
if (subcommand === "set") {
|
|
590
|
+
if (!key || value === undefined) {
|
|
591
|
+
console.error("Usage: agentblame config set <key> <value>");
|
|
592
|
+
console.error(`Valid keys: ${config_1.VALID_CONFIG_KEYS.join(", ")}`);
|
|
593
|
+
process.exit(1);
|
|
594
|
+
}
|
|
595
|
+
if (!config_1.VALID_CONFIG_KEYS.includes(key)) {
|
|
596
|
+
console.error(`Unknown config key: ${key}`);
|
|
597
|
+
console.error(`Valid keys: ${config_1.VALID_CONFIG_KEYS.join(", ")}`);
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
const parsedValue = (0, config_1.parseConfigValue)(key, value);
|
|
601
|
+
if (parsedValue === null) {
|
|
602
|
+
console.error(`Invalid value for ${key}: ${value}`);
|
|
603
|
+
console.error("For boolean settings, use: true/false, yes/no, or 1/0");
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
await (0, config_1.setConfig)(repoRoot, key, parsedValue);
|
|
607
|
+
console.log(`Set ${key} = ${parsedValue}`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
console.error(`Unknown config subcommand: ${subcommand}`);
|
|
611
|
+
console.error("Usage: agentblame config [get|set] <key> [value]");
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
497
614
|
async function runPrune() {
|
|
498
615
|
// Find repo root and set database directory
|
|
499
|
-
const repoRoot = await (0,
|
|
616
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
500
617
|
if (!repoRoot) {
|
|
501
618
|
console.error("Not in a git repository");
|
|
502
619
|
process.exit(1);
|
|
503
620
|
}
|
|
504
|
-
const
|
|
505
|
-
(0,
|
|
621
|
+
const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
|
|
622
|
+
(0, database_1.setDatabasePath)(dbPath);
|
|
506
623
|
console.log("\nAgent Blame Prune\n");
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
console.log(`
|
|
624
|
+
// Clean up old database entries
|
|
625
|
+
const dbResult = (0, database_1.cleanupOldEntries)();
|
|
626
|
+
console.log(` Database: Removed ${dbResult.removed} sessions, kept ${dbResult.kept}`);
|
|
627
|
+
// Clean up stale working directories
|
|
628
|
+
const workingResult = await (0, storage_1.cleanupStaleWorkingDirs)(repoRoot);
|
|
629
|
+
console.log(` Working dirs: Cleaned ${workingResult.cleaned.length}, kept ${workingResult.kept.length}`);
|
|
510
630
|
console.log("\nPrune complete!");
|
|
511
631
|
}
|
|
632
|
+
async function runMigrate() {
|
|
633
|
+
// Find repo root
|
|
634
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
635
|
+
if (!repoRoot) {
|
|
636
|
+
console.error("Not in a git repository");
|
|
637
|
+
process.exit(1);
|
|
638
|
+
}
|
|
639
|
+
console.log("\nAgent Blame v3 Migration\n");
|
|
640
|
+
// Ensure new directories exist
|
|
641
|
+
(0, storage_1.ensureAgentBlameDirs)(repoRoot);
|
|
642
|
+
const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
|
|
643
|
+
(0, database_1.setDatabasePath)(dbPath);
|
|
644
|
+
// Reset database to v3 schema
|
|
645
|
+
console.log(" Resetting database to v3 schema...");
|
|
646
|
+
(0, database_1.resetDatabase)();
|
|
647
|
+
console.log(" \x1b[32m✓\x1b[0m Database migrated");
|
|
648
|
+
// Initialize analytics
|
|
649
|
+
console.log(" Initializing analytics...");
|
|
650
|
+
await (0, analytics_1.initAnalytics)(repoRoot);
|
|
651
|
+
console.log(" \x1b[32m✓\x1b[0m Analytics initialized");
|
|
652
|
+
// Remove legacy .agentblame directory if it exists
|
|
653
|
+
const legacyDir = path.join(repoRoot, ".agentblame");
|
|
654
|
+
if (fs.existsSync(legacyDir)) {
|
|
655
|
+
console.log(" Removing legacy database...");
|
|
656
|
+
await fs.promises.rm(legacyDir, { recursive: true });
|
|
657
|
+
console.log(" \x1b[32m✓\x1b[0m Legacy database removed");
|
|
658
|
+
}
|
|
659
|
+
console.log("\n\x1b[32m✓\x1b[0m Migration complete!");
|
|
660
|
+
console.log("\nNote: Existing v2 git notes remain readable.");
|
|
661
|
+
console.log("New commits will use the v3 format.\n");
|
|
662
|
+
}
|
|
663
|
+
async function runDebug() {
|
|
664
|
+
const repoRoot = await (0, gitCli_1.getRepoRoot)(process.cwd());
|
|
665
|
+
if (!repoRoot) {
|
|
666
|
+
console.error("Not in a git repository");
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}
|
|
669
|
+
const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
|
|
670
|
+
(0, database_1.setDatabasePath)(dbPath);
|
|
671
|
+
console.log("\n\x1b[1mAgent Blame Debug Info\x1b[0m\n");
|
|
672
|
+
// Show repo info
|
|
673
|
+
console.log(`\x1b[36mRepository:\x1b[0m ${repoRoot}`);
|
|
674
|
+
// Show current HEAD
|
|
675
|
+
const head = await (0, storage_1.getGitHead)(repoRoot);
|
|
676
|
+
console.log(`\x1b[36mCurrent HEAD:\x1b[0m ${head || "none"}`);
|
|
677
|
+
// Show database stats
|
|
678
|
+
console.log("\n\x1b[1mDatabase:\x1b[0m");
|
|
679
|
+
try {
|
|
680
|
+
const stats = (0, database_1.getStats)();
|
|
681
|
+
console.log(` Sessions: ${stats.sessions}`);
|
|
682
|
+
console.log(` Prompts: ${stats.prompts}`);
|
|
683
|
+
console.log(` Tool Calls: ${stats.toolCalls}`);
|
|
684
|
+
// Show recent sessions with their tool calls
|
|
685
|
+
if (stats.sessions > 0) {
|
|
686
|
+
console.log("\n\x1b[1mRecent Sessions:\x1b[0m");
|
|
687
|
+
const sessions = (0, database_1.getRecentSessions)(5);
|
|
688
|
+
for (const session of sessions) {
|
|
689
|
+
console.log(`\n \x1b[33m${session.id}\x1b[0m`);
|
|
690
|
+
console.log(` Agent: ${session.agent}`);
|
|
691
|
+
console.log(` Model: ${session.model || "unknown"}`);
|
|
692
|
+
console.log(` Created: ${session.createdAt}`);
|
|
693
|
+
console.log(` First Commit: ${session.firstCommitSha || "pending"}`);
|
|
694
|
+
// Show tool calls for this session
|
|
695
|
+
const toolCalls = (0, database_1.getToolCallsForSession)(session.id);
|
|
696
|
+
if (toolCalls.length > 0) {
|
|
697
|
+
// Count tool types (all names are lowercase)
|
|
698
|
+
const fileModifyingNames = ["edit", "write", "multiedit"];
|
|
699
|
+
const fileModifying = toolCalls.filter(tc => fileModifyingNames.includes(tc.toolName));
|
|
700
|
+
const readOnly = toolCalls.filter(tc => !fileModifyingNames.includes(tc.toolName));
|
|
701
|
+
console.log(` Tool Calls: ${toolCalls.length} total (${fileModifying.length} edits, ${readOnly.length} other)`);
|
|
702
|
+
console.log(` Sequence:`);
|
|
703
|
+
for (const tc of toolCalls.slice(0, 10)) {
|
|
704
|
+
const isEdit = fileModifyingNames.includes(tc.toolName);
|
|
705
|
+
const icon = isEdit ? "✏️" : "🔍";
|
|
706
|
+
const filePart = tc.filePath ? ` → ${tc.filePath}` : "";
|
|
707
|
+
console.log(` ${icon} ${tc.toolName}${filePart}`);
|
|
708
|
+
}
|
|
709
|
+
if (toolCalls.length > 10) {
|
|
710
|
+
console.log(` ... and ${toolCalls.length - 10} more`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
catch (err) {
|
|
717
|
+
console.log(` \x1b[31mError:\x1b[0m ${err}`);
|
|
718
|
+
}
|
|
719
|
+
// Show working directories
|
|
720
|
+
console.log("\n\x1b[1mWorking Directories:\x1b[0m");
|
|
721
|
+
const baseShas = (0, storage_1.getActiveBaseShas)(repoRoot);
|
|
722
|
+
if (baseShas.length === 0) {
|
|
723
|
+
console.log(" \x1b[33mNo working directories found\x1b[0m");
|
|
724
|
+
console.log(" This means no AI edits have been captured since the last commit.");
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
for (const baseSha of baseShas) {
|
|
728
|
+
console.log(`\n \x1b[33m${baseSha}\x1b[0m`);
|
|
729
|
+
const entries = (0, storage_1.readWorkingLog)(repoRoot, baseSha);
|
|
730
|
+
if (entries.length === 0) {
|
|
731
|
+
console.log(" \x1b[31mEmpty (no snapshots.jsonl entries)\x1b[0m");
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
console.log(` Entries: ${entries.length}`);
|
|
735
|
+
for (const entry of entries.slice(0, 5)) {
|
|
736
|
+
const sessionStr = entry.session ? entry.session.slice(0, 8) : "human";
|
|
737
|
+
console.log(` - ${entry.file} (${entry.type}, session: ${sessionStr})`);
|
|
738
|
+
}
|
|
739
|
+
if (entries.length > 5) {
|
|
740
|
+
console.log(` ... and ${entries.length - 5} more`);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// Check agentblame directory structure
|
|
746
|
+
console.log("\n\x1b[1mDirectory Structure:\x1b[0m");
|
|
747
|
+
const agentBlameDir = (0, storage_1.getAgentBlameGitDir)(repoRoot);
|
|
748
|
+
if (fs.existsSync(agentBlameDir)) {
|
|
749
|
+
console.log(` ${agentBlameDir}/`);
|
|
750
|
+
try {
|
|
751
|
+
const entries = fs.readdirSync(agentBlameDir);
|
|
752
|
+
for (const entry of entries) {
|
|
753
|
+
const entryPath = path.join(agentBlameDir, entry);
|
|
754
|
+
const stat = fs.statSync(entryPath);
|
|
755
|
+
if (stat.isDirectory()) {
|
|
756
|
+
const subEntries = fs.readdirSync(entryPath);
|
|
757
|
+
console.log(` ${entry}/ (${subEntries.length} items)`);
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
console.log(` ${entry} (${stat.size} bytes)`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
catch (err) {
|
|
765
|
+
console.log(` \x1b[31mError reading directory:\x1b[0m ${err}`);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
console.log(` \x1b[31mNot found:\x1b[0m ${agentBlameDir}`);
|
|
770
|
+
console.log(" Run 'agentblame init' to set up the database.");
|
|
771
|
+
}
|
|
772
|
+
console.log("");
|
|
773
|
+
}
|
|
512
774
|
main().catch((err) => {
|
|
513
775
|
console.error(err);
|
|
514
776
|
process.exit(1);
|