super-memory-pro 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 (107) hide show
  1. package/README.md +247 -0
  2. package/dist/config.d.ts +6 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +25 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/db/connection.d.ts +20 -0
  7. package/dist/db/connection.d.ts.map +1 -0
  8. package/dist/db/connection.js +66 -0
  9. package/dist/db/connection.js.map +1 -0
  10. package/dist/db/migrations/001_initial.sql +102 -0
  11. package/dist/db/migrations.d.ts +14 -0
  12. package/dist/db/migrations.d.ts.map +1 -0
  13. package/dist/db/migrations.js +86 -0
  14. package/dist/db/migrations.js.map +1 -0
  15. package/dist/db/queries.d.ts +24 -0
  16. package/dist/db/queries.d.ts.map +1 -0
  17. package/dist/db/queries.js +179 -0
  18. package/dist/db/queries.js.map +1 -0
  19. package/dist/hooks/event-handler.d.ts +13 -0
  20. package/dist/hooks/event-handler.d.ts.map +1 -0
  21. package/dist/hooks/event-handler.js +80 -0
  22. package/dist/hooks/event-handler.js.map +1 -0
  23. package/dist/hooks/index.d.ts +4 -0
  24. package/dist/hooks/index.d.ts.map +1 -0
  25. package/dist/hooks/index.js +7 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/hooks/message-handler.d.ts +13 -0
  28. package/dist/hooks/message-handler.d.ts.map +1 -0
  29. package/dist/hooks/message-handler.js +47 -0
  30. package/dist/hooks/message-handler.js.map +1 -0
  31. package/dist/hooks/session-compactor.d.ts +15 -0
  32. package/dist/hooks/session-compactor.d.ts.map +1 -0
  33. package/dist/hooks/session-compactor.js +40 -0
  34. package/dist/hooks/session-compactor.js.map +1 -0
  35. package/dist/hooks/system-transform.d.ts +27 -0
  36. package/dist/hooks/system-transform.d.ts.map +1 -0
  37. package/dist/hooks/system-transform.js +76 -0
  38. package/dist/hooks/system-transform.js.map +1 -0
  39. package/dist/hooks/tool-handler.d.ts +14 -0
  40. package/dist/hooks/tool-handler.d.ts.map +1 -0
  41. package/dist/hooks/tool-handler.js +94 -0
  42. package/dist/hooks/tool-handler.js.map +1 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +152 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/installer/index.d.ts +19 -0
  48. package/dist/installer/index.d.ts.map +1 -0
  49. package/dist/installer/index.js +553 -0
  50. package/dist/installer/index.js.map +1 -0
  51. package/dist/memory/extractor.d.ts +29 -0
  52. package/dist/memory/extractor.d.ts.map +1 -0
  53. package/dist/memory/extractor.js +426 -0
  54. package/dist/memory/extractor.js.map +1 -0
  55. package/dist/memory/index.d.ts +7 -0
  56. package/dist/memory/index.d.ts.map +1 -0
  57. package/dist/memory/index.js +7 -0
  58. package/dist/memory/index.js.map +1 -0
  59. package/dist/memory/processor.d.ts +50 -0
  60. package/dist/memory/processor.d.ts.map +1 -0
  61. package/dist/memory/processor.js +199 -0
  62. package/dist/memory/processor.js.map +1 -0
  63. package/dist/memory/search.d.ts +35 -0
  64. package/dist/memory/search.d.ts.map +1 -0
  65. package/dist/memory/search.js +170 -0
  66. package/dist/memory/search.js.map +1 -0
  67. package/dist/memory/store.d.ts +32 -0
  68. package/dist/memory/store.d.ts.map +1 -0
  69. package/dist/memory/store.js +112 -0
  70. package/dist/memory/store.js.map +1 -0
  71. package/dist/server/index.d.ts +16 -0
  72. package/dist/server/index.d.ts.map +1 -0
  73. package/dist/server/index.js +49 -0
  74. package/dist/server/index.js.map +1 -0
  75. package/dist/server/middleware/async-handler.d.ts +11 -0
  76. package/dist/server/middleware/async-handler.d.ts.map +1 -0
  77. package/dist/server/middleware/async-handler.js +14 -0
  78. package/dist/server/middleware/async-handler.js.map +1 -0
  79. package/dist/server/middleware/error-handler.d.ts +11 -0
  80. package/dist/server/middleware/error-handler.d.ts.map +1 -0
  81. package/dist/server/middleware/error-handler.js +18 -0
  82. package/dist/server/middleware/error-handler.js.map +1 -0
  83. package/dist/server/routes/health.d.ts +2 -0
  84. package/dist/server/routes/health.d.ts.map +1 -0
  85. package/dist/server/routes/health.js +25 -0
  86. package/dist/server/routes/health.js.map +1 -0
  87. package/dist/server/routes/memory.d.ts +2 -0
  88. package/dist/server/routes/memory.d.ts.map +1 -0
  89. package/dist/server/routes/memory.js +139 -0
  90. package/dist/server/routes/memory.js.map +1 -0
  91. package/dist/server/schemas.d.ts +51 -0
  92. package/dist/server/schemas.d.ts.map +1 -0
  93. package/dist/server/schemas.js +23 -0
  94. package/dist/server/schemas.js.map +1 -0
  95. package/dist/tools/definitions.d.ts +130 -0
  96. package/dist/tools/definitions.d.ts.map +1 -0
  97. package/dist/tools/definitions.js +78 -0
  98. package/dist/tools/definitions.js.map +1 -0
  99. package/dist/tools/index.d.ts +32 -0
  100. package/dist/tools/index.d.ts.map +1 -0
  101. package/dist/tools/index.js +148 -0
  102. package/dist/tools/index.js.map +1 -0
  103. package/dist/types.d.ts +108 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +12 -0
  106. package/dist/types.js.map +1 -0
  107. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ <div align="center">
