engramx 1.0.1 → 2.0.1

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +67 -7
  3. package/dist/{aider-context-TNGSXMVY.js → aider-context-BC5R2ZTA.js} +1 -1
  4. package/dist/cache-AK6CF3BC.js +10 -0
  5. package/dist/chunk-22INHMKB.js +31 -0
  6. package/dist/chunk-533LR4I7.js +220 -0
  7. package/dist/{chunk-QOG4K427.js → chunk-C6GBUOAL.js} +1 -1
  8. package/dist/chunk-CIQQ5Y3S.js +338 -0
  9. package/dist/chunk-KL6NSPVA.js +59 -0
  10. package/dist/{chunk-SBHGK5WA.js → chunk-PEH54LYC.js} +85 -3
  11. package/dist/{chunk-6SFMVYUN.js → chunk-SJT7VS2G.js} +127 -23
  12. package/dist/cli.js +383 -258
  13. package/dist/{core-77MHT3QV.js → core-6IY5L6II.js} +2 -2
  14. package/dist/{cursor-mdc-HWVUZUZH.js → cursor-mdc-GJ7E5LDD.js} +1 -1
  15. package/dist/{exporter-A3VSLS4U.js → exporter-GWU2GF23.js} +1 -1
  16. package/dist/grammars/tree-sitter-go.wasm +0 -0
  17. package/dist/grammars/tree-sitter-javascript.wasm +0 -0
  18. package/dist/grammars/tree-sitter-python.wasm +0 -0
  19. package/dist/grammars/tree-sitter-rust.wasm +0 -0
  20. package/dist/grammars/tree-sitter-tsx.wasm +0 -0
  21. package/dist/grammars/tree-sitter-typescript.wasm +0 -0
  22. package/dist/{importer-LU2YFZDY.js → importer-V62NGZRK.js} +1 -1
  23. package/dist/index.js +3 -3
  24. package/dist/{migrate-5ZJWF2HD.js → migrate-UKCO6BUU.js} +3 -1
  25. package/dist/plugin-loader-STTGYIL5.js +106 -0
  26. package/dist/serve.js +2 -2
  27. package/dist/server-6AOI7NQP.js +1370 -0
  28. package/dist/{tuner-2LVIEE5V.js → tuner-KFNNGKG3.js} +4 -2
  29. package/dist/windsurf-rules-C7SVDHBL.js +59 -0
  30. package/package.json +4 -3
  31. package/dist/chunk-CEAANHHX.js +0 -88
  32. package/dist/server-I3C74ZLB.js +0 -193
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runMigrations
3
- } from "./chunk-SBHGK5WA.js";
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 statSync2 } from "fs";
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
- function extractDirectory(dirPath, rootDir) {
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 (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "__pycache__" || entry.name === "vendor" || entry.name === ".engram") {
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 { nodes, edges } = extractFile(fullPath, root);
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
- try {
1242
- const content = readFileSync2(fullPath, "utf-8");
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 = statSync(resolved).isDirectory();
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
- const { nodes, edges, fileCount, totalLines } = extractDirectory(root);
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
- store.clearAll();
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 = statSync2(dbPath).mtimeMs;
1981
+ graphMtimeMs = statSync3(dbPath).mtimeMs;
1878
1982
  } catch {
1879
1983
  return empty;
1880
1984
  }
1881
1985
  let fileMtimeMs = null;
1882
1986
  try {
1883
- fileMtimeMs = statSync2(abs).mtimeMs;
1987
+ fileMtimeMs = statSync3(abs).mtimeMs;
1884
1988
  } catch {
1885
1989
  fileMtimeMs = null;
1886
1990
  }