@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.
Files changed (68) hide show
  1. package/dist/agentblame.js +3500 -0
  2. package/dist/blame.d.ts +4 -1
  3. package/dist/blame.js +280 -78
  4. package/dist/blame.js.map +1 -1
  5. package/dist/capture.d.ts +4 -7
  6. package/dist/capture.js +464 -486
  7. package/dist/capture.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +334 -72
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/analytics.d.ts +179 -0
  12. package/dist/lib/analytics.js +833 -0
  13. package/dist/lib/analytics.js.map +1 -0
  14. package/dist/lib/attribution.d.ts +54 -0
  15. package/dist/lib/attribution.js +266 -0
  16. package/dist/lib/attribution.js.map +1 -0
  17. package/dist/lib/checkpoint.d.ts +97 -0
  18. package/dist/lib/checkpoint.js +441 -0
  19. package/dist/lib/checkpoint.js.map +1 -0
  20. package/dist/lib/config.d.ts +46 -0
  21. package/dist/lib/config.js +123 -0
  22. package/dist/lib/config.js.map +1 -0
  23. package/dist/lib/database.d.ts +115 -85
  24. package/dist/lib/database.js +305 -325
  25. package/dist/lib/database.js.map +1 -1
  26. package/dist/lib/delta.d.ts +78 -0
  27. package/dist/lib/delta.js +309 -0
  28. package/dist/lib/delta.js.map +1 -0
  29. package/dist/lib/git/gitBlame.js +9 -4
  30. package/dist/lib/git/gitBlame.js.map +1 -1
  31. package/dist/lib/git/gitConfig.d.ts +5 -3
  32. package/dist/lib/git/gitConfig.js +41 -6
  33. package/dist/lib/git/gitConfig.js.map +1 -1
  34. package/dist/lib/git/gitDiff.d.ts +13 -1
  35. package/dist/lib/git/gitDiff.js +39 -7
  36. package/dist/lib/git/gitDiff.js.map +1 -1
  37. package/dist/lib/git/gitNotes.d.ts +30 -4
  38. package/dist/lib/git/gitNotes.js +140 -24
  39. package/dist/lib/git/gitNotes.js.map +1 -1
  40. package/dist/lib/hooks.d.ts +1 -0
  41. package/dist/lib/hooks.js +148 -27
  42. package/dist/lib/hooks.js.map +1 -1
  43. package/dist/lib/index.d.ts +7 -0
  44. package/dist/lib/index.js +13 -0
  45. package/dist/lib/index.js.map +1 -1
  46. package/dist/lib/storage.d.ts +163 -0
  47. package/dist/lib/storage.js +823 -0
  48. package/dist/lib/storage.js.map +1 -0
  49. package/dist/lib/trace.d.ts +118 -0
  50. package/dist/lib/trace.js +499 -0
  51. package/dist/lib/trace.js.map +1 -0
  52. package/dist/lib/types.d.ts +322 -114
  53. package/dist/lib/types.js +2 -1
  54. package/dist/lib/types.js.map +1 -1
  55. package/dist/lib/util.d.ts +8 -8
  56. package/dist/lib/util.js +18 -22
  57. package/dist/lib/util.js.map +1 -1
  58. package/dist/lib/watcher.d.ts +104 -0
  59. package/dist/lib/watcher.js +398 -0
  60. package/dist/lib/watcher.js.map +1 -0
  61. package/dist/post-merge.js +460 -421
  62. package/dist/post-merge.js.map +1 -1
  63. package/dist/process.d.ts +6 -5
  64. package/dist/process.js +182 -158
  65. package/dist/process.js.map +1 -1
  66. package/dist/sync.js +172 -131
  67. package/dist/sync.js.map +1 -1
  68. 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 lib_1 = require("./lib");
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 status Show pending AI edits
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
- // Check if tag already exists
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
- // Get the root commit(s)
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
- // Create the tag
165
- const tagResult = await (0, lib_1.runGit)(repoRoot, ["tag", ANALYTICS_TAG, rootSha], 5000);
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") || hh?.command?.includes("capture")));
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
- ? "powershell -c \"irm bun.sh/install.ps1 | iex\""
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, lib_1.getRepoRoot)(process.cwd());
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
- const agentblameDir = (0, lib_1.getAgentBlameDirForRepo)(repoRoot);
281
- (0, lib_1.setAgentBlameDir)(agentblameDir);
282
- (0, lib_1.initDatabase)();
283
- results.push({ name: "Database", success: true });
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
- // Add .agentblame/ and .opencode/ to .gitignore
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 += "\n# OpenCode local plugin (installed by agentblame init)\n.opencode/\n";
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 (err) {
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, lib_1.installCursorHooks)(repoRoot);
339
+ const cursorSuccess = await (0, hooks_1.installCursorHooks)(repoRoot);
312
340
  results.push({ name: "Cursor hooks", success: cursorSuccess });