2
+ <br/>
3
+ <h1>🧠 Super Memory</h1>
4
+ <p><strong>Ultra-persistent, automatic memory plugin for OpenCode</strong></p>
5
+ <p><em>Learn. Remember. Recall. Across every session.</em></p>
6
+ <br/>
7
+
8
+ <p>
9
+ <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript"/>
10
+ <img src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white" alt="Node.js"/>
11
+ <img src="https://img.shields.io/badge/PostgreSQL-4169E1?style=for-the-badge&logo=postgresql&logoColor=white" alt="PostgreSQL"/>
12
+ <img src="https://img.shields.io/badge/Express-000000?style=for-the-badge&logo=express&logoColor=white" alt="Express"/>
13
+ </p>
14
+
15
+ <p>
16
+ <img src="https://img.shields.io/github/license/luftwaffe66/Super-Memory" alt="License"/>
17
+ <img src="https://img.shields.io/badge/OpenCode-Plugin-8B5CF6" alt="OpenCode Plugin"/>
18
+ <img src="https://img.shields.io/badge/status-alpha-yellow" alt="Status"/>
19
+ </p>
20
+
21
+ <img src="super-memory-opencode.png" alt="Super Memory Banner" width="100%"/>
22
+ <br/>
23
+ </div>
24
+
25
+ ---
26
+
27
+ ## 🌟 What is Super Memory?
28
+
29
+ **Super Memory** is an OpenCode plugin that gives your AI coding assistant **permanent, cross-session memory**. It automatically learns from every interaction — your tech stack, decisions, preferences, and project context — and recalls them in future sessions.
30
+
31
+ > **No more repeating yourself.** Every session builds upon the last.
32
+
33
+ ### ✨ Key Features
34
+
35
+ - **🧠 Automatic Learning** — Every message, command, and tool result is analyzed and stored
36
+ - **🔍 Smart Recall** — Relevant memories are injected into the system prompt on each session
37
+ - **📂 Tech Stack Detection** — Automatically recognizes languages, frameworks, databases, and tools
38
+ - **📝 Decision Tracking** — Captures architectural decisions and preferences
39
+ - **👤 User Profiling** — Learns your working style, preferences, and patterns
40
+ - **🔧 Custom Tools** — Query, save, and manage memories directly from chat
41
+ - **🔄 Cross-Platform** — Works on Linux, macOS, Windows, and Termux (Android)
42
+ - **📦 Auto-Installer** — Smart setup that detects and configures PostgreSQL automatically
43
+
44
+ ---
45
+
46
+ ## 🏗️ Architecture
47
+
48
+ ```
49
+ ┌─────────────────────────────────────────────────────────┐
50
+ │ OpenCode │
51
+ │ ┌──────────────────────────────────────────────────┐ │
52
+ │ │ Super Memory Plugin (TS/Node) │ │
53
+ │ │ │ │
54
+ │ │ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │ │
55
+ │ │ │ Hooks │ │ Tools │ │ Express API │ │ │
56
+ │ │ │ │ │ │ │ Server │ │ │
57
+ │ │ │ • Message│ │ • mem_save│ │ │ │ │
58
+ │ │ │ • System │ │ • mem_sea-│ │ CRUD + Search │ │ │
59
+ │ │ │ Prompt │ │ rch │ │ + Health │ │ │
60
+ │ │ │ • Compact│ │ • mem_for-│ │ │ │ │
61
+ │ │ │ ion │ │ get │ │ │ │ │
62
+ │ │ │ • Tool │ │ • mem_sta-│ │ │ │ │
63
+ │ │ │ Execute│ │ ts │ │ │ │ │
64
+ │ │ └────┬─────┘ └────┬─────┘ └───────┬────────┘ │ │
65
+ │ │ │ │ │ │ │
66
+ │ │ └──────────────┴────────────────┘ │ │
67
+ │ │ │ │ │
68
+ │ │ ┌────────▼────────┐ │ │
69
+ │ │ │ Memory │ │ │
70
+ │ │ │ Processor │ │ │
71
+ │ │ │ (Extract → │ │ │
72
+ │ │ │ Store → │ │ │
73
+ │ │ │ Recall) │ │ │
74
+ │ │ └────────┬────────┘ │ │
75
+ │ └────────────────────────┼───────────────────────────┘ │
76
+ │ │ │
77
+ │ ┌──────▼──────┐ │
78
+ │ │ PostgreSQL │ │
79
+ │ │ (ultra_memo-│ │
80
+ │ │ ry DB) │ │
81
+ │ └─────────────┘ │
82
+ └─────────────────────────────────────────────────────────┘
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 🚀 Quick Start
88
+
89
+ ### Prerequisites
90
+
91
+ - **Node.js** >= 18.0.0
92
+ - **PostgreSQL** (auto-installed if missing)
93
+ - **OpenCode** with plugin support
94
+
95
+ ### Installation
96
+
97
+ ```bash
98
+ # Clone the repository
99
+ git clone https://github.com/luftwaffe66/Super-Memory.git
100
+ cd Super-Memory
101
+
102
+ # Install dependencies
103
+ npm install
104
+
105
+ # Run the installer (detects/installs PostgreSQL, creates DB, runs migrations)
106
+ npm run install:plugin
107
+
108
+ # Build the plugin
109
+ npm run build
110
+ ```
111
+
112
+ ### OpenCode Configuration
113
+
114
+ Add the plugin to your `~/.config/opencode/opencode.jsonc`:
115
+
116
+ ```json
117
+ {
118
+ "plugin": [
119
+ "super-memory"
120
+ ]
121
+ }
122
+ ```
123
+
124
+ > **Note**: For local development, use `npm link` or a local path reference.
125
+
126
+ ---
127
+
128
+ ## 🛠️ Memory Tools
129
+
130
+ Super Memory registers four custom tools accessible from your OpenCode chat:
131
+
132
+ | Tool | Description | Example |
133
+ |------|-------------|---------|
134
+ | `mem_save` | Save an explicit memory | `mem_save(key="framework", content="Using React with TypeScript")` |
135
+ | `mem_search` | Search stored memories | `mem_search(query="React components")` |
136
+ | `mem_forget` | Delete a specific memory | `mem_forget(id="uuid-here")` |
137
+ | `mem_stats` | View memory statistics | `mem_stats()` |
138
+
139
+ ---
140
+
141
+ ## 🧠 How It Works
142
+
143
+ ### Automatic Learning
144
+
145
+ Every message you send is processed through multiple extractors:
146
+
147
+ 1. **Tech Stack Detector** — Identifies languages, frameworks, and tools mentioned
148
+ 2. **Decision Parser** — Captures architectural and design decisions
149
+ 3. **Preference Analyzer** — Learns your working style and preferences
150
+ 4. **Pattern Recognizer** — Identifies recurring workflows and commands
151
+
152
+ ### Smart Recall
153
+
154
+ When a new session starts, Super Memory:
155
+
156
+ 1. Searches for memories relevant to the current conversation
157
+ 2. Ranks them by importance and relevance
158
+ 3. Injects the most relevant memories into the system prompt
159
+ 4. Updates access timestamps to track usage patterns
160
+
161
+ ### Data Storage
162
+
163
+ All memories are stored in PostgreSQL with:
164
+ - **Full-text search** via `tsvector` indexes for fast retrieval
165
+ - **JSONB columns** for flexible metadata
166
+ - **Automatic deduplication** to prevent memory bloat
167
+ - **Importance scoring** (1-5) to prioritize critical information
168
+
169
+ ---
170
+
171
+ ## 📁 Project Structure
172
+
173
+ ```
174
+ Super-Memory/
175
+ ├── package.json # Plugin manifest
176
+ ├── tsconfig.json # TypeScript configuration
177
+ ├── scripts/
178
+ │ └── install.sh # Shell installer wrapper
179
+ ├── src/
180
+ │ ├── index.ts # Plugin entry point
181
+ │ ├── types.ts # TypeScript type definitions
182
+ │ ├── config.ts # Environment configuration
183
+ │ ├── db/
184
+ │ │ ├── connection.ts # PostgreSQL connection pool
185
+ │ │ ├── queries.ts # Database CRUD operations
186
+ │ │ ├── migrations.ts # Schema migration runner
187
+ │ │ └── migrations/
188
+ │ │ └── 001_initial.sql # Initial schema
189
+ │ ├── memory/
190
+ │ │ ├── index.ts # Module barrel
191
+ │ │ ├── extractor.ts # Pattern extraction (regex/heuristic)
192
+ │ │ ├── store.ts # Memory storage with dedup
193
+ │ │ ├── search.ts # Memory search and formatting
194
+ │ │ └── processor.ts # Core orchestrator
195
+ │ ├── server/
196
+ │ │ ├── index.ts # Express app factory
197
+ │ │ ├── schemas.ts # Zod validation schemas
198
+ │ │ ├── middleware/
199
+ │ │ │ ├── async-handler.ts # Async error wrapper
200
+ │ │ │ └── error-handler.ts # Global error handler
201
+ │ │ └── routes/
202
+ │ │ ├── health.ts # Health check endpoint
203
+ │ │ └── memory.ts # Memory CRUD REST API
204
+ │ ├── hooks/
205
+ │ │ └── ... # OpenCode hook handlers (WIP)
206
+ │ ├── tools/
207
+ │ │ └── definitions.ts # Custom tool schemas
208
+ │ └── installer/
209
+ │ └── index.ts # Cross-platform installer
210
+ └── tests/ # Test suite (WIP)
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 🌐 Cross-Platform Support
216
+
217
+ Super Memory's auto-installer handles multiple environments:
218
+
219
+ | Platform | Package Manager | PostgreSQL Install |
220
+ |----------|---------------|-------------------|
221
+ | **Linux** | `apt`, `dnf`, `yum`, `apk` | Auto-detected, installs if missing |
222
+ | **macOS** | `brew` | `brew install postgresql@17` |
223
+ | **Termux (Android)** | `pkg` | `pkg install postgresql` |
224
+ | **Windows** | — | Manual setup (instructions provided) |
225
+
226
+ The installer **never hardcodes `sudo`** — it detects whether it's available and uses it only when necessary.
227
+
228
+ ---
229
+
230
+ ## 📄 License
231
+
232
+ This project is licensed under the **MIT License**.
233
+
234
+ ---
235
+
236
+ <div align="center">
237
+ <br/>
238
+ <p>
239
+ Built with ❤️ for the OpenCode ecosystem
240
+ </p>
241
+ <p>
242
+ <a href="https://github.com/luftwaffe66/Super-Memory/issues">Report Bug</a>
243
+ ·
244
+ <a href="https://github.com/luftwaffe66/Super-Memory/issues">Request Feature</a>
245
+ </p>
246
+ <br/>
247
+ </div>
@@ -0,0 +1,6 @@
1
+ import type { PluginConfig } from './types.js';
2
+ /**
3
+ * Load configuration from environment variables with sensible defaults.
4
+ */
5
+ export declare function loadConfig(): PluginConfig;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAoBzC"}
package/dist/config.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Load configuration from environment variables with sensible defaults.
3
+ */
4
+ export function loadConfig() {
5
+ return {
6
+ db: {
7
+ host: process.env.UM_DB_HOST || 'localhost',
8
+ port: parseInt(process.env.UM_DB_PORT || '5432', 10),
9
+ database: process.env.UM_DB_NAME || 'ultra_memory',
10
+ user: process.env.UM_DB_USER || process.env.USER || 'postgres',
11
+ password: process.env.UM_DB_PASSWORD || '',
12
+ maxConnections: parseInt(process.env.UM_DB_POOL_MAX || '10', 10),
13
+ },
14
+ server: {
15
+ port: parseInt(process.env.UM_SERVER_PORT || '0', 10), // 0 = random port
16
+ host: process.env.UM_SERVER_HOST || '127.0.0.1',
17
+ },
18
+ memory: {
19
+ maxInjections: parseInt(process.env.UM_MAX_INJECTIONS || '5', 10),
20
+ minImportance: parseInt(process.env.UM_MIN_IMPORTANCE || '2', 10),
21
+ autoExtract: process.env.UM_AUTO_EXTRACT !== 'false',
22
+ },
23
+ };
24
+ }
25
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,EAAE,EAAE;YACF,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;YAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,EAAE,CAAC;YACpD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc;YAClD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,UAAU;YAC9D,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;YAC1C,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,EAAE,EAAE,CAAC;SACjE;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,GAAG,EAAE,EAAE,CAAC,EAAE,kBAAkB;YACzE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,WAAW;SAChD;QACD,MAAM,EAAE;YACN,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,EAAE,EAAE,CAAC;YACjE,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,EAAE,EAAE,CAAC;YACjE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO;SACrD;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import pg from 'pg';
2
+ import type { PluginConfig } from '../types.js';
3
+ /**
4
+ * Create and return the PostgreSQL connection pool.
5
+ * Lazily initialized — safe to call multiple times.
6
+ */
7
+ export declare function getPool(config?: PluginConfig): pg.Pool;
8
+ /**
9
+ * Quick health check: returns true if the database is reachable.
10
+ */
11
+ export declare function healthCheck(): Promise<boolean>;
12
+ /**
13
+ * Gracefully close the connection pool.
14
+ */
15
+ export declare function closePool(): Promise<void>;
16
+ /**
17
+ * Connect to a different database (used by installer to create the target DB).
18
+ */
19
+ export declare function createAdminPool(config: PluginConfig): pg.Pool;
20
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,EAAE,CAAC,IAAI,CAwBtD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAQpD;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,EAAE,CAAC,IAAI,CAU7D"}
@@ -0,0 +1,66 @@
1
+ import pg from 'pg';
2
+ let pool = null;
3
+ /**
4
+ * Create and return the PostgreSQL connection pool.
5
+ * Lazily initialized — safe to call multiple times.
6
+ */
7
+ export function getPool(config) {
8
+ if (pool)
9
+ return pool;
10
+ const cfg = config;
11
+ pool = new pg.Pool({
12
+ host: cfg.db.host,
13
+ port: cfg.db.port,
14
+ database: cfg.db.database,
15
+ user: cfg.db.user,
16
+ password: cfg.db.password,
17
+ max: cfg.db.maxConnections,
18
+ idleTimeoutMillis: 30_000,
19
+ connectionTimeoutMillis: 5_000,
20
+ });
21
+ pool.on('error', (err) => {
22
+ console.error('[UltraMemory] PostgreSQL pool error:', err.message);
23
+ });
24
+ pool.on('connect', () => {
25
+ // Connection established — nothing to log in production
26
+ });
27
+ return pool;
28
+ }
29
+ /**
30
+ * Quick health check: returns true if the database is reachable.
31
+ */
32
+ export async function healthCheck() {
33
+ if (!pool)
34
+ return false;
35
+ try {
36
+ const result = await pool.query('SELECT 1 AS ok');
37
+ return result.rows[0]?.ok === 1;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /**
44
+ * Gracefully close the connection pool.
45
+ */
46
+ export async function closePool() {
47
+ if (pool) {
48
+ await pool.end();
49
+ pool = null;
50
+ }
51
+ }
52
+ /**
53
+ * Connect to a different database (used by installer to create the target DB).
54
+ */
55
+ export function createAdminPool(config) {
56
+ return new pg.Pool({
57
+ host: config.db.host,
58
+ port: config.db.port,
59
+ database: 'postgres',
60
+ user: config.db.user,
61
+ password: config.db.password,
62
+ max: 1,
63
+ connectionTimeoutMillis: 5_000,
64
+ });
65
+ }
66
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,IAAI,IAAI,GAAmB,IAAI,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,MAAqB;IAC3C,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,GAAG,GAAG,MAAO,CAAC;IACpB,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC;QACjB,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI;QACjB,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI;QACjB,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ;QACzB,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI;QACjB,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ;QACzB,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,cAAc;QAC1B,iBAAiB,EAAE,MAAM;QACzB,uBAAuB,EAAE,KAAK;KAC/B,CAAC,CAAC;IAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACvB,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACtB,wDAAwD;IAC1D,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAClD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI;QACpB,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI;QACpB,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI;QACpB,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ;QAC5B,GAAG,EAAE,CAAC;QACN,uBAAuB,EAAE,KAAK;KAC/B,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,102 @@
1
+ -- Ultra Memory: Initial Schema
2
+ -- Creates core tables for persistent memory storage
3
+
4
+ -- Migration tracking table
5
+ CREATE TABLE IF NOT EXISTS _migrations (
6
+ id SERIAL PRIMARY KEY,
7
+ name TEXT NOT NULL UNIQUE,
8
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
9
+ );
10
+
11
+ -- Projects table
12
+ CREATE TABLE IF NOT EXISTS projects (
13
+ id TEXT PRIMARY KEY,
14
+ name TEXT NOT NULL,
15
+ path TEXT,
16
+ description TEXT,
17
+ tech_stack JSONB NOT NULL DEFAULT '[]'::jsonb,
18
+ conventions JSONB NOT NULL DEFAULT '{}'::jsonb,
19
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
20
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
21
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
22
+ );
23
+
24
+ -- Sessions table
25
+ CREATE TABLE IF NOT EXISTS sessions (
26
+ id TEXT PRIMARY KEY,
27
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
28
+ summary TEXT,
29
+ message_count INTEGER NOT NULL DEFAULT 0,
30
+ tokens_used INTEGER NOT NULL DEFAULT 0,
31
+ started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
32
+ ended_at TIMESTAMPTZ,
33
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb
34
+ );
35
+
36
+ -- Core memories table
37
+ CREATE TABLE IF NOT EXISTS memories (
38
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
39
+ memory_type TEXT NOT NULL CHECK (memory_type IN (
40
+ 'user_profile',
41
+ 'tech_stack',
42
+ 'project_context',
43
+ 'session',
44
+ 'pattern',
45
+ 'decision',
46
+ 'explicit'
47
+ )),
48
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
49
+ session_id TEXT REFERENCES sessions(id) ON DELETE SET NULL,
50
+ key TEXT NOT NULL,
51
+ value JSONB NOT NULL DEFAULT '{}'::jsonb,
52
+ content TEXT NOT NULL,
53
+ importance INTEGER NOT NULL DEFAULT 1 CHECK (importance BETWEEN 1 AND 5),
54
+ source TEXT NOT NULL DEFAULT 'auto',
55
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
56
+ access_count INTEGER NOT NULL DEFAULT 0,
57
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
58
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
59
+ last_accessed_at TIMESTAMPTZ,
60
+ -- Full-text search vector
61
+ search_vector TSVECTOR GENERATED ALWAYS AS (
62
+ to_tsvector('english', coalesce(content, ''))
63
+ ) STORED,
64
+ -- Unique constraint: one key per type per project
65
+ UNIQUE (memory_type, project_id, key)
66
+ );
67
+
68
+ -- Indexes
69
+ CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type);
70
+ CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project_id);
71
+ CREATE INDEX IF NOT EXISTS idx_memories_key ON memories(key);
72
+ CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance DESC);
73
+ CREATE INDEX IF NOT EXISTS idx_memories_fts ON memories USING GIN(search_vector);
74
+ CREATE INDEX IF NOT EXISTS idx_memories_created ON memories(created_at DESC);
75
+ CREATE INDEX IF NOT EXISTS idx_memories_last_access ON memories(last_accessed_at DESC NULLS LAST);
76
+ CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id);
77
+ CREATE INDEX IF NOT EXISTS idx_sessions_started ON sessions(started_at DESC);
78
+
79
+ -- Updated_at trigger function
80
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
81
+ RETURNS TRIGGER AS $$
82
+ BEGIN
83
+ NEW.updated_at = NOW();
84
+ RETURN NEW;
85
+ END;
86
+ $$ LANGUAGE plpgsql;
87
+
88
+ -- Apply trigger to tables
89
+ DO $$
90
+ BEGIN
91
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_projects_updated_at') THEN
92
+ CREATE TRIGGER update_projects_updated_at
93
+ BEFORE UPDATE ON projects
94
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
95
+ END IF;
96
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_memories_updated_at') THEN
97
+ CREATE TRIGGER update_memories_updated_at
98
+ BEFORE UPDATE ON memories
99
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
100
+ END IF;
101
+ END;
102
+ $$;
@@ -0,0 +1,14 @@
1
+ import pg from 'pg';
2
+ /**
3
+ * Run all pending migrations.
4
+ * Idempotent: safe to call multiple times.
5
+ */
6
+ export declare function runMigrations(pool?: pg.Pool): Promise<string[]>;
7
+ /**
8
+ * Get migration status.
9
+ */
10
+ export declare function getMigrationStatus(pool?: pg.Pool): Promise<{
11
+ name: string;
12
+ applied_at: string;
13
+ }[]>;
14
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,IAAI,CAAC;AA8BpB;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyCrE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAUxG"}
@@ -0,0 +1,86 @@
1
+ import { readFileSync, readdirSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getPool } from './connection.js';
5
+ function getMigrationsDir() {
6
+ // Resolve migrations directory relative to this source file at runtime
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ return join(__dirname, 'migrations');
9
+ }
10
+ /**
11
+ * Ensure the _migrations tracking table exists.
12
+ */
13
+ async function ensureMigrationTable(pool) {
14
+ await pool.query(`
15
+ CREATE TABLE IF NOT EXISTS _migrations (
16
+ id SERIAL PRIMARY KEY,
17
+ name TEXT NOT NULL UNIQUE,
18
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
19
+ )
20
+ `);
21
+ }
22
+ /**
23
+ * Get list of already-applied migrations.
24
+ */
25
+ async function getAppliedMigrations(pool) {
26
+ const result = await pool.query('SELECT name FROM _migrations ORDER BY name');
27
+ return new Set(result.rows.map((r) => r.name));
28
+ }
29
+ /**
30
+ * Run all pending migrations.
31
+ * Idempotent: safe to call multiple times.
32
+ */
33
+ export async function runMigrations(pool) {
34
+ const p = pool ?? getPool();
35
+ const applied = [];
36
+ await ensureMigrationTable(p);
37
+ const alreadyApplied = await getAppliedMigrations(p);
38
+ const migrationsDir = getMigrationsDir();
39
+ let files;
40
+ try {
41
+ files = readdirSync(migrationsDir)
42
+ .filter((f) => f.endsWith('.sql'))
43
+ .sort();
44
+ }
45
+ catch {
46
+ console.warn('[UltraMemory] No migration directory found at', migrationsDir);
47
+ return [];
48
+ }
49
+ for (const file of files) {
50
+ if (alreadyApplied.has(file))
51
+ continue;
52
+ const sql = readFileSync(join(migrationsDir, file), 'utf-8');
53
+ const client = await p.connect();
54
+ try {
55
+ await client.query('BEGIN');
56
+ await client.query(sql);
57
+ await client.query('INSERT INTO _migrations (name) VALUES ($1)', [file]);
58
+ await client.query('COMMIT');
59
+ applied.push(file);
60
+ console.log(`[UltraMemory] Applied migration: ${file}`);
61
+ }
62
+ catch (err) {
63
+ await client.query('ROLLBACK');
64
+ console.error(`[UltraMemory] Migration ${file} failed:`, err);
65
+ throw err;
66
+ }
67
+ finally {
68
+ client.release();
69
+ }
70
+ }
71
+ return applied;
72
+ }
73
+ /**
74
+ * Get migration status.
75
+ */
76
+ export async function getMigrationStatus(pool) {
77
+ const p = pool ?? getPool();
78
+ try {
79
+ const result = await p.query('SELECT name, applied_at FROM _migrations ORDER BY name');
80
+ return result.rows;
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { MemoryCreate, MemoryQuery, MemoryRecord, MemorySearchResult, MemoryType, MemoryUpdate, Session, Project } from '../types.js';
2
+ export declare function createMemory(data: MemoryCreate): Promise<MemoryRecord>;
3
+ export declare function getMemory(id: string): Promise<MemoryRecord | null>;
4
+ export declare function searchMemories(query: MemoryQuery): Promise<MemorySearchResult[]>;
5
+ export declare function updateMemory(id: string, data: MemoryUpdate): Promise<MemoryRecord | null>;
6
+ export declare function deleteMemory(id: string): Promise<boolean>;
7
+ export declare function recordMemoryAccess(id: string): Promise<void>;
8
+ export declare function getMemoryStats(): Promise<{
9
+ type: MemoryType;
10
+ count: number;
11
+ }[]>;
12
+ export declare function createSession(id: string, projectId?: string): Promise<Session>;
13
+ export declare function getSession(id: string): Promise<Session | null>;
14
+ export declare function updateSession(id: string, data: Partial<Pick<Session, 'summary' | 'message_count' | 'tokens_used' | 'ended_at' | 'metadata'>>): Promise<Session | null>;
15
+ export declare function createProject(data: {
16
+ id: string;
17
+ name: string;
18
+ path?: string;
19
+ description?: string;
20
+ }): Promise<Project>;
21
+ export declare function getProject(id: string): Promise<Project | null>;
22
+ export declare function updateProjectTechStack(projectId: string, tech: string[]): Promise<void>;
23
+ export declare function getAllProjects(): Promise<Project[]>;
24
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAK3I,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAqB5E;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAOxE;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAiDtF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA6B/F;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAI/D;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAMrF;AAID,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOpF;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAIpE;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,eAAe,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAkB5K;AAID,wBAAsB,aAAa,CAAC,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAU7H;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAIpE;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7F;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAIzD"}