@asifkibria/claude-code-toolkit 1.0.7 → 1.3.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 (79) hide show
  1. package/README.md +172 -475
  2. package/dist/CLAUDE.md +7 -0
  3. package/dist/__tests__/dashboard.test.d.ts +2 -0
  4. package/dist/__tests__/dashboard.test.d.ts.map +1 -0
  5. package/dist/__tests__/dashboard.test.js +606 -0
  6. package/dist/__tests__/dashboard.test.js.map +1 -0
  7. package/dist/__tests__/mcp-validator.test.d.ts +2 -0
  8. package/dist/__tests__/mcp-validator.test.d.ts.map +1 -0
  9. package/dist/__tests__/mcp-validator.test.js +217 -0
  10. package/dist/__tests__/mcp-validator.test.js.map +1 -0
  11. package/dist/__tests__/security.test.d.ts +2 -0
  12. package/dist/__tests__/security.test.d.ts.map +1 -0
  13. package/dist/__tests__/security.test.js +375 -0
  14. package/dist/__tests__/security.test.js.map +1 -0
  15. package/dist/__tests__/session-recovery.test.d.ts +2 -0
  16. package/dist/__tests__/session-recovery.test.d.ts.map +1 -0
  17. package/dist/__tests__/session-recovery.test.js +230 -0
  18. package/dist/__tests__/session-recovery.test.js.map +1 -0
  19. package/dist/__tests__/storage.test.d.ts +2 -0
  20. package/dist/__tests__/storage.test.d.ts.map +1 -0
  21. package/dist/__tests__/storage.test.js +241 -0
  22. package/dist/__tests__/storage.test.js.map +1 -0
  23. package/dist/__tests__/trace.test.d.ts +2 -0
  24. package/dist/__tests__/trace.test.d.ts.map +1 -0
  25. package/dist/__tests__/trace.test.js +376 -0
  26. package/dist/__tests__/trace.test.js.map +1 -0
  27. package/dist/cli.js +680 -39
  28. package/dist/cli.js.map +1 -1
  29. package/dist/index.js +674 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/lib/alerts.d.ts +38 -0
  32. package/dist/lib/alerts.d.ts.map +1 -0
  33. package/dist/lib/alerts.js +296 -0
  34. package/dist/lib/alerts.js.map +1 -0
  35. package/dist/lib/dashboard-ui.d.ts +2 -0
  36. package/dist/lib/dashboard-ui.d.ts.map +1 -0
  37. package/dist/lib/dashboard-ui.js +2267 -0
  38. package/dist/lib/dashboard-ui.js.map +1 -0
  39. package/dist/lib/dashboard.d.ts +16 -0
  40. package/dist/lib/dashboard.d.ts.map +1 -0
  41. package/dist/lib/dashboard.js +1571 -0
  42. package/dist/lib/dashboard.js.map +1 -0
  43. package/dist/lib/git.d.ts +29 -0
  44. package/dist/lib/git.d.ts.map +1 -0
  45. package/dist/lib/git.js +124 -0
  46. package/dist/lib/git.js.map +1 -0
  47. package/dist/lib/logs.d.ts +42 -0
  48. package/dist/lib/logs.d.ts.map +1 -0
  49. package/dist/lib/logs.js +166 -0
  50. package/dist/lib/logs.js.map +1 -0
  51. package/dist/lib/mcp-validator.d.ts +109 -0
  52. package/dist/lib/mcp-validator.d.ts.map +1 -0
  53. package/dist/lib/mcp-validator.js +601 -0
  54. package/dist/lib/mcp-validator.js.map +1 -0
  55. package/dist/lib/scanner.d.ts +6 -2
  56. package/dist/lib/scanner.d.ts.map +1 -1
  57. package/dist/lib/scanner.js +120 -19
  58. package/dist/lib/scanner.js.map +1 -1
  59. package/dist/lib/search.d.ts +56 -0
  60. package/dist/lib/search.d.ts.map +1 -0
  61. package/dist/lib/search.js +284 -0
  62. package/dist/lib/search.js.map +1 -0
  63. package/dist/lib/security.d.ts +96 -0
  64. package/dist/lib/security.d.ts.map +1 -0
  65. package/dist/lib/security.js +795 -0
  66. package/dist/lib/security.js.map +1 -0
  67. package/dist/lib/session-recovery.d.ts +60 -0
  68. package/dist/lib/session-recovery.d.ts.map +1 -0
  69. package/dist/lib/session-recovery.js +433 -0
  70. package/dist/lib/session-recovery.js.map +1 -0
  71. package/dist/lib/storage.d.ts +68 -0
  72. package/dist/lib/storage.d.ts.map +1 -0
  73. package/dist/lib/storage.js +503 -0
  74. package/dist/lib/storage.js.map +1 -0
  75. package/dist/lib/trace.d.ts +119 -0
  76. package/dist/lib/trace.d.ts.map +1 -0
  77. package/dist/lib/trace.js +649 -0
  78. package/dist/lib/trace.js.map +1 -0
  79. package/package.json +11 -3
package/dist/cli.js CHANGED
@@ -7,6 +7,15 @@ import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import * as os from "os";
9
9
  import { findAllJsonlFiles, findBackupFiles, scanFile, fixFile, getConversationStats, restoreFromBackup, deleteOldBackups, exportConversationToFile, estimateContextSize, formatContextEstimate, generateUsageAnalytics, formatUsageAnalytics, findDuplicates, formatDuplicateReport, findArchiveCandidates, archiveConversations, formatArchiveReport, runMaintenance, formatMaintenanceReport, generateCronSchedule, generateLaunchdPlist, } from "./lib/scanner.js";
