memorix 1.1.8 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1787,6 +1787,7 @@ function loadYamlConfig(projectRoot) {
1787
1787
  agent: { ...userConfig.agent, ...projectConfig.agent },
1788
1788
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
1789
1789
  git: { ...userConfig.git, ...projectConfig.git },
1790
+ codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
1790
1791
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
1791
1792
  server: { ...userConfig.server, ...projectConfig.server },
1792
1793
  team: { ...userConfig.team, ...projectConfig.team }
@@ -2069,6 +2070,7 @@ function mergeTomlConfig(base, override) {
2069
2070
  embedding: { ...base.embedding, ...override.embedding },
2070
2071
  hooks: { ...base.hooks, ...override.hooks },
2071
2072
  git: { ...base.git, ...override.git },
2073
+ codegraph: { ...base.codegraph, ...override.codegraph },
2072
2074
  server: { ...base.server, ...override.server }
2073
2075
  };
2074
2076
  }
@@ -2210,6 +2212,15 @@ var init_dotenv_loader = __esm({
2210
2212
  });
2211
2213
 
2212
2214
  // src/config/resolved-config.ts
2215
+ var resolved_config_exports = {};
2216
+ __export(resolved_config_exports, {
2217
+ getResolvedAgentLane: () => getResolvedAgentLane,
2218
+ getResolvedConfig: () => getResolvedConfig,
2219
+ getResolvedConfigForCwd: () => getResolvedConfigForCwd,
2220
+ getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
2221
+ getResolvedMemoryLane: () => getResolvedMemoryLane,
2222
+ resetResolvedConfigCache: () => resetResolvedConfigCache
2223
+ });
2213
2224
  import { homedir as homedir5 } from "os";
2214
2225
  import { existsSync as existsSync6 } from "fs";
