@shahmilsaari/memory-core 0.2.8 → 0.2.11
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.
- package/README.md +221 -58
- package/dist/chunk-73SRPNAL.js +196 -0
- package/dist/chunk-HAGRPKR3.js +30 -0
- package/dist/chunk-KSLFLWB4.js +32 -0
- package/dist/cli.js +935 -386
- package/dist/db-KU4EEG4Y.js +28 -0
- package/dist/embedding-PAYD2JYW.js +8 -0
- package/package.json +4 -2
- package/profiles/go-api.yml +43 -0
- package/profiles/nextjs.yml +0 -32
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ You decide the architecture. memory-core remembers it. Every AI tool — Copilot
|
|
|
8
8
|
npx @shahmilsaari/memory-core init
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Fully local or cloud — your choice. **v0.2.10**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -18,10 +18,10 @@ Without memory-core, every AI agent starts fresh. It doesn't know you're using C
|
|
|
18
18
|
|
|
19
19
|
With memory-core:
|
|
20
20
|
|
|
21
|
-
1. You run `init` once — it verifies your PostgreSQL and Ollama connections, picks your model,
|
|
21
|
+
1. You run `init` once — it verifies your PostgreSQL and Ollama connections, picks your model, lets you choose which agents to generate files for, and installs a pre-commit hook
|
|
22
22
|
2. Those agents read the files and follow your rules — automatically
|
|
23
23
|
3. Watch mode catches violations as you type, not just at commit time
|
|
24
|
-
4. When you commit,
|
|
24
|
+
4. When you commit, the hook checks your code before the commit goes through (advisory by default — logs violations but never blocks)
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -152,7 +152,7 @@ CREATE INDEX IF NOT EXISTS memories_scope_idx ON memories (scope);
|
|
|
152
152
|
# 1. Go to your project
|
|
153
153
|
cd my-api
|
|
154
154
|
|
|
155
|
-
# 2. Initialize — verifies connections, picks your model, generates
|
|
155
|
+
# 2. Initialize — verifies connections, picks your model, generates config files
|
|
156
156
|
npx @shahmilsaari/memory-core init
|
|
157
157
|
|
|
158
158
|
# 3. Load 281 predefined best-practice rules
|
|
@@ -173,18 +173,36 @@ npx @shahmilsaari/memory-core init
|
|
|
173
173
|
|
|
174
174
|
Walks you through:
|
|
175
175
|
- PostgreSQL connection URL — **tested live, retries until connected**
|
|
176
|
-
- Ollama URL — **tested live, retries until reachable**
|
|
177
|
-
- Code-checking
|
|
176
|
+
- Ollama URL — **tested live, retries until reachable** (used for embeddings)
|
|
177
|
+
- Code-checking provider — **Ollama (local), OpenAI, Anthropic, or MiniMax**
|
|
178
|
+
- Code-checking model — picked from a list, verified before continuing
|
|
178
179
|
- Project name, type, architecture, language
|
|
179
|
-
-
|
|
180
|
+
- Which agents to generate files for — **multi-select, all pre-checked, Space to deselect** — saved to `.memory-core.json`
|
|
181
|
+
- Hook mode — **advisory** (logs violations, never blocks) or **strict** (blocks commits)
|
|
180
182
|
- Whether to enable caveman mode (optional token saver)
|
|
181
183
|
|
|
182
|
-
Generates config files for every
|
|
184
|
+
Generates config files for every selected AI agent, saves your choices to `.memory-core.json`, and automatically adds all generated files to `.gitignore` under a `# memory-core generated files` block.
|
|
183
185
|
|
|
184
186
|
At the end, the banner shows live ✓/✗ status for PostgreSQL and Ollama so you know everything is working.
|
|
185
187
|
|
|
186
188
|
---
|
|
187
189
|
|
|
190
|
+
### `sync` — Refresh all agent files
|
|
191
|
+
|
|
192
|
+
After saving new memories, regenerate every agent file to include them.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npx @shahmilsaari/memory-core sync
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Multi-selects which agents to sync (pre-checked from your `.memory-core.json` config). Skips files that haven't changed and reports how many were updated vs already up to date:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
3 updated, 11 already up to date
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
188
206
|
### `remember` — Save a decision
|
|
189
207
|
|
|
190
208
|
Made a decision your team should never forget? Save it.
|
|
@@ -213,16 +231,6 @@ npx @shahmilsaari/memory-core remember "Use DTOs for all API responses" \
|
|
|
213
231
|
|
|
214
232
|
---
|
|
215
233
|
|
|
216
|
-
### `sync` — Refresh all agent files
|
|
217
|
-
|
|
218
|
-
After saving new memories, regenerate every agent file to include them.
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
npx @shahmilsaari/memory-core sync
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
234
|
### `search` — Find a rule or decision
|
|
227
235
|
|
|
228
236
|
```bash
|
|
@@ -246,17 +254,67 @@ npx @shahmilsaari/memory-core seed --force # re-seed existin
|
|
|
246
254
|
|
|
247
255
|
---
|
|
248
256
|
|
|
249
|
-
### `
|
|
257
|
+
### `watch` — Catch violations as you type
|
|
250
258
|
|
|
251
259
|
```bash
|
|
252
|
-
npx @shahmilsaari/memory-core
|
|
260
|
+
npx @shahmilsaari/memory-core watch
|
|
253
261
|
```
|
|
254
262
|
|
|
255
|
-
|
|
263
|
+
Runs in the background and checks each file the moment you save it. You see violations immediately — before you even think about committing.
|
|
256
264
|
|
|
257
|
-
|
|
265
|
+
```
|
|
266
|
+
archmind watch — real-time rule enforcement
|
|
258
267
|
|
|
259
|
-
|
|
268
|
+
watching: /your/project
|
|
269
|
+
model: llama3.2
|
|
270
|
+
rules: 24
|
|
271
|
+
ctrl+c to stop
|
|
272
|
+
|
|
273
|
+
[10:42:11] saved: src/controllers/user.ts
|
|
274
|
+
|
|
275
|
+
✗ 1 violation in src/controllers/user.ts
|
|
276
|
+
|
|
277
|
+
[1] src/controllers/user.ts:34
|
|
278
|
+
Rule: Thin controllers — business logic belongs in services
|
|
279
|
+
Why: Logic in controllers cannot be reused from other entry points
|
|
280
|
+
Issue: Password hashing inside the route handler
|
|
281
|
+
Fix: Move to UserService.hashPassword()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Options:
|
|
285
|
+
```bash
|
|
286
|
+
npx @shahmilsaari/memory-core watch --path src/ # watch a specific folder only
|
|
287
|
+
npx @shahmilsaari/memory-core watch --verbose # show extra details
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Only checks source files — ignores `node_modules`, `dist`, config files, JSON, etc.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
### `check` — Manual check (for CI)
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npx @shahmilsaari/memory-core check --staged # check staged files
|
|
298
|
+
npx @shahmilsaari/memory-core check --staged --verbose # with extra detail
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Same as the pre-commit hook. Use this in CI/CD pipelines.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### `hook install` — Install the pre-commit hook
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
npx @shahmilsaari/memory-core hook install # advisory mode (default)
|
|
309
|
+
npx @shahmilsaari/memory-core hook install --advisory # logs violations, never blocks
|
|
310
|
+
npx @shahmilsaari/memory-core hook install --strict # blocks commits on violations
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Installs a git pre-commit hook in the current project. Every time you run `git commit`, your code is checked against your architecture rules.
|
|
314
|
+
|
|
315
|
+
**Advisory mode (default):** violations are logged so you can see them, but the commit always goes through. Useful for getting used to the rules without disrupting your flow.
|
|
316
|
+
|
|
317
|
+
**Strict mode:** violations block the commit entirely. You see exactly what's wrong and how to fix it:
|
|
260
318
|
|
|
261
319
|
```
|
|
262
320
|
✗ 2 rule violations found — commit blocked
|
|
@@ -278,57 +336,130 @@ When a violation is found, the commit is blocked and you see exactly what's wron
|
|
|
278
336
|
To save as memory: memory-core remember "<lesson>"
|
|
279
337
|
```
|
|
280
338
|
|
|
339
|
+
The hook mode is chosen during `init` — no separate step needed unless you want to change it later.
|
|
340
|
+
|
|
281
341
|
```bash
|
|
282
342
|
npx @shahmilsaari/memory-core hook uninstall # remove the hook
|
|
283
343
|
```
|
|
284
344
|
|
|
285
345
|
---
|
|
286
346
|
|
|
287
|
-
### `
|
|
347
|
+
### `list` — List memories from the database
|
|
288
348
|
|
|
289
349
|
```bash
|
|
290
|
-
npx @shahmilsaari/memory-core
|
|
350
|
+
npx @shahmilsaari/memory-core list
|
|
291
351
|
```
|
|
292
352
|
|
|
293
|
-
|
|
353
|
+
Shows all stored memories. Filter the results:
|
|
294
354
|
|
|
355
|
+
```bash
|
|
356
|
+
npx @shahmilsaari/memory-core list --type rule
|
|
357
|
+
npx @shahmilsaari/memory-core list --scope global
|
|
358
|
+
npx @shahmilsaari/memory-core list --arch clean-architecture
|
|
359
|
+
npx @shahmilsaari/memory-core list --limit 50
|
|
295
360
|
```
|
|
296
|
-
archmind watch — real-time rule enforcement
|
|
297
361
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
362
|
+
| Flag | What it does |
|
|
363
|
+
|---|---|
|
|
364
|
+
| `--type <type>` | Filter by type: `decision` `rule` `pattern` `note` |
|
|
365
|
+
| `--scope <scope>` | Filter by scope: `global` `project` |
|
|
366
|
+
| `--arch <architecture>` | Filter by architecture profile |
|
|
367
|
+
| `--limit <n>` | Max results (default 200) |
|
|
302
368
|
|
|
303
|
-
|
|
369
|
+
---
|
|
304
370
|
|
|
305
|
-
|
|
371
|
+
### `remove` — Delete a memory
|
|
306
372
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
Why: Logic in controllers cannot be reused from other entry points
|
|
310
|
-
Issue: Password hashing inside the route handler
|
|
311
|
-
Fix: Move to UserService.hashPassword()
|
|
373
|
+
```bash
|
|
374
|
+
npx @shahmilsaari/memory-core remove <id>
|
|
312
375
|
```
|
|
313
376
|
|
|
314
|
-
|
|
377
|
+
Deletes a memory by its ID. Get the ID from `list` or `search`.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
### `edit` — Edit a memory
|
|
382
|
+
|
|
315
383
|
```bash
|
|
316
|
-
npx @shahmilsaari/memory-core
|
|
317
|
-
npx @shahmilsaari/memory-core watch --verbose # show extra details
|
|
384
|
+
npx @shahmilsaari/memory-core edit <id>
|
|
318
385
|
```
|
|
319
386
|
|
|
320
|
-
|
|
387
|
+
Opens the memory interactively so you can update its content, reason, tags, type, or scope.
|
|
321
388
|
|
|
322
389
|
---
|
|
323
390
|
|
|
324
|
-
### `
|
|
391
|
+
### `export` — Export memories to a file
|
|
325
392
|
|
|
326
393
|
```bash
|
|
327
|
-
npx @shahmilsaari/memory-core
|
|
328
|
-
npx @shahmilsaari/memory-core
|
|
394
|
+
npx @shahmilsaari/memory-core export
|
|
395
|
+
npx @shahmilsaari/memory-core export --output path/to/my-rules.json
|
|
329
396
|
```
|
|
330
397
|
|
|
331
|
-
|
|
398
|
+
Exports all memories from the database to `memories.json` in the project root (or the path you specify). Makes your rules portable and version-controllable — commit the file alongside your code.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
### `import` — Import memories from a file
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
npx @shahmilsaari/memory-core import
|
|
406
|
+
npx @shahmilsaari/memory-core import --file path/to/my-rules.json
|
|
407
|
+
npx @shahmilsaari/memory-core import --url https://example.com/team-rules.json
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Imports memories into the local database. Skips duplicates by content hash — safe to run more than once.
|
|
411
|
+
|
|
412
|
+
| Flag | What it does |
|
|
413
|
+
|---|---|
|
|
414
|
+
| `--file <path>` | Import from a custom local file path |
|
|
415
|
+
| `--url <url>` | Import from a remote URL |
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### `ignore` — Mark false positives
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
npx @shahmilsaari/memory-core ignore "controllers may import prisma directly in migration scripts"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Saves a pattern so the hook and watcher never flag it again. Useful for intentional exceptions to a rule.
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npx @shahmilsaari/memory-core ignore --list # show all saved ignore patterns
|
|
429
|
+
npx @shahmilsaari/memory-core ignore --remove <id> # delete an ignore pattern
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
### `ci-setup` — GitHub Actions integration
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
npx @shahmilsaari/memory-core ci-setup
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Generates `.github/workflows/memory-core.yml`. Adds a PR check that runs your architecture rules on every pull request — same checks as the local hook, enforced in CI.
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
### `stats` — Violation counters
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
npx @shahmilsaari/memory-core stats
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Shows which rules fire most often and which files have the most violations. Useful for spotting systemic issues in the codebase.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
### `reset` — Remove all generated files
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
npx @shahmilsaari/memory-core reset # remove all generated files + .memory-core.json
|
|
458
|
+
npx @shahmilsaari/memory-core reset --soft # keep config and DB, remove only generated files
|
|
459
|
+
npx @shahmilsaari/memory-core reset --db # also drop the memories table from the database
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Cleans up everything memory-core created. Use `--soft` if you want to re-run `init` without losing your saved memories.
|
|
332
463
|
|
|
333
464
|
---
|
|
334
465
|
|
|
@@ -387,7 +518,7 @@ Pick the one that matches how your project is structured.
|
|
|
387
518
|
| Modular Monolith | You're building feature modules that might become microservices later |
|
|
388
519
|
| MVC | Standard web app with controllers and services |
|
|
389
520
|
| Hexagonal | You want ports and adapters for maximum testability |
|
|
390
|
-
|
|
|
521
|
+
| Go REST API | Go backend API with idiomatic error handling and clean package structure |
|
|
391
522
|
| Laravel | Laravel with service-repository pattern |
|
|
392
523
|
| NestJS | Modules, guards, pipes, DTOs, repository pattern |
|
|
393
524
|
|
|
@@ -422,7 +553,7 @@ Cuts AI response length by 65–75% by removing filler words. Opt in during `ini
|
|
|
422
553
|
```bash
|
|
423
554
|
# Starting a new project
|
|
424
555
|
cd my-api
|
|
425
|
-
npx @shahmilsaari/memory-core init # verifies connections, picks model, installs hook
|
|
556
|
+
npx @shahmilsaari/memory-core init # verifies connections, picks model, selects agents, installs hook
|
|
426
557
|
npx @shahmilsaari/memory-core seed # load 281 best-practice rules
|
|
427
558
|
|
|
428
559
|
# Made an architectural decision? Save it.
|
|
@@ -435,7 +566,13 @@ npx @shahmilsaari/memory-core sync
|
|
|
435
566
|
# Not sure how something was decided? Search.
|
|
436
567
|
npx @shahmilsaari/memory-core search "caching strategy"
|
|
437
568
|
|
|
438
|
-
#
|
|
569
|
+
# See what rules are saved
|
|
570
|
+
npx @shahmilsaari/memory-core list --type rule
|
|
571
|
+
|
|
572
|
+
# Export rules to version control
|
|
573
|
+
npx @shahmilsaari/memory-core export
|
|
574
|
+
|
|
575
|
+
# Commit code → hook checks it automatically before committing
|
|
439
576
|
git commit -m "add user endpoint"
|
|
440
577
|
```
|
|
441
578
|
|
|
@@ -448,9 +585,12 @@ memory-core creates `.memory-core.env` automatically during `init`. You can also
|
|
|
448
585
|
| Variable | Required | Default | What it does |
|
|
449
586
|
|---|---|---|---|
|
|
450
587
|
| `DATABASE_URL` | Yes | — | PostgreSQL connection string |
|
|
451
|
-
| `OLLAMA_URL` | No | `http://localhost:11434` | Where Ollama is running |
|
|
452
|
-
| `OLLAMA_MODEL` | No | `nomic-embed-text` | Model used for search (embeddings) |
|
|
453
|
-
| `
|
|
588
|
+
| `OLLAMA_URL` | No | `http://localhost:11434` | Where Ollama is running (used for embeddings) |
|
|
589
|
+
| `OLLAMA_MODEL` | No | `nomic-embed-text` | Model used for search (embeddings) — always Ollama |
|
|
590
|
+
| `CHAT_PROVIDER` | No | `ollama` | Provider for code checking: `ollama`, `openai`, `anthropic`, `minimax` |
|
|
591
|
+
| `CHAT_MODEL` | No | `llama3.2` | Model used for code checking — chosen during `init` |
|
|
592
|
+
| `CHAT_API_KEY` | No | — | API key for OpenAI / Anthropic / MiniMax (not needed for Ollama) |
|
|
593
|
+
| `OLLAMA_CHAT_MODEL` | No | `llama3.2` | Legacy alias for `CHAT_MODEL` when provider is `ollama` |
|
|
454
594
|
|
|
455
595
|
---
|
|
456
596
|
|
|
@@ -463,7 +603,16 @@ pgvector isn't installed for your PostgreSQL version. Follow the [pgvector insta
|
|
|
463
603
|
Start Ollama: `brew services start ollama` (macOS) or `ollama serve` (Linux).
|
|
464
604
|
|
|
465
605
|
**`Chat model "llama3.2" not found`**
|
|
466
|
-
Run `ollama pull llama3.2`. Or switch
|
|
606
|
+
Run `ollama pull llama3.2`. Or switch provider/model by updating `CHAT_PROVIDER` and `CHAT_MODEL` in `.memory-core.env`.
|
|
607
|
+
|
|
608
|
+
**Using an API key instead of Ollama for code checking**
|
|
609
|
+
During `init` choose OpenAI, Anthropic, or MiniMax when prompted for the check provider. Or set manually in `.memory-core.env`:
|
|
610
|
+
```
|
|
611
|
+
CHAT_PROVIDER=openai
|
|
612
|
+
CHAT_MODEL=gpt-4o
|
|
613
|
+
CHAT_API_KEY=sk-...
|
|
614
|
+
```
|
|
615
|
+
Embeddings always use Ollama (`nomic-embed-text`) regardless of provider.
|
|
467
616
|
|
|
468
617
|
**`DATABASE_URL is not set`**
|
|
469
618
|
Run `npx @shahmilsaari/memory-core init` — it will create the `.memory-core.env` file for you.
|
|
@@ -476,6 +625,9 @@ createuser -s $(whoami)
|
|
|
476
625
|
**Hook is flagging JSON or config files**
|
|
477
626
|
It won't — the hook only checks source code files: `.ts .tsx .js .jsx .py .php .rb .go .java .cs .swift .kt .rs .vue .svelte`. Everything else is skipped automatically.
|
|
478
627
|
|
|
628
|
+
**Hook is flagging something that's intentional**
|
|
629
|
+
Save an ignore pattern: `npx @shahmilsaari/memory-core ignore "your exception here"`. The hook and watcher will never flag it again.
|
|
630
|
+
|
|
479
631
|
---
|
|
480
632
|
|
|
481
633
|
## Roadmap
|
|
@@ -487,11 +639,22 @@ It won't — the hook only checks source code files: `.ts .tsx .js .jsx .py .php
|
|
|
487
639
|
| ✓ | Connection validation — PostgreSQL and Ollama verified during setup |
|
|
488
640
|
| ✓ | Svelte 5 / SvelteKit profile and 37 rules |
|
|
489
641
|
| ✓ | NestJS profile and 39 rules |
|
|
490
|
-
| |
|
|
491
|
-
| |
|
|
492
|
-
| |
|
|
493
|
-
| |
|
|
494
|
-
| |
|
|
642
|
+
| ✓ | Hook auto-prompt — hook mode offered during init, no separate step needed |
|
|
643
|
+
| ✓ | CI/CD — `ci-setup` generates GitHub Actions workflow for PR enforcement |
|
|
644
|
+
| ✓ | Violation stats — see which rules fire most and which files break most |
|
|
645
|
+
| ✓ | Agent selection — choose which agents to generate files for during init |
|
|
646
|
+
| ✓ | Export / import — portable memories.json for version control and team sharing |
|
|
647
|
+
| ✓ | List / remove / edit — full CRUD for stored memories |
|
|
648
|
+
| ✓ | False positive tagging — `ignore` command saves exceptions for hook and watcher |
|
|
649
|
+
| ✓ | Reset command — clean up generated files and optionally drop the DB table |
|
|
650
|
+
| ✓ | Test suite — Vitest smoke tests for all core commands and providers |
|
|
651
|
+
| ✓ | Multi-provider code checking — Ollama, OpenAI, Anthropic, MiniMax |
|
|
652
|
+
| | Context-aware retrieval — surface the most relevant rules for the file being edited |
|
|
653
|
+
| | Model guidance during init — recommend a model based on machine specs |
|
|
654
|
+
| | `--debug` flag — verbose output for diagnosing hook and watcher issues |
|
|
655
|
+
| | Violation → rule pipeline — auto-suggest a new rule when the same violation repeats |
|
|
656
|
+
| | Rule analytics dashboard — visual breakdown of rule coverage and violations |
|
|
657
|
+
| | Team sync — shared database so the whole team works from the same rule set |
|
|
495
658
|
|
|
496
659
|
---
|
|
497
660
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
Config
|
|
4
|
+
} from "./chunk-KSLFLWB4.js";
|
|
5
|
+
|
|
6
|
+
// src/db.ts
|
|
7
|
+
import pg from "pg";
|
|
8
|
+
import { createHash } from "crypto";
|
|
9
|
+
var { Pool } = pg;
|
|
10
|
+
var pool = null;
|
|
11
|
+
var migrationsRun = false;
|
|
12
|
+
function hashMemoryContent(content) {
|
|
13
|
+
return createHash("md5").update(content.trim()).digest("hex");
|
|
14
|
+
}
|
|
15
|
+
function getPool() {
|
|
16
|
+
if (!pool) {
|
|
17
|
+
if (!Config.databaseUrl) {
|
|
18
|
+
throw new Error("DATABASE_URL is not set. Add it to your .env or .memory-core.env file.");
|
|
19
|
+
}
|
|
20
|
+
pool = new Pool({ connectionString: Config.databaseUrl });
|
|
21
|
+
}
|
|
22
|
+
return pool;
|
|
23
|
+
}
|
|
24
|
+
async function runMigrations() {
|
|
25
|
+
if (migrationsRun) return;
|
|
26
|
+
const client = await getPool().connect();
|
|
27
|
+
try {
|
|
28
|
+
await client.query("BEGIN");
|
|
29
|
+
await client.query(`ALTER TABLE memories ADD COLUMN IF NOT EXISTS reason TEXT`);
|
|
30
|
+
await client.query(`ALTER TABLE memories ADD COLUMN IF NOT EXISTS content_hash TEXT`);
|
|
31
|
+
await client.query(
|
|
32
|
+
`UPDATE memories
|
|
33
|
+
SET content_hash = md5(trim(content))
|
|
34
|
+
WHERE content_hash IS NULL`
|
|
35
|
+
);
|
|
36
|
+
await client.query(`CREATE INDEX IF NOT EXISTS memories_content_hash_idx ON memories (content_hash)`);
|
|
37
|
+
await client.query("COMMIT");
|
|
38
|
+
migrationsRun = true;
|
|
39
|
+
} catch (err) {
|
|
40
|
+
await client.query("ROLLBACK");
|
|
41
|
+
throw err;
|
|
42
|
+
} finally {
|
|
43
|
+
client.release();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function saveMemory(memory) {
|
|
47
|
+
await runMigrations();
|
|
48
|
+
const { type, scope, architecture, projectName, title, content, reason, tags, embedding } = memory;
|
|
49
|
+
const contentHash = hashMemoryContent(content);
|
|
50
|
+
await getPool().query(
|
|
51
|
+
`INSERT INTO memories (type, scope, architecture, project_name, title, content, reason, tags, embedding, content_hash)
|
|
52
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
|
|
53
|
+
[type, scope, architecture ?? null, projectName ?? null, title ?? null, content, reason ?? null, tags ?? [], `[${embedding.join(",")}]`, contentHash]
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
async function upsertMemory(memory) {
|
|
57
|
+
await runMigrations();
|
|
58
|
+
const contentHash = hashMemoryContent(memory.content);
|
|
59
|
+
const existing = await getPool().query(
|
|
60
|
+
`SELECT id FROM memories
|
|
61
|
+
WHERE content_hash = $1
|
|
62
|
+
AND COALESCE(architecture, '') = COALESCE($2, '')
|
|
63
|
+
AND scope = $3
|
|
64
|
+
AND type = $4
|
|
65
|
+
LIMIT 1`,
|
|
66
|
+
[contentHash, memory.architecture ?? null, memory.scope, memory.type]
|
|
67
|
+
);
|
|
68
|
+
if (existing.rowCount) return "skipped";
|
|
69
|
+
await saveMemory(memory);
|
|
70
|
+
return "inserted";
|
|
71
|
+
}
|
|
72
|
+
async function listMemories(filters = {}) {
|
|
73
|
+
await runMigrations();
|
|
74
|
+
const where = [];
|
|
75
|
+
const params = [];
|
|
76
|
+
if (filters.type) {
|
|
77
|
+
params.push(filters.type);
|
|
78
|
+
where.push(`type = $${params.length}`);
|
|
79
|
+
}
|
|
80
|
+
if (filters.scope) {
|
|
81
|
+
params.push(filters.scope);
|
|
82
|
+
where.push(`scope = $${params.length}`);
|
|
83
|
+
}
|
|
84
|
+
if (filters.architecture) {
|
|
85
|
+
params.push(filters.architecture);
|
|
86
|
+
where.push(`architecture = $${params.length}`);
|
|
87
|
+
}
|
|
88
|
+
const limit = filters.limit ?? 200;
|
|
89
|
+
params.push(limit);
|
|
90
|
+
const result = await getPool().query(
|
|
91
|
+
`SELECT id, type, scope, architecture, project_name, title, content, reason, tags, content_hash
|
|
92
|
+
FROM memories
|
|
93
|
+
${where.length ? `WHERE ${where.join(" AND ")}` : ""}
|
|
94
|
+
ORDER BY id ASC
|
|
95
|
+
LIMIT $${params.length}`,
|
|
96
|
+
params
|
|
97
|
+
);
|
|
98
|
+
return result.rows;
|
|
99
|
+
}
|
|
100
|
+
async function getMemory(id) {
|
|
101
|
+
await runMigrations();
|
|
102
|
+
const result = await getPool().query(
|
|
103
|
+
`SELECT id, type, scope, architecture, project_name, title, content, reason, tags, content_hash
|
|
104
|
+
FROM memories
|
|
105
|
+
WHERE id = $1`,
|
|
106
|
+
[id]
|
|
107
|
+
);
|
|
108
|
+
return result.rows[0] ?? null;
|
|
109
|
+
}
|
|
110
|
+
async function deleteMemory(id) {
|
|
111
|
+
await runMigrations();
|
|
112
|
+
const result = await getPool().query(`DELETE FROM memories WHERE id = $1`, [id]);
|
|
113
|
+
return (result.rowCount ?? 0) > 0;
|
|
114
|
+
}
|
|
115
|
+
async function updateMemory(id, patch) {
|
|
116
|
+
await runMigrations();
|
|
117
|
+
const current = await getMemory(id);
|
|
118
|
+
if (!current) return null;
|
|
119
|
+
const content = patch.content ?? current.content;
|
|
120
|
+
const contentHash = hashMemoryContent(content);
|
|
121
|
+
const embedding = patch.embedding ? `[${patch.embedding.join(",")}]` : null;
|
|
122
|
+
const result = await getPool().query(
|
|
123
|
+
`UPDATE memories
|
|
124
|
+
SET type = $2,
|
|
125
|
+
scope = $3,
|
|
126
|
+
title = $4,
|
|
127
|
+
content = $5,
|
|
128
|
+
reason = $6,
|
|
129
|
+
tags = $7,
|
|
130
|
+
content_hash = $8,
|
|
131
|
+
embedding = COALESCE($9::vector, embedding)
|
|
132
|
+
WHERE id = $1
|
|
133
|
+
RETURNING id, type, scope, architecture, project_name, title, content, reason, tags, content_hash`,
|
|
134
|
+
[
|
|
135
|
+
id,
|
|
136
|
+
patch.type ?? current.type,
|
|
137
|
+
patch.scope ?? current.scope,
|
|
138
|
+
patch.title ?? current.title ?? null,
|
|
139
|
+
content,
|
|
140
|
+
patch.reason ?? current.reason ?? null,
|
|
141
|
+
patch.tags ?? current.tags ?? [],
|
|
142
|
+
contentHash,
|
|
143
|
+
embedding
|
|
144
|
+
]
|
|
145
|
+
);
|
|
146
|
+
return result.rows[0] ?? null;
|
|
147
|
+
}
|
|
148
|
+
async function searchMemories(embedding, architecture, limit = 10) {
|
|
149
|
+
await runMigrations();
|
|
150
|
+
const vector = `[${embedding.join(",")}]`;
|
|
151
|
+
const params = [vector];
|
|
152
|
+
let whereClause = "";
|
|
153
|
+
if (architecture) {
|
|
154
|
+
whereClause = `WHERE (architecture = $2 OR scope = 'global')`;
|
|
155
|
+
params.push(architecture);
|
|
156
|
+
}
|
|
157
|
+
const client = await getPool().connect();
|
|
158
|
+
try {
|
|
159
|
+
await client.query("BEGIN");
|
|
160
|
+
await client.query("SET LOCAL ivfflat.probes = 10");
|
|
161
|
+
const result = await client.query(
|
|
162
|
+
`SELECT id, type, scope, architecture, project_name, title, content, reason, tags,
|
|
163
|
+
1 - (embedding <=> $1) AS similarity
|
|
164
|
+
FROM memories
|
|
165
|
+
${whereClause}
|
|
166
|
+
ORDER BY embedding <=> $1
|
|
167
|
+
LIMIT $${params.length + 1}`,
|
|
168
|
+
[...params, limit]
|
|
169
|
+
);
|
|
170
|
+
await client.query("COMMIT");
|
|
171
|
+
return result.rows;
|
|
172
|
+
} finally {
|
|
173
|
+
client.release();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function closePool() {
|
|
177
|
+
if (pool) {
|
|
178
|
+
await pool.end();
|
|
179
|
+
pool = null;
|
|
180
|
+
migrationsRun = false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
hashMemoryContent,
|
|
186
|
+
getPool,
|
|
187
|
+
runMigrations,
|
|
188
|
+
saveMemory,
|
|
189
|
+
upsertMemory,
|
|
190
|
+
listMemories,
|
|
191
|
+
getMemory,
|
|
192
|
+
deleteMemory,
|
|
193
|
+
updateMemory,
|
|
194
|
+
searchMemories,
|
|
195
|
+
closePool
|
|
196
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
Config
|
|
4
|
+
} from "./chunk-KSLFLWB4.js";
|
|
5
|
+
|
|
6
|
+
// src/embedding.ts
|
|
7
|
+
async function embed(text) {
|
|
8
|
+
let response;
|
|
9
|
+
try {
|
|
10
|
+
response = await fetch(`${Config.ollamaUrl}/api/embeddings`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: { "Content-Type": "application/json" },
|
|
13
|
+
body: JSON.stringify({ model: Config.ollamaModel, prompt: text })
|
|
14
|
+
});
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Cannot reach Ollama at ${Config.ollamaUrl}. Run: ollama serve`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const body = await response.text();
|
|
22
|
+
throw new Error(`Ollama embedding failed (${response.status}): ${body}`);
|
|
23
|
+
}
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
return data.embedding;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
embed
|
|
30
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config.ts
|
|
4
|
+
import { config } from "dotenv";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
var localEnv = join(process.cwd(), ".memory-core.env");
|
|
8
|
+
config({ path: existsSync(localEnv) ? localEnv : join(process.cwd(), ".env") });
|
|
9
|
+
var Config = {
|
|
10
|
+
get databaseUrl() {
|
|
11
|
+
return process.env.DATABASE_URL ?? "";
|
|
12
|
+
},
|
|
13
|
+
get ollamaUrl() {
|
|
14
|
+
return process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
15
|
+
},
|
|
16
|
+
get ollamaModel() {
|
|
17
|
+
return process.env.OLLAMA_MODEL ?? "nomic-embed-text";
|
|
18
|
+
},
|
|
19
|
+
get chatModel() {
|
|
20
|
+
return process.env.CHAT_MODEL ?? process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
|
|
21
|
+
},
|
|
22
|
+
get chatProvider() {
|
|
23
|
+
return process.env.CHAT_PROVIDER ?? "ollama";
|
|
24
|
+
},
|
|
25
|
+
get chatApiKey() {
|
|
26
|
+
return process.env.CHAT_API_KEY ?? "";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
Config
|
|
32
|
+
};
|