313
- const claudeSuccess = await (0, lib_1.installClaudeHooks)(repoRoot);
341
+ const claudeSuccess = await (0, hooks_1.installClaudeHooks)(repoRoot);
314
342
  results.push({ name: "Claude Code hooks", success: claudeSuccess });
315
- const opencodeSuccess = await (0, lib_1.installOpenCodeHooks)(repoRoot);
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, lib_1.installGitHook)(repoRoot);
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, lib_1.configureNotesSync)(repoRoot);
348
+ const notesPushSuccess = await (0, gitConfig_1.configureNotesSync)(repoRoot);
321
349
  results.push({ name: "Notes auto-push", success: notesPushSuccess });
322
- const githubActionSuccess = await (0, lib_1.installGitHubAction)(repoRoot);
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, lib_1.getRepoRoot)(process.cwd());
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, lib_1.uninstallCursorHooks)(repoRoot);
422
+ const cursorSuccess = await (0, hooks_1.uninstallCursorHooks)(repoRoot);
395
423
  results.push({ name: "Cursor hooks", success: cursorSuccess });
396
- const claudeSuccess = await (0, lib_1.uninstallClaudeHooks)(repoRoot);
424
+ const claudeSuccess = await (0, hooks_1.uninstallClaudeHooks)(repoRoot);
397
425
  results.push({ name: "Claude Code hooks", success: claudeSuccess });
398
- const opencodeSuccess = await (0, lib_1.uninstallOpenCodeHooks)(repoRoot);
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, lib_1.uninstallGitHook)(repoRoot);
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, lib_1.removeNotesSync)(repoRoot);
431
+ const notesPushSuccess = await (0, gitConfig_1.removeNotesSync)(repoRoot);
404
432
  results.push({ name: "Notes auto-push", success: notesPushSuccess });
405
- const githubActionSuccess = await (0, lib_1.uninstallGitHubAction)(repoRoot);
433
+ const githubActionSuccess = await (0, hooks_1.uninstallGitHubAction)(repoRoot);
406
434
  results.push({ name: "GitHub Actions workflow", success: githubActionSuccess });
407
- // Remove .agentblame directory (database)
408
- const agentblameDir = (0, lib_1.getAgentBlameDirForRepo)(repoRoot);
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(agentblameDir)) {
412
- await fs.promises.rm(agentblameDir, { recursive: true });
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, lib_1.getRepoRoot)(process.cwd());
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 agentblameDir = (0, lib_1.getAgentBlameDirForRepo)(repoRoot);
479
- (0, lib_1.setAgentBlameDir)(agentblameDir);
480
- console.log("\nAgent Blame Status\n");
481
- const pendingCount = (0, lib_1.getPendingEditCount)();
482
- console.log(`Pending AI edits: ${pendingCount}`);
483
- if (pendingCount > 0) {
484
- console.log("\nRecent pending edits:");
485
- const recent = (0, lib_1.getRecentPendingEdits)(5);
486
- for (const edit of recent) {
487
- const time = new Date(edit.timestamp).toLocaleTimeString();
488
- const file = edit.filePath.split("/").pop();
489
- console.log(` [${edit.provider}] ${file} at ${time}`);
490
- }
491
- if (pendingCount > 5) {
492
- console.log(` ... and ${pendingCount - 5} more`);
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, lib_1.getRepoRoot)(process.cwd());
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 agentblameDir = (0, lib_1.getAgentBlameDirForRepo)(repoRoot);
505
- (0, lib_1.setAgentBlameDir)(agentblameDir);
621
+ const dbPath = (0, storage_1.getDatabasePath)(repoRoot);
622
+ (0, database_1.setDatabasePath)(dbPath);
506
623
  console.log("\nAgent Blame Prune\n");
507
- const result = (0, lib_1.cleanupOldEntries)();
508
- console.log(` Removed: ${result.removed} old entries`);
509
- console.log(` Kept: ${result.kept} entries`);
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);