@asifkibria/claude-code-toolkit 1.0.2 → 1.2.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 +165 -214
- package/dist/CLAUDE.md +7 -0
- package/dist/__tests__/dashboard.test.d.ts +2 -0
- package/dist/__tests__/dashboard.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard.test.js +606 -0
- package/dist/__tests__/dashboard.test.js.map +1 -0
- package/dist/__tests__/mcp-validator.test.d.ts +2 -0
- package/dist/__tests__/mcp-validator.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-validator.test.js +217 -0
- package/dist/__tests__/mcp-validator.test.js.map +1 -0
- package/dist/__tests__/scanner.test.js +350 -1
- package/dist/__tests__/scanner.test.js.map +1 -1
- package/dist/__tests__/security.test.d.ts +2 -0
- package/dist/__tests__/security.test.d.ts.map +1 -0
- package/dist/__tests__/security.test.js +375 -0
- package/dist/__tests__/security.test.js.map +1 -0
- package/dist/__tests__/session-recovery.test.d.ts +2 -0
- package/dist/__tests__/session-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/session-recovery.test.js +230 -0
- package/dist/__tests__/session-recovery.test.js.map +1 -0
- package/dist/__tests__/storage.test.d.ts +2 -0
- package/dist/__tests__/storage.test.d.ts.map +1 -0
- package/dist/__tests__/storage.test.js +241 -0
- package/dist/__tests__/storage.test.js.map +1 -0
- package/dist/__tests__/trace.test.d.ts +2 -0
- package/dist/__tests__/trace.test.d.ts.map +1 -0
- package/dist/__tests__/trace.test.js +376 -0
- package/dist/__tests__/trace.test.js.map +1 -0
- package/dist/cli.js +501 -20
- package/dist/cli.js.map +1 -1
- package/dist/index.js +950 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/dashboard-ui.d.ts +2 -0
- package/dist/lib/dashboard-ui.d.ts.map +1 -0
- package/dist/lib/dashboard-ui.js +2075 -0
- package/dist/lib/dashboard-ui.js.map +1 -0
- package/dist/lib/dashboard.d.ts +15 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/dashboard.js +1422 -0
- package/dist/lib/dashboard.js.map +1 -0
- package/dist/lib/logs.d.ts +42 -0
- package/dist/lib/logs.d.ts.map +1 -0
- package/dist/lib/logs.js +166 -0
- package/dist/lib/logs.js.map +1 -0
- package/dist/lib/mcp-validator.d.ts +86 -0
- package/dist/lib/mcp-validator.d.ts.map +1 -0
- package/dist/lib/mcp-validator.js +463 -0
- package/dist/lib/mcp-validator.js.map +1 -0
- package/dist/lib/scanner.d.ts +187 -2
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/scanner.js +1224 -14
- package/dist/lib/scanner.js.map +1 -1
- package/dist/lib/security.d.ts +57 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +423 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/session-recovery.d.ts +60 -0
- package/dist/lib/session-recovery.d.ts.map +1 -0
- package/dist/lib/session-recovery.js +433 -0
- package/dist/lib/session-recovery.js.map +1 -0
- package/dist/lib/storage.d.ts +68 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +500 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/trace.d.ts +119 -0
- package/dist/lib/trace.d.ts.map +1 -0
- package/dist/lib/trace.js +649 -0
- package/dist/lib/trace.js.map +1 -0
- package/package.json +11 -3
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import * as os from "os";
|
|
9
|
-
import { findAllJsonlFiles, findBackupFiles, scanFile, fixFile, getConversationStats, restoreFromBackup, deleteOldBackups, } from "./lib/scanner.js";
|
|
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, } from "./lib/mcp-validator.js";
|
|
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";
|
|
14
|
+
import { inventoryTraces, cleanTraces, wipeAllTraces, generateTraceGuardHooks, formatTraceInventory, formatTraceCleanReport, formatTraceGuardConfig, } from "./lib/trace.js";
|
|
15
|
+
import { startDashboard, stopDashboard, isDashboardRunning } from "./lib/dashboard.js";
|
|
10
16
|
function formatContentType(type) {
|
|
11
17
|
switch (type) {
|
|
12
18
|
case "image": return "🖼️ image";
|
|
@@ -30,41 +36,83 @@ function formatDate(date) {
|
|
|
30
36
|
}
|
|
31
37
|
function printHelp() {
|
|
32
38
|
console.log(`
|
|
33
|
-
Claude Code Toolkit v1.0
|
|
34
|
-
Maintain, optimize, and troubleshoot your Claude Code installation.
|
|
35
|
-
Fixes oversized images, PDFs, documents, and large text content.
|
|
39
|
+
Claude Code Toolkit v1.2.0
|
|
40
|
+
Maintain, optimize, secure, and troubleshoot your Claude Code installation.
|
|
36
41
|
|
|
37
42
|
USAGE:
|
|
38
43
|
cct <command> [options]
|
|
39
44
|
claude-code-toolkit <command> [options]
|
|
40
45
|
|
|
41
46
|
COMMANDS:
|
|
42
|
-
health
|
|
43
|
-
stats
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
health Quick health check (start here!)
|
|
48
|
+
stats Show conversation statistics
|
|
49
|
+
context Estimate context/token usage
|
|
50
|
+
analytics Usage analytics dashboard
|
|
51
|
+
duplicates Find duplicate content and conversations
|
|
52
|
+
archive Archive old/inactive conversations
|
|
53
|
+
maintenance Run maintenance checks and actions
|
|
54
|
+
scan Scan for issues (dry run)
|
|
55
|
+
fix Fix all detected issues
|
|
56
|
+
export Export conversation to markdown or JSON
|
|
57
|
+
backups List backup files
|
|
58
|
+
restore <path> Restore from a backup file
|
|
59
|
+
cleanup Delete old backup files
|
|
60
|
+
clean Analyze and clean .claude directory
|
|
61
|
+
mcp-validate Validate MCP server configurations
|
|
62
|
+
sessions List all sessions with health status
|
|
63
|
+
recover <id> Diagnose/repair/extract from a session
|
|
64
|
+
security-scan Scan conversations for leaked secrets
|
|
65
|
+
audit <id> Audit a session's actions (files, commands, etc.)
|
|
66
|
+
retention Enforce data retention policy
|
|
67
|
+
dashboard Open web dashboard in browser
|
|
68
|
+
dashboard --daemon Run dashboard as background process
|
|
69
|
+
dashboard --stop Stop background dashboard
|
|
70
|
+
trace Show full trace inventory
|
|
71
|
+
trace clean Selective trace cleanup
|
|
72
|
+
trace wipe Secure wipe all traces (requires --confirm)
|
|
73
|
+
trace guard Generate trace prevention hooks
|
|
49
74
|
|
|
50
75
|
OPTIONS:
|
|
51
76
|
-f, --file <path> Target a specific file
|
|
77
|
+
-o, --output <path> Output file path (for export)
|
|
78
|
+
--format <type> Export format: markdown or json (default: markdown)
|
|
79
|
+
--with-tools Include tool results in export
|
|
52
80
|
-d, --dry-run Show what would be done without making changes
|
|
53
81
|
--no-backup Skip creating backups when fixing (not recommended)
|
|
54
|
-
--days <n> For cleanup:
|
|
82
|
+
--days <n> For cleanup/archive/retention: days threshold (default: 7/30)
|
|
55
83
|
--limit <n> For stats: limit results (default: 10)
|
|
56
84
|
--sort <field> For stats: sort by size|messages|images|modified
|
|
85
|
+
--auto For maintenance: run automatically without prompts
|
|
86
|
+
--schedule For maintenance: show cron/launchd setup
|
|
87
|
+
--category <type> For clean: target specific category
|
|
88
|
+
--test For mcp-validate: test server connectivity
|
|
89
|
+
--extract For recover: extract salvageable content
|
|
90
|
+
--repair For recover: attempt repair
|
|
91
|
+
--confirm For trace wipe: confirm destructive operation
|
|
92
|
+
--keep-settings For trace wipe: preserve settings.json
|
|
93
|
+
--mode <mode> For trace guard: paranoid|moderate|minimal
|
|
94
|
+
--install For trace guard: auto-install hooks
|
|
95
|
+
--categories <list> For trace clean: comma-separated categories
|
|
96
|
+
--port <n> For dashboard: port number (default: 1405)
|
|
97
|
+
--daemon For dashboard: run as background process
|
|
98
|
+
--stop For dashboard: stop background process
|
|
99
|
+
--project <path> Filter by project path
|
|
57
100
|
-h, --help Show this help message
|
|
58
101
|
-v, --version Show version
|
|
59
102
|
|
|
60
103
|
EXAMPLES:
|
|
61
104
|
cct health # Quick health check
|
|
62
|
-
cct
|
|
63
|
-
cct
|
|
64
|
-
cct
|
|
65
|
-
cct
|
|
66
|
-
cct
|
|
67
|
-
cct
|
|
105
|
+
cct clean --dry-run # Preview .claude directory cleanup
|
|
106
|
+
cct mcp-validate --test # Validate and test MCP servers
|
|
107
|
+
cct sessions # List all sessions
|
|
108
|
+
cct recover abc-123 --repair # Repair a corrupted session
|
|
109
|
+
cct security-scan # Scan for leaked secrets
|
|
110
|
+
cct audit abc-123 # Audit session actions
|
|
111
|
+
cct retention --days 60 --dry-run # Preview retention policy
|
|
112
|
+
cct trace # Show trace inventory
|
|
113
|
+
cct trace clean --days 7 # Clean old traces
|
|
114
|
+
cct trace wipe --confirm # Secure wipe all traces
|
|
115
|
+
cct trace guard --mode moderate # Generate trace prevention hooks
|
|
68
116
|
|
|
69
117
|
For more info: https://github.com/asifkibria/claude-code-toolkit
|
|
70
118
|
`);
|
|
@@ -72,12 +120,31 @@ For more info: https://github.com/asifkibria/claude-code-toolkit
|
|
|
72
120
|
function parseArgs(args) {
|
|
73
121
|
const result = {
|
|
74
122
|
command: "",
|
|
123
|
+
subcommand: undefined,
|
|
75
124
|
file: undefined,
|
|
125
|
+
output: undefined,
|
|
126
|
+
format: "markdown",
|
|
127
|
+
withTools: false,
|
|
76
128
|
dryRun: false,
|
|
77
129
|
noBackup: false,
|
|
78
130
|
days: 7,
|
|
79
131
|
limit: 10,
|
|
80
132
|
sort: "size",
|
|
133
|
+
auto: false,
|
|
134
|
+
schedule: false,
|
|
135
|
+
category: undefined,
|
|
136
|
+
test: false,
|
|
137
|
+
extract: false,
|
|
138
|
+
repair: false,
|
|
139
|
+
confirm: false,
|
|
140
|
+
keepSettings: false,
|
|
141
|
+
mode: "moderate",
|
|
142
|
+
install: false,
|
|
143
|
+
categories: undefined,
|
|
144
|
+
project: undefined,
|
|
145
|
+
port: 1405,
|
|
146
|
+
daemon: false,
|
|
147
|
+
stop: false,
|
|
81
148
|
};
|
|
82
149
|
for (let i = 0; i < args.length; i++) {
|
|
83
150
|
const arg = args[i];
|
|
@@ -86,13 +153,28 @@ function parseArgs(args) {
|
|
|
86
153
|
process.exit(0);
|
|
87
154
|
}
|
|
88
155
|
if (arg === "-v" || arg === "--version") {
|
|
89
|
-
console.log("1.0
|
|
156
|
+
console.log("1.2.0");
|
|
90
157
|
process.exit(0);
|
|
91
158
|
}
|
|
92
159
|
if (arg === "-f" || arg === "--file") {
|
|
93
160
|
result.file = args[++i];
|
|
94
161
|
continue;
|
|
95
162
|
}
|
|
163
|
+
if (arg === "-o" || arg === "--output") {
|
|
164
|
+
result.output = args[++i];
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (arg === "--format") {
|
|
168
|
+
const fmt = args[++i];
|
|
169
|
+
if (fmt === "json" || fmt === "markdown") {
|
|
170
|
+
result.format = fmt;
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (arg === "--with-tools") {
|
|
175
|
+
result.withTools = true;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
96
178
|
if (arg === "-d" || arg === "--dry-run") {
|
|
97
179
|
result.dryRun = true;
|
|
98
180
|
continue;
|
|
@@ -113,12 +195,77 @@ function parseArgs(args) {
|
|
|
113
195
|
result.sort = args[++i];
|
|
114
196
|
continue;
|
|
115
197
|
}
|
|
198
|
+
if (arg === "--auto") {
|
|
199
|
+
result.auto = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (arg === "--schedule") {
|
|
203
|
+
result.schedule = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (arg === "--category") {
|
|
207
|
+
result.category = args[++i];
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (arg === "--test") {
|
|
211
|
+
result.test = true;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (arg === "--extract") {
|
|
215
|
+
result.extract = true;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (arg === "--repair") {
|
|
219
|
+
result.repair = true;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (arg === "--confirm") {
|
|
223
|
+
result.confirm = true;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (arg === "--keep-settings") {
|
|
227
|
+
result.keepSettings = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (arg === "--mode") {
|
|
231
|
+
result.mode = args[++i];
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (arg === "--install") {
|
|
235
|
+
result.install = true;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (arg === "--categories") {
|
|
239
|
+
result.categories = args[++i];
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (arg === "--port") {
|
|
243
|
+
result.port = parseInt(args[++i], 10);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (arg === "--daemon") {
|
|
247
|
+
result.daemon = true;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (arg === "--stop") {
|
|
251
|
+
result.stop = true;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (arg === "--project") {
|
|
255
|
+
result.project = args[++i];
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
116
258
|
if (!arg.startsWith("-") && !result.command) {
|
|
117
259
|
result.command = arg;
|
|
118
260
|
continue;
|
|
119
261
|
}
|
|
120
|
-
if (!arg.startsWith("-") && result.command === "restore") {
|
|
262
|
+
if (!arg.startsWith("-") && (result.command === "restore" || result.command === "recover" || result.command === "audit")) {
|
|
121
263
|
result.file = arg;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (!arg.startsWith("-") && result.command === "trace" && !result.subcommand) {
|
|
267
|
+
result.subcommand = arg;
|
|
268
|
+
continue;
|
|
122
269
|
}
|
|
123
270
|
}
|
|
124
271
|
return result;
|
|
@@ -322,6 +469,269 @@ async function cmdCleanup(days, dryRun) {
|
|
|
322
469
|
}
|
|
323
470
|
}
|
|
324
471
|
}
|
|
472
|
+
async function cmdExport(file, output, format, withTools) {
|
|
473
|
+
if (!file) {
|
|
474
|
+
console.error("Please specify a conversation file with -f or --file");
|
|
475
|
+
console.error("Example: cct export -f ~/.claude/projects/myproject/conversation.jsonl");
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
if (!fs.existsSync(file)) {
|
|
479
|
+
console.error(`File not found: ${file}`);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
const ext = format === "json" ? ".json" : ".md";
|
|
483
|
+
const outputPath = output || file.replace(".jsonl", `-export${ext}`);
|
|
484
|
+
console.log(`Exporting conversation...`);
|
|
485
|
+
console.log(` Source: ${file}`);
|
|
486
|
+
console.log(` Format: ${format}`);
|
|
487
|
+
console.log(` Output: ${outputPath}`);
|
|
488
|
+
console.log();
|
|
489
|
+
const result = exportConversationToFile(file, outputPath, {
|
|
490
|
+
format,
|
|
491
|
+
includeToolResults: withTools,
|
|
492
|
+
includeTimestamps: true,
|
|
493
|
+
});
|
|
494
|
+
if (!result.success) {
|
|
495
|
+
console.error(`\x1b[31mError: ${result.error}\x1b[0m`);
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
498
|
+
console.log(`\x1b[32m✓ Exported ${result.messageCount} messages to ${outputPath}\x1b[0m`);
|
|
499
|
+
}
|
|
500
|
+
async function cmdContext(file) {
|
|
501
|
+
if (!file) {
|
|
502
|
+
// If no file specified, show summary for all conversations
|
|
503
|
+
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
504
|
+
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
const files = findAllJsonlFiles(PROJECTS_DIR);
|
|
508
|
+
const estimates = files.map((f) => {
|
|
509
|
+
try {
|
|
510
|
+
return estimateContextSize(f);
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
}).filter(Boolean);
|
|
516
|
+
estimates.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
517
|
+
const totalTokens = estimates.reduce((sum, e) => sum + e.totalTokens, 0);
|
|
518
|
+
console.log("Context Usage Summary\n");
|
|
519
|
+
console.log(`Total conversations: ${estimates.length}`);
|
|
520
|
+
console.log(`Combined tokens: ~${totalTokens.toLocaleString()}\n`);
|
|
521
|
+
console.log("Top 10 by context size:\n");
|
|
522
|
+
for (const estimate of estimates.slice(0, 10)) {
|
|
523
|
+
const relPath = path.relative(PROJECTS_DIR, estimate.file);
|
|
524
|
+
const shortPath = relPath.length > 50 ? "..." + relPath.slice(-47) : relPath;
|
|
525
|
+
console.log(`\x1b[36m${shortPath}\x1b[0m`);
|
|
526
|
+
console.log(` ~${estimate.totalTokens.toLocaleString()} tokens (${estimate.messageCount} messages)`);
|
|
527
|
+
if (estimate.warnings.length > 0) {
|
|
528
|
+
console.log(` \x1b[33m⚠ ${estimate.warnings[0]}\x1b[0m`);
|
|
529
|
+
}
|
|
530
|
+
console.log();
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (!fs.existsSync(file)) {
|
|
535
|
+
console.error(`File not found: ${file}`);
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
const estimate = estimateContextSize(file);
|
|
539
|
+
console.log(formatContextEstimate(estimate));
|
|
540
|
+
}
|
|
541
|
+
async function cmdAnalytics() {
|
|
542
|
+
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
543
|
+
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
console.log("Generating analytics...\n");
|
|
547
|
+
const analytics = generateUsageAnalytics(PROJECTS_DIR, 30);
|
|
548
|
+
console.log(formatUsageAnalytics(analytics));
|
|
549
|
+
}
|
|
550
|
+
async function cmdDuplicates() {
|
|
551
|
+
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
552
|
+
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
console.log("Scanning for duplicates...\n");
|
|
556
|
+
const report = findDuplicates(PROJECTS_DIR);
|
|
557
|
+
console.log(formatDuplicateReport(report));
|
|
558
|
+
}
|
|
559
|
+
async function cmdArchive(days, dryRun) {
|
|
560
|
+
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
561
|
+
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
562
|
+
process.exit(1);
|
|
563
|
+
}
|
|
564
|
+
const archiveDays = days > 7 ? days : 30;
|
|
565
|
+
console.log(`Finding conversations inactive for ${archiveDays}+ days...\n`);
|
|
566
|
+
const candidates = findArchiveCandidates(PROJECTS_DIR, { minDaysInactive: archiveDays });
|
|
567
|
+
if (candidates.length === 0) {
|
|
568
|
+
console.log("✓ No conversations eligible for archiving.\n");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const result = archiveConversations(PROJECTS_DIR, { minDaysInactive: archiveDays, dryRun });
|
|
572
|
+
console.log(formatArchiveReport(candidates, result, dryRun));
|
|
573
|
+
if (dryRun && candidates.length > 0) {
|
|
574
|
+
console.log("\x1b[33mRun without --dry-run to archive these conversations.\x1b[0m\n");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function cmdMaintenance(dryRun, auto, schedule) {
|
|
578
|
+
if (schedule) {
|
|
579
|
+
console.log("Scheduled Maintenance Setup\n");
|
|
580
|
+
console.log("=== For macOS (launchd) ===");
|
|
581
|
+
console.log("Save this to ~/Library/LaunchAgents/com.claude-code-toolkit.maintenance.plist:\n");
|
|
582
|
+
console.log(generateLaunchdPlist());
|
|
583
|
+
console.log("\nThen run: launchctl load ~/Library/LaunchAgents/com.claude-code-toolkit.maintenance.plist\n");
|
|
584
|
+
console.log("=== For Linux/Unix (cron) ===\n");
|
|
585
|
+
console.log(generateCronSchedule());
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
589
|
+
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
590
|
+
process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
console.log("Running maintenance checks...\n");
|
|
593
|
+
const report = runMaintenance(PROJECTS_DIR, { dryRun: !auto });
|
|
594
|
+
console.log(formatMaintenanceReport(report, !auto));
|
|
595
|
+
if (!auto && report.actions.length > 0) {
|
|
596
|
+
console.log("\x1b[33mRun with --auto to perform maintenance actions automatically.\x1b[0m\n");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function cmdClean(dryRun, days, category) {
|
|
600
|
+
const analysis = analyzeClaudeStorage();
|
|
601
|
+
console.log(formatStorageReport(analysis));
|
|
602
|
+
const options = { dryRun: dryRun || true, days, categories: category ? [category] : undefined };
|
|
603
|
+
const targets = findCleanupTargets(undefined, options);
|
|
604
|
+
if (targets.length === 0) {
|
|
605
|
+
console.log("\x1b[32m✓ Nothing to clean.\x1b[0m\n");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const result = cleanClaudeDirectory(undefined, { ...options, dryRun });
|
|
609
|
+
console.log(formatCleanupReport(targets, result, dryRun));
|
|
610
|
+
}
|
|
611
|
+
async function cmdMcpValidate(file, test) {
|
|
612
|
+
const report = await diagnoseMcpServers({ test, projectDir: process.cwd() });
|
|
613
|
+
console.log(formatMcpDiagnosticReport(report));
|
|
614
|
+
}
|
|
615
|
+
async function cmdSessions(project) {
|
|
616
|
+
const sessions = listSessions(undefined, { project });
|
|
617
|
+
if (sessions.length === 0) {
|
|
618
|
+
console.log("No sessions found.\n");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
console.log(formatSessionReport(sessions));
|
|
622
|
+
}
|
|
623
|
+
async function cmdRecover(sessionId, extract, repair) {
|
|
624
|
+
if (!sessionId) {
|
|
625
|
+
console.error("Usage: cct recover <session-id> [--extract] [--repair]");
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
const sessions = listSessions();
|
|
629
|
+
const session = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
|
|
630
|
+
if (!session) {
|
|
631
|
+
console.error(`Session not found: ${sessionId}`);
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
const diag = diagnoseSession(session.filePath);
|
|
635
|
+
console.log(formatSessionDiagnosticReport(diag));
|
|
636
|
+
if (repair) {
|
|
637
|
+
console.log("\nRepairing session...");
|
|
638
|
+
const result = repairSession(session.filePath);
|
|
639
|
+
if (result.success) {
|
|
640
|
+
console.log(`\x1b[32m✓ Repaired. Removed ${result.linesRemoved} invalid lines.\x1b[0m`);
|
|
641
|
+
if (result.backupPath)
|
|
642
|
+
console.log(` Backup: ${result.backupPath}`);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
console.log(`\x1b[31m✗ Repair failed: ${result.error}\x1b[0m`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (extract) {
|
|
649
|
+
console.log("\nExtracting session content...");
|
|
650
|
+
const content = extractSessionContent(session.filePath);
|
|
651
|
+
console.log(` User messages: ${content.userMessages.length}`);
|
|
652
|
+
console.log(` Assistant messages: ${content.assistantMessages.length}`);
|
|
653
|
+
console.log(` File edits: ${content.fileEdits.length}`);
|
|
654
|
+
console.log(` Commands run: ${content.commandsRun.length}`);
|
|
655
|
+
if (content.fileEdits.length > 0) {
|
|
656
|
+
console.log("\nFile edits:");
|
|
657
|
+
for (const edit of content.fileEdits.slice(0, 10)) {
|
|
658
|
+
console.log(` ${edit.path}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (content.commandsRun.length > 0) {
|
|
662
|
+
console.log("\nCommands:");
|
|
663
|
+
for (const cmd of content.commandsRun.slice(0, 10)) {
|
|
664
|
+
const short = cmd.length > 80 ? cmd.slice(0, 77) + "..." : cmd;
|
|
665
|
+
console.log(` $ ${short}`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async function cmdSecurityScan(file) {
|
|
671
|
+
console.log("Scanning for secrets in conversation data...\n");
|
|
672
|
+
const result = scanForSecrets(undefined, { file });
|
|
673
|
+
console.log(formatSecretsScanReport(result));
|
|
674
|
+
}
|
|
675
|
+
async function cmdAudit(sessionId) {
|
|
676
|
+
if (!sessionId) {
|
|
677
|
+
console.error("Usage: cct audit <session-id>");
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
const sessions = listSessions();
|
|
681
|
+
const session = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
|
|
682
|
+
if (!session) {
|
|
683
|
+
console.error(`Session not found: ${sessionId}`);
|
|
684
|
+
process.exit(1);
|
|
685
|
+
}
|
|
686
|
+
const audit = auditSession(session.filePath);
|
|
687
|
+
console.log(formatAuditReport(audit));
|
|
688
|
+
}
|
|
689
|
+
async function cmdRetention(days, dryRun) {
|
|
690
|
+
const retentionDays = days > 7 ? days : 30;
|
|
691
|
+
const result = enforceRetention(undefined, { days: retentionDays, dryRun });
|
|
692
|
+
console.log(formatRetentionReport(result));
|
|
693
|
+
}
|
|
694
|
+
async function cmdTrace(subcommand, options) {
|
|
695
|
+
if (!subcommand) {
|
|
696
|
+
const inv = inventoryTraces(undefined, { project: options?.project });
|
|
697
|
+
console.log(formatTraceInventory(inv));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (subcommand === "clean") {
|
|
701
|
+
const cleanOpts = {
|
|
702
|
+
dryRun: options?.dryRun ?? true,
|
|
703
|
+
days: options?.days,
|
|
704
|
+
categories: options?.categories?.split(","),
|
|
705
|
+
project: options?.project,
|
|
706
|
+
};
|
|
707
|
+
const result = cleanTraces(undefined, cleanOpts);
|
|
708
|
+
console.log(formatTraceCleanReport(result));
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (subcommand === "wipe") {
|
|
712
|
+
if (!options?.confirm) {
|
|
713
|
+
console.log("\x1b[31m⚠ This will securely wipe ALL Claude Code traces.\x1b[0m");
|
|
714
|
+
console.log("Run with --confirm to execute.\n");
|
|
715
|
+
const inv = inventoryTraces();
|
|
716
|
+
console.log(`Would wipe: ${inv.totalFiles} files (${formatBytes(inv.totalSize)})`);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const result = wipeAllTraces(undefined, {
|
|
720
|
+
confirm: true,
|
|
721
|
+
keepSettings: options.keepSettings,
|
|
722
|
+
});
|
|
723
|
+
console.log(result.wipeReceipt);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
if (subcommand === "guard") {
|
|
727
|
+
const mode = (options?.mode || "moderate");
|
|
728
|
+
const config = generateTraceGuardHooks({ mode });
|
|
729
|
+
console.log(formatTraceGuardConfig(config));
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
console.error(`Unknown trace subcommand: ${subcommand}`);
|
|
733
|
+
console.error("Usage: cct trace [clean|wipe|guard]");
|
|
734
|
+
}
|
|
325
735
|
async function cmdHealth() {
|
|
326
736
|
if (!fs.existsSync(PROJECTS_DIR)) {
|
|
327
737
|
console.error(`Claude projects directory not found: ${PROJECTS_DIR}`);
|
|
@@ -376,6 +786,24 @@ async function main() {
|
|
|
376
786
|
case "stats":
|
|
377
787
|
await cmdStats(args.limit, args.sort);
|
|
378
788
|
break;
|
|
789
|
+
case "context":
|
|
790
|
+
await cmdContext(args.file);
|
|
791
|
+
break;
|
|
792
|
+
case "analytics":
|
|
793
|
+
await cmdAnalytics();
|
|
794
|
+
break;
|
|
795
|
+
case "duplicates":
|
|
796
|
+
await cmdDuplicates();
|
|
797
|
+
break;
|
|
798
|
+
case "archive":
|
|
799
|
+
await cmdArchive(args.days, args.dryRun);
|
|
800
|
+
break;
|
|
801
|
+
case "maintenance":
|
|
802
|
+
await cmdMaintenance(args.dryRun, args.auto, args.schedule);
|
|
803
|
+
break;
|
|
804
|
+
case "export":
|
|
805
|
+
await cmdExport(args.file, args.output, args.format, args.withTools);
|
|
806
|
+
break;
|
|
379
807
|
case "backups":
|
|
380
808
|
await cmdBackups();
|
|
381
809
|
break;
|
|
@@ -388,6 +816,59 @@ async function main() {
|
|
|
388
816
|
case "health":
|
|
389
817
|
await cmdHealth();
|
|
390
818
|
break;
|
|
819
|
+
case "clean":
|
|
820
|
+
await cmdClean(args.dryRun, args.days, args.category);
|
|
821
|
+
break;
|
|
822
|
+
case "mcp-validate":
|
|
823
|
+
await cmdMcpValidate(args.file, args.test);
|
|
824
|
+
break;
|
|
825
|
+
case "sessions":
|
|
826
|
+
await cmdSessions(args.project);
|
|
827
|
+
break;
|
|
828
|
+
case "recover":
|
|
829
|
+
await cmdRecover(args.file, args.extract, args.repair);
|
|
830
|
+
break;
|
|
831
|
+
case "security-scan":
|
|
832
|
+
await cmdSecurityScan(args.file);
|
|
833
|
+
break;
|
|
834
|
+
case "audit":
|
|
835
|
+
await cmdAudit(args.file);
|
|
836
|
+
break;
|
|
837
|
+
case "retention":
|
|
838
|
+
await cmdRetention(args.days, args.dryRun);
|
|
839
|
+
break;
|
|
840
|
+
case "trace":
|
|
841
|
+
await cmdTrace(args.subcommand, {
|
|
842
|
+
dryRun: args.dryRun,
|
|
843
|
+
days: args.days,
|
|
844
|
+
categories: args.categories,
|
|
845
|
+
project: args.project,
|
|
846
|
+
confirm: args.confirm,
|
|
847
|
+
keepSettings: args.keepSettings,
|
|
848
|
+
mode: args.mode,
|
|
849
|
+
install: args.install,
|
|
850
|
+
});
|
|
851
|
+
break;
|
|
852
|
+
case "dashboard":
|
|
853
|
+
if (args.stop) {
|
|
854
|
+
const stopped = stopDashboard();
|
|
855
|
+
if (stopped) {
|
|
856
|
+
console.log("\x1b[32m✓ Dashboard stopped.\x1b[0m");
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
const status = isDashboardRunning();
|
|
860
|
+
if (!status.running) {
|
|
861
|
+
console.log("No dashboard process found.");
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
console.error("Failed to stop dashboard.");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
await startDashboard({ port: args.port, daemon: args.daemon });
|
|
870
|
+
}
|
|
871
|
+
break;
|
|
391
872
|
default:
|
|
392
873
|
console.error(`Unknown command: ${args.command}`);
|
|
393
874
|
printHelp();
|