gitmem-mcp 0.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.
Files changed (316) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CLAUDE.md.template +65 -0
  3. package/LICENSE +21 -0
  4. package/README.md +221 -0
  5. package/bin/gitmem.js +383 -0
  6. package/dist/commands/check.d.ts +33 -0
  7. package/dist/commands/check.d.ts.map +1 -0
  8. package/dist/commands/check.js +492 -0
  9. package/dist/commands/check.js.map +1 -0
  10. package/dist/constants/closing-questions.d.ts +40 -0
  11. package/dist/constants/closing-questions.d.ts.map +1 -0
  12. package/dist/constants/closing-questions.js +107 -0
  13. package/dist/constants/closing-questions.js.map +1 -0
  14. package/dist/diagnostics/anonymizer.d.ts +55 -0
  15. package/dist/diagnostics/anonymizer.d.ts.map +1 -0
  16. package/dist/diagnostics/anonymizer.js +191 -0
  17. package/dist/diagnostics/anonymizer.js.map +1 -0
  18. package/dist/diagnostics/channels.d.ts +132 -0
  19. package/dist/diagnostics/channels.d.ts.map +1 -0
  20. package/dist/diagnostics/channels.js +150 -0
  21. package/dist/diagnostics/channels.js.map +1 -0
  22. package/dist/diagnostics/collector.d.ts +183 -0
  23. package/dist/diagnostics/collector.d.ts.map +1 -0
  24. package/dist/diagnostics/collector.js +227 -0
  25. package/dist/diagnostics/collector.js.map +1 -0
  26. package/dist/diagnostics/index.d.ts +28 -0
  27. package/dist/diagnostics/index.d.ts.map +1 -0
  28. package/dist/diagnostics/index.js +31 -0
  29. package/dist/diagnostics/index.js.map +1 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +18 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/schemas/absorb-observations.d.ts +63 -0
  35. package/dist/schemas/absorb-observations.d.ts.map +1 -0
  36. package/dist/schemas/absorb-observations.js +25 -0
  37. package/dist/schemas/absorb-observations.js.map +1 -0
  38. package/dist/schemas/active-sessions.d.ts +71 -0
  39. package/dist/schemas/active-sessions.d.ts.map +1 -0
  40. package/dist/schemas/active-sessions.js +19 -0
  41. package/dist/schemas/active-sessions.js.map +1 -0
  42. package/dist/schemas/analyze.d.ts +38 -0
  43. package/dist/schemas/analyze.d.ts.map +1 -0
  44. package/dist/schemas/analyze.js +30 -0
  45. package/dist/schemas/analyze.js.map +1 -0
  46. package/dist/schemas/common.d.ts +55 -0
  47. package/dist/schemas/common.d.ts.map +1 -0
  48. package/dist/schemas/common.js +65 -0
  49. package/dist/schemas/common.js.map +1 -0
  50. package/dist/schemas/create-decision.d.ts +48 -0
  51. package/dist/schemas/create-decision.d.ts.map +1 -0
  52. package/dist/schemas/create-decision.js +31 -0
  53. package/dist/schemas/create-decision.js.map +1 -0
  54. package/dist/schemas/create-learning.d.ts +107 -0
  55. package/dist/schemas/create-learning.d.ts.map +1 -0
  56. package/dist/schemas/create-learning.js +64 -0
  57. package/dist/schemas/create-learning.js.map +1 -0
  58. package/dist/schemas/get-transcript.d.ts +24 -0
  59. package/dist/schemas/get-transcript.d.ts.map +1 -0
  60. package/dist/schemas/get-transcript.js +22 -0
  61. package/dist/schemas/get-transcript.js.map +1 -0
  62. package/dist/schemas/index.d.ts +23 -0
  63. package/dist/schemas/index.d.ts.map +1 -0
  64. package/dist/schemas/index.js +23 -0
  65. package/dist/schemas/index.js.map +1 -0
  66. package/dist/schemas/log.d.ts +36 -0
  67. package/dist/schemas/log.d.ts.map +1 -0
  68. package/dist/schemas/log.js +27 -0
  69. package/dist/schemas/log.js.map +1 -0
  70. package/dist/schemas/prepare-context.d.ts +41 -0
  71. package/dist/schemas/prepare-context.d.ts.map +1 -0
  72. package/dist/schemas/prepare-context.js +31 -0
  73. package/dist/schemas/prepare-context.js.map +1 -0
  74. package/dist/schemas/recall.d.ts +41 -0
  75. package/dist/schemas/recall.d.ts.map +1 -0
  76. package/dist/schemas/recall.js +47 -0
  77. package/dist/schemas/recall.js.map +1 -0
  78. package/dist/schemas/record-scar-usage-batch.d.ts +82 -0
  79. package/dist/schemas/record-scar-usage-batch.d.ts.map +1 -0
  80. package/dist/schemas/record-scar-usage-batch.js +25 -0
  81. package/dist/schemas/record-scar-usage-batch.js.map +1 -0
  82. package/dist/schemas/record-scar-usage.d.ts +51 -0
  83. package/dist/schemas/record-scar-usage.d.ts.map +1 -0
  84. package/dist/schemas/record-scar-usage.js +32 -0
  85. package/dist/schemas/record-scar-usage.js.map +1 -0
  86. package/dist/schemas/save-transcript.d.ts +38 -0
  87. package/dist/schemas/save-transcript.d.ts.map +1 -0
  88. package/dist/schemas/save-transcript.js +30 -0
  89. package/dist/schemas/save-transcript.js.map +1 -0
  90. package/dist/schemas/search.d.ts +36 -0
  91. package/dist/schemas/search.d.ts.map +1 -0
  92. package/dist/schemas/search.js +27 -0
  93. package/dist/schemas/search.js.map +1 -0
  94. package/dist/schemas/session-close.d.ts +371 -0
  95. package/dist/schemas/session-close.d.ts.map +1 -0
  96. package/dist/schemas/session-close.js +95 -0
  97. package/dist/schemas/session-close.js.map +1 -0
  98. package/dist/schemas/session-start.d.ts +46 -0
  99. package/dist/schemas/session-start.d.ts.map +1 -0
  100. package/dist/schemas/session-start.js +33 -0
  101. package/dist/schemas/session-start.js.map +1 -0
  102. package/dist/schemas/thread.d.ts +72 -0
  103. package/dist/schemas/thread.d.ts.map +1 -0
  104. package/dist/schemas/thread.js +39 -0
  105. package/dist/schemas/thread.js.map +1 -0
  106. package/dist/server.d.ts +22 -0
  107. package/dist/server.d.ts.map +1 -0
  108. package/dist/server.js +313 -0
  109. package/dist/server.js.map +1 -0
  110. package/dist/services/active-sessions.d.ts +66 -0
  111. package/dist/services/active-sessions.d.ts.map +1 -0
  112. package/dist/services/active-sessions.js +311 -0
  113. package/dist/services/active-sessions.js.map +1 -0
  114. package/dist/services/agent-detection.d.ts +25 -0
  115. package/dist/services/agent-detection.d.ts.map +1 -0
  116. package/dist/services/agent-detection.js +93 -0
  117. package/dist/services/agent-detection.js.map +1 -0
  118. package/dist/services/analytics.d.ts +201 -0
  119. package/dist/services/analytics.d.ts.map +1 -0
  120. package/dist/services/analytics.js +483 -0
  121. package/dist/services/analytics.js.map +1 -0
  122. package/dist/services/cache.d.ts +148 -0
  123. package/dist/services/cache.d.ts.map +1 -0
  124. package/dist/services/cache.js +384 -0
  125. package/dist/services/cache.js.map +1 -0
  126. package/dist/services/cache.test.d.ts +8 -0
  127. package/dist/services/cache.test.d.ts.map +1 -0
  128. package/dist/services/cache.test.js +267 -0
  129. package/dist/services/cache.test.js.map +1 -0
  130. package/dist/services/compliance-validator.d.ts +30 -0
  131. package/dist/services/compliance-validator.d.ts.map +1 -0
  132. package/dist/services/compliance-validator.js +257 -0
  133. package/dist/services/compliance-validator.js.map +1 -0
  134. package/dist/services/config.d.ts +48 -0
  135. package/dist/services/config.d.ts.map +1 -0
  136. package/dist/services/config.js +128 -0
  137. package/dist/services/config.js.map +1 -0
  138. package/dist/services/embedding.d.ts +58 -0
  139. package/dist/services/embedding.d.ts.map +1 -0
  140. package/dist/services/embedding.js +243 -0
  141. package/dist/services/embedding.js.map +1 -0
  142. package/dist/services/gitmem-dir.d.ts +38 -0
  143. package/dist/services/gitmem-dir.d.ts.map +1 -0
  144. package/dist/services/gitmem-dir.js +84 -0
  145. package/dist/services/gitmem-dir.js.map +1 -0
  146. package/dist/services/local-file-storage.d.ts +56 -0
  147. package/dist/services/local-file-storage.d.ts.map +1 -0
  148. package/dist/services/local-file-storage.js +213 -0
  149. package/dist/services/local-file-storage.js.map +1 -0
  150. package/dist/services/local-vector-search.d.ts +137 -0
  151. package/dist/services/local-vector-search.d.ts.map +1 -0
  152. package/dist/services/local-vector-search.js +311 -0
  153. package/dist/services/local-vector-search.js.map +1 -0
  154. package/dist/services/metrics.d.ts +104 -0
  155. package/dist/services/metrics.d.ts.map +1 -0
  156. package/dist/services/metrics.js +264 -0
  157. package/dist/services/metrics.js.map +1 -0
  158. package/dist/services/session-state.d.ts +113 -0
  159. package/dist/services/session-state.d.ts.map +1 -0
  160. package/dist/services/session-state.js +203 -0
  161. package/dist/services/session-state.js.map +1 -0
  162. package/dist/services/startup.d.ts +112 -0
  163. package/dist/services/startup.d.ts.map +1 -0
  164. package/dist/services/startup.js +436 -0
  165. package/dist/services/startup.js.map +1 -0
  166. package/dist/services/storage.d.ts +43 -0
  167. package/dist/services/storage.d.ts.map +1 -0
  168. package/dist/services/storage.js +92 -0
  169. package/dist/services/storage.js.map +1 -0
  170. package/dist/services/supabase-client.d.ts +163 -0
  171. package/dist/services/supabase-client.d.ts.map +1 -0
  172. package/dist/services/supabase-client.js +510 -0
  173. package/dist/services/supabase-client.js.map +1 -0
  174. package/dist/services/thread-dedup.d.ts +44 -0
  175. package/dist/services/thread-dedup.d.ts.map +1 -0
  176. package/dist/services/thread-dedup.js +113 -0
  177. package/dist/services/thread-dedup.js.map +1 -0
  178. package/dist/services/thread-manager.d.ts +77 -0
  179. package/dist/services/thread-manager.d.ts.map +1 -0
  180. package/dist/services/thread-manager.js +250 -0
  181. package/dist/services/thread-manager.js.map +1 -0
  182. package/dist/services/thread-suggestions.d.ts +66 -0
  183. package/dist/services/thread-suggestions.d.ts.map +1 -0
  184. package/dist/services/thread-suggestions.js +243 -0
  185. package/dist/services/thread-suggestions.js.map +1 -0
  186. package/dist/services/thread-supabase.d.ts +111 -0
  187. package/dist/services/thread-supabase.d.ts.map +1 -0
  188. package/dist/services/thread-supabase.js +459 -0
  189. package/dist/services/thread-supabase.js.map +1 -0
  190. package/dist/services/thread-vitality.d.ts +65 -0
  191. package/dist/services/thread-vitality.d.ts.map +1 -0
  192. package/dist/services/thread-vitality.js +143 -0
  193. package/dist/services/thread-vitality.js.map +1 -0
  194. package/dist/services/tier.d.ts +52 -0
  195. package/dist/services/tier.d.ts.map +1 -0
  196. package/dist/services/tier.js +109 -0
  197. package/dist/services/tier.js.map +1 -0
  198. package/dist/services/timezone.d.ts +37 -0
  199. package/dist/services/timezone.d.ts.map +1 -0
  200. package/dist/services/timezone.js +147 -0
  201. package/dist/services/timezone.js.map +1 -0
  202. package/dist/services/transcript-chunker.d.ts +18 -0
  203. package/dist/services/transcript-chunker.d.ts.map +1 -0
  204. package/dist/services/transcript-chunker.js +237 -0
  205. package/dist/services/transcript-chunker.js.map +1 -0
  206. package/dist/services/triple-writer.d.ts +128 -0
  207. package/dist/services/triple-writer.d.ts.map +1 -0
  208. package/dist/services/triple-writer.js +338 -0
  209. package/dist/services/triple-writer.js.map +1 -0
  210. package/dist/services/variant-assignment.d.ts +92 -0
  211. package/dist/services/variant-assignment.d.ts.map +1 -0
  212. package/dist/services/variant-assignment.js +196 -0
  213. package/dist/services/variant-assignment.js.map +1 -0
  214. package/dist/tools/absorb-observations.d.ts +16 -0
  215. package/dist/tools/absorb-observations.d.ts.map +1 -0
  216. package/dist/tools/absorb-observations.js +82 -0
  217. package/dist/tools/absorb-observations.js.map +1 -0
  218. package/dist/tools/analyze.d.ts +55 -0
  219. package/dist/tools/analyze.d.ts.map +1 -0
  220. package/dist/tools/analyze.js +139 -0
  221. package/dist/tools/analyze.js.map +1 -0
  222. package/dist/tools/cleanup-threads.d.ts +47 -0
  223. package/dist/tools/cleanup-threads.d.ts.map +1 -0
  224. package/dist/tools/cleanup-threads.js +127 -0
  225. package/dist/tools/cleanup-threads.js.map +1 -0
  226. package/dist/tools/confirm-scars.d.ts +23 -0
  227. package/dist/tools/confirm-scars.d.ts.map +1 -0
  228. package/dist/tools/confirm-scars.js +209 -0
  229. package/dist/tools/confirm-scars.js.map +1 -0
  230. package/dist/tools/create-decision.d.ts +15 -0
  231. package/dist/tools/create-decision.d.ts.map +1 -0
  232. package/dist/tools/create-decision.js +138 -0
  233. package/dist/tools/create-decision.js.map +1 -0
  234. package/dist/tools/create-learning.d.ts +15 -0
  235. package/dist/tools/create-learning.d.ts.map +1 -0
  236. package/dist/tools/create-learning.js +226 -0
  237. package/dist/tools/create-learning.js.map +1 -0
  238. package/dist/tools/create-thread.d.ts +42 -0
  239. package/dist/tools/create-thread.d.ts.map +1 -0
  240. package/dist/tools/create-thread.js +180 -0
  241. package/dist/tools/create-thread.js.map +1 -0
  242. package/dist/tools/definitions.d.ts +5013 -0
  243. package/dist/tools/definitions.d.ts.map +1 -0
  244. package/dist/tools/definitions.js +2017 -0
  245. package/dist/tools/definitions.js.map +1 -0
  246. package/dist/tools/dismiss-suggestion.d.ts +20 -0
  247. package/dist/tools/dismiss-suggestion.d.ts.map +1 -0
  248. package/dist/tools/dismiss-suggestion.js +40 -0
  249. package/dist/tools/dismiss-suggestion.js.map +1 -0
  250. package/dist/tools/get-transcript.d.ts +24 -0
  251. package/dist/tools/get-transcript.d.ts.map +1 -0
  252. package/dist/tools/get-transcript.js +52 -0
  253. package/dist/tools/get-transcript.js.map +1 -0
  254. package/dist/tools/graph-traverse.d.ts +83 -0
  255. package/dist/tools/graph-traverse.d.ts.map +1 -0
  256. package/dist/tools/graph-traverse.js +394 -0
  257. package/dist/tools/graph-traverse.js.map +1 -0
  258. package/dist/tools/list-threads.d.ts +15 -0
  259. package/dist/tools/list-threads.d.ts.map +1 -0
  260. package/dist/tools/list-threads.js +114 -0
  261. package/dist/tools/list-threads.js.map +1 -0
  262. package/dist/tools/log.d.ts +43 -0
  263. package/dist/tools/log.d.ts.map +1 -0
  264. package/dist/tools/log.js +157 -0
  265. package/dist/tools/log.js.map +1 -0
  266. package/dist/tools/prepare-context.d.ts +36 -0
  267. package/dist/tools/prepare-context.d.ts.map +1 -0
  268. package/dist/tools/prepare-context.js +353 -0
  269. package/dist/tools/prepare-context.js.map +1 -0
  270. package/dist/tools/promote-suggestion.d.ts +25 -0
  271. package/dist/tools/promote-suggestion.d.ts.map +1 -0
  272. package/dist/tools/promote-suggestion.js +60 -0
  273. package/dist/tools/promote-suggestion.js.map +1 -0
  274. package/dist/tools/recall.d.ts +77 -0
  275. package/dist/tools/recall.d.ts.map +1 -0
  276. package/dist/tools/recall.js +423 -0
  277. package/dist/tools/recall.js.map +1 -0
  278. package/dist/tools/recall.test.d.ts +5 -0
  279. package/dist/tools/recall.test.d.ts.map +1 -0
  280. package/dist/tools/recall.test.js +155 -0
  281. package/dist/tools/recall.test.js.map +1 -0
  282. package/dist/tools/record-scar-usage-batch.d.ts +10 -0
  283. package/dist/tools/record-scar-usage-batch.d.ts.map +1 -0
  284. package/dist/tools/record-scar-usage-batch.js +153 -0
  285. package/dist/tools/record-scar-usage-batch.js.map +1 -0
  286. package/dist/tools/record-scar-usage.d.ts +14 -0
  287. package/dist/tools/record-scar-usage.d.ts.map +1 -0
  288. package/dist/tools/record-scar-usage.js +94 -0
  289. package/dist/tools/record-scar-usage.js.map +1 -0
  290. package/dist/tools/resolve-thread.d.ts +16 -0
  291. package/dist/tools/resolve-thread.d.ts.map +1 -0
  292. package/dist/tools/resolve-thread.js +102 -0
  293. package/dist/tools/resolve-thread.js.map +1 -0
  294. package/dist/tools/save-transcript.d.ts +29 -0
  295. package/dist/tools/save-transcript.d.ts.map +1 -0
  296. package/dist/tools/save-transcript.js +97 -0
  297. package/dist/tools/save-transcript.js.map +1 -0
  298. package/dist/tools/search.d.ts +46 -0
  299. package/dist/tools/search.d.ts.map +1 -0
  300. package/dist/tools/search.js +186 -0
  301. package/dist/tools/search.js.map +1 -0
  302. package/dist/tools/session-close.d.ts +14 -0
  303. package/dist/tools/session-close.d.ts.map +1 -0
  304. package/dist/tools/session-close.js +881 -0
  305. package/dist/tools/session-close.js.map +1 -0
  306. package/dist/tools/session-start.d.ts +38 -0
  307. package/dist/tools/session-start.d.ts.map +1 -0
  308. package/dist/tools/session-start.js +1104 -0
  309. package/dist/tools/session-start.js.map +1 -0
  310. package/dist/types/index.d.ts +456 -0
  311. package/dist/types/index.d.ts.map +1 -0
  312. package/dist/types/index.js +5 -0
  313. package/dist/types/index.js.map +1 -0
  314. package/package.json +76 -0
  315. package/schema/setup.sql +193 -0
  316. package/schema/starter-scars.json +206 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Embedding Service — Multi-provider embedding abstraction
3
+ *
4
+ * Generates text embeddings for semantic search using multiple providers.
5
+ * Auto-detects provider from available API keys.
6
+ *
7
+ * Provider detection (in priority order):
8
+ * GITMEM_EMBEDDING_PROVIDER=openai|openrouter|ollama — force provider
9
+ * OPENAI_API_KEY → OpenAI direct (text-embedding-3-small)
10
+ * OPENROUTER_API_KEY → OpenRouter (text-embedding-3-small via openrouter.ai)
11
+ * OLLAMA_URL → Ollama local (nomic-embed-text, 768-dim — requires pgvector dimension match)
12
+ *
13
+ * If no provider is configured, embed() returns null (graceful degradation).
14
+ * Records stored without embeddings can still be retrieved by ID/filters,
15
+ * but won't appear in semantic search results.
16
+ */
17
+ // Default embedding dimensions per provider
18
+ const OPENAI_EMBEDDING_DIM = 1536;
19
+ const OLLAMA_DEFAULT_DIM = 768;
20
+ // Model configuration
21
+ const OPENAI_EMBEDDING_MODEL = "text-embedding-3-small";
22
+ const OPENROUTER_EMBEDDING_MODEL = "openai/text-embedding-3-small";
23
+ const OLLAMA_EMBEDDING_MODEL = "nomic-embed-text";
24
+ // API URLs
25
+ const OPENAI_API_URL = "https://api.openai.com/v1/embeddings";
26
+ const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/embeddings";
27
+ /**
28
+ * Normalize a vector to unit length
29
+ */
30
+ function normalize(vec) {
31
+ let magnitude = 0;
32
+ for (const v of vec) {
33
+ magnitude += v * v;
34
+ }
35
+ magnitude = Math.sqrt(magnitude);
36
+ if (magnitude === 0)
37
+ return vec;
38
+ return vec.map((v) => v / magnitude);
39
+ }
40
+ /**
41
+ * Detect the best available embedding provider from environment
42
+ */
43
+ export function detectProvider() {
44
+ const forced = process.env.GITMEM_EMBEDDING_PROVIDER?.toLowerCase();
45
+ // Forced provider
46
+ if (forced && forced !== "auto") {
47
+ switch (forced) {
48
+ case "openai": {
49
+ const key = process.env.OPENAI_API_KEY;
50
+ if (!key) {
51
+ console.warn("[embedding] GITMEM_EMBEDDING_PROVIDER=openai but OPENAI_API_KEY not set");
52
+ return { provider: "none", apiUrl: "", apiKey: "", model: "", expectedDim: 0 };
53
+ }
54
+ return {
55
+ provider: "openai",
56
+ apiUrl: OPENAI_API_URL,
57
+ apiKey: key,
58
+ model: OPENAI_EMBEDDING_MODEL,
59
+ expectedDim: OPENAI_EMBEDDING_DIM,
60
+ };
61
+ }
62
+ case "openrouter": {
63
+ const key = process.env.OPENROUTER_API_KEY;
64
+ if (!key) {
65
+ console.warn("[embedding] GITMEM_EMBEDDING_PROVIDER=openrouter but OPENROUTER_API_KEY not set");
66
+ return { provider: "none", apiUrl: "", apiKey: "", model: "", expectedDim: 0 };
67
+ }
68
+ return {
69
+ provider: "openrouter",
70
+ apiUrl: OPENROUTER_API_URL,
71
+ apiKey: key,
72
+ model: OPENROUTER_EMBEDDING_MODEL,
73
+ expectedDim: OPENAI_EMBEDDING_DIM,
74
+ };
75
+ }
76
+ case "ollama": {
77
+ const url = process.env.OLLAMA_URL || "http://localhost:11434";
78
+ return {
79
+ provider: "ollama",
80
+ apiUrl: `${url}/api/embed`,
81
+ apiKey: "",
82
+ model: process.env.GITMEM_OLLAMA_MODEL || OLLAMA_EMBEDDING_MODEL,
83
+ expectedDim: parseInt(process.env.GITMEM_EMBEDDING_DIM || String(OLLAMA_DEFAULT_DIM), 10),
84
+ };
85
+ }
86
+ default:
87
+ console.warn(`[embedding] Unknown provider: ${forced}`);
88
+ return { provider: "none", apiUrl: "", apiKey: "", model: "", expectedDim: 0 };
89
+ }
90
+ }
91
+ // Auto-detect from available keys (priority: OpenAI > OpenRouter > Ollama)
92
+ if (process.env.OPENAI_API_KEY) {
93
+ return {
94
+ provider: "openai",
95
+ apiUrl: OPENAI_API_URL,
96
+ apiKey: process.env.OPENAI_API_KEY,
97
+ model: OPENAI_EMBEDDING_MODEL,
98
+ expectedDim: OPENAI_EMBEDDING_DIM,
99
+ };
100
+ }
101
+ if (process.env.OPENROUTER_API_KEY) {
102
+ return {
103
+ provider: "openrouter",
104
+ apiUrl: OPENROUTER_API_URL,
105
+ apiKey: process.env.OPENROUTER_API_KEY,
106
+ model: OPENROUTER_EMBEDDING_MODEL,
107
+ expectedDim: OPENAI_EMBEDDING_DIM,
108
+ };
109
+ }
110
+ if (process.env.OLLAMA_URL) {
111
+ const url = process.env.OLLAMA_URL;
112
+ return {
113
+ provider: "ollama",
114
+ apiUrl: `${url}/api/embed`,
115
+ apiKey: "",
116
+ model: process.env.GITMEM_OLLAMA_MODEL || OLLAMA_EMBEDDING_MODEL,
117
+ expectedDim: parseInt(process.env.GITMEM_EMBEDDING_DIM || String(OLLAMA_DEFAULT_DIM), 10),
118
+ };
119
+ }
120
+ return { provider: "none", apiUrl: "", apiKey: "", model: "", expectedDim: 0 };
121
+ }
122
+ // Cached config (loaded once per process)
123
+ let _config = null;
124
+ /**
125
+ * Get the current embedding configuration (cached)
126
+ */
127
+ export function getEmbeddingConfig() {
128
+ if (!_config) {
129
+ _config = detectProvider();
130
+ if (_config.provider !== "none") {
131
+ console.error(`[embedding] Provider: ${_config.provider} (model: ${_config.model}, dim: ${_config.expectedDim})`);
132
+ }
133
+ else {
134
+ console.error("[embedding] No embedding provider configured — scars will be stored without embeddings");
135
+ }
136
+ }
137
+ return _config;
138
+ }
139
+ /**
140
+ * Reset cached config (for testing)
141
+ */
142
+ export function resetEmbeddingConfig() {
143
+ _config = null;
144
+ }
145
+ /**
146
+ * Generate an embedding for text using OpenAI-compatible API (OpenAI or OpenRouter)
147
+ */
148
+ async function embedOpenAI(text, config) {
149
+ const response = await fetch(config.apiUrl, {
150
+ method: "POST",
151
+ headers: {
152
+ Authorization: `Bearer ${config.apiKey}`,
153
+ "Content-Type": "application/json",
154
+ },
155
+ body: JSON.stringify({
156
+ model: config.model,
157
+ input: text,
158
+ }),
159
+ });
160
+ if (!response.ok) {
161
+ const errorText = await response.text();
162
+ throw new Error(`${config.provider} embedding error (${response.status}): ${errorText}`);
163
+ }
164
+ const data = (await response.json());
165
+ if (!data.data || data.data.length === 0) {
166
+ throw new Error(`No embedding data in ${config.provider} response`);
167
+ }
168
+ return data.data[0].embedding;
169
+ }
170
+ /**
171
+ * Generate an embedding using Ollama local API
172
+ */
173
+ async function embedOllama(text, config) {
174
+ const response = await fetch(config.apiUrl, {
175
+ method: "POST",
176
+ headers: {
177
+ "Content-Type": "application/json",
178
+ },
179
+ body: JSON.stringify({
180
+ model: config.model,
181
+ input: text,
182
+ }),
183
+ });
184
+ if (!response.ok) {
185
+ const errorText = await response.text();
186
+ throw new Error(`Ollama embedding error (${response.status}): ${errorText}`);
187
+ }
188
+ const data = (await response.json());
189
+ if (!data.embeddings || data.embeddings.length === 0) {
190
+ throw new Error("No embedding data in Ollama response");
191
+ }
192
+ return data.embeddings[0];
193
+ }
194
+ /**
195
+ * Generate an embedding for the given text.
196
+ *
197
+ * Returns normalized embedding vector, or null if no provider is configured.
198
+ * Throws on API errors (network, auth, rate limit).
199
+ */
200
+ export async function embed(text) {
201
+ const config = getEmbeddingConfig();
202
+ if (config.provider === "none") {
203
+ return null;
204
+ }
205
+ let raw;
206
+ switch (config.provider) {
207
+ case "openai":
208
+ case "openrouter":
209
+ raw = await embedOpenAI(text, config);
210
+ break;
211
+ case "ollama":
212
+ raw = await embedOllama(text, config);
213
+ break;
214
+ default:
215
+ return null;
216
+ }
217
+ // Validate dimensions
218
+ if (raw.length !== config.expectedDim) {
219
+ console.warn(`[embedding] Unexpected dimensions: got ${raw.length}, expected ${config.expectedDim}`);
220
+ // Don't throw — store what we got, let pgvector validate
221
+ }
222
+ return normalize(raw);
223
+ }
224
+ /**
225
+ * Get the expected embedding dimension for the current provider.
226
+ * Returns 0 if no provider configured.
227
+ */
228
+ export function getEmbeddingDim() {
229
+ return getEmbeddingConfig().expectedDim;
230
+ }
231
+ /**
232
+ * Check if embedding generation is available.
233
+ */
234
+ export function isEmbeddingAvailable() {
235
+ return getEmbeddingConfig().provider !== "none";
236
+ }
237
+ /**
238
+ * Get the current provider name.
239
+ */
240
+ export function getProviderName() {
241
+ return getEmbeddingConfig().provider;
242
+ }
243
+ //# sourceMappingURL=embedding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../src/services/embedding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Resolved .gitmem directory path
3
+ *
4
+ * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
+ * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
6
+ * The MCP server is long-running, so we resolve the path once and cache it.
7
+ *
8
+ * Resolution order:
9
+ * 1. Cached path from session_start (most reliable — session_start created the directory)
10
+ * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
+ * 3. Fall back to process.cwd()/.gitmem (original behavior)
12
+ */
13
+ /**
14
+ * Set the .gitmem directory path (called by session_start after creating it)
15
+ */
16
+ export declare function setGitmemDir(dir: string): void;
17
+ /**
18
+ * Get the resolved .gitmem directory path
19
+ */
20
+ export declare function getGitmemDir(): string;
21
+ /**
22
+ * Get a file path within the .gitmem directory
23
+ */
24
+ export declare function getGitmemPath(filename: string): string;
25
+ /**
26
+ * Get the per-session directory path: .gitmem/sessions/<sessionId>/
27
+ * Creates the directory if it doesn't exist.
28
+ */
29
+ export declare function getSessionDir(sessionId: string): string;
30
+ /**
31
+ * Get a file path within a per-session directory.
32
+ */
33
+ export declare function getSessionPath(sessionId: string, filename: string): string;
34
+ /**
35
+ * Clear the cached path (for testing)
36
+ */
37
+ export declare function clearGitmemDirCache(): void;
38
+ //# sourceMappingURL=gitmem-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitmem-dir.d.ts","sourceRoot":"","sources":["../../src/services/gitmem-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CA6BrC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Resolved .gitmem directory path
3
+ *
4
+ * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
+ * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
6
+ * The MCP server is long-running, so we resolve the path once and cache it.
7
+ *
8
+ * Resolution order:
9
+ * 1. Cached path from session_start (most reliable — session_start created the directory)
10
+ * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
+ * 3. Fall back to process.cwd()/.gitmem (original behavior)
12
+ */
13
+ import * as path from "path";
14
+ import * as fs from "fs";
15
+ let cachedGitmemDir = null;
16
+ /**
17
+ * Set the .gitmem directory path (called by session_start after creating it)
18
+ */
19
+ export function setGitmemDir(dir) {
20
+ cachedGitmemDir = dir;
21
+ console.error(`[gitmem-dir] Cached .gitmem path: ${dir}`);
22
+ }
23
+ /**
24
+ * Get the resolved .gitmem directory path
25
+ */
26
+ export function getGitmemDir() {
27
+ // 1. Use cached path from session_start
28
+ if (cachedGitmemDir && fs.existsSync(cachedGitmemDir)) {
29
+ return cachedGitmemDir;
30
+ }
31
+ // 2. Walk up from CWD looking for existing .gitmem directory
32
+ // Sentinel files checked in priority order:
33
+ // - active-sessions.json (multi-session registry, GIT-19)
34
+ // - config.json (project-level gitmem config)
35
+ const sentinels = ["active-sessions.json", "config.json"];
36
+ let dir = process.cwd();
37
+ const root = path.parse(dir).root;
38
+ while (dir !== root) {
39
+ const candidate = path.join(dir, ".gitmem");
40
+ for (const sentinel of sentinels) {
41
+ if (fs.existsSync(path.join(candidate, sentinel))) {
42
+ cachedGitmemDir = candidate;
43
+ console.error(`[gitmem-dir] Found .gitmem via walk-up (${sentinel}): ${candidate}`);
44
+ return candidate;
45
+ }
46
+ }
47
+ dir = path.dirname(dir);
48
+ }
49
+ // 3. Fall back to CWD (original behavior)
50
+ const fallback = path.join(process.cwd(), ".gitmem");
51
+ console.error(`[gitmem-dir] Falling back to CWD: ${fallback}`);
52
+ return fallback;
53
+ }
54
+ /**
55
+ * Get a file path within the .gitmem directory
56
+ */
57
+ export function getGitmemPath(filename) {
58
+ return path.join(getGitmemDir(), filename);
59
+ }
60
+ /**
61
+ * Get the per-session directory path: .gitmem/sessions/<sessionId>/
62
+ * Creates the directory if it doesn't exist.
63
+ */
64
+ export function getSessionDir(sessionId) {
65
+ const sessionsDir = path.join(getGitmemDir(), "sessions", sessionId);
66
+ if (!fs.existsSync(sessionsDir)) {
67
+ fs.mkdirSync(sessionsDir, { recursive: true });
68
+ console.error(`[gitmem-dir] Created session directory: ${sessionsDir}`);
69
+ }
70
+ return sessionsDir;
71
+ }
72
+ /**
73
+ * Get a file path within a per-session directory.
74
+ */
75
+ export function getSessionPath(sessionId, filename) {
76
+ return path.join(getSessionDir(sessionId), filename);
77
+ }
78
+ /**
79
+ * Clear the cached path (for testing)
80
+ */
81
+ export function clearGitmemDirCache() {
82
+ cachedGitmemDir = null;
83
+ }
84
+ //# sourceMappingURL=gitmem-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitmem-dir.js","sourceRoot":"","sources":["../../src/services/gitmem-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,IAAI,eAAe,GAAkB,IAAI,CAAC;AAE1C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,eAAe,GAAG,GAAG,CAAC;IACtB,OAAO,CAAC,KAAK,CAAC,qCAAqC,GAAG,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,wCAAwC;IACxC,IAAI,eAAe,IAAI,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACtD,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,6DAA6D;IAC7D,+CAA+C;IAC/C,8DAA8D;IAC9D,2DAA2D;IAC3D,MAAM,SAAS,GAAG,CAAC,sBAAsB,EAAE,aAAa,CAAC,CAAC;IAC1D,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAClC,OAAO,GAAG,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;gBAClD,eAAe,GAAG,SAAS,CAAC;gBAC5B,OAAO,CAAC,KAAK,CAAC,2CAA2C,QAAQ,MAAM,SAAS,EAAE,CAAC,CAAC;gBACpF,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,CAAC,KAAK,CAAC,qCAAqC,QAAQ,EAAE,CAAC,CAAC;IAC/D,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,KAAK,CAAC,2CAA2C,WAAW,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,QAAgB;IAChE,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Local File Storage — Free Tier Backend
3
+ *
4
+ * Stores scars, sessions, decisions, and scar usage as JSON files
5
+ * in the .gitmem/ directory of the current project.
6
+ *
7
+ * Provides keyword-based search (no embeddings needed).
8
+ */
9
+ import type { RelevantScar } from "../types/index.js";
10
+ export declare class LocalFileStorage {
11
+ private basePath;
12
+ constructor(basePath?: string);
13
+ private ensureDir;
14
+ private getFilePath;
15
+ private readCollection;
16
+ private writeCollection;
17
+ /**
18
+ * List records with optional filters
19
+ */
20
+ list<T extends Record<string, unknown>>(collection: string, options?: {
21
+ filters?: Record<string, string>;
22
+ order?: string;
23
+ limit?: number;
24
+ }): Promise<T[]>;
25
+ /**
26
+ * Get a single record by ID
27
+ */
28
+ get<T extends Record<string, unknown>>(collection: string, id: string): Promise<T | null>;
29
+ /**
30
+ * Upsert a record (insert or update by ID)
31
+ */
32
+ upsert<T extends Record<string, unknown>>(collection: string, data: T & {
33
+ id: string;
34
+ }): Promise<T>;
35
+ /**
36
+ * Delete a record by ID
37
+ */
38
+ delete(collection: string, id: string): Promise<boolean>;
39
+ /**
40
+ * Keyword-based search for scars (free tier alternative to semantic search)
41
+ *
42
+ * Scores by: title match (3x), keyword match (2x), description match (1x)
43
+ */
44
+ keywordSearch(query: string, k?: number): Promise<RelevantScar[]>;
45
+ /**
46
+ * Get the count of learnings stored locally
47
+ */
48
+ getLearningCount(): number;
49
+ /**
50
+ * Load starter scars from a JSON file into local storage
51
+ */
52
+ loadStarterScars(scarsPath: string): Promise<number>;
53
+ }
54
+ export declare function getLocalFileStorage(): LocalFileStorage;
55
+ export declare function resetLocalFileStorage(): void;
56
+ //# sourceMappingURL=local-file-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-file-storage.d.ts","sourceRoot":"","sources":["../../src/services/local-file-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKtD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,CAAC,EAAE,MAAM;IAK7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAoBvB;;OAEG;IACG,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KACX,GACL,OAAO,CAAC,CAAC,EAAE,CAAC;IA8Bf;;OAEG;IACG,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzC,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKpB;;OAEG;IACG,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GACvB,OAAO,CAAC,CAAC,CAAC;IAcb;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ9D;;;;OAIG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,SAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyClE;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAyB3D;AAeD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAKtD;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Local File Storage — Free Tier Backend
3
+ *
4
+ * Stores scars, sessions, decisions, and scar usage as JSON files
5
+ * in the .gitmem/ directory of the current project.
6
+ *
7
+ * Provides keyword-based search (no embeddings needed).
8
+ */
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
12
+ const WARN_FILE_SIZE = 1 * 1024 * 1024; // 1MB
13
+ export class LocalFileStorage {
14
+ basePath;
15
+ constructor(basePath) {
16
+ this.basePath = basePath || path.join(process.cwd(), ".gitmem");
17
+ this.ensureDir();
18
+ }
19
+ ensureDir() {
20
+ if (!fs.existsSync(this.basePath)) {
21
+ fs.mkdirSync(this.basePath, { recursive: true });
22
+ }
23
+ }
24
+ getFilePath(collection) {
25
+ return path.join(this.basePath, `${collection}.json`);
26
+ }
27
+ readCollection(collection) {
28
+ const filePath = this.getFilePath(collection);
29
+ if (!fs.existsSync(filePath))
30
+ return [];
31
+ try {
32
+ const raw = fs.readFileSync(filePath, "utf-8");
33
+ return JSON.parse(raw);
34
+ }
35
+ catch {
36
+ console.error(`[local-storage] Failed to read ${collection}.json, starting fresh`);
37
+ return [];
38
+ }
39
+ }
40
+ writeCollection(collection, data) {
41
+ const filePath = this.getFilePath(collection);
42
+ const json = JSON.stringify(data, null, 2);
43
+ const size = Buffer.byteLength(json, "utf-8");
44
+ if (size > WARN_FILE_SIZE) {
45
+ console.error(`[local-storage] Warning: ${collection}.json is ${(size / 1024).toFixed(0)}KB`);
46
+ }
47
+ if (size > MAX_FILE_SIZE) {
48
+ console.error(`[local-storage] ${collection}.json exceeds 10MB, evicting oldest entries`);
49
+ // Keep most recent 80% of entries
50
+ const trimmed = data.slice(Math.floor(data.length * 0.2));
51
+ fs.writeFileSync(filePath, JSON.stringify(trimmed, null, 2), "utf-8");
52
+ return;
53
+ }
54
+ fs.writeFileSync(filePath, json, "utf-8");
55
+ }
56
+ /**
57
+ * List records with optional filters
58
+ */
59
+ async list(collection, options = {}) {
60
+ let records = this.readCollection(collection);
61
+ // Apply simple filters
62
+ if (options.filters) {
63
+ for (const [key, value] of Object.entries(options.filters)) {
64
+ const cleanValue = value.startsWith("eq.") ? value.slice(3) : value;
65
+ records = records.filter((r) => String(r[key]) === cleanValue);
66
+ }
67
+ }
68
+ // Apply ordering
69
+ if (options.order) {
70
+ const [field, direction] = options.order.split(".");
71
+ const asc = direction !== "desc";
72
+ records.sort((a, b) => {
73
+ const av = String(a[field] || "");
74
+ const bv = String(b[field] || "");
75
+ return asc ? av.localeCompare(bv) : bv.localeCompare(av);
76
+ });
77
+ }
78
+ // Apply limit
79
+ if (options.limit) {
80
+ records = records.slice(0, options.limit);
81
+ }
82
+ return records;
83
+ }
84
+ /**
85
+ * Get a single record by ID
86
+ */
87
+ async get(collection, id) {
88
+ const records = this.readCollection(collection);
89
+ return records.find((r) => r.id === id) || null;
90
+ }
91
+ /**
92
+ * Upsert a record (insert or update by ID)
93
+ */
94
+ async upsert(collection, data) {
95
+ const records = this.readCollection(collection);
96
+ const existingIndex = records.findIndex((r) => r.id === data.id);
97
+ if (existingIndex >= 0) {
98
+ records[existingIndex] = { ...records[existingIndex], ...data };
99
+ }
100
+ else {
101
+ records.push(data);
102
+ }
103
+ this.writeCollection(collection, records);
104
+ return data;
105
+ }
106
+ /**
107
+ * Delete a record by ID
108
+ */
109
+ async delete(collection, id) {
110
+ const records = this.readCollection(collection);
111
+ const filtered = records.filter((r) => r.id !== id);
112
+ if (filtered.length === records.length)
113
+ return false;
114
+ this.writeCollection(collection, filtered);
115
+ return true;
116
+ }
117
+ /**
118
+ * Keyword-based search for scars (free tier alternative to semantic search)
119
+ *
120
+ * Scores by: title match (3x), keyword match (2x), description match (1x)
121
+ */
122
+ async keywordSearch(query, k = 5) {
123
+ const learnings = this.readCollection("learnings");
124
+ const queryTokens = tokenize(query.toLowerCase());
125
+ if (queryTokens.length === 0)
126
+ return [];
127
+ const maxScore = queryTokens.length * 6; // max possible per token: 3+2+1
128
+ const scored = learnings.map((l) => {
129
+ let score = 0;
130
+ const titleTokens = tokenize(String(l.title || "").toLowerCase());
131
+ const descTokens = tokenize(String(l.description || "").toLowerCase());
132
+ const kwTokens = (l.keywords || []).map((k) => k.toLowerCase());
133
+ for (const qt of queryTokens) {
134
+ if (titleTokens.some((t) => t.includes(qt) || qt.includes(t)))
135
+ score += 3;
136
+ if (kwTokens.some((k) => k.includes(qt) || qt.includes(k)))
137
+ score += 2;
138
+ if (descTokens.some((t) => t.includes(qt) || qt.includes(t)))
139
+ score += 1;
140
+ }
141
+ return {
142
+ learning: l,
143
+ score,
144
+ similarity: Math.round((score / maxScore) * 1000) / 1000,
145
+ };
146
+ });
147
+ return scored
148
+ .filter((s) => s.score > 0)
149
+ .sort((a, b) => b.score - a.score)
150
+ .slice(0, k)
151
+ .map((s) => ({
152
+ id: String(s.learning.id),
153
+ title: String(s.learning.title),
154
+ description: String(s.learning.description),
155
+ severity: String(s.learning.severity || "medium"),
156
+ counter_arguments: s.learning.counter_arguments || [],
157
+ similarity: s.similarity,
158
+ }));
159
+ }
160
+ /**
161
+ * Get the count of learnings stored locally
162
+ */
163
+ getLearningCount() {
164
+ return this.readCollection("learnings").length;
165
+ }
166
+ /**
167
+ * Load starter scars from a JSON file into local storage
168
+ */
169
+ async loadStarterScars(scarsPath) {
170
+ try {
171
+ const raw = fs.readFileSync(scarsPath, "utf-8");
172
+ const scars = JSON.parse(raw);
173
+ const existing = this.readCollection("learnings");
174
+ const existingIds = new Set(existing.map((e) => e.id));
175
+ let loaded = 0;
176
+ for (const scar of scars) {
177
+ if (!existingIds.has(scar.id)) {
178
+ existing.push(scar);
179
+ loaded++;
180
+ }
181
+ }
182
+ if (loaded > 0) {
183
+ this.writeCollection("learnings", existing);
184
+ }
185
+ return loaded;
186
+ }
187
+ catch (error) {
188
+ console.error("[local-storage] Failed to load starter scars:", error);
189
+ return 0;
190
+ }
191
+ }
192
+ }
193
+ /**
194
+ * Tokenize text into words, stripping punctuation
195
+ */
196
+ function tokenize(text) {
197
+ return text
198
+ .replace(/[^\w\s-]/g, " ")
199
+ .split(/\s+/)
200
+ .filter((t) => t.length > 1);
201
+ }
202
+ // Singleton instance
203
+ let _instance = null;
204
+ export function getLocalFileStorage() {
205
+ if (!_instance) {
206
+ _instance = new LocalFileStorage();
207
+ }
208
+ return _instance;
209
+ }
210
+ export function resetLocalFileStorage() {
211
+ _instance = null;
212
+ }
213
+ //# sourceMappingURL=local-file-storage.js.map