@shahmilsaari/memory-core 1.0.0 → 1.0.3
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 +15 -0
- package/dist/{chunk-J7SPACA3.js → chunk-PDQXIKL7.js} +1847 -457
- package/dist/cli.js +312 -75
- package/dist/dashboard/assets/index-C612Qqha.js +2 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/{dashboard-server-QMNMSEPJ.js → dashboard-server-AUX4BQP6.js} +141 -37
- package/package.json +1 -1
- package/dist/chunk-HAGRPKR3.js +0 -30
- package/dist/chunk-KSLFLWB4.js +0 -32
- package/dist/chunk-WUL7HLAA.js +0 -264
- package/dist/dashboard/assets/index-BuVM8CHT.js +0 -2
- package/dist/db-MF3VKVKH.js +0 -30
- package/dist/embedding-PAYD2JYW.js +0 -8
package/dist/cli.js
CHANGED
|
@@ -4,35 +4,23 @@ import {
|
|
|
4
4
|
OUTPUT_FILES,
|
|
5
5
|
buildContextQuery,
|
|
6
6
|
callChatModel,
|
|
7
|
-
|
|
7
|
+
closePool,
|
|
8
8
|
detectProject,
|
|
9
|
+
findAstDeterministicViolationsForDiff,
|
|
9
10
|
generate,
|
|
10
11
|
getAllowPatterns,
|
|
11
12
|
getChatProviderLabel,
|
|
13
|
+
getDefaultApplicationContainer,
|
|
14
|
+
getPool,
|
|
12
15
|
inferProjectArchitectures,
|
|
13
16
|
listProfiles,
|
|
14
|
-
|
|
17
|
+
migrateGraphSnapshots,
|
|
18
|
+
probeGraphSnapshotStore,
|
|
15
19
|
retrieveContextualMemories,
|
|
16
20
|
retrieveMemorySelection,
|
|
17
|
-
seeds,
|
|
18
|
-
startWatch
|
|
19
|
-
} from "./chunk-J7SPACA3.js";
|
|
20
|
-
import {
|
|
21
|
-
embed
|
|
22
|
-
} from "./chunk-HAGRPKR3.js";
|
|
23
|
-
import {
|
|
24
|
-
closePool,
|
|
25
|
-
deleteMemories,
|
|
26
|
-
deleteMemory,
|
|
27
|
-
getMemory,
|
|
28
|
-
getPool,
|
|
29
|
-
listMemories,
|
|
30
21
|
runMigrations,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
upsertMemory
|
|
34
|
-
} from "./chunk-WUL7HLAA.js";
|
|
35
|
-
import "./chunk-KSLFLWB4.js";
|
|
22
|
+
seeds
|
|
23
|
+
} from "./chunk-PDQXIKL7.js";
|
|
36
24
|
|
|
37
25
|
// src/cli.ts
|
|
38
26
|
import { Command } from "commander";
|
|
@@ -40,7 +28,7 @@ import { input, select, confirm } from "@inquirer/prompts";
|
|
|
40
28
|
import chalk2 from "chalk";
|
|
41
29
|
import ora from "ora";
|
|
42
30
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync, appendFileSync, rmSync } from "fs";
|
|
43
|
-
import { join as join3, dirname } from "path";
|
|
31
|
+
import { join as join3, dirname, resolve } from "path";
|
|
44
32
|
import { homedir } from "os";
|
|
45
33
|
|
|
46
34
|
// src/hook.ts
|
|
@@ -134,10 +122,18 @@ var reasonMap = new Map(
|
|
|
134
122
|
);
|
|
135
123
|
var HOOK_PATH = join2(".git", "hooks", "pre-commit");
|
|
136
124
|
var HOOK_MARKER = "# archmind-memory-core";
|
|
137
|
-
function
|
|
125
|
+
function buildHookBody(advisory) {
|
|
138
126
|
const suffix = advisory ? " || true" : "";
|
|
139
|
-
return
|
|
140
|
-
|
|
127
|
+
return `${HOOK_MARKER}${advisory ? " advisory" : ""}
|
|
128
|
+
if [ "\${MEMORY_CORE_SKIP_HOOK:-}" = "1" ] || [ "\${ARCHMIND_SKIP_HOOK:-}" = "1" ] || [ "\${HUSKY:-}" = "0" ] || [ "\${HUSKY_SKIP_HOOKS:-}" = "1" ]; then
|
|
129
|
+
exit 0
|
|
130
|
+
fi
|
|
131
|
+
if [ -n "\${SKIP:-}" ] && echo ",$SKIP," | grep -qiE ',(memory-core|archmind),'; then
|
|
132
|
+
exit 0
|
|
133
|
+
fi
|
|
134
|
+
if [ -n "\${SKIP_HOOKS:-}" ]; then
|
|
135
|
+
exit 0
|
|
136
|
+
fi
|
|
141
137
|
if command -v memory-core >/dev/null 2>&1; then
|
|
142
138
|
memory-core check --staged${suffix}
|
|
143
139
|
elif [ -f "./node_modules/.bin/memory-core" ]; then
|
|
@@ -149,6 +145,26 @@ else
|
|
|
149
145
|
fi
|
|
150
146
|
`;
|
|
151
147
|
}
|
|
148
|
+
function buildHookScript(advisory) {
|
|
149
|
+
return `#!/bin/sh
|
|
150
|
+
|
|
151
|
+
${buildHookBody(advisory)}`;
|
|
152
|
+
}
|
|
153
|
+
function normalizeHookPreamble(content) {
|
|
154
|
+
const lines = content.split("\n");
|
|
155
|
+
const normalized = [];
|
|
156
|
+
let shebangSeen = false;
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
if (/^\s*#!\/bin\/sh\s*$/.test(line)) {
|
|
159
|
+
if (shebangSeen) continue;
|
|
160
|
+
shebangSeen = true;
|
|
161
|
+
normalized.push("#!/bin/sh");
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
normalized.push(line);
|
|
165
|
+
}
|
|
166
|
+
return normalized.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
167
|
+
}
|
|
152
168
|
function recordViolations(violations, source = "hook") {
|
|
153
169
|
const statsPath = join2(process.cwd(), ".memory-core-stats.json");
|
|
154
170
|
let stats = { rules: {}, files: {} };
|
|
@@ -173,6 +189,7 @@ function recordViolations(violations, source = "hook") {
|
|
|
173
189
|
async function promptToSaveViolations(violations) {
|
|
174
190
|
if (!process.stdin.isTTY || violations.length === 0) return;
|
|
175
191
|
try {
|
|
192
|
+
const app = getDefaultApplicationContainer();
|
|
176
193
|
const { confirm: confirm2, input: input2 } = await import("@inquirer/prompts");
|
|
177
194
|
const save = await confirm2({
|
|
178
195
|
message: "Save a caught violation as a project rule?",
|
|
@@ -185,15 +202,12 @@ async function promptToSaveViolations(violations) {
|
|
|
185
202
|
message: "Why should this rule exist?",
|
|
186
203
|
default: selected.reason ?? selected.issue ?? ""
|
|
187
204
|
});
|
|
188
|
-
|
|
189
|
-
const { upsertMemory: upsertMemory2 } = await import("./db-MF3VKVKH.js");
|
|
190
|
-
await upsertMemory2({
|
|
205
|
+
await app.services.memoryEngine.remember({
|
|
191
206
|
type: "rule",
|
|
192
207
|
scope: "project",
|
|
193
208
|
content: selected.rule,
|
|
194
209
|
reason: reason || void 0,
|
|
195
|
-
tags: ["violation"]
|
|
196
|
-
embedding: await embed2(selected.rule)
|
|
210
|
+
tags: ["violation"]
|
|
197
211
|
});
|
|
198
212
|
console.log(chalk.green(" \u2713 Saved as project rule. Run memory-core sync to propagate it.\n"));
|
|
199
213
|
} catch (err) {
|
|
@@ -203,9 +217,8 @@ async function promptToSaveViolations(violations) {
|
|
|
203
217
|
}
|
|
204
218
|
async function loadIgnorePatterns() {
|
|
205
219
|
try {
|
|
206
|
-
const
|
|
207
|
-
const ignores = await
|
|
208
|
-
await closePool2();
|
|
220
|
+
const app = getDefaultApplicationContainer();
|
|
221
|
+
const ignores = await app.services.memoryEngine.list({ type: "ignore", limit: 1e3 });
|
|
209
222
|
return ignores.map((ignore) => ignore.content);
|
|
210
223
|
} catch {
|
|
211
224
|
return [];
|
|
@@ -418,18 +431,26 @@ function installHook(advisory = true) {
|
|
|
418
431
|
process.exit(1);
|
|
419
432
|
}
|
|
420
433
|
const script = buildHookScript(advisory);
|
|
434
|
+
const body = buildHookBody(advisory).trimEnd();
|
|
421
435
|
if (existsSync2(HOOK_PATH)) {
|
|
422
436
|
const existing = readFileSync2(HOOK_PATH, "utf-8");
|
|
423
437
|
if (existing.includes(HOOK_MARKER)) {
|
|
424
438
|
const markerIndex = existing.indexOf(HOOK_MARKER);
|
|
425
|
-
const
|
|
426
|
-
|
|
439
|
+
const beforeRaw = markerIndex > 0 ? existing.slice(0, markerIndex) : "";
|
|
440
|
+
const normalizedBefore = normalizeHookPreamble(beforeRaw);
|
|
441
|
+
const preamble = normalizedBefore.length > 0 ? normalizedBefore : "#!/bin/sh";
|
|
442
|
+
const preambleWithShebang = preamble.startsWith("#!/bin/sh") ? preamble : `#!/bin/sh
|
|
443
|
+
${preamble}`;
|
|
444
|
+
writeFileSync2(HOOK_PATH, `${preambleWithShebang}
|
|
445
|
+
|
|
446
|
+
${body}
|
|
447
|
+
`);
|
|
427
448
|
chmodSync(HOOK_PATH, 493);
|
|
428
449
|
const modeLabel2 = advisory ? chalk.cyan("advisory") : chalk.yellow("strict");
|
|
429
450
|
console.log(chalk.green("\n \u2713 Pre-commit hook updated") + chalk.dim(` (${modeLabel2} mode)`));
|
|
430
451
|
return;
|
|
431
452
|
}
|
|
432
|
-
writeFileSync2(HOOK_PATH, existing.trimEnd() + "\n\n" +
|
|
453
|
+
writeFileSync2(HOOK_PATH, existing.trimEnd() + "\n\n" + body + "\n");
|
|
433
454
|
} else {
|
|
434
455
|
writeFileSync2(HOOK_PATH, script);
|
|
435
456
|
}
|
|
@@ -450,7 +471,7 @@ function uninstallHook() {
|
|
|
450
471
|
return;
|
|
451
472
|
}
|
|
452
473
|
const markerIndex = content.indexOf(HOOK_MARKER);
|
|
453
|
-
const before = markerIndex > 1 ? content.slice(0, markerIndex)
|
|
474
|
+
const before = markerIndex > 1 ? normalizeHookPreamble(content.slice(0, markerIndex)) : "";
|
|
454
475
|
if (before && before !== "#!/bin/sh") {
|
|
455
476
|
writeFileSync2(HOOK_PATH, `${before}
|
|
456
477
|
`);
|
|
@@ -526,6 +547,12 @@ Do not include any text outside the JSON object.`;
|
|
|
526
547
|
console.log(chalk.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"));
|
|
527
548
|
}
|
|
528
549
|
const deterministicViolations = findDeterministicViolations(diff, rules, avoids, allowPatterns);
|
|
550
|
+
const astViolations = findAstDeterministicViolationsForDiff(diff, {
|
|
551
|
+
cwd: process.cwd(),
|
|
552
|
+
config,
|
|
553
|
+
rules,
|
|
554
|
+
reasonLookup: reasonMap
|
|
555
|
+
});
|
|
529
556
|
let modelViolations = [];
|
|
530
557
|
try {
|
|
531
558
|
const raw = await callChatModel([
|
|
@@ -559,7 +586,7 @@ ${diffToSend}` }
|
|
|
559
586
|
}
|
|
560
587
|
}
|
|
561
588
|
modelViolations = await verifyViolations(diff, modelViolations, allowPatterns, options.debug ?? false);
|
|
562
|
-
let violations = dedupeViolations([...deterministicViolations, ...modelViolations]);
|
|
589
|
+
let violations = dedupeViolations([...deterministicViolations, ...astViolations, ...modelViolations]);
|
|
563
590
|
violations = applyAllowPatterns(violations, allowPatterns);
|
|
564
591
|
if (violations.length === 0) {
|
|
565
592
|
console.log(chalk.green(" \u2713 No rule violations \u2014 commit allowed.\n"));
|
|
@@ -584,6 +611,7 @@ ${diffToSend}` }
|
|
|
584
611
|
});
|
|
585
612
|
console.log(chalk.dim(" Fix the violations above, then commit again."));
|
|
586
613
|
console.log(chalk.dim(" To bypass (not recommended): git commit --no-verify"));
|
|
614
|
+
console.log(chalk.dim(" Env bypass: MEMORY_CORE_SKIP_HOOK=1 git commit"));
|
|
587
615
|
console.log(chalk.dim(' To save as memory: memory-core remember "<lesson>"'));
|
|
588
616
|
console.log();
|
|
589
617
|
recordViolations(violations);
|
|
@@ -806,6 +834,7 @@ var GITIGNORE_HEADING = "# memory-core generated files";
|
|
|
806
834
|
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
807
835
|
var DEFAULT_EMBEDDING_MODEL = "nomic-embed-text";
|
|
808
836
|
var DEFAULT_CHAT_MODEL = "llama3.2";
|
|
837
|
+
var phase1 = getDefaultApplicationContainer();
|
|
809
838
|
function getEnvPath() {
|
|
810
839
|
const memoryEnv = join3(process.cwd(), ".memory-core.env");
|
|
811
840
|
if (existsSync3(memoryEnv)) return memoryEnv;
|
|
@@ -1045,6 +1074,35 @@ function truncate(value, length) {
|
|
|
1045
1074
|
if (!value) return "";
|
|
1046
1075
|
return value.length > length ? `${value.slice(0, Math.max(0, length - 1))}\u2026` : value;
|
|
1047
1076
|
}
|
|
1077
|
+
function toMemoryTableRow(memory) {
|
|
1078
|
+
return {
|
|
1079
|
+
id: memory.id,
|
|
1080
|
+
type: memory.type,
|
|
1081
|
+
scope: memory.scope,
|
|
1082
|
+
title: memory.title,
|
|
1083
|
+
content: memory.content,
|
|
1084
|
+
reason: memory.reason,
|
|
1085
|
+
context: memory.context,
|
|
1086
|
+
tags: memory.tags ?? [],
|
|
1087
|
+
similarity: memory.similarity
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function toPortableFromRecord(memory) {
|
|
1091
|
+
return toPortableMemory({
|
|
1092
|
+
id: memory.id,
|
|
1093
|
+
type: memory.type,
|
|
1094
|
+
scope: memory.scope,
|
|
1095
|
+
architecture: memory.architecture,
|
|
1096
|
+
project_name: memory.projectName,
|
|
1097
|
+
title: memory.title,
|
|
1098
|
+
content: memory.content,
|
|
1099
|
+
reason: memory.reason,
|
|
1100
|
+
context: memory.context,
|
|
1101
|
+
tags: memory.tags ?? [],
|
|
1102
|
+
content_hash: memory.contentHash,
|
|
1103
|
+
similarity: memory.similarity
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1048
1106
|
function printMemoryTable(memories, title = "Rules in memory") {
|
|
1049
1107
|
console.log(chalk2.bold(`
|
|
1050
1108
|
${title} (${memories.length} total)
|
|
@@ -1066,6 +1124,23 @@ function getCurrentListArchitectures(config) {
|
|
|
1066
1124
|
function printStatusLine(label, value) {
|
|
1067
1125
|
console.log(` ${chalk2.dim(label.padEnd(18))} ${value}`);
|
|
1068
1126
|
}
|
|
1127
|
+
function abbreviate(value, max = 96) {
|
|
1128
|
+
return value.length > max ? `${value.slice(0, max - 1)}\u2026` : value;
|
|
1129
|
+
}
|
|
1130
|
+
function graphStoreFilePath(cwd = process.cwd()) {
|
|
1131
|
+
return join3(cwd, ".memory-core", "graph-snapshots.json");
|
|
1132
|
+
}
|
|
1133
|
+
function graphBackendLabel(values) {
|
|
1134
|
+
return values.DATABASE_URL ? "postgres (file fallback enabled)" : "file";
|
|
1135
|
+
}
|
|
1136
|
+
async function getGraphSnapshotCount(cwd = process.cwd()) {
|
|
1137
|
+
try {
|
|
1138
|
+
const snapshots = await phase1.services.graphEngine.listSnapshots(resolve(cwd), 1e4);
|
|
1139
|
+
return snapshots.length;
|
|
1140
|
+
} catch {
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1069
1144
|
async function runModelDoctor() {
|
|
1070
1145
|
const { envPath, values } = readRuntimeEnv();
|
|
1071
1146
|
const provider2 = getConfiguredProvider(values);
|
|
@@ -1151,6 +1226,7 @@ async function printProjectStatus() {
|
|
|
1151
1226
|
const statsPath = join3(process.cwd(), ".memory-core-stats.json");
|
|
1152
1227
|
const dbError = await verifyDatabaseConnection(values.DATABASE_URL ?? "");
|
|
1153
1228
|
const ollamaError = await verifyOllamaConnection(values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
|
|
1229
|
+
const graphCount = await getGraphSnapshotCount(process.cwd());
|
|
1154
1230
|
console.log(chalk2.bold("\n memory-core status\n"));
|
|
1155
1231
|
printStatusLine("Project", config?.projectName ?? process.cwd().split("/").pop() ?? "unknown");
|
|
1156
1232
|
printStatusLine("Project type", config?.projectType ?? chalk2.yellow("not initialized"));
|
|
@@ -1168,6 +1244,9 @@ async function printProjectStatus() {
|
|
|
1168
1244
|
printStatusLine("Generated files", String(generatedFiles.length));
|
|
1169
1245
|
printStatusLine("Hook", existsSync3(hookPath) ? "installed" : "not installed");
|
|
1170
1246
|
printStatusLine("Stats file", existsSync3(statsPath) ? ".memory-core-stats.json" : chalk2.gray("none"));
|
|
1247
|
+
printStatusLine("Graph backend", graphBackendLabel(values));
|
|
1248
|
+
printStatusLine("Graph store", graphStoreFilePath(process.cwd()));
|
|
1249
|
+
printStatusLine("Graph snapshots", graphCount === null ? chalk2.gray("unavailable") : String(graphCount));
|
|
1171
1250
|
console.log();
|
|
1172
1251
|
printStatusLine("Database URL", redactDatabaseUrl(values.DATABASE_URL ?? ""));
|
|
1173
1252
|
printStatusLine("Ollama URL", values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
|
|
@@ -1655,8 +1734,7 @@ program.command("remember <text>").description("Save a new memory to the central
|
|
|
1655
1734
|
}
|
|
1656
1735
|
const spinner = ora("Saving memory\u2026").start();
|
|
1657
1736
|
try {
|
|
1658
|
-
|
|
1659
|
-
await saveMemory({
|
|
1737
|
+
await phase1.services.memoryEngine.remember({
|
|
1660
1738
|
type: opts.type,
|
|
1661
1739
|
scope: opts.scope,
|
|
1662
1740
|
architecture: config?.backendArchitecture ?? config?.frontendFramework,
|
|
@@ -1664,8 +1742,7 @@ program.command("remember <text>").description("Save a new memory to the central
|
|
|
1664
1742
|
content: text,
|
|
1665
1743
|
reason: reason || void 0,
|
|
1666
1744
|
context: buildMemoryContext(opts),
|
|
1667
|
-
tags: parseTags(opts.tags)
|
|
1668
|
-
embedding
|
|
1745
|
+
tags: parseTags(opts.tags)
|
|
1669
1746
|
});
|
|
1670
1747
|
const reasonLine = reason ? chalk2.gray(`
|
|
1671
1748
|
Why: ${reason}`) : "";
|
|
@@ -1682,11 +1759,12 @@ program.command("search <query>").description("Search memories using semantic si
|
|
|
1682
1759
|
const spinner = ora("Searching\u2026").start();
|
|
1683
1760
|
try {
|
|
1684
1761
|
const architectures = inferProjectArchitectures(process.cwd(), config);
|
|
1685
|
-
const
|
|
1686
|
-
query,
|
|
1762
|
+
const result = await phase1.services.retrievalEngine.retrieve({
|
|
1763
|
+
text: query,
|
|
1687
1764
|
architectures,
|
|
1688
|
-
parseInt(opts.limit, 10)
|
|
1689
|
-
);
|
|
1765
|
+
limit: parseInt(opts.limit, 10)
|
|
1766
|
+
});
|
|
1767
|
+
const results = result.items;
|
|
1690
1768
|
spinner.stop();
|
|
1691
1769
|
if (results.length === 0) {
|
|
1692
1770
|
console.log(chalk2.yellow("No memories found."));
|
|
@@ -1715,8 +1793,8 @@ program.command("search <query>").description("Search memories using semantic si
|
|
|
1715
1793
|
program.command("export").description(`Export DB memories to ${MEMORY_FILE}`).option("-o, --output <file>", `Output file (default: ${MEMORY_FILE})`).action(async (opts) => {
|
|
1716
1794
|
const spinner = ora("Exporting memories\u2026").start();
|
|
1717
1795
|
try {
|
|
1718
|
-
const memories = await
|
|
1719
|
-
const portable = memories.map(
|
|
1796
|
+
const memories = await phase1.services.memoryEngine.list({ limit: 1e4 });
|
|
1797
|
+
const portable = memories.map(toPortableFromRecord);
|
|
1720
1798
|
const outputPath = opts.output ? join3(process.cwd(), opts.output) : writeMemoryFile(portable);
|
|
1721
1799
|
if (opts.output) {
|
|
1722
1800
|
writeFileSync3(outputPath, JSON.stringify(portable, null, 2) + "\n", "utf-8");
|
|
@@ -1738,8 +1816,7 @@ program.command("import").description(`Import memories from ${MEMORY_FILE}`).opt
|
|
|
1738
1816
|
let skipped = 0;
|
|
1739
1817
|
spinner.text = `Importing ${memories.length} memories\u2026`;
|
|
1740
1818
|
for (const memory of memories) {
|
|
1741
|
-
const
|
|
1742
|
-
const result = await upsertMemory({
|
|
1819
|
+
const result = await phase1.services.memoryEngine.remember({
|
|
1743
1820
|
type: memory.type,
|
|
1744
1821
|
scope: memory.scope,
|
|
1745
1822
|
architecture: memory.architecture,
|
|
@@ -1748,8 +1825,7 @@ program.command("import").description(`Import memories from ${MEMORY_FILE}`).opt
|
|
|
1748
1825
|
content: memory.content,
|
|
1749
1826
|
reason: memory.reason,
|
|
1750
1827
|
context: memory.context,
|
|
1751
|
-
tags: memory.tags
|
|
1752
|
-
embedding
|
|
1828
|
+
tags: memory.tags
|
|
1753
1829
|
});
|
|
1754
1830
|
if (result === "inserted") inserted++;
|
|
1755
1831
|
else skipped++;
|
|
@@ -1770,7 +1846,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1770
1846
|
const config = readProjectConfig();
|
|
1771
1847
|
const architectures = opts.arch ? opts.arch : opts.all ? void 0 : getCurrentListArchitectures(config);
|
|
1772
1848
|
const projectName = opts.project ? opts.project : opts.all || opts.arch ? void 0 : config?.projectName;
|
|
1773
|
-
const memories = await
|
|
1849
|
+
const memories = await phase1.services.memoryEngine.list({
|
|
1774
1850
|
type: opts.type,
|
|
1775
1851
|
scope: opts.scope,
|
|
1776
1852
|
architecture: architectures,
|
|
@@ -1779,7 +1855,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1779
1855
|
limit: parseInt(opts.limit, 10)
|
|
1780
1856
|
});
|
|
1781
1857
|
const title = opts.all ? "All memories" : `Current project memories${architectures ? ` (${Array.isArray(architectures) ? architectures.join(", ") : architectures})` : ""}`;
|
|
1782
|
-
printMemoryTable(memories, title);
|
|
1858
|
+
printMemoryTable(memories.map(toMemoryTableRow), title);
|
|
1783
1859
|
if (!opts.all) {
|
|
1784
1860
|
console.log(chalk2.gray(" Showing current project context plus shared/global memories. Use --all for the full database.\n"));
|
|
1785
1861
|
}
|
|
@@ -1793,7 +1869,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1793
1869
|
program.command("remove <id>").description("Remove a memory by ID").option("--no-sync", "Skip automatic agent file sync after removal").action(async (id, opts) => {
|
|
1794
1870
|
try {
|
|
1795
1871
|
const config = readProjectConfig();
|
|
1796
|
-
const deleted = await
|
|
1872
|
+
const deleted = await phase1.services.memoryEngine.removeById(parseInt(id, 10));
|
|
1797
1873
|
if (!deleted) {
|
|
1798
1874
|
console.log(chalk2.yellow(`No memory found with ID ${id}`));
|
|
1799
1875
|
process.exit(1);
|
|
@@ -1810,7 +1886,7 @@ program.command("remove <id>").description("Remove a memory by ID").option("--no
|
|
|
1810
1886
|
program.command("forget").description("Bulk-delete memories by tag, scope, type, or architecture").option("--tag <tag>", "Delete memories with this tag").option("--scope <scope>", "Filter by scope").option("--type <type>", "Filter by type").option("--arch <architecture>", "Filter by architecture").option("--no-sync", "Skip automatic agent file sync after deletion").action(async (opts) => {
|
|
1811
1887
|
try {
|
|
1812
1888
|
const config = readProjectConfig();
|
|
1813
|
-
const deleted = await
|
|
1889
|
+
const deleted = await phase1.services.memoryEngine.removeMany({
|
|
1814
1890
|
tag: opts.tag,
|
|
1815
1891
|
scope: opts.scope,
|
|
1816
1892
|
type: opts.type,
|
|
@@ -1831,7 +1907,7 @@ program.command("edit <id>").description("Edit a memory interactively").option("
|
|
|
1831
1907
|
const memoryId = parseInt(id, 10);
|
|
1832
1908
|
try {
|
|
1833
1909
|
const config = readProjectConfig();
|
|
1834
|
-
const existing = await
|
|
1910
|
+
const existing = await phase1.services.memoryEngine.getById(memoryId);
|
|
1835
1911
|
if (!existing) {
|
|
1836
1912
|
console.log(chalk2.yellow(`No memory found with ID ${id}`));
|
|
1837
1913
|
process.exit(1);
|
|
@@ -1846,16 +1922,14 @@ program.command("edit <id>").description("Edit a memory interactively").option("
|
|
|
1846
1922
|
const examples = await input({ message: "Examples? (comma-separated)", default: existing.context?.examples?.join(",") ?? "" });
|
|
1847
1923
|
const source = await input({ message: "Source?", default: existing.context?.source ?? "" });
|
|
1848
1924
|
const tags = await input({ message: "Tags?", default: existing.tags.join(",") });
|
|
1849
|
-
|
|
1850
|
-
await updateMemory(memoryId, {
|
|
1925
|
+
await phase1.services.memoryEngine.update(memoryId, {
|
|
1851
1926
|
type,
|
|
1852
1927
|
scope,
|
|
1853
1928
|
title: title || void 0,
|
|
1854
1929
|
content,
|
|
1855
1930
|
reason: reason || void 0,
|
|
1856
1931
|
context: buildMemoryContext({ appliesTo, avoidWhen, example: examples, source }),
|
|
1857
|
-
tags: parseTags(tags)
|
|
1858
|
-
embedding
|
|
1932
|
+
tags: parseTags(tags)
|
|
1859
1933
|
});
|
|
1860
1934
|
console.log(chalk2.green(`Updated memory ${id}`));
|
|
1861
1935
|
await autoSyncGeneratedFiles(config, "edit", opts.sync);
|
|
@@ -1870,11 +1944,12 @@ program.command("ignore [pattern]").description("Manage project-scoped false-pos
|
|
|
1870
1944
|
try {
|
|
1871
1945
|
const config = readProjectConfig();
|
|
1872
1946
|
if (opts.list) {
|
|
1873
|
-
|
|
1947
|
+
const ignores = await phase1.services.memoryEngine.list({ type: "ignore", limit: 1e3 });
|
|
1948
|
+
printMemoryTable(ignores.map(toMemoryTableRow), "Ignored patterns");
|
|
1874
1949
|
return;
|
|
1875
1950
|
}
|
|
1876
1951
|
if (opts.remove) {
|
|
1877
|
-
const deleted = await
|
|
1952
|
+
const deleted = await phase1.services.memoryEngine.removeById(parseInt(opts.remove, 10));
|
|
1878
1953
|
if (!deleted) {
|
|
1879
1954
|
console.log(chalk2.yellow(`No ignore pattern found with ID ${opts.remove}`));
|
|
1880
1955
|
process.exit(1);
|
|
@@ -1887,15 +1962,13 @@ program.command("ignore [pattern]").description("Manage project-scoped false-pos
|
|
|
1887
1962
|
console.error(chalk2.red("Provide a pattern, --list, or --remove <id>"));
|
|
1888
1963
|
process.exit(1);
|
|
1889
1964
|
}
|
|
1890
|
-
|
|
1891
|
-
await upsertMemory({
|
|
1965
|
+
await phase1.services.memoryEngine.remember({
|
|
1892
1966
|
type: "ignore",
|
|
1893
1967
|
scope: "project",
|
|
1894
1968
|
architecture: config?.backendArchitecture ?? config?.frontendFramework,
|
|
1895
1969
|
projectName: config?.projectName,
|
|
1896
1970
|
content: pattern,
|
|
1897
|
-
tags: ["ignore"]
|
|
1898
|
-
embedding
|
|
1971
|
+
tags: ["ignore"]
|
|
1899
1972
|
});
|
|
1900
1973
|
console.log(chalk2.green(`Ignored pattern saved: "${pattern}"`));
|
|
1901
1974
|
await autoSyncGeneratedFiles(config, "ignore", opts.sync);
|
|
@@ -2020,7 +2093,7 @@ program.command("stats").description("Show violation counters recorded by check
|
|
|
2020
2093
|
console.log();
|
|
2021
2094
|
});
|
|
2022
2095
|
program.command("dashboard").description("Start the live Svelte dashboard with WebSocket watch events").option("-p, --port <port>", "Dashboard port", "5178").option("--path <dir>", "Directory to watch (default: current directory)").option("--no-watch", "Serve the dashboard without starting file watch").action(async (opts) => {
|
|
2023
|
-
const { startDashboard } = await import("./dashboard-server-
|
|
2096
|
+
const { startDashboard } = await import("./dashboard-server-AUX4BQP6.js");
|
|
2024
2097
|
await startDashboard({
|
|
2025
2098
|
port: parseInt(opts.port, 10),
|
|
2026
2099
|
path: opts.path,
|
|
@@ -2038,7 +2111,6 @@ program.command("seed").description("Load all predefined memories into the datab
|
|
|
2038
2111
|
for (const seed of filtered) {
|
|
2039
2112
|
const spinner = ora(`[${seed.architecture}] ${seed.title}`).start();
|
|
2040
2113
|
try {
|
|
2041
|
-
const embedding = await embed(seed.content);
|
|
2042
2114
|
const payload = {
|
|
2043
2115
|
type: seed.type,
|
|
2044
2116
|
scope: seed.scope,
|
|
@@ -2046,15 +2118,14 @@ program.command("seed").description("Load all predefined memories into the datab
|
|
|
2046
2118
|
title: seed.title,
|
|
2047
2119
|
content: seed.content,
|
|
2048
2120
|
reason: seed.reason,
|
|
2049
|
-
tags: seed.tags
|
|
2050
|
-
embedding
|
|
2121
|
+
tags: seed.tags
|
|
2051
2122
|
};
|
|
2052
2123
|
if (opts.force) {
|
|
2053
|
-
await
|
|
2124
|
+
await phase1.services.memoryEngine.rememberForce(payload);
|
|
2054
2125
|
saved++;
|
|
2055
2126
|
spinner.succeed(chalk2.gray(`[${seed.architecture}] ${seed.title}`));
|
|
2056
2127
|
} else {
|
|
2057
|
-
const result = await
|
|
2128
|
+
const result = await phase1.services.memoryEngine.remember(payload);
|
|
2058
2129
|
if (result === "inserted") {
|
|
2059
2130
|
saved++;
|
|
2060
2131
|
spinner.succeed(chalk2.gray(`[${seed.architecture}] ${seed.title}`));
|
|
@@ -2097,7 +2168,11 @@ program.command("global").description("Sync your memory into every AI agent glob
|
|
|
2097
2168
|
let memories = [];
|
|
2098
2169
|
try {
|
|
2099
2170
|
const architectures = opts.arch ? [opts.arch] : inferProjectArchitectures(process.cwd(), readProjectConfig());
|
|
2100
|
-
memories =
|
|
2171
|
+
memories = (await phase1.services.retrievalEngine.retrieve({
|
|
2172
|
+
text: "architecture rules coding standards",
|
|
2173
|
+
architectures,
|
|
2174
|
+
limit: 30
|
|
2175
|
+
})).items;
|
|
2101
2176
|
} catch (err) {
|
|
2102
2177
|
spinner.fail(`Could not fetch memories: ${err.message}`);
|
|
2103
2178
|
process.exit(1);
|
|
@@ -2256,6 +2331,164 @@ program.command("status").description("Show the current memory-core project and
|
|
|
2256
2331
|
process.exit(1);
|
|
2257
2332
|
}
|
|
2258
2333
|
});
|
|
2334
|
+
var graph = program.command("graph").description("Build and inspect dependency graph snapshots");
|
|
2335
|
+
graph.command("migrate").description("Create or update PostgreSQL graph snapshot schema").action(async () => {
|
|
2336
|
+
const { values } = readRuntimeEnv();
|
|
2337
|
+
if (!values.DATABASE_URL) {
|
|
2338
|
+
console.error(chalk2.red("Graph migration requires DATABASE_URL. Configure it in .memory-core.env or .env."));
|
|
2339
|
+
process.exit(1);
|
|
2340
|
+
}
|
|
2341
|
+
const spinner = ora("Migrating graph snapshot schema\u2026").start();
|
|
2342
|
+
try {
|
|
2343
|
+
await migrateGraphSnapshots();
|
|
2344
|
+
spinner.succeed("Graph snapshot schema is ready in PostgreSQL");
|
|
2345
|
+
} catch (err) {
|
|
2346
|
+
spinner.fail(`Graph migration failed: ${err.message}`);
|
|
2347
|
+
process.exit(1);
|
|
2348
|
+
}
|
|
2349
|
+
});
|
|
2350
|
+
graph.command("doctor").description("Inspect graph storage backend health (PostgreSQL + file fallback)").option("--path <dir>", "Project root path (default: current directory)").action(async (opts) => {
|
|
2351
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2352
|
+
const { values } = readRuntimeEnv();
|
|
2353
|
+
const filePath = graphStoreFilePath(cwd);
|
|
2354
|
+
const usesPostgres = Boolean(values.DATABASE_URL);
|
|
2355
|
+
let ok = true;
|
|
2356
|
+
console.log(chalk2.bold("\n graph doctor\n"));
|
|
2357
|
+
printStatusLine("Project path", cwd);
|
|
2358
|
+
printStatusLine("Configured backend", usesPostgres ? "postgres + file fallback" : "file");
|
|
2359
|
+
printStatusLine("File store", filePath);
|
|
2360
|
+
console.log();
|
|
2361
|
+
if (!usesPostgres) {
|
|
2362
|
+
console.log(chalk2.yellow(" \u26A0 DATABASE_URL not set \u2014 using file backend only."));
|
|
2363
|
+
} else {
|
|
2364
|
+
try {
|
|
2365
|
+
await migrateGraphSnapshots();
|
|
2366
|
+
const probe = await probeGraphSnapshotStore(cwd);
|
|
2367
|
+
console.log(chalk2.green(" \u2713 Graph PostgreSQL") + chalk2.dim(` ready (${probe.snapshotCount} snapshot records for this root)`));
|
|
2368
|
+
} catch (err) {
|
|
2369
|
+
ok = false;
|
|
2370
|
+
console.log(chalk2.red(" \u2717 Graph PostgreSQL") + chalk2.dim(` ${err.message}`));
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
try {
|
|
2374
|
+
const count = await getGraphSnapshotCount(cwd);
|
|
2375
|
+
console.log(chalk2.green(" \u2713 Graph service") + chalk2.dim(` readable (${count ?? 0} snapshots visible)`));
|
|
2376
|
+
} catch (err) {
|
|
2377
|
+
ok = false;
|
|
2378
|
+
console.log(chalk2.red(" \u2717 Graph service") + chalk2.dim(` ${err.message}`));
|
|
2379
|
+
}
|
|
2380
|
+
if (existsSync3(filePath)) {
|
|
2381
|
+
console.log(chalk2.green(" \u2713 File fallback") + chalk2.dim(" store exists"));
|
|
2382
|
+
} else {
|
|
2383
|
+
console.log(chalk2.yellow(" \u26A0 File fallback") + chalk2.dim(" store not created yet (run graph build once)"));
|
|
2384
|
+
}
|
|
2385
|
+
console.log();
|
|
2386
|
+
if (!ok) process.exit(1);
|
|
2387
|
+
});
|
|
2388
|
+
graph.command("build").description("Build a dependency graph snapshot and persist it").option("--path <dir>", "Project root path (default: current directory)").action(async (opts) => {
|
|
2389
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2390
|
+
const spinner = ora("Building dependency graph\u2026").start();
|
|
2391
|
+
try {
|
|
2392
|
+
const { snapshot } = await phase1.services.graphEngine.buildAndStoreSnapshot({ cwd });
|
|
2393
|
+
spinner.succeed(`Graph snapshot saved: ${snapshot.id}`);
|
|
2394
|
+
console.log(chalk2.gray(` Root: ${snapshot.rootPath}`));
|
|
2395
|
+
console.log(chalk2.gray(` Nodes: ${snapshot.nodes.length}`));
|
|
2396
|
+
console.log(chalk2.gray(` Edges: ${snapshot.edges.length}
|
|
2397
|
+
`));
|
|
2398
|
+
} catch (err) {
|
|
2399
|
+
spinner.fail(`Graph build failed: ${err.message}`);
|
|
2400
|
+
process.exit(1);
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
graph.command("list").description("List saved dependency graph snapshots").option("--path <dir>", "Project root path (default: current directory)").option("-n, --limit <n>", "Number of snapshots to list", "10").action(async (opts) => {
|
|
2404
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2405
|
+
try {
|
|
2406
|
+
const snapshots = await phase1.services.graphEngine.listSnapshots(cwd, parseInt(opts.limit, 10));
|
|
2407
|
+
if (snapshots.length === 0) {
|
|
2408
|
+
console.log(chalk2.yellow("\n No graph snapshots found. Run: memory-core graph build\n"));
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
console.log(chalk2.bold("\n Graph snapshots\n"));
|
|
2412
|
+
snapshots.forEach((snapshot, index) => {
|
|
2413
|
+
console.log(` ${index + 1}. ${snapshot.id} ${chalk2.dim(snapshot.createdAt)} nodes=${snapshot.nodes.length} edges=${snapshot.edges.length}`);
|
|
2414
|
+
});
|
|
2415
|
+
console.log();
|
|
2416
|
+
} catch (err) {
|
|
2417
|
+
console.error(chalk2.red(`Graph list failed: ${err.message}`));
|
|
2418
|
+
process.exit(1);
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
2421
|
+
graph.command("show [snapshotId]").description("Show a saved graph snapshot (latest by default)").option("--path <dir>", "Project root path (default: current directory)").option("--edges <n>", "Number of edges to print", "25").action(async (snapshotId, opts) => {
|
|
2422
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2423
|
+
try {
|
|
2424
|
+
const snapshot = snapshotId ? await phase1.services.graphEngine.getSnapshot(cwd, snapshotId) : await phase1.services.graphEngine.latest(cwd);
|
|
2425
|
+
if (!snapshot) {
|
|
2426
|
+
console.log(chalk2.yellow("\n No matching graph snapshot found. Run: memory-core graph build\n"));
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
console.log(chalk2.bold(`
|
|
2430
|
+
Graph ${snapshot.id ?? "(latest)"}
|
|
2431
|
+
`));
|
|
2432
|
+
console.log(chalk2.gray(` Root: ${snapshot.rootPath}`));
|
|
2433
|
+
console.log(chalk2.gray(` Created: ${snapshot.createdAt ?? "unknown"}`));
|
|
2434
|
+
console.log(chalk2.gray(` Nodes: ${snapshot.nodes.length}`));
|
|
2435
|
+
console.log(chalk2.gray(` Edges: ${snapshot.edges.length}
|
|
2436
|
+
`));
|
|
2437
|
+
const limit = parseInt(opts.edges, 10);
|
|
2438
|
+
snapshot.edges.slice(0, limit).forEach((edge, index) => {
|
|
2439
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2440
|
+
});
|
|
2441
|
+
if (snapshot.edges.length > limit) {
|
|
2442
|
+
console.log(chalk2.dim(`
|
|
2443
|
+
\u2026 ${snapshot.edges.length - limit} more edges not shown
|
|
2444
|
+
`));
|
|
2445
|
+
} else {
|
|
2446
|
+
console.log();
|
|
2447
|
+
}
|
|
2448
|
+
} catch (err) {
|
|
2449
|
+
console.error(chalk2.red(`Graph show failed: ${err.message}`));
|
|
2450
|
+
process.exit(1);
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2453
|
+
graph.command("diff <leftSnapshotId> [rightSnapshotId]").description("Diff two snapshots (right defaults to latest)").option("--path <dir>", "Project root path (default: current directory)").action(async (leftSnapshotId, rightSnapshotId, opts) => {
|
|
2454
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2455
|
+
try {
|
|
2456
|
+
const left = await phase1.services.graphEngine.getSnapshot(cwd, leftSnapshotId);
|
|
2457
|
+
if (!left) {
|
|
2458
|
+
console.error(chalk2.red(`Left snapshot not found: ${leftSnapshotId}`));
|
|
2459
|
+
process.exit(1);
|
|
2460
|
+
}
|
|
2461
|
+
const right = rightSnapshotId ? await phase1.services.graphEngine.getSnapshot(cwd, rightSnapshotId) : await phase1.services.graphEngine.latest(cwd);
|
|
2462
|
+
if (!right) {
|
|
2463
|
+
console.error(chalk2.red(`Right snapshot not found${rightSnapshotId ? `: ${rightSnapshotId}` : ""}`));
|
|
2464
|
+
process.exit(1);
|
|
2465
|
+
}
|
|
2466
|
+
const diff = phase1.services.graphEngine.diffGraphs(left, right);
|
|
2467
|
+
console.log(chalk2.bold("\n Graph diff\n"));
|
|
2468
|
+
console.log(chalk2.gray(` Left: ${left.id}`));
|
|
2469
|
+
console.log(chalk2.gray(` Right: ${right.id}`));
|
|
2470
|
+
console.log(chalk2.green(` + Nodes: ${diff.addedNodes.length}`));
|
|
2471
|
+
console.log(chalk2.red(` - Nodes: ${diff.removedNodes.length}`));
|
|
2472
|
+
console.log(chalk2.green(` + Edges: ${diff.addedEdges.length}`));
|
|
2473
|
+
console.log(chalk2.red(` - Edges: ${diff.removedEdges.length}`));
|
|
2474
|
+
if (diff.addedEdges.length > 0) {
|
|
2475
|
+
console.log(chalk2.green("\n Added edges"));
|
|
2476
|
+
diff.addedEdges.slice(0, 20).forEach((edge, index) => {
|
|
2477
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
if (diff.removedEdges.length > 0) {
|
|
2481
|
+
console.log(chalk2.red("\n Removed edges"));
|
|
2482
|
+
diff.removedEdges.slice(0, 20).forEach((edge, index) => {
|
|
2483
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
console.log();
|
|
2487
|
+
} catch (err) {
|
|
2488
|
+
console.error(chalk2.red(`Graph diff failed: ${err.message}`));
|
|
2489
|
+
process.exit(1);
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2259
2492
|
var hook = program.command("hook").description("Manage the pre-commit rule enforcement hook");
|
|
2260
2493
|
hook.command("install").description("Install pre-commit hook (advisory mode by default \u2014 logs violations, never blocks)").option("--advisory", "Log violations but never block commits (default)").option("--strict", "Block commits that violate your rules").action((opts) => {
|
|
2261
2494
|
const advisory = opts.strict ? false : true;
|
|
@@ -2271,7 +2504,11 @@ program.command("check").description("Check staged changes against architecture
|
|
|
2271
2504
|
}
|
|
2272
2505
|
await checkStaged({ verbose: opts.verbose ?? false, debug: opts.debug ?? false });
|
|
2273
2506
|
});
|
|
2274
|
-
program.command("watch").description("Watch source files and check violations in real-time on every save").option("--path <dir>", "Directory to watch (default: current directory)").option("--verbose", "Show diff size and model details per file").option("--debug", "Show prompt, diff, and raw model response").action((opts) => {
|
|
2275
|
-
|
|
2507
|
+
program.command("watch").description("Watch source files and check violations in real-time on every save").option("--path <dir>", "Directory to watch (default: current directory)").option("--verbose", "Show diff size and model details per file").option("--debug", "Show prompt, diff, and raw model response").action(async (opts) => {
|
|
2508
|
+
await phase1.providers.watchService.start({
|
|
2509
|
+
path: opts.path,
|
|
2510
|
+
verbose: opts.verbose,
|
|
2511
|
+
debug: opts.debug
|
|
2512
|
+
});
|
|
2276
2513
|
});
|
|
2277
2514
|
program.parseAsync(process.argv);
|