squish-memory 1.0.1 → 1.0.2

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 (278) hide show
  1. package/.env.mcp.example +4 -0
  2. package/README.md +35 -17
  3. package/config/plugin-manifest.json +1 -1
  4. package/dist/api/web/web.js +27 -1
  5. package/dist/commands/mcp-server.js +1 -1
  6. package/dist/core/mcp/server.js +27 -1
  7. package/dist/core/mcp/types.d.ts +4 -4
  8. package/dist/core/memory/memories.js +9 -6
  9. package/dist/core/scheduler/cron-scheduler.js +66 -13
  10. package/dist/db/adapter.js +88 -20
  11. package/dist/db/bootstrap.js +145 -71
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +195 -49
  14. package/generated/mcp/manifest.json +23 -23
  15. package/package.json +30 -18
  16. package/scripts/install-interactive.mjs +7 -4
  17. package/skills/memory-guide/SKILL.md +94 -18
  18. package/skills/squish-cli/SKILL.md +61 -21
  19. package/skills/squish-mcp/SKILL.md +46 -2
  20. package/skills/squish-memory/SKILL.md +30 -16
  21. package/dist/algorithms/analytics/token-estimator.d.ts.map +0 -1
  22. package/dist/algorithms/analytics/token-estimator.js.map +0 -1
  23. package/dist/algorithms/detection/hash-filters.d.ts.map +0 -1
  24. package/dist/algorithms/detection/hash-filters.js.map +0 -1
  25. package/dist/algorithms/detection/semantic-ranker.d.ts.map +0 -1
  26. package/dist/algorithms/detection/semantic-ranker.js.map +0 -1
  27. package/dist/algorithms/detection/two-stage-detector.d.ts.map +0 -1
  28. package/dist/algorithms/detection/two-stage-detector.js.map +0 -1
  29. package/dist/algorithms/handlers/approve-merge.d.ts.map +0 -1
  30. package/dist/algorithms/handlers/approve-merge.js.map +0 -1
  31. package/dist/algorithms/handlers/detect-duplicates.d.ts.map +0 -1
  32. package/dist/algorithms/handlers/detect-duplicates.js.map +0 -1
  33. package/dist/algorithms/handlers/get-stats.d.ts.map +0 -1
  34. package/dist/algorithms/handlers/get-stats.js.map +0 -1
  35. package/dist/algorithms/handlers/list-proposals.d.ts.map +0 -1
  36. package/dist/algorithms/handlers/list-proposals.js.map +0 -1
  37. package/dist/algorithms/handlers/preview-merge.d.ts.map +0 -1
  38. package/dist/algorithms/handlers/preview-merge.js.map +0 -1
  39. package/dist/algorithms/handlers/reject-merge.d.ts.map +0 -1
  40. package/dist/algorithms/handlers/reject-merge.js.map +0 -1
  41. package/dist/algorithms/handlers/reverse-merge.d.ts.map +0 -1
  42. package/dist/algorithms/handlers/reverse-merge.js.map +0 -1
  43. package/dist/algorithms/safety/safety-checks.d.ts.map +0 -1
  44. package/dist/algorithms/safety/safety-checks.js.map +0 -1
  45. package/dist/algorithms/strategies/merge-strategies.d.ts.map +0 -1
  46. package/dist/algorithms/strategies/merge-strategies.js.map +0 -1
  47. package/dist/algorithms/utils/response-builder.d.ts.map +0 -1
  48. package/dist/algorithms/utils/response-builder.js.map +0 -1
  49. package/dist/api/web/index.d.ts.map +0 -1
  50. package/dist/api/web/index.js.map +0 -1
  51. package/dist/api/web/web-server.d.ts.map +0 -1
  52. package/dist/api/web/web-server.js.map +0 -1
  53. package/dist/api/web/web.d.ts.map +0 -1
  54. package/dist/api/web/web.js.map +0 -1
  55. package/dist/commands/managed-sync.d.ts.map +0 -1
  56. package/dist/commands/managed-sync.js.map +0 -1
  57. package/dist/commands/mcp-server.d.ts.map +0 -1
  58. package/dist/commands/mcp-server.js.map +0 -1
  59. package/dist/config.d.ts.map +0 -1
  60. package/dist/config.js.map +0 -1
  61. package/dist/core/agent-memory.d.ts.map +0 -1
  62. package/dist/core/agent-memory.js.map +0 -1
  63. package/dist/core/associations.d.ts.map +0 -1
  64. package/dist/core/associations.js.map +0 -1
  65. package/dist/core/cache.d.ts.map +0 -1
  66. package/dist/core/cache.js.map +0 -1
  67. package/dist/core/consolidation.d.ts.map +0 -1
  68. package/dist/core/consolidation.js.map +0 -1
  69. package/dist/core/context-paging.d.ts.map +0 -1
  70. package/dist/core/context-paging.js.map +0 -1
  71. package/dist/core/context.d.ts.map +0 -1
  72. package/dist/core/context.js.map +0 -1
  73. package/dist/core/core-memory.d.ts.map +0 -1
  74. package/dist/core/core-memory.js.map +0 -1
  75. package/dist/core/database.d.ts.map +0 -1
  76. package/dist/core/database.js.map +0 -1
  77. package/dist/core/embeddings/google-multimodal.d.ts.map +0 -1
  78. package/dist/core/embeddings/google-multimodal.js.map +0 -1
  79. package/dist/core/embeddings/qmd-client.d.ts.map +0 -1
  80. package/dist/core/embeddings/qmd-client.js.map +0 -1
  81. package/dist/core/embeddings.d.ts.map +0 -1
  82. package/dist/core/embeddings.js.map +0 -1
  83. package/dist/core/governance.d.ts.map +0 -1
  84. package/dist/core/governance.js.map +0 -1
  85. package/dist/core/index.d.ts.map +0 -1
  86. package/dist/core/index.js.map +0 -1
  87. package/dist/core/layers/generator.d.ts.map +0 -1
  88. package/dist/core/layers/generator.js.map +0 -1
  89. package/dist/core/lifecycle.d.ts.map +0 -1
  90. package/dist/core/lifecycle.js.map +0 -1
  91. package/dist/core/local-embeddings.d.ts.map +0 -1
  92. package/dist/core/local-embeddings.js.map +0 -1
  93. package/dist/core/logger.d.ts.map +0 -1
  94. package/dist/core/logger.js.map +0 -1
  95. package/dist/core/mcp/client.d.ts.map +0 -1
  96. package/dist/core/mcp/client.js.map +0 -1
  97. package/dist/core/mcp/index.d.ts.map +0 -1
  98. package/dist/core/mcp/index.js.map +0 -1
  99. package/dist/core/mcp/server.d.ts.map +0 -1
  100. package/dist/core/mcp/server.js.map +0 -1
  101. package/dist/core/mcp/standalone-server.d.ts.map +0 -1
  102. package/dist/core/mcp/standalone-server.js.map +0 -1
  103. package/dist/core/mcp/tools.d.ts.map +0 -1
  104. package/dist/core/mcp/tools.js.map +0 -1
  105. package/dist/core/mcp/types.d.ts.map +0 -1
  106. package/dist/core/mcp/types.js.map +0 -1
  107. package/dist/core/memory/bridge-discovery.d.ts.map +0 -1
  108. package/dist/core/memory/bridge-discovery.js.map +0 -1
  109. package/dist/core/memory/categorizer.d.ts.map +0 -1
  110. package/dist/core/memory/categorizer.js.map +0 -1
  111. package/dist/core/memory/conflict-detector.d.ts.map +0 -1
  112. package/dist/core/memory/conflict-detector.js.map +0 -1
  113. package/dist/core/memory/consolidation.d.ts.map +0 -1
  114. package/dist/core/memory/consolidation.js.map +0 -1
  115. package/dist/core/memory/context-collector.d.ts.map +0 -1
  116. package/dist/core/memory/context-collector.js.map +0 -1
  117. package/dist/core/memory/contradiction-resolver.d.ts.map +0 -1
  118. package/dist/core/memory/contradiction-resolver.js.map +0 -1
  119. package/dist/core/memory/edit-workflow.d.ts.map +0 -1
  120. package/dist/core/memory/edit-workflow.js.map +0 -1
  121. package/dist/core/memory/entity-extractor.d.ts.map +0 -1
  122. package/dist/core/memory/entity-extractor.js.map +0 -1
  123. package/dist/core/memory/entity-resolver.d.ts.map +0 -1
  124. package/dist/core/memory/entity-resolver.js.map +0 -1
  125. package/dist/core/memory/fact-extractor.d.ts.map +0 -1
  126. package/dist/core/memory/fact-extractor.js.map +0 -1
  127. package/dist/core/memory/feedback-tracker.d.ts.map +0 -1
  128. package/dist/core/memory/feedback-tracker.js.map +0 -1
  129. package/dist/core/memory/hybrid-retrieval.d.ts.map +0 -1
  130. package/dist/core/memory/hybrid-retrieval.js.map +0 -1
  131. package/dist/core/memory/hybrid-scorer.d.ts.map +0 -1
  132. package/dist/core/memory/hybrid-scorer.js.map +0 -1
  133. package/dist/core/memory/hybrid-search.d.ts.map +0 -1
  134. package/dist/core/memory/hybrid-search.js.map +0 -1
  135. package/dist/core/memory/importance.d.ts.map +0 -1
  136. package/dist/core/memory/importance.js.map +0 -1
  137. package/dist/core/memory/index.d.ts.map +0 -1
  138. package/dist/core/memory/index.js.map +0 -1
  139. package/dist/core/memory/memories.d.ts.map +0 -1
  140. package/dist/core/memory/memories.js.map +0 -1
  141. package/dist/core/memory/memory-manager.d.ts.map +0 -1
  142. package/dist/core/memory/memory-manager.js.map +0 -1
  143. package/dist/core/memory/progressive-disclosure.d.ts.map +0 -1
  144. package/dist/core/memory/progressive-disclosure.js.map +0 -1
  145. package/dist/core/memory/query-processor.d.ts.map +0 -1
  146. package/dist/core/memory/query-processor.js.map +0 -1
  147. package/dist/core/memory/query-rewriter.d.ts.map +0 -1
  148. package/dist/core/memory/query-rewriter.js.map +0 -1
  149. package/dist/core/memory/response-analyzer.d.ts.map +0 -1
  150. package/dist/core/memory/response-analyzer.js.map +0 -1
  151. package/dist/core/memory/serialization.d.ts.map +0 -1
  152. package/dist/core/memory/serialization.js.map +0 -1
  153. package/dist/core/memory/stats.d.ts.map +0 -1
  154. package/dist/core/memory/stats.js.map +0 -1
  155. package/dist/core/memory/telemetry.d.ts.map +0 -1
  156. package/dist/core/memory/telemetry.js.map +0 -1
  157. package/dist/core/memory/temporal-facts.d.ts.map +0 -1
  158. package/dist/core/memory/temporal-facts.js.map +0 -1
  159. package/dist/core/memory/temporal-parser.d.ts.map +0 -1
  160. package/dist/core/memory/temporal-parser.js.map +0 -1
  161. package/dist/core/memory/trigger-detector.d.ts.map +0 -1
  162. package/dist/core/memory/trigger-detector.js.map +0 -1
  163. package/dist/core/memory/write-gate.d.ts.map +0 -1
  164. package/dist/core/memory/write-gate.js.map +0 -1
  165. package/dist/core/namespaces/index.d.ts.map +0 -1
  166. package/dist/core/namespaces/index.js.map +0 -1
  167. package/dist/core/namespaces/uri-parser.d.ts.map +0 -1
  168. package/dist/core/namespaces/uri-parser.js.map +0 -1
  169. package/dist/core/observations.d.ts.map +0 -1
  170. package/dist/core/observations.js.map +0 -1
  171. package/dist/core/privacy.d.ts.map +0 -1
  172. package/dist/core/privacy.js.map +0 -1
  173. package/dist/core/projects.d.ts.map +0 -1
  174. package/dist/core/projects.js.map +0 -1
  175. package/dist/core/redis.d.ts.map +0 -1
  176. package/dist/core/redis.js.map +0 -1
  177. package/dist/core/requirements.d.ts.map +0 -1
  178. package/dist/core/requirements.js.map +0 -1
  179. package/dist/core/scheduler/cron-scheduler.d.ts.map +0 -1
  180. package/dist/core/scheduler/cron-scheduler.js.map +0 -1
  181. package/dist/core/scheduler/heartbeat.d.ts.map +0 -1
  182. package/dist/core/scheduler/heartbeat.js.map +0 -1
  183. package/dist/core/scheduler/index.d.ts.map +0 -1
  184. package/dist/core/scheduler/index.js.map +0 -1
  185. package/dist/core/scheduler/job-runner.d.ts.map +0 -1
  186. package/dist/core/scheduler/job-runner.js.map +0 -1
  187. package/dist/core/search/conversations.d.ts.map +0 -1
  188. package/dist/core/search/conversations.js.map +0 -1
  189. package/dist/core/search/entities.d.ts.map +0 -1
  190. package/dist/core/search/entities.js.map +0 -1
  191. package/dist/core/search/folder-context.d.ts.map +0 -1
  192. package/dist/core/search/folder-context.js.map +0 -1
  193. package/dist/core/search/index.d.ts.map +0 -1
  194. package/dist/core/search/index.js.map +0 -1
  195. package/dist/core/search/qmd-search.d.ts.map +0 -1
  196. package/dist/core/search/qmd-search.js.map +0 -1
  197. package/dist/core/secret-detector.d.ts.map +0 -1
  198. package/dist/core/secret-detector.js.map +0 -1
  199. package/dist/core/session/auto-load.d.ts.map +0 -1
  200. package/dist/core/session/auto-load.js.map +0 -1
  201. package/dist/core/session/index.d.ts.map +0 -1
  202. package/dist/core/session/index.js.map +0 -1
  203. package/dist/core/session/types.d.ts.map +0 -1
  204. package/dist/core/session/types.js.map +0 -1
  205. package/dist/core/session-hooks/self-iteration-job.d.ts.map +0 -1
  206. package/dist/core/session-hooks/self-iteration-job.js.map +0 -1
  207. package/dist/core/session-hooks/session-hooks.d.ts.map +0 -1
  208. package/dist/core/session-hooks/session-hooks.js.map +0 -1
  209. package/dist/core/snapshots/cleanup.d.ts.map +0 -1
  210. package/dist/core/snapshots/cleanup.js.map +0 -1
  211. package/dist/core/snapshots/comparison.d.ts.map +0 -1
  212. package/dist/core/snapshots/comparison.js.map +0 -1
  213. package/dist/core/snapshots/creation.d.ts.map +0 -1
  214. package/dist/core/snapshots/creation.js.map +0 -1
  215. package/dist/core/snapshots/retrieval.d.ts.map +0 -1
  216. package/dist/core/snapshots/retrieval.js.map +0 -1
  217. package/dist/core/snapshots/stats.d.ts.map +0 -1
  218. package/dist/core/snapshots/stats.js.map +0 -1
  219. package/dist/core/snapshots.d.ts.map +0 -1
  220. package/dist/core/snapshots.js.map +0 -1
  221. package/dist/core/summarization/cleanup.d.ts.map +0 -1
  222. package/dist/core/summarization/cleanup.js.map +0 -1
  223. package/dist/core/summarization/queries.d.ts.map +0 -1
  224. package/dist/core/summarization/queries.js.map +0 -1
  225. package/dist/core/summarization/stats.d.ts.map +0 -1
  226. package/dist/core/summarization/stats.js.map +0 -1
  227. package/dist/core/summarization/strategies.d.ts.map +0 -1
  228. package/dist/core/summarization/strategies.js.map +0 -1
  229. package/dist/core/summarization.d.ts.map +0 -1
  230. package/dist/core/summarization.js.map +0 -1
  231. package/dist/core/sync/qmd-sync.d.ts.map +0 -1
  232. package/dist/core/sync/qmd-sync.js.map +0 -1
  233. package/dist/core/temporal-facts.d.ts.map +0 -1
  234. package/dist/core/temporal-facts.js.map +0 -1
  235. package/dist/core/tracing/collector.d.ts.map +0 -1
  236. package/dist/core/tracing/collector.js.map +0 -1
  237. package/dist/core/tracing/visualizer.d.ts.map +0 -1
  238. package/dist/core/tracing/visualizer.js.map +0 -1
  239. package/dist/core/utils/cleanup-operations.d.ts.map +0 -1
  240. package/dist/core/utils/cleanup-operations.js.map +0 -1
  241. package/dist/core/utils/content-extraction.d.ts.map +0 -1
  242. package/dist/core/utils/content-extraction.js.map +0 -1
  243. package/dist/core/utils/filter-builder.d.ts.map +0 -1
  244. package/dist/core/utils/filter-builder.js.map +0 -1
  245. package/dist/core/utils/history-traversal.d.ts.map +0 -1
  246. package/dist/core/utils/history-traversal.js.map +0 -1
  247. package/dist/core/utils/memory-operations.d.ts.map +0 -1
  248. package/dist/core/utils/memory-operations.js.map +0 -1
  249. package/dist/core/utils/query-operations.d.ts.map +0 -1
  250. package/dist/core/utils/query-operations.js.map +0 -1
  251. package/dist/core/utils/summarization-helpers.d.ts.map +0 -1
  252. package/dist/core/utils/summarization-helpers.js.map +0 -1
  253. package/dist/core/utils/temporal-queries.d.ts.map +0 -1
  254. package/dist/core/utils/temporal-queries.js.map +0 -1
  255. package/dist/core/utils/version-management.d.ts.map +0 -1
  256. package/dist/core/utils/version-management.js.map +0 -1
  257. package/dist/core/utils.d.ts.map +0 -1
  258. package/dist/core/utils.js.map +0 -1
  259. package/dist/core/worker.d.ts.map +0 -1
  260. package/dist/core/worker.js.map +0 -1
  261. package/dist/db/adapter.d.ts.map +0 -1
  262. package/dist/db/adapter.js.map +0 -1
  263. package/dist/db/bootstrap.d.ts.map +0 -1
  264. package/dist/db/bootstrap.js.map +0 -1
  265. package/dist/db/index.d.ts.map +0 -1
  266. package/dist/db/index.js.map +0 -1
  267. package/dist/db/schema.d.ts.map +0 -1
  268. package/dist/db/schema.js.map +0 -1
  269. package/dist/drizzle/schema-sqlite.d.ts.map +0 -1
  270. package/dist/drizzle/schema-sqlite.js.map +0 -1
  271. package/dist/drizzle/schema.d.ts.map +0 -1
  272. package/dist/drizzle/schema.js.map +0 -1
  273. package/dist/index.d.ts.map +0 -1
  274. package/dist/index.js.map +0 -1
  275. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts.map +0 -1
  276. package/packages/plugin-claude-code/dist/plugin-wrapper.js.map +0 -1
  277. package/packages/plugin-openclaw/dist/index.d.ts.map +0 -1
  278. package/packages/plugin-openclaw/dist/index.js.map +0 -1
