@asifkibria/claude-code-toolkit 1.3.1 → 1.4.2

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 +16 -1
  2. package/dist/__tests__/alerts.test.d.ts +2 -0
  3. package/dist/__tests__/alerts.test.d.ts.map +1 -0
  4. package/dist/__tests__/alerts.test.js +195 -0
  5. package/dist/__tests__/alerts.test.js.map +1 -0
  6. package/dist/__tests__/bookmarks.test.d.ts +2 -0
  7. package/dist/__tests__/bookmarks.test.d.ts.map +1 -0
  8. package/dist/__tests__/bookmarks.test.js +170 -0
  9. package/dist/__tests__/bookmarks.test.js.map +1 -0
  10. package/dist/__tests__/bulk.test.d.ts +2 -0
  11. package/dist/__tests__/bulk.test.d.ts.map +1 -0
  12. package/dist/__tests__/bulk.test.js +245 -0
  13. package/dist/__tests__/bulk.test.js.map +1 -0
  14. package/dist/__tests__/dashboard.test.js +2 -2
  15. package/dist/__tests__/dashboard.test.js.map +1 -1
  16. package/dist/__tests__/git.test.d.ts +2 -0
  17. package/dist/__tests__/git.test.d.ts.map +1 -0
  18. package/dist/__tests__/git.test.js +216 -0
  19. package/dist/__tests__/git.test.js.map +1 -0
  20. package/dist/__tests__/logs.test.d.ts +2 -0
  21. package/dist/__tests__/logs.test.d.ts.map +1 -0
  22. package/dist/__tests__/logs.test.js +196 -0
  23. package/dist/__tests__/logs.test.js.map +1 -0
  24. package/dist/__tests__/search.test.d.ts +2 -0
  25. package/dist/__tests__/search.test.d.ts.map +1 -0
  26. package/dist/__tests__/search.test.js +155 -0
  27. package/dist/__tests__/search.test.js.map +1 -0
  28. package/dist/cli.js +269 -12
  29. package/dist/cli.js.map +1 -1
  30. package/dist/index.js +5 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/bookmarks.d.ts +69 -0
  33. package/dist/lib/bookmarks.d.ts.map +1 -0
  34. package/dist/lib/bookmarks.js +225 -0
  35. package/dist/lib/bookmarks.js.map +1 -0
  36. package/dist/lib/bulk.d.ts +78 -0
  37. package/dist/lib/bulk.d.ts.map +1 -0
  38. package/dist/lib/bulk.js +257 -0
  39. package/dist/lib/bulk.js.map +1 -0
  40. package/dist/lib/dashboard-ui.d.ts.map +1 -1
  41. package/dist/lib/dashboard-ui.js +425 -19
  42. package/dist/lib/dashboard-ui.js.map +1 -1
  43. package/dist/lib/dashboard.d.ts.map +1 -1
  44. package/dist/lib/dashboard.js +398 -20
  45. package/dist/lib/dashboard.js.map +1 -1
  46. package/dist/lib/export.d.ts +41 -0
  47. package/dist/lib/export.d.ts.map +1 -0
  48. package/dist/lib/export.js +428 -0
  49. package/dist/lib/export.js.map +1 -0
  50. package/dist/lib/git.d.ts.map +1 -1
  51. package/dist/lib/git.js +2 -1
  52. package/dist/lib/git.js.map +1 -1
  53. package/dist/lib/mcp-validator.d.ts.map +1 -1
  54. package/dist/lib/mcp-validator.js +2 -1
  55. package/dist/lib/mcp-validator.js.map +1 -1
  56. package/dist/lib/scanner.d.ts.map +1 -1
  57. package/dist/lib/scanner.js +2 -12
  58. package/dist/lib/scanner.js.map +1 -1
  59. package/dist/lib/security.d.ts.map +1 -1
  60. package/dist/lib/security.js +12 -1
  61. package/dist/lib/security.js.map +1 -1
  62. package/dist/lib/session-recovery.d.ts.map +1 -1
  63. package/dist/lib/session-recovery.js +25 -2
  64. package/dist/lib/session-recovery.js.map +1 -1
  65. package/dist/lib/utils.d.ts +15 -0
  66. package/dist/lib/utils.d.ts.map +1 -0
  67. package/dist/lib/utils.js +78 -0
  68. package/dist/lib/utils.js.map +1 -0
  69. package/package.json +2 -2
