@asifkibria/claude-code-toolkit 1.2.0 → 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.
- package/README.md +126 -113
- package/dist/cli.js +365 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/alerts.d.ts +38 -0
- package/dist/lib/alerts.d.ts.map +1 -0
- package/dist/lib/alerts.js +296 -0
- package/dist/lib/alerts.js.map +1 -0
- package/dist/lib/dashboard-ui.d.ts.map +1 -1
- package/dist/lib/dashboard-ui.js +215 -23
- package/dist/lib/dashboard-ui.js.map +1 -1
- package/dist/lib/dashboard.d.ts +2 -1
- package/dist/lib/dashboard.d.ts.map +1 -1
- package/dist/lib/dashboard.js +159 -10
- package/dist/lib/dashboard.js.map +1 -1
- package/dist/lib/git.d.ts +29 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +124 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/mcp-validator.d.ts +23 -0
- package/dist/lib/mcp-validator.d.ts.map +1 -1
- package/dist/lib/mcp-validator.js +138 -0
- package/dist/lib/mcp-validator.js.map +1 -1
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/scanner.js +103 -5
- package/dist/lib/scanner.js.map +1 -1
- package/dist/lib/search.d.ts +56 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +284 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/security.d.ts +39 -0
- package/dist/lib/security.d.ts.map +1 -1
- package/dist/lib/security.js +379 -7
- package/dist/lib/security.js.map +1 -1
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +5 -2
- package/dist/lib/storage.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,14 @@ 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
10
|
import { analyzeClaudeStorage, cleanClaudeDirectory, findCleanupTargets, formatStorageReport, formatCleanupReport, } from "./lib/storage.js";
|
|
11
|
-
import { diagnoseMcpServers, formatMcpDiagnosticReport, } from "./lib/mcp-validator.js";
|
|
11
|
+
import { diagnoseMcpServers, formatMcpDiagnosticReport, analyzeMcpPerformance, formatMcpPerformanceReport, } from "./lib/mcp-validator.js";
|
|
12
12
|
import { listSessions, diagnoseSession, repairSession, extractSessionContent, formatSessionReport, formatSessionDiagnosticReport, } from "./lib/session-recovery.js";
|
|
13
|
-
import { scanForSecrets, auditSession, enforceRetention, formatSecretsScanReport, formatAuditReport, formatRetentionReport, } from "./lib/security.js";
|
|
13
|
+
import { scanForSecrets, scanForPII, auditSession, enforceRetention, formatSecretsScanReport, formatPIIScanReport, formatAuditReport, formatRetentionReport, } from "./lib/security.js";
|
|
14
14
|
import { inventoryTraces, cleanTraces, wipeAllTraces, generateTraceGuardHooks, formatTraceInventory, formatTraceCleanReport, formatTraceGuardConfig, } from "./lib/trace.js";
|
|
15
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";
|
|
16
19
|
function formatContentType(type) {
|
|
17
20
|
switch (type) {
|
|
18
21
|
case "image": return "🖼️ image";
|
|
@@ -36,7 +39,7 @@ function formatDate(date) {
|
|
|
36
39
|
}
|
|
37
40
|
function printHelp() {
|
|
38
41
|
console.log(`
|
|
39
|
-
Claude Code Toolkit v1.
|
|
42
|
+
Claude Code Toolkit v1.3.0
|
|
40
43
|
Maintain, optimize, secure, and troubleshoot your Claude Code installation.
|
|
41
44
|
|
|
42
45
|
USAGE:
|
|
@@ -45,25 +48,35 @@ USAGE:
|
|
|
45
48
|
|
|
46
49
|
COMMANDS:
|
|
47
50
|
health Quick health check (start here!)
|
|
51
|
+
search <query> Full-text search across all conversations
|
|
48
52
|
stats Show conversation statistics
|
|
49
53
|
context Estimate context/token usage
|
|
54
|
+
cost Estimate API costs based on token usage
|
|
50
55
|
analytics Usage analytics dashboard
|
|
51
56
|
duplicates Find duplicate content and conversations
|
|
52
57
|
archive Archive old/inactive conversations
|
|
53
58
|
maintenance Run maintenance checks and actions
|
|
54
59
|
scan Scan for issues (dry run)
|
|
55
60
|
fix Fix all detected issues
|
|
61
|
+
watch Monitor sessions for new oversized content
|
|
56
62
|
export Export conversation to markdown or JSON
|
|
57
63
|
backups List backup files
|
|
58
64
|
restore <path> Restore from a backup file
|
|
59
65
|
cleanup Delete old backup files
|
|
60
66
|
clean Analyze and clean .claude directory
|
|
61
67
|
mcp-validate Validate MCP server configurations
|
|
68
|
+
mcp-perf Track MCP server performance and usage
|
|
62
69
|
sessions List all sessions with health status
|
|
70
|
+
diff <id1> <id2> Compare two sessions
|
|
71
|
+
git Link sessions to git branches/commits
|
|
63
72
|
recover <id> Diagnose/repair/extract from a session
|
|
64
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!)
|
|
65
76
|
audit <id> Audit a session's actions (files, commands, etc.)
|
|
66
77
|
retention Enforce data retention policy
|
|
78
|
+
alerts Check for issues and notifications
|
|
79
|
+
quotas Show usage quotas and limits
|
|
67
80
|
dashboard Open web dashboard in browser
|
|
68
81
|
dashboard --daemon Run dashboard as background process
|
|
69
82
|
dashboard --stop Stop background dashboard
|
|
@@ -75,12 +88,14 @@ COMMANDS:
|
|
|
75
88
|
OPTIONS:
|
|
76
89
|
-f, --file <path> Target a specific file
|
|
77
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)
|
|
78
93
|
--format <type> Export format: markdown or json (default: markdown)
|
|
79
94
|
--with-tools Include tool results in export
|
|
80
95
|
-d, --dry-run Show what would be done without making changes
|
|
81
96
|
--no-backup Skip creating backups when fixing (not recommended)
|
|
82
97
|
--days <n> For cleanup/archive/retention: days threshold (default: 7/30)
|
|
83
|
-
--limit <n> For stats: limit results (default: 10)
|
|
98
|
+
--limit <n> For stats/search: limit results (default: 10/50)
|
|
84
99
|
--sort <field> For stats: sort by size|messages|images|modified
|
|
85
100
|
--auto For maintenance: run automatically without prompts
|
|
86
101
|
--schedule For maintenance: show cron/launchd setup
|
|
@@ -96,12 +111,19 @@ OPTIONS:
|
|
|
96
111
|
--port <n> For dashboard: port number (default: 1405)
|
|
97
112
|
--daemon For dashboard: run as background process
|
|
98
113
|
--stop For dashboard: stop background process
|
|
114
|
+
--token <value> Require dashboard auth with bearer token
|
|
99
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
|
|
100
119
|
-h, --help Show this help message
|
|
101
120
|
-v, --version Show version
|
|
102
121
|
|
|
103
122
|
EXAMPLES:
|
|
104
123
|
cct health # Quick health check
|
|
124
|
+
cct search "authentication" # Search all conversations
|
|
125
|
+
cct search "api" --role user # Search only user messages
|
|
126
|
+
cct cost # Estimate API costs
|
|
105
127
|
cct clean --dry-run # Preview .claude directory cleanup
|
|
106
128
|
cct mcp-validate --test # Validate and test MCP servers
|
|
107
129
|
cct sessions # List all sessions
|
|
@@ -123,6 +145,8 @@ function parseArgs(args) {
|
|
|
123
145
|
subcommand: undefined,
|
|
124
146
|
file: undefined,
|
|
125
147
|
output: undefined,
|
|
148
|
+
query: undefined,
|
|
149
|
+
role: undefined,
|
|
126
150
|
format: "markdown",
|
|
127
151
|
withTools: false,
|
|
128
152
|
dryRun: false,
|
|
@@ -145,6 +169,11 @@ function parseArgs(args) {
|
|
|
145
169
|
port: 1405,
|
|
146
170
|
daemon: false,
|
|
147
171
|
stop: false,
|
|
172
|
+
token: undefined,
|
|
173
|
+
json: false,
|
|
174
|
+
interval: 15,
|
|
175
|
+
autoFix: false,
|
|
176
|
+
details: false,
|
|
148
177
|
};
|
|
149
178
|
for (let i = 0; i < args.length; i++) {
|
|
150
179
|
const arg = args[i];
|
|
@@ -153,7 +182,7 @@ function parseArgs(args) {
|
|
|
153
182
|
process.exit(0);
|
|
154
183
|
}
|
|
155
184
|
if (arg === "-v" || arg === "--version") {
|
|
156
|
-
console.log("1.
|
|
185
|
+
console.log("1.3.0");
|
|
157
186
|
process.exit(0);
|
|
158
187
|
}
|
|
159
188
|
if (arg === "-f" || arg === "--file") {
|
|
@@ -164,6 +193,14 @@ function parseArgs(args) {
|
|
|
164
193
|
result.output = args[++i];
|
|
165
194
|
continue;
|
|
166
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
|
+
}
|
|
167
204
|
if (arg === "--format") {
|
|
168
205
|
const fmt = args[++i];
|
|
169
206
|
if (fmt === "json" || fmt === "markdown") {
|
|
@@ -255,6 +292,26 @@ function parseArgs(args) {
|
|
|
255
292
|
result.project = args[++i];
|
|
256
293
|
continue;
|
|
257
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
|
+
}
|
|
258
315
|
if (!arg.startsWith("-") && !result.command) {
|
|
259
316
|
result.command = arg;
|
|
260
317
|
continue;
|
|
@@ -263,6 +320,19 @@ function parseArgs(args) {
|
|
|
263
320
|
result.file = arg;
|
|
264
321
|
continue;
|
|
265
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
|
+
}
|
|
266
336
|
if (!arg.startsWith("-") && result.command === "trace" && !result.subcommand) {
|
|
267
337
|
result.subcommand = arg;
|
|
268
338
|
continue;
|
|
@@ -270,13 +340,22 @@ function parseArgs(args) {
|
|
|
270
340
|
}
|
|
271
341
|
return result;
|
|
272
342
|
}
|
|
273
|
-
async function cmdScan(file) {
|
|
343
|
+
async function cmdScan(file, asJson) {
|
|
274
344
|
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
275
345
|
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
276
346
|
process.exit(1);
|
|
277
347
|
}
|
|
278
348
|
const files = file ? [file] : findAllJsonlFiles(PROJECTS_DIR);
|
|
279
|
-
|
|
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
|
+
}
|
|
280
359
|
let totalIssues = 0;
|
|
281
360
|
let filesWithIssues = 0;
|
|
282
361
|
for (const f of files) {
|
|
@@ -286,10 +365,21 @@ async function cmdScan(file) {
|
|
|
286
365
|
filesWithIssues++;
|
|
287
366
|
totalIssues += result.issues.length;
|
|
288
367
|
const relPath = path.relative(PROJECTS_DIR, f);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
+
}
|
|
292
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
|
+
});
|
|
293
383
|
}
|
|
294
384
|
}
|
|
295
385
|
catch {
|
|
@@ -297,6 +387,11 @@ async function cmdScan(file) {
|
|
|
297
387
|
}
|
|
298
388
|
}
|
|
299
389
|
console.log();
|
|
390
|
+
summary.totalIssues = totalIssues;
|
|
391
|
+
summary.filesWithIssues = filesWithIssues;
|
|
392
|
+
if (asJson) {
|
|
393
|
+
return summary;
|
|
394
|
+
}
|
|
300
395
|
if (totalIssues === 0) {
|
|
301
396
|
console.log("\x1b[32m✓ No oversized content found.\x1b[0m");
|
|
302
397
|
}
|
|
@@ -305,6 +400,100 @@ async function cmdScan(file) {
|
|
|
305
400
|
console.log("Run 'cct fix' to fix them.");
|
|
306
401
|
}
|
|
307
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
|
+
}
|
|
308
497
|
async function cmdFix(file, noBackup = false) {
|
|
309
498
|
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
310
499
|
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
@@ -574,7 +763,7 @@ async function cmdArchive(days, dryRun) {
|
|
|
574
763
|
console.log("\x1b[33mRun without --dry-run to archive these conversations.\x1b[0m\n");
|
|
575
764
|
}
|
|
576
765
|
}
|
|
577
|
-
async function cmdMaintenance(
|
|
766
|
+
async function cmdMaintenance(auto, schedule) {
|
|
578
767
|
if (schedule) {
|
|
579
768
|
console.log("Scheduled Maintenance Setup\n");
|
|
580
769
|
console.log("=== For macOS (launchd) ===");
|
|
@@ -608,7 +797,7 @@ async function cmdClean(dryRun, days, category) {
|
|
|
608
797
|
const result = cleanClaudeDirectory(undefined, { ...options, dryRun });
|
|
609
798
|
console.log(formatCleanupReport(targets, result, dryRun));
|
|
610
799
|
}
|
|
611
|
-
async function cmdMcpValidate(
|
|
800
|
+
async function cmdMcpValidate(test) {
|
|
612
801
|
const report = await diagnoseMcpServers({ test, projectDir: process.cwd() });
|
|
613
802
|
console.log(formatMcpDiagnosticReport(report));
|
|
614
803
|
}
|
|
@@ -672,6 +861,14 @@ async function cmdSecurityScan(file) {
|
|
|
672
861
|
const result = scanForSecrets(undefined, { file });
|
|
673
862
|
console.log(formatSecretsScanReport(result));
|
|
674
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
|
+
}
|
|
675
872
|
async function cmdAudit(sessionId) {
|
|
676
873
|
if (!sessionId) {
|
|
677
874
|
console.error("Usage: cct audit <session-id>");
|
|
@@ -732,6 +929,127 @@ async function cmdTrace(subcommand, options) {
|
|
|
732
929
|
console.error(`Unknown trace subcommand: ${subcommand}`);
|
|
733
930
|
console.error("Usage: cct trace [clean|wipe|guard]");
|
|
734
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
|
+
}
|
|
735
1053
|
async function cmdHealth() {
|
|
736
1054
|
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
737
1055
|
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
@@ -772,13 +1090,14 @@ async function cmdHealth() {
|
|
|
772
1090
|
}
|
|
773
1091
|
async function main() {
|
|
774
1092
|
const args = parseArgs(process.argv.slice(2));
|
|
1093
|
+
let commandResult;
|
|
775
1094
|
if (!args.command) {
|
|
776
1095
|
printHelp();
|
|
777
1096
|
process.exit(0);
|
|
778
1097
|
}
|
|
779
1098
|
switch (args.command) {
|
|
780
1099
|
case "scan":
|
|
781
|
-
await cmdScan(args.file);
|
|
1100
|
+
commandResult = await cmdScan(args.file, args.json);
|
|
782
1101
|
break;
|
|
783
1102
|
case "fix":
|
|
784
1103
|
await cmdFix(args.file, args.noBackup);
|
|
@@ -799,7 +1118,7 @@ async function main() {
|
|
|
799
1118
|
await cmdArchive(args.days, args.dryRun);
|
|
800
1119
|
break;
|
|
801
1120
|
case "maintenance":
|
|
802
|
-
await cmdMaintenance(args.
|
|
1121
|
+
await cmdMaintenance(args.auto, args.schedule);
|
|
803
1122
|
break;
|
|
804
1123
|
case "export":
|
|
805
1124
|
await cmdExport(args.file, args.output, args.format, args.withTools);
|
|
@@ -816,27 +1135,51 @@ async function main() {
|
|
|
816
1135
|
case "health":
|
|
817
1136
|
await cmdHealth();
|
|
818
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;
|
|
819
1144
|
case "clean":
|
|
820
1145
|
await cmdClean(args.dryRun, args.days, args.category);
|
|
821
1146
|
break;
|
|
822
1147
|
case "mcp-validate":
|
|
823
|
-
await cmdMcpValidate(args.
|
|
1148
|
+
await cmdMcpValidate(args.test);
|
|
1149
|
+
break;
|
|
1150
|
+
case "mcp-perf":
|
|
1151
|
+
await cmdMcpPerf();
|
|
824
1152
|
break;
|
|
825
1153
|
case "sessions":
|
|
826
1154
|
await cmdSessions(args.project);
|
|
827
1155
|
break;
|
|
1156
|
+
case "diff":
|
|
1157
|
+
await cmdDiff(args.file, args.subcommand);
|
|
1158
|
+
break;
|
|
1159
|
+
case "git":
|
|
1160
|
+
await cmdGit();
|
|
1161
|
+
break;
|
|
828
1162
|
case "recover":
|
|
829
1163
|
await cmdRecover(args.file, args.extract, args.repair);
|
|
830
1164
|
break;
|
|
831
1165
|
case "security-scan":
|
|
832
1166
|
await cmdSecurityScan(args.file);
|
|
833
1167
|
break;
|
|
1168
|
+
case "pii-scan":
|
|
1169
|
+
await cmdPIIScan(args.file, args.details);
|
|
1170
|
+
break;
|
|
834
1171
|
case "audit":
|
|
835
1172
|
await cmdAudit(args.file);
|
|
836
1173
|
break;
|
|
837
1174
|
case "retention":
|
|
838
1175
|
await cmdRetention(args.days, args.dryRun);
|
|
839
1176
|
break;
|
|
1177
|
+
case "alerts":
|
|
1178
|
+
await cmdAlerts();
|
|
1179
|
+
break;
|
|
1180
|
+
case "quotas":
|
|
1181
|
+
await cmdQuotas();
|
|
1182
|
+
break;
|
|
840
1183
|
case "trace":
|
|
841
1184
|
await cmdTrace(args.subcommand, {
|
|
842
1185
|
dryRun: args.dryRun,
|
|
@@ -849,6 +1192,9 @@ async function main() {
|
|
|
849
1192
|
install: args.install,
|
|
850
1193
|
});
|
|
851
1194
|
break;
|
|
1195
|
+
case "watch":
|
|
1196
|
+
await cmdWatch(args.json, args.interval, args.autoFix);
|
|
1197
|
+
break;
|
|
852
1198
|
case "dashboard":
|
|
853
1199
|
if (args.stop) {
|
|
854
1200
|
const stopped = stopDashboard();
|
|
@@ -866,7 +1212,7 @@ async function main() {
|
|
|
866
1212
|
}
|
|
867
1213
|
}
|
|
868
1214
|
else {
|
|
869
|
-
await startDashboard({ port: args.port, daemon: args.daemon });
|
|
1215
|
+
await startDashboard({ port: args.port, daemon: args.daemon, authToken: args.token });
|
|
870
1216
|
}
|
|
871
1217
|
break;
|
|
872
1218
|
default:
|
|
@@ -874,6 +1220,9 @@ async function main() {
|
|
|
874
1220
|
printHelp();
|
|
875
1221
|
process.exit(1);
|
|
876
1222
|
}
|
|
1223
|
+
if (args.json && commandResult !== undefined) {
|
|
1224
|
+
console.log(JSON.stringify(commandResult, null, 2));
|
|
1225
|
+
}
|
|
877
1226
|
}
|
|
878
1227
|
main().catch((err) => {
|
|
879
1228
|
console.error("Error:", err.message);
|