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,120 @@
1
+ /**
2
+ * kit memory — shared project / responsibility-area memory (the curated tier).
3
+ *
4
+ * This is CONTEXT, not raw memory: durable, curated, intentional knowledge that
5
+ * is safe to share with the team and travels with the repo. Treated LIKE CODE:
6
+ * - committed TEXT (.kit/shared/memory.jsonl) → diffable, PR-reviewable, gitleaks-scannable;
7
+ * - deny-by-default — nothing is auto-shared, you promote entries explicitly;
8
+ * - allow-listed schema — only safe fields (no raw dumps);
9
+ * - fail-closed secret-scan on write (reuses kit's SECRET_PATTERNS);
10
+ * - provenance + receipts (author + source_ref) so colleagues can trust it.
11
+ *
12
+ * Organized by `area` (e.g. "stripe", "whatsapp", "plugins") so a growing system
13
+ * stays navigable: "how did we build X, what's next, is it secure?" = that area's
14
+ * entries (with receipts). Entries are few (curated) → plain JSONL + JS query; no
15
+ * second database. Querying never calls a model.
16
+ */
17
+ import { randomBytes } from "node:crypto";
18
+ import { execFileSync } from "node:child_process";
19
+ import { existsSync, readFileSync, appendFileSync, mkdirSync } from "node:fs";
20
+ import { join, dirname } from "node:path";
21
+ import { findSecrets } from "../utils/redactSecrets.js";
22
+ export function getSharedPath(root) {
23
+ return join(root, ".kit", "shared", "memory.jsonl");
24
+ }
25
+ function gitAuthor(root) {
26
+ const read = (key) => {
27
+ try {
28
+ return execFileSync("git", ["config", key], {
29
+ cwd: root,
30
+ encoding: "utf8",
31
+ stdio: ["ignore", "pipe", "ignore"],
32
+ }).trim();
33
+ }
34
+ catch {
35
+ return "";
36
+ }
37
+ };
38
+ const name = read("user.name");
39
+ const email = read("user.email");
40
+ if (name && email)
41
+ return `${name} <${email}>`;
42
+ return name || email || "unknown";
43
+ }
44
+ function gitHead(root) {
45
+ try {
46
+ const sha = execFileSync("git", ["rev-parse", "--short", "HEAD"], {
47
+ cwd: root,
48
+ encoding: "utf8",
49
+ stdio: ["ignore", "pipe", "ignore"],
50
+ }).trim();
51
+ return sha || undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ export function readShared(root) {
58
+ const path = getSharedPath(root);
59
+ if (!existsSync(path))
60
+ return [];
61
+ const out = [];
62
+ for (const line of readFileSync(path, "utf8").split("\n")) {
63
+ const t = line.trim();
64
+ if (!t)
65
+ continue;
66
+ try {
67
+ out.push(JSON.parse(t));
68
+ }
69
+ catch {
70
+ // skip malformed lines, keep the rest readable
71
+ }
72
+ }
73
+ return out;
74
+ }
75
+ /**
76
+ * Promote one entry into the shared store. Fail-closed: refuses (throws) if any
77
+ * text field contains a secret. Only allow-listed fields are persisted — no raw
78
+ * tool output / env dumps can sneak in. Author + source_ref give provenance.
79
+ */
80
+ export function shareEntry(root, input, now) {
81
+ const refs = input.refs ?? [];
82
+ const scanned = [input.title, input.body, ...refs].join("\n");
83
+ const found = findSecrets(scanned);
84
+ if (found.length) {
85
+ throw new Error(`refused: entry contains ${found.length} secret(s) (${found
86
+ .map((f) => f.label)
87
+ .join(", ")}) — shared memory must be secret-clean`);
88
+ }
89
+ const entry = {
90
+ id: randomBytes(3).toString("hex"),
91
+ area: input.area,
92
+ kind: input.kind,
93
+ title: input.title,
94
+ body: input.body,
95
+ refs,
96
+ author: gitAuthor(root),
97
+ ts: now,
98
+ source_ref: gitHead(root),
99
+ };
100
+ const path = getSharedPath(root);
101
+ mkdirSync(dirname(path), { recursive: true });
102
+ appendFileSync(path, JSON.stringify(entry) + "\n");
103
+ return entry;
104
+ }
105
+ export function listAreas(root) {
106
+ const counts = new Map();
107
+ for (const e of readShared(root))
108
+ counts.set(e.area, (counts.get(e.area) ?? 0) + 1);
109
+ return [...counts.entries()]
110
+ .map(([area, count]) => ({ area, count }))
111
+ .sort((a, b) => a.area.localeCompare(b.area));
112
+ }
113
+ export function queryArea(root, area) {
114
+ return readShared(root).filter((e) => e.area === area);
115
+ }
116
+ export function searchShared(root, query) {
117
+ const q = query.toLowerCase();
118
+ return readShared(root).filter((e) => e.title.toLowerCase().includes(q) || e.body.toLowerCase().includes(q));
119
+ }
120
+ //# sourceMappingURL=shared%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared 2.js","sourceRoot":"","sources":["../../src/memory/shared 2.ts"],"names":[],"mappings":"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"}
@@ -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[];
@@ -0,0 +1,120 @@
1
+ /**
2
+ * kit memory — shared project / responsibility-area memory (the curated tier).
3
+ *
4
+ * This is CONTEXT, not raw memory: durable, curated, intentional knowledge that
5
+ * is safe to share with the team and travels with the repo. Treated LIKE CODE:
6
+ * - committed TEXT (.kit/shared/memory.jsonl) → diffable, PR-reviewable, gitleaks-scannable;
7
+ * - deny-by-default — nothing is auto-shared, you promote entries explicitly;
8
+ * - allow-listed schema — only safe fields (no raw dumps);
9
+ * - fail-closed secret-scan on write (reuses kit's SECRET_PATTERNS);
10
+ * - provenance + receipts (author + source_ref) so colleagues can trust it.
11
+ *
12
+ * Organized by `area` (e.g. "stripe", "whatsapp", "plugins") so a growing system
13
+ * stays navigable: "how did we build X, what's next, is it secure?" = that area's
14
+ * entries (with receipts). Entries are few (curated) → plain JSONL + JS query; no
15
+ * second database. Querying never calls a model.
16
+ */
17
+ import { randomBytes } from "node:crypto";
18
+ import { execFileSync } from "node:child_process";
19
+ import { existsSync, readFileSync, appendFileSync, mkdirSync } from "node:fs";
20
+ import { join, dirname } from "node:path";
21
+ import { findSecrets } from "../utils/redactSecrets.js";
22
+ export function getSharedPath(root) {
23
+ return join(root, ".kit", "shared", "memory.jsonl");
24
+ }
25
+ function gitAuthor(root) {
26
+ const read = (key) => {
27
+ try {
28
+ return execFileSync("git", ["config", key], {
29
+ cwd: root,
30
+ encoding: "utf8",
31
+ stdio: ["ignore", "pipe", "ignore"],
32
+ }).trim();
33
+ }
34
+ catch {
35
+ return "";
36
+ }
37
+ };
38
+ const name = read("user.name");
39
+ const email = read("user.email");
40
+ if (name && email)
41
+ return `${name} <${email}>`;
42
+ return name || email || "unknown";
43
+ }
44
+ function gitHead(root) {
45
+ try {
46
+ const sha = execFileSync("git", ["rev-parse", "--short", "HEAD"], {
47
+ cwd: root,
48
+ encoding: "utf8",
49
+ stdio: ["ignore", "pipe", "ignore"],
50
+ }).trim();
51
+ return sha || undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ export function readShared(root) {
58
+ const path = getSharedPath(root);
59
+ if (!existsSync(path))
60
+ return [];
61
+ const out = [];
62
+ for (const line of readFileSync(path, "utf8").split("\n")) {
63
+ const t = line.trim();
64
+ if (!t)
65
+ continue;
66
+ try {
67
+ out.push(JSON.parse(t));
68
+ }
69
+ catch {
70
+ // skip malformed lines, keep the rest readable
71
+ }
72
+ }
73
+ return out;
74
+ }
75
+ /**
76
+ * Promote one entry into the shared store. Fail-closed: refuses (throws) if any
77
+ * text field contains a secret. Only allow-listed fields are persisted — no raw
78
+ * tool output / env dumps can sneak in. Author + source_ref give provenance.
79
+ */
80
+ export function shareEntry(root, input, now) {
81
+ const refs = input.refs ?? [];
82
+ const scanned = [input.title, input.body, ...refs].join("\n");
83
+ const found = findSecrets(scanned);
84
+ if (found.length) {
85
+ throw new Error(`refused: entry contains ${found.length} secret(s) (${found
86
+ .map((f) => f.label)
87
+ .join(", ")}) — shared memory must be secret-clean`);
88
+ }
89
+ const entry = {
90
+ id: randomBytes(3).toString("hex"),
91
+ area: input.area,
92
+ kind: input.kind,
93
+ title: input.title,
94
+ body: input.body,
95
+ refs,
96
+ author: gitAuthor(root),
97
+ ts: now,
98
+ source_ref: gitHead(root),
99
+ };
100
+ const path = getSharedPath(root);
101
+ mkdirSync(dirname(path), { recursive: true });
102
+ appendFileSync(path, JSON.stringify(entry) + "\n");
103
+ return entry;
104
+ }
105
+ export function listAreas(root) {
106
+ const counts = new Map();
107
+ for (const e of readShared(root))
108
+ counts.set(e.area, (counts.get(e.area) ?? 0) + 1);
109
+ return [...counts.entries()]
110
+ .map(([area, count]) => ({ area, count }))
111
+ .sort((a, b) => a.area.localeCompare(b.area));
112
+ }
113
+ export function queryArea(root, area) {
114
+ return readShared(root).filter((e) => e.area === area);
115
+ }
116
+ export function searchShared(root, query) {
117
+ const q = query.toLowerCase();
118
+ return readShared(root).filter((e) => e.title.toLowerCase().includes(q) || e.body.toLowerCase().includes(q));
119
+ }
120
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/memory/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * kit memory — named copilots (saved threads).
3
+ *
4
+ * Claude Code's resume list labels sessions by the first message you happened to
5
+ * type, so the thread you want is unfindable. Here you bookmark the threads worth
6
+ * keeping under a real name ("a fleet of named copilots"); a small curated list
7
+ * replaces the scrap heap. Scoped per project by default (the personal store holds
8
+ * all projects; this just filters). Pure read/write — no model calls.
9
+ */
10
+ import type { DatabaseSync } from "node:sqlite";
11
+ export interface SavedThread {
12
+ name: string;
13
+ session_id: string;
14
+ summary: string | null;
15
+ project_path: string | null;
16
+ saved_at: string | null;
17
+ }
18
+ export interface SaveThreadInput {
19
+ name: string;
20
+ sessionId: string;
21
+ summary?: string;
22
+ projectPath?: string;
23
+ }
24
+ export declare function saveThread(db: DatabaseSync, input: SaveThreadInput): void;
25
+ export declare function listThreads(db: DatabaseSync, opts?: {
26
+ projectPath?: string;
27
+ }): SavedThread[];
28
+ export declare function getThread(db: DatabaseSync, name: string): SavedThread | undefined;
29
+ export declare function removeThread(db: DatabaseSync, name: string): boolean;
30
+ /** Most recent session that touched this project (by message timestamp). */
31
+ export declare function latestSessionId(db: DatabaseSync, opts?: {
32
+ projectPath?: string;
33
+ }): string | undefined;
34
+ /** Resolve a thread by name, or by 1-based index into the (scoped) list. */
35
+ export declare function resolveThread(db: DatabaseSync, ref: string, opts?: {
36
+ projectPath?: string;
37
+ }): SavedThread | undefined;
@@ -0,0 +1,50 @@
1
+ export function saveThread(db, input) {
2
+ db.prepare(`INSERT INTO saved_threads (name, session_id, summary, project_path, saved_at)
3
+ VALUES (?, ?, ?, ?, datetime('now'))
4
+ ON CONFLICT(name) DO UPDATE SET
5
+ session_id = excluded.session_id,
6
+ summary = COALESCE(excluded.summary, saved_threads.summary),
7
+ project_path = COALESCE(excluded.project_path, saved_threads.project_path),
8
+ saved_at = datetime('now')`).run(input.name, input.sessionId, input.summary ?? null, input.projectPath ?? null);
9
+ }
10
+ export function listThreads(db, opts = {}) {
11
+ if (opts.projectPath) {
12
+ return db
13
+ .prepare("SELECT * FROM saved_threads WHERE project_path = ? ORDER BY saved_at DESC")
14
+ .all(opts.projectPath);
15
+ }
16
+ return db
17
+ .prepare("SELECT * FROM saved_threads ORDER BY saved_at DESC")
18
+ .all();
19
+ }
20
+ export function getThread(db, name) {
21
+ return db.prepare("SELECT * FROM saved_threads WHERE name = ?").get(name);
22
+ }
23
+ export function removeThread(db, name) {
24
+ return Number(db.prepare("DELETE FROM saved_threads WHERE name = ?").run(name).changes) > 0;
25
+ }
26
+ /** Most recent session that touched this project (by message timestamp). */
27
+ export function latestSessionId(db, opts = {}) {
28
+ let row;
29
+ if (opts.projectPath) {
30
+ row = db
31
+ .prepare(`SELECT session_id FROM messages
32
+ WHERE cwd = ? OR cwd LIKE ?
33
+ ORDER BY timestamp DESC LIMIT 1`)
34
+ .get(opts.projectPath, `${opts.projectPath}/%`);
35
+ }
36
+ else {
37
+ row = db
38
+ .prepare("SELECT session_id FROM messages ORDER BY timestamp DESC LIMIT 1")
39
+ .get();
40
+ }
41
+ return row?.session_id;
42
+ }
43
+ /** Resolve a thread by name, or by 1-based index into the (scoped) list. */
44
+ export function resolveThread(db, ref, opts = {}) {
45
+ if (/^\d+$/.test(ref)) {
46
+ return listThreads(db, opts)[Number(ref) - 1];
47
+ }
48
+ return getThread(db, ref);
49
+ }
50
+ //# sourceMappingURL=threads%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads 2.js","sourceRoot":"","sources":["../../src/memory/threads 2.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,UAAU,CAAC,EAAgB,EAAE,KAAsB;IACjE,EAAE,CAAC,OAAO,CACR;;;;;;kCAM8B,CAC/B,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,EAAgB,EAChB,OAAiC,EAAE;IAEnC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,OAAO,EAAE;aACN,OAAO,CAAC,2EAA2E,CAAC;aACpF,GAAG,CAAC,IAAI,CAAC,WAAW,CAA6B,CAAC;IACvD,CAAC;IACD,OAAO,EAAE;SACN,OAAO,CAAC,oDAAoD,CAAC;SAC7D,GAAG,EAA8B,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAgB,EAAE,IAAY;IACtD,OAAO,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,IAAI,CAE3D,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAgB,EAAE,IAAY;IACzD,OAAO,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAC7B,EAAgB,EAChB,OAAiC,EAAE;IAEnC,IAAI,GAAuC,CAAC;IAC5C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,GAAG,EAAE;aACL,OAAO,CACN;;yCAEiC,CAClC;aACA,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,WAAW,IAAI,CAAuC,CAAC;IAC1F,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,EAAE;aACL,OAAO,CAAC,iEAAiE,CAAC;aAC1E,GAAG,EAAwC,CAAC;IACjD,CAAC;IACD,OAAO,GAAG,EAAE,UAAU,CAAC;AACzB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAC3B,EAAgB,EAChB,GAAW,EACX,OAAiC,EAAE;IAEnC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * kit memory — named copilots (saved threads).
3
+ *
4
+ * Claude Code's resume list labels sessions by the first message you happened to
5
+ * type, so the thread you want is unfindable. Here you bookmark the threads worth
6
+ * keeping under a real name ("a fleet of named copilots"); a small curated list
7
+ * replaces the scrap heap. Scoped per project by default (the personal store holds
8
+ * all projects; this just filters). Pure read/write — no model calls.
9
+ */
10
+ import type { DatabaseSync } from "node:sqlite";
11
+ export interface SavedThread {
12
+ name: string;
13
+ session_id: string;
14
+ summary: string | null;
15
+ project_path: string | null;
16
+ saved_at: string | null;
17
+ }
18
+ export interface SaveThreadInput {
19
+ name: string;
20
+ sessionId: string;
21
+ summary?: string;
22
+ projectPath?: string;
23
+ }
24
+ export declare function saveThread(db: DatabaseSync, input: SaveThreadInput): void;
25
+ export declare function listThreads(db: DatabaseSync, opts?: {
26
+ projectPath?: string;
27
+ }): SavedThread[];
28
+ export declare function getThread(db: DatabaseSync, name: string): SavedThread | undefined;
29
+ export declare function removeThread(db: DatabaseSync, name: string): boolean;
30
+ /** Most recent session that touched this project (by message timestamp). */
31
+ export declare function latestSessionId(db: DatabaseSync, opts?: {
32
+ projectPath?: string;
33
+ }): string | undefined;
34
+ /** Resolve a thread by name, or by 1-based index into the (scoped) list. */
35
+ export declare function resolveThread(db: DatabaseSync, ref: string, opts?: {
36
+ projectPath?: string;
37
+ }): SavedThread | undefined;
@@ -0,0 +1,50 @@
1
+ export function saveThread(db, input) {
2
+ db.prepare(`INSERT INTO saved_threads (name, session_id, summary, project_path, saved_at)
3
+ VALUES (?, ?, ?, ?, datetime('now'))
4
+ ON CONFLICT(name) DO UPDATE SET
5
+ session_id = excluded.session_id,
6
+ summary = COALESCE(excluded.summary, saved_threads.summary),
7
+ project_path = COALESCE(excluded.project_path, saved_threads.project_path),
8
+ saved_at = datetime('now')`).run(input.name, input.sessionId, input.summary ?? null, input.projectPath ?? null);
9
+ }
10
+ export function listThreads(db, opts = {}) {
11
+ if (opts.projectPath) {
12
+ return db
13
+ .prepare("SELECT * FROM saved_threads WHERE project_path = ? ORDER BY saved_at DESC")
14
+ .all(opts.projectPath);
15
+ }
16
+ return db
17
+ .prepare("SELECT * FROM saved_threads ORDER BY saved_at DESC")
18
+ .all();
19
+ }
20
+ export function getThread(db, name) {
21
+ return db.prepare("SELECT * FROM saved_threads WHERE name = ?").get(name);
22
+ }
23
+ export function removeThread(db, name) {
24
+ return Number(db.prepare("DELETE FROM saved_threads WHERE name = ?").run(name).changes) > 0;
25
+ }
26
+ /** Most recent session that touched this project (by message timestamp). */
27
+ export function latestSessionId(db, opts = {}) {
28
+ let row;
29
+ if (opts.projectPath) {
30
+ row = db
31
+ .prepare(`SELECT session_id FROM messages
32
+ WHERE cwd = ? OR cwd LIKE ?
33
+ ORDER BY timestamp DESC LIMIT 1`)
34
+ .get(opts.projectPath, `${opts.projectPath}/%`);
35
+ }
36
+ else {
37
+ row = db
38
+ .prepare("SELECT session_id FROM messages ORDER BY timestamp DESC LIMIT 1")
39
+ .get();
40
+ }
41
+ return row?.session_id;
42
+ }
43
+ /** Resolve a thread by name, or by 1-based index into the (scoped) list. */
44
+ export function resolveThread(db, ref, opts = {}) {
45
+ if (/^\d+$/.test(ref)) {
46
+ return listThreads(db, opts)[Number(ref) - 1];
47
+ }
48
+ return getThread(db, ref);
49
+ }
50
+ //# sourceMappingURL=threads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/memory/threads.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,UAAU,CAAC,EAAgB,EAAE,KAAsB;IACjE,EAAE,CAAC,OAAO,CACR;;;;;;kCAM8B,CAC/B,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,EAAgB,EAChB,OAAiC,EAAE;IAEnC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,OAAO,EAAE;aACN,OAAO,CAAC,2EAA2E,CAAC;aACpF,GAAG,CAAC,IAAI,CAAC,WAAW,CAA6B,CAAC;IACvD,CAAC;IACD,OAAO,EAAE;SACN,OAAO,CAAC,oDAAoD,CAAC;SAC7D,GAAG,EAA8B,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAgB,EAAE,IAAY;IACtD,OAAO,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,IAAI,CAE3D,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAgB,EAAE,IAAY;IACzD,OAAO,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAC7B,EAAgB,EAChB,OAAiC,EAAE;IAEnC,IAAI,GAAuC,CAAC;IAC5C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,GAAG,EAAE;aACL,OAAO,CACN;;yCAEiC,CAClC;aACA,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,WAAW,IAAI,CAAuC,CAAC;IAC1F,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,EAAE;aACL,OAAO,CAAC,iEAAiE,CAAC;aAC1E,GAAG,EAAwC,CAAC;IACjD,CAAC;IACD,OAAO,GAAG,EAAE,UAAU,CAAC;AACzB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAC3B,EAAgB,EAChB,GAAW,EACX,OAAiC,EAAE;IAEnC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { openMemoryDb, upsertSession, insertMessage } from "./db.js";
4
+ import { saveThread, listThreads, getThread, removeThread, latestSessionId, resolveThread, } from "./threads.js";
5
+ describe("memory threads (named copilots)", () => {
6
+ const fresh = () => openMemoryDb(":memory:");
7
+ it("save + list + get; name is the key (re-save updates)", () => {
8
+ const db = fresh();
9
+ saveThread(db, { name: "launch", sessionId: "s1", projectPath: "/repo/app-a" });
10
+ saveThread(db, { name: "launch", sessionId: "s2", projectPath: "/repo/app-a" });
11
+ assert.equal(listThreads(db).length, 1);
12
+ assert.equal(getThread(db, "launch")?.session_id, "s2");
13
+ db.close();
14
+ });
15
+ it("lists scoped by project", () => {
16
+ const db = fresh();
17
+ saveThread(db, { name: "a", sessionId: "s1", projectPath: "/repo/app-a" });
18
+ saveThread(db, { name: "b", sessionId: "s2", projectPath: "/repo/app-b" });
19
+ assert.equal(listThreads(db).length, 2);
20
+ const scoped = listThreads(db, { projectPath: "/repo/app-a" });
21
+ assert.equal(scoped.length, 1);
22
+ assert.equal(scoped[0]?.name, "a");
23
+ db.close();
24
+ });
25
+ it("remove tosses a thread", () => {
26
+ const db = fresh();
27
+ saveThread(db, { name: "x", sessionId: "s1" });
28
+ assert.equal(removeThread(db, "x"), true);
29
+ assert.equal(listThreads(db).length, 0);
30
+ assert.equal(removeThread(db, "x"), false);
31
+ db.close();
32
+ });
33
+ it("latestSessionId picks the most recent session touching the project", () => {
34
+ const db = fresh();
35
+ upsertSession(db, { sessionId: "old", harness: "claude-code" });
36
+ upsertSession(db, { sessionId: "new", harness: "claude-code" });
37
+ insertMessage(db, {
38
+ uuid: "m1",
39
+ sessionId: "old",
40
+ type: "user",
41
+ content: "a",
42
+ cwd: "/repo/app-a",
43
+ timestamp: "2026-06-01T00:00:00Z",
44
+ });
45
+ insertMessage(db, {
46
+ uuid: "m2",
47
+ sessionId: "new",
48
+ type: "user",
49
+ content: "b",
50
+ cwd: "/repo/app-a",
51
+ timestamp: "2026-06-02T00:00:00Z",
52
+ });
53
+ assert.equal(latestSessionId(db, { projectPath: "/repo/app-a" }), "new");
54
+ db.close();
55
+ });
56
+ it("resolveThread accepts a name or a 1-based index", () => {
57
+ const db = fresh();
58
+ saveThread(db, { name: "first", sessionId: "s1", projectPath: "/repo/app-a" });
59
+ saveThread(db, { name: "second", sessionId: "s2", projectPath: "/repo/app-a" });
60
+ assert.equal(resolveThread(db, "first", { projectPath: "/repo/app-a" })?.session_id, "s1");
61
+ const list = listThreads(db, { projectPath: "/repo/app-a" });
62
+ assert.equal(resolveThread(db, "1", { projectPath: "/repo/app-a" })?.name, list[0]?.name);
63
+ db.close();
64
+ });
65
+ });
66
+ //# sourceMappingURL=threads.test%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads.test 2.js","sourceRoot":"","sources":["../../src/memory/threads.test 2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * kit memory — shared types for the local conversation store.
3
+ */
4
+ export interface SessionInput {
5
+ sessionId: string;
6
+ /** Which agent harness produced the session: claude-code | codex | cursor | … */
7
+ harness: string;
8
+ project?: string;
9
+ firstMessageAt?: string;
10
+ lastMessageAt?: string;
11
+ isAgentSidechain?: boolean;
12
+ }
13
+ export interface MessageInput {
14
+ /** Stable id from the transcript — used for idempotent upsert (one row per message). */
15
+ uuid: string;
16
+ sessionId: string;
17
+ parentUuid?: string;
18
+ /** Transcript event type, e.g. "user" | "assistant". */
19
+ type: string;
20
+ role?: string;
21
+ content?: string;
22
+ model?: string;
23
+ inputTokens?: number;
24
+ outputTokens?: number;
25
+ timestamp?: string;
26
+ cwd?: string;
27
+ gitBranch?: string;
28
+ version?: string;
29
+ }
30
+ export interface ToolUseInput {
31
+ messageUuid?: string;
32
+ sessionId?: string;
33
+ toolName: string;
34
+ toolInput?: string;
35
+ timestamp?: string;
36
+ }
37
+ export interface SearchHit {
38
+ id: number;
39
+ uuid: string | null;
40
+ sessionId: string;
41
+ role: string | null;
42
+ content: string | null;
43
+ timestamp: string | null;
44
+ }
45
+ export interface MemoryStats {
46
+ sessions: number;
47
+ messages: number;
48
+ toolUses: number;
49
+ pendingOpen: number;
50
+ dbPath: string;
51
+ sizeBytes: number;
52
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * kit memory — shared types for the local conversation store.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types%202.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types 2.js","sourceRoot":"","sources":["../../src/memory/types 2.ts"],"names":[],"mappings":"AAAA;;GAEG"}