sandstream-kit 1.0.0 → 1.1.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 (86) hide show
  1. package/README.md +18 -13
  2. package/dist/cli.js +458 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/database.d.ts +2 -2
  5. package/dist/database.js +9 -14
  6. package/dist/database.js.map +1 -1
  7. package/dist/lock.js +5 -3
  8. package/dist/lock.js.map +1 -1
  9. package/dist/memory/backup 2.d.ts +6 -0
  10. package/dist/memory/backup 2.js +80 -0
  11. package/dist/memory/backup 2.js.map +1 -0
  12. package/dist/memory/backup.d.ts +6 -0
  13. package/dist/memory/backup.js +80 -0
  14. package/dist/memory/backup.js.map +1 -0
  15. package/dist/memory/db 2.d.ts +40 -0
  16. package/dist/memory/db 2.js +233 -0
  17. package/dist/memory/db 2.js.map +1 -0
  18. package/dist/memory/db.d.ts +40 -0
  19. package/dist/memory/db.js +233 -0
  20. package/dist/memory/db.js.map +1 -0
  21. package/dist/memory/hook 2.d.ts +6 -0
  22. package/dist/memory/hook 2.js +51 -0
  23. package/dist/memory/hook 2.js.map +1 -0
  24. package/dist/memory/hook.d.ts +6 -0
  25. package/dist/memory/hook.js +51 -0
  26. package/dist/memory/hook.js.map +1 -0
  27. package/dist/memory/hook.test 2.d.ts +1 -0
  28. package/dist/memory/hook.test 2.js +35 -0
  29. package/dist/memory/hook.test 2.js.map +1 -0
  30. package/dist/memory/install 2.d.ts +8 -0
  31. package/dist/memory/install 2.js +72 -0
  32. package/dist/memory/install 2.js.map +1 -0
  33. package/dist/memory/install.d.ts +8 -0
  34. package/dist/memory/install.js +72 -0
  35. package/dist/memory/install.js.map +1 -0
  36. package/dist/memory/install.test 2.d.ts +1 -0
  37. package/dist/memory/install.test 2.js +59 -0
  38. package/dist/memory/install.test 2.js.map +1 -0
  39. package/dist/memory/pal 2.d.ts +47 -0
  40. package/dist/memory/pal 2.js +154 -0
  41. package/dist/memory/pal 2.js.map +1 -0
  42. package/dist/memory/pal.d.ts +47 -0
  43. package/dist/memory/pal.js +154 -0
  44. package/dist/memory/pal.js.map +1 -0
  45. package/dist/memory/parser.d.ts +25 -0
  46. package/dist/memory/parser.js +164 -0
  47. package/dist/memory/parser.js.map +1 -0
  48. package/dist/memory/project 2.d.ts +1 -0
  49. package/dist/memory/project 2.js +24 -0
  50. package/dist/memory/project 2.js.map +1 -0
  51. package/dist/memory/project.d.ts +1 -0
  52. package/dist/memory/project.js +24 -0
  53. package/dist/memory/project.js.map +1 -0
  54. package/dist/memory/scan 2.d.ts +15 -0
  55. package/dist/memory/scan 2.js +94 -0
  56. package/dist/memory/scan 2.js.map +1 -0
  57. package/dist/memory/scan.d.ts +15 -0
  58. package/dist/memory/scan.js +94 -0
  59. package/dist/memory/scan.js.map +1 -0
  60. package/dist/memory/scan.test 2.d.ts +1 -0
  61. package/dist/memory/scan.test 2.js +55 -0
  62. package/dist/memory/scan.test 2.js.map +1 -0
  63. package/dist/memory/shared 2.d.ts +33 -0
  64. package/dist/memory/shared 2.js +120 -0
  65. package/dist/memory/shared 2.js.map +1 -0
  66. package/dist/memory/shared.d.ts +33 -0
  67. package/dist/memory/shared.js +120 -0
  68. package/dist/memory/shared.js.map +1 -0
  69. package/dist/memory/threads 2.d.ts +37 -0
  70. package/dist/memory/threads 2.js +50 -0
  71. package/dist/memory/threads 2.js.map +1 -0
  72. package/dist/memory/threads.d.ts +37 -0
  73. package/dist/memory/threads.js +50 -0
  74. package/dist/memory/threads.js.map +1 -0
  75. package/dist/memory/threads.test 2.d.ts +1 -0
  76. package/dist/memory/threads.test 2.js +66 -0
  77. package/dist/memory/threads.test 2.js.map +1 -0
  78. package/dist/memory/types 2.d.ts +52 -0
  79. package/dist/memory/types 2.js +5 -0
  80. package/dist/memory/types 2.js.map +1 -0
  81. package/dist/memory/types.d.ts +52 -0
  82. package/dist/memory/types.js +5 -0
  83. package/dist/memory/types.js.map +1 -0
  84. package/dist/secrets-pull.d.ts +1 -1
  85. package/dist/secrets-pull.js +1 -1
  86. package/package.json +1 -1
