whatileaked 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/dist/cli.js +76 -14
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -194,6 +194,11 @@ npx whatileaked scan || echo "found something"
194
194
  | --- | --- |
195
195
  | Claude Code | `~/.claude/projects/` · `~/.claude/CLAUDE.md` |
196
196
  | Codex | `~/.codex/sessions/` · `~/.codex/AGENTS.md` |
197
+ | Cursor | `~/.cursor/projects/*/agent-transcripts/` |
198
+
199
+ Cursor keeps its older Ask-mode chats in SQLite rather than jsonl, and those are
200
+ not scanned. Reading them needs `node:sqlite`, which arrived in Node 22.5, and
201
+ this runs on Node 20. Agent transcripts are covered; Ask history is not.
197
202
 
198
203
  Transcripts record credentials that were already sent. Instruction and memory
199
204
  files are worse: they are read again at the start of the next session, so a
package/dist/cli.js CHANGED
@@ -2524,7 +2524,8 @@ async function walkDirectories(root) {
2524
2524
  // src/transcript/entry.ts
2525
2525
  var SourceName = {
2526
2526
  "claude-code": "claude-code",
2527
- codex: "codex"
2527
+ codex: "codex",
2528
+ cursor: "cursor"
2528
2529
  };
2529
2530
 
2530
2531
  // src/transcript/shape.ts
@@ -2556,16 +2557,18 @@ var ClaudeCodeSource = class {
2556
2557
  for (const slug of await listDirectories(root)) {
2557
2558
  const dir = join2(root, slug);
2558
2559
  let project = slug;
2559
- for (const file of await listFiles(dir, FileExtension.jsonl)) {
2560
- const path = join2(dir, file);
2561
- const cwd = await firstCwd(path);
2562
- if (cwd !== null) project = basename(cwd);
2563
- yield {
2564
- source: this.name,
2565
- kind: FileKind.transcript,
2566
- path,
2567
- project: cwd === null ? slug : basename(cwd)
2568
- };
2560
+ for (const nested of await walkDirectories(dir)) {
2561
+ for (const file of await listFiles(nested, FileExtension.jsonl)) {
2562
+ const path = join2(nested, file);
2563
+ const cwd = await firstCwd(path);
2564
+ if (cwd !== null) project = basename(cwd);
2565
+ yield {
2566
+ source: this.name,
2567
+ kind: FileKind.transcript,
2568
+ path,
2569
+ project: cwd === null ? slug : basename(cwd)
2570
+ };
2571
+ }
2569
2572
  }
2570
2573
  for (const nested of await walkDirectories(join2(dir, "memory"))) {
2571
2574
  for (const file of await listFiles(nested, FileExtension.markdown)) {
@@ -2630,6 +2633,61 @@ async function firstCwd2(path) {
2630
2633
  return null;
2631
2634
  }
2632
2635
 
2636
+ // src/sources/cursor-source.ts
2637
+ import { stat as stat2 } from "fs/promises";
2638
+ import { basename as basename3, join as join4 } from "path";
2639
+ var CursorSource = class {
2640
+ name = SourceName.cursor;
2641
+ #home;
2642
+ constructor(home2) {
2643
+ this.#home = home2;
2644
+ }
2645
+ async *discover() {
2646
+ const root = join4(this.#home, ".cursor", "projects");
2647
+ for (const slug of await listDirectories(root)) {
2648
+ const project = await projectName(slug);
2649
+ for (const dir of await walkDirectories(join4(root, slug, "agent-transcripts"))) {
2650
+ for (const file of await listFiles(dir, FileExtension.jsonl)) {
2651
+ yield {
2652
+ source: this.name,
2653
+ kind: FileKind.transcript,
2654
+ path: join4(dir, file),
2655
+ project
2656
+ };
2657
+ }
2658
+ }
2659
+ }
2660
+ }
2661
+ };
2662
+ async function projectName(slug) {
2663
+ const path = await resolveSlug(slug);
2664
+ return path === null ? slug : basename3(path);
2665
+ }
2666
+ async function resolveSlug(slug) {
2667
+ const parts = slug.split("-");
2668
+ let path = "";
2669
+ let index = 0;
2670
+ while (index < parts.length) {
2671
+ let end = index;
2672
+ let candidate = `${path}/${parts[index]}`;
2673
+ while (!await isDirectory(candidate) && end + 1 < parts.length) {
2674
+ end++;
2675
+ candidate = `${candidate}-${parts[end]}`;
2676
+ }
2677
+ if (!await isDirectory(candidate)) return null;
2678
+ path = candidate;
2679
+ index = end + 1;
2680
+ }
2681
+ return path === "" ? null : path;
2682
+ }
2683
+ async function isDirectory(path) {
2684
+ try {
2685
+ return (await stat2(path)).isDirectory();
2686
+ } catch {
2687
+ return false;
2688
+ }
2689
+ }
2690
+
2633
2691
  // src/wipe/confirm.ts
2634
2692
  import { createInterface as createInterface3 } from "readline/promises";
2635
2693
  var CONFIRMATION = "wipe";
@@ -2646,7 +2704,7 @@ var TerminalPrompt = class {
2646
2704
  };
2647
2705
 
2648
2706
  // src/wipe/wipe-run.ts
2649
- import { readFile, rename, stat as stat2, writeFile } from "fs/promises";
2707
+ import { readFile, rename, stat as stat3, writeFile } from "fs/promises";
2650
2708
 
2651
2709
  // src/wipe/redact.ts
2652
2710
  function placeholder(rule) {
@@ -2734,7 +2792,7 @@ var WipeRun = class {
2734
2792
  redacted += secrets.size;
2735
2793
  return redactLine(line, secrets);
2736
2794
  });
2737
- const { mode } = await stat2(file);
2795
+ const { mode } = await stat3(file);
2738
2796
  const temporary = `${file}.whatileaked-tmp`;
2739
2797
  await writeFile(temporary, rewritten.join("\n"), { encoding: "utf8", mode });
2740
2798
  await rename(temporary, file);
@@ -2774,7 +2832,11 @@ async function main(argv) {
2774
2832
  for (const line of home()) write(line);
2775
2833
  return 0;
2776
2834
  }
2777
- const sources = [new ClaudeCodeSource(homedir()), new CodexSource(homedir())];
2835
+ const sources = [
2836
+ new ClaudeCodeSource(homedir()),
2837
+ new CodexSource(homedir()),
2838
+ new CursorSource(homedir())
2839
+ ];
2778
2840
  const scanner = new Scanner(new KeywordIndex(compileRules(SECRET_RULE_SOURCES)));
2779
2841
  return command === Command.wipe ? await wipe(sources, scanner, write) : await scan(sources, scanner, write);
2780
2842
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whatileaked",
3
- "version": "0.2.0",
4
- "description": "Scan your local Claude Code and Codex transcripts for credentials you already sent to a model provider.",
3
+ "version": "0.3.0",
4
+ "description": "Scan your local Claude Code, Codex and Cursor transcripts for credentials you already sent to a model provider.",
5
5
  "type": "module",
6
6
  "imports": {
7
7
  "#scripts/*": "./scripts/*.ts",