engramx 1.0.1 → 2.0.0
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/CHANGELOG.md +118 -0
- package/README.md +67 -7
- package/dist/{aider-context-TNGSXMVY.js → aider-context-BC5R2ZTA.js} +1 -1
- package/dist/cache-AK6CF3BC.js +10 -0
- package/dist/chunk-22INHMKB.js +31 -0
- package/dist/chunk-533LR4I7.js +220 -0
- package/dist/{chunk-QOG4K427.js → chunk-C6GBUOAL.js} +1 -1
- package/dist/chunk-CIQQ5Y3S.js +338 -0
- package/dist/chunk-KL6NSPVA.js +59 -0
- package/dist/{chunk-SBHGK5WA.js → chunk-PEH54LYC.js} +85 -3
- package/dist/{chunk-6SFMVYUN.js → chunk-SJT7VS2G.js} +127 -23
- package/dist/cli.js +380 -258
- package/dist/{core-77MHT3QV.js → core-6IY5L6II.js} +2 -2
- package/dist/{cursor-mdc-HWVUZUZH.js → cursor-mdc-GJ7E5LDD.js} +1 -1
- package/dist/{exporter-A3VSLS4U.js → exporter-GWU2GF23.js} +1 -1
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/grammars/tree-sitter-python.wasm +0 -0
- package/dist/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/{importer-LU2YFZDY.js → importer-V62NGZRK.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/{migrate-5ZJWF2HD.js → migrate-UKCO6BUU.js} +3 -1
- package/dist/plugin-loader-FCOMVOX7.js +100 -0
- package/dist/serve.js +2 -2
- package/dist/server-VBRTTECZ.js +1363 -0
- package/dist/{tuner-2LVIEE5V.js → tuner-KFNNGKG3.js} +4 -2
- package/dist/windsurf-rules-C7SVDHBL.js +59 -0
- package/package.json +4 -3
- package/dist/chunk-CEAANHHX.js +0 -88
- package/dist/server-I3C74ZLB.js +0 -193
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runMigrations
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PEH54LYC.js";
|
|
4
4
|
|
|
5
5
|
// src/core.ts
|
|
6
6
|
import { join as join4, resolve as resolve2, relative as relative2 } from "path";
|
|
7
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2, unlinkSync, statSync as
|
|
7
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2, unlinkSync, statSync as statSync3 } from "fs";
|
|
8
8
|
import { homedir } from "os";
|
|
9
9
|
|
|
10
10
|
// src/graph/store.ts
|
|
@@ -326,6 +326,15 @@ var GraphStore = class _GraphStore {
|
|
|
326
326
|
[key, value]
|
|
327
327
|
);
|
|
328
328
|
}
|
|
329
|
+
/** Remove all nodes and edges originating from a specific source file. */
|
|
330
|
+
removeNodesForFile(sourceFile) {
|
|
331
|
+
this.db.run(
|
|
332
|
+
`DELETE FROM edges WHERE source IN (SELECT id FROM nodes WHERE source_file = ?)
|
|
333
|
+
OR target IN (SELECT id FROM nodes WHERE source_file = ?)`,
|
|
334
|
+
[sourceFile, sourceFile]
|
|
335
|
+
);
|
|
336
|
+
this.db.run("DELETE FROM nodes WHERE source_file = ?", [sourceFile]);
|
|
337
|
+
}
|
|
329
338
|
clearAll() {
|
|
330
339
|
this.db.run("DELETE FROM nodes");
|
|
331
340
|
this.db.run("DELETE FROM edges");
|
|
@@ -458,6 +467,22 @@ var GraphStore = class _GraphStore {
|
|
|
458
467
|
ttl: row.ttl ?? 3600
|
|
459
468
|
};
|
|
460
469
|
}
|
|
470
|
+
// ─── Low-level DB access (for cache module) ──────────────────
|
|
471
|
+
/** Run raw SQL (DDL, DML). For cache table creation and updates. */
|
|
472
|
+
runSql(sql, params) {
|
|
473
|
+
if (params && params.length > 0) {
|
|
474
|
+
const stmt = this.db.prepare(sql);
|
|
475
|
+
stmt.bind(params);
|
|
476
|
+
stmt.step();
|
|
477
|
+
stmt.free();
|
|
478
|
+
} else {
|
|
479
|
+
this.db.run(sql);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/** Prepare a statement for row-by-row iteration. Caller must free(). */
|
|
483
|
+
prepare(sql) {
|
|
484
|
+
return this.db.prepare(sql);
|
|
485
|
+
}
|
|
461
486
|
// ─── Lifecycle ────────────────────────────────────────────────
|
|
462
487
|
close() {
|
|
463
488
|
this.save();
|
|
@@ -865,7 +890,7 @@ function toPosixPath(p) {
|
|
|
865
890
|
}
|
|
866
891
|
|
|
867
892
|
// src/miners/ast-miner.ts
|
|
868
|
-
import { readFileSync as readFileSync2, readdirSync, realpathSync } from "fs";
|
|
893
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, realpathSync, statSync } from "fs";
|
|
869
894
|
import { basename, extname, join, relative } from "path";
|
|
870
895
|
var LANG_CONFIGS = {
|
|
871
896
|
typescript: {
|
|
@@ -950,7 +975,7 @@ function makeId(...parts) {
|
|
|
950
975
|
function extractFile(filePath, rootDir) {
|
|
951
976
|
const ext = extname(filePath).toLowerCase();
|
|
952
977
|
const lang = EXT_TO_LANG[ext];
|
|
953
|
-
if (!lang) return { nodes: [], edges: [] };
|
|
978
|
+
if (!lang) return { nodes: [], edges: [], lineCount: 0 };
|
|
954
979
|
const content = readFileSync2(filePath, "utf-8");
|
|
955
980
|
const lines = content.split("\n");
|
|
956
981
|
const relPath = toPosixPath(relative(rootDir, filePath));
|
|
@@ -1019,7 +1044,7 @@ function extractFile(filePath, rootDir) {
|
|
|
1019
1044
|
addEdge
|
|
1020
1045
|
);
|
|
1021
1046
|
}
|
|
1022
|
-
return { nodes, edges };
|
|
1047
|
+
return { nodes, edges, lineCount: lines.length };
|
|
1023
1048
|
}
|
|
1024
1049
|
function extractWithPatterns(content, lines, lang, fileId, stem, relPath, addNode, addEdge) {
|
|
1025
1050
|
const patterns = getPatterns(lang);
|
|
@@ -1205,13 +1230,57 @@ function getPatterns(lang) {
|
|
|
1205
1230
|
return { classes: [], functions: [], imports: [], exports: [] };
|
|
1206
1231
|
}
|
|
1207
1232
|
}
|
|
1208
|
-
|
|
1233
|
+
var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1234
|
+
"node_modules",
|
|
1235
|
+
"dist",
|
|
1236
|
+
"build",
|
|
1237
|
+
"__pycache__",
|
|
1238
|
+
"vendor",
|
|
1239
|
+
".engram",
|
|
1240
|
+
".git",
|
|
1241
|
+
".next",
|
|
1242
|
+
".nuxt",
|
|
1243
|
+
".output",
|
|
1244
|
+
"coverage",
|
|
1245
|
+
".turbo",
|
|
1246
|
+
".cache",
|
|
1247
|
+
"target",
|
|
1248
|
+
// Rust
|
|
1249
|
+
"venv",
|
|
1250
|
+
".venv",
|
|
1251
|
+
"env"
|
|
1252
|
+
]);
|
|
1253
|
+
function loadIgnorePatterns(rootDir) {
|
|
1254
|
+
const ignorePath = join(rootDir, ".engramignore");
|
|
1255
|
+
const patterns = /* @__PURE__ */ new Set();
|
|
1256
|
+
if (!existsSync2(ignorePath)) return patterns;
|
|
1257
|
+
try {
|
|
1258
|
+
const content = readFileSync2(ignorePath, "utf-8");
|
|
1259
|
+
for (const line of content.split("\n")) {
|
|
1260
|
+
const trimmed = line.trim();
|
|
1261
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1262
|
+
patterns.add(trimmed.replace(/\/+$/, ""));
|
|
1263
|
+
}
|
|
1264
|
+
} catch {
|
|
1265
|
+
}
|
|
1266
|
+
return patterns;
|
|
1267
|
+
}
|
|
1268
|
+
function extractDirectory(dirPath, rootDir, options = {}) {
|
|
1209
1269
|
const root = rootDir ?? dirPath;
|
|
1210
1270
|
const allNodes = [];
|
|
1211
1271
|
const allEdges = [];
|
|
1212
1272
|
let fileCount = 0;
|
|
1213
1273
|
let totalLines = 0;
|
|
1274
|
+
let skippedCount = 0;
|
|
1275
|
+
const mtimes = /* @__PURE__ */ new Map();
|
|
1214
1276
|
const visitedDirs = /* @__PURE__ */ new Set();
|
|
1277
|
+
const ignorePatterns = loadIgnorePatterns(root);
|
|
1278
|
+
function shouldSkipDir(name) {
|
|
1279
|
+
if (name.startsWith(".")) return true;
|
|
1280
|
+
if (DEFAULT_SKIP_DIRS.has(name)) return true;
|
|
1281
|
+
if (ignorePatterns.has(name)) return true;
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1215
1284
|
function walk(dir) {
|
|
1216
1285
|
let realDir;
|
|
1217
1286
|
try {
|
|
@@ -1225,28 +1294,35 @@ function extractDirectory(dirPath, rootDir) {
|
|
|
1225
1294
|
for (const entry of entries) {
|
|
1226
1295
|
const fullPath = join(dir, entry.name);
|
|
1227
1296
|
if (entry.isDirectory()) {
|
|
1228
|
-
if (
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1297
|
+
if (shouldSkipDir(entry.name)) continue;
|
|
1231
1298
|
walk(fullPath);
|
|
1232
1299
|
continue;
|
|
1233
1300
|
}
|
|
1234
1301
|
if (!entry.isFile()) continue;
|
|
1235
1302
|
const ext = extname(entry.name).toLowerCase();
|
|
1236
1303
|
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
1237
|
-
const
|
|
1304
|
+
const relPath = toPosixPath(relative(root, fullPath));
|
|
1305
|
+
if (ignorePatterns.has(entry.name) || ignorePatterns.has(relPath)) continue;
|
|
1306
|
+
const fileMtime = statSync(fullPath).mtimeMs;
|
|
1307
|
+
mtimes.set(relPath, fileMtime);
|
|
1308
|
+
if (options.previousMtimes) {
|
|
1309
|
+
const prevMtime = options.previousMtimes.get(relPath);
|
|
1310
|
+
if (prevMtime !== void 0 && prevMtime === fileMtime) {
|
|
1311
|
+
skippedCount++;
|
|
1312
|
+
options.onProgress?.(fileCount, skippedCount, relPath);
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
const { nodes, edges, lineCount } = extractFile(fullPath, root);
|
|
1238
1317
|
allNodes.push(...nodes);
|
|
1239
1318
|
allEdges.push(...edges);
|
|
1240
1319
|
fileCount++;
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
totalLines += content.split("\n").length;
|
|
1244
|
-
} catch {
|
|
1245
|
-
}
|
|
1320
|
+
totalLines += lineCount;
|
|
1321
|
+
options.onProgress?.(fileCount, skippedCount, relPath);
|
|
1246
1322
|
}
|
|
1247
1323
|
}
|
|
1248
1324
|
walk(dirPath);
|
|
1249
|
-
return { nodes: allNodes, edges: allEdges, fileCount, totalLines };
|
|
1325
|
+
return { nodes: allNodes, edges: allEdges, fileCount, totalLines, mtimes, skippedCount };
|
|
1250
1326
|
}
|
|
1251
1327
|
|
|
1252
1328
|
// src/miners/git-miner.ts
|
|
@@ -1465,7 +1541,7 @@ import {
|
|
|
1465
1541
|
readFileSync as readFileSync4,
|
|
1466
1542
|
readdirSync as readdirSync3,
|
|
1467
1543
|
realpathSync as realpathSync2,
|
|
1468
|
-
statSync
|
|
1544
|
+
statSync as statSync2
|
|
1469
1545
|
} from "fs";
|
|
1470
1546
|
import { basename as basename3, dirname as dirname2, join as join3 } from "path";
|
|
1471
1547
|
function makeId4(...parts) {
|
|
@@ -1600,7 +1676,7 @@ function discoverSkillFiles(skillsDir) {
|
|
|
1600
1676
|
if (entry.isSymbolicLink()) {
|
|
1601
1677
|
try {
|
|
1602
1678
|
const resolved = realpathSync2(join3(skillsDir, entry.name));
|
|
1603
|
-
isDir =
|
|
1679
|
+
isDir = statSync2(resolved).isDirectory();
|
|
1604
1680
|
} catch {
|
|
1605
1681
|
continue;
|
|
1606
1682
|
}
|
|
@@ -1763,7 +1839,22 @@ async function init(projectRoot, options = {}) {
|
|
|
1763
1839
|
throw err;
|
|
1764
1840
|
}
|
|
1765
1841
|
try {
|
|
1766
|
-
|
|
1842
|
+
let previousMtimes;
|
|
1843
|
+
if (options.incremental) {
|
|
1844
|
+
const store2 = await getStore(root);
|
|
1845
|
+
try {
|
|
1846
|
+
const mtimeJson = store2.getStat("file_mtimes");
|
|
1847
|
+
if (mtimeJson) {
|
|
1848
|
+
previousMtimes = new Map(JSON.parse(mtimeJson));
|
|
1849
|
+
}
|
|
1850
|
+
} finally {
|
|
1851
|
+
store2.close();
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
const { nodes, edges, fileCount, totalLines, mtimes, skippedCount } = extractDirectory(root, void 0, {
|
|
1855
|
+
previousMtimes,
|
|
1856
|
+
onProgress: options.onProgress
|
|
1857
|
+
});
|
|
1767
1858
|
const gitResult = mineGitHistory(root);
|
|
1768
1859
|
const sessionResult = mineSessionHistory(root);
|
|
1769
1860
|
let skillCount = 0;
|
|
@@ -1790,10 +1881,21 @@ async function init(projectRoot, options = {}) {
|
|
|
1790
1881
|
];
|
|
1791
1882
|
const store = await getStore(root);
|
|
1792
1883
|
try {
|
|
1793
|
-
|
|
1884
|
+
if (options.incremental && previousMtimes) {
|
|
1885
|
+
const clearedFiles = /* @__PURE__ */ new Set();
|
|
1886
|
+
for (const node of allNodes) {
|
|
1887
|
+
if (node.sourceFile && !clearedFiles.has(node.sourceFile)) {
|
|
1888
|
+
store.removeNodesForFile(node.sourceFile);
|
|
1889
|
+
clearedFiles.add(node.sourceFile);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
} else {
|
|
1893
|
+
store.clearAll();
|
|
1894
|
+
}
|
|
1794
1895
|
store.bulkUpsert(allNodes, allEdges);
|
|
1795
1896
|
store.setStat("last_mined", String(Date.now()));
|
|
1796
1897
|
store.setStat("project_root", root);
|
|
1898
|
+
store.setStat("file_mtimes", JSON.stringify([...mtimes.entries()]));
|
|
1797
1899
|
} finally {
|
|
1798
1900
|
store.close();
|
|
1799
1901
|
}
|
|
@@ -1803,7 +1905,9 @@ async function init(projectRoot, options = {}) {
|
|
|
1803
1905
|
fileCount,
|
|
1804
1906
|
totalLines,
|
|
1805
1907
|
timeMs: Date.now() - start,
|
|
1806
|
-
skillCount
|
|
1908
|
+
skillCount,
|
|
1909
|
+
skippedFiles: skippedCount,
|
|
1910
|
+
incremental: options.incremental ?? false
|
|
1807
1911
|
};
|
|
1808
1912
|
} finally {
|
|
1809
1913
|
try {
|
|
@@ -1874,13 +1978,13 @@ async function getFileContext(projectRoot, absFilePath) {
|
|
|
1874
1978
|
const dbPath = getDbPath(root);
|
|
1875
1979
|
let graphMtimeMs = 0;
|
|
1876
1980
|
try {
|
|
1877
|
-
graphMtimeMs =
|
|
1981
|
+
graphMtimeMs = statSync3(dbPath).mtimeMs;
|
|
1878
1982
|
} catch {
|
|
1879
1983
|
return empty;
|
|
1880
1984
|
}
|
|
1881
1985
|
let fileMtimeMs = null;
|
|
1882
1986
|
try {
|
|
1883
|
-
fileMtimeMs =
|
|
1987
|
+
fileMtimeMs = statSync3(abs).mtimeMs;
|
|
1884
1988
|
} catch {
|
|
1885
1989
|
fileMtimeMs = null;
|
|
1886
1990
|
}
|