@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.
Files changed (69) hide show
  1. package/README.md +165 -214
  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__/scanner.test.js +350 -1
  12. package/dist/__tests__/scanner.test.js.map +1 -1
  13. package/dist/__tests__/security.test.d.ts +2 -0
  14. package/dist/__tests__/security.test.d.ts.map +1 -0
  15. package/dist/__tests__/security.test.js +375 -0
  16. package/dist/__tests__/security.test.js.map +1 -0
  17. package/dist/__tests__/session-recovery.test.d.ts +2 -0
  18. package/dist/__tests__/session-recovery.test.d.ts.map +1 -0
  19. package/dist/__tests__/session-recovery.test.js +230 -0
  20. package/dist/__tests__/session-recovery.test.js.map +1 -0
  21. package/dist/__tests__/storage.test.d.ts +2 -0
  22. package/dist/__tests__/storage.test.d.ts.map +1 -0
  23. package/dist/__tests__/storage.test.js +241 -0
  24. package/dist/__tests__/storage.test.js.map +1 -0
  25. package/dist/__tests__/trace.test.d.ts +2 -0
  26. package/dist/__tests__/trace.test.d.ts.map +1 -0
  27. package/dist/__tests__/trace.test.js +376 -0
  28. package/dist/__tests__/trace.test.js.map +1 -0
  29. package/dist/cli.js +501 -20
  30. package/dist/cli.js.map +1 -1
  31. package/dist/index.js +950 -3
  32. package/dist/index.js.map +1 -1
  33. package/dist/lib/dashboard-ui.d.ts +2 -0
  34. package/dist/lib/dashboard-ui.d.ts.map +1 -0
  35. package/dist/lib/dashboard-ui.js +2075 -0
  36. package/dist/lib/dashboard-ui.js.map +1 -0
  37. package/dist/lib/dashboard.d.ts +15 -0
  38. package/dist/lib/dashboard.d.ts.map +1 -0
  39. package/dist/lib/dashboard.js +1422 -0
  40. package/dist/lib/dashboard.js.map +1 -0
  41. package/dist/lib/logs.d.ts +42 -0
  42. package/dist/lib/logs.d.ts.map +1 -0
  43. package/dist/lib/logs.js +166 -0
  44. package/dist/lib/logs.js.map +1 -0
  45. package/dist/lib/mcp-validator.d.ts +86 -0
  46. package/dist/lib/mcp-validator.d.ts.map +1 -0
  47. package/dist/lib/mcp-validator.js +463 -0
  48. package/dist/lib/mcp-validator.js.map +1 -0
  49. package/dist/lib/scanner.d.ts +187 -2
  50. package/dist/lib/scanner.d.ts.map +1 -1
  51. package/dist/lib/scanner.js +1224 -14
  52. package/dist/lib/scanner.js.map +1 -1
  53. package/dist/lib/security.d.ts +57 -0
  54. package/dist/lib/security.d.ts.map +1 -0
  55. package/dist/lib/security.js +423 -0
  56. package/dist/lib/security.js.map +1 -0
  57. package/dist/lib/session-recovery.d.ts +60 -0
  58. package/dist/lib/session-recovery.d.ts.map +1 -0
  59. package/dist/lib/session-recovery.js +433 -0
  60. package/dist/lib/session-recovery.js.map +1 -0
  61. package/dist/lib/storage.d.ts +68 -0
  62. package/dist/lib/storage.d.ts.map +1 -0
  63. package/dist/lib/storage.js +500 -0
  64. package/dist/lib/storage.js.map +1 -0
  65. package/dist/lib/trace.d.ts +119 -0
  66. package/dist/lib/trace.d.ts.map +1 -0
  67. package/dist/lib/trace.js +649 -0
  68. package/dist/lib/trace.js.map +1 -0
  69. package/package.json +11 -3
