coding-friend-cli 1.16.0 → 1.17.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 (75) hide show
  1. package/README.md +12 -0
  2. package/dist/{chunk-D4EWPGBL.js → chunk-C5LYVVEI.js} +1 -1
  3. package/dist/{chunk-X5WEODUD.js → chunk-CYQU33FY.js} +1 -0
  4. package/dist/{chunk-QNLL3ZDF.js → chunk-G6CEEMAR.js} +3 -3
  5. package/dist/{chunk-4DB4XTSL.js → chunk-KTX4MGMR.js} +15 -1
  6. package/dist/{chunk-KJUGTLPQ.js → chunk-YO6JKGR3.js} +38 -2
  7. package/dist/{config-AIZJJ5D2.js → config-LZFXXOI4.js} +276 -14
  8. package/dist/{dev-WJ5QQ35B.js → dev-R3IYWZ3M.js} +2 -2
  9. package/dist/{disable-JDVOQNZG.js → disable-R6K5YJN4.js} +2 -2
  10. package/dist/{enable-JBJ4Q2S7.js → enable-HF4PYVJN.js} +2 -2
  11. package/dist/{host-NA7LZ4HX.js → host-SYZH3FVC.js} +4 -4
  12. package/dist/index.js +78 -18
  13. package/dist/{init-FZ3GG53E.js → init-YK6YRTOT.js} +102 -6
  14. package/dist/{install-I3GOS56Q.js → install-Q4PWEU43.js} +4 -4
  15. package/dist/{mcp-DLS3J6QJ.js → mcp-TBEDYELW.js} +4 -4
  16. package/dist/memory-7RM67ZLS.js +668 -0
  17. package/dist/postinstall.js +1 -1
  18. package/dist/{session-E3CZJJZQ.js → session-H4XW2WXH.js} +1 -1
  19. package/dist/{statusline-6HQCDWBD.js → statusline-6Y2EBAFQ.js} +1 -1
  20. package/dist/{uninstall-JN5YIKKM.js → uninstall-3PSUDGI4.js} +3 -3
  21. package/dist/{update-OWS4IJTG.js → update-WL6SFGGO.js} +4 -4
  22. package/lib/cf-memory/CHANGELOG.md +15 -0
  23. package/lib/cf-memory/README.md +284 -0
  24. package/lib/cf-memory/package-lock.json +2790 -0
  25. package/lib/cf-memory/package.json +31 -0
  26. package/lib/cf-memory/scripts/migrate-frontmatter.ts +134 -0
  27. package/lib/cf-memory/src/__tests__/daemon-e2e.test.ts +223 -0
  28. package/lib/cf-memory/src/__tests__/daemon.test.ts +407 -0
  29. package/lib/cf-memory/src/__tests__/dedup.test.ts +103 -0
  30. package/lib/cf-memory/src/__tests__/embeddings.test.ts +292 -0
  31. package/lib/cf-memory/src/__tests__/lazy-install.test.ts +210 -0
  32. package/lib/cf-memory/src/__tests__/markdown-backend.test.ts +410 -0
  33. package/lib/cf-memory/src/__tests__/migration.test.ts +255 -0
  34. package/lib/cf-memory/src/__tests__/migrations.test.ts +288 -0
  35. package/lib/cf-memory/src/__tests__/minisearch-backend.test.ts +262 -0
  36. package/lib/cf-memory/src/__tests__/ollama.test.ts +48 -0
  37. package/lib/cf-memory/src/__tests__/schema.test.ts +128 -0
  38. package/lib/cf-memory/src/__tests__/search.test.ts +115 -0
  39. package/lib/cf-memory/src/__tests__/temporal-decay.test.ts +54 -0
  40. package/lib/cf-memory/src/__tests__/tier.test.ts +293 -0
  41. package/lib/cf-memory/src/__tests__/tools.test.ts +83 -0
  42. package/lib/cf-memory/src/backends/markdown.ts +318 -0
  43. package/lib/cf-memory/src/backends/minisearch.ts +203 -0
  44. package/lib/cf-memory/src/backends/sqlite/embeddings.ts +286 -0
  45. package/lib/cf-memory/src/backends/sqlite/index.ts +549 -0
  46. package/lib/cf-memory/src/backends/sqlite/migrations.ts +188 -0
  47. package/lib/cf-memory/src/backends/sqlite/schema.ts +120 -0
  48. package/lib/cf-memory/src/backends/sqlite/search.ts +296 -0
  49. package/lib/cf-memory/src/bin/cf-memory.ts +2 -0
  50. package/lib/cf-memory/src/daemon/entry.ts +99 -0
  51. package/lib/cf-memory/src/daemon/process.ts +220 -0
  52. package/lib/cf-memory/src/daemon/server.ts +166 -0
  53. package/lib/cf-memory/src/daemon/watcher.ts +90 -0
  54. package/lib/cf-memory/src/index.ts +45 -0
  55. package/lib/cf-memory/src/lib/backend.ts +23 -0
  56. package/lib/cf-memory/src/lib/daemon-client.ts +163 -0
  57. package/lib/cf-memory/src/lib/dedup.ts +80 -0
  58. package/lib/cf-memory/src/lib/lazy-install.ts +274 -0
  59. package/lib/cf-memory/src/lib/ollama.ts +76 -0
  60. package/lib/cf-memory/src/lib/temporal-decay.ts +19 -0
  61. package/lib/cf-memory/src/lib/tier.ts +107 -0
  62. package/lib/cf-memory/src/lib/types.ts +109 -0
  63. package/lib/cf-memory/src/resources/index.ts +62 -0
  64. package/lib/cf-memory/src/server.ts +20 -0
  65. package/lib/cf-memory/src/tools/delete.ts +38 -0
  66. package/lib/cf-memory/src/tools/list.ts +38 -0
  67. package/lib/cf-memory/src/tools/retrieve.ts +52 -0
  68. package/lib/cf-memory/src/tools/search.ts +47 -0
  69. package/lib/cf-memory/src/tools/store.ts +70 -0
  70. package/lib/cf-memory/src/tools/update.ts +62 -0
  71. package/lib/cf-memory/tsconfig.json +15 -0
  72. package/lib/cf-memory/vitest.config.ts +7 -0
  73. package/lib/learn-host/CHANGELOG.md +4 -0
  74. package/lib/learn-host/package.json +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,292 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import {
3
+ contentHash,
4
+ prepareEmbeddingText,
5
+ MODEL_DIMS,
6
+ DEFAULT_EMBEDDING_DIMS,
7
+ resolveModelDims,
8
+ EmbeddingPipeline,
9
+ EmbeddingCache,
10
+ } from "../backends/sqlite/embeddings.js";
11
+ import type { DatabaseLike } from "../backends/sqlite/migrations.js";
12
+
13
+ describe("contentHash()", () => {
14
+ it("returns a 16-character hex string", () => {
15
+ const hash = contentHash("hello world");
16
+ expect(hash).toHaveLength(16);
17
+ expect(hash).toMatch(/^[0-9a-f]+$/);
18
+ });
19
+
20
+ it("returns consistent hashes for the same input", () => {
21
+ const h1 = contentHash("test content");
22
+ const h2 = contentHash("test content");
23
+ expect(h1).toBe(h2);
24
+ });
25
+
26
+ it("returns different hashes for different inputs", () => {
27
+ const h1 = contentHash("content A");
28
+ const h2 = contentHash("content B");
29
+ expect(h1).not.toBe(h2);
30
+ });
31
+ });
32
+
33
+ describe("prepareEmbeddingText()", () => {
34
+ it("combines title, description, tags, and content", () => {
35
+ const text = prepareEmbeddingText({
36
+ title: "API Auth",
37
+ description: "JWT tokens in httpOnly cookies",
38
+ tags: ["auth", "jwt"],
39
+ content: "The project uses JWT.",
40
+ });
41
+
42
+ expect(text).toContain("API Auth");
43
+ expect(text).toContain("JWT tokens");
44
+ expect(text).toContain("auth jwt");
45
+ expect(text).toContain("The project uses JWT.");
46
+ });
47
+
48
+ it("handles empty tags", () => {
49
+ const text = prepareEmbeddingText({
50
+ title: "Title",
51
+ description: "Desc",
52
+ tags: [],
53
+ content: "Content",
54
+ });
55
+
56
+ expect(text).toContain("Title");
57
+ expect(text).toContain("Desc");
58
+ expect(text).toContain("Content");
59
+ });
60
+
61
+ it("truncates long content to ~2000 chars", () => {
62
+ const longContent = "a".repeat(5000);
63
+ const text = prepareEmbeddingText({
64
+ title: "T",
65
+ description: "D",
66
+ tags: [],
67
+ content: longContent,
68
+ });
69
+
70
+ // Content portion should be truncated
71
+ expect(text.length).toBeLessThan(2200);
72
+ });
73
+
74
+ it("filters empty fields", () => {
75
+ const text = prepareEmbeddingText({
76
+ title: "Title",
77
+ description: "",
78
+ tags: [],
79
+ content: "Content",
80
+ });
81
+
82
+ // Should not have double newlines from empty fields
83
+ expect(text).not.toContain("\n\n\n");
84
+ });
85
+ });
86
+
87
+ describe("MODEL_DIMS", () => {
88
+ it("contains transformers.js model entries", () => {
89
+ expect(MODEL_DIMS["Xenova/all-MiniLM-L6-v2"]).toBe(384);
90
+ expect(MODEL_DIMS["Xenova/all-MiniLM-L12-v2"]).toBe(384);
91
+ });
92
+
93
+ it("contains ollama model entries", () => {
94
+ expect(MODEL_DIMS["all-minilm:l6-v2"]).toBe(384);
95
+ expect(MODEL_DIMS["all-minilm"]).toBe(384);
96
+ expect(MODEL_DIMS["nomic-embed-text"]).toBe(768);
97
+ expect(MODEL_DIMS["mxbai-embed-large"]).toBe(1024);
98
+ });
99
+
100
+ it("contains snowflake-arctic-embed variants", () => {
101
+ expect(MODEL_DIMS["snowflake-arctic-embed:s"]).toBe(384);
102
+ expect(MODEL_DIMS["snowflake-arctic-embed:m"]).toBe(768);
103
+ expect(MODEL_DIMS["snowflake-arctic-embed:l"]).toBe(1024);
104
+ });
105
+
106
+ it("contains bge model variants", () => {
107
+ expect(MODEL_DIMS["bge-small-en-v1.5"]).toBe(384);
108
+ expect(MODEL_DIMS["bge-base-en-v1.5"]).toBe(768);
109
+ expect(MODEL_DIMS["bge-large-en-v1.5"]).toBe(1024);
110
+ });
111
+ });
112
+
113
+ describe("DEFAULT_EMBEDDING_DIMS", () => {
114
+ it("is 384", () => {
115
+ expect(DEFAULT_EMBEDDING_DIMS).toBe(384);
116
+ });
117
+ });
118
+
119
+ describe("resolveModelDims()", () => {
120
+ it("returns correct dims for known transformers model", () => {
121
+ expect(resolveModelDims("Xenova/all-MiniLM-L6-v2", "transformers")).toBe(
122
+ 384,
123
+ );
124
+ });
125
+
126
+ it("returns correct dims for known ollama model", () => {
127
+ expect(resolveModelDims("nomic-embed-text", "ollama")).toBe(768);
128
+ });
129
+
130
+ it("returns correct dims for mxbai-embed-large", () => {
131
+ expect(resolveModelDims("mxbai-embed-large", "ollama")).toBe(1024);
132
+ });
133
+
134
+ it("returns default 384 for undefined model", () => {
135
+ expect(resolveModelDims(undefined, "transformers")).toBe(384);
136
+ });
137
+
138
+ it("returns default 384 for unknown model and logs warning", () => {
139
+ const stderrSpy = vi
140
+ .spyOn(process.stderr, "write")
141
+ .mockImplementation(() => true);
142
+ const dims = resolveModelDims("some-unknown-model", "ollama");
143
+ expect(dims).toBe(384);
144
+ expect(stderrSpy).toHaveBeenCalledWith(
145
+ expect.stringContaining("some-unknown-model"),
146
+ );
147
+ stderrSpy.mockRestore();
148
+ });
149
+ });
150
+
151
+ describe("EmbeddingPipeline.dims", () => {
152
+ it("returns 384 for default transformers config", () => {
153
+ const pipeline = new EmbeddingPipeline();
154
+ expect(pipeline.dims).toBe(384);
155
+ });
156
+
157
+ it("returns 384 for default ollama config", () => {
158
+ const pipeline = new EmbeddingPipeline({ provider: "ollama" });
159
+ expect(pipeline.dims).toBe(384);
160
+ });
161
+
162
+ it("returns 768 for nomic-embed-text ollama model", () => {
163
+ const pipeline = new EmbeddingPipeline({
164
+ provider: "ollama",
165
+ model: "nomic-embed-text",
166
+ });
167
+ expect(pipeline.dims).toBe(768);
168
+ });
169
+
170
+ it("returns 1024 for mxbai-embed-large ollama model", () => {
171
+ const pipeline = new EmbeddingPipeline({
172
+ provider: "ollama",
173
+ model: "mxbai-embed-large",
174
+ });
175
+ expect(pipeline.dims).toBe(1024);
176
+ });
177
+ });
178
+
179
+ describe("EmbeddingPipeline.modelName", () => {
180
+ it("returns default transformers model when no model specified", () => {
181
+ const pipeline = new EmbeddingPipeline();
182
+ expect(pipeline.modelName).toBe("Xenova/all-MiniLM-L6-v2");
183
+ });
184
+
185
+ it("returns default ollama model when no model specified", () => {
186
+ const pipeline = new EmbeddingPipeline({ provider: "ollama" });
187
+ expect(pipeline.modelName).toBe("all-minilm:l6-v2");
188
+ });
189
+
190
+ it("returns configured model name", () => {
191
+ const pipeline = new EmbeddingPipeline({
192
+ provider: "ollama",
193
+ model: "nomic-embed-text",
194
+ });
195
+ expect(pipeline.modelName).toBe("nomic-embed-text");
196
+ });
197
+ });
198
+
199
+ describe("EmbeddingCache.get() with dims parameter", () => {
200
+ function createMockCacheDb(embeddingData?: {
201
+ hash: string;
202
+ embedding: Buffer;
203
+ }): DatabaseLike {
204
+ return {
205
+ exec() {},
206
+ pragma() {
207
+ return undefined;
208
+ },
209
+ prepare(sql: string) {
210
+ return {
211
+ get(...params: unknown[]) {
212
+ if (
213
+ sql.includes("SELECT embedding FROM embedding_cache") &&
214
+ embeddingData &&
215
+ params[0] === embeddingData.hash
216
+ ) {
217
+ return { embedding: embeddingData.embedding };
218
+ }
219
+ return undefined;
220
+ },
221
+ run() {
222
+ return {};
223
+ },
224
+ };
225
+ },
226
+ };
227
+ }
228
+
229
+ it("returns Float32Array with specified dims", () => {
230
+ const dims = 768;
231
+ const floats = new Float32Array(dims);
232
+ for (let i = 0; i < dims; i++) floats[i] = i * 0.001;
233
+ const buffer = Buffer.from(
234
+ floats.buffer,
235
+ floats.byteOffset,
236
+ floats.byteLength,
237
+ );
238
+
239
+ const db = createMockCacheDb({ hash: "testhash", embedding: buffer });
240
+ const cache = new EmbeddingCache(db);
241
+ const result = cache.get("testhash", dims);
242
+
243
+ expect(result).not.toBeNull();
244
+ expect(result!.length).toBe(dims);
245
+ expect(result![0]).toBeCloseTo(0);
246
+ expect(result![1]).toBeCloseTo(0.001);
247
+ });
248
+
249
+ it("returns null for missing hash", () => {
250
+ const db = createMockCacheDb();
251
+ const cache = new EmbeddingCache(db);
252
+ const result = cache.get("missinghash", 384);
253
+ expect(result).toBeNull();
254
+ });
255
+
256
+ it("works with 384 dims", () => {
257
+ const dims = 384;
258
+ const floats = new Float32Array(dims);
259
+ floats[0] = 1.0;
260
+ const buffer = Buffer.from(
261
+ floats.buffer,
262
+ floats.byteOffset,
263
+ floats.byteLength,
264
+ );
265
+
266
+ const db = createMockCacheDb({ hash: "hash384", embedding: buffer });
267
+ const cache = new EmbeddingCache(db);
268
+ const result = cache.get("hash384", dims);
269
+
270
+ expect(result).not.toBeNull();
271
+ expect(result!.length).toBe(384);
272
+ expect(result![0]).toBeCloseTo(1.0);
273
+ });
274
+
275
+ it("returns null when cached buffer is smaller than requested dims", () => {
276
+ // Simulate stale cache: 384-dim embedding cached, but requesting 768 dims
277
+ const smallDims = 384;
278
+ const floats = new Float32Array(smallDims);
279
+ floats[0] = 1.0;
280
+ const buffer = Buffer.from(
281
+ floats.buffer,
282
+ floats.byteOffset,
283
+ floats.byteLength,
284
+ );
285
+
286
+ const db = createMockCacheDb({ hash: "stale", embedding: buffer });
287
+ const cache = new EmbeddingCache(db);
288
+ const result = cache.get("stale", 768);
289
+
290
+ expect(result).toBeNull();
291
+ });
292
+ });
@@ -0,0 +1,210 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdirSync, rmSync, writeFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { tmpdir } from "os";
5
+ import {
6
+ areDepsInstalled,
7
+ areSqliteDepsAvailable,
8
+ areEmbeddingDepsAvailable,
9
+ readManifest,
10
+ getManifestPath,
11
+ getDepsDir,
12
+ SQLITE_DEPS,
13
+ EMBEDDING_DEPS,
14
+ ALL_DEPS,
15
+ type DepsManifest,
16
+ } from "../lib/lazy-install.js";
17
+
18
+ let testDir: string;
19
+ let counter = 0;
20
+
21
+ beforeEach(() => {
22
+ testDir = join(tmpdir(), `cf-memory-lazy-test-${Date.now()}-${++counter}`);
23
+ mkdirSync(testDir, { recursive: true });
24
+ });
25
+
26
+ afterEach(() => {
27
+ rmSync(testDir, { recursive: true, force: true });
28
+ });
29
+
30
+ describe("getDepsDir()", () => {
31
+ it("returns a path under ~/.coding-friend/memory/", () => {
32
+ const dir = getDepsDir();
33
+ expect(dir).toContain(".coding-friend");
34
+ expect(dir).toContain("memory");
35
+ });
36
+ });
37
+
38
+ describe("getManifestPath()", () => {
39
+ it("returns deps.json path within depsDir", () => {
40
+ const p = getManifestPath(testDir);
41
+ expect(p).toBe(join(testDir, "deps.json"));
42
+ });
43
+ });
44
+
45
+ describe("readManifest()", () => {
46
+ it("returns null when no manifest exists", () => {
47
+ expect(readManifest(testDir)).toBeNull();
48
+ });
49
+
50
+ it("returns parsed manifest when file exists", () => {
51
+ const manifest: DepsManifest = {
52
+ version: 1,
53
+ installed: { "better-sqlite3": "11.0.0" },
54
+ installedAt: "2026-01-01T00:00:00Z",
55
+ };
56
+ writeFileSync(join(testDir, "deps.json"), JSON.stringify(manifest));
57
+
58
+ const result = readManifest(testDir);
59
+ expect(result).toEqual(manifest);
60
+ });
61
+
62
+ it("returns null for invalid JSON", () => {
63
+ writeFileSync(join(testDir, "deps.json"), "not json");
64
+ expect(readManifest(testDir)).toBeNull();
65
+ });
66
+ });
67
+
68
+ describe("areDepsInstalled()", () => {
69
+ it("returns false when no manifest exists", () => {
70
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
71
+ });
72
+
73
+ it("returns false when manifest version doesn't match", () => {
74
+ writeFileSync(
75
+ join(testDir, "deps.json"),
76
+ JSON.stringify({ version: 999, installed: {}, installedAt: "" }),
77
+ );
78
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
79
+ });
80
+
81
+ it("returns false when required dep is missing from manifest", () => {
82
+ writeFileSync(
83
+ join(testDir, "deps.json"),
84
+ JSON.stringify({
85
+ version: 1,
86
+ installed: { "better-sqlite3": "11.0.0" },
87
+ installedAt: "",
88
+ }),
89
+ );
90
+ // SQLITE_DEPS requires both better-sqlite3 and sqlite-vec
91
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
92
+ });
93
+
94
+ it("returns false when major version doesn't match", () => {
95
+ // Create fake node_modules
96
+ mkdirSync(join(testDir, "node_modules", "better-sqlite3"), {
97
+ recursive: true,
98
+ });
99
+ mkdirSync(join(testDir, "node_modules", "sqlite-vec"), {
100
+ recursive: true,
101
+ });
102
+
103
+ writeFileSync(
104
+ join(testDir, "deps.json"),
105
+ JSON.stringify({
106
+ version: 1,
107
+ installed: {
108
+ "better-sqlite3": "9.0.0", // required ^11.0.0
109
+ "sqlite-vec": "0.1.6",
110
+ },
111
+ installedAt: "",
112
+ }),
113
+ );
114
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
115
+ });
116
+
117
+ it("returns false when module dir doesn't exist on disk", () => {
118
+ writeFileSync(
119
+ join(testDir, "deps.json"),
120
+ JSON.stringify({
121
+ version: 1,
122
+ installed: {
123
+ "better-sqlite3": "11.0.0",
124
+ "sqlite-vec": "0.1.6",
125
+ },
126
+ installedAt: "",
127
+ }),
128
+ );
129
+ // No node_modules on disk
130
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
131
+ });
132
+
133
+ it("returns false for 0.x packages with wrong minor version", () => {
134
+ // ^0.1.6 should reject 0.2.0 (different minor)
135
+ mkdirSync(join(testDir, "node_modules", "better-sqlite3"), {
136
+ recursive: true,
137
+ });
138
+ mkdirSync(join(testDir, "node_modules", "sqlite-vec"), {
139
+ recursive: true,
140
+ });
141
+
142
+ writeFileSync(
143
+ join(testDir, "deps.json"),
144
+ JSON.stringify({
145
+ version: 1,
146
+ installed: {
147
+ "better-sqlite3": "11.0.0",
148
+ "sqlite-vec": "0.2.0", // required ^0.1.6, minor mismatch
149
+ },
150
+ installedAt: "",
151
+ }),
152
+ );
153
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(false);
154
+ });
155
+
156
+ it("returns true when all deps match", () => {
157
+ // Create fake node_modules
158
+ mkdirSync(join(testDir, "node_modules", "better-sqlite3"), {
159
+ recursive: true,
160
+ });
161
+ mkdirSync(join(testDir, "node_modules", "sqlite-vec"), {
162
+ recursive: true,
163
+ });
164
+
165
+ writeFileSync(
166
+ join(testDir, "deps.json"),
167
+ JSON.stringify({
168
+ version: 1,
169
+ installed: {
170
+ "better-sqlite3": "11.2.0",
171
+ "sqlite-vec": "0.1.8",
172
+ },
173
+ installedAt: "",
174
+ }),
175
+ );
176
+ expect(areDepsInstalled(SQLITE_DEPS, testDir)).toBe(true);
177
+ });
178
+ });
179
+
180
+ describe("areSqliteDepsAvailable()", () => {
181
+ it("returns false when deps not installed", () => {
182
+ expect(areSqliteDepsAvailable(testDir)).toBe(false);
183
+ });
184
+ });
185
+
186
+ describe("areEmbeddingDepsAvailable()", () => {
187
+ it("returns false when deps not installed", () => {
188
+ expect(areEmbeddingDepsAvailable(testDir)).toBe(false);
189
+ });
190
+ });
191
+
192
+ describe("dependency constants", () => {
193
+ it("SQLITE_DEPS includes better-sqlite3 and sqlite-vec", () => {
194
+ expect(SQLITE_DEPS["better-sqlite3"]).toBeDefined();
195
+ expect(SQLITE_DEPS["sqlite-vec"]).toBeDefined();
196
+ });
197
+
198
+ it("EMBEDDING_DEPS includes @huggingface/transformers", () => {
199
+ expect(EMBEDDING_DEPS["@huggingface/transformers"]).toBeDefined();
200
+ });
201
+
202
+ it("ALL_DEPS is a superset of both", () => {
203
+ for (const key of Object.keys(SQLITE_DEPS)) {
204
+ expect(ALL_DEPS[key]).toBeDefined();
205
+ }
206
+ for (const key of Object.keys(EMBEDDING_DEPS)) {
207
+ expect(ALL_DEPS[key]).toBeDefined();
208
+ }
209
+ });
210
+ });