10
+ import { analyzeClaudeStorage, cleanClaudeDirectory, findCleanupTargets, formatStorageReport, formatCleanupReport, } from "./lib/storage.js";
11
+ import { diagnoseMcpServers, formatMcpDiagnosticReport, analyzeMcpPerformance, formatMcpPerformanceReport, } from "./lib/mcp-validator.js";
12
+ import { listSessions, diagnoseSession, repairSession, extractSessionContent, formatSessionReport, formatSessionDiagnosticReport, } from "./lib/session-recovery.js";
13
+ import { scanForSecrets, scanForPII, auditSession, enforceRetention, formatSecretsScanReport, formatPIIScanReport, formatAuditReport, formatRetentionReport, } from "./lib/security.js";
14
+ import { inventoryTraces, cleanTraces, wipeAllTraces, generateTraceGuardHooks, formatTraceInventory, formatTraceCleanReport, formatTraceGuardConfig, } from "./lib/trace.js";
15
+ import { startDashboard, stopDashboard, isDashboardRunning } from "./lib/dashboard.js";
16
+ import { searchConversations, formatSearchReport, compareSessionsByCID, formatSessionDiff } from "./lib/search.js";
17
+ import { linkSessionsToGit, formatGitLinkReport } from "./lib/git.js";
18
+ import { checkAlerts, checkQuotas, formatAlertsReport, formatQuotasReport } from "./lib/alerts.js";
10
19
  function formatContentType(type) {
11
20
  switch (type) {
12
21
  case "image": return "🖼️ image";
@@ -30,57 +39,102 @@ function formatDate(date) {
30
39
  }
31
40
  function printHelp() {
32
41
  console.log(`
33
- Claude Code Toolkit v1.0.7
34
- Maintain, optimize, and troubleshoot your Claude Code installation.
35
- Fixes oversized images, PDFs, documents, and large text content.
42
+ Claude Code Toolkit v1.3.0
43
+ Maintain, optimize, secure, and troubleshoot your Claude Code installation.
36
44
 
37
45
  USAGE:
38
46
  cct <command> [options]
39
47
  claude-code-toolkit <command> [options]
40
48
 
41
49
  COMMANDS:
42
- health Quick health check (start here!)
43
- stats Show conversation statistics
44
- context Estimate context/token usage
45
- analytics Usage analytics dashboard
46
- duplicates Find duplicate content and conversations
47
- archive Archive old/inactive conversations
48
- maintenance Run maintenance checks and actions
49
- scan Scan for issues (dry run)
50
- fix Fix all detected issues
51
- export Export conversation to markdown or JSON
52
- backups List backup files
53
- restore <path> Restore from a backup file
54
- cleanup Delete old backup files
50
+ health Quick health check (start here!)
51
+ search <query> Full-text search across all conversations
52
+ stats Show conversation statistics
53
+ context Estimate context/token usage
54
+ cost Estimate API costs based on token usage
55
+ analytics Usage analytics dashboard
56
+ duplicates Find duplicate content and conversations
57
+ archive Archive old/inactive conversations
58
+ maintenance Run maintenance checks and actions
59
+ scan Scan for issues (dry run)
60
+ fix Fix all detected issues
61
+ watch Monitor sessions for new oversized content
62
+ export Export conversation to markdown or JSON
63
+ backups List backup files
64
+ restore <path> Restore from a backup file
65
+ cleanup Delete old backup files
66
+ clean Analyze and clean .claude directory
67
+ mcp-validate Validate MCP server configurations
68
+ mcp-perf Track MCP server performance and usage
69
+ sessions List all sessions with health status
70
+ diff <id1> <id2> Compare two sessions
71
+ git Link sessions to git branches/commits
72
+ recover <id> Diagnose/repair/extract from a session
73
+ security-scan Scan conversations for leaked secrets
74
+ pii-scan Scan conversations for personal data (PII)
75
+ --details: Show unmasked values (handle with care!)
76
+ audit <id> Audit a session's actions (files, commands, etc.)
77
+ retention Enforce data retention policy
78
+ alerts Check for issues and notifications
79
+ quotas Show usage quotas and limits
80
+ dashboard Open web dashboard in browser
81
+ dashboard --daemon Run dashboard as background process
82
+ dashboard --stop Stop background dashboard
83
+ trace Show full trace inventory
84
+ trace clean Selective trace cleanup
85
+ trace wipe Secure wipe all traces (requires --confirm)
86
+ trace guard Generate trace prevention hooks
55
87
 
56
88
  OPTIONS:
57
89
  -f, --file <path> Target a specific file
58
90
  -o, --output <path> Output file path (for export)
91
+ -q, --query <text> Search query text
92
+ --role <type> For search: filter by role (user|assistant|all)
59
93
  --format <type> Export format: markdown or json (default: markdown)
60
94
  --with-tools Include tool results in export
61
95
  -d, --dry-run Show what would be done without making changes
62
96
  --no-backup Skip creating backups when fixing (not recommended)
63
- --days <n> For cleanup/archive: days threshold (default: 7/30)
64
- --limit <n> For stats: limit results (default: 10)
97
+ --days <n> For cleanup/archive/retention: days threshold (default: 7/30)
98
+ --limit <n> For stats/search: limit results (default: 10/50)
65
99
  --sort <field> For stats: sort by size|messages|images|modified
66
100
  --auto For maintenance: run automatically without prompts
67
101
  --schedule For maintenance: show cron/launchd setup
102
+ --category <type> For clean: target specific category
103
+ --test For mcp-validate: test server connectivity
104
+ --extract For recover: extract salvageable content
105
+ --repair For recover: attempt repair
106
+ --confirm For trace wipe: confirm destructive operation
107
+ --keep-settings For trace wipe: preserve settings.json
108
+ --mode <mode> For trace guard: paranoid|moderate|minimal
109
+ --install For trace guard: auto-install hooks
110
+ --categories <list> For trace clean: comma-separated categories
111
+ --port <n> For dashboard: port number (default: 1405)
112
+ --daemon For dashboard: run as background process
113
+ --stop For dashboard: stop background process
114
+ --token <value> Require dashboard auth with bearer token
115
+ --project <path> Filter by project path
116
+ --json Output structured JSON (scan/watch)
117
+ --interval <sec> For watch: polling interval in seconds (default: 15)
118
+ --auto-fix For watch: automatically fix detected issues
68
119
  -h, --help Show this help message
69
120
  -v, --version Show version
70
121
 
71
122
  EXAMPLES:
72
123
  cct health # Quick health check
73
- cct stats --limit 5 --sort size # Top 5 largest conversations
74
- cct context -f /path/to/file # Estimate context/token usage
75
- cct analytics # Usage analytics dashboard
76
- cct duplicates # Find duplicate content
77
- cct archive --days 60 --dry-run # Preview conversations to archive
78
- cct maintenance # Run maintenance checks
79
- cct maintenance --schedule # Show scheduled maintenance setup
80
- cct scan # Scan for issues
81
- cct fix # Fix all issues
82
- cct export -f /path/to/file # Export to markdown
83
- cct cleanup --days 30 --dry-run # Preview old backups to delete
124
+ cct search "authentication" # Search all conversations
125
+ cct search "api" --role user # Search only user messages
126
+ cct cost # Estimate API costs
127
+ cct clean --dry-run # Preview .claude directory cleanup
128
+ cct mcp-validate --test # Validate and test MCP servers
129
+ cct sessions # List all sessions
130
+ cct recover abc-123 --repair # Repair a corrupted session
131
+ cct security-scan # Scan for leaked secrets
132
+ cct audit abc-123 # Audit session actions
133
+ cct retention --days 60 --dry-run # Preview retention policy
134
+ cct trace # Show trace inventory
135
+ cct trace clean --days 7 # Clean old traces
136
+ cct trace wipe --confirm # Secure wipe all traces
137
+ cct trace guard --mode moderate # Generate trace prevention hooks
84
138
 
85
139
  For more info: https://github.com/asifkibria/claude-code-toolkit
86
140
  `);
@@ -88,8 +142,11 @@ For more info: https://github.com/asifkibria/claude-code-toolkit
88
142
  function parseArgs(args) {
89
143
  const result = {
90
144
  command: "",
145
+ subcommand: undefined,
91
146
  file: undefined,
92
147
  output: undefined,
148
+ query: undefined,
149
+ role: undefined,
93
150
  format: "markdown",
94
151
  withTools: false,
95
152
  dryRun: false,
@@ -99,6 +156,24 @@ function parseArgs(args) {
99
156
  sort: "size",
100
157
  auto: false,
101
158
  schedule: false,
159
+ category: undefined,
160
+ test: false,
161
+ extract: false,
162
+ repair: false,
163
+ confirm: false,
164
+ keepSettings: false,
165
+ mode: "moderate",
166
+ install: false,
167
+ categories: undefined,
168
+ project: undefined,
169
+ port: 1405,
170
+ daemon: false,
171
+ stop: false,
172
+ token: undefined,
173
+ json: false,
174
+ interval: 15,
175
+ autoFix: false,
176
+ details: false,
102
177
  };
103
178
  for (let i = 0; i < args.length; i++) {
104
179
  const arg = args[i];
@@ -107,7 +182,7 @@ function parseArgs(args) {
107
182
  process.exit(0);
108
183
  }
109
184
  if (arg === "-v" || arg === "--version") {
110
- console.log("1.0.7");
185
+ console.log("1.3.0");
111
186
  process.exit(0);
112
187
  }
113
188
  if (arg === "-f" || arg === "--file") {
@@ -118,6 +193,14 @@ function parseArgs(args) {
118
193
  result.output = args[++i];
119
194
  continue;
120
195
  }
196
+ if (arg === "-q" || arg === "--query") {
197
+ result.query = args[++i];
198
+ continue;
199
+ }
200
+ if (arg === "--role") {
201
+ result.role = args[++i];
202
+ continue;
203
+ }
121
204
  if (arg === "--format") {
122
205
  const fmt = args[++i];
123
206
  if (fmt === "json" || fmt === "markdown") {
@@ -157,23 +240,122 @@ function parseArgs(args) {
157
240
  result.schedule = true;
158
241
  continue;
159
242
  }
243
+ if (arg === "--category") {
244
+ result.category = args[++i];
245
+ continue;
246
+ }
247
+ if (arg === "--test") {
248
+ result.test = true;
249
+ continue;
250
+ }
251
+ if (arg === "--extract") {
252
+ result.extract = true;
253
+ continue;
254
+ }
255
+ if (arg === "--repair") {
256
+ result.repair = true;
257
+ continue;
258
+ }
259
+ if (arg === "--confirm") {
260
+ result.confirm = true;
261
+ continue;
262
+ }
263
+ if (arg === "--keep-settings") {
264
+ result.keepSettings = true;
265
+ continue;
266
+ }
267
+ if (arg === "--mode") {
268
+ result.mode = args[++i];
269
+ continue;
270
+ }
271
+ if (arg === "--install") {
272
+ result.install = true;
273
+ continue;
274
+ }
275
+ if (arg === "--categories") {
276
+ result.categories = args[++i];
277
+ continue;
278
+ }
279
+ if (arg === "--port") {
280
+ result.port = parseInt(args[++i], 10);
281
+ continue;
282
+ }
283
+ if (arg === "--daemon") {
284
+ result.daemon = true;
285
+ continue;
286
+ }
287
+ if (arg === "--stop") {
288
+ result.stop = true;
289
+ continue;
290
+ }
291
+ if (arg === "--project") {
292
+ result.project = args[++i];
293
+ continue;
294
+ }
295
+ if (arg === "--token") {
296
+ result.token = args[++i];
297
+ continue;
298
+ }
299
+ if (arg === "--json") {
300
+ result.json = true;
301
+ continue;
302
+ }
303
+ if (arg === "--interval") {
304
+ result.interval = parseInt(args[++i], 10);
305
+ continue;
306
+ }
307
+ if (arg === "--auto-fix") {
308
+ result.autoFix = true;
309
+ continue;
310
+ }
311
+ if (arg === "--details") {
312
+ result.details = true;
313
+ continue;
314
+ }
160
315
  if (!arg.startsWith("-") && !result.command) {
161
316
  result.command = arg;
162
317
  continue;
163
318
  }
164
- if (!arg.startsWith("-") && result.command === "restore") {
319
+ if (!arg.startsWith("-") && (result.command === "restore" || result.command === "recover" || result.command === "audit")) {
165
320
  result.file = arg;
321
+ continue;
322
+ }
323
+ if (!arg.startsWith("-") && result.command === "search" && !result.query) {
324
+ result.query = arg;
325
+ continue;
326
+ }
327
+ if (!arg.startsWith("-") && result.command === "diff") {
328
+ if (!result.file) {
329
+ result.file = arg;
330
+ }
331
+ else if (!result.subcommand) {
332
+ result.subcommand = arg;
333
+ }
334
+ continue;
335
+ }
336
+ if (!arg.startsWith("-") && result.command === "trace" && !result.subcommand) {
337
+ result.subcommand = arg;
338
+ continue;
166
339
  }
167
340
  }
168
341
  return result;
169
342
  }
170
- async function cmdScan(file) {
343
+ async function cmdScan(file, asJson) {
171
344
  if (!fs.existsSync(PROJECTS_DIR)) {
172
345
  console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
173
346
  process.exit(1);
174
347
  }
175
348
  const files = file ? [file] : findAllJsonlFiles(PROJECTS_DIR);
176
- console.log(`Scanning ${files.length} file(s)...\n`);
349
+ const summary = {
350
+ success: true,
351
+ filesScanned: files.length,
352
+ filesWithIssues: 0,
353
+ totalIssues: 0,
354
+ files: [],
355
+ };
356
+ if (!asJson) {
357
+ console.log(`Scanning ${files.length} file(s)...\n`);
358
+ }
177
359
  let totalIssues = 0;
178
360
  let filesWithIssues = 0;
179
361
  for (const f of files) {
@@ -183,10 +365,21 @@ async function cmdScan(file) {
183
365
  filesWithIssues++;
184
366
  totalIssues += result.issues.length;
185
367
  const relPath = path.relative(PROJECTS_DIR, f);
186
- console.log(`\x1b[33m${relPath}\x1b[0m`);
187
- for (const issue of result.issues) {
188
- console.log(` Line ${issue.line}: ${formatContentType(issue.contentType)} (~${formatBytes(issue.estimatedSize)})`);
368
+ if (!asJson) {
369
+ console.log(`\x1b[33m${relPath}\x1b[0m`);
370
+ for (const issue of result.issues) {
371
+ console.log(` Line ${issue.line}: ${formatContentType(issue.contentType)} (~${formatBytes(issue.estimatedSize)})`);
372
+ }
189
373
  }
374
+ summary.files.push({
375
+ file: relPath,
376
+ issues: result.issues.map((issue) => ({
377
+ line: issue.line,
378
+ type: issue.contentType,
379
+ location: issue.location,
380
+ size: formatBytes(issue.estimatedSize),
381
+ })),
382
+ });
190
383
  }
191
384
  }
192
385
  catch {
@@ -194,6 +387,11 @@ async function cmdScan(file) {
194
387
  }
195
388
  }
196
389
  console.log();
390
+ summary.totalIssues = totalIssues;
391
+ summary.filesWithIssues = filesWithIssues;
392
+ if (asJson) {
393
+ return summary;
394
+ }
197
395
  if (totalIssues === 0) {
198
396
  console.log("\x1b[32m✓ No oversized content found.\x1b[0m");
199
397
  }
@@ -202,6 +400,100 @@ async function cmdScan(file) {
202
400
  console.log("Run 'cct fix' to fix them.");
203
401
  }
204
402
  }
403
+ async function cmdWatch(asJson, intervalSeconds, autoFix) {
404
+ if (!fs.existsSync(PROJECTS_DIR)) {
405
+ console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
406
+ process.exit(1);
407
+ }
408
+ const effectiveSeconds = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds : 15;
409
+ const intervalMs = Math.max(5, effectiveSeconds) * 1000;
410
+ const seen = new Map();
411
+ const logEvent = (event) => {
412
+ if (asJson) {
413
+ console.log(JSON.stringify(event));
414
+ }
415
+ else {
416
+ const actionText = event.action === "fixed" ? "Fixed" : event.action === "error" ? "Error" : "Detected";
417
+ console.log(`[${event.timestamp}] ${actionText}: ${event.file} (${event.issues} issue(s))${event.details ? ` - ${event.details}` : ""}`);
418
+ }
419
+ };
420
+ const checkFiles = async () => {
421
+ const files = findAllJsonlFiles(PROJECTS_DIR);
422
+ const active = new Set();
423
+ for (const file of files) {
424
+ active.add(file);
425
+ let stat;
426
+ try {
427
+ stat = fs.statSync(file);
428
+ }
429
+ catch {
430
+ continue;
431
+ }
432
+ const last = seen.get(file) || 0;
433
+ if (stat.mtimeMs <= last)
434
+ continue;
435
+ seen.set(file, stat.mtimeMs);
436
+ let result;
437
+ try {
438
+ result = scanFile(file);
439
+ }
440
+ catch {
441
+ continue;
442
+ }
443
+ if (result.issues.length === 0)
444
+ continue;
445
+ const relPath = path.relative(PROJECTS_DIR, file);
446
+ const eventBase = {
447
+ timestamp: new Date().toISOString(),
448
+ file: relPath,
449
+ issues: result.issues.length,
450
+ action: "detected",
451
+ };
452
+ if (autoFix) {
453
+ try {
454
+ const fixResult = fixFile(file);
455
+ if (fixResult.fixed) {
456
+ logEvent({ ...eventBase, action: "fixed" });
457
+ }
458
+ else {
459
+ logEvent({ ...eventBase, action: "error", details: "No changes applied" });
460
+ }
461
+ }
462
+ catch (err) {
463
+ logEvent({ ...eventBase, action: "error", details: err instanceof Error ? err.message : String(err) });
464
+ }
465
+ }
466
+ else {
467
+ logEvent(eventBase);
468
+ }
469
+ }
470
+ for (const entry of Array.from(seen.keys())) {
471
+ if (!active.has(entry)) {
472
+ seen.delete(entry);
473
+ }
474
+ }
475
+ };
476
+ await checkFiles();
477
+ if (!asJson) {
478
+ console.log(`Watching ${PROJECTS_DIR} every ${intervalMs / 1000}s. Press Ctrl+C to stop.`);
479
+ if (autoFix) {
480
+ console.log("Auto-fix is enabled. Backups will be created before changes.\n");
481
+ }
482
+ }
483
+ setInterval(() => {
484
+ checkFiles().catch((err) => {
485
+ if (asJson) {
486
+ console.log(JSON.stringify({ timestamp: new Date().toISOString(), action: "error", details: err.message }));
487
+ }
488
+ else {
489
+ console.error("Watch error:", err);
490
+ }
491
+ });
492
+ }, intervalMs);
493
+ return new Promise(() => {
494
+ // Keep process alive until Ctrl+C
495
+ });
496
+ }
205
497
  async function cmdFix(file, noBackup = false) {
206
498
  if (!fs.existsSync(PROJECTS_DIR)) {
207
499
  console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
@@ -471,7 +763,7 @@ async function cmdArchive(days, dryRun) {
471
763
  console.log("\x1b[33mRun without --dry-run to archive these conversations.\x1b[0m\n");
472
764
  }
473
765
  }
474
- async function cmdMaintenance(dryRun, auto, schedule) {
766
+ async function cmdMaintenance(auto, schedule) {
475
767
  if (schedule) {
476
768
  console.log("Scheduled Maintenance Setup\n");
477
769
  console.log("=== For macOS (launchd) ===");
@@ -493,6 +785,271 @@ async function cmdMaintenance(dryRun, auto, schedule) {
493
785
  console.log("\x1b[33mRun with --auto to perform maintenance actions automatically.\x1b[0m\n");
494
786
  }
495
787
  }
788
+ async function cmdClean(dryRun, days, category) {
789
+ const analysis = analyzeClaudeStorage();
790
+ console.log(formatStorageReport(analysis));
791
+ const options = { dryRun: dryRun || true, days, categories: category ? [category] : undefined };
792
+ const targets = findCleanupTargets(undefined, options);
793
+ if (targets.length === 0) {
794
+ console.log("\x1b[32m✓ Nothing to clean.\x1b[0m\n");
795
+ return;
796
+ }
797
+ const result = cleanClaudeDirectory(undefined, { ...options, dryRun });
798
+ console.log(formatCleanupReport(targets, result, dryRun));
799
+ }
800
+ async function cmdMcpValidate(test) {
801
+ const report = await diagnoseMcpServers({ test, projectDir: process.cwd() });
802
+ console.log(formatMcpDiagnosticReport(report));
803
+ }
804
+ async function cmdSessions(project) {
805
+ const sessions = listSessions(undefined, { project });
806
+ if (sessions.length === 0) {
807
+ console.log("No sessions found.\n");
808
+ return;
809
+ }
810
+ console.log(formatSessionReport(sessions));
811
+ }
812
+ async function cmdRecover(sessionId, extract, repair) {
813
+ if (!sessionId) {
814
+ console.error("Usage: cct recover <session-id> [--extract] [--repair]");
815
+ process.exit(1);
816
+ }
817
+ const sessions = listSessions();
818
+ const session = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
819
+ if (!session) {
820
+ console.error(`Session not found: ${sessionId}`);
821
+ process.exit(1);
822
+ }
823
+ const diag = diagnoseSession(session.filePath);
824
+ console.log(formatSessionDiagnosticReport(diag));
825
+ if (repair) {
826
+ console.log("\nRepairing session...");
827
+ const result = repairSession(session.filePath);
828
+ if (result.success) {
829
+ console.log(`\x1b[32m✓ Repaired. Removed ${result.linesRemoved} invalid lines.\x1b[0m`);
830
+ if (result.backupPath)
831
+ console.log(` Backup: ${result.backupPath}`);
832
+ }
833
+ else {
834
+ console.log(`\x1b[31m✗ Repair failed: ${result.error}\x1b[0m`);
835
+ }
836
+ }
837
+ if (extract) {
838
+ console.log("\nExtracting session content...");
839
+ const content = extractSessionContent(session.filePath);
840
+ console.log(` User messages: ${content.userMessages.length}`);
841
+ console.log(` Assistant messages: ${content.assistantMessages.length}`);
842
+ console.log(` File edits: ${content.fileEdits.length}`);
843
+ console.log(` Commands run: ${content.commandsRun.length}`);
844
+ if (content.fileEdits.length > 0) {
845
+ console.log("\nFile edits:");
846
+ for (const edit of content.fileEdits.slice(0, 10)) {
847
+ console.log(` ${edit.path}`);
848
+ }
849
+ }
850
+ if (content.commandsRun.length > 0) {
851
+ console.log("\nCommands:");
852
+ for (const cmd of content.commandsRun.slice(0, 10)) {
853
+ const short = cmd.length > 80 ? cmd.slice(0, 77) + "..." : cmd;
854
+ console.log(` $ ${short}`);
855
+ }
856
+ }
857
+ }
858
+ }
859
+ async function cmdSecurityScan(file) {
860
+ console.log("Scanning for secrets in conversation data...\n");
861
+ const result = scanForSecrets(undefined, { file });
862
+ console.log(formatSecretsScanReport(result));
863
+ }
864
+ async function cmdPIIScan(file, details) {
865
+ console.log("Scanning for Personal Identifiable Information (PII)...\n");
866
+ if (details) {
867
+ console.log("\x1b[33m⚠ WARNING: Showing unmasked PII values. Handle with care!\x1b[0m\n");
868
+ }
869
+ const result = scanForPII(undefined, { file, includeFullValues: details });
870
+ console.log(formatPIIScanReport(result, details));
871
+ }
872
+ async function cmdAudit(sessionId) {
873
+ if (!sessionId) {
874
+ console.error("Usage: cct audit <session-id>");
875
+ process.exit(1);
876
+ }
877
+ const sessions = listSessions();
878
+ const session = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
879
+ if (!session) {
880
+ console.error(`Session not found: ${sessionId}`);
881
+ process.exit(1);
882
+ }
883
+ const audit = auditSession(session.filePath);
884
+ console.log(formatAuditReport(audit));
885
+ }
886
+ async function cmdRetention(days, dryRun) {
887
+ const retentionDays = days > 7 ? days : 30;
888
+ const result = enforceRetention(undefined, { days: retentionDays, dryRun });
889
+ console.log(formatRetentionReport(result));
890
+ }
891
+ async function cmdTrace(subcommand, options) {
892
+ if (!subcommand) {
893
+ const inv = inventoryTraces(undefined, { project: options?.project });
894
+ console.log(formatTraceInventory(inv));
895
+ return;
896
+ }
897
+ if (subcommand === "clean") {
898
+ const cleanOpts = {
899
+ dryRun: options?.dryRun ?? true,
900
+ days: options?.days,
901
+ categories: options?.categories?.split(","),
902
+ project: options?.project,
903
+ };
904
+ const result = cleanTraces(undefined, cleanOpts);
905
+ console.log(formatTraceCleanReport(result));
906
+ return;
907
+ }
908
+ if (subcommand === "wipe") {
909
+ if (!options?.confirm) {
910
+ console.log("\x1b[31m⚠ This will securely wipe ALL Claude Code traces.\x1b[0m");
911
+ console.log("Run with --confirm to execute.\n");
912
+ const inv = inventoryTraces();
913
+ console.log(`Would wipe: ${inv.totalFiles} files (${formatBytes(inv.totalSize)})`);
914
+ return;
915
+ }
916
+ const result = wipeAllTraces(undefined, {
917
+ confirm: true,
918
+ keepSettings: options.keepSettings,
919
+ });
920
+ console.log(result.wipeReceipt);
921
+ return;
922
+ }
923
+ if (subcommand === "guard") {
924
+ const mode = (options?.mode || "moderate");
925
+ const config = generateTraceGuardHooks({ mode });
926
+ console.log(formatTraceGuardConfig(config));
927
+ return;
928
+ }
929
+ console.error(`Unknown trace subcommand: ${subcommand}`);
930
+ console.error("Usage: cct trace [clean|wipe|guard]");
931
+ }
932
+ async function cmdSearch(query, options) {
933
+ if (!query || query.length < 2) {
934
+ console.error("Usage: cct search <query> [--role user|assistant|all] [--limit n] [--project name]");
935
+ console.error("Query must be at least 2 characters.");
936
+ process.exit(1);
937
+ }
938
+ if (!fs.existsSync(PROJECTS_DIR)) {
939
+ console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
940
+ process.exit(1);
941
+ }
942
+ console.log(`Searching for "${query}"...\n`);
943
+ const searchOpts = {
944
+ query,
945
+ limit: options?.limit || 50,
946
+ role: options?.role || "all",
947
+ project: options?.project,
948
+ daysBack: options?.days,
949
+ };
950
+ const report = searchConversations(searchOpts);
951
+ console.log(formatSearchReport(report));
952
+ }
953
+ async function cmdCost() {
954
+ if (!fs.existsSync(PROJECTS_DIR)) {
955
+ console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
956
+ process.exit(1);
957
+ }
958
+ console.log("Calculating API cost estimates...\n");
959
+ const files = findAllJsonlFiles(PROJECTS_DIR);
960
+ let totalInputTokens = 0;
961
+ let totalOutputTokens = 0;
962
+ let sessionCount = 0;
963
+ const projectCosts = new Map();
964
+ for (const file of files) {
965
+ try {
966
+ const estimate = estimateContextSize(file);
967
+ const projectName = path.basename(path.dirname(file));
968
+ const b = estimate.breakdown;
969
+ const inputTokens = b.userTokens + b.systemTokens + b.toolUseTokens;
970
+ const outputTokens = b.assistantTokens + b.toolResultTokens;
971
+ if (!projectCosts.has(projectName)) {
972
+ projectCosts.set(projectName, { input: 0, output: 0, sessions: 0 });
973
+ }
974
+ const project = projectCosts.get(projectName);
975
+ project.input += inputTokens;
976
+ project.output += outputTokens;
977
+ project.sessions++;
978
+ totalInputTokens += inputTokens;
979
+ totalOutputTokens += outputTokens;
980
+ sessionCount++;
981
+ }
982
+ catch {
983
+ // Skip
984
+ }
985
+ }
986
+ const inputCostPerMillion = 15;
987
+ const outputCostPerMillion = 75;
988
+ const totalInputCost = (totalInputTokens / 1_000_000) * inputCostPerMillion;
989
+ const totalOutputCost = (totalOutputTokens / 1_000_000) * outputCostPerMillion;
990
+ const totalCost = totalInputCost + totalOutputCost;
991
+ console.log("API Cost Estimate (Claude Sonnet pricing)\n");
992
+ console.log("═".repeat(50));
993
+ console.log(`Sessions analyzed: ${sessionCount}`);
994
+ console.log(`\nToken Usage:`);
995
+ console.log(` Input tokens: ${totalInputTokens.toLocaleString()}`);
996
+ console.log(` Output tokens: ${totalOutputTokens.toLocaleString()}`);
997
+ console.log(` Total tokens: ${(totalInputTokens + totalOutputTokens).toLocaleString()}`);
998
+ console.log(`\nEstimated Cost:`);
999
+ console.log(` Input: $${totalInputCost.toFixed(2)} ($${inputCostPerMillion}/M tokens)`);
1000
+ console.log(` Output: $${totalOutputCost.toFixed(2)} ($${outputCostPerMillion}/M tokens)`);
1001
+ console.log(` \x1b[36mTotal: $${totalCost.toFixed(2)}\x1b[0m\n`);
1002
+ const sortedProjects = Array.from(projectCosts.entries())
1003
+ .sort((a, b) => (b[1].input + b[1].output) - (a[1].input + a[1].output))
1004
+ .slice(0, 10);
1005
+ if (sortedProjects.length > 0) {
1006
+ console.log("Top Projects by Token Usage:\n");
1007
+ for (const [name, data] of sortedProjects) {
1008
+ const projectTotal = data.input + data.output;
1009
+ const projectCost = ((data.input / 1_000_000) * inputCostPerMillion) + ((data.output / 1_000_000) * outputCostPerMillion);
1010
+ console.log(` ${name}`);
1011
+ console.log(` ${projectTotal.toLocaleString()} tokens (~$${projectCost.toFixed(2)}) - ${data.sessions} session(s)`);
1012
+ }
1013
+ }
1014
+ console.log("\n\x1b[33mNote: These are estimates based on stored conversation history.\x1b[0m");
1015
+ console.log("\x1b[33mActual costs depend on your API plan and usage patterns.\x1b[0m\n");
1016
+ }
1017
+ async function cmdDiff(sessionId1, sessionId2) {
1018
+ if (!sessionId1 || !sessionId2) {
1019
+ console.error("Usage: cct diff <session-id-1> <session-id-2>");
1020
+ console.error("Example: cct diff abc123 def456");
1021
+ process.exit(1);
1022
+ }
1023
+ if (!fs.existsSync(PROJECTS_DIR)) {
1024
+ console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
1025
+ process.exit(1);
1026
+ }
1027
+ const diff = compareSessionsByCID({ sessionId1, sessionId2 });
1028
+ if (!diff) {
1029
+ console.error("One or both sessions not found.");
1030
+ console.error("Use 'cct sessions' to list available sessions.");
1031
+ process.exit(1);
1032
+ }
1033
+ console.log(formatSessionDiff(diff));
1034
+ }
1035
+ async function cmdGit() {
1036
+ console.log("Linking sessions to git repositories...\n");
1037
+ const report = linkSessionsToGit();
1038
+ console.log(formatGitLinkReport(report));
1039
+ }
1040
+ async function cmdMcpPerf() {
1041
+ console.log("Analyzing MCP tool performance...\n");
1042
+ const report = analyzeMcpPerformance();
1043
+ console.log(formatMcpPerformanceReport(report));
1044
+ }
1045
+ async function cmdAlerts() {
1046
+ const report = checkAlerts();
1047
+ console.log(formatAlertsReport(report));
1048
+ }
1049
+ async function cmdQuotas() {
1050
+ const quotas = checkQuotas();
1051
+ console.log(formatQuotasReport(quotas));
1052
+ }
496
1053
  async function cmdHealth() {
497
1054
  if (!fs.existsSync(PROJECTS_DIR)) {
498
1055
  console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
@@ -533,13 +1090,14 @@ async function cmdHealth() {
533
1090
  }
534
1091
  async function main() {
535
1092
  const args = parseArgs(process.argv.slice(2));
1093
+ let commandResult;
536
1094
  if (!args.command) {
537
1095
  printHelp();
538
1096
  process.exit(0);
539
1097
  }
540
1098
  switch (args.command) {
541
1099
  case "scan":
542
- await cmdScan(args.file);
1100
+ commandResult = await cmdScan(args.file, args.json);
543
1101
  break;
544
1102
  case "fix":
545
1103
  await cmdFix(args.file, args.noBackup);
@@ -560,7 +1118,7 @@ async function main() {
560
1118
  await cmdArchive(args.days, args.dryRun);
561
1119
  break;
562
1120
  case "maintenance":
563
- await cmdMaintenance(args.dryRun, args.auto, args.schedule);
1121
+ await cmdMaintenance(args.auto, args.schedule);
564
1122
  break;
565
1123
  case "export":
566
1124
  await cmdExport(args.file, args.output, args.format, args.withTools);
@@ -577,11 +1135,94 @@ async function main() {
577
1135
  case "health":
578
1136
  await cmdHealth();
579
1137
  break;
1138
+ case "search":
1139
+ await cmdSearch(args.query, { role: args.role, limit: args.limit, project: args.project, days: args.days });
1140
+ break;
1141
+ case "cost":
1142
+ await cmdCost();
1143
+ break;
1144
+ case "clean":
1145
+ await cmdClean(args.dryRun, args.days, args.category);
1146
+ break;
1147
+ case "mcp-validate":
1148
+ await cmdMcpValidate(args.test);
1149
+ break;
1150
+ case "mcp-perf":
1151
+ await cmdMcpPerf();
1152
+ break;
1153
+ case "sessions":
1154
+ await cmdSessions(args.project);
1155
+ break;
1156
+ case "diff":
1157
+ await cmdDiff(args.file, args.subcommand);
1158
+ break;
1159
+ case "git":
1160
+ await cmdGit();
1161
+ break;
1162
+ case "recover":
1163
+ await cmdRecover(args.file, args.extract, args.repair);
1164
+ break;
1165
+ case "security-scan":
1166
+ await cmdSecurityScan(args.file);
1167
+ break;
1168
+ case "pii-scan":
1169
+ await cmdPIIScan(args.file, args.details);
1170
+ break;
1171
+ case "audit":
1172
+ await cmdAudit(args.file);
1173
+ break;
1174
+ case "retention":
1175
+ await cmdRetention(args.days, args.dryRun);
1176
+ break;
1177
+ case "alerts":
1178
+ await cmdAlerts();
1179
+ break;
1180
+ case "quotas":
1181
+ await cmdQuotas();
1182
+ break;
1183
+ case "trace":
1184
+ await cmdTrace(args.subcommand, {
1185
+ dryRun: args.dryRun,
1186
+ days: args.days,
1187
+ categories: args.categories,
1188
+ project: args.project,
1189
+ confirm: args.confirm,
1190
+ keepSettings: args.keepSettings,
1191
+ mode: args.mode,
1192
+ install: args.install,
1193
+ });
1194
+ break;
1195
+ case "watch":
1196
+ await cmdWatch(args.json, args.interval, args.autoFix);
1197
+ break;
1198
+ case "dashboard":
1199
+ if (args.stop) {
1200
+ const stopped = stopDashboard();
1201
+ if (stopped) {
1202
+ console.log("\x1b[32m✓ Dashboard stopped.\x1b[0m");
1203
+ }
1204
+ else {
1205
+ const status = isDashboardRunning();
1206
+ if (!status.running) {
1207
+ console.log("No dashboard process found.");
1208
+ }
1209
+ else {
1210
+ console.error("Failed to stop dashboard.");
1211
+ }
1212
+ }
1213
+ }
1214
+ else {
1215
+ await startDashboard({ port: args.port, daemon: args.daemon, authToken: args.token });
1216
+ }
1217
+ break;
580
1218
  default:
581
1219
  console.error(`Unknown command: ${args.command}`);
582
1220
  printHelp();
583
1221
  process.exit(1);
584
1222
  }
1223
+ if (args.json && commandResult !== undefined) {
1224
+ console.log(JSON.stringify(commandResult, null, 2));
1225
+ }
585
1226
  }
586
1227
  main().catch((err) => {
587
1228
  console.error("Error:", err.message);