@@ -0,0 +1,375 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import { scanForSecrets, auditSession, enforceRetention, formatSecretsScanReport, formatAuditReport, formatRetentionReport, } from "../lib/security.js";
6
+ const TEST_DIR = path.join(os.tmpdir(), "cct-security-test");
7
+ function createFile(content, ...parts) {
8
+ const filePath = path.join(TEST_DIR, ...parts);
9
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
10
+ fs.writeFileSync(filePath, content, "utf-8");
11
+ return filePath;
12
+ }
13
+ function createJsonlFile(lines, ...parts) {
14
+ const content = lines.map(l => JSON.stringify(l)).join("\n");
15
+ return createFile(content, ...parts);
16
+ }
17
+ function setFileAge(filePath, days) {
18
+ const mtime = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
19
+ fs.utimesSync(filePath, mtime, mtime);
20
+ }
21
+ describe("Security Module", () => {
22
+ beforeEach(() => {
23
+ if (fs.existsSync(TEST_DIR)) {
24
+ fs.rmSync(TEST_DIR, { recursive: true });
25
+ }
26
+ fs.mkdirSync(TEST_DIR, { recursive: true });
27
+ });
28
+ afterEach(() => {
29
+ if (fs.existsSync(TEST_DIR)) {
30
+ fs.rmSync(TEST_DIR, { recursive: true });
31
+ }
32
+ });
33
+ describe("scanForSecrets", () => {
34
+ it("should detect AWS access keys", () => {
35
+ const filePath = createJsonlFile([{
36
+ type: "assistant",
37
+ message: {
38
+ role: "assistant",
39
+ content: [{ type: "text", text: "Your key is AKIAIOSFODNN7EXAMPLE" }],
40
+ },
41
+ }], "aws.jsonl");
42
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
43
+ expect(result.totalFindings).toBeGreaterThan(0);
44
+ expect(result.findings[0].type).toBe("aws_key");
45
+ expect(result.findings[0].severity).toBe("critical");
46
+ });
47
+ it("should detect GitHub tokens", () => {
48
+ const filePath = createJsonlFile([{
49
+ type: "assistant",
50
+ message: {
51
+ role: "assistant",
52
+ content: [{ type: "text", text: "Token: ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij" }],
53
+ },
54
+ }], "github.jsonl");
55
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
56
+ expect(result.totalFindings).toBeGreaterThan(0);
57
+ expect(result.findings[0].type).toBe("api_token");
58
+ });
59
+ it("should detect private keys", () => {
60
+ const filePath = createJsonlFile([{
61
+ type: "user",
62
+ message: {
63
+ role: "user",
64
+ content: [{ type: "text", text: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpA..." }],
65
+ },
66
+ }], "privkey.jsonl");
67
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
68
+ expect(result.totalFindings).toBeGreaterThan(0);
69
+ expect(result.findings[0].type).toBe("private_key");
70
+ });
71
+ it("should detect connection strings", () => {
72
+ const filePath = createJsonlFile([{
73
+ type: "assistant",
74
+ message: {
75
+ role: "assistant",
76
+ content: [{ type: "text", text: "mongodb://user:pass@host:27017/db" }],
77
+ },
78
+ }], "connstr.jsonl");
79
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
80
+ expect(result.totalFindings).toBeGreaterThan(0);
81
+ expect(result.findings[0].type).toBe("connection_string");
82
+ });
83
+ it("should detect JWT tokens", () => {
84
+ const filePath = createJsonlFile([{
85
+ type: "assistant",
86
+ message: {
87
+ role: "assistant",
88
+ content: [{ type: "text", text: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" }],
89
+ },
90
+ }], "jwt.jsonl");
91
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
92
+ expect(result.totalFindings).toBeGreaterThan(0);
93
+ expect(result.findings[0].type).toBe("jwt");
94
+ });
95
+ it("should detect passwords in config", () => {
96
+ const filePath = createJsonlFile([{
97
+ type: "user",
98
+ message: {
99
+ role: "user",
100
+ content: [{ type: "text", text: 'password = "SuperSecret123!"' }],
101
+ },
102
+ }], "password.jsonl");
103
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
104
+ expect(result.totalFindings).toBeGreaterThan(0);
105
+ expect(result.findings[0].type).toBe("password");
106
+ });
107
+ it("should detect sk- API keys", () => {
108
+ const filePath = createJsonlFile([{
109
+ type: "assistant",
110
+ message: {
111
+ role: "assistant",
112
+ content: [{ type: "text", text: "API key: sk-abcdefghijklmnopqrstuvwx" }],
113
+ },
114
+ }], "apikey.jsonl");
115
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
116
+ expect(result.totalFindings).toBeGreaterThan(0);
117
+ });
118
+ it("should return empty for clean files", () => {
119
+ const filePath = createJsonlFile([{
120
+ type: "user",
121
+ message: {
122
+ role: "user",
123
+ content: [{ type: "text", text: "Just normal text, nothing secret" }],
124
+ },
125
+ }], "clean.jsonl");
126
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
127
+ expect(result.totalFindings).toBe(0);
128
+ });
129
+ it("should mask secret previews", () => {
130
+ const filePath = createJsonlFile([{
131
+ type: "assistant",
132
+ message: {
133
+ role: "assistant",
134
+ content: [{ type: "text", text: "AKIAIOSFODNN7EXAMPLE" }],
135
+ },
136
+ }], "masked.jsonl");
137
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
138
+ expect(result.findings[0].maskedPreview).toContain("****");
139
+ expect(result.findings[0].maskedPreview).not.toBe("AKIAIOSFODNN7EXAMPLE");
140
+ });
141
+ it("should scan tool_use inputs for secrets", () => {
142
+ const filePath = createJsonlFile([{
143
+ type: "assistant",
144
+ message: {
145
+ role: "assistant",
146
+ content: [{
147
+ type: "tool_use",
148
+ name: "Bash",
149
+ input: { command: "export AWS_SECRET_ACCESS_KEY='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'" },
150
+ }],
151
+ },
152
+ }], "tool-secret.jsonl");
153
+ const result = scanForSecrets(TEST_DIR, { file: filePath });
154
+ expect(result.totalFindings).toBeGreaterThan(0);
155
+ });
156
+ it("should scan all files when no specific file given", () => {
157
+ createJsonlFile([{
158
+ type: "user",
159
+ message: { role: "user", content: [{ type: "text", text: "AKIAIOSFODNN7EXAMPLE" }] },
160
+ }], "proj1", "session.jsonl");
161
+ createJsonlFile([{
162
+ type: "user",
163
+ message: { role: "user", content: [{ type: "text", text: "clean text" }] },
164
+ }], "proj2", "session.jsonl");
165
+ const result = scanForSecrets(TEST_DIR);
166
+ expect(result.filesScanned).toBe(2);
167
+ expect(result.totalFindings).toBe(1);
168
+ });
169
+ });
170
+ describe("auditSession", () => {
171
+ it("should extract file reads", () => {
172
+ const filePath = createJsonlFile([{
173
+ type: "assistant",
174
+ timestamp: "2026-01-01T10:00:00Z",
175
+ message: {
176
+ role: "assistant",
177
+ content: [{
178
+ type: "tool_use",
179
+ name: "Read",
180
+ input: { file_path: "/src/app.ts" },
181
+ }],
182
+ },
183
+ }], "reads.jsonl");
184
+ const audit = auditSession(filePath);
185
+ expect(audit.filesRead).toContain("/src/app.ts");
186
+ expect(audit.actions.length).toBe(1);
187
+ expect(audit.actions[0].type).toBe("file_read");
188
+ });
189
+ it("should extract file writes and edits", () => {
190
+ const filePath = createJsonlFile([
191
+ {
192
+ type: "assistant",
193
+ timestamp: "2026-01-01T10:00:00Z",
194
+ message: {
195
+ role: "assistant",
196
+ content: [{
197
+ type: "tool_use",
198
+ name: "Write",
199
+ input: { file_path: "/src/new.ts", content: "code" },
200
+ }],
201
+ },
202
+ },
203
+ {
204
+ type: "assistant",
205
+ timestamp: "2026-01-01T10:01:00Z",
206
+ message: {
207
+ role: "assistant",
208
+ content: [{
209
+ type: "tool_use",
210
+ name: "Edit",
211
+ input: { file_path: "/src/existing.ts", old_string: "a", new_string: "b" },
212
+ }],
213
+ },
214
+ },
215
+ ], "writes.jsonl");
216
+ const audit = auditSession(filePath);
217
+ expect(audit.filesWritten).toContain("/src/new.ts");
218
+ expect(audit.filesWritten).toContain("/src/existing.ts");
219
+ });
220
+ it("should extract bash commands", () => {
221
+ const filePath = createJsonlFile([{
222
+ type: "assistant",
223
+ timestamp: "2026-01-01T10:00:00Z",
224
+ message: {
225
+ role: "assistant",
226
+ content: [{
227
+ type: "tool_use",
228
+ name: "Bash",
229
+ input: { command: "npm test" },
230
+ }],
231
+ },
232
+ }], "commands.jsonl");
233
+ const audit = auditSession(filePath);
234
+ expect(audit.commandsRun).toContain("npm test");
235
+ });
236
+ it("should extract MCP tool calls", () => {
237
+ const filePath = createJsonlFile([{
238
+ type: "assistant",
239
+ timestamp: "2026-01-01T10:00:00Z",
240
+ message: {
241
+ role: "assistant",
242
+ content: [{
243
+ type: "tool_use",
244
+ name: "mcp__github__create_issue",
245
+ input: { title: "test" },
246
+ }],
247
+ },
248
+ }], "mcp.jsonl");
249
+ const audit = auditSession(filePath);
250
+ expect(audit.mcpToolsUsed).toContain("mcp__github__create_issue");
251
+ });
252
+ it("should extract web fetch URLs", () => {
253
+ const filePath = createJsonlFile([{
254
+ type: "assistant",
255
+ timestamp: "2026-01-01T10:00:00Z",
256
+ message: {
257
+ role: "assistant",
258
+ content: [{
259
+ type: "tool_use",
260
+ name: "WebFetch",
261
+ input: { url: "https://example.com/api" },
262
+ }],
263
+ },
264
+ }], "webfetch.jsonl");
265
+ const audit = auditSession(filePath);
266
+ expect(audit.urlsFetched).toContain("https://example.com/api");
267
+ });
268
+ it("should calculate duration from timestamps", () => {
269
+ const filePath = createJsonlFile([
270
+ {
271
+ type: "user",
272
+ timestamp: "2026-01-01T10:00:00Z",
273
+ message: { role: "user", content: [{ type: "text", text: "start" }] },
274
+ },
275
+ {
276
+ type: "assistant",
277
+ timestamp: "2026-01-01T10:30:00Z",
278
+ message: { role: "assistant", content: [{ type: "text", text: "end" }] },
279
+ },
280
+ ], "duration.jsonl");
281
+ const audit = auditSession(filePath);
282
+ expect(audit.duration).toBe(30);
283
+ });
284
+ });
285
+ describe("enforceRetention", () => {
286
+ it("should identify old sessions for deletion", () => {
287
+ const f = createJsonlFile([{ type: "user", message: { role: "user", content: [] } }], "old-project", "old.jsonl");
288
+ setFileAge(f, 60);
289
+ const result = enforceRetention(TEST_DIR, { days: 30, dryRun: true });
290
+ expect(result.sessionsDeleted).toBe(1);
291
+ expect(result.dryRun).toBe(true);
292
+ expect(fs.existsSync(f)).toBe(true);
293
+ });
294
+ it("should delete old sessions when not dry run", () => {
295
+ const f = createJsonlFile([{ type: "user", message: { role: "user", content: [] } }], "old-project", "old.jsonl");
296
+ setFileAge(f, 60);
297
+ const result = enforceRetention(TEST_DIR, { days: 30, dryRun: false });
298
+ expect(result.sessionsDeleted).toBe(1);
299
+ expect(fs.existsSync(f)).toBe(false);
300
+ });
301
+ it("should not delete recent sessions", () => {
302
+ createJsonlFile([{ type: "user", message: { role: "user", content: [] } }], "new-project", "new.jsonl");
303
+ const result = enforceRetention(TEST_DIR, { days: 30, dryRun: true });
304
+ expect(result.sessionsDeleted).toBe(0);
305
+ });
306
+ });
307
+ describe("formatSecretsScanReport", () => {
308
+ it("should format clean scan", () => {
309
+ const result = {
310
+ filesScanned: 5,
311
+ totalFindings: 0,
312
+ findings: [],
313
+ summary: {},
314
+ scannedAt: new Date(),
315
+ };
316
+ const report = formatSecretsScanReport(result);
317
+ expect(report).toContain("SECRETS SCAN");
318
+ expect(report).toContain("No secrets found");
319
+ });
320
+ it("should format scan with findings", () => {
321
+ const result = {
322
+ filesScanned: 1,
323
+ totalFindings: 1,
324
+ findings: [{
325
+ file: "/tmp/test.jsonl",
326
+ line: 1,
327
+ type: "aws_key",
328
+ pattern: "AWS Access Key ID",
329
+ maskedPreview: "AKIA****",
330
+ severity: "critical",
331
+ }],
332
+ summary: { aws_key: 1 },
333
+ scannedAt: new Date(),
334
+ };
335
+ const report = formatSecretsScanReport(result);
336
+ expect(report).toContain("AWS Access Key ID");
337
+ expect(report).toContain("critical");
338
+ });
339
+ });
340
+ describe("formatAuditReport", () => {
341
+ it("should format an audit report", () => {
342
+ const filePath = createJsonlFile([{
343
+ type: "assistant",
344
+ timestamp: "2026-01-01T10:00:00Z",
345
+ message: {
346
+ role: "assistant",
347
+ content: [{
348
+ type: "tool_use",
349
+ name: "Bash",
350
+ input: { command: "npm test" },
351
+ }],
352
+ },
353
+ }], "audit.jsonl");
354
+ const audit = auditSession(filePath);
355
+ const report = formatAuditReport(audit);
356
+ expect(report).toContain("SESSION AUDIT");
357
+ expect(report).toContain("npm test");
358
+ });
359
+ });
360
+ describe("formatRetentionReport", () => {
361
+ it("should format dry run report", () => {
362
+ const result = {
363
+ sessionsDeleted: 5,
364
+ sessionsExported: 0,
365
+ spaceFreed: 1024 * 1024,
366
+ errors: [],
367
+ dryRun: true,
368
+ };
369
+ const report = formatRetentionReport(result);
370
+ expect(report).toContain("DRY RUN");
371
+ expect(report).toContain("5");
372
+ });
373
+ });
374
+ });
375
+ //# sourceMappingURL=security.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.test.js","sourceRoot":"","sources":["../../src/__tests__/security.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session-recovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-recovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/session-recovery.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,230 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import { listSessions, diagnoseSession, repairSession, extractSessionContent, formatSessionReport, formatSessionDiagnosticReport, } from "../lib/session-recovery.js";
6
+ const TEST_DIR = path.join(os.tmpdir(), "cct-session-recovery-test");
7
+ function createFile(content, ...parts) {
8
+ const filePath = path.join(TEST_DIR, ...parts);
9
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
10
+ fs.writeFileSync(filePath, content, "utf-8");
11
+ return filePath;
12
+ }
13
+ function createJsonlFile(lines, ...parts) {
14
+ const content = lines.map(l => JSON.stringify(l)).join("\n");
15
+ return createFile(content, ...parts);
16
+ }
17
+ describe("Session Recovery Module", () => {
18
+ beforeEach(() => {
19
+ if (fs.existsSync(TEST_DIR)) {
20
+ fs.rmSync(TEST_DIR, { recursive: true });
21
+ }
22
+ fs.mkdirSync(TEST_DIR, { recursive: true });
23
+ });
24
+ afterEach(() => {
25
+ if (fs.existsSync(TEST_DIR)) {
26
+ fs.rmSync(TEST_DIR, { recursive: true });
27
+ }
28
+ });
29
+ describe("listSessions", () => {
30
+ it("should list sessions from sessions-index.json", () => {
31
+ const sessionId = "abc-123-def";
32
+ createJsonlFile([
33
+ { type: "user", message: { role: "user", content: [{ type: "text", text: "hello" }] } },
34
+ { type: "assistant", message: { role: "assistant", content: [{ type: "text", text: "hi" }] } },
35
+ ], "test-project", `${sessionId}.jsonl`);
36
+ createFile(JSON.stringify({
37
+ version: 1,
38
+ entries: [{ sessionId, messageCount: 2, created: "2026-01-01T00:00:00Z", modified: "2026-01-01T01:00:00Z" }],
39
+ originalPath: "/test/project",
40
+ }), "test-project", "sessions-index.json");
41
+ const sessions = listSessions(TEST_DIR);
42
+ expect(sessions.length).toBe(1);
43
+ expect(sessions[0].id).toBe(sessionId);
44
+ expect(sessions[0].status).toBe("healthy");
45
+ });
46
+ it("should detect orphaned sessions", () => {
47
+ createJsonlFile([{ type: "user", message: { role: "user", content: [{ type: "text", text: "test" }] } }], "test-project", "orphan-session.jsonl");
48
+ createFile(JSON.stringify({ version: 1, entries: [] }), "test-project", "sessions-index.json");
49
+ const sessions = listSessions(TEST_DIR);
50
+ expect(sessions.length).toBe(1);
51
+ expect(sessions[0].status).toBe("orphaned");
52
+ });
53
+ it("should detect empty sessions", () => {
54
+ createFile("", "test-project", "empty-session.jsonl");
55
+ createFile(JSON.stringify({ version: 1, entries: [] }), "test-project", "sessions-index.json");
56
+ const sessions = listSessions(TEST_DIR);
57
+ expect(sessions.length).toBe(1);
58
+ expect(sessions[0].status).toBe("empty");
59
+ });
60
+ it("should detect corrupted sessions", () => {
61
+ createFile("valid json\nnot valid {{{", "test-project", "corrupt-session.jsonl");
62
+ const sessions = listSessions(TEST_DIR);
63
+ const corrupt = sessions.find(s => s.id === "corrupt-session");
64
+ expect(corrupt).toBeDefined();
65
+ expect(corrupt.status).toBe("corrupted");
66
+ });
67
+ it("should return empty for non-existent directory", () => {
68
+ const sessions = listSessions(path.join(TEST_DIR, "nonexistent"));
69
+ expect(sessions).toEqual([]);
70
+ });
71
+ it("should count subagents", () => {
72
+ const sessionId = "session-with-agents";
73
+ createJsonlFile([{ type: "user", message: { role: "user", content: [{ type: "text", text: "test" }] } }], "test-project", `${sessionId}.jsonl`);
74
+ createFile("agent data", "test-project", sessionId, "subagents", "agent-1.jsonl");
75
+ createFile("agent data", "test-project", sessionId, "subagents", "agent-2.jsonl");
76
+ createFile(JSON.stringify({
77
+ version: 1,
78
+ entries: [{ sessionId, messageCount: 1 }],
79
+ }), "test-project", "sessions-index.json");
80
+ const sessions = listSessions(TEST_DIR);
81
+ expect(sessions[0].subagentCount).toBe(2);
82
+ });
83
+ });
84
+ describe("diagnoseSession", () => {
85
+ it("should diagnose a healthy session", () => {
86
+ const filePath = createJsonlFile([
87
+ { type: "user", message: { role: "user", content: [{ type: "text", text: "hello" }] } },
88
+ { type: "assistant", message: { role: "assistant", content: [{ type: "text", text: "hi" }] } },
89
+ ], "healthy.jsonl");
90
+ const diag = diagnoseSession(filePath);
91
+ expect(diag.validLines).toBe(2);
92
+ expect(diag.corruptedLines).toBe(0);
93
+ expect(diag.estimatedRecovery).toBe(100);
94
+ expect(diag.issues.length).toBe(0);
95
+ });
96
+ it("should detect truncated lines", () => {
97
+ const filePath = createFile('{"type":"user","message":{"role":"user","content":[{"type":"text","text":"hello"}]}}\n{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"hi', "truncated.jsonl");
98
+ const diag = diagnoseSession(filePath);
99
+ expect(diag.truncatedLines).toBe(1);
100
+ expect(diag.issues.some(i => i.type === "truncated")).toBe(true);
101
+ });
102
+ it("should detect invalid JSON", () => {
103
+ const filePath = createFile('{"valid":"json"}\nnot json at all}', "invalid.jsonl");
104
+ const diag = diagnoseSession(filePath);
105
+ expect(diag.corruptedLines).toBe(1);
106
+ });
107
+ it("should detect missing content", () => {
108
+ const filePath = createJsonlFile([{ type: "user", message: { role: "user" } }], "no-content.jsonl");
109
+ const diag = diagnoseSession(filePath);
110
+ expect(diag.issues.some(i => i.type === "missing_content")).toBe(true);
111
+ });
112
+ it("should detect consecutive same-role messages", () => {
113
+ const filePath = createJsonlFile([
114
+ { type: "user", message: { role: "user", content: [{ type: "text", text: "msg1" }] } },
115
+ { type: "user", message: { role: "user", content: [{ type: "text", text: "msg2" }] } },
116
+ ], "sequence.jsonl");
117
+ const diag = diagnoseSession(filePath);
118
+ expect(diag.issues.some(i => i.type === "sequence_error")).toBe(true);
119
+ });
120
+ it("should calculate recovery percentage", () => {
121
+ const filePath = createFile('{"type":"user","message":{"role":"user","content":[]}}\n{"type":"user","message":{"role":"user","content":[]}}\ninvalid\ninvalid', "mixed.jsonl");
122
+ const diag = diagnoseSession(filePath);
123
+ expect(diag.estimatedRecovery).toBe(50);
124
+ });
125
+ it("should handle unreadable file", () => {
126
+ const diag = diagnoseSession(path.join(TEST_DIR, "nonexistent.jsonl"));
127
+ expect(diag.recoverable).toBe(false);
128
+ expect(diag.estimatedRecovery).toBe(0);
129
+ });
130
+ });
131
+ describe("repairSession", () => {
132
+ it("should create backup and remove invalid lines", () => {
133
+ const filePath = createFile('{"valid":"line1"}\ninvalid json\n{"valid":"line2"}', "repair.jsonl");
134
+ const result = repairSession(filePath);
135
+ expect(result.success).toBe(true);
136
+ expect(result.linesRemoved).toBe(1);
137
+ expect(result.backupPath).toBeTruthy();
138
+ expect(fs.existsSync(result.backupPath)).toBe(true);
139
+ const repaired = fs.readFileSync(filePath, "utf-8");
140
+ expect(repaired.trim().split("\n").length).toBe(2);
141
+ });
142
+ it("should skip backup when option set", () => {
143
+ const filePath = createFile('{"valid":"line"}', "no-backup.jsonl");
144
+ const result = repairSession(filePath, { backup: false });
145
+ expect(result.success).toBe(true);
146
+ expect(result.backupPath).toBe("");
147
+ });
148
+ it("should handle nonexistent file", () => {
149
+ const result = repairSession(path.join(TEST_DIR, "nope.jsonl"));
150
+ expect(result.success).toBe(false);
151
+ expect(result.error).toBeTruthy();
152
+ });
153
+ });
154
+ describe("extractSessionContent", () => {
155
+ it("should extract user and assistant messages", () => {
156
+ const filePath = createJsonlFile([
157
+ { type: "user", message: { role: "user", content: [{ type: "text", text: "What is 2+2?" }] } },
158
+ { type: "assistant", message: { role: "assistant", content: [{ type: "text", text: "4" }] } },
159
+ ], "extract.jsonl");
160
+ const extract = extractSessionContent(filePath);
161
+ expect(extract.userMessages).toContain("What is 2+2?");
162
+ expect(extract.assistantMessages).toContain("4");
163
+ });
164
+ it("should extract file edits", () => {
165
+ const filePath = createJsonlFile([{
166
+ type: "assistant",
167
+ message: {
168
+ role: "assistant",
169
+ content: [{
170
+ type: "tool_use",
171
+ name: "Write",
172
+ input: { file_path: "/tmp/test.ts", content: "console.log('hello')" },
173
+ }],
174
+ },
175
+ }], "edits.jsonl");
176
+ const extract = extractSessionContent(filePath);
177
+ expect(extract.fileEdits.length).toBe(1);
178
+ expect(extract.fileEdits[0].path).toBe("/tmp/test.ts");
179
+ });
180
+ it("should extract commands", () => {
181
+ const filePath = createJsonlFile([{
182
+ type: "assistant",
183
+ message: {
184
+ role: "assistant",
185
+ content: [{
186
+ type: "tool_use",
187
+ name: "Bash",
188
+ input: { command: "npm test" },
189
+ }],
190
+ },
191
+ }], "commands.jsonl");
192
+ const extract = extractSessionContent(filePath);
193
+ expect(extract.commandsRun).toContain("npm test");
194
+ });
195
+ it("should handle empty file", () => {
196
+ const filePath = createFile("", "empty.jsonl");
197
+ const extract = extractSessionContent(filePath);
198
+ expect(extract.userMessages).toEqual([]);
199
+ });
200
+ });
201
+ describe("formatSessionReport", () => {
202
+ it("should format a session report", () => {
203
+ const sessions = [{
204
+ id: "test-session-id",
205
+ project: "test-project",
206
+ projectPath: "/test",
207
+ filePath: "/tmp/test.jsonl",
208
+ messageCount: 10,
209
+ created: new Date(),
210
+ modified: new Date(),
211
+ sizeBytes: 1024,
212
+ status: "healthy",
213
+ subagentCount: 0,
214
+ }];
215
+ const report = formatSessionReport(sessions);
216
+ expect(report).toContain("SESSION OVERVIEW");
217
+ expect(report).toContain("Healthy: 1");
218
+ });
219
+ });
220
+ describe("formatSessionDiagnosticReport", () => {
221
+ it("should format a diagnostic report", () => {
222
+ const filePath = createJsonlFile([{ type: "user", message: { role: "user", content: [] } }], "diag.jsonl");
223
+ const diag = diagnoseSession(filePath);
224
+ const report = formatSessionDiagnosticReport(diag);
225
+ expect(report).toContain("Session Diagnosis");
226
+ expect(report).toContain("Recovery estimate");
227
+ });
228
+ });
229
+ });
230
+ //# sourceMappingURL=session-recovery.test.js.map