@asifkibria/claude-code-toolkit 1.0.7 → 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 +152 -468
- 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__/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 +322 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.js +554 -2
- 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 +6 -2
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/scanner.js +17 -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/index.js
CHANGED
|
@@ -11,6 +11,12 @@ import * as fs from "fs";
|
|
|
11
11
|
import * as path from "path";
|
|
12
12
|
import * as os from "os";
|
|
13
13
|
import { findAllJsonlFiles, findBackupFiles, scanFile, fixFile, getConversationStats, restoreFromBackup, deleteOldBackups, exportConversation, estimateContextSize, generateUsageAnalytics, findDuplicates, findArchiveCandidates, archiveConversations, runMaintenance, } from "./lib/scanner.js";
|
|
14
|
+
import { analyzeClaudeStorage, cleanClaudeDirectory, } from "./lib/storage.js";
|
|
15
|
+
import { diagnoseMcpServers, } from "./lib/mcp-validator.js";
|
|
16
|
+
import { listSessions, diagnoseSession, repairSession, extractSessionContent, } from "./lib/session-recovery.js";
|
|
17
|
+
import { scanForSecrets, auditSession, enforceRetention, } from "./lib/security.js";
|
|
18
|
+
import { inventoryTraces, cleanTraces, wipeAllTraces, generateTraceGuardHooks, } from "./lib/trace.js";
|
|
19
|
+
import { startDashboard } from "./lib/dashboard.js";
|
|
14
20
|
const CLAUDE_DIR = path.join(os.homedir(), ".claude");
|
|
15
21
|
const PROJECTS_DIR = path.join(CLAUDE_DIR, "projects");
|
|
16
22
|
function formatBytes(bytes) {
|
|
@@ -34,7 +40,7 @@ function formatContentType(type) {
|
|
|
34
40
|
}
|
|
35
41
|
const server = new Server({
|
|
36
42
|
name: "claude-code-toolkit",
|
|
37
|
-
version: "1.0
|
|
43
|
+
version: "1.2.0",
|
|
38
44
|
}, {
|
|
39
45
|
capabilities: {
|
|
40
46
|
tools: {},
|
|
@@ -235,6 +241,138 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
235
241
|
},
|
|
236
242
|
},
|
|
237
243
|
},
|
|
244
|
+
{
|
|
245
|
+
name: "clean_claude_directory",
|
|
246
|
+
description: "Analyze and clean the .claude directory. Removes debug logs, empty todos, old snapshots, and orphaned data to free disk space.",
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
dry_run: { type: "boolean", description: "Preview without deleting. Default: true" },
|
|
251
|
+
days: { type: "number", description: "Age threshold in days. Default: 7" },
|
|
252
|
+
category: { type: "string", description: "Clean specific category: debug, todos, shell-snapshots, file-history, session-env, cache" },
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "validate_mcp_config",
|
|
258
|
+
description: "Validate MCP server configurations. Checks JSON syntax, command existence, and optionally tests server connectivity.",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
test: { type: "boolean", description: "Test server connectivity (spawns processes with 5s timeout). Default: false" },
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "list_sessions",
|
|
268
|
+
description: "List all Claude Code sessions with health status. Shows session ID, project, size, message count, and whether the session is healthy, corrupted, empty, or orphaned.",
|
|
269
|
+
inputSchema: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
project: { type: "string", description: "Filter by project path" },
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "recover_session",
|
|
278
|
+
description: "Diagnose, repair, or extract content from a Claude Code session. Use for corrupted or crashed sessions.",
|
|
279
|
+
inputSchema: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
session_id: { type: "string", description: "Session ID (or prefix) to recover" },
|
|
283
|
+
repair: { type: "boolean", description: "Attempt to repair by removing invalid lines. Default: false" },
|
|
284
|
+
extract: { type: "boolean", description: "Extract salvageable content (messages, edits, commands). Default: false" },
|
|
285
|
+
},
|
|
286
|
+
required: ["session_id"],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: "security_scan",
|
|
291
|
+
description: "Scan conversation files for leaked secrets (AWS keys, API tokens, passwords, private keys, connection strings, JWTs).",
|
|
292
|
+
inputSchema: {
|
|
293
|
+
type: "object",
|
|
294
|
+
properties: {
|
|
295
|
+
file: { type: "string", description: "Scan a specific file. Default: all conversation files" },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "audit_session",
|
|
301
|
+
description: "Generate an audit report for a session showing all files read/written, commands executed, MCP tools used, and URLs fetched.",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
session_id: { type: "string", description: "Session ID (or prefix) to audit" },
|
|
306
|
+
},
|
|
307
|
+
required: ["session_id"],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "enforce_retention",
|
|
312
|
+
description: "Apply data retention policy by deleting sessions older than specified days.",
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
days: { type: "number", description: "Delete sessions older than this many days. Default: 30" },
|
|
317
|
+
dry_run: { type: "boolean", description: "Preview without deleting. Default: true" },
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: "inventory_traces",
|
|
323
|
+
description: "Show complete inventory of all traces Claude Code has stored on disk, categorized by sensitivity level (critical/high/medium/low).",
|
|
324
|
+
inputSchema: {
|
|
325
|
+
type: "object",
|
|
326
|
+
properties: {
|
|
327
|
+
project: { type: "string", description: "Filter by project path" },
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "clean_traces",
|
|
333
|
+
description: "Selectively clean Claude Code traces by category, age, or project.",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: "object",
|
|
336
|
+
properties: {
|
|
337
|
+
categories: { type: "string", description: "Comma-separated categories to clean" },
|
|
338
|
+
days: { type: "number", description: "Only clean traces older than this many days" },
|
|
339
|
+
project: { type: "string", description: "Only clean traces for specific project" },
|
|
340
|
+
dry_run: { type: "boolean", description: "Preview without deleting. Default: true" },
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: "wipe_traces",
|
|
346
|
+
description: "Securely wipe ALL Claude Code traces. Overwrites files with zeros before deletion. Requires explicit confirmation.",
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: "object",
|
|
349
|
+
properties: {
|
|
350
|
+
confirm: { type: "boolean", description: "Must be true to execute wipe" },
|
|
351
|
+
keep_settings: { type: "boolean", description: "Preserve settings.json and CLAUDE.md. Default: false" },
|
|
352
|
+
},
|
|
353
|
+
required: ["confirm"],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "generate_trace_guard",
|
|
358
|
+
description: "Generate hook configurations for ongoing trace prevention. Outputs JSON ready to add to settings.",
|
|
359
|
+
inputSchema: {
|
|
360
|
+
type: "object",
|
|
361
|
+
properties: {
|
|
362
|
+
mode: { type: "string", description: "Guard mode: paranoid (delete everything), moderate (24h retention), minimal (7-day cleanup). Default: moderate" },
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "start_dashboard",
|
|
368
|
+
description: "Start the web dashboard on a local port. Returns the URL to open in a browser.",
|
|
369
|
+
inputSchema: {
|
|
370
|
+
type: "object",
|
|
371
|
+
properties: {
|
|
372
|
+
port: { type: "number", description: "Port number to listen on. Default: 1405" },
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
238
376
|
],
|
|
239
377
|
};
|
|
240
378
|
});
|
|
@@ -910,6 +1048,420 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
910
1048
|
}
|
|
911
1049
|
return { content: [{ type: "text", text: output }] };
|
|
912
1050
|
}
|
|
1051
|
+
case "clean_claude_directory": {
|
|
1052
|
+
const dryRun = typedArgs.dry_run !== false;
|
|
1053
|
+
const days = typedArgs.days || 7;
|
|
1054
|
+
const category = typedArgs.category;
|
|
1055
|
+
const analysis = analyzeClaudeStorage();
|
|
1056
|
+
const result = cleanClaudeDirectory(undefined, {
|
|
1057
|
+
dryRun,
|
|
1058
|
+
days,
|
|
1059
|
+
categories: category ? [category] : undefined,
|
|
1060
|
+
});
|
|
1061
|
+
const totalFiles = analysis.categories.reduce((sum, c) => sum + c.fileCount, 0);
|
|
1062
|
+
let output = `🧹 **Directory ${dryRun ? "Analysis" : "Cleanup"}**\n\n`;
|
|
1063
|
+
output += `## Storage Analysis\n`;
|
|
1064
|
+
output += `| Category | Size | Files |\n`;
|
|
1065
|
+
output += `|----------|------|-------|\n`;
|
|
1066
|
+
for (const cat of analysis.categories) {
|
|
1067
|
+
output += `| ${cat.name} | ${formatBytes(cat.totalSize)} | ${cat.fileCount} |\n`;
|
|
1068
|
+
}
|
|
1069
|
+
output += `| **Total** | **${formatBytes(analysis.totalSize)}** | **${totalFiles}** |\n\n`;
|
|
1070
|
+
if (result.deleted.length > 0) {
|
|
1071
|
+
output += `## ${dryRun ? "Cleanable Items" : "Cleaned Items"}\n`;
|
|
1072
|
+
output += `- Files: ${result.deleted.length}\n`;
|
|
1073
|
+
output += `- Space ${dryRun ? "freeable" : "freed"}: ${formatBytes(result.freed)}\n\n`;
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
output += `✓ Nothing to clean.\n\n`;
|
|
1077
|
+
}
|
|
1078
|
+
if (dryRun && result.deleted.length > 0) {
|
|
1079
|
+
output += `ℹ️ Set \`dry_run: false\` to actually clean.\n`;
|
|
1080
|
+
}
|
|
1081
|
+
if (result.errors.length > 0) {
|
|
1082
|
+
output += `\n⚠️ Errors:\n`;
|
|
1083
|
+
for (const err of result.errors) {
|
|
1084
|
+
output += `- ${err}\n`;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return { content: [{ type: "text", text: output }] };
|
|
1088
|
+
}
|
|
1089
|
+
case "validate_mcp_config": {
|
|
1090
|
+
const test = typedArgs.test === true;
|
|
1091
|
+
const report = await diagnoseMcpServers({ test });
|
|
1092
|
+
let output = `🔌 **MCP Config Validation**\n\n`;
|
|
1093
|
+
output += `| Metric | Value |\n`;
|
|
1094
|
+
output += `|--------|-------|\n`;
|
|
1095
|
+
output += `| Config files | ${report.configs.length} |\n`;
|
|
1096
|
+
output += `| Total servers | ${report.totalServers} |\n`;
|
|
1097
|
+
output += `| Healthy | ${report.healthyServers} |\n\n`;
|
|
1098
|
+
for (const config of report.configs) {
|
|
1099
|
+
output += `### ${config.configPath}\n`;
|
|
1100
|
+
for (const server of config.servers) {
|
|
1101
|
+
const hasErrors = config.issues.some(i => i.server === server.name && i.severity === "error");
|
|
1102
|
+
output += `- **${server.name}** \`${server.command}\` ${hasErrors ? "✗" : "✓"}\n`;
|
|
1103
|
+
}
|
|
1104
|
+
if (config.issues.length > 0) {
|
|
1105
|
+
output += `\n**Issues:**\n`;
|
|
1106
|
+
for (const issue of config.issues) {
|
|
1107
|
+
const icon = issue.severity === "error" ? "✗" : issue.severity === "warning" ? "⚠️" : "ℹ️";
|
|
1108
|
+
output += `- ${icon} [${issue.server}] ${issue.message}\n`;
|
|
1109
|
+
if (issue.fix)
|
|
1110
|
+
output += ` Fix: ${issue.fix}\n`;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
output += `\n`;
|
|
1114
|
+
}
|
|
1115
|
+
if (report.testResults) {
|
|
1116
|
+
output += `## Connectivity Tests\n`;
|
|
1117
|
+
output += `| Server | Status | Time |\n`;
|
|
1118
|
+
output += `|--------|--------|------|\n`;
|
|
1119
|
+
for (const t of report.testResults) {
|
|
1120
|
+
const status = t.reachable ? "✓ reachable" : "✗ unreachable";
|
|
1121
|
+
const time = t.startupTime ? `${t.startupTime}ms` : "-";
|
|
1122
|
+
output += `| ${t.server} | ${status} | ${time} |\n`;
|
|
1123
|
+
}
|
|
1124
|
+
output += `\n`;
|
|
1125
|
+
}
|
|
1126
|
+
if (report.duplicateServers.length > 0) {
|
|
1127
|
+
output += `## Duplicates\n`;
|
|
1128
|
+
for (const dup of report.duplicateServers) {
|
|
1129
|
+
output += `- **${dup.name}** found in ${dup.locations.length} configs\n`;
|
|
1130
|
+
}
|
|
1131
|
+
output += `\n`;
|
|
1132
|
+
}
|
|
1133
|
+
if (report.recommendations.length > 0) {
|
|
1134
|
+
output += `## Recommendations\n`;
|
|
1135
|
+
for (const rec of report.recommendations) {
|
|
1136
|
+
output += `- ${rec}\n`;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return { content: [{ type: "text", text: output }] };
|
|
1140
|
+
}
|
|
1141
|
+
case "list_sessions": {
|
|
1142
|
+
const project = typedArgs.project;
|
|
1143
|
+
const sessions = listSessions(undefined, { project });
|
|
1144
|
+
if (sessions.length === 0) {
|
|
1145
|
+
return { content: [{ type: "text", text: "No sessions found." }] };
|
|
1146
|
+
}
|
|
1147
|
+
let output = `📋 **Sessions** (${sessions.length})\n\n`;
|
|
1148
|
+
output += `| ID | Status | Messages | Size | Project |\n`;
|
|
1149
|
+
output += `|----|--------|----------|------|--------|\n`;
|
|
1150
|
+
for (const s of sessions.slice(0, 50)) {
|
|
1151
|
+
const shortId = s.id.slice(0, 8);
|
|
1152
|
+
const icon = s.status === "healthy" ? "✓" : s.status === "corrupted" ? "✗" : s.status === "empty" ? "○" : "?";
|
|
1153
|
+
const shortProject = s.project ? (s.project.length > 25 ? "..." + s.project.slice(-22) : s.project) : "-";
|
|
1154
|
+
output += `| ${shortId} | ${icon} ${s.status} | ${s.messageCount} | ${formatBytes(s.sizeBytes)} | ${shortProject} |\n`;
|
|
1155
|
+
}
|
|
1156
|
+
if (sessions.length > 50) {
|
|
1157
|
+
output += `\n... and ${sessions.length - 50} more sessions\n`;
|
|
1158
|
+
}
|
|
1159
|
+
const healthy = sessions.filter(s => s.status === "healthy").length;
|
|
1160
|
+
const corrupted = sessions.filter(s => s.status === "corrupted").length;
|
|
1161
|
+
const orphaned = sessions.filter(s => s.status === "orphaned").length;
|
|
1162
|
+
output += `\n**Summary:** ${healthy} healthy, ${corrupted} corrupted, ${orphaned} orphaned\n`;
|
|
1163
|
+
return { content: [{ type: "text", text: output }] };
|
|
1164
|
+
}
|
|
1165
|
+
case "recover_session": {
|
|
1166
|
+
const sessionId = typedArgs.session_id;
|
|
1167
|
+
const repair = typedArgs.repair === true;
|
|
1168
|
+
const extract = typedArgs.extract === true;
|
|
1169
|
+
const sessions = listSessions();
|
|
1170
|
+
const match = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
|
|
1171
|
+
if (!match) {
|
|
1172
|
+
return {
|
|
1173
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
1174
|
+
isError: true,
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
const diagnosis = diagnoseSession(match.filePath);
|
|
1178
|
+
let output = `🔍 **Session Recovery: ${match.id.slice(0, 8)}**\n\n`;
|
|
1179
|
+
output += `- Path: \`${match.filePath}\`\n`;
|
|
1180
|
+
output += `- Status: ${diagnosis.issues.length === 0 ? "✓ Healthy" : "✗ Issues found"}\n`;
|
|
1181
|
+
output += `- Total lines: ${diagnosis.totalLines}\n`;
|
|
1182
|
+
output += `- Valid lines: ${diagnosis.validLines}\n`;
|
|
1183
|
+
output += `- Corrupted lines: ${diagnosis.corruptedLines}\n\n`;
|
|
1184
|
+
if (diagnosis.issues.length > 0) {
|
|
1185
|
+
output += `**Issues:**\n`;
|
|
1186
|
+
for (const issue of diagnosis.issues.slice(0, 20)) {
|
|
1187
|
+
output += `- Line ${issue.line}: ${issue.type} - ${issue.message}\n`;
|
|
1188
|
+
}
|
|
1189
|
+
if (diagnosis.issues.length > 20) {
|
|
1190
|
+
output += `- ... and ${diagnosis.issues.length - 20} more\n`;
|
|
1191
|
+
}
|
|
1192
|
+
output += `\n`;
|
|
1193
|
+
}
|
|
1194
|
+
if (repair) {
|
|
1195
|
+
const repairResult = repairSession(match.filePath);
|
|
1196
|
+
output += `## Repair\n`;
|
|
1197
|
+
output += `- Lines removed: ${repairResult.linesRemoved}\n`;
|
|
1198
|
+
output += `- Lines fixed: ${repairResult.linesFixed}\n`;
|
|
1199
|
+
if (repairResult.backupPath) {
|
|
1200
|
+
output += `- Backup: \`${repairResult.backupPath}\`\n`;
|
|
1201
|
+
}
|
|
1202
|
+
output += `\n`;
|
|
1203
|
+
}
|
|
1204
|
+
if (extract) {
|
|
1205
|
+
const content = extractSessionContent(match.filePath);
|
|
1206
|
+
output += `## Extracted Content\n`;
|
|
1207
|
+
output += `- User messages: ${content.userMessages.length}\n`;
|
|
1208
|
+
output += `- Assistant messages: ${content.assistantMessages.length}\n`;
|
|
1209
|
+
output += `- File edits: ${content.fileEdits.length}\n`;
|
|
1210
|
+
output += `- Commands: ${content.commandsRun.length}\n\n`;
|
|
1211
|
+
if (content.userMessages.length > 0) {
|
|
1212
|
+
output += `**User Messages (first 5):**\n`;
|
|
1213
|
+
for (const msg of content.userMessages.slice(0, 5)) {
|
|
1214
|
+
const preview = msg.length > 100 ? msg.slice(0, 100) + "..." : msg;
|
|
1215
|
+
output += `- ${preview}\n`;
|
|
1216
|
+
}
|
|
1217
|
+
output += `\n`;
|
|
1218
|
+
}
|
|
1219
|
+
if (content.fileEdits.length > 0) {
|
|
1220
|
+
output += `**Files Modified:**\n`;
|
|
1221
|
+
for (const f of content.fileEdits.slice(0, 10)) {
|
|
1222
|
+
output += `- \`${f.path}\`\n`;
|
|
1223
|
+
}
|
|
1224
|
+
output += `\n`;
|
|
1225
|
+
}
|
|
1226
|
+
if (content.commandsRun.length > 0) {
|
|
1227
|
+
output += `**Commands Run:**\n`;
|
|
1228
|
+
for (const cmd of content.commandsRun.slice(0, 10)) {
|
|
1229
|
+
output += `- \`${cmd}\`\n`;
|
|
1230
|
+
}
|
|
1231
|
+
output += `\n`;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return { content: [{ type: "text", text: output }] };
|
|
1235
|
+
}
|
|
1236
|
+
case "security_scan": {
|
|
1237
|
+
const file = typedArgs.file;
|
|
1238
|
+
const result = scanForSecrets(undefined, { file });
|
|
1239
|
+
if (result.totalFindings === 0) {
|
|
1240
|
+
return {
|
|
1241
|
+
content: [{ type: "text", text: `✓ Scanned ${result.filesScanned} file(s). No secrets found.` }],
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
let output = `🔐 **Security Scan**\n\n`;
|
|
1245
|
+
output += `| Metric | Value |\n`;
|
|
1246
|
+
output += `|--------|-------|\n`;
|
|
1247
|
+
output += `| Files scanned | ${result.filesScanned} |\n`;
|
|
1248
|
+
output += `| Findings | ${result.totalFindings} |\n\n`;
|
|
1249
|
+
output += `## Findings by Type\n`;
|
|
1250
|
+
output += `| Type | Count |\n`;
|
|
1251
|
+
output += `|------|-------|\n`;
|
|
1252
|
+
for (const [type, count] of Object.entries(result.summary)) {
|
|
1253
|
+
output += `| ${type} | ${count} |\n`;
|
|
1254
|
+
}
|
|
1255
|
+
output += `\n`;
|
|
1256
|
+
output += `## Details\n`;
|
|
1257
|
+
for (const finding of result.findings.slice(0, 25)) {
|
|
1258
|
+
const icon = finding.severity === "critical" ? "🔴" : finding.severity === "high" ? "🟠" : "🟡";
|
|
1259
|
+
const shortFile = path.basename(finding.file);
|
|
1260
|
+
output += `- ${icon} **${finding.pattern}** [${finding.severity}]\n`;
|
|
1261
|
+
output += ` File: \`${shortFile}\` Line: ${finding.line}\n`;
|
|
1262
|
+
output += ` Preview: \`${finding.maskedPreview}\`\n`;
|
|
1263
|
+
}
|
|
1264
|
+
if (result.findings.length > 25) {
|
|
1265
|
+
output += `\n... and ${result.findings.length - 25} more findings\n`;
|
|
1266
|
+
}
|
|
1267
|
+
return { content: [{ type: "text", text: output }] };
|
|
1268
|
+
}
|
|
1269
|
+
case "audit_session": {
|
|
1270
|
+
const sessionId = typedArgs.session_id;
|
|
1271
|
+
const sessions = listSessions();
|
|
1272
|
+
const match = sessions.find(s => s.id === sessionId || s.id.startsWith(sessionId));
|
|
1273
|
+
if (!match) {
|
|
1274
|
+
return {
|
|
1275
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
1276
|
+
isError: true,
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
const audit = auditSession(match.filePath);
|
|
1280
|
+
let output = `📝 **Session Audit: ${match.id.slice(0, 8)}**\n\n`;
|
|
1281
|
+
output += `- Total actions: ${audit.actions.length}\n`;
|
|
1282
|
+
if (audit.duration)
|
|
1283
|
+
output += `- Duration: ${audit.duration} minutes\n`;
|
|
1284
|
+
output += `\n`;
|
|
1285
|
+
if (audit.filesRead.length > 0) {
|
|
1286
|
+
output += `## Files Read (${audit.filesRead.length})\n`;
|
|
1287
|
+
for (const f of audit.filesRead.slice(0, 20)) {
|
|
1288
|
+
output += `- \`${f}\`\n`;
|
|
1289
|
+
}
|
|
1290
|
+
if (audit.filesRead.length > 20)
|
|
1291
|
+
output += `- ... and ${audit.filesRead.length - 20} more\n`;
|
|
1292
|
+
output += `\n`;
|
|
1293
|
+
}
|
|
1294
|
+
if (audit.filesWritten.length > 0) {
|
|
1295
|
+
output += `## Files Written (${audit.filesWritten.length})\n`;
|
|
1296
|
+
for (const f of audit.filesWritten.slice(0, 20)) {
|
|
1297
|
+
output += `- \`${f}\`\n`;
|
|
1298
|
+
}
|
|
1299
|
+
if (audit.filesWritten.length > 20)
|
|
1300
|
+
output += `- ... and ${audit.filesWritten.length - 20} more\n`;
|
|
1301
|
+
output += `\n`;
|
|
1302
|
+
}
|
|
1303
|
+
if (audit.commandsRun.length > 0) {
|
|
1304
|
+
output += `## Commands (${audit.commandsRun.length})\n`;
|
|
1305
|
+
for (const cmd of audit.commandsRun.slice(0, 20)) {
|
|
1306
|
+
output += `- \`${cmd}\`\n`;
|
|
1307
|
+
}
|
|
1308
|
+
if (audit.commandsRun.length > 20)
|
|
1309
|
+
output += `- ... and ${audit.commandsRun.length - 20} more\n`;
|
|
1310
|
+
output += `\n`;
|
|
1311
|
+
}
|
|
1312
|
+
if (audit.mcpToolsUsed.length > 0) {
|
|
1313
|
+
output += `## MCP Tools (${audit.mcpToolsUsed.length})\n`;
|
|
1314
|
+
for (const tool of audit.mcpToolsUsed) {
|
|
1315
|
+
output += `- \`${tool}\`\n`;
|
|
1316
|
+
}
|
|
1317
|
+
output += `\n`;
|
|
1318
|
+
}
|
|
1319
|
+
if (audit.urlsFetched.length > 0) {
|
|
1320
|
+
output += `## URLs Fetched (${audit.urlsFetched.length})\n`;
|
|
1321
|
+
for (const url of audit.urlsFetched.slice(0, 10)) {
|
|
1322
|
+
output += `- ${url}\n`;
|
|
1323
|
+
}
|
|
1324
|
+
output += `\n`;
|
|
1325
|
+
}
|
|
1326
|
+
return { content: [{ type: "text", text: output }] };
|
|
1327
|
+
}
|
|
1328
|
+
case "enforce_retention": {
|
|
1329
|
+
const days = typedArgs.days || 30;
|
|
1330
|
+
const dryRun = typedArgs.dry_run !== false;
|
|
1331
|
+
const result = enforceRetention(undefined, { days, dryRun });
|
|
1332
|
+
let output = `🗑️ **Retention ${dryRun ? "Preview" : "Enforced"}**\n\n`;
|
|
1333
|
+
output += `| Metric | Value |\n`;
|
|
1334
|
+
output += `|--------|-------|\n`;
|
|
1335
|
+
output += `| Sessions ${dryRun ? "eligible" : "deleted"} | ${result.sessionsDeleted} |\n`;
|
|
1336
|
+
output += `| Space ${dryRun ? "freeable" : "freed"} | ${formatBytes(result.spaceFreed)} |\n`;
|
|
1337
|
+
output += `| Threshold | ${days} days |\n\n`;
|
|
1338
|
+
if (result.sessionsDeleted === 0) {
|
|
1339
|
+
output += `✓ No sessions older than ${days} days.\n`;
|
|
1340
|
+
}
|
|
1341
|
+
else if (dryRun) {
|
|
1342
|
+
output += `ℹ️ Set \`dry_run: false\` to actually delete.\n`;
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
output += `✓ ${result.sessionsDeleted} session(s) deleted.\n`;
|
|
1346
|
+
}
|
|
1347
|
+
if (result.errors.length > 0) {
|
|
1348
|
+
output += `\n⚠️ Errors:\n`;
|
|
1349
|
+
for (const err of result.errors) {
|
|
1350
|
+
output += `- ${err}\n`;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return { content: [{ type: "text", text: output }] };
|
|
1354
|
+
}
|
|
1355
|
+
case "inventory_traces": {
|
|
1356
|
+
const project = typedArgs.project;
|
|
1357
|
+
const inventory = inventoryTraces(undefined, { project });
|
|
1358
|
+
let output = `🔎 **Trace Inventory**\n\n`;
|
|
1359
|
+
output += `| Metric | Value |\n`;
|
|
1360
|
+
output += `|--------|-------|\n`;
|
|
1361
|
+
output += `| Total files | ${inventory.totalFiles} |\n`;
|
|
1362
|
+
output += `| Total size | ${formatBytes(inventory.totalSize)} |\n`;
|
|
1363
|
+
output += `| Critical items | ${inventory.criticalItems} |\n`;
|
|
1364
|
+
output += `| High items | ${inventory.highItems} |\n\n`;
|
|
1365
|
+
output += `## Categories\n`;
|
|
1366
|
+
output += `| Category | Sensitivity | Files | Size |\n`;
|
|
1367
|
+
output += `|----------|-------------|-------|------|\n`;
|
|
1368
|
+
for (const cat of inventory.categories) {
|
|
1369
|
+
if (cat.fileCount === 0)
|
|
1370
|
+
continue;
|
|
1371
|
+
const icon = cat.sensitivity === "critical" ? "🔴" : cat.sensitivity === "high" ? "🟠" : cat.sensitivity === "medium" ? "🟡" : "🟢";
|
|
1372
|
+
output += `| ${icon} ${cat.name} | ${cat.sensitivity} | ${cat.fileCount} | ${formatBytes(cat.totalSize)} |\n`;
|
|
1373
|
+
}
|
|
1374
|
+
output += `\n`;
|
|
1375
|
+
if (inventory.criticalItems > 0) {
|
|
1376
|
+
output += `⚠️ ${inventory.criticalItems} critical trace items found. Consider running \`clean_traces\` or \`wipe_traces\`.\n`;
|
|
1377
|
+
}
|
|
1378
|
+
return { content: [{ type: "text", text: output }] };
|
|
1379
|
+
}
|
|
1380
|
+
case "clean_traces": {
|
|
1381
|
+
const categories = typedArgs.categories;
|
|
1382
|
+
const days = typedArgs.days;
|
|
1383
|
+
const project = typedArgs.project;
|
|
1384
|
+
const dryRun = typedArgs.dry_run !== false;
|
|
1385
|
+
const result = cleanTraces(undefined, {
|
|
1386
|
+
categories: categories ? categories.split(",").map(c => c.trim()) : undefined,
|
|
1387
|
+
days,
|
|
1388
|
+
project,
|
|
1389
|
+
dryRun,
|
|
1390
|
+
});
|
|
1391
|
+
let output = `🧹 **Trace ${dryRun ? "Cleanup Preview" : "Cleanup Complete"}**\n\n`;
|
|
1392
|
+
output += `| Metric | Value |\n`;
|
|
1393
|
+
output += `|--------|-------|\n`;
|
|
1394
|
+
output += `| Files ${dryRun ? "to delete" : "deleted"} | ${result.deleted.length} |\n`;
|
|
1395
|
+
output += `| Space ${dryRun ? "freeable" : "freed"} | ${formatBytes(result.freed)} |\n`;
|
|
1396
|
+
output += `| Categories | ${result.categoriesAffected.join(", ") || "none"} |\n\n`;
|
|
1397
|
+
if (result.deleted.length === 0) {
|
|
1398
|
+
output += `✓ Nothing to clean.\n`;
|
|
1399
|
+
}
|
|
1400
|
+
else if (dryRun) {
|
|
1401
|
+
output += `ℹ️ Set \`dry_run: false\` to actually delete.\n`;
|
|
1402
|
+
}
|
|
1403
|
+
else {
|
|
1404
|
+
output += `✓ Cleanup complete.\n`;
|
|
1405
|
+
}
|
|
1406
|
+
if (result.errors.length > 0) {
|
|
1407
|
+
output += `\n⚠️ Errors:\n`;
|
|
1408
|
+
for (const err of result.errors) {
|
|
1409
|
+
output += `- ${err}\n`;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return { content: [{ type: "text", text: output }] };
|
|
1413
|
+
}
|
|
1414
|
+
case "wipe_traces": {
|
|
1415
|
+
const confirm = typedArgs.confirm === true;
|
|
1416
|
+
const keepSettings = typedArgs.keep_settings === true;
|
|
1417
|
+
if (!confirm) {
|
|
1418
|
+
return {
|
|
1419
|
+
content: [{ type: "text", text: "⚠️ **Wipe requires explicit confirmation.** Set `confirm: true` to securely wipe all Claude Code traces. This action is irreversible." }],
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
const result = wipeAllTraces(undefined, {
|
|
1423
|
+
confirm: true,
|
|
1424
|
+
keepSettings,
|
|
1425
|
+
});
|
|
1426
|
+
let output = `💀 **Trace Wipe Complete**\n\n`;
|
|
1427
|
+
output += `| Metric | Value |\n`;
|
|
1428
|
+
output += `|--------|-------|\n`;
|
|
1429
|
+
output += `| Files wiped | ${result.filesWiped} |\n`;
|
|
1430
|
+
output += `| Space freed | ${formatBytes(result.bytesFreed)} |\n`;
|
|
1431
|
+
output += `| Categories | ${result.categoriesWiped.join(", ")} |\n\n`;
|
|
1432
|
+
if (result.preserved.length > 0) {
|
|
1433
|
+
output += `**Preserved:** ${result.preserved.join(", ")}\n\n`;
|
|
1434
|
+
}
|
|
1435
|
+
output += `✓ All traces securely wiped.\n`;
|
|
1436
|
+
return { content: [{ type: "text", text: output }] };
|
|
1437
|
+
}
|
|
1438
|
+
case "generate_trace_guard": {
|
|
1439
|
+
const mode = typedArgs.mode || "moderate";
|
|
1440
|
+
const config = generateTraceGuardHooks({ mode });
|
|
1441
|
+
let output = `🛡️ **Trace Guard Configuration (${mode} mode)**\n\n`;
|
|
1442
|
+
output += `## Hooks\n`;
|
|
1443
|
+
for (const hook of config.hooks) {
|
|
1444
|
+
output += `### ${hook.event}\n`;
|
|
1445
|
+
output += `- ${hook.description}\n`;
|
|
1446
|
+
output += `- Command: \`${hook.command}\`\n`;
|
|
1447
|
+
if (hook.matcher)
|
|
1448
|
+
output += `- Matcher: \`${hook.matcher}\`\n`;
|
|
1449
|
+
output += `\n`;
|
|
1450
|
+
}
|
|
1451
|
+
output += `## Installation\n`;
|
|
1452
|
+
output += `Add this to your \`~/.claude/settings.json\`:\n\n`;
|
|
1453
|
+
output += `\`\`\`json\n${config.settingsJson}\n\`\`\`\n\n`;
|
|
1454
|
+
output += config.instructions + `\n`;
|
|
1455
|
+
return { content: [{ type: "text", text: output }] };
|
|
1456
|
+
}
|
|
1457
|
+
case "start_dashboard": {
|
|
1458
|
+
const port = typedArgs.port || 1405;
|
|
1459
|
+
await startDashboard({ port, open: false });
|
|
1460
|
+
const url = `http://localhost:${port}`;
|
|
1461
|
+
return {
|
|
1462
|
+
content: [{ type: "text", text: `Dashboard started at ${url}\n\nOpen this URL in your browser to view the dashboard.` }],
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
913
1465
|
default:
|
|
914
1466
|
throw new Error(`Unknown tool: ${name}`);
|
|
915
1467
|
}
|
|
@@ -924,7 +1476,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
924
1476
|
async function main() {
|
|
925
1477
|
const transport = new StdioServerTransport();
|
|
926
1478
|
await server.connect(transport);
|
|
927
|
-
console.error("Claude Code Toolkit MCP Server v1.0
|
|
1479
|
+
console.error("Claude Code Toolkit MCP Server v1.2.0 running on stdio");
|
|
928
1480
|
}
|
|
929
1481
|
main().catch(console.error);
|
|
930
1482
|
//# sourceMappingURL=index.js.map
|