@shahmilsaari/memory-core 0.2.4 → 0.2.6
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/dist/cli.js +98 -38
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20,25 +20,26 @@ var __filename = fileURLToPath(import.meta.url);
|
|
|
20
20
|
var __dirname = dirname(__filename);
|
|
21
21
|
var PKG_ROOT = join(__dirname, "..");
|
|
22
22
|
var OUTPUT_FILES = [
|
|
23
|
-
{ template: "CLAUDE.md.hbs", path: "CLAUDE.md" },
|
|
24
|
-
{ template: "copilot-instructions.md.hbs", path: ".github/copilot-instructions.md" },
|
|
25
|
-
{ template: "cursorrules.hbs", path: ".cursorrules" },
|
|
26
|
-
{ template: "cursor-rule.mdc.hbs", path: ".cursor/rules/memory-core.mdc" },
|
|
27
|
-
{ template: "windsurfrules.hbs", path: ".windsurfrules" },
|
|
28
|
-
{ template: "clinerules.hbs", path: ".clinerules" },
|
|
29
|
-
{ template: "roo-rule.md.hbs", path: ".roo/rules/memory-core.md" },
|
|
30
|
-
{ template: "aider.conf.yml.hbs", path: ".aider.conf.yml" },
|
|
31
|
-
{ template: "continue-config.json.hbs", path: ".continue/config.json", skipIfExists: true },
|
|
32
|
-
{ template: "DEVIN.md.hbs", path: "DEVIN.md" },
|
|
33
|
-
{ template: "amazonq-guidelines.md.hbs", path: ".amazonq/dev/guidelines.md" },
|
|
34
|
-
{ template: "gemini-styleguide.md.hbs", path: ".gemini/styleguide.md" },
|
|
35
|
-
{ template: "zed-settings.json.hbs", path: ".zed/settings.json", skipIfExists: true },
|
|
36
|
-
{ template: "jetbrains-ai.md.hbs", path: ".idea/ai-instructions.md" },
|
|
37
|
-
{ template: "AGENTS.md.hbs", path: "AGENTS.md" },
|
|
38
|
-
{ template: "AI_RULES.md.hbs", path: "AI_RULES.md" },
|
|
39
|
-
{ template: "ARCHITECTURE.md.hbs", path: "ARCHITECTURE.md" },
|
|
40
|
-
{ template: "PROJECT_MEMORY.md.hbs", path: "PROJECT_MEMORY.md" }
|
|
23
|
+
{ template: "CLAUDE.md.hbs", path: "CLAUDE.md", agent: "Claude Code" },
|
|
24
|
+
{ template: "copilot-instructions.md.hbs", path: ".github/copilot-instructions.md", agent: "GitHub Copilot" },
|
|
25
|
+
{ template: "cursorrules.hbs", path: ".cursorrules", agent: "Cursor" },
|
|
26
|
+
{ template: "cursor-rule.mdc.hbs", path: ".cursor/rules/memory-core.mdc", agent: "Cursor" },
|
|
27
|
+
{ template: "windsurfrules.hbs", path: ".windsurfrules", agent: "Windsurf" },
|
|
28
|
+
{ template: "clinerules.hbs", path: ".clinerules", agent: "Cline" },
|
|
29
|
+
{ template: "roo-rule.md.hbs", path: ".roo/rules/memory-core.md", agent: "Roo Code" },
|
|
30
|
+
{ template: "aider.conf.yml.hbs", path: ".aider.conf.yml", agent: "Aider" },
|
|
31
|
+
{ template: "continue-config.json.hbs", path: ".continue/config.json", agent: "Continue.dev", skipIfExists: true },
|
|
32
|
+
{ template: "DEVIN.md.hbs", path: "DEVIN.md", agent: "Devin" },
|
|
33
|
+
{ template: "amazonq-guidelines.md.hbs", path: ".amazonq/dev/guidelines.md", agent: "Amazon Q" },
|
|
34
|
+
{ template: "gemini-styleguide.md.hbs", path: ".gemini/styleguide.md", agent: "Gemini Code Assist" },
|
|
35
|
+
{ template: "zed-settings.json.hbs", path: ".zed/settings.json", agent: "Zed AI", skipIfExists: true },
|
|
36
|
+
{ template: "jetbrains-ai.md.hbs", path: ".idea/ai-instructions.md", agent: "JetBrains AI" },
|
|
37
|
+
{ template: "AGENTS.md.hbs", path: "AGENTS.md", agent: "OpenHands" },
|
|
38
|
+
{ template: "AI_RULES.md.hbs", path: "AI_RULES.md", agent: "Shared" },
|
|
39
|
+
{ template: "ARCHITECTURE.md.hbs", path: "ARCHITECTURE.md", agent: "Shared" },
|
|
40
|
+
{ template: "PROJECT_MEMORY.md.hbs", path: "PROJECT_MEMORY.md", agent: "Shared" }
|
|
41
41
|
];
|
|
42
|
+
var AGENT_NAMES = [...new Set(OUTPUT_FILES.map((f) => f.agent))];
|
|
42
43
|
Handlebars.registerHelper(
|
|
43
44
|
"join",
|
|
44
45
|
(arr, sep) => Array.isArray(arr) ? arr.join(sep) : ""
|
|
@@ -127,23 +128,34 @@ function renderTemplate(templateName, data) {
|
|
|
127
128
|
function writeFile(filePath, content) {
|
|
128
129
|
const dir = dirname(filePath);
|
|
129
130
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
131
|
+
if (existsSync(filePath)) {
|
|
132
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
133
|
+
if (existing === content) return "skipped";
|
|
134
|
+
}
|
|
130
135
|
writeFileSync(filePath, content, "utf-8");
|
|
136
|
+
return "written";
|
|
131
137
|
}
|
|
132
|
-
async function generate(options, cwd = process.cwd()) {
|
|
138
|
+
async function generate(options, cwd = process.cwd(), onlyAgents) {
|
|
133
139
|
const data = buildTemplateData(options);
|
|
134
140
|
const written = [];
|
|
135
|
-
|
|
141
|
+
const skipped = [];
|
|
142
|
+
const files = onlyAgents ? OUTPUT_FILES.filter((f) => onlyAgents.includes(f.agent)) : OUTPUT_FILES;
|
|
143
|
+
for (const output of files) {
|
|
136
144
|
const targetPath = join(cwd, output.path);
|
|
137
|
-
if (output.skipIfExists && existsSync(targetPath))
|
|
145
|
+
if (output.skipIfExists && existsSync(targetPath)) {
|
|
146
|
+
skipped.push(output.path);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
138
149
|
try {
|
|
139
150
|
const content = renderTemplate(output.template, data);
|
|
140
|
-
writeFile(targetPath, content);
|
|
141
|
-
written.push(output.path);
|
|
151
|
+
const result = writeFile(targetPath, content);
|
|
152
|
+
if (result === "written") written.push(output.path);
|
|
153
|
+
else skipped.push(output.path);
|
|
142
154
|
} catch (err) {
|
|
143
155
|
if (!(err instanceof Error && err.message.includes("Template not found"))) throw err;
|
|
144
156
|
}
|
|
145
157
|
}
|
|
146
|
-
return written;
|
|
158
|
+
return { written, skipped };
|
|
147
159
|
}
|
|
148
160
|
|
|
149
161
|
// src/seeds.ts
|
|
@@ -699,6 +711,20 @@ import { execSync } from "child_process";
|
|
|
699
711
|
import { writeFileSync as writeFileSync2, existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, chmodSync } from "fs";
|
|
700
712
|
import { join as join4 } from "path";
|
|
701
713
|
import chalk from "chalk";
|
|
714
|
+
async function resolveModel(ollamaUrl, chatModel) {
|
|
715
|
+
try {
|
|
716
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(3e3) });
|
|
717
|
+
if (!res.ok) return chatModel;
|
|
718
|
+
const data = await res.json();
|
|
719
|
+
const models = data.models ?? [];
|
|
720
|
+
const exact = models.find((m) => m.name === chatModel);
|
|
721
|
+
if (exact) return exact.name;
|
|
722
|
+
const prefixed = models.find((m) => m.name.startsWith(`${chatModel}:`));
|
|
723
|
+
if (prefixed) return prefixed.name;
|
|
724
|
+
} catch {
|
|
725
|
+
}
|
|
726
|
+
return chatModel;
|
|
727
|
+
}
|
|
702
728
|
var reasonMap = new Map(
|
|
703
729
|
seeds.filter((s) => s.reason).map((s) => [s.content, s.reason])
|
|
704
730
|
);
|
|
@@ -794,7 +820,7 @@ async function checkStaged(options = {}) {
|
|
|
794
820
|
}
|
|
795
821
|
if (rules.length === 0) return;
|
|
796
822
|
const ollamaUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
797
|
-
const chatModel = process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
|
|
823
|
+
const chatModel = await resolveModel(ollamaUrl, process.env.OLLAMA_CHAT_MODEL ?? "llama3.2");
|
|
798
824
|
const MAX_DIFF = 8e3;
|
|
799
825
|
const truncated = diff.length > MAX_DIFF;
|
|
800
826
|
const diffToSend = truncated ? diff.slice(0, MAX_DIFF) + "\n\n[diff truncated]" : diff;
|
|
@@ -918,6 +944,20 @@ import { execSync as execSync2 } from "child_process";
|
|
|
918
944
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
919
945
|
import { join as join5, relative } from "path";
|
|
920
946
|
import chalk2 from "chalk";
|
|
947
|
+
async function resolveModel2(ollamaUrl, chatModel) {
|
|
948
|
+
try {
|
|
949
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(3e3) });
|
|
950
|
+
if (!res.ok) return chatModel;
|
|
951
|
+
const data = await res.json();
|
|
952
|
+
const models = data.models ?? [];
|
|
953
|
+
const exact = models.find((m) => m.name === chatModel);
|
|
954
|
+
if (exact) return exact.name;
|
|
955
|
+
const prefixed = models.find((m) => m.name.startsWith(`${chatModel}:`));
|
|
956
|
+
if (prefixed) return prefixed.name;
|
|
957
|
+
} catch {
|
|
958
|
+
}
|
|
959
|
+
return chatModel;
|
|
960
|
+
}
|
|
921
961
|
function getFileLines(filePath) {
|
|
922
962
|
try {
|
|
923
963
|
return readFileSync4(filePath, "utf-8").split("\n");
|
|
@@ -993,7 +1033,7 @@ async function checkFile(filePath, cwd, config2, verbose) {
|
|
|
993
1033
|
const { rules, avoids } = getProfileRules(config2);
|
|
994
1034
|
if (rules.length === 0) return;
|
|
995
1035
|
const ollamaUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
996
|
-
const chatModel = process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
|
|
1036
|
+
const chatModel = await resolveModel2(ollamaUrl, process.env.OLLAMA_CHAT_MODEL ?? "llama3.2");
|
|
997
1037
|
const MAX_DIFF = 6e3;
|
|
998
1038
|
const truncated = diff.length > MAX_DIFF;
|
|
999
1039
|
const diffToSend = truncated ? diff.slice(0, MAX_DIFF) + "\n\n[diff truncated]" : diff;
|
|
@@ -1092,7 +1132,7 @@ ${diffToSend}` }
|
|
|
1092
1132
|
}
|
|
1093
1133
|
}
|
|
1094
1134
|
}
|
|
1095
|
-
function startWatch(options = {}) {
|
|
1135
|
+
async function startWatch(options = {}) {
|
|
1096
1136
|
const cwd = process.cwd();
|
|
1097
1137
|
const config2 = loadConfig(cwd);
|
|
1098
1138
|
if (!config2) {
|
|
@@ -1106,7 +1146,7 @@ function startWatch(options = {}) {
|
|
|
1106
1146
|
}
|
|
1107
1147
|
const watchPath = options.path ?? cwd;
|
|
1108
1148
|
const ollamaUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
1109
|
-
const chatModel = process.env.OLLAMA_CHAT_MODEL ?? "llama3.2";
|
|
1149
|
+
const chatModel = await resolveModel2(ollamaUrl, process.env.OLLAMA_CHAT_MODEL ?? "llama3.2");
|
|
1110
1150
|
console.log(chalk2.cyan("\n archmind watch \u2014 real-time rule enforcement\n"));
|
|
1111
1151
|
console.log(chalk2.dim(` watching: ${watchPath}`));
|
|
1112
1152
|
console.log(chalk2.dim(` model: ${chatModel}`));
|
|
@@ -1304,10 +1344,12 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1304
1344
|
try {
|
|
1305
1345
|
const res = await fetch(`${ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
1306
1346
|
const data = await res.json();
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
);
|
|
1310
|
-
|
|
1347
|
+
const models = data.models ?? [];
|
|
1348
|
+
const exact = models.find((m) => m.name === chatModel);
|
|
1349
|
+
const prefixed = models.find((m) => m.name.startsWith(`${chatModel}:`));
|
|
1350
|
+
const match = exact ?? prefixed;
|
|
1351
|
+
if (match) {
|
|
1352
|
+
chatModel = match.name;
|
|
1311
1353
|
modelSpinner.succeed(chalk3.green(`${chatModel} is installed and ready`));
|
|
1312
1354
|
break;
|
|
1313
1355
|
} else {
|
|
@@ -1441,7 +1483,7 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1441
1483
|
process.cwd()
|
|
1442
1484
|
);
|
|
1443
1485
|
writeProjectConfig(config2);
|
|
1444
|
-
spinner.succeed(`Generated ${written.length} files`);
|
|
1486
|
+
spinner.succeed(`Generated ${written.written.length} files`);
|
|
1445
1487
|
if (enableHook) {
|
|
1446
1488
|
installHook();
|
|
1447
1489
|
}
|
|
@@ -1450,15 +1492,25 @@ program.command("init").description("Initialize memory-core in the current proje
|
|
|
1450
1492
|
process.env.OLLAMA_URL ?? "http://localhost:11434",
|
|
1451
1493
|
process.env.OLLAMA_CHAT_MODEL ?? "llama3.2"
|
|
1452
1494
|
);
|
|
1453
|
-
printBanner(config2.projectName, written.length, status);
|
|
1495
|
+
printBanner(config2.projectName, written.written.length, status);
|
|
1454
1496
|
await closePool();
|
|
1455
1497
|
});
|
|
1456
|
-
program.command("sync").description("Re-pull memories and regenerate
|
|
1498
|
+
program.command("sync").description("Re-pull memories and regenerate AI agent files").action(async () => {
|
|
1457
1499
|
const config2 = readProjectConfig();
|
|
1458
1500
|
if (!config2) {
|
|
1459
1501
|
console.error(chalk3.red("No .memory-core.json found. Run: memory-core init"));
|
|
1460
1502
|
process.exit(1);
|
|
1461
1503
|
}
|
|
1504
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
1505
|
+
const selectedAgents = await checkbox({
|
|
1506
|
+
message: "Which agents do you want to sync?",
|
|
1507
|
+
choices: AGENT_NAMES.map((name) => ({ name, value: name, checked: true })),
|
|
1508
|
+
instructions: " (Space to toggle, A to select all, Enter to confirm)"
|
|
1509
|
+
});
|
|
1510
|
+
if (selectedAgents.length === 0) {
|
|
1511
|
+
console.log(chalk3.yellow(" No agents selected \u2014 nothing to sync."));
|
|
1512
|
+
process.exit(0);
|
|
1513
|
+
}
|
|
1462
1514
|
const spinner = ora("Syncing memories\u2026").start();
|
|
1463
1515
|
let memories = [];
|
|
1464
1516
|
try {
|
|
@@ -1468,7 +1520,7 @@ program.command("sync").description("Re-pull memories and regenerate all AI agen
|
|
|
1468
1520
|
} catch (err) {
|
|
1469
1521
|
spinner.warn(`Could not retrieve memories: ${err.message}`);
|
|
1470
1522
|
}
|
|
1471
|
-
const
|
|
1523
|
+
const result = await generate(
|
|
1472
1524
|
{
|
|
1473
1525
|
projectName: config2.projectName,
|
|
1474
1526
|
projectType: config2.projectType,
|
|
@@ -1478,9 +1530,17 @@ program.command("sync").description("Re-pull memories and regenerate all AI agen
|
|
|
1478
1530
|
memories,
|
|
1479
1531
|
caveman: config2.caveman
|
|
1480
1532
|
},
|
|
1481
|
-
process.cwd()
|
|
1533
|
+
process.cwd(),
|
|
1534
|
+
selectedAgents
|
|
1482
1535
|
);
|
|
1483
|
-
|
|
1536
|
+
const updatedCount = result.written.length;
|
|
1537
|
+
const skippedCount = result.skipped.length;
|
|
1538
|
+
spinner.succeed(
|
|
1539
|
+
`Synced \u2014 ${chalk3.green(`${updatedCount} updated`)}, ${chalk3.dim(`${skippedCount} already up to date`)}`
|
|
1540
|
+
);
|
|
1541
|
+
if (result.written.length > 0) {
|
|
1542
|
+
result.written.forEach((f) => console.log(chalk3.gray(` \u2713 ${f}`)));
|
|
1543
|
+
}
|
|
1484
1544
|
await closePool();
|
|
1485
1545
|
});
|
|
1486
1546
|
program.command("remember <text>").description("Save a new memory to the central database").option("-t, --type <type>", "Memory type (decision|rule|pattern|note)", "decision").option("-s, --scope <scope>", "Scope (global|project)", "project").option("--tags <tags>", "Comma-separated tags").option("-r, --reason <reason>", "Why this rule exists \u2014 helps agents understand intent and debug violations").action(async (text, opts) => {
|