package/README.md CHANGED
@@ -52,7 +52,7 @@ Then ask Claude: "Check your health" or "Fix any issues"
52
52
  | **Session Recovery** | Fix broken sessions, repair corrupted files, extract content |
53
53
  | **Search** | Full-text search across all conversations |
54
54
  | **Security** | Secret detection, PII scanning, session auditing |
55
- | **Storage** | Analytics, cleanup, archiving, snapshots |
55
+ | **Storage** | Analytics, cleanup, archiving, snapshots, **project purge** |
56
56
  | **Monitoring** | Web dashboard, alerts, quotas, real-time updates |
57
57
  | **MCP** | Server validation, performance tracking |
58
58
  | **Git** | Link sessions to branches/commits |
@@ -128,6 +128,18 @@ cct archive --days 60 # Archive old conversations
128
128
  cct trace clean # Selective trace cleanup
129
129
  ```
130
130
 
131
+ ### 5b. Project-Level Cleanup
132
+
133
+ Delete all sessions, files, and directories for a specific project.
134
+
135
+ ```bash
136
+ cct bulk-delete --project "my-app" # Delete sessions for a project
137
+ cct bulk-archive --project "my-app" # Archive sessions for a project
138
+ cct bulk-delete --project "my-app" --days 30 # Only sessions older than 30 days
139
+ ```
140
+
141
+ **Dashboard**: The Sessions tab has a project filter dropdown with **Archive All**, **Delete Sessions**, and **Purge Project** buttons. Purge removes the entire project directory from `~/.claude/projects/`.
142
+
131
143
  ### 6. Git Integration
132
144
 
133
145
  Link your Claude sessions to git branches and commits.
@@ -226,6 +238,9 @@ cct mcp-validate --test
226
238
  | `cct restore` | Restore from backup |
227
239
  | `cct cleanup` | Delete old backups |
228
240
  | `cct export` | Export conversation |
241
+ | `cct bulk-delete` | **Delete sessions by project/age** |
242
+ | `cct bulk-archive` | **Archive sessions by project/age** |
243
+ | `cct bulk-export` | Export multiple sessions |
229
244
 
230
245
  ---
231
246
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=alerts.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/alerts.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,195 @@
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 { checkAlerts, checkQuotas, formatAlertsReport, formatQuotasReport, } from "../lib/alerts.js";
6
+ const TEST_DIR = path.join(os.tmpdir(), "cct-alerts-test");
7
+ const PROJECTS_DIR = path.join(TEST_DIR, "projects");
8
+ function createSession(projectName, fileName, content, sizeOverride) {
9
+ const dir = path.join(PROJECTS_DIR, projectName);
10
+ fs.mkdirSync(dir, { recursive: true });
11
+ const filePath = path.join(dir, fileName);
12
+ fs.writeFileSync(filePath, content, "utf-8");
13
+ return filePath;
14
+ }
15
+ function setFileAge(filePath, days) {
16
+ const mtime = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
17
+ fs.utimesSync(filePath, mtime, mtime);
18
+ }
19
+ describe("Alerts Module", () => {
20
+ beforeEach(() => {
21
+ if (fs.existsSync(TEST_DIR))
22
+ fs.rmSync(TEST_DIR, { recursive: true });
23
+ fs.mkdirSync(PROJECTS_DIR, { recursive: true });
24
+ });
25
+ afterEach(() => {
26
+ if (fs.existsSync(TEST_DIR))
27
+ fs.rmSync(TEST_DIR, { recursive: true });
28
+ });
29
+ describe("checkAlerts", () => {
30
+ it("should return empty alerts when no projects dir exists", () => {
31
+ const result = checkAlerts(path.join(TEST_DIR, "nonexistent"));
32
+ expect(result.alerts).toHaveLength(0);
33
+ expect(result.critical).toBe(0);
34
+ expect(result.warning).toBe(0);
35
+ expect(result.info).toBe(0);
36
+ });
37
+ it("should return no alerts for clean small session set", () => {
38
+ createSession("proj1", "session1.jsonl", JSON.stringify({ type: "user", message: { role: "user", content: [] } }));
39
+ const result = checkAlerts(TEST_DIR);
40
+ expect(result.alerts.length).toBe(0);
41
+ });
42
+ it("should warn when session count exceeds limit", () => {
43
+ for (let i = 0; i < 5; i++) {
44
+ createSession(`proj${i}`, `session${i}.jsonl`, JSON.stringify({ type: "user" }));
45
+ }
46
+ const result = checkAlerts(TEST_DIR, { maxSessions: 3 });
47
+ const sessionAlert = result.alerts.find(a => a.category === "session" && a.title.includes("count"));
48
+ expect(sessionAlert).toBeDefined();
49
+ expect(sessionAlert.severity).toBe("warning");
50
+ });
51
+ it("should detect corrupted sessions", () => {
52
+ createSession("proj1", "corrupt.jsonl", "not valid json");
53
+ const result = checkAlerts(TEST_DIR);
54
+ const corruptAlert = result.alerts.find(a => a.title.includes("Corrupted"));
55
+ expect(corruptAlert).toBeDefined();
56
+ expect(corruptAlert.severity).toBe("warning");
57
+ });
58
+ it("should detect empty sessions when more than 3 exist", () => {
59
+ for (let i = 0; i < 4; i++) {
60
+ createSession("proj1", `empty${i}.jsonl`, "");
61
+ }
62
+ const result = checkAlerts(TEST_DIR);
63
+ const emptyAlert = result.alerts.find(a => a.title.includes("Empty"));
64
+ expect(emptyAlert).toBeDefined();
65
+ expect(emptyAlert.severity).toBe("info");
66
+ });
67
+ it("should create info alert for old sessions", () => {
68
+ const f = createSession("proj1", "old.jsonl", JSON.stringify({ type: "user" }));
69
+ setFileAge(f, 100);
70
+ const result = checkAlerts(TEST_DIR, { maxRetentionDays: 30 });
71
+ const retentionAlert = result.alerts.find(a => a.category === "retention");
72
+ expect(retentionAlert).toBeDefined();
73
+ expect(retentionAlert.severity).toBe("info");
74
+ });
75
+ it("should sort alerts with critical first", () => {
76
+ for (let i = 0; i < 5; i++) {
77
+ createSession(`proj${i}`, `s${i}.jsonl`, JSON.stringify({ type: "user" }));
78
+ }
79
+ const f = createSession("oldproj", "old.jsonl", JSON.stringify({ type: "user" }));
80
+ setFileAge(f, 100);
81
+ const result = checkAlerts(TEST_DIR, { maxSessions: 3, maxRetentionDays: 30 });
82
+ if (result.alerts.length > 1) {
83
+ const severityOrder = { critical: 0, warning: 1, info: 2 };
84
+ for (let i = 0; i < result.alerts.length - 1; i++) {
85
+ expect(severityOrder[result.alerts[i].severity]).toBeLessThanOrEqual(severityOrder[result.alerts[i + 1].severity]);
86
+ }
87
+ }
88
+ });
89
+ it("should include actionable field and action text", () => {
90
+ for (let i = 0; i < 5; i++) {
91
+ createSession(`proj${i}`, `s${i}.jsonl`, JSON.stringify({ type: "user" }));
92
+ }
93
+ const result = checkAlerts(TEST_DIR, { maxSessions: 3 });
94
+ const actionableAlerts = result.alerts.filter(a => a.actionable);
95
+ expect(actionableAlerts.length).toBeGreaterThan(0);
96
+ expect(actionableAlerts[0].action).toBeTruthy();
97
+ });
98
+ it("should count alert severities correctly", () => {
99
+ createSession("proj1", "corrupt.jsonl", "not valid json");
100
+ for (let i = 0; i < 5; i++) {
101
+ createSession(`proj${i}`, `s${i}.jsonl`, JSON.stringify({ type: "user" }));
102
+ }
103
+ const result = checkAlerts(TEST_DIR, { maxSessions: 3 });
104
+ expect(result.warning).toBe(result.alerts.filter(a => a.severity === "warning").length);
105
+ expect(result.critical).toBe(result.alerts.filter(a => a.severity === "critical").length);
106
+ expect(result.info).toBe(result.alerts.filter(a => a.severity === "info").length);
107
+ });
108
+ });
109
+ describe("checkQuotas", () => {
110
+ it("should return empty array when no projects dir exists", () => {
111
+ const result = checkQuotas(undefined, path.join(TEST_DIR, "nonexistent"));
112
+ expect(result).toHaveLength(0);
113
+ });
114
+ it("should return quotas for storage and session count", () => {
115
+ createSession("proj1", "s1.jsonl", JSON.stringify({ type: "user" }));
116
+ const result = checkQuotas({ maxStorageMB: 500, maxSessions: 100 }, TEST_DIR);
117
+ const storageQuota = result.find(q => q.name === "Storage");
118
+ const sessionQuota = result.find(q => q.name === "Sessions");
119
+ expect(storageQuota).toBeDefined();
120
+ expect(sessionQuota).toBeDefined();
121
+ });
122
+ it("should mark quota as exceeded when over limit", () => {
123
+ for (let i = 0; i < 5; i++) {
124
+ createSession(`proj${i}`, `s${i}.jsonl`, JSON.stringify({ type: "user" }));
125
+ }
126
+ const result = checkQuotas({ maxSessions: 3 }, TEST_DIR);
127
+ const sessionQuota = result.find(q => q.name === "Sessions");
128
+ expect(sessionQuota.exceeded).toBe(true);
129
+ });
130
+ it("should not mark quota as exceeded when under limit", () => {
131
+ createSession("proj1", "s1.jsonl", JSON.stringify({ type: "user" }));
132
+ const result = checkQuotas({ maxSessions: 100 }, TEST_DIR);
133
+ const sessionQuota = result.find(q => q.name === "Sessions");
134
+ expect(sessionQuota.exceeded).toBe(false);
135
+ });
136
+ });
137
+ describe("formatAlertsReport", () => {
138
+ it("should show clean message when no alerts", () => {
139
+ const report = { alerts: [], critical: 0, warning: 0, info: 0, generatedAt: new Date() };
140
+ const output = formatAlertsReport(report);
141
+ expect(output).toContain("No alerts");
142
+ });
143
+ it("should include alert title and message in output", () => {
144
+ const report = {
145
+ alerts: [{
146
+ id: "test",
147
+ severity: "warning",
148
+ category: "session",
149
+ title: "Too many sessions",
150
+ message: "100 sessions detected.",
151
+ timestamp: new Date(),
152
+ actionable: true,
153
+ action: "Run cct retention",
154
+ }],
155
+ critical: 0,
156
+ warning: 1,
157
+ info: 0,
158
+ generatedAt: new Date(),
159
+ };
160
+ const output = formatAlertsReport(report);
161
+ expect(output).toContain("Too many sessions");
162
+ expect(output).toContain("100 sessions detected.");
163
+ });
164
+ });
165
+ describe("formatQuotasReport", () => {
166
+ it("should show no quotas message when empty", () => {
167
+ const output = formatQuotasReport([]);
168
+ expect(output).toContain("No quotas");
169
+ });
170
+ it("should include quota name and percentage", () => {
171
+ const output = formatQuotasReport([{
172
+ name: "Storage",
173
+ current: 50,
174
+ limit: 500,
175
+ unit: "MB",
176
+ exceeded: false,
177
+ percentage: 10,
178
+ }]);
179
+ expect(output).toContain("Storage");
180
+ expect(output).toContain("10%");
181
+ });
182
+ it("should indicate exceeded quotas", () => {
183
+ const output = formatQuotasReport([{
184
+ name: "Sessions",
185
+ current: 150,
186
+ limit: 100,
187
+ unit: "sessions",
188
+ exceeded: true,
189
+ percentage: 150,
190
+ }]);
191
+ expect(output).toContain("EXCEEDED");
192
+ });
193
+ });
194
+ });
195
+ //# sourceMappingURL=alerts.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.test.js","sourceRoot":"","sources":["../../src/__tests__/alerts.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bookmarks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookmarks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bookmarks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,170 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import * as path from "path";
3
+ import * as fs from "fs";
4
+ // bookmarks.ts computes BOOKMARKS_FILE at module load using os.homedir().
5
+ // Use a hardcoded path to avoid circular mock issues.
6
+ const TEST_DIR = "/tmp/cct-bookmarks-test";
7
+ const BOOKMARKS_FILE = path.join(TEST_DIR, ".claude", "bookmarks.json");
8
+ vi.mock("os", () => ({
9
+ homedir: () => TEST_DIR,
10
+ tmpdir: () => "/tmp",
11
+ }));
12
+ async function getModule() {
13
+ vi.resetModules();
14
+ return import("../lib/bookmarks.js");
15
+ }
16
+ describe("Bookmarks Module", () => {
17
+ beforeEach(() => {
18
+ if (fs.existsSync(TEST_DIR))
19
+ fs.rmSync(TEST_DIR, { recursive: true });
20
+ fs.mkdirSync(path.join(TEST_DIR, ".claude"), { recursive: true });
21
+ vi.resetModules();
22
+ });
23
+ afterEach(() => {
24
+ if (fs.existsSync(TEST_DIR))
25
+ fs.rmSync(TEST_DIR, { recursive: true });
26
+ });
27
+ describe("addBookmark", () => {
28
+ it("should create a new bookmark and save it", async () => {
29
+ const { addBookmark } = await getModule();
30
+ const bm = addBookmark("session-abc", 5, { label: "Important" });
31
+ expect(bm.sessionId).toBe("session-abc");
32
+ expect(bm.lineNumber).toBe(5);
33
+ expect(bm.label).toBe("Important");
34
+ expect(bm.id).toMatch(/^bm_/);
35
+ });
36
+ it("should return existing bookmark when adding duplicate", async () => {
37
+ const { addBookmark } = await getModule();
38
+ const bm1 = addBookmark("session-abc", 5);
39
+ const bm2 = addBookmark("session-abc", 5);
40
+ expect(bm1.id).toBe(bm2.id);
41
+ });
42
+ it("should update label on existing bookmark", async () => {
43
+ const { addBookmark } = await getModule();
44
+ addBookmark("session-abc", 5, { label: "Old Label" });
45
+ const updated = addBookmark("session-abc", 5, { label: "New Label" });
46
+ expect(updated.label).toBe("New Label");
47
+ });
48
+ it("should persist bookmarks to disk", async () => {
49
+ const { addBookmark } = await getModule();
50
+ addBookmark("session-xyz", 10);
51
+ const data = JSON.parse(fs.readFileSync(BOOKMARKS_FILE, "utf-8"));
52
+ expect(data.bookmarks.some((b) => b.sessionId === "session-xyz")).toBe(true);
53
+ });
54
+ });
55
+ describe("removeBookmark", () => {
56
+ it("should remove an existing bookmark and return true", async () => {
57
+ const { addBookmark, removeBookmark } = await getModule();
58
+ const bm = addBookmark("session-abc", 3);
59
+ const result = removeBookmark(bm.id);
60
+ expect(result).toBe(true);
61
+ });
62
+ it("should return false when bookmark does not exist", async () => {
63
+ const { removeBookmark } = await getModule();
64
+ const result = removeBookmark("nonexistent-id");
65
+ expect(result).toBe(false);
66
+ });
67
+ });
68
+ describe("getSessionBookmarks", () => {
69
+ it("should return bookmarks for a session sorted by line number", async () => {
70
+ const { addBookmark, getSessionBookmarks } = await getModule();
71
+ addBookmark("session-abc", 10);
72
+ addBookmark("session-abc", 2);
73
+ addBookmark("session-abc", 7);
74
+ const bms = getSessionBookmarks("session-abc");
75
+ expect(bms.map(b => b.lineNumber)).toEqual([2, 7, 10]);
76
+ });
77
+ it("should return empty array when no bookmarks for session", async () => {
78
+ const { getSessionBookmarks } = await getModule();
79
+ const bms = getSessionBookmarks("session-no-bookmarks");
80
+ expect(bms).toHaveLength(0);
81
+ });
82
+ });
83
+ describe("tagSession", () => {
84
+ it("should create a new session tag entry", async () => {
85
+ const { tagSession } = await getModule();
86
+ const tag = tagSession("session-abc", { tags: ["frontend", "bug"], name: "My Session" });
87
+ expect(tag.sessionId).toBe("session-abc");
88
+ expect(tag.tags).toContain("frontend");
89
+ expect(tag.name).toBe("My Session");
90
+ });
91
+ it("should deduplicate tags", async () => {
92
+ const { tagSession } = await getModule();
93
+ const tag = tagSession("session-abc", { tags: ["frontend", "frontend", "bug"] });
94
+ expect(tag.tags.filter((t) => t === "frontend")).toHaveLength(1);
95
+ });
96
+ it("should update existing session tag", async () => {
97
+ const { tagSession } = await getModule();
98
+ tagSession("session-abc", { name: "First Name" });
99
+ const updated = tagSession("session-abc", { name: "Second Name" });
100
+ expect(updated.name).toBe("Second Name");
101
+ });
102
+ });
103
+ describe("starSession / getStarredSessions", () => {
104
+ it("should star a session", async () => {
105
+ const { starSession, getStarredSessions } = await getModule();
106
+ starSession("session-abc");
107
+ const starred = getStarredSessions();
108
+ expect(starred.some((s) => s.sessionId === "session-abc")).toBe(true);
109
+ });
110
+ it("should unstar a session", async () => {
111
+ const { starSession, getStarredSessions, tagSession } = await getModule();
112
+ starSession("session-abc");
113
+ tagSession("session-abc", { starred: false });
114
+ const starred = getStarredSessions();
115
+ expect(starred.some((s) => s.sessionId === "session-abc")).toBe(false);
116
+ });
117
+ });
118
+ describe("addTagToSession / removeTagFromSession", () => {
119
+ it("should add a tag to a session", async () => {
120
+ const { addTagToSession } = await getModule();
121
+ const result = addTagToSession("session-abc", "important");
122
+ expect(result.tags).toContain("important");
123
+ });
124
+ it("should not duplicate tags when adding same tag twice", async () => {
125
+ const { addTagToSession } = await getModule();
126
+ addTagToSession("session-abc", "important");
127
+ const result = addTagToSession("session-abc", "important");
128
+ expect(result.tags.filter((t) => t === "important")).toHaveLength(1);
129
+ });
130
+ it("should remove a tag from a session", async () => {
131
+ const { addTagToSession, removeTagFromSession } = await getModule();
132
+ addTagToSession("session-abc", "to-remove");
133
+ const result = removeTagFromSession("session-abc", "to-remove");
134
+ expect(result.tags).not.toContain("to-remove");
135
+ });
136
+ it("should return null when session not found for removeTagFromSession", async () => {
137
+ const { removeTagFromSession } = await getModule();
138
+ const result = removeTagFromSession("nonexistent-session", "tag");
139
+ expect(result).toBeNull();
140
+ });
141
+ });
142
+ describe("clearAllBookmarks", () => {
143
+ it("should clear all bookmarks and tags and return counts", async () => {
144
+ const { addBookmark, tagSession, clearAllBookmarks } = await getModule();
145
+ addBookmark("session-1", 1);
146
+ addBookmark("session-2", 2);
147
+ tagSession("session-1", { name: "Test" });
148
+ const result = clearAllBookmarks();
149
+ expect(result.bookmarksCleared).toBe(2);
150
+ expect(result.tagsCleared).toBe(1);
151
+ });
152
+ });
153
+ describe("formatBookmarkReport", () => {
154
+ it("should format summary report correctly", async () => {
155
+ const { formatBookmarkReport } = await getModule();
156
+ const summary = {
157
+ totalBookmarks: 3,
158
+ totalTaggedSessions: 2,
159
+ starredSessions: 1,
160
+ totalTags: 4,
161
+ topTags: [{ tag: "frontend", count: 2 }],
162
+ recentBookmarks: [],
163
+ };
164
+ const output = formatBookmarkReport(summary);
165
+ expect(output).toContain("Total Bookmarks: 3");
166
+ expect(output).toContain("frontend");
167
+ });
168
+ });
169
+ });
170
+ //# sourceMappingURL=bookmarks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookmarks.test.js","sourceRoot":"","sources":["../../src/__tests__/bookmarks.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bulk.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bulk.test.ts"],"names":[],"mappings":""}