claude-code-swarm 0.3.3 โ†’ 0.3.5

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 (273) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +22 -1
  3. package/.claude-plugin/run-agent-inbox-mcp.sh +76 -0
  4. package/.claude-plugin/run-minimem-mcp.sh +98 -0
  5. package/.claude-plugin/run-opentasks-mcp.sh +65 -0
  6. package/CLAUDE.md +200 -36
  7. package/README.md +65 -0
  8. package/e2e/helpers/cleanup.mjs +17 -3
  9. package/e2e/helpers/map-mock-server.mjs +201 -25
  10. package/e2e/helpers/sidecar.mjs +222 -0
  11. package/e2e/helpers/workspace.mjs +2 -1
  12. package/e2e/tier5-sidecar-inbox.test.mjs +900 -0
  13. package/e2e/tier6-inbox-mcp.test.mjs +173 -0
  14. package/e2e/tier6-live-agent.test.mjs +759 -0
  15. package/e2e/vitest.config.e2e.mjs +1 -1
  16. package/hooks/hooks.json +15 -8
  17. package/package.json +13 -1
  18. package/references/agent-inbox/CLAUDE.md +151 -0
  19. package/references/agent-inbox/README.md +238 -0
  20. package/references/agent-inbox/docs/CLAUDE-CODE-SWARM-PROPOSAL.md +137 -0
  21. package/references/agent-inbox/docs/DESIGN.md +1156 -0
  22. package/references/agent-inbox/hooks/inbox-hook.mjs +119 -0
  23. package/references/agent-inbox/hooks/register-hook.mjs +69 -0
  24. package/references/agent-inbox/package-lock.json +3347 -0
  25. package/references/agent-inbox/package.json +58 -0
  26. package/references/agent-inbox/rules/agent-inbox.md +78 -0
  27. package/references/agent-inbox/src/federation/address.ts +61 -0
  28. package/references/agent-inbox/src/federation/connection-manager.ts +573 -0
  29. package/references/agent-inbox/src/federation/delivery-queue.ts +222 -0
  30. package/references/agent-inbox/src/federation/index.ts +6 -0
  31. package/references/agent-inbox/src/federation/routing-engine.ts +188 -0
  32. package/references/agent-inbox/src/federation/trust.ts +71 -0
  33. package/references/agent-inbox/src/index.ts +390 -0
  34. package/references/agent-inbox/src/ipc/ipc-server.ts +207 -0
  35. package/references/agent-inbox/src/jsonrpc/mail-server.ts +382 -0
  36. package/references/agent-inbox/src/map/map-client.ts +414 -0
  37. package/references/agent-inbox/src/mcp/mcp-server.ts +272 -0
  38. package/references/agent-inbox/src/mesh/delivery-bridge.ts +110 -0
  39. package/references/agent-inbox/src/mesh/mesh-connector.ts +41 -0
  40. package/references/agent-inbox/src/mesh/mesh-transport.ts +157 -0
  41. package/references/agent-inbox/src/mesh/type-mapper.ts +239 -0
  42. package/references/agent-inbox/src/push/notifier.ts +233 -0
  43. package/references/agent-inbox/src/registry/warm-registry.ts +255 -0
  44. package/references/agent-inbox/src/router/message-router.ts +175 -0
  45. package/references/agent-inbox/src/storage/interface.ts +48 -0
  46. package/references/agent-inbox/src/storage/memory.ts +145 -0
  47. package/references/agent-inbox/src/storage/sqlite.ts +671 -0
  48. package/references/agent-inbox/src/traceability/traceability.ts +183 -0
  49. package/references/agent-inbox/src/types.ts +303 -0
  50. package/references/agent-inbox/test/federation/address.test.ts +101 -0
  51. package/references/agent-inbox/test/federation/connection-manager.test.ts +546 -0
  52. package/references/agent-inbox/test/federation/delivery-queue.test.ts +159 -0
  53. package/references/agent-inbox/test/federation/integration.test.ts +857 -0
  54. package/references/agent-inbox/test/federation/routing-engine.test.ts +117 -0
  55. package/references/agent-inbox/test/federation/sdk-integration.test.ts +744 -0
  56. package/references/agent-inbox/test/federation/trust.test.ts +89 -0
  57. package/references/agent-inbox/test/ipc-jsonrpc.test.ts +113 -0
  58. package/references/agent-inbox/test/ipc-server.test.ts +197 -0
  59. package/references/agent-inbox/test/mail-server.test.ts +285 -0
  60. package/references/agent-inbox/test/map-client.test.ts +408 -0
  61. package/references/agent-inbox/test/mesh/delivery-bridge.test.ts +178 -0
  62. package/references/agent-inbox/test/mesh/e2e-mesh.test.ts +527 -0
  63. package/references/agent-inbox/test/mesh/e2e-real-meshpeer.test.ts +629 -0
  64. package/references/agent-inbox/test/mesh/federation-mesh.test.ts +269 -0
  65. package/references/agent-inbox/test/mesh/mesh-connector.test.ts +66 -0
  66. package/references/agent-inbox/test/mesh/mesh-transport.test.ts +191 -0
  67. package/references/agent-inbox/test/mesh/meshpeer-integration.test.ts +442 -0
  68. package/references/agent-inbox/test/mesh/mock-mesh.ts +125 -0
  69. package/references/agent-inbox/test/mesh/mock-meshpeer.ts +266 -0
  70. package/references/agent-inbox/test/mesh/type-mapper.test.ts +226 -0
  71. package/references/agent-inbox/test/message-router.test.ts +184 -0
  72. package/references/agent-inbox/test/push-notifier.test.ts +139 -0
  73. package/references/agent-inbox/test/registry/warm-registry.test.ts +171 -0
  74. package/references/agent-inbox/test/sqlite-prefix.test.ts +192 -0
  75. package/references/agent-inbox/test/sqlite-storage.test.ts +243 -0
  76. package/references/agent-inbox/test/storage.test.ts +196 -0
  77. package/references/agent-inbox/test/traceability.test.ts +123 -0
  78. package/references/agent-inbox/test/wake.test.ts +330 -0
  79. package/references/agent-inbox/tsconfig.json +20 -0
  80. package/references/agent-inbox/tsup.config.ts +10 -0
  81. package/references/agent-inbox/vitest.config.ts +8 -0
  82. package/references/minimem/.claude/settings.json +7 -0
  83. package/references/minimem/.sudocode/issues.jsonl +18 -0
  84. package/references/minimem/.sudocode/specs.jsonl +1 -0
  85. package/references/minimem/CLAUDE.md +329 -0
  86. package/references/minimem/README.md +565 -0
  87. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  88. package/references/minimem/claude-plugin/.mcp.json +7 -0
  89. package/references/minimem/claude-plugin/README.md +158 -0
  90. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  91. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  92. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  93. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  94. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  95. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  96. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  97. package/references/minimem/media/banner.png +0 -0
  98. package/references/minimem/package-lock.json +5373 -0
  99. package/references/minimem/package.json +76 -0
  100. package/references/minimem/scripts/postbuild.js +49 -0
  101. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  102. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  103. package/references/minimem/src/__tests__/helpers.ts +199 -0
  104. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  105. package/references/minimem/src/__tests__/knowledge-frontmatter.test.ts +148 -0
  106. package/references/minimem/src/__tests__/knowledge.test.ts +148 -0
  107. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  108. package/references/minimem/src/__tests__/session.test.ts +190 -0
  109. package/references/minimem/src/cli/__tests__/commands.test.ts +760 -0
  110. package/references/minimem/src/cli/__tests__/contained-layout.test.ts +286 -0
  111. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  112. package/references/minimem/src/cli/commands/append.ts +76 -0
  113. package/references/minimem/src/cli/commands/config.ts +262 -0
  114. package/references/minimem/src/cli/commands/conflicts.ts +415 -0
  115. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  116. package/references/minimem/src/cli/commands/index.ts +12 -0
  117. package/references/minimem/src/cli/commands/init.ts +166 -0
  118. package/references/minimem/src/cli/commands/mcp.ts +221 -0
  119. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  120. package/references/minimem/src/cli/commands/search.ts +223 -0
  121. package/references/minimem/src/cli/commands/status.ts +84 -0
  122. package/references/minimem/src/cli/commands/store.ts +189 -0
  123. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  124. package/references/minimem/src/cli/commands/sync.ts +70 -0
  125. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  126. package/references/minimem/src/cli/config.ts +611 -0
  127. package/references/minimem/src/cli/index.ts +299 -0
  128. package/references/minimem/src/cli/shared.ts +189 -0
  129. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  130. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  131. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  132. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  133. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  134. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  135. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  136. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  137. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  138. package/references/minimem/src/cli/sync/central.ts +292 -0
  139. package/references/minimem/src/cli/sync/conflicts.ts +205 -0
  140. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  141. package/references/minimem/src/cli/sync/detection.ts +138 -0
  142. package/references/minimem/src/cli/sync/index.ts +107 -0
  143. package/references/minimem/src/cli/sync/operations.ts +373 -0
  144. package/references/minimem/src/cli/sync/registry.ts +279 -0
  145. package/references/minimem/src/cli/sync/state.ts +358 -0
  146. package/references/minimem/src/cli/sync/validation.ts +206 -0
  147. package/references/minimem/src/cli/sync/watcher.ts +237 -0
  148. package/references/minimem/src/cli/version.ts +34 -0
  149. package/references/minimem/src/core/index.ts +9 -0
  150. package/references/minimem/src/core/indexer.ts +628 -0
  151. package/references/minimem/src/core/searcher.ts +221 -0
  152. package/references/minimem/src/db/schema.ts +183 -0
  153. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  154. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  155. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  156. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  157. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  158. package/references/minimem/src/index.ts +132 -0
  159. package/references/minimem/src/internal.ts +299 -0
  160. package/references/minimem/src/minimem.ts +1291 -0
  161. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  162. package/references/minimem/src/search/graph.ts +234 -0
  163. package/references/minimem/src/search/hybrid.ts +151 -0
  164. package/references/minimem/src/search/search.ts +256 -0
  165. package/references/minimem/src/server/__tests__/mcp.test.ts +347 -0
  166. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  167. package/references/minimem/src/server/mcp.ts +326 -0
  168. package/references/minimem/src/server/tools.ts +720 -0
  169. package/references/minimem/src/session.ts +460 -0
  170. package/references/minimem/src/store/__tests__/manifest.test.ts +177 -0
  171. package/references/minimem/src/store/__tests__/materialize.test.ts +52 -0
  172. package/references/minimem/src/store/__tests__/store-graph.test.ts +228 -0
  173. package/references/minimem/src/store/index.ts +27 -0
  174. package/references/minimem/src/store/manifest.ts +203 -0
  175. package/references/minimem/src/store/materialize.ts +185 -0
  176. package/references/minimem/src/store/store-graph.ts +252 -0
  177. package/references/minimem/tsconfig.json +19 -0
  178. package/references/minimem/tsup.config.ts +26 -0
  179. package/references/minimem/vitest.config.ts +29 -0
  180. package/references/openteams/src/cli/generate.ts +23 -1
  181. package/references/openteams/src/generators/agent-prompt-generator.test.ts +94 -0
  182. package/references/openteams/src/generators/agent-prompt-generator.ts +42 -13
  183. package/references/openteams/src/generators/package-generator.ts +9 -1
  184. package/references/openteams/src/generators/skill-generator.test.ts +28 -0
  185. package/references/openteams/src/generators/skill-generator.ts +10 -4
  186. package/references/skill-tree/.claude/settings.json +6 -0
  187. package/references/skill-tree/.sudocode/issues.jsonl +19 -0
  188. package/references/skill-tree/.sudocode/specs.jsonl +3 -0
  189. package/references/skill-tree/CLAUDE.md +132 -0
  190. package/references/skill-tree/README.md +396 -0
  191. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  192. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  193. package/references/skill-tree/docs/TODOS.md +91 -0
  194. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  195. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  196. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  197. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  198. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  199. package/references/skill-tree/docs/scraper/README.md +170 -0
  200. package/references/skill-tree/examples/basic-usage.ts +157 -0
  201. package/references/skill-tree/package-lock.json +1852 -0
  202. package/references/skill-tree/package.json +66 -0
  203. package/references/skill-tree/plan.md +78 -0
  204. package/references/skill-tree/scraper/README.md +123 -0
  205. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  206. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  207. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  208. package/references/skill-tree/scraper/package-lock.json +6329 -0
  209. package/references/skill-tree/scraper/package.json +68 -0
  210. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  211. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  212. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  213. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  214. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  215. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  216. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  217. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  218. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  219. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  220. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  221. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  222. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  223. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  224. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  225. package/references/skill-tree/test/run-all.ts +106 -0
  226. package/references/skill-tree/test/utils.ts +128 -0
  227. package/references/skill-tree/vitest.config.ts +16 -0
  228. package/references/swarmkit/src/commands/init/phases/configure.ts +0 -22
  229. package/references/swarmkit/src/commands/init/phases/global-setup.ts +5 -3
  230. package/references/swarmkit/src/commands/init/wizard.ts +2 -2
  231. package/references/swarmkit/src/packages/setup.test.ts +53 -7
  232. package/references/swarmkit/src/packages/setup.ts +37 -1
  233. package/scripts/bootstrap.mjs +26 -1
  234. package/scripts/generate-agents.mjs +5 -1
  235. package/scripts/map-hook.mjs +97 -64
  236. package/scripts/map-sidecar.mjs +179 -25
  237. package/scripts/team-loader.mjs +12 -41
  238. package/skills/swarm/SKILL.md +89 -25
  239. package/src/__tests__/agent-generator.test.mjs +6 -13
  240. package/src/__tests__/bootstrap.test.mjs +124 -1
  241. package/src/__tests__/config.test.mjs +200 -27
  242. package/src/__tests__/e2e-live-map.test.mjs +536 -0
  243. package/src/__tests__/e2e-mesh-sidecar.test.mjs +570 -0
  244. package/src/__tests__/e2e-native-task-hooks.test.mjs +376 -0
  245. package/src/__tests__/e2e-sidecar-bridge.test.mjs +477 -0
  246. package/src/__tests__/helpers.mjs +13 -0
  247. package/src/__tests__/inbox.test.mjs +22 -89
  248. package/src/__tests__/index.test.mjs +35 -9
  249. package/src/__tests__/integration.test.mjs +513 -0
  250. package/src/__tests__/map-events.test.mjs +514 -150
  251. package/src/__tests__/mesh-connection.test.mjs +308 -0
  252. package/src/__tests__/opentasks-client.test.mjs +517 -0
  253. package/src/__tests__/paths.test.mjs +185 -41
  254. package/src/__tests__/sidecar-client.test.mjs +35 -0
  255. package/src/__tests__/sidecar-server.test.mjs +124 -0
  256. package/src/__tests__/skilltree-client.test.mjs +80 -0
  257. package/src/agent-generator.mjs +104 -33
  258. package/src/bootstrap.mjs +150 -10
  259. package/src/config.mjs +81 -17
  260. package/src/context-output.mjs +58 -8
  261. package/src/inbox.mjs +9 -54
  262. package/src/index.mjs +39 -8
  263. package/src/map-connection.mjs +4 -3
  264. package/src/map-events.mjs +350 -80
  265. package/src/mesh-connection.mjs +148 -0
  266. package/src/opentasks-client.mjs +269 -0
  267. package/src/paths.mjs +182 -27
  268. package/src/sessionlog.mjs +14 -9
  269. package/src/sidecar-client.mjs +81 -27
  270. package/src/sidecar-server.mjs +175 -16
  271. package/src/skilltree-client.mjs +173 -0
  272. package/src/template.mjs +68 -4
  273. package/vitest.config.mjs +1 -0
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "minimem",
3
+ "version": "0.0.7",
4
+ "description": "A lightweight file-based memory system with vector search for AI agents",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./session": {
16
+ "types": "./dist/session.d.ts",
17
+ "import": "./dist/session.js",
18
+ "require": "./dist/session.cjs"
19
+ },
20
+ "./internal": {
21
+ "types": "./dist/internal.d.ts",
22
+ "import": "./dist/internal.js",
23
+ "require": "./dist/internal.cjs"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup && npm run postbuild",
31
+ "postbuild": "node scripts/postbuild.js",
32
+ "dev": "tsup --watch",
33
+ "test": "vitest run",
34
+ "test:integration": "npx tsx --test src/__tests__/minimem.integration.test.ts",
35
+ "test:cli": "npx tsx --test src/cli/__tests__/commands.test.ts",
36
+ "test:knowledge": "npx tsx --test src/__tests__/knowledge.test.ts",
37
+ "test:all": "npm run test && npm run test:integration && npm run test:cli && npm run test:knowledge",
38
+ "prepublishOnly": "npm run build",
39
+ "version:patch": "npm version patch && git push && git push --tags",
40
+ "version:minor": "npm version minor && git push && git push --tags",
41
+ "version:major": "npm version major && git push && git push --tags",
42
+ "publish:npm": "npm run build && npm publish"
43
+ },
44
+ "keywords": [
45
+ "memory",
46
+ "vector-search",
47
+ "embeddings",
48
+ "sqlite",
49
+ "ai-agent",
50
+ "rag",
51
+ "semantic-search"
52
+ ],
53
+ "author": "Alex Ngai",
54
+ "license": "MIT",
55
+ "engines": {
56
+ "node": ">=22.0.0"
57
+ },
58
+ "bin": {
59
+ "minimem": "./dist/cli/index.js"
60
+ },
61
+ "dependencies": {
62
+ "chokidar": "^4.0.3",
63
+ "commander": "^12.0.0",
64
+ "minimatch": "^10.1.1",
65
+ "sqlite-vec": "^0.1.6"
66
+ },
67
+ "optionalDependencies": {
68
+ "node-llama-cpp": "^3.0.0"
69
+ },
70
+ "devDependencies": {
71
+ "@types/node": "^22.0.0",
72
+ "tsup": "^8.0.0",
73
+ "typescript": "^5.0.0",
74
+ "vitest": "^2.0.0"
75
+ }
76
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cross-platform postbuild script
5
+ *
6
+ * Fixes the sqlite import in the CLI bundle.
7
+ * esbuild/tsup strips the "node:" prefix from built-in modules,
8
+ * but node:sqlite requires it.
9
+ */
10
+
11
+ import { readFileSync, writeFileSync } from "node:fs";
12
+ import { join, dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+
17
+ const filesToPatch = [
18
+ join(__dirname, "../dist/cli/index.js"),
19
+ join(__dirname, "../dist/index.cjs"),
20
+ join(__dirname, "../dist/session.cjs"),
21
+ join(__dirname, "../dist/internal.cjs"),
22
+ ];
23
+
24
+ for (const filePath of filesToPatch) {
25
+ try {
26
+ let content = readFileSync(filePath, "utf-8");
27
+
28
+ // Fix sqlite import (esbuild strips the node: prefix)
29
+ const before = content;
30
+ content = content.replace(/from "sqlite"/g, 'from "node:sqlite"');
31
+ content = content.replace(/from 'sqlite'/g, "from 'node:sqlite'");
32
+ content = content.replace(/require\("sqlite"\)/g, 'require("node:sqlite")');
33
+ content = content.replace(/require\('sqlite'\)/g, "require('node:sqlite')");
34
+
35
+ if (content !== before) {
36
+ writeFileSync(filePath, content);
37
+ console.log(`โœ“ Fixed node:sqlite import in ${filePath}`);
38
+ } else {
39
+ console.log(`โœ“ No sqlite import fixes needed in ${filePath}`);
40
+ }
41
+ } catch (error) {
42
+ if (error.code === "ENOENT") {
43
+ console.log(`โŠ˜ Skipped ${filePath} (not found)`);
44
+ } else {
45
+ console.error(`Error patching ${filePath}:`, error.message);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Edge case tests
3
+ *
4
+ * Tests for boundary conditions and unusual inputs:
5
+ * - Empty files
6
+ * - Unicode content (emoji, CJK, RTL)
7
+ * - Very large files
8
+ * - Files with only frontmatter
9
+ * - Windows line endings
10
+ * - Path validation edge cases
11
+ */
12
+
13
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
14
+ import fs from "node:fs/promises";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+
18
+ import {
19
+ chunkMarkdown,
20
+ hashText,
21
+ buildFileEntry,
22
+ listMemoryFiles,
23
+ isMemoryPath,
24
+ cosineSimilarity,
25
+ truncateUtf16Safe,
26
+ } from "../internal.js";
27
+ import {
28
+ parseFrontmatter,
29
+ serializeFrontmatter,
30
+ addSessionToContent,
31
+ type SessionContext,
32
+ } from "../session.js";
33
+
34
+ describe("Edge case: empty files", () => {
35
+ let tempDir: string;
36
+
37
+ beforeEach(async () => {
38
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "minimem-edge-"));
39
+ });
40
+
41
+ afterEach(async () => {
42
+ await fs.rm(tempDir, { recursive: true, force: true });
43
+ });
44
+
45
+ it("listMemoryFiles includes empty .md files", async () => {
46
+ await fs.writeFile(path.join(tempDir, "MEMORY.md"), "");
47
+ const files = await listMemoryFiles(tempDir);
48
+ expect(files).toHaveLength(1);
49
+ });
50
+
51
+ it("chunkMarkdown handles empty string", () => {
52
+ const chunks = chunkMarkdown("", { tokens: 256, overlap: 32 });
53
+ expect(chunks.length).toBeLessThanOrEqual(1);
54
+ });
55
+
56
+ it("chunkMarkdown handles single newline", () => {
57
+ const chunks = chunkMarkdown("\n", { tokens: 256, overlap: 32 });
58
+ expect(chunks.length).toBeLessThanOrEqual(1);
59
+ });
60
+
61
+ it("chunkMarkdown handles whitespace-only content", () => {
62
+ const chunks = chunkMarkdown(" \n \n ", { tokens: 256, overlap: 32 });
63
+ expect(chunks.length).toBeGreaterThan(0);
64
+ // Each chunk should have text defined
65
+ for (const chunk of chunks) {
66
+ expect(chunk.text).toBeDefined();
67
+ expect(chunk.hash).toBeDefined();
68
+ }
69
+ });
70
+
71
+ it("buildFileEntry handles empty file", async () => {
72
+ const filePath = path.join(tempDir, "empty.md");
73
+ await fs.writeFile(filePath, "");
74
+
75
+ const entry = await buildFileEntry(filePath, tempDir);
76
+ expect(entry.path).toBe("empty.md");
77
+ expect(entry.size).toBe(0);
78
+ expect(entry.hash).toBe(hashText(""));
79
+ });
80
+ });
81
+
82
+ describe("Edge case: unicode content", () => {
83
+ it("chunkMarkdown handles emoji content", () => {
84
+ const content = "# ๐Ÿš€ Project Launch\n\n๐Ÿ“ Notes about the launch ๐ŸŽ‰\n\n## โœ… Tasks\n- ๐Ÿ”ฅ Deploy\n- ๐Ÿงช Test";
85
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
86
+ expect(chunks.length).toBeGreaterThan(0);
87
+ expect(chunks[0].text).toContain("๐Ÿš€");
88
+ });
89
+
90
+ it("chunkMarkdown handles CJK characters", () => {
91
+ const content = "# ้กน็›ฎ็ฌ”่ฎฐ\n\n่ฟ™ๆ˜ฏไธ€ไธชๆต‹่ฏ•ๆ–‡ไปถใ€‚\n\n## ไผš่ฎฎ่ฎฐๅฝ•\nไปŠๅคฉ่ฎจ่ฎบไบ†ๆ–ฐๅŠŸ่ƒฝใ€‚";
92
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
93
+ expect(chunks.length).toBeGreaterThan(0);
94
+ expect(chunks[0].text).toContain("้กน็›ฎ็ฌ”่ฎฐ");
95
+ });
96
+
97
+ it("chunkMarkdown handles RTL text (Arabic)", () => {
98
+ const content = "# ู…ู„ุงุญุธุงุช ุงู„ู…ุดุฑูˆุน\n\nู‡ุฐุง ู…ู„ู ุงุฎุชุจุงุฑ.\n\n## ุณุฌู„ ุงู„ุงุฌุชู…ุงุนุงุช";
99
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
100
+ expect(chunks.length).toBeGreaterThan(0);
101
+ expect(chunks[0].text).toContain("ู…ู„ุงุญุธุงุช");
102
+ });
103
+
104
+ it("chunkMarkdown handles mixed scripts", () => {
105
+ const content = "# Mixed: English, ไธญๆ–‡, ุงู„ุนุฑุจูŠุฉ, ฮ•ฮปฮปฮทฮฝฮนฮบฮฌ\n\nContent with ะผะฝoะณะพ different scripts.";
106
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
107
+ expect(chunks.length).toBeGreaterThan(0);
108
+ });
109
+
110
+ it("hashText produces consistent hashes for unicode", () => {
111
+ const emoji = "๐Ÿš€๐ŸŽ‰";
112
+ const h1 = hashText(emoji);
113
+ const h2 = hashText(emoji);
114
+ expect(h1).toBe(h2);
115
+ expect(h1.length).toBe(64);
116
+ });
117
+
118
+ it("parseFrontmatter handles unicode values", () => {
119
+ const content = '---\nsession:\n source: "ใƒ†ใ‚นใƒˆ"\ncreated: 2024-01-01\n---\n# ๅ†…ๅฎน';
120
+ const { frontmatter, body } = parseFrontmatter(content);
121
+ expect(frontmatter?.session?.source).toBe("ใƒ†ใ‚นใƒˆ");
122
+ expect(body).toBe("# ๅ†…ๅฎน");
123
+ });
124
+
125
+ it("truncateUtf16Safe truncates at char boundary", () => {
126
+ const emoji = "๐Ÿš€๐ŸŽ‰๐Ÿ”ฅ๐Ÿ’ก"; // Each emoji is 2 UTF-16 code units
127
+ const truncated = truncateUtf16Safe(emoji, 2);
128
+ // Should truncate at character count
129
+ expect(truncated.length).toBe(2);
130
+ });
131
+ });
132
+
133
+ describe("Edge case: large files", () => {
134
+ it("chunkMarkdown splits large content into multiple chunks", () => {
135
+ // Generate ~100KB of content
136
+ const lines: string[] = [];
137
+ for (let i = 0; i < 2000; i++) {
138
+ lines.push(`Line ${i}: This is a test line with enough content to fill chunks properly.`);
139
+ }
140
+ const content = lines.join("\n");
141
+
142
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
143
+ expect(chunks.length).toBeGreaterThan(10);
144
+
145
+ // Verify line numbers are monotonically increasing
146
+ for (let i = 1; i < chunks.length; i++) {
147
+ expect(chunks[i].startLine).toBeGreaterThanOrEqual(chunks[i - 1].startLine);
148
+ }
149
+
150
+ // Verify all content is captured (first and last lines)
151
+ expect(chunks[0].text).toContain("Line 0");
152
+ expect(chunks[chunks.length - 1].text).toContain("Line 1999");
153
+ });
154
+
155
+ it("chunkMarkdown handles single very long line", () => {
156
+ const longLine = "a".repeat(10000);
157
+ const chunks = chunkMarkdown(longLine, { tokens: 256, overlap: 32 });
158
+ expect(chunks.length).toBeGreaterThan(1);
159
+
160
+ // All chunks should have valid hash and line numbers
161
+ for (const chunk of chunks) {
162
+ expect(chunk.hash).toBeDefined();
163
+ expect(chunk.hash.length).toBe(64);
164
+ expect(chunk.startLine).toBe(1); // Single line = always line 1
165
+ expect(chunk.endLine).toBe(1);
166
+ }
167
+
168
+ // Total text across chunks (minus overlap) should cover original content
169
+ // Just verify first and last segments are present
170
+ expect(chunks[0].text.length).toBeGreaterThan(0);
171
+ expect(chunks[chunks.length - 1].text.length).toBeGreaterThan(0);
172
+ });
173
+ });
174
+
175
+ describe("Edge case: files with only frontmatter", () => {
176
+ it("parseFrontmatter handles file with only frontmatter and empty body", () => {
177
+ const content = "---\ncreated: 2024-01-01\ntags: [test]\n---\n";
178
+ const { frontmatter, body } = parseFrontmatter(content);
179
+ expect(frontmatter).toBeDefined();
180
+ expect(frontmatter?.created).toBe("2024-01-01");
181
+ expect(body).toBe("");
182
+ });
183
+
184
+ it("chunkMarkdown handles frontmatter-only content", () => {
185
+ const content = "---\ncreated: 2024-01-01\n---\n";
186
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
187
+ // Should produce at least one chunk containing the frontmatter text
188
+ expect(chunks.length).toBeGreaterThan(0);
189
+ });
190
+ });
191
+
192
+ describe("Edge case: Windows line endings", () => {
193
+ it("chunkMarkdown handles CRLF line endings", () => {
194
+ const content = "# Title\r\n\r\nParagraph one.\r\n\r\nParagraph two.\r\n";
195
+ const chunks = chunkMarkdown(content, { tokens: 256, overlap: 32 });
196
+ expect(chunks.length).toBeGreaterThan(0);
197
+ // Content should be preserved (with \r)
198
+ expect(chunks[0].text).toContain("Title");
199
+ });
200
+
201
+ it("parseFrontmatter handles CRLF frontmatter", () => {
202
+ const content = "---\r\ncreated: 2024-01-01\r\n---\r\n# Body";
203
+ // The regex uses \n, so CRLF may not match.
204
+ // This documents the current behavior.
205
+ const { frontmatter, body } = parseFrontmatter(content);
206
+ // On CRLF, the frontmatter regex may not match since it looks for \n
207
+ // This is a known limitation
208
+ if (frontmatter) {
209
+ expect(frontmatter.created).toBeDefined();
210
+ }
211
+ expect(body).toBeTruthy();
212
+ });
213
+ });
214
+
215
+ describe("Edge case: path validation", () => {
216
+ it("isMemoryPath rejects path traversal attempts", () => {
217
+ expect(isMemoryPath("../../../etc/passwd")).toBe(false);
218
+ expect(isMemoryPath("memory/../../etc/passwd")).toBe(true); // starts with memory/ so returns true
219
+ });
220
+
221
+ it("isMemoryPath handles various path formats", () => {
222
+ expect(isMemoryPath("MEMORY.md")).toBe(true);
223
+ expect(isMemoryPath("memory.md")).toBe(true);
224
+ expect(isMemoryPath("memory/notes.md")).toBe(true);
225
+ expect(isMemoryPath("memory/deep/nested/file.md")).toBe(true);
226
+ expect(isMemoryPath("MEMORY.txt")).toBe(false);
227
+ expect(isMemoryPath("notes.md")).toBe(false);
228
+ expect(isMemoryPath("src/memory.md")).toBe(false);
229
+ });
230
+
231
+ it("isMemoryPath handles empty and whitespace", () => {
232
+ expect(isMemoryPath("")).toBe(false);
233
+ expect(isMemoryPath(" ")).toBe(false);
234
+ });
235
+ });
236
+
237
+ describe("Edge case: cosineSimilarity", () => {
238
+ it("handles vectors of different lengths", () => {
239
+ const a = [1, 2, 3, 4, 5];
240
+ const b = [1, 2, 3];
241
+ // Should use min length
242
+ const sim = cosineSimilarity(a, b);
243
+ expect(sim).toBeGreaterThan(0);
244
+ expect(sim).toBeLessThanOrEqual(1);
245
+ });
246
+
247
+ it("handles very small values", () => {
248
+ const a = [1e-10, 1e-10];
249
+ const b = [1e-10, 1e-10];
250
+ const sim = cosineSimilarity(a, b);
251
+ expect(sim).toBeCloseTo(1);
252
+ });
253
+
254
+ it("handles large vectors", () => {
255
+ const size = 1536; // OpenAI embedding dimension
256
+ const a = Array.from({ length: size }, (_, i) => Math.sin(i));
257
+ const b = Array.from({ length: size }, (_, i) => Math.cos(i));
258
+ const sim = cosineSimilarity(a, b);
259
+ expect(sim).toBeGreaterThan(-1);
260
+ expect(sim).toBeLessThan(1);
261
+ });
262
+ });
263
+
264
+ describe("Edge case: session tracking", () => {
265
+ it("addSessionToContent handles content without existing frontmatter", () => {
266
+ const session: SessionContext = {
267
+ id: "test-123",
268
+ source: "test",
269
+ };
270
+ const result = addSessionToContent("# Hello", session);
271
+ expect(result).toContain("---");
272
+ expect(result).toContain("id: test-123");
273
+ expect(result).toContain("# Hello");
274
+ });
275
+
276
+ it("serializeFrontmatter handles empty session", () => {
277
+ const yaml = serializeFrontmatter({ session: {} });
278
+ expect(yaml).toContain("---");
279
+ expect(yaml).toContain("session:");
280
+ });
281
+
282
+ it("serializeFrontmatter handles paths with home directory", () => {
283
+ const home = os.homedir();
284
+ const session: SessionContext = {
285
+ project: path.join(home, "projects", "test"),
286
+ };
287
+ const yaml = serializeFrontmatter({ session });
288
+ // Should use ~ for home directory
289
+ expect(yaml).toContain("~/projects/test");
290
+ });
291
+
292
+ it("parseFrontmatter roundtrips through serialize", () => {
293
+ const original = {
294
+ session: { id: "abc", source: "test" },
295
+ created: "2024-01-01T00:00:00Z",
296
+ tags: ["tag1", "tag2"],
297
+ };
298
+ const serialized = serializeFrontmatter(original);
299
+ const { frontmatter } = parseFrontmatter(serialized + "body");
300
+
301
+ expect(frontmatter?.session?.id).toBe("abc");
302
+ expect(frontmatter?.session?.source).toBe("test");
303
+ expect(frontmatter?.created).toBe("2024-01-01T00:00:00Z");
304
+ expect(frontmatter?.tags).toEqual(["tag1", "tag2"]);
305
+ });
306
+ });
307
+
308
+ describe("Edge case: hashText determinism", () => {
309
+ it("same content always produces same hash", () => {
310
+ const content = "Hello, World!";
311
+ const hashes = Array.from({ length: 100 }, () => hashText(content));
312
+ const unique = new Set(hashes);
313
+ expect(unique.size).toBe(1);
314
+ });
315
+
316
+ it("different content produces different hashes", () => {
317
+ const hashes = new Set<string>();
318
+ for (let i = 0; i < 100; i++) {
319
+ hashes.add(hashText(`content-${i}`));
320
+ }
321
+ expect(hashes.size).toBe(100);
322
+ });
323
+
324
+ it("handles special characters consistently", () => {
325
+ const special = "line1\nline2\ttab\r\nwindows\0null";
326
+ const h1 = hashText(special);
327
+ const h2 = hashText(special);
328
+ expect(h1).toBe(h2);
329
+ });
330
+ });
331
+
332
+ describe("Edge case: buildFileEntry", () => {
333
+ let tempDir: string;
334
+
335
+ beforeEach(async () => {
336
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "minimem-edge-"));
337
+ });
338
+
339
+ afterEach(async () => {
340
+ await fs.rm(tempDir, { recursive: true, force: true });
341
+ });
342
+
343
+ it("handles file with only whitespace", async () => {
344
+ const filePath = path.join(tempDir, "whitespace.md");
345
+ await fs.writeFile(filePath, " \n\n \n");
346
+
347
+ const entry = await buildFileEntry(filePath, tempDir);
348
+ expect(entry.size).toBeGreaterThan(0);
349
+ expect(entry.hash).toBe(hashText(" \n\n \n"));
350
+ });
351
+
352
+ it("handles file with unicode content", async () => {
353
+ const content = "# ๐Ÿš€ Launch Notes\n\nๆ—ฅๆœฌ่ชžใƒ†ใ‚นใƒˆ";
354
+ const filePath = path.join(tempDir, "unicode.md");
355
+ await fs.writeFile(filePath, content);
356
+
357
+ const entry = await buildFileEntry(filePath, tempDir);
358
+ expect(entry.hash).toBe(hashText(content));
359
+ expect(entry.size).toBeGreaterThan(0);
360
+ });
361
+
362
+ it("handles deeply nested path", async () => {
363
+ const nested = path.join(tempDir, "memory", "a", "b", "c");
364
+ await fs.mkdir(nested, { recursive: true });
365
+ const filePath = path.join(nested, "deep.md");
366
+ await fs.writeFile(filePath, "# Deep");
367
+
368
+ const entry = await buildFileEntry(filePath, tempDir);
369
+ expect(entry.path).toBe("memory/a/b/c/deep.md");
370
+ });
371
+ });