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/sdk.js CHANGED
@@ -1791,6 +1791,7 @@ function loadYamlConfig(projectRoot) {
1791
1791
  agent: { ...userConfig.agent, ...projectConfig.agent },
1792
1792
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
1793
1793
  git: { ...userConfig.git, ...projectConfig.git },
1794
+ codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
1794
1795
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
1795
1796
  server: { ...userConfig.server, ...projectConfig.server },
1796
1797
  team: { ...userConfig.team, ...projectConfig.team }
@@ -2073,6 +2074,7 @@ function mergeTomlConfig(base, override) {
2073
2074
  embedding: { ...base.embedding, ...override.embedding },
2074
2075
  hooks: { ...base.hooks, ...override.hooks },
2075
2076
  git: { ...base.git, ...override.git },
2077
+ codegraph: { ...base.codegraph, ...override.codegraph },
2076
2078
  server: { ...base.server, ...override.server }
2077
2079
  };
2078
2080
  }
@@ -2214,6 +2216,15 @@ var init_dotenv_loader = __esm({
2214
2216
  });
2215
2217
 
2216
2218
  // src/config/resolved-config.ts
2219
+ var resolved_config_exports = {};
2220
+ __export(resolved_config_exports, {
2221
+ getResolvedAgentLane: () => getResolvedAgentLane,
2222
+ getResolvedConfig: () => getResolvedConfig,
2223
+ getResolvedConfigForCwd: () => getResolvedConfigForCwd,
2224
+ getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
2225
+ getResolvedMemoryLane: () => getResolvedMemoryLane,
2226
+ resetResolvedConfigCache: () => resetResolvedConfigCache
2227
+ });
2217
2228
  import { homedir as homedir5 } from "os";
2218
2229
  import { existsSync as existsSync6 } from "fs";
