@shahmilsaari/memory-core 0.2.0 → 0.2.4
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 +31 -25
- package/dist/cli.js +129 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,11 +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
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4. When you commit
|
|
25
|
-
5. Watch mode catches violations as you type, not just at commit time
|
|
21
|
+
1. You run `init` once — it verifies your PostgreSQL and Ollama connections, picks your model, and generates config files for every AI agent
|
|
22
|
+
2. Those agents read the files and follow your rules — automatically
|
|
23
|
+
3. Watch mode catches violations as you type, not just at commit time
|
|
24
|
+
4. When you commit, Claude Code automatically checks your code before the commit goes through
|
|
26
25
|
|
|
27
26
|
---
|
|
28
27
|
|
|
@@ -106,10 +105,10 @@ ollama serve &
|
|
|
106
105
|
|
|
107
106
|
**Windows** — [download from ollama.com](https://ollama.com/download)
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
Pull the embedding model. The code-checking model is chosen during `init`:
|
|
110
109
|
```bash
|
|
111
|
-
ollama pull nomic-embed-text #
|
|
112
|
-
ollama pull llama3.2 #
|
|
110
|
+
ollama pull nomic-embed-text # required for search
|
|
111
|
+
ollama pull llama3.2 # or whichever model you plan to use
|
|
113
112
|
```
|
|
114
113
|
|
|
115
114
|
---
|
|
@@ -153,14 +152,11 @@ CREATE INDEX IF NOT EXISTS memories_scope_idx ON memories (scope);
|
|
|
153
152
|
# 1. Go to your project
|
|
154
153
|
cd my-api
|
|
155
154
|
|
|
156
|
-
# 2. Initialize —
|
|
155
|
+
# 2. Initialize — verifies connections, picks your model, generates all config files
|
|
157
156
|
npx @shahmilsaari/memory-core init
|
|
158
157
|
|
|
159
158
|
# 3. Load 281 predefined best-practice rules
|
|
160
159
|
npx @shahmilsaari/memory-core seed
|
|
161
|
-
|
|
162
|
-
# 4. Install the pre-commit hook (optional but recommended)
|
|
163
|
-
npx @shahmilsaari/memory-core hook install
|
|
164
160
|
```
|
|
165
161
|
|
|
166
162
|
That's it. Every AI agent in your project now has your rules.
|
|
@@ -175,15 +171,18 @@ That's it. Every AI agent in your project now has your rules.
|
|
|
175
171
|
npx @shahmilsaari/memory-core init
|
|
176
172
|
```
|
|
177
173
|
|
|
178
|
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
174
|
+
Walks you through:
|
|
175
|
+
- PostgreSQL connection URL — **tested live, retries until connected**
|
|
176
|
+
- Ollama URL — **tested live, retries until reachable**
|
|
177
|
+
- Code-checking model — **verified to be installed in your Ollama**
|
|
178
|
+
- Project name, type, architecture, language
|
|
179
|
+
- Whether to enable the pre-commit hook (asked during setup — no separate step needed)
|
|
183
180
|
- Whether to enable caveman mode (optional token saver)
|
|
184
181
|
|
|
185
182
|
Generates config files for every supported AI agent and saves your choices to `.memory-core.json`.
|
|
186
183
|
|
|
184
|
+
At the end, the banner shows live ✓/✗ status for PostgreSQL and Ollama so you know everything is working.
|
|
185
|
+
|
|
187
186
|
---
|
|
188
187
|
|
|
189
188
|
### `remember` — Save a decision
|
|
@@ -253,7 +252,9 @@ npx @shahmilsaari/memory-core seed --force # re-seed existin
|
|
|
253
252
|
npx @shahmilsaari/memory-core hook install
|
|
254
253
|
```
|
|
255
254
|
|
|
256
|
-
Installs a git pre-commit hook. Every time you run `git commit`, your
|
|
255
|
+
Installs a git pre-commit hook in the current project. Every time you run `git commit`, your code is checked against your architecture rules before the commit goes through.
|
|
256
|
+
|
|
257
|
+
> **Note:** If you use Claude Code, the hook is offered automatically during `init` — no separate step needed.
|
|
257
258
|
|
|
258
259
|
When a violation is found, the commit is blocked and you see exactly what's wrong and how to fix it:
|
|
259
260
|
|
|
@@ -396,7 +397,7 @@ Pick the one that matches how your project is structured.
|
|
|
396
397
|
| React | Functional components, hooks, React Query, Zustand |
|
|
397
398
|
| Vue 3 | Composition API, Pinia, composables |
|
|
398
399
|
| Angular | Standalone components, signals, OnPush strategy |
|
|
399
|
-
| Svelte | Svelte 5 runes, SvelteKit load functions |
|
|
400
|
+
| Svelte | Svelte 5 runes, SvelteKit load functions, snippets |
|
|
400
401
|
| Nuxt 3 | Fullstack Vue with SSR |
|
|
401
402
|
| React Native | Mobile apps |
|
|
402
403
|
|
|
@@ -421,9 +422,8 @@ Cuts AI response length by 65–75% by removing filler words. Opt in during `ini
|
|
|
421
422
|
```bash
|
|
422
423
|
# Starting a new project
|
|
423
424
|
cd my-api
|
|
424
|
-
npx @shahmilsaari/memory-core init
|
|
425
|
-
npx @shahmilsaari/memory-core seed
|
|
426
|
-
npx @shahmilsaari/memory-core hook install
|
|
425
|
+
npx @shahmilsaari/memory-core init # verifies connections, picks model, installs hook
|
|
426
|
+
npx @shahmilsaari/memory-core seed # load 281 best-practice rules
|
|
427
427
|
|
|
428
428
|
# Made an architectural decision? Save it.
|
|
429
429
|
npx @shahmilsaari/memory-core remember "All auth goes through middleware, never in controllers" \
|
|
@@ -435,7 +435,7 @@ npx @shahmilsaari/memory-core sync
|
|
|
435
435
|
# Not sure how something was decided? Search.
|
|
436
436
|
npx @shahmilsaari/memory-core search "caching strategy"
|
|
437
437
|
|
|
438
|
-
# Commit code →
|
|
438
|
+
# Commit code → Claude Code checks it automatically before committing
|
|
439
439
|
git commit -m "add user endpoint"
|
|
440
440
|
```
|
|
441
441
|
|
|
@@ -449,8 +449,8 @@ memory-core creates `.memory-core.env` automatically during `init`. You can also
|
|
|
449
449
|
|---|---|---|---|
|
|
450
450
|
| `DATABASE_URL` | Yes | — | PostgreSQL connection string |
|
|
451
451
|
| `OLLAMA_URL` | No | `http://localhost:11434` | Where Ollama is running |
|
|
452
|
-
| `OLLAMA_MODEL` | No | `nomic-embed-text` | Model used for search |
|
|
453
|
-
| `OLLAMA_CHAT_MODEL` | No | `llama3.2` | Model used for code checking |
|
|
452
|
+
| `OLLAMA_MODEL` | No | `nomic-embed-text` | Model used for search (embeddings) |
|
|
453
|
+
| `OLLAMA_CHAT_MODEL` | No | `llama3.2` | Model used for code checking — chosen during `init` |
|
|
454
454
|
|
|
455
455
|
---
|
|
456
456
|
|
|
@@ -483,6 +483,10 @@ It won't — the hook only checks source code files: `.ts .tsx .js .jsx .py .php
|
|
|
483
483
|
| | Feature |
|
|
484
484
|
|---|---|
|
|
485
485
|
| ✓ | Watch mode — real-time violation alerts on save |
|
|
486
|
+
| ✓ | Model picker — choose your Ollama model during init |
|
|
487
|
+
| ✓ | Connection validation — PostgreSQL and Ollama verified during setup |
|
|
488
|
+
| ✓ | Svelte 5 / SvelteKit profile and 37 rules |
|
|
489
|
+
| ✓ | NestJS profile and 39 rules |
|
|
486
490
|
| | CI/CD — fail PRs that violate your rules |
|
|
487
491
|
| | Violation memory — auto-save what went wrong as a new rule |
|
|
488
492
|
| | Rule analytics — see which rules get broken most |
|
|
@@ -494,3 +498,5 @@ It won't — the hook only checks source code files: `.ts .tsx .js .jsx .py .php
|
|
|
494
498
|
## License
|
|
495
499
|
|
|
496
500
|
MIT
|
|
501
|
+
|
|
502
|
+
*Built by [Shahmil Saari](https://github.com/shahmilsaari)*
|
package/dist/cli.js
CHANGED
|
@@ -530,9 +530,18 @@ import { join as join2 } from "path";
|
|
|
530
530
|
var localEnv = join2(process.cwd(), ".memory-core.env");
|
|
531
531
|
config({ path: existsSync2(localEnv) ? localEnv : join2(process.cwd(), ".env") });
|
|
532
532
|
var Config = {
|
|
533
|
-
databaseUrl
|
|
534
|
-
|
|
535
|
-
|
|
533
|
+
get databaseUrl() {
|
|
534
|
+
return process.env.DATABASE_URL ?? "";
|
|
535
|
+
},
|
|
536
|
+
get ollamaUrl() {
|
|
537
|
+
return process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
538
|
+
},
|
|
539
|
+
get ollamaModel() {
|
|
540
|
+
return process.env.OLLAMA_MODEL ?? "nomic-embed-text";
|
|
541
|
+
},
|
|
542
|
+
get chatModel() {
|
|
543
|
+
return process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
|
|
544
|
+
}
|
|
536
545
|
};
|
|
537
546
|
|
|
538
547
|
// src/embedding.ts
|
|
@@ -1157,7 +1166,9 @@ function startWatch(options = {}) {
|
|
|
1157
1166
|
}
|
|
1158
1167
|
|
|
1159
1168
|
// src/cli.ts
|
|
1160
|
-
function printBanner(projectName, agentCount) {
|
|
1169
|
+
function printBanner(projectName, agentCount, status) {
|
|
1170
|
+
const pg2 = status ? status.postgresOk ? chalk3.green(" \u2713 PostgreSQL ") + chalk3.bold("connected") : chalk3.red(" \u2717 PostgreSQL ") + chalk3.bold("not connected \u2014 check DATABASE_URL") : chalk3.green(" \u2713 Memory ") + chalk3.bold("PostgreSQL + pgvector ready");
|
|
1171
|
+
const ol = status ? status.ollamaOk ? chalk3.green(" \u2713 Ollama ") + chalk3.bold(`connected (model: ${status.chatModel})`) : chalk3.red(" \u2717 Ollama ") + chalk3.bold("not running \u2014 start with: ollama serve") : null;
|
|
1161
1172
|
const lines = [
|
|
1162
1173
|
"",
|
|
1163
1174
|
chalk3.cyan(" \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 "),
|
|
@@ -1171,7 +1182,8 @@ function printBanner(projectName, agentCount) {
|
|
|
1171
1182
|
"",
|
|
1172
1183
|
chalk3.green(` \u2713 Project `) + chalk3.bold(projectName),
|
|
1173
1184
|
chalk3.green(` \u2713 Agents `) + chalk3.bold(`${agentCount} AI agents configured`),
|
|
1174
|
-
|
|
1185
|
+
pg2,
|
|
1186
|
+
...ol ? [ol] : [],
|
|
1175
1187
|
"",
|
|
1176
1188
|
chalk3.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1177
1189
|
chalk3.dim(" Built by ") + chalk3.bold.white("Shahmil Saari"),
|
|
@@ -1186,6 +1198,35 @@ function printBanner(projectName, agentCount) {
|
|
|
1186
1198
|
];
|
|
1187
1199
|
lines.forEach((l) => console.log(l));
|
|
1188
1200
|
}
|
|
1201
|
+
async function checkConnections(dbUrl, ollamaUrl, chatModel) {
|
|
1202
|
+
const spinner = ora("Checking connections\u2026").start();
|
|
1203
|
+
let postgresOk = false;
|
|
1204
|
+
let ollamaOk = false;
|
|
1205
|
+
try {
|
|
1206
|
+
const { Pool: Pool2 } = (await import("pg")).default;
|
|
1207
|
+
const testPool = new Pool2({ connectionString: dbUrl, connectionTimeoutMillis: 5e3 });
|
|
1208
|
+
await testPool.query("SELECT 1");
|
|
1209
|
+
await testPool.end();
|
|
1210
|
+
postgresOk = true;
|
|
1211
|
+
} catch {
|
|
1212
|
+
postgresOk = false;
|
|
1213
|
+
}
|
|
1214
|
+
try {
|
|
1215
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
1216
|
+
ollamaOk = res.ok;
|
|
1217
|
+
} catch {
|
|
1218
|
+
ollamaOk = false;
|
|
1219
|
+
}
|
|
1220
|
+
spinner.stop();
|
|
1221
|
+
console.log(
|
|
1222
|
+
postgresOk ? chalk3.green(" \u2713 PostgreSQL") + chalk3.dim(" \u2014 connected") : chalk3.red(" \u2717 PostgreSQL") + chalk3.dim(" \u2014 cannot connect. Check DATABASE_URL and that PostgreSQL is running.")
|
|
1223
|
+
);
|
|
1224
|
+
console.log(
|
|
1225
|
+
ollamaOk ? chalk3.green(" \u2713 Ollama ") + chalk3.dim(` \u2014 connected (${chatModel})`) : chalk3.red(" \u2717 Ollama ") + chalk3.dim(" \u2014 not reachable. Run: ollama serve")
|
|
1226
|
+
);
|
|
1227
|
+
console.log();
|
|
1228
|
+
return { postgresOk, ollamaOk, chatModel };
|
|
1229
|
+
}
|
|
1189
1230
|
var { version } = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf-8"));
|
|
1190
1231
|
var CONFIG_FILE = ".memory-core.json";
|
|
1191
1232
|
function readProjectConfig() {
|
|
@@ -1208,27 +1249,77 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1208
1249
|
const envPath = join6(process.cwd(), ".memory-core.env");
|
|
1209
1250
|
const hasEnv = existsSync6(envPath) || existsSync6(join6(process.cwd(), ".env")) || !!process.env.DATABASE_URL;
|
|
1210
1251
|
if (!hasEnv) {
|
|
1211
|
-
console.log(chalk3.dim(" No .memory-core.env found \u2014 let's set up your
|
|
1252
|
+
console.log(chalk3.dim(" No .memory-core.env found \u2014 let's set up your connection.\n"));
|
|
1212
1253
|
const dbUser = process.env.USER ?? process.env.USERNAME ?? "postgres";
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1254
|
+
let dbUrl = "";
|
|
1255
|
+
while (true) {
|
|
1256
|
+
dbUrl = await input({
|
|
1257
|
+
message: "PostgreSQL connection URL?",
|
|
1258
|
+
default: dbUrl || `postgresql://${dbUser}@localhost:5432/memory_core`
|
|
1259
|
+
});
|
|
1260
|
+
const pgSpinner = ora(" Testing PostgreSQL connection\u2026").start();
|
|
1261
|
+
try {
|
|
1262
|
+
const { Pool: Pool2 } = (await import("pg")).default;
|
|
1263
|
+
const testPool = new Pool2({ connectionString: dbUrl, connectionTimeoutMillis: 5e3 });
|
|
1264
|
+
await testPool.query("SELECT 1");
|
|
1265
|
+
await testPool.end();
|
|
1266
|
+
pgSpinner.succeed(chalk3.green("PostgreSQL connected"));
|
|
1267
|
+
break;
|
|
1268
|
+
} catch (err) {
|
|
1269
|
+
pgSpinner.fail(chalk3.red(`Cannot connect: ${err.message}`));
|
|
1270
|
+
console.log(chalk3.yellow(" Please check that PostgreSQL is running and the URL is correct.\n"));
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
let ollamaUrl = "";
|
|
1274
|
+
while (true) {
|
|
1275
|
+
ollamaUrl = await input({
|
|
1276
|
+
message: "Ollama URL?",
|
|
1277
|
+
default: ollamaUrl || "http://localhost:11434"
|
|
1278
|
+
});
|
|
1279
|
+
const ollamaSpinner = ora(" Testing Ollama connection\u2026").start();
|
|
1280
|
+
try {
|
|
1281
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
1282
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1283
|
+
ollamaSpinner.succeed(chalk3.green("Ollama connected"));
|
|
1284
|
+
break;
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
ollamaSpinner.fail(chalk3.red(`Cannot reach Ollama: ${err.message}`));
|
|
1287
|
+
console.log(chalk3.yellow(" Make sure Ollama is running: ollama serve\n"));
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
let chatModel = "";
|
|
1291
|
+
while (true) {
|
|
1292
|
+
const chatModelChoice = await select({
|
|
1293
|
+
message: "Which Ollama model for code checking?",
|
|
1294
|
+
choices: [
|
|
1295
|
+
{ name: "llama3.2 (fast, 2GB, recommended for most machines)", value: "llama3.2" },
|
|
1296
|
+
{ name: "qwen2.5-coder (better code understanding, 4.7GB)", value: "qwen2.5-coder" },
|
|
1297
|
+
{ name: "mistral (balanced, 4.1GB)", value: "mistral" },
|
|
1298
|
+
{ name: "codellama (code-focused, 3.8GB)", value: "codellama" },
|
|
1299
|
+
{ name: "Other (enter manually)", value: "__custom__" }
|
|
1300
|
+
]
|
|
1301
|
+
});
|
|
1302
|
+
chatModel = chatModelChoice === "__custom__" ? await input({ message: "Model name?", default: "llama3.2" }) : chatModelChoice;
|
|
1303
|
+
const modelSpinner = ora(` Checking if ${chatModel} is installed\u2026`).start();
|
|
1304
|
+
try {
|
|
1305
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
1306
|
+
const data = await res.json();
|
|
1307
|
+
const installed = (data.models ?? []).some(
|
|
1308
|
+
(m) => m.name === chatModel || m.name.startsWith(`${chatModel}:`)
|
|
1309
|
+
);
|
|
1310
|
+
if (installed) {
|
|
1311
|
+
modelSpinner.succeed(chalk3.green(`${chatModel} is installed and ready`));
|
|
1312
|
+
break;
|
|
1313
|
+
} else {
|
|
1314
|
+
modelSpinner.fail(chalk3.red(`${chatModel} is not installed in your Ollama`));
|
|
1315
|
+
console.log(chalk3.yellow(` Run: ollama pull ${chatModel} \u2014 or pick a different model.
|
|
1316
|
+
`));
|
|
1317
|
+
}
|
|
1318
|
+
} catch {
|
|
1319
|
+
modelSpinner.warn(chalk3.yellow("Could not verify model \u2014 continuing anyway"));
|
|
1320
|
+
break;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1232
1323
|
const envContent = [
|
|
1233
1324
|
`DATABASE_URL=${dbUrl}`,
|
|
1234
1325
|
`OLLAMA_URL=${ollamaUrl}`,
|
|
@@ -1309,6 +1400,10 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1309
1400
|
]
|
|
1310
1401
|
});
|
|
1311
1402
|
}
|
|
1403
|
+
const enableHook = await confirm({
|
|
1404
|
+
message: "Enable pre-commit hook? (blocks commits that violate your rules)",
|
|
1405
|
+
default: true
|
|
1406
|
+
});
|
|
1312
1407
|
const config2 = {
|
|
1313
1408
|
projectName,
|
|
1314
1409
|
projectType,
|
|
@@ -1347,7 +1442,15 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1347
1442
|
);
|
|
1348
1443
|
writeProjectConfig(config2);
|
|
1349
1444
|
spinner.succeed(`Generated ${written.length} files`);
|
|
1350
|
-
|
|
1445
|
+
if (enableHook) {
|
|
1446
|
+
installHook();
|
|
1447
|
+
}
|
|
1448
|
+
const status = await checkConnections(
|
|
1449
|
+
process.env.DATABASE_URL ?? "",
|
|
1450
|
+
process.env.OLLAMA_URL ?? "http://localhost:11434",
|
|
1451
|
+
process.env.OLLAMA_CHAT_MODEL ?? "llama3.2"
|
|
1452
|
+
);
|
|
1453
|
+
printBanner(config2.projectName, written.length, status);
|
|
1351
1454
|
await closePool();
|
|
1352
1455
|
});
|
|
1353
1456
|
program.command("sync").description("Re-pull memories and regenerate all AI agent files").action(async () => {
|