2215
2226
  function getResolvedConfig(options = {}) {
@@ -2287,6 +2298,9 @@ function getResolvedConfig(options = {}) {
2287
2298
  excludePatterns: firstArray(toml.git?.exclude_patterns, yaml2.git?.excludePatterns),
2288
2299
  noiseKeywords: firstArray(toml.git?.noise_keywords, yaml2.git?.noiseKeywords)
2289
2300
  },
2301
+ codegraph: {
2302
+ excludePatterns: firstArray(toml.codegraph?.exclude_patterns, yaml2.codegraph?.excludePatterns)
2303
+ },
2290
2304
  server: {
2291
2305
  transport: first(toml.server?.transport, yaml2.server?.transport),
2292
2306
  dashboard: firstBool(toml.server?.dashboard, yaml2.server?.dashboard),
@@ -2308,6 +2322,10 @@ function getResolvedConfig(options = {}) {
2308
2322
  };
2309
2323
  return resolved;
2310
2324
  }
2325
+ function getResolvedConfigForCwd(cwd = process.cwd()) {
2326
+ const project = detectProject(cwd);
2327
+ return getResolvedConfig({ projectRoot: project?.rootPath ?? null });
2328
+ }
2311
2329
  function getResolvedAgentLane(options = {}) {
2312
2330
  const resolved = getResolvedConfig(options);
2313
2331
  return {
@@ -2324,6 +2342,8 @@ function getResolvedMemoryLane(options = {}) {
2324
2342
  function getResolvedEmbeddingLane(options = {}) {
2325
2343
  return getResolvedConfig(options).embedding;
2326
2344
  }
2345
+ function resetResolvedConfigCache() {
2346
+ }
2327
2347
  function first(...values) {
2328
2348
  return values.find((value) => value !== void 0 && value !== null && value !== "");
2329
2349
  }
@@ -4818,7 +4838,7 @@ __export(orama_store_exports, {
4818
4838
  resetDb: () => resetDb,
4819
4839
  searchObservations: () => searchObservations
4820
4840
  });
4821
- import { create, insert as insert2, search, remove as remove2, update, count } from "@orama/orama";
4841
+ import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
4822
4842
  function getLastSearchMode(projectId) {
4823
4843
  return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
4824
4844
  }
@@ -4829,8 +4849,12 @@ function makeEntryKey(projectId, observationId) {
4829
4849
  return `${projectId ?? ""}::${observationId}`;
4830
4850
  }
4831
4851
  function rememberObservationDoc(doc) {
4832
- if (!doc.projectId || typeof doc.observationId !== "number") return;
4833
- docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), doc);
4852
+ const publicDoc = { ...doc };
4853
+ delete publicDoc.embedding;
4854
+ if (doc.projectId && typeof doc.observationId === "number") {
4855
+ docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
4856
+ }
4857
+ return publicDoc;
4834
4858
  }
4835
4859
  function isCommandLikeQuery(query) {
4836
4860
  return COMMAND_LIKE_QUERY2.test(query);
@@ -4932,14 +4956,14 @@ async function batchGenerateEmbeddings(texts) {
4932
4956
  }
4933
4957
  async function hydrateIndex(observations2) {
4934
4958
  const database = await getDb();
4935
- const currentCount = await count(database);
4936
- if (currentCount > 0) return 0;
4937
4959
  let inserted = 0;
4938
4960
  for (const obs of observations2) {
4939
4961
  if (!obs || !obs.id || !obs.projectId) continue;
4940
4962
  try {
4963
+ const id = makeOramaObservationId(obs.projectId, obs.id);
4964
+ if (getByID(database, id)) continue;
4941
4965
  const doc = {
4942
- id: makeOramaObservationId(obs.projectId, obs.id),
4966
+ id,
4943
4967
  observationId: obs.id,
4944
4968
  entityName: obs.entityName || "",
4945
4969
  type: obs.type || "discovery",
@@ -5033,6 +5057,7 @@ async function searchObservations(options) {
5033
5057
  let searchParams = {
5034
5058
  term: originalQuery,
5035
5059
  limit: requestLimit,
5060
+ includeVectors: true,
5036
5061
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
5037
5062
  // Search specific fields (not tokens, accessCount, etc.)
5038
5063
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
@@ -5104,6 +5129,7 @@ async function searchObservations(options) {
5104
5129
  const vectorOnlyParams = {
5105
5130
  term: "",
5106
5131
  limit: requestLimit,
5132
+ includeVectors: true,
5107
5133
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
5108
5134
  mode: "vector",
5109
5135
  vector: {
@@ -5402,6 +5428,7 @@ async function getObservationsByIds(ids, projectId) {
5402
5428
  }
5403
5429
  const searchResult = await search(database, {
5404
5430
  term: "",
5431
+ includeVectors: true,
5405
5432
  where: {
5406
5433
  observationId: { eq: id },
5407
5434
  ...projectId ? { projectId } : {}
@@ -5409,7 +5436,7 @@ async function getObservationsByIds(ids, projectId) {
5409
5436
  limit: 1
5410
5437
  });
5411
5438
  if (searchResult.hits.length > 0) {
5412
- results.push(searchResult.hits[0].document);
5439
+ results.push(rememberObservationDoc(searchResult.hits[0].document));
5413
5440
  }
5414
5441
  }
5415
5442
  return results;
@@ -6377,6 +6404,44 @@ var init_store = __esm({
6377
6404
  ORDER BY path, startLine
6378
6405
  LIMIT ?
6379
6406
  `).all(projectId, like, like, like, limit).map(rowToSymbol);
6407
+ }
6408
+ listSymbols(projectId) {
6409
+ return this.db.prepare(`
6410
+ SELECT * FROM code_symbols
6411
+ WHERE projectId = ? AND stale = 0
6412
+ ORDER BY path, startLine
6413
+ `).all(projectId).map(rowToSymbol);
6414
+ }
6415
+ findSymbolsByNames(projectId, names, fileIds = []) {
6416
+ const candidates = [...new Set(names.map((name) => name.trim()).filter(Boolean))];
6417
+ if (candidates.length === 0) return [];
6418
+ const candidateJson = JSON.stringify(candidates);
6419
+ const hintedFiles = [...new Set(fileIds.map((fileId) => fileId.trim()).filter(Boolean))];
6420
+ if (hintedFiles.length > 0) {
6421
+ return this.db.prepare(`
6422
+ SELECT * FROM code_symbols
6423
+ WHERE projectId = ?
6424
+ AND stale = 0
6425
+ AND name IN (SELECT value FROM json_each(?))
6426
+ AND fileId IN (SELECT value FROM json_each(?))
6427
+ ORDER BY path, startLine
6428
+ `).all(projectId, candidateJson, JSON.stringify(hintedFiles)).map(rowToSymbol);
6429
+ }
6430
+ return this.db.prepare(`
6431
+ SELECT symbols.*
6432
+ FROM code_symbols AS symbols
6433
+ INNER JOIN (
6434
+ SELECT name
6435
+ FROM code_symbols
6436
+ WHERE projectId = ?
6437
+ AND stale = 0
6438
+ AND name IN (SELECT value FROM json_each(?))
6439
+ GROUP BY name
6440
+ HAVING COUNT(*) = 1
6441
+ ) AS unambiguous ON unambiguous.name = symbols.name
6442
+ WHERE symbols.projectId = ? AND symbols.stale = 0
6443
+ ORDER BY symbols.path, symbols.startLine
6444
+ `).all(projectId, candidateJson, projectId).map(rowToSymbol);
6380
6445
  }
6381
6446
  listSymbolsForFile(fileId) {
6382
6447
  return this.db.prepare(`SELECT * FROM code_symbols WHERE fileId = ? AND stale = 0 ORDER BY startLine`).all(fileId).map(rowToSymbol);
@@ -6390,6 +6455,22 @@ var init_store = __esm({
6390
6455
  WHERE projectId = ? AND observationId = ?
6391
6456
  ORDER BY status, id
6392
6457
  `).all(projectId, observationId).map(rowToRef);
6458
+ }
6459
+ listProjectObservationRefs(projectId) {
6460
+ return this.db.prepare(`
6461
+ SELECT * FROM observation_code_refs
6462
+ WHERE projectId = ?
6463
+ ORDER BY observationId, status, id
6464
+ `).all(projectId).map(rowToRef);
6465
+ }
6466
+ listReferencedSymbols(projectId) {
6467
+ return this.db.prepare(`
6468
+ SELECT DISTINCT symbols.*
6469
+ FROM code_symbols AS symbols
6470
+ INNER JOIN observation_code_refs AS refs ON refs.symbolId = symbols.id
6471
+ WHERE refs.projectId = ? AND symbols.projectId = ? AND symbols.stale = 0
6472
+ ORDER BY symbols.path, symbols.startLine
6473
+ `).all(projectId, projectId).map(rowToSymbol);
6393
6474
  }
6394
6475
  status(projectId) {
6395
6476
  const files = this.db.prepare(`SELECT COUNT(*) AS count FROM code_files WHERE projectId = ?`).get(projectId).count;
@@ -6416,6 +6497,15 @@ function mentionsSymbol(text, symbol) {
6416
6497
  const name = symbol.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6417
6498
  return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
6418
6499
  }
6500
+ function identifierCandidates(text) {
6501
+ return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
6502
+ }
6503
+ function codeIdentifierCandidates(text) {
6504
+ const explicit = /* @__PURE__ */ new Set();
6505
+ for (const match of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match[1]);
6506
+ for (const match of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match[1]);
6507
+ return identifierCandidates(text).filter((candidate) => explicit.has(candidate) || candidate.includes("::") || /[!?=]$/.test(candidate) || /^[A-Z]/.test(candidate) || /[_$]/.test(candidate) || /[a-z0-9][A-Z]/.test(candidate) || /[A-Z]{2}/.test(candidate));
6508
+ }
6419
6509
  function fileRef(projectId, obs, file) {
6420
6510
  return {
6421
6511
  id: makeObservationCodeRefId(projectId, obs.id, file.id),
@@ -6442,40 +6532,101 @@ function symbolRef(projectId, obs, file, symbol) {
6442
6532
  createdAt: obs.createdAt
6443
6533
  };
6444
6534
  }
6445
- async function bindObservationToCode(store, obs) {
6535
+ function resolveObservationCodeRefs(obs, lookup) {
6446
6536
  const refs = /* @__PURE__ */ new Map();
6447
6537
  const text = observationText(obs);
6448
6538
  const candidateFiles = /* @__PURE__ */ new Map();
6449
6539
  for (const rawPath of obs.filesModified ?? []) {
6450
- const file = store.getFile(obs.projectId, normalizeCodePath(rawPath));
6540
+ const file = lookup.findFile(normalizeCodePath(rawPath));
6451
6541
  if (!file) continue;
6452
6542
  candidateFiles.set(file.id, file);
6453
6543
  const ref = fileRef(obs.projectId, obs, file);
6454
6544
  refs.set(ref.id, ref);
6455
6545
  }
6456
- const symbols = store.findSymbols(obs.projectId, "", 500);
6546
+ const hintedFileIds = new Set(candidateFiles.keys());
6547
+ const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
6548
+ const symbols = lookup.findSymbols(symbolNames, [...hintedFileIds]);
6549
+ const symbolsByName = /* @__PURE__ */ new Map();
6457
6550
  for (const symbol of symbols) {
6458
- if (!mentionsSymbol(text, symbol)) continue;
6459
- const file = candidateFiles.get(symbol.fileId) ?? store.getFile(obs.projectId, symbol.path);
6460
- if (!file) continue;
6461
- candidateFiles.set(file.id, file);
6462
- const ref = symbolRef(obs.projectId, obs, file, symbol);
6463
- refs.set(ref.id, ref);
6551
+ const group = symbolsByName.get(symbol.name) ?? [];
6552
+ group.push(symbol);
6553
+ symbolsByName.set(symbol.name, group);
6464
6554
  }
6465
- const result = [...refs.values()];
6466
- store.replaceObservationRefs(obs.projectId, obs.id, result);
6467
- return result;
6555
+ for (const group of symbolsByName.values()) {
6556
+ const candidates = group.length === 1 ? group : [];
6557
+ for (const symbol of candidates) {
6558
+ if (!mentionsSymbol(text, symbol)) continue;
6559
+ const file = candidateFiles.get(symbol.fileId) ?? lookup.findFile(symbol.path);
6560
+ if (!file) continue;
6561
+ candidateFiles.set(file.id, file);
6562
+ const ref = symbolRef(obs.projectId, obs, file, symbol);
6563
+ refs.set(ref.id, ref);
6564
+ }
6565
+ }
6566
+ return [...refs.values()];
6567
+ }
6568
+ function createStoreLookup(store, projectId) {
6569
+ return {
6570
+ findFile: (path20) => store.getFile(projectId, path20) ?? void 0,
6571
+ findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
6572
+ };
6573
+ }
6574
+ function createSnapshotLookup(files, symbols) {
6575
+ const filesByPath = new Map(files.map((file) => [normalizeCodePath(file.path), file]));
6576
+ const symbolsByName = /* @__PURE__ */ new Map();
6577
+ for (const symbol of symbols) {
6578
+ const group = symbolsByName.get(symbol.name) ?? [];
6579
+ group.push(symbol);
6580
+ symbolsByName.set(symbol.name, group);
6581
+ }
6582
+ return {
6583
+ findFile: (path20) => filesByPath.get(normalizeCodePath(path20)),
6584
+ findSymbols: (names, hintedFileIds) => {
6585
+ const hinted = new Set(hintedFileIds);
6586
+ const found = [];
6587
+ for (const name of names) {
6588
+ const candidates = symbolsByName.get(name) ?? [];
6589
+ found.push(...hinted.size > 0 ? candidates.filter((symbol) => hinted.has(symbol.fileId)) : candidates);
6590
+ }
6591
+ return found;
6592
+ }
6593
+ };
6594
+ }
6595
+ async function bindObservationToCode(store, obs) {
6596
+ const refs = resolveObservationCodeRefs(obs, createStoreLookup(store, obs.projectId));
6597
+ store.replaceObservationRefs(obs.projectId, obs.id, refs);
6598
+ return refs;
6468
6599
  }
6469
6600
  async function backfillMissingObservationCodeRefs(store, observations2) {
6470
6601
  let observationsBackfilled = 0;
6471
6602
  let refsBackfilled = 0;
6603
+ const boundObservationIdsByProject = /* @__PURE__ */ new Map();
6604
+ const lookupByProject = /* @__PURE__ */ new Map();
6605
+ for (const projectId of new Set(observations2.map((observation) => observation.projectId))) {
6606
+ boundObservationIdsByProject.set(
6607
+ projectId,
6608
+ new Set(store.listProjectObservationRefs(projectId).map((ref) => ref.observationId))
6609
+ );
6610
+ lookupByProject.set(
6611
+ projectId,
6612
+ createSnapshotLookup(store.listFiles(projectId), store.listSymbols(projectId))
6613
+ );
6614
+ }
6615
+ const refsToInsert = [];
6472
6616
  for (const observation of observations2) {
6473
- if (store.listObservationRefs(observation.projectId, observation.id).length > 0) continue;
6474
- const refs = await bindObservationToCode(store, observation);
6617
+ if (boundObservationIdsByProject.get(observation.projectId)?.has(observation.id)) continue;
6618
+ if ((observation.filesModified?.length ?? 0) === 0 && codeIdentifierCandidates(observationText(observation)).length === 0) {
6619
+ continue;
6620
+ }
6621
+ const lookup = lookupByProject.get(observation.projectId);
6622
+ if (!lookup) continue;
6623
+ const refs = resolveObservationCodeRefs(observation, lookup);
6475
6624
  if (refs.length === 0) continue;
6476
6625
  observationsBackfilled += 1;
6477
6626
  refsBackfilled += refs.length;
6627
+ refsToInsert.push(...refs);
6478
6628
  }
6629
+ store.upsertObservationRefs(refsToInsert);
6479
6630
  return {
6480
6631
  observationsScanned: observations2.length,
6481
6632
  observationsBackfilled,
@@ -9296,6 +9447,58 @@ var init_current_facts = __esm({
9296
9447
  }
9297
9448
  });
9298
9449
 
9450
+ // src/codegraph/exclude.ts
9451
+ function normalizeCodeGraphExcludePatterns(exclude) {
9452
+ return [.../* @__PURE__ */ new Set([
9453
+ ...DEFAULT_CODEGRAPH_EXCLUDES,
9454
+ ...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
9455
+ ])];
9456
+ }
9457
+ function isCodeGraphExcludedPath(path20, exclude) {
9458
+ const normalized = normalizeCodePath2(path20);
9459
+ return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
9460
+ }
9461
+ function normalizeCodePath2(path20) {
9462
+ return path20.replace(/\\/g, "/").replace(/^\.\/+/, "");
9463
+ }
9464
+ function matchesPattern(path20, pattern) {
9465
+ if (pattern.endsWith("/**")) {
9466
+ const base = pattern.slice(0, -3);
9467
+ if (base.startsWith("**/")) {
9468
+ const suffix = base.slice(3);
9469
+ return path20 === suffix || path20.endsWith(`/${suffix}`) || path20.includes(`/${suffix}/`);
9470
+ }
9471
+ if (!base.includes("/")) {
9472
+ return path20 === base || path20.startsWith(`${base}/`) || path20.includes(`/${base}/`);
9473
+ }
9474
+ return path20 === base || path20.startsWith(`${base}/`);
9475
+ }
9476
+ if (pattern.startsWith("**/")) {
9477
+ const suffix = pattern.slice(3);
9478
+ return path20 === suffix || path20.endsWith(`/${suffix}`);
9479
+ }
9480
+ return path20 === pattern || path20.startsWith(`${pattern}/`);
9481
+ }
9482
+ var DEFAULT_CODEGRAPH_EXCLUDES;
9483
+ var init_exclude = __esm({
9484
+ "src/codegraph/exclude.ts"() {
9485
+ "use strict";
9486
+ init_esm_shims();
9487
+ DEFAULT_CODEGRAPH_EXCLUDES = [
9488
+ "node_modules/**",
9489
+ "dist/**",
9490
+ "build/**",
9491
+ "coverage/**",
9492
+ ".next/**",
9493
+ ".turbo/**",
9494
+ ".git/**",
9495
+ ".tmp/**",
9496
+ ".worktrees/**",
9497
+ ".claude/worktrees/**"
9498
+ ];
9499
+ }
9500
+ });
9501
+
9299
9502
  // src/codegraph/lite-provider.ts
9300
9503
  import { createHash as createHash5 } from "crypto";
9301
9504
  import { readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
@@ -9311,18 +9514,6 @@ function languageForPath(path20) {
9311
9514
  const ext = extension(path20);
9312
9515
  return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
9313
9516
  }
9314
- function isExcluded(path20, exclude) {
9315
- const normalized = normalizeCodePath(path20);
9316
- return exclude.some((pattern) => {
9317
- const p = normalizeCodePath(pattern);
9318
- if (p.endsWith("/**")) {
9319
- const base = p.slice(0, -3);
9320
- return normalized === base || normalized.startsWith(`${base}/`);
9321
- }
9322
- if (p.startsWith("**/")) return normalized.endsWith(p.slice(3));
9323
- return normalized === p || normalized.startsWith(`${p}/`);
9324
- });
9325
- }
9326
9517
  function walk(root, exclude, maxFiles) {
9327
9518
  const out = [];
9328
9519
  const visit = (dir) => {
@@ -9336,7 +9527,7 @@ function walk(root, exclude, maxFiles) {
9336
9527
  for (const entry of entries) {
9337
9528
  const abs = join20(dir, entry.name);
9338
9529
  const rel = normalizeCodePath(relative(root, abs));
9339
- if (isExcluded(rel, exclude)) continue;
9530
+ if (isCodeGraphExcludedPath(rel, exclude)) continue;
9340
9531
  if (entry.isDirectory()) {
9341
9532
  if (entry.name === ".git" || entry.name === ".worktrees" || entry.name === ".tmp" || entry.name === "node_modules") continue;
9342
9533
  if (rel === ".claude/worktrees" || rel.startsWith(".claude/worktrees/")) continue;
@@ -9424,18 +9615,7 @@ function extractImportEdges(projectId, file, text, indexedAt) {
9424
9615
  return edges;
9425
9616
  }
9426
9617
  async function indexProjectLite(options) {
9427
- const exclude = options.exclude ?? [
9428
- "node_modules/**",
9429
- "dist/**",
9430
- "build/**",
9431
- "coverage/**",
9432
- ".next/**",
9433
- ".turbo/**",
9434
- ".git/**",
9435
- ".tmp/**",
9436
- ".worktrees/**",
9437
- ".claude/worktrees/**"
9438
- ];
9618
+ const exclude = normalizeCodeGraphExcludePatterns(options.exclude);
9439
9619
  const maxFiles = options.maxFiles ?? 5e3;
9440
9620
  const indexedAt = (/* @__PURE__ */ new Date()).toISOString();
9441
9621
  const paths = walk(options.projectRoot, exclude, maxFiles);
@@ -9474,6 +9654,7 @@ var init_lite_provider = __esm({
9474
9654
  "use strict";
9475
9655
  init_esm_shims();
9476
9656
  init_ids();
9657
+ init_exclude();
9477
9658
  LANGUAGE_BY_EXTENSION = /* @__PURE__ */ new Map([
9478
9659
  [".ts", "typescript"],
9479
9660
  [".tsx", "typescript"],
@@ -9585,8 +9766,8 @@ var init_lite_provider = __esm({
9585
9766
  },
9586
9767
  ruby: {
9587
9768
  symbols: [
9588
- { kind: "class", re: /^class\s+([A-Za-z_][\w:]*?)\b/gm },
9589
- { kind: "function", re: /^def\s+([A-Za-z_][\w!?=]*)\b/gm }
9769
+ { kind: "class", re: /^class\s+([A-Za-z_][\w]*(?:::[A-Za-z_][\w]*)*)/gm },
9770
+ { kind: "function", re: /^def\s+([A-Za-z_][\w]*[!?=]?)/gm }
9590
9771
  ],
9591
9772
  imports: [/require\s+['"]([^'"]+)['"]/g]
9592
9773
  },
@@ -9643,30 +9824,24 @@ function countLanguages(files) {
9643
9824
  }
9644
9825
  return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
9645
9826
  }
9646
- function normalizePath2(path20) {
9647
- return path20.replace(/\\/g, "/");
9648
- }
9649
- function isGeneratedPath(path20) {
9650
- const normalized = normalizePath2(path20);
9651
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
9652
- }
9653
9827
  function suggestedReadRank(path20) {
9654
- const normalized = normalizePath2(path20);
9828
+ const normalized = path20.replace(/\\/g, "/");
9655
9829
  if (normalized.startsWith("src/")) return 0;
9656
9830
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
9657
9831
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
9658
9832
  return 3;
9659
9833
  }
9660
- function compactSuggestedReads(paths, limit = 8) {
9661
- return uniq(paths).filter((path20) => !isGeneratedPath(path20)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
9834
+ function compactSuggestedReads(paths, limit = 8, exclude) {
9835
+ return uniq(paths).filter((path20) => !isCodeGraphExcludedPath(path20, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
9662
9836
  }
9663
- function collectGraph(store, projectId, observations2) {
9837
+ function collectGraph(store, projectId, observations2, exclude) {
9664
9838
  const files = store.listFiles(projectId);
9665
- const symbols = files.flatMap((file) => store.listSymbolsForFile(file.id));
9839
+ const symbols = store.listReferencedSymbols(projectId);
9666
9840
  const filesById = new Map(files.map((file) => [file.id, file]));
9667
9841
  const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
9668
9842
  const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
9669
- const refs = observations2.flatMap((obs) => store.listObservationRefs(projectId, obs.id));
9843
+ const activeObservationIds = new Set(observations2.map((obs) => obs.id));
9844
+ const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
9670
9845
  const freshness = {
9671
9846
  current: 0,
9672
9847
  suspect: 0,
@@ -9682,7 +9857,9 @@ function collectGraph(store, projectId, observations2) {
9682
9857
  freshness[result.status] += 1;
9683
9858
  const observation = observationsById.get(ref.observationId);
9684
9859
  if (!observation) continue;
9685
- if (result.status === "current" && file) suggestedReads.push(file.path);
9860
+ const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
9861
+ if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
9862
+ if (excluded) continue;
9686
9863
  sources.push({
9687
9864
  observationId: observation.id,
9688
9865
  title: observation.title,
@@ -9699,12 +9876,10 @@ function collectGraph(store, projectId, observations2) {
9699
9876
  refs,
9700
9877
  freshness,
9701
9878
  sources,
9702
- suggestedReads: compactSuggestedReads(suggestedReads)
9879
+ suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
9703
9880
  };
9704
9881
  }
9705
- function buildProjectContextOverview(input) {
9706
- const active = activeObservations(input.observations, input.project.id);
9707
- const graph = collectGraph(input.store, input.project.id, active);
9882
+ function overviewFromGraph(input) {
9708
9883
  const status = input.store.status(input.project.id);
9709
9884
  return {
9710
9885
  project: input.project,
@@ -9715,20 +9890,26 @@ function buildProjectContextOverview(input) {
9715
9890
  edges: status.edges,
9716
9891
  refs: status.refs,
9717
9892
  ...status.indexedAt ? { indexedAt: status.indexedAt } : {},
9718
- languages: countLanguages(graph.files)
9893
+ languages: countLanguages(input.graph.files)
9719
9894
  },
9720
9895
  memory: {
9721
9896
  total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
9722
- active: active.length
9897
+ active: input.active.length
9723
9898
  },
9724
- freshness: graph.freshness,
9725
- suggestedReads: graph.suggestedReads
9899
+ freshness: input.graph.freshness,
9900
+ suggestedReads: input.graph.suggestedReads
9726
9901
  };
9727
9902
  }
9728
9903
  function buildProjectContextExplain(input) {
9729
- const overview = buildProjectContextOverview(input);
9730
9904
  const active = activeObservations(input.observations, input.project.id);
9731
- const graph = collectGraph(input.store, input.project.id, active);
9905
+ const graph = collectGraph(input.store, input.project.id, active, input.exclude);
9906
+ const overview = overviewFromGraph({
9907
+ project: input.project,
9908
+ store: input.store,
9909
+ observations: input.observations,
9910
+ active,
9911
+ graph
9912
+ });
9732
9913
  return {
9733
9914
  project: input.project,
9734
9915
  sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
@@ -9740,11 +9921,12 @@ var init_project_context = __esm({
9740
9921
  "use strict";
9741
9922
  init_esm_shims();
9742
9923
  init_freshness2();
9924
+ init_exclude();
9743
9925
  }
9744
9926
  });
9745
9927
 
9746
9928
  // src/codegraph/task-lens.ts
9747
- function normalizePath3(path20) {
9929
+ function normalizePath2(path20) {
9748
9930
  return path20.replace(/\\/g, "/");
9749
9931
  }
9750
9932
  function tokenize(text) {
@@ -9774,7 +9956,7 @@ function resolveTaskLens(task) {
9774
9956
  return best.score > 0 ? LENSES[best.id] : LENSES.general;
9775
9957
  }
9776
9958
  function pathKindScore(path20, lens) {
9777
- const normalized = normalizePath3(path20).toLowerCase();
9959
+ const normalized = normalizePath2(path20).toLowerCase();
9778
9960
  const name = normalized.split("/").pop() ?? normalized;
9779
9961
  const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
9780
9962
  const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
@@ -9823,7 +10005,7 @@ function sourceTaskMatchScore(source, task) {
9823
10005
  return 0;
9824
10006
  }
9825
10007
  function fallbackPathRank(path20) {
9826
- const normalized = normalizePath3(path20);
10008
+ const normalized = normalizePath2(path20);
9827
10009
  if (normalized.startsWith("src/")) return 0;
9828
10010
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
9829
10011
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
@@ -9832,7 +10014,7 @@ function fallbackPathRank(path20) {
9832
10014
  }
9833
10015
  function rankLensPaths(paths, lens, task) {
9834
10016
  const tokens = tokenize(task ?? "");
9835
- return [...new Set(paths.map(normalizePath3))].map((path20, index) => ({
10017
+ return [...new Set(paths.map(normalizePath2))].map((path20, index) => ({
9836
10018
  path: path20,
9837
10019
  index,
9838
10020
  score: pathKindScore(path20, lens) + tokenScore(path20, tokens),
@@ -10086,6 +10268,7 @@ async function buildAutoProjectContext(input) {
10086
10268
  const now = input.now ?? /* @__PURE__ */ new Date();
10087
10269
  const task = input.task?.trim();
10088
10270
  const lens = resolveTaskLens(task);
10271
+ const exclude = input.exclude ?? getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph.excludePatterns;
10089
10272
  const store = new CodeGraphStore();
10090
10273
  await store.init(input.dataDir);
10091
10274
  const initialStatus = store.status(input.project.id);
@@ -10103,7 +10286,8 @@ async function buildAutoProjectContext(input) {
10103
10286
  try {
10104
10287
  const indexed = await indexProjectLite({
10105
10288
  projectId: input.project.id,
10106
- projectRoot: input.project.rootPath
10289
+ projectRoot: input.project.rootPath,
10290
+ exclude
10107
10291
  });
10108
10292
  store.replaceProjectIndex(input.project.id, indexed);
10109
10293
  const backfill = await backfillMissingObservationCodeRefs(
@@ -10120,16 +10304,13 @@ async function buildAutoProjectContext(input) {
10120
10304
  };
10121
10305
  }
10122
10306
  }
10123
- const overview = buildProjectContextOverview({
10124
- project: input.project,
10125
- store,
10126
- observations: input.observations
10127
- });
10128
10307
  const explain = buildProjectContextExplain({
10129
10308
  project: input.project,
10130
10309
  store,
10131
- observations: input.observations
10310
+ observations: input.observations,
10311
+ exclude
10132
10312
  });
10313
+ const overview = explain.overview;
10133
10314
  return {
10134
10315
  project: input.project,
10135
10316
  ...task ? { task } : {},
@@ -10338,6 +10519,7 @@ var init_auto_context = __esm({
10338
10519
  "src/codegraph/auto-context.ts"() {
10339
10520
  "use strict";
10340
10521
  init_esm_shims();
10522
+ init_resolved_config();
10341
10523
  init_binder();
10342
10524
  init_current_facts();
10343
10525
  init_lite_provider();
@@ -10365,10 +10547,6 @@ function tokenize2(text) {
10365
10547
  function timestampOf(observation) {
10366
10548
  return Date.parse(observation.updatedAt ?? observation.createdAt ?? "") || 0;
10367
10549
  }
10368
- function isGeneratedPath2(path20) {
10369
- const normalized = path20.replace(/\\/g, "/");
10370
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
10371
- }
10372
10550
  function relevanceScore(observation, taskTokens) {
10373
10551
  if (taskTokens.length === 0) return 0;
10374
10552
  const title = observation.title.toLowerCase();
@@ -10419,7 +10597,8 @@ function assembleContextPackForTask(input) {
10419
10597
  refs,
10420
10598
  files,
10421
10599
  symbols,
10422
- suggestedVerification: input.suggestedVerification
10600
+ suggestedVerification: input.suggestedVerification,
10601
+ exclude: input.exclude
10423
10602
  });
10424
10603
  }
10425
10604
  function assembleContextPack(input) {
@@ -10438,6 +10617,7 @@ function assembleContextPack(input) {
10438
10617
  observationIdsWithRefs.add(ref.observationId);
10439
10618
  const file = ref.fileId ? files.get(ref.fileId) : void 0;
10440
10619
  const symbol = ref.symbolId ? symbols.get(ref.symbolId) : void 0;
10620
+ if (file && isCodeGraphExcludedPath(file.path, input.exclude)) continue;
10441
10621
  const freshness = evaluateCodeRefFreshness(ref, file, symbol);
10442
10622
  if (freshness.status === "current") {
10443
10623
  const memoryKey = `${observation.id}:${freshness.status}`;
@@ -10503,8 +10683,8 @@ function buildContextPackPrompt(pack) {
10503
10683
  const reliableMemories = pack.memories.filter((memory) => memory.status === "current");
10504
10684
  const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
10505
10685
  const lines = ["## Task", pack.task, "", "## Reliable Memories"];
10506
- const visibleCodeFacts = pack.codeFacts.filter((fact) => !isGeneratedPath2(fact.path)).slice(0, 5);
10507
- const visibleSuggestedReads = pack.suggestedReads.filter((path20) => !isGeneratedPath2(path20)).slice(0, 5);
10686
+ const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
10687
+ const visibleSuggestedReads = pack.suggestedReads.filter((path20) => !isCodeGraphExcludedPath(path20)).slice(0, 5);
10508
10688
  if (reliableMemories.length === 0) lines.push("- none");
10509
10689
  for (const memory of reliableMemories) {
10510
10690
  lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
@@ -10539,6 +10719,7 @@ var init_context_pack = __esm({
10539
10719
  "use strict";
10540
10720
  init_esm_shims();
10541
10721
  init_freshness2();
10722
+ init_exclude();
10542
10723
  }
10543
10724
  });
10544
10725
 
@@ -20009,7 +20190,7 @@ The path should point to a directory containing a .git folder.`
20009
20190
  };
20010
20191
  const server = existingServer ?? new McpServer({
20011
20192
  name: "memorix",
20012
- version: true ? "1.1.8" : "1.0.1"
20193
+ version: true ? "1.1.10" : "1.0.1"
20013
20194
  });
20014
20195
  const originalRegisterTool = server.registerTool.bind(server);
20015
20196
  server.registerTool = ((name, ...args) => {
@@ -20711,15 +20892,18 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
20711
20892
  return withFreshIndex(async () => {
20712
20893
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
20713
20894
  const { assembleContextPackForTask: assembleContextPackForTask2, buildContextPackPrompt: buildContextPackPrompt2 } = await Promise.resolve().then(() => (init_context_pack(), context_pack_exports));
20895
+ const { getResolvedConfig: getResolvedConfig2 } = await Promise.resolve().then(() => (init_resolved_config(), resolved_config_exports));
20714
20896
  const store = new CodeGraphStore2();
20715
20897
  await store.init(projectDir2);
20898
+ const exclude = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph.excludePatterns;
20716
20899
  const observations2 = getAllObservations().filter((obs) => obs.projectId === project.id && (obs.status ?? "active") === "active").reverse();
20717
20900
  const pack = assembleContextPackForTask2({
20718
20901
  store,
20719
20902
  projectId: project.id,
20720
20903
  task,
20721
20904
  observations: observations2,
20722
- limit: typeof limit === "number" ? limit : 20
20905
+ limit: typeof limit === "number" ? limit : 20,
20906
+ exclude
20723
20907
  });
20724
20908
  const text = buildContextPackPrompt2(pack);
20725
20909
  return {