2219
2230
  function getResolvedConfig(options = {}) {
@@ -2291,6 +2302,9 @@ function getResolvedConfig(options = {}) {
2291
2302
  excludePatterns: firstArray(toml.git?.exclude_patterns, yaml2.git?.excludePatterns),
2292
2303
  noiseKeywords: firstArray(toml.git?.noise_keywords, yaml2.git?.noiseKeywords)
2293
2304
  },
2305
+ codegraph: {
2306
+ excludePatterns: firstArray(toml.codegraph?.exclude_patterns, yaml2.codegraph?.excludePatterns)
2307
+ },
2294
2308
  server: {
2295
2309
  transport: first(toml.server?.transport, yaml2.server?.transport),
2296
2310
  dashboard: firstBool(toml.server?.dashboard, yaml2.server?.dashboard),
@@ -2312,6 +2326,10 @@ function getResolvedConfig(options = {}) {
2312
2326
  };
2313
2327
  return resolved;
2314
2328
  }
2329
+ function getResolvedConfigForCwd(cwd = process.cwd()) {
2330
+ const project = detectProject(cwd);
2331
+ return getResolvedConfig({ projectRoot: project?.rootPath ?? null });
2332
+ }
2315
2333
  function getResolvedAgentLane(options = {}) {
2316
2334
  const resolved = getResolvedConfig(options);
2317
2335
  return {
@@ -2328,6 +2346,8 @@ function getResolvedMemoryLane(options = {}) {
2328
2346
  function getResolvedEmbeddingLane(options = {}) {
2329
2347
  return getResolvedConfig(options).embedding;
2330
2348
  }
2349
+ function resetResolvedConfigCache() {
2350
+ }
2331
2351
  function first(...values) {
2332
2352
  return values.find((value) => value !== void 0 && value !== null && value !== "");
2333
2353
  }
@@ -4822,7 +4842,7 @@ __export(orama_store_exports, {
4822
4842
  resetDb: () => resetDb,
4823
4843
  searchObservations: () => searchObservations
4824
4844
  });
4825
- import { create, insert as insert2, search, remove as remove2, update, count } from "@orama/orama";
4845
+ import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
4826
4846
  function getLastSearchMode(projectId) {
4827
4847
  return lastSearchModeByProject.get(projectId ?? SEARCH_MODE_DEFAULT_KEY) ?? "fulltext";
4828
4848
  }
@@ -4833,8 +4853,12 @@ function makeEntryKey(projectId, observationId) {
4833
4853
  return `${projectId ?? ""}::${observationId}`;
4834
4854
  }
4835
4855
  function rememberObservationDoc(doc) {
4836
- if (!doc.projectId || typeof doc.observationId !== "number") return;
4837
- docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), doc);
4856
+ const publicDoc = { ...doc };
4857
+ delete publicDoc.embedding;
4858
+ if (doc.projectId && typeof doc.observationId === "number") {
4859
+ docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
4860
+ }
4861
+ return publicDoc;
4838
4862
  }
4839
4863
  function isCommandLikeQuery(query) {
4840
4864
  return COMMAND_LIKE_QUERY2.test(query);
@@ -4936,14 +4960,14 @@ async function batchGenerateEmbeddings(texts) {
4936
4960
  }
4937
4961
  async function hydrateIndex(observations2) {
4938
4962
  const database = await getDb();
4939
- const currentCount = await count(database);
4940
- if (currentCount > 0) return 0;
4941
4963
  let inserted = 0;
4942
4964
  for (const obs of observations2) {
4943
4965
  if (!obs || !obs.id || !obs.projectId) continue;
4944
4966
  try {
4967
+ const id = makeOramaObservationId(obs.projectId, obs.id);
4968
+ if (getByID(database, id)) continue;
4945
4969
  const doc = {
4946
- id: makeOramaObservationId(obs.projectId, obs.id),
4970
+ id,
4947
4971
  observationId: obs.id,
4948
4972
  entityName: obs.entityName || "",
4949
4973
  type: obs.type || "discovery",
@@ -5037,6 +5061,7 @@ async function searchObservations(options) {
5037
5061
  let searchParams = {
5038
5062
  term: originalQuery,
5039
5063
  limit: requestLimit,
5064
+ includeVectors: true,
5040
5065
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
5041
5066
  // Search specific fields (not tokens, accessCount, etc.)
5042
5067
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
@@ -5108,6 +5133,7 @@ async function searchObservations(options) {
5108
5133
  const vectorOnlyParams = {
5109
5134
  term: "",
5110
5135
  limit: requestLimit,
5136
+ includeVectors: true,
5111
5137
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
5112
5138
  mode: "vector",
5113
5139
  vector: {
@@ -5406,6 +5432,7 @@ async function getObservationsByIds(ids, projectId) {
5406
5432
  }
5407
5433
  const searchResult = await search(database, {
5408
5434
  term: "",
5435
+ includeVectors: true,
5409
5436
  where: {
5410
5437
  observationId: { eq: id },
5411
5438
  ...projectId ? { projectId } : {}
@@ -5413,7 +5440,7 @@ async function getObservationsByIds(ids, projectId) {
5413
5440
  limit: 1
5414
5441
  });
5415
5442
  if (searchResult.hits.length > 0) {
5416
- results.push(searchResult.hits[0].document);
5443
+ results.push(rememberObservationDoc(searchResult.hits[0].document));
5417
5444
  }
5418
5445
  }
5419
5446
  return results;
@@ -6381,6 +6408,44 @@ var init_store = __esm({
6381
6408
  ORDER BY path, startLine
6382
6409
  LIMIT ?
6383
6410
  `).all(projectId, like, like, like, limit).map(rowToSymbol);
6411
+ }
6412
+ listSymbols(projectId) {
6413
+ return this.db.prepare(`
6414
+ SELECT * FROM code_symbols
6415
+ WHERE projectId = ? AND stale = 0
6416
+ ORDER BY path, startLine
6417
+ `).all(projectId).map(rowToSymbol);
6418
+ }
6419
+ findSymbolsByNames(projectId, names, fileIds = []) {
6420
+ const candidates = [...new Set(names.map((name) => name.trim()).filter(Boolean))];
6421
+ if (candidates.length === 0) return [];
6422
+ const candidateJson = JSON.stringify(candidates);
6423
+ const hintedFiles = [...new Set(fileIds.map((fileId) => fileId.trim()).filter(Boolean))];
6424
+ if (hintedFiles.length > 0) {
6425
+ return this.db.prepare(`
6426
+ SELECT * FROM code_symbols
6427
+ WHERE projectId = ?
6428
+ AND stale = 0
6429
+ AND name IN (SELECT value FROM json_each(?))
6430
+ AND fileId IN (SELECT value FROM json_each(?))
6431
+ ORDER BY path, startLine
6432
+ `).all(projectId, candidateJson, JSON.stringify(hintedFiles)).map(rowToSymbol);
6433
+ }
6434
+ return this.db.prepare(`
6435
+ SELECT symbols.*
6436
+ FROM code_symbols AS symbols
6437
+ INNER JOIN (
6438
+ SELECT name
6439
+ FROM code_symbols
6440
+ WHERE projectId = ?
6441
+ AND stale = 0
6442
+ AND name IN (SELECT value FROM json_each(?))
6443
+ GROUP BY name
6444
+ HAVING COUNT(*) = 1
6445
+ ) AS unambiguous ON unambiguous.name = symbols.name
6446
+ WHERE symbols.projectId = ? AND symbols.stale = 0
6447
+ ORDER BY symbols.path, symbols.startLine
6448
+ `).all(projectId, candidateJson, projectId).map(rowToSymbol);
6384
6449
  }
6385
6450
  listSymbolsForFile(fileId) {
6386
6451
  return this.db.prepare(`SELECT * FROM code_symbols WHERE fileId = ? AND stale = 0 ORDER BY startLine`).all(fileId).map(rowToSymbol);
@@ -6394,6 +6459,22 @@ var init_store = __esm({
6394
6459
  WHERE projectId = ? AND observationId = ?
6395
6460
  ORDER BY status, id
6396
6461
  `).all(projectId, observationId).map(rowToRef);
6462
+ }
6463
+ listProjectObservationRefs(projectId) {
6464
+ return this.db.prepare(`
6465
+ SELECT * FROM observation_code_refs
6466
+ WHERE projectId = ?
6467
+ ORDER BY observationId, status, id
6468
+ `).all(projectId).map(rowToRef);
6469
+ }
6470
+ listReferencedSymbols(projectId) {
6471
+ return this.db.prepare(`
6472
+ SELECT DISTINCT symbols.*
6473
+ FROM code_symbols AS symbols
6474
+ INNER JOIN observation_code_refs AS refs ON refs.symbolId = symbols.id
6475
+ WHERE refs.projectId = ? AND symbols.projectId = ? AND symbols.stale = 0
6476
+ ORDER BY symbols.path, symbols.startLine
6477
+ `).all(projectId, projectId).map(rowToSymbol);
6397
6478
  }
6398
6479
  status(projectId) {
6399
6480
  const files = this.db.prepare(`SELECT COUNT(*) AS count FROM code_files WHERE projectId = ?`).get(projectId).count;
@@ -6420,6 +6501,15 @@ function mentionsSymbol(text, symbol) {
6420
6501
  const name = symbol.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6421
6502
  return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
6422
6503
  }
6504
+ function identifierCandidates(text) {
6505
+ return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
6506
+ }
6507
+ function codeIdentifierCandidates(text) {
6508
+ const explicit = /* @__PURE__ */ new Set();
6509
+ for (const match of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match[1]);
6510
+ for (const match of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match[1]);
6511
+ 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));
6512
+ }
6423
6513
  function fileRef(projectId, obs, file) {
6424
6514
  return {
6425
6515
  id: makeObservationCodeRefId(projectId, obs.id, file.id),
@@ -6446,40 +6536,101 @@ function symbolRef(projectId, obs, file, symbol) {
6446
6536
  createdAt: obs.createdAt
6447
6537
  };
6448
6538
  }
6449
- async function bindObservationToCode(store, obs) {
6539
+ function resolveObservationCodeRefs(obs, lookup) {
6450
6540
  const refs = /* @__PURE__ */ new Map();
6451
6541
  const text = observationText(obs);
6452
6542
  const candidateFiles = /* @__PURE__ */ new Map();
6453
6543
  for (const rawPath of obs.filesModified ?? []) {
6454
- const file = store.getFile(obs.projectId, normalizeCodePath(rawPath));
6544
+ const file = lookup.findFile(normalizeCodePath(rawPath));
6455
6545
  if (!file) continue;
6456
6546
  candidateFiles.set(file.id, file);
6457
6547
  const ref = fileRef(obs.projectId, obs, file);
6458
6548
  refs.set(ref.id, ref);
6459
6549
  }
6460
- const symbols = store.findSymbols(obs.projectId, "", 500);
6550
+ const hintedFileIds = new Set(candidateFiles.keys());
6551
+ const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
6552
+ const symbols = lookup.findSymbols(symbolNames, [...hintedFileIds]);
6553
+ const symbolsByName = /* @__PURE__ */ new Map();
6461
6554
  for (const symbol of symbols) {
6462
- if (!mentionsSymbol(text, symbol)) continue;
6463
- const file = candidateFiles.get(symbol.fileId) ?? store.getFile(obs.projectId, symbol.path);
6464
- if (!file) continue;
6465
- candidateFiles.set(file.id, file);
6466
- const ref = symbolRef(obs.projectId, obs, file, symbol);
6467
- refs.set(ref.id, ref);
6555
+ const group = symbolsByName.get(symbol.name) ?? [];
6556
+ group.push(symbol);
6557
+ symbolsByName.set(symbol.name, group);
6468
6558
  }
6469
- const result = [...refs.values()];
6470
- store.replaceObservationRefs(obs.projectId, obs.id, result);
6471
- return result;
6559
+ for (const group of symbolsByName.values()) {
6560
+ const candidates = group.length === 1 ? group : [];
6561
+ for (const symbol of candidates) {
6562
+ if (!mentionsSymbol(text, symbol)) continue;
6563
+ const file = candidateFiles.get(symbol.fileId) ?? lookup.findFile(symbol.path);
6564
+ if (!file) continue;
6565
+ candidateFiles.set(file.id, file);
6566
+ const ref = symbolRef(obs.projectId, obs, file, symbol);
6567
+ refs.set(ref.id, ref);
6568
+ }
6569
+ }
6570
+ return [...refs.values()];
6571
+ }
6572
+ function createStoreLookup(store, projectId) {
6573
+ return {
6574
+ findFile: (path20) => store.getFile(projectId, path20) ?? void 0,
6575
+ findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
6576
+ };
6577
+ }
6578
+ function createSnapshotLookup(files, symbols) {
6579
+ const filesByPath = new Map(files.map((file) => [normalizeCodePath(file.path), file]));
6580
+ const symbolsByName = /* @__PURE__ */ new Map();
6581
+ for (const symbol of symbols) {
6582
+ const group = symbolsByName.get(symbol.name) ?? [];
6583
+ group.push(symbol);
6584
+ symbolsByName.set(symbol.name, group);
6585
+ }
6586
+ return {
6587
+ findFile: (path20) => filesByPath.get(normalizeCodePath(path20)),
6588
+ findSymbols: (names, hintedFileIds) => {
6589
+ const hinted = new Set(hintedFileIds);
6590
+ const found = [];
6591
+ for (const name of names) {
6592
+ const candidates = symbolsByName.get(name) ?? [];
6593
+ found.push(...hinted.size > 0 ? candidates.filter((symbol) => hinted.has(symbol.fileId)) : candidates);
6594
+ }
6595
+ return found;
6596
+ }
6597
+ };
6598
+ }
6599
+ async function bindObservationToCode(store, obs) {
6600
+ const refs = resolveObservationCodeRefs(obs, createStoreLookup(store, obs.projectId));
6601
+ store.replaceObservationRefs(obs.projectId, obs.id, refs);
6602
+ return refs;
6472
6603
  }
6473
6604
  async function backfillMissingObservationCodeRefs(store, observations2) {
6474
6605
  let observationsBackfilled = 0;
6475
6606
  let refsBackfilled = 0;
6607
+ const boundObservationIdsByProject = /* @__PURE__ */ new Map();
6608
+ const lookupByProject = /* @__PURE__ */ new Map();
6609
+ for (const projectId of new Set(observations2.map((observation) => observation.projectId))) {
6610
+ boundObservationIdsByProject.set(
6611
+ projectId,
6612
+ new Set(store.listProjectObservationRefs(projectId).map((ref) => ref.observationId))
6613
+ );
6614
+ lookupByProject.set(
6615
+ projectId,
6616
+ createSnapshotLookup(store.listFiles(projectId), store.listSymbols(projectId))
6617
+ );
6618
+ }
6619
+ const refsToInsert = [];
6476
6620
  for (const observation of observations2) {
6477
- if (store.listObservationRefs(observation.projectId, observation.id).length > 0) continue;
6478
- const refs = await bindObservationToCode(store, observation);
6621
+ if (boundObservationIdsByProject.get(observation.projectId)?.has(observation.id)) continue;
6622
+ if ((observation.filesModified?.length ?? 0) === 0 && codeIdentifierCandidates(observationText(observation)).length === 0) {
6623
+ continue;
6624
+ }
6625
+ const lookup = lookupByProject.get(observation.projectId);
6626
+ if (!lookup) continue;
6627
+ const refs = resolveObservationCodeRefs(observation, lookup);
6479
6628
  if (refs.length === 0) continue;
6480
6629
  observationsBackfilled += 1;
6481
6630
  refsBackfilled += refs.length;
6631
+ refsToInsert.push(...refs);
6482
6632
  }
6633
+ store.upsertObservationRefs(refsToInsert);
6483
6634
  return {
6484
6635
  observationsScanned: observations2.length,
6485
6636
  observationsBackfilled,
@@ -9300,6 +9451,58 @@ var init_current_facts = __esm({
9300
9451
  }
9301
9452
  });
9302
9453
 
9454
+ // src/codegraph/exclude.ts
9455
+ function normalizeCodeGraphExcludePatterns(exclude) {
9456
+ return [.../* @__PURE__ */ new Set([
9457
+ ...DEFAULT_CODEGRAPH_EXCLUDES,
9458
+ ...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
9459
+ ])];
9460
+ }
9461
+ function isCodeGraphExcludedPath(path20, exclude) {
9462
+ const normalized = normalizeCodePath2(path20);
9463
+ return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
9464
+ }
9465
+ function normalizeCodePath2(path20) {
9466
+ return path20.replace(/\\/g, "/").replace(/^\.\/+/, "");
9467
+ }
9468
+ function matchesPattern(path20, pattern) {
9469
+ if (pattern.endsWith("/**")) {
9470
+ const base = pattern.slice(0, -3);
9471
+ if (base.startsWith("**/")) {
9472
+ const suffix = base.slice(3);
9473
+ return path20 === suffix || path20.endsWith(`/${suffix}`) || path20.includes(`/${suffix}/`);
9474
+ }
9475
+ if (!base.includes("/")) {
9476
+ return path20 === base || path20.startsWith(`${base}/`) || path20.includes(`/${base}/`);
9477
+ }
9478
+ return path20 === base || path20.startsWith(`${base}/`);
9479
+ }
9480
+ if (pattern.startsWith("**/")) {
9481
+ const suffix = pattern.slice(3);
9482
+ return path20 === suffix || path20.endsWith(`/${suffix}`);
9483
+ }
9484
+ return path20 === pattern || path20.startsWith(`${pattern}/`);
9485
+ }
9486
+ var DEFAULT_CODEGRAPH_EXCLUDES;
9487
+ var init_exclude = __esm({
9488
+ "src/codegraph/exclude.ts"() {
9489
+ "use strict";
9490
+ init_esm_shims();
9491
+ DEFAULT_CODEGRAPH_EXCLUDES = [
9492
+ "node_modules/**",
9493
+ "dist/**",
9494
+ "build/**",
9495
+ "coverage/**",
9496
+ ".next/**",
9497
+ ".turbo/**",
9498
+ ".git/**",
9499
+ ".tmp/**",
9500
+ ".worktrees/**",
9501
+ ".claude/worktrees/**"
9502
+ ];
9503
+ }
9504
+ });
9505
+
9303
9506
  // src/codegraph/lite-provider.ts
9304
9507
  import { createHash as createHash5 } from "crypto";
9305
9508
  import { readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
@@ -9315,18 +9518,6 @@ function languageForPath(path20) {
9315
9518
  const ext = extension(path20);
9316
9519
  return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
9317
9520
  }
9318
- function isExcluded(path20, exclude) {
9319
- const normalized = normalizeCodePath(path20);
9320
- return exclude.some((pattern) => {
9321
- const p = normalizeCodePath(pattern);
9322
- if (p.endsWith("/**")) {
9323
- const base = p.slice(0, -3);
9324
- return normalized === base || normalized.startsWith(`${base}/`);
9325
- }
9326
- if (p.startsWith("**/")) return normalized.endsWith(p.slice(3));
9327
- return normalized === p || normalized.startsWith(`${p}/`);
9328
- });
9329
- }
9330
9521
  function walk(root, exclude, maxFiles) {
9331
9522
  const out = [];
9332
9523
  const visit = (dir) => {
@@ -9340,7 +9531,7 @@ function walk(root, exclude, maxFiles) {
9340
9531
  for (const entry of entries) {
9341
9532
  const abs = join20(dir, entry.name);
9342
9533
  const rel = normalizeCodePath(relative(root, abs));
9343
- if (isExcluded(rel, exclude)) continue;
9534
+ if (isCodeGraphExcludedPath(rel, exclude)) continue;
9344
9535
  if (entry.isDirectory()) {
9345
9536
  if (entry.name === ".git" || entry.name === ".worktrees" || entry.name === ".tmp" || entry.name === "node_modules") continue;
9346
9537
  if (rel === ".claude/worktrees" || rel.startsWith(".claude/worktrees/")) continue;
@@ -9428,18 +9619,7 @@ function extractImportEdges(projectId, file, text, indexedAt) {
9428
9619
  return edges;
9429
9620
  }
9430
9621
  async function indexProjectLite(options) {
9431
- const exclude = options.exclude ?? [
9432
- "node_modules/**",
9433
- "dist/**",
9434
- "build/**",
9435
- "coverage/**",
9436
- ".next/**",
9437
- ".turbo/**",
9438
- ".git/**",
9439
- ".tmp/**",
9440
- ".worktrees/**",
9441
- ".claude/worktrees/**"
9442
- ];
9622
+ const exclude = normalizeCodeGraphExcludePatterns(options.exclude);
9443
9623
  const maxFiles = options.maxFiles ?? 5e3;
9444
9624
  const indexedAt = (/* @__PURE__ */ new Date()).toISOString();
9445
9625
  const paths = walk(options.projectRoot, exclude, maxFiles);
@@ -9478,6 +9658,7 @@ var init_lite_provider = __esm({
9478
9658
  "use strict";
9479
9659
  init_esm_shims();
9480
9660
  init_ids();
9661
+ init_exclude();
9481
9662
  LANGUAGE_BY_EXTENSION = /* @__PURE__ */ new Map([
9482
9663
  [".ts", "typescript"],
9483
9664
  [".tsx", "typescript"],
@@ -9589,8 +9770,8 @@ var init_lite_provider = __esm({
9589
9770
  },
9590
9771
  ruby: {
9591
9772
  symbols: [
9592
- { kind: "class", re: /^class\s+([A-Za-z_][\w:]*?)\b/gm },
9593
- { kind: "function", re: /^def\s+([A-Za-z_][\w!?=]*)\b/gm }
9773
+ { kind: "class", re: /^class\s+([A-Za-z_][\w]*(?:::[A-Za-z_][\w]*)*)/gm },
9774
+ { kind: "function", re: /^def\s+([A-Za-z_][\w]*[!?=]?)/gm }
9594
9775
  ],
9595
9776
  imports: [/require\s+['"]([^'"]+)['"]/g]
9596
9777
  },
@@ -9647,30 +9828,24 @@ function countLanguages(files) {
9647
9828
  }
9648
9829
  return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
9649
9830
  }
9650
- function normalizePath2(path20) {
9651
- return path20.replace(/\\/g, "/");
9652
- }
9653
- function isGeneratedPath(path20) {
9654
- const normalized = normalizePath2(path20);
9655
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
9656
- }
9657
9831
  function suggestedReadRank(path20) {
9658
- const normalized = normalizePath2(path20);
9832
+ const normalized = path20.replace(/\\/g, "/");
9659
9833
  if (normalized.startsWith("src/")) return 0;
9660
9834
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
9661
9835
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
9662
9836
  return 3;
9663
9837
  }
9664
- function compactSuggestedReads(paths, limit = 8) {
9665
- return uniq(paths).filter((path20) => !isGeneratedPath(path20)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
9838
+ function compactSuggestedReads(paths, limit = 8, exclude) {
9839
+ return uniq(paths).filter((path20) => !isCodeGraphExcludedPath(path20, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
9666
9840
  }
9667
- function collectGraph(store, projectId, observations2) {
9841
+ function collectGraph(store, projectId, observations2, exclude) {
9668
9842
  const files = store.listFiles(projectId);
9669
- const symbols = files.flatMap((file) => store.listSymbolsForFile(file.id));
9843
+ const symbols = store.listReferencedSymbols(projectId);
9670
9844
  const filesById = new Map(files.map((file) => [file.id, file]));
9671
9845
  const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
9672
9846
  const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
9673
- const refs = observations2.flatMap((obs) => store.listObservationRefs(projectId, obs.id));
9847
+ const activeObservationIds = new Set(observations2.map((obs) => obs.id));
9848
+ const refs = store.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
9674
9849
  const freshness = {
9675
9850
  current: 0,
9676
9851
  suspect: 0,
@@ -9686,7 +9861,9 @@ function collectGraph(store, projectId, observations2) {
9686
9861
  freshness[result.status] += 1;
9687
9862
  const observation = observationsById.get(ref.observationId);
9688
9863
  if (!observation) continue;
9689
- if (result.status === "current" && file) suggestedReads.push(file.path);
9864
+ const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
9865
+ if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
9866
+ if (excluded) continue;
9690
9867
  sources.push({
9691
9868
  observationId: observation.id,
9692
9869
  title: observation.title,
@@ -9703,12 +9880,10 @@ function collectGraph(store, projectId, observations2) {
9703
9880
  refs,
9704
9881
  freshness,
9705
9882
  sources,
9706
- suggestedReads: compactSuggestedReads(suggestedReads)
9883
+ suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
9707
9884
  };
9708
9885
  }
9709
- function buildProjectContextOverview(input) {
9710
- const active = activeObservations(input.observations, input.project.id);
9711
- const graph = collectGraph(input.store, input.project.id, active);
9886
+ function overviewFromGraph(input) {
9712
9887
  const status = input.store.status(input.project.id);
9713
9888
  return {
9714
9889
  project: input.project,
@@ -9719,20 +9894,26 @@ function buildProjectContextOverview(input) {
9719
9894
  edges: status.edges,
9720
9895
  refs: status.refs,
9721
9896
  ...status.indexedAt ? { indexedAt: status.indexedAt } : {},
9722
- languages: countLanguages(graph.files)
9897
+ languages: countLanguages(input.graph.files)
9723
9898
  },
9724
9899
  memory: {
9725
9900
  total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
9726
- active: active.length
9901
+ active: input.active.length
9727
9902
  },
9728
- freshness: graph.freshness,
9729
- suggestedReads: graph.suggestedReads
9903
+ freshness: input.graph.freshness,
9904
+ suggestedReads: input.graph.suggestedReads
9730
9905
  };
9731
9906
  }
9732
9907
  function buildProjectContextExplain(input) {
9733
- const overview = buildProjectContextOverview(input);
9734
9908
  const active = activeObservations(input.observations, input.project.id);
9735
- const graph = collectGraph(input.store, input.project.id, active);
9909
+ const graph = collectGraph(input.store, input.project.id, active, input.exclude);
9910
+ const overview = overviewFromGraph({
9911
+ project: input.project,
9912
+ store: input.store,
9913
+ observations: input.observations,
9914
+ active,
9915
+ graph
9916
+ });
9736
9917
  return {
9737
9918
  project: input.project,
9738
9919
  sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
@@ -9744,11 +9925,12 @@ var init_project_context = __esm({
9744
9925
  "use strict";
9745
9926
  init_esm_shims();
9746
9927
  init_freshness2();
9928
+ init_exclude();
9747
9929
  }
9748
9930
  });
9749
9931
 
9750
9932
  // src/codegraph/task-lens.ts
9751
- function normalizePath3(path20) {
9933
+ function normalizePath2(path20) {
9752
9934
  return path20.replace(/\\/g, "/");
9753
9935
  }
9754
9936
  function tokenize(text) {
@@ -9778,7 +9960,7 @@ function resolveTaskLens(task) {
9778
9960
  return best.score > 0 ? LENSES[best.id] : LENSES.general;
9779
9961
  }
9780
9962
  function pathKindScore(path20, lens) {
9781
- const normalized = normalizePath3(path20).toLowerCase();
9963
+ const normalized = normalizePath2(path20).toLowerCase();
9782
9964
  const name = normalized.split("/").pop() ?? normalized;
9783
9965
  const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
9784
9966
  const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
@@ -9827,7 +10009,7 @@ function sourceTaskMatchScore(source, task) {
9827
10009
  return 0;
9828
10010
  }
9829
10011
  function fallbackPathRank(path20) {
9830
- const normalized = normalizePath3(path20);
10012
+ const normalized = normalizePath2(path20);
9831
10013
  if (normalized.startsWith("src/")) return 0;
9832
10014
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
9833
10015
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
@@ -9836,7 +10018,7 @@ function fallbackPathRank(path20) {
9836
10018
  }
9837
10019
  function rankLensPaths(paths, lens, task) {
9838
10020
  const tokens = tokenize(task ?? "");
9839
- return [...new Set(paths.map(normalizePath3))].map((path20, index) => ({
10021
+ return [...new Set(paths.map(normalizePath2))].map((path20, index) => ({
9840
10022
  path: path20,
9841
10023
  index,
9842
10024
  score: pathKindScore(path20, lens) + tokenScore(path20, tokens),
@@ -10090,6 +10272,7 @@ async function buildAutoProjectContext(input) {
10090
10272
  const now = input.now ?? /* @__PURE__ */ new Date();
10091
10273
  const task = input.task?.trim();
10092
10274
  const lens = resolveTaskLens(task);
10275
+ const exclude = input.exclude ?? getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph.excludePatterns;
10093
10276
  const store = new CodeGraphStore();
10094
10277
  await store.init(input.dataDir);
10095
10278
  const initialStatus = store.status(input.project.id);
@@ -10107,7 +10290,8 @@ async function buildAutoProjectContext(input) {
10107
10290
  try {
10108
10291
  const indexed = await indexProjectLite({
10109
10292
  projectId: input.project.id,
10110
- projectRoot: input.project.rootPath
10293
+ projectRoot: input.project.rootPath,
10294
+ exclude
10111
10295
  });
10112
10296
  store.replaceProjectIndex(input.project.id, indexed);
10113
10297
  const backfill = await backfillMissingObservationCodeRefs(
@@ -10124,16 +10308,13 @@ async function buildAutoProjectContext(input) {
10124
10308
  };
10125
10309
  }
10126
10310
  }
10127
- const overview = buildProjectContextOverview({
10128
- project: input.project,
10129
- store,
10130
- observations: input.observations
10131
- });
10132
10311
  const explain = buildProjectContextExplain({
10133
10312
  project: input.project,
10134
10313
  store,
10135
- observations: input.observations
10314
+ observations: input.observations,
10315
+ exclude
10136
10316
  });
10317
+ const overview = explain.overview;
10137
10318
  return {
10138
10319
  project: input.project,
10139
10320
  ...task ? { task } : {},
@@ -10342,6 +10523,7 @@ var init_auto_context = __esm({
10342
10523
  "src/codegraph/auto-context.ts"() {
10343
10524
  "use strict";
10344
10525
  init_esm_shims();
10526
+ init_resolved_config();
10345
10527
  init_binder();
10346
10528
  init_current_facts();
10347
10529
  init_lite_provider();
@@ -10369,10 +10551,6 @@ function tokenize2(text) {
10369
10551
  function timestampOf(observation) {
10370
10552
  return Date.parse(observation.updatedAt ?? observation.createdAt ?? "") || 0;
10371
10553
  }
10372
- function isGeneratedPath2(path20) {
10373
- const normalized = path20.replace(/\\/g, "/");
10374
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
10375
- }
10376
10554
  function relevanceScore(observation, taskTokens) {
10377
10555
  if (taskTokens.length === 0) return 0;
10378
10556
  const title = observation.title.toLowerCase();
@@ -10423,7 +10601,8 @@ function assembleContextPackForTask(input) {
10423
10601
  refs,
10424
10602
  files,
10425
10603
  symbols,
10426
- suggestedVerification: input.suggestedVerification
10604
+ suggestedVerification: input.suggestedVerification,
10605
+ exclude: input.exclude
10427
10606
  });
10428
10607
  }
10429
10608
  function assembleContextPack(input) {
@@ -10442,6 +10621,7 @@ function assembleContextPack(input) {
10442
10621
  observationIdsWithRefs.add(ref.observationId);
10443
10622
  const file = ref.fileId ? files.get(ref.fileId) : void 0;
10444
10623
  const symbol = ref.symbolId ? symbols.get(ref.symbolId) : void 0;
10624
+ if (file && isCodeGraphExcludedPath(file.path, input.exclude)) continue;
10445
10625
  const freshness = evaluateCodeRefFreshness(ref, file, symbol);
10446
10626
  if (freshness.status === "current") {
10447
10627
  const memoryKey = `${observation.id}:${freshness.status}`;
@@ -10507,8 +10687,8 @@ function buildContextPackPrompt(pack) {
10507
10687
  const reliableMemories = pack.memories.filter((memory) => memory.status === "current");
10508
10688
  const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
10509
10689
  const lines = ["## Task", pack.task, "", "## Reliable Memories"];
10510
- const visibleCodeFacts = pack.codeFacts.filter((fact) => !isGeneratedPath2(fact.path)).slice(0, 5);
10511
- const visibleSuggestedReads = pack.suggestedReads.filter((path20) => !isGeneratedPath2(path20)).slice(0, 5);
10690
+ const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
10691
+ const visibleSuggestedReads = pack.suggestedReads.filter((path20) => !isCodeGraphExcludedPath(path20)).slice(0, 5);
10512
10692
  if (reliableMemories.length === 0) lines.push("- none");
10513
10693
  for (const memory of reliableMemories) {
10514
10694
  lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
@@ -10543,6 +10723,7 @@ var init_context_pack = __esm({
10543
10723
  "use strict";
10544
10724
  init_esm_shims();
10545
10725
  init_freshness2();
10726
+ init_exclude();
10546
10727
  }
10547
10728
  });
10548
10729
 
@@ -19924,7 +20105,7 @@ The path should point to a directory containing a .git folder.`
19924
20105
  };
19925
20106
  const server = existingServer ?? new McpServer({
19926
20107
  name: "memorix",
19927
- version: true ? "1.1.8" : "1.0.1"
20108
+ version: true ? "1.1.10" : "1.0.1"
19928
20109
  });
19929
20110
  const originalRegisterTool = server.registerTool.bind(server);
19930
20111
  server.registerTool = ((name, ...args) => {
@@ -20626,15 +20807,18 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
20626
20807
  return withFreshIndex(async () => {
20627
20808
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
20628
20809
  const { assembleContextPackForTask: assembleContextPackForTask2, buildContextPackPrompt: buildContextPackPrompt2 } = await Promise.resolve().then(() => (init_context_pack(), context_pack_exports));
20810
+ const { getResolvedConfig: getResolvedConfig2 } = await Promise.resolve().then(() => (init_resolved_config(), resolved_config_exports));
20629
20811
  const store = new CodeGraphStore2();
20630
20812
  await store.init(projectDir2);
20813
+ const exclude = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph.excludePatterns;
20631
20814
  const observations2 = getAllObservations().filter((obs) => obs.projectId === project.id && (obs.status ?? "active") === "active").reverse();
20632
20815
  const pack = assembleContextPackForTask2({
20633
20816
  store,
20634
20817
  projectId: project.id,
20635
20818
  task,
20636
20819
  observations: observations2,
20637
- limit: typeof limit === "number" ? limit : 20
20820
+ limit: typeof limit === "number" ? limit : 20,
20821
+ exclude
20638
20822
  });
20639
20823
  const text = buildContextPackPrompt2(pack);
20640
20824
  return {