package/.env.mcp.example CHANGED
@@ -2,6 +2,10 @@
2
2
  SQUISH_MCP_PORT=8767
3
3
  SQUISH_MCP_SERVER_ENABLED=true
4
4
 
5
+ # CORS Configuration (comma-separated origins, * wildcard for port)
6
+ # Default: localhost and 127.0.0.1 on any port
7
+ # SQUISH_CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:8080
8
+
5
9
  # Embeddings Provider: local | openai | ollama | google | none | auto
6
10
  # Default: local (TF-IDF, no API needed)
7
11
  SQUISH_EMBEDDINGS_PROVIDER=local
package/README.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Squish - Universal Two-Tier Memory for AI Agents
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/squish-memory)](https://www.npmjs.com/package/squish-memory)
4
+ [![npm downloads](https://img.shields.io/npm/dm/squish-memory)](https://www.npmjs.com/package/squish-memory)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
7
+
3
8
  **Squish gives any AI agent persistent, intelligent memory through a two-tier architecture.** Without memory, agents forget everything between sessions. With Squish, they learn, adapt, and get smarter over time - regardless of which agent framework you use.
4
9
 
5
10
  ```bash
6
11
  npm install squish-memory
7
12
  ```
8
13
 
14
+ **Works out-of-the-box.** No database setup required - local TF-IDF embeddings work instantly. Upgrade to vector search with OpenAI/Ollama when ready.
15
+
9
16
  ## Why Agents Need Memory
10
17
 
11
18
  | Without Squish | With Squish |
@@ -18,8 +25,8 @@ npm install squish-memory
18
25
  ## How It Works
19
26
 
20
27
  Squish uses a two-tier memory architecture for optimal performance:
21
- - **Fast Search Tier**: QMD (Quick Markdown Search) provides lightning-fast hybrid BM25 + vector search
22
- - **Persistent Storage Tier**: SQLite (local) or PostgreSQL (team) ensures durable, reliable memory storage
28
+ - **Short-term Memory (QMD)**: Lightning-fast file-based search using QMD (BM25 + vector). Instant recall, optimized for recent/active context.
29
+ - **Long-term Memory (Database)**: SQLite (local) or PostgreSQL (team) for durable, searchable storage of important memories.
23
30
 
24
31
  ```
25
32
  Agent Action -----> [Squish Memory Layer]
@@ -35,19 +42,18 @@ Agent Action -----> [Squish Memory Layer]
35
42
  │ Write Gate │ <-- Validate, sanitize, score
36
43
  └──────────────┘
37
44
  |
38
- v
39
- ┌─────────────────────┐
40
- │ Dual Storage Write │
41
- ──→ QMD Index (fast search)
42
- ──→ SQLite/Postgres (durable storage)
43
- └─────────────────────┘
44
- |
45
- v
46
- ┌─────────────────────┐
47
- │ Hybrid Retrieval │
48
- │ QMD Search +
49
- │ Vector Ranking
50
- └─────────────────────┘
45
+ ┌───────────────┴───────────────┐
46
+ v v
47
+ ┌──────────────┐ ┌──────────────┐
48
+ Short-term │ │ Long-term
49
+ QMD (Files) │ │ SQLite/PG
50
+ │ Fast recall │ │ Persistent │
51
+ └──────────────┘ └──────────────┘
52
+ | |
53
+ v v
54
+ ┌──────────────────────────────────────────┐
55
+ Hybrid Retrieval: QMD + Vector Ranking
56
+ └──────────────────────────────────────────┘
51
57
  |
52
58
  v
53
59
  Agent Context
@@ -73,6 +79,18 @@ Agent Action -----> [Squish Memory Layer]
73
79
 
74
80
  ## Quick Start
75
81
 
82
+ ### Simple Example
83
+ ```bash
84
+ # Store a memory
85
+ squish remember "User prefers TypeScript"
86
+
87
+ # Search memories
88
+ squish search "preferences"
89
+
90
+ # Check health
91
+ squish health
92
+ ```
93
+
76
94
  ### Universal Plugin Installer (Recommended)
77
95
  ```bash
78
96
  # Install for your AI assistant(s)
@@ -288,8 +306,8 @@ DATABASE_URL=postgresql://user:pass@host/db
288
306
 
289
307
  ### Two-Tier Memory System
290
308
  Squish employs a two-tier architecture for optimal performance and reliability:
291
- - **Fast Search Tier**: QMD (Quick Markdown Search) provides hybrid BM25 + vector search with sub-second response times
292
- - **Persistent Storage Tier**: SQLite (local mode) or PostgreSQL (team mode) ensures durable, ACID-compliant memory storage
309
+ - **Short-term (QMD)**: File-based search using QMD (BM25 + vectors). Ultra-fast for recent context and active memories.
310
+ - **Long-term (Database)**: SQLite (local) or PostgreSQL (team). Durable ACID-compliant storage for important memories that need persistence.
293
311
 
294
312
  ### Universal Interfaces
295
313
  - **MCP Server**: Native integration for Claude Code, OpenClaw, and any MCP-compatible agent
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "squish-memory",
3
3
  "name": "Squish Memory",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "description": "Universal two-tier memory system for AI agents with hybrid search, core memory, and MCP-first architecture",
6
6
  "capabilities": ["mcp", "cli", "web"],
7
7
  "targets": {
@@ -1,5 +1,6 @@
1
1
  import express from 'express';
2
2
  import cors from 'cors';
3
+ import rateLimit from 'express-rate-limit';
3
4
  import { logger } from '../../core/logger.js';
4
5
  import { getRecentMemories } from '../../core/memory/memories.js';
5
6
  import { getObservationsForProject } from '../../core/observations.js';
@@ -7,7 +8,32 @@ import { getAllProjects, getProjectByPath } from '../../core/projects.js';
7
8
  import { getDb } from '../../db/index.js';
8
9
  const app = express();
9
10
  const PORT = process.env.SQUISH_WEB_PORT || 37777;
10
- app.use(cors());
11
+ const allowedOrigins = process.env.SQUISH_CORS_ORIGINS?.split(',').map(s => s.trim()) || ['http://localhost:*', 'http://127.0.0.1:*'];
12
+ const appCors = cors({
13
+ origin: (origin, callback) => {
14
+ if (!origin || allowedOrigins.some(allowed => {
15
+ if (allowed.endsWith(':*')) {
16
+ const prefix = allowed.slice(0, -1);
17
+ return origin.startsWith(prefix);
18
+ }
19
+ return origin === allowed;
20
+ })) {
21
+ callback(null, true);
22
+ }
23
+ else {
24
+ callback(new Error('Not allowed by CORS'));
25
+ }
26
+ }
27
+ });
28
+ const limiter = rateLimit({
29
+ windowMs: 15 * 60 * 1000,
30
+ max: 100,
31
+ message: { error: 'Too many requests, please try again later.' },
32
+ standardHeaders: true,
33
+ legacyHeaders: false,
34
+ });
35
+ app.use(appCors);
36
+ app.use(limiter);
11
37
  app.use(express.json());
12
38
  // Health check endpoint
13
39
  app.get('/api/health', async (req, res) => {
@@ -21,7 +21,7 @@ import { getDb } from "../db/index.js";
21
21
  import { getSchema } from "../db/schema.js";
22
22
  import { eq } from "drizzle-orm";
23
23
  const SERVER_NAME = "squish-memory";
24
- const SERVER_VERSION = "1.0.1";
24
+ const SERVER_VERSION = "1.0.2";
25
25
  function parseArgs() {
26
26
  const args = process.argv.slice(2);
27
27
  let mode = "stdio";
@@ -1,5 +1,6 @@
1
1
  import express from 'express';
2
2
  import cors from 'cors';
3
+ import rateLimit from 'express-rate-limit';
3
4
  import { logger } from '../logger.js';
4
5
  export class MCPServer {
5
6
  app = express();
@@ -16,7 +17,32 @@ export class MCPServer {
16
17
  this.setupRoutes();
17
18
  }
18
19
  setupMiddleware() {
19
- this.app.use(cors());
20
+ const allowedOrigins = process.env.SQUISH_CORS_ORIGINS?.split(',').map(s => s.trim()) || ['http://localhost:*', 'http://127.0.0.1:*'];
21
+ const appCors = cors({
22
+ origin: (origin, callback) => {
23
+ if (!origin || allowedOrigins.some(allowed => {
24
+ if (allowed.endsWith(':*')) {
25
+ const prefix = allowed.slice(0, -1);
26
+ return origin.startsWith(prefix);
27
+ }
28
+ return origin === allowed;
29
+ })) {
30
+ callback(null, true);
31
+ }
32
+ else {
33
+ callback(new Error('Not allowed by CORS'));
34
+ }
35
+ }
36
+ });
37
+ const limiter = rateLimit({
38
+ windowMs: 15 * 60 * 1000,
39
+ max: 100,
40
+ message: { error: 'Too many requests, please try again later.' },
41
+ standardHeaders: true,
42
+ legacyHeaders: false,
43
+ });
44
+ this.app.use(appCors);
45
+ this.app.use(limiter);
20
46
  this.app.use(express.json());
21
47
  }
22
48
  setupRoutes() {
@@ -30,29 +30,29 @@ export declare const MCPToolResultSchema: z.ZodObject<{
30
30
  mimeType: z.ZodOptional<z.ZodString>;
31
31
  }, "strip", z.ZodTypeAny, {
32
32
  type: "text" | "image" | "resource";
33
- data?: string | undefined;
34
33
  text?: string | undefined;
34
+ data?: string | undefined;
35
35
  mimeType?: string | undefined;
36
36
  }, {
37
37
  type: "text" | "image" | "resource";
38
- data?: string | undefined;
39
38
  text?: string | undefined;
39
+ data?: string | undefined;
40
40
  mimeType?: string | undefined;
41
41
  }>, "many">;
42
42
  isError: z.ZodOptional<z.ZodBoolean>;
43
43
  }, "strip", z.ZodTypeAny, {
44
44
  content: {
45
45
  type: "text" | "image" | "resource";
46
- data?: string | undefined;
47
46
  text?: string | undefined;
47
+ data?: string | undefined;
48
48
  mimeType?: string | undefined;
49
49
  }[];
50
50
  isError?: boolean | undefined;
51
51
  }, {
52
52
  content: {
53
53
  type: "text" | "image" | "resource";
54
- data?: string | undefined;
55
54
  text?: string | undefined;
55
+ data?: string | undefined;
56
56
  mimeType?: string | undefined;
57
57
  }[];
58
58
  isError?: boolean | undefined;
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
- import { desc, eq } from 'drizzle-orm';
2
+ import { eq } from 'drizzle-orm';
3
3
  import { getDb } from '../../db/index.js';
4
4
  import { getSchema } from '../../db/schema.js';
5
5
  import { config } from '../../config.js';
@@ -143,14 +143,17 @@ export async function getMemoryById(id, incrementAccess = true) {
143
143
  export async function getRecentMemories(projectPath, limit) {
144
144
  try {
145
145
  const db = createDatabaseClient(await getDb());
146
- const schema = await getSchema();
146
+ const sqlite = db.$client;
147
147
  const project = await getProjectByPath(projectPath);
148
148
  if (!project)
149
149
  return [];
150
- const rows = await db.select().from(schema.memories)
151
- .where(eq(schema.memories.projectId, project.id))
152
- .orderBy(desc(schema.memories.createdAt))
153
- .limit(limit);
150
+ // Use raw SQL to avoid drizzle column name issues
151
+ const rows = sqlite.prepare(`
152
+ SELECT * FROM memories
153
+ WHERE project_id = ?
154
+ ORDER BY created_at DESC
155
+ LIMIT ?
156
+ `).all(project.id, limit);
154
157
  return rows.map((row) => normalizeMemory(row));
155
158
  }
156
159
  catch (error) {
@@ -65,18 +65,61 @@ async function ensureDefaultJobs(db) {
65
65
  },
66
66
  ];
67
67
  for (const job of defaultJobs) {
68
- const existing = await db
69
- .select()
70
- .from(maintenanceJobs)
71
- .where(eq(maintenanceJobs.jobName, job.jobName))
72
- .limit(1);
68
+ let existing;
69
+ try {
70
+ existing = await db
71
+ .select()
72
+ .from(maintenanceJobs)
73
+ .where(eq(maintenanceJobs.jobName, job.jobName))
74
+ .limit(1);
75
+ }
76
+ catch (queryError) {
77
+ logger.error(`[Scheduler] Query failed for job ${job.jobName}:`, queryError.message);
78
+ // Try raw SQL fallback
79
+ try {
80
+ const rawDb = db.$client;
81
+ if (rawDb && typeof rawDb.prepare === 'function') {
82
+ existing = rawDb.prepare('SELECT * FROM maintenance_jobs WHERE job_name = ?').all(job.jobName);
83
+ }
84
+ }
85
+ catch (fallbackError) {
86
+ logger.error(`[Scheduler] Fallback query also failed:`, fallbackError.message);
87
+ throw queryError;
88
+ }
89
+ }
73
90
  if (existing.length === 0) {
74
- await db.insert(maintenanceJobs).values({
75
- ...job,
76
- totalRuns: 0,
77
- successCount: 0,
78
- failureCount: 0,
79
- });
91
+ try {
92
+ await db.insert(maintenanceJobs).values({
93
+ jobName: job.jobName,
94
+ jobType: job.jobType,
95
+ cronExpression: job.cronExpression,
96
+ enabled: job.enabled,
97
+ jobConfig: job.jobConfig,
98
+ totalRuns: 0,
99
+ successCount: 0,
100
+ failureCount: 0,
101
+ lastRunAt: null,
102
+ nextRunAt: null,
103
+ lastRunDuration: null,
104
+ lastRunStatus: null,
105
+ lastRunError: null,
106
+ });
107
+ }
108
+ catch (insertError) {
109
+ // Fallback to raw SQL if drizzle insert fails
110
+ logger.warn(`[Scheduler] Drizzle insert failed, using raw SQL: ${insertError.message}`);
111
+ const rawDb = db.$client;
112
+ if (rawDb && typeof rawDb.prepare === 'function') {
113
+ const stmt = rawDb.prepare(`
114
+ INSERT INTO maintenance_jobs
115
+ (id, job_name, job_type, cron_expression, enabled, job_config,
116
+ total_runs, success_count, failure_count, last_run_at, next_run_at,
117
+ last_run_duration, last_run_status, last_run_error)
118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
119
+ `);
120
+ stmt.run(crypto.randomUUID(), job.jobName, job.jobType, job.cronExpression, job.enabled ? 1 : 0, JSON.stringify(job.jobConfig), 0, 0, 0, null, null, null, null, null);
121
+ }
122
+ }
80
123
  logger.info(`[Scheduler] Created default job: ${job.jobName}`);
81
124
  // Register self-iteration handler
82
125
  if (job.jobName === 'self_iteration') {
@@ -115,7 +158,7 @@ export async function scheduleJob(job) {
115
158
  .set({ nextRunAt: nextRun })
116
159
  .where(eq(maintenanceJobs.id, job.id));
117
160
  }
118
- logger.info(`[Scheduler] Scheduled ${job.jobName} with cron: ${job.cronExpression}, next run: ${nextRun?.toISOString()}`);
161
+ logger.info(`[Scheduler] Scheduled ${job.jobName} with cron: ${job.cronExpression}${nextRun ? `, next run: ${nextRun.toISOString()}` : ''}`);
119
162
  }
120
163
  export async function executeJob(job) {
121
164
  const db = await getDb();
@@ -183,13 +226,23 @@ function getNextRunTime(cronExpression) {
183
226
  try {
184
227
  const now = new Date();
185
228
  const parts = cronExpression.split(' ');
186
- if (parts[2] === '*' && parts[3] === '*' && parts[4] === '*') {
229
+ // Daily jobs: MM HH * * *
230
+ if (parts[2] === '*' && parts[3] === '*' && parts[4] === '*' && parts[1] !== '*') {
187
231
  const next = new Date(now);
188
232
  next.setHours(parseInt(parts[1]), parseInt(parts[0]), 0, 0);
189
233
  if (next <= now)
190
234
  next.setDate(next.getDate() + 1);
191
235
  return next;
192
236
  }
237
+ // Hourly jobs: MM * * * *
238
+ if (parts[1] === '*' && parts[2] === '*' && parts[3] === '*' && parts[4] === '*') {
239
+ const next = new Date(now);
240
+ next.setMinutes(parseInt(parts[0]), 0, 0);
241
+ if (next <= now)
242
+ next.setHours(next.getHours() + 1);
243
+ return next;
244
+ }
245
+ // Weekly jobs: MM HH * * D
193
246
  if (parts[4] !== '*') {
194
247
  const dayOfWeek = parseInt(parts[4]);
195
248
  const next = new Date(now);
@@ -1,6 +1,19 @@
1
1
  import { config, getDataDir } from '../config.js';
2
2
  import { ensurePostgresSchema, ensureSqliteSchema } from './bootstrap.js';
3
3
  import { logger } from '../core/logger.js';
4
+ async function createBunSqliteDb(dbPath) {
5
+ // @ts-ignore - bun:sqlite module not found in types but works at runtime
6
+ const { drizzle } = await import('drizzle-orm/bun-sqlite');
7
+ const schemaModule = await import('../drizzle/schema-sqlite.js');
8
+ // Bun SQLite doesn't need file path for in-memory, but we'll use file path for persistence
9
+ // @ts-ignore - bun:sqlite module not found in types but works at runtime
10
+ const sqlite = new (await import('bun:sqlite')).default(dbPath);
11
+ // Enable foreign keys
12
+ sqlite.exec('PRAGMA foreign_keys = ON');
13
+ await ensureSqliteSchema(sqlite);
14
+ logger.info('SQLite initialized with bun:sqlite');
15
+ return drizzle(sqlite, { schema: schemaModule });
16
+ }
4
17
  export async function createDb() {
5
18
  if (config.isTeamMode) {
6
19
  return createPostgresDb();
@@ -22,31 +35,41 @@ async function createPostgresDb() {
22
35
  }
23
36
  async function createSqliteDb() {
24
37
  const dbPath = `${getDataDir()}/squish.db`;
25
- // Try better-sqlite3 first (best performance)
38
+ // Try bun:sqlite first (Bun's built-in SQLite)
26
39
  try {
27
- return await createBetterSqliteDb(dbPath);
40
+ return await createBunSqliteDb(dbPath);
28
41
  }
29
- catch (betterSqliteError) {
30
- logger.warn('better-sqlite3 failed, trying sql.js fallback', {
31
- error: betterSqliteError.message
42
+ catch (bunSqliteError) {
43
+ logger.warn('bun:sqlite failed, trying better-sqlite3', {
44
+ error: bunSqliteError.message
32
45
  });
33
- // Fallback to sql.js (pure JavaScript, no native module)
46
+ // Try better-sqlite3 second (best performance)
34
47
  try {
35
- return await createSqlJsDb(dbPath);
48
+ return await createBetterSqliteDb(dbPath);
36
49
  }
37
- catch (sqlJsError) {
38
- // Both failed - this is critical, Squish cannot work without DB
39
- logger.error('CRITICAL: SQLite database initialization failed', {
40
- betterSqliteError: betterSqliteError.message,
41
- sqlJsError: sqlJsError.message
50
+ catch (betterSqliteError) {
51
+ logger.warn('better-sqlite3 failed, trying sql.js fallback', {
52
+ error: betterSqliteError.message
42
53
  });
43
- throw new Error(`Squish requires SQLite to function. Database initialization failed.\n` +
44
- `Primary error (better-sqlite3): ${betterSqliteError.message}\n` +
45
- `Fallback error (sql.js): ${sqlJsError.message}\n\n` +
46
- `Solutions:\n` +
47
- `1. Rebuild native module: npm rebuild better-sqlite3\n` +
48
- `2. Install build tools: npm install -g windows-build-tools (Windows)\n` +
49
- `3. Or use PostgreSQL instead by setting DATABASE_URL environment variable`);
54
+ // Fallback to sql.js (pure JavaScript, no native module)
55
+ try {
56
+ return await createSqlJsDb(dbPath);
57
+ }
58
+ catch (sqlJsError) {
59
+ // All failed - this is critical, Squish cannot work without DB
60
+ logger.error('CRITICAL: SQLite database initialization failed', {
61
+ bunSqliteError: bunSqliteError.message,
62
+ betterSqliteError: betterSqliteError.message,
63
+ sqlJsError: sqlJsError.message
64
+ });
65
+ throw new Error(`Squish requires SQLite to function. Database initialization failed.\n` +
66
+ `Primary error (bun:sqlite): ${bunSqliteError.message}\n` +
67
+ `Secondary error (better-sqlite3): ${betterSqliteError.message}\n` +
68
+ `Fallback error (sql.js): ${sqlJsError.message}\n\n` +
69
+ `Solutions:\n` +
70
+ `1. Install build tools: npm install -g windows-build-tools (Windows)\n` +
71
+ `2. Or use PostgreSQL instead by setting DATABASE_URL environment variable`);
72
+ }
50
73
  }
51
74
  }
52
75
  }
@@ -77,7 +100,7 @@ async function createSqlJsDb(dbPath) {
77
100
  }
78
101
  const sqlite = new SQL.Database(data);
79
102
  // Enable foreign keys
80
- sqlite.run('PRAGMA foreign_keys = ON');
103
+ sqlite.exec('PRAGMA foreign_keys = ON');
81
104
  // Schema bootstrap
82
105
  await ensureSqliteSchema(sqlite);
83
106
  // Persist database on changes (sql.js is in-memory by default)
@@ -89,6 +112,51 @@ async function createSqlJsDb(dbPath) {
89
112
  fs.writeFileSync(dbPath, Buffer.from(data));
90
113
  return result;
91
114
  };
115
+ // Add prepare method for compatibility with drizzle-orm/sql-js
116
+ sqlite.prepare = (query) => {
117
+ const stmt = sqlite.prepare(query);
118
+ const preparedStatement = {};
119
+ preparedStatement.bind = (bindParams) => {
120
+ bindParams.forEach((param, index) => {
121
+ stmt.bind(param, index + 1);
122
+ });
123
+ return preparedStatement;
124
+ };
125
+ preparedStatement.step = () => {
126
+ return stmt.step();
127
+ };
128
+ preparedStatement.getAsObject = () => {
129
+ return stmt.getAsObject();
130
+ };
131
+ preparedStatement.free = () => {
132
+ stmt.free();
133
+ };
134
+ preparedStatement.run = () => {
135
+ const result = stmt.step() ? { changes: sqlite.changes() } : { changes: 0 };
136
+ stmt.reset();
137
+ return result;
138
+ };
139
+ preparedStatement.get = (bindParams = []) => {
140
+ if (bindParams.length > 0) {
141
+ preparedStatement.bind(bindParams);
142
+ }
143
+ const row = stmt.step() ? stmt.getAsObject() : undefined;
144
+ stmt.reset();
145
+ return row;
146
+ };
147
+ preparedStatement.all = (bindParams = []) => {
148
+ if (bindParams.length > 0) {
149
+ preparedStatement.bind(bindParams);
150
+ }
151
+ const rows = [];
152
+ while (stmt.step()) {
153
+ rows.push(stmt.getAsObject());
154
+ }
155
+ stmt.reset();
156
+ return rows;
157
+ };
158
+ return preparedStatement;
159
+ };
92
160
  logger.info('SQLite initialized with sql.js (pure JavaScript fallback)');
93
161
  return drizzle(sqlite, { schema: schemaModule });
94
162
  }