@@ -0,0 +1,164 @@
1
+ /**
2
+ * kit memory — Claude Code transcript parser.
3
+ *
4
+ * Reads raw session transcripts from ~/.claude/projects/<project>/<session>.jsonl
5
+ * and indexes them into the memory store. RAW + idempotent: one row per message
6
+ * (deduped by uuid), no summarisation. Re-running is safe — already-seen messages
7
+ * are ignored. Field mapping mirrors the Claude Code transcript format (also used
8
+ * by cloudctx, MIT). Other harnesses (Codex/Cursor) get their own parser later;
9
+ * sessions carry a `harness` tag so the store stays multi-harness.
10
+ */
11
+ import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { join, basename } from "node:path";
14
+ import { insertMessage, insertToolUse, upsertSession } from "./db.js";
15
+ export function getClaudeProjectsDir() {
16
+ const base = process.env.KIT_CLAUDE_DIR ?? join(homedir(), ".claude");
17
+ return join(base, "projects");
18
+ }
19
+ /** Flatten transcript content (string or block array) into searchable plain text. */
20
+ export function extractText(content) {
21
+ if (typeof content === "string")
22
+ return content;
23
+ if (Array.isArray(content)) {
24
+ return content
25
+ .map((b) => {
26
+ if (b?.type === "text")
27
+ return b.text ?? "";
28
+ if (b?.type === "tool_use")
29
+ return `[Tool: ${b.name ?? "unknown"}]`;
30
+ if (b?.type === "tool_result")
31
+ return "[Tool Result]";
32
+ return "";
33
+ })
34
+ .filter(Boolean)
35
+ .join("\n");
36
+ }
37
+ return "";
38
+ }
39
+ /** Extract tool_use blocks from a message's content. */
40
+ export function extractToolUses(content) {
41
+ if (!Array.isArray(content))
42
+ return [];
43
+ const out = [];
44
+ for (const b of content) {
45
+ if (b?.type === "tool_use") {
46
+ out.push({
47
+ name: b.name ?? "unknown",
48
+ input: b.input === undefined ? "" : JSON.stringify(b.input),
49
+ });
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ function indexFile(db, filepath, project) {
55
+ const sessionId = basename(filepath, ".jsonl");
56
+ let raw;
57
+ try {
58
+ raw = readFileSync(filepath, "utf8");
59
+ }
60
+ catch {
61
+ return { messages: 0, toolUses: 0 };
62
+ }
63
+ // Ensure the session row exists before inserting messages so per-message
64
+ // counters resolve against a real row.
65
+ upsertSession(db, { sessionId, harness: "claude-code", project });
66
+ let messages = 0;
67
+ let toolUses = 0;
68
+ let firstTs;
69
+ let lastTs;
70
+ let isSidechain = false;
71
+ for (const line of raw.split("\n")) {
72
+ const trimmed = line.trim();
73
+ if (!trimmed)
74
+ continue;
75
+ let rec;
76
+ try {
77
+ rec = JSON.parse(trimmed);
78
+ }
79
+ catch {
80
+ continue; // skip malformed lines, keep indexing the rest
81
+ }
82
+ if (rec.type !== "user" && rec.type !== "assistant")
83
+ continue;
84
+ if (!rec.uuid)
85
+ continue;
86
+ if (rec.isSidechain)
87
+ isSidechain = true;
88
+ const msg = rec.message;
89
+ const ts = rec.timestamp;
90
+ if (ts) {
91
+ if (!firstTs)
92
+ firstTs = ts;
93
+ lastTs = ts;
94
+ }
95
+ const added = insertMessage(db, {
96
+ uuid: rec.uuid,
97
+ sessionId: rec.sessionId ?? sessionId,
98
+ parentUuid: rec.parentUuid,
99
+ type: rec.type,
100
+ role: msg?.role,
101
+ content: extractText(msg?.content),
102
+ model: msg?.model,
103
+ inputTokens: msg?.usage?.input_tokens,
104
+ outputTokens: msg?.usage?.output_tokens,
105
+ timestamp: ts,
106
+ cwd: rec.cwd,
107
+ gitBranch: rec.gitBranch,
108
+ version: rec.version,
109
+ });
110
+ if (added) {
111
+ messages++;
112
+ for (const tool of extractToolUses(msg?.content)) {
113
+ insertToolUse(db, {
114
+ messageUuid: rec.uuid,
115
+ sessionId: rec.sessionId ?? sessionId,
116
+ toolName: tool.name,
117
+ toolInput: tool.input,
118
+ timestamp: ts,
119
+ });
120
+ toolUses++;
121
+ }
122
+ }
123
+ }
124
+ // Final upsert records the session's time bounds + sidechain flag.
125
+ upsertSession(db, {
126
+ sessionId,
127
+ harness: "claude-code",
128
+ project,
129
+ firstMessageAt: firstTs,
130
+ lastMessageAt: lastTs,
131
+ isAgentSidechain: isSidechain,
132
+ });
133
+ return { messages, toolUses };
134
+ }
135
+ /** Walk ~/.claude/projects and index every transcript. Idempotent. */
136
+ export function indexClaudeTranscripts(db) {
137
+ const projectsDir = getClaudeProjectsDir();
138
+ const result = { files: 0, sessions: 0, messages: 0, toolUses: 0 };
139
+ if (!existsSync(projectsDir))
140
+ return result;
141
+ for (const projectName of readdirSync(projectsDir).sort()) {
142
+ const projectPath = join(projectsDir, projectName);
143
+ let isDir = false;
144
+ try {
145
+ isDir = statSync(projectPath).isDirectory();
146
+ }
147
+ catch {
148
+ continue;
149
+ }
150
+ if (!isDir)
151
+ continue;
152
+ for (const entry of readdirSync(projectPath).sort()) {
153
+ if (!entry.endsWith(".jsonl"))
154
+ continue;
155
+ const counts = indexFile(db, join(projectPath, entry), projectName);
156
+ result.files++;
157
+ result.sessions++;
158
+ result.messages += counts.messages;
159
+ result.toolUses += counts.toolUses;
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/memory/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function getCurrentProjectRoot(cwd?: string): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * kit memory — current-project resolution.
3
+ *
4
+ * Memory search defaults to the current project (relevance + blast-radius
5
+ * containment); the repo root is the project boundary. Falls back to cwd when not
6
+ * inside a git repo. Pure read — no model calls, no writes.
7
+ */
8
+ import { execFileSync } from "node:child_process";
9
+ export function getCurrentProjectRoot(cwd = process.cwd()) {
10
+ try {
11
+ const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
12
+ cwd,
13
+ encoding: "utf8",
14
+ stdio: ["ignore", "pipe", "ignore"],
15
+ }).trim();
16
+ if (root)
17
+ return root;
18
+ }
19
+ catch {
20
+ // not a git repo (or git unavailable) — fall back to cwd
21
+ }
22
+ return cwd;
23
+ }
24
+ //# sourceMappingURL=project%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project 2.js","sourceRoot":"","sources":["../../src/memory/project 2.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,qBAAqB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC/D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE;YACjE,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function getCurrentProjectRoot(cwd?: string): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * kit memory — current-project resolution.
3
+ *
4
+ * Memory search defaults to the current project (relevance + blast-radius
5
+ * containment); the repo root is the project boundary. Falls back to cwd when not
6
+ * inside a git repo. Pure read — no model calls, no writes.
7
+ */
8
+ import { execFileSync } from "node:child_process";
9
+ export function getCurrentProjectRoot(cwd = process.cwd()) {
10
+ try {
11
+ const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
12
+ cwd,
13
+ encoding: "utf8",
14
+ stdio: ["ignore", "pipe", "ignore"],
15
+ }).trim();
16
+ if (root)
17
+ return root;
18
+ }
19
+ catch {
20
+ // not a git repo (or git unavailable) — fall back to cwd
21
+ }
22
+ return cwd;
23
+ }
24
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/memory/project.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,qBAAqB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC/D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE;YACjE,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ export type ScanConfidence = "high" | "heuristic";
3
+ export interface ScanFinding {
4
+ label: string;
5
+ preview: string;
6
+ confidence: ScanConfidence;
7
+ /** How many cells matched this (label, preview). */
8
+ count: number;
9
+ /** One example location, e.g. "messages#23839.content". */
10
+ sample: string;
11
+ /** Distinct project hints (which repo the secret leaked in), e.g. ["acme-app"]. */
12
+ projects: string[];
13
+ }
14
+ /** Scan every text cell for stored secrets. Deduped, confidence-tiered, project-attributed. */
15
+ export declare function scanDbForSecrets(db: DatabaseSync): ScanFinding[];
@@ -0,0 +1,94 @@
1
+ /**
2
+ * kit memory — secret scan over the store.
3
+ *
4
+ * The memory DB is secret-dense (it indexes raw transcripts). gitleaks and most
5
+ * scanners only see text files, not SQLite cell contents — so this scans the text
6
+ * columns directly, reusing kit's SECRET_PATTERNS via findSecrets (DRY). Findings
7
+ * are MASKED (label + short preview), never the raw secret.
8
+ *
9
+ * Findings are DEDUPED by (label, preview) with an occurrence count, split by
10
+ * CONFIDENCE so the genuinely dangerous keys (sk_live, AIzaSy, AKIA, ghp_, …) are
11
+ * not buried under the over-eager `KEY=value` heuristic, and ATTRIBUTED to the
12
+ * project(s) they leaked in (via each row's cwd) so you know which provider account
13
+ * to rotate. Only high-confidence findings make `kit memory scan` exit non-zero.
14
+ */
15
+ import { basename } from "node:path";
16
+ import { findSecrets } from "../utils/redactSecrets.js";
17
+ const TARGETS = [
18
+ {
19
+ table: "messages",
20
+ idCol: "id",
21
+ columns: ["content"],
22
+ select: "SELECT id, content, cwd AS __project FROM messages",
23
+ },
24
+ {
25
+ table: "tool_uses",
26
+ idCol: "id",
27
+ columns: ["tool_input"],
28
+ select: "SELECT tool_uses.id AS id, tool_uses.tool_input AS tool_input, m.cwd AS __project " +
29
+ "FROM tool_uses LEFT JOIN messages m ON m.uuid = tool_uses.message_uuid",
30
+ },
31
+ {
32
+ table: "pending_actions",
33
+ idCol: "id",
34
+ columns: ["title", "detail", "verify_cmd"],
35
+ select: "SELECT id, title, detail, verify_cmd, scope AS __project FROM pending_actions",
36
+ },
37
+ {
38
+ table: "saved_threads",
39
+ idCol: "name",
40
+ columns: ["summary"],
41
+ select: "SELECT name, summary, project_path AS __project FROM saved_threads",
42
+ },
43
+ ];
44
+ // Heuristic labels are pattern-based guesses (KEY=value, tfstate blobs) that
45
+ // frequently match benign env vars / file paths. Everything else is a structured,
46
+ // high-confidence credential pattern.
47
+ const HEURISTIC_LABELS = new Set(["kv-secret", "tfstate-value"]);
48
+ function projectName(raw) {
49
+ if (typeof raw !== "string" || !raw)
50
+ return null;
51
+ return raw.includes("/") ? basename(raw) : raw;
52
+ }
53
+ /** Scan every text cell for stored secrets. Deduped, confidence-tiered, project-attributed. */
54
+ export function scanDbForSecrets(db) {
55
+ const byKey = new Map();
56
+ for (const target of TARGETS) {
57
+ const rows = db.prepare(target.select).all();
58
+ for (const row of rows) {
59
+ const proj = projectName(row.__project);
60
+ for (const col of target.columns) {
61
+ const val = row[col];
62
+ if (typeof val !== "string" || !val)
63
+ continue;
64
+ for (const f of findSecrets(val)) {
65
+ const key = `${f.label} ${f.preview}`;
66
+ let entry = byKey.get(key);
67
+ if (!entry) {
68
+ entry = {
69
+ label: f.label,
70
+ preview: f.preview,
71
+ confidence: HEURISTIC_LABELS.has(f.label) ? "heuristic" : "high",
72
+ count: 0,
73
+ sample: `${target.table}#${row[target.idCol]}.${col}`,
74
+ projects: [],
75
+ _projects: new Set(),
76
+ };
77
+ byKey.set(key, entry);
78
+ }
79
+ entry.count++;
80
+ if (proj)
81
+ entry._projects.add(proj);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return [...byKey.values()]
87
+ .map(({ _projects, ...f }) => ({ ...f, projects: [..._projects].sort() }))
88
+ .sort((a, b) => {
89
+ if (a.confidence !== b.confidence)
90
+ return a.confidence === "high" ? -1 : 1;
91
+ return b.count - a.count;
92
+ });
93
+ }
94
+ //# sourceMappingURL=scan%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan 2.js","sourceRoot":"","sources":["../../src/memory/scan 2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ export type ScanConfidence = "high" | "heuristic";
3
+ export interface ScanFinding {
4
+ label: string;
5
+ preview: string;
6
+ confidence: ScanConfidence;
7
+ /** How many cells matched this (label, preview). */
8
+ count: number;
9
+ /** One example location, e.g. "messages#23839.content". */
10
+ sample: string;
11
+ /** Distinct project hints (which repo the secret leaked in), e.g. ["acme-app"]. */
12
+ projects: string[];
13
+ }
14
+ /** Scan every text cell for stored secrets. Deduped, confidence-tiered, project-attributed. */
15
+ export declare function scanDbForSecrets(db: DatabaseSync): ScanFinding[];
@@ -0,0 +1,94 @@
1
+ /**
2
+ * kit memory — secret scan over the store.
3
+ *
4
+ * The memory DB is secret-dense (it indexes raw transcripts). gitleaks and most
5
+ * scanners only see text files, not SQLite cell contents — so this scans the text
6
+ * columns directly, reusing kit's SECRET_PATTERNS via findSecrets (DRY). Findings
7
+ * are MASKED (label + short preview), never the raw secret.
8
+ *
9
+ * Findings are DEDUPED by (label, preview) with an occurrence count, split by
10
+ * CONFIDENCE so the genuinely dangerous keys (sk_live, AIzaSy, AKIA, ghp_, …) are
11
+ * not buried under the over-eager `KEY=value` heuristic, and ATTRIBUTED to the
12
+ * project(s) they leaked in (via each row's cwd) so you know which provider account
13
+ * to rotate. Only high-confidence findings make `kit memory scan` exit non-zero.
14
+ */
15
+ import { basename } from "node:path";
16
+ import { findSecrets } from "../utils/redactSecrets.js";
17
+ const TARGETS = [
18
+ {
19
+ table: "messages",
20
+ idCol: "id",
21
+ columns: ["content"],
22
+ select: "SELECT id, content, cwd AS __project FROM messages",
23
+ },
24
+ {
25
+ table: "tool_uses",
26
+ idCol: "id",
27
+ columns: ["tool_input"],
28
+ select: "SELECT tool_uses.id AS id, tool_uses.tool_input AS tool_input, m.cwd AS __project " +
29
+ "FROM tool_uses LEFT JOIN messages m ON m.uuid = tool_uses.message_uuid",
30
+ },
31
+ {
32
+ table: "pending_actions",
33
+ idCol: "id",
34
+ columns: ["title", "detail", "verify_cmd"],
35
+ select: "SELECT id, title, detail, verify_cmd, scope AS __project FROM pending_actions",
36
+ },
37
+ {
38
+ table: "saved_threads",
39
+ idCol: "name",
40
+ columns: ["summary"],
41
+ select: "SELECT name, summary, project_path AS __project FROM saved_threads",
42
+ },
43
+ ];
44
+ // Heuristic labels are pattern-based guesses (KEY=value, tfstate blobs) that
45
+ // frequently match benign env vars / file paths. Everything else is a structured,
46
+ // high-confidence credential pattern.
47
+ const HEURISTIC_LABELS = new Set(["kv-secret", "tfstate-value"]);
48
+ function projectName(raw) {
49
+ if (typeof raw !== "string" || !raw)
50
+ return null;
51
+ return raw.includes("/") ? basename(raw) : raw;
52
+ }
53
+ /** Scan every text cell for stored secrets. Deduped, confidence-tiered, project-attributed. */
54
+ export function scanDbForSecrets(db) {
55
+ const byKey = new Map();
56
+ for (const target of TARGETS) {
57
+ const rows = db.prepare(target.select).all();
58
+ for (const row of rows) {
59
+ const proj = projectName(row.__project);
60
+ for (const col of target.columns) {
61
+ const val = row[col];
62
+ if (typeof val !== "string" || !val)
63
+ continue;
64
+ for (const f of findSecrets(val)) {
65
+ const key = `${f.label} ${f.preview}`;
66
+ let entry = byKey.get(key);
67
+ if (!entry) {
68
+ entry = {
69
+ label: f.label,
70
+ preview: f.preview,
71
+ confidence: HEURISTIC_LABELS.has(f.label) ? "heuristic" : "high",
72
+ count: 0,
73
+ sample: `${target.table}#${row[target.idCol]}.${col}`,
74
+ projects: [],
75
+ _projects: new Set(),
76
+ };
77
+ byKey.set(key, entry);
78
+ }
79
+ entry.count++;
80
+ if (proj)
81
+ entry._projects.add(proj);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return [...byKey.values()]
87
+ .map(({ _projects, ...f }) => ({ ...f, projects: [..._projects].sort() }))
88
+ .sort((a, b) => {
89
+ if (a.confidence !== b.confidence)
90
+ return a.confidence === "high" ? -1 : 1;
91
+ return b.count - a.count;
92
+ });
93
+ }
94
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/memory/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { openMemoryDb, upsertSession, insertMessage } from "./db.js";
4
+ import { scanDbForSecrets } from "./scan.js";
5
+ describe("memory secret-scan", () => {
6
+ it("flags a stored secret (masked, high-confidence) and locates it", () => {
7
+ const db = openMemoryDb(":memory:");
8
+ upsertSession(db, { sessionId: "s1", harness: "claude-code" });
9
+ const fake = "sk_live_" + "A".repeat(24); // synthetic, non-real
10
+ insertMessage(db, { uuid: "u1", sessionId: "s1", type: "user", content: `the key is ${fake}` });
11
+ insertMessage(db, { uuid: "u2", sessionId: "s1", type: "user", content: "totally clean message" });
12
+ const findings = scanDbForSecrets(db);
13
+ assert.equal(findings.length, 1);
14
+ assert.equal(findings[0]?.label, "stripe-key");
15
+ assert.equal(findings[0]?.confidence, "high");
16
+ assert.equal(findings[0]?.count, 1);
17
+ assert.match(findings[0]?.sample ?? "", /^messages#\d+\.content$/);
18
+ assert.ok(!findings[0]?.preview.includes("A".repeat(24)), "preview is masked, not the raw secret");
19
+ db.close();
20
+ });
21
+ it("dedupes the same secret across rows with an occurrence count", () => {
22
+ const db = openMemoryDb(":memory:");
23
+ upsertSession(db, { sessionId: "s1", harness: "claude-code" });
24
+ const fake = "sk_live_" + "B".repeat(24);
25
+ insertMessage(db, { uuid: "u1", sessionId: "s1", type: "user", content: `key ${fake}` });
26
+ insertMessage(db, { uuid: "u2", sessionId: "s1", type: "user", content: `again ${fake}` });
27
+ const findings = scanDbForSecrets(db);
28
+ assert.equal(findings.length, 1, "one unique finding, not two");
29
+ assert.equal(findings[0]?.count, 2);
30
+ db.close();
31
+ });
32
+ it("attributes a finding to the project it leaked in (via cwd)", () => {
33
+ const db = openMemoryDb(":memory:");
34
+ upsertSession(db, { sessionId: "s1", harness: "claude-code" });
35
+ const fake = "sk_live_" + "C".repeat(24);
36
+ insertMessage(db, {
37
+ uuid: "u1",
38
+ sessionId: "s1",
39
+ type: "user",
40
+ content: `key ${fake}`,
41
+ cwd: "/Users/me/dev/app-a",
42
+ });
43
+ const findings = scanDbForSecrets(db);
44
+ assert.deepEqual(findings[0]?.projects, ["app-a"]);
45
+ db.close();
46
+ });
47
+ it("returns nothing for a clean db", () => {
48
+ const db = openMemoryDb(":memory:");
49
+ upsertSession(db, { sessionId: "s1", harness: "claude-code" });
50
+ insertMessage(db, { uuid: "u1", sessionId: "s1", type: "user", content: "no secrets here" });
51
+ assert.deepEqual(scanDbForSecrets(db), []);
52
+ db.close();
53
+ });
54
+ });
55
+ //# sourceMappingURL=scan.test%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.test 2.js","sourceRoot":"","sources":["../../src/memory/scan.test 2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACpC,aAAa,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB;QAChE,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,IAAI,EAAE,EAAE,CAAC,CAAC;QAChG,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;QACnG,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,EAAE,yBAAyB,CAAC,CAAC;QACnE,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,uCAAuC,CAAC,CAAC;QACnG,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACpC,aAAa,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzC,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;QACzF,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3F,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,6BAA6B,CAAC,CAAC;QAChE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACpC,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACpC,aAAa,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzC,aAAa,CAAC,EAAE,EAAE;YAChB,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,IAAI;YACf,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,OAAO,IAAI,EAAE;YACtB,GAAG,EAAE,qBAAqB;SAC3B,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACpC,aAAa,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/D,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC7F,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3C,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ export type SharedKind = "decision" | "convention" | "how-built" | "status" | "security" | "note";
2
+ export interface SharedEntry {
3
+ id: string;
4
+ area: string;
5
+ kind: SharedKind;
6
+ title: string;
7
+ body: string;
8
+ refs: string[];
9
+ author: string;
10
+ ts: string;
11
+ source_ref?: string;
12
+ }
13
+ export interface ShareInput {
14
+ area: string;
15
+ kind: SharedKind;
16
+ title: string;
17
+ body: string;
18
+ refs?: string[];
19
+ }
20
+ export declare function getSharedPath(root: string): string;
21
+ export declare function readShared(root: string): SharedEntry[];
22
+ /**
23
+ * Promote one entry into the shared store. Fail-closed: refuses (throws) if any
24
+ * text field contains a secret. Only allow-listed fields are persisted — no raw
25
+ * tool output / env dumps can sneak in. Author + source_ref give provenance.
26
+ */
27
+ export declare function shareEntry(root: string, input: ShareInput, now: string): SharedEntry;
28
+ export declare function listAreas(root: string): {
29
+ area: string;
30
+ count: number;
31
+ }[];
32
+ export declare function queryArea(root: string, area: string): SharedEntry[];
33
+ export declare function searchShared(root: string, query: string): SharedEntry[];