squish-memory 1.0.2 → 1.1.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 (341) hide show
  1. package/.env.example +130 -0
  2. package/CHANGELOG.md +55 -0
  3. package/README.md +150 -287
  4. package/config/hooks/claude-code-hooks.json +39 -0
  5. package/config/hooks/cursor-hooks.json +30 -0
  6. package/config/hooks/opencode-hooks.json +30 -0
  7. package/config/hooks/windsurf-hooks.json +30 -0
  8. package/config/mcp-mode-semantics.json +23 -21
  9. package/config/plugin-manifest.json +101 -152
  10. package/{plugin.json → config/plugin.json} +2 -2
  11. package/config/settings.json +52 -51
  12. package/{commands → core/commands}/init.md +39 -39
  13. package/dist/config.d.ts +28 -4
  14. package/dist/config.js +97 -29
  15. package/dist/core/adapters/config/claude-code.d.ts +45 -0
  16. package/dist/core/adapters/config/claude-code.js +113 -0
  17. package/dist/core/adapters/config/cursor.d.ts +26 -0
  18. package/dist/core/adapters/config/cursor.js +74 -0
  19. package/dist/core/adapters/config/opencode.d.ts +23 -0
  20. package/dist/core/adapters/config/opencode.js +73 -0
  21. package/dist/core/adapters/config/windsurf.d.ts +26 -0
  22. package/dist/core/adapters/config/windsurf.js +74 -0
  23. package/dist/core/adapters/index.d.ts +45 -0
  24. package/dist/core/adapters/index.js +84 -0
  25. package/dist/core/adapters/scripts/install-adapter.d.ts +19 -0
  26. package/dist/core/adapters/scripts/install-adapter.js +149 -0
  27. package/dist/core/adapters/timeline.d.ts +23 -0
  28. package/dist/core/adapters/timeline.js +88 -0
  29. package/dist/core/adapters/types.d.ts +157 -0
  30. package/dist/core/adapters/types.js +50 -0
  31. package/dist/{algorithms → core/algorithms}/analytics/token-estimator.d.ts +1 -1
  32. package/dist/{algorithms → core/algorithms}/analytics/token-estimator.js +3 -3
  33. package/dist/{algorithms → core/algorithms}/detection/semantic-ranker.d.ts +1 -1
  34. package/dist/{algorithms → core/algorithms}/detection/semantic-ranker.js +1 -1
  35. package/dist/{algorithms → core/algorithms}/detection/two-stage-detector.d.ts +1 -1
  36. package/dist/{algorithms → core/algorithms}/detection/two-stage-detector.js +7 -10
  37. package/dist/{algorithms → core/algorithms}/handlers/approve-merge.js +4 -4
  38. package/dist/{algorithms → core/algorithms}/handlers/detect-duplicates.js +3 -3
  39. package/dist/{algorithms → core/algorithms}/handlers/get-stats.js +3 -3
  40. package/dist/{algorithms → core/algorithms}/handlers/list-proposals.js +3 -3
  41. package/dist/{algorithms → core/algorithms}/handlers/preview-merge.js +3 -3
  42. package/dist/{algorithms → core/algorithms}/handlers/reject-merge.js +3 -3
  43. package/dist/{algorithms → core/algorithms}/handlers/reverse-merge.js +3 -3
  44. package/dist/core/algorithms/index.d.ts +21 -0
  45. package/dist/core/algorithms/index.js +26 -0
  46. package/dist/core/algorithms/operations/cache-maintenance.d.ts +12 -0
  47. package/dist/core/algorithms/operations/cache-maintenance.js +157 -0
  48. package/dist/{algorithms → core/algorithms}/safety/safety-checks.d.ts +1 -1
  49. package/dist/{algorithms → core/algorithms}/strategies/merge-strategies.d.ts +19 -1
  50. package/dist/{algorithms → core/algorithms}/strategies/merge-strategies.js +74 -123
  51. package/dist/core/algorithms/types.d.ts +133 -0
  52. package/dist/core/algorithms/types.js +5 -0
  53. package/dist/core/associations.d.ts +1 -2
  54. package/dist/core/associations.js +1 -2
  55. package/dist/core/autosave.d.ts +19 -0
  56. package/dist/core/autosave.js +16 -0
  57. package/dist/{commands → core/commands}/managed-sync.js +5 -5
  58. package/dist/core/commands/mcp-server.js +739 -0
  59. package/dist/core/context/agent-context.d.ts +106 -0
  60. package/dist/core/context/agent-context.js +274 -0
  61. package/dist/core/{context-paging.d.ts → context/context-paging.d.ts} +2 -12
  62. package/dist/core/{context-paging.js → context/context-paging.js} +19 -39
  63. package/dist/core/context/context-window.d.ts +40 -0
  64. package/dist/core/context/context-window.js +177 -0
  65. package/dist/core/context/context.js +22 -0
  66. package/dist/core/embeddings.d.ts +1 -1
  67. package/dist/core/embeddings.js +54 -2
  68. package/dist/core/error-handling.d.ts +63 -0
  69. package/dist/core/error-handling.js +173 -0
  70. package/dist/core/external-folder/index.d.ts +102 -0
  71. package/dist/core/external-folder/index.js +294 -0
  72. package/dist/core/hooks/agent-hooks.d.ts +74 -0
  73. package/dist/core/hooks/agent-hooks.js +244 -0
  74. package/dist/core/hooks/auto-tagger.d.ts +19 -0
  75. package/dist/core/hooks/auto-tagger.js +155 -0
  76. package/dist/core/hooks/capture-filter.d.ts +41 -0
  77. package/dist/core/hooks/capture-filter.js +128 -0
  78. package/dist/core/index.d.ts +6 -6
  79. package/dist/core/index.js +6 -6
  80. package/dist/core/{agent-memory.js → ingestion/agent-memory.js} +5 -7
  81. package/dist/core/{core-memory.js → ingestion/core-memory.js} +4 -4
  82. package/dist/core/ingestion/learnings.d.ts +57 -0
  83. package/dist/core/ingestion/learnings.js +202 -0
  84. package/dist/core/lib/db-client.d.ts +114 -0
  85. package/dist/core/lib/db-client.js +130 -0
  86. package/dist/core/lib/schemas.d.ts +129 -0
  87. package/dist/core/lib/schemas.js +87 -0
  88. package/dist/core/{utils.d.ts → lib/utils.d.ts} +1 -0
  89. package/dist/core/{utils.js → lib/utils.js} +31 -15
  90. package/dist/core/lib/validation.d.ts +38 -0
  91. package/dist/core/lib/validation.js +151 -0
  92. package/dist/core/lifecycle.d.ts +7 -0
  93. package/dist/core/lifecycle.js +140 -20
  94. package/dist/core/local-embeddings.d.ts +6 -1
  95. package/dist/core/local-embeddings.js +6 -15
  96. package/dist/core/logger.js +7 -1
  97. package/dist/core/mcp/tools.js +35 -3
  98. package/dist/core/memory/categorizer.js +1 -0
  99. package/dist/core/memory/conflict-detector.js +1 -1
  100. package/dist/core/memory/consolidation.d.ts +1 -10
  101. package/dist/core/memory/consolidation.js +2 -11
  102. package/dist/core/memory/context-collector.js +1 -1
  103. package/dist/core/memory/edit-workflow.js +1 -1
  104. package/dist/core/memory/entity-resolver.js +7 -7
  105. package/dist/core/memory/fact-extractor.js +12 -12
  106. package/dist/core/memory/feedback-tracker.js +1 -1
  107. package/dist/core/memory/hooks.d.ts +88 -0
  108. package/dist/core/memory/hooks.js +174 -0
  109. package/dist/core/memory/hybrid-retrieval.js +2 -2
  110. package/dist/core/memory/hybrid-search.d.ts +1 -6
  111. package/dist/core/memory/hybrid-search.js +70 -84
  112. package/dist/core/memory/importance.d.ts +8 -13
  113. package/dist/core/memory/importance.js +47 -74
  114. package/dist/core/memory/loader.d.ts +31 -0
  115. package/dist/core/memory/loader.js +141 -0
  116. package/dist/core/memory/markdown/markdown-storage.d.ts +72 -0
  117. package/dist/core/memory/markdown/markdown-storage.js +243 -0
  118. package/dist/core/memory/memories.d.ts +12 -4
  119. package/dist/core/memory/memories.js +192 -180
  120. package/dist/core/memory/memory-lifecycle.d.ts +8 -0
  121. package/dist/core/memory/memory-lifecycle.js +55 -0
  122. package/dist/core/memory/migrate.d.ts +21 -0
  123. package/dist/core/memory/migrate.js +134 -0
  124. package/dist/core/memory/normalization.d.ts +22 -0
  125. package/dist/core/memory/normalization.js +26 -0
  126. package/dist/core/memory/progressive-disclosure.js +1 -1
  127. package/dist/core/memory/query-rewriter.js +9 -9
  128. package/dist/core/memory/serialization.d.ts +4 -0
  129. package/dist/core/memory/serialization.js +49 -0
  130. package/dist/core/memory/stats.d.ts +5 -0
  131. package/dist/core/memory/stats.js +63 -12
  132. package/dist/core/memory/temporal-facts.js +21 -0
  133. package/dist/core/memory/write-gate.js +1 -1
  134. package/dist/core/obsidian-vault.d.ts +30 -0
  135. package/dist/core/obsidian-vault.js +94 -0
  136. package/dist/core/places/index.d.ts +14 -0
  137. package/dist/core/places/index.js +14 -0
  138. package/dist/core/places/memory-places.d.ts +68 -0
  139. package/dist/core/places/memory-places.js +261 -0
  140. package/dist/core/places/places.d.ts +88 -0
  141. package/dist/core/places/places.js +314 -0
  142. package/dist/core/places/rules.d.ts +74 -0
  143. package/dist/core/places/rules.js +240 -0
  144. package/dist/core/places/walking.d.ts +56 -0
  145. package/dist/core/places/walking.js +121 -0
  146. package/dist/core/projects.d.ts +5 -0
  147. package/dist/core/projects.js +39 -18
  148. package/dist/core/responses.d.ts +96 -0
  149. package/dist/core/responses.js +122 -0
  150. package/dist/core/scheduler/cron-scheduler.js +29 -7
  151. package/dist/core/scheduler/index.d.ts +1 -1
  152. package/dist/core/scheduler/index.js +1 -1
  153. package/dist/core/scheduler/job-runner.js +1 -1
  154. package/dist/core/search/conversations.js +40 -42
  155. package/dist/core/search/entities.js +6 -9
  156. package/dist/core/search/graph-boost.d.ts +7 -0
  157. package/dist/core/search/graph-boost.js +23 -0
  158. package/dist/core/search/qmd-search.js +4 -4
  159. package/dist/core/security/encrypt.d.ts +6 -0
  160. package/dist/core/security/encrypt.js +47 -0
  161. package/dist/core/{governance.d.ts → security/governance.d.ts} +6 -1
  162. package/dist/core/security/governance.js +79 -0
  163. package/dist/core/session/auto-load.js +6 -6
  164. package/dist/core/session/index.d.ts +1 -1
  165. package/dist/core/session/index.js +1 -1
  166. package/dist/core/session/self-iteration-job.d.ts +20 -0
  167. package/dist/core/session/self-iteration-job.js +282 -0
  168. package/dist/core/session/session-hooks.d.ts +18 -0
  169. package/dist/core/session/session-hooks.js +58 -0
  170. package/dist/core/session-hooks/self-iteration-job.js +35 -35
  171. package/dist/core/{cache.js → storage/cache.js} +2 -2
  172. package/dist/core/sync/qmd-sync.d.ts +1 -13
  173. package/dist/core/sync/qmd-sync.js +1 -13
  174. package/dist/core/toon.d.ts +43 -0
  175. package/dist/core/toon.js +160 -0
  176. package/dist/core/utils/memory-operations.js +1 -1
  177. package/dist/core/utils/vector-operations.d.ts +71 -0
  178. package/dist/core/utils/vector-operations.js +129 -0
  179. package/dist/db/adapter.d.ts +3 -3
  180. package/dist/db/adapter.js +99 -88
  181. package/dist/db/bootstrap.js +820 -522
  182. package/dist/{drizzle → db/drizzle}/schema-sqlite.d.ts +74 -25
  183. package/dist/{drizzle → db/drizzle}/schema-sqlite.js +91 -24
  184. package/dist/{drizzle → db/drizzle}/schema.d.ts +79 -32
  185. package/dist/{drizzle → db/drizzle}/schema.js +106 -35
  186. package/dist/db/drizzle.config.d.ts +3 -0
  187. package/dist/db/drizzle.config.js +12 -0
  188. package/dist/db/index.d.ts +1 -5
  189. package/dist/db/index.js +51 -8
  190. package/dist/db/neon.d.ts +8 -0
  191. package/dist/db/neon.js +20 -0
  192. package/dist/db/schema/index.d.ts +40 -0
  193. package/dist/db/schema/index.js +105 -0
  194. package/dist/db/schema/tables/context-sessions.d.ts +9 -0
  195. package/dist/db/schema/tables/context-sessions.js +37 -0
  196. package/dist/db/schema/tables/conversations.d.ts +9 -0
  197. package/dist/db/schema/tables/conversations.js +47 -0
  198. package/dist/db/schema/tables/core-memory.d.ts +9 -0
  199. package/dist/db/schema/tables/core-memory.js +41 -0
  200. package/dist/db/schema/tables/entities.d.ts +9 -0
  201. package/dist/db/schema/tables/entities.js +39 -0
  202. package/dist/db/schema/tables/entity-relations.d.ts +9 -0
  203. package/dist/db/schema/tables/entity-relations.js +31 -0
  204. package/dist/db/schema/tables/learnings.d.ts +9 -0
  205. package/dist/db/schema/tables/learnings.js +66 -0
  206. package/dist/db/schema/tables/memories.d.ts +9 -0
  207. package/dist/db/schema/tables/memories.js +161 -0
  208. package/dist/db/schema/tables/memory-associations.d.ts +9 -0
  209. package/dist/db/schema/tables/memory-associations.js +39 -0
  210. package/dist/db/schema/tables/memory-hash-cache.d.ts +9 -0
  211. package/dist/db/schema/tables/memory-hash-cache.js +29 -0
  212. package/dist/db/schema/tables/memory-merge-history.d.ts +9 -0
  213. package/dist/db/schema/tables/memory-merge-history.js +33 -0
  214. package/dist/db/schema/tables/memory-merge-proposals.d.ts +9 -0
  215. package/dist/db/schema/tables/memory-merge-proposals.js +39 -0
  216. package/dist/db/schema/tables/messages.d.ts +9 -0
  217. package/dist/db/schema/tables/messages.js +41 -0
  218. package/dist/db/schema/tables/namespaces.d.ts +9 -0
  219. package/dist/db/schema/tables/namespaces.js +37 -0
  220. package/dist/db/schema/tables/projects.d.ts +9 -0
  221. package/dist/db/schema/tables/projects.js +31 -0
  222. package/dist/db/schema/tables/users.d.ts +9 -0
  223. package/dist/db/schema/tables/users.js +27 -0
  224. package/dist/db/schema.d.ts +1 -1
  225. package/dist/db/schema.js +2 -2
  226. package/dist/db/supabase.d.ts +9 -0
  227. package/dist/db/supabase.js +24 -0
  228. package/dist/index.d.ts +2 -14
  229. package/dist/index.js +1320 -640
  230. package/dist/vendor/sql.js/sql-wasm.wasm +0 -0
  231. package/dist/webui/server.d.ts +5 -0
  232. package/dist/{api/web/web.js → webui/server.js} +511 -508
  233. package/generated/mcp/manifest.json +1 -1
  234. package/{.mcp.json → mcp.json.example} +1 -1
  235. package/package.json +159 -181
  236. package/scripts/README.md +60 -0
  237. package/scripts/copy-runtime-assets.mjs +26 -0
  238. package/scripts/generate-mcp.mjs +264 -264
  239. package/scripts/github-release.sh +4 -4
  240. package/scripts/install-claude-code.sh +85 -0
  241. package/scripts/install-cursor.sh +56 -0
  242. package/scripts/install-hooks.sh +73 -0
  243. package/scripts/install-interactive.mjs +357 -677
  244. package/scripts/install-opencode.sh +75 -0
  245. package/scripts/install-windsurf.sh +67 -0
  246. package/skills/squish-memory/SKILL.md +104 -114
  247. package/skills/squish-memory/{install.mjs → scripts/install.mjs} +2 -2
  248. package/skills/squish-memory/{install.sh → scripts/install.sh} +2 -2
  249. package/skills/squish-memory/write_skill.js +2 -0
  250. package/.claude-plugin/marketplace.json +0 -20
  251. package/.claude-plugin/plugin.json +0 -32
  252. package/.env.mcp.example +0 -60
  253. package/QUICK-START.md +0 -71
  254. package/bin/squish-add.mjs +0 -32
  255. package/bin/squish-rm.mjs +0 -21
  256. package/commands/observe.md +0 -5
  257. package/dist/api/web/index.d.ts +0 -3
  258. package/dist/api/web/index.js +0 -4
  259. package/dist/api/web/web-server.d.ts +0 -3
  260. package/dist/api/web/web-server.js +0 -6
  261. package/dist/api/web/web.d.ts +0 -4
  262. package/dist/commands/mcp-server.js +0 -393
  263. package/dist/core/context.js +0 -24
  264. package/dist/core/governance.js +0 -64
  265. package/dist/core/observations.d.ts +0 -26
  266. package/dist/core/observations.js +0 -110
  267. package/dist/core/requirements.d.ts +0 -20
  268. package/dist/core/requirements.js +0 -35
  269. package/hooks/hooks.json +0 -52
  270. package/hooks/post-tool-use.js +0 -26
  271. package/hooks/session-end.js +0 -28
  272. package/hooks/session-start.js +0 -33
  273. package/hooks/user-prompt-submit.js +0 -26
  274. package/hooks/utils.js +0 -153
  275. package/npx-installer.js +0 -208
  276. package/packages/plugin-claude-code/README.md +0 -73
  277. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts +0 -35
  278. package/packages/plugin-claude-code/dist/plugin-wrapper.js +0 -191
  279. package/packages/plugin-claude-code/package.json +0 -31
  280. package/packages/plugin-openclaw/README.md +0 -70
  281. package/packages/plugin-openclaw/dist/index.d.ts +0 -49
  282. package/packages/plugin-openclaw/dist/index.js +0 -262
  283. package/packages/plugin-openclaw/openclaw.plugin.json +0 -94
  284. package/packages/plugin-openclaw/package.json +0 -31
  285. package/packages/plugin-opencode/install.mjs +0 -217
  286. package/packages/plugin-opencode/package.json +0 -21
  287. package/scripts/db/check-db.mjs +0 -88
  288. package/scripts/db/fix-all-columns.mjs +0 -52
  289. package/scripts/db/fix-schema-all.mjs +0 -55
  290. package/scripts/db/fix-schema-full.mjs +0 -46
  291. package/scripts/db/fix-schema.mjs +0 -38
  292. package/scripts/db/init-db.mjs +0 -13
  293. package/scripts/db/recreate-db.mjs +0 -14
  294. package/scripts/install-mcp.mjs +0 -116
  295. package/scripts/install-web.sh +0 -120
  296. package/scripts/install.mjs +0 -340
  297. package/scripts/openclaw-bootstrap.mjs +0 -127
  298. package/scripts/package-release.sh +0 -71
  299. package/scripts/test/test-all-systems.mjs +0 -139
  300. package/scripts/test/test-memory-system.mjs +0 -139
  301. package/scripts/test/test-v0.5.0.mjs +0 -210
  302. package/skills/memory-guide/SKILL.md +0 -332
  303. package/skills/squish-cli/SKILL.md +0 -240
  304. package/skills/squish-mcp/SKILL.md +0 -355
  305. package/skills/squish-memory/claude-desktop.json +0 -12
  306. package/skills/squish-memory/openclaw.json +0 -13
  307. package/skills/squish-memory/opencode.json +0 -14
  308. package/skills/squish-memory/skill.json +0 -32
  309. /package/{commands → core/commands}/context-paging.md +0 -0
  310. /package/{commands → core/commands}/context-status.md +0 -0
  311. /package/{commands → core/commands}/context.md +0 -0
  312. /package/{commands → core/commands}/core-memory.md +0 -0
  313. /package/{commands → core/commands}/health.md +0 -0
  314. /package/{commands → core/commands}/merge.md +0 -0
  315. /package/{commands → core/commands}/recall.md +0 -0
  316. /package/{commands → core/commands}/remember.md +0 -0
  317. /package/{commands → core/commands}/search.md +0 -0
  318. /package/dist/{algorithms → core/algorithms}/detection/hash-filters.d.ts +0 -0
  319. /package/dist/{algorithms → core/algorithms}/detection/hash-filters.js +0 -0
  320. /package/dist/{algorithms → core/algorithms}/handlers/approve-merge.d.ts +0 -0
  321. /package/dist/{algorithms → core/algorithms}/handlers/detect-duplicates.d.ts +0 -0
  322. /package/dist/{algorithms → core/algorithms}/handlers/get-stats.d.ts +0 -0
  323. /package/dist/{algorithms → core/algorithms}/handlers/list-proposals.d.ts +0 -0
  324. /package/dist/{algorithms → core/algorithms}/handlers/preview-merge.d.ts +0 -0
  325. /package/dist/{algorithms → core/algorithms}/handlers/reject-merge.d.ts +0 -0
  326. /package/dist/{algorithms → core/algorithms}/handlers/reverse-merge.d.ts +0 -0
  327. /package/dist/{algorithms → core/algorithms}/safety/safety-checks.js +0 -0
  328. /package/dist/{algorithms → core/algorithms}/utils/response-builder.d.ts +0 -0
  329. /package/dist/{algorithms → core/algorithms}/utils/response-builder.js +0 -0
  330. /package/dist/{commands → core/commands}/managed-sync.d.ts +0 -0
  331. /package/dist/{commands → core/commands}/mcp-server.d.ts +0 -0
  332. /package/dist/core/{context.d.ts → context/context.d.ts} +0 -0
  333. /package/dist/core/{agent-memory.d.ts → ingestion/agent-memory.d.ts} +0 -0
  334. /package/dist/core/{core-memory.d.ts → ingestion/core-memory.d.ts} +0 -0
  335. /package/dist/core/{privacy.d.ts → security/privacy.d.ts} +0 -0
  336. /package/dist/core/{privacy.js → security/privacy.js} +0 -0
  337. /package/dist/core/{secret-detector.d.ts → security/secret-detector.d.ts} +0 -0
  338. /package/dist/core/{secret-detector.js → security/secret-detector.js} +0 -0
  339. /package/dist/core/{cache.d.ts → storage/cache.d.ts} +0 -0
  340. /package/dist/core/{database.d.ts → storage/database.d.ts} +0 -0
  341. /package/dist/core/{database.js → storage/database.js} +0 -0
@@ -0,0 +1,739 @@
1
+ #!/usr/bin/env node
2
+ // CRITICAL: Redirect console.log to stderr to prevent JSON-RPC stream corruption
3
+ // MCP stdio requires stdout to contain ONLY valid JSON-RPC messages
4
+ console.log = console.error;
5
+ console.info = console.error;
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
9
+ import express from "express";
10
+ import { z } from "zod";
11
+ import { config } from "../../config.js";
12
+ import { writeFileSync } from "fs";
13
+ import { join } from "path";
14
+ import { hybridSearch } from "../../core/memory/hybrid-retrieval.js";
15
+ import { rememberMemory, search as searchMemories, getMemory, getRecent } from "../../core/memory/memories.js";
16
+ import { getQMDClient } from "../../core/embeddings/qmd-client.js";
17
+ import { createAssociation, getRelatedMemories } from "../../core/associations.js";
18
+ import { createLearning, getLearnings } from "../../core/ingestion/learnings.js";
19
+ import { requireProject, getAllProjects } from "../../core/projects.js";
20
+ import { getMemoryStats } from "../../core/memory/stats.js";
21
+ import { getDb } from "../../db/index.js";
22
+ import { getSchema } from "../../db/schema.js";
23
+ import { eq } from "drizzle-orm";
24
+ import { encrypt, decrypt } from "../../core/security/encrypt.js";
25
+ import { startWorker, stopWorker } from "../../core/worker.js";
26
+ import { initializeScheduler } from "../../core/scheduler/cron-scheduler.js";
27
+ import { serializeTags } from "../../core/memory/serialization.js";
28
+ const SERVER_NAME = "squish-memory";
29
+ const SERVER_VERSION = "1.1.5";
30
+ function parseArgs() {
31
+ const args = process.argv.slice(2);
32
+ let mode = "stdio";
33
+ let port = config.mcpServerPort || 8767;
34
+ let health = false;
35
+ for (let i = 0; i < args.length; i++) {
36
+ if (args[i] === "--http" || args[i] === "-h") {
37
+ mode = "http";
38
+ }
39
+ else if (args[i] === "--stdio" || args[i] === "-s") {
40
+ mode = "stdio";
41
+ }
42
+ else if (args[i] === "--port" || args[i] === "-p") {
43
+ port = parseInt(args[i + 1], 10) || 8767;
44
+ i++;
45
+ }
46
+ else if (args[i] === "--health" || args[i] === "--check") {
47
+ health = true;
48
+ }
49
+ }
50
+ if (process.env.SQUISH_MCP_MODE === "http") {
51
+ mode = "http";
52
+ }
53
+ return { mode, port, health };
54
+ }
55
+ function safeRegisterTool(server, name, definition, handler) {
56
+ try {
57
+ server.registerTool(name, definition, handler);
58
+ console.error(`[MCP] Registered tool: ${name}`);
59
+ return true;
60
+ }
61
+ catch (error) {
62
+ console.error(`[MCP] Failed to register tool ${name}:`, error);
63
+ return false;
64
+ }
65
+ }
66
+ function createSquishServer() {
67
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
68
+ let toolCount = 0;
69
+ console.error(`[MCP] Starting tool registration...`);
70
+ if (safeRegisterTool(server, "squish_search", {
71
+ description: "Hybrid search across QMD, SQLite DB, and embeddings with graph expansion",
72
+ inputSchema: {
73
+ query: z.string().describe("Search query"),
74
+ limit: z.number().min(1).max(100).default(5).describe("Maximum results"),
75
+ project: z.string().optional().describe("Project path filter"),
76
+ mode: z.enum(["hybrid", "qmd", "db", "semantic"]).default("hybrid").describe("Search mode")
77
+ }
78
+ }, async ({ query, limit = 5, project, mode = "hybrid" }) => {
79
+ const results = await hybridSearch({
80
+ query,
81
+ limit,
82
+ project,
83
+ candidateLimit: 50,
84
+ resultLimit: limit
85
+ });
86
+ const formatted = results.map((r, i) => `${i + 1}. [${r.type || "memory"}] ${r.content?.substring(0, 200)}... (score: ${r.hybridScore?.toFixed(2)})`).join("\n");
87
+ return { content: [{ type: "text", text: `Found ${results.length} memories:\n\n${formatted}` }] };
88
+ }))
89
+ toolCount++;
90
+ // squish_timeline - 3-layer progressive disclosure
91
+ if (safeRegisterTool(server, "squish_timeline", {
92
+ description: "3-layer progressive disclosure - index (~50 tokens), timeline (~200 tokens), detail (~2000 tokens)",
93
+ inputSchema: {
94
+ query: z.string().describe("Search query"),
95
+ depth: z.enum(["index", "timeline", "detail"]).default("index").describe("Progressive disclosure depth"),
96
+ limit: z.number().min(1).max(100).default(10).describe("Max results"),
97
+ project: z.string().optional().describe("Project path")
98
+ }
99
+ }, async ({ query, depth = "index", limit = 10, project }) => {
100
+ const { getTimeline } = await import('../../core/adapters/timeline.js');
101
+ const result = await getTimeline(query, depth, limit, project);
102
+ const formatted = result.results.map((r, i) => {
103
+ if (depth === "index") {
104
+ return `${i + 1}. ${r.title}`;
105
+ }
106
+ else if (depth === "timeline") {
107
+ return `${i + 1}. [${r.type}] ${r.content} (${r.tags?.join(', ') || 'no tags'})`;
108
+ }
109
+ else {
110
+ return `${i + 1}. [${r.type}] ${r.content?.substring(0, 200)}...`;
111
+ }
112
+ }).join("\n");
113
+ return { content: [{ type: "text", text: `Timeline (${depth}, ~${result.tokenEstimate} tokens):\n\n${formatted}` }] };
114
+ }))
115
+ toolCount++;
116
+ // Note: For session context, use squish_context tool (already exists)
117
+ // It provides project memories + observations + entities
118
+ if (safeRegisterTool(server, "squish_remember", {
119
+ description: "Store a new memory in Squish with automatic embedding",
120
+ inputSchema: {
121
+ content: z.string().describe("Memory content to store"),
122
+ type: z.enum(["observation", "fact", "decision", "context", "preference"]).default("observation").describe("Memory type"),
123
+ tags: z.array(z.string()).optional().describe("Optional tags"),
124
+ project: z.string().optional().describe("Project path")
125
+ }
126
+ }, async ({ content, type = "observation", tags = [], project }) => {
127
+ const memory = await rememberMemory({ content, type: type, tags, project });
128
+ return { content: [{ type: "text", text: `Memory stored: ${memory.id}` }] };
129
+ }))
130
+ toolCount++;
131
+ if (safeRegisterTool(server, "squish_recall", {
132
+ description: "Retrieve a specific memory by ID",
133
+ inputSchema: {
134
+ memoryId: z.string().uuid().describe("Memory ID to retrieve")
135
+ }
136
+ }, async ({ memoryId }) => {
137
+ const memory = await getMemory(memoryId);
138
+ if (!memory) {
139
+ return { content: [{ type: "text", text: `Memory not found: ${memoryId}` }], isError: true };
140
+ }
141
+ return { content: [{ type: "text", text: JSON.stringify(memory, null, 2) }] };
142
+ }))
143
+ toolCount++;
144
+ if (safeRegisterTool(server, "squish_forget", {
145
+ description: "Delete a memory by ID, or bulk delete with filters (older-than, search, type)",
146
+ inputSchema: {
147
+ memoryId: z.string().optional().describe("Memory ID to delete (single)"),
148
+ olderThan: z.string().optional().describe("Bulk delete memories older than (e.g., '30 days', '6 months')"),
149
+ search: z.string().optional().describe("Search query to match specific memories"),
150
+ type: z.string().optional().describe("Filter by memory type"),
151
+ confirm: z.boolean().optional().describe("Actually delete (default is dry-run)"),
152
+ limit: z.number().optional().describe("Max memories to delete"),
153
+ project: z.string().optional().describe("Project path (defaults to current)")
154
+ }
155
+ }, async ({ memoryId, olderThan, search, type, confirm = false, limit = 100, project }) => {
156
+ const db = await getDb();
157
+ const schema = await getSchema();
158
+ const sqliteDb = db;
159
+ const proj = project || process.cwd();
160
+ // Single memory deletion
161
+ if (memoryId) {
162
+ await sqliteDb.delete(schema.memories).where(eq(schema.memories.id, memoryId));
163
+ return { content: [{ type: "text", text: `Memory deleted: ${memoryId}` }] };
164
+ }
165
+ // Bulk deletion
166
+ if (!olderThan && !search) {
167
+ return { content: [{ type: "text", text: "Error: Provide memoryId or use --older-than / --search for bulk delete" }], isError: true };
168
+ }
169
+ const results = await searchMemories({ query: search || '', type: type, limit, project: proj });
170
+ let filtered = results;
171
+ if (olderThan) {
172
+ filtered = filterByDateRange(results, '', olderThan);
173
+ }
174
+ const deleted = [];
175
+ if (confirm) {
176
+ for (const mem of filtered) {
177
+ await sqliteDb.delete(schema.memories).where(eq(schema.memories.id, mem.id));
178
+ deleted.push(mem.id);
179
+ }
180
+ }
181
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, matched: filtered.length, deleted: deleted.length, dryRun: !confirm }, null, 2) }] };
182
+ }))
183
+ toolCount++;
184
+ if (safeRegisterTool(server, "squish_update", {
185
+ description: "Update an existing memory",
186
+ inputSchema: {
187
+ memoryId: z.string().uuid().describe("Memory ID to update"),
188
+ content: z.string().optional().describe("New content"),
189
+ tags: z.array(z.string()).optional().describe("New tags"),
190
+ type: z.enum(["observation", "fact", "decision", "context", "preference"]).optional().describe("New type")
191
+ }
192
+ }, async ({ memoryId, content, tags, type }) => {
193
+ const db = await getDb();
194
+ const schema = await getSchema();
195
+ const updates = {};
196
+ if (content)
197
+ updates.content = content;
198
+ if (tags)
199
+ updates.tags = serializeTags(tags);
200
+ if (type)
201
+ updates.type = type;
202
+ if (Object.keys(updates).length === 0) {
203
+ return { content: [{ type: "text", text: "No updates provided" }], isError: true };
204
+ }
205
+ // Cast to any to handle Drizzle ORM union type issue
206
+ const sqliteDb2 = db;
207
+ await sqliteDb2.update(schema.memories).set(updates).where(eq(schema.memories.id, memoryId));
208
+ return { content: [{ type: "text", text: `Memory updated: ${memoryId}` }] };
209
+ }))
210
+ toolCount++;
211
+ // squish_link - Unified graph operations (find related, add links, list)
212
+ if (safeRegisterTool(server, "squish_link", {
213
+ description: "Manage memory associations: find related memories, add links, or list associations",
214
+ inputSchema: {
215
+ action: z.enum(["find", "add", "list"]).describe("Action: find, add, or list"),
216
+ memoryId: z.string().optional().describe("Memory ID (for find action)"),
217
+ fromMemoryId: z.string().optional().describe("Source memory ID (for add action)"),
218
+ toMemoryId: z.string().optional().describe("Target memory ID (for add action)"),
219
+ type: z.string().optional().describe("Association type (for add action): relates_to, supports, contradicts, supersedes, duplicate"),
220
+ weight: z.number().min(0).max(1).default(0.5).describe("Association strength (0-1)"),
221
+ depth: z.number().min(1).max(5).default(2).describe("Graph traversal depth (for find action)"),
222
+ minWeight: z.number().min(0).max(1).default(0.3).describe("Minimum weight (for find action)")
223
+ }
224
+ }, async ({ action, memoryId, fromMemoryId, toMemoryId, type = "relates_to", weight = 0.5, depth = 2, minWeight = 0.3 }) => {
225
+ if (action === "find") {
226
+ if (!memoryId) {
227
+ return { content: [{ type: "text", text: "Error: memoryId required for find action" }], isError: true };
228
+ }
229
+ const related = await getRelatedMemories(memoryId, depth * 5);
230
+ const filtered = related.filter((r) => r.weight >= minWeight);
231
+ const formatted = filtered.map((r, i) => `${i + 1}. [${r.type || "memory"}] ${r.content?.substring(0, 100)}... (weight: ${r.weight?.toFixed(2)})`).join("\n");
232
+ return { content: [{ type: "text", text: `Found ${filtered.length} related memories:\n\n${formatted}` }] };
233
+ }
234
+ if (action === "add") {
235
+ if (!fromMemoryId || !toMemoryId) {
236
+ return { content: [{ type: "text", text: "Error: fromMemoryId and toMemoryId required for add action" }], isError: true };
237
+ }
238
+ await createAssociation(fromMemoryId, toMemoryId, type, weight);
239
+ return { content: [{ type: "text", text: `Association created: ${fromMemoryId} -> ${toMemoryId} (${type})` }] };
240
+ }
241
+ if (action === "list") {
242
+ const db = await getDb();
243
+ const schema = await getSchema();
244
+ const sqliteDb = db;
245
+ const associations = await sqliteDb.select().from(schema.memoryAssociations).limit(100);
246
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, count: associations.length, associations }, null, 2) }] };
247
+ }
248
+ return { content: [{ type: "text", text: "Error: invalid action. Use find, add, or list" }], isError: true };
249
+ }))
250
+ toolCount++;
251
+ if (safeRegisterTool(server, "squish_context", {
252
+ description: "Get project context or list registered projects",
253
+ inputSchema: {
254
+ project: z.string().optional().describe("Project path"),
255
+ limit: z.number().min(1).max(50).default(10).describe("Maximum memories to return"),
256
+ listProjects: z.boolean().optional().describe("List registered projects instead of loading context")
257
+ }
258
+ }, async ({ project, limit = 10, listProjects = false }) => {
259
+ if (listProjects) {
260
+ const projects = await getAllProjects();
261
+ const formatted = projects.map((p, i) => `${i + 1}. ${p.name}\n Path: ${p.path}\n ID: ${p.id}`).join("\n\n");
262
+ return { content: [{ type: "text", text: `Found ${projects.length} projects:\n\n${formatted}` }] };
263
+ }
264
+ if (!project) {
265
+ return { content: [{ type: "text", text: "Error: project is required unless listProjects=true" }], isError: true };
266
+ }
267
+ const projectRecord = await requireProject(project);
268
+ const recentMemories = await searchMemories({ query: "", project, limit });
269
+ const learnings = await getLearnings(project, 5);
270
+ const context = {
271
+ project: projectRecord,
272
+ recentMemories: recentMemories.slice(0, limit),
273
+ recentLearnings: learnings
274
+ };
275
+ return { content: [{ type: "text", text: JSON.stringify(context, null, 2) }] };
276
+ }))
277
+ toolCount++;
278
+ if (safeRegisterTool(server, "squish_learn", {
279
+ description: "Record learning: success, failure, fix, or insight. Auto-links to similar memories if above 85% similarity.",
280
+ inputSchema: {
281
+ type: z.enum(["success", "failure", "fix", "insight"]).describe("Learning type (required)"),
282
+ content: z.string().describe("What happened or what was learned"),
283
+ context: z.string().optional().describe("Additional context or result"),
284
+ action: z.string().optional().describe("Action performed"),
285
+ target: z.string().optional().describe("Target file or resource"),
286
+ memoryId: z.string().uuid().optional().describe("Optional memory ID to link this learning to"),
287
+ autoLink: z.boolean().optional().describe("Auto-link to similar memories (default: true)"),
288
+ project: z.string().optional().describe("Project path")
289
+ }
290
+ }, async ({ type, content, context, action, target, memoryId, autoLink, project }) => {
291
+ const learning = await createLearning({ type, content, context, action, target, project, memoryId, autoLink });
292
+ return { content: [{ type: "text", text: `Learning recorded: ${learning.id}\nType: ${type}\nContent: ${content}${memoryId ? '\nLinked to memory: ' + memoryId : ''}` }] };
293
+ }))
294
+ toolCount++;
295
+ if (safeRegisterTool(server, "squish_health", {
296
+ description: "Check Squish system health status",
297
+ inputSchema: {}
298
+ }, async () => {
299
+ const qmdClient = await getQMDClient();
300
+ const qmdAvailable = await qmdClient.isAvailable();
301
+ return { content: [{ type: "text", text: JSON.stringify({
302
+ status: "ok",
303
+ version: SERVER_VERSION,
304
+ mode: config.isManagedMode ? "managed" : "local",
305
+ embeddings: config.embeddingsProvider,
306
+ qmd: qmdAvailable ? "available" : "unavailable",
307
+ timestamp: new Date().toISOString()
308
+ }, null, 2) }] };
309
+ }))
310
+ toolCount++;
311
+ if (safeRegisterTool(server, "squish_stats", {
312
+ description: "Get memory statistics for a project",
313
+ inputSchema: {
314
+ project: z.string().optional().describe("Project path (defaults to current)")
315
+ }
316
+ }, async ({ project }) => {
317
+ const stats = await getMemoryStats(project || process.cwd());
318
+ return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
319
+ }))
320
+ toolCount++;
321
+ if (safeRegisterTool(server, "squish_confidence", {
322
+ description: "Get or set confidence level for a memory (0-100)",
323
+ inputSchema: {
324
+ memoryId: z.string().uuid().describe("Memory ID"),
325
+ level: z.number().min(0).max(100).optional().describe("Confidence level to set (0-100)")
326
+ }
327
+ }, async ({ memoryId, level }) => {
328
+ const db = await getDb();
329
+ const schema = await getSchema();
330
+ if (level !== undefined) {
331
+ const sqliteDb = db;
332
+ await sqliteDb.update(schema.memories)
333
+ .set({ confidence: level })
334
+ .where(eq(schema.memories.id, memoryId));
335
+ return { content: [{ type: "text", text: `Confidence set to ${level} for memory ${memoryId}` }] };
336
+ }
337
+ const sqliteDb2 = db;
338
+ const result = await sqliteDb2.select().from(schema.memories).where(eq(schema.memories.id, memoryId));
339
+ if (result.length === 0) {
340
+ return { content: [{ type: "text", text: `Memory not found: ${memoryId}` }], isError: true };
341
+ }
342
+ return { content: [{ type: "text", text: `Confidence for memory ${memoryId}: ${result[0].confidence}` }] };
343
+ }))
344
+ toolCount++;
345
+ if (safeRegisterTool(server, "squish_pin", {
346
+ description: "Pin or unpin a memory to prevent consolidation",
347
+ inputSchema: {
348
+ memoryId: z.string().uuid().describe("Memory ID"),
349
+ pinned: z.boolean().default(true).describe("Pin (true) or unpin (false)")
350
+ }
351
+ }, async ({ memoryId, pinned }) => {
352
+ const db = await getDb();
353
+ const schema = await getSchema();
354
+ const sqliteDb = db;
355
+ await sqliteDb.update(schema.memories)
356
+ .set({ isPinned: pinned })
357
+ .where(eq(schema.memories.id, memoryId));
358
+ return { content: [{ type: "text", text: `Memory ${memoryId} ${pinned ? 'pinned' : 'unpinned'}` }] };
359
+ }))
360
+ toolCount++;
361
+ // Register tool to set encryption passphrase
362
+ if (safeRegisterTool(server, "squish_set_passphrase", {
363
+ description: "Set the client-side encryption passphrase (writes to .env in data directory)",
364
+ inputSchema: {
365
+ passphrase: z.string().min(1).describe("Encryption passphrase to store")
366
+ }
367
+ }, async ({ passphrase }) => {
368
+ const envPath = join(config.dataDir, ".env");
369
+ try {
370
+ writeFileSync(envPath, `SQUISH_ENCRYPTION_PASSPHRASE=${passphrase}\n`, { flag: "w" });
371
+ return { content: [{ type: "text", text: `Passphrase written to ${envPath}` }] };
372
+ }
373
+ catch (error) {
374
+ return { content: [{ type: "text", text: `Failed to write passphrase: ${error}` }], isError: true };
375
+ }
376
+ }))
377
+ toolCount++;
378
+ // Register tool to rotate encryption passphrase (re-encrypt all encrypted memories)
379
+ if (safeRegisterTool(server, "squish_rotate_key", {
380
+ description: "Rotate the encryption passphrase - re-encrypts all memories with a new passphrase",
381
+ inputSchema: {
382
+ oldPassphrase: z.string().min(1).describe("Current encryption passphrase"),
383
+ newPassphrase: z.string().min(1).describe("New encryption passphrase")
384
+ }
385
+ }, async ({ oldPassphrase, newPassphrase }) => {
386
+ try {
387
+ const db = await getDb();
388
+ const schema = await getSchema();
389
+ // Fetch all encrypted memories
390
+ const sqliteDb = db;
391
+ const encryptedMemories = await sqliteDb
392
+ .select()
393
+ .from(schema.memories)
394
+ .where(eq(schema.memories.isEncrypted, true));
395
+ let rotated = 0;
396
+ for (const mem of encryptedMemories) {
397
+ try {
398
+ // Decrypt with old passphrase
399
+ const decrypted = decrypt(mem.encryptedContent, mem.encryptionNonce, oldPassphrase);
400
+ // Re-encrypt with new passphrase
401
+ const { ciphertext, nonce } = encrypt(decrypted, newPassphrase);
402
+ // Update memory
403
+ await sqliteDb
404
+ .update(schema.memories)
405
+ .set({
406
+ encryptedContent: ciphertext,
407
+ encryptionNonce: nonce
408
+ })
409
+ .where(eq(schema.memories.id, mem.id));
410
+ rotated++;
411
+ }
412
+ catch (e) {
413
+ // Skip memories that fail to decrypt (wrong passphrase)
414
+ }
415
+ }
416
+ // Update .env file with new passphrase
417
+ const envPath = join(config.dataDir, ".env");
418
+ writeFileSync(envPath, `SQUISH_ENCRYPTION_PASSPHRASE=${newPassphrase}\n`, { flag: "w" });
419
+ return { content: [{ type: "text", text: `Rotated encryption key for ${rotated} memories. New passphrase saved to ${envPath}` }] };
420
+ }
421
+ catch (error) {
422
+ return { content: [{ type: "text", text: `Failed to rotate key: ${error.message}` }], isError: true };
423
+ }
424
+ }))
425
+ toolCount++;
426
+ // Helper function for date filtering (same as in index.ts)
427
+ function parseDate(input) {
428
+ if (!input)
429
+ return null;
430
+ const now = new Date();
431
+ const lower = input.toLowerCase().trim();
432
+ if (!lower)
433
+ return null;
434
+ const parsed = new Date(input);
435
+ if (!isNaN(parsed.getTime()))
436
+ return parsed;
437
+ const dayMatch = lower.match(/(\d+)\s*day/i);
438
+ const weekMatch = lower.match(/(\d+)\s*week/i);
439
+ const monthMatch = lower.match(/(\d+)\s*month/i);
440
+ if (lower === 'today') {
441
+ const d = new Date(now);
442
+ d.setHours(0, 0, 0, 0);
443
+ return d;
444
+ }
445
+ if (lower === 'yesterday')
446
+ return new Date(now.getTime() - 86400000);
447
+ if (lower === 'thisweek' || lower === 'this week') {
448
+ const d = new Date(now);
449
+ d.setDate(d.getDate() - d.getDay());
450
+ d.setHours(0, 0, 0, 0);
451
+ return d;
452
+ }
453
+ if (dayMatch)
454
+ return new Date(now.getTime() - parseInt(dayMatch[1]) * 86400000);
455
+ if (weekMatch)
456
+ return new Date(now.getTime() - parseInt(weekMatch[1]) * 604800000);
457
+ if (monthMatch)
458
+ return new Date(now.getTime() - parseInt(monthMatch[1]) * 2592000000);
459
+ return null;
460
+ }
461
+ function filterByDateRange(items, since, until) {
462
+ const sinceDate = parseDate(since || '');
463
+ const untilDate = parseDate(until || '');
464
+ return items.filter(item => {
465
+ if (!item.createdAt)
466
+ return true;
467
+ const created = new Date(item.createdAt);
468
+ if (sinceDate && created < sinceDate)
469
+ return false;
470
+ if (untilDate && created > untilDate)
471
+ return false;
472
+ return true;
473
+ });
474
+ }
475
+ // squish_recent - Unified recent memories (replaces squish_today, squish_yesterday, squish_thisweek)
476
+ if (safeRegisterTool(server, "squish_recent", {
477
+ description: "Get recent memories by period (today, yesterday, thisweek, 7days, 30days, or custom)",
478
+ inputSchema: {
479
+ period: z.string().optional().describe("Period: today, yesterday, thisweek, 7days, 14days, 30days, 90days"),
480
+ since: z.string().optional().describe("Start date (alternative to period, e.g., '3 days', '2026-01-01')"),
481
+ until: z.string().optional().describe("End date (alternative to period, e.g., 'now', '2026-01-15')"),
482
+ limit: z.number().optional().describe("Max results to return"),
483
+ project: z.string().optional().describe("Project path (defaults to current)")
484
+ }
485
+ }, async ({ period = 'today', since, until, limit = 10, project }) => {
486
+ const proj = project || process.cwd();
487
+ let sinceDate, untilDate;
488
+ if (since && until) {
489
+ sinceDate = since;
490
+ untilDate = until;
491
+ }
492
+ else if (since) {
493
+ sinceDate = since;
494
+ untilDate = 'now';
495
+ }
496
+ else {
497
+ const periodMap = {
498
+ today: ['today', 'now'],
499
+ yesterday: ['yesterday', 'today'],
500
+ thisweek: ['thisweek', 'now'],
501
+ '7days': ['7 days', 'now'],
502
+ '14days': ['14 days', 'now'],
503
+ '30days': ['30 days', 'now'],
504
+ '90days': ['90 days', 'now'],
505
+ };
506
+ const mapped = periodMap[period];
507
+ if (mapped) {
508
+ [sinceDate, untilDate] = mapped;
509
+ }
510
+ else {
511
+ sinceDate = period;
512
+ untilDate = 'now';
513
+ }
514
+ }
515
+ const results = await getRecent(proj, 100);
516
+ const filtered = filterByDateRange(results, sinceDate, untilDate);
517
+ const limited = filtered.slice(0, limit);
518
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, period, since: sinceDate, until: untilDate, count: limited.length, results: limited }, null, 2) }] };
519
+ }))
520
+ toolCount++;
521
+ // squish_stale - Show stale memories
522
+ if (safeRegisterTool(server, "squish_stale", {
523
+ description: "Show stale memories (old, low-confidence, or rarely accessed)",
524
+ inputSchema: {
525
+ days: z.number().optional().describe("Show memories older than N days"),
526
+ limit: z.number().optional().describe("Max results to return"),
527
+ project: z.string().optional().describe("Project path (defaults to current)")
528
+ }
529
+ }, async ({ days = 30, limit = 20, project }) => {
530
+ const proj = project || process.cwd();
531
+ const cutoffDate = new Date(Date.now() - days * 86400000);
532
+ const results = await getRecent(proj, 500);
533
+ const stale = results.filter((m) => {
534
+ const created = m.createdAt ? new Date(m.createdAt) : null;
535
+ const isOld = created && created < cutoffDate;
536
+ const isLowConfidence = m.confidenceLevel === 'outdated' || m.confidenceLevel === 'speculative';
537
+ const hasLowImportance = (m.importance || 50) < 40;
538
+ return isOld || isLowConfidence || hasLowImportance;
539
+ });
540
+ const limited = stale.slice(0, limit);
541
+ const summary = {
542
+ totalStale: stale.length,
543
+ old: stale.filter((m) => m.createdAt && new Date(m.createdAt) < cutoffDate).length,
544
+ lowConfidence: stale.filter((m) => m.confidenceLevel === 'outdated' || m.confidenceLevel === 'speculative').length,
545
+ lowImportance: stale.filter((m) => (m.importance || 50) < 40).length,
546
+ };
547
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, summary, memories: limited }, null, 2) }] };
548
+ }))
549
+ toolCount++;
550
+ // squish_note - Quick brain dump
551
+ if (safeRegisterTool(server, "squish_note", {
552
+ description: "Quick brain dump - store a raw memory to process later",
553
+ inputSchema: {
554
+ content: z.string().describe("The note content to store"),
555
+ project: z.string().optional().describe("Project path (defaults to current)")
556
+ }
557
+ }, async ({ content, project }) => {
558
+ const result = await rememberMemory({
559
+ content,
560
+ type: 'observation',
561
+ tags: ['note', 'quick'],
562
+ project: project || process.cwd(),
563
+ });
564
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, message: 'Note saved', id: result.id }, null, 2) }] };
565
+ }))
566
+ toolCount++;
567
+ // squish_tag - Manage tags on memories
568
+ if (safeRegisterTool(server, "squish_tag", {
569
+ description: "Add or remove tags from memories",
570
+ inputSchema: {
571
+ action: z.enum(["add", "remove"]).describe("Action: add or remove"),
572
+ tag: z.string().describe("Tag name to add or remove"),
573
+ search: z.string().optional().describe("Search query to match memories"),
574
+ olderThan: z.string().optional().describe("Only tag memories older than (e.g., '30 days')"),
575
+ type: z.string().optional().describe("Filter by memory type"),
576
+ confirm: z.boolean().optional().describe("Actually execute the changes (default is dry-run)"),
577
+ limit: z.number().optional().describe("Max memories to process"),
578
+ project: z.string().optional().describe("Project path (defaults to current)")
579
+ }
580
+ }, async ({ action, tag, search, olderThan, type, confirm = false, limit = 50, project }) => {
581
+ const proj = project || process.cwd();
582
+ const db = await getDb();
583
+ const schema = await getSchema();
584
+ const sqliteDb = db;
585
+ let results = search
586
+ ? await searchMemories({ query: search, type: type, limit: limit * 2, project: proj })
587
+ : await getRecent(proj, limit * 2);
588
+ if (olderThan) {
589
+ results = filterByDateRange(results, '', olderThan);
590
+ }
591
+ const tagged = [];
592
+ for (const mem of results.slice(0, limit)) {
593
+ const currentTags = mem.tags || [];
594
+ const newTags = action === 'add'
595
+ ? [...new Set([...currentTags, tag])]
596
+ : currentTags.filter((t) => t !== tag);
597
+ await sqliteDb.update(schema.memories).set({ tags: serializeTags(newTags) }).where(eq(schema.memories.id, mem.id));
598
+ tagged.push(mem.id);
599
+ }
600
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true, action, tag, matched: results.length, processed: tagged.length, dryRun: !confirm }, null, 2) }] };
601
+ }))
602
+ toolCount++;
603
+ console.error(`[MCP] Tool registration complete. Registered ${toolCount} tools.`);
604
+ return { server, toolCount };
605
+ }
606
+ async function runStdio(server, toolCount) {
607
+ console.error(`[MCP] Starting in STDIO mode...`);
608
+ const transport = new StdioServerTransport();
609
+ transport.onclose = () => {
610
+ console.error(`[MCP] STDIO transport closed`);
611
+ };
612
+ await server.connect(transport);
613
+ console.error(`[MCP] Connected via stdio. ${toolCount} tools available.`);
614
+ // Keep process alive - wait for stdin to close or process signals
615
+ await new Promise((resolve) => {
616
+ process.stdin.on('close', () => {
617
+ console.error(`[MCP] STDIO stdin closed, shutting down`);
618
+ resolve();
619
+ });
620
+ process.on('SIGINT', () => {
621
+ console.error(`[MCP] Received SIGINT, shutting down`);
622
+ resolve();
623
+ });
624
+ process.on('SIGTERM', () => {
625
+ console.error(`[MCP] Received SIGTERM, shutting down`);
626
+ resolve();
627
+ });
628
+ });
629
+ }
630
+ async function runHttp(server, port) {
631
+ console.error(`[MCP] Starting in HTTP mode on port ${port}...`);
632
+ const app = express();
633
+ app.use(express.json());
634
+ const transports = new Map();
635
+ app.get("/health", (req, res) => {
636
+ res.json({ status: "ok", server: SERVER_NAME, version: SERVER_VERSION });
637
+ });
638
+ app.get("/sse", async (req, res) => {
639
+ const transport = new SSEServerTransport("/message", res);
640
+ const sessionId = Math.random().toString(36).substring(7);
641
+ transports.set(sessionId, transport);
642
+ console.error(`[MCP] SSE connection established: ${sessionId}`);
643
+ await server.connect(transport);
644
+ req.on("close", () => {
645
+ console.error(`[MCP] SSE connection closed: ${sessionId}`);
646
+ transports.delete(sessionId);
647
+ });
648
+ });
649
+ app.post("/message", async (req, res) => {
650
+ const sessionId = req.headers["mcp-session-id"] || "default";
651
+ const transport = transports.get(sessionId);
652
+ if (!transport) {
653
+ res.status(400).json({ error: "No active session" });
654
+ return;
655
+ }
656
+ try {
657
+ await transport.handlePostMessage(req, res);
658
+ }
659
+ catch (error) {
660
+ console.error(`[MCP] Error handling message:`, error);
661
+ res.status(500).json({ error: "Internal server error" });
662
+ }
663
+ });
664
+ await new Promise((resolve) => app.listen(port, () => {
665
+ console.error(`[MCP] HTTP server listening on port ${port}`);
666
+ console.error(`[MCP] SSE endpoint: http://localhost:${port}/sse`);
667
+ console.error(`[MCP] Health: http://localhost:${port}/health`);
668
+ resolve();
669
+ }));
670
+ }
671
+ async function runHealthCheck() {
672
+ console.error(`[MCP] Running health check...`);
673
+ try {
674
+ const { server, toolCount } = createSquishServer();
675
+ console.error(`[MCP] Health check passed. Server initialized with ${toolCount} tools.`);
676
+ process.exit(0);
677
+ }
678
+ catch (error) {
679
+ console.error(`[MCP] Health check failed:`, error);
680
+ process.exit(1);
681
+ }
682
+ }
683
+ async function main() {
684
+ try {
685
+ console.error(`[${SERVER_NAME}] v${SERVER_VERSION} initializing...`);
686
+ console.error(`[${SERVER_NAME}] Mode: ${config.isManagedMode ? "managed" : "local"}`);
687
+ console.error(`[${SERVER_NAME}] Embeddings: ${config.embeddingsProvider}`);
688
+ const { mode, port, health } = parseArgs();
689
+ if (health) {
690
+ await runHealthCheck();
691
+ return;
692
+ }
693
+ const { server, toolCount } = createSquishServer();
694
+ // Start background worker for lifecycle maintenance, decay, etc.
695
+ try {
696
+ await startWorker();
697
+ console.error(`[${SERVER_NAME}] Background worker started`);
698
+ }
699
+ catch (error) {
700
+ console.error(`[${SERVER_NAME}] Warning: Failed to start background worker:`, error);
701
+ }
702
+ // Initialize cron scheduler for scheduled jobs
703
+ try {
704
+ await initializeScheduler();
705
+ console.error(`[${SERVER_NAME}] Cron scheduler initialized`);
706
+ }
707
+ catch (error) {
708
+ console.error(`[${SERVER_NAME}] Warning: Failed to initialize scheduler:`, error);
709
+ }
710
+ const shutdown = async () => {
711
+ console.error(`[${SERVER_NAME}] Shutting down...`);
712
+ try {
713
+ await stopWorker();
714
+ console.error(`[${SERVER_NAME}] Background worker stopped`);
715
+ }
716
+ catch (error) {
717
+ console.error(`[${SERVER_NAME}] Error stopping worker:`, error);
718
+ }
719
+ process.exit(0);
720
+ };
721
+ process.on("SIGINT", shutdown);
722
+ process.on("SIGTERM", shutdown);
723
+ if (mode === "stdio") {
724
+ await runStdio(server, toolCount);
725
+ }
726
+ else {
727
+ await runHttp(server, port);
728
+ }
729
+ }
730
+ catch (error) {
731
+ console.error(`[${SERVER_NAME}] Fatal error:`, error);
732
+ process.exit(1);
733
+ }
734
+ }
735
+ main().catch((error) => {
736
+ console.error(`[${SERVER_NAME}] Fatal error:`, error);
737
+ process.exit(1);
738
+ });
739
+ //# sourceMappingURL=mcp-server.js.map