@shahmilsaari/memory-core 0.2.18 → 1.0.2
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 +268 -68
- 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 +4 -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
|
|
@@ -173,6 +161,7 @@ function recordViolations(violations, source = "hook") {
|
|
|
173
161
|
async function promptToSaveViolations(violations) {
|
|
174
162
|
if (!process.stdin.isTTY || violations.length === 0) return;
|
|
175
163
|
try {
|
|
164
|
+
const app = getDefaultApplicationContainer();
|
|
176
165
|
const { confirm: confirm2, input: input2 } = await import("@inquirer/prompts");
|
|
177
166
|
const save = await confirm2({
|
|
178
167
|
message: "Save a caught violation as a project rule?",
|
|
@@ -185,15 +174,12 @@ async function promptToSaveViolations(violations) {
|
|
|
185
174
|
message: "Why should this rule exist?",
|
|
186
175
|
default: selected.reason ?? selected.issue ?? ""
|
|
187
176
|
});
|
|
188
|
-
|
|
189
|
-
const { upsertMemory: upsertMemory2 } = await import("./db-MF3VKVKH.js");
|
|
190
|
-
await upsertMemory2({
|
|
177
|
+
await app.services.memoryEngine.remember({
|
|
191
178
|
type: "rule",
|
|
192
179
|
scope: "project",
|
|
193
180
|
content: selected.rule,
|
|
194
181
|
reason: reason || void 0,
|
|
195
|
-
tags: ["violation"]
|
|
196
|
-
embedding: await embed2(selected.rule)
|
|
182
|
+
tags: ["violation"]
|
|
197
183
|
});
|
|
198
184
|
console.log(chalk.green(" \u2713 Saved as project rule. Run memory-core sync to propagate it.\n"));
|
|
199
185
|
} catch (err) {
|
|
@@ -203,9 +189,8 @@ async function promptToSaveViolations(violations) {
|
|
|
203
189
|
}
|
|
204
190
|
async function loadIgnorePatterns() {
|
|
205
191
|
try {
|
|
206
|
-
const
|
|
207
|
-
const ignores = await
|
|
208
|
-
await closePool2();
|
|
192
|
+
const app = getDefaultApplicationContainer();
|
|
193
|
+
const ignores = await app.services.memoryEngine.list({ type: "ignore", limit: 1e3 });
|
|
209
194
|
return ignores.map((ignore) => ignore.content);
|
|
210
195
|
} catch {
|
|
211
196
|
return [];
|
|
@@ -526,6 +511,12 @@ Do not include any text outside the JSON object.`;
|
|
|
526
511
|
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
512
|
}
|
|
528
513
|
const deterministicViolations = findDeterministicViolations(diff, rules, avoids, allowPatterns);
|
|
514
|
+
const astViolations = findAstDeterministicViolationsForDiff(diff, {
|
|
515
|
+
cwd: process.cwd(),
|
|
516
|
+
config,
|
|
517
|
+
rules,
|
|
518
|
+
reasonLookup: reasonMap
|
|
519
|
+
});
|
|
529
520
|
let modelViolations = [];
|
|
530
521
|
try {
|
|
531
522
|
const raw = await callChatModel([
|
|
@@ -559,7 +550,7 @@ ${diffToSend}` }
|
|
|
559
550
|
}
|
|
560
551
|
}
|
|
561
552
|
modelViolations = await verifyViolations(diff, modelViolations, allowPatterns, options.debug ?? false);
|
|
562
|
-
let violations = dedupeViolations([...deterministicViolations, ...modelViolations]);
|
|
553
|
+
let violations = dedupeViolations([...deterministicViolations, ...astViolations, ...modelViolations]);
|
|
563
554
|
violations = applyAllowPatterns(violations, allowPatterns);
|
|
564
555
|
if (violations.length === 0) {
|
|
565
556
|
console.log(chalk.green(" \u2713 No rule violations \u2014 commit allowed.\n"));
|
|
@@ -806,6 +797,7 @@ var GITIGNORE_HEADING = "# memory-core generated files";
|
|
|
806
797
|
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
807
798
|
var DEFAULT_EMBEDDING_MODEL = "nomic-embed-text";
|
|
808
799
|
var DEFAULT_CHAT_MODEL = "llama3.2";
|
|
800
|
+
var phase1 = getDefaultApplicationContainer();
|
|
809
801
|
function getEnvPath() {
|
|
810
802
|
const memoryEnv = join3(process.cwd(), ".memory-core.env");
|
|
811
803
|
if (existsSync3(memoryEnv)) return memoryEnv;
|
|
@@ -1045,6 +1037,35 @@ function truncate(value, length) {
|
|
|
1045
1037
|
if (!value) return "";
|
|
1046
1038
|
return value.length > length ? `${value.slice(0, Math.max(0, length - 1))}\u2026` : value;
|
|
1047
1039
|
}
|
|
1040
|
+
function toMemoryTableRow(memory) {
|
|
1041
|
+
return {
|
|
1042
|
+
id: memory.id,
|
|
1043
|
+
type: memory.type,
|
|
1044
|
+
scope: memory.scope,
|
|
1045
|
+
title: memory.title,
|
|
1046
|
+
content: memory.content,
|
|
1047
|
+
reason: memory.reason,
|
|
1048
|
+
context: memory.context,
|
|
1049
|
+
tags: memory.tags ?? [],
|
|
1050
|
+
similarity: memory.similarity
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
function toPortableFromRecord(memory) {
|
|
1054
|
+
return toPortableMemory({
|
|
1055
|
+
id: memory.id,
|
|
1056
|
+
type: memory.type,
|
|
1057
|
+
scope: memory.scope,
|
|
1058
|
+
architecture: memory.architecture,
|
|
1059
|
+
project_name: memory.projectName,
|
|
1060
|
+
title: memory.title,
|
|
1061
|
+
content: memory.content,
|
|
1062
|
+
reason: memory.reason,
|
|
1063
|
+
context: memory.context,
|
|
1064
|
+
tags: memory.tags ?? [],
|
|
1065
|
+
content_hash: memory.contentHash,
|
|
1066
|
+
similarity: memory.similarity
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1048
1069
|
function printMemoryTable(memories, title = "Rules in memory") {
|
|
1049
1070
|
console.log(chalk2.bold(`
|
|
1050
1071
|
${title} (${memories.length} total)
|
|
@@ -1066,6 +1087,23 @@ function getCurrentListArchitectures(config) {
|
|
|
1066
1087
|
function printStatusLine(label, value) {
|
|
1067
1088
|
console.log(` ${chalk2.dim(label.padEnd(18))} ${value}`);
|
|
1068
1089
|
}
|
|
1090
|
+
function abbreviate(value, max = 96) {
|
|
1091
|
+
return value.length > max ? `${value.slice(0, max - 1)}\u2026` : value;
|
|
1092
|
+
}
|
|
1093
|
+
function graphStoreFilePath(cwd = process.cwd()) {
|
|
1094
|
+
return join3(cwd, ".memory-core", "graph-snapshots.json");
|
|
1095
|
+
}
|
|
1096
|
+
function graphBackendLabel(values) {
|
|
1097
|
+
return values.DATABASE_URL ? "postgres (file fallback enabled)" : "file";
|
|
1098
|
+
}
|
|
1099
|
+
async function getGraphSnapshotCount(cwd = process.cwd()) {
|
|
1100
|
+
try {
|
|
1101
|
+
const snapshots = await phase1.services.graphEngine.listSnapshots(resolve(cwd), 1e4);
|
|
1102
|
+
return snapshots.length;
|
|
1103
|
+
} catch {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1069
1107
|
async function runModelDoctor() {
|
|
1070
1108
|
const { envPath, values } = readRuntimeEnv();
|
|
1071
1109
|
const provider2 = getConfiguredProvider(values);
|
|
@@ -1151,6 +1189,7 @@ async function printProjectStatus() {
|
|
|
1151
1189
|
const statsPath = join3(process.cwd(), ".memory-core-stats.json");
|
|
1152
1190
|
const dbError = await verifyDatabaseConnection(values.DATABASE_URL ?? "");
|
|
1153
1191
|
const ollamaError = await verifyOllamaConnection(values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
|
|
1192
|
+
const graphCount = await getGraphSnapshotCount(process.cwd());
|
|
1154
1193
|
console.log(chalk2.bold("\n memory-core status\n"));
|
|
1155
1194
|
printStatusLine("Project", config?.projectName ?? process.cwd().split("/").pop() ?? "unknown");
|
|
1156
1195
|
printStatusLine("Project type", config?.projectType ?? chalk2.yellow("not initialized"));
|
|
@@ -1168,6 +1207,9 @@ async function printProjectStatus() {
|
|
|
1168
1207
|
printStatusLine("Generated files", String(generatedFiles.length));
|
|
1169
1208
|
printStatusLine("Hook", existsSync3(hookPath) ? "installed" : "not installed");
|
|
1170
1209
|
printStatusLine("Stats file", existsSync3(statsPath) ? ".memory-core-stats.json" : chalk2.gray("none"));
|
|
1210
|
+
printStatusLine("Graph backend", graphBackendLabel(values));
|
|
1211
|
+
printStatusLine("Graph store", graphStoreFilePath(process.cwd()));
|
|
1212
|
+
printStatusLine("Graph snapshots", graphCount === null ? chalk2.gray("unavailable") : String(graphCount));
|
|
1171
1213
|
console.log();
|
|
1172
1214
|
printStatusLine("Database URL", redactDatabaseUrl(values.DATABASE_URL ?? ""));
|
|
1173
1215
|
printStatusLine("Ollama URL", values.OLLAMA_URL ?? DEFAULT_OLLAMA_URL);
|
|
@@ -1655,8 +1697,7 @@ program.command("remember <text>").description("Save a new memory to the central
|
|
|
1655
1697
|
}
|
|
1656
1698
|
const spinner = ora("Saving memory\u2026").start();
|
|
1657
1699
|
try {
|
|
1658
|
-
|
|
1659
|
-
await saveMemory({
|
|
1700
|
+
await phase1.services.memoryEngine.remember({
|
|
1660
1701
|
type: opts.type,
|
|
1661
1702
|
scope: opts.scope,
|
|
1662
1703
|
architecture: config?.backendArchitecture ?? config?.frontendFramework,
|
|
@@ -1664,8 +1705,7 @@ program.command("remember <text>").description("Save a new memory to the central
|
|
|
1664
1705
|
content: text,
|
|
1665
1706
|
reason: reason || void 0,
|
|
1666
1707
|
context: buildMemoryContext(opts),
|
|
1667
|
-
tags: parseTags(opts.tags)
|
|
1668
|
-
embedding
|
|
1708
|
+
tags: parseTags(opts.tags)
|
|
1669
1709
|
});
|
|
1670
1710
|
const reasonLine = reason ? chalk2.gray(`
|
|
1671
1711
|
Why: ${reason}`) : "";
|
|
@@ -1682,11 +1722,12 @@ program.command("search <query>").description("Search memories using semantic si
|
|
|
1682
1722
|
const spinner = ora("Searching\u2026").start();
|
|
1683
1723
|
try {
|
|
1684
1724
|
const architectures = inferProjectArchitectures(process.cwd(), config);
|
|
1685
|
-
const
|
|
1686
|
-
query,
|
|
1725
|
+
const result = await phase1.services.retrievalEngine.retrieve({
|
|
1726
|
+
text: query,
|
|
1687
1727
|
architectures,
|
|
1688
|
-
parseInt(opts.limit, 10)
|
|
1689
|
-
);
|
|
1728
|
+
limit: parseInt(opts.limit, 10)
|
|
1729
|
+
});
|
|
1730
|
+
const results = result.items;
|
|
1690
1731
|
spinner.stop();
|
|
1691
1732
|
if (results.length === 0) {
|
|
1692
1733
|
console.log(chalk2.yellow("No memories found."));
|
|
@@ -1715,8 +1756,8 @@ program.command("search <query>").description("Search memories using semantic si
|
|
|
1715
1756
|
program.command("export").description(`Export DB memories to ${MEMORY_FILE}`).option("-o, --output <file>", `Output file (default: ${MEMORY_FILE})`).action(async (opts) => {
|
|
1716
1757
|
const spinner = ora("Exporting memories\u2026").start();
|
|
1717
1758
|
try {
|
|
1718
|
-
const memories = await
|
|
1719
|
-
const portable = memories.map(
|
|
1759
|
+
const memories = await phase1.services.memoryEngine.list({ limit: 1e4 });
|
|
1760
|
+
const portable = memories.map(toPortableFromRecord);
|
|
1720
1761
|
const outputPath = opts.output ? join3(process.cwd(), opts.output) : writeMemoryFile(portable);
|
|
1721
1762
|
if (opts.output) {
|
|
1722
1763
|
writeFileSync3(outputPath, JSON.stringify(portable, null, 2) + "\n", "utf-8");
|
|
@@ -1738,8 +1779,7 @@ program.command("import").description(`Import memories from ${MEMORY_FILE}`).opt
|
|
|
1738
1779
|
let skipped = 0;
|
|
1739
1780
|
spinner.text = `Importing ${memories.length} memories\u2026`;
|
|
1740
1781
|
for (const memory of memories) {
|
|
1741
|
-
const
|
|
1742
|
-
const result = await upsertMemory({
|
|
1782
|
+
const result = await phase1.services.memoryEngine.remember({
|
|
1743
1783
|
type: memory.type,
|
|
1744
1784
|
scope: memory.scope,
|
|
1745
1785
|
architecture: memory.architecture,
|
|
@@ -1748,8 +1788,7 @@ program.command("import").description(`Import memories from ${MEMORY_FILE}`).opt
|
|
|
1748
1788
|
content: memory.content,
|
|
1749
1789
|
reason: memory.reason,
|
|
1750
1790
|
context: memory.context,
|
|
1751
|
-
tags: memory.tags
|
|
1752
|
-
embedding
|
|
1791
|
+
tags: memory.tags
|
|
1753
1792
|
});
|
|
1754
1793
|
if (result === "inserted") inserted++;
|
|
1755
1794
|
else skipped++;
|
|
@@ -1770,7 +1809,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1770
1809
|
const config = readProjectConfig();
|
|
1771
1810
|
const architectures = opts.arch ? opts.arch : opts.all ? void 0 : getCurrentListArchitectures(config);
|
|
1772
1811
|
const projectName = opts.project ? opts.project : opts.all || opts.arch ? void 0 : config?.projectName;
|
|
1773
|
-
const memories = await
|
|
1812
|
+
const memories = await phase1.services.memoryEngine.list({
|
|
1774
1813
|
type: opts.type,
|
|
1775
1814
|
scope: opts.scope,
|
|
1776
1815
|
architecture: architectures,
|
|
@@ -1779,7 +1818,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1779
1818
|
limit: parseInt(opts.limit, 10)
|
|
1780
1819
|
});
|
|
1781
1820
|
const title = opts.all ? "All memories" : `Current project memories${architectures ? ` (${Array.isArray(architectures) ? architectures.join(", ") : architectures})` : ""}`;
|
|
1782
|
-
printMemoryTable(memories, title);
|
|
1821
|
+
printMemoryTable(memories.map(toMemoryTableRow), title);
|
|
1783
1822
|
if (!opts.all) {
|
|
1784
1823
|
console.log(chalk2.gray(" Showing current project context plus shared/global memories. Use --all for the full database.\n"));
|
|
1785
1824
|
}
|
|
@@ -1793,7 +1832,7 @@ program.command("list").description("List memories from the local database").opt
|
|
|
1793
1832
|
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
1833
|
try {
|
|
1795
1834
|
const config = readProjectConfig();
|
|
1796
|
-
const deleted = await
|
|
1835
|
+
const deleted = await phase1.services.memoryEngine.removeById(parseInt(id, 10));
|
|
1797
1836
|
if (!deleted) {
|
|
1798
1837
|
console.log(chalk2.yellow(`No memory found with ID ${id}`));
|
|
1799
1838
|
process.exit(1);
|
|
@@ -1810,7 +1849,7 @@ program.command("remove <id>").description("Remove a memory by ID").option("--no
|
|
|
1810
1849
|
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
1850
|
try {
|
|
1812
1851
|
const config = readProjectConfig();
|
|
1813
|
-
const deleted = await
|
|
1852
|
+
const deleted = await phase1.services.memoryEngine.removeMany({
|
|
1814
1853
|
tag: opts.tag,
|
|
1815
1854
|
scope: opts.scope,
|
|
1816
1855
|
type: opts.type,
|
|
@@ -1831,7 +1870,7 @@ program.command("edit <id>").description("Edit a memory interactively").option("
|
|
|
1831
1870
|
const memoryId = parseInt(id, 10);
|
|
1832
1871
|
try {
|
|
1833
1872
|
const config = readProjectConfig();
|
|
1834
|
-
const existing = await
|
|
1873
|
+
const existing = await phase1.services.memoryEngine.getById(memoryId);
|
|
1835
1874
|
if (!existing) {
|
|
1836
1875
|
console.log(chalk2.yellow(`No memory found with ID ${id}`));
|
|
1837
1876
|
process.exit(1);
|
|
@@ -1846,16 +1885,14 @@ program.command("edit <id>").description("Edit a memory interactively").option("
|
|
|
1846
1885
|
const examples = await input({ message: "Examples? (comma-separated)", default: existing.context?.examples?.join(",") ?? "" });
|
|
1847
1886
|
const source = await input({ message: "Source?", default: existing.context?.source ?? "" });
|
|
1848
1887
|
const tags = await input({ message: "Tags?", default: existing.tags.join(",") });
|
|
1849
|
-
|
|
1850
|
-
await updateMemory(memoryId, {
|
|
1888
|
+
await phase1.services.memoryEngine.update(memoryId, {
|
|
1851
1889
|
type,
|
|
1852
1890
|
scope,
|
|
1853
1891
|
title: title || void 0,
|
|
1854
1892
|
content,
|
|
1855
1893
|
reason: reason || void 0,
|
|
1856
1894
|
context: buildMemoryContext({ appliesTo, avoidWhen, example: examples, source }),
|
|
1857
|
-
tags: parseTags(tags)
|
|
1858
|
-
embedding
|
|
1895
|
+
tags: parseTags(tags)
|
|
1859
1896
|
});
|
|
1860
1897
|
console.log(chalk2.green(`Updated memory ${id}`));
|
|
1861
1898
|
await autoSyncGeneratedFiles(config, "edit", opts.sync);
|
|
@@ -1870,11 +1907,12 @@ program.command("ignore [pattern]").description("Manage project-scoped false-pos
|
|
|
1870
1907
|
try {
|
|
1871
1908
|
const config = readProjectConfig();
|
|
1872
1909
|
if (opts.list) {
|
|
1873
|
-
|
|
1910
|
+
const ignores = await phase1.services.memoryEngine.list({ type: "ignore", limit: 1e3 });
|
|
1911
|
+
printMemoryTable(ignores.map(toMemoryTableRow), "Ignored patterns");
|
|
1874
1912
|
return;
|
|
1875
1913
|
}
|
|
1876
1914
|
if (opts.remove) {
|
|
1877
|
-
const deleted = await
|
|
1915
|
+
const deleted = await phase1.services.memoryEngine.removeById(parseInt(opts.remove, 10));
|
|
1878
1916
|
if (!deleted) {
|
|
1879
1917
|
console.log(chalk2.yellow(`No ignore pattern found with ID ${opts.remove}`));
|
|
1880
1918
|
process.exit(1);
|
|
@@ -1887,15 +1925,13 @@ program.command("ignore [pattern]").description("Manage project-scoped false-pos
|
|
|
1887
1925
|
console.error(chalk2.red("Provide a pattern, --list, or --remove <id>"));
|
|
1888
1926
|
process.exit(1);
|
|
1889
1927
|
}
|
|
1890
|
-
|
|
1891
|
-
await upsertMemory({
|
|
1928
|
+
await phase1.services.memoryEngine.remember({
|
|
1892
1929
|
type: "ignore",
|
|
1893
1930
|
scope: "project",
|
|
1894
1931
|
architecture: config?.backendArchitecture ?? config?.frontendFramework,
|
|
1895
1932
|
projectName: config?.projectName,
|
|
1896
1933
|
content: pattern,
|
|
1897
|
-
tags: ["ignore"]
|
|
1898
|
-
embedding
|
|
1934
|
+
tags: ["ignore"]
|
|
1899
1935
|
});
|
|
1900
1936
|
console.log(chalk2.green(`Ignored pattern saved: "${pattern}"`));
|
|
1901
1937
|
await autoSyncGeneratedFiles(config, "ignore", opts.sync);
|
|
@@ -2020,7 +2056,7 @@ program.command("stats").description("Show violation counters recorded by check
|
|
|
2020
2056
|
console.log();
|
|
2021
2057
|
});
|
|
2022
2058
|
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-
|
|
2059
|
+
const { startDashboard } = await import("./dashboard-server-AUX4BQP6.js");
|
|
2024
2060
|
await startDashboard({
|
|
2025
2061
|
port: parseInt(opts.port, 10),
|
|
2026
2062
|
path: opts.path,
|
|
@@ -2038,7 +2074,6 @@ program.command("seed").description("Load all predefined memories into the datab
|
|
|
2038
2074
|
for (const seed of filtered) {
|
|
2039
2075
|
const spinner = ora(`[${seed.architecture}] ${seed.title}`).start();
|
|
2040
2076
|
try {
|
|
2041
|
-
const embedding = await embed(seed.content);
|
|
2042
2077
|
const payload = {
|
|
2043
2078
|
type: seed.type,
|
|
2044
2079
|
scope: seed.scope,
|
|
@@ -2046,15 +2081,14 @@ program.command("seed").description("Load all predefined memories into the datab
|
|
|
2046
2081
|
title: seed.title,
|
|
2047
2082
|
content: seed.content,
|
|
2048
2083
|
reason: seed.reason,
|
|
2049
|
-
tags: seed.tags
|
|
2050
|
-
embedding
|
|
2084
|
+
tags: seed.tags
|
|
2051
2085
|
};
|
|
2052
2086
|
if (opts.force) {
|
|
2053
|
-
await
|
|
2087
|
+
await phase1.services.memoryEngine.rememberForce(payload);
|
|
2054
2088
|
saved++;
|
|
2055
2089
|
spinner.succeed(chalk2.gray(`[${seed.architecture}] ${seed.title}`));
|
|
2056
2090
|
} else {
|
|
2057
|
-
const result = await
|
|
2091
|
+
const result = await phase1.services.memoryEngine.remember(payload);
|
|
2058
2092
|
if (result === "inserted") {
|
|
2059
2093
|
saved++;
|
|
2060
2094
|
spinner.succeed(chalk2.gray(`[${seed.architecture}] ${seed.title}`));
|
|
@@ -2097,7 +2131,11 @@ program.command("global").description("Sync your memory into every AI agent glob
|
|
|
2097
2131
|
let memories = [];
|
|
2098
2132
|
try {
|
|
2099
2133
|
const architectures = opts.arch ? [opts.arch] : inferProjectArchitectures(process.cwd(), readProjectConfig());
|
|
2100
|
-
memories =
|
|
2134
|
+
memories = (await phase1.services.retrievalEngine.retrieve({
|
|
2135
|
+
text: "architecture rules coding standards",
|
|
2136
|
+
architectures,
|
|
2137
|
+
limit: 30
|
|
2138
|
+
})).items;
|
|
2101
2139
|
} catch (err) {
|
|
2102
2140
|
spinner.fail(`Could not fetch memories: ${err.message}`);
|
|
2103
2141
|
process.exit(1);
|
|
@@ -2256,6 +2294,164 @@ program.command("status").description("Show the current memory-core project and
|
|
|
2256
2294
|
process.exit(1);
|
|
2257
2295
|
}
|
|
2258
2296
|
});
|
|
2297
|
+
var graph = program.command("graph").description("Build and inspect dependency graph snapshots");
|
|
2298
|
+
graph.command("migrate").description("Create or update PostgreSQL graph snapshot schema").action(async () => {
|
|
2299
|
+
const { values } = readRuntimeEnv();
|
|
2300
|
+
if (!values.DATABASE_URL) {
|
|
2301
|
+
console.error(chalk2.red("Graph migration requires DATABASE_URL. Configure it in .memory-core.env or .env."));
|
|
2302
|
+
process.exit(1);
|
|
2303
|
+
}
|
|
2304
|
+
const spinner = ora("Migrating graph snapshot schema\u2026").start();
|
|
2305
|
+
try {
|
|
2306
|
+
await migrateGraphSnapshots();
|
|
2307
|
+
spinner.succeed("Graph snapshot schema is ready in PostgreSQL");
|
|
2308
|
+
} catch (err) {
|
|
2309
|
+
spinner.fail(`Graph migration failed: ${err.message}`);
|
|
2310
|
+
process.exit(1);
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
graph.command("doctor").description("Inspect graph storage backend health (PostgreSQL + file fallback)").option("--path <dir>", "Project root path (default: current directory)").action(async (opts) => {
|
|
2314
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2315
|
+
const { values } = readRuntimeEnv();
|
|
2316
|
+
const filePath = graphStoreFilePath(cwd);
|
|
2317
|
+
const usesPostgres = Boolean(values.DATABASE_URL);
|
|
2318
|
+
let ok = true;
|
|
2319
|
+
console.log(chalk2.bold("\n graph doctor\n"));
|
|
2320
|
+
printStatusLine("Project path", cwd);
|
|
2321
|
+
printStatusLine("Configured backend", usesPostgres ? "postgres + file fallback" : "file");
|
|
2322
|
+
printStatusLine("File store", filePath);
|
|
2323
|
+
console.log();
|
|
2324
|
+
if (!usesPostgres) {
|
|
2325
|
+
console.log(chalk2.yellow(" \u26A0 DATABASE_URL not set \u2014 using file backend only."));
|
|
2326
|
+
} else {
|
|
2327
|
+
try {
|
|
2328
|
+
await migrateGraphSnapshots();
|
|
2329
|
+
const probe = await probeGraphSnapshotStore(cwd);
|
|
2330
|
+
console.log(chalk2.green(" \u2713 Graph PostgreSQL") + chalk2.dim(` ready (${probe.snapshotCount} snapshot records for this root)`));
|
|
2331
|
+
} catch (err) {
|
|
2332
|
+
ok = false;
|
|
2333
|
+
console.log(chalk2.red(" \u2717 Graph PostgreSQL") + chalk2.dim(` ${err.message}`));
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
try {
|
|
2337
|
+
const count = await getGraphSnapshotCount(cwd);
|
|
2338
|
+
console.log(chalk2.green(" \u2713 Graph service") + chalk2.dim(` readable (${count ?? 0} snapshots visible)`));
|
|
2339
|
+
} catch (err) {
|
|
2340
|
+
ok = false;
|
|
2341
|
+
console.log(chalk2.red(" \u2717 Graph service") + chalk2.dim(` ${err.message}`));
|
|
2342
|
+
}
|
|
2343
|
+
if (existsSync3(filePath)) {
|
|
2344
|
+
console.log(chalk2.green(" \u2713 File fallback") + chalk2.dim(" store exists"));
|
|
2345
|
+
} else {
|
|
2346
|
+
console.log(chalk2.yellow(" \u26A0 File fallback") + chalk2.dim(" store not created yet (run graph build once)"));
|
|
2347
|
+
}
|
|
2348
|
+
console.log();
|
|
2349
|
+
if (!ok) process.exit(1);
|
|
2350
|
+
});
|
|
2351
|
+
graph.command("build").description("Build a dependency graph snapshot and persist it").option("--path <dir>", "Project root path (default: current directory)").action(async (opts) => {
|
|
2352
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2353
|
+
const spinner = ora("Building dependency graph\u2026").start();
|
|
2354
|
+
try {
|
|
2355
|
+
const { snapshot } = await phase1.services.graphEngine.buildAndStoreSnapshot({ cwd });
|
|
2356
|
+
spinner.succeed(`Graph snapshot saved: ${snapshot.id}`);
|
|
2357
|
+
console.log(chalk2.gray(` Root: ${snapshot.rootPath}`));
|
|
2358
|
+
console.log(chalk2.gray(` Nodes: ${snapshot.nodes.length}`));
|
|
2359
|
+
console.log(chalk2.gray(` Edges: ${snapshot.edges.length}
|
|
2360
|
+
`));
|
|
2361
|
+
} catch (err) {
|
|
2362
|
+
spinner.fail(`Graph build failed: ${err.message}`);
|
|
2363
|
+
process.exit(1);
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
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) => {
|
|
2367
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2368
|
+
try {
|
|
2369
|
+
const snapshots = await phase1.services.graphEngine.listSnapshots(cwd, parseInt(opts.limit, 10));
|
|
2370
|
+
if (snapshots.length === 0) {
|
|
2371
|
+
console.log(chalk2.yellow("\n No graph snapshots found. Run: memory-core graph build\n"));
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
console.log(chalk2.bold("\n Graph snapshots\n"));
|
|
2375
|
+
snapshots.forEach((snapshot, index) => {
|
|
2376
|
+
console.log(` ${index + 1}. ${snapshot.id} ${chalk2.dim(snapshot.createdAt)} nodes=${snapshot.nodes.length} edges=${snapshot.edges.length}`);
|
|
2377
|
+
});
|
|
2378
|
+
console.log();
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
console.error(chalk2.red(`Graph list failed: ${err.message}`));
|
|
2381
|
+
process.exit(1);
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
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) => {
|
|
2385
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2386
|
+
try {
|
|
2387
|
+
const snapshot = snapshotId ? await phase1.services.graphEngine.getSnapshot(cwd, snapshotId) : await phase1.services.graphEngine.latest(cwd);
|
|
2388
|
+
if (!snapshot) {
|
|
2389
|
+
console.log(chalk2.yellow("\n No matching graph snapshot found. Run: memory-core graph build\n"));
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
console.log(chalk2.bold(`
|
|
2393
|
+
Graph ${snapshot.id ?? "(latest)"}
|
|
2394
|
+
`));
|
|
2395
|
+
console.log(chalk2.gray(` Root: ${snapshot.rootPath}`));
|
|
2396
|
+
console.log(chalk2.gray(` Created: ${snapshot.createdAt ?? "unknown"}`));
|
|
2397
|
+
console.log(chalk2.gray(` Nodes: ${snapshot.nodes.length}`));
|
|
2398
|
+
console.log(chalk2.gray(` Edges: ${snapshot.edges.length}
|
|
2399
|
+
`));
|
|
2400
|
+
const limit = parseInt(opts.edges, 10);
|
|
2401
|
+
snapshot.edges.slice(0, limit).forEach((edge, index) => {
|
|
2402
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2403
|
+
});
|
|
2404
|
+
if (snapshot.edges.length > limit) {
|
|
2405
|
+
console.log(chalk2.dim(`
|
|
2406
|
+
\u2026 ${snapshot.edges.length - limit} more edges not shown
|
|
2407
|
+
`));
|
|
2408
|
+
} else {
|
|
2409
|
+
console.log();
|
|
2410
|
+
}
|
|
2411
|
+
} catch (err) {
|
|
2412
|
+
console.error(chalk2.red(`Graph show failed: ${err.message}`));
|
|
2413
|
+
process.exit(1);
|
|
2414
|
+
}
|
|
2415
|
+
});
|
|
2416
|
+
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) => {
|
|
2417
|
+
const cwd = resolve(opts.path ?? process.cwd());
|
|
2418
|
+
try {
|
|
2419
|
+
const left = await phase1.services.graphEngine.getSnapshot(cwd, leftSnapshotId);
|
|
2420
|
+
if (!left) {
|
|
2421
|
+
console.error(chalk2.red(`Left snapshot not found: ${leftSnapshotId}`));
|
|
2422
|
+
process.exit(1);
|
|
2423
|
+
}
|
|
2424
|
+
const right = rightSnapshotId ? await phase1.services.graphEngine.getSnapshot(cwd, rightSnapshotId) : await phase1.services.graphEngine.latest(cwd);
|
|
2425
|
+
if (!right) {
|
|
2426
|
+
console.error(chalk2.red(`Right snapshot not found${rightSnapshotId ? `: ${rightSnapshotId}` : ""}`));
|
|
2427
|
+
process.exit(1);
|
|
2428
|
+
}
|
|
2429
|
+
const diff = phase1.services.graphEngine.diffGraphs(left, right);
|
|
2430
|
+
console.log(chalk2.bold("\n Graph diff\n"));
|
|
2431
|
+
console.log(chalk2.gray(` Left: ${left.id}`));
|
|
2432
|
+
console.log(chalk2.gray(` Right: ${right.id}`));
|
|
2433
|
+
console.log(chalk2.green(` + Nodes: ${diff.addedNodes.length}`));
|
|
2434
|
+
console.log(chalk2.red(` - Nodes: ${diff.removedNodes.length}`));
|
|
2435
|
+
console.log(chalk2.green(` + Edges: ${diff.addedEdges.length}`));
|
|
2436
|
+
console.log(chalk2.red(` - Edges: ${diff.removedEdges.length}`));
|
|
2437
|
+
if (diff.addedEdges.length > 0) {
|
|
2438
|
+
console.log(chalk2.green("\n Added edges"));
|
|
2439
|
+
diff.addedEdges.slice(0, 20).forEach((edge, index) => {
|
|
2440
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
if (diff.removedEdges.length > 0) {
|
|
2444
|
+
console.log(chalk2.red("\n Removed edges"));
|
|
2445
|
+
diff.removedEdges.slice(0, 20).forEach((edge, index) => {
|
|
2446
|
+
console.log(` ${index + 1}. ${abbreviate(edge.from)} ${chalk2.dim(`--${edge.kind}-->`)} ${abbreviate(edge.to)}`);
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
console.log();
|
|
2450
|
+
} catch (err) {
|
|
2451
|
+
console.error(chalk2.red(`Graph diff failed: ${err.message}`));
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
});
|
|
2259
2455
|
var hook = program.command("hook").description("Manage the pre-commit rule enforcement hook");
|
|
2260
2456
|
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
2457
|
const advisory = opts.strict ? false : true;
|
|
@@ -2271,7 +2467,11 @@ program.command("check").description("Check staged changes against architecture
|
|
|
2271
2467
|
}
|
|
2272
2468
|
await checkStaged({ verbose: opts.verbose ?? false, debug: opts.debug ?? false });
|
|
2273
2469
|
});
|
|
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
|
-
|
|
2470
|
+
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) => {
|
|
2471
|
+
await phase1.providers.watchService.start({
|
|
2472
|
+
path: opts.path,
|
|
2473
|
+
verbose: opts.verbose,
|
|
2474
|
+
debug: opts.debug
|
|
2475
|
+
});
|
|
2276
2476
|
});
|
|
2277
2477
|
program.parseAsync(process.argv);
|