sandstream-kit 1.0.1 → 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 +18 -0
- package/dist/check-web-search.js +37 -6
- package/dist/check-web-search.js.map +1 -1
- package/dist/cli.js +520 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/database.d.ts +2 -2
- package/dist/database.js +9 -14
- package/dist/database.js.map +1 -1
- package/dist/lock.js +5 -3
- package/dist/lock.js.map +1 -1
- package/dist/memory/backup 2.d.ts +6 -0
- package/dist/memory/backup 2.js +80 -0
- package/dist/memory/backup 2.js.map +1 -0
- package/dist/memory/backup.d.ts +6 -0
- package/dist/memory/backup.js +80 -0
- package/dist/memory/backup.js.map +1 -0
- package/dist/memory/codex.d.ts +5 -0
- package/dist/memory/codex.js +150 -0
- package/dist/memory/codex.js.map +1 -0
- package/dist/memory/continue.d.ts +5 -0
- package/dist/memory/continue.js +116 -0
- package/dist/memory/continue.js.map +1 -0
- package/dist/memory/db 2.d.ts +40 -0
- package/dist/memory/db 2.js +233 -0
- package/dist/memory/db 2.js.map +1 -0
- package/dist/memory/db.d.ts +51 -0
- package/dist/memory/db.js +275 -0
- package/dist/memory/db.js.map +1 -0
- package/dist/memory/gemini.d.ts +5 -0
- package/dist/memory/gemini.js +176 -0
- package/dist/memory/gemini.js.map +1 -0
- package/dist/memory/hook 2.d.ts +6 -0
- package/dist/memory/hook 2.js +51 -0
- package/dist/memory/hook 2.js.map +1 -0
- package/dist/memory/hook.d.ts +15 -0
- package/dist/memory/hook.js +84 -0
- package/dist/memory/hook.js.map +1 -0
- package/dist/memory/hook.test 2.d.ts +1 -0
- package/dist/memory/hook.test 2.js +35 -0
- package/dist/memory/hook.test 2.js.map +1 -0
- package/dist/memory/install 2.d.ts +8 -0
- package/dist/memory/install 2.js +72 -0
- package/dist/memory/install 2.js.map +1 -0
- package/dist/memory/install.d.ts +8 -0
- package/dist/memory/install.js +73 -0
- package/dist/memory/install.js.map +1 -0
- package/dist/memory/install.test 2.d.ts +1 -0
- package/dist/memory/install.test 2.js +59 -0
- package/dist/memory/install.test 2.js.map +1 -0
- package/dist/memory/merge.d.ts +18 -0
- package/dist/memory/merge.js +109 -0
- package/dist/memory/merge.js.map +1 -0
- package/dist/memory/pal 2.d.ts +47 -0
- package/dist/memory/pal 2.js +154 -0
- package/dist/memory/pal 2.js.map +1 -0
- package/dist/memory/pal.d.ts +47 -0
- package/dist/memory/pal.js +154 -0
- package/dist/memory/pal.js.map +1 -0
- package/dist/memory/parser.d.ts +34 -0
- package/dist/memory/parser.js +195 -0
- package/dist/memory/parser.js.map +1 -0
- package/dist/memory/project 2.d.ts +1 -0
- package/dist/memory/project 2.js +24 -0
- package/dist/memory/project 2.js.map +1 -0
- package/dist/memory/project.d.ts +1 -0
- package/dist/memory/project.js +24 -0
- package/dist/memory/project.js.map +1 -0
- package/dist/memory/scan 2.d.ts +15 -0
- package/dist/memory/scan 2.js +94 -0
- package/dist/memory/scan 2.js.map +1 -0
- package/dist/memory/scan.d.ts +15 -0
- package/dist/memory/scan.js +94 -0
- package/dist/memory/scan.js.map +1 -0
- package/dist/memory/scan.test 2.d.ts +1 -0
- package/dist/memory/scan.test 2.js +55 -0
- package/dist/memory/scan.test 2.js.map +1 -0
- package/dist/memory/shared 2.d.ts +33 -0
- package/dist/memory/shared 2.js +120 -0
- package/dist/memory/shared 2.js.map +1 -0
- package/dist/memory/shared.d.ts +33 -0
- package/dist/memory/shared.js +120 -0
- package/dist/memory/shared.js.map +1 -0
- package/dist/memory/threads 2.d.ts +37 -0
- package/dist/memory/threads 2.js +50 -0
- package/dist/memory/threads 2.js.map +1 -0
- package/dist/memory/threads.d.ts +37 -0
- package/dist/memory/threads.js +50 -0
- package/dist/memory/threads.js.map +1 -0
- package/dist/memory/threads.test 2.d.ts +1 -0
- package/dist/memory/threads.test 2.js +66 -0
- package/dist/memory/threads.test 2.js.map +1 -0
- package/dist/memory/types 2.d.ts +52 -0
- package/dist/memory/types 2.js +5 -0
- package/dist/memory/types 2.js.map +1 -0
- package/dist/memory/types.d.ts +52 -0
- package/dist/memory/types.js +5 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/status.d.ts +9 -0
- package/dist/status.js +119 -0
- package/dist/status.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface PendingAction {
|
|
3
|
+
id: string;
|
|
4
|
+
status: string;
|
|
5
|
+
title: string;
|
|
6
|
+
detail: string | null;
|
|
7
|
+
scope: string | null;
|
|
8
|
+
kind: string;
|
|
9
|
+
verify_cmd: string | null;
|
|
10
|
+
created_at: string | null;
|
|
11
|
+
next_check: string | null;
|
|
12
|
+
snooze_until: string | null;
|
|
13
|
+
closed_at: string | null;
|
|
14
|
+
verify_passes: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PalAddInput {
|
|
17
|
+
title: string;
|
|
18
|
+
detail?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
kind?: "manual" | "auto";
|
|
21
|
+
verifyCmd?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function palAdd(db: DatabaseSync, input: PalAddInput): string;
|
|
24
|
+
export interface PalListOptions {
|
|
25
|
+
status?: string;
|
|
26
|
+
/** Restrict to this scope plus globally-scoped (NULL) items. Omit = every scope. */
|
|
27
|
+
scope?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function palList(db: DatabaseSync, opts?: PalListOptions): PendingAction[];
|
|
30
|
+
export declare function palDone(db: DatabaseSync, id: string): boolean;
|
|
31
|
+
export declare function palSnooze(db: DatabaseSync, id: string, days: number): boolean;
|
|
32
|
+
export interface AutoVerifyResult {
|
|
33
|
+
checked: number;
|
|
34
|
+
closed: string[];
|
|
35
|
+
reopened: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
39
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
40
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
41
|
+
*/
|
|
42
|
+
export declare function palAutoVerify(db: DatabaseSync, confirmPasses?: number): AutoVerifyResult;
|
|
43
|
+
export declare function getLegacyLedgerPath(): string;
|
|
44
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
45
|
+
export declare function importLegacyLedger(db: DatabaseSync, path?: string): {
|
|
46
|
+
imported: number;
|
|
47
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kit memory — PAL (Pending Action Ledger), folded into the memory store.
|
|
3
|
+
*
|
|
4
|
+
* PAL is the STRUCTURED, actionable layer on top of raw conversation memory:
|
|
5
|
+
* "blocked-on-you" items that survive sessions and auto-close when their verify
|
|
6
|
+
* command starts passing. It lives in the `pending_actions` table of the same
|
|
7
|
+
* SQLite store. Deterministic; the only side effect is running operator-defined
|
|
8
|
+
* verify commands (local shell, with a timeout). Fail-open / no-info aware.
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
function newId(db) {
|
|
16
|
+
for (let i = 0; i < 100; i++) {
|
|
17
|
+
const id = randomBytes(2).toString("hex"); // 4 hex chars, e.g. "ec95"
|
|
18
|
+
if (!db.prepare("SELECT 1 FROM pending_actions WHERE id = ?").get(id))
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
throw new Error("could not allocate a unique pending-action id");
|
|
22
|
+
}
|
|
23
|
+
export function palAdd(db, input) {
|
|
24
|
+
const id = newId(db);
|
|
25
|
+
const kind = input.kind ?? (input.verifyCmd ? "auto" : "manual");
|
|
26
|
+
db.prepare(`INSERT INTO pending_actions (id, status, title, detail, scope, kind, verify_cmd)
|
|
27
|
+
VALUES (?, 'open', ?, ?, ?, ?, ?)`).run(id, input.title, input.detail ?? null, input.scope ?? null, kind, input.verifyCmd ?? null);
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
export function palList(db, opts = {}) {
|
|
31
|
+
const status = opts.status ?? "open";
|
|
32
|
+
if (opts.scope !== undefined) {
|
|
33
|
+
return db
|
|
34
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? AND (scope = ? OR scope IS NULL) ORDER BY created_at, id")
|
|
35
|
+
.all(status, opts.scope);
|
|
36
|
+
}
|
|
37
|
+
return db
|
|
38
|
+
.prepare("SELECT * FROM pending_actions WHERE status = ? ORDER BY created_at, id")
|
|
39
|
+
.all(status);
|
|
40
|
+
}
|
|
41
|
+
export function palDone(db, id) {
|
|
42
|
+
const res = db
|
|
43
|
+
.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now') WHERE id=? AND status!='closed'")
|
|
44
|
+
.run(id);
|
|
45
|
+
return Number(res.changes) > 0;
|
|
46
|
+
}
|
|
47
|
+
export function palSnooze(db, id, days) {
|
|
48
|
+
const d = Math.max(1, Math.floor(days));
|
|
49
|
+
const res = db
|
|
50
|
+
.prepare("UPDATE pending_actions SET status='snoozed', snooze_until=datetime('now', ?) WHERE id=?")
|
|
51
|
+
.run(`+${d} days`, id);
|
|
52
|
+
return Number(res.changes) > 0;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run a verify command. true = pass (exit 0), false = ran but failed, null = no-info.
|
|
56
|
+
*
|
|
57
|
+
* SECURITY: this runs `cmd` through a shell on purpose — verify commands routinely
|
|
58
|
+
* need pipes / `&&` (e.g. `curl -fsS … | grep 200`). The trust boundary: `verify_cmd`
|
|
59
|
+
* is OPERATOR-AUTHORED and lives only in the PERSONAL store (~/.kit/memory.db); running
|
|
60
|
+
* it is equivalent to the operator running their own command — no untrusted data is
|
|
61
|
+
* interpolated, so this is not a command-injection sink. INVARIANT for Track D (shared
|
|
62
|
+
* memory): shared/synced items must NEVER carry an executable `verify_cmd` that runs
|
|
63
|
+
* unreviewed — only manual items or review-gated verifies cross the sharing boundary.
|
|
64
|
+
*/
|
|
65
|
+
function runVerify(cmd) {
|
|
66
|
+
try {
|
|
67
|
+
execSync(cmd, { stdio: "ignore", timeout: 15_000 });
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const status = err.status;
|
|
72
|
+
if (typeof status === "number")
|
|
73
|
+
return false; // ran, non-zero exit
|
|
74
|
+
return null; // spawn error / timeout → no-info, leave state unchanged
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Auto-verify `auto` items. An OPEN item that passes `confirmPasses` consecutive
|
|
79
|
+
* times is closed (a pass increments the streak, a fail resets it). A CLOSED item
|
|
80
|
+
* whose verify now FAILS is reopened (reopen-on-regress). No-info leaves it alone.
|
|
81
|
+
*/
|
|
82
|
+
export function palAutoVerify(db, confirmPasses = 2) {
|
|
83
|
+
const out = { checked: 0, closed: [], reopened: [] };
|
|
84
|
+
const rows = db
|
|
85
|
+
.prepare("SELECT * FROM pending_actions WHERE kind='auto' AND verify_cmd IS NOT NULL AND status IN ('open','closed')")
|
|
86
|
+
.all();
|
|
87
|
+
for (const r of rows) {
|
|
88
|
+
const result = runVerify(r.verify_cmd);
|
|
89
|
+
if (result === null)
|
|
90
|
+
continue; // no-info
|
|
91
|
+
out.checked++;
|
|
92
|
+
if (r.status === "open") {
|
|
93
|
+
if (result) {
|
|
94
|
+
const passes = r.verify_passes + 1;
|
|
95
|
+
if (passes >= confirmPasses) {
|
|
96
|
+
db.prepare("UPDATE pending_actions SET status='closed', closed_at=datetime('now'), verify_passes=? WHERE id=?").run(passes, r.id);
|
|
97
|
+
out.closed.push(r.id);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
db.prepare("UPDATE pending_actions SET verify_passes=? WHERE id=?").run(passes, r.id);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (r.verify_passes !== 0) {
|
|
104
|
+
db.prepare("UPDATE pending_actions SET verify_passes=0 WHERE id=?").run(r.id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (r.status === "closed" && !result) {
|
|
108
|
+
db.prepare("UPDATE pending_actions SET status='open', verify_passes=0, closed_at=NULL WHERE id=?").run(r.id);
|
|
109
|
+
out.reopened.push(r.id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
// ── Migration from the legacy python PAL ledger ───────────────────────────────
|
|
115
|
+
export function getLegacyLedgerPath() {
|
|
116
|
+
return process.env.KIT_PAL_LEDGER ?? join(homedir(), ".claude", "pal", "ledger.jsonl");
|
|
117
|
+
}
|
|
118
|
+
/** Import the old `~/.claude/pal/ledger.jsonl` into pending_actions. Idempotent (by id). */
|
|
119
|
+
export function importLegacyLedger(db, path = getLegacyLedgerPath()) {
|
|
120
|
+
if (!existsSync(path))
|
|
121
|
+
return { imported: 0 };
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = readFileSync(path, "utf8");
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return { imported: 0 };
|
|
128
|
+
}
|
|
129
|
+
const insert = db.prepare(`INSERT OR IGNORE INTO pending_actions
|
|
130
|
+
(id, status, title, detail, scope, kind, verify_cmd, created_at, next_check, verify_passes)
|
|
131
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
132
|
+
let imported = 0;
|
|
133
|
+
for (const line of raw.split("\n")) {
|
|
134
|
+
const t = line.trim();
|
|
135
|
+
if (!t)
|
|
136
|
+
continue;
|
|
137
|
+
let e;
|
|
138
|
+
try {
|
|
139
|
+
e = JSON.parse(t);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!e.id || !e.title)
|
|
145
|
+
continue;
|
|
146
|
+
const status = e.status === "done" ? "closed" : (e.status ?? "open");
|
|
147
|
+
const kind = e.verify ? "auto" : "manual";
|
|
148
|
+
const res = insert.run(e.id, status, e.title, e.why ?? null, e.repo ?? null, kind, e.verify ?? null, e.ts ?? null, e.next_check ?? null, e.pass_streak ?? 0);
|
|
149
|
+
if (Number(res.changes) > 0)
|
|
150
|
+
imported++;
|
|
151
|
+
}
|
|
152
|
+
return { imported };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=pal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pal.js","sourceRoot":"","sources":["../../src/memory/pal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export interface IndexResult {
|
|
3
|
+
files: number;
|
|
4
|
+
sessions: number;
|
|
5
|
+
messages: number;
|
|
6
|
+
toolUses: number;
|
|
7
|
+
/** Files skipped because they were unchanged since the last index (incremental). */
|
|
8
|
+
filesSkipped: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function getClaudeProjectsDir(): string;
|
|
11
|
+
interface ContentBlock {
|
|
12
|
+
type?: string;
|
|
13
|
+
text?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
input?: unknown;
|
|
16
|
+
}
|
|
17
|
+
type Content = string | ContentBlock[] | undefined;
|
|
18
|
+
/** Flatten transcript content (string or block array) into searchable plain text. */
|
|
19
|
+
export declare function extractText(content: Content): string;
|
|
20
|
+
/** Extract tool_use blocks from a message's content. */
|
|
21
|
+
export declare function extractToolUses(content: Content): {
|
|
22
|
+
name: string;
|
|
23
|
+
input: string;
|
|
24
|
+
}[];
|
|
25
|
+
/** Walk ~/.claude/projects and index every transcript. Idempotent. */
|
|
26
|
+
export declare function indexClaudeTranscripts(db: DatabaseSync): IndexResult;
|
|
27
|
+
/** Per-harness index results, keyed by harness name. */
|
|
28
|
+
export type HarnessResults = Record<string, IndexResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Index every supported harness's transcripts into the store. Returns per-harness
|
|
31
|
+
* counts. Adding a harness = one more parser here (Copilot, Gemini, Aider, Cursor…).
|
|
32
|
+
*/
|
|
33
|
+
export declare function indexAllHarnesses(db: DatabaseSync): HarnessResults;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
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, isFileIndexed, markFileIndexed, } from "./db.js";
|
|
15
|
+
import { indexCodexSessions } from "./codex.js";
|
|
16
|
+
import { indexGeminiSessions } from "./gemini.js";
|
|
17
|
+
import { indexContinueSessions } from "./continue.js";
|
|
18
|
+
export function getClaudeProjectsDir() {
|
|
19
|
+
const base = process.env.KIT_CLAUDE_DIR ?? join(homedir(), ".claude");
|
|
20
|
+
return join(base, "projects");
|
|
21
|
+
}
|
|
22
|
+
/** Flatten transcript content (string or block array) into searchable plain text. */
|
|
23
|
+
export function extractText(content) {
|
|
24
|
+
if (typeof content === "string")
|
|
25
|
+
return content;
|
|
26
|
+
if (Array.isArray(content)) {
|
|
27
|
+
return content
|
|
28
|
+
.map((b) => {
|
|
29
|
+
if (b?.type === "text")
|
|
30
|
+
return b.text ?? "";
|
|
31
|
+
if (b?.type === "tool_use")
|
|
32
|
+
return `[Tool: ${b.name ?? "unknown"}]`;
|
|
33
|
+
if (b?.type === "tool_result")
|
|
34
|
+
return "[Tool Result]";
|
|
35
|
+
return "";
|
|
36
|
+
})
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join("\n");
|
|
39
|
+
}
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
/** Extract tool_use blocks from a message's content. */
|
|
43
|
+
export function extractToolUses(content) {
|
|
44
|
+
if (!Array.isArray(content))
|
|
45
|
+
return [];
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const b of content) {
|
|
48
|
+
if (b?.type === "tool_use") {
|
|
49
|
+
out.push({
|
|
50
|
+
name: b.name ?? "unknown",
|
|
51
|
+
input: b.input === undefined ? "" : JSON.stringify(b.input),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
function indexFile(db, filepath, project) {
|
|
58
|
+
const sessionId = basename(filepath, ".jsonl");
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
raw = readFileSync(filepath, "utf8");
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return { messages: 0, toolUses: 0 };
|
|
65
|
+
}
|
|
66
|
+
// Ensure the session row exists before inserting messages so per-message
|
|
67
|
+
// counters resolve against a real row.
|
|
68
|
+
upsertSession(db, { sessionId, harness: "claude-code", project });
|
|
69
|
+
let messages = 0;
|
|
70
|
+
let toolUses = 0;
|
|
71
|
+
let firstTs;
|
|
72
|
+
let lastTs;
|
|
73
|
+
let isSidechain = false;
|
|
74
|
+
for (const line of raw.split("\n")) {
|
|
75
|
+
const trimmed = line.trim();
|
|
76
|
+
if (!trimmed)
|
|
77
|
+
continue;
|
|
78
|
+
let rec;
|
|
79
|
+
try {
|
|
80
|
+
rec = JSON.parse(trimmed);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
continue; // skip malformed lines, keep indexing the rest
|
|
84
|
+
}
|
|
85
|
+
if (rec.type !== "user" && rec.type !== "assistant")
|
|
86
|
+
continue;
|
|
87
|
+
if (!rec.uuid)
|
|
88
|
+
continue;
|
|
89
|
+
if (rec.isSidechain)
|
|
90
|
+
isSidechain = true;
|
|
91
|
+
const msg = rec.message;
|
|
92
|
+
const ts = rec.timestamp;
|
|
93
|
+
if (ts) {
|
|
94
|
+
if (!firstTs)
|
|
95
|
+
firstTs = ts;
|
|
96
|
+
lastTs = ts;
|
|
97
|
+
}
|
|
98
|
+
const added = insertMessage(db, {
|
|
99
|
+
uuid: rec.uuid,
|
|
100
|
+
sessionId: rec.sessionId ?? sessionId,
|
|
101
|
+
parentUuid: rec.parentUuid,
|
|
102
|
+
type: rec.type,
|
|
103
|
+
role: msg?.role,
|
|
104
|
+
content: extractText(msg?.content),
|
|
105
|
+
model: msg?.model,
|
|
106
|
+
inputTokens: msg?.usage?.input_tokens,
|
|
107
|
+
outputTokens: msg?.usage?.output_tokens,
|
|
108
|
+
timestamp: ts,
|
|
109
|
+
cwd: rec.cwd,
|
|
110
|
+
gitBranch: rec.gitBranch,
|
|
111
|
+
version: rec.version,
|
|
112
|
+
});
|
|
113
|
+
if (added) {
|
|
114
|
+
messages++;
|
|
115
|
+
for (const tool of extractToolUses(msg?.content)) {
|
|
116
|
+
insertToolUse(db, {
|
|
117
|
+
messageUuid: rec.uuid,
|
|
118
|
+
sessionId: rec.sessionId ?? sessionId,
|
|
119
|
+
toolName: tool.name,
|
|
120
|
+
toolInput: tool.input,
|
|
121
|
+
timestamp: ts,
|
|
122
|
+
});
|
|
123
|
+
toolUses++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Final upsert records the session's time bounds + sidechain flag.
|
|
128
|
+
upsertSession(db, {
|
|
129
|
+
sessionId,
|
|
130
|
+
harness: "claude-code",
|
|
131
|
+
project,
|
|
132
|
+
firstMessageAt: firstTs,
|
|
133
|
+
lastMessageAt: lastTs,
|
|
134
|
+
isAgentSidechain: isSidechain,
|
|
135
|
+
});
|
|
136
|
+
return { messages, toolUses };
|
|
137
|
+
}
|
|
138
|
+
/** Walk ~/.claude/projects and index every transcript. Idempotent. */
|
|
139
|
+
export function indexClaudeTranscripts(db) {
|
|
140
|
+
const projectsDir = getClaudeProjectsDir();
|
|
141
|
+
const result = { files: 0, sessions: 0, messages: 0, toolUses: 0, filesSkipped: 0 };
|
|
142
|
+
if (!existsSync(projectsDir))
|
|
143
|
+
return result;
|
|
144
|
+
for (const projectName of readdirSync(projectsDir).sort()) {
|
|
145
|
+
const projectPath = join(projectsDir, projectName);
|
|
146
|
+
let isDir = false;
|
|
147
|
+
try {
|
|
148
|
+
isDir = statSync(projectPath).isDirectory();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!isDir)
|
|
154
|
+
continue;
|
|
155
|
+
for (const entry of readdirSync(projectPath).sort()) {
|
|
156
|
+
if (!entry.endsWith(".jsonl"))
|
|
157
|
+
continue;
|
|
158
|
+
const filePath = join(projectPath, entry);
|
|
159
|
+
let st;
|
|
160
|
+
try {
|
|
161
|
+
st = statSync(filePath);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Skip files unchanged since the last index (incremental — avoids re-reading
|
|
167
|
+
// every transcript each run; the per-message uuid dedup still backstops it).
|
|
168
|
+
const mtimeMs = Math.floor(st.mtimeMs);
|
|
169
|
+
if (isFileIndexed(db, filePath, mtimeMs, st.size)) {
|
|
170
|
+
result.filesSkipped++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const counts = indexFile(db, filePath, projectName);
|
|
174
|
+
markFileIndexed(db, filePath, mtimeMs, st.size);
|
|
175
|
+
result.files++;
|
|
176
|
+
result.sessions++;
|
|
177
|
+
result.messages += counts.messages;
|
|
178
|
+
result.toolUses += counts.toolUses;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Index every supported harness's transcripts into the store. Returns per-harness
|
|
185
|
+
* counts. Adding a harness = one more parser here (Copilot, Gemini, Aider, Cursor…).
|
|
186
|
+
*/
|
|
187
|
+
export function indexAllHarnesses(db) {
|
|
188
|
+
return {
|
|
189
|
+
"claude-code": indexClaudeTranscripts(db),
|
|
190
|
+
codex: indexCodexSessions(db),
|
|
191
|
+
gemini: indexGeminiSessions(db),
|
|
192
|
+
continue: indexContinueSessions(db),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/memory/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAWtD,MAAM,UAAU,oBAAoB;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAChC,CAAC;AAUD,qFAAqF;AACrF,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM;gBAAE,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;YAC5C,IAAI,CAAC,EAAE,IAAI,KAAK,UAAU;gBAAE,OAAO,UAAU,CAAC,CAAC,IAAI,IAAI,SAAS,GAAG,CAAC;YACpE,IAAI,CAAC,EAAE,IAAI,KAAK,aAAa;gBAAE,OAAO,eAAe,CAAC;YACtD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,GAAG,GAAsC,EAAE,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,SAAS;gBACzB,KAAK,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;aAC5D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAoBD,SAAS,SAAS,CAChB,EAAgB,EAChB,QAAgB,EAChB,OAAe;IAEf,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACtC,CAAC;IAED,yEAAyE;IACzE,uCAAuC;IACvC,aAAa,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;IAElE,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAA2B,CAAC;IAChC,IAAI,MAA0B,CAAC;IAC/B,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,GAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqB,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,+CAA+C;QAC3D,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QAC9D,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,SAAS;QACxB,IAAI,GAAG,CAAC,WAAW;YAAE,WAAW,GAAG,IAAI,CAAC;QAExC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC;QACxB,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC;QACzB,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,OAAO;gBAAE,OAAO,GAAG,EAAE,CAAC;YAC3B,MAAM,GAAG,EAAE,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,EAAE;YAC9B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;YACrC,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,EAAE,IAAI;YACf,OAAO,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC;YAClC,KAAK,EAAE,GAAG,EAAE,KAAK;YACjB,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY;YACrC,YAAY,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa;YACvC,SAAS,EAAE,EAAE;YACb,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAC;QAEH,IAAI,KAAK,EAAE,CAAC;YACV,QAAQ,EAAE,CAAC;YACX,KAAK,MAAM,IAAI,IAAI,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;gBACjD,aAAa,CAAC,EAAE,EAAE;oBAChB,WAAW,EAAE,GAAG,CAAC,IAAI;oBACrB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS;oBACrC,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,SAAS,EAAE,IAAI,CAAC,KAAK;oBACrB,SAAS,EAAE,EAAE;iBACd,CAAC,CAAC;gBACH,QAAQ,EAAE,CAAC;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,aAAa,CAAC,EAAE,EAAE;QAChB,SAAS;QACT,OAAO,EAAE,aAAa;QACtB,OAAO;QACP,cAAc,EAAE,OAAO;QACvB,aAAa,EAAE,MAAM;QACrB,gBAAgB,EAAE,WAAW;KAC9B,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,sBAAsB,CAAC,EAAgB;IACrD,MAAM,WAAW,GAAG,oBAAoB,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACjG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,MAAM,CAAC;IAE5C,KAAK,MAAM,WAAW,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACnD,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;YAC1C,IAAI,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,6EAA6E;YAC7E,6EAA6E;YAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClD,MAAM,CAAC,YAAY,EAAE,CAAC;gBACtB,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;YACpD,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC;YACnC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAKD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAgB;IAChD,OAAO;QACL,aAAa,EAAE,sBAAsB,CAAC,EAAE,CAAC;QACzC,KAAK,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC7B,MAAM,EAAE,mBAAmB,CAAC,EAAE,CAAC;QAC/B,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC;KACpC,CAAC;AACJ